scintilla: Update scintilla with changeset 3662:1d1c06df8a2f using gtk+3
[anjuta-extras.git] / plugins / scintilla / scintilla / Editor.cxx
blobd1d0e7f917ae72249261171f1b09fb16e8f6106d
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 <algorithm>
17 #include <memory>
19 #include "Platform.h"
21 #include "ILexer.h"
22 #include "Scintilla.h"
24 #include "SplitVector.h"
25 #include "Partitioning.h"
26 #include "RunStyles.h"
27 #include "ContractionState.h"
28 #include "CellBuffer.h"
29 #include "KeyMap.h"
30 #include "Indicator.h"
31 #include "XPM.h"
32 #include "LineMarker.h"
33 #include "Style.h"
34 #include "ViewStyle.h"
35 #include "CharClassify.h"
36 #include "Decoration.h"
37 #include "Document.h"
38 #include "Selection.h"
39 #include "PositionCache.h"
40 #include "Editor.h"
42 #ifdef SCI_NAMESPACE
43 using namespace Scintilla;
44 #endif
47 return whether this modification represents an operation that
48 may reasonably be deferred (not done now OR [possibly] at all)
50 static bool CanDeferToLastStep(const DocModification &mh) {
51 if (mh.modificationType & (SC_MOD_BEFOREINSERT | SC_MOD_BEFOREDELETE))
52 return true; // CAN skip
53 if (!(mh.modificationType & (SC_PERFORMED_UNDO | SC_PERFORMED_REDO)))
54 return false; // MUST do
55 if (mh.modificationType & SC_MULTISTEPUNDOREDO)
56 return true; // CAN skip
57 return false; // PRESUMABLY must do
60 static bool CanEliminate(const DocModification &mh) {
61 return
62 (mh.modificationType & (SC_MOD_BEFOREINSERT | SC_MOD_BEFOREDELETE)) != 0;
66 return whether this modification represents the FINAL step
67 in a [possibly lengthy] multi-step Undo/Redo sequence
69 static bool IsLastStep(const DocModification &mh) {
70 return
71 (mh.modificationType & (SC_PERFORMED_UNDO | SC_PERFORMED_REDO)) != 0
72 && (mh.modificationType & SC_MULTISTEPUNDOREDO) != 0
73 && (mh.modificationType & SC_LASTSTEPINUNDOREDO) != 0
74 && (mh.modificationType & SC_MULTILINEUNDOREDO) != 0;
77 Caret::Caret() :
78 active(false), on(false), period(500) {}
80 Timer::Timer() :
81 ticking(false), ticksToWait(0), tickerID(0) {}
83 Idler::Idler() :
84 state(false), idlerID(0) {}
86 static inline bool IsControlCharacter(int ch) {
87 // iscntrl returns true for lots of chars > 127 which are displayable
88 return ch >= 0 && ch < ' ';
91 static inline bool IsAllSpacesOrTabs(char *s, unsigned int len) {
92 for (unsigned int i = 0; i < len; i++) {
93 // This is safe because IsSpaceOrTab() will return false for null terminators
94 if (!IsSpaceOrTab(s[i]))
95 return false;
97 return true;
100 Editor::Editor() {
101 ctrlID = 0;
103 stylesValid = false;
105 printMagnification = 0;
106 printColourMode = SC_PRINT_NORMAL;
107 printWrapState = eWrapWord;
108 cursorMode = SC_CURSORNORMAL;
109 controlCharSymbol = 0; /* Draw the control characters */
111 hasFocus = false;
112 hideSelection = false;
113 inOverstrike = false;
114 errorStatus = 0;
115 mouseDownCaptures = true;
117 bufferedDraw = true;
118 twoPhaseDraw = true;
120 lastClickTime = 0;
121 dwellDelay = SC_TIME_FOREVER;
122 ticksToDwell = SC_TIME_FOREVER;
123 dwelling = false;
124 ptMouseLast.x = 0;
125 ptMouseLast.y = 0;
126 inDragDrop = ddNone;
127 dropWentOutside = false;
128 posDrag = SelectionPosition(invalidPosition);
129 posDrop = SelectionPosition(invalidPosition);
130 hotSpotClickPos = INVALID_POSITION;
131 selectionType = selChar;
133 lastXChosen = 0;
134 lineAnchor = 0;
135 originalAnchorPos = 0;
136 wordSelectAnchorStartPos = 0;
137 wordSelectAnchorEndPos = 0;
138 wordSelectInitialCaretPos = -1;
140 primarySelection = true;
142 caretXPolicy = CARET_SLOP | CARET_EVEN;
143 caretXSlop = 50;
145 caretYPolicy = CARET_EVEN;
146 caretYSlop = 0;
148 searchAnchor = 0;
150 xOffset = 0;
151 xCaretMargin = 50;
152 horizontalScrollBarVisible = true;
153 scrollWidth = 2000;
154 trackLineWidth = false;
155 lineWidthMaxSeen = 0;
156 verticalScrollBarVisible = true;
157 endAtLastLine = true;
158 caretSticky = SC_CARETSTICKY_OFF;
159 multipleSelection = false;
160 additionalSelectionTyping = false;
161 multiPasteMode = SC_MULTIPASTE_ONCE;
162 additionalCaretsBlink = true;
163 additionalCaretsVisible = true;
164 virtualSpaceOptions = SCVS_NONE;
166 pixmapLine = Surface::Allocate();
167 pixmapSelMargin = Surface::Allocate();
168 pixmapSelPattern = Surface::Allocate();
169 pixmapIndentGuide = Surface::Allocate();
170 pixmapIndentGuideHighlight = Surface::Allocate();
172 targetStart = 0;
173 targetEnd = 0;
174 searchFlags = 0;
176 topLine = 0;
177 posTopLine = 0;
179 lengthForEncode = -1;
181 needUpdateUI = 0;
182 ContainerNeedsUpdate(SC_UPDATE_CONTENT);
183 braces[0] = invalidPosition;
184 braces[1] = invalidPosition;
185 bracesMatchStyle = STYLE_BRACEBAD;
186 highlightGuideColumn = 0;
188 theEdge = 0;
190 paintState = notPainting;
192 modEventMask = SC_MODEVENTMASKALL;
194 pdoc = new Document();
195 pdoc->AddRef();
196 pdoc->AddWatcher(this, 0);
198 recordingMacro = false;
199 foldFlags = 0;
201 wrapState = eWrapNone;
202 wrapWidth = LineLayout::wrapWidthInfinite;
203 wrapStart = wrapLineLarge;
204 wrapEnd = wrapLineLarge;
205 wrapVisualFlags = 0;
206 wrapVisualFlagsLocation = 0;
207 wrapVisualStartIndent = 0;
208 wrapIndentMode = SC_WRAPINDENT_FIXED;
209 wrapAddIndent = 0;
211 convertPastes = true;
213 hsStart = -1;
214 hsEnd = -1;
216 llc.SetLevel(LineLayoutCache::llcCaret);
217 posCache.SetSize(0x400);
220 Editor::~Editor() {
221 pdoc->RemoveWatcher(this, 0);
222 pdoc->Release();
223 pdoc = 0;
224 DropGraphics();
225 delete pixmapLine;
226 delete pixmapSelMargin;
227 delete pixmapSelPattern;
228 delete pixmapIndentGuide;
229 delete pixmapIndentGuideHighlight;
232 void Editor::Finalise() {
233 SetIdle(false);
234 CancelModes();
237 void Editor::DropGraphics() {
238 pixmapLine->Release();
239 pixmapSelMargin->Release();
240 pixmapSelPattern->Release();
241 pixmapIndentGuide->Release();
242 pixmapIndentGuideHighlight->Release();
245 void Editor::InvalidateStyleData() {
246 stylesValid = false;
247 DropGraphics();
248 palette.Release();
249 llc.Invalidate(LineLayout::llInvalid);
250 posCache.Clear();
253 void Editor::InvalidateStyleRedraw() {
254 NeedWrapping();
255 InvalidateStyleData();
256 Redraw();
259 void Editor::RefreshColourPalette(Palette &pal, bool want) {
260 vs.RefreshColourPalette(pal, want);
263 void Editor::RefreshStyleData() {
264 if (!stylesValid) {
265 stylesValid = true;
266 AutoSurface surface(this);
267 if (surface) {
268 vs.Refresh(*surface);
269 RefreshColourPalette(palette, true);
270 palette.Allocate(wMain);
271 RefreshColourPalette(palette, false);
273 if (wrapIndentMode == SC_WRAPINDENT_INDENT) {
274 wrapAddIndent = pdoc->IndentSize() * vs.spaceWidth;
275 } else if (wrapIndentMode == SC_WRAPINDENT_SAME) {
276 wrapAddIndent = 0;
277 } else { //SC_WRAPINDENT_FIXED
278 wrapAddIndent = wrapVisualStartIndent * vs.aveCharWidth;
279 if ((wrapVisualFlags & SC_WRAPVISUALFLAG_START) && (wrapAddIndent <= 0))
280 wrapAddIndent = vs.aveCharWidth; // must indent to show start visual
282 SetScrollBars();
283 SetRectangularRange();
287 PRectangle Editor::GetClientRectangle() {
288 return wMain.GetClientPosition();
291 PRectangle Editor::GetTextRectangle() {
292 PRectangle rc = GetClientRectangle();
293 rc.left += vs.fixedColumnWidth;
294 rc.right -= vs.rightMarginWidth;
295 return rc;
298 int Editor::LinesOnScreen() {
299 PRectangle rcClient = GetClientRectangle();
300 int htClient = rcClient.bottom - rcClient.top;
301 //Platform::DebugPrintf("lines on screen = %d\n", htClient / lineHeight + 1);
302 return htClient / vs.lineHeight;
305 int Editor::LinesToScroll() {
306 int retVal = LinesOnScreen() - 1;
307 if (retVal < 1)
308 return 1;
309 else
310 return retVal;
313 int Editor::MaxScrollPos() {
314 //Platform::DebugPrintf("Lines %d screen = %d maxScroll = %d\n",
315 //LinesTotal(), LinesOnScreen(), LinesTotal() - LinesOnScreen() + 1);
316 int retVal = cs.LinesDisplayed();
317 if (endAtLastLine) {
318 retVal -= LinesOnScreen();
319 } else {
320 retVal--;
322 if (retVal < 0) {
323 return 0;
324 } else {
325 return retVal;
329 const char *ControlCharacterString(unsigned char ch) {
330 const char *reps[] = {
331 "NUL", "SOH", "STX", "ETX", "EOT", "ENQ", "ACK", "BEL",
332 "BS", "HT", "LF", "VT", "FF", "CR", "SO", "SI",
333 "DLE", "DC1", "DC2", "DC3", "DC4", "NAK", "SYN", "ETB",
334 "CAN", "EM", "SUB", "ESC", "FS", "GS", "RS", "US"
336 if (ch < (sizeof(reps) / sizeof(reps[0]))) {
337 return reps[ch];
338 } else {
339 return "BAD";
344 * Convenience class to ensure LineLayout objects are always disposed.
346 class AutoLineLayout {
347 LineLayoutCache &llc;
348 LineLayout *ll;
349 AutoLineLayout &operator=(const AutoLineLayout &);
350 public:
351 AutoLineLayout(LineLayoutCache &llc_, LineLayout *ll_) : llc(llc_), ll(ll_) {}
352 ~AutoLineLayout() {
353 llc.Dispose(ll);
354 ll = 0;
356 LineLayout *operator->() const {
357 return ll;
359 operator LineLayout *() const {
360 return ll;
362 void Set(LineLayout *ll_) {
363 llc.Dispose(ll);
364 ll = ll_;
368 SelectionPosition Editor::ClampPositionIntoDocument(SelectionPosition sp) const {
369 if (sp.Position() < 0) {
370 return SelectionPosition(0);
371 } else if (sp.Position() > pdoc->Length()) {
372 return SelectionPosition(pdoc->Length());
373 } else {
374 // If not at end of line then set offset to 0
375 if (!pdoc->IsLineEndPosition(sp.Position()))
376 sp.SetVirtualSpace(0);
377 return sp;
381 Point Editor::LocationFromPosition(SelectionPosition pos) {
382 Point pt;
383 RefreshStyleData();
384 if (pos.Position() == INVALID_POSITION)
385 return pt;
386 int line = pdoc->LineFromPosition(pos.Position());
387 int lineVisible = cs.DisplayFromDoc(line);
388 //Platform::DebugPrintf("line=%d\n", line);
389 AutoSurface surface(this);
390 AutoLineLayout ll(llc, RetrieveLineLayout(line));
391 if (surface && ll) {
392 // -1 because of adding in for visible lines in following loop.
393 pt.y = (lineVisible - topLine - 1) * vs.lineHeight;
394 pt.x = 0;
395 unsigned int posLineStart = pdoc->LineStart(line);
396 LayoutLine(line, surface, vs, ll, wrapWidth);
397 int posInLine = pos.Position() - posLineStart;
398 // In case of very long line put x at arbitrary large position
399 if (posInLine > ll->maxLineLength) {
400 pt.x = ll->positions[ll->maxLineLength] - ll->positions[ll->LineStart(ll->lines)];
403 for (int subLine = 0; subLine < ll->lines; subLine++) {
404 if ((posInLine >= ll->LineStart(subLine)) && (posInLine <= ll->LineStart(subLine + 1))) {
405 pt.x = ll->positions[posInLine] - ll->positions[ll->LineStart(subLine)];
406 if (ll->wrapIndent != 0) {
407 int lineStart = ll->LineStart(subLine);
408 if (lineStart != 0) // Wrapped
409 pt.x += ll->wrapIndent;
412 if (posInLine >= ll->LineStart(subLine)) {
413 pt.y += vs.lineHeight;
416 pt.x += vs.fixedColumnWidth - xOffset;
418 pt.x += pos.VirtualSpace() * static_cast<int>(vs.styles[ll->EndLineStyle()].spaceWidth);
419 return pt;
422 Point Editor::LocationFromPosition(int pos) {
423 return LocationFromPosition(SelectionPosition(pos));
426 int Editor::XFromPosition(int pos) {
427 Point pt = LocationFromPosition(pos);
428 return pt.x - vs.fixedColumnWidth + xOffset;
431 int Editor::XFromPosition(SelectionPosition sp) {
432 Point pt = LocationFromPosition(sp);
433 return pt.x - vs.fixedColumnWidth + xOffset;
436 int Editor::LineFromLocation(Point pt) {
437 return cs.DocFromDisplay(pt.y / vs.lineHeight + topLine);
440 void Editor::SetTopLine(int topLineNew) {
441 if (topLine != topLineNew) {
442 topLine = topLineNew;
443 ContainerNeedsUpdate(SC_UPDATE_V_SCROLL);
445 posTopLine = pdoc->LineStart(cs.DocFromDisplay(topLine));
448 SelectionPosition Editor::SPositionFromLocation(Point pt, bool canReturnInvalid, bool charPosition, bool virtualSpace) {
449 RefreshStyleData();
450 if (canReturnInvalid) {
451 PRectangle rcClient = GetTextRectangle();
452 if (!rcClient.Contains(pt))
453 return SelectionPosition(INVALID_POSITION);
454 if (pt.x < vs.fixedColumnWidth)
455 return SelectionPosition(INVALID_POSITION);
456 if (pt.y < 0)
457 return SelectionPosition(INVALID_POSITION);
459 pt.x = pt.x - vs.fixedColumnWidth + xOffset;
460 int visibleLine = pt.y / vs.lineHeight + topLine;
461 if (pt.y < 0) { // Division rounds towards 0
462 visibleLine = (pt.y - (vs.lineHeight - 1)) / vs.lineHeight + topLine;
464 if (!canReturnInvalid && (visibleLine < 0))
465 visibleLine = 0;
466 int lineDoc = cs.DocFromDisplay(visibleLine);
467 if (canReturnInvalid && (lineDoc < 0))
468 return SelectionPosition(INVALID_POSITION);
469 if (lineDoc >= pdoc->LinesTotal())
470 return SelectionPosition(canReturnInvalid ? INVALID_POSITION : pdoc->Length());
471 unsigned int posLineStart = pdoc->LineStart(lineDoc);
472 SelectionPosition retVal(canReturnInvalid ? INVALID_POSITION : static_cast<int>(posLineStart));
473 AutoSurface surface(this);
474 AutoLineLayout ll(llc, RetrieveLineLayout(lineDoc));
475 if (surface && ll) {
476 LayoutLine(lineDoc, surface, vs, ll, wrapWidth);
477 int lineStartSet = cs.DisplayFromDoc(lineDoc);
478 int subLine = visibleLine - lineStartSet;
479 if (subLine < ll->lines) {
480 int lineStart = ll->LineStart(subLine);
481 int lineEnd = ll->LineLastVisible(subLine);
482 int subLineStart = ll->positions[lineStart];
484 if (ll->wrapIndent != 0) {
485 if (lineStart != 0) // Wrapped
486 pt.x -= ll->wrapIndent;
488 int i = ll->FindBefore(pt.x + subLineStart, lineStart, lineEnd);
489 while (i < lineEnd) {
490 if (charPosition) {
491 if ((pt.x + subLineStart) < (ll->positions[i + 1])) {
492 return SelectionPosition(pdoc->MovePositionOutsideChar(i + posLineStart, 1));
494 } else {
495 if ((pt.x + subLineStart) < ((ll->positions[i] + ll->positions[i + 1]) / 2)) {
496 return SelectionPosition(pdoc->MovePositionOutsideChar(i + posLineStart, 1));
499 i++;
501 if (virtualSpace) {
502 const int spaceWidth = static_cast<int>(vs.styles[ll->EndLineStyle()].spaceWidth);
503 int spaceOffset = (pt.x + subLineStart - ll->positions[lineEnd] + spaceWidth / 2) /
504 spaceWidth;
505 return SelectionPosition(lineEnd + posLineStart, spaceOffset);
506 } else if (canReturnInvalid) {
507 if (pt.x < (ll->positions[lineEnd] - subLineStart)) {
508 return SelectionPosition(pdoc->MovePositionOutsideChar(lineEnd + posLineStart, 1));
510 } else {
511 return SelectionPosition(lineEnd + posLineStart);
514 if (!canReturnInvalid)
515 return SelectionPosition(ll->numCharsInLine + posLineStart);
517 return retVal;
520 int Editor::PositionFromLocation(Point pt, bool canReturnInvalid, bool charPosition) {
521 return SPositionFromLocation(pt, canReturnInvalid, charPosition, false).Position();
525 * Find the document position corresponding to an x coordinate on a particular document line.
526 * Ensure is between whole characters when document is in multi-byte or UTF-8 mode.
528 int Editor::PositionFromLineX(int lineDoc, int x) {
529 RefreshStyleData();
530 if (lineDoc >= pdoc->LinesTotal())
531 return pdoc->Length();
532 //Platform::DebugPrintf("Position of (%d,%d) line = %d top=%d\n", pt.x, pt.y, line, topLine);
533 AutoSurface surface(this);
534 AutoLineLayout ll(llc, RetrieveLineLayout(lineDoc));
535 int retVal = 0;
536 if (surface && ll) {
537 unsigned int posLineStart = pdoc->LineStart(lineDoc);
538 LayoutLine(lineDoc, surface, vs, ll, wrapWidth);
539 retVal = ll->numCharsBeforeEOL + posLineStart;
540 int subLine = 0;
541 int lineStart = ll->LineStart(subLine);
542 int lineEnd = ll->LineLastVisible(subLine);
543 int subLineStart = ll->positions[lineStart];
545 if (ll->wrapIndent != 0) {
546 if (lineStart != 0) // Wrapped
547 x -= ll->wrapIndent;
549 int i = ll->FindBefore(x + subLineStart, lineStart, lineEnd);
550 while (i < lineEnd) {
551 if ((x + subLineStart) < ((ll->positions[i] + ll->positions[i + 1]) / 2)) {
552 retVal = pdoc->MovePositionOutsideChar(i + posLineStart, 1);
553 break;
555 i++;
558 return retVal;
562 * Find the document position corresponding to an x coordinate on a particular document line.
563 * Ensure is between whole characters when document is in multi-byte or UTF-8 mode.
565 SelectionPosition Editor::SPositionFromLineX(int lineDoc, int x) {
566 RefreshStyleData();
567 if (lineDoc >= pdoc->LinesTotal())
568 return SelectionPosition(pdoc->Length());
569 //Platform::DebugPrintf("Position of (%d,%d) line = %d top=%d\n", pt.x, pt.y, line, topLine);
570 AutoSurface surface(this);
571 AutoLineLayout ll(llc, RetrieveLineLayout(lineDoc));
572 int retVal = 0;
573 if (surface && ll) {
574 unsigned int posLineStart = pdoc->LineStart(lineDoc);
575 LayoutLine(lineDoc, surface, vs, ll, wrapWidth);
576 int subLine = 0;
577 int lineStart = ll->LineStart(subLine);
578 int lineEnd = ll->LineLastVisible(subLine);
579 int subLineStart = ll->positions[lineStart];
581 if (ll->wrapIndent != 0) {
582 if (lineStart != 0) // Wrapped
583 x -= ll->wrapIndent;
585 int i = ll->FindBefore(x + subLineStart, lineStart, lineEnd);
586 while (i < lineEnd) {
587 if ((x + subLineStart) < ((ll->positions[i] + ll->positions[i + 1]) / 2)) {
588 retVal = pdoc->MovePositionOutsideChar(i + posLineStart, 1);
589 return SelectionPosition(retVal);
591 i++;
593 const int spaceWidth = static_cast<int>(vs.styles[ll->EndLineStyle()].spaceWidth);
594 int spaceOffset = (x + subLineStart - ll->positions[lineEnd] + spaceWidth / 2) / spaceWidth;
595 return SelectionPosition(lineEnd + posLineStart, spaceOffset);
597 return SelectionPosition(retVal);
601 * If painting then abandon the painting because a wider redraw is needed.
602 * @return true if calling code should stop drawing.
604 bool Editor::AbandonPaint() {
605 if ((paintState == painting) && !paintingAllText) {
606 paintState = paintAbandoned;
608 return paintState == paintAbandoned;
611 void Editor::RedrawRect(PRectangle rc) {
612 //Platform::DebugPrintf("Redraw %0d,%0d - %0d,%0d\n", rc.left, rc.top, rc.right, rc.bottom);
614 // Clip the redraw rectangle into the client area
615 PRectangle rcClient = GetClientRectangle();
616 if (rc.top < rcClient.top)
617 rc.top = rcClient.top;
618 if (rc.bottom > rcClient.bottom)
619 rc.bottom = rcClient.bottom;
620 if (rc.left < rcClient.left)
621 rc.left = rcClient.left;
622 if (rc.right > rcClient.right)
623 rc.right = rcClient.right;
625 if ((rc.bottom > rc.top) && (rc.right > rc.left)) {
626 wMain.InvalidateRectangle(rc);
630 void Editor::Redraw() {
631 //Platform::DebugPrintf("Redraw all\n");
632 PRectangle rcClient = GetClientRectangle();
633 wMain.InvalidateRectangle(rcClient);
634 //wMain.InvalidateAll();
637 void Editor::RedrawSelMargin(int line, bool allAfter) {
638 if (!AbandonPaint()) {
639 if (vs.maskInLine) {
640 Redraw();
641 } else {
642 PRectangle rcSelMargin = GetClientRectangle();
643 rcSelMargin.right = vs.fixedColumnWidth;
644 if (line != -1) {
645 int position = pdoc->LineStart(line);
646 PRectangle rcLine = RectangleFromRange(position, position);
647 rcSelMargin.top = rcLine.top;
648 if (!allAfter)
649 rcSelMargin.bottom = rcLine.bottom;
651 wMain.InvalidateRectangle(rcSelMargin);
656 PRectangle Editor::RectangleFromRange(int start, int end) {
657 int minPos = start;
658 if (minPos > end)
659 minPos = end;
660 int maxPos = start;
661 if (maxPos < end)
662 maxPos = end;
663 int minLine = cs.DisplayFromDoc(pdoc->LineFromPosition(minPos));
664 int lineDocMax = pdoc->LineFromPosition(maxPos);
665 int maxLine = cs.DisplayFromDoc(lineDocMax) + cs.GetHeight(lineDocMax) - 1;
666 PRectangle rcClient = GetTextRectangle();
667 PRectangle rc;
668 rc.left = vs.fixedColumnWidth;
669 rc.top = (minLine - topLine) * vs.lineHeight;
670 if (rc.top < 0)
671 rc.top = 0;
672 rc.right = rcClient.right;
673 rc.bottom = (maxLine - topLine + 1) * vs.lineHeight;
674 // Ensure PRectangle is within 16 bit space
675 rc.top = Platform::Clamp(rc.top, -32000, 32000);
676 rc.bottom = Platform::Clamp(rc.bottom, -32000, 32000);
678 return rc;
681 void Editor::InvalidateRange(int start, int end) {
682 RedrawRect(RectangleFromRange(start, end));
685 int Editor::CurrentPosition() {
686 return sel.MainCaret();
689 bool Editor::SelectionEmpty() {
690 return sel.Empty();
693 SelectionPosition Editor::SelectionStart() {
694 return sel.RangeMain().Start();
697 SelectionPosition Editor::SelectionEnd() {
698 return sel.RangeMain().End();
701 void Editor::SetRectangularRange() {
702 if (sel.IsRectangular()) {
703 int xAnchor = XFromPosition(sel.Rectangular().anchor);
704 int xCaret = XFromPosition(sel.Rectangular().caret);
705 if (sel.selType == Selection::selThin) {
706 xCaret = xAnchor;
708 int lineAnchorRect = pdoc->LineFromPosition(sel.Rectangular().anchor.Position());
709 int lineCaret = pdoc->LineFromPosition(sel.Rectangular().caret.Position());
710 int increment = (lineCaret > lineAnchorRect) ? 1 : -1;
711 for (int line=lineAnchorRect; line != lineCaret+increment; line += increment) {
712 SelectionRange range(SPositionFromLineX(line, xCaret), SPositionFromLineX(line, xAnchor));
713 if ((virtualSpaceOptions & SCVS_RECTANGULARSELECTION) == 0)
714 range.ClearVirtualSpace();
715 if (line == lineAnchorRect)
716 sel.SetSelection(range);
717 else
718 sel.AddSelectionWithoutTrim(range);
723 void Editor::ThinRectangularRange() {
724 if (sel.IsRectangular()) {
725 sel.selType = Selection::selThin;
726 if (sel.Rectangular().caret < sel.Rectangular().anchor) {
727 sel.Rectangular() = SelectionRange(sel.Range(sel.Count()-1).caret, sel.Range(0).anchor);
728 } else {
729 sel.Rectangular() = SelectionRange(sel.Range(sel.Count()-1).anchor, sel.Range(0).caret);
731 SetRectangularRange();
735 void Editor::InvalidateSelection(SelectionRange newMain, bool invalidateWholeSelection) {
736 if (sel.Count() > 1 || !(sel.RangeMain().anchor == newMain.anchor) || sel.IsRectangular()) {
737 invalidateWholeSelection = true;
739 int firstAffected = Platform::Minimum(sel.RangeMain().Start().Position(), newMain.Start().Position());
740 // +1 for lastAffected ensures caret repainted
741 int lastAffected = Platform::Maximum(newMain.caret.Position()+1, newMain.anchor.Position());
742 lastAffected = Platform::Maximum(lastAffected, sel.RangeMain().End().Position());
743 if (invalidateWholeSelection) {
744 for (size_t r=0; r<sel.Count(); r++) {
745 firstAffected = Platform::Minimum(firstAffected, sel.Range(r).caret.Position());
746 firstAffected = Platform::Minimum(firstAffected, sel.Range(r).anchor.Position());
747 lastAffected = Platform::Maximum(lastAffected, sel.Range(r).caret.Position()+1);
748 lastAffected = Platform::Maximum(lastAffected, sel.Range(r).anchor.Position());
751 ContainerNeedsUpdate(SC_UPDATE_SELECTION);
752 InvalidateRange(firstAffected, lastAffected);
755 void Editor::SetSelection(SelectionPosition currentPos_, SelectionPosition anchor_) {
756 currentPos_ = ClampPositionIntoDocument(currentPos_);
757 anchor_ = ClampPositionIntoDocument(anchor_);
758 int currentLine = pdoc->LineFromPosition(currentPos_.Position());
759 /* For Line selection - ensure the anchor and caret are always
760 at the beginning and end of the region lines. */
761 if (sel.selType == Selection::selLines) {
762 if (currentPos_ > anchor_) {
763 anchor_ = SelectionPosition(pdoc->LineStart(pdoc->LineFromPosition(anchor_.Position())));
764 currentPos_ = SelectionPosition(pdoc->LineEnd(pdoc->LineFromPosition(currentPos_.Position())));
765 } else {
766 currentPos_ = SelectionPosition(pdoc->LineStart(pdoc->LineFromPosition(currentPos_.Position())));
767 anchor_ = SelectionPosition(pdoc->LineEnd(pdoc->LineFromPosition(anchor_.Position())));
770 SelectionRange rangeNew(currentPos_, anchor_);
771 if (sel.Count() > 1 || !(sel.RangeMain() == rangeNew)) {
772 InvalidateSelection(rangeNew);
774 sel.RangeMain() = rangeNew;
775 SetRectangularRange();
776 ClaimSelection();
778 if (highlightDelimiter.NeedsDrawing(currentLine)) {
779 RedrawSelMargin();
783 void Editor::SetSelection(int currentPos_, int anchor_) {
784 SetSelection(SelectionPosition(currentPos_), SelectionPosition(anchor_));
787 // Just move the caret on the main selection
788 void Editor::SetSelection(SelectionPosition currentPos_) {
789 currentPos_ = ClampPositionIntoDocument(currentPos_);
790 int currentLine = pdoc->LineFromPosition(currentPos_.Position());
791 if (sel.Count() > 1 || !(sel.RangeMain().caret == currentPos_)) {
792 InvalidateSelection(SelectionRange(currentPos_));
794 if (sel.IsRectangular()) {
795 sel.Rectangular() =
796 SelectionRange(SelectionPosition(currentPos_), sel.Rectangular().anchor);
797 SetRectangularRange();
798 } else {
799 sel.RangeMain() =
800 SelectionRange(SelectionPosition(currentPos_), sel.RangeMain().anchor);
802 ClaimSelection();
804 if (highlightDelimiter.NeedsDrawing(currentLine)) {
805 RedrawSelMargin();
809 void Editor::SetSelection(int currentPos_) {
810 SetSelection(SelectionPosition(currentPos_));
813 void Editor::SetEmptySelection(SelectionPosition currentPos_) {
814 int currentLine = pdoc->LineFromPosition(currentPos_.Position());
815 SelectionRange rangeNew(ClampPositionIntoDocument(currentPos_));
816 if (sel.Count() > 1 || !(sel.RangeMain() == rangeNew)) {
817 InvalidateSelection(rangeNew);
819 sel.Clear();
820 sel.RangeMain() = rangeNew;
821 SetRectangularRange();
822 ClaimSelection();
824 if (highlightDelimiter.NeedsDrawing(currentLine)) {
825 RedrawSelMargin();
829 void Editor::SetEmptySelection(int currentPos_) {
830 SetEmptySelection(SelectionPosition(currentPos_));
833 bool Editor::RangeContainsProtected(int start, int end) const {
834 if (vs.ProtectionActive()) {
835 if (start > end) {
836 int t = start;
837 start = end;
838 end = t;
840 int mask = pdoc->stylingBitsMask;
841 for (int pos = start; pos < end; pos++) {
842 if (vs.styles[pdoc->StyleAt(pos) & mask].IsProtected())
843 return true;
846 return false;
849 bool Editor::SelectionContainsProtected() {
850 for (size_t r=0; r<sel.Count(); r++) {
851 if (RangeContainsProtected(sel.Range(r).Start().Position(),
852 sel.Range(r).End().Position())) {
853 return true;
856 return false;
860 * Asks document to find a good position and then moves out of any invisible positions.
862 int Editor::MovePositionOutsideChar(int pos, int moveDir, bool checkLineEnd) const {
863 return MovePositionOutsideChar(SelectionPosition(pos), moveDir, checkLineEnd).Position();
866 SelectionPosition Editor::MovePositionOutsideChar(SelectionPosition pos, int moveDir, bool checkLineEnd) const {
867 int posMoved = pdoc->MovePositionOutsideChar(pos.Position(), moveDir, checkLineEnd);
868 if (posMoved != pos.Position())
869 pos.SetPosition(posMoved);
870 if (vs.ProtectionActive()) {
871 int mask = pdoc->stylingBitsMask;
872 if (moveDir > 0) {
873 if ((pos.Position() > 0) && vs.styles[pdoc->StyleAt(pos.Position() - 1) & mask].IsProtected()) {
874 while ((pos.Position() < pdoc->Length()) &&
875 (vs.styles[pdoc->StyleAt(pos.Position()) & mask].IsProtected()))
876 pos.Add(1);
878 } else if (moveDir < 0) {
879 if (vs.styles[pdoc->StyleAt(pos.Position()) & mask].IsProtected()) {
880 while ((pos.Position() > 0) &&
881 (vs.styles[pdoc->StyleAt(pos.Position() - 1) & mask].IsProtected()))
882 pos.Add(-1);
886 return pos;
889 int Editor::MovePositionTo(SelectionPosition newPos, Selection::selTypes selt, bool ensureVisible) {
890 bool simpleCaret = (sel.Count() == 1) && sel.Empty();
891 SelectionPosition spCaret = sel.Last();
893 int delta = newPos.Position() - sel.MainCaret();
894 newPos = ClampPositionIntoDocument(newPos);
895 newPos = MovePositionOutsideChar(newPos, delta);
896 if (!multipleSelection && sel.IsRectangular() && (selt == Selection::selStream)) {
897 // Can't turn into multiple selection so clear additional selections
898 InvalidateSelection(SelectionRange(newPos), true);
899 SelectionRange rangeMain = sel.RangeMain();
900 sel.SetSelection(rangeMain);
902 if (!sel.IsRectangular() && (selt == Selection::selRectangle)) {
903 // Switching to rectangular
904 SelectionRange rangeMain = sel.RangeMain();
905 sel.Clear();
906 sel.Rectangular() = rangeMain;
908 if (selt != Selection::noSel) {
909 sel.selType = selt;
911 if (selt != Selection::noSel || sel.MoveExtends()) {
912 SetSelection(newPos);
913 } else {
914 SetEmptySelection(newPos);
916 ShowCaretAtCurrentPosition();
917 if (ensureVisible) {
918 XYScrollPosition newXY = XYScrollToMakeVisible(true, true, true);
919 if (simpleCaret && (newXY.xOffset == xOffset)) {
920 // simple vertical scroll then invalidate
921 ScrollTo(newXY.topLine);
922 InvalidateSelection(SelectionRange(spCaret), true);
923 } else {
924 SetXYScroll(newXY);
928 int currentLine = pdoc->LineFromPosition(newPos.Position());
930 if (highlightDelimiter.NeedsDrawing(currentLine)) {
931 RedrawSelMargin();
933 return 0;
936 int Editor::MovePositionTo(int newPos, Selection::selTypes selt, bool ensureVisible) {
937 return MovePositionTo(SelectionPosition(newPos), selt, ensureVisible);
940 SelectionPosition Editor::MovePositionSoVisible(SelectionPosition pos, int moveDir) {
941 pos = ClampPositionIntoDocument(pos);
942 pos = MovePositionOutsideChar(pos, moveDir);
943 int lineDoc = pdoc->LineFromPosition(pos.Position());
944 if (cs.GetVisible(lineDoc)) {
945 return pos;
946 } else {
947 int lineDisplay = cs.DisplayFromDoc(lineDoc);
948 if (moveDir > 0) {
949 // lineDisplay is already line before fold as lines in fold use display line of line after fold
950 lineDisplay = Platform::Clamp(lineDisplay, 0, cs.LinesDisplayed());
951 return SelectionPosition(pdoc->LineStart(cs.DocFromDisplay(lineDisplay)));
952 } else {
953 lineDisplay = Platform::Clamp(lineDisplay - 1, 0, cs.LinesDisplayed());
954 return SelectionPosition(pdoc->LineEnd(cs.DocFromDisplay(lineDisplay)));
959 SelectionPosition Editor::MovePositionSoVisible(int pos, int moveDir) {
960 return MovePositionSoVisible(SelectionPosition(pos), moveDir);
963 Point Editor::PointMainCaret() {
964 return LocationFromPosition(sel.Range(sel.Main()).caret);
968 * Choose the x position that the caret will try to stick to
969 * as it moves up and down.
971 void Editor::SetLastXChosen() {
972 Point pt = PointMainCaret();
973 lastXChosen = pt.x + xOffset;
976 void Editor::ScrollTo(int line, bool moveThumb) {
977 int topLineNew = Platform::Clamp(line, 0, MaxScrollPos());
978 if (topLineNew != topLine) {
979 // Try to optimise small scrolls
980 #ifndef UNDER_CE
981 int linesToMove = topLine - topLineNew;
982 #endif
983 SetTopLine(topLineNew);
984 // Optimize by styling the view as this will invalidate any needed area
985 // which could abort the initial paint if discovered later.
986 StyleToPositionInView(PositionAfterArea(GetClientRectangle()));
987 #ifndef UNDER_CE
988 // Perform redraw rather than scroll if many lines would be redrawn anyway.
989 if ((abs(linesToMove) <= 10) && (paintState == notPainting)) {
990 ScrollText(linesToMove);
991 } else {
992 Redraw();
994 #else
995 Redraw();
996 #endif
997 if (moveThumb) {
998 SetVerticalScrollPos();
1003 void Editor::ScrollText(int /* linesToMove */) {
1004 //Platform::DebugPrintf("Editor::ScrollText %d\n", linesToMove);
1005 Redraw();
1008 void Editor::HorizontalScrollTo(int xPos) {
1009 //Platform::DebugPrintf("HorizontalScroll %d\n", xPos);
1010 if (xPos < 0)
1011 xPos = 0;
1012 if ((wrapState == eWrapNone) && (xOffset != xPos)) {
1013 xOffset = xPos;
1014 ContainerNeedsUpdate(SC_UPDATE_H_SCROLL);
1015 SetHorizontalScrollPos();
1016 RedrawRect(GetClientRectangle());
1020 void Editor::VerticalCentreCaret() {
1021 int lineDoc = pdoc->LineFromPosition(sel.IsRectangular() ? sel.Rectangular().caret.Position() : sel.MainCaret());
1022 int lineDisplay = cs.DisplayFromDoc(lineDoc);
1023 int newTop = lineDisplay - (LinesOnScreen() / 2);
1024 if (topLine != newTop) {
1025 SetTopLine(newTop > 0 ? newTop : 0);
1026 RedrawRect(GetClientRectangle());
1030 void Editor::MoveSelectedLines(int lineDelta) {
1032 // if selection doesn't start at the beginning of the line, set the new start
1033 int selectionStart = SelectionStart().Position();
1034 int startLine = pdoc->LineFromPosition(selectionStart);
1035 int beginningOfStartLine = pdoc->LineStart(startLine);
1036 selectionStart = beginningOfStartLine;
1038 // if selection doesn't end at the beginning of a line greater than that of the start,
1039 // then set it at the beginning of the next one
1040 int selectionEnd = SelectionEnd().Position();
1041 int endLine = pdoc->LineFromPosition(selectionEnd);
1042 int beginningOfEndLine = pdoc->LineStart(endLine);
1043 if (selectionEnd > beginningOfEndLine
1044 || selectionStart == selectionEnd) {
1045 selectionEnd = pdoc->LineStart(endLine + 1);
1048 // if there's nowhere for the selection to move
1049 // (i.e. at the beginning going up or at the end going down),
1050 // stop it right there!
1051 if ((selectionStart == 0 && lineDelta < 0)
1052 || (selectionEnd == pdoc->Length() && lineDelta > 0)
1053 || selectionStart == selectionEnd) {
1054 return;
1057 UndoGroup ug(pdoc);
1059 SetSelection(selectionStart, selectionEnd);
1061 SelectionText selectedText;
1062 CopySelectionRange(&selectedText);
1064 int selectionLength = SelectionRange(selectionStart, selectionEnd).Length();
1065 ClearSelection();
1067 Point currentLocation = LocationFromPosition(CurrentPosition());
1068 int currentLine = LineFromLocation(currentLocation);
1069 GoToLine(currentLine + lineDelta);
1071 pdoc->InsertCString(CurrentPosition(), selectedText.s);
1072 SetSelection(CurrentPosition(), CurrentPosition() + selectionLength);
1075 void Editor::MoveSelectedLinesUp() {
1076 MoveSelectedLines(-1);
1079 void Editor::MoveSelectedLinesDown() {
1080 MoveSelectedLines(1);
1083 void Editor::MoveCaretInsideView(bool ensureVisible) {
1084 PRectangle rcClient = GetTextRectangle();
1085 Point pt = PointMainCaret();
1086 if (pt.y < rcClient.top) {
1087 MovePositionTo(SPositionFromLocation(
1088 Point(lastXChosen - xOffset, rcClient.top)),
1089 Selection::noSel, ensureVisible);
1090 } else if ((pt.y + vs.lineHeight - 1) > rcClient.bottom) {
1091 int yOfLastLineFullyDisplayed = rcClient.top + (LinesOnScreen() - 1) * vs.lineHeight;
1092 MovePositionTo(SPositionFromLocation(
1093 Point(lastXChosen - xOffset, rcClient.top + yOfLastLineFullyDisplayed)),
1094 Selection::noSel, ensureVisible);
1098 int Editor::DisplayFromPosition(int pos) {
1099 int lineDoc = pdoc->LineFromPosition(pos);
1100 int lineDisplay = cs.DisplayFromDoc(lineDoc);
1101 AutoSurface surface(this);
1102 AutoLineLayout ll(llc, RetrieveLineLayout(lineDoc));
1103 if (surface && ll) {
1104 LayoutLine(lineDoc, surface, vs, ll, wrapWidth);
1105 unsigned int posLineStart = pdoc->LineStart(lineDoc);
1106 int posInLine = pos - posLineStart;
1107 lineDisplay--; // To make up for first increment ahead.
1108 for (int subLine = 0; subLine < ll->lines; subLine++) {
1109 if (posInLine >= ll->LineStart(subLine)) {
1110 lineDisplay++;
1114 return lineDisplay;
1118 * Ensure the caret is reasonably visible in context.
1120 Caret policy in SciTE
1122 If slop is set, we can define a slop value.
1123 This value defines an unwanted zone (UZ) where the caret is... unwanted.
1124 This zone is defined as a number of pixels near the vertical margins,
1125 and as a number of lines near the horizontal margins.
1126 By keeping the caret away from the edges, it is seen within its context,
1127 so it is likely that the identifier that the caret is on can be completely seen,
1128 and that the current line is seen with some of the lines following it which are
1129 often dependent on that line.
1131 If strict is set, the policy is enforced... strictly.
1132 The caret is centred on the display if slop is not set,
1133 and cannot go in the UZ if slop is set.
1135 If jumps is set, the display is moved more energetically
1136 so the caret can move in the same direction longer before the policy is applied again.
1137 '3UZ' notation is used to indicate three time the size of the UZ as a distance to the margin.
1139 If even is not set, instead of having symmetrical UZs,
1140 the left and bottom UZs are extended up to right and top UZs respectively.
1141 This way, we favour the displaying of useful information: the begining of lines,
1142 where most code reside, and the lines after the caret, eg. the body of a function.
1144 | | | | |
1145 slop | strict | jumps | even | Caret can go to the margin | When reaching limit (caret going out of
1146 | | | | | visibility or going into the UZ) display is...
1147 -----+--------+-------+------+--------------------------------------------+--------------------------------------------------------------
1148 0 | 0 | 0 | 0 | Yes | moved to put caret on top/on right
1149 0 | 0 | 0 | 1 | Yes | moved by one position
1150 0 | 0 | 1 | 0 | Yes | moved to put caret on top/on right
1151 0 | 0 | 1 | 1 | Yes | centred on the caret
1152 0 | 1 | - | 0 | Caret is always on top/on right of display | -
1153 0 | 1 | - | 1 | No, caret is always centred | -
1154 1 | 0 | 0 | 0 | Yes | moved to put caret out of the asymmetrical UZ
1155 1 | 0 | 0 | 1 | Yes | moved to put caret out of the UZ
1156 1 | 0 | 1 | 0 | Yes | moved to put caret at 3UZ of the top or right margin
1157 1 | 0 | 1 | 1 | Yes | moved to put caret at 3UZ of the margin
1158 1 | 1 | - | 0 | Caret is always at UZ of top/right margin | -
1159 1 | 1 | 0 | 1 | No, kept out of UZ | moved by one position
1160 1 | 1 | 1 | 1 | No, kept out of UZ | moved to put caret at 3UZ of the margin
1163 Editor::XYScrollPosition Editor::XYScrollToMakeVisible(const bool useMargin, const bool vert, const bool horiz) {
1164 PRectangle rcClient = GetTextRectangle();
1165 const SelectionPosition posCaret = posDrag.IsValid() ? posDrag : sel.RangeMain().caret;
1166 const Point pt = LocationFromPosition(posCaret);
1167 const Point ptBottomCaret(pt.x, pt.y + vs.lineHeight - 1);
1168 const int lineCaret = DisplayFromPosition(posCaret.Position());
1170 XYScrollPosition newXY(xOffset, topLine);
1172 // Vertical positioning
1173 if (vert && (pt.y < rcClient.top || ptBottomCaret.y >= rcClient.bottom || (caretYPolicy & CARET_STRICT) != 0)) {
1174 const int linesOnScreen = LinesOnScreen();
1175 const int halfScreen = Platform::Maximum(linesOnScreen - 1, 2) / 2;
1176 const bool bSlop = (caretYPolicy & CARET_SLOP) != 0;
1177 const bool bStrict = (caretYPolicy & CARET_STRICT) != 0;
1178 const bool bJump = (caretYPolicy & CARET_JUMPS) != 0;
1179 const bool bEven = (caretYPolicy & CARET_EVEN) != 0;
1181 // It should be possible to scroll the window to show the caret,
1182 // but this fails to remove the caret on GTK+
1183 if (bSlop) { // A margin is defined
1184 int yMoveT, yMoveB;
1185 if (bStrict) {
1186 int yMarginT, yMarginB;
1187 if (!useMargin) {
1188 // In drag mode, avoid moves
1189 // otherwise, a double click will select several lines.
1190 yMarginT = yMarginB = 0;
1191 } else {
1192 // yMarginT must equal to caretYSlop, with a minimum of 1 and
1193 // a maximum of slightly less than half the heigth of the text area.
1194 yMarginT = Platform::Clamp(caretYSlop, 1, halfScreen);
1195 if (bEven) {
1196 yMarginB = yMarginT;
1197 } else {
1198 yMarginB = linesOnScreen - yMarginT - 1;
1201 yMoveT = yMarginT;
1202 if (bEven) {
1203 if (bJump) {
1204 yMoveT = Platform::Clamp(caretYSlop * 3, 1, halfScreen);
1206 yMoveB = yMoveT;
1207 } else {
1208 yMoveB = linesOnScreen - yMoveT - 1;
1210 if (lineCaret < topLine + yMarginT) {
1211 // Caret goes too high
1212 newXY.topLine = lineCaret - yMoveT;
1213 } else if (lineCaret > topLine + linesOnScreen - 1 - yMarginB) {
1214 // Caret goes too low
1215 newXY.topLine = lineCaret - linesOnScreen + 1 + yMoveB;
1217 } else { // Not strict
1218 yMoveT = bJump ? caretYSlop * 3 : caretYSlop;
1219 yMoveT = Platform::Clamp(yMoveT, 1, halfScreen);
1220 if (bEven) {
1221 yMoveB = yMoveT;
1222 } else {
1223 yMoveB = linesOnScreen - yMoveT - 1;
1225 if (lineCaret < topLine) {
1226 // Caret goes too high
1227 newXY.topLine = lineCaret - yMoveT;
1228 } else if (lineCaret > topLine + linesOnScreen - 1) {
1229 // Caret goes too low
1230 newXY.topLine = lineCaret - linesOnScreen + 1 + yMoveB;
1233 } else { // No slop
1234 if (!bStrict && !bJump) {
1235 // Minimal move
1236 if (lineCaret < topLine) {
1237 // Caret goes too high
1238 newXY.topLine = lineCaret;
1239 } else if (lineCaret > topLine + linesOnScreen - 1) {
1240 // Caret goes too low
1241 if (bEven) {
1242 newXY.topLine = lineCaret - linesOnScreen + 1;
1243 } else {
1244 newXY.topLine = lineCaret;
1247 } else { // Strict or going out of display
1248 if (bEven) {
1249 // Always center caret
1250 newXY.topLine = lineCaret - halfScreen;
1251 } else {
1252 // Always put caret on top of display
1253 newXY.topLine = lineCaret;
1257 newXY.topLine = Platform::Clamp(newXY.topLine, 0, MaxScrollPos());
1260 // Horizontal positioning
1261 if (horiz && (wrapState == eWrapNone)) {
1262 const int halfScreen = Platform::Maximum(rcClient.Width() - 4, 4) / 2;
1263 const bool bSlop = (caretXPolicy & CARET_SLOP) != 0;
1264 const bool bStrict = (caretXPolicy & CARET_STRICT) != 0;
1265 const bool bJump = (caretXPolicy & CARET_JUMPS) != 0;
1266 const bool bEven = (caretXPolicy & CARET_EVEN) != 0;
1268 if (bSlop) { // A margin is defined
1269 int xMoveL, xMoveR;
1270 if (bStrict) {
1271 int xMarginL, xMarginR;
1272 if (!useMargin) {
1273 // In drag mode, avoid moves unless very near of the margin
1274 // otherwise, a simple click will select text.
1275 xMarginL = xMarginR = 2;
1276 } else {
1277 // xMargin must equal to caretXSlop, with a minimum of 2 and
1278 // a maximum of slightly less than half the width of the text area.
1279 xMarginR = Platform::Clamp(caretXSlop, 2, halfScreen);
1280 if (bEven) {
1281 xMarginL = xMarginR;
1282 } else {
1283 xMarginL = rcClient.Width() - xMarginR - 4;
1286 if (bJump && bEven) {
1287 // Jump is used only in even mode
1288 xMoveL = xMoveR = Platform::Clamp(caretXSlop * 3, 1, halfScreen);
1289 } else {
1290 xMoveL = xMoveR = 0; // Not used, avoid a warning
1292 if (pt.x < rcClient.left + xMarginL) {
1293 // Caret is on the left of the display
1294 if (bJump && bEven) {
1295 newXY.xOffset -= xMoveL;
1296 } else {
1297 // Move just enough to allow to display the caret
1298 newXY.xOffset -= (rcClient.left + xMarginL) - pt.x;
1300 } else if (pt.x >= rcClient.right - xMarginR) {
1301 // Caret is on the right of the display
1302 if (bJump && bEven) {
1303 newXY.xOffset += xMoveR;
1304 } else {
1305 // Move just enough to allow to display the caret
1306 newXY.xOffset += pt.x - (rcClient.right - xMarginR) + 1;
1309 } else { // Not strict
1310 xMoveR = bJump ? caretXSlop * 3 : caretXSlop;
1311 xMoveR = Platform::Clamp(xMoveR, 1, halfScreen);
1312 if (bEven) {
1313 xMoveL = xMoveR;
1314 } else {
1315 xMoveL = rcClient.Width() - xMoveR - 4;
1317 if (pt.x < rcClient.left) {
1318 // Caret is on the left of the display
1319 newXY.xOffset -= xMoveL;
1320 } else if (pt.x >= rcClient.right) {
1321 // Caret is on the right of the display
1322 newXY.xOffset += xMoveR;
1325 } else { // No slop
1326 if (bStrict ||
1327 (bJump && (pt.x < rcClient.left || pt.x >= rcClient.right))) {
1328 // Strict or going out of display
1329 if (bEven) {
1330 // Center caret
1331 newXY.xOffset += pt.x - rcClient.left - halfScreen;
1332 } else {
1333 // Put caret on right
1334 newXY.xOffset += pt.x - rcClient.right + 1;
1336 } else {
1337 // Move just enough to allow to display the caret
1338 if (pt.x < rcClient.left) {
1339 // Caret is on the left of the display
1340 if (bEven) {
1341 newXY.xOffset -= rcClient.left - pt.x;
1342 } else {
1343 newXY.xOffset += pt.x - rcClient.right + 1;
1345 } else if (pt.x >= rcClient.right) {
1346 // Caret is on the right of the display
1347 newXY.xOffset += pt.x - rcClient.right + 1;
1351 // In case of a jump (find result) largely out of display, adjust the offset to display the caret
1352 if (pt.x + xOffset < rcClient.left + newXY.xOffset) {
1353 newXY.xOffset = pt.x + xOffset - rcClient.left;
1354 } else if (pt.x + xOffset >= rcClient.right + newXY.xOffset) {
1355 newXY.xOffset = pt.x + xOffset - rcClient.right + 1;
1356 if (vs.caretStyle == CARETSTYLE_BLOCK) {
1357 // Ensure we can see a good portion of the block caret
1358 newXY.xOffset += vs.aveCharWidth;
1361 if (newXY.xOffset < 0) {
1362 newXY.xOffset = 0;
1366 return newXY;
1369 void Editor::SetXYScroll(XYScrollPosition newXY) {
1370 if ((newXY.topLine != topLine) || (newXY.xOffset != xOffset)) {
1371 if (newXY.topLine != topLine) {
1372 SetTopLine(newXY.topLine);
1373 SetVerticalScrollPos();
1375 if (newXY.xOffset != xOffset) {
1376 xOffset = newXY.xOffset;
1377 ContainerNeedsUpdate(SC_UPDATE_H_SCROLL);
1378 if (newXY.xOffset > 0) {
1379 PRectangle rcText = GetTextRectangle();
1380 if (horizontalScrollBarVisible &&
1381 rcText.Width() + xOffset > scrollWidth) {
1382 scrollWidth = xOffset + rcText.Width();
1383 SetScrollBars();
1386 SetHorizontalScrollPos();
1388 Redraw();
1389 UpdateSystemCaret();
1393 void Editor::EnsureCaretVisible(bool useMargin, bool vert, bool horiz) {
1394 SetXYScroll(XYScrollToMakeVisible(useMargin, vert, horiz));
1397 void Editor::ShowCaretAtCurrentPosition() {
1398 if (hasFocus) {
1399 caret.active = true;
1400 caret.on = true;
1401 SetTicking(true);
1402 } else {
1403 caret.active = false;
1404 caret.on = false;
1406 InvalidateCaret();
1409 void Editor::DropCaret() {
1410 caret.active = false;
1411 InvalidateCaret();
1414 void Editor::InvalidateCaret() {
1415 if (posDrag.IsValid()) {
1416 InvalidateRange(posDrag.Position(), posDrag.Position() + 1);
1417 } else {
1418 for (size_t r=0; r<sel.Count(); r++) {
1419 InvalidateRange(sel.Range(r).caret.Position(), sel.Range(r).caret.Position() + 1);
1422 UpdateSystemCaret();
1425 void Editor::UpdateSystemCaret() {
1428 void Editor::NeedWrapping(int docLineStart, int docLineEnd) {
1429 docLineStart = Platform::Clamp(docLineStart, 0, pdoc->LinesTotal());
1430 if (wrapStart > docLineStart) {
1431 wrapStart = docLineStart;
1432 llc.Invalidate(LineLayout::llPositions);
1434 if (wrapEnd < docLineEnd) {
1435 wrapEnd = docLineEnd;
1437 wrapEnd = Platform::Clamp(wrapEnd, 0, pdoc->LinesTotal());
1438 // Wrap lines during idle.
1439 if ((wrapState != eWrapNone) && (wrapEnd != wrapStart)) {
1440 SetIdle(true);
1444 bool Editor::WrapOneLine(Surface *surface, int lineToWrap) {
1445 AutoLineLayout ll(llc, RetrieveLineLayout(lineToWrap));
1446 int linesWrapped = 1;
1447 if (ll) {
1448 LayoutLine(lineToWrap, surface, vs, ll, wrapWidth);
1449 linesWrapped = ll->lines;
1451 return cs.SetHeight(lineToWrap, linesWrapped +
1452 (vs.annotationVisible ? pdoc->AnnotationLines(lineToWrap) : 0));
1455 // Check if wrapping needed and perform any needed wrapping.
1456 // fullwrap: if true, all lines which need wrapping will be done,
1457 // in this single call.
1458 // priorityWrapLineStart: If greater than or equal to zero, all lines starting from
1459 // here to 1 page + 100 lines past will be wrapped (even if there are
1460 // more lines under wrapping process in idle).
1461 // If it is neither fullwrap, nor priorityWrap, then 1 page + 100 lines will be
1462 // wrapped, if there are any wrapping going on in idle. (Generally this
1463 // condition is called only from idler).
1464 // Return true if wrapping occurred.
1465 bool Editor::WrapLines(bool fullWrap, int priorityWrapLineStart) {
1466 // If there are any pending wraps, do them during idle if possible.
1467 int linesInOneCall = LinesOnScreen() + 100;
1468 if (wrapState != eWrapNone) {
1469 if (wrapStart < wrapEnd) {
1470 if (!SetIdle(true)) {
1471 // Idle processing not supported so full wrap required.
1472 fullWrap = true;
1475 if (!fullWrap && priorityWrapLineStart >= 0 &&
1476 // .. and if the paint window is outside pending wraps
1477 (((priorityWrapLineStart + linesInOneCall) < wrapStart) ||
1478 (priorityWrapLineStart > wrapEnd))) {
1479 // No priority wrap pending
1480 return false;
1483 int goodTopLine = topLine;
1484 bool wrapOccurred = false;
1485 if (wrapStart <= pdoc->LinesTotal()) {
1486 if (wrapState == eWrapNone) {
1487 if (wrapWidth != LineLayout::wrapWidthInfinite) {
1488 wrapWidth = LineLayout::wrapWidthInfinite;
1489 for (int lineDoc = 0; lineDoc < pdoc->LinesTotal(); lineDoc++) {
1490 cs.SetHeight(lineDoc, 1 +
1491 (vs.annotationVisible ? pdoc->AnnotationLines(lineDoc) : 0));
1493 wrapOccurred = true;
1495 wrapStart = wrapLineLarge;
1496 wrapEnd = wrapLineLarge;
1497 } else {
1498 if (wrapEnd >= pdoc->LinesTotal())
1499 wrapEnd = pdoc->LinesTotal();
1500 //ElapsedTime et;
1501 int lineDocTop = cs.DocFromDisplay(topLine);
1502 int subLineTop = topLine - cs.DisplayFromDoc(lineDocTop);
1503 PRectangle rcTextArea = GetClientRectangle();
1504 rcTextArea.left = vs.fixedColumnWidth;
1505 rcTextArea.right -= vs.rightMarginWidth;
1506 wrapWidth = rcTextArea.Width();
1507 RefreshStyleData();
1508 AutoSurface surface(this);
1509 if (surface) {
1510 bool priorityWrap = false;
1511 int lastLineToWrap = wrapEnd;
1512 int lineToWrap = wrapStart;
1513 if (!fullWrap) {
1514 if (priorityWrapLineStart >= 0) {
1515 // This is a priority wrap.
1516 lineToWrap = priorityWrapLineStart;
1517 lastLineToWrap = priorityWrapLineStart + linesInOneCall;
1518 priorityWrap = true;
1519 } else {
1520 // This is idle wrap.
1521 lastLineToWrap = wrapStart + linesInOneCall;
1523 if (lastLineToWrap >= wrapEnd)
1524 lastLineToWrap = wrapEnd;
1525 } // else do a fullWrap.
1527 // Ensure all lines being wrapped are styled.
1528 pdoc->EnsureStyledTo(pdoc->LineEnd(lastLineToWrap));
1530 // Platform::DebugPrintf("Wraplines: full = %d, priorityStart = %d (wrapping: %d to %d)\n", fullWrap, priorityWrapLineStart, lineToWrap, lastLineToWrap);
1531 // Platform::DebugPrintf("Pending wraps: %d to %d\n", wrapStart, wrapEnd);
1532 while (lineToWrap < lastLineToWrap) {
1533 if (WrapOneLine(surface, lineToWrap)) {
1534 wrapOccurred = true;
1536 lineToWrap++;
1538 if (!priorityWrap)
1539 wrapStart = lineToWrap;
1540 // If wrapping is done, bring it to resting position
1541 if (wrapStart >= wrapEnd) {
1542 wrapStart = wrapLineLarge;
1543 wrapEnd = wrapLineLarge;
1546 goodTopLine = cs.DisplayFromDoc(lineDocTop);
1547 if (subLineTop < cs.GetHeight(lineDocTop))
1548 goodTopLine += subLineTop;
1549 else
1550 goodTopLine += cs.GetHeight(lineDocTop);
1551 //double durWrap = et.Duration(true);
1552 //Platform::DebugPrintf("Wrap:%9.6g \n", durWrap);
1555 if (wrapOccurred) {
1556 SetScrollBars();
1557 SetTopLine(Platform::Clamp(goodTopLine, 0, MaxScrollPos()));
1558 SetVerticalScrollPos();
1560 return wrapOccurred;
1563 void Editor::LinesJoin() {
1564 if (!RangeContainsProtected(targetStart, targetEnd)) {
1565 UndoGroup ug(pdoc);
1566 bool prevNonWS = true;
1567 for (int pos = targetStart; pos < targetEnd; pos++) {
1568 if (IsEOLChar(pdoc->CharAt(pos))) {
1569 targetEnd -= pdoc->LenChar(pos);
1570 pdoc->DelChar(pos);
1571 if (prevNonWS) {
1572 // Ensure at least one space separating previous lines
1573 pdoc->InsertChar(pos, ' ');
1574 targetEnd++;
1576 } else {
1577 prevNonWS = pdoc->CharAt(pos) != ' ';
1583 const char *Editor::StringFromEOLMode(int eolMode) {
1584 if (eolMode == SC_EOL_CRLF) {
1585 return "\r\n";
1586 } else if (eolMode == SC_EOL_CR) {
1587 return "\r";
1588 } else {
1589 return "\n";
1593 void Editor::LinesSplit(int pixelWidth) {
1594 if (!RangeContainsProtected(targetStart, targetEnd)) {
1595 if (pixelWidth == 0) {
1596 PRectangle rcText = GetTextRectangle();
1597 pixelWidth = rcText.Width();
1599 int lineStart = pdoc->LineFromPosition(targetStart);
1600 int lineEnd = pdoc->LineFromPosition(targetEnd);
1601 const char *eol = StringFromEOLMode(pdoc->eolMode);
1602 UndoGroup ug(pdoc);
1603 for (int line = lineStart; line <= lineEnd; line++) {
1604 AutoSurface surface(this);
1605 AutoLineLayout ll(llc, RetrieveLineLayout(line));
1606 if (surface && ll) {
1607 unsigned int posLineStart = pdoc->LineStart(line);
1608 LayoutLine(line, surface, vs, ll, pixelWidth);
1609 for (int subLine = 1; subLine < ll->lines; subLine++) {
1610 pdoc->InsertCString(posLineStart + (subLine - 1) * strlen(eol) +
1611 ll->LineStart(subLine), eol);
1612 targetEnd += static_cast<int>(strlen(eol));
1615 lineEnd = pdoc->LineFromPosition(targetEnd);
1620 int Editor::SubstituteMarkerIfEmpty(int markerCheck, int markerDefault) {
1621 if (vs.markers[markerCheck].markType == SC_MARK_EMPTY)
1622 return markerDefault;
1623 return markerCheck;
1626 // Avoid 64 bit compiler warnings.
1627 // Scintilla does not support text buffers larger than 2**31
1628 static int istrlen(const char *s) {
1629 return static_cast<int>(strlen(s));
1632 bool ValidStyledText(ViewStyle &vs, size_t styleOffset, const StyledText &st) {
1633 if (st.multipleStyles) {
1634 for (size_t iStyle=0; iStyle<st.length; iStyle++) {
1635 if (!vs.ValidStyle(styleOffset + st.styles[iStyle]))
1636 return false;
1638 } else {
1639 if (!vs.ValidStyle(styleOffset + st.style))
1640 return false;
1642 return true;
1645 static int WidthStyledText(Surface *surface, ViewStyle &vs, int styleOffset,
1646 const char *text, const unsigned char *styles, size_t len) {
1647 int width = 0;
1648 size_t start = 0;
1649 while (start < len) {
1650 size_t style = styles[start];
1651 size_t endSegment = start;
1652 while ((endSegment+1 < len) && (static_cast<size_t>(styles[endSegment+1]) == style))
1653 endSegment++;
1654 width += surface->WidthText(vs.styles[style+styleOffset].font, text + start, endSegment - start + 1);
1655 start = endSegment + 1;
1657 return width;
1660 static int WidestLineWidth(Surface *surface, ViewStyle &vs, int styleOffset, const StyledText &st) {
1661 int widthMax = 0;
1662 size_t start = 0;
1663 while (start < st.length) {
1664 size_t lenLine = st.LineLength(start);
1665 int widthSubLine;
1666 if (st.multipleStyles) {
1667 widthSubLine = WidthStyledText(surface, vs, styleOffset, st.text + start, st.styles + start, lenLine);
1668 } else {
1669 widthSubLine = surface->WidthText(vs.styles[styleOffset + st.style].font, st.text + start, lenLine);
1671 if (widthSubLine > widthMax)
1672 widthMax = widthSubLine;
1673 start += lenLine + 1;
1675 return widthMax;
1678 void DrawStyledText(Surface *surface, ViewStyle &vs, int styleOffset, PRectangle rcText, int ascent,
1679 const StyledText &st, size_t start, size_t length) {
1681 if (st.multipleStyles) {
1682 int x = rcText.left;
1683 size_t i = 0;
1684 while (i < length) {
1685 size_t end = i;
1686 int style = st.styles[i + start];
1687 while (end < length-1 && st.styles[start+end+1] == style)
1688 end++;
1689 style += styleOffset;
1690 int width = surface->WidthText(vs.styles[style].font, st.text + start + i, end - i + 1);
1691 PRectangle rcSegment = rcText;
1692 rcSegment.left = x;
1693 rcSegment.right = x + width + 1;
1694 surface->DrawTextNoClip(rcSegment, vs.styles[style].font,
1695 ascent, st.text + start + i, end - i + 1,
1696 vs.styles[style].fore.allocated,
1697 vs.styles[style].back.allocated);
1698 x += width;
1699 i = end + 1;
1701 } else {
1702 int style = st.style + styleOffset;
1703 surface->DrawTextNoClip(rcText, vs.styles[style].font,
1704 rcText.top + vs.maxAscent, st.text + start, length,
1705 vs.styles[style].fore.allocated,
1706 vs.styles[style].back.allocated);
1710 void Editor::PaintSelMargin(Surface *surfWindow, PRectangle &rc) {
1711 if (vs.fixedColumnWidth == 0)
1712 return;
1714 PRectangle rcMargin = GetClientRectangle();
1715 rcMargin.right = vs.fixedColumnWidth;
1717 if (!rc.Intersects(rcMargin))
1718 return;
1720 Surface *surface;
1721 if (bufferedDraw) {
1722 surface = pixmapSelMargin;
1723 } else {
1724 surface = surfWindow;
1727 PRectangle rcSelMargin = rcMargin;
1728 rcSelMargin.right = rcMargin.left;
1730 for (int margin = 0; margin < vs.margins; margin++) {
1731 if (vs.ms[margin].width > 0) {
1733 rcSelMargin.left = rcSelMargin.right;
1734 rcSelMargin.right = rcSelMargin.left + vs.ms[margin].width;
1736 if (vs.ms[margin].style != SC_MARGIN_NUMBER) {
1737 /* alternate scheme:
1738 if (vs.ms[margin].mask & SC_MASK_FOLDERS)
1739 surface->FillRectangle(rcSelMargin, vs.styles[STYLE_DEFAULT].back.allocated);
1740 else
1741 // Required because of special way brush is created for selection margin
1742 surface->FillRectangle(rcSelMargin, pixmapSelPattern);
1744 if (vs.ms[margin].mask & SC_MASK_FOLDERS)
1745 // Required because of special way brush is created for selection margin
1746 surface->FillRectangle(rcSelMargin, *pixmapSelPattern);
1747 else {
1748 ColourAllocated colour;
1749 switch (vs.ms[margin].style) {
1750 case SC_MARGIN_BACK:
1751 colour = vs.styles[STYLE_DEFAULT].back.allocated;
1752 break;
1753 case SC_MARGIN_FORE:
1754 colour = vs.styles[STYLE_DEFAULT].fore.allocated;
1755 break;
1756 default:
1757 colour = vs.styles[STYLE_LINENUMBER].back.allocated;
1758 break;
1760 surface->FillRectangle(rcSelMargin, colour);
1762 } else {
1763 surface->FillRectangle(rcSelMargin, vs.styles[STYLE_LINENUMBER].back.allocated);
1766 int visibleLine = topLine;
1767 int yposScreen = 0;
1768 // Work out whether the top line is whitespace located after a
1769 // lessening of fold level which implies a 'fold tail' but which should not
1770 // be displayed until the last of a sequence of whitespace.
1771 bool needWhiteClosure = false;
1772 int level = pdoc->GetLevel(cs.DocFromDisplay(topLine));
1773 if (level & SC_FOLDLEVELWHITEFLAG) {
1774 int lineBack = cs.DocFromDisplay(topLine);
1775 int levelPrev = level;
1776 while ((lineBack > 0) && (levelPrev & SC_FOLDLEVELWHITEFLAG)) {
1777 lineBack--;
1778 levelPrev = pdoc->GetLevel(lineBack);
1780 if (!(levelPrev & SC_FOLDLEVELHEADERFLAG)) {
1781 if ((level & SC_FOLDLEVELNUMBERMASK) < (levelPrev & SC_FOLDLEVELNUMBERMASK))
1782 needWhiteClosure = true;
1785 if (highlightDelimiter.isEnabled && (vs.ms[margin].mask & SC_MASK_FOLDERS)) {
1786 int lineBack = cs.DocFromDisplay(topLine);
1787 int lineFront = cs.DocFromDisplay(((rcMargin.bottom - rcMargin.top) / vs.lineHeight) + topLine) + 1;
1788 pdoc->GetHighlightDelimiters(highlightDelimiter, pdoc->LineFromPosition(CurrentPosition()), lineBack, lineFront);
1791 // Old code does not know about new markers needed to distinguish all cases
1792 int folderOpenMid = SubstituteMarkerIfEmpty(SC_MARKNUM_FOLDEROPENMID,
1793 SC_MARKNUM_FOLDEROPEN);
1794 int folderEnd = SubstituteMarkerIfEmpty(SC_MARKNUM_FOLDEREND,
1795 SC_MARKNUM_FOLDER);
1797 while ((visibleLine < cs.LinesDisplayed()) && yposScreen < rcMargin.bottom) {
1799 PLATFORM_ASSERT(visibleLine < cs.LinesDisplayed());
1800 int lineDoc = cs.DocFromDisplay(visibleLine);
1801 PLATFORM_ASSERT(cs.GetVisible(lineDoc));
1802 bool firstSubLine = visibleLine == cs.DisplayFromDoc(lineDoc);
1803 bool lastSubLine = visibleLine == (cs.DisplayFromDoc(lineDoc + 1) - 1);
1805 // Decide which fold indicator should be displayed
1806 level = pdoc->GetLevel(lineDoc);
1807 int levelNext = pdoc->GetLevel(lineDoc + 1);
1808 int marks = pdoc->GetMark(lineDoc);
1809 if (!firstSubLine)
1810 marks = 0;
1811 int levelNum = level & SC_FOLDLEVELNUMBERMASK;
1812 int levelNextNum = levelNext & SC_FOLDLEVELNUMBERMASK;
1813 if (level & SC_FOLDLEVELHEADERFLAG) {
1814 if (firstSubLine) {
1815 if (levelNum < levelNextNum) {
1816 if (cs.GetExpanded(lineDoc)) {
1817 if (levelNum == SC_FOLDLEVELBASE)
1818 marks |= 1 << SC_MARKNUM_FOLDEROPEN;
1819 else
1820 marks |= 1 << folderOpenMid;
1821 } else {
1822 if (levelNum == SC_FOLDLEVELBASE)
1823 marks |= 1 << SC_MARKNUM_FOLDER;
1824 else
1825 marks |= 1 << folderEnd;
1827 } else if (levelNum > SC_FOLDLEVELBASE) {
1828 marks |= 1 << SC_MARKNUM_FOLDERSUB;
1830 } else {
1831 if (levelNum < levelNextNum) {
1832 if (cs.GetExpanded(lineDoc)) {
1833 marks |= 1 << SC_MARKNUM_FOLDERSUB;
1834 } else if (levelNum > SC_FOLDLEVELBASE) {
1835 marks |= 1 << SC_MARKNUM_FOLDERSUB;
1837 } else if (levelNum > SC_FOLDLEVELBASE) {
1838 marks |= 1 << SC_MARKNUM_FOLDERSUB;
1841 needWhiteClosure = false;
1842 } else if (level & SC_FOLDLEVELWHITEFLAG) {
1843 if (needWhiteClosure) {
1844 if (levelNext & SC_FOLDLEVELWHITEFLAG) {
1845 marks |= 1 << SC_MARKNUM_FOLDERSUB;
1846 } else if (levelNum > SC_FOLDLEVELBASE) {
1847 marks |= 1 << SC_MARKNUM_FOLDERMIDTAIL;
1848 needWhiteClosure = false;
1849 } else {
1850 marks |= 1 << SC_MARKNUM_FOLDERTAIL;
1851 needWhiteClosure = false;
1853 } else if (levelNum > SC_FOLDLEVELBASE) {
1854 if (levelNextNum < levelNum) {
1855 if (levelNextNum > SC_FOLDLEVELBASE) {
1856 marks |= 1 << SC_MARKNUM_FOLDERMIDTAIL;
1857 } else {
1858 marks |= 1 << SC_MARKNUM_FOLDERTAIL;
1860 } else {
1861 marks |= 1 << SC_MARKNUM_FOLDERSUB;
1864 } else if (levelNum > SC_FOLDLEVELBASE) {
1865 if (levelNextNum < levelNum) {
1866 needWhiteClosure = false;
1867 if (levelNext & SC_FOLDLEVELWHITEFLAG) {
1868 marks |= 1 << SC_MARKNUM_FOLDERSUB;
1869 needWhiteClosure = true;
1870 } else if (lastSubLine) {
1871 if (levelNextNum > SC_FOLDLEVELBASE) {
1872 marks |= 1 << SC_MARKNUM_FOLDERMIDTAIL;
1873 } else {
1874 marks |= 1 << SC_MARKNUM_FOLDERTAIL;
1876 } else {
1877 marks |= 1 << SC_MARKNUM_FOLDERSUB;
1879 } else {
1880 marks |= 1 << SC_MARKNUM_FOLDERSUB;
1883 marks &= vs.ms[margin].mask;
1884 PRectangle rcMarker = rcSelMargin;
1885 rcMarker.top = yposScreen;
1886 rcMarker.bottom = yposScreen + vs.lineHeight;
1887 if (vs.ms[margin].style == SC_MARGIN_NUMBER) {
1888 char number[100];
1889 number[0] = '\0';
1890 if (firstSubLine)
1891 sprintf(number, "%d", lineDoc + 1);
1892 if (foldFlags & SC_FOLDFLAG_LEVELNUMBERS) {
1893 int lev = pdoc->GetLevel(lineDoc);
1894 sprintf(number, "%c%c %03X %03X",
1895 (lev & SC_FOLDLEVELHEADERFLAG) ? 'H' : '_',
1896 (lev & SC_FOLDLEVELWHITEFLAG) ? 'W' : '_',
1897 lev & SC_FOLDLEVELNUMBERMASK,
1898 lev >> 16
1901 PRectangle rcNumber = rcMarker;
1902 // Right justify
1903 int width = surface->WidthText(vs.styles[STYLE_LINENUMBER].font, number, istrlen(number));
1904 int xpos = rcNumber.right - width - 3;
1905 rcNumber.left = xpos;
1906 surface->DrawTextNoClip(rcNumber, vs.styles[STYLE_LINENUMBER].font,
1907 rcNumber.top + vs.maxAscent, number, istrlen(number),
1908 vs.styles[STYLE_LINENUMBER].fore.allocated,
1909 vs.styles[STYLE_LINENUMBER].back.allocated);
1910 } else if (vs.ms[margin].style == SC_MARGIN_TEXT || vs.ms[margin].style == SC_MARGIN_RTEXT) {
1911 if (firstSubLine) {
1912 const StyledText stMargin = pdoc->MarginStyledText(lineDoc);
1913 if (stMargin.text && ValidStyledText(vs, vs.marginStyleOffset, stMargin)) {
1914 surface->FillRectangle(rcMarker,
1915 vs.styles[stMargin.StyleAt(0)+vs.marginStyleOffset].back.allocated);
1916 if (vs.ms[margin].style == SC_MARGIN_RTEXT) {
1917 int width = WidestLineWidth(surface, vs, vs.marginStyleOffset, stMargin);
1918 rcMarker.left = rcMarker.right - width - 3;
1920 DrawStyledText(surface, vs, vs.marginStyleOffset, rcMarker, rcMarker.top + vs.maxAscent,
1921 stMargin, 0, stMargin.length);
1926 if (marks) {
1927 for (int markBit = 0; (markBit < 32) && marks; markBit++) {
1928 if (marks & 1) {
1929 LineMarker::typeOfFold tFold;
1930 if (!highlightDelimiter.isCurrentBlockHighlight(lineDoc)) {
1931 tFold = LineMarker::undefined;
1932 } else if (highlightDelimiter.isBodyBlockFold(lineDoc)) {
1933 tFold = LineMarker::body;
1934 } else if (highlightDelimiter.isHeadBlockFold(lineDoc)) {
1935 tFold = LineMarker::head;
1936 } else if (highlightDelimiter.isTailBlockFold(lineDoc)) {
1937 tFold = LineMarker::tail;
1938 } else {
1939 //Normally, this branch is never used. But I prefer to manage it anyway.
1940 tFold = LineMarker::undefined;
1942 vs.markers[markBit].Draw(surface, rcMarker, vs.styles[STYLE_LINENUMBER].font, tFold);
1944 marks >>= 1;
1948 visibleLine++;
1949 yposScreen += vs.lineHeight;
1954 PRectangle rcBlankMargin = rcMargin;
1955 rcBlankMargin.left = rcSelMargin.right;
1956 surface->FillRectangle(rcBlankMargin, vs.styles[STYLE_DEFAULT].back.allocated);
1958 if (bufferedDraw) {
1959 surfWindow->Copy(rcMargin, Point(), *pixmapSelMargin);
1963 void DrawTabArrow(Surface *surface, PRectangle rcTab, int ymid) {
1964 int ydiff = (rcTab.bottom - rcTab.top) / 2;
1965 int xhead = rcTab.right - 1 - ydiff;
1966 if (xhead <= rcTab.left) {
1967 ydiff -= rcTab.left - xhead - 1;
1968 xhead = rcTab.left - 1;
1970 if ((rcTab.left + 2) < (rcTab.right - 1))
1971 surface->MoveTo(rcTab.left + 2, ymid);
1972 else
1973 surface->MoveTo(rcTab.right - 1, ymid);
1974 surface->LineTo(rcTab.right - 1, ymid);
1975 surface->LineTo(xhead, ymid - ydiff);
1976 surface->MoveTo(rcTab.right - 1, ymid);
1977 surface->LineTo(xhead, ymid + ydiff);
1980 LineLayout *Editor::RetrieveLineLayout(int lineNumber) {
1981 int posLineStart = pdoc->LineStart(lineNumber);
1982 int posLineEnd = pdoc->LineStart(lineNumber + 1);
1983 PLATFORM_ASSERT(posLineEnd >= posLineStart);
1984 int lineCaret = pdoc->LineFromPosition(sel.MainCaret());
1985 return llc.Retrieve(lineNumber, lineCaret,
1986 posLineEnd - posLineStart, pdoc->GetStyleClock(),
1987 LinesOnScreen() + 1, pdoc->LinesTotal());
1990 static bool GoodTrailByte(int v) {
1991 return (v >= 0x80) && (v < 0xc0);
1994 bool BadUTF(const char *s, int len, int &trailBytes) {
1995 // For the rules: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
1996 if (trailBytes) {
1997 trailBytes--;
1998 return false;
2000 const unsigned char *us = reinterpret_cast<const unsigned char *>(s);
2001 if (*us < 0x80) {
2002 // Single bytes easy
2003 return false;
2004 } else if (*us > 0xF4) {
2005 // Characters longer than 4 bytes not possible in current UTF-8
2006 return true;
2007 } else if (*us >= 0xF0) {
2008 // 4 bytes
2009 if (len < 4)
2010 return true;
2011 if (GoodTrailByte(us[1]) && GoodTrailByte(us[2]) && GoodTrailByte(us[3])) {
2012 if (*us == 0xf4) {
2013 // Check if encoding a value beyond the last Unicode character 10FFFF
2014 if (us[1] > 0x8f) {
2015 return true;
2016 } else if (us[1] == 0x8f) {
2017 if (us[2] > 0xbf) {
2018 return true;
2019 } else if (us[2] == 0xbf) {
2020 if (us[3] > 0xbf) {
2021 return true;
2025 } else if ((*us == 0xf0) && ((us[1] & 0xf0) == 0x80)) {
2026 // Overlong
2027 return true;
2029 trailBytes = 3;
2030 return false;
2031 } else {
2032 return true;
2034 } else if (*us >= 0xE0) {
2035 // 3 bytes
2036 if (len < 3)
2037 return true;
2038 if (GoodTrailByte(us[1]) && GoodTrailByte(us[2])) {
2039 if ((*us == 0xe0) && ((us[1] & 0xe0) == 0x80)) {
2040 // Overlong
2041 return true;
2043 if ((*us == 0xed) && ((us[1] & 0xe0) == 0xa0)) {
2044 // Surrogate
2045 return true;
2047 if ((*us == 0xef) && (us[1] == 0xbf) && (us[2] == 0xbe)) {
2048 // U+FFFE
2049 return true;
2051 if ((*us == 0xef) && (us[1] == 0xbf) && (us[2] == 0xbf)) {
2052 // U+FFFF
2053 return true;
2055 trailBytes = 2;
2056 return false;
2057 } else {
2058 return true;
2060 } else if (*us >= 0xC2) {
2061 // 2 bytes
2062 if (len < 2)
2063 return true;
2064 if (GoodTrailByte(us[1])) {
2065 trailBytes = 1;
2066 return false;
2067 } else {
2068 return true;
2070 } else if (*us >= 0xC0) {
2071 // Overlong encoding
2072 return true;
2073 } else {
2074 // Trail byte
2075 return true;
2080 * Fill in the LineLayout data for the given line.
2081 * Copy the given @a line and its styles from the document into local arrays.
2082 * Also determine the x position at which each character starts.
2084 void Editor::LayoutLine(int line, Surface *surface, ViewStyle &vstyle, LineLayout *ll, int width) {
2085 if (!ll)
2086 return;
2088 PLATFORM_ASSERT(line < pdoc->LinesTotal());
2089 PLATFORM_ASSERT(ll->chars != NULL);
2090 int posLineStart = pdoc->LineStart(line);
2091 int posLineEnd = pdoc->LineStart(line + 1);
2092 // If the line is very long, limit the treatment to a length that should fit in the viewport
2093 if (posLineEnd > (posLineStart + ll->maxLineLength)) {
2094 posLineEnd = posLineStart + ll->maxLineLength;
2096 if (ll->validity == LineLayout::llCheckTextAndStyle) {
2097 int lineLength = posLineEnd - posLineStart;
2098 if (!vstyle.viewEOL) {
2099 int cid = posLineEnd - 1;
2100 while ((cid > posLineStart) && IsEOLChar(pdoc->CharAt(cid))) {
2101 cid--;
2102 lineLength--;
2105 if (lineLength == ll->numCharsInLine) {
2106 // See if chars, styles, indicators, are all the same
2107 bool allSame = true;
2108 const int styleMask = pdoc->stylingBitsMask;
2109 // Check base line layout
2110 char styleByte = 0;
2111 int numCharsInLine = 0;
2112 while (numCharsInLine < lineLength) {
2113 int charInDoc = numCharsInLine + posLineStart;
2114 char chDoc = pdoc->CharAt(charInDoc);
2115 styleByte = pdoc->StyleAt(charInDoc);
2116 allSame = allSame &&
2117 (ll->styles[numCharsInLine] == static_cast<unsigned char>(styleByte & styleMask));
2118 allSame = allSame &&
2119 (ll->indicators[numCharsInLine] == static_cast<char>(styleByte & ~styleMask));
2120 if (vstyle.styles[ll->styles[numCharsInLine]].caseForce == Style::caseMixed)
2121 allSame = allSame &&
2122 (ll->chars[numCharsInLine] == chDoc);
2123 else if (vstyle.styles[ll->styles[numCharsInLine]].caseForce == Style::caseLower)
2124 allSame = allSame &&
2125 (ll->chars[numCharsInLine] == static_cast<char>(tolower(chDoc)));
2126 else // Style::caseUpper
2127 allSame = allSame &&
2128 (ll->chars[numCharsInLine] == static_cast<char>(toupper(chDoc)));
2129 numCharsInLine++;
2131 allSame = allSame && (ll->styles[numCharsInLine] == styleByte); // For eolFilled
2132 if (allSame) {
2133 ll->validity = LineLayout::llPositions;
2134 } else {
2135 ll->validity = LineLayout::llInvalid;
2137 } else {
2138 ll->validity = LineLayout::llInvalid;
2141 if (ll->validity == LineLayout::llInvalid) {
2142 ll->widthLine = LineLayout::wrapWidthInfinite;
2143 ll->lines = 1;
2144 if (vstyle.edgeState == EDGE_BACKGROUND) {
2145 ll->edgeColumn = pdoc->FindColumn(line, theEdge);
2146 if (ll->edgeColumn >= posLineStart) {
2147 ll->edgeColumn -= posLineStart;
2149 } else {
2150 ll->edgeColumn = -1;
2153 char styleByte;
2154 const int styleMask = pdoc->stylingBitsMask;
2155 ll->styleBitsSet = 0;
2156 // Fill base line layout
2157 const int lineLength = posLineEnd - posLineStart;
2158 pdoc->GetCharRange(ll->chars, posLineStart, lineLength);
2159 pdoc->GetStyleRange(ll->styles, posLineStart, lineLength);
2160 int numCharsBeforeEOL = lineLength;
2161 while ((numCharsBeforeEOL > 0) && IsEOLChar(ll->chars[numCharsBeforeEOL-1])) {
2162 numCharsBeforeEOL--;
2164 const int numCharsInLine = (vstyle.viewEOL) ? lineLength : numCharsBeforeEOL;
2165 for (int styleInLine = 0; styleInLine < numCharsInLine; styleInLine++) {
2166 styleByte = ll->styles[styleInLine];
2167 ll->styleBitsSet |= styleByte;
2168 ll->styles[styleInLine] = static_cast<char>(styleByte & styleMask);
2169 ll->indicators[styleInLine] = static_cast<char>(styleByte & ~styleMask);
2171 styleByte = static_cast<char>(((lineLength > 0) ? ll->styles[lineLength-1] : 0) & styleMask);
2172 if (vstyle.someStylesForceCase) {
2173 for (int charInLine = 0; charInLine<lineLength; charInLine++) {
2174 char chDoc = ll->chars[charInLine];
2175 if (vstyle.styles[ll->styles[charInLine]].caseForce == Style::caseUpper)
2176 ll->chars[charInLine] = static_cast<char>(toupper(chDoc));
2177 else if (vstyle.styles[ll->styles[charInLine]].caseForce == Style::caseLower)
2178 ll->chars[charInLine] = static_cast<char>(tolower(chDoc));
2181 ll->xHighlightGuide = 0;
2182 // Extra element at the end of the line to hold end x position and act as
2183 ll->chars[numCharsInLine] = 0; // Also triggers processing in the loops as this is a control character
2184 ll->styles[numCharsInLine] = styleByte; // For eolFilled
2185 ll->indicators[numCharsInLine] = 0;
2187 // Layout the line, determining the position of each character,
2188 // with an extra element at the end for the end of the line.
2189 int startseg = 0; // Start of the current segment, in char. number
2190 int startsegx = 0; // Start of the current segment, in pixels
2191 ll->positions[0] = 0;
2192 unsigned int tabWidth = vstyle.spaceWidth * pdoc->tabInChars;
2193 bool lastSegItalics = false;
2194 Font &ctrlCharsFont = vstyle.styles[STYLE_CONTROLCHAR].font;
2196 int ctrlCharWidth[32] = {0};
2197 bool isControlNext = IsControlCharacter(ll->chars[0]);
2198 int trailBytes = 0;
2199 bool isBadUTFNext = IsUnicodeMode() && BadUTF(ll->chars, numCharsInLine, trailBytes);
2200 for (int charInLine = 0; charInLine < numCharsInLine; charInLine++) {
2201 bool isControl = isControlNext;
2202 isControlNext = IsControlCharacter(ll->chars[charInLine + 1]);
2203 bool isBadUTF = isBadUTFNext;
2204 isBadUTFNext = IsUnicodeMode() && BadUTF(ll->chars + charInLine + 1, numCharsInLine - charInLine - 1, trailBytes);
2205 if ((ll->styles[charInLine] != ll->styles[charInLine + 1]) ||
2206 isControl || isControlNext || isBadUTF || isBadUTFNext) {
2207 ll->positions[startseg] = 0;
2208 if (vstyle.styles[ll->styles[charInLine]].visible) {
2209 if (isControl) {
2210 if (ll->chars[charInLine] == '\t') {
2211 ll->positions[charInLine + 1] = ((((startsegx + 2) /
2212 tabWidth) + 1) * tabWidth) - startsegx;
2213 } else if (controlCharSymbol < 32) {
2214 if (ctrlCharWidth[ll->chars[charInLine]] == 0) {
2215 const char *ctrlChar = ControlCharacterString(ll->chars[charInLine]);
2216 // +3 For a blank on front and rounded edge each side:
2217 ctrlCharWidth[ll->chars[charInLine]] =
2218 surface->WidthText(ctrlCharsFont, ctrlChar, istrlen(ctrlChar)) + 3;
2220 ll->positions[charInLine + 1] = ctrlCharWidth[ll->chars[charInLine]];
2221 } else {
2222 char cc[2] = { static_cast<char>(controlCharSymbol), '\0' };
2223 surface->MeasureWidths(ctrlCharsFont, cc, 1,
2224 ll->positions + startseg + 1);
2226 lastSegItalics = false;
2227 } else if (isBadUTF) {
2228 char hexits[4];
2229 sprintf(hexits, "x%2X", ll->chars[charInLine] & 0xff);
2230 ll->positions[charInLine + 1] =
2231 surface->WidthText(ctrlCharsFont, hexits, istrlen(hexits)) + 3;
2232 } else { // Regular character
2233 int lenSeg = charInLine - startseg + 1;
2234 if ((lenSeg == 1) && (' ' == ll->chars[startseg])) {
2235 lastSegItalics = false;
2236 // Over half the segments are single characters and of these about half are space characters.
2237 ll->positions[charInLine + 1] = vstyle.styles[ll->styles[charInLine]].spaceWidth;
2238 } else {
2239 lastSegItalics = vstyle.styles[ll->styles[charInLine]].italic;
2240 posCache.MeasureWidths(surface, vstyle, ll->styles[charInLine], ll->chars + startseg,
2241 lenSeg, ll->positions + startseg + 1, pdoc);
2244 } else { // invisible
2245 for (int posToZero = startseg; posToZero <= (charInLine + 1); posToZero++) {
2246 ll->positions[posToZero] = 0;
2249 for (int posToIncrease = startseg; posToIncrease <= (charInLine + 1); posToIncrease++) {
2250 ll->positions[posToIncrease] += startsegx;
2252 startsegx = ll->positions[charInLine + 1];
2253 startseg = charInLine + 1;
2256 // Small hack to make lines that end with italics not cut off the edge of the last character
2257 if ((startseg > 0) && lastSegItalics) {
2258 ll->positions[startseg] += 2;
2260 ll->numCharsInLine = numCharsInLine;
2261 ll->numCharsBeforeEOL = numCharsBeforeEOL;
2262 ll->validity = LineLayout::llPositions;
2264 // Hard to cope when too narrow, so just assume there is space
2265 if (width < 20) {
2266 width = 20;
2268 if ((ll->validity == LineLayout::llPositions) || (ll->widthLine != width)) {
2269 ll->widthLine = width;
2270 if (width == LineLayout::wrapWidthInfinite) {
2271 ll->lines = 1;
2272 } else if (width > ll->positions[ll->numCharsInLine]) {
2273 // Simple common case where line does not need wrapping.
2274 ll->lines = 1;
2275 } else {
2276 if (wrapVisualFlags & SC_WRAPVISUALFLAG_END) {
2277 width -= vstyle.aveCharWidth; // take into account the space for end wrap mark
2279 ll->wrapIndent = wrapAddIndent;
2280 if (wrapIndentMode != SC_WRAPINDENT_FIXED)
2281 for (int i = 0; i < ll->numCharsInLine; i++) {
2282 if (!IsSpaceOrTab(ll->chars[i])) {
2283 ll->wrapIndent += ll->positions[i]; // Add line indent
2284 break;
2287 // Check for text width minimum
2288 if (ll->wrapIndent > width - static_cast<int>(vstyle.aveCharWidth) * 15)
2289 ll->wrapIndent = wrapAddIndent;
2290 // Check for wrapIndent minimum
2291 if ((wrapVisualFlags & SC_WRAPVISUALFLAG_START) && (ll->wrapIndent < static_cast<int>(vstyle.aveCharWidth)))
2292 ll->wrapIndent = vstyle.aveCharWidth; // Indent to show start visual
2293 ll->lines = 0;
2294 // Calculate line start positions based upon width.
2295 int lastGoodBreak = 0;
2296 int lastLineStart = 0;
2297 int startOffset = 0;
2298 int p = 0;
2299 while (p < ll->numCharsInLine) {
2300 if ((ll->positions[p + 1] - startOffset) >= width) {
2301 if (lastGoodBreak == lastLineStart) {
2302 // Try moving to start of last character
2303 if (p > 0) {
2304 lastGoodBreak = pdoc->MovePositionOutsideChar(p + posLineStart, -1)
2305 - posLineStart;
2307 if (lastGoodBreak == lastLineStart) {
2308 // Ensure at least one character on line.
2309 lastGoodBreak = pdoc->MovePositionOutsideChar(lastGoodBreak + posLineStart + 1, 1)
2310 - posLineStart;
2313 lastLineStart = lastGoodBreak;
2314 ll->lines++;
2315 ll->SetLineStart(ll->lines, lastGoodBreak);
2316 startOffset = ll->positions[lastGoodBreak];
2317 // take into account the space for start wrap mark and indent
2318 startOffset -= ll->wrapIndent;
2319 p = lastGoodBreak + 1;
2320 continue;
2322 if (p > 0) {
2323 if (wrapState == eWrapChar) {
2324 lastGoodBreak = pdoc->MovePositionOutsideChar(p + posLineStart, -1)
2325 - posLineStart;
2326 p = pdoc->MovePositionOutsideChar(p + 1 + posLineStart, 1) - posLineStart;
2327 continue;
2328 } else if (ll->styles[p] != ll->styles[p - 1]) {
2329 lastGoodBreak = p;
2330 } else if (IsSpaceOrTab(ll->chars[p - 1]) && !IsSpaceOrTab(ll->chars[p])) {
2331 lastGoodBreak = p;
2334 p++;
2336 ll->lines++;
2338 ll->validity = LineLayout::llLines;
2342 ColourAllocated Editor::SelectionBackground(ViewStyle &vsDraw, bool main) {
2343 return main ?
2344 (primarySelection ? vsDraw.selbackground.allocated : vsDraw.selbackground2.allocated) :
2345 vsDraw.selAdditionalBackground.allocated;
2348 ColourAllocated Editor::TextBackground(ViewStyle &vsDraw, bool overrideBackground,
2349 ColourAllocated background, int inSelection, bool inHotspot, int styleMain, int i, LineLayout *ll) {
2350 if (inSelection == 1) {
2351 if (vsDraw.selbackset && (vsDraw.selAlpha == SC_ALPHA_NOALPHA)) {
2352 return SelectionBackground(vsDraw, true);
2354 } else if (inSelection == 2) {
2355 if (vsDraw.selbackset && (vsDraw.selAdditionalAlpha == SC_ALPHA_NOALPHA)) {
2356 return SelectionBackground(vsDraw, false);
2358 } else {
2359 if ((vsDraw.edgeState == EDGE_BACKGROUND) &&
2360 (i >= ll->edgeColumn) &&
2361 !IsEOLChar(ll->chars[i]))
2362 return vsDraw.edgecolour.allocated;
2363 if (inHotspot && vsDraw.hotspotBackgroundSet)
2364 return vsDraw.hotspotBackground.allocated;
2365 if (overrideBackground && (styleMain != STYLE_BRACELIGHT) && (styleMain != STYLE_BRACEBAD))
2366 return background;
2368 return vsDraw.styles[styleMain].back.allocated;
2371 void Editor::DrawIndentGuide(Surface *surface, int lineVisible, int lineHeight, int start, PRectangle rcSegment, bool highlight) {
2372 Point from(0, ((lineVisible & 1) && (lineHeight & 1)) ? 1 : 0);
2373 PRectangle rcCopyArea(start + 1, rcSegment.top, start + 2, rcSegment.bottom);
2374 surface->Copy(rcCopyArea, from,
2375 highlight ? *pixmapIndentGuideHighlight : *pixmapIndentGuide);
2378 void Editor::DrawWrapMarker(Surface *surface, PRectangle rcPlace,
2379 bool isEndMarker, ColourAllocated wrapColour) {
2380 surface->PenColour(wrapColour);
2382 enum { xa = 1 }; // gap before start
2383 int w = rcPlace.right - rcPlace.left - xa - 1;
2385 bool xStraight = isEndMarker; // x-mirrored symbol for start marker
2386 bool yStraight = true;
2387 //bool yStraight= isEndMarker; // comment in for start marker y-mirrowed
2389 int x0 = xStraight ? rcPlace.left : rcPlace.right - 1;
2390 int y0 = yStraight ? rcPlace.top : rcPlace.bottom - 1;
2392 int dy = (rcPlace.bottom - rcPlace.top) / 5;
2393 int y = (rcPlace.bottom - rcPlace.top) / 2 + dy;
2395 struct Relative {
2396 Surface *surface;
2397 int xBase;
2398 int xDir;
2399 int yBase;
2400 int yDir;
2401 void MoveTo(int xRelative, int yRelative) {
2402 surface->MoveTo(xBase + xDir * xRelative, yBase + yDir * yRelative);
2404 void LineTo(int xRelative, int yRelative) {
2405 surface->LineTo(xBase + xDir * xRelative, yBase + yDir * yRelative);
2408 Relative rel = {surface, x0, xStraight ? 1 : -1, y0, yStraight ? 1 : -1};
2410 // arrow head
2411 rel.MoveTo(xa, y);
2412 rel.LineTo(xa + 2*w / 3, y - dy);
2413 rel.MoveTo(xa, y);
2414 rel.LineTo(xa + 2*w / 3, y + dy);
2416 // arrow body
2417 rel.MoveTo(xa, y);
2418 rel.LineTo(xa + w, y);
2419 rel.LineTo(xa + w, y - 2 * dy);
2420 rel.LineTo(xa - 1, // on windows lineto is exclusive endpoint, perhaps GTK not...
2421 y - 2 * dy);
2424 static void SimpleAlphaRectangle(Surface *surface, PRectangle rc, ColourAllocated fill, int alpha) {
2425 if (alpha != SC_ALPHA_NOALPHA) {
2426 surface->AlphaRectangle(rc, 0, fill, alpha, fill, alpha, 0);
2430 void DrawTextBlob(Surface *surface, ViewStyle &vsDraw, PRectangle rcSegment,
2431 const char *s, ColourAllocated textBack, ColourAllocated textFore, bool twoPhaseDraw) {
2432 if (!twoPhaseDraw) {
2433 surface->FillRectangle(rcSegment, textBack);
2435 Font &ctrlCharsFont = vsDraw.styles[STYLE_CONTROLCHAR].font;
2436 int normalCharHeight = surface->Ascent(ctrlCharsFont) -
2437 surface->InternalLeading(ctrlCharsFont);
2438 PRectangle rcCChar = rcSegment;
2439 rcCChar.left = rcCChar.left + 1;
2440 rcCChar.top = rcSegment.top + vsDraw.maxAscent - normalCharHeight;
2441 rcCChar.bottom = rcSegment.top + vsDraw.maxAscent + 1;
2442 PRectangle rcCentral = rcCChar;
2443 rcCentral.top++;
2444 rcCentral.bottom--;
2445 surface->FillRectangle(rcCentral, textFore);
2446 PRectangle rcChar = rcCChar;
2447 rcChar.left++;
2448 rcChar.right--;
2449 surface->DrawTextClipped(rcChar, ctrlCharsFont,
2450 rcSegment.top + vsDraw.maxAscent, s, istrlen(s),
2451 textBack, textFore);
2454 void Editor::DrawEOL(Surface *surface, ViewStyle &vsDraw, PRectangle rcLine, LineLayout *ll,
2455 int line, int lineEnd, int xStart, int subLine, int subLineStart,
2456 bool overrideBackground, ColourAllocated background,
2457 bool drawWrapMarkEnd, ColourAllocated wrapColour) {
2459 const int posLineStart = pdoc->LineStart(line);
2460 const int styleMask = pdoc->stylingBitsMask;
2461 PRectangle rcSegment = rcLine;
2463 const bool lastSubLine = subLine == (ll->lines - 1);
2464 int virtualSpace = 0;
2465 if (lastSubLine) {
2466 const int spaceWidth = static_cast<int>(vsDraw.styles[ll->EndLineStyle()].spaceWidth);
2467 virtualSpace = sel.VirtualSpaceFor(pdoc->LineEnd(line)) * spaceWidth;
2470 // Fill in a PRectangle representing the end of line characters
2472 int xEol = ll->positions[lineEnd] - subLineStart;
2474 // Fill the virtual space and show selections within it
2475 if (virtualSpace) {
2476 rcSegment.left = xEol + xStart;
2477 rcSegment.right = xEol + xStart + virtualSpace;
2478 surface->FillRectangle(rcSegment, overrideBackground ? background : vsDraw.styles[ll->styles[ll->numCharsInLine] & styleMask].back.allocated);
2479 if (!hideSelection && ((vsDraw.selAlpha == SC_ALPHA_NOALPHA) || (vsDraw.selAdditionalAlpha == SC_ALPHA_NOALPHA))) {
2480 SelectionSegment virtualSpaceRange(SelectionPosition(pdoc->LineEnd(line)), SelectionPosition(pdoc->LineEnd(line), sel.VirtualSpaceFor(pdoc->LineEnd(line))));
2481 for (size_t r=0; r<sel.Count(); r++) {
2482 int alpha = (r == sel.Main()) ? vsDraw.selAlpha : vsDraw.selAdditionalAlpha;
2483 if (alpha == SC_ALPHA_NOALPHA) {
2484 SelectionSegment portion = sel.Range(r).Intersect(virtualSpaceRange);
2485 if (!portion.Empty()) {
2486 const int spaceWidth = static_cast<int>(vsDraw.styles[ll->EndLineStyle()].spaceWidth);
2487 rcSegment.left = xStart + ll->positions[portion.start.Position() - posLineStart] - subLineStart + portion.start.VirtualSpace() * spaceWidth;
2488 rcSegment.right = xStart + ll->positions[portion.end.Position() - posLineStart] - subLineStart + portion.end.VirtualSpace() * spaceWidth;
2489 rcSegment.left = Platform::Maximum(rcSegment.left, rcLine.left);
2490 rcSegment.right = Platform::Minimum(rcSegment.right, rcLine.right);
2491 surface->FillRectangle(rcSegment, SelectionBackground(vsDraw, r == sel.Main()));
2498 int posAfterLineEnd = pdoc->LineStart(line + 1);
2499 int eolInSelection = (subLine == (ll->lines - 1)) ? sel.InSelectionForEOL(posAfterLineEnd) : 0;
2500 int alpha = (eolInSelection == 1) ? vsDraw.selAlpha : vsDraw.selAdditionalAlpha;
2502 // Draw the [CR], [LF], or [CR][LF] blobs if visible line ends are on
2503 int blobsWidth = 0;
2504 if (lastSubLine) {
2505 for (int eolPos=ll->numCharsBeforeEOL; eolPos<ll->numCharsInLine; eolPos++) {
2506 rcSegment.left = xStart + ll->positions[eolPos] - subLineStart + virtualSpace;
2507 rcSegment.right = xStart + ll->positions[eolPos+1] - subLineStart + virtualSpace;
2508 blobsWidth += rcSegment.Width();
2509 const char *ctrlChar = ControlCharacterString(ll->chars[eolPos]);
2510 int inSelection = 0;
2511 bool inHotspot = false;
2512 int styleMain = ll->styles[eolPos];
2513 ColourAllocated textBack = TextBackground(vsDraw, overrideBackground, background, inSelection, inHotspot, styleMain, eolPos, ll);
2514 ColourAllocated textFore = vsDraw.styles[styleMain].fore.allocated;
2515 if (!hideSelection && eolInSelection && vsDraw.selbackset && (line < pdoc->LinesTotal() - 1)) {
2516 if (alpha == SC_ALPHA_NOALPHA) {
2517 surface->FillRectangle(rcSegment, SelectionBackground(vsDraw, eolInSelection == 1));
2518 } else {
2519 surface->FillRectangle(rcSegment, textBack);
2520 SimpleAlphaRectangle(surface, rcSegment, SelectionBackground(vsDraw, eolInSelection == 1), alpha);
2522 } else {
2523 surface->FillRectangle(rcSegment, textBack);
2525 DrawTextBlob(surface, vsDraw, rcSegment, ctrlChar, textBack, textFore, twoPhaseDraw);
2529 // Draw the eol-is-selected rectangle
2530 rcSegment.left = xEol + xStart + virtualSpace + blobsWidth;
2531 rcSegment.right = xEol + xStart + virtualSpace + blobsWidth + vsDraw.aveCharWidth;
2533 if (!hideSelection && eolInSelection && vsDraw.selbackset && (line < pdoc->LinesTotal() - 1) && (alpha == SC_ALPHA_NOALPHA)) {
2534 surface->FillRectangle(rcSegment, SelectionBackground(vsDraw, eolInSelection == 1));
2535 } else {
2536 if (overrideBackground) {
2537 surface->FillRectangle(rcSegment, background);
2538 } else if (line < pdoc->LinesTotal() - 1) {
2539 surface->FillRectangle(rcSegment, vsDraw.styles[ll->styles[ll->numCharsInLine] & styleMask].back.allocated);
2540 } else if (vsDraw.styles[ll->styles[ll->numCharsInLine] & styleMask].eolFilled) {
2541 surface->FillRectangle(rcSegment, vsDraw.styles[ll->styles[ll->numCharsInLine] & styleMask].back.allocated);
2542 } else {
2543 surface->FillRectangle(rcSegment, vsDraw.styles[STYLE_DEFAULT].back.allocated);
2545 if (!hideSelection && eolInSelection && vsDraw.selbackset && (line < pdoc->LinesTotal() - 1) && (alpha != SC_ALPHA_NOALPHA)) {
2546 SimpleAlphaRectangle(surface, rcSegment, SelectionBackground(vsDraw, eolInSelection == 1), alpha);
2550 // Fill the remainder of the line
2551 rcSegment.left = xEol + xStart + virtualSpace + blobsWidth + vsDraw.aveCharWidth;
2552 if (rcSegment.left < rcLine.left)
2553 rcSegment.left = rcLine.left;
2554 rcSegment.right = rcLine.right;
2556 if (!hideSelection && vsDraw.selEOLFilled && eolInSelection && vsDraw.selbackset && (line < pdoc->LinesTotal() - 1) && (alpha == SC_ALPHA_NOALPHA)) {
2557 surface->FillRectangle(rcSegment, SelectionBackground(vsDraw, eolInSelection == 1));
2558 } else {
2559 if (overrideBackground) {
2560 surface->FillRectangle(rcSegment, background);
2561 } else if (vsDraw.styles[ll->styles[ll->numCharsInLine] & styleMask].eolFilled) {
2562 surface->FillRectangle(rcSegment, vsDraw.styles[ll->styles[ll->numCharsInLine] & styleMask].back.allocated);
2563 } else {
2564 surface->FillRectangle(rcSegment, vsDraw.styles[STYLE_DEFAULT].back.allocated);
2566 if (!hideSelection && vsDraw.selEOLFilled && eolInSelection && vsDraw.selbackset && (line < pdoc->LinesTotal() - 1) && (alpha != SC_ALPHA_NOALPHA)) {
2567 SimpleAlphaRectangle(surface, rcSegment, SelectionBackground(vsDraw, eolInSelection == 1), alpha);
2571 if (drawWrapMarkEnd) {
2572 PRectangle rcPlace = rcSegment;
2574 if (wrapVisualFlagsLocation & SC_WRAPVISUALFLAGLOC_END_BY_TEXT) {
2575 rcPlace.left = xEol + xStart + virtualSpace;
2576 rcPlace.right = rcPlace.left + vsDraw.aveCharWidth;
2577 } else {
2578 // draw left of the right text margin, to avoid clipping by the current clip rect
2579 rcPlace.right = rcLine.right - vs.rightMarginWidth;
2580 rcPlace.left = rcPlace.right - vsDraw.aveCharWidth;
2582 DrawWrapMarker(surface, rcPlace, true, wrapColour);
2586 void Editor::DrawIndicator(int indicNum, int startPos, int endPos, Surface *surface, ViewStyle &vsDraw,
2587 int xStart, PRectangle rcLine, LineLayout *ll, int subLine) {
2588 const int subLineStart = ll->positions[ll->LineStart(subLine)];
2589 PRectangle rcIndic(
2590 ll->positions[startPos] + xStart - subLineStart,
2591 rcLine.top + vsDraw.maxAscent,
2592 ll->positions[endPos] + xStart - subLineStart,
2593 rcLine.top + vsDraw.maxAscent + 3);
2594 vsDraw.indicators[indicNum].Draw(surface, rcIndic, rcLine);
2597 void Editor::DrawIndicators(Surface *surface, ViewStyle &vsDraw, int line, int xStart,
2598 PRectangle rcLine, LineLayout *ll, int subLine, int lineEnd, bool under) {
2599 // Draw decorators
2600 const int posLineStart = pdoc->LineStart(line);
2601 const int lineStart = ll->LineStart(subLine);
2602 const int posLineEnd = posLineStart + lineEnd;
2604 if (!under) {
2605 // Draw indicators
2606 // foreach indicator...
2607 for (int indicnum = 0, mask = 1 << pdoc->stylingBits; mask < 0x100; indicnum++) {
2608 if (!(mask & ll->styleBitsSet)) {
2609 mask <<= 1;
2610 continue;
2612 int startPos = -1;
2613 // foreach style pos in line...
2614 for (int indicPos = lineStart; indicPos <= lineEnd; indicPos++) {
2615 // look for starts...
2616 if (startPos < 0) {
2617 // NOT in indicator run, looking for START
2618 if (indicPos < lineEnd && (ll->indicators[indicPos] & mask))
2619 startPos = indicPos;
2621 // ... or ends
2622 if (startPos >= 0) {
2623 // IN indicator run, looking for END
2624 if (indicPos >= lineEnd || !(ll->indicators[indicPos] & mask)) {
2625 // AT end of indicator run, DRAW it!
2626 DrawIndicator(indicnum, startPos, indicPos, surface, vsDraw, xStart, rcLine, ll, subLine);
2627 // RESET control var
2628 startPos = -1;
2632 mask <<= 1;
2636 for (Decoration *deco = pdoc->decorations.root; deco; deco = deco->next) {
2637 if (under == vsDraw.indicators[deco->indicator].under) {
2638 int startPos = posLineStart + lineStart;
2639 if (!deco->rs.ValueAt(startPos)) {
2640 startPos = deco->rs.EndRun(startPos);
2642 while ((startPos < posLineEnd) && (deco->rs.ValueAt(startPos))) {
2643 int endPos = deco->rs.EndRun(startPos);
2644 if (endPos > posLineEnd)
2645 endPos = posLineEnd;
2646 DrawIndicator(deco->indicator, startPos - posLineStart, endPos - posLineStart,
2647 surface, vsDraw, xStart, rcLine, ll, subLine);
2648 startPos = deco->rs.EndRun(endPos);
2653 // Use indicators to highlight matching braces
2654 if ((vs.braceHighlightIndicatorSet && (bracesMatchStyle == STYLE_BRACELIGHT)) ||
2655 (vs.braceBadLightIndicatorSet && (bracesMatchStyle == STYLE_BRACEBAD))) {
2656 int braceIndicator = (bracesMatchStyle == STYLE_BRACELIGHT) ? vs.braceHighlightIndicator : vs.braceBadLightIndicator;
2657 if (under == vsDraw.indicators[braceIndicator].under) {
2658 Range rangeLine(posLineStart + lineStart, posLineEnd);
2659 if (rangeLine.ContainsCharacter(braces[0])) {
2660 int braceOffset = braces[0] - posLineStart;
2661 if (braceOffset < ll->numCharsInLine) {
2662 DrawIndicator(braceIndicator, braceOffset, braceOffset + 1, surface, vsDraw, xStart, rcLine, ll, subLine);
2665 if (rangeLine.ContainsCharacter(braces[1])) {
2666 int braceOffset = braces[1] - posLineStart;
2667 if (braceOffset < ll->numCharsInLine) {
2668 DrawIndicator(braceIndicator, braceOffset, braceOffset + 1, surface, vsDraw, xStart, rcLine, ll, subLine);
2675 void Editor::DrawAnnotation(Surface *surface, ViewStyle &vsDraw, int line, int xStart,
2676 PRectangle rcLine, LineLayout *ll, int subLine) {
2677 int indent = pdoc->GetLineIndentation(line) * vsDraw.spaceWidth;
2678 PRectangle rcSegment = rcLine;
2679 int annotationLine = subLine - ll->lines;
2680 const StyledText stAnnotation = pdoc->AnnotationStyledText(line);
2681 if (stAnnotation.text && ValidStyledText(vsDraw, vsDraw.annotationStyleOffset, stAnnotation)) {
2682 surface->FillRectangle(rcSegment, vsDraw.styles[0].back.allocated);
2683 if (vs.annotationVisible == ANNOTATION_BOXED) {
2684 // Only care about calculating width if need to draw box
2685 int widthAnnotation = WidestLineWidth(surface, vsDraw, vsDraw.annotationStyleOffset, stAnnotation);
2686 widthAnnotation += vsDraw.spaceWidth * 2; // Margins
2687 rcSegment.left = xStart + indent;
2688 rcSegment.right = rcSegment.left + widthAnnotation;
2689 surface->PenColour(vsDraw.styles[vsDraw.annotationStyleOffset].fore.allocated);
2690 } else {
2691 rcSegment.left = xStart;
2693 const int annotationLines = pdoc->AnnotationLines(line);
2694 size_t start = 0;
2695 size_t lengthAnnotation = stAnnotation.LineLength(start);
2696 int lineInAnnotation = 0;
2697 while ((lineInAnnotation < annotationLine) && (start < stAnnotation.length)) {
2698 start += lengthAnnotation + 1;
2699 lengthAnnotation = stAnnotation.LineLength(start);
2700 lineInAnnotation++;
2702 PRectangle rcText = rcSegment;
2703 if (vs.annotationVisible == ANNOTATION_BOXED) {
2704 surface->FillRectangle(rcText,
2705 vsDraw.styles[stAnnotation.StyleAt(start) + vsDraw.annotationStyleOffset].back.allocated);
2706 rcText.left += vsDraw.spaceWidth;
2708 DrawStyledText(surface, vsDraw, vsDraw.annotationStyleOffset, rcText, rcText.top + vsDraw.maxAscent,
2709 stAnnotation, start, lengthAnnotation);
2710 if (vs.annotationVisible == ANNOTATION_BOXED) {
2711 surface->MoveTo(rcSegment.left, rcSegment.top);
2712 surface->LineTo(rcSegment.left, rcSegment.bottom);
2713 surface->MoveTo(rcSegment.right, rcSegment.top);
2714 surface->LineTo(rcSegment.right, rcSegment.bottom);
2715 if (subLine == ll->lines) {
2716 surface->MoveTo(rcSegment.left, rcSegment.top);
2717 surface->LineTo(rcSegment.right, rcSegment.top);
2719 if (subLine == ll->lines+annotationLines-1) {
2720 surface->MoveTo(rcSegment.left, rcSegment.bottom - 1);
2721 surface->LineTo(rcSegment.right, rcSegment.bottom - 1);
2727 void Editor::DrawLine(Surface *surface, ViewStyle &vsDraw, int line, int lineVisible, int xStart,
2728 PRectangle rcLine, LineLayout *ll, int subLine) {
2730 PRectangle rcSegment = rcLine;
2732 // Using one font for all control characters so it can be controlled independently to ensure
2733 // the box goes around the characters tightly. Seems to be no way to work out what height
2734 // is taken by an individual character - internal leading gives varying results.
2735 Font &ctrlCharsFont = vsDraw.styles[STYLE_CONTROLCHAR].font;
2737 // See if something overrides the line background color: Either if caret is on the line
2738 // and background color is set for that, or if a marker is defined that forces its background
2739 // color onto the line, or if a marker is defined but has no selection margin in which to
2740 // display itself (as long as it's not an SC_MARK_EMPTY marker). These are checked in order
2741 // with the earlier taking precedence. When multiple markers cause background override,
2742 // the color for the highest numbered one is used.
2743 bool overrideBackground = false;
2744 ColourAllocated background;
2745 if (caret.active && vsDraw.showCaretLineBackground && (vsDraw.caretLineAlpha == SC_ALPHA_NOALPHA) && ll->containsCaret) {
2746 overrideBackground = true;
2747 background = vsDraw.caretLineBackground.allocated;
2749 if (!overrideBackground) {
2750 int marks = pdoc->GetMark(line);
2751 for (int markBit = 0; (markBit < 32) && marks; markBit++) {
2752 if ((marks & 1) && (vsDraw.markers[markBit].markType == SC_MARK_BACKGROUND) &&
2753 (vsDraw.markers[markBit].alpha == SC_ALPHA_NOALPHA)) {
2754 background = vsDraw.markers[markBit].back.allocated;
2755 overrideBackground = true;
2757 marks >>= 1;
2760 if (!overrideBackground) {
2761 if (vsDraw.maskInLine) {
2762 int marksMasked = pdoc->GetMark(line) & vsDraw.maskInLine;
2763 if (marksMasked) {
2764 for (int markBit = 0; (markBit < 32) && marksMasked; markBit++) {
2765 if ((marksMasked & 1) && (vsDraw.markers[markBit].markType != SC_MARK_EMPTY) &&
2766 (vsDraw.markers[markBit].alpha == SC_ALPHA_NOALPHA)) {
2767 overrideBackground = true;
2768 background = vsDraw.markers[markBit].back.allocated;
2770 marksMasked >>= 1;
2776 bool drawWhitespaceBackground = (vsDraw.viewWhitespace != wsInvisible) &&
2777 (!overrideBackground) && (vsDraw.whitespaceBackgroundSet);
2779 bool inIndentation = subLine == 0; // Do not handle indentation except on first subline.
2780 int indentWidth = pdoc->IndentSize() * vsDraw.spaceWidth;
2782 int posLineStart = pdoc->LineStart(line);
2784 int startseg = ll->LineStart(subLine);
2785 int subLineStart = ll->positions[startseg];
2786 if (subLine >= ll->lines) {
2787 DrawAnnotation(surface, vsDraw, line, xStart, rcLine, ll, subLine);
2788 return; // No further drawing
2790 int lineStart = 0;
2791 int lineEnd = 0;
2792 if (subLine < ll->lines) {
2793 lineStart = ll->LineStart(subLine);
2794 lineEnd = ll->LineStart(subLine + 1);
2795 if (subLine == ll->lines - 1) {
2796 lineEnd = ll->numCharsBeforeEOL;
2800 ColourAllocated wrapColour = vsDraw.styles[STYLE_DEFAULT].fore.allocated;
2801 if (vsDraw.whitespaceForegroundSet)
2802 wrapColour = vsDraw.whitespaceForeground.allocated;
2804 bool drawWrapMarkEnd = false;
2806 if (wrapVisualFlags & SC_WRAPVISUALFLAG_END) {
2807 if (subLine + 1 < ll->lines) {
2808 drawWrapMarkEnd = ll->LineStart(subLine + 1) != 0;
2812 if (ll->wrapIndent != 0) {
2814 bool continuedWrapLine = false;
2815 if (subLine < ll->lines) {
2816 continuedWrapLine = ll->LineStart(subLine) != 0;
2819 if (continuedWrapLine) {
2820 // draw continuation rect
2821 PRectangle rcPlace = rcSegment;
2823 rcPlace.left = ll->positions[startseg] + xStart - subLineStart;
2824 rcPlace.right = rcPlace.left + ll->wrapIndent;
2826 // default bgnd here..
2827 surface->FillRectangle(rcSegment, overrideBackground ? background :
2828 vsDraw.styles[STYLE_DEFAULT].back.allocated);
2830 // main line style would be below but this would be inconsistent with end markers
2831 // also would possibly not be the style at wrap point
2832 //int styleMain = ll->styles[lineStart];
2833 //surface->FillRectangle(rcPlace, vsDraw.styles[styleMain].back.allocated);
2835 if (wrapVisualFlags & SC_WRAPVISUALFLAG_START) {
2837 if (wrapVisualFlagsLocation & SC_WRAPVISUALFLAGLOC_START_BY_TEXT)
2838 rcPlace.left = rcPlace.right - vsDraw.aveCharWidth;
2839 else
2840 rcPlace.right = rcPlace.left + vsDraw.aveCharWidth;
2842 DrawWrapMarker(surface, rcPlace, false, wrapColour);
2845 xStart += ll->wrapIndent;
2849 bool selBackDrawn = vsDraw.selbackset &&
2850 ((vsDraw.selAlpha == SC_ALPHA_NOALPHA) || (vsDraw.selAdditionalAlpha == SC_ALPHA_NOALPHA));
2852 // Does not take margin into account but not significant
2853 int xStartVisible = subLineStart - xStart;
2855 ll->psel = &sel;
2857 BreakFinder bfBack(ll, lineStart, lineEnd, posLineStart, xStartVisible, selBackDrawn, pdoc);
2858 int next = bfBack.First();
2860 // Background drawing loop
2861 while (twoPhaseDraw && (next < lineEnd)) {
2863 startseg = next;
2864 next = bfBack.Next();
2865 int i = next - 1;
2866 int iDoc = i + posLineStart;
2868 rcSegment.left = ll->positions[startseg] + xStart - subLineStart;
2869 rcSegment.right = ll->positions[i + 1] + xStart - subLineStart;
2870 // Only try to draw if really visible - enhances performance by not calling environment to
2871 // draw strings that are completely past the right side of the window.
2872 if ((rcSegment.left <= rcLine.right) && (rcSegment.right >= rcLine.left)) {
2873 // Clip to line rectangle, since may have a huge position which will not work with some platforms
2874 rcSegment.left = Platform::Maximum(rcSegment.left, rcLine.left);
2875 rcSegment.right = Platform::Minimum(rcSegment.right, rcLine.right);
2877 int styleMain = ll->styles[i];
2878 const int inSelection = hideSelection ? 0 : sel.CharacterInSelection(iDoc);
2879 bool inHotspot = (ll->hsStart != -1) && (iDoc >= ll->hsStart) && (iDoc < ll->hsEnd);
2880 ColourAllocated textBack = TextBackground(vsDraw, overrideBackground, background, inSelection, inHotspot, styleMain, i, ll);
2881 if (ll->chars[i] == '\t') {
2882 // Tab display
2883 if (drawWhitespaceBackground &&
2884 (!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways))
2885 textBack = vsDraw.whitespaceBackground.allocated;
2886 surface->FillRectangle(rcSegment, textBack);
2887 } else if (IsControlCharacter(ll->chars[i])) {
2888 // Control character display
2889 inIndentation = false;
2890 surface->FillRectangle(rcSegment, textBack);
2891 } else {
2892 // Normal text display
2893 surface->FillRectangle(rcSegment, textBack);
2894 if (vsDraw.viewWhitespace != wsInvisible ||
2895 (inIndentation && vsDraw.viewIndentationGuides == ivReal)) {
2896 for (int cpos = 0; cpos <= i - startseg; cpos++) {
2897 if (ll->chars[cpos + startseg] == ' ') {
2898 if (drawWhitespaceBackground &&
2899 (!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways)) {
2900 PRectangle rcSpace(ll->positions[cpos + startseg] + xStart - subLineStart,
2901 rcSegment.top,
2902 ll->positions[cpos + startseg + 1] + xStart - subLineStart,
2903 rcSegment.bottom);
2904 surface->FillRectangle(rcSpace, vsDraw.whitespaceBackground.allocated);
2906 } else {
2907 inIndentation = false;
2912 } else if (rcSegment.left > rcLine.right) {
2913 break;
2917 if (twoPhaseDraw) {
2918 DrawEOL(surface, vsDraw, rcLine, ll, line, lineEnd,
2919 xStart, subLine, subLineStart, overrideBackground, background,
2920 drawWrapMarkEnd, wrapColour);
2923 DrawIndicators(surface, vsDraw, line, xStart, rcLine, ll, subLine, lineEnd, true);
2925 if (vsDraw.edgeState == EDGE_LINE) {
2926 int edgeX = theEdge * vsDraw.spaceWidth;
2927 rcSegment.left = edgeX + xStart;
2928 rcSegment.right = rcSegment.left + 1;
2929 surface->FillRectangle(rcSegment, vsDraw.edgecolour.allocated);
2932 // Draw underline mark as part of background if not transparent
2933 int marks = pdoc->GetMark(line);
2934 int markBit;
2935 for (markBit = 0; (markBit < 32) && marks; markBit++) {
2936 if ((marks & 1) && (vsDraw.markers[markBit].markType == SC_MARK_UNDERLINE) &&
2937 (vsDraw.markers[markBit].alpha == SC_ALPHA_NOALPHA)) {
2938 PRectangle rcUnderline = rcLine;
2939 rcUnderline.top = rcUnderline.bottom - 2;
2940 surface->FillRectangle(rcUnderline, vsDraw.markers[markBit].back.allocated);
2942 marks >>= 1;
2945 inIndentation = subLine == 0; // Do not handle indentation except on first subline.
2946 // Foreground drawing loop
2947 BreakFinder bfFore(ll, lineStart, lineEnd, posLineStart, xStartVisible,
2948 ((!twoPhaseDraw && selBackDrawn) || vsDraw.selforeset), pdoc);
2949 next = bfFore.First();
2951 while (next < lineEnd) {
2953 startseg = next;
2954 next = bfFore.Next();
2955 int i = next - 1;
2957 int iDoc = i + posLineStart;
2959 rcSegment.left = ll->positions[startseg] + xStart - subLineStart;
2960 rcSegment.right = ll->positions[i + 1] + xStart - subLineStart;
2961 // Only try to draw if really visible - enhances performance by not calling environment to
2962 // draw strings that are completely past the right side of the window.
2963 if ((rcSegment.left <= rcLine.right) && (rcSegment.right >= rcLine.left)) {
2964 int styleMain = ll->styles[i];
2965 ColourAllocated textFore = vsDraw.styles[styleMain].fore.allocated;
2966 Font &textFont = vsDraw.styles[styleMain].font;
2967 //hotspot foreground
2968 if (ll->hsStart != -1 && iDoc >= ll->hsStart && iDoc < hsEnd) {
2969 if (vsDraw.hotspotForegroundSet)
2970 textFore = vsDraw.hotspotForeground.allocated;
2972 const int inSelection = hideSelection ? 0 : sel.CharacterInSelection(iDoc);
2973 if (inSelection && (vsDraw.selforeset)) {
2974 textFore = (inSelection == 1) ? vsDraw.selforeground.allocated : vsDraw.selAdditionalForeground.allocated;
2976 bool inHotspot = (ll->hsStart != -1) && (iDoc >= ll->hsStart) && (iDoc < ll->hsEnd);
2977 ColourAllocated textBack = TextBackground(vsDraw, overrideBackground, background, inSelection, inHotspot, styleMain, i, ll);
2978 if (ll->chars[i] == '\t') {
2979 // Tab display
2980 if (!twoPhaseDraw) {
2981 if (drawWhitespaceBackground &&
2982 (!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways))
2983 textBack = vsDraw.whitespaceBackground.allocated;
2984 surface->FillRectangle(rcSegment, textBack);
2986 if ((vsDraw.viewWhitespace != wsInvisible) ||
2987 (inIndentation && vsDraw.viewIndentationGuides != ivNone)) {
2988 if (vsDraw.whitespaceForegroundSet)
2989 textFore = vsDraw.whitespaceForeground.allocated;
2990 surface->PenColour(textFore);
2992 if (inIndentation && vsDraw.viewIndentationGuides == ivReal) {
2993 for (int xIG = ll->positions[i] / indentWidth * indentWidth; xIG < ll->positions[i + 1]; xIG += indentWidth) {
2994 if (xIG >= ll->positions[i] && xIG > 0) {
2995 DrawIndentGuide(surface, lineVisible, vsDraw.lineHeight, xIG + xStart, rcSegment,
2996 (ll->xHighlightGuide == xIG));
3000 if (vsDraw.viewWhitespace != wsInvisible) {
3001 if (!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways) {
3002 PRectangle rcTab(rcSegment.left + 1, rcSegment.top + 4,
3003 rcSegment.right - 1, rcSegment.bottom - vsDraw.maxDescent);
3004 DrawTabArrow(surface, rcTab, rcSegment.top + vsDraw.lineHeight / 2);
3007 } else if (IsControlCharacter(ll->chars[i])) {
3008 // Control character display
3009 inIndentation = false;
3010 if (controlCharSymbol < 32) {
3011 // Draw the character
3012 const char *ctrlChar = ControlCharacterString(ll->chars[i]);
3013 DrawTextBlob(surface, vsDraw, rcSegment, ctrlChar, textBack, textFore, twoPhaseDraw);
3014 } else {
3015 char cc[2] = { static_cast<char>(controlCharSymbol), '\0' };
3016 surface->DrawTextNoClip(rcSegment, ctrlCharsFont,
3017 rcSegment.top + vsDraw.maxAscent,
3018 cc, 1, textBack, textFore);
3020 } else if ((i == startseg) && (static_cast<unsigned char>(ll->chars[i]) >= 0x80) && IsUnicodeMode()) {
3021 // A single byte >= 0x80 in UTF-8 is a bad byte and is displayed as its hex value
3022 char hexits[4];
3023 sprintf(hexits, "x%2X", ll->chars[i] & 0xff);
3024 DrawTextBlob(surface, vsDraw, rcSegment, hexits, textBack, textFore, twoPhaseDraw);
3025 } else {
3026 // Normal text display
3027 if (vsDraw.styles[styleMain].visible) {
3028 if (twoPhaseDraw) {
3029 surface->DrawTextTransparent(rcSegment, textFont,
3030 rcSegment.top + vsDraw.maxAscent, ll->chars + startseg,
3031 i - startseg + 1, textFore);
3032 } else {
3033 surface->DrawTextNoClip(rcSegment, textFont,
3034 rcSegment.top + vsDraw.maxAscent, ll->chars + startseg,
3035 i - startseg + 1, textFore, textBack);
3038 if (vsDraw.viewWhitespace != wsInvisible ||
3039 (inIndentation && vsDraw.viewIndentationGuides != ivNone)) {
3040 for (int cpos = 0; cpos <= i - startseg; cpos++) {
3041 if (ll->chars[cpos + startseg] == ' ') {
3042 if (vsDraw.viewWhitespace != wsInvisible) {
3043 if (vsDraw.whitespaceForegroundSet)
3044 textFore = vsDraw.whitespaceForeground.allocated;
3045 if (!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways) {
3046 int xmid = (ll->positions[cpos + startseg] + ll->positions[cpos + startseg + 1]) / 2;
3047 if (!twoPhaseDraw && drawWhitespaceBackground &&
3048 (!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways)) {
3049 textBack = vsDraw.whitespaceBackground.allocated;
3050 PRectangle rcSpace(ll->positions[cpos + startseg] + xStart - subLineStart,
3051 rcSegment.top,
3052 ll->positions[cpos + startseg + 1] + xStart - subLineStart,
3053 rcSegment.bottom);
3054 surface->FillRectangle(rcSpace, textBack);
3056 PRectangle rcDot(xmid + xStart - subLineStart, rcSegment.top + vsDraw.lineHeight / 2, 0, 0);
3057 rcDot.right = rcDot.left + vs.whitespaceSize;
3058 rcDot.bottom = rcDot.top + vs.whitespaceSize;
3059 surface->FillRectangle(rcDot, textFore);
3062 if (inIndentation && vsDraw.viewIndentationGuides == ivReal) {
3063 int startSpace = ll->positions[cpos + startseg];
3064 if (startSpace > 0 && (startSpace % indentWidth == 0)) {
3065 DrawIndentGuide(surface, lineVisible, vsDraw.lineHeight, startSpace + xStart, rcSegment,
3066 (ll->xHighlightGuide == ll->positions[cpos + startseg]));
3069 } else {
3070 inIndentation = false;
3075 if (ll->hsStart != -1 && vsDraw.hotspotUnderline && iDoc >= ll->hsStart && iDoc < ll->hsEnd) {
3076 PRectangle rcUL = rcSegment;
3077 rcUL.top = rcUL.top + vsDraw.maxAscent + 1;
3078 rcUL.bottom = rcUL.top + 1;
3079 if (vsDraw.hotspotForegroundSet)
3080 surface->FillRectangle(rcUL, vsDraw.hotspotForeground.allocated);
3081 else
3082 surface->FillRectangle(rcUL, textFore);
3083 } else if (vsDraw.styles[styleMain].underline) {
3084 PRectangle rcUL = rcSegment;
3085 rcUL.top = rcUL.top + vsDraw.maxAscent + 1;
3086 rcUL.bottom = rcUL.top + 1;
3087 surface->FillRectangle(rcUL, textFore);
3089 } else if (rcSegment.left > rcLine.right) {
3090 break;
3093 if ((vsDraw.viewIndentationGuides == ivLookForward || vsDraw.viewIndentationGuides == ivLookBoth)
3094 && (subLine == 0)) {
3095 int indentSpace = pdoc->GetLineIndentation(line);
3096 int xStartText = ll->positions[pdoc->GetLineIndentPosition(line) - posLineStart];
3098 // Find the most recent line with some text
3100 int lineLastWithText = line;
3101 while (lineLastWithText > Platform::Maximum(line-20, 0) && pdoc->IsWhiteLine(lineLastWithText)) {
3102 lineLastWithText--;
3104 if (lineLastWithText < line) {
3105 xStartText = 100000; // Don't limit to visible indentation on empty line
3106 // This line is empty, so use indentation of last line with text
3107 int indentLastWithText = pdoc->GetLineIndentation(lineLastWithText);
3108 int isFoldHeader = pdoc->GetLevel(lineLastWithText) & SC_FOLDLEVELHEADERFLAG;
3109 if (isFoldHeader) {
3110 // Level is one more level than parent
3111 indentLastWithText += pdoc->IndentSize();
3113 if (vsDraw.viewIndentationGuides == ivLookForward) {
3114 // In viLookForward mode, previous line only used if it is a fold header
3115 if (isFoldHeader) {
3116 indentSpace = Platform::Maximum(indentSpace, indentLastWithText);
3118 } else { // viLookBoth
3119 indentSpace = Platform::Maximum(indentSpace, indentLastWithText);
3123 int lineNextWithText = line;
3124 while (lineNextWithText < Platform::Minimum(line+20, pdoc->LinesTotal()) && pdoc->IsWhiteLine(lineNextWithText)) {
3125 lineNextWithText++;
3127 if (lineNextWithText > line) {
3128 xStartText = 100000; // Don't limit to visible indentation on empty line
3129 // This line is empty, so use indentation of first next line with text
3130 indentSpace = Platform::Maximum(indentSpace,
3131 pdoc->GetLineIndentation(lineNextWithText));
3134 for (int indentPos = pdoc->IndentSize(); indentPos < indentSpace; indentPos += pdoc->IndentSize()) {
3135 int xIndent = indentPos * vsDraw.spaceWidth;
3136 if (xIndent < xStartText) {
3137 DrawIndentGuide(surface, lineVisible, vsDraw.lineHeight, xIndent + xStart, rcSegment,
3138 (ll->xHighlightGuide == xIndent));
3143 DrawIndicators(surface, vsDraw, line, xStart, rcLine, ll, subLine, lineEnd, false);
3145 // End of the drawing of the current line
3146 if (!twoPhaseDraw) {
3147 DrawEOL(surface, vsDraw, rcLine, ll, line, lineEnd,
3148 xStart, subLine, subLineStart, overrideBackground, background,
3149 drawWrapMarkEnd, wrapColour);
3151 if (!hideSelection && ((vsDraw.selAlpha != SC_ALPHA_NOALPHA) || (vsDraw.selAdditionalAlpha != SC_ALPHA_NOALPHA))) {
3152 // For each selection draw
3153 int virtualSpaces = 0;
3154 if (subLine == (ll->lines - 1)) {
3155 virtualSpaces = sel.VirtualSpaceFor(pdoc->LineEnd(line));
3157 SelectionPosition posStart(posLineStart);
3158 SelectionPosition posEnd(posLineStart + lineEnd, virtualSpaces);
3159 SelectionSegment virtualSpaceRange(posStart, posEnd);
3160 for (size_t r=0; r<sel.Count(); r++) {
3161 int alpha = (r == sel.Main()) ? vsDraw.selAlpha : vsDraw.selAdditionalAlpha;
3162 if (alpha != SC_ALPHA_NOALPHA) {
3163 SelectionSegment portion = sel.Range(r).Intersect(virtualSpaceRange);
3164 if (!portion.Empty()) {
3165 const int spaceWidth = static_cast<int>(vsDraw.styles[ll->EndLineStyle()].spaceWidth);
3166 rcSegment.left = xStart + ll->positions[portion.start.Position() - posLineStart] - subLineStart + portion.start.VirtualSpace() * spaceWidth;
3167 rcSegment.right = xStart + ll->positions[portion.end.Position() - posLineStart] - subLineStart + portion.end.VirtualSpace() * spaceWidth;
3168 rcSegment.left = Platform::Maximum(rcSegment.left, rcLine.left);
3169 rcSegment.right = Platform::Minimum(rcSegment.right, rcLine.right);
3170 SimpleAlphaRectangle(surface, rcSegment, SelectionBackground(vsDraw, r == sel.Main()), alpha);
3176 // Draw any translucent whole line states
3177 rcSegment.left = 0;
3178 rcSegment.right = rcLine.right - 1;
3179 if (caret.active && vsDraw.showCaretLineBackground && ll->containsCaret) {
3180 SimpleAlphaRectangle(surface, rcSegment, vsDraw.caretLineBackground.allocated, vsDraw.caretLineAlpha);
3182 marks = pdoc->GetMark(line);
3183 for (markBit = 0; (markBit < 32) && marks; markBit++) {
3184 if ((marks & 1) && (vsDraw.markers[markBit].markType == SC_MARK_BACKGROUND)) {
3185 SimpleAlphaRectangle(surface, rcSegment, vsDraw.markers[markBit].back.allocated, vsDraw.markers[markBit].alpha);
3186 } else if ((marks & 1) && (vsDraw.markers[markBit].markType == SC_MARK_UNDERLINE)) {
3187 PRectangle rcUnderline = rcSegment;
3188 rcUnderline.top = rcUnderline.bottom - 2;
3189 SimpleAlphaRectangle(surface, rcUnderline, vsDraw.markers[markBit].back.allocated, vsDraw.markers[markBit].alpha);
3191 marks >>= 1;
3193 if (vsDraw.maskInLine) {
3194 int marksMasked = pdoc->GetMark(line) & vsDraw.maskInLine;
3195 if (marksMasked) {
3196 for (markBit = 0; (markBit < 32) && marksMasked; markBit++) {
3197 if ((marksMasked & 1) && (vsDraw.markers[markBit].markType != SC_MARK_EMPTY)) {
3198 SimpleAlphaRectangle(surface, rcSegment, vsDraw.markers[markBit].back.allocated, vsDraw.markers[markBit].alpha);
3200 marksMasked >>= 1;
3206 void Editor::DrawBlockCaret(Surface *surface, ViewStyle &vsDraw, LineLayout *ll, int subLine,
3207 int xStart, int offset, int posCaret, PRectangle rcCaret, ColourAllocated caretColour) {
3209 int lineStart = ll->LineStart(subLine);
3210 int posBefore = posCaret;
3211 int posAfter = MovePositionOutsideChar(posCaret + 1, 1);
3212 int numCharsToDraw = posAfter - posCaret;
3214 // Work out where the starting and ending offsets are. We need to
3215 // see if the previous character shares horizontal space, such as a
3216 // glyph / combining character. If so we'll need to draw that too.
3217 int offsetFirstChar = offset;
3218 int offsetLastChar = offset + (posAfter - posCaret);
3219 while ((offsetLastChar - numCharsToDraw) >= lineStart) {
3220 if ((ll->positions[offsetLastChar] - ll->positions[offsetLastChar - numCharsToDraw]) > 0) {
3221 // The char does not share horizontal space
3222 break;
3224 // Char shares horizontal space, update the numChars to draw
3225 // Update posBefore to point to the prev char
3226 posBefore = MovePositionOutsideChar(posBefore - 1, -1);
3227 numCharsToDraw = posAfter - posBefore;
3228 offsetFirstChar = offset - (posCaret - posBefore);
3231 // See if the next character shares horizontal space, if so we'll
3232 // need to draw that too.
3233 numCharsToDraw = offsetLastChar - offsetFirstChar;
3234 while ((offsetLastChar < ll->LineStart(subLine + 1)) && (offsetLastChar <= ll->numCharsInLine)) {
3235 // Update posAfter to point to the 2nd next char, this is where
3236 // the next character ends, and 2nd next begins. We'll need
3237 // to compare these two
3238 posBefore = posAfter;
3239 posAfter = MovePositionOutsideChar(posAfter + 1, 1);
3240 offsetLastChar = offset + (posAfter - posCaret);
3241 if ((ll->positions[offsetLastChar] - ll->positions[offsetLastChar - (posAfter - posBefore)]) > 0) {
3242 // The char does not share horizontal space
3243 break;
3245 // Char shares horizontal space, update the numChars to draw
3246 numCharsToDraw = offsetLastChar - offsetFirstChar;
3249 // We now know what to draw, update the caret drawing rectangle
3250 rcCaret.left = ll->positions[offsetFirstChar] - ll->positions[lineStart] + xStart;
3251 rcCaret.right = ll->positions[offsetFirstChar+numCharsToDraw] - ll->positions[lineStart] + xStart;
3253 // Adjust caret position to take into account any word wrapping symbols.
3254 if ((ll->wrapIndent != 0) && (lineStart != 0)) {
3255 int wordWrapCharWidth = ll->wrapIndent;
3256 rcCaret.left += wordWrapCharWidth;
3257 rcCaret.right += wordWrapCharWidth;
3260 // This character is where the caret block is, we override the colours
3261 // (inversed) for drawing the caret here.
3262 int styleMain = ll->styles[offsetFirstChar];
3263 surface->DrawTextClipped(rcCaret, vsDraw.styles[styleMain].font,
3264 rcCaret.top + vsDraw.maxAscent, ll->chars + offsetFirstChar,
3265 numCharsToDraw, vsDraw.styles[styleMain].back.allocated,
3266 caretColour);
3269 void Editor::RefreshPixMaps(Surface *surfaceWindow) {
3270 if (!pixmapSelPattern->Initialised()) {
3271 const int patternSize = 8;
3272 pixmapSelPattern->InitPixMap(patternSize, patternSize, surfaceWindow, wMain.GetID());
3273 // This complex procedure is to reproduce the checkerboard dithered pattern used by windows
3274 // for scroll bars and Visual Studio for its selection margin. The colour of this pattern is half
3275 // way between the chrome colour and the chrome highlight colour making a nice transition
3276 // between the window chrome and the content area. And it works in low colour depths.
3277 PRectangle rcPattern(0, 0, patternSize, patternSize);
3279 // Initialize default colours based on the chrome colour scheme. Typically the highlight is white.
3280 ColourAllocated colourFMFill = vs.selbar.allocated;
3281 ColourAllocated colourFMStripes = vs.selbarlight.allocated;
3283 if (!(vs.selbarlight.desired == ColourDesired(0xff, 0xff, 0xff))) {
3284 // User has chosen an unusual chrome colour scheme so just use the highlight edge colour.
3285 // (Typically, the highlight colour is white.)
3286 colourFMFill = vs.selbarlight.allocated;
3289 if (vs.foldmarginColourSet) {
3290 // override default fold margin colour
3291 colourFMFill = vs.foldmarginColour.allocated;
3293 if (vs.foldmarginHighlightColourSet) {
3294 // override default fold margin highlight colour
3295 colourFMStripes = vs.foldmarginHighlightColour.allocated;
3298 pixmapSelPattern->FillRectangle(rcPattern, colourFMFill);
3299 for (int y = 0; y < patternSize; y++) {
3300 for (int x = y % 2; x < patternSize; x+=2) {
3301 PRectangle rcPixel(x, y, x+1, y+1);
3302 pixmapSelPattern->FillRectangle(rcPixel, colourFMStripes);
3307 if (!pixmapIndentGuide->Initialised()) {
3308 // 1 extra pixel in height so can handle odd/even positions and so produce a continuous line
3309 pixmapIndentGuide->InitPixMap(1, vs.lineHeight + 1, surfaceWindow, wMain.GetID());
3310 pixmapIndentGuideHighlight->InitPixMap(1, vs.lineHeight + 1, surfaceWindow, wMain.GetID());
3311 PRectangle rcIG(0, 0, 1, vs.lineHeight);
3312 pixmapIndentGuide->FillRectangle(rcIG, vs.styles[STYLE_INDENTGUIDE].back.allocated);
3313 pixmapIndentGuide->PenColour(vs.styles[STYLE_INDENTGUIDE].fore.allocated);
3314 pixmapIndentGuideHighlight->FillRectangle(rcIG, vs.styles[STYLE_BRACELIGHT].back.allocated);
3315 pixmapIndentGuideHighlight->PenColour(vs.styles[STYLE_BRACELIGHT].fore.allocated);
3316 for (int stripe = 1; stripe < vs.lineHeight + 1; stripe += 2) {
3317 PRectangle rcPixel(0, stripe, 1, stripe+1);
3318 pixmapIndentGuide->FillRectangle(rcPixel, vs.styles[STYLE_INDENTGUIDE].fore.allocated);
3319 pixmapIndentGuideHighlight->FillRectangle(rcPixel, vs.styles[STYLE_BRACELIGHT].fore.allocated);
3323 if (bufferedDraw) {
3324 if (!pixmapLine->Initialised()) {
3325 PRectangle rcClient = GetClientRectangle();
3326 pixmapLine->InitPixMap(rcClient.Width(), vs.lineHeight,
3327 surfaceWindow, wMain.GetID());
3328 pixmapSelMargin->InitPixMap(vs.fixedColumnWidth,
3329 rcClient.Height(), surfaceWindow, wMain.GetID());
3334 void Editor::DrawCarets(Surface *surface, ViewStyle &vsDraw, int lineDoc, int xStart,
3335 PRectangle rcLine, LineLayout *ll, int subLine) {
3336 // When drag is active it is the only caret drawn
3337 bool drawDrag = posDrag.IsValid();
3338 if (hideSelection && !drawDrag)
3339 return;
3340 const int posLineStart = pdoc->LineStart(lineDoc);
3341 // For each selection draw
3342 for (size_t r=0; (r<sel.Count()) || drawDrag; r++) {
3343 const bool mainCaret = r == sel.Main();
3344 const SelectionPosition posCaret = (drawDrag ? posDrag : sel.Range(r).caret);
3345 const int offset = posCaret.Position() - posLineStart;
3346 const int spaceWidth = static_cast<int>(vsDraw.styles[ll->EndLineStyle()].spaceWidth);
3347 const int virtualOffset = posCaret.VirtualSpace() * spaceWidth;
3348 if (ll->InLine(offset, subLine) && offset <= ll->numCharsBeforeEOL) {
3349 int xposCaret = ll->positions[offset] + virtualOffset - ll->positions[ll->LineStart(subLine)];
3350 if (ll->wrapIndent != 0) {
3351 int lineStart = ll->LineStart(subLine);
3352 if (lineStart != 0) // Wrapped
3353 xposCaret += ll->wrapIndent;
3355 bool caretBlinkState = (caret.active && caret.on) || (!additionalCaretsBlink && !mainCaret);
3356 bool caretVisibleState = additionalCaretsVisible || mainCaret;
3357 if ((xposCaret >= 0) && (vsDraw.caretWidth > 0) && (vsDraw.caretStyle != CARETSTYLE_INVISIBLE) &&
3358 ((posDrag.IsValid()) || (caretBlinkState && caretVisibleState))) {
3359 bool caretAtEOF = false;
3360 bool caretAtEOL = false;
3361 bool drawBlockCaret = false;
3362 int widthOverstrikeCaret;
3363 int caretWidthOffset = 0;
3364 PRectangle rcCaret = rcLine;
3366 if (posCaret.Position() == pdoc->Length()) { // At end of document
3367 caretAtEOF = true;
3368 widthOverstrikeCaret = vsDraw.aveCharWidth;
3369 } else if ((posCaret.Position() - posLineStart) >= ll->numCharsInLine) { // At end of line
3370 caretAtEOL = true;
3371 widthOverstrikeCaret = vsDraw.aveCharWidth;
3372 } else {
3373 widthOverstrikeCaret = ll->positions[offset + 1] - ll->positions[offset];
3375 if (widthOverstrikeCaret < 3) // Make sure its visible
3376 widthOverstrikeCaret = 3;
3378 if (xposCaret > 0)
3379 caretWidthOffset = 1; // Move back so overlaps both character cells.
3380 xposCaret += xStart;
3381 if (posDrag.IsValid()) {
3382 /* Dragging text, use a line caret */
3383 rcCaret.left = xposCaret - caretWidthOffset;
3384 rcCaret.right = rcCaret.left + vsDraw.caretWidth;
3385 } else if (inOverstrike) {
3386 /* Overstrike (insert mode), use a modified bar caret */
3387 rcCaret.top = rcCaret.bottom - 2;
3388 rcCaret.left = xposCaret + 1;
3389 rcCaret.right = rcCaret.left + widthOverstrikeCaret - 1;
3390 } else if (vsDraw.caretStyle == CARETSTYLE_BLOCK) {
3391 /* Block caret */
3392 rcCaret.left = xposCaret;
3393 if (!caretAtEOL && !caretAtEOF && (ll->chars[offset] != '\t') && !(IsControlCharacter(ll->chars[offset]))) {
3394 drawBlockCaret = true;
3395 rcCaret.right = xposCaret + widthOverstrikeCaret;
3396 } else {
3397 rcCaret.right = xposCaret + vsDraw.aveCharWidth;
3399 } else {
3400 /* Line caret */
3401 rcCaret.left = xposCaret - caretWidthOffset;
3402 rcCaret.right = rcCaret.left + vsDraw.caretWidth;
3404 ColourAllocated caretColour = mainCaret ? vsDraw.caretcolour.allocated : vsDraw.additionalCaretColour.allocated;
3405 if (drawBlockCaret) {
3406 DrawBlockCaret(surface, vsDraw, ll, subLine, xStart, offset, posCaret.Position(), rcCaret, caretColour);
3407 } else {
3408 surface->FillRectangle(rcCaret, caretColour);
3412 if (drawDrag)
3413 break;
3417 void Editor::Paint(Surface *surfaceWindow, PRectangle rcArea) {
3418 //Platform::DebugPrintf("Paint:%1d (%3d,%3d) ... (%3d,%3d)\n",
3419 // paintingAllText, rcArea.left, rcArea.top, rcArea.right, rcArea.bottom);
3421 StyleToPositionInView(PositionAfterArea(rcArea));
3423 pixmapLine->Release();
3424 RefreshStyleData();
3425 RefreshPixMaps(surfaceWindow);
3427 PRectangle rcClient = GetClientRectangle();
3428 //Platform::DebugPrintf("Client: (%3d,%3d) ... (%3d,%3d) %d\n",
3429 // rcClient.left, rcClient.top, rcClient.right, rcClient.bottom);
3431 surfaceWindow->SetPalette(&palette, true);
3432 pixmapLine->SetPalette(&palette, !hasFocus);
3434 int screenLinePaintFirst = rcArea.top / vs.lineHeight;
3436 int xStart = vs.fixedColumnWidth - xOffset;
3437 int ypos = 0;
3438 if (!bufferedDraw)
3439 ypos += screenLinePaintFirst * vs.lineHeight;
3440 int yposScreen = screenLinePaintFirst * vs.lineHeight;
3442 bool paintAbandonedByStyling = paintState == paintAbandoned;
3443 if (needUpdateUI) {
3444 // Deselect palette by selecting a temporary palette
3445 Palette palTemp;
3446 surfaceWindow->SetPalette(&palTemp, true);
3448 NotifyUpdateUI();
3449 needUpdateUI = 0;
3451 RefreshStyleData();
3452 RefreshPixMaps(surfaceWindow);
3453 surfaceWindow->SetPalette(&palette, true);
3454 pixmapLine->SetPalette(&palette, !hasFocus);
3457 // Call priority lines wrap on a window of lines which are likely
3458 // to rendered with the following paint (that is wrap the visible
3459 // lines first).
3460 int startLineToWrap = cs.DocFromDisplay(topLine) - 5;
3461 if (startLineToWrap < 0)
3462 startLineToWrap = 0;
3463 if (WrapLines(false, startLineToWrap)) {
3464 // The wrapping process has changed the height of some lines so
3465 // abandon this paint for a complete repaint.
3466 if (AbandonPaint()) {
3467 return;
3469 RefreshPixMaps(surfaceWindow); // In case pixmaps invalidated by scrollbar change
3471 PLATFORM_ASSERT(pixmapSelPattern->Initialised());
3473 if (paintState != paintAbandoned) {
3474 PaintSelMargin(surfaceWindow, rcArea);
3476 PRectangle rcRightMargin = rcClient;
3477 rcRightMargin.left = rcRightMargin.right - vs.rightMarginWidth;
3478 if (rcArea.Intersects(rcRightMargin)) {
3479 surfaceWindow->FillRectangle(rcRightMargin, vs.styles[STYLE_DEFAULT].back.allocated);
3483 if (paintState == paintAbandoned) {
3484 // Either styling or NotifyUpdateUI noticed that painting is needed
3485 // outside the current painting rectangle
3486 //Platform::DebugPrintf("Abandoning paint\n");
3487 if (wrapState != eWrapNone) {
3488 if (paintAbandonedByStyling) {
3489 // Styling has spilled over a line end, such as occurs by starting a multiline
3490 // comment. The width of subsequent text may have changed, so rewrap.
3491 NeedWrapping(cs.DocFromDisplay(topLine));
3494 return;
3496 //Platform::DebugPrintf("start display %d, offset = %d\n", pdoc->Length(), xOffset);
3498 // Do the painting
3499 if (rcArea.right > vs.fixedColumnWidth) {
3501 Surface *surface = surfaceWindow;
3502 if (bufferedDraw) {
3503 surface = pixmapLine;
3504 PLATFORM_ASSERT(pixmapLine->Initialised());
3506 surface->SetUnicodeMode(IsUnicodeMode());
3507 surface->SetDBCSMode(CodePage());
3509 int visibleLine = topLine + screenLinePaintFirst;
3511 SelectionPosition posCaret = sel.RangeMain().caret;
3512 if (posDrag.IsValid())
3513 posCaret = posDrag;
3514 int lineCaret = pdoc->LineFromPosition(posCaret.Position());
3516 // Remove selection margin from drawing area so text will not be drawn
3517 // on it in unbuffered mode.
3518 PRectangle rcTextArea = rcClient;
3519 rcTextArea.left = vs.fixedColumnWidth;
3520 rcTextArea.right -= vs.rightMarginWidth;
3521 surfaceWindow->SetClip(rcTextArea);
3523 // Loop on visible lines
3524 //double durLayout = 0.0;
3525 //double durPaint = 0.0;
3526 //double durCopy = 0.0;
3527 //ElapsedTime etWhole;
3528 int lineDocPrevious = -1; // Used to avoid laying out one document line multiple times
3529 AutoLineLayout ll(llc, 0);
3530 while (visibleLine < cs.LinesDisplayed() && yposScreen < rcArea.bottom) {
3532 int lineDoc = cs.DocFromDisplay(visibleLine);
3533 // Only visible lines should be handled by the code within the loop
3534 PLATFORM_ASSERT(cs.GetVisible(lineDoc));
3535 int lineStartSet = cs.DisplayFromDoc(lineDoc);
3536 int subLine = visibleLine - lineStartSet;
3538 // Copy this line and its styles from the document into local arrays
3539 // and determine the x position at which each character starts.
3540 //ElapsedTime et;
3541 if (lineDoc != lineDocPrevious) {
3542 ll.Set(0);
3543 ll.Set(RetrieveLineLayout(lineDoc));
3544 LayoutLine(lineDoc, surface, vs, ll, wrapWidth);
3545 lineDocPrevious = lineDoc;
3547 //durLayout += et.Duration(true);
3549 if (ll) {
3550 ll->containsCaret = lineDoc == lineCaret;
3551 if (hideSelection) {
3552 ll->containsCaret = false;
3555 GetHotSpotRange(ll->hsStart, ll->hsEnd);
3557 PRectangle rcLine = rcClient;
3558 rcLine.top = ypos;
3559 rcLine.bottom = ypos + vs.lineHeight;
3561 bool bracesIgnoreStyle = false;
3562 if ((vs.braceHighlightIndicatorSet && (bracesMatchStyle == STYLE_BRACELIGHT)) ||
3563 (vs.braceBadLightIndicatorSet && (bracesMatchStyle == STYLE_BRACEBAD))) {
3564 bracesIgnoreStyle = true;
3566 Range rangeLine(pdoc->LineStart(lineDoc), pdoc->LineStart(lineDoc + 1));
3567 // Highlight the current braces if any
3568 ll->SetBracesHighlight(rangeLine, braces, static_cast<char>(bracesMatchStyle),
3569 highlightGuideColumn * vs.spaceWidth, bracesIgnoreStyle);
3571 // Draw the line
3572 DrawLine(surface, vs, lineDoc, visibleLine, xStart, rcLine, ll, subLine);
3573 //durPaint += et.Duration(true);
3575 // Restore the previous styles for the brace highlights in case layout is in cache.
3576 ll->RestoreBracesHighlight(rangeLine, braces, bracesIgnoreStyle);
3578 bool expanded = cs.GetExpanded(lineDoc);
3579 const int level = pdoc->GetLevel(lineDoc);
3580 const int levelNext = pdoc->GetLevel(lineDoc + 1);
3581 if ((level & SC_FOLDLEVELHEADERFLAG) &&
3582 ((level & SC_FOLDLEVELNUMBERMASK) < (levelNext & SC_FOLDLEVELNUMBERMASK))) {
3583 // Paint the line above the fold
3584 if ((expanded && (foldFlags & SC_FOLDFLAG_LINEBEFORE_EXPANDED))
3586 (!expanded && (foldFlags & SC_FOLDFLAG_LINEBEFORE_CONTRACTED))) {
3587 PRectangle rcFoldLine = rcLine;
3588 rcFoldLine.bottom = rcFoldLine.top + 1;
3589 surface->FillRectangle(rcFoldLine, vs.styles[STYLE_DEFAULT].fore.allocated);
3591 // Paint the line below the fold
3592 if ((expanded && (foldFlags & SC_FOLDFLAG_LINEAFTER_EXPANDED))
3594 (!expanded && (foldFlags & SC_FOLDFLAG_LINEAFTER_CONTRACTED))) {
3595 PRectangle rcFoldLine = rcLine;
3596 rcFoldLine.top = rcFoldLine.bottom - 1;
3597 surface->FillRectangle(rcFoldLine, vs.styles[STYLE_DEFAULT].fore.allocated);
3601 DrawCarets(surface, vs, lineDoc, xStart, rcLine, ll, subLine);
3603 if (bufferedDraw) {
3604 Point from(vs.fixedColumnWidth, 0);
3605 PRectangle rcCopyArea(vs.fixedColumnWidth, yposScreen,
3606 rcClient.right, yposScreen + vs.lineHeight);
3607 surfaceWindow->Copy(rcCopyArea, from, *pixmapLine);
3610 lineWidthMaxSeen = Platform::Maximum(
3611 lineWidthMaxSeen, ll->positions[ll->numCharsInLine]);
3612 //durCopy += et.Duration(true);
3615 if (!bufferedDraw) {
3616 ypos += vs.lineHeight;
3619 yposScreen += vs.lineHeight;
3620 visibleLine++;
3622 //gdk_flush();
3624 ll.Set(0);
3625 //if (durPaint < 0.00000001)
3626 // durPaint = 0.00000001;
3628 // Right column limit indicator
3629 PRectangle rcBeyondEOF = rcClient;
3630 rcBeyondEOF.left = vs.fixedColumnWidth;
3631 rcBeyondEOF.right = rcBeyondEOF.right;
3632 rcBeyondEOF.top = (cs.LinesDisplayed() - topLine) * vs.lineHeight;
3633 if (rcBeyondEOF.top < rcBeyondEOF.bottom) {
3634 surfaceWindow->FillRectangle(rcBeyondEOF, vs.styles[STYLE_DEFAULT].back.allocated);
3635 if (vs.edgeState == EDGE_LINE) {
3636 int edgeX = theEdge * vs.spaceWidth;
3637 rcBeyondEOF.left = edgeX + xStart;
3638 rcBeyondEOF.right = rcBeyondEOF.left + 1;
3639 surfaceWindow->FillRectangle(rcBeyondEOF, vs.edgecolour.allocated);
3642 //Platform::DebugPrintf(
3643 //"Layout:%9.6g Paint:%9.6g Ratio:%9.6g Copy:%9.6g Total:%9.6g\n",
3644 //durLayout, durPaint, durLayout / durPaint, durCopy, etWhole.Duration());
3645 NotifyPainted();
3649 // Space (3 space characters) between line numbers and text when printing.
3650 #define lineNumberPrintSpace " "
3652 ColourDesired InvertedLight(ColourDesired orig) {
3653 unsigned int r = orig.GetRed();
3654 unsigned int g = orig.GetGreen();
3655 unsigned int b = orig.GetBlue();
3656 unsigned int l = (r + g + b) / 3; // There is a better calculation for this that matches human eye
3657 unsigned int il = 0xff - l;
3658 if (l == 0)
3659 return ColourDesired(0xff, 0xff, 0xff);
3660 r = r * il / l;
3661 g = g * il / l;
3662 b = b * il / l;
3663 return ColourDesired(Platform::Minimum(r, 0xff), Platform::Minimum(g, 0xff), Platform::Minimum(b, 0xff));
3666 // This is mostly copied from the Paint method but with some things omitted
3667 // such as the margin markers, line numbers, selection and caret
3668 // Should be merged back into a combined Draw method.
3669 long Editor::FormatRange(bool draw, Sci_RangeToFormat *pfr) {
3670 if (!pfr)
3671 return 0;
3673 AutoSurface surface(pfr->hdc, this);
3674 if (!surface)
3675 return 0;
3676 AutoSurface surfaceMeasure(pfr->hdcTarget, this);
3677 if (!surfaceMeasure) {
3678 return 0;
3681 // Can't use measurements cached for screen
3682 posCache.Clear();
3684 ViewStyle vsPrint(vs);
3686 // Modify the view style for printing as do not normally want any of the transient features to be printed
3687 // Printing supports only the line number margin.
3688 int lineNumberIndex = -1;
3689 for (int margin = 0; margin < ViewStyle::margins; margin++) {
3690 if ((vsPrint.ms[margin].style == SC_MARGIN_NUMBER) && (vsPrint.ms[margin].width > 0)) {
3691 lineNumberIndex = margin;
3692 } else {
3693 vsPrint.ms[margin].width = 0;
3696 vsPrint.showMarkedLines = false;
3697 vsPrint.fixedColumnWidth = 0;
3698 vsPrint.zoomLevel = printMagnification;
3699 vsPrint.viewIndentationGuides = ivNone;
3700 // Don't show the selection when printing
3701 vsPrint.selbackset = false;
3702 vsPrint.selforeset = false;
3703 vsPrint.selAlpha = SC_ALPHA_NOALPHA;
3704 vsPrint.selAdditionalAlpha = SC_ALPHA_NOALPHA;
3705 vsPrint.whitespaceBackgroundSet = false;
3706 vsPrint.whitespaceForegroundSet = false;
3707 vsPrint.showCaretLineBackground = false;
3708 // Don't highlight matching braces using indicators
3709 vsPrint.braceHighlightIndicatorSet = false;
3710 vsPrint.braceBadLightIndicatorSet = false;
3712 // Set colours for printing according to users settings
3713 for (size_t sty = 0; sty < vsPrint.stylesSize; sty++) {
3714 if (printColourMode == SC_PRINT_INVERTLIGHT) {
3715 vsPrint.styles[sty].fore.desired = InvertedLight(vsPrint.styles[sty].fore.desired);
3716 vsPrint.styles[sty].back.desired = InvertedLight(vsPrint.styles[sty].back.desired);
3717 } else if (printColourMode == SC_PRINT_BLACKONWHITE) {
3718 vsPrint.styles[sty].fore.desired = ColourDesired(0, 0, 0);
3719 vsPrint.styles[sty].back.desired = ColourDesired(0xff, 0xff, 0xff);
3720 } else if (printColourMode == SC_PRINT_COLOURONWHITE) {
3721 vsPrint.styles[sty].back.desired = ColourDesired(0xff, 0xff, 0xff);
3722 } else if (printColourMode == SC_PRINT_COLOURONWHITEDEFAULTBG) {
3723 if (sty <= STYLE_DEFAULT) {
3724 vsPrint.styles[sty].back.desired = ColourDesired(0xff, 0xff, 0xff);
3728 // White background for the line numbers
3729 vsPrint.styles[STYLE_LINENUMBER].back.desired = ColourDesired(0xff, 0xff, 0xff);
3731 vsPrint.Refresh(*surfaceMeasure);
3732 // Determining width must hapen after fonts have been realised in Refresh
3733 int lineNumberWidth = 0;
3734 if (lineNumberIndex >= 0) {
3735 lineNumberWidth = surfaceMeasure->WidthText(vsPrint.styles[STYLE_LINENUMBER].font,
3736 "99999" lineNumberPrintSpace, 5 + istrlen(lineNumberPrintSpace));
3737 vsPrint.ms[lineNumberIndex].width = lineNumberWidth;
3738 vsPrint.Refresh(*surfaceMeasure); // Recalculate fixedColumnWidth
3740 // Ensure colours are set up
3741 vsPrint.RefreshColourPalette(palette, true);
3742 vsPrint.RefreshColourPalette(palette, false);
3744 int linePrintStart = pdoc->LineFromPosition(pfr->chrg.cpMin);
3745 int linePrintLast = linePrintStart + (pfr->rc.bottom - pfr->rc.top) / vsPrint.lineHeight - 1;
3746 if (linePrintLast < linePrintStart)
3747 linePrintLast = linePrintStart;
3748 int linePrintMax = pdoc->LineFromPosition(pfr->chrg.cpMax);
3749 if (linePrintLast > linePrintMax)
3750 linePrintLast = linePrintMax;
3751 //Platform::DebugPrintf("Formatting lines=[%0d,%0d,%0d] top=%0d bottom=%0d line=%0d %0d\n",
3752 // linePrintStart, linePrintLast, linePrintMax, pfr->rc.top, pfr->rc.bottom, vsPrint.lineHeight,
3753 // surfaceMeasure->Height(vsPrint.styles[STYLE_LINENUMBER].font));
3754 int endPosPrint = pdoc->Length();
3755 if (linePrintLast < pdoc->LinesTotal())
3756 endPosPrint = pdoc->LineStart(linePrintLast + 1);
3758 // Ensure we are styled to where we are formatting.
3759 pdoc->EnsureStyledTo(endPosPrint);
3761 int xStart = vsPrint.fixedColumnWidth + pfr->rc.left;
3762 int ypos = pfr->rc.top;
3764 int lineDoc = linePrintStart;
3766 int nPrintPos = pfr->chrg.cpMin;
3767 int visibleLine = 0;
3768 int widthPrint = pfr->rc.right - pfr->rc.left - vsPrint.fixedColumnWidth;
3769 if (printWrapState == eWrapNone)
3770 widthPrint = LineLayout::wrapWidthInfinite;
3772 while (lineDoc <= linePrintLast && ypos < pfr->rc.bottom) {
3774 // When printing, the hdc and hdcTarget may be the same, so
3775 // changing the state of surfaceMeasure may change the underlying
3776 // state of surface. Therefore, any cached state is discarded before
3777 // using each surface.
3778 surfaceMeasure->FlushCachedState();
3780 // Copy this line and its styles from the document into local arrays
3781 // and determine the x position at which each character starts.
3782 LineLayout ll(8000);
3783 LayoutLine(lineDoc, surfaceMeasure, vsPrint, &ll, widthPrint);
3785 ll.containsCaret = false;
3787 PRectangle rcLine;
3788 rcLine.left = pfr->rc.left;
3789 rcLine.top = ypos;
3790 rcLine.right = pfr->rc.right - 1;
3791 rcLine.bottom = ypos + vsPrint.lineHeight;
3793 // When document line is wrapped over multiple display lines, find where
3794 // to start printing from to ensure a particular position is on the first
3795 // line of the page.
3796 if (visibleLine == 0) {
3797 int startWithinLine = nPrintPos - pdoc->LineStart(lineDoc);
3798 for (int iwl = 0; iwl < ll.lines - 1; iwl++) {
3799 if (ll.LineStart(iwl) <= startWithinLine && ll.LineStart(iwl + 1) >= startWithinLine) {
3800 visibleLine = -iwl;
3804 if (ll.lines > 1 && startWithinLine >= ll.LineStart(ll.lines - 1)) {
3805 visibleLine = -(ll.lines - 1);
3809 if (draw && lineNumberWidth &&
3810 (ypos + vsPrint.lineHeight <= pfr->rc.bottom) &&
3811 (visibleLine >= 0)) {
3812 char number[100];
3813 sprintf(number, "%d" lineNumberPrintSpace, lineDoc + 1);
3814 PRectangle rcNumber = rcLine;
3815 rcNumber.right = rcNumber.left + lineNumberWidth;
3816 // Right justify
3817 rcNumber.left = rcNumber.right - surfaceMeasure->WidthText(
3818 vsPrint.styles[STYLE_LINENUMBER].font, number, istrlen(number));
3819 surface->FlushCachedState();
3820 surface->DrawTextNoClip(rcNumber, vsPrint.styles[STYLE_LINENUMBER].font,
3821 ypos + vsPrint.maxAscent, number, istrlen(number),
3822 vsPrint.styles[STYLE_LINENUMBER].fore.allocated,
3823 vsPrint.styles[STYLE_LINENUMBER].back.allocated);
3826 // Draw the line
3827 surface->FlushCachedState();
3829 for (int iwl = 0; iwl < ll.lines; iwl++) {
3830 if (ypos + vsPrint.lineHeight <= pfr->rc.bottom) {
3831 if (visibleLine >= 0) {
3832 if (draw) {
3833 rcLine.top = ypos;
3834 rcLine.bottom = ypos + vsPrint.lineHeight;
3835 DrawLine(surface, vsPrint, lineDoc, visibleLine, xStart, rcLine, &ll, iwl);
3837 ypos += vsPrint.lineHeight;
3839 visibleLine++;
3840 if (iwl == ll.lines - 1)
3841 nPrintPos = pdoc->LineStart(lineDoc + 1);
3842 else
3843 nPrintPos += ll.LineStart(iwl + 1) - ll.LineStart(iwl);
3847 ++lineDoc;
3850 // Clear cache so measurements are not used for screen
3851 posCache.Clear();
3853 return nPrintPos;
3856 int Editor::TextWidth(int style, const char *text) {
3857 RefreshStyleData();
3858 AutoSurface surface(this);
3859 if (surface) {
3860 return surface->WidthText(vs.styles[style].font, text, istrlen(text));
3861 } else {
3862 return 1;
3866 // Empty method is overridden on GTK+ to show / hide scrollbars
3867 void Editor::ReconfigureScrollBars() {}
3869 void Editor::SetScrollBars() {
3870 RefreshStyleData();
3872 int nMax = MaxScrollPos();
3873 int nPage = LinesOnScreen();
3874 bool modified = ModifyScrollBars(nMax + nPage - 1, nPage);
3875 if (modified) {
3876 DwellEnd(true);
3879 // TODO: ensure always showing as many lines as possible
3880 // May not be, if, for example, window made larger
3881 if (topLine > MaxScrollPos()) {
3882 SetTopLine(Platform::Clamp(topLine, 0, MaxScrollPos()));
3883 SetVerticalScrollPos();
3884 Redraw();
3886 if (modified) {
3887 if (!AbandonPaint())
3888 Redraw();
3890 //Platform::DebugPrintf("end max = %d page = %d\n", nMax, nPage);
3893 void Editor::ChangeSize() {
3894 DropGraphics();
3895 SetScrollBars();
3896 if (wrapState != eWrapNone) {
3897 PRectangle rcTextArea = GetClientRectangle();
3898 rcTextArea.left = vs.fixedColumnWidth;
3899 rcTextArea.right -= vs.rightMarginWidth;
3900 if (wrapWidth != rcTextArea.Width()) {
3901 NeedWrapping();
3902 Redraw();
3907 int Editor::InsertSpace(int position, unsigned int spaces) {
3908 if (spaces > 0) {
3909 std::string spaceText(spaces, ' ');
3910 pdoc->InsertString(position, spaceText.c_str(), spaces);
3911 position += spaces;
3913 return position;
3916 void Editor::AddChar(char ch) {
3917 char s[2];
3918 s[0] = ch;
3919 s[1] = '\0';
3920 AddCharUTF(s, 1);
3923 void Editor::FilterSelections() {
3924 if (!additionalSelectionTyping && (sel.Count() > 1)) {
3925 SelectionRange rangeOnly = sel.RangeMain();
3926 InvalidateSelection(rangeOnly, true);
3927 sel.SetSelection(rangeOnly);
3931 // AddCharUTF inserts an array of bytes which may or may not be in UTF-8.
3932 void Editor::AddCharUTF(char *s, unsigned int len, bool treatAsDBCS) {
3933 FilterSelections();
3935 UndoGroup ug(pdoc, (sel.Count() > 1) || !sel.Empty() || inOverstrike);
3936 for (size_t r=0; r<sel.Count(); r++) {
3937 if (!RangeContainsProtected(sel.Range(r).Start().Position(),
3938 sel.Range(r).End().Position())) {
3939 int positionInsert = sel.Range(r).Start().Position();
3940 if (!sel.Range(r).Empty()) {
3941 if (sel.Range(r).Length()) {
3942 pdoc->DeleteChars(positionInsert, sel.Range(r).Length());
3943 sel.Range(r).ClearVirtualSpace();
3944 } else {
3945 // Range is all virtual so collapse to start of virtual space
3946 sel.Range(r).MinimizeVirtualSpace();
3948 } else if (inOverstrike) {
3949 if (positionInsert < pdoc->Length()) {
3950 if (!IsEOLChar(pdoc->CharAt(positionInsert))) {
3951 pdoc->DelChar(positionInsert);
3952 sel.Range(r).ClearVirtualSpace();
3956 positionInsert = InsertSpace(positionInsert, sel.Range(r).caret.VirtualSpace());
3957 if (pdoc->InsertString(positionInsert, s, len)) {
3958 sel.Range(r).caret.SetPosition(positionInsert + len);
3959 sel.Range(r).anchor.SetPosition(positionInsert + len);
3961 sel.Range(r).ClearVirtualSpace();
3962 // If in wrap mode rewrap current line so EnsureCaretVisible has accurate information
3963 if (wrapState != eWrapNone) {
3964 AutoSurface surface(this);
3965 if (surface) {
3966 if (WrapOneLine(surface, pdoc->LineFromPosition(positionInsert))) {
3967 SetScrollBars();
3968 SetVerticalScrollPos();
3969 Redraw();
3976 if (wrapState != eWrapNone) {
3977 SetScrollBars();
3979 ThinRectangularRange();
3980 // If in wrap mode rewrap current line so EnsureCaretVisible has accurate information
3981 EnsureCaretVisible();
3982 // Avoid blinking during rapid typing:
3983 ShowCaretAtCurrentPosition();
3984 if ((caretSticky == SC_CARETSTICKY_OFF) ||
3985 ((caretSticky == SC_CARETSTICKY_WHITESPACE) && !IsAllSpacesOrTabs(s, len))) {
3986 SetLastXChosen();
3989 if (treatAsDBCS) {
3990 NotifyChar((static_cast<unsigned char>(s[0]) << 8) |
3991 static_cast<unsigned char>(s[1]));
3992 } else {
3993 int byte = static_cast<unsigned char>(s[0]);
3994 if ((byte < 0xC0) || (1 == len)) {
3995 // Handles UTF-8 characters between 0x01 and 0x7F and single byte
3996 // characters when not in UTF-8 mode.
3997 // Also treats \0 and naked trail bytes 0x80 to 0xBF as valid
3998 // characters representing themselves.
3999 } else {
4000 // Unroll 1 to 3 byte UTF-8 sequences. See reference data at:
4001 // http://www.cl.cam.ac.uk/~mgk25/unicode.html
4002 // http://www.cl.cam.ac.uk/~mgk25/ucs/examples/UTF-8-test.txt
4003 if (byte < 0xE0) {
4004 int byte2 = static_cast<unsigned char>(s[1]);
4005 if ((byte2 & 0xC0) == 0x80) {
4006 // Two-byte-character lead-byte followed by a trail-byte.
4007 byte = (((byte & 0x1F) << 6) | (byte2 & 0x3F));
4009 // A two-byte-character lead-byte not followed by trail-byte
4010 // represents itself.
4011 } else if (byte < 0xF0) {
4012 int byte2 = static_cast<unsigned char>(s[1]);
4013 int byte3 = static_cast<unsigned char>(s[2]);
4014 if (((byte2 & 0xC0) == 0x80) && ((byte3 & 0xC0) == 0x80)) {
4015 // Three-byte-character lead byte followed by two trail bytes.
4016 byte = (((byte & 0x0F) << 12) | ((byte2 & 0x3F) << 6) |
4017 (byte3 & 0x3F));
4019 // A three-byte-character lead-byte not followed by two trail-bytes
4020 // represents itself.
4023 NotifyChar(byte);
4026 if (recordingMacro) {
4027 NotifyMacroRecord(SCI_REPLACESEL, 0, reinterpret_cast<sptr_t>(s));
4031 void Editor::InsertPaste(SelectionPosition selStart, const char *text, int len) {
4032 if (multiPasteMode == SC_MULTIPASTE_ONCE) {
4033 selStart = SelectionPosition(InsertSpace(selStart.Position(), selStart.VirtualSpace()));
4034 if (pdoc->InsertString(selStart.Position(), text, len)) {
4035 SetEmptySelection(selStart.Position() + len);
4037 } else {
4038 // SC_MULTIPASTE_EACH
4039 for (size_t r=0; r<sel.Count(); r++) {
4040 if (!RangeContainsProtected(sel.Range(r).Start().Position(),
4041 sel.Range(r).End().Position())) {
4042 int positionInsert = sel.Range(r).Start().Position();
4043 if (!sel.Range(r).Empty()) {
4044 if (sel.Range(r).Length()) {
4045 pdoc->DeleteChars(positionInsert, sel.Range(r).Length());
4046 sel.Range(r).ClearVirtualSpace();
4047 } else {
4048 // Range is all virtual so collapse to start of virtual space
4049 sel.Range(r).MinimizeVirtualSpace();
4052 positionInsert = InsertSpace(positionInsert, sel.Range(r).caret.VirtualSpace());
4053 if (pdoc->InsertString(positionInsert, text, len)) {
4054 sel.Range(r).caret.SetPosition(positionInsert + len);
4055 sel.Range(r).anchor.SetPosition(positionInsert + len);
4057 sel.Range(r).ClearVirtualSpace();
4063 void Editor::ClearSelection(bool retainMultipleSelections) {
4064 if (!sel.IsRectangular() && !retainMultipleSelections)
4065 FilterSelections();
4066 UndoGroup ug(pdoc);
4067 for (size_t r=0; r<sel.Count(); r++) {
4068 if (!sel.Range(r).Empty()) {
4069 if (!RangeContainsProtected(sel.Range(r).Start().Position(),
4070 sel.Range(r).End().Position())) {
4071 pdoc->DeleteChars(sel.Range(r).Start().Position(),
4072 sel.Range(r).Length());
4073 sel.Range(r) = sel.Range(r).Start();
4077 ThinRectangularRange();
4078 sel.RemoveDuplicates();
4079 ClaimSelection();
4082 void Editor::ClearAll() {
4084 UndoGroup ug(pdoc);
4085 if (0 != pdoc->Length()) {
4086 pdoc->DeleteChars(0, pdoc->Length());
4088 if (!pdoc->IsReadOnly()) {
4089 cs.Clear();
4090 pdoc->AnnotationClearAll();
4091 pdoc->MarginClearAll();
4094 sel.Clear();
4095 SetTopLine(0);
4096 SetVerticalScrollPos();
4097 InvalidateStyleRedraw();
4100 void Editor::ClearDocumentStyle() {
4101 Decoration *deco = pdoc->decorations.root;
4102 while (deco) {
4103 // Save next in case deco deleted
4104 Decoration *decoNext = deco->next;
4105 if (deco->indicator < INDIC_CONTAINER) {
4106 pdoc->decorations.SetCurrentIndicator(deco->indicator);
4107 pdoc->DecorationFillRange(0, 0, pdoc->Length());
4109 deco = decoNext;
4111 pdoc->StartStyling(0, '\377');
4112 pdoc->SetStyleFor(pdoc->Length(), 0);
4113 cs.ShowAll();
4114 pdoc->ClearLevels();
4117 void Editor::CopyAllowLine() {
4118 SelectionText selectedText;
4119 CopySelectionRange(&selectedText, true);
4120 CopyToClipboard(selectedText);
4123 void Editor::Cut() {
4124 pdoc->CheckReadOnly();
4125 if (!pdoc->IsReadOnly() && !SelectionContainsProtected()) {
4126 Copy();
4127 ClearSelection();
4131 void Editor::PasteRectangular(SelectionPosition pos, const char *ptr, int len) {
4132 if (pdoc->IsReadOnly() || SelectionContainsProtected()) {
4133 return;
4135 sel.Clear();
4136 sel.RangeMain() = SelectionRange(pos);
4137 int line = pdoc->LineFromPosition(sel.MainCaret());
4138 UndoGroup ug(pdoc);
4139 sel.RangeMain().caret = SelectionPosition(
4140 InsertSpace(sel.RangeMain().caret.Position(), sel.RangeMain().caret.VirtualSpace()));
4141 int xInsert = XFromPosition(sel.RangeMain().caret);
4142 bool prevCr = false;
4143 while ((len > 0) && IsEOLChar(ptr[len-1]))
4144 len--;
4145 for (int i = 0; i < len; i++) {
4146 if (IsEOLChar(ptr[i])) {
4147 if ((ptr[i] == '\r') || (!prevCr))
4148 line++;
4149 if (line >= pdoc->LinesTotal()) {
4150 if (pdoc->eolMode != SC_EOL_LF)
4151 pdoc->InsertChar(pdoc->Length(), '\r');
4152 if (pdoc->eolMode != SC_EOL_CR)
4153 pdoc->InsertChar(pdoc->Length(), '\n');
4155 // Pad the end of lines with spaces if required
4156 sel.RangeMain().caret.SetPosition(PositionFromLineX(line, xInsert));
4157 if ((XFromPosition(sel.MainCaret()) < xInsert) && (i + 1 < len)) {
4158 while (XFromPosition(sel.MainCaret()) < xInsert) {
4159 pdoc->InsertChar(sel.MainCaret(), ' ');
4160 sel.RangeMain().caret.Add(1);
4163 prevCr = ptr[i] == '\r';
4164 } else {
4165 pdoc->InsertString(sel.MainCaret(), ptr + i, 1);
4166 sel.RangeMain().caret.Add(1);
4167 prevCr = false;
4170 SetEmptySelection(pos);
4173 bool Editor::CanPaste() {
4174 return !pdoc->IsReadOnly() && !SelectionContainsProtected();
4177 void Editor::Clear() {
4178 // If multiple selections, don't delete EOLS
4179 if (sel.Empty()) {
4180 UndoGroup ug(pdoc, sel.Count() > 1);
4181 for (size_t r=0; r<sel.Count(); r++) {
4182 if (!RangeContainsProtected(sel.Range(r).caret.Position(), sel.Range(r).caret.Position() + 1)) {
4183 if (sel.Range(r).Start().VirtualSpace()) {
4184 if (sel.Range(r).anchor < sel.Range(r).caret)
4185 sel.Range(r) = SelectionPosition(InsertSpace(sel.Range(r).anchor.Position(), sel.Range(r).anchor.VirtualSpace()));
4186 else
4187 sel.Range(r) = SelectionPosition(InsertSpace(sel.Range(r).caret.Position(), sel.Range(r).caret.VirtualSpace()));
4189 if ((sel.Count() == 1) || !IsEOLChar(pdoc->CharAt(sel.Range(r).caret.Position()))) {
4190 pdoc->DelChar(sel.Range(r).caret.Position());
4191 sel.Range(r).ClearVirtualSpace();
4192 } // else multiple selection so don't eat line ends
4193 } else {
4194 sel.Range(r).ClearVirtualSpace();
4197 } else {
4198 ClearSelection();
4200 sel.RemoveDuplicates();
4203 void Editor::SelectAll() {
4204 sel.Clear();
4205 SetSelection(0, pdoc->Length());
4206 Redraw();
4209 void Editor::Undo() {
4210 if (pdoc->CanUndo()) {
4211 InvalidateCaret();
4212 int newPos = pdoc->Undo();
4213 if (newPos >= 0)
4214 SetEmptySelection(newPos);
4215 EnsureCaretVisible();
4219 void Editor::Redo() {
4220 if (pdoc->CanRedo()) {
4221 int newPos = pdoc->Redo();
4222 if (newPos >= 0)
4223 SetEmptySelection(newPos);
4224 EnsureCaretVisible();
4228 void Editor::DelChar() {
4229 if (!RangeContainsProtected(sel.MainCaret(), sel.MainCaret() + 1)) {
4230 pdoc->DelChar(sel.MainCaret());
4232 // Avoid blinking during rapid typing:
4233 ShowCaretAtCurrentPosition();
4236 void Editor::DelCharBack(bool allowLineStartDeletion) {
4237 if (!sel.IsRectangular())
4238 FilterSelections();
4239 if (sel.IsRectangular())
4240 allowLineStartDeletion = false;
4241 UndoGroup ug(pdoc, (sel.Count() > 1) || !sel.Empty());
4242 if (sel.Empty()) {
4243 for (size_t r=0; r<sel.Count(); r++) {
4244 if (!RangeContainsProtected(sel.Range(r).caret.Position(), sel.Range(r).caret.Position() + 1)) {
4245 if (sel.Range(r).caret.VirtualSpace()) {
4246 sel.Range(r).caret.SetVirtualSpace(sel.Range(r).caret.VirtualSpace() - 1);
4247 sel.Range(r).anchor.SetVirtualSpace(sel.Range(r).caret.VirtualSpace());
4248 } else {
4249 int lineCurrentPos = pdoc->LineFromPosition(sel.Range(r).caret.Position());
4250 if (allowLineStartDeletion || (pdoc->LineStart(lineCurrentPos) != sel.Range(r).caret.Position())) {
4251 if (pdoc->GetColumn(sel.Range(r).caret.Position()) <= pdoc->GetLineIndentation(lineCurrentPos) &&
4252 pdoc->GetColumn(sel.Range(r).caret.Position()) > 0 && pdoc->backspaceUnindents) {
4253 UndoGroup ugInner(pdoc, !ug.Needed());
4254 int indentation = pdoc->GetLineIndentation(lineCurrentPos);
4255 int indentationStep = pdoc->IndentSize();
4256 if (indentation % indentationStep == 0) {
4257 pdoc->SetLineIndentation(lineCurrentPos, indentation - indentationStep);
4258 } else {
4259 pdoc->SetLineIndentation(lineCurrentPos, indentation - (indentation % indentationStep));
4261 // SetEmptySelection
4262 sel.Range(r) = SelectionRange(pdoc->GetLineIndentPosition(lineCurrentPos),
4263 pdoc->GetLineIndentPosition(lineCurrentPos));
4264 } else {
4265 pdoc->DelCharBack(sel.Range(r).caret.Position());
4269 } else {
4270 sel.Range(r).ClearVirtualSpace();
4273 } else {
4274 ClearSelection();
4276 sel.RemoveDuplicates();
4277 // Avoid blinking during rapid typing:
4278 ShowCaretAtCurrentPosition();
4281 void Editor::NotifyFocus(bool) {}
4283 void Editor::NotifyStyleToNeeded(int endStyleNeeded) {
4284 SCNotification scn = {0};
4285 scn.nmhdr.code = SCN_STYLENEEDED;
4286 scn.position = endStyleNeeded;
4287 NotifyParent(scn);
4290 void Editor::NotifyStyleNeeded(Document *, void *, int endStyleNeeded) {
4291 NotifyStyleToNeeded(endStyleNeeded);
4294 void Editor::NotifyLexerChanged(Document *, void *) {
4297 void Editor::NotifyErrorOccurred(Document *, void *, int status) {
4298 errorStatus = status;
4301 void Editor::NotifyChar(int ch) {
4302 SCNotification scn = {0};
4303 scn.nmhdr.code = SCN_CHARADDED;
4304 scn.ch = ch;
4305 NotifyParent(scn);
4308 void Editor::NotifySavePoint(bool isSavePoint) {
4309 SCNotification scn = {0};
4310 if (isSavePoint) {
4311 scn.nmhdr.code = SCN_SAVEPOINTREACHED;
4312 } else {
4313 scn.nmhdr.code = SCN_SAVEPOINTLEFT;
4315 NotifyParent(scn);
4318 void Editor::NotifyModifyAttempt() {
4319 SCNotification scn = {0};
4320 scn.nmhdr.code = SCN_MODIFYATTEMPTRO;
4321 NotifyParent(scn);
4324 void Editor::NotifyDoubleClick(Point pt, bool shift, bool ctrl, bool alt) {
4325 SCNotification scn = {0};
4326 scn.nmhdr.code = SCN_DOUBLECLICK;
4327 scn.line = LineFromLocation(pt);
4328 scn.position = PositionFromLocation(pt, true);
4329 scn.modifiers = (shift ? SCI_SHIFT : 0) | (ctrl ? SCI_CTRL : 0) |
4330 (alt ? SCI_ALT : 0);
4331 NotifyParent(scn);
4334 void Editor::NotifyHotSpotDoubleClicked(int position, bool shift, bool ctrl, bool alt) {
4335 SCNotification scn = {0};
4336 scn.nmhdr.code = SCN_HOTSPOTDOUBLECLICK;
4337 scn.position = position;
4338 scn.modifiers = (shift ? SCI_SHIFT : 0) | (ctrl ? SCI_CTRL : 0) |
4339 (alt ? SCI_ALT : 0);
4340 NotifyParent(scn);
4343 void Editor::NotifyHotSpotClicked(int position, bool shift, bool ctrl, bool alt) {
4344 SCNotification scn = {0};
4345 scn.nmhdr.code = SCN_HOTSPOTCLICK;
4346 scn.position = position;
4347 scn.modifiers = (shift ? SCI_SHIFT : 0) | (ctrl ? SCI_CTRL : 0) |
4348 (alt ? SCI_ALT : 0);
4349 NotifyParent(scn);
4352 void Editor::NotifyHotSpotReleaseClick(int position, bool shift, bool ctrl, bool alt) {
4353 SCNotification scn = {0};
4354 scn.nmhdr.code = SCN_HOTSPOTRELEASECLICK;
4355 scn.position = position;
4356 scn.modifiers = (shift ? SCI_SHIFT : 0) | (ctrl ? SCI_CTRL : 0) |
4357 (alt ? SCI_ALT : 0);
4358 NotifyParent(scn);
4361 void Editor::NotifyUpdateUI() {
4362 SCNotification scn = {0};
4363 scn.nmhdr.code = SCN_UPDATEUI;
4364 scn.updated = needUpdateUI;
4365 NotifyParent(scn);
4368 void Editor::NotifyPainted() {
4369 SCNotification scn = {0};
4370 scn.nmhdr.code = SCN_PAINTED;
4371 NotifyParent(scn);
4374 void Editor::NotifyIndicatorClick(bool click, int position, bool shift, bool ctrl, bool alt) {
4375 int mask = pdoc->decorations.AllOnFor(position);
4376 if ((click && mask) || pdoc->decorations.clickNotified) {
4377 SCNotification scn = {0};
4378 pdoc->decorations.clickNotified = click;
4379 scn.nmhdr.code = click ? SCN_INDICATORCLICK : SCN_INDICATORRELEASE;
4380 scn.modifiers = (shift ? SCI_SHIFT : 0) | (ctrl ? SCI_CTRL : 0) | (alt ? SCI_ALT : 0);
4381 scn.position = position;
4382 NotifyParent(scn);
4386 bool Editor::NotifyMarginClick(Point pt, bool shift, bool ctrl, bool alt) {
4387 int marginClicked = -1;
4388 int x = 0;
4389 for (int margin = 0; margin < ViewStyle::margins; margin++) {
4390 if ((pt.x > x) && (pt.x < x + vs.ms[margin].width))
4391 marginClicked = margin;
4392 x += vs.ms[margin].width;
4394 if ((marginClicked >= 0) && vs.ms[marginClicked].sensitive) {
4395 SCNotification scn = {0};
4396 scn.nmhdr.code = SCN_MARGINCLICK;
4397 scn.modifiers = (shift ? SCI_SHIFT : 0) | (ctrl ? SCI_CTRL : 0) |
4398 (alt ? SCI_ALT : 0);
4399 scn.position = pdoc->LineStart(LineFromLocation(pt));
4400 scn.margin = marginClicked;
4401 NotifyParent(scn);
4402 return true;
4403 } else {
4404 return false;
4408 void Editor::NotifyNeedShown(int pos, int len) {
4409 SCNotification scn = {0};
4410 scn.nmhdr.code = SCN_NEEDSHOWN;
4411 scn.position = pos;
4412 scn.length = len;
4413 NotifyParent(scn);
4416 void Editor::NotifyDwelling(Point pt, bool state) {
4417 SCNotification scn = {0};
4418 scn.nmhdr.code = state ? SCN_DWELLSTART : SCN_DWELLEND;
4419 scn.position = PositionFromLocation(pt, true);
4420 scn.x = pt.x;
4421 scn.y = pt.y;
4422 NotifyParent(scn);
4425 void Editor::NotifyZoom() {
4426 SCNotification scn = {0};
4427 scn.nmhdr.code = SCN_ZOOM;
4428 NotifyParent(scn);
4431 // Notifications from document
4432 void Editor::NotifyModifyAttempt(Document *, void *) {
4433 //Platform::DebugPrintf("** Modify Attempt\n");
4434 NotifyModifyAttempt();
4437 void Editor::NotifySavePoint(Document *, void *, bool atSavePoint) {
4438 //Platform::DebugPrintf("** Save Point %s\n", atSavePoint ? "On" : "Off");
4439 NotifySavePoint(atSavePoint);
4442 void Editor::CheckModificationForWrap(DocModification mh) {
4443 if (mh.modificationType & (SC_MOD_INSERTTEXT | SC_MOD_DELETETEXT)) {
4444 llc.Invalidate(LineLayout::llCheckTextAndStyle);
4445 if (wrapState != eWrapNone) {
4446 int lineDoc = pdoc->LineFromPosition(mh.position);
4447 int lines = Platform::Maximum(0, mh.linesAdded);
4448 NeedWrapping(lineDoc, lineDoc + lines + 1);
4450 // Fix up annotation heights
4451 int lineDoc = pdoc->LineFromPosition(mh.position);
4452 int lines = Platform::Maximum(0, mh.linesAdded);
4453 SetAnnotationHeights(lineDoc, lineDoc + lines + 2);
4457 // Move a position so it is still after the same character as before the insertion.
4458 static inline int MovePositionForInsertion(int position, int startInsertion, int length) {
4459 if (position > startInsertion) {
4460 return position + length;
4462 return position;
4465 // Move a position so it is still after the same character as before the deletion if that
4466 // character is still present else after the previous surviving character.
4467 static inline int MovePositionForDeletion(int position, int startDeletion, int length) {
4468 if (position > startDeletion) {
4469 int endDeletion = startDeletion + length;
4470 if (position > endDeletion) {
4471 return position - length;
4472 } else {
4473 return startDeletion;
4475 } else {
4476 return position;
4480 void Editor::NotifyModified(Document *, DocModification mh, void *) {
4481 ContainerNeedsUpdate(SC_UPDATE_CONTENT);
4482 if (paintState == painting) {
4483 CheckForChangeOutsidePaint(Range(mh.position, mh.position + mh.length));
4485 if (mh.modificationType & SC_MOD_CHANGELINESTATE) {
4486 if (paintState == painting) {
4487 CheckForChangeOutsidePaint(
4488 Range(pdoc->LineStart(mh.line), pdoc->LineStart(mh.line + 1)));
4489 } else {
4490 // Could check that change is before last visible line.
4491 Redraw();
4494 if (mh.modificationType & SC_MOD_LEXERSTATE) {
4495 if (paintState == painting) {
4496 CheckForChangeOutsidePaint(
4497 Range(mh.position, mh.position + mh.length));
4498 } else {
4499 Redraw();
4502 if (mh.modificationType & (SC_MOD_CHANGESTYLE | SC_MOD_CHANGEINDICATOR)) {
4503 if (mh.modificationType & SC_MOD_CHANGESTYLE) {
4504 pdoc->IncrementStyleClock();
4506 if (paintState == notPainting) {
4507 if (mh.position < pdoc->LineStart(topLine)) {
4508 // Styling performed before this view
4509 Redraw();
4510 } else {
4511 InvalidateRange(mh.position, mh.position + mh.length);
4514 if (mh.modificationType & SC_MOD_CHANGESTYLE) {
4515 llc.Invalidate(LineLayout::llCheckTextAndStyle);
4517 } else {
4518 // Move selection and brace highlights
4519 if (mh.modificationType & SC_MOD_INSERTTEXT) {
4520 sel.MovePositions(true, mh.position, mh.length);
4521 braces[0] = MovePositionForInsertion(braces[0], mh.position, mh.length);
4522 braces[1] = MovePositionForInsertion(braces[1], mh.position, mh.length);
4523 } else if (mh.modificationType & SC_MOD_DELETETEXT) {
4524 sel.MovePositions(false, mh.position, mh.length);
4525 braces[0] = MovePositionForDeletion(braces[0], mh.position, mh.length);
4526 braces[1] = MovePositionForDeletion(braces[1], mh.position, mh.length);
4528 if (cs.LinesDisplayed() < cs.LinesInDoc()) {
4529 // Some lines are hidden so may need shown.
4530 // TODO: check if the modified area is hidden.
4531 if (mh.modificationType & SC_MOD_BEFOREINSERT) {
4532 int lineOfPos = pdoc->LineFromPosition(mh.position);
4533 bool insertingNewLine = false;
4534 for (int i=0; i < mh.length; i++) {
4535 if ((mh.text[i] == '\n') || (mh.text[i] == '\r'))
4536 insertingNewLine = true;
4538 if (insertingNewLine && (mh.position != pdoc->LineStart(lineOfPos)))
4539 NotifyNeedShown(mh.position, pdoc->LineStart(lineOfPos+1) - mh.position);
4540 else
4541 NotifyNeedShown(mh.position, 0);
4542 } else if (mh.modificationType & SC_MOD_BEFOREDELETE) {
4543 NotifyNeedShown(mh.position, mh.length);
4546 if (mh.linesAdded != 0) {
4547 // Update contraction state for inserted and removed lines
4548 // lineOfPos should be calculated in context of state before modification, shouldn't it
4549 int lineOfPos = pdoc->LineFromPosition(mh.position);
4550 if (mh.linesAdded > 0) {
4551 cs.InsertLines(lineOfPos, mh.linesAdded);
4552 } else {
4553 cs.DeleteLines(lineOfPos, -mh.linesAdded);
4556 if (mh.modificationType & SC_MOD_CHANGEANNOTATION) {
4557 int lineDoc = pdoc->LineFromPosition(mh.position);
4558 if (vs.annotationVisible) {
4559 cs.SetHeight(lineDoc, cs.GetHeight(lineDoc) + mh.annotationLinesAdded);
4560 Redraw();
4563 CheckModificationForWrap(mh);
4564 if (mh.linesAdded != 0) {
4565 // Avoid scrolling of display if change before current display
4566 if (mh.position < posTopLine && !CanDeferToLastStep(mh)) {
4567 int newTop = Platform::Clamp(topLine + mh.linesAdded, 0, MaxScrollPos());
4568 if (newTop != topLine) {
4569 SetTopLine(newTop);
4570 SetVerticalScrollPos();
4574 //Platform::DebugPrintf("** %x Doc Changed\n", this);
4575 // TODO: could invalidate from mh.startModification to end of screen
4576 //InvalidateRange(mh.position, mh.position + mh.length);
4577 if (paintState == notPainting && !CanDeferToLastStep(mh)) {
4578 QueueStyling(pdoc->Length());
4579 Redraw();
4581 } else {
4582 //Platform::DebugPrintf("** %x Line Changed %d .. %d\n", this,
4583 // mh.position, mh.position + mh.length);
4584 if (paintState == notPainting && mh.length && !CanEliminate(mh)) {
4585 QueueStyling(mh.position + mh.length);
4586 InvalidateRange(mh.position, mh.position + mh.length);
4591 if (mh.linesAdded != 0 && !CanDeferToLastStep(mh)) {
4592 SetScrollBars();
4595 if ((mh.modificationType & SC_MOD_CHANGEMARKER) || (mh.modificationType & SC_MOD_CHANGEMARGIN)) {
4596 if ((paintState == notPainting) || !PaintContainsMargin()) {
4597 if (mh.modificationType & SC_MOD_CHANGEFOLD) {
4598 // Fold changes can affect the drawing of following lines so redraw whole margin
4599 RedrawSelMargin(mh.line-1, true);
4600 } else {
4601 RedrawSelMargin(mh.line);
4606 // NOW pay the piper WRT "deferred" visual updates
4607 if (IsLastStep(mh)) {
4608 SetScrollBars();
4609 Redraw();
4612 // If client wants to see this modification
4613 if (mh.modificationType & modEventMask) {
4614 if ((mh.modificationType & (SC_MOD_CHANGESTYLE | SC_MOD_CHANGEINDICATOR)) == 0) {
4615 // Real modification made to text of document.
4616 NotifyChange(); // Send EN_CHANGE
4619 SCNotification scn = {0};
4620 scn.nmhdr.code = SCN_MODIFIED;
4621 scn.position = mh.position;
4622 scn.modificationType = mh.modificationType;
4623 scn.text = mh.text;
4624 scn.length = mh.length;
4625 scn.linesAdded = mh.linesAdded;
4626 scn.line = mh.line;
4627 scn.foldLevelNow = mh.foldLevelNow;
4628 scn.foldLevelPrev = mh.foldLevelPrev;
4629 scn.token = mh.token;
4630 scn.annotationLinesAdded = mh.annotationLinesAdded;
4631 NotifyParent(scn);
4635 void Editor::NotifyDeleted(Document *, void *) {
4636 /* Do nothing */
4639 void Editor::NotifyMacroRecord(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
4641 // Enumerates all macroable messages
4642 switch (iMessage) {
4643 case SCI_CUT:
4644 case SCI_COPY:
4645 case SCI_PASTE:
4646 case SCI_CLEAR:
4647 case SCI_REPLACESEL:
4648 case SCI_ADDTEXT:
4649 case SCI_INSERTTEXT:
4650 case SCI_APPENDTEXT:
4651 case SCI_CLEARALL:
4652 case SCI_SELECTALL:
4653 case SCI_GOTOLINE:
4654 case SCI_GOTOPOS:
4655 case SCI_SEARCHANCHOR:
4656 case SCI_SEARCHNEXT:
4657 case SCI_SEARCHPREV:
4658 case SCI_LINEDOWN:
4659 case SCI_LINEDOWNEXTEND:
4660 case SCI_PARADOWN:
4661 case SCI_PARADOWNEXTEND:
4662 case SCI_LINEUP:
4663 case SCI_LINEUPEXTEND:
4664 case SCI_PARAUP:
4665 case SCI_PARAUPEXTEND:
4666 case SCI_CHARLEFT:
4667 case SCI_CHARLEFTEXTEND:
4668 case SCI_CHARRIGHT:
4669 case SCI_CHARRIGHTEXTEND:
4670 case SCI_WORDLEFT:
4671 case SCI_WORDLEFTEXTEND:
4672 case SCI_WORDRIGHT:
4673 case SCI_WORDRIGHTEXTEND:
4674 case SCI_WORDPARTLEFT:
4675 case SCI_WORDPARTLEFTEXTEND:
4676 case SCI_WORDPARTRIGHT:
4677 case SCI_WORDPARTRIGHTEXTEND:
4678 case SCI_WORDLEFTEND:
4679 case SCI_WORDLEFTENDEXTEND:
4680 case SCI_WORDRIGHTEND:
4681 case SCI_WORDRIGHTENDEXTEND:
4682 case SCI_HOME:
4683 case SCI_HOMEEXTEND:
4684 case SCI_LINEEND:
4685 case SCI_LINEENDEXTEND:
4686 case SCI_HOMEWRAP:
4687 case SCI_HOMEWRAPEXTEND:
4688 case SCI_LINEENDWRAP:
4689 case SCI_LINEENDWRAPEXTEND:
4690 case SCI_DOCUMENTSTART:
4691 case SCI_DOCUMENTSTARTEXTEND:
4692 case SCI_DOCUMENTEND:
4693 case SCI_DOCUMENTENDEXTEND:
4694 case SCI_STUTTEREDPAGEUP:
4695 case SCI_STUTTEREDPAGEUPEXTEND:
4696 case SCI_STUTTEREDPAGEDOWN:
4697 case SCI_STUTTEREDPAGEDOWNEXTEND:
4698 case SCI_PAGEUP:
4699 case SCI_PAGEUPEXTEND:
4700 case SCI_PAGEDOWN:
4701 case SCI_PAGEDOWNEXTEND:
4702 case SCI_EDITTOGGLEOVERTYPE:
4703 case SCI_CANCEL:
4704 case SCI_DELETEBACK:
4705 case SCI_TAB:
4706 case SCI_BACKTAB:
4707 case SCI_FORMFEED:
4708 case SCI_VCHOME:
4709 case SCI_VCHOMEEXTEND:
4710 case SCI_VCHOMEWRAP:
4711 case SCI_VCHOMEWRAPEXTEND:
4712 case SCI_DELWORDLEFT:
4713 case SCI_DELWORDRIGHT:
4714 case SCI_DELWORDRIGHTEND:
4715 case SCI_DELLINELEFT:
4716 case SCI_DELLINERIGHT:
4717 case SCI_LINECOPY:
4718 case SCI_LINECUT:
4719 case SCI_LINEDELETE:
4720 case SCI_LINETRANSPOSE:
4721 case SCI_LINEDUPLICATE:
4722 case SCI_LOWERCASE:
4723 case SCI_UPPERCASE:
4724 case SCI_LINESCROLLDOWN:
4725 case SCI_LINESCROLLUP:
4726 case SCI_DELETEBACKNOTLINE:
4727 case SCI_HOMEDISPLAY:
4728 case SCI_HOMEDISPLAYEXTEND:
4729 case SCI_LINEENDDISPLAY:
4730 case SCI_LINEENDDISPLAYEXTEND:
4731 case SCI_SETSELECTIONMODE:
4732 case SCI_LINEDOWNRECTEXTEND:
4733 case SCI_LINEUPRECTEXTEND:
4734 case SCI_CHARLEFTRECTEXTEND:
4735 case SCI_CHARRIGHTRECTEXTEND:
4736 case SCI_HOMERECTEXTEND:
4737 case SCI_VCHOMERECTEXTEND:
4738 case SCI_LINEENDRECTEXTEND:
4739 case SCI_PAGEUPRECTEXTEND:
4740 case SCI_PAGEDOWNRECTEXTEND:
4741 case SCI_SELECTIONDUPLICATE:
4742 case SCI_COPYALLOWLINE:
4743 case SCI_VERTICALCENTRECARET:
4744 case SCI_MOVESELECTEDLINESUP:
4745 case SCI_MOVESELECTEDLINESDOWN:
4746 break;
4748 // Filter out all others like display changes. Also, newlines are redundant
4749 // with char insert messages.
4750 case SCI_NEWLINE:
4751 default:
4752 // printf("Filtered out %ld of macro recording\n", iMessage);
4753 return ;
4756 // Send notification
4757 SCNotification scn = {0};
4758 scn.nmhdr.code = SCN_MACRORECORD;
4759 scn.message = iMessage;
4760 scn.wParam = wParam;
4761 scn.lParam = lParam;
4762 NotifyParent(scn);
4765 // Something has changed that the container should know about
4766 void Editor::ContainerNeedsUpdate(int flags) {
4767 needUpdateUI |= flags;
4771 * Force scroll and keep position relative to top of window.
4773 * If stuttered = true and not already at first/last row, move to first/last row of window.
4774 * If stuttered = true and already at first/last row, scroll as normal.
4776 void Editor::PageMove(int direction, Selection::selTypes selt, bool stuttered) {
4777 int topLineNew;
4778 SelectionPosition newPos;
4780 int currentLine = pdoc->LineFromPosition(sel.MainCaret());
4781 int topStutterLine = topLine + caretYSlop;
4782 int bottomStutterLine =
4783 pdoc->LineFromPosition(PositionFromLocation(
4784 Point(lastXChosen - xOffset, direction * vs.lineHeight * LinesToScroll())))
4785 - caretYSlop - 1;
4787 if (stuttered && (direction < 0 && currentLine > topStutterLine)) {
4788 topLineNew = topLine;
4789 newPos = SPositionFromLocation(Point(lastXChosen - xOffset, vs.lineHeight * caretYSlop),
4790 false, false, UserVirtualSpace());
4792 } else if (stuttered && (direction > 0 && currentLine < bottomStutterLine)) {
4793 topLineNew = topLine;
4794 newPos = SPositionFromLocation(Point(lastXChosen - xOffset, vs.lineHeight * (LinesToScroll() - caretYSlop)),
4795 false, false, UserVirtualSpace());
4797 } else {
4798 Point pt = LocationFromPosition(sel.MainCaret());
4800 topLineNew = Platform::Clamp(
4801 topLine + direction * LinesToScroll(), 0, MaxScrollPos());
4802 newPos = SPositionFromLocation(
4803 Point(lastXChosen - xOffset, pt.y + direction * (vs.lineHeight * LinesToScroll())),
4804 false, false, UserVirtualSpace());
4807 if (topLineNew != topLine) {
4808 SetTopLine(topLineNew);
4809 MovePositionTo(newPos, selt);
4810 Redraw();
4811 SetVerticalScrollPos();
4812 } else {
4813 MovePositionTo(newPos, selt);
4817 void Editor::ChangeCaseOfSelection(int caseMapping) {
4818 UndoGroup ug(pdoc);
4819 for (size_t r=0; r<sel.Count(); r++) {
4820 SelectionRange current = sel.Range(r);
4821 SelectionRange currentNoVS = current;
4822 currentNoVS.ClearVirtualSpace();
4823 char *text = CopyRange(currentNoVS.Start().Position(), currentNoVS.End().Position());
4824 size_t rangeBytes = currentNoVS.Length();
4825 if (rangeBytes > 0) {
4826 std::string sText(text, rangeBytes);
4828 std::string sMapped = CaseMapString(sText, caseMapping);
4830 if (sMapped != sText) {
4831 size_t firstDifference = 0;
4832 while (sMapped[firstDifference] == sText[firstDifference])
4833 firstDifference++;
4834 size_t lastDifference = sMapped.size() - 1;
4835 while (sMapped[lastDifference] == sText[lastDifference])
4836 lastDifference--;
4837 size_t endSame = sMapped.size() - 1 - lastDifference;
4838 pdoc->DeleteChars(currentNoVS.Start().Position() + firstDifference,
4839 rangeBytes - firstDifference - endSame);
4840 pdoc->InsertString(currentNoVS.Start().Position() + firstDifference,
4841 sMapped.c_str() + firstDifference, lastDifference - firstDifference + 1);
4842 // Automatic movement changes selection so reset to exactly the same as it was.
4843 sel.Range(r) = current;
4846 delete []text;
4850 void Editor::LineTranspose() {
4851 int line = pdoc->LineFromPosition(sel.MainCaret());
4852 if (line > 0) {
4853 UndoGroup ug(pdoc);
4854 int startPrev = pdoc->LineStart(line - 1);
4855 int endPrev = pdoc->LineEnd(line - 1);
4856 int start = pdoc->LineStart(line);
4857 int end = pdoc->LineEnd(line);
4858 char *line1 = CopyRange(startPrev, endPrev);
4859 int len1 = endPrev - startPrev;
4860 char *line2 = CopyRange(start, end);
4861 int len2 = end - start;
4862 pdoc->DeleteChars(start, len2);
4863 pdoc->DeleteChars(startPrev, len1);
4864 pdoc->InsertString(startPrev, line2, len2);
4865 pdoc->InsertString(start - len1 + len2, line1, len1);
4866 MovePositionTo(SelectionPosition(start - len1 + len2));
4867 delete []line1;
4868 delete []line2;
4872 void Editor::Duplicate(bool forLine) {
4873 if (sel.Empty()) {
4874 forLine = true;
4876 UndoGroup ug(pdoc, sel.Count() > 1);
4877 const char *eol = "";
4878 int eolLen = 0;
4879 if (forLine) {
4880 eol = StringFromEOLMode(pdoc->eolMode);
4881 eolLen = istrlen(eol);
4883 for (size_t r=0; r<sel.Count(); r++) {
4884 SelectionPosition start = sel.Range(r).Start();
4885 SelectionPosition end = sel.Range(r).End();
4886 if (forLine) {
4887 int line = pdoc->LineFromPosition(sel.Range(r).caret.Position());
4888 start = SelectionPosition(pdoc->LineStart(line));
4889 end = SelectionPosition(pdoc->LineEnd(line));
4891 char *text = CopyRange(start.Position(), end.Position());
4892 if (forLine)
4893 pdoc->InsertString(end.Position(), eol, eolLen);
4894 pdoc->InsertString(end.Position() + eolLen, text, SelectionRange(end, start).Length());
4895 delete []text;
4897 if (sel.Count() && sel.IsRectangular()) {
4898 SelectionPosition last = sel.Last();
4899 if (forLine) {
4900 int line = pdoc->LineFromPosition(last.Position());
4901 last = SelectionPosition(last.Position() + pdoc->LineStart(line+1) - pdoc->LineStart(line));
4903 if (sel.Rectangular().anchor > sel.Rectangular().caret)
4904 sel.Rectangular().anchor = last;
4905 else
4906 sel.Rectangular().caret = last;
4907 SetRectangularRange();
4911 void Editor::CancelModes() {
4912 sel.SetMoveExtends(false);
4915 void Editor::NewLine() {
4916 ClearSelection();
4917 const char *eol = "\n";
4918 if (pdoc->eolMode == SC_EOL_CRLF) {
4919 eol = "\r\n";
4920 } else if (pdoc->eolMode == SC_EOL_CR) {
4921 eol = "\r";
4922 } // else SC_EOL_LF -> "\n" already set
4923 if (pdoc->InsertCString(sel.MainCaret(), eol)) {
4924 SetEmptySelection(sel.MainCaret() + istrlen(eol));
4925 while (*eol) {
4926 NotifyChar(*eol);
4927 if (recordingMacro) {
4928 char txt[2];
4929 txt[0] = *eol;
4930 txt[1] = '\0';
4931 NotifyMacroRecord(SCI_REPLACESEL, 0, reinterpret_cast<sptr_t>(txt));
4933 eol++;
4936 SetLastXChosen();
4937 SetScrollBars();
4938 EnsureCaretVisible();
4939 // Avoid blinking during rapid typing:
4940 ShowCaretAtCurrentPosition();
4943 void Editor::CursorUpOrDown(int direction, Selection::selTypes selt) {
4944 SelectionPosition caretToUse = sel.Range(sel.Main()).caret;
4945 if (sel.IsRectangular()) {
4946 if (selt == Selection::noSel) {
4947 caretToUse = (direction > 0) ? sel.Limits().end : sel.Limits().start;
4948 } else {
4949 caretToUse = sel.Rectangular().caret;
4952 Point pt = LocationFromPosition(caretToUse);
4953 int lineDoc = pdoc->LineFromPosition(caretToUse.Position());
4954 Point ptStartLine = LocationFromPosition(pdoc->LineStart(lineDoc));
4955 int subLine = (pt.y - ptStartLine.y) / vs.lineHeight;
4956 int commentLines = vs.annotationVisible ? pdoc->AnnotationLines(lineDoc) : 0;
4957 SelectionPosition posNew = SPositionFromLocation(
4958 Point(lastXChosen - xOffset, pt.y + direction * vs.lineHeight), false, false, UserVirtualSpace());
4959 if ((direction > 0) && (subLine >= (cs.GetHeight(lineDoc) - 1 - commentLines))) {
4960 posNew = SPositionFromLocation(
4961 Point(lastXChosen - xOffset, pt.y + (commentLines + 1) * vs.lineHeight), false, false, UserVirtualSpace());
4963 if (direction < 0) {
4964 // Line wrapping may lead to a location on the same line, so
4965 // seek back if that is the case.
4966 // There is an equivalent case when moving down which skips
4967 // over a line but as that does not trap the user it is fine.
4968 Point ptNew = LocationFromPosition(posNew.Position());
4969 while ((posNew.Position() > 0) && (pt.y == ptNew.y)) {
4970 posNew.Add(- 1);
4971 posNew.SetVirtualSpace(0);
4972 ptNew = LocationFromPosition(posNew.Position());
4975 MovePositionTo(posNew, selt);
4978 void Editor::ParaUpOrDown(int direction, Selection::selTypes selt) {
4979 int lineDoc, savedPos = sel.MainCaret();
4980 do {
4981 MovePositionTo(SelectionPosition(direction > 0 ? pdoc->ParaDown(sel.MainCaret()) : pdoc->ParaUp(sel.MainCaret())), selt);
4982 lineDoc = pdoc->LineFromPosition(sel.MainCaret());
4983 if (direction > 0) {
4984 if (sel.MainCaret() >= pdoc->Length() && !cs.GetVisible(lineDoc)) {
4985 if (selt == Selection::noSel) {
4986 MovePositionTo(SelectionPosition(pdoc->LineEndPosition(savedPos)));
4988 break;
4991 } while (!cs.GetVisible(lineDoc));
4994 int Editor::StartEndDisplayLine(int pos, bool start) {
4995 RefreshStyleData();
4996 int line = pdoc->LineFromPosition(pos);
4997 AutoSurface surface(this);
4998 AutoLineLayout ll(llc, RetrieveLineLayout(line));
4999 int posRet = INVALID_POSITION;
5000 if (surface && ll) {
5001 unsigned int posLineStart = pdoc->LineStart(line);
5002 LayoutLine(line, surface, vs, ll, wrapWidth);
5003 int posInLine = pos - posLineStart;
5004 if (posInLine <= ll->maxLineLength) {
5005 for (int subLine = 0; subLine < ll->lines; subLine++) {
5006 if ((posInLine >= ll->LineStart(subLine)) && (posInLine <= ll->LineStart(subLine + 1))) {
5007 if (start) {
5008 posRet = ll->LineStart(subLine) + posLineStart;
5009 } else {
5010 if (subLine == ll->lines - 1)
5011 posRet = ll->LineStart(subLine + 1) + posLineStart;
5012 else
5013 posRet = ll->LineStart(subLine + 1) + posLineStart - 1;
5019 if (posRet == INVALID_POSITION) {
5020 return pos;
5021 } else {
5022 return posRet;
5026 int Editor::KeyCommand(unsigned int iMessage) {
5027 switch (iMessage) {
5028 case SCI_LINEDOWN:
5029 CursorUpOrDown(1);
5030 break;
5031 case SCI_LINEDOWNEXTEND:
5032 CursorUpOrDown(1, Selection::selStream);
5033 break;
5034 case SCI_LINEDOWNRECTEXTEND:
5035 CursorUpOrDown(1, Selection::selRectangle);
5036 break;
5037 case SCI_PARADOWN:
5038 ParaUpOrDown(1);
5039 break;
5040 case SCI_PARADOWNEXTEND:
5041 ParaUpOrDown(1, Selection::selStream);
5042 break;
5043 case SCI_LINESCROLLDOWN:
5044 ScrollTo(topLine + 1);
5045 MoveCaretInsideView(false);
5046 break;
5047 case SCI_LINEUP:
5048 CursorUpOrDown(-1);
5049 break;
5050 case SCI_LINEUPEXTEND:
5051 CursorUpOrDown(-1, Selection::selStream);
5052 break;
5053 case SCI_LINEUPRECTEXTEND:
5054 CursorUpOrDown(-1, Selection::selRectangle);
5055 break;
5056 case SCI_PARAUP:
5057 ParaUpOrDown(-1);
5058 break;
5059 case SCI_PARAUPEXTEND:
5060 ParaUpOrDown(-1, Selection::selStream);
5061 break;
5062 case SCI_LINESCROLLUP:
5063 ScrollTo(topLine - 1);
5064 MoveCaretInsideView(false);
5065 break;
5066 case SCI_CHARLEFT:
5067 if (SelectionEmpty() || sel.MoveExtends()) {
5068 if ((sel.Count() == 1) && pdoc->IsLineEndPosition(sel.MainCaret()) && sel.RangeMain().caret.VirtualSpace()) {
5069 SelectionPosition spCaret = sel.RangeMain().caret;
5070 spCaret.SetVirtualSpace(spCaret.VirtualSpace() - 1);
5071 MovePositionTo(spCaret);
5072 } else {
5073 MovePositionTo(MovePositionSoVisible(
5074 SelectionPosition((sel.LimitsForRectangularElseMain().start).Position() - 1), -1));
5076 } else {
5077 MovePositionTo(sel.LimitsForRectangularElseMain().start);
5079 SetLastXChosen();
5080 break;
5081 case SCI_CHARLEFTEXTEND:
5082 if (pdoc->IsLineEndPosition(sel.MainCaret()) && sel.RangeMain().caret.VirtualSpace()) {
5083 SelectionPosition spCaret = sel.RangeMain().caret;
5084 spCaret.SetVirtualSpace(spCaret.VirtualSpace() - 1);
5085 MovePositionTo(spCaret, Selection::selStream);
5086 } else {
5087 MovePositionTo(MovePositionSoVisible(SelectionPosition(sel.MainCaret() - 1), -1), Selection::selStream);
5089 SetLastXChosen();
5090 break;
5091 case SCI_CHARLEFTRECTEXTEND:
5092 if (pdoc->IsLineEndPosition(sel.MainCaret()) && sel.RangeMain().caret.VirtualSpace()) {
5093 SelectionPosition spCaret = sel.RangeMain().caret;
5094 spCaret.SetVirtualSpace(spCaret.VirtualSpace() - 1);
5095 MovePositionTo(spCaret, Selection::selRectangle);
5096 } else {
5097 MovePositionTo(MovePositionSoVisible(SelectionPosition(sel.MainCaret() - 1), -1), Selection::selRectangle);
5099 SetLastXChosen();
5100 break;
5101 case SCI_CHARRIGHT:
5102 if (SelectionEmpty() || sel.MoveExtends()) {
5103 if ((virtualSpaceOptions & SCVS_USERACCESSIBLE) && pdoc->IsLineEndPosition(sel.MainCaret())) {
5104 SelectionPosition spCaret = sel.RangeMain().caret;
5105 spCaret.SetVirtualSpace(spCaret.VirtualSpace() + 1);
5106 MovePositionTo(spCaret);
5107 } else {
5108 MovePositionTo(MovePositionSoVisible(
5109 SelectionPosition((sel.LimitsForRectangularElseMain().end).Position() + 1), 1));
5111 } else {
5112 MovePositionTo(sel.LimitsForRectangularElseMain().end);
5114 SetLastXChosen();
5115 break;
5116 case SCI_CHARRIGHTEXTEND:
5117 if ((virtualSpaceOptions & SCVS_USERACCESSIBLE) && pdoc->IsLineEndPosition(sel.MainCaret())) {
5118 SelectionPosition spCaret = sel.RangeMain().caret;
5119 spCaret.SetVirtualSpace(spCaret.VirtualSpace() + 1);
5120 MovePositionTo(spCaret, Selection::selStream);
5121 } else {
5122 MovePositionTo(MovePositionSoVisible(SelectionPosition(sel.MainCaret() + 1), 1), Selection::selStream);
5124 SetLastXChosen();
5125 break;
5126 case SCI_CHARRIGHTRECTEXTEND:
5127 if ((virtualSpaceOptions & SCVS_RECTANGULARSELECTION) && pdoc->IsLineEndPosition(sel.MainCaret())) {
5128 SelectionPosition spCaret = sel.RangeMain().caret;
5129 spCaret.SetVirtualSpace(spCaret.VirtualSpace() + 1);
5130 MovePositionTo(spCaret, Selection::selRectangle);
5131 } else {
5132 MovePositionTo(MovePositionSoVisible(SelectionPosition(sel.MainCaret() + 1), 1), Selection::selRectangle);
5134 SetLastXChosen();
5135 break;
5136 case SCI_WORDLEFT:
5137 MovePositionTo(MovePositionSoVisible(pdoc->NextWordStart(sel.MainCaret(), -1), -1));
5138 SetLastXChosen();
5139 break;
5140 case SCI_WORDLEFTEXTEND:
5141 MovePositionTo(MovePositionSoVisible(pdoc->NextWordStart(sel.MainCaret(), -1), -1), Selection::selStream);
5142 SetLastXChosen();
5143 break;
5144 case SCI_WORDRIGHT:
5145 MovePositionTo(MovePositionSoVisible(pdoc->NextWordStart(sel.MainCaret(), 1), 1));
5146 SetLastXChosen();
5147 break;
5148 case SCI_WORDRIGHTEXTEND:
5149 MovePositionTo(MovePositionSoVisible(pdoc->NextWordStart(sel.MainCaret(), 1), 1), Selection::selStream);
5150 SetLastXChosen();
5151 break;
5153 case SCI_WORDLEFTEND:
5154 MovePositionTo(MovePositionSoVisible(pdoc->NextWordEnd(sel.MainCaret(), -1), -1));
5155 SetLastXChosen();
5156 break;
5157 case SCI_WORDLEFTENDEXTEND:
5158 MovePositionTo(MovePositionSoVisible(pdoc->NextWordEnd(sel.MainCaret(), -1), -1), Selection::selStream);
5159 SetLastXChosen();
5160 break;
5161 case SCI_WORDRIGHTEND:
5162 MovePositionTo(MovePositionSoVisible(pdoc->NextWordEnd(sel.MainCaret(), 1), 1));
5163 SetLastXChosen();
5164 break;
5165 case SCI_WORDRIGHTENDEXTEND:
5166 MovePositionTo(MovePositionSoVisible(pdoc->NextWordEnd(sel.MainCaret(), 1), 1), Selection::selStream);
5167 SetLastXChosen();
5168 break;
5170 case SCI_HOME:
5171 MovePositionTo(pdoc->LineStart(pdoc->LineFromPosition(sel.MainCaret())));
5172 SetLastXChosen();
5173 break;
5174 case SCI_HOMEEXTEND:
5175 MovePositionTo(pdoc->LineStart(pdoc->LineFromPosition(sel.MainCaret())), Selection::selStream);
5176 SetLastXChosen();
5177 break;
5178 case SCI_HOMERECTEXTEND:
5179 MovePositionTo(pdoc->LineStart(pdoc->LineFromPosition(sel.MainCaret())), Selection::selRectangle);
5180 SetLastXChosen();
5181 break;
5182 case SCI_LINEEND:
5183 MovePositionTo(pdoc->LineEndPosition(sel.MainCaret()));
5184 SetLastXChosen();
5185 break;
5186 case SCI_LINEENDEXTEND:
5187 MovePositionTo(pdoc->LineEndPosition(sel.MainCaret()), Selection::selStream);
5188 SetLastXChosen();
5189 break;
5190 case SCI_LINEENDRECTEXTEND:
5191 MovePositionTo(pdoc->LineEndPosition(sel.MainCaret()), Selection::selRectangle);
5192 SetLastXChosen();
5193 break;
5194 case SCI_HOMEWRAP: {
5195 SelectionPosition homePos = MovePositionSoVisible(StartEndDisplayLine(sel.MainCaret(), true), -1);
5196 if (sel.RangeMain().caret <= homePos)
5197 homePos = SelectionPosition(pdoc->LineStart(pdoc->LineFromPosition(sel.MainCaret())));
5198 MovePositionTo(homePos);
5199 SetLastXChosen();
5201 break;
5202 case SCI_HOMEWRAPEXTEND: {
5203 SelectionPosition homePos = MovePositionSoVisible(StartEndDisplayLine(sel.MainCaret(), true), -1);
5204 if (sel.RangeMain().caret <= homePos)
5205 homePos = SelectionPosition(pdoc->LineStart(pdoc->LineFromPosition(sel.MainCaret())));
5206 MovePositionTo(homePos, Selection::selStream);
5207 SetLastXChosen();
5209 break;
5210 case SCI_LINEENDWRAP: {
5211 SelectionPosition endPos = MovePositionSoVisible(StartEndDisplayLine(sel.MainCaret(), false), 1);
5212 SelectionPosition realEndPos = SelectionPosition(pdoc->LineEndPosition(sel.MainCaret()));
5213 if (endPos > realEndPos // if moved past visible EOLs
5214 || sel.RangeMain().caret >= endPos) // if at end of display line already
5215 endPos = realEndPos;
5216 MovePositionTo(endPos);
5217 SetLastXChosen();
5219 break;
5220 case SCI_LINEENDWRAPEXTEND: {
5221 SelectionPosition endPos = MovePositionSoVisible(StartEndDisplayLine(sel.MainCaret(), false), 1);
5222 SelectionPosition realEndPos = SelectionPosition(pdoc->LineEndPosition(sel.MainCaret()));
5223 if (endPos > realEndPos // if moved past visible EOLs
5224 || sel.RangeMain().caret >= endPos) // if at end of display line already
5225 endPos = realEndPos;
5226 MovePositionTo(endPos, Selection::selStream);
5227 SetLastXChosen();
5229 break;
5230 case SCI_DOCUMENTSTART:
5231 MovePositionTo(0);
5232 SetLastXChosen();
5233 break;
5234 case SCI_DOCUMENTSTARTEXTEND:
5235 MovePositionTo(0, Selection::selStream);
5236 SetLastXChosen();
5237 break;
5238 case SCI_DOCUMENTEND:
5239 MovePositionTo(pdoc->Length());
5240 SetLastXChosen();
5241 break;
5242 case SCI_DOCUMENTENDEXTEND:
5243 MovePositionTo(pdoc->Length(), Selection::selStream);
5244 SetLastXChosen();
5245 break;
5246 case SCI_STUTTEREDPAGEUP:
5247 PageMove(-1, Selection::noSel, true);
5248 break;
5249 case SCI_STUTTEREDPAGEUPEXTEND:
5250 PageMove(-1, Selection::selStream, true);
5251 break;
5252 case SCI_STUTTEREDPAGEDOWN:
5253 PageMove(1, Selection::noSel, true);
5254 break;
5255 case SCI_STUTTEREDPAGEDOWNEXTEND:
5256 PageMove(1, Selection::selStream, true);
5257 break;
5258 case SCI_PAGEUP:
5259 PageMove(-1);
5260 break;
5261 case SCI_PAGEUPEXTEND:
5262 PageMove(-1, Selection::selStream);
5263 break;
5264 case SCI_PAGEUPRECTEXTEND:
5265 PageMove(-1, Selection::selRectangle);
5266 break;
5267 case SCI_PAGEDOWN:
5268 PageMove(1);
5269 break;
5270 case SCI_PAGEDOWNEXTEND:
5271 PageMove(1, Selection::selStream);
5272 break;
5273 case SCI_PAGEDOWNRECTEXTEND:
5274 PageMove(1, Selection::selRectangle);
5275 break;
5276 case SCI_EDITTOGGLEOVERTYPE:
5277 inOverstrike = !inOverstrike;
5278 DropCaret();
5279 ShowCaretAtCurrentPosition();
5280 ContainerNeedsUpdate(SC_UPDATE_CONTENT);
5281 NotifyUpdateUI();
5282 break;
5283 case SCI_CANCEL: // Cancel any modes - handled in subclass
5284 // Also unselect text
5285 CancelModes();
5286 break;
5287 case SCI_DELETEBACK:
5288 DelCharBack(true);
5289 if ((caretSticky == SC_CARETSTICKY_OFF) || (caretSticky == SC_CARETSTICKY_WHITESPACE)) {
5290 SetLastXChosen();
5292 EnsureCaretVisible();
5293 break;
5294 case SCI_DELETEBACKNOTLINE:
5295 DelCharBack(false);
5296 if ((caretSticky == SC_CARETSTICKY_OFF) || (caretSticky == SC_CARETSTICKY_WHITESPACE)) {
5297 SetLastXChosen();
5299 EnsureCaretVisible();
5300 break;
5301 case SCI_TAB:
5302 Indent(true);
5303 if (caretSticky == SC_CARETSTICKY_OFF) {
5304 SetLastXChosen();
5306 EnsureCaretVisible();
5307 ShowCaretAtCurrentPosition(); // Avoid blinking
5308 break;
5309 case SCI_BACKTAB:
5310 Indent(false);
5311 if ((caretSticky == SC_CARETSTICKY_OFF) || (caretSticky == SC_CARETSTICKY_WHITESPACE)) {
5312 SetLastXChosen();
5314 EnsureCaretVisible();
5315 ShowCaretAtCurrentPosition(); // Avoid blinking
5316 break;
5317 case SCI_NEWLINE:
5318 NewLine();
5319 break;
5320 case SCI_FORMFEED:
5321 AddChar('\f');
5322 break;
5323 case SCI_VCHOME:
5324 MovePositionTo(pdoc->VCHomePosition(sel.MainCaret()));
5325 SetLastXChosen();
5326 break;
5327 case SCI_VCHOMEEXTEND:
5328 MovePositionTo(pdoc->VCHomePosition(sel.MainCaret()), Selection::selStream);
5329 SetLastXChosen();
5330 break;
5331 case SCI_VCHOMERECTEXTEND:
5332 MovePositionTo(pdoc->VCHomePosition(sel.MainCaret()), Selection::selRectangle);
5333 SetLastXChosen();
5334 break;
5335 case SCI_VCHOMEWRAP: {
5336 SelectionPosition homePos = SelectionPosition(pdoc->VCHomePosition(sel.MainCaret()));
5337 SelectionPosition viewLineStart = MovePositionSoVisible(StartEndDisplayLine(sel.MainCaret(), true), -1);
5338 if ((viewLineStart < sel.RangeMain().caret) && (viewLineStart > homePos))
5339 homePos = viewLineStart;
5341 MovePositionTo(homePos);
5342 SetLastXChosen();
5344 break;
5345 case SCI_VCHOMEWRAPEXTEND: {
5346 SelectionPosition homePos = SelectionPosition(pdoc->VCHomePosition(sel.MainCaret()));
5347 SelectionPosition viewLineStart = MovePositionSoVisible(StartEndDisplayLine(sel.MainCaret(), true), -1);
5348 if ((viewLineStart < sel.RangeMain().caret) && (viewLineStart > homePos))
5349 homePos = viewLineStart;
5351 MovePositionTo(homePos, Selection::selStream);
5352 SetLastXChosen();
5354 break;
5355 case SCI_ZOOMIN:
5356 if (vs.zoomLevel < 20) {
5357 vs.zoomLevel++;
5358 InvalidateStyleRedraw();
5359 NotifyZoom();
5361 break;
5362 case SCI_ZOOMOUT:
5363 if (vs.zoomLevel > -10) {
5364 vs.zoomLevel--;
5365 InvalidateStyleRedraw();
5366 NotifyZoom();
5368 break;
5369 case SCI_DELWORDLEFT: {
5370 int startWord = pdoc->NextWordStart(sel.MainCaret(), -1);
5371 pdoc->DeleteChars(startWord, sel.MainCaret() - startWord);
5372 sel.RangeMain().ClearVirtualSpace();
5373 SetLastXChosen();
5375 break;
5376 case SCI_DELWORDRIGHT: {
5377 UndoGroup ug(pdoc);
5378 sel.RangeMain().caret = SelectionPosition(
5379 InsertSpace(sel.RangeMain().caret.Position(), sel.RangeMain().caret.VirtualSpace()));
5380 sel.RangeMain().anchor = sel.RangeMain().caret;
5381 int endWord = pdoc->NextWordStart(sel.MainCaret(), 1);
5382 pdoc->DeleteChars(sel.MainCaret(), endWord - sel.MainCaret());
5384 break;
5385 case SCI_DELWORDRIGHTEND: {
5386 UndoGroup ug(pdoc);
5387 sel.RangeMain().caret = SelectionPosition(
5388 InsertSpace(sel.RangeMain().caret.Position(), sel.RangeMain().caret.VirtualSpace()));
5389 int endWord = pdoc->NextWordEnd(sel.MainCaret(), 1);
5390 pdoc->DeleteChars(sel.MainCaret(), endWord - sel.MainCaret());
5392 break;
5393 case SCI_DELLINELEFT: {
5394 int line = pdoc->LineFromPosition(sel.MainCaret());
5395 int start = pdoc->LineStart(line);
5396 pdoc->DeleteChars(start, sel.MainCaret() - start);
5397 sel.RangeMain().ClearVirtualSpace();
5398 SetLastXChosen();
5400 break;
5401 case SCI_DELLINERIGHT: {
5402 int line = pdoc->LineFromPosition(sel.MainCaret());
5403 int end = pdoc->LineEnd(line);
5404 pdoc->DeleteChars(sel.MainCaret(), end - sel.MainCaret());
5406 break;
5407 case SCI_LINECOPY: {
5408 int lineStart = pdoc->LineFromPosition(SelectionStart().Position());
5409 int lineEnd = pdoc->LineFromPosition(SelectionEnd().Position());
5410 CopyRangeToClipboard(pdoc->LineStart(lineStart),
5411 pdoc->LineStart(lineEnd + 1));
5413 break;
5414 case SCI_LINECUT: {
5415 int lineStart = pdoc->LineFromPosition(SelectionStart().Position());
5416 int lineEnd = pdoc->LineFromPosition(SelectionEnd().Position());
5417 int start = pdoc->LineStart(lineStart);
5418 int end = pdoc->LineStart(lineEnd + 1);
5419 SetSelection(start, end);
5420 Cut();
5421 SetLastXChosen();
5423 break;
5424 case SCI_LINEDELETE: {
5425 int line = pdoc->LineFromPosition(sel.MainCaret());
5426 int start = pdoc->LineStart(line);
5427 int end = pdoc->LineStart(line + 1);
5428 pdoc->DeleteChars(start, end - start);
5430 break;
5431 case SCI_LINETRANSPOSE:
5432 LineTranspose();
5433 break;
5434 case SCI_LINEDUPLICATE:
5435 Duplicate(true);
5436 break;
5437 case SCI_SELECTIONDUPLICATE:
5438 Duplicate(false);
5439 break;
5440 case SCI_LOWERCASE:
5441 ChangeCaseOfSelection(cmLower);
5442 break;
5443 case SCI_UPPERCASE:
5444 ChangeCaseOfSelection(cmUpper);
5445 break;
5446 case SCI_WORDPARTLEFT:
5447 MovePositionTo(MovePositionSoVisible(pdoc->WordPartLeft(sel.MainCaret()), -1));
5448 SetLastXChosen();
5449 break;
5450 case SCI_WORDPARTLEFTEXTEND:
5451 MovePositionTo(MovePositionSoVisible(pdoc->WordPartLeft(sel.MainCaret()), -1), Selection::selStream);
5452 SetLastXChosen();
5453 break;
5454 case SCI_WORDPARTRIGHT:
5455 MovePositionTo(MovePositionSoVisible(pdoc->WordPartRight(sel.MainCaret()), 1));
5456 SetLastXChosen();
5457 break;
5458 case SCI_WORDPARTRIGHTEXTEND:
5459 MovePositionTo(MovePositionSoVisible(pdoc->WordPartRight(sel.MainCaret()), 1), Selection::selStream);
5460 SetLastXChosen();
5461 break;
5462 case SCI_HOMEDISPLAY:
5463 MovePositionTo(MovePositionSoVisible(
5464 StartEndDisplayLine(sel.MainCaret(), true), -1));
5465 SetLastXChosen();
5466 break;
5467 case SCI_HOMEDISPLAYEXTEND:
5468 MovePositionTo(MovePositionSoVisible(
5469 StartEndDisplayLine(sel.MainCaret(), true), -1), Selection::selStream);
5470 SetLastXChosen();
5471 break;
5472 case SCI_LINEENDDISPLAY:
5473 MovePositionTo(MovePositionSoVisible(
5474 StartEndDisplayLine(sel.MainCaret(), false), 1));
5475 SetLastXChosen();
5476 break;
5477 case SCI_LINEENDDISPLAYEXTEND:
5478 MovePositionTo(MovePositionSoVisible(
5479 StartEndDisplayLine(sel.MainCaret(), false), 1), Selection::selStream);
5480 SetLastXChosen();
5481 break;
5483 return 0;
5486 int Editor::KeyDefault(int, int) {
5487 return 0;
5490 int Editor::KeyDown(int key, bool shift, bool ctrl, bool alt, bool *consumed) {
5491 DwellEnd(false);
5492 int modifiers = (shift ? SCI_SHIFT : 0) | (ctrl ? SCI_CTRL : 0) |
5493 (alt ? SCI_ALT : 0);
5494 int msg = kmap.Find(key, modifiers);
5495 if (msg) {
5496 if (consumed)
5497 *consumed = true;
5498 return WndProc(msg, 0, 0);
5499 } else {
5500 if (consumed)
5501 *consumed = false;
5502 return KeyDefault(key, modifiers);
5506 void Editor::SetWhitespaceVisible(int view) {
5507 vs.viewWhitespace = static_cast<WhiteSpaceVisibility>(view);
5510 int Editor::GetWhitespaceVisible() {
5511 return vs.viewWhitespace;
5514 void Editor::Indent(bool forwards) {
5515 for (size_t r=0; r<sel.Count(); r++) {
5516 int lineOfAnchor = pdoc->LineFromPosition(sel.Range(r).anchor.Position());
5517 int caretPosition = sel.Range(r).caret.Position();
5518 int lineCurrentPos = pdoc->LineFromPosition(caretPosition);
5519 if (lineOfAnchor == lineCurrentPos) {
5520 if (forwards) {
5521 UndoGroup ug(pdoc);
5522 pdoc->DeleteChars(sel.Range(r).Start().Position(), sel.Range(r).Length());
5523 caretPosition = sel.Range(r).caret.Position();
5524 if (pdoc->GetColumn(caretPosition) <= pdoc->GetColumn(pdoc->GetLineIndentPosition(lineCurrentPos)) &&
5525 pdoc->tabIndents) {
5526 int indentation = pdoc->GetLineIndentation(lineCurrentPos);
5527 int indentationStep = pdoc->IndentSize();
5528 pdoc->SetLineIndentation(lineCurrentPos, indentation + indentationStep - indentation % indentationStep);
5529 sel.Range(r) = SelectionRange(pdoc->GetLineIndentPosition(lineCurrentPos));
5530 } else {
5531 if (pdoc->useTabs) {
5532 pdoc->InsertChar(caretPosition, '\t');
5533 sel.Range(r) = SelectionRange(caretPosition+1);
5534 } else {
5535 int numSpaces = (pdoc->tabInChars) -
5536 (pdoc->GetColumn(caretPosition) % (pdoc->tabInChars));
5537 if (numSpaces < 1)
5538 numSpaces = pdoc->tabInChars;
5539 for (int i = 0; i < numSpaces; i++) {
5540 pdoc->InsertChar(caretPosition + i, ' ');
5542 sel.Range(r) = SelectionRange(caretPosition+numSpaces);
5545 } else {
5546 if (pdoc->GetColumn(caretPosition) <= pdoc->GetLineIndentation(lineCurrentPos) &&
5547 pdoc->tabIndents) {
5548 UndoGroup ug(pdoc);
5549 int indentation = pdoc->GetLineIndentation(lineCurrentPos);
5550 int indentationStep = pdoc->IndentSize();
5551 pdoc->SetLineIndentation(lineCurrentPos, indentation - indentationStep);
5552 sel.Range(r) = SelectionRange(pdoc->GetLineIndentPosition(lineCurrentPos));
5553 } else {
5554 int newColumn = ((pdoc->GetColumn(caretPosition) - 1) / pdoc->tabInChars) *
5555 pdoc->tabInChars;
5556 if (newColumn < 0)
5557 newColumn = 0;
5558 int newPos = caretPosition;
5559 while (pdoc->GetColumn(newPos) > newColumn)
5560 newPos--;
5561 sel.Range(r) = SelectionRange(newPos);
5564 } else { // Multiline
5565 int anchorPosOnLine = sel.Range(r).anchor.Position() - pdoc->LineStart(lineOfAnchor);
5566 int currentPosPosOnLine = caretPosition - pdoc->LineStart(lineCurrentPos);
5567 // Multiple lines selected so indent / dedent
5568 int lineTopSel = Platform::Minimum(lineOfAnchor, lineCurrentPos);
5569 int lineBottomSel = Platform::Maximum(lineOfAnchor, lineCurrentPos);
5570 if (pdoc->LineStart(lineBottomSel) == sel.Range(r).anchor.Position() || pdoc->LineStart(lineBottomSel) == caretPosition)
5571 lineBottomSel--; // If not selecting any characters on a line, do not indent
5573 UndoGroup ug(pdoc);
5574 pdoc->Indent(forwards, lineBottomSel, lineTopSel);
5576 if (lineOfAnchor < lineCurrentPos) {
5577 if (currentPosPosOnLine == 0)
5578 sel.Range(r) = SelectionRange(pdoc->LineStart(lineCurrentPos), pdoc->LineStart(lineOfAnchor));
5579 else
5580 sel.Range(r) = SelectionRange(pdoc->LineStart(lineCurrentPos + 1), pdoc->LineStart(lineOfAnchor));
5581 } else {
5582 if (anchorPosOnLine == 0)
5583 sel.Range(r) = SelectionRange(pdoc->LineStart(lineCurrentPos), pdoc->LineStart(lineOfAnchor));
5584 else
5585 sel.Range(r) = SelectionRange(pdoc->LineStart(lineCurrentPos), pdoc->LineStart(lineOfAnchor + 1));
5591 class CaseFolderASCII : public CaseFolderTable {
5592 public:
5593 CaseFolderASCII() {
5594 StandardASCII();
5596 ~CaseFolderASCII() {
5598 virtual size_t Fold(char *folded, size_t sizeFolded, const char *mixed, size_t lenMixed) {
5599 if (lenMixed > sizeFolded) {
5600 return 0;
5601 } else {
5602 for (size_t i=0; i<lenMixed; i++) {
5603 folded[i] = mapping[static_cast<unsigned char>(mixed[i])];
5605 return lenMixed;
5611 CaseFolder *Editor::CaseFolderForEncoding() {
5612 // Simple default that only maps ASCII upper case to lower case.
5613 return new CaseFolderASCII();
5617 * Search of a text in the document, in the given range.
5618 * @return The position of the found text, -1 if not found.
5620 long Editor::FindText(
5621 uptr_t wParam, ///< Search modes : @c SCFIND_MATCHCASE, @c SCFIND_WHOLEWORD,
5622 ///< @c SCFIND_WORDSTART, @c SCFIND_REGEXP or @c SCFIND_POSIX.
5623 sptr_t lParam) { ///< @c TextToFind structure: The text to search for in the given range.
5625 Sci_TextToFind *ft = reinterpret_cast<Sci_TextToFind *>(lParam);
5626 int lengthFound = istrlen(ft->lpstrText);
5627 std::auto_ptr<CaseFolder> pcf(CaseFolderForEncoding());
5628 int pos = pdoc->FindText(ft->chrg.cpMin, ft->chrg.cpMax, ft->lpstrText,
5629 (wParam & SCFIND_MATCHCASE) != 0,
5630 (wParam & SCFIND_WHOLEWORD) != 0,
5631 (wParam & SCFIND_WORDSTART) != 0,
5632 (wParam & SCFIND_REGEXP) != 0,
5633 wParam,
5634 &lengthFound,
5635 pcf.get());
5636 if (pos != -1) {
5637 ft->chrgText.cpMin = pos;
5638 ft->chrgText.cpMax = pos + lengthFound;
5640 return pos;
5644 * Relocatable search support : Searches relative to current selection
5645 * point and sets the selection to the found text range with
5646 * each search.
5649 * Anchor following searches at current selection start: This allows
5650 * multiple incremental interactive searches to be macro recorded
5651 * while still setting the selection to found text so the find/select
5652 * operation is self-contained.
5654 void Editor::SearchAnchor() {
5655 searchAnchor = SelectionStart().Position();
5659 * Find text from current search anchor: Must call @c SearchAnchor first.
5660 * Used for next text and previous text requests.
5661 * @return The position of the found text, -1 if not found.
5663 long Editor::SearchText(
5664 unsigned int iMessage, ///< Accepts both @c SCI_SEARCHNEXT and @c SCI_SEARCHPREV.
5665 uptr_t wParam, ///< Search modes : @c SCFIND_MATCHCASE, @c SCFIND_WHOLEWORD,
5666 ///< @c SCFIND_WORDSTART, @c SCFIND_REGEXP or @c SCFIND_POSIX.
5667 sptr_t lParam) { ///< The text to search for.
5669 const char *txt = reinterpret_cast<char *>(lParam);
5670 int pos;
5671 int lengthFound = istrlen(txt);
5672 std::auto_ptr<CaseFolder> pcf(CaseFolderForEncoding());
5673 if (iMessage == SCI_SEARCHNEXT) {
5674 pos = pdoc->FindText(searchAnchor, pdoc->Length(), txt,
5675 (wParam & SCFIND_MATCHCASE) != 0,
5676 (wParam & SCFIND_WHOLEWORD) != 0,
5677 (wParam & SCFIND_WORDSTART) != 0,
5678 (wParam & SCFIND_REGEXP) != 0,
5679 wParam,
5680 &lengthFound,
5681 pcf.get());
5682 } else {
5683 pos = pdoc->FindText(searchAnchor, 0, txt,
5684 (wParam & SCFIND_MATCHCASE) != 0,
5685 (wParam & SCFIND_WHOLEWORD) != 0,
5686 (wParam & SCFIND_WORDSTART) != 0,
5687 (wParam & SCFIND_REGEXP) != 0,
5688 wParam,
5689 &lengthFound,
5690 pcf.get());
5692 if (pos != -1) {
5693 SetSelection(pos, pos + lengthFound);
5696 return pos;
5699 std::string Editor::CaseMapString(const std::string &s, int caseMapping) {
5700 std::string ret(s);
5701 for (size_t i=0; i<ret.size(); i++) {
5702 switch (caseMapping) {
5703 case cmUpper:
5704 if (ret[i] >= 'a' && ret[i] <= 'z')
5705 ret[i] = static_cast<char>(ret[i] - 'a' + 'A');
5706 break;
5707 case cmLower:
5708 if (ret[i] >= 'A' && ret[i] <= 'Z')
5709 ret[i] = static_cast<char>(ret[i] - 'A' + 'a');
5710 break;
5713 return ret;
5717 * Search for text in the target range of the document.
5718 * @return The position of the found text, -1 if not found.
5720 long Editor::SearchInTarget(const char *text, int length) {
5721 int lengthFound = length;
5723 std::auto_ptr<CaseFolder> pcf(CaseFolderForEncoding());
5724 int pos = pdoc->FindText(targetStart, targetEnd, text,
5725 (searchFlags & SCFIND_MATCHCASE) != 0,
5726 (searchFlags & SCFIND_WHOLEWORD) != 0,
5727 (searchFlags & SCFIND_WORDSTART) != 0,
5728 (searchFlags & SCFIND_REGEXP) != 0,
5729 searchFlags,
5730 &lengthFound,
5731 pcf.get());
5732 if (pos != -1) {
5733 targetStart = pos;
5734 targetEnd = pos + lengthFound;
5736 return pos;
5739 void Editor::GoToLine(int lineNo) {
5740 if (lineNo > pdoc->LinesTotal())
5741 lineNo = pdoc->LinesTotal();
5742 if (lineNo < 0)
5743 lineNo = 0;
5744 SetEmptySelection(pdoc->LineStart(lineNo));
5745 ShowCaretAtCurrentPosition();
5746 EnsureCaretVisible();
5749 static bool Close(Point pt1, Point pt2) {
5750 if (abs(pt1.x - pt2.x) > 3)
5751 return false;
5752 if (abs(pt1.y - pt2.y) > 3)
5753 return false;
5754 return true;
5757 char *Editor::CopyRange(int start, int end) {
5758 char *text = 0;
5759 if (start < end) {
5760 int len = end - start;
5761 text = new char[len + 1];
5762 for (int i = 0; i < len; i++) {
5763 text[i] = pdoc->CharAt(start + i);
5765 text[len] = '\0';
5767 return text;
5770 void Editor::CopySelectionRange(SelectionText *ss, bool allowLineCopy) {
5771 if (sel.Empty()) {
5772 if (allowLineCopy) {
5773 int currentLine = pdoc->LineFromPosition(sel.MainCaret());
5774 int start = pdoc->LineStart(currentLine);
5775 int end = pdoc->LineEnd(currentLine);
5777 char *text = CopyRange(start, end);
5778 int textLen = text ? strlen(text) : 0;
5779 // include room for \r\n\0
5780 textLen += 3;
5781 char *textWithEndl = new char[textLen];
5782 textWithEndl[0] = '\0';
5783 if (text)
5784 strncat(textWithEndl, text, textLen);
5785 if (pdoc->eolMode != SC_EOL_LF)
5786 strncat(textWithEndl, "\r", textLen);
5787 if (pdoc->eolMode != SC_EOL_CR)
5788 strncat(textWithEndl, "\n", textLen);
5789 ss->Set(textWithEndl, strlen(textWithEndl) + 1,
5790 pdoc->dbcsCodePage, vs.styles[STYLE_DEFAULT].characterSet, false, true);
5791 delete []text;
5793 } else {
5794 int delimiterLength = 0;
5795 if (sel.selType == Selection::selRectangle) {
5796 if (pdoc->eolMode == SC_EOL_CRLF) {
5797 delimiterLength = 2;
5798 } else {
5799 delimiterLength = 1;
5802 int size = sel.Length() + delimiterLength * sel.Count();
5803 char *text = new char[size + 1];
5804 int j = 0;
5805 std::vector<SelectionRange> rangesInOrder = sel.RangesCopy();
5806 if (sel.selType == Selection::selRectangle)
5807 std::sort(rangesInOrder.begin(), rangesInOrder.end());
5808 for (size_t r=0; r<rangesInOrder.size(); r++) {
5809 SelectionRange current = rangesInOrder[r];
5810 for (int i = current.Start().Position();
5811 i < current.End().Position();
5812 i++) {
5813 text[j++] = pdoc->CharAt(i);
5815 if (sel.selType == Selection::selRectangle) {
5816 if (pdoc->eolMode != SC_EOL_LF) {
5817 text[j++] = '\r';
5819 if (pdoc->eolMode != SC_EOL_CR) {
5820 text[j++] = '\n';
5824 text[size] = '\0';
5825 ss->Set(text, size + 1, pdoc->dbcsCodePage,
5826 vs.styles[STYLE_DEFAULT].characterSet, sel.IsRectangular(), sel.selType == Selection::selLines);
5830 void Editor::CopyRangeToClipboard(int start, int end) {
5831 start = pdoc->ClampPositionIntoDocument(start);
5832 end = pdoc->ClampPositionIntoDocument(end);
5833 SelectionText selectedText;
5834 selectedText.Set(CopyRange(start, end), end - start + 1,
5835 pdoc->dbcsCodePage, vs.styles[STYLE_DEFAULT].characterSet, false, false);
5836 CopyToClipboard(selectedText);
5839 void Editor::CopyText(int length, const char *text) {
5840 SelectionText selectedText;
5841 selectedText.Copy(text, length + 1,
5842 pdoc->dbcsCodePage, vs.styles[STYLE_DEFAULT].characterSet, false, false);
5843 CopyToClipboard(selectedText);
5846 void Editor::SetDragPosition(SelectionPosition newPos) {
5847 if (newPos.Position() >= 0) {
5848 newPos = MovePositionOutsideChar(newPos, 1);
5849 posDrop = newPos;
5851 if (!(posDrag == newPos)) {
5852 caret.on = true;
5853 SetTicking(true);
5854 InvalidateCaret();
5855 posDrag = newPos;
5856 InvalidateCaret();
5860 void Editor::DisplayCursor(Window::Cursor c) {
5861 if (cursorMode == SC_CURSORNORMAL)
5862 wMain.SetCursor(c);
5863 else
5864 wMain.SetCursor(static_cast<Window::Cursor>(cursorMode));
5867 bool Editor::DragThreshold(Point ptStart, Point ptNow) {
5868 int xMove = ptStart.x - ptNow.x;
5869 int yMove = ptStart.y - ptNow.y;
5870 int distanceSquared = xMove * xMove + yMove * yMove;
5871 return distanceSquared > 16;
5874 void Editor::StartDrag() {
5875 // Always handled by subclasses
5876 //SetMouseCapture(true);
5877 //DisplayCursor(Window::cursorArrow);
5880 void Editor::DropAt(SelectionPosition position, const char *value, bool moving, bool rectangular) {
5881 //Platform::DebugPrintf("DropAt %d %d\n", inDragDrop, position);
5882 if (inDragDrop == ddDragging)
5883 dropWentOutside = false;
5885 bool positionWasInSelection = PositionInSelection(position.Position());
5887 bool positionOnEdgeOfSelection =
5888 (position == SelectionStart()) || (position == SelectionEnd());
5890 if ((inDragDrop != ddDragging) || !(positionWasInSelection) ||
5891 (positionOnEdgeOfSelection && !moving)) {
5893 SelectionPosition selStart = SelectionStart();
5894 SelectionPosition selEnd = SelectionEnd();
5896 UndoGroup ug(pdoc);
5898 SelectionPosition positionAfterDeletion = position;
5899 if ((inDragDrop == ddDragging) && moving) {
5900 // Remove dragged out text
5901 if (rectangular || sel.selType == Selection::selLines) {
5902 for (size_t r=0; r<sel.Count(); r++) {
5903 if (position >= sel.Range(r).Start()) {
5904 if (position > sel.Range(r).End()) {
5905 positionAfterDeletion.Add(-sel.Range(r).Length());
5906 } else {
5907 positionAfterDeletion.Add(-SelectionRange(position, sel.Range(r).Start()).Length());
5911 } else {
5912 if (position > selStart) {
5913 positionAfterDeletion.Add(-SelectionRange(selEnd, selStart).Length());
5916 ClearSelection();
5918 position = positionAfterDeletion;
5920 if (rectangular) {
5921 PasteRectangular(position, value, istrlen(value));
5922 // Should try to select new rectangle but it may not be a rectangle now so just select the drop position
5923 SetEmptySelection(position);
5924 } else {
5925 position = MovePositionOutsideChar(position, sel.MainCaret() - position.Position());
5926 position = SelectionPosition(InsertSpace(position.Position(), position.VirtualSpace()));
5927 if (pdoc->InsertCString(position.Position(), value)) {
5928 SelectionPosition posAfterInsertion = position;
5929 posAfterInsertion.Add(istrlen(value));
5930 SetSelection(posAfterInsertion, position);
5933 } else if (inDragDrop == ddDragging) {
5934 SetEmptySelection(position);
5939 * @return true if given position is inside the selection,
5941 bool Editor::PositionInSelection(int pos) {
5942 pos = MovePositionOutsideChar(pos, sel.MainCaret() - pos);
5943 for (size_t r=0; r<sel.Count(); r++) {
5944 if (sel.Range(r).Contains(pos))
5945 return true;
5947 return false;
5950 bool Editor::PointInSelection(Point pt) {
5951 SelectionPosition pos = SPositionFromLocation(pt);
5952 int xPos = XFromPosition(pos);
5953 for (size_t r=0; r<sel.Count(); r++) {
5954 SelectionRange range = sel.Range(r);
5955 if (range.Contains(pos)) {
5956 bool hit = true;
5957 if (pos == range.Start()) {
5958 // see if just before selection
5959 if (pt.x < xPos) {
5960 hit = false;
5963 if (pos == range.End()) {
5964 // see if just after selection
5965 if (pt.x > xPos) {
5966 hit = false;
5969 if (hit)
5970 return true;
5973 return false;
5976 bool Editor::PointInSelMargin(Point pt) {
5977 // Really means: "Point in a margin"
5978 if (vs.fixedColumnWidth > 0) { // There is a margin
5979 PRectangle rcSelMargin = GetClientRectangle();
5980 rcSelMargin.right = vs.fixedColumnWidth - vs.leftMarginWidth;
5981 return rcSelMargin.Contains(pt);
5982 } else {
5983 return false;
5987 Window::Cursor Editor::GetMarginCursor(Point pt) {
5988 int x = 0;
5989 for (int margin = 0; margin < ViewStyle::margins; margin++) {
5990 if ((pt.x >= x) && (pt.x < x + vs.ms[margin].width))
5991 return static_cast<Window::Cursor>(vs.ms[margin].cursor);
5992 x += vs.ms[margin].width;
5994 return Window::cursorReverseArrow;
5997 void Editor::LineSelection(int lineCurrent_, int lineAnchor_) {
5998 if (lineAnchor_ < lineCurrent_) {
5999 SetSelection(pdoc->LineStart(lineCurrent_ + 1),
6000 pdoc->LineStart(lineAnchor_));
6001 } else if (lineAnchor_ > lineCurrent_) {
6002 SetSelection(pdoc->LineStart(lineCurrent_),
6003 pdoc->LineStart(lineAnchor_ + 1));
6004 } else { // Same line, select it
6005 SetSelection(pdoc->LineStart(lineAnchor_ + 1),
6006 pdoc->LineStart(lineAnchor_));
6010 void Editor::WordSelection(int pos) {
6011 if (pos < wordSelectAnchorStartPos) {
6012 // Extend backward to the word containing pos.
6013 // Skip ExtendWordSelect if the line is empty or if pos is after the last character.
6014 // This ensures that a series of empty lines isn't counted as a single "word".
6015 if (!pdoc->IsLineEndPosition(pos))
6016 pos = pdoc->ExtendWordSelect(pdoc->MovePositionOutsideChar(pos + 1, 1), -1);
6017 SetSelection(pos, wordSelectAnchorEndPos);
6018 } else if (pos > wordSelectAnchorEndPos) {
6019 // Extend forward to the word containing the character to the left of pos.
6020 // Skip ExtendWordSelect if the line is empty or if pos is the first position on the line.
6021 // This ensures that a series of empty lines isn't counted as a single "word".
6022 if (pos > pdoc->LineStart(pdoc->LineFromPosition(pos)))
6023 pos = pdoc->ExtendWordSelect(pdoc->MovePositionOutsideChar(pos - 1, -1), 1);
6024 SetSelection(pos, wordSelectAnchorStartPos);
6025 } else {
6026 // Select only the anchored word
6027 if (pos >= originalAnchorPos)
6028 SetSelection(wordSelectAnchorEndPos, wordSelectAnchorStartPos);
6029 else
6030 SetSelection(wordSelectAnchorStartPos, wordSelectAnchorEndPos);
6034 void Editor::DwellEnd(bool mouseMoved) {
6035 if (mouseMoved)
6036 ticksToDwell = dwellDelay;
6037 else
6038 ticksToDwell = SC_TIME_FOREVER;
6039 if (dwelling && (dwellDelay < SC_TIME_FOREVER)) {
6040 dwelling = false;
6041 NotifyDwelling(ptMouseLast, dwelling);
6045 void Editor::MouseLeave() {
6046 SetHotSpotRange(NULL);
6047 if (!HaveMouseCapture()) {
6048 ptMouseLast = Point(-1,-1);
6049 DwellEnd(true);
6053 static bool AllowVirtualSpace(int virtualSpaceOptions, bool rectangular) {
6054 return ((virtualSpaceOptions & SCVS_USERACCESSIBLE) != 0)
6055 || (rectangular && ((virtualSpaceOptions & SCVS_RECTANGULARSELECTION) != 0));
6058 void Editor::ButtonDown(Point pt, unsigned int curTime, bool shift, bool ctrl, bool alt) {
6059 //Platform::DebugPrintf("ButtonDown %d %d = %d alt=%d %d\n", curTime, lastClickTime, curTime - lastClickTime, alt, inDragDrop);
6060 ptMouseLast = pt;
6061 SelectionPosition newPos = SPositionFromLocation(pt, false, false, AllowVirtualSpace(virtualSpaceOptions, alt));
6062 newPos = MovePositionOutsideChar(newPos, sel.MainCaret() - newPos.Position());
6063 inDragDrop = ddNone;
6064 sel.SetMoveExtends(false);
6066 bool processed = NotifyMarginClick(pt, shift, ctrl, alt);
6067 if (processed)
6068 return;
6070 NotifyIndicatorClick(true, newPos.Position(), shift, ctrl, alt);
6072 bool inSelMargin = PointInSelMargin(pt);
6073 if (shift & !inSelMargin) {
6074 SetSelection(newPos.Position());
6076 if (((curTime - lastClickTime) < Platform::DoubleClickTime()) && Close(pt, lastClick)) {
6077 //Platform::DebugPrintf("Double click %d %d = %d\n", curTime, lastClickTime, curTime - lastClickTime);
6078 SetMouseCapture(true);
6079 SetEmptySelection(newPos.Position());
6080 bool doubleClick = false;
6081 // Stop mouse button bounce changing selection type
6082 if (!Platform::MouseButtonBounce() || curTime != lastClickTime) {
6083 if (selectionType == selChar) {
6084 selectionType = selWord;
6085 doubleClick = true;
6086 } else if (selectionType == selWord) {
6087 selectionType = selLine;
6088 } else {
6089 selectionType = selChar;
6090 originalAnchorPos = sel.MainCaret();
6094 if (selectionType == selWord) {
6095 int charPos = originalAnchorPos;
6096 if (sel.MainCaret() == originalAnchorPos) {
6097 charPos = PositionFromLocation(pt, false, true);
6098 charPos = MovePositionOutsideChar(charPos, -1);
6101 int startWord, endWord;
6102 if ((sel.MainCaret() >= originalAnchorPos) && !pdoc->IsLineEndPosition(charPos)) {
6103 startWord = pdoc->ExtendWordSelect(pdoc->MovePositionOutsideChar(charPos + 1, 1), -1);
6104 endWord = pdoc->ExtendWordSelect(charPos, 1);
6105 } else {
6106 // Selecting backwards, or anchor beyond last character on line. In these cases,
6107 // we select the word containing the character to the *left* of the anchor.
6108 if (charPos > pdoc->LineStart(pdoc->LineFromPosition(charPos))) {
6109 startWord = pdoc->ExtendWordSelect(charPos, -1);
6110 endWord = pdoc->ExtendWordSelect(startWord, 1);
6111 } else {
6112 // Anchor at start of line; select nothing to begin with.
6113 startWord = charPos;
6114 endWord = charPos;
6118 wordSelectAnchorStartPos = startWord;
6119 wordSelectAnchorEndPos = endWord;
6120 wordSelectInitialCaretPos = sel.MainCaret();
6121 WordSelection(wordSelectInitialCaretPos);
6122 } else if (selectionType == selLine) {
6123 lineAnchor = LineFromLocation(pt);
6124 SetSelection(pdoc->LineStart(lineAnchor + 1), pdoc->LineStart(lineAnchor));
6125 //Platform::DebugPrintf("Triple click: %d - %d\n", anchor, currentPos);
6126 } else {
6127 SetEmptySelection(sel.MainCaret());
6129 //Platform::DebugPrintf("Double click: %d - %d\n", anchor, currentPos);
6130 if (doubleClick) {
6131 NotifyDoubleClick(pt, shift, ctrl, alt);
6132 if (PositionIsHotspot(newPos.Position()))
6133 NotifyHotSpotDoubleClicked(newPos.Position(), shift, ctrl, alt);
6135 } else { // Single click
6136 if (inSelMargin) {
6137 sel.selType = Selection::selStream;
6138 if (ctrl) {
6139 SelectAll();
6140 lastClickTime = curTime;
6141 return;
6143 if (!shift) {
6144 lineAnchor = LineFromLocation(pt);
6145 // Single click in margin: select whole line
6146 LineSelection(lineAnchor, lineAnchor);
6147 SetSelection(pdoc->LineStart(lineAnchor + 1),
6148 pdoc->LineStart(lineAnchor));
6149 } else {
6150 // Single shift+click in margin: select from line anchor to clicked line
6151 if (sel.MainAnchor() > sel.MainCaret())
6152 lineAnchor = pdoc->LineFromPosition(sel.MainAnchor() - 1);
6153 else
6154 lineAnchor = pdoc->LineFromPosition(sel.MainAnchor());
6155 int lineStart = LineFromLocation(pt);
6156 LineSelection(lineStart, lineAnchor);
6157 //lineAnchor = lineStart; // Keep the same anchor for ButtonMove
6160 SetDragPosition(SelectionPosition(invalidPosition));
6161 SetMouseCapture(true);
6162 selectionType = selLine;
6163 } else {
6164 if (PointIsHotspot(pt)) {
6165 NotifyHotSpotClicked(newPos.Position(), shift, ctrl, alt);
6166 hotSpotClickPos = PositionFromLocation(pt,true,false);
6168 if (!shift) {
6169 if (PointInSelection(pt) && !SelectionEmpty())
6170 inDragDrop = ddInitial;
6171 else
6172 inDragDrop = ddNone;
6174 SetMouseCapture(true);
6175 if (inDragDrop != ddInitial) {
6176 SetDragPosition(SelectionPosition(invalidPosition));
6177 if (!shift) {
6178 if (ctrl && multipleSelection) {
6179 SelectionRange range(newPos);
6180 sel.TentativeSelection(range);
6181 InvalidateSelection(range, true);
6182 } else {
6183 InvalidateSelection(SelectionRange(newPos), true);
6184 if (sel.Count() > 1)
6185 Redraw();
6186 if ((sel.Count() > 1) || (sel.selType != Selection::selStream))
6187 sel.Clear();
6188 sel.selType = alt ? Selection::selRectangle : Selection::selStream;
6189 SetSelection(newPos, newPos);
6192 SelectionPosition anchorCurrent = newPos;
6193 if (shift)
6194 anchorCurrent = sel.IsRectangular() ?
6195 sel.Rectangular().anchor : sel.RangeMain().anchor;
6196 sel.selType = alt ? Selection::selRectangle : Selection::selStream;
6197 selectionType = selChar;
6198 originalAnchorPos = sel.MainCaret();
6199 sel.Rectangular() = SelectionRange(newPos, anchorCurrent);
6200 SetRectangularRange();
6204 lastClickTime = curTime;
6205 lastXChosen = pt.x + xOffset;
6206 ShowCaretAtCurrentPosition();
6209 bool Editor::PositionIsHotspot(int position) {
6210 return vs.styles[pdoc->StyleAt(position) & pdoc->stylingBitsMask].hotspot;
6213 bool Editor::PointIsHotspot(Point pt) {
6214 int pos = PositionFromLocation(pt, true);
6215 if (pos == INVALID_POSITION)
6216 return false;
6217 return PositionIsHotspot(pos);
6220 void Editor::SetHotSpotRange(Point *pt) {
6221 if (pt) {
6222 int pos = PositionFromLocation(*pt);
6224 // If we don't limit this to word characters then the
6225 // range can encompass more than the run range and then
6226 // the underline will not be drawn properly.
6227 int hsStart_ = pdoc->ExtendStyleRange(pos, -1, vs.hotspotSingleLine);
6228 int hsEnd_ = pdoc->ExtendStyleRange(pos, 1, vs.hotspotSingleLine);
6230 // Only invalidate the range if the hotspot range has changed...
6231 if (hsStart_ != hsStart || hsEnd_ != hsEnd) {
6232 if (hsStart != -1) {
6233 InvalidateRange(hsStart, hsEnd);
6235 hsStart = hsStart_;
6236 hsEnd = hsEnd_;
6237 InvalidateRange(hsStart, hsEnd);
6239 } else {
6240 if (hsStart != -1) {
6241 int hsStart_ = hsStart;
6242 int hsEnd_ = hsEnd;
6243 hsStart = -1;
6244 hsEnd = -1;
6245 InvalidateRange(hsStart_, hsEnd_);
6246 } else {
6247 hsStart = -1;
6248 hsEnd = -1;
6253 void Editor::GetHotSpotRange(int &hsStart_, int &hsEnd_) {
6254 hsStart_ = hsStart;
6255 hsEnd_ = hsEnd;
6258 void Editor::ButtonMove(Point pt) {
6259 if ((ptMouseLast.x != pt.x) || (ptMouseLast.y != pt.y)) {
6260 DwellEnd(true);
6263 SelectionPosition movePos = SPositionFromLocation(pt, false, false,
6264 AllowVirtualSpace(virtualSpaceOptions, sel.IsRectangular()));
6265 movePos = MovePositionOutsideChar(movePos, sel.MainCaret() - movePos.Position());
6267 if (inDragDrop == ddInitial) {
6268 if (DragThreshold(ptMouseLast, pt)) {
6269 SetMouseCapture(false);
6270 SetDragPosition(movePos);
6271 CopySelectionRange(&drag);
6272 StartDrag();
6274 return;
6277 ptMouseLast = pt;
6278 //Platform::DebugPrintf("Move %d %d\n", pt.x, pt.y);
6279 if (HaveMouseCapture()) {
6281 // Slow down autoscrolling/selection
6282 autoScrollTimer.ticksToWait -= timer.tickSize;
6283 if (autoScrollTimer.ticksToWait > 0)
6284 return;
6285 autoScrollTimer.ticksToWait = autoScrollDelay;
6287 // Adjust selection
6288 if (posDrag.IsValid()) {
6289 SetDragPosition(movePos);
6290 } else {
6291 if (selectionType == selChar) {
6292 if (sel.IsRectangular()) {
6293 sel.Rectangular() = SelectionRange(movePos, sel.Rectangular().anchor);
6294 SetSelection(movePos, sel.RangeMain().anchor);
6295 } else if (sel.Count() > 1) {
6296 SelectionRange range(movePos, sel.RangeMain().anchor);
6297 sel.TentativeSelection(range);
6298 InvalidateSelection(range, true);
6299 } else {
6300 SetSelection(movePos, sel.RangeMain().anchor);
6302 } else if (selectionType == selWord) {
6303 // Continue selecting by word
6304 if (movePos.Position() == wordSelectInitialCaretPos) { // Didn't move
6305 // No need to do anything. Previously this case was lumped
6306 // in with "Moved forward", but that can be harmful in this
6307 // case: a handler for the NotifyDoubleClick re-adjusts
6308 // the selection for a fancier definition of "word" (for
6309 // example, in Perl it is useful to include the leading
6310 // '$', '%' or '@' on variables for word selection). In this
6311 // the ButtonMove() called via Tick() for auto-scrolling
6312 // could result in the fancier word selection adjustment
6313 // being unmade.
6314 } else {
6315 wordSelectInitialCaretPos = -1;
6316 WordSelection(movePos.Position());
6318 } else {
6319 // Continue selecting by line
6320 int lineMove = LineFromLocation(pt);
6321 LineSelection(lineMove, lineAnchor);
6325 // Autoscroll
6326 PRectangle rcClient = GetClientRectangle();
6327 if (pt.y > rcClient.bottom) {
6328 int lineMove = cs.DisplayFromDoc(LineFromLocation(pt));
6329 if (lineMove < 0) {
6330 lineMove = cs.DisplayFromDoc(pdoc->LinesTotal() - 1);
6332 ScrollTo(lineMove - LinesOnScreen() + 1);
6333 Redraw();
6334 } else if (pt.y < rcClient.top) {
6335 int lineMove = cs.DisplayFromDoc(LineFromLocation(pt));
6336 ScrollTo(lineMove - 1);
6337 Redraw();
6339 EnsureCaretVisible(false, false, true);
6341 if (hsStart != -1 && !PositionIsHotspot(movePos.Position()))
6342 SetHotSpotRange(NULL);
6344 if (hotSpotClickPos != INVALID_POSITION && PositionFromLocation(pt,true,false) != hotSpotClickPos) {
6345 if (inDragDrop == ddNone) {
6346 DisplayCursor(Window::cursorText);
6348 hotSpotClickPos = INVALID_POSITION;
6351 } else {
6352 if (vs.fixedColumnWidth > 0) { // There is a margin
6353 if (PointInSelMargin(pt)) {
6354 DisplayCursor(GetMarginCursor(pt));
6355 SetHotSpotRange(NULL);
6356 return; // No need to test for selection
6359 // Display regular (drag) cursor over selection
6360 if (PointInSelection(pt) && !SelectionEmpty()) {
6361 DisplayCursor(Window::cursorArrow);
6362 } else if (PointIsHotspot(pt)) {
6363 DisplayCursor(Window::cursorHand);
6364 SetHotSpotRange(&pt);
6365 } else {
6366 DisplayCursor(Window::cursorText);
6367 SetHotSpotRange(NULL);
6372 void Editor::ButtonUp(Point pt, unsigned int curTime, bool ctrl) {
6373 //Platform::DebugPrintf("ButtonUp %d %d\n", HaveMouseCapture(), inDragDrop);
6374 SelectionPosition newPos = SPositionFromLocation(pt, false, false,
6375 AllowVirtualSpace(virtualSpaceOptions, sel.IsRectangular()));
6376 newPos = MovePositionOutsideChar(newPos, sel.MainCaret() - newPos.Position());
6377 if (inDragDrop == ddInitial) {
6378 inDragDrop = ddNone;
6379 SetEmptySelection(newPos.Position());
6380 selectionType = selChar;
6381 originalAnchorPos = sel.MainCaret();
6383 if (hotSpotClickPos != INVALID_POSITION && PointIsHotspot(pt)) {
6384 hotSpotClickPos = INVALID_POSITION;
6385 NotifyHotSpotReleaseClick(newPos.Position(), false, ctrl, false);
6387 if (HaveMouseCapture()) {
6388 if (PointInSelMargin(pt)) {
6389 DisplayCursor(GetMarginCursor(pt));
6390 } else {
6391 DisplayCursor(Window::cursorText);
6392 SetHotSpotRange(NULL);
6394 ptMouseLast = pt;
6395 SetMouseCapture(false);
6396 NotifyIndicatorClick(false, newPos.Position(), false, false, false);
6397 if (inDragDrop == ddDragging) {
6398 SelectionPosition selStart = SelectionStart();
6399 SelectionPosition selEnd = SelectionEnd();
6400 if (selStart < selEnd) {
6401 if (drag.len) {
6402 if (ctrl) {
6403 if (pdoc->InsertString(newPos.Position(), drag.s, drag.len)) {
6404 SetSelection(newPos.Position(), newPos.Position() + drag.len);
6406 } else if (newPos < selStart) {
6407 pdoc->DeleteChars(selStart.Position(), drag.len);
6408 if (pdoc->InsertString(newPos.Position(), drag.s, drag.len)) {
6409 SetSelection(newPos.Position(), newPos.Position() + drag.len);
6411 } else if (newPos > selEnd) {
6412 pdoc->DeleteChars(selStart.Position(), drag.len);
6413 newPos.Add(-drag.len);
6414 if (pdoc->InsertString(newPos.Position(), drag.s, drag.len)) {
6415 SetSelection(newPos.Position(), newPos.Position() + drag.len);
6417 } else {
6418 SetEmptySelection(newPos.Position());
6420 drag.Free();
6422 selectionType = selChar;
6424 } else {
6425 if (selectionType == selChar) {
6426 if (sel.Count() > 1) {
6427 sel.RangeMain() =
6428 SelectionRange(newPos, sel.Range(sel.Count() - 1).anchor);
6429 InvalidateSelection(sel.RangeMain(), true);
6430 } else {
6431 SetSelection(newPos, sel.RangeMain().anchor);
6434 sel.CommitTentative();
6436 SetRectangularRange();
6437 lastClickTime = curTime;
6438 lastClick = pt;
6439 lastXChosen = pt.x + xOffset;
6440 if (sel.selType == Selection::selStream) {
6441 SetLastXChosen();
6443 inDragDrop = ddNone;
6444 EnsureCaretVisible(false);
6448 // Called frequently to perform background UI including
6449 // caret blinking and automatic scrolling.
6450 void Editor::Tick() {
6451 if (HaveMouseCapture()) {
6452 // Auto scroll
6453 ButtonMove(ptMouseLast);
6455 if (caret.period > 0) {
6456 timer.ticksToWait -= timer.tickSize;
6457 if (timer.ticksToWait <= 0) {
6458 caret.on = !caret.on;
6459 timer.ticksToWait = caret.period;
6460 if (caret.active) {
6461 InvalidateCaret();
6465 if (horizontalScrollBarVisible && trackLineWidth && (lineWidthMaxSeen > scrollWidth)) {
6466 scrollWidth = lineWidthMaxSeen;
6467 SetScrollBars();
6469 if ((dwellDelay < SC_TIME_FOREVER) &&
6470 (ticksToDwell > 0) &&
6471 (!HaveMouseCapture()) &&
6472 (ptMouseLast.y >= 0)) {
6473 ticksToDwell -= timer.tickSize;
6474 if (ticksToDwell <= 0) {
6475 dwelling = true;
6476 NotifyDwelling(ptMouseLast, dwelling);
6481 bool Editor::Idle() {
6483 bool idleDone;
6485 bool wrappingDone = wrapState == eWrapNone;
6487 if (!wrappingDone) {
6488 // Wrap lines during idle.
6489 WrapLines(false, -1);
6490 // No more wrapping
6491 if (wrapStart == wrapEnd)
6492 wrappingDone = true;
6495 // Add more idle things to do here, but make sure idleDone is
6496 // set correctly before the function returns. returning
6497 // false will stop calling this idle funtion until SetIdle() is
6498 // called again.
6500 idleDone = wrappingDone; // && thatDone && theOtherThingDone...
6502 return !idleDone;
6505 void Editor::SetFocusState(bool focusState) {
6506 hasFocus = focusState;
6507 NotifyFocus(hasFocus);
6508 if (hasFocus) {
6509 ShowCaretAtCurrentPosition();
6510 } else {
6511 CancelModes();
6512 DropCaret();
6516 int Editor::PositionAfterArea(PRectangle rcArea) {
6517 // The start of the document line after the display line after the area
6518 // This often means that the line after a modification is restyled which helps
6519 // detect multiline comment additions and heals single line comments
6520 int lineAfter = topLine + (rcArea.bottom - 1) / vs.lineHeight + 1;
6521 if (lineAfter < cs.LinesDisplayed())
6522 return pdoc->LineStart(cs.DocFromDisplay(lineAfter) + 1);
6523 else
6524 return pdoc->Length();
6527 // Style to a position within the view. If this causes a change at end of last line then
6528 // affects later lines so style all the viewed text.
6529 void Editor::StyleToPositionInView(Position pos) {
6530 int endWindow = PositionAfterArea(GetClientRectangle());
6531 if (pos > endWindow)
6532 pos = endWindow;
6533 int styleAtEnd = pdoc->StyleAt(pos-1);
6534 pdoc->EnsureStyledTo(pos);
6535 if ((endWindow > pos) && (styleAtEnd != pdoc->StyleAt(pos-1))) {
6536 // Style at end of line changed so is multi-line change like starting a comment
6537 // so require rest of window to be styled.
6538 pdoc->EnsureStyledTo(endWindow);
6542 void Editor::IdleStyling() {
6543 // Style the line after the modification as this allows modifications that change just the
6544 // line of the modification to heal instead of propagating to the rest of the window.
6545 StyleToPositionInView(pdoc->LineStart(pdoc->LineFromPosition(styleNeeded.upTo) + 2));
6547 if (needUpdateUI) {
6548 NotifyUpdateUI();
6549 needUpdateUI = 0;
6551 styleNeeded.Reset();
6554 void Editor::QueueStyling(int upTo) {
6555 styleNeeded.NeedUpTo(upTo);
6558 bool Editor::PaintContains(PRectangle rc) {
6559 if (rc.Empty()) {
6560 return true;
6561 } else {
6562 return rcPaint.Contains(rc);
6566 bool Editor::PaintContainsMargin() {
6567 PRectangle rcSelMargin = GetClientRectangle();
6568 rcSelMargin.right = vs.fixedColumnWidth;
6569 return PaintContains(rcSelMargin);
6572 void Editor::CheckForChangeOutsidePaint(Range r) {
6573 if (paintState == painting && !paintingAllText) {
6574 //Platform::DebugPrintf("Checking range in paint %d-%d\n", r.start, r.end);
6575 if (!r.Valid())
6576 return;
6578 PRectangle rcRange = RectangleFromRange(r.start, r.end);
6579 PRectangle rcText = GetTextRectangle();
6580 if (rcRange.top < rcText.top) {
6581 rcRange.top = rcText.top;
6583 if (rcRange.bottom > rcText.bottom) {
6584 rcRange.bottom = rcText.bottom;
6587 if (!PaintContains(rcRange)) {
6588 AbandonPaint();
6593 void Editor::SetBraceHighlight(Position pos0, Position pos1, int matchStyle) {
6594 if ((pos0 != braces[0]) || (pos1 != braces[1]) || (matchStyle != bracesMatchStyle)) {
6595 if ((braces[0] != pos0) || (matchStyle != bracesMatchStyle)) {
6596 CheckForChangeOutsidePaint(Range(braces[0]));
6597 CheckForChangeOutsidePaint(Range(pos0));
6598 braces[0] = pos0;
6600 if ((braces[1] != pos1) || (matchStyle != bracesMatchStyle)) {
6601 CheckForChangeOutsidePaint(Range(braces[1]));
6602 CheckForChangeOutsidePaint(Range(pos1));
6603 braces[1] = pos1;
6605 bracesMatchStyle = matchStyle;
6606 if (paintState == notPainting) {
6607 Redraw();
6612 void Editor::SetAnnotationHeights(int start, int end) {
6613 if (vs.annotationVisible) {
6614 for (int line=start; line<end; line++) {
6615 cs.SetHeight(line, pdoc->AnnotationLines(line) + 1);
6620 void Editor::SetDocPointer(Document *document) {
6621 //Platform::DebugPrintf("** %x setdoc to %x\n", pdoc, document);
6622 pdoc->RemoveWatcher(this, 0);
6623 pdoc->Release();
6624 if (document == NULL) {
6625 pdoc = new Document();
6626 } else {
6627 pdoc = document;
6629 pdoc->AddRef();
6631 // Ensure all positions within document
6632 sel.Clear();
6633 targetStart = 0;
6634 targetEnd = 0;
6636 braces[0] = invalidPosition;
6637 braces[1] = invalidPosition;
6639 // Reset the contraction state to fully shown.
6640 cs.Clear();
6641 cs.InsertLines(0, pdoc->LinesTotal() - 1);
6642 SetAnnotationHeights(0, pdoc->LinesTotal());
6643 llc.Deallocate();
6644 NeedWrapping();
6646 pdoc->AddWatcher(this, 0);
6647 SetScrollBars();
6648 Redraw();
6651 void Editor::SetAnnotationVisible(int visible) {
6652 if (vs.annotationVisible != visible) {
6653 bool changedFromOrToHidden = ((vs.annotationVisible != 0) != (visible != 0));
6654 vs.annotationVisible = visible;
6655 if (changedFromOrToHidden) {
6656 int dir = vs.annotationVisible ? 1 : -1;
6657 for (int line=0; line<pdoc->LinesTotal(); line++) {
6658 int annotationLines = pdoc->AnnotationLines(line);
6659 if (annotationLines > 0) {
6660 cs.SetHeight(line, cs.GetHeight(line) + annotationLines * dir);
6664 Redraw();
6669 * Recursively expand a fold, making lines visible except where they have an unexpanded parent.
6671 void Editor::Expand(int &line, bool doExpand) {
6672 int lineMaxSubord = pdoc->GetLastChild(line);
6673 line++;
6674 while (line <= lineMaxSubord) {
6675 if (doExpand)
6676 cs.SetVisible(line, line, true);
6677 int level = pdoc->GetLevel(line);
6678 if (level & SC_FOLDLEVELHEADERFLAG) {
6679 if (doExpand && cs.GetExpanded(line)) {
6680 Expand(line, true);
6681 } else {
6682 Expand(line, false);
6684 } else {
6685 line++;
6690 void Editor::ToggleContraction(int line) {
6691 if (line >= 0) {
6692 if ((pdoc->GetLevel(line) & SC_FOLDLEVELHEADERFLAG) == 0) {
6693 line = pdoc->GetFoldParent(line);
6694 if (line < 0)
6695 return;
6698 if (cs.GetExpanded(line)) {
6699 int lineMaxSubord = pdoc->GetLastChild(line);
6700 if (lineMaxSubord > line) {
6701 cs.SetExpanded(line, 0);
6702 cs.SetVisible(line + 1, lineMaxSubord, false);
6704 int lineCurrent = pdoc->LineFromPosition(sel.MainCaret());
6705 if (lineCurrent > line && lineCurrent <= lineMaxSubord) {
6706 // This does not re-expand the fold
6707 EnsureCaretVisible();
6710 SetScrollBars();
6711 Redraw();
6714 } else {
6715 if (!(cs.GetVisible(line))) {
6716 EnsureLineVisible(line, false);
6717 GoToLine(line);
6719 cs.SetExpanded(line, 1);
6720 Expand(line, true);
6721 SetScrollBars();
6722 Redraw();
6727 int Editor::ContractedFoldNext(int lineStart) {
6728 for (int line = lineStart; line<pdoc->LinesTotal();) {
6729 if (!cs.GetExpanded(line) && (pdoc->GetLevel(line) & SC_FOLDLEVELHEADERFLAG))
6730 return line;
6731 line = cs.ContractedNext(line+1);
6732 if (line < 0)
6733 return -1;
6736 return -1;
6740 * Recurse up from this line to find any folds that prevent this line from being visible
6741 * and unfold them all.
6743 void Editor::EnsureLineVisible(int lineDoc, bool enforcePolicy) {
6745 // In case in need of wrapping to ensure DisplayFromDoc works.
6746 WrapLines(true, -1);
6748 if (!cs.GetVisible(lineDoc)) {
6749 int lineParent = pdoc->GetFoldParent(lineDoc);
6750 if (lineParent >= 0) {
6751 if (lineDoc != lineParent)
6752 EnsureLineVisible(lineParent, enforcePolicy);
6753 if (!cs.GetExpanded(lineParent)) {
6754 cs.SetExpanded(lineParent, 1);
6755 Expand(lineParent, true);
6758 SetScrollBars();
6759 Redraw();
6761 if (enforcePolicy) {
6762 int lineDisplay = cs.DisplayFromDoc(lineDoc);
6763 if (visiblePolicy & VISIBLE_SLOP) {
6764 if ((topLine > lineDisplay) || ((visiblePolicy & VISIBLE_STRICT) && (topLine + visibleSlop > lineDisplay))) {
6765 SetTopLine(Platform::Clamp(lineDisplay - visibleSlop, 0, MaxScrollPos()));
6766 SetVerticalScrollPos();
6767 Redraw();
6768 } else if ((lineDisplay > topLine + LinesOnScreen() - 1) ||
6769 ((visiblePolicy & VISIBLE_STRICT) && (lineDisplay > topLine + LinesOnScreen() - 1 - visibleSlop))) {
6770 SetTopLine(Platform::Clamp(lineDisplay - LinesOnScreen() + 1 + visibleSlop, 0, MaxScrollPos()));
6771 SetVerticalScrollPos();
6772 Redraw();
6774 } else {
6775 if ((topLine > lineDisplay) || (lineDisplay > topLine + LinesOnScreen() - 1) || (visiblePolicy & VISIBLE_STRICT)) {
6776 SetTopLine(Platform::Clamp(lineDisplay - LinesOnScreen() / 2 + 1, 0, MaxScrollPos()));
6777 SetVerticalScrollPos();
6778 Redraw();
6784 int Editor::GetTag(char *tagValue, int tagNumber) {
6785 char name[3] = "\\?";
6786 const char *text = 0;
6787 int length = 0;
6788 if ((tagNumber >= 1) && (tagNumber <= 9)) {
6789 name[1] = static_cast<char>(tagNumber + '0');
6790 length = 2;
6791 text = pdoc->SubstituteByPosition(name, &length);
6793 if (tagValue) {
6794 if (text)
6795 memcpy(tagValue, text, length + 1);
6796 else
6797 *tagValue = '\0';
6799 return length;
6802 int Editor::ReplaceTarget(bool replacePatterns, const char *text, int length) {
6803 UndoGroup ug(pdoc);
6804 if (length == -1)
6805 length = istrlen(text);
6806 if (replacePatterns) {
6807 text = pdoc->SubstituteByPosition(text, &length);
6808 if (!text) {
6809 return 0;
6812 if (targetStart != targetEnd)
6813 pdoc->DeleteChars(targetStart, targetEnd - targetStart);
6814 targetEnd = targetStart;
6815 pdoc->InsertString(targetStart, text, length);
6816 targetEnd = targetStart + length;
6817 return length;
6820 bool Editor::IsUnicodeMode() const {
6821 return pdoc && (SC_CP_UTF8 == pdoc->dbcsCodePage);
6824 int Editor::CodePage() const {
6825 if (pdoc)
6826 return pdoc->dbcsCodePage;
6827 else
6828 return 0;
6831 int Editor::WrapCount(int line) {
6832 AutoSurface surface(this);
6833 AutoLineLayout ll(llc, RetrieveLineLayout(line));
6835 if (surface && ll) {
6836 LayoutLine(line, surface, vs, ll, wrapWidth);
6837 return ll->lines;
6838 } else {
6839 return 1;
6843 void Editor::AddStyledText(char *buffer, int appendLength) {
6844 // The buffer consists of alternating character bytes and style bytes
6845 size_t textLength = appendLength / 2;
6846 char *text = new char[textLength];
6847 size_t i;
6848 for (i = 0; i < textLength; i++) {
6849 text[i] = buffer[i*2];
6851 pdoc->InsertString(CurrentPosition(), text, textLength);
6852 for (i = 0; i < textLength; i++) {
6853 text[i] = buffer[i*2+1];
6855 pdoc->StartStyling(CurrentPosition(), static_cast<char>(0xff));
6856 pdoc->SetStyles(textLength, text);
6857 delete []text;
6858 SetEmptySelection(sel.MainCaret() + textLength);
6861 static bool ValidMargin(unsigned long wParam) {
6862 return wParam < ViewStyle::margins;
6865 static char *CharPtrFromSPtr(sptr_t lParam) {
6866 return reinterpret_cast<char *>(lParam);
6869 void Editor::StyleSetMessage(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
6870 vs.EnsureStyle(wParam);
6871 switch (iMessage) {
6872 case SCI_STYLESETFORE:
6873 vs.styles[wParam].fore.desired = ColourDesired(lParam);
6874 break;
6875 case SCI_STYLESETBACK:
6876 vs.styles[wParam].back.desired = ColourDesired(lParam);
6877 break;
6878 case SCI_STYLESETBOLD:
6879 vs.styles[wParam].bold = lParam != 0;
6880 break;
6881 case SCI_STYLESETITALIC:
6882 vs.styles[wParam].italic = lParam != 0;
6883 break;
6884 case SCI_STYLESETEOLFILLED:
6885 vs.styles[wParam].eolFilled = lParam != 0;
6886 break;
6887 case SCI_STYLESETSIZE:
6888 vs.styles[wParam].size = lParam;
6889 break;
6890 case SCI_STYLESETFONT:
6891 if (lParam != 0) {
6892 vs.SetStyleFontName(wParam, CharPtrFromSPtr(lParam));
6894 break;
6895 case SCI_STYLESETUNDERLINE:
6896 vs.styles[wParam].underline = lParam != 0;
6897 break;
6898 case SCI_STYLESETCASE:
6899 vs.styles[wParam].caseForce = static_cast<Style::ecaseForced>(lParam);
6900 break;
6901 case SCI_STYLESETCHARACTERSET:
6902 vs.styles[wParam].characterSet = lParam;
6903 break;
6904 case SCI_STYLESETVISIBLE:
6905 vs.styles[wParam].visible = lParam != 0;
6906 break;
6907 case SCI_STYLESETCHANGEABLE:
6908 vs.styles[wParam].changeable = lParam != 0;
6909 break;
6910 case SCI_STYLESETHOTSPOT:
6911 vs.styles[wParam].hotspot = lParam != 0;
6912 break;
6914 InvalidateStyleRedraw();
6917 sptr_t Editor::StyleGetMessage(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
6918 vs.EnsureStyle(wParam);
6919 switch (iMessage) {
6920 case SCI_STYLEGETFORE:
6921 return vs.styles[wParam].fore.desired.AsLong();
6922 case SCI_STYLEGETBACK:
6923 return vs.styles[wParam].back.desired.AsLong();
6924 case SCI_STYLEGETBOLD:
6925 return vs.styles[wParam].bold ? 1 : 0;
6926 case SCI_STYLEGETITALIC:
6927 return vs.styles[wParam].italic ? 1 : 0;
6928 case SCI_STYLEGETEOLFILLED:
6929 return vs.styles[wParam].eolFilled ? 1 : 0;
6930 case SCI_STYLEGETSIZE:
6931 return vs.styles[wParam].size;
6932 case SCI_STYLEGETFONT:
6933 if (!vs.styles[wParam].fontName)
6934 return 0;
6935 if (lParam != 0)
6936 strcpy(CharPtrFromSPtr(lParam), vs.styles[wParam].fontName);
6937 return strlen(vs.styles[wParam].fontName);
6938 case SCI_STYLEGETUNDERLINE:
6939 return vs.styles[wParam].underline ? 1 : 0;
6940 case SCI_STYLEGETCASE:
6941 return static_cast<int>(vs.styles[wParam].caseForce);
6942 case SCI_STYLEGETCHARACTERSET:
6943 return vs.styles[wParam].characterSet;
6944 case SCI_STYLEGETVISIBLE:
6945 return vs.styles[wParam].visible ? 1 : 0;
6946 case SCI_STYLEGETCHANGEABLE:
6947 return vs.styles[wParam].changeable ? 1 : 0;
6948 case SCI_STYLEGETHOTSPOT:
6949 return vs.styles[wParam].hotspot ? 1 : 0;
6951 return 0;
6954 sptr_t Editor::StringResult(sptr_t lParam, const char *val) {
6955 const int n = strlen(val);
6956 if (lParam != 0) {
6957 char *ptr = reinterpret_cast<char *>(lParam);
6958 strcpy(ptr, val);
6960 return n; // Not including NUL
6963 sptr_t Editor::WndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
6964 //Platform::DebugPrintf("S start wnd proc %d %d %d\n",iMessage, wParam, lParam);
6966 // Optional macro recording hook
6967 if (recordingMacro)
6968 NotifyMacroRecord(iMessage, wParam, lParam);
6970 switch (iMessage) {
6972 case SCI_GETTEXT: {
6973 if (lParam == 0)
6974 return pdoc->Length() + 1;
6975 if (wParam == 0)
6976 return 0;
6977 char *ptr = CharPtrFromSPtr(lParam);
6978 unsigned int iChar = 0;
6979 for (; iChar < wParam - 1; iChar++)
6980 ptr[iChar] = pdoc->CharAt(iChar);
6981 ptr[iChar] = '\0';
6982 return iChar;
6985 case SCI_SETTEXT: {
6986 if (lParam == 0)
6987 return 0;
6988 UndoGroup ug(pdoc);
6989 pdoc->DeleteChars(0, pdoc->Length());
6990 SetEmptySelection(0);
6991 pdoc->InsertCString(0, CharPtrFromSPtr(lParam));
6992 return 1;
6995 case SCI_GETTEXTLENGTH:
6996 return pdoc->Length();
6998 case SCI_CUT:
6999 Cut();
7000 SetLastXChosen();
7001 break;
7003 case SCI_COPY:
7004 Copy();
7005 break;
7007 case SCI_COPYALLOWLINE:
7008 CopyAllowLine();
7009 break;
7011 case SCI_VERTICALCENTRECARET:
7012 VerticalCentreCaret();
7013 break;
7015 case SCI_MOVESELECTEDLINESUP:
7016 MoveSelectedLinesUp();
7017 break;
7019 case SCI_MOVESELECTEDLINESDOWN:
7020 MoveSelectedLinesDown();
7021 break;
7023 case SCI_COPYRANGE:
7024 CopyRangeToClipboard(wParam, lParam);
7025 break;
7027 case SCI_COPYTEXT:
7028 CopyText(wParam, CharPtrFromSPtr(lParam));
7029 break;
7031 case SCI_PASTE:
7032 Paste();
7033 if ((caretSticky == SC_CARETSTICKY_OFF) || (caretSticky == SC_CARETSTICKY_WHITESPACE)) {
7034 SetLastXChosen();
7036 EnsureCaretVisible();
7037 break;
7039 case SCI_CLEAR:
7040 Clear();
7041 SetLastXChosen();
7042 EnsureCaretVisible();
7043 break;
7045 case SCI_UNDO:
7046 Undo();
7047 SetLastXChosen();
7048 break;
7050 case SCI_CANUNDO:
7051 return (pdoc->CanUndo() && !pdoc->IsReadOnly()) ? 1 : 0;
7053 case SCI_EMPTYUNDOBUFFER:
7054 pdoc->DeleteUndoHistory();
7055 return 0;
7057 case SCI_GETFIRSTVISIBLELINE:
7058 return topLine;
7060 case SCI_SETFIRSTVISIBLELINE:
7061 ScrollTo(wParam);
7062 break;
7064 case SCI_GETLINE: { // Risk of overwriting the end of the buffer
7065 int lineStart = pdoc->LineStart(wParam);
7066 int lineEnd = pdoc->LineStart(wParam + 1);
7067 if (lParam == 0) {
7068 return lineEnd - lineStart;
7070 char *ptr = CharPtrFromSPtr(lParam);
7071 int iPlace = 0;
7072 for (int iChar = lineStart; iChar < lineEnd; iChar++) {
7073 ptr[iPlace++] = pdoc->CharAt(iChar);
7075 return iPlace;
7078 case SCI_GETLINECOUNT:
7079 if (pdoc->LinesTotal() == 0)
7080 return 1;
7081 else
7082 return pdoc->LinesTotal();
7084 case SCI_GETMODIFY:
7085 return !pdoc->IsSavePoint();
7087 case SCI_SETSEL: {
7088 int nStart = static_cast<int>(wParam);
7089 int nEnd = static_cast<int>(lParam);
7090 if (nEnd < 0)
7091 nEnd = pdoc->Length();
7092 if (nStart < 0)
7093 nStart = nEnd; // Remove selection
7094 InvalidateSelection(SelectionRange(nStart, nEnd));
7095 sel.Clear();
7096 sel.selType = Selection::selStream;
7097 SetSelection(nEnd, nStart);
7098 EnsureCaretVisible();
7100 break;
7102 case SCI_GETSELTEXT: {
7103 SelectionText selectedText;
7104 CopySelectionRange(&selectedText);
7105 if (lParam == 0) {
7106 return selectedText.len ? selectedText.len : 1;
7107 } else {
7108 char *ptr = CharPtrFromSPtr(lParam);
7109 int iChar = 0;
7110 if (selectedText.len) {
7111 for (; iChar < selectedText.len; iChar++)
7112 ptr[iChar] = selectedText.s[iChar];
7113 } else {
7114 ptr[0] = '\0';
7116 return iChar;
7120 case SCI_LINEFROMPOSITION:
7121 if (static_cast<int>(wParam) < 0)
7122 return 0;
7123 return pdoc->LineFromPosition(wParam);
7125 case SCI_POSITIONFROMLINE:
7126 if (static_cast<int>(wParam) < 0)
7127 wParam = pdoc->LineFromPosition(SelectionStart().Position());
7128 if (wParam == 0)
7129 return 0; // Even if there is no text, there is a first line that starts at 0
7130 if (static_cast<int>(wParam) > pdoc->LinesTotal())
7131 return -1;
7132 //if (wParam > pdoc->LineFromPosition(pdoc->Length())) // Useful test, anyway...
7133 // return -1;
7134 return pdoc->LineStart(wParam);
7136 // Replacement of the old Scintilla interpretation of EM_LINELENGTH
7137 case SCI_LINELENGTH:
7138 if ((static_cast<int>(wParam) < 0) ||
7139 (static_cast<int>(wParam) > pdoc->LineFromPosition(pdoc->Length())))
7140 return 0;
7141 return pdoc->LineStart(wParam + 1) - pdoc->LineStart(wParam);
7143 case SCI_REPLACESEL: {
7144 if (lParam == 0)
7145 return 0;
7146 UndoGroup ug(pdoc);
7147 ClearSelection();
7148 char *replacement = CharPtrFromSPtr(lParam);
7149 pdoc->InsertCString(sel.MainCaret(), replacement);
7150 SetEmptySelection(sel.MainCaret() + istrlen(replacement));
7151 EnsureCaretVisible();
7153 break;
7155 case SCI_SETTARGETSTART:
7156 targetStart = wParam;
7157 break;
7159 case SCI_GETTARGETSTART:
7160 return targetStart;
7162 case SCI_SETTARGETEND:
7163 targetEnd = wParam;
7164 break;
7166 case SCI_GETTARGETEND:
7167 return targetEnd;
7169 case SCI_TARGETFROMSELECTION:
7170 if (sel.MainCaret() < sel.MainAnchor()) {
7171 targetStart = sel.MainCaret();
7172 targetEnd = sel.MainAnchor();
7173 } else {
7174 targetStart = sel.MainAnchor();
7175 targetEnd = sel.MainCaret();
7177 break;
7179 case SCI_REPLACETARGET:
7180 PLATFORM_ASSERT(lParam);
7181 return ReplaceTarget(false, CharPtrFromSPtr(lParam), wParam);
7183 case SCI_REPLACETARGETRE:
7184 PLATFORM_ASSERT(lParam);
7185 return ReplaceTarget(true, CharPtrFromSPtr(lParam), wParam);
7187 case SCI_SEARCHINTARGET:
7188 PLATFORM_ASSERT(lParam);
7189 return SearchInTarget(CharPtrFromSPtr(lParam), wParam);
7191 case SCI_SETSEARCHFLAGS:
7192 searchFlags = wParam;
7193 break;
7195 case SCI_GETSEARCHFLAGS:
7196 return searchFlags;
7198 case SCI_GETTAG:
7199 return GetTag(CharPtrFromSPtr(lParam), wParam);
7201 case SCI_POSITIONBEFORE:
7202 return pdoc->MovePositionOutsideChar(wParam - 1, -1, true);
7204 case SCI_POSITIONAFTER:
7205 return pdoc->MovePositionOutsideChar(wParam + 1, 1, true);
7207 case SCI_LINESCROLL:
7208 ScrollTo(topLine + lParam);
7209 HorizontalScrollTo(xOffset + wParam * vs.spaceWidth);
7210 return 1;
7212 case SCI_SETXOFFSET:
7213 xOffset = wParam;
7214 ContainerNeedsUpdate(SC_UPDATE_H_SCROLL);
7215 SetHorizontalScrollPos();
7216 Redraw();
7217 break;
7219 case SCI_GETXOFFSET:
7220 return xOffset;
7222 case SCI_CHOOSECARETX:
7223 SetLastXChosen();
7224 break;
7226 case SCI_SCROLLCARET:
7227 EnsureCaretVisible();
7228 break;
7230 case SCI_SETREADONLY:
7231 pdoc->SetReadOnly(wParam != 0);
7232 return 1;
7234 case SCI_GETREADONLY:
7235 return pdoc->IsReadOnly();
7237 case SCI_CANPASTE:
7238 return CanPaste();
7240 case SCI_POINTXFROMPOSITION:
7241 if (lParam < 0) {
7242 return 0;
7243 } else {
7244 Point pt = LocationFromPosition(lParam);
7245 return pt.x;
7248 case SCI_POINTYFROMPOSITION:
7249 if (lParam < 0) {
7250 return 0;
7251 } else {
7252 Point pt = LocationFromPosition(lParam);
7253 return pt.y;
7256 case SCI_FINDTEXT:
7257 return FindText(wParam, lParam);
7259 case SCI_GETTEXTRANGE: {
7260 if (lParam == 0)
7261 return 0;
7262 Sci_TextRange *tr = reinterpret_cast<Sci_TextRange *>(lParam);
7263 int cpMax = tr->chrg.cpMax;
7264 if (cpMax == -1)
7265 cpMax = pdoc->Length();
7266 PLATFORM_ASSERT(cpMax <= pdoc->Length());
7267 int len = cpMax - tr->chrg.cpMin; // No -1 as cpMin and cpMax are referring to inter character positions
7268 pdoc->GetCharRange(tr->lpstrText, tr->chrg.cpMin, len);
7269 // Spec says copied text is terminated with a NUL
7270 tr->lpstrText[len] = '\0';
7271 return len; // Not including NUL
7274 case SCI_HIDESELECTION:
7275 hideSelection = wParam != 0;
7276 Redraw();
7277 break;
7279 case SCI_FORMATRANGE:
7280 return FormatRange(wParam != 0, reinterpret_cast<Sci_RangeToFormat *>(lParam));
7282 case SCI_GETMARGINLEFT:
7283 return vs.leftMarginWidth;
7285 case SCI_GETMARGINRIGHT:
7286 return vs.rightMarginWidth;
7288 case SCI_SETMARGINLEFT:
7289 vs.leftMarginWidth = lParam;
7290 InvalidateStyleRedraw();
7291 break;
7293 case SCI_SETMARGINRIGHT:
7294 vs.rightMarginWidth = lParam;
7295 InvalidateStyleRedraw();
7296 break;
7298 // Control specific mesages
7300 case SCI_ADDTEXT: {
7301 if (lParam == 0)
7302 return 0;
7303 pdoc->InsertString(CurrentPosition(), CharPtrFromSPtr(lParam), wParam);
7304 SetEmptySelection(sel.MainCaret() + wParam);
7305 return 0;
7308 case SCI_ADDSTYLEDTEXT:
7309 if (lParam)
7310 AddStyledText(CharPtrFromSPtr(lParam), wParam);
7311 return 0;
7313 case SCI_INSERTTEXT: {
7314 if (lParam == 0)
7315 return 0;
7316 int insertPos = wParam;
7317 if (static_cast<int>(wParam) == -1)
7318 insertPos = CurrentPosition();
7319 int newCurrent = CurrentPosition();
7320 char *sz = CharPtrFromSPtr(lParam);
7321 pdoc->InsertCString(insertPos, sz);
7322 if (newCurrent > insertPos)
7323 newCurrent += istrlen(sz);
7324 SetEmptySelection(newCurrent);
7325 return 0;
7328 case SCI_APPENDTEXT:
7329 pdoc->InsertString(pdoc->Length(), CharPtrFromSPtr(lParam), wParam);
7330 return 0;
7332 case SCI_CLEARALL:
7333 ClearAll();
7334 return 0;
7336 case SCI_CLEARDOCUMENTSTYLE:
7337 ClearDocumentStyle();
7338 return 0;
7340 case SCI_SETUNDOCOLLECTION:
7341 pdoc->SetUndoCollection(wParam != 0);
7342 return 0;
7344 case SCI_GETUNDOCOLLECTION:
7345 return pdoc->IsCollectingUndo();
7347 case SCI_BEGINUNDOACTION:
7348 pdoc->BeginUndoAction();
7349 return 0;
7351 case SCI_ENDUNDOACTION:
7352 pdoc->EndUndoAction();
7353 return 0;
7355 case SCI_GETCARETPERIOD:
7356 return caret.period;
7358 case SCI_SETCARETPERIOD:
7359 caret.period = wParam;
7360 break;
7362 case SCI_SETWORDCHARS: {
7363 pdoc->SetDefaultCharClasses(false);
7364 if (lParam == 0)
7365 return 0;
7366 pdoc->SetCharClasses(reinterpret_cast<unsigned char *>(lParam), CharClassify::ccWord);
7368 break;
7370 case SCI_SETWHITESPACECHARS: {
7371 if (lParam == 0)
7372 return 0;
7373 pdoc->SetCharClasses(reinterpret_cast<unsigned char *>(lParam), CharClassify::ccSpace);
7375 break;
7377 case SCI_SETCHARSDEFAULT:
7378 pdoc->SetDefaultCharClasses(true);
7379 break;
7381 case SCI_GETLENGTH:
7382 return pdoc->Length();
7384 case SCI_ALLOCATE:
7385 pdoc->Allocate(wParam);
7386 break;
7388 case SCI_GETCHARAT:
7389 return pdoc->CharAt(wParam);
7391 case SCI_SETCURRENTPOS:
7392 if (sel.IsRectangular()) {
7393 sel.Rectangular().caret.SetPosition(wParam);
7394 SetRectangularRange();
7395 Redraw();
7396 } else {
7397 SetSelection(wParam, sel.MainAnchor());
7399 break;
7401 case SCI_GETCURRENTPOS:
7402 return sel.IsRectangular() ? sel.Rectangular().caret.Position() : sel.MainCaret();
7404 case SCI_SETANCHOR:
7405 if (sel.IsRectangular()) {
7406 sel.Rectangular().anchor.SetPosition(wParam);
7407 SetRectangularRange();
7408 Redraw();
7409 } else {
7410 SetSelection(sel.MainCaret(), wParam);
7412 break;
7414 case SCI_GETANCHOR:
7415 return sel.IsRectangular() ? sel.Rectangular().anchor.Position() : sel.MainAnchor();
7417 case SCI_SETSELECTIONSTART:
7418 SetSelection(Platform::Maximum(sel.MainCaret(), wParam), wParam);
7419 break;
7421 case SCI_GETSELECTIONSTART:
7422 return sel.LimitsForRectangularElseMain().start.Position();
7424 case SCI_SETSELECTIONEND:
7425 SetSelection(wParam, Platform::Minimum(sel.MainAnchor(), wParam));
7426 break;
7428 case SCI_GETSELECTIONEND:
7429 return sel.LimitsForRectangularElseMain().end.Position();
7431 case SCI_SETPRINTMAGNIFICATION:
7432 printMagnification = wParam;
7433 break;
7435 case SCI_GETPRINTMAGNIFICATION:
7436 return printMagnification;
7438 case SCI_SETPRINTCOLOURMODE:
7439 printColourMode = wParam;
7440 break;
7442 case SCI_GETPRINTCOLOURMODE:
7443 return printColourMode;
7445 case SCI_SETPRINTWRAPMODE:
7446 printWrapState = (wParam == SC_WRAP_WORD) ? eWrapWord : eWrapNone;
7447 break;
7449 case SCI_GETPRINTWRAPMODE:
7450 return printWrapState;
7452 case SCI_GETSTYLEAT:
7453 if (static_cast<int>(wParam) >= pdoc->Length())
7454 return 0;
7455 else
7456 return pdoc->StyleAt(wParam);
7458 case SCI_REDO:
7459 Redo();
7460 break;
7462 case SCI_SELECTALL:
7463 SelectAll();
7464 break;
7466 case SCI_SETSAVEPOINT:
7467 pdoc->SetSavePoint();
7468 break;
7470 case SCI_GETSTYLEDTEXT: {
7471 if (lParam == 0)
7472 return 0;
7473 Sci_TextRange *tr = reinterpret_cast<Sci_TextRange *>(lParam);
7474 int iPlace = 0;
7475 for (int iChar = tr->chrg.cpMin; iChar < tr->chrg.cpMax; iChar++) {
7476 tr->lpstrText[iPlace++] = pdoc->CharAt(iChar);
7477 tr->lpstrText[iPlace++] = pdoc->StyleAt(iChar);
7479 tr->lpstrText[iPlace] = '\0';
7480 tr->lpstrText[iPlace + 1] = '\0';
7481 return iPlace;
7484 case SCI_CANREDO:
7485 return (pdoc->CanRedo() && !pdoc->IsReadOnly()) ? 1 : 0;
7487 case SCI_MARKERLINEFROMHANDLE:
7488 return pdoc->LineFromHandle(wParam);
7490 case SCI_MARKERDELETEHANDLE:
7491 pdoc->DeleteMarkFromHandle(wParam);
7492 break;
7494 case SCI_GETVIEWWS:
7495 return vs.viewWhitespace;
7497 case SCI_SETVIEWWS:
7498 vs.viewWhitespace = static_cast<WhiteSpaceVisibility>(wParam);
7499 Redraw();
7500 break;
7502 case SCI_GETWHITESPACESIZE:
7503 return vs.whitespaceSize;
7505 case SCI_SETWHITESPACESIZE:
7506 vs.whitespaceSize = static_cast<int>(wParam);
7507 Redraw();
7508 break;
7510 case SCI_POSITIONFROMPOINT:
7511 return PositionFromLocation(Point(wParam, lParam), false, false);
7513 case SCI_POSITIONFROMPOINTCLOSE:
7514 return PositionFromLocation(Point(wParam, lParam), true, false);
7516 case SCI_CHARPOSITIONFROMPOINT:
7517 return PositionFromLocation(Point(wParam, lParam), false, true);
7519 case SCI_CHARPOSITIONFROMPOINTCLOSE:
7520 return PositionFromLocation(Point(wParam, lParam), true, true);
7522 case SCI_GOTOLINE:
7523 GoToLine(wParam);
7524 break;
7526 case SCI_GOTOPOS:
7527 SetEmptySelection(wParam);
7528 EnsureCaretVisible();
7529 break;
7531 case SCI_GETCURLINE: {
7532 int lineCurrentPos = pdoc->LineFromPosition(sel.MainCaret());
7533 int lineStart = pdoc->LineStart(lineCurrentPos);
7534 unsigned int lineEnd = pdoc->LineStart(lineCurrentPos + 1);
7535 if (lParam == 0) {
7536 return 1 + lineEnd - lineStart;
7538 PLATFORM_ASSERT(wParam > 0);
7539 char *ptr = CharPtrFromSPtr(lParam);
7540 unsigned int iPlace = 0;
7541 for (unsigned int iChar = lineStart; iChar < lineEnd && iPlace < wParam - 1; iChar++) {
7542 ptr[iPlace++] = pdoc->CharAt(iChar);
7544 ptr[iPlace] = '\0';
7545 return sel.MainCaret() - lineStart;
7548 case SCI_GETENDSTYLED:
7549 return pdoc->GetEndStyled();
7551 case SCI_GETEOLMODE:
7552 return pdoc->eolMode;
7554 case SCI_SETEOLMODE:
7555 pdoc->eolMode = wParam;
7556 break;
7558 case SCI_STARTSTYLING:
7559 pdoc->StartStyling(wParam, static_cast<char>(lParam));
7560 break;
7562 case SCI_SETSTYLING:
7563 pdoc->SetStyleFor(wParam, static_cast<char>(lParam));
7564 break;
7566 case SCI_SETSTYLINGEX: // Specify a complete styling buffer
7567 if (lParam == 0)
7568 return 0;
7569 pdoc->SetStyles(wParam, CharPtrFromSPtr(lParam));
7570 break;
7572 case SCI_SETBUFFEREDDRAW:
7573 bufferedDraw = wParam != 0;
7574 break;
7576 case SCI_GETBUFFEREDDRAW:
7577 return bufferedDraw;
7579 case SCI_GETTWOPHASEDRAW:
7580 return twoPhaseDraw;
7582 case SCI_SETTWOPHASEDRAW:
7583 twoPhaseDraw = wParam != 0;
7584 InvalidateStyleRedraw();
7585 break;
7587 case SCI_SETFONTQUALITY:
7588 vs.extraFontFlag &= ~SC_EFF_QUALITY_MASK;
7589 vs.extraFontFlag |= (wParam & SC_EFF_QUALITY_MASK);
7590 InvalidateStyleRedraw();
7591 break;
7593 case SCI_GETFONTQUALITY:
7594 return (vs.extraFontFlag & SC_EFF_QUALITY_MASK);
7596 case SCI_SETTABWIDTH:
7597 if (wParam > 0) {
7598 pdoc->tabInChars = wParam;
7599 if (pdoc->indentInChars == 0)
7600 pdoc->actualIndentInChars = pdoc->tabInChars;
7602 InvalidateStyleRedraw();
7603 break;
7605 case SCI_GETTABWIDTH:
7606 return pdoc->tabInChars;
7608 case SCI_SETINDENT:
7609 pdoc->indentInChars = wParam;
7610 if (pdoc->indentInChars != 0)
7611 pdoc->actualIndentInChars = pdoc->indentInChars;
7612 else
7613 pdoc->actualIndentInChars = pdoc->tabInChars;
7614 InvalidateStyleRedraw();
7615 break;
7617 case SCI_GETINDENT:
7618 return pdoc->indentInChars;
7620 case SCI_SETUSETABS:
7621 pdoc->useTabs = wParam != 0;
7622 InvalidateStyleRedraw();
7623 break;
7625 case SCI_GETUSETABS:
7626 return pdoc->useTabs;
7628 case SCI_SETLINEINDENTATION:
7629 pdoc->SetLineIndentation(wParam, lParam);
7630 break;
7632 case SCI_GETLINEINDENTATION:
7633 return pdoc->GetLineIndentation(wParam);
7635 case SCI_GETLINEINDENTPOSITION:
7636 return pdoc->GetLineIndentPosition(wParam);
7638 case SCI_SETTABINDENTS:
7639 pdoc->tabIndents = wParam != 0;
7640 break;
7642 case SCI_GETTABINDENTS:
7643 return pdoc->tabIndents;
7645 case SCI_SETBACKSPACEUNINDENTS:
7646 pdoc->backspaceUnindents = wParam != 0;
7647 break;
7649 case SCI_GETBACKSPACEUNINDENTS:
7650 return pdoc->backspaceUnindents;
7652 case SCI_SETMOUSEDWELLTIME:
7653 dwellDelay = wParam;
7654 ticksToDwell = dwellDelay;
7655 break;
7657 case SCI_GETMOUSEDWELLTIME:
7658 return dwellDelay;
7660 case SCI_WORDSTARTPOSITION:
7661 return pdoc->ExtendWordSelect(wParam, -1, lParam != 0);
7663 case SCI_WORDENDPOSITION:
7664 return pdoc->ExtendWordSelect(wParam, 1, lParam != 0);
7666 case SCI_SETWRAPMODE:
7667 switch (wParam) {
7668 case SC_WRAP_WORD:
7669 wrapState = eWrapWord;
7670 break;
7671 case SC_WRAP_CHAR:
7672 wrapState = eWrapChar;
7673 break;
7674 default:
7675 wrapState = eWrapNone;
7676 break;
7678 xOffset = 0;
7679 ContainerNeedsUpdate(SC_UPDATE_H_SCROLL);
7680 InvalidateStyleRedraw();
7681 ReconfigureScrollBars();
7682 break;
7684 case SCI_GETWRAPMODE:
7685 return wrapState;
7687 case SCI_SETWRAPVISUALFLAGS:
7688 if (wrapVisualFlags != static_cast<int>(wParam)) {
7689 wrapVisualFlags = wParam;
7690 InvalidateStyleRedraw();
7691 ReconfigureScrollBars();
7693 break;
7695 case SCI_GETWRAPVISUALFLAGS:
7696 return wrapVisualFlags;
7698 case SCI_SETWRAPVISUALFLAGSLOCATION:
7699 wrapVisualFlagsLocation = wParam;
7700 InvalidateStyleRedraw();
7701 break;
7703 case SCI_GETWRAPVISUALFLAGSLOCATION:
7704 return wrapVisualFlagsLocation;
7706 case SCI_SETWRAPSTARTINDENT:
7707 if (wrapVisualStartIndent != static_cast<int>(wParam)) {
7708 wrapVisualStartIndent = wParam;
7709 InvalidateStyleRedraw();
7710 ReconfigureScrollBars();
7712 break;
7714 case SCI_GETWRAPSTARTINDENT:
7715 return wrapVisualStartIndent;
7717 case SCI_SETWRAPINDENTMODE:
7718 if (wrapIndentMode != static_cast<int>(wParam)) {
7719 wrapIndentMode = wParam;
7720 InvalidateStyleRedraw();
7721 ReconfigureScrollBars();
7723 break;
7725 case SCI_GETWRAPINDENTMODE:
7726 return wrapIndentMode;
7728 case SCI_SETLAYOUTCACHE:
7729 llc.SetLevel(wParam);
7730 break;
7732 case SCI_GETLAYOUTCACHE:
7733 return llc.GetLevel();
7735 case SCI_SETPOSITIONCACHE:
7736 posCache.SetSize(wParam);
7737 break;
7739 case SCI_GETPOSITIONCACHE:
7740 return posCache.GetSize();
7742 case SCI_SETSCROLLWIDTH:
7743 PLATFORM_ASSERT(wParam > 0);
7744 if ((wParam > 0) && (wParam != static_cast<unsigned int >(scrollWidth))) {
7745 lineWidthMaxSeen = 0;
7746 scrollWidth = wParam;
7747 SetScrollBars();
7749 break;
7751 case SCI_GETSCROLLWIDTH:
7752 return scrollWidth;
7754 case SCI_SETSCROLLWIDTHTRACKING:
7755 trackLineWidth = wParam != 0;
7756 break;
7758 case SCI_GETSCROLLWIDTHTRACKING:
7759 return trackLineWidth;
7761 case SCI_LINESJOIN:
7762 LinesJoin();
7763 break;
7765 case SCI_LINESSPLIT:
7766 LinesSplit(wParam);
7767 break;
7769 case SCI_TEXTWIDTH:
7770 PLATFORM_ASSERT(wParam < vs.stylesSize);
7771 PLATFORM_ASSERT(lParam);
7772 return TextWidth(wParam, CharPtrFromSPtr(lParam));
7774 case SCI_TEXTHEIGHT:
7775 return vs.lineHeight;
7777 case SCI_SETENDATLASTLINE:
7778 PLATFORM_ASSERT((wParam == 0) || (wParam == 1));
7779 if (endAtLastLine != (wParam != 0)) {
7780 endAtLastLine = wParam != 0;
7781 SetScrollBars();
7783 break;
7785 case SCI_GETENDATLASTLINE:
7786 return endAtLastLine;
7788 case SCI_SETCARETSTICKY:
7789 PLATFORM_ASSERT((wParam >= SC_CARETSTICKY_OFF) && (wParam <= SC_CARETSTICKY_WHITESPACE));
7790 if ((wParam >= SC_CARETSTICKY_OFF) && (wParam <= SC_CARETSTICKY_WHITESPACE)) {
7791 caretSticky = wParam;
7793 break;
7795 case SCI_GETCARETSTICKY:
7796 return caretSticky;
7798 case SCI_TOGGLECARETSTICKY:
7799 caretSticky = !caretSticky;
7800 break;
7802 case SCI_GETCOLUMN:
7803 return pdoc->GetColumn(wParam);
7805 case SCI_FINDCOLUMN:
7806 return pdoc->FindColumn(wParam, lParam);
7808 case SCI_SETHSCROLLBAR :
7809 if (horizontalScrollBarVisible != (wParam != 0)) {
7810 horizontalScrollBarVisible = wParam != 0;
7811 SetScrollBars();
7812 ReconfigureScrollBars();
7814 break;
7816 case SCI_GETHSCROLLBAR:
7817 return horizontalScrollBarVisible;
7819 case SCI_SETVSCROLLBAR:
7820 if (verticalScrollBarVisible != (wParam != 0)) {
7821 verticalScrollBarVisible = wParam != 0;
7822 SetScrollBars();
7823 ReconfigureScrollBars();
7825 break;
7827 case SCI_GETVSCROLLBAR:
7828 return verticalScrollBarVisible;
7830 case SCI_SETINDENTATIONGUIDES:
7831 vs.viewIndentationGuides = IndentView(wParam);
7832 Redraw();
7833 break;
7835 case SCI_GETINDENTATIONGUIDES:
7836 return vs.viewIndentationGuides;
7838 case SCI_SETHIGHLIGHTGUIDE:
7839 if ((highlightGuideColumn != static_cast<int>(wParam)) || (wParam > 0)) {
7840 highlightGuideColumn = wParam;
7841 Redraw();
7843 break;
7845 case SCI_GETHIGHLIGHTGUIDE:
7846 return highlightGuideColumn;
7848 case SCI_GETLINEENDPOSITION:
7849 return pdoc->LineEnd(wParam);
7851 case SCI_SETCODEPAGE:
7852 if (ValidCodePage(wParam)) {
7853 pdoc->dbcsCodePage = wParam;
7854 InvalidateStyleRedraw();
7856 break;
7858 case SCI_GETCODEPAGE:
7859 return pdoc->dbcsCodePage;
7861 case SCI_SETUSEPALETTE:
7862 palette.allowRealization = wParam != 0;
7863 InvalidateStyleRedraw();
7864 break;
7866 case SCI_GETUSEPALETTE:
7867 return palette.allowRealization;
7869 // Marker definition and setting
7870 case SCI_MARKERDEFINE:
7871 if (wParam <= MARKER_MAX)
7872 vs.markers[wParam].markType = lParam;
7873 InvalidateStyleData();
7874 RedrawSelMargin();
7875 break;
7877 case SCI_MARKERSYMBOLDEFINED:
7878 if (wParam <= MARKER_MAX)
7879 return vs.markers[wParam].markType;
7880 else
7881 return 0;
7883 case SCI_MARKERSETFORE:
7884 if (wParam <= MARKER_MAX)
7885 vs.markers[wParam].fore.desired = ColourDesired(lParam);
7886 InvalidateStyleData();
7887 RedrawSelMargin();
7888 break;
7889 case SCI_MARKERSETBACKSELECTED:
7890 if (wParam <= MARKER_MAX)
7891 vs.markers[wParam].backSelected.desired = ColourDesired(lParam);
7892 InvalidateStyleRedraw();
7893 break;
7894 case SCI_MARKERENABLEHIGHLIGHT:
7895 highlightDelimiter.isEnabled = wParam == 1;
7896 InvalidateStyleRedraw();
7897 break;
7898 case SCI_MARKERSETBACK:
7899 if (wParam <= MARKER_MAX)
7900 vs.markers[wParam].back.desired = ColourDesired(lParam);
7901 InvalidateStyleData();
7902 RedrawSelMargin();
7903 break;
7904 case SCI_MARKERSETALPHA:
7905 if (wParam <= MARKER_MAX)
7906 vs.markers[wParam].alpha = lParam;
7907 InvalidateStyleRedraw();
7908 break;
7909 case SCI_MARKERADD: {
7910 int markerID = pdoc->AddMark(wParam, lParam);
7911 return markerID;
7913 case SCI_MARKERADDSET:
7914 if (lParam != 0)
7915 pdoc->AddMarkSet(wParam, lParam);
7916 break;
7918 case SCI_MARKERDELETE:
7919 pdoc->DeleteMark(wParam, lParam);
7920 break;
7922 case SCI_MARKERDELETEALL:
7923 pdoc->DeleteAllMarks(static_cast<int>(wParam));
7924 break;
7926 case SCI_MARKERGET:
7927 return pdoc->GetMark(wParam);
7929 case SCI_MARKERNEXT: {
7930 int lt = pdoc->LinesTotal();
7931 for (int iLine = wParam; iLine < lt; iLine++) {
7932 if ((pdoc->GetMark(iLine) & lParam) != 0)
7933 return iLine;
7936 return -1;
7938 case SCI_MARKERPREVIOUS: {
7939 for (int iLine = wParam; iLine >= 0; iLine--) {
7940 if ((pdoc->GetMark(iLine) & lParam) != 0)
7941 return iLine;
7944 return -1;
7946 case SCI_MARKERDEFINEPIXMAP:
7947 if (wParam <= MARKER_MAX) {
7948 vs.markers[wParam].SetXPM(CharPtrFromSPtr(lParam));
7950 InvalidateStyleData();
7951 RedrawSelMargin();
7952 break;
7954 case SCI_SETMARGINTYPEN:
7955 if (ValidMargin(wParam)) {
7956 vs.ms[wParam].style = lParam;
7957 InvalidateStyleRedraw();
7959 break;
7961 case SCI_GETMARGINTYPEN:
7962 if (ValidMargin(wParam))
7963 return vs.ms[wParam].style;
7964 else
7965 return 0;
7967 case SCI_SETMARGINWIDTHN:
7968 if (ValidMargin(wParam)) {
7969 // Short-circuit if the width is unchanged, to avoid unnecessary redraw.
7970 if (vs.ms[wParam].width != lParam) {
7971 vs.ms[wParam].width = lParam;
7972 InvalidateStyleRedraw();
7975 break;
7977 case SCI_GETMARGINWIDTHN:
7978 if (ValidMargin(wParam))
7979 return vs.ms[wParam].width;
7980 else
7981 return 0;
7983 case SCI_SETMARGINMASKN:
7984 if (ValidMargin(wParam)) {
7985 vs.ms[wParam].mask = lParam;
7986 InvalidateStyleRedraw();
7988 break;
7990 case SCI_GETMARGINMASKN:
7991 if (ValidMargin(wParam))
7992 return vs.ms[wParam].mask;
7993 else
7994 return 0;
7996 case SCI_SETMARGINSENSITIVEN:
7997 if (ValidMargin(wParam)) {
7998 vs.ms[wParam].sensitive = lParam != 0;
7999 InvalidateStyleRedraw();
8001 break;
8003 case SCI_GETMARGINSENSITIVEN:
8004 if (ValidMargin(wParam))
8005 return vs.ms[wParam].sensitive ? 1 : 0;
8006 else
8007 return 0;
8009 case SCI_SETMARGINCURSORN:
8010 if (ValidMargin(wParam))
8011 vs.ms[wParam].cursor = lParam;
8012 break;
8014 case SCI_GETMARGINCURSORN:
8015 if (ValidMargin(wParam))
8016 return vs.ms[wParam].cursor;
8017 else
8018 return 0;
8020 case SCI_STYLECLEARALL:
8021 vs.ClearStyles();
8022 InvalidateStyleRedraw();
8023 break;
8025 case SCI_STYLESETFORE:
8026 case SCI_STYLESETBACK:
8027 case SCI_STYLESETBOLD:
8028 case SCI_STYLESETITALIC:
8029 case SCI_STYLESETEOLFILLED:
8030 case SCI_STYLESETSIZE:
8031 case SCI_STYLESETFONT:
8032 case SCI_STYLESETUNDERLINE:
8033 case SCI_STYLESETCASE:
8034 case SCI_STYLESETCHARACTERSET:
8035 case SCI_STYLESETVISIBLE:
8036 case SCI_STYLESETCHANGEABLE:
8037 case SCI_STYLESETHOTSPOT:
8038 StyleSetMessage(iMessage, wParam, lParam);
8039 break;
8041 case SCI_STYLEGETFORE:
8042 case SCI_STYLEGETBACK:
8043 case SCI_STYLEGETBOLD:
8044 case SCI_STYLEGETITALIC:
8045 case SCI_STYLEGETEOLFILLED:
8046 case SCI_STYLEGETSIZE:
8047 case SCI_STYLEGETFONT:
8048 case SCI_STYLEGETUNDERLINE:
8049 case SCI_STYLEGETCASE:
8050 case SCI_STYLEGETCHARACTERSET:
8051 case SCI_STYLEGETVISIBLE:
8052 case SCI_STYLEGETCHANGEABLE:
8053 case SCI_STYLEGETHOTSPOT:
8054 return StyleGetMessage(iMessage, wParam, lParam);
8056 case SCI_STYLERESETDEFAULT:
8057 vs.ResetDefaultStyle();
8058 InvalidateStyleRedraw();
8059 break;
8060 case SCI_SETSTYLEBITS:
8061 vs.EnsureStyle((1 << wParam) - 1);
8062 pdoc->SetStylingBits(wParam);
8063 break;
8065 case SCI_GETSTYLEBITS:
8066 return pdoc->stylingBits;
8068 case SCI_SETLINESTATE:
8069 return pdoc->SetLineState(wParam, lParam);
8071 case SCI_GETLINESTATE:
8072 return pdoc->GetLineState(wParam);
8074 case SCI_GETMAXLINESTATE:
8075 return pdoc->GetMaxLineState();
8077 case SCI_GETCARETLINEVISIBLE:
8078 return vs.showCaretLineBackground;
8079 case SCI_SETCARETLINEVISIBLE:
8080 vs.showCaretLineBackground = wParam != 0;
8081 InvalidateStyleRedraw();
8082 break;
8083 case SCI_GETCARETLINEBACK:
8084 return vs.caretLineBackground.desired.AsLong();
8085 case SCI_SETCARETLINEBACK:
8086 vs.caretLineBackground.desired = wParam;
8087 InvalidateStyleRedraw();
8088 break;
8089 case SCI_GETCARETLINEBACKALPHA:
8090 return vs.caretLineAlpha;
8091 case SCI_SETCARETLINEBACKALPHA:
8092 vs.caretLineAlpha = wParam;
8093 InvalidateStyleRedraw();
8094 break;
8096 // Folding messages
8098 case SCI_VISIBLEFROMDOCLINE:
8099 return cs.DisplayFromDoc(wParam);
8101 case SCI_DOCLINEFROMVISIBLE:
8102 return cs.DocFromDisplay(wParam);
8104 case SCI_WRAPCOUNT:
8105 return WrapCount(wParam);
8107 case SCI_SETFOLDLEVEL: {
8108 int prev = pdoc->SetLevel(wParam, lParam);
8109 if (prev != lParam)
8110 RedrawSelMargin();
8111 return prev;
8114 case SCI_GETFOLDLEVEL:
8115 return pdoc->GetLevel(wParam);
8117 case SCI_GETLASTCHILD:
8118 return pdoc->GetLastChild(wParam, lParam);
8120 case SCI_GETFOLDPARENT:
8121 return pdoc->GetFoldParent(wParam);
8123 case SCI_SHOWLINES:
8124 cs.SetVisible(wParam, lParam, true);
8125 SetScrollBars();
8126 Redraw();
8127 break;
8129 case SCI_HIDELINES:
8130 if (wParam > 0)
8131 cs.SetVisible(wParam, lParam, false);
8132 SetScrollBars();
8133 Redraw();
8134 break;
8136 case SCI_GETLINEVISIBLE:
8137 return cs.GetVisible(wParam);
8139 case SCI_SETFOLDEXPANDED:
8140 if (cs.SetExpanded(wParam, lParam != 0)) {
8141 RedrawSelMargin();
8143 break;
8145 case SCI_GETFOLDEXPANDED:
8146 return cs.GetExpanded(wParam);
8148 case SCI_SETFOLDFLAGS:
8149 foldFlags = wParam;
8150 Redraw();
8151 break;
8153 case SCI_TOGGLEFOLD:
8154 ToggleContraction(wParam);
8155 break;
8157 case SCI_CONTRACTEDFOLDNEXT:
8158 return ContractedFoldNext(wParam);
8160 case SCI_ENSUREVISIBLE:
8161 EnsureLineVisible(wParam, false);
8162 break;
8164 case SCI_ENSUREVISIBLEENFORCEPOLICY:
8165 EnsureLineVisible(wParam, true);
8166 break;
8168 case SCI_SEARCHANCHOR:
8169 SearchAnchor();
8170 break;
8172 case SCI_SEARCHNEXT:
8173 case SCI_SEARCHPREV:
8174 return SearchText(iMessage, wParam, lParam);
8176 case SCI_SETXCARETPOLICY:
8177 caretXPolicy = wParam;
8178 caretXSlop = lParam;
8179 break;
8181 case SCI_SETYCARETPOLICY:
8182 caretYPolicy = wParam;
8183 caretYSlop = lParam;
8184 break;
8186 case SCI_SETVISIBLEPOLICY:
8187 visiblePolicy = wParam;
8188 visibleSlop = lParam;
8189 break;
8191 case SCI_LINESONSCREEN:
8192 return LinesOnScreen();
8194 case SCI_SETSELFORE:
8195 vs.selforeset = wParam != 0;
8196 vs.selforeground.desired = ColourDesired(lParam);
8197 vs.selAdditionalForeground.desired = ColourDesired(lParam);
8198 InvalidateStyleRedraw();
8199 break;
8201 case SCI_SETSELBACK:
8202 vs.selbackset = wParam != 0;
8203 vs.selbackground.desired = ColourDesired(lParam);
8204 vs.selAdditionalBackground.desired = ColourDesired(lParam);
8205 InvalidateStyleRedraw();
8206 break;
8208 case SCI_SETSELALPHA:
8209 vs.selAlpha = wParam;
8210 vs.selAdditionalAlpha = wParam;
8211 InvalidateStyleRedraw();
8212 break;
8214 case SCI_GETSELALPHA:
8215 return vs.selAlpha;
8217 case SCI_GETSELEOLFILLED:
8218 return vs.selEOLFilled;
8220 case SCI_SETSELEOLFILLED:
8221 vs.selEOLFilled = wParam != 0;
8222 InvalidateStyleRedraw();
8223 break;
8225 case SCI_SETWHITESPACEFORE:
8226 vs.whitespaceForegroundSet = wParam != 0;
8227 vs.whitespaceForeground.desired = ColourDesired(lParam);
8228 InvalidateStyleRedraw();
8229 break;
8231 case SCI_SETWHITESPACEBACK:
8232 vs.whitespaceBackgroundSet = wParam != 0;
8233 vs.whitespaceBackground.desired = ColourDesired(lParam);
8234 InvalidateStyleRedraw();
8235 break;
8237 case SCI_SETCARETFORE:
8238 vs.caretcolour.desired = ColourDesired(wParam);
8239 InvalidateStyleRedraw();
8240 break;
8242 case SCI_GETCARETFORE:
8243 return vs.caretcolour.desired.AsLong();
8245 case SCI_SETCARETSTYLE:
8246 if (wParam >= CARETSTYLE_INVISIBLE && wParam <= CARETSTYLE_BLOCK)
8247 vs.caretStyle = wParam;
8248 else
8249 /* Default to the line caret */
8250 vs.caretStyle = CARETSTYLE_LINE;
8251 InvalidateStyleRedraw();
8252 break;
8254 case SCI_GETCARETSTYLE:
8255 return vs.caretStyle;
8257 case SCI_SETCARETWIDTH:
8258 if (wParam <= 0)
8259 vs.caretWidth = 0;
8260 else if (wParam >= 3)
8261 vs.caretWidth = 3;
8262 else
8263 vs.caretWidth = wParam;
8264 InvalidateStyleRedraw();
8265 break;
8267 case SCI_GETCARETWIDTH:
8268 return vs.caretWidth;
8270 case SCI_ASSIGNCMDKEY:
8271 kmap.AssignCmdKey(Platform::LowShortFromLong(wParam),
8272 Platform::HighShortFromLong(wParam), lParam);
8273 break;
8275 case SCI_CLEARCMDKEY:
8276 kmap.AssignCmdKey(Platform::LowShortFromLong(wParam),
8277 Platform::HighShortFromLong(wParam), SCI_NULL);
8278 break;
8280 case SCI_CLEARALLCMDKEYS:
8281 kmap.Clear();
8282 break;
8284 case SCI_INDICSETSTYLE:
8285 if (wParam <= INDIC_MAX) {
8286 vs.indicators[wParam].style = lParam;
8287 InvalidateStyleRedraw();
8289 break;
8291 case SCI_INDICGETSTYLE:
8292 return (wParam <= INDIC_MAX) ? vs.indicators[wParam].style : 0;
8294 case SCI_INDICSETFORE:
8295 if (wParam <= INDIC_MAX) {
8296 vs.indicators[wParam].fore.desired = ColourDesired(lParam);
8297 InvalidateStyleRedraw();
8299 break;
8301 case SCI_INDICGETFORE:
8302 return (wParam <= INDIC_MAX) ? vs.indicators[wParam].fore.desired.AsLong() : 0;
8304 case SCI_INDICSETUNDER:
8305 if (wParam <= INDIC_MAX) {
8306 vs.indicators[wParam].under = lParam != 0;
8307 InvalidateStyleRedraw();
8309 break;
8311 case SCI_INDICGETUNDER:
8312 return (wParam <= INDIC_MAX) ? vs.indicators[wParam].under : 0;
8314 case SCI_INDICSETALPHA:
8315 if (wParam <= INDIC_MAX && lParam >=0 && lParam <= 255) {
8316 vs.indicators[wParam].fillAlpha = lParam;
8317 InvalidateStyleRedraw();
8319 break;
8321 case SCI_INDICGETALPHA:
8322 return (wParam <= INDIC_MAX) ? vs.indicators[wParam].fillAlpha : 0;
8324 case SCI_INDICSETOUTLINEALPHA:
8325 if (wParam <= INDIC_MAX && lParam >=0 && lParam <= 255) {
8326 vs.indicators[wParam].outlineAlpha = lParam;
8327 InvalidateStyleRedraw();
8329 break;
8331 case SCI_INDICGETOUTLINEALPHA:
8332 return (wParam <= INDIC_MAX) ? vs.indicators[wParam].outlineAlpha : 0;
8334 case SCI_SETINDICATORCURRENT:
8335 pdoc->decorations.SetCurrentIndicator(wParam);
8336 break;
8337 case SCI_GETINDICATORCURRENT:
8338 return pdoc->decorations.GetCurrentIndicator();
8339 case SCI_SETINDICATORVALUE:
8340 pdoc->decorations.SetCurrentValue(wParam);
8341 break;
8342 case SCI_GETINDICATORVALUE:
8343 return pdoc->decorations.GetCurrentValue();
8345 case SCI_INDICATORFILLRANGE:
8346 pdoc->DecorationFillRange(wParam, pdoc->decorations.GetCurrentValue(), lParam);
8347 break;
8349 case SCI_INDICATORCLEARRANGE:
8350 pdoc->DecorationFillRange(wParam, 0, lParam);
8351 break;
8353 case SCI_INDICATORALLONFOR:
8354 return pdoc->decorations.AllOnFor(wParam);
8356 case SCI_INDICATORVALUEAT:
8357 return pdoc->decorations.ValueAt(wParam, lParam);
8359 case SCI_INDICATORSTART:
8360 return pdoc->decorations.Start(wParam, lParam);
8362 case SCI_INDICATOREND:
8363 return pdoc->decorations.End(wParam, lParam);
8365 case SCI_LINEDOWN:
8366 case SCI_LINEDOWNEXTEND:
8367 case SCI_PARADOWN:
8368 case SCI_PARADOWNEXTEND:
8369 case SCI_LINEUP:
8370 case SCI_LINEUPEXTEND:
8371 case SCI_PARAUP:
8372 case SCI_PARAUPEXTEND:
8373 case SCI_CHARLEFT:
8374 case SCI_CHARLEFTEXTEND:
8375 case SCI_CHARRIGHT:
8376 case SCI_CHARRIGHTEXTEND:
8377 case SCI_WORDLEFT:
8378 case SCI_WORDLEFTEXTEND:
8379 case SCI_WORDRIGHT:
8380 case SCI_WORDRIGHTEXTEND:
8381 case SCI_WORDLEFTEND:
8382 case SCI_WORDLEFTENDEXTEND:
8383 case SCI_WORDRIGHTEND:
8384 case SCI_WORDRIGHTENDEXTEND:
8385 case SCI_HOME:
8386 case SCI_HOMEEXTEND:
8387 case SCI_LINEEND:
8388 case SCI_LINEENDEXTEND:
8389 case SCI_HOMEWRAP:
8390 case SCI_HOMEWRAPEXTEND:
8391 case SCI_LINEENDWRAP:
8392 case SCI_LINEENDWRAPEXTEND:
8393 case SCI_DOCUMENTSTART:
8394 case SCI_DOCUMENTSTARTEXTEND:
8395 case SCI_DOCUMENTEND:
8396 case SCI_DOCUMENTENDEXTEND:
8398 case SCI_STUTTEREDPAGEUP:
8399 case SCI_STUTTEREDPAGEUPEXTEND:
8400 case SCI_STUTTEREDPAGEDOWN:
8401 case SCI_STUTTEREDPAGEDOWNEXTEND:
8403 case SCI_PAGEUP:
8404 case SCI_PAGEUPEXTEND:
8405 case SCI_PAGEDOWN:
8406 case SCI_PAGEDOWNEXTEND:
8407 case SCI_EDITTOGGLEOVERTYPE:
8408 case SCI_CANCEL:
8409 case SCI_DELETEBACK:
8410 case SCI_TAB:
8411 case SCI_BACKTAB:
8412 case SCI_NEWLINE:
8413 case SCI_FORMFEED:
8414 case SCI_VCHOME:
8415 case SCI_VCHOMEEXTEND:
8416 case SCI_VCHOMEWRAP:
8417 case SCI_VCHOMEWRAPEXTEND:
8418 case SCI_ZOOMIN:
8419 case SCI_ZOOMOUT:
8420 case SCI_DELWORDLEFT:
8421 case SCI_DELWORDRIGHT:
8422 case SCI_DELWORDRIGHTEND:
8423 case SCI_DELLINELEFT:
8424 case SCI_DELLINERIGHT:
8425 case SCI_LINECOPY:
8426 case SCI_LINECUT:
8427 case SCI_LINEDELETE:
8428 case SCI_LINETRANSPOSE:
8429 case SCI_LINEDUPLICATE:
8430 case SCI_LOWERCASE:
8431 case SCI_UPPERCASE:
8432 case SCI_LINESCROLLDOWN:
8433 case SCI_LINESCROLLUP:
8434 case SCI_WORDPARTLEFT:
8435 case SCI_WORDPARTLEFTEXTEND:
8436 case SCI_WORDPARTRIGHT:
8437 case SCI_WORDPARTRIGHTEXTEND:
8438 case SCI_DELETEBACKNOTLINE:
8439 case SCI_HOMEDISPLAY:
8440 case SCI_HOMEDISPLAYEXTEND:
8441 case SCI_LINEENDDISPLAY:
8442 case SCI_LINEENDDISPLAYEXTEND:
8443 case SCI_LINEDOWNRECTEXTEND:
8444 case SCI_LINEUPRECTEXTEND:
8445 case SCI_CHARLEFTRECTEXTEND:
8446 case SCI_CHARRIGHTRECTEXTEND:
8447 case SCI_HOMERECTEXTEND:
8448 case SCI_VCHOMERECTEXTEND:
8449 case SCI_LINEENDRECTEXTEND:
8450 case SCI_PAGEUPRECTEXTEND:
8451 case SCI_PAGEDOWNRECTEXTEND:
8452 case SCI_SELECTIONDUPLICATE:
8453 return KeyCommand(iMessage);
8455 case SCI_BRACEHIGHLIGHT:
8456 SetBraceHighlight(static_cast<int>(wParam), lParam, STYLE_BRACELIGHT);
8457 break;
8459 case SCI_BRACEHIGHLIGHTINDICATOR:
8460 if (lParam >= 0 && lParam <= INDIC_MAX) {
8461 vs.braceHighlightIndicatorSet = wParam != 0;
8462 vs.braceHighlightIndicator = lParam;
8464 break;
8466 case SCI_BRACEBADLIGHT:
8467 SetBraceHighlight(static_cast<int>(wParam), -1, STYLE_BRACEBAD);
8468 break;
8470 case SCI_BRACEBADLIGHTINDICATOR:
8471 if (lParam >= 0 && lParam <= INDIC_MAX) {
8472 vs.braceBadLightIndicatorSet = wParam != 0;
8473 vs.braceBadLightIndicator = lParam;
8475 break;
8477 case SCI_BRACEMATCH:
8478 // wParam is position of char to find brace for,
8479 // lParam is maximum amount of text to restyle to find it
8480 return pdoc->BraceMatch(wParam, lParam);
8482 case SCI_GETVIEWEOL:
8483 return vs.viewEOL;
8485 case SCI_SETVIEWEOL:
8486 vs.viewEOL = wParam != 0;
8487 InvalidateStyleRedraw();
8488 break;
8490 case SCI_SETZOOM:
8491 vs.zoomLevel = wParam;
8492 InvalidateStyleRedraw();
8493 NotifyZoom();
8494 break;
8496 case SCI_GETZOOM:
8497 return vs.zoomLevel;
8499 case SCI_GETEDGECOLUMN:
8500 return theEdge;
8502 case SCI_SETEDGECOLUMN:
8503 theEdge = wParam;
8504 InvalidateStyleRedraw();
8505 break;
8507 case SCI_GETEDGEMODE:
8508 return vs.edgeState;
8510 case SCI_SETEDGEMODE:
8511 vs.edgeState = wParam;
8512 InvalidateStyleRedraw();
8513 break;
8515 case SCI_GETEDGECOLOUR:
8516 return vs.edgecolour.desired.AsLong();
8518 case SCI_SETEDGECOLOUR:
8519 vs.edgecolour.desired = ColourDesired(wParam);
8520 InvalidateStyleRedraw();
8521 break;
8523 case SCI_GETDOCPOINTER:
8524 return reinterpret_cast<sptr_t>(pdoc);
8526 case SCI_SETDOCPOINTER:
8527 CancelModes();
8528 SetDocPointer(reinterpret_cast<Document *>(lParam));
8529 return 0;
8531 case SCI_CREATEDOCUMENT: {
8532 Document *doc = new Document();
8533 if (doc) {
8534 doc->AddRef();
8536 return reinterpret_cast<sptr_t>(doc);
8539 case SCI_ADDREFDOCUMENT:
8540 (reinterpret_cast<Document *>(lParam))->AddRef();
8541 break;
8543 case SCI_RELEASEDOCUMENT:
8544 (reinterpret_cast<Document *>(lParam))->Release();
8545 break;
8547 case SCI_SETMODEVENTMASK:
8548 modEventMask = wParam;
8549 return 0;
8551 case SCI_GETMODEVENTMASK:
8552 return modEventMask;
8554 case SCI_CONVERTEOLS:
8555 pdoc->ConvertLineEnds(wParam);
8556 SetSelection(sel.MainCaret(), sel.MainAnchor()); // Ensure selection inside document
8557 return 0;
8559 case SCI_SETLENGTHFORENCODE:
8560 lengthForEncode = wParam;
8561 return 0;
8563 case SCI_SELECTIONISRECTANGLE:
8564 return sel.selType == Selection::selRectangle ? 1 : 0;
8566 case SCI_SETSELECTIONMODE: {
8567 switch (wParam) {
8568 case SC_SEL_STREAM:
8569 sel.SetMoveExtends(!sel.MoveExtends() || (sel.selType != Selection::selStream));
8570 sel.selType = Selection::selStream;
8571 break;
8572 case SC_SEL_RECTANGLE:
8573 sel.SetMoveExtends(!sel.MoveExtends() || (sel.selType != Selection::selRectangle));
8574 sel.selType = Selection::selRectangle;
8575 break;
8576 case SC_SEL_LINES:
8577 sel.SetMoveExtends(!sel.MoveExtends() || (sel.selType != Selection::selLines));
8578 sel.selType = Selection::selLines;
8579 break;
8580 case SC_SEL_THIN:
8581 sel.SetMoveExtends(!sel.MoveExtends() || (sel.selType != Selection::selThin));
8582 sel.selType = Selection::selThin;
8583 break;
8584 default:
8585 sel.SetMoveExtends(!sel.MoveExtends() || (sel.selType != Selection::selStream));
8586 sel.selType = Selection::selStream;
8588 InvalidateSelection(sel.RangeMain(), true);
8590 case SCI_GETSELECTIONMODE:
8591 switch (sel.selType) {
8592 case Selection::selStream:
8593 return SC_SEL_STREAM;
8594 case Selection::selRectangle:
8595 return SC_SEL_RECTANGLE;
8596 case Selection::selLines:
8597 return SC_SEL_LINES;
8598 case Selection::selThin:
8599 return SC_SEL_THIN;
8600 default: // ?!
8601 return SC_SEL_STREAM;
8603 case SCI_GETLINESELSTARTPOSITION:
8604 case SCI_GETLINESELENDPOSITION: {
8605 SelectionSegment segmentLine(SelectionPosition(pdoc->LineStart(wParam)),
8606 SelectionPosition(pdoc->LineEnd(wParam)));
8607 for (size_t r=0; r<sel.Count(); r++) {
8608 SelectionSegment portion = sel.Range(r).Intersect(segmentLine);
8609 if (portion.start.IsValid()) {
8610 return (iMessage == SCI_GETLINESELSTARTPOSITION) ? portion.start.Position() : portion.end.Position();
8613 return INVALID_POSITION;
8616 case SCI_SETOVERTYPE:
8617 inOverstrike = wParam != 0;
8618 break;
8620 case SCI_GETOVERTYPE:
8621 return inOverstrike ? 1 : 0;
8623 case SCI_SETFOCUS:
8624 SetFocusState(wParam != 0);
8625 break;
8627 case SCI_GETFOCUS:
8628 return hasFocus;
8630 case SCI_SETSTATUS:
8631 errorStatus = wParam;
8632 break;
8634 case SCI_GETSTATUS:
8635 return errorStatus;
8637 case SCI_SETMOUSEDOWNCAPTURES:
8638 mouseDownCaptures = wParam != 0;
8639 break;
8641 case SCI_GETMOUSEDOWNCAPTURES:
8642 return mouseDownCaptures;
8644 case SCI_SETCURSOR:
8645 cursorMode = wParam;
8646 DisplayCursor(Window::cursorText);
8647 break;
8649 case SCI_GETCURSOR:
8650 return cursorMode;
8652 case SCI_SETCONTROLCHARSYMBOL:
8653 controlCharSymbol = wParam;
8654 break;
8656 case SCI_GETCONTROLCHARSYMBOL:
8657 return controlCharSymbol;
8659 case SCI_STARTRECORD:
8660 recordingMacro = true;
8661 return 0;
8663 case SCI_STOPRECORD:
8664 recordingMacro = false;
8665 return 0;
8667 case SCI_MOVECARETINSIDEVIEW:
8668 MoveCaretInsideView();
8669 break;
8671 case SCI_SETFOLDMARGINCOLOUR:
8672 vs.foldmarginColourSet = wParam != 0;
8673 vs.foldmarginColour.desired = ColourDesired(lParam);
8674 InvalidateStyleRedraw();
8675 break;
8677 case SCI_SETFOLDMARGINHICOLOUR:
8678 vs.foldmarginHighlightColourSet = wParam != 0;
8679 vs.foldmarginHighlightColour.desired = ColourDesired(lParam);
8680 InvalidateStyleRedraw();
8681 break;
8683 case SCI_SETHOTSPOTACTIVEFORE:
8684 vs.hotspotForegroundSet = wParam != 0;
8685 vs.hotspotForeground.desired = ColourDesired(lParam);
8686 InvalidateStyleRedraw();
8687 break;
8689 case SCI_GETHOTSPOTACTIVEFORE:
8690 return vs.hotspotForeground.desired.AsLong();
8692 case SCI_SETHOTSPOTACTIVEBACK:
8693 vs.hotspotBackgroundSet = wParam != 0;
8694 vs.hotspotBackground.desired = ColourDesired(lParam);
8695 InvalidateStyleRedraw();
8696 break;
8698 case SCI_GETHOTSPOTACTIVEBACK:
8699 return vs.hotspotBackground.desired.AsLong();
8701 case SCI_SETHOTSPOTACTIVEUNDERLINE:
8702 vs.hotspotUnderline = wParam != 0;
8703 InvalidateStyleRedraw();
8704 break;
8706 case SCI_GETHOTSPOTACTIVEUNDERLINE:
8707 return vs.hotspotUnderline ? 1 : 0;
8709 case SCI_SETHOTSPOTSINGLELINE:
8710 vs.hotspotSingleLine = wParam != 0;
8711 InvalidateStyleRedraw();
8712 break;
8714 case SCI_GETHOTSPOTSINGLELINE:
8715 return vs.hotspotSingleLine ? 1 : 0;
8717 case SCI_SETPASTECONVERTENDINGS:
8718 convertPastes = wParam != 0;
8719 break;
8721 case SCI_GETPASTECONVERTENDINGS:
8722 return convertPastes ? 1 : 0;
8724 case SCI_GETCHARACTERPOINTER:
8725 return reinterpret_cast<sptr_t>(pdoc->BufferPointer());
8727 case SCI_SETEXTRAASCENT:
8728 vs.extraAscent = wParam;
8729 InvalidateStyleRedraw();
8730 break;
8732 case SCI_GETEXTRAASCENT:
8733 return vs.extraAscent;
8735 case SCI_SETEXTRADESCENT:
8736 vs.extraDescent = wParam;
8737 InvalidateStyleRedraw();
8738 break;
8740 case SCI_GETEXTRADESCENT:
8741 return vs.extraDescent;
8743 case SCI_MARGINSETSTYLEOFFSET:
8744 vs.marginStyleOffset = wParam;
8745 InvalidateStyleRedraw();
8746 break;
8748 case SCI_MARGINGETSTYLEOFFSET:
8749 return vs.marginStyleOffset;
8751 case SCI_MARGINSETTEXT:
8752 pdoc->MarginSetText(wParam, CharPtrFromSPtr(lParam));
8753 break;
8755 case SCI_MARGINGETTEXT: {
8756 const StyledText st = pdoc->MarginStyledText(wParam);
8757 if (lParam) {
8758 if (st.text)
8759 memcpy(CharPtrFromSPtr(lParam), st.text, st.length);
8760 else
8761 strcpy(CharPtrFromSPtr(lParam), "");
8763 return st.length;
8766 case SCI_MARGINSETSTYLE:
8767 pdoc->MarginSetStyle(wParam, lParam);
8768 break;
8770 case SCI_MARGINGETSTYLE: {
8771 const StyledText st = pdoc->MarginStyledText(wParam);
8772 return st.style;
8775 case SCI_MARGINSETSTYLES:
8776 pdoc->MarginSetStyles(wParam, reinterpret_cast<const unsigned char *>(lParam));
8777 break;
8779 case SCI_MARGINGETSTYLES: {
8780 const StyledText st = pdoc->MarginStyledText(wParam);
8781 if (lParam) {
8782 if (st.styles)
8783 memcpy(CharPtrFromSPtr(lParam), st.styles, st.length);
8784 else
8785 strcpy(CharPtrFromSPtr(lParam), "");
8787 return st.styles ? st.length : 0;
8790 case SCI_MARGINTEXTCLEARALL:
8791 pdoc->MarginClearAll();
8792 break;
8794 case SCI_ANNOTATIONSETTEXT:
8795 pdoc->AnnotationSetText(wParam, CharPtrFromSPtr(lParam));
8796 break;
8798 case SCI_ANNOTATIONGETTEXT: {
8799 const StyledText st = pdoc->AnnotationStyledText(wParam);
8800 if (lParam) {
8801 if (st.text)
8802 memcpy(CharPtrFromSPtr(lParam), st.text, st.length);
8803 else
8804 strcpy(CharPtrFromSPtr(lParam), "");
8806 return st.length;
8809 case SCI_ANNOTATIONGETSTYLE: {
8810 const StyledText st = pdoc->AnnotationStyledText(wParam);
8811 return st.style;
8814 case SCI_ANNOTATIONSETSTYLE:
8815 pdoc->AnnotationSetStyle(wParam, lParam);
8816 break;
8818 case SCI_ANNOTATIONSETSTYLES:
8819 pdoc->AnnotationSetStyles(wParam, reinterpret_cast<const unsigned char *>(lParam));
8820 break;
8822 case SCI_ANNOTATIONGETSTYLES: {
8823 const StyledText st = pdoc->AnnotationStyledText(wParam);
8824 if (lParam) {
8825 if (st.styles)
8826 memcpy(CharPtrFromSPtr(lParam), st.styles, st.length);
8827 else
8828 strcpy(CharPtrFromSPtr(lParam), "");
8830 return st.styles ? st.length : 0;
8833 case SCI_ANNOTATIONGETLINES:
8834 return pdoc->AnnotationLines(wParam);
8836 case SCI_ANNOTATIONCLEARALL:
8837 pdoc->AnnotationClearAll();
8838 break;
8840 case SCI_ANNOTATIONSETVISIBLE:
8841 SetAnnotationVisible(wParam);
8842 break;
8844 case SCI_ANNOTATIONGETVISIBLE:
8845 return vs.annotationVisible;
8847 case SCI_ANNOTATIONSETSTYLEOFFSET:
8848 vs.annotationStyleOffset = wParam;
8849 InvalidateStyleRedraw();
8850 break;
8852 case SCI_ANNOTATIONGETSTYLEOFFSET:
8853 return vs.annotationStyleOffset;
8855 case SCI_ADDUNDOACTION:
8856 pdoc->AddUndoAction(wParam, lParam & UNDO_MAY_COALESCE);
8857 break;
8859 case SCI_SETMULTIPLESELECTION:
8860 multipleSelection = wParam != 0;
8861 InvalidateCaret();
8862 break;
8864 case SCI_GETMULTIPLESELECTION:
8865 return multipleSelection;
8867 case SCI_SETADDITIONALSELECTIONTYPING:
8868 additionalSelectionTyping = wParam != 0;
8869 InvalidateCaret();
8870 break;
8872 case SCI_GETADDITIONALSELECTIONTYPING:
8873 return additionalSelectionTyping;
8875 case SCI_SETMULTIPASTE:
8876 multiPasteMode = wParam;
8877 break;
8879 case SCI_GETMULTIPASTE:
8880 return multiPasteMode;
8882 case SCI_SETADDITIONALCARETSBLINK:
8883 additionalCaretsBlink = wParam != 0;
8884 InvalidateCaret();
8885 break;
8887 case SCI_GETADDITIONALCARETSBLINK:
8888 return additionalCaretsBlink;
8890 case SCI_SETADDITIONALCARETSVISIBLE:
8891 additionalCaretsVisible = wParam != 0;
8892 InvalidateCaret();
8893 break;
8895 case SCI_GETADDITIONALCARETSVISIBLE:
8896 return additionalCaretsVisible;
8898 case SCI_GETSELECTIONS:
8899 return sel.Count();
8901 case SCI_CLEARSELECTIONS:
8902 sel.Clear();
8903 Redraw();
8904 break;
8906 case SCI_SETSELECTION:
8907 sel.SetSelection(SelectionRange(wParam, lParam));
8908 Redraw();
8909 break;
8911 case SCI_ADDSELECTION:
8912 sel.AddSelection(SelectionRange(wParam, lParam));
8913 Redraw();
8914 break;
8916 case SCI_SETMAINSELECTION:
8917 sel.SetMain(wParam);
8918 Redraw();
8919 break;
8921 case SCI_GETMAINSELECTION:
8922 return sel.Main();
8924 case SCI_SETSELECTIONNCARET:
8925 sel.Range(wParam).caret.SetPosition(lParam);
8926 Redraw();
8927 break;
8929 case SCI_GETSELECTIONNCARET:
8930 return sel.Range(wParam).caret.Position();
8932 case SCI_SETSELECTIONNANCHOR:
8933 sel.Range(wParam).anchor.SetPosition(lParam);
8934 Redraw();
8935 break;
8936 case SCI_GETSELECTIONNANCHOR:
8937 return sel.Range(wParam).anchor.Position();
8939 case SCI_SETSELECTIONNCARETVIRTUALSPACE:
8940 sel.Range(wParam).caret.SetVirtualSpace(lParam);
8941 Redraw();
8942 break;
8944 case SCI_GETSELECTIONNCARETVIRTUALSPACE:
8945 return sel.Range(wParam).caret.VirtualSpace();
8947 case SCI_SETSELECTIONNANCHORVIRTUALSPACE:
8948 sel.Range(wParam).anchor.SetVirtualSpace(lParam);
8949 Redraw();
8950 break;
8952 case SCI_GETSELECTIONNANCHORVIRTUALSPACE:
8953 return sel.Range(wParam).anchor.VirtualSpace();
8955 case SCI_SETSELECTIONNSTART:
8956 sel.Range(wParam).anchor.SetPosition(lParam);
8957 Redraw();
8958 break;
8960 case SCI_GETSELECTIONNSTART:
8961 return sel.Range(wParam).Start().Position();
8963 case SCI_SETSELECTIONNEND:
8964 sel.Range(wParam).caret.SetPosition(lParam);
8965 Redraw();
8966 break;
8968 case SCI_GETSELECTIONNEND:
8969 return sel.Range(wParam).End().Position();
8971 case SCI_SETRECTANGULARSELECTIONCARET:
8972 if (!sel.IsRectangular())
8973 sel.Clear();
8974 sel.selType = Selection::selRectangle;
8975 sel.Rectangular().caret.SetPosition(wParam);
8976 SetRectangularRange();
8977 Redraw();
8978 break;
8980 case SCI_GETRECTANGULARSELECTIONCARET:
8981 return sel.Rectangular().caret.Position();
8983 case SCI_SETRECTANGULARSELECTIONANCHOR:
8984 if (!sel.IsRectangular())
8985 sel.Clear();
8986 sel.selType = Selection::selRectangle;
8987 sel.Rectangular().anchor.SetPosition(wParam);
8988 SetRectangularRange();
8989 Redraw();
8990 break;
8992 case SCI_GETRECTANGULARSELECTIONANCHOR:
8993 return sel.Rectangular().anchor.Position();
8995 case SCI_SETRECTANGULARSELECTIONCARETVIRTUALSPACE:
8996 if (!sel.IsRectangular())
8997 sel.Clear();
8998 sel.selType = Selection::selRectangle;
8999 sel.Rectangular().caret.SetVirtualSpace(wParam);
9000 SetRectangularRange();
9001 Redraw();
9002 break;
9004 case SCI_GETRECTANGULARSELECTIONCARETVIRTUALSPACE:
9005 return sel.Rectangular().caret.VirtualSpace();
9007 case SCI_SETRECTANGULARSELECTIONANCHORVIRTUALSPACE:
9008 if (!sel.IsRectangular())
9009 sel.Clear();
9010 sel.selType = Selection::selRectangle;
9011 sel.Rectangular().anchor.SetVirtualSpace(wParam);
9012 SetRectangularRange();
9013 Redraw();
9014 break;
9016 case SCI_GETRECTANGULARSELECTIONANCHORVIRTUALSPACE:
9017 return sel.Rectangular().anchor.VirtualSpace();
9019 case SCI_SETVIRTUALSPACEOPTIONS:
9020 virtualSpaceOptions = wParam;
9021 break;
9023 case SCI_GETVIRTUALSPACEOPTIONS:
9024 return virtualSpaceOptions;
9026 case SCI_SETADDITIONALSELFORE:
9027 vs.selAdditionalForeground.desired = ColourDesired(wParam);
9028 InvalidateStyleRedraw();
9029 break;
9031 case SCI_SETADDITIONALSELBACK:
9032 vs.selAdditionalBackground.desired = ColourDesired(wParam);
9033 InvalidateStyleRedraw();
9034 break;
9036 case SCI_SETADDITIONALSELALPHA:
9037 vs.selAdditionalAlpha = wParam;
9038 InvalidateStyleRedraw();
9039 break;
9041 case SCI_GETADDITIONALSELALPHA:
9042 return vs.selAdditionalAlpha;
9044 case SCI_SETADDITIONALCARETFORE:
9045 vs.additionalCaretColour.desired = ColourDesired(wParam);
9046 InvalidateStyleRedraw();
9047 break;
9049 case SCI_GETADDITIONALCARETFORE:
9050 return vs.additionalCaretColour.desired.AsLong();
9052 case SCI_ROTATESELECTION:
9053 sel.RotateMain();
9054 InvalidateSelection(sel.RangeMain(), true);
9055 break;
9057 case SCI_SWAPMAINANCHORCARET:
9058 InvalidateSelection(sel.RangeMain());
9059 sel.RangeMain() = SelectionRange(sel.RangeMain().anchor, sel.RangeMain().caret);
9060 break;
9062 case SCI_CHANGELEXERSTATE:
9063 pdoc->ChangeLexerState(wParam, lParam);
9064 break;
9066 default:
9067 return DefWndProc(iMessage, wParam, lParam);
9069 //Platform::DebugPrintf("end wnd proc\n");
9070 return 0l;