]> git.saurik.com Git - wxWidgets.git/blame - src/stc/scintilla/src/Editor.cxx
Correct the misleading example of using id ranges in XRC documentation.
[wxWidgets.git] / src / stc / scintilla / src / Editor.cxx
CommitLineData
9ce192d4 1// Scintilla source code edit control
65ec6247
RD
2/** @file Editor.cxx
3 ** Main code for the edit control.
4 **/
591d01be 5// Copyright 1998-2004 by Neil Hodgson <neilh@scintilla.org>
9ce192d4
RD
6// The License.txt file describes the conditions under which this software may be distributed.
7
8#include <stdlib.h>
9#include <string.h>
10#include <stdio.h>
11#include <ctype.h>
12
9e96e16f 13#include <string>
9ce192d4 14
9e96e16f
RD
15// With Borland C++ 5.5, including <string> includes Windows.h leading to defining
16// FindText to FindTextA which makes calls here to Document::FindText fail.
17#ifdef __BORLANDC__
18#ifdef FindText
19#undef FindText
20#endif
9e730a78 21#endif
9e96e16f
RD
22
23#include "Platform.h"
24
9ce192d4 25#include "Scintilla.h"
d134f170 26
7e0c58e9
RD
27#include "SplitVector.h"
28#include "Partitioning.h"
29#include "RunStyles.h"
9ce192d4 30#include "ContractionState.h"
9ce192d4
RD
31#include "CellBuffer.h"
32#include "KeyMap.h"
33#include "Indicator.h"
9e730a78 34#include "XPM.h"
9ce192d4
RD
35#include "LineMarker.h"
36#include "Style.h"
37#include "ViewStyle.h"
b8193d80 38#include "CharClassify.h"
7e0c58e9 39#include "Decoration.h"
9ce192d4 40#include "Document.h"
9e96e16f 41#include "Selection.h"
7e0c58e9 42#include "PositionCache.h"
9ce192d4
RD
43#include "Editor.h"
44
7e0c58e9
RD
45#ifdef SCI_NAMESPACE
46using namespace Scintilla;
47#endif
48
1e9bafca
RD
49/*
50 return whether this modification represents an operation that
51 may reasonably be deferred (not done now OR [possibly] at all)
52*/
53static bool CanDeferToLastStep(const DocModification& mh) {
7e0c58e9 54 if (mh.modificationType & (SC_MOD_BEFOREINSERT | SC_MOD_BEFOREDELETE))
1e9bafca 55 return true; // CAN skip
7e0c58e9 56 if (!(mh.modificationType & (SC_PERFORMED_UNDO | SC_PERFORMED_REDO)))
1e9bafca
RD
57 return false; // MUST do
58 if (mh.modificationType & SC_MULTISTEPUNDOREDO)
59 return true; // CAN skip
60 return false; // PRESUMABLY must do
61}
62
63static bool CanEliminate(const DocModification& mh) {
64 return
7e0c58e9 65 (mh.modificationType & (SC_MOD_BEFOREINSERT | SC_MOD_BEFOREDELETE)) != 0;
1e9bafca
RD
66}
67
68/*
69 return whether this modification represents the FINAL step
70 in a [possibly lengthy] multi-step Undo/Redo sequence
71*/
72static bool IsLastStep(const DocModification& mh) {
73 return
7e0c58e9
RD
74 (mh.modificationType & (SC_PERFORMED_UNDO | SC_PERFORMED_REDO)) != 0
75 && (mh.modificationType & SC_MULTISTEPUNDOREDO) != 0
76 && (mh.modificationType & SC_LASTSTEPINUNDOREDO) != 0
77 && (mh.modificationType & SC_MULTILINEUNDOREDO) != 0;
1e9bafca
RD
78}
79
9ce192d4 80Caret::Caret() :
7e0c58e9 81 active(false), on(false), period(500) {}
9ce192d4
RD
82
83Timer::Timer() :
7e0c58e9 84 ticking(false), ticksToWait(0), tickerID(0) {}
9ce192d4 85
8e54aaed 86Idler::Idler() :
7e0c58e9 87 state(false), idlerID(0) {}
1a2fb4cd 88
7e0c58e9
RD
89static inline bool IsControlCharacter(int ch) {
90 // iscntrl returns true for lots of chars > 127 which are displayable
91 return ch >= 0 && ch < ' ';
1a2fb4cd
RD
92}
93
9ce192d4
RD
94Editor::Editor() {
95 ctrlID = 0;
96
97 stylesValid = false;
98
d134f170
RD
99 printMagnification = 0;
100 printColourMode = SC_PRINT_NORMAL;
9e730a78 101 printWrapState = eWrapWord;
65ec6247 102 cursorMode = SC_CURSORNORMAL;
1a2fb4cd 103 controlCharSymbol = 0; /* Draw the control characters */
d134f170 104
65ec6247 105 hasFocus = false;
9ce192d4
RD
106 hideSelection = false;
107 inOverstrike = false;
65ec6247
RD
108 errorStatus = 0;
109 mouseDownCaptures = true;
9ce192d4
RD
110
111 bufferedDraw = true;
9e730a78 112 twoPhaseDraw = true;
9ce192d4
RD
113
114 lastClickTime = 0;
65ec6247
RD
115 dwellDelay = SC_TIME_FOREVER;
116 ticksToDwell = SC_TIME_FOREVER;
117 dwelling = false;
9ce192d4
RD
118 ptMouseLast.x = 0;
119 ptMouseLast.y = 0;
7e0c58e9 120 inDragDrop = ddNone;
9ce192d4 121 dropWentOutside = false;
9e96e16f
RD
122 posDrag = SelectionPosition(invalidPosition);
123 posDrop = SelectionPosition(invalidPosition);
9ce192d4
RD
124 selectionType = selChar;
125
126 lastXChosen = 0;
127 lineAnchor = 0;
128 originalAnchorPos = 0;
129
d134f170 130 primarySelection = true;
9ce192d4 131
a834585d
RD
132 caretXPolicy = CARET_SLOP | CARET_EVEN;
133 caretXSlop = 50;
9ce192d4 134
a834585d
RD
135 caretYPolicy = CARET_EVEN;
136 caretYSlop = 0;
65ec6247 137
9ce192d4 138 searchAnchor = 0;
d134f170 139
9ce192d4
RD
140 xOffset = 0;
141 xCaretMargin = 50;
f6bcfd97 142 horizontalScrollBarVisible = true;
a834585d 143 scrollWidth = 2000;
7e0c58e9
RD
144 trackLineWidth = false;
145 lineWidthMaxSeen = 0;
9e730a78 146 verticalScrollBarVisible = true;
a834585d 147 endAtLastLine = true;
1e9bafca 148 caretSticky = false;
9e96e16f
RD
149 multipleSelection = false;
150 additionalSelectionTyping = false;
151 additionalCaretsBlink = true;
152 additionalCaretsVisible = true;
153 virtualSpaceOptions = SCVS_NONE;
d134f170 154
1a2fb4cd
RD
155 pixmapLine = Surface::Allocate();
156 pixmapSelMargin = Surface::Allocate();
157 pixmapSelPattern = Surface::Allocate();
158 pixmapIndentGuide = Surface::Allocate();
159 pixmapIndentGuideHighlight = Surface::Allocate();
160
65ec6247
RD
161 targetStart = 0;
162 targetEnd = 0;
163 searchFlags = 0;
164
9ce192d4
RD
165 topLine = 0;
166 posTopLine = 0;
d134f170 167
1e9bafca 168 lengthForEncode = -1;
a33203cb 169
9ce192d4 170 needUpdateUI = true;
d134f170
RD
171 braces[0] = invalidPosition;
172 braces[1] = invalidPosition;
9ce192d4 173 bracesMatchStyle = STYLE_BRACEBAD;
d134f170
RD
174 highlightGuideColumn = 0;
175
9ce192d4 176 theEdge = 0;
d134f170 177
9ce192d4 178 paintState = notPainting;
d134f170 179
9ce192d4
RD
180 modEventMask = SC_MODEVENTMASKALL;
181
182 pdoc = new Document();
9e730a78 183 pdoc->AddRef();
9ce192d4
RD
184 pdoc->AddWatcher(this, 0);
185
b8b0e402 186 recordingMacro = false;
9ce192d4 187 foldFlags = 0;
1a2fb4cd
RD
188
189 wrapState = eWrapNone;
190 wrapWidth = LineLayout::wrapWidthInfinite;
b8193d80
RD
191 wrapStart = wrapLineLarge;
192 wrapEnd = wrapLineLarge;
591d01be
RD
193 wrapVisualFlags = 0;
194 wrapVisualFlagsLocation = 0;
195 wrapVisualStartIndent = 0;
9e96e16f
RD
196 wrapIndentMode = SC_WRAPINDENT_FIXED;
197 wrapAddIndent = 0;
1a2fb4cd 198
1e9bafca
RD
199 convertPastes = true;
200
9e730a78
RD
201 hsStart = -1;
202 hsEnd = -1;
203
204 llc.SetLevel(LineLayoutCache::llcCaret);
7e0c58e9 205 posCache.SetSize(0x400);
9ce192d4
RD
206}
207
208Editor::~Editor() {
209 pdoc->RemoveWatcher(this, 0);
210 pdoc->Release();
211 pdoc = 0;
212 DropGraphics();
1a2fb4cd
RD
213 delete pixmapLine;
214 delete pixmapSelMargin;
215 delete pixmapSelPattern;
216 delete pixmapIndentGuide;
217 delete pixmapIndentGuideHighlight;
9ce192d4
RD
218}
219
220void Editor::Finalise() {
8e54aaed 221 SetIdle(false);
d134f170 222 CancelModes();
9ce192d4
RD
223}
224
225void Editor::DropGraphics() {
1a2fb4cd
RD
226 pixmapLine->Release();
227 pixmapSelMargin->Release();
228 pixmapSelPattern->Release();
229 pixmapIndentGuide->Release();
1e9bafca 230 pixmapIndentGuideHighlight->Release();
9ce192d4
RD
231}
232
233void Editor::InvalidateStyleData() {
234 stylesValid = false;
9ce192d4 235 DropGraphics();
9e96e16f 236 palette.Release();
1a2fb4cd 237 llc.Invalidate(LineLayout::llInvalid);
7e0c58e9 238 posCache.Clear();
9ce192d4
RD
239}
240
241void Editor::InvalidateStyleRedraw() {
f114b858 242 NeedWrapping();
9ce192d4
RD
243 InvalidateStyleData();
244 Redraw();
245}
246
247void Editor::RefreshColourPalette(Palette &pal, bool want) {
248 vs.RefreshColourPalette(pal, want);
249}
250
251void Editor::RefreshStyleData() {
252 if (!stylesValid) {
253 stylesValid = true;
9e730a78 254 AutoSurface surface(this);
1a2fb4cd
RD
255 if (surface) {
256 vs.Refresh(*surface);
257 RefreshColourPalette(palette, true);
258 palette.Allocate(wMain);
259 RefreshColourPalette(palette, false);
260 }
9e96e16f
RD
261 if (wrapIndentMode == SC_WRAPINDENT_INDENT) {
262 wrapAddIndent = pdoc->IndentSize() * vs.spaceWidth;
263 } else if (wrapIndentMode == SC_WRAPINDENT_SAME) {
264 wrapAddIndent = 0;
265 } else { //SC_WRAPINDENT_FIXED
266 wrapAddIndent = wrapVisualStartIndent * vs.aveCharWidth;
267 if ((wrapVisualFlags & SC_WRAPVISUALFLAG_START) && (wrapAddIndent <= 0))
268 wrapAddIndent = vs.aveCharWidth; // must indent to show start visual
269 }
9ce192d4 270 SetScrollBars();
9e96e16f 271 SetRectangularRange();
9ce192d4
RD
272 }
273}
274
275PRectangle Editor::GetClientRectangle() {
65ec6247 276 return wMain.GetClientPosition();
9ce192d4
RD
277}
278
279PRectangle Editor::GetTextRectangle() {
280 PRectangle rc = GetClientRectangle();
281 rc.left += vs.fixedColumnWidth;
282 rc.right -= vs.rightMarginWidth;
283 return rc;
284}
285
286int Editor::LinesOnScreen() {
287 PRectangle rcClient = GetClientRectangle();
288 int htClient = rcClient.bottom - rcClient.top;
289 //Platform::DebugPrintf("lines on screen = %d\n", htClient / lineHeight + 1);
7e0c58e9 290 return htClient / vs.lineHeight;
9ce192d4
RD
291}
292
293int Editor::LinesToScroll() {
294 int retVal = LinesOnScreen() - 1;
295 if (retVal < 1)
296 return 1;
297 else
298 return retVal;
299}
300
301int Editor::MaxScrollPos() {
302 //Platform::DebugPrintf("Lines %d screen = %d maxScroll = %d\n",
303 //LinesTotal(), LinesOnScreen(), LinesTotal() - LinesOnScreen() + 1);
a834585d
RD
304 int retVal = cs.LinesDisplayed();
305 if (endAtLastLine) {
306 retVal -= LinesOnScreen();
307 } else {
308 retVal--;
309 }
310 if (retVal < 0) {
9ce192d4 311 return 0;
a834585d 312 } else {
9ce192d4 313 return retVal;
a834585d 314 }
9ce192d4
RD
315}
316
f6bcfd97 317const char *ControlCharacterString(unsigned char ch) {
9ce192d4 318 const char *reps[] = {
9e730a78
RD
319 "NUL", "SOH", "STX", "ETX", "EOT", "ENQ", "ACK", "BEL",
320 "BS", "HT", "LF", "VT", "FF", "CR", "SO", "SI",
321 "DLE", "DC1", "DC2", "DC3", "DC4", "NAK", "SYN", "ETB",
322 "CAN", "EM", "SUB", "ESC", "FS", "GS", "RS", "US"
9ce192d4
RD
323 };
324 if (ch < (sizeof(reps) / sizeof(reps[0]))) {
325 return reps[ch];
326 } else {
327 return "BAD";
328 }
329}
330
8e54aaed
RD
331/**
332 * Convenience class to ensure LineLayout objects are always disposed.
333 */
9e730a78
RD
334class AutoLineLayout {
335 LineLayoutCache &llc;
336 LineLayout *ll;
9e96e16f 337 AutoLineLayout &operator=(const AutoLineLayout &);
9e730a78
RD
338public:
339 AutoLineLayout(LineLayoutCache &llc_, LineLayout *ll_) : llc(llc_), ll(ll_) {}
340 ~AutoLineLayout() {
341 llc.Dispose(ll);
342 ll = 0;
343 }
344 LineLayout *operator->() const {
345 return ll;
346 }
347 operator LineLayout *() const {
348 return ll;
349 }
350 void Set(LineLayout *ll_) {
351 llc.Dispose(ll);
352 ll = ll_;
353 }
354};
355
9e96e16f
RD
356SelectionPosition Editor::ClampPositionIntoDocument(SelectionPosition sp) const {
357 if (sp.Position() < 0) {
358 return SelectionPosition(0);
359 } else if (sp.Position() > pdoc->Length()) {
360 return SelectionPosition(pdoc->Length());
361 } else {
362 // If not at end of line then set offset to 0
363 if (!pdoc->IsLineEndPosition(sp.Position()))
364 sp.SetVirtualSpace(0);
365 return sp;
8e54aaed 366 }
7e0c58e9 367}
7e0c58e9 368
9e96e16f 369Point Editor::LocationFromPosition(SelectionPosition pos) {
65ec6247 370 Point pt;
9ce192d4 371 RefreshStyleData();
9e96e16f 372 if (pos.Position() == INVALID_POSITION)
65ec6247 373 return pt;
9e96e16f 374 int line = pdoc->LineFromPosition(pos.Position());
9ce192d4
RD
375 int lineVisible = cs.DisplayFromDoc(line);
376 //Platform::DebugPrintf("line=%d\n", line);
9e730a78
RD
377 AutoSurface surface(this);
378 AutoLineLayout ll(llc, RetrieveLineLayout(line));
1a2fb4cd
RD
379 if (surface && ll) {
380 // -1 because of adding in for visible lines in following loop.
381 pt.y = (lineVisible - topLine - 1) * vs.lineHeight;
382 pt.x = 0;
383 unsigned int posLineStart = pdoc->LineStart(line);
384 LayoutLine(line, surface, vs, ll, wrapWidth);
9e96e16f 385 int posInLine = pos.Position() - posLineStart;
1a2fb4cd
RD
386 // In case of very long line put x at arbitrary large position
387 if (posInLine > ll->maxLineLength) {
388 pt.x = ll->positions[ll->maxLineLength] - ll->positions[ll->LineStart(ll->lines)];
389 }
591d01be 390
9e730a78
RD
391 for (int subLine = 0; subLine < ll->lines; subLine++) {
392 if ((posInLine >= ll->LineStart(subLine)) && (posInLine <= ll->LineStart(subLine + 1))) {
1a2fb4cd 393 pt.x = ll->positions[posInLine] - ll->positions[ll->LineStart(subLine)];
9e96e16f 394 if (ll->wrapIndent != 0) {
591d01be
RD
395 int lineStart = ll->LineStart(subLine);
396 if (lineStart != 0) // Wrapped
9e96e16f 397 pt.x += ll->wrapIndent;
591d01be 398 }
1a2fb4cd
RD
399 }
400 if (posInLine >= ll->LineStart(subLine)) {
401 pt.y += vs.lineHeight;
402 }
403 }
404 pt.x += vs.fixedColumnWidth - xOffset;
9ce192d4 405 }
9e96e16f 406 pt.x += pos.VirtualSpace() * static_cast<int>(vs.styles[ll->EndLineStyle()].spaceWidth);
9ce192d4
RD
407 return pt;
408}
409
9e96e16f
RD
410Point Editor::LocationFromPosition(int pos) {
411 return LocationFromPosition(SelectionPosition(pos));
412}
413
65ec6247 414int Editor::XFromPosition(int pos) {
9ce192d4
RD
415 Point pt = LocationFromPosition(pos);
416 return pt.x - vs.fixedColumnWidth + xOffset;
417}
d134f170 418
9e96e16f
RD
419int Editor::XFromPosition(SelectionPosition sp) {
420 Point pt = LocationFromPosition(sp);
421 return pt.x - vs.fixedColumnWidth + xOffset;
422}
423
9ce192d4
RD
424int Editor::LineFromLocation(Point pt) {
425 return cs.DocFromDisplay(pt.y / vs.lineHeight + topLine);
426}
427
428void Editor::SetTopLine(int topLineNew) {
429 topLine = topLineNew;
a33203cb 430 posTopLine = pdoc->LineStart(cs.DocFromDisplay(topLine));
9ce192d4
RD
431}
432
9e96e16f 433SelectionPosition Editor::SPositionFromLocation(Point pt, bool canReturnInvalid, bool charPosition, bool virtualSpace) {
9ce192d4 434 RefreshStyleData();
9e96e16f
RD
435 if (canReturnInvalid) {
436 PRectangle rcClient = GetTextRectangle();
437 if (!rcClient.Contains(pt))
438 return SelectionPosition(INVALID_POSITION);
439 if (pt.x < vs.fixedColumnWidth)
440 return SelectionPosition(INVALID_POSITION);
441 if (pt.y < 0)
442 return SelectionPosition(INVALID_POSITION);
443 }
9ce192d4 444 pt.x = pt.x - vs.fixedColumnWidth + xOffset;
1a2fb4cd 445 int visibleLine = pt.y / vs.lineHeight + topLine;
9ce192d4 446 if (pt.y < 0) { // Division rounds towards 0
1a2fb4cd 447 visibleLine = (pt.y - (vs.lineHeight - 1)) / vs.lineHeight + topLine;
9ce192d4 448 }
9e96e16f 449 if (!canReturnInvalid && (visibleLine < 0))
1a2fb4cd
RD
450 visibleLine = 0;
451 int lineDoc = cs.DocFromDisplay(visibleLine);
9e96e16f
RD
452 if (canReturnInvalid && (lineDoc < 0))
453 return SelectionPosition(INVALID_POSITION);
1a2fb4cd 454 if (lineDoc >= pdoc->LinesTotal())
9e96e16f 455 return SelectionPosition(canReturnInvalid ? INVALID_POSITION : pdoc->Length());
9e730a78 456 unsigned int posLineStart = pdoc->LineStart(lineDoc);
9e96e16f 457 SelectionPosition retVal(canReturnInvalid ? INVALID_POSITION : static_cast<int>(posLineStart));
9e730a78
RD
458 AutoSurface surface(this);
459 AutoLineLayout ll(llc, RetrieveLineLayout(lineDoc));
1a2fb4cd
RD
460 if (surface && ll) {
461 LayoutLine(lineDoc, surface, vs, ll, wrapWidth);
1a2fb4cd
RD
462 int lineStartSet = cs.DisplayFromDoc(lineDoc);
463 int subLine = visibleLine - lineStartSet;
464 if (subLine < ll->lines) {
465 int lineStart = ll->LineStart(subLine);
7e0c58e9 466 int lineEnd = ll->LineLastVisible(subLine);
1a2fb4cd 467 int subLineStart = ll->positions[lineStart];
591d01be 468
9e96e16f 469 if (ll->wrapIndent != 0) {
591d01be 470 if (lineStart != 0) // Wrapped
9e96e16f 471 pt.x -= ll->wrapIndent;
591d01be 472 }
7e0c58e9
RD
473 int i = ll->FindBefore(pt.x + subLineStart, lineStart, lineEnd);
474 while (i < lineEnd) {
9e96e16f
RD
475 if (charPosition) {
476 if ((pt.x + subLineStart) < (ll->positions[i + 1])) {
477 return SelectionPosition(pdoc->MovePositionOutsideChar(i + posLineStart, 1));
478 }
479 } else {
480 if ((pt.x + subLineStart) < ((ll->positions[i] + ll->positions[i + 1]) / 2)) {
481 return SelectionPosition(pdoc->MovePositionOutsideChar(i + posLineStart, 1));
482 }
1a2fb4cd 483 }
7e0c58e9 484 i++;
1a2fb4cd 485 }
9e96e16f
RD
486 if (virtualSpace) {
487 const int spaceWidth = static_cast<int>(vs.styles[ll->EndLineStyle()].spaceWidth);
488 int spaceOffset = (pt.x + subLineStart - ll->positions[lineEnd] + spaceWidth / 2) /
489 spaceWidth;
490 return SelectionPosition(lineEnd + posLineStart, spaceOffset);
491 } else if (canReturnInvalid) {
492 if (pt.x < (ll->positions[lineEnd] - subLineStart)) {
493 return SelectionPosition(pdoc->MovePositionOutsideChar(lineEnd + posLineStart, 1));
494 }
495 } else {
496 return SelectionPosition(lineEnd + posLineStart);
497 }
9ce192d4 498 }
9e96e16f
RD
499 if (!canReturnInvalid)
500 return SelectionPosition(ll->numCharsInLine + posLineStart);
9ce192d4 501 }
1a2fb4cd 502 return retVal;
9ce192d4
RD
503}
504
9e96e16f
RD
505int Editor::PositionFromLocation(Point pt, bool canReturnInvalid, bool charPosition) {
506 return SPositionFromLocation(pt, canReturnInvalid, charPosition, false).Position();
507}
508
509/**
510 * Find the document position corresponding to an x coordinate on a particular document line.
511 * Ensure is between whole characters when document is in multi-byte or UTF-8 mode.
512 */
513int Editor::PositionFromLineX(int lineDoc, int x) {
65ec6247 514 RefreshStyleData();
1a2fb4cd 515 if (lineDoc >= pdoc->LinesTotal())
9e96e16f
RD
516 return pdoc->Length();
517 //Platform::DebugPrintf("Position of (%d,%d) line = %d top=%d\n", pt.x, pt.y, line, topLine);
9e730a78
RD
518 AutoSurface surface(this);
519 AutoLineLayout ll(llc, RetrieveLineLayout(lineDoc));
9e96e16f 520 int retVal = 0;
1a2fb4cd 521 if (surface && ll) {
1a2fb4cd 522 unsigned int posLineStart = pdoc->LineStart(lineDoc);
9e96e16f
RD
523 LayoutLine(lineDoc, surface, vs, ll, wrapWidth);
524 retVal = ll->numCharsBeforeEOL + posLineStart;
525 int subLine = 0;
526 int lineStart = ll->LineStart(subLine);
527 int lineEnd = ll->LineLastVisible(subLine);
528 int subLineStart = ll->positions[lineStart];
591d01be 529
9e96e16f
RD
530 if (ll->wrapIndent != 0) {
531 if (lineStart != 0) // Wrapped
532 x -= ll->wrapIndent;
533 }
534 int i = ll->FindBefore(x + subLineStart, lineStart, lineEnd);
535 while (i < lineEnd) {
536 if ((x + subLineStart) < ((ll->positions[i] + ll->positions[i + 1]) / 2)) {
537 retVal = pdoc->MovePositionOutsideChar(i + posLineStart, 1);
538 break;
b8193d80 539 }
9e96e16f 540 i++;
65ec6247
RD
541 }
542 }
9e96e16f 543 return retVal;
65ec6247
RD
544}
545
a834585d
RD
546/**
547 * Find the document position corresponding to an x coordinate on a particular document line.
548 * Ensure is between whole characters when document is in multi-byte or UTF-8 mode.
549 */
9e96e16f 550SelectionPosition Editor::SPositionFromLineX(int lineDoc, int x) {
9ce192d4 551 RefreshStyleData();
1a2fb4cd 552 if (lineDoc >= pdoc->LinesTotal())
9e96e16f 553 return SelectionPosition(pdoc->Length());
9ce192d4 554 //Platform::DebugPrintf("Position of (%d,%d) line = %d top=%d\n", pt.x, pt.y, line, topLine);
9e730a78
RD
555 AutoSurface surface(this);
556 AutoLineLayout ll(llc, RetrieveLineLayout(lineDoc));
1a2fb4cd
RD
557 int retVal = 0;
558 if (surface && ll) {
559 unsigned int posLineStart = pdoc->LineStart(lineDoc);
560 LayoutLine(lineDoc, surface, vs, ll, wrapWidth);
1a2fb4cd
RD
561 int subLine = 0;
562 int lineStart = ll->LineStart(subLine);
7e0c58e9 563 int lineEnd = ll->LineLastVisible(subLine);
1a2fb4cd 564 int subLineStart = ll->positions[lineStart];
591d01be 565
9e96e16f 566 if (ll->wrapIndent != 0) {
591d01be 567 if (lineStart != 0) // Wrapped
9e96e16f 568 x -= ll->wrapIndent;
591d01be 569 }
7e0c58e9
RD
570 int i = ll->FindBefore(x + subLineStart, lineStart, lineEnd);
571 while (i < lineEnd) {
572 if ((x + subLineStart) < ((ll->positions[i] + ll->positions[i + 1]) / 2)) {
1a2fb4cd 573 retVal = pdoc->MovePositionOutsideChar(i + posLineStart, 1);
9e96e16f 574 return SelectionPosition(retVal);
1a2fb4cd 575 }
7e0c58e9 576 i++;
9ce192d4 577 }
9e96e16f
RD
578 const int spaceWidth = static_cast<int>(vs.styles[ll->EndLineStyle()].spaceWidth);
579 int spaceOffset = (x + subLineStart - ll->positions[lineEnd] + spaceWidth / 2) / spaceWidth;
580 return SelectionPosition(lineEnd + posLineStart, spaceOffset);
9ce192d4 581 }
9e96e16f 582 return SelectionPosition(retVal);
9ce192d4
RD
583}
584
8e54aaed
RD
585/**
586 * If painting then abandon the painting because a wider redraw is needed.
587 * @return true if calling code should stop drawing.
588 */
a834585d
RD
589bool Editor::AbandonPaint() {
590 if ((paintState == painting) && !paintingAllText) {
591 paintState = paintAbandoned;
592 }
593 return paintState == paintAbandoned;
594}
595
9ce192d4 596void Editor::RedrawRect(PRectangle rc) {
65ec6247 597 //Platform::DebugPrintf("Redraw %0d,%0d - %0d,%0d\n", rc.left, rc.top, rc.right, rc.bottom);
ce1ecc6d 598
65ec6247
RD
599 // Clip the redraw rectangle into the client area
600 PRectangle rcClient = GetClientRectangle();
601 if (rc.top < rcClient.top)
602 rc.top = rcClient.top;
603 if (rc.bottom > rcClient.bottom)
604 rc.bottom = rcClient.bottom;
605 if (rc.left < rcClient.left)
606 rc.left = rcClient.left;
607 if (rc.right > rcClient.right)
608 rc.right = rcClient.right;
609
610 if ((rc.bottom > rc.top) && (rc.right > rc.left)) {
611 wMain.InvalidateRectangle(rc);
612 }
9ce192d4
RD
613}
614
615void Editor::Redraw() {
616 //Platform::DebugPrintf("Redraw all\n");
e14d10b0
RD
617 PRectangle rcClient = GetClientRectangle();
618 wMain.InvalidateRectangle(rcClient);
619 //wMain.InvalidateAll();
9ce192d4
RD
620}
621
1e9bafca 622void Editor::RedrawSelMargin(int line) {
a834585d
RD
623 if (!AbandonPaint()) {
624 if (vs.maskInLine) {
625 Redraw();
626 } else {
627 PRectangle rcSelMargin = GetClientRectangle();
628 rcSelMargin.right = vs.fixedColumnWidth;
1e9bafca
RD
629 if (line != -1) {
630 int position = pdoc->LineStart(line);
631 PRectangle rcLine = RectangleFromRange(position, position);
632 rcSelMargin.top = rcLine.top;
633 rcSelMargin.bottom = rcLine.bottom;
634 }
a834585d
RD
635 wMain.InvalidateRectangle(rcSelMargin);
636 }
9ce192d4
RD
637 }
638}
639
640PRectangle Editor::RectangleFromRange(int start, int end) {
641 int minPos = start;
642 if (minPos > end)
643 minPos = end;
644 int maxPos = start;
645 if (maxPos < end)
646 maxPos = end;
647 int minLine = cs.DisplayFromDoc(pdoc->LineFromPosition(minPos));
1a2fb4cd
RD
648 int lineDocMax = pdoc->LineFromPosition(maxPos);
649 int maxLine = cs.DisplayFromDoc(lineDocMax) + cs.GetHeight(lineDocMax) - 1;
9ce192d4
RD
650 PRectangle rcClient = GetTextRectangle();
651 PRectangle rc;
652 rc.left = vs.fixedColumnWidth;
653 rc.top = (minLine - topLine) * vs.lineHeight;
654 if (rc.top < 0)
655 rc.top = 0;
656 rc.right = rcClient.right;
657 rc.bottom = (maxLine - topLine + 1) * vs.lineHeight;
658 // Ensure PRectangle is within 16 bit space
659 rc.top = Platform::Clamp(rc.top, -32000, 32000);
660 rc.bottom = Platform::Clamp(rc.bottom, -32000, 32000);
661
662 return rc;
663}
664
665void Editor::InvalidateRange(int start, int end) {
666 RedrawRect(RectangleFromRange(start, end));
667}
668
669int Editor::CurrentPosition() {
9e96e16f 670 return sel.MainCaret();
9ce192d4
RD
671}
672
673bool Editor::SelectionEmpty() {
9e96e16f 674 return sel.Empty();
9ce192d4
RD
675}
676
9e96e16f
RD
677SelectionPosition Editor::SelectionStart() {
678 return sel.RangeMain().Start();
9ce192d4
RD
679}
680
9e96e16f
RD
681SelectionPosition Editor::SelectionEnd() {
682 return sel.RangeMain().End();
8e54aaed
RD
683}
684
1e9bafca 685void Editor::SetRectangularRange() {
9e96e16f
RD
686 if (sel.IsRectangular()) {
687 int xAnchor = XFromPosition(sel.Rectangular().anchor);
688 int xCaret = XFromPosition(sel.Rectangular().caret);
689 if (sel.selType == Selection::selThin) {
690 xCaret = xAnchor;
691 }
692 int lineAnchor = pdoc->LineFromPosition(sel.Rectangular().anchor.Position());
693 int lineCaret = pdoc->LineFromPosition(sel.Rectangular().caret.Position());
694 int increment = (lineCaret > lineAnchor) ? 1 : -1;
695 for (int line=lineAnchor; line != lineCaret+increment; line += increment) {
696 SelectionRange range(SPositionFromLineX(line, xCaret), SPositionFromLineX(line, xAnchor));
697 if ((virtualSpaceOptions & SCVS_RECTANGULARSELECTION) == 0)
698 range.ClearVirtualSpace();
699 if (line == lineAnchor)
700 sel.SetSelection(range);
701 else
702 sel.AddSelection(range);
703 }
704 }
705}
706
707void Editor::ThinRectangularRange() {
708 if (sel.IsRectangular()) {
709 sel.selType = Selection::selThin;
710 if (sel.Rectangular().caret < sel.Rectangular().anchor) {
711 sel.Rectangular() = SelectionRange(sel.Range(sel.Count()-1).caret, sel.Range(0).anchor);
712 } else {
713 sel.Rectangular() = SelectionRange(sel.Range(sel.Count()-1).anchor, sel.Range(0).caret);
714 }
715 SetRectangularRange();
716 }
717}
718
719void Editor::InvalidateSelection(SelectionRange newMain, bool invalidateWholeSelection) {
720 if (sel.Count() > 1 || !(sel.RangeMain().anchor == newMain.anchor) || sel.IsRectangular()) {
721 invalidateWholeSelection = true;
722 }
723 int firstAffected = Platform::Minimum(sel.RangeMain().Start().Position(), newMain.Start().Position());
724 // +1 for lastAffected ensures caret repainted
725 int lastAffected = Platform::Maximum(newMain.caret.Position()+1, newMain.anchor.Position());
726 lastAffected = Platform::Maximum(lastAffected, sel.RangeMain().End().Position());
727 if (invalidateWholeSelection) {
728 for (size_t r=0; r<sel.Count(); r++) {
729 firstAffected = Platform::Minimum(firstAffected, sel.Range(r).caret.Position());
730 firstAffected = Platform::Minimum(firstAffected, sel.Range(r).anchor.Position());
731 lastAffected = Platform::Maximum(lastAffected, sel.Range(r).caret.Position()+1);
732 lastAffected = Platform::Maximum(lastAffected, sel.Range(r).anchor.Position());
733 }
734 }
8e54aaed
RD
735 needUpdateUI = true;
736 InvalidateRange(firstAffected, lastAffected);
9ce192d4
RD
737}
738
9e96e16f
RD
739void Editor::SetSelection(SelectionPosition currentPos_, SelectionPosition anchor_) {
740 SelectionRange rangeNew(ClampPositionIntoDocument(currentPos_),
741 ClampPositionIntoDocument(anchor_));
742 if (sel.Count() > 1 || !(sel.RangeMain() == rangeNew)) {
743 InvalidateSelection(rangeNew);
8e54aaed 744 }
9e96e16f 745 sel.RangeMain() = rangeNew;
1e9bafca 746 SetRectangularRange();
9ce192d4
RD
747 ClaimSelection();
748}
749
9e96e16f
RD
750void Editor::SetSelection(int currentPos_, int anchor_) {
751 SetSelection(SelectionPosition(currentPos_), SelectionPosition(anchor_));
752}
753
754// Just move the caret on the main selection
755void Editor::SetSelection(SelectionPosition currentPos_) {
756 currentPos_ = ClampPositionIntoDocument(currentPos_);
757 if (sel.Count() > 1 || !(sel.RangeMain().caret == currentPos_)) {
758 InvalidateSelection(SelectionRange(currentPos_));
759 }
760 if (sel.IsRectangular()) {
761 sel.Rectangular() =
762 SelectionRange(SelectionPosition(currentPos_), sel.Rectangular().anchor);
763 SetRectangularRange();
764 } else {
765 sel.RangeMain() =
766 SelectionRange(SelectionPosition(currentPos_), sel.RangeMain().anchor);
767 }
768 ClaimSelection();
769}
770
9ce192d4 771void Editor::SetSelection(int currentPos_) {
9e96e16f
RD
772 SetSelection(SelectionPosition(currentPos_));
773}
774
775void Editor::SetEmptySelection(SelectionPosition currentPos_) {
776 SelectionRange rangeNew(ClampPositionIntoDocument(currentPos_));
777 if (sel.Count() > 1 || !(sel.RangeMain() == rangeNew)) {
778 InvalidateSelection(rangeNew);
8e54aaed 779 }
9e96e16f
RD
780 sel.Clear();
781 sel.RangeMain() = rangeNew;
1e9bafca 782 SetRectangularRange();
9ce192d4 783 ClaimSelection();
9e96e16f 784
9ce192d4
RD
785}
786
787void Editor::SetEmptySelection(int currentPos_) {
9e96e16f 788 SetEmptySelection(SelectionPosition(currentPos_));
9ce192d4
RD
789}
790
9e730a78
RD
791bool Editor::RangeContainsProtected(int start, int end) const {
792 if (vs.ProtectionActive()) {
793 if (start > end) {
794 int t = start;
795 start = end;
796 end = t;
797 }
798 int mask = pdoc->stylingBitsMask;
799 for (int pos = start; pos < end; pos++) {
800 if (vs.styles[pdoc->StyleAt(pos) & mask].IsProtected())
801 return true;
802 }
803 }
804 return false;
805}
806
8e54aaed 807bool Editor::SelectionContainsProtected() {
9e96e16f
RD
808 for (size_t r=0; r<sel.Count(); r++) {
809 if (RangeContainsProtected(sel.Range(r).Start().Position(),
810 sel.Range(r).End().Position())) {
811 return true;
8e54aaed
RD
812 }
813 }
9e96e16f 814 return false;
9e730a78
RD
815}
816
8e54aaed
RD
817/**
818 * Asks document to find a good position and then moves out of any invisible positions.
819 */
9e96e16f
RD
820int Editor::MovePositionOutsideChar(int pos, int moveDir, bool checkLineEnd) const {
821 return MovePositionOutsideChar(SelectionPosition(pos), moveDir, checkLineEnd).Position();
822}
823
824SelectionPosition Editor::MovePositionOutsideChar(SelectionPosition pos, int moveDir, bool checkLineEnd) const {
825 int posMoved = pdoc->MovePositionOutsideChar(pos.Position(), moveDir, checkLineEnd);
826 if (posMoved != pos.Position())
827 pos.SetPosition(posMoved);
9e730a78
RD
828 if (vs.ProtectionActive()) {
829 int mask = pdoc->stylingBitsMask;
830 if (moveDir > 0) {
9e96e16f
RD
831 if ((pos.Position() > 0) && vs.styles[pdoc->StyleAt(pos.Position() - 1) & mask].IsProtected()) {
832 while ((pos.Position() < pdoc->Length()) &&
833 (vs.styles[pdoc->StyleAt(pos.Position()) & mask].IsProtected()))
834 pos.Add(1);
9e730a78
RD
835 }
836 } else if (moveDir < 0) {
9e96e16f
RD
837 if (vs.styles[pdoc->StyleAt(pos.Position()) & mask].IsProtected()) {
838 while ((pos.Position() > 0) &&
839 (vs.styles[pdoc->StyleAt(pos.Position() - 1) & mask].IsProtected()))
840 pos.Add(-1);
9e730a78
RD
841 }
842 }
d134f170
RD
843 }
844 return pos;
845}
846
9e96e16f
RD
847int Editor::MovePositionTo(SelectionPosition newPos, Selection::selTypes selt, bool ensureVisible) {
848 int delta = newPos.Position() - sel.MainCaret();
849 newPos = ClampPositionIntoDocument(newPos);
d134f170 850 newPos = MovePositionOutsideChar(newPos, delta);
9e96e16f
RD
851 if (!sel.IsRectangular() && (selt == Selection::selRectangle)) {
852 // Switching to rectangular
853 SelectionRange rangeMain = sel.RangeMain();
854 sel.Clear();
855 sel.Rectangular() = rangeMain;
8e54aaed 856 }
9e96e16f
RD
857 if (selt != Selection::noSel) {
858 sel.selType = selt;
859 }
860 if (selt != Selection::noSel || sel.MoveExtends()) {
9ce192d4
RD
861 SetSelection(newPos);
862 } else {
863 SetEmptySelection(newPos);
864 }
9e730a78 865 ShowCaretAtCurrentPosition();
8e54aaed 866 if (ensureVisible) {
f114b858 867 EnsureCaretVisible();
8e54aaed 868 }
9ce192d4
RD
869 return 0;
870}
871
9e96e16f
RD
872int Editor::MovePositionTo(int newPos, Selection::selTypes selt, bool ensureVisible) {
873 return MovePositionTo(SelectionPosition(newPos), selt, ensureVisible);
874}
875
876SelectionPosition Editor::MovePositionSoVisible(SelectionPosition pos, int moveDir) {
877 pos = ClampPositionIntoDocument(pos);
d134f170 878 pos = MovePositionOutsideChar(pos, moveDir);
9e96e16f 879 int lineDoc = pdoc->LineFromPosition(pos.Position());
9ce192d4
RD
880 if (cs.GetVisible(lineDoc)) {
881 return pos;
882 } else {
883 int lineDisplay = cs.DisplayFromDoc(lineDoc);
884 if (moveDir > 0) {
1a2fb4cd
RD
885 // lineDisplay is already line before fold as lines in fold use display line of line after fold
886 lineDisplay = Platform::Clamp(lineDisplay, 0, cs.LinesDisplayed());
9e96e16f 887 return SelectionPosition(pdoc->LineStart(cs.DocFromDisplay(lineDisplay)));
9ce192d4 888 } else {
1a2fb4cd 889 lineDisplay = Platform::Clamp(lineDisplay - 1, 0, cs.LinesDisplayed());
9e96e16f 890 return SelectionPosition(pdoc->LineEnd(cs.DocFromDisplay(lineDisplay)));
9ce192d4
RD
891 }
892 }
893}
894
9e96e16f
RD
895SelectionPosition Editor::MovePositionSoVisible(int pos, int moveDir) {
896 return MovePositionSoVisible(SelectionPosition(pos), moveDir);
897}
898
899Point Editor::PointMainCaret() {
900 return LocationFromPosition(sel.Range(sel.Main()).caret);
901}
902
8e54aaed
RD
903/**
904 * Choose the x position that the caret will try to stick to
905 * as it moves up and down.
906 */
9ce192d4 907void Editor::SetLastXChosen() {
9e96e16f 908 Point pt = PointMainCaret();
9ce192d4
RD
909 lastXChosen = pt.x;
910}
911
9e730a78 912void Editor::ScrollTo(int line, bool moveThumb) {
9ce192d4
RD
913 int topLineNew = Platform::Clamp(line, 0, MaxScrollPos());
914 if (topLineNew != topLine) {
915 // Try to optimise small scrolls
916 int linesToMove = topLine - topLineNew;
917 SetTopLine(topLineNew);
918 ShowCaretAtCurrentPosition();
919 // Perform redraw rather than scroll if many lines would be redrawn anyway.
1e9bafca 920#ifndef UNDER_CE
7e0c58e9 921 if ((abs(linesToMove) <= 10) && (paintState == notPainting)) {
9ce192d4
RD
922 ScrollText(linesToMove);
923 } else {
924 Redraw();
925 }
1e9bafca
RD
926#else
927 Redraw();
928#endif
9e730a78
RD
929 if (moveThumb) {
930 SetVerticalScrollPos();
931 }
9ce192d4
RD
932 }
933}
934
f6bcfd97 935void Editor::ScrollText(int /* linesToMove */) {
9ce192d4
RD
936 //Platform::DebugPrintf("Editor::ScrollText %d\n", linesToMove);
937 Redraw();
938}
939
940void Editor::HorizontalScrollTo(int xPos) {
941 //Platform::DebugPrintf("HorizontalScroll %d\n", xPos);
1a2fb4cd
RD
942 if (xPos < 0)
943 xPos = 0;
944 if ((wrapState == eWrapNone) && (xOffset != xPos)) {
945 xOffset = xPos;
946 SetHorizontalScrollPos();
947 RedrawRect(GetClientRectangle());
948 }
9ce192d4
RD
949}
950
f114b858 951void Editor::MoveCaretInsideView(bool ensureVisible) {
f6bcfd97 952 PRectangle rcClient = GetTextRectangle();
9e96e16f 953 Point pt = PointMainCaret();
f6bcfd97 954 if (pt.y < rcClient.top) {
9e96e16f 955 MovePositionTo(SPositionFromLocation(
7e0c58e9 956 Point(lastXChosen, rcClient.top)),
9e96e16f 957 Selection::noSel, ensureVisible);
f6bcfd97 958 } else if ((pt.y + vs.lineHeight - 1) > rcClient.bottom) {
d134f170 959 int yOfLastLineFullyDisplayed = rcClient.top + (LinesOnScreen() - 1) * vs.lineHeight;
9e96e16f 960 MovePositionTo(SPositionFromLocation(
7e0c58e9 961 Point(lastXChosen, rcClient.top + yOfLastLineFullyDisplayed)),
9e96e16f 962 Selection::noSel, ensureVisible);
f6bcfd97
BP
963 }
964}
965
1a2fb4cd
RD
966int Editor::DisplayFromPosition(int pos) {
967 int lineDoc = pdoc->LineFromPosition(pos);
968 int lineDisplay = cs.DisplayFromDoc(lineDoc);
9e730a78
RD
969 AutoSurface surface(this);
970 AutoLineLayout ll(llc, RetrieveLineLayout(lineDoc));
1a2fb4cd
RD
971 if (surface && ll) {
972 LayoutLine(lineDoc, surface, vs, ll, wrapWidth);
973 unsigned int posLineStart = pdoc->LineStart(lineDoc);
974 int posInLine = pos - posLineStart;
975 lineDisplay--; // To make up for first increment ahead.
9e730a78
RD
976 for (int subLine = 0; subLine < ll->lines; subLine++) {
977 if (posInLine >= ll->LineStart(subLine)) {
1a2fb4cd
RD
978 lineDisplay++;
979 }
980 }
981 }
1a2fb4cd
RD
982 return lineDisplay;
983}
984
a834585d
RD
985/**
986 * Ensure the caret is reasonably visible in context.
987 *
988Caret policy in SciTE
989
990If slop is set, we can define a slop value.
991This value defines an unwanted zone (UZ) where the caret is... unwanted.
992This zone is defined as a number of pixels near the vertical margins,
993and as a number of lines near the horizontal margins.
994By keeping the caret away from the edges, it is seen within its context,
995so it is likely that the identifier that the caret is on can be completely seen,
996and that the current line is seen with some of the lines following it which are
997often dependent on that line.
998
999If strict is set, the policy is enforced... strictly.
1000The caret is centred on the display if slop is not set,
1001and cannot go in the UZ if slop is set.
1002
1003If jumps is set, the display is moved more energetically
1004so the caret can move in the same direction longer before the policy is applied again.
1005'3UZ' notation is used to indicate three time the size of the UZ as a distance to the margin.
1006
1007If even is not set, instead of having symmetrical UZs,
1008the left and bottom UZs are extended up to right and top UZs respectively.
1009This way, we favour the displaying of useful information: the begining of lines,
1010where most code reside, and the lines after the caret, eg. the body of a function.
1011
1012 | | | | |
9e96e16f 1013slop | strict | jumps | even | Caret can go to the margin | When reaching limit (caret going out of
a834585d
RD
1014 | | | | | visibility or going into the UZ) display is...
1015-----+--------+-------+------+--------------------------------------------+--------------------------------------------------------------
1016 0 | 0 | 0 | 0 | Yes | moved to put caret on top/on right
1017 0 | 0 | 0 | 1 | Yes | moved by one position
1018 0 | 0 | 1 | 0 | Yes | moved to put caret on top/on right
1019 0 | 0 | 1 | 1 | Yes | centred on the caret
1020 0 | 1 | - | 0 | Caret is always on top/on right of display | -
1021 0 | 1 | - | 1 | No, caret is always centred | -
1022 1 | 0 | 0 | 0 | Yes | moved to put caret out of the asymmetrical UZ
1023 1 | 0 | 0 | 1 | Yes | moved to put caret out of the UZ
1024 1 | 0 | 1 | 0 | Yes | moved to put caret at 3UZ of the top or right margin
1025 1 | 0 | 1 | 1 | Yes | moved to put caret at 3UZ of the margin
1026 1 | 1 | - | 0 | Caret is always at UZ of top/right margin | -
1027 1 | 1 | 0 | 1 | No, kept out of UZ | moved by one position
1028 1 | 1 | 1 | 1 | No, kept out of UZ | moved to put caret at 3UZ of the margin
1029*/
65ec6247
RD
1030void Editor::EnsureCaretVisible(bool useMargin, bool vert, bool horiz) {
1031 //Platform::DebugPrintf("EnsureCaretVisible %d %s\n", xOffset, useMargin ? " margin" : " ");
9ce192d4 1032 PRectangle rcClient = GetTextRectangle();
65ec6247 1033 //int rcClientFullWidth = rcClient.Width();
9e96e16f
RD
1034 SelectionPosition posCaret = sel.RangeMain().caret;
1035 if (posDrag.IsValid()) {
9ce192d4 1036 posCaret = posDrag;
a834585d 1037 }
9ce192d4 1038 Point pt = LocationFromPosition(posCaret);
9ce192d4 1039 Point ptBottomCaret = pt;
9ce192d4 1040 ptBottomCaret.y += vs.lineHeight - 1;
9e96e16f 1041 int lineCaret = DisplayFromPosition(posCaret.Position());
a834585d 1042 bool bSlop, bStrict, bJump, bEven;
d134f170 1043
65ec6247 1044 // Vertical positioning
a834585d
RD
1045 if (vert && (pt.y < rcClient.top || ptBottomCaret.y > rcClient.bottom || (caretYPolicy & CARET_STRICT) != 0)) {
1046 int linesOnScreen = LinesOnScreen();
1047 int halfScreen = Platform::Maximum(linesOnScreen - 1, 2) / 2;
1048 int newTopLine = topLine;
1049 bSlop = (caretYPolicy & CARET_SLOP) != 0;
1050 bStrict = (caretYPolicy & CARET_STRICT) != 0;
1051 bJump = (caretYPolicy & CARET_JUMPS) != 0;
1052 bEven = (caretYPolicy & CARET_EVEN) != 0;
1053
9ce192d4
RD
1054 // It should be possible to scroll the window to show the caret,
1055 // but this fails to remove the caret on GTK+
a834585d
RD
1056 if (bSlop) { // A margin is defined
1057 int yMoveT, yMoveB;
1058 if (bStrict) {
1059 int yMarginT, yMarginB;
1060 if (!useMargin) {
1061 // In drag mode, avoid moves
1062 // otherwise, a double click will select several lines.
1063 yMarginT = yMarginB = 0;
1064 } else {
1065 // yMarginT must equal to caretYSlop, with a minimum of 1 and
1066 // a maximum of slightly less than half the heigth of the text area.
1067 yMarginT = Platform::Clamp(caretYSlop, 1, halfScreen);
1068 if (bEven) {
1069 yMarginB = yMarginT;
1070 } else {
1071 yMarginB = linesOnScreen - yMarginT - 1;
1072 }
1073 }
1074 yMoveT = yMarginT;
1075 if (bEven) {
1076 if (bJump) {
1077 yMoveT = Platform::Clamp(caretYSlop * 3, 1, halfScreen);
1078 }
1079 yMoveB = yMoveT;
1080 } else {
1081 yMoveB = linesOnScreen - yMoveT - 1;
1082 }
1083 if (lineCaret < topLine + yMarginT) {
1084 // Caret goes too high
1085 newTopLine = lineCaret - yMoveT;
1086 } else if (lineCaret > topLine + linesOnScreen - 1 - yMarginB) {
1087 // Caret goes too low
1088 newTopLine = lineCaret - linesOnScreen + 1 + yMoveB;
1089 }
1090 } else { // Not strict
1091 yMoveT = bJump ? caretYSlop * 3 : caretYSlop;
1092 yMoveT = Platform::Clamp(yMoveT, 1, halfScreen);
1093 if (bEven) {
1094 yMoveB = yMoveT;
1095 } else {
1096 yMoveB = linesOnScreen - yMoveT - 1;
1097 }
1098 if (lineCaret < topLine) {
1099 // Caret goes too high
1100 newTopLine = lineCaret - yMoveT;
1101 } else if (lineCaret > topLine + linesOnScreen - 1) {
1102 // Caret goes too low
1103 newTopLine = lineCaret - linesOnScreen + 1 + yMoveB;
1104 }
9ce192d4 1105 }
a834585d
RD
1106 } else { // No slop
1107 if (!bStrict && !bJump) {
1108 // Minimal move
1109 if (lineCaret < topLine) {
1110 // Caret goes too high
1111 newTopLine = lineCaret;
1112 } else if (lineCaret > topLine + linesOnScreen - 1) {
1113 // Caret goes too low
1114 if (bEven) {
1115 newTopLine = lineCaret - linesOnScreen + 1;
1116 } else {
1117 newTopLine = lineCaret;
1118 }
1119 }
1120 } else { // Strict or going out of display
1121 if (bEven) {
1122 // Always center caret
1123 newTopLine = lineCaret - halfScreen;
1124 } else {
1125 // Always put caret on top of display
1126 newTopLine = lineCaret;
1127 }
9ce192d4
RD
1128 }
1129 }
a834585d
RD
1130 newTopLine = Platform::Clamp(newTopLine, 0, MaxScrollPos());
1131 if (newTopLine != topLine) {
9e730a78 1132 Redraw();
a834585d
RD
1133 SetTopLine(newTopLine);
1134 SetVerticalScrollPos();
a834585d 1135 }
65ec6247
RD
1136 }
1137
1138 // Horizontal positioning
1a2fb4cd 1139 if (horiz && (wrapState == eWrapNone)) {
a834585d 1140 int halfScreen = Platform::Maximum(rcClient.Width() - 4, 4) / 2;
9ce192d4 1141 int xOffsetNew = xOffset;
a834585d
RD
1142 bSlop = (caretXPolicy & CARET_SLOP) != 0;
1143 bStrict = (caretXPolicy & CARET_STRICT) != 0;
1144 bJump = (caretXPolicy & CARET_JUMPS) != 0;
1145 bEven = (caretXPolicy & CARET_EVEN) != 0;
1146
1147 if (bSlop) { // A margin is defined
1148 int xMoveL, xMoveR;
1149 if (bStrict) {
1150 int xMarginL, xMarginR;
1151 if (!useMargin) {
1152 // In drag mode, avoid moves unless very near of the margin
1153 // otherwise, a simple click will select text.
1154 xMarginL = xMarginR = 2;
1155 } else {
1156 // xMargin must equal to caretXSlop, with a minimum of 2 and
1157 // a maximum of slightly less than half the width of the text area.
1158 xMarginR = Platform::Clamp(caretXSlop, 2, halfScreen);
1159 if (bEven) {
1160 xMarginL = xMarginR;
1161 } else {
1162 xMarginL = rcClient.Width() - xMarginR - 4;
1163 }
1164 }
1165 if (bJump && bEven) {
1166 // Jump is used only in even mode
1167 xMoveL = xMoveR = Platform::Clamp(caretXSlop * 3, 1, halfScreen);
1168 } else {
1169 xMoveL = xMoveR = 0; // Not used, avoid a warning
1170 }
1171 if (pt.x < rcClient.left + xMarginL) {
1172 // Caret is on the left of the display
1173 if (bJump && bEven) {
1174 xOffsetNew -= xMoveL;
1175 } else {
1176 // Move just enough to allow to display the caret
1177 xOffsetNew -= (rcClient.left + xMarginL) - pt.x;
1178 }
1179 } else if (pt.x >= rcClient.right - xMarginR) {
1180 // Caret is on the right of the display
1181 if (bJump && bEven) {
1182 xOffsetNew += xMoveR;
1183 } else {
1184 // Move just enough to allow to display the caret
1185 xOffsetNew += pt.x - (rcClient.right - xMarginR) + 1;
1186 }
1187 }
1188 } else { // Not strict
1189 xMoveR = bJump ? caretXSlop * 3 : caretXSlop;
1190 xMoveR = Platform::Clamp(xMoveR, 1, halfScreen);
1191 if (bEven) {
1192 xMoveL = xMoveR;
1193 } else {
1194 xMoveL = rcClient.Width() - xMoveR - 4;
1195 }
1196 if (pt.x < rcClient.left) {
1197 // Caret is on the left of the display
1198 xOffsetNew -= xMoveL;
1199 } else if (pt.x >= rcClient.right) {
1200 // Caret is on the right of the display
1201 xOffsetNew += xMoveR;
1202 }
1203 }
1204 } else { // No slop
1205 if (bStrict ||
9e730a78 1206 (bJump && (pt.x < rcClient.left || pt.x >= rcClient.right))) {
a834585d
RD
1207 // Strict or going out of display
1208 if (bEven) {
1209 // Center caret
1210 xOffsetNew += pt.x - rcClient.left - halfScreen;
1211 } else {
1212 // Put caret on right
1213 xOffsetNew += pt.x - rcClient.right + 1;
1214 }
1215 } else {
1216 // Move just enough to allow to display the caret
1217 if (pt.x < rcClient.left) {
1218 // Caret is on the left of the display
1219 if (bEven) {
1220 xOffsetNew -= rcClient.left - pt.x;
1221 } else {
1222 xOffsetNew += pt.x - rcClient.right + 1;
1223 }
1224 } else if (pt.x >= rcClient.right) {
1225 // Caret is on the right of the display
1226 xOffsetNew += pt.x - rcClient.right + 1;
1227 }
1228 }
1229 }
1230 // In case of a jump (find result) largely out of display, adjust the offset to display the caret
1231 if (pt.x + xOffset < rcClient.left + xOffsetNew) {
1232 xOffsetNew = pt.x + xOffset - rcClient.left;
1233 } else if (pt.x + xOffset >= rcClient.right + xOffsetNew) {
1234 xOffsetNew = pt.x + xOffset - rcClient.right + 1;
7e0c58e9
RD
1235 if (vs.caretStyle == CARETSTYLE_BLOCK) {
1236 // Ensure we can see a good portion of the block caret
1237 xOffsetNew += vs.aveCharWidth;
1238 }
a834585d
RD
1239 }
1240 if (xOffsetNew < 0) {
9ce192d4 1241 xOffsetNew = 0;
a834585d 1242 }
9ce192d4
RD
1243 if (xOffset != xOffsetNew) {
1244 xOffset = xOffsetNew;
9e730a78
RD
1245 if (xOffsetNew > 0) {
1246 PRectangle rcText = GetTextRectangle();
7e0c58e9 1247 if (horizontalScrollBarVisible &&
9e730a78
RD
1248 rcText.Width() + xOffset > scrollWidth) {
1249 scrollWidth = xOffset + rcText.Width();
1250 SetScrollBars();
1251 }
1252 }
9ce192d4
RD
1253 SetHorizontalScrollPos();
1254 Redraw();
1255 }
1256 }
a33203cb 1257 UpdateSystemCaret();
9ce192d4
RD
1258}
1259
1260void Editor::ShowCaretAtCurrentPosition() {
1a2fb4cd
RD
1261 if (hasFocus) {
1262 caret.active = true;
1263 caret.on = true;
1264 SetTicking(true);
1265 } else {
9ce192d4
RD
1266 caret.active = false;
1267 caret.on = false;
9ce192d4 1268 }
1a2fb4cd 1269 InvalidateCaret();
9ce192d4
RD
1270}
1271
1272void Editor::DropCaret() {
1273 caret.active = false;
1274 InvalidateCaret();
1275}
1276
1277void Editor::InvalidateCaret() {
9e96e16f
RD
1278 if (posDrag.IsValid()) {
1279 InvalidateRange(posDrag.Position(), posDrag.Position() + 1);
1280 } else {
1281 for (size_t r=0; r<sel.Count(); r++) {
1282 InvalidateRange(sel.Range(r).caret.Position(), sel.Range(r).caret.Position() + 1);
1283 }
1284 }
a33203cb
RD
1285 UpdateSystemCaret();
1286}
1287
1288void Editor::UpdateSystemCaret() {
9ce192d4
RD
1289}
1290
b8193d80
RD
1291void Editor::NeedWrapping(int docLineStart, int docLineEnd) {
1292 docLineStart = Platform::Clamp(docLineStart, 0, pdoc->LinesTotal());
1293 if (wrapStart > docLineStart) {
1294 wrapStart = docLineStart;
a834585d
RD
1295 llc.Invalidate(LineLayout::llPositions);
1296 }
b8193d80
RD
1297 if (wrapEnd < docLineEnd) {
1298 wrapEnd = docLineEnd;
8e54aaed 1299 }
b8193d80 1300 wrapEnd = Platform::Clamp(wrapEnd, 0, pdoc->LinesTotal());
8e54aaed 1301 // Wrap lines during idle.
b8193d80 1302 if ((wrapState != eWrapNone) && (wrapEnd != wrapStart)) {
8e54aaed
RD
1303 SetIdle(true);
1304 }
1a2fb4cd
RD
1305}
1306
7e0c58e9
RD
1307bool Editor::WrapOneLine(Surface *surface, int lineToWrap) {
1308 AutoLineLayout ll(llc, RetrieveLineLayout(lineToWrap));
1309 int linesWrapped = 1;
1310 if (ll) {
1311 LayoutLine(lineToWrap, surface, vs, ll, wrapWidth);
1312 linesWrapped = ll->lines;
1313 }
9e96e16f
RD
1314 return cs.SetHeight(lineToWrap, linesWrapped +
1315 (vs.annotationVisible ? pdoc->AnnotationLines(lineToWrap) : 0));
7e0c58e9
RD
1316}
1317
1a2fb4cd 1318// Check if wrapping needed and perform any needed wrapping.
8e54aaed
RD
1319// fullwrap: if true, all lines which need wrapping will be done,
1320// in this single call.
9e96e16f 1321// priorityWrapLineStart: If greater than or equal to zero, all lines starting from
b8193d80 1322// here to 1 page + 100 lines past will be wrapped (even if there are
8e54aaed 1323// more lines under wrapping process in idle).
b8193d80 1324// If it is neither fullwrap, nor priorityWrap, then 1 page + 100 lines will be
8e54aaed
RD
1325// wrapped, if there are any wrapping going on in idle. (Generally this
1326// condition is called only from idler).
1a2fb4cd 1327// Return true if wrapping occurred.
8e54aaed
RD
1328bool Editor::WrapLines(bool fullWrap, int priorityWrapLineStart) {
1329 // If there are any pending wraps, do them during idle if possible.
b8193d80 1330 int linesInOneCall = LinesOnScreen() + 100;
8e54aaed 1331 if (wrapState != eWrapNone) {
b8193d80
RD
1332 if (wrapStart < wrapEnd) {
1333 if (!SetIdle(true)) {
1334 // Idle processing not supported so full wrap required.
8e54aaed
RD
1335 fullWrap = true;
1336 }
1337 }
1338 if (!fullWrap && priorityWrapLineStart >= 0 &&
7e0c58e9
RD
1339 // .. and if the paint window is outside pending wraps
1340 (((priorityWrapLineStart + linesInOneCall) < wrapStart) ||
1341 (priorityWrapLineStart > wrapEnd))) {
8e54aaed
RD
1342 // No priority wrap pending
1343 return false;
1344 }
1345 }
1a2fb4cd
RD
1346 int goodTopLine = topLine;
1347 bool wrapOccurred = false;
b8193d80 1348 if (wrapStart <= pdoc->LinesTotal()) {
1a2fb4cd
RD
1349 if (wrapState == eWrapNone) {
1350 if (wrapWidth != LineLayout::wrapWidthInfinite) {
1351 wrapWidth = LineLayout::wrapWidthInfinite;
9e730a78 1352 for (int lineDoc = 0; lineDoc < pdoc->LinesTotal(); lineDoc++) {
9e96e16f
RD
1353 cs.SetHeight(lineDoc, 1 +
1354 (vs.annotationVisible ? pdoc->AnnotationLines(lineDoc) : 0));
1a2fb4cd
RD
1355 }
1356 wrapOccurred = true;
1357 }
b8193d80
RD
1358 wrapStart = wrapLineLarge;
1359 wrapEnd = wrapLineLarge;
1a2fb4cd 1360 } else {
b8193d80
RD
1361 if (wrapEnd >= pdoc->LinesTotal())
1362 wrapEnd = pdoc->LinesTotal();
a834585d 1363 //ElapsedTime et;
1a2fb4cd
RD
1364 int lineDocTop = cs.DocFromDisplay(topLine);
1365 int subLineTop = topLine - cs.DisplayFromDoc(lineDocTop);
1366 PRectangle rcTextArea = GetClientRectangle();
1367 rcTextArea.left = vs.fixedColumnWidth;
1368 rcTextArea.right -= vs.rightMarginWidth;
1369 wrapWidth = rcTextArea.Width();
1370 // Ensure all of the document is styled.
1371 pdoc->EnsureStyledTo(pdoc->Length());
8e54aaed 1372 RefreshStyleData();
9e730a78 1373 AutoSurface surface(this);
1a2fb4cd 1374 if (surface) {
8e54aaed 1375 bool priorityWrap = false;
b8193d80
RD
1376 int lastLineToWrap = wrapEnd;
1377 int lineToWrap = wrapStart;
8e54aaed
RD
1378 if (!fullWrap) {
1379 if (priorityWrapLineStart >= 0) {
1380 // This is a priority wrap.
b8193d80
RD
1381 lineToWrap = priorityWrapLineStart;
1382 lastLineToWrap = priorityWrapLineStart + linesInOneCall;
8e54aaed
RD
1383 priorityWrap = true;
1384 } else {
1385 // This is idle wrap.
b8193d80 1386 lastLineToWrap = wrapStart + linesInOneCall;
8e54aaed 1387 }
b8193d80
RD
1388 if (lastLineToWrap >= wrapEnd)
1389 lastLineToWrap = wrapEnd;
8e54aaed
RD
1390 } // else do a fullWrap.
1391
b8193d80
RD
1392 // Platform::DebugPrintf("Wraplines: full = %d, priorityStart = %d (wrapping: %d to %d)\n", fullWrap, priorityWrapLineStart, lineToWrap, lastLineToWrap);
1393 // Platform::DebugPrintf("Pending wraps: %d to %d\n", wrapStart, wrapEnd);
1394 while (lineToWrap < lastLineToWrap) {
7e0c58e9 1395 if (WrapOneLine(surface, lineToWrap)) {
1a2fb4cd
RD
1396 wrapOccurred = true;
1397 }
b8193d80 1398 lineToWrap++;
1a2fb4cd 1399 }
b8193d80
RD
1400 if (!priorityWrap)
1401 wrapStart = lineToWrap;
8e54aaed 1402 // If wrapping is done, bring it to resting position
b8193d80
RD
1403 if (wrapStart >= wrapEnd) {
1404 wrapStart = wrapLineLarge;
1405 wrapEnd = wrapLineLarge;
8e54aaed 1406 }
1a2fb4cd
RD
1407 }
1408 goodTopLine = cs.DisplayFromDoc(lineDocTop);
1409 if (subLineTop < cs.GetHeight(lineDocTop))
1410 goodTopLine += subLineTop;
1411 else
1412 goodTopLine += cs.GetHeight(lineDocTop);
a834585d 1413 //double durWrap = et.Duration(true);
e4f0b986 1414 //Platform::DebugPrintf("Wrap:%9.6g \n", durWrap);
1a2fb4cd
RD
1415 }
1416 }
1417 if (wrapOccurred) {
1418 SetScrollBars();
1419 SetTopLine(Platform::Clamp(goodTopLine, 0, MaxScrollPos()));
1420 SetVerticalScrollPos();
1421 }
1422 return wrapOccurred;
1423}
1424
9e730a78
RD
1425void Editor::LinesJoin() {
1426 if (!RangeContainsProtected(targetStart, targetEnd)) {
9e96e16f 1427 UndoGroup ug(pdoc);
9e730a78
RD
1428 bool prevNonWS = true;
1429 for (int pos = targetStart; pos < targetEnd; pos++) {
1430 if (IsEOLChar(pdoc->CharAt(pos))) {
1431 targetEnd -= pdoc->LenChar(pos);
1432 pdoc->DelChar(pos);
1433 if (prevNonWS) {
1434 // Ensure at least one space separating previous lines
1435 pdoc->InsertChar(pos, ' ');
7e0c58e9 1436 targetEnd++;
9e730a78
RD
1437 }
1438 } else {
1439 prevNonWS = pdoc->CharAt(pos) != ' ';
1440 }
1441 }
9e730a78
RD
1442 }
1443}
1444
9e96e16f 1445const char *Editor::StringFromEOLMode(int eolMode) {
9e730a78
RD
1446 if (eolMode == SC_EOL_CRLF) {
1447 return "\r\n";
1448 } else if (eolMode == SC_EOL_CR) {
1449 return "\r";
1450 } else {
1451 return "\n";
1452 }
1453}
1454
1455void Editor::LinesSplit(int pixelWidth) {
1456 if (!RangeContainsProtected(targetStart, targetEnd)) {
1457 if (pixelWidth == 0) {
1458 PRectangle rcText = GetTextRectangle();
1459 pixelWidth = rcText.Width();
1460 }
1461 int lineStart = pdoc->LineFromPosition(targetStart);
1462 int lineEnd = pdoc->LineFromPosition(targetEnd);
1463 const char *eol = StringFromEOLMode(pdoc->eolMode);
9e96e16f 1464 UndoGroup ug(pdoc);
9e730a78
RD
1465 for (int line = lineStart; line <= lineEnd; line++) {
1466 AutoSurface surface(this);
1467 AutoLineLayout ll(llc, RetrieveLineLayout(line));
1468 if (surface && ll) {
1469 unsigned int posLineStart = pdoc->LineStart(line);
1470 LayoutLine(line, surface, vs, ll, pixelWidth);
1471 for (int subLine = 1; subLine < ll->lines; subLine++) {
7e0c58e9
RD
1472 pdoc->InsertCString(posLineStart + (subLine - 1) * strlen(eol) +
1473 ll->LineStart(subLine), eol);
88a8b04e 1474 targetEnd += static_cast<int>(strlen(eol));
9e730a78
RD
1475 }
1476 }
b8193d80 1477 lineEnd = pdoc->LineFromPosition(targetEnd);
9e730a78 1478 }
9e730a78
RD
1479 }
1480}
1481
65ec6247
RD
1482int Editor::SubstituteMarkerIfEmpty(int markerCheck, int markerDefault) {
1483 if (vs.markers[markerCheck].markType == SC_MARK_EMPTY)
1484 return markerDefault;
1485 return markerCheck;
1486}
1487
88a8b04e
RD
1488// Avoid 64 bit compiler warnings.
1489// Scintilla does not support text buffers larger than 2**31
1490static int istrlen(const char *s) {
1491 return static_cast<int>(strlen(s));
1492}
1493
9e96e16f
RD
1494bool ValidStyledText(ViewStyle &vs, size_t styleOffset, const StyledText &st) {
1495 if (st.multipleStyles) {
1496 for (size_t iStyle=0;iStyle<st.length; iStyle++) {
1497 if (!vs.ValidStyle(styleOffset + st.styles[iStyle]))
1498 return false;
1499 }
1500 } else {
1501 if (!vs.ValidStyle(styleOffset + st.style))
1502 return false;
1503 }
1504 return true;
1505}
1506
1507static int WidthStyledText(Surface *surface, ViewStyle &vs, int styleOffset,
1508 const char *text, const unsigned char *styles, size_t len) {
1509 int width = 0;
1510 size_t start = 0;
1511 while (start < len) {
1512 size_t style = styles[start];
1513 size_t endSegment = start;
1514 while ((endSegment+1 < len) && (static_cast<size_t>(styles[endSegment+1]) == style))
1515 endSegment++;
1516 width += surface->WidthText(vs.styles[style+styleOffset].font, text + start, endSegment - start + 1);
1517 start = endSegment + 1;
1518 }
1519 return width;
1520}
1521
1522static int WidestLineWidth(Surface *surface, ViewStyle &vs, int styleOffset, const StyledText &st) {
1523 int widthMax = 0;
1524 size_t start = 0;
1525 while (start < st.length) {
1526 size_t lenLine = st.LineLength(start);
1527 int widthSubLine;
1528 if (st.multipleStyles) {
1529 widthSubLine = WidthStyledText(surface, vs, styleOffset, st.text + start, st.styles + start, lenLine);
1530 } else {
1531 widthSubLine = surface->WidthText(vs.styles[styleOffset + st.style].font, st.text + start, lenLine);
1532 }
1533 if (widthSubLine > widthMax)
1534 widthMax = widthSubLine;
1535 start += lenLine + 1;
1536 }
1537 return widthMax;
1538}
1539
1540void DrawStyledText(Surface *surface, ViewStyle &vs, int styleOffset, PRectangle rcText, int ascent,
1541 const StyledText &st, size_t start, size_t length) {
1542
1543 if (st.multipleStyles) {
1544 int x = rcText.left;
1545 size_t i = 0;
1546 while (i < length) {
1547 size_t end = i;
1548 int style = st.styles[i + start];
1549 while (end < length-1 && st.styles[start+end+1] == style)
1550 end++;
1551 style += styleOffset;
1552 int width = surface->WidthText(vs.styles[style].font, st.text + start + i, end - i + 1);
1553 PRectangle rcSegment = rcText;
1554 rcSegment.left = x;
1555 rcSegment.right = x + width + 1;
1556 surface->DrawTextNoClip(rcSegment, vs.styles[style].font,
1557 ascent, st.text + start + i, end - i + 1,
1558 vs.styles[style].fore.allocated,
1559 vs.styles[style].back.allocated);
1560 x += width;
1561 i = end + 1;
1562 }
1563 } else {
1564 int style = st.style + styleOffset;
1565 surface->DrawTextNoClip(rcText, vs.styles[style].font,
1566 rcText.top + vs.maxAscent, st.text + start, length,
1567 vs.styles[style].fore.allocated,
1568 vs.styles[style].back.allocated);
1569 }
1570}
1571
9ce192d4
RD
1572void Editor::PaintSelMargin(Surface *surfWindow, PRectangle &rc) {
1573 if (vs.fixedColumnWidth == 0)
65ec6247 1574 return;
9ce192d4
RD
1575
1576 PRectangle rcMargin = GetClientRectangle();
1577 rcMargin.right = vs.fixedColumnWidth;
1578
1579 if (!rc.Intersects(rcMargin))
65ec6247 1580 return;
9ce192d4
RD
1581
1582 Surface *surface;
1583 if (bufferedDraw) {
1a2fb4cd 1584 surface = pixmapSelMargin;
9ce192d4
RD
1585 } else {
1586 surface = surfWindow;
1587 }
1588
1589 PRectangle rcSelMargin = rcMargin;
1590 rcSelMargin.right = rcMargin.left;
1591
d134f170 1592 for (int margin = 0; margin < vs.margins; margin++) {
9ce192d4 1593 if (vs.ms[margin].width > 0) {
d134f170 1594
9ce192d4
RD
1595 rcSelMargin.left = rcSelMargin.right;
1596 rcSelMargin.right = rcSelMargin.left + vs.ms[margin].width;
1597
b8193d80 1598 if (vs.ms[margin].style != SC_MARGIN_NUMBER) {
9ce192d4
RD
1599 /* alternate scheme:
1600 if (vs.ms[margin].mask & SC_MASK_FOLDERS)
ce1ecc6d 1601 surface->FillRectangle(rcSelMargin, vs.styles[STYLE_DEFAULT].back.allocated);
9ce192d4
RD
1602 else
1603 // Required because of special way brush is created for selection margin
ce1ecc6d 1604 surface->FillRectangle(rcSelMargin, pixmapSelPattern);
9ce192d4
RD
1605 */
1606 if (vs.ms[margin].mask & SC_MASK_FOLDERS)
1607 // Required because of special way brush is created for selection margin
1a2fb4cd 1608 surface->FillRectangle(rcSelMargin, *pixmapSelPattern);
b8193d80
RD
1609 else {
1610 ColourAllocated colour;
1611 switch (vs.ms[margin].style) {
1612 case SC_MARGIN_BACK:
1613 colour = vs.styles[STYLE_DEFAULT].back.allocated;
1614 break;
1615 case SC_MARGIN_FORE:
1616 colour = vs.styles[STYLE_DEFAULT].fore.allocated;
1617 break;
1618 default:
1619 colour = vs.styles[STYLE_LINENUMBER].back.allocated;
1620 break;
1621 }
1622 surface->FillRectangle(rcSelMargin, colour);
1623 }
9ce192d4
RD
1624 } else {
1625 surface->FillRectangle(rcSelMargin, vs.styles[STYLE_LINENUMBER].back.allocated);
1626 }
d134f170 1627
9ce192d4 1628 int visibleLine = topLine;
9ce192d4
RD
1629 int yposScreen = 0;
1630
1a2fb4cd 1631 // Work out whether the top line is whitespace located after a
65ec6247
RD
1632 // lessening of fold level which implies a 'fold tail' but which should not
1633 // be displayed until the last of a sequence of whitespace.
1a2fb4cd
RD
1634 bool needWhiteClosure = false;
1635 int level = pdoc->GetLevel(cs.DocFromDisplay(topLine));
65ec6247 1636 if (level & SC_FOLDLEVELWHITEFLAG) {
1a2fb4cd 1637 int lineBack = cs.DocFromDisplay(topLine);
65ec6247
RD
1638 int levelPrev = level;
1639 while ((lineBack > 0) && (levelPrev & SC_FOLDLEVELWHITEFLAG)) {
1640 lineBack--;
1641 levelPrev = pdoc->GetLevel(lineBack);
1642 }
1643 if (!(levelPrev & SC_FOLDLEVELHEADERFLAG)) {
1644 if ((level & SC_FOLDLEVELNUMBERMASK) < (levelPrev & SC_FOLDLEVELNUMBERMASK))
1645 needWhiteClosure = true;
1646 }
1647 }
1a2fb4cd 1648
65ec6247 1649 // Old code does not know about new markers needed to distinguish all cases
1a2fb4cd 1650 int folderOpenMid = SubstituteMarkerIfEmpty(SC_MARKNUM_FOLDEROPENMID,
7e0c58e9 1651 SC_MARKNUM_FOLDEROPEN);
1a2fb4cd 1652 int folderEnd = SubstituteMarkerIfEmpty(SC_MARKNUM_FOLDEREND,
7e0c58e9 1653 SC_MARKNUM_FOLDER);
1a2fb4cd 1654
f6bcfd97 1655 while ((visibleLine < cs.LinesDisplayed()) && yposScreen < rcMargin.bottom) {
65ec6247 1656
1a2fb4cd
RD
1657 PLATFORM_ASSERT(visibleLine < cs.LinesDisplayed());
1658
1659 int lineDoc = cs.DocFromDisplay(visibleLine);
1660 PLATFORM_ASSERT(cs.GetVisible(lineDoc));
1661 bool firstSubLine = visibleLine == cs.DisplayFromDoc(lineDoc);
1662
65ec6247 1663 // Decide which fold indicator should be displayed
1a2fb4cd 1664 level = pdoc->GetLevel(lineDoc);
9e730a78 1665 int levelNext = pdoc->GetLevel(lineDoc + 1);
1a2fb4cd
RD
1666 int marks = pdoc->GetMark(lineDoc);
1667 if (!firstSubLine)
1668 marks = 0;
65ec6247
RD
1669 int levelNum = level & SC_FOLDLEVELNUMBERMASK;
1670 int levelNextNum = levelNext & SC_FOLDLEVELNUMBERMASK;
1671 if (level & SC_FOLDLEVELHEADERFLAG) {
1a2fb4cd
RD
1672 if (firstSubLine) {
1673 if (cs.GetExpanded(lineDoc)) {
1674 if (levelNum == SC_FOLDLEVELBASE)
1675 marks |= 1 << SC_MARKNUM_FOLDEROPEN;
1676 else
1677 marks |= 1 << folderOpenMid;
1678 } else {
1679 if (levelNum == SC_FOLDLEVELBASE)
1680 marks |= 1 << SC_MARKNUM_FOLDER;
1681 else
1682 marks |= 1 << folderEnd;
1683 }
9ce192d4 1684 } else {
1a2fb4cd 1685 marks |= 1 << SC_MARKNUM_FOLDERSUB;
65ec6247
RD
1686 }
1687 needWhiteClosure = false;
1688 } else if (level & SC_FOLDLEVELWHITEFLAG) {
1689 if (needWhiteClosure) {
1690 if (levelNext & SC_FOLDLEVELWHITEFLAG) {
1691 marks |= 1 << SC_MARKNUM_FOLDERSUB;
1692 } else if (levelNum > SC_FOLDLEVELBASE) {
1693 marks |= 1 << SC_MARKNUM_FOLDERMIDTAIL;
1694 needWhiteClosure = false;
1695 } else {
1696 marks |= 1 << SC_MARKNUM_FOLDERTAIL;
1697 needWhiteClosure = false;
1698 }
1699 } else if (levelNum > SC_FOLDLEVELBASE) {
1700 if (levelNextNum < levelNum) {
1701 if (levelNextNum > SC_FOLDLEVELBASE) {
1702 marks |= 1 << SC_MARKNUM_FOLDERMIDTAIL;
1703 } else {
1704 marks |= 1 << SC_MARKNUM_FOLDERTAIL;
1705 }
1706 } else {
1707 marks |= 1 << SC_MARKNUM_FOLDERSUB;
1708 }
1709 }
1710 } else if (levelNum > SC_FOLDLEVELBASE) {
1711 if (levelNextNum < levelNum) {
1712 needWhiteClosure = false;
1713 if (levelNext & SC_FOLDLEVELWHITEFLAG) {
1714 marks |= 1 << SC_MARKNUM_FOLDERSUB;
1715 needWhiteClosure = true;
1716 } else if (levelNextNum > SC_FOLDLEVELBASE) {
1717 marks |= 1 << SC_MARKNUM_FOLDERMIDTAIL;
1718 } else {
1719 marks |= 1 << SC_MARKNUM_FOLDERTAIL;
1720 }
1721 } else {
1722 marks |= 1 << SC_MARKNUM_FOLDERSUB;
9ce192d4
RD
1723 }
1724 }
65ec6247 1725
9ce192d4
RD
1726 marks &= vs.ms[margin].mask;
1727 PRectangle rcMarker = rcSelMargin;
1728 rcMarker.top = yposScreen;
1729 rcMarker.bottom = yposScreen + vs.lineHeight;
b8193d80 1730 if (vs.ms[margin].style == SC_MARGIN_NUMBER) {
9ce192d4
RD
1731 char number[100];
1732 number[0] = '\0';
1a2fb4cd
RD
1733 if (firstSubLine)
1734 sprintf(number, "%d", lineDoc + 1);
e14d10b0
RD
1735 if (foldFlags & SC_FOLDFLAG_LEVELNUMBERS) {
1736 int lev = pdoc->GetLevel(lineDoc);
1737 sprintf(number, "%c%c %03X %03X",
7e0c58e9
RD
1738 (lev & SC_FOLDLEVELHEADERFLAG) ? 'H' : '_',
1739 (lev & SC_FOLDLEVELWHITEFLAG) ? 'W' : '_',
1740 lev & SC_FOLDLEVELNUMBERMASK,
1741 lev >> 16
1742 );
e14d10b0 1743 }
d134f170 1744 PRectangle rcNumber = rcMarker;
9ce192d4 1745 // Right justify
88a8b04e 1746 int width = surface->WidthText(vs.styles[STYLE_LINENUMBER].font, number, istrlen(number));
f6bcfd97 1747 int xpos = rcNumber.right - width - 3;
9ce192d4 1748 rcNumber.left = xpos;
1a2fb4cd 1749 surface->DrawTextNoClip(rcNumber, vs.styles[STYLE_LINENUMBER].font,
7e0c58e9
RD
1750 rcNumber.top + vs.maxAscent, number, istrlen(number),
1751 vs.styles[STYLE_LINENUMBER].fore.allocated,
1752 vs.styles[STYLE_LINENUMBER].back.allocated);
9e96e16f
RD
1753 } else if (vs.ms[margin].style == SC_MARGIN_TEXT || vs.ms[margin].style == SC_MARGIN_RTEXT) {
1754 if (firstSubLine) {
1755 const StyledText stMargin = pdoc->MarginStyledText(lineDoc);
1756 if (stMargin.text && ValidStyledText(vs, vs.marginStyleOffset, stMargin)) {
1757 surface->FillRectangle(rcMarker,
1758 vs.styles[stMargin.StyleAt(0)+vs.marginStyleOffset].back.allocated);
1759 if (vs.ms[margin].style == SC_MARGIN_RTEXT) {
1760 int width = WidestLineWidth(surface, vs, vs.marginStyleOffset, stMargin);
1761 rcMarker.left = rcMarker.right - width - 3;
1762 }
1763 DrawStyledText(surface, vs, vs.marginStyleOffset, rcMarker, rcMarker.top + vs.maxAscent,
1764 stMargin, 0, stMargin.length);
1765 }
1766 }
9ce192d4 1767 }
d134f170 1768
9ce192d4
RD
1769 if (marks) {
1770 for (int markBit = 0; (markBit < 32) && marks; markBit++) {
1771 if (marks & 1) {
b8b0e402 1772 vs.markers[markBit].Draw(surface, rcMarker, vs.styles[STYLE_LINENUMBER].font);
9ce192d4
RD
1773 }
1774 marks >>= 1;
1775 }
1776 }
d134f170 1777
9ce192d4 1778 visibleLine++;
9ce192d4
RD
1779 yposScreen += vs.lineHeight;
1780 }
1781 }
1782 }
1783
1784 PRectangle rcBlankMargin = rcMargin;
1785 rcBlankMargin.left = rcSelMargin.right;
d134f170
RD
1786 surface->FillRectangle(rcBlankMargin, vs.styles[STYLE_DEFAULT].back.allocated);
1787
9ce192d4 1788 if (bufferedDraw) {
1a2fb4cd 1789 surfWindow->Copy(rcMargin, Point(), *pixmapSelMargin);
9ce192d4
RD
1790 }
1791}
1792
1793void DrawTabArrow(Surface *surface, PRectangle rcTab, int ymid) {
1794 int ydiff = (rcTab.bottom - rcTab.top) / 2;
1795 int xhead = rcTab.right - 1 - ydiff;
1a2fb4cd
RD
1796 if (xhead <= rcTab.left) {
1797 ydiff -= rcTab.left - xhead - 1;
1798 xhead = rcTab.left - 1;
1799 }
9ce192d4
RD
1800 if ((rcTab.left + 2) < (rcTab.right - 1))
1801 surface->MoveTo(rcTab.left + 2, ymid);
1802 else
1803 surface->MoveTo(rcTab.right - 1, ymid);
1804 surface->LineTo(rcTab.right - 1, ymid);
1805 surface->LineTo(xhead, ymid - ydiff);
1806 surface->MoveTo(rcTab.right - 1, ymid);
1807 surface->LineTo(xhead, ymid + ydiff);
1808}
1809
1a2fb4cd
RD
1810LineLayout *Editor::RetrieveLineLayout(int lineNumber) {
1811 int posLineStart = pdoc->LineStart(lineNumber);
1812 int posLineEnd = pdoc->LineStart(lineNumber + 1);
7e0c58e9 1813 PLATFORM_ASSERT(posLineEnd >= posLineStart);
9e96e16f 1814 int lineCaret = pdoc->LineFromPosition(sel.MainCaret());
e4f0b986 1815 return llc.Retrieve(lineNumber, lineCaret,
7e0c58e9
RD
1816 posLineEnd - posLineStart, pdoc->GetStyleClock(),
1817 LinesOnScreen() + 1, pdoc->LinesTotal());
1818}
1819
1820static bool GoodTrailByte(int v) {
1821 return (v >= 0x80) && (v < 0xc0);
1822}
1823
1824bool BadUTF(const char *s, int len, int &trailBytes) {
1825 if (trailBytes) {
1826 trailBytes--;
1827 return false;
1828 }
1829 const unsigned char *us = reinterpret_cast<const unsigned char *>(s);
1830 if (*us < 0x80) {
1831 // Single bytes easy
1832 return false;
1833 } else if (*us > 0xF4) {
1834 // Characters longer than 4 bytes not possible in current UTF-8
1835 return true;
1836 } else if (*us >= 0xF0) {
1837 // 4 bytes
1838 if (len < 4)
1839 return true;
1840 if (GoodTrailByte(us[1]) && GoodTrailByte(us[2]) && GoodTrailByte(us[3])) {
1841 trailBytes = 3;
1842 return false;
1843 } else {
1844 return true;
1845 }
1846 } else if (*us >= 0xE0) {
1847 // 3 bytes
1848 if (len < 3)
1849 return true;
1850 if (GoodTrailByte(us[1]) && GoodTrailByte(us[2])) {
1851 trailBytes = 2;
1852 return false;
1853 } else {
1854 return true;
1855 }
1856 } else if (*us >= 0xC2) {
1857 // 2 bytes
1858 if (len < 2)
1859 return true;
1860 if (GoodTrailByte(us[1])) {
1861 trailBytes = 1;
1862 return false;
1863 } else {
1864 return true;
1865 }
1866 } else if (*us >= 0xC0) {
1867 // Overlong encoding
1868 return true;
1869 } else {
1870 // Trail byte
1871 return true;
1872 }
1a2fb4cd
RD
1873}
1874
65ec6247
RD
1875/**
1876 * Fill in the LineLayout data for the given line.
1877 * Copy the given @a line and its styles from the document into local arrays.
1878 * Also determine the x position at which each character starts.
1879 */
1a2fb4cd
RD
1880void Editor::LayoutLine(int line, Surface *surface, ViewStyle &vstyle, LineLayout *ll, int width) {
1881 if (!ll)
1882 return;
7e0c58e9 1883
8e54aaed 1884 PLATFORM_ASSERT(line < pdoc->LinesTotal());
7e0c58e9 1885 PLATFORM_ASSERT(ll->chars != NULL);
9ce192d4 1886 int posLineStart = pdoc->LineStart(line);
a834585d
RD
1887 int posLineEnd = pdoc->LineStart(line + 1);
1888 // If the line is very long, limit the treatment to a length that should fit in the viewport
1889 if (posLineEnd > (posLineStart + ll->maxLineLength)) {
1890 posLineEnd = posLineStart + ll->maxLineLength;
1891 }
1892 if (ll->validity == LineLayout::llCheckTextAndStyle) {
1e9bafca
RD
1893 int lineLength = posLineEnd - posLineStart;
1894 if (!vstyle.viewEOL) {
1895 int cid = posLineEnd - 1;
1896 while ((cid > posLineStart) && IsEOLChar(pdoc->CharAt(cid))) {
1897 cid--;
1898 lineLength--;
a834585d
RD
1899 }
1900 }
1901 if (lineLength == ll->numCharsInLine) {
a834585d
RD
1902 // See if chars, styles, indicators, are all the same
1903 bool allSame = true;
8e54aaed 1904 const int styleMask = pdoc->stylingBitsMask;
a834585d 1905 // Check base line layout
1e9bafca
RD
1906 char styleByte = 0;
1907 int numCharsInLine = 0;
1908 while (numCharsInLine < lineLength) {
1909 int charInDoc = numCharsInLine + posLineStart;
a834585d 1910 char chDoc = pdoc->CharAt(charInDoc);
1e9bafca
RD
1911 styleByte = pdoc->StyleAt(charInDoc);
1912 allSame = allSame &&
7e0c58e9 1913 (ll->styles[numCharsInLine] == static_cast<unsigned char>(styleByte & styleMask));
1e9bafca 1914 allSame = allSame &&
7e0c58e9 1915 (ll->indicators[numCharsInLine] == static_cast<char>(styleByte & ~styleMask));
1e9bafca 1916 if (vstyle.styles[ll->styles[numCharsInLine]].caseForce == Style::caseMixed)
9e730a78 1917 allSame = allSame &&
7e0c58e9 1918 (ll->chars[numCharsInLine] == chDoc);
1e9bafca 1919 else if (vstyle.styles[ll->styles[numCharsInLine]].caseForce == Style::caseLower)
9e730a78 1920 allSame = allSame &&
7e0c58e9 1921 (ll->chars[numCharsInLine] == static_cast<char>(tolower(chDoc)));
1e9bafca
RD
1922 else // Style::caseUpper
1923 allSame = allSame &&
7e0c58e9 1924 (ll->chars[numCharsInLine] == static_cast<char>(toupper(chDoc)));
1e9bafca 1925 numCharsInLine++;
a834585d 1926 }
1e9bafca 1927 allSame = allSame && (ll->styles[numCharsInLine] == styleByte); // For eolFilled
a834585d
RD
1928 if (allSame) {
1929 ll->validity = LineLayout::llPositions;
1930 } else {
1931 ll->validity = LineLayout::llInvalid;
1932 }
1933 } else {
1934 ll->validity = LineLayout::llInvalid;
1935 }
1936 }
1a2fb4cd
RD
1937 if (ll->validity == LineLayout::llInvalid) {
1938 ll->widthLine = LineLayout::wrapWidthInfinite;
1939 ll->lines = 1;
1940 int numCharsInLine = 0;
9e96e16f 1941 int numCharsBeforeEOL = 0;
1a2fb4cd
RD
1942 if (vstyle.edgeState == EDGE_BACKGROUND) {
1943 ll->edgeColumn = pdoc->FindColumn(line, theEdge);
1944 if (ll->edgeColumn >= posLineStart) {
1945 ll->edgeColumn -= posLineStart;
1946 }
1947 } else {
1948 ll->edgeColumn = -1;
1949 }
e4f0b986 1950
1a2fb4cd
RD
1951 char styleByte = 0;
1952 int styleMask = pdoc->stylingBitsMask;
1e9bafca 1953 ll->styleBitsSet = 0;
1a2fb4cd
RD
1954 // Fill base line layout
1955 for (int charInDoc = posLineStart; charInDoc < posLineEnd; charInDoc++) {
1956 char chDoc = pdoc->CharAt(charInDoc);
1957 styleByte = pdoc->StyleAt(charInDoc);
1e9bafca 1958 ll->styleBitsSet |= styleByte;
9e730a78 1959 if (vstyle.viewEOL || (!IsEOLChar(chDoc))) {
1a2fb4cd
RD
1960 ll->chars[numCharsInLine] = chDoc;
1961 ll->styles[numCharsInLine] = static_cast<char>(styleByte & styleMask);
1962 ll->indicators[numCharsInLine] = static_cast<char>(styleByte & ~styleMask);
1963 if (vstyle.styles[ll->styles[numCharsInLine]].caseForce == Style::caseUpper)
1964 ll->chars[numCharsInLine] = static_cast<char>(toupper(chDoc));
1965 else if (vstyle.styles[ll->styles[numCharsInLine]].caseForce == Style::caseLower)
1966 ll->chars[numCharsInLine] = static_cast<char>(tolower(chDoc));
1967 numCharsInLine++;
9e96e16f
RD
1968 if (!IsEOLChar(chDoc))
1969 numCharsBeforeEOL++;
1a2fb4cd
RD
1970 }
1971 }
a834585d 1972 ll->xHighlightGuide = 0;
1a2fb4cd
RD
1973 // Extra element at the end of the line to hold end x position and act as
1974 ll->chars[numCharsInLine] = 0; // Also triggers processing in the loops as this is a control character
1975 ll->styles[numCharsInLine] = styleByte; // For eolFilled
1976 ll->indicators[numCharsInLine] = 0;
e4f0b986 1977
1a2fb4cd
RD
1978 // Layout the line, determining the position of each character,
1979 // with an extra element at the end for the end of the line.
1980 int startseg = 0; // Start of the current segment, in char. number
1981 int startsegx = 0; // Start of the current segment, in pixels
1982 ll->positions[0] = 0;
1983 unsigned int tabWidth = vstyle.spaceWidth * pdoc->tabInChars;
1984 bool lastSegItalics = false;
a834585d 1985 Font &ctrlCharsFont = vstyle.styles[STYLE_CONTROLCHAR].font;
e4f0b986 1986
1e9bafca 1987 int ctrlCharWidth[32] = {0};
f114b858 1988 bool isControlNext = IsControlCharacter(ll->chars[0]);
7e0c58e9
RD
1989 int trailBytes = 0;
1990 bool isBadUTFNext = IsUnicodeMode() && BadUTF(ll->chars, numCharsInLine, trailBytes);
1a2fb4cd 1991 for (int charInLine = 0; charInLine < numCharsInLine; charInLine++) {
f114b858
RD
1992 bool isControl = isControlNext;
1993 isControlNext = IsControlCharacter(ll->chars[charInLine + 1]);
7e0c58e9
RD
1994 bool isBadUTF = isBadUTFNext;
1995 isBadUTFNext = IsUnicodeMode() && BadUTF(ll->chars + charInLine + 1, numCharsInLine - charInLine - 1, trailBytes);
1a2fb4cd 1996 if ((ll->styles[charInLine] != ll->styles[charInLine + 1]) ||
7e0c58e9 1997 isControl || isControlNext || isBadUTF || isBadUTFNext) {
1a2fb4cd
RD
1998 ll->positions[startseg] = 0;
1999 if (vstyle.styles[ll->styles[charInLine]].visible) {
f114b858 2000 if (isControl) {
1a2fb4cd
RD
2001 if (ll->chars[charInLine] == '\t') {
2002 ll->positions[charInLine + 1] = ((((startsegx + 2) /
7e0c58e9 2003 tabWidth) + 1) * tabWidth) - startsegx;
1a2fb4cd 2004 } else if (controlCharSymbol < 32) {
1e9bafca
RD
2005 if (ctrlCharWidth[ll->chars[charInLine]] == 0) {
2006 const char *ctrlChar = ControlCharacterString(ll->chars[charInLine]);
2007 // +3 For a blank on front and rounded edge each side:
2008 ctrlCharWidth[ll->chars[charInLine]] =
7e0c58e9 2009 surface->WidthText(ctrlCharsFont, ctrlChar, istrlen(ctrlChar)) + 3;
1e9bafca
RD
2010 }
2011 ll->positions[charInLine + 1] = ctrlCharWidth[ll->chars[charInLine]];
1a2fb4cd
RD
2012 } else {
2013 char cc[2] = { static_cast<char>(controlCharSymbol), '\0' };
2014 surface->MeasureWidths(ctrlCharsFont, cc, 1,
7e0c58e9 2015 ll->positions + startseg + 1);
1a2fb4cd
RD
2016 }
2017 lastSegItalics = false;
7e0c58e9
RD
2018 } else if (isBadUTF) {
2019 char hexits[3];
2020 sprintf(hexits, "%2X", ll->chars[charInLine] & 0xff);
2021 ll->positions[charInLine + 1] =
2022 surface->WidthText(ctrlCharsFont, hexits, istrlen(hexits)) + 3;
1a2fb4cd 2023 } else { // Regular character
1a2fb4cd
RD
2024 int lenSeg = charInLine - startseg + 1;
2025 if ((lenSeg == 1) && (' ' == ll->chars[startseg])) {
f114b858 2026 lastSegItalics = false;
1a2fb4cd
RD
2027 // Over half the segments are single characters and of these about half are space characters.
2028 ll->positions[charInLine + 1] = vstyle.styles[ll->styles[charInLine]].spaceWidth;
2029 } else {
f114b858 2030 lastSegItalics = vstyle.styles[ll->styles[charInLine]].italic;
7e0c58e9
RD
2031 posCache.MeasureWidths(surface, vstyle, ll->styles[charInLine], ll->chars + startseg,
2032 lenSeg, ll->positions + startseg + 1);
1a2fb4cd 2033 }
d134f170 2034 }
1a2fb4cd
RD
2035 } else { // invisible
2036 for (int posToZero = startseg; posToZero <= (charInLine + 1); posToZero++) {
2037 ll->positions[posToZero] = 0;
d134f170
RD
2038 }
2039 }
1a2fb4cd
RD
2040 for (int posToIncrease = startseg; posToIncrease <= (charInLine + 1); posToIncrease++) {
2041 ll->positions[posToIncrease] += startsegx;
9ce192d4 2042 }
1a2fb4cd
RD
2043 startsegx = ll->positions[charInLine + 1];
2044 startseg = charInLine + 1;
9ce192d4 2045 }
9ce192d4 2046 }
1a2fb4cd
RD
2047 // Small hack to make lines that end with italics not cut off the edge of the last character
2048 if ((startseg > 0) && lastSegItalics) {
2049 ll->positions[startseg] += 2;
2050 }
2051 ll->numCharsInLine = numCharsInLine;
9e96e16f 2052 ll->numCharsBeforeEOL = numCharsBeforeEOL;
1a2fb4cd
RD
2053 ll->validity = LineLayout::llPositions;
2054 }
2055 // Hard to cope when too narrow, so just assume there is space
2056 if (width < 20) {
2057 width = 20;
9ce192d4 2058 }
1a2fb4cd
RD
2059 if ((ll->validity == LineLayout::llPositions) || (ll->widthLine != width)) {
2060 ll->widthLine = width;
2061 if (width == LineLayout::wrapWidthInfinite) {
2062 ll->lines = 1;
a834585d
RD
2063 } else if (width > ll->positions[ll->numCharsInLine]) {
2064 // Simple common case where line does not need wrapping.
2065 ll->lines = 1;
1a2fb4cd 2066 } else {
591d01be
RD
2067 if (wrapVisualFlags & SC_WRAPVISUALFLAG_END) {
2068 width -= vstyle.aveCharWidth; // take into account the space for end wrap mark
2069 }
9e96e16f
RD
2070 ll->wrapIndent = wrapAddIndent;
2071 if (wrapIndentMode != SC_WRAPINDENT_FIXED)
2072 for (int i = 0; i < ll->numCharsInLine; i++) {
2073 if (!IsSpaceOrTab(ll->chars[i])) {
2074 ll->wrapIndent += ll->positions[i]; // Add line indent
2075 break;
2076 }
2077 }
2078 // Check for text width minimum
2079 if (ll->wrapIndent > width - static_cast<int>(vstyle.aveCharWidth) * 15)
2080 ll->wrapIndent = wrapAddIndent;
2081 // Check for wrapIndent minimum
2082 if ((wrapVisualFlags & SC_WRAPVISUALFLAG_START) && (ll->wrapIndent < static_cast<int>(vstyle.aveCharWidth)))
2083 ll->wrapIndent = vstyle.aveCharWidth; // Indent to show start visual
1a2fb4cd
RD
2084 ll->lines = 0;
2085 // Calculate line start positions based upon width.
1a2fb4cd
RD
2086 int lastGoodBreak = 0;
2087 int lastLineStart = 0;
2088 int startOffset = 0;
9e730a78 2089 int p = 0;
1a2fb4cd 2090 while (p < ll->numCharsInLine) {
9e730a78 2091 if ((ll->positions[p + 1] - startOffset) >= width) {
1a2fb4cd
RD
2092 if (lastGoodBreak == lastLineStart) {
2093 // Try moving to start of last character
2094 if (p > 0) {
2095 lastGoodBreak = pdoc->MovePositionOutsideChar(p + posLineStart, -1)
7e0c58e9 2096 - posLineStart;
1a2fb4cd
RD
2097 }
2098 if (lastGoodBreak == lastLineStart) {
2099 // Ensure at least one character on line.
9e730a78 2100 lastGoodBreak = pdoc->MovePositionOutsideChar(lastGoodBreak + posLineStart + 1, 1)
7e0c58e9 2101 - posLineStart;
1a2fb4cd
RD
2102 }
2103 }
2104 lastLineStart = lastGoodBreak;
2105 ll->lines++;
2106 ll->SetLineStart(ll->lines, lastGoodBreak);
2107 startOffset = ll->positions[lastGoodBreak];
591d01be 2108 // take into account the space for start wrap mark and indent
9e96e16f 2109 startOffset -= ll->wrapIndent;
1a2fb4cd
RD
2110 p = lastGoodBreak + 1;
2111 continue;
2112 }
2113 if (p > 0) {
b8193d80 2114 if (wrapState == eWrapChar) {
1e9bafca 2115 lastGoodBreak = pdoc->MovePositionOutsideChar(p + posLineStart, -1)
7e0c58e9 2116 - posLineStart;
1e9bafca
RD
2117 p = pdoc->MovePositionOutsideChar(p + 1 + posLineStart, 1) - posLineStart;
2118 continue;
2119 } else if (ll->styles[p] != ll->styles[p - 1]) {
1a2fb4cd 2120 lastGoodBreak = p;
9e730a78 2121 } else if (IsSpaceOrTab(ll->chars[p - 1]) && !IsSpaceOrTab(ll->chars[p])) {
1a2fb4cd
RD
2122 lastGoodBreak = p;
2123 }
2124 }
2125 p++;
2126 }
2127 ll->lines++;
2128 }
2129 ll->validity = LineLayout::llLines;
65ec6247 2130 }
9ce192d4
RD
2131}
2132
9e96e16f
RD
2133ColourAllocated Editor::SelectionBackground(ViewStyle &vsDraw, bool main) {
2134 return main ?
2135 (primarySelection ? vsDraw.selbackground.allocated : vsDraw.selbackground2.allocated) :
2136 vsDraw.selAdditionalBackground.allocated;
b8193d80
RD
2137}
2138
9e730a78 2139ColourAllocated Editor::TextBackground(ViewStyle &vsDraw, bool overrideBackground,
9e96e16f
RD
2140 ColourAllocated background, int inSelection, bool inHotspot, int styleMain, int i, LineLayout *ll) {
2141 if (inSelection == 1) {
b8193d80 2142 if (vsDraw.selbackset && (vsDraw.selAlpha == SC_ALPHA_NOALPHA)) {
9e96e16f
RD
2143 return SelectionBackground(vsDraw, true);
2144 }
2145 } else if (inSelection == 2) {
2146 if (vsDraw.selbackset && (vsDraw.selAdditionalAlpha == SC_ALPHA_NOALPHA)) {
2147 return SelectionBackground(vsDraw, false);
9e730a78
RD
2148 }
2149 } else {
2150 if ((vsDraw.edgeState == EDGE_BACKGROUND) &&
2151 (i >= ll->edgeColumn) &&
2152 !IsEOLChar(ll->chars[i]))
2153 return vsDraw.edgecolour.allocated;
8e54aaed 2154 if (inHotspot && vsDraw.hotspotBackgroundSet)
9e730a78 2155 return vsDraw.hotspotBackground.allocated;
7e0c58e9 2156 if (overrideBackground && (styleMain != STYLE_BRACELIGHT) && (styleMain != STYLE_BRACEBAD))
9e730a78
RD
2157 return background;
2158 }
2159 return vsDraw.styles[styleMain].back.allocated;
2160}
2161
2162void Editor::DrawIndentGuide(Surface *surface, int lineVisible, int lineHeight, int start, PRectangle rcSegment, bool highlight) {
2163 Point from(0, ((lineVisible & 1) && (lineHeight & 1)) ? 1 : 0);
2164 PRectangle rcCopyArea(start + 1, rcSegment.top, start + 2, rcSegment.bottom);
2165 surface->Copy(rcCopyArea, from,
7e0c58e9 2166 highlight ? *pixmapIndentGuideHighlight : *pixmapIndentGuide);
9e730a78
RD
2167}
2168
591d01be 2169void Editor::DrawWrapMarker(Surface *surface, PRectangle rcPlace,
7e0c58e9 2170 bool isEndMarker, ColourAllocated wrapColour) {
591d01be
RD
2171 surface->PenColour(wrapColour);
2172
2173 enum { xa = 1 }; // gap before start
2174 int w = rcPlace.right - rcPlace.left - xa - 1;
2175
2176 bool xStraight = isEndMarker; // x-mirrored symbol for start marker
2177 bool yStraight = true;
2178 //bool yStraight= isEndMarker; // comment in for start marker y-mirrowed
2179
2180 int x0 = xStraight ? rcPlace.left : rcPlace.right - 1;
2181 int y0 = yStraight ? rcPlace.top : rcPlace.bottom - 1;
2182
2183 int dy = (rcPlace.bottom - rcPlace.top) / 5;
2184 int y = (rcPlace.bottom - rcPlace.top) / 2 + dy;
2185
2186 struct Relative {
2187 Surface *surface;
2188 int xBase;
2189 int xDir;
2190 int yBase;
2191 int yDir;
2192 void MoveTo(int xRelative, int yRelative) {
7e0c58e9 2193 surface->MoveTo(xBase + xDir * xRelative, yBase + yDir * yRelative);
591d01be
RD
2194 }
2195 void LineTo(int xRelative, int yRelative) {
7e0c58e9 2196 surface->LineTo(xBase + xDir * xRelative, yBase + yDir * yRelative);
591d01be
RD
2197 }
2198 };
b8193d80 2199 Relative rel = {surface, x0, xStraight ? 1 : -1, y0, yStraight ? 1 : -1};
591d01be
RD
2200
2201 // arrow head
2202 rel.MoveTo(xa, y);
2203 rel.LineTo(xa + 2*w / 3, y - dy);
2204 rel.MoveTo(xa, y);
2205 rel.LineTo(xa + 2*w / 3, y + dy);
2206
2207 // arrow body
2208 rel.MoveTo(xa, y);
2209 rel.LineTo(xa + w, y);
2210 rel.LineTo(xa + w, y - 2 * dy);
2211 rel.LineTo(xa - 1, // on windows lineto is exclusive endpoint, perhaps GTK not...
7e0c58e9 2212 y - 2 * dy);
591d01be
RD
2213}
2214
b8193d80
RD
2215static void SimpleAlphaRectangle(Surface *surface, PRectangle rc, ColourAllocated fill, int alpha) {
2216 if (alpha != SC_ALPHA_NOALPHA) {
2217 surface->AlphaRectangle(rc, 0, fill, alpha, fill, alpha, 0);
2218 }
2219}
2220
9e96e16f
RD
2221void DrawTextBlob(Surface *surface, ViewStyle &vsDraw, PRectangle rcSegment,
2222 const char *s, ColourAllocated textBack, ColourAllocated textFore, bool twoPhaseDraw) {
2223 if (!twoPhaseDraw) {
2224 surface->FillRectangle(rcSegment, textBack);
2225 }
2226 Font &ctrlCharsFont = vsDraw.styles[STYLE_CONTROLCHAR].font;
2227 int normalCharHeight = surface->Ascent(ctrlCharsFont) -
2228 surface->InternalLeading(ctrlCharsFont);
2229 PRectangle rcCChar = rcSegment;
2230 rcCChar.left = rcCChar.left + 1;
2231 rcCChar.top = rcSegment.top + vsDraw.maxAscent - normalCharHeight;
2232 rcCChar.bottom = rcSegment.top + vsDraw.maxAscent + 1;
2233 PRectangle rcCentral = rcCChar;
2234 rcCentral.top++;
2235 rcCentral.bottom--;
2236 surface->FillRectangle(rcCentral, textFore);
2237 PRectangle rcChar = rcCChar;
2238 rcChar.left++;
2239 rcChar.right--;
2240 surface->DrawTextClipped(rcChar, ctrlCharsFont,
2241 rcSegment.top + vsDraw.maxAscent, s, istrlen(s),
2242 textBack, textFore);
2243}
2244
9e730a78 2245void Editor::DrawEOL(Surface *surface, ViewStyle &vsDraw, PRectangle rcLine, LineLayout *ll,
7e0c58e9
RD
2246 int line, int lineEnd, int xStart, int subLine, int subLineStart,
2247 bool overrideBackground, ColourAllocated background,
2248 bool drawWrapMarkEnd, ColourAllocated wrapColour) {
9e730a78 2249
9e96e16f
RD
2250 const int posLineStart = pdoc->LineStart(line);
2251 const int styleMask = pdoc->stylingBitsMask;
9e730a78
RD
2252 PRectangle rcSegment = rcLine;
2253
9e96e16f
RD
2254 const bool lastSubLine = subLine == (ll->lines - 1);
2255 int virtualSpace = 0;
2256 if (lastSubLine) {
2257 const int spaceWidth = static_cast<int>(vsDraw.styles[ll->EndLineStyle()].spaceWidth);
2258 virtualSpace = sel.VirtualSpaceFor(pdoc->LineEnd(line)) * spaceWidth;
2259 }
2260
9e730a78 2261 // Fill in a PRectangle representing the end of line characters
9e96e16f 2262
9e730a78 2263 int xEol = ll->positions[lineEnd] - subLineStart;
591d01be 2264
9e96e16f
RD
2265 // Fill the virtual space and show selections within it
2266 if (virtualSpace) {
2267 rcSegment.left = xEol + xStart;
2268 rcSegment.right = xEol + xStart + virtualSpace;
2269 surface->FillRectangle(rcSegment, overrideBackground ? background : vsDraw.styles[ll->styles[ll->numCharsInLine] & styleMask].back.allocated);
2270 if (!hideSelection && ((vsDraw.selAlpha == SC_ALPHA_NOALPHA) || (vsDraw.selAdditionalAlpha == SC_ALPHA_NOALPHA))) {
2271 SelectionSegment virtualSpaceRange(SelectionPosition(pdoc->LineEnd(line)), SelectionPosition(pdoc->LineEnd(line), sel.VirtualSpaceFor(pdoc->LineEnd(line))));
2272 for (size_t r=0; r<sel.Count(); r++) {
2273 int alpha = (r == sel.Main()) ? vsDraw.selAlpha : vsDraw.selAdditionalAlpha;
2274 if (alpha == SC_ALPHA_NOALPHA) {
2275 SelectionSegment portion = sel.Range(r).Intersect(virtualSpaceRange);
2276 if (!portion.Empty()) {
2277 const int spaceWidth = static_cast<int>(vsDraw.styles[ll->EndLineStyle()].spaceWidth);
2278 rcSegment.left = xStart + ll->positions[portion.start.Position() - posLineStart] - subLineStart + portion.start.VirtualSpace() * spaceWidth;
2279 rcSegment.right = xStart + ll->positions[portion.end.Position() - posLineStart] - subLineStart + portion.end.VirtualSpace() * spaceWidth;
2280 rcSegment.left = Platform::Maximum(rcSegment.left, rcLine.left);
2281 rcSegment.right = Platform::Minimum(rcSegment.right, rcLine.right);
2282 surface->FillRectangle(rcSegment, SelectionBackground(vsDraw, r == sel.Main()));
2283 }
2284 }
2285 }
2286 }
2287 }
2288
2289 int posAfterLineEnd = pdoc->LineStart(line + 1);
2290 int eolInSelection = (subLine == (ll->lines - 1)) ? sel.InSelectionForEOL(posAfterLineEnd) : 0;
2291 int alpha = (eolInSelection == 1) ? vsDraw.selAlpha : vsDraw.selAdditionalAlpha;
2292
2293 // Draw the [CR], [LF], or [CR][LF] blobs if visible line ends are on
2294 int blobsWidth = 0;
2295 if (lastSubLine) {
2296 for (int eolPos=ll->numCharsBeforeEOL; eolPos<ll->numCharsInLine; eolPos++) {
2297 rcSegment.left = xStart + ll->positions[eolPos] - subLineStart + virtualSpace;
2298 rcSegment.right = xStart + ll->positions[eolPos+1] - subLineStart + virtualSpace;
2299 blobsWidth += rcSegment.Width();
2300 const char *ctrlChar = ControlCharacterString(ll->chars[eolPos]);
2301 int inSelection = 0;
2302 bool inHotspot = false;
2303 int styleMain = ll->styles[eolPos];
2304 ColourAllocated textBack = TextBackground(vsDraw, overrideBackground, background, inSelection, inHotspot, styleMain, eolPos, ll);
2305 ColourAllocated textFore = vsDraw.styles[styleMain].fore.allocated;
2306 if (!hideSelection && eolInSelection && vsDraw.selbackset && (line < pdoc->LinesTotal() - 1)) {
2307 if (alpha == SC_ALPHA_NOALPHA) {
2308 surface->FillRectangle(rcSegment, SelectionBackground(vsDraw, eolInSelection == 1));
2309 } else {
2310 surface->FillRectangle(rcSegment, textBack);
2311 SimpleAlphaRectangle(surface, rcSegment, SelectionBackground(vsDraw, eolInSelection == 1), alpha);
2312 }
2313 } else {
2314 surface->FillRectangle(rcSegment, textBack);
2315 }
2316 DrawTextBlob(surface, vsDraw, rcSegment, ctrlChar, textBack, textFore, twoPhaseDraw);
2317 }
2318 }
2319
2320 // Draw the eol-is-selected rectangle
2321 rcSegment.left = xEol + xStart + virtualSpace + blobsWidth;
2322 rcSegment.right = xEol + xStart + virtualSpace + blobsWidth + vsDraw.aveCharWidth;
2323
2324 if (!hideSelection && eolInSelection && vsDraw.selbackset && (line < pdoc->LinesTotal() - 1) && (alpha == SC_ALPHA_NOALPHA)) {
2325 surface->FillRectangle(rcSegment, SelectionBackground(vsDraw, eolInSelection == 1));
9e730a78 2326 } else {
b8193d80
RD
2327 if (overrideBackground) {
2328 surface->FillRectangle(rcSegment, background);
9e96e16f
RD
2329 } else if (line < pdoc->LinesTotal() - 1) {
2330 surface->FillRectangle(rcSegment, vsDraw.styles[ll->styles[ll->numCharsInLine] & styleMask].back.allocated);
2331 } else if (vsDraw.styles[ll->styles[ll->numCharsInLine] & styleMask].eolFilled) {
b8193d80 2332 surface->FillRectangle(rcSegment, vsDraw.styles[ll->styles[ll->numCharsInLine] & styleMask].back.allocated);
9e96e16f
RD
2333 } else {
2334 surface->FillRectangle(rcSegment, vsDraw.styles[STYLE_DEFAULT].back.allocated);
b8193d80 2335 }
9e96e16f
RD
2336 if (!hideSelection && eolInSelection && vsDraw.selbackset && (line < pdoc->LinesTotal() - 1) && (alpha != SC_ALPHA_NOALPHA)) {
2337 SimpleAlphaRectangle(surface, rcSegment, SelectionBackground(vsDraw, eolInSelection == 1), alpha);
b8193d80 2338 }
9e730a78
RD
2339 }
2340
9e96e16f
RD
2341 // Fill the remainder of the line
2342 rcSegment.left = xEol + xStart + virtualSpace + blobsWidth + vsDraw.aveCharWidth;
9e730a78 2343 rcSegment.right = rcLine.right;
591d01be 2344
9e96e16f
RD
2345 if (!hideSelection && vsDraw.selEOLFilled && eolInSelection && vsDraw.selbackset && (line < pdoc->LinesTotal() - 1) && (alpha == SC_ALPHA_NOALPHA)) {
2346 surface->FillRectangle(rcSegment, SelectionBackground(vsDraw, eolInSelection == 1));
7e0c58e9
RD
2347 } else {
2348 if (overrideBackground) {
2349 surface->FillRectangle(rcSegment, background);
2350 } else if (vsDraw.styles[ll->styles[ll->numCharsInLine] & styleMask].eolFilled) {
2351 surface->FillRectangle(rcSegment, vsDraw.styles[ll->styles[ll->numCharsInLine] & styleMask].back.allocated);
2352 } else {
2353 surface->FillRectangle(rcSegment, vsDraw.styles[STYLE_DEFAULT].back.allocated);
2354 }
9e96e16f
RD
2355 if (!hideSelection && vsDraw.selEOLFilled && eolInSelection && vsDraw.selbackset && (line < pdoc->LinesTotal() - 1) && (alpha != SC_ALPHA_NOALPHA)) {
2356 SimpleAlphaRectangle(surface, rcSegment, SelectionBackground(vsDraw, eolInSelection == 1), alpha);
7e0c58e9
RD
2357 }
2358 }
2359
591d01be
RD
2360 if (drawWrapMarkEnd) {
2361 PRectangle rcPlace = rcSegment;
2362
2363 if (wrapVisualFlagsLocation & SC_WRAPVISUALFLAGLOC_END_BY_TEXT) {
9e96e16f 2364 rcPlace.left = xEol + xStart + virtualSpace;
591d01be
RD
2365 rcPlace.right = rcPlace.left + vsDraw.aveCharWidth;
2366 } else {
2367 // draw left of the right text margin, to avoid clipping by the current clip rect
2368 rcPlace.right = rcLine.right - vs.rightMarginWidth;
2369 rcPlace.left = rcPlace.right - vsDraw.aveCharWidth;
2370 }
2371 DrawWrapMarker(surface, rcPlace, true, wrapColour);
2372 }
9e730a78
RD
2373}
2374
7e0c58e9
RD
2375void Editor::DrawIndicators(Surface *surface, ViewStyle &vsDraw, int line, int xStart,
2376 PRectangle rcLine, LineLayout *ll, int subLine, int lineEnd, bool under) {
2377 // Draw decorators
2378 const int posLineStart = pdoc->LineStart(line);
2379 const int lineStart = ll->LineStart(subLine);
2380 const int subLineStart = ll->positions[lineStart];
2381 const int posLineEnd = posLineStart + lineEnd;
2382
2383 if (!under) {
2384 // Draw indicators
2385 // foreach indicator...
2386 for (int indicnum = 0, mask = 1 << pdoc->stylingBits; mask < 0x100; indicnum++) {
2387 if (!(mask & ll->styleBitsSet)) {
2388 mask <<= 1;
2389 continue;
2390 }
2391 int startPos = -1;
2392 // foreach style pos in line...
2393 for (int indicPos = lineStart; indicPos <= lineEnd; indicPos++) {
2394 // look for starts...
2395 if (startPos < 0) {
2396 // NOT in indicator run, looking for START
2397 if (indicPos < lineEnd && (ll->indicators[indicPos] & mask))
2398 startPos = indicPos;
2399 }
2400 // ... or ends
2401 if (startPos >= 0) {
2402 // IN indicator run, looking for END
2403 if (indicPos >= lineEnd || !(ll->indicators[indicPos] & mask)) {
2404 // AT end of indicator run, DRAW it!
2405 PRectangle rcIndic(
2406 ll->positions[startPos] + xStart - subLineStart,
2407 rcLine.top + vsDraw.maxAscent,
2408 ll->positions[indicPos] + xStart - subLineStart,
2409 rcLine.top + vsDraw.maxAscent + 3);
2410 vsDraw.indicators[indicnum].Draw(surface, rcIndic, rcLine);
2411 // RESET control var
2412 startPos = -1;
2413 }
2414 }
2415 }
2416 mask <<= 1;
2417 }
2418 }
2419
2420 for (Decoration *deco = pdoc->decorations.root; deco; deco = deco->next) {
2421 if (under == vsDraw.indicators[deco->indicator].under) {
2422 int startPos = posLineStart + lineStart;
2423 if (!deco->rs.ValueAt(startPos)) {
2424 startPos = deco->rs.EndRun(startPos);
2425 }
2426 while ((startPos < posLineEnd) && (deco->rs.ValueAt(startPos))) {
2427 int endPos = deco->rs.EndRun(startPos);
2428 if (endPos > posLineEnd)
2429 endPos = posLineEnd;
2430 PRectangle rcIndic(
2431 ll->positions[startPos - posLineStart] + xStart - subLineStart,
2432 rcLine.top + vsDraw.maxAscent,
2433 ll->positions[endPos - posLineStart] + xStart - subLineStart,
2434 rcLine.top + vsDraw.maxAscent + 3);
2435 vsDraw.indicators[deco->indicator].Draw(surface, rcIndic, rcLine);
2436 startPos = deco->rs.EndRun(endPos);
2437 }
2438 }
2439 }
2440}
2441
9e96e16f
RD
2442void Editor::DrawAnnotation(Surface *surface, ViewStyle &vsDraw, int line, int xStart,
2443 PRectangle rcLine, LineLayout *ll, int subLine) {
2444 int indent = pdoc->GetLineIndentation(line) * vsDraw.spaceWidth;
2445 PRectangle rcSegment = rcLine;
2446 int annotationLine = subLine - ll->lines;
2447 const StyledText stAnnotation = pdoc->AnnotationStyledText(line);
2448 if (stAnnotation.text && ValidStyledText(vsDraw, vsDraw.annotationStyleOffset, stAnnotation)) {
2449 surface->FillRectangle(rcSegment, vsDraw.styles[0].back.allocated);
2450 if (vs.annotationVisible == ANNOTATION_BOXED) {
2451 // Only care about calculating width if need to draw box
2452 int widthAnnotation = WidestLineWidth(surface, vsDraw, vsDraw.annotationStyleOffset, stAnnotation);
2453 widthAnnotation += vsDraw.spaceWidth * 2; // Margins
2454 rcSegment.left = xStart + indent;
2455 rcSegment.right = rcSegment.left + widthAnnotation;
2456 surface->PenColour(vsDraw.styles[vsDraw.annotationStyleOffset].fore.allocated);
2457 } else {
2458 rcSegment.left = xStart;
2459 }
2460 const int annotationLines = pdoc->AnnotationLines(line);
2461 size_t start = 0;
2462 size_t lengthAnnotation = stAnnotation.LineLength(start);
2463 int lineInAnnotation = 0;
2464 while ((lineInAnnotation < annotationLine) && (start < stAnnotation.length)) {
2465 start += lengthAnnotation + 1;
2466 lengthAnnotation = stAnnotation.LineLength(start);
2467 lineInAnnotation++;
2468 }
2469 PRectangle rcText = rcSegment;
2470 if (vs.annotationVisible == ANNOTATION_BOXED) {
2471 surface->FillRectangle(rcText,
2472 vsDraw.styles[stAnnotation.StyleAt(start) + vsDraw.annotationStyleOffset].back.allocated);
2473 rcText.left += vsDraw.spaceWidth;
2474 }
2475 DrawStyledText(surface, vsDraw, vsDraw.annotationStyleOffset, rcText, rcText.top + vsDraw.maxAscent,
2476 stAnnotation, start, lengthAnnotation);
2477 if (vs.annotationVisible == ANNOTATION_BOXED) {
2478 surface->MoveTo(rcSegment.left, rcSegment.top);
2479 surface->LineTo(rcSegment.left, rcSegment.bottom);
2480 surface->MoveTo(rcSegment.right, rcSegment.top);
2481 surface->LineTo(rcSegment.right, rcSegment.bottom);
2482 if (subLine == ll->lines){
2483 surface->MoveTo(rcSegment.left, rcSegment.top);
2484 surface->LineTo(rcSegment.right, rcSegment.top);
2485 }
2486 if (subLine == ll->lines+annotationLines-1) {
2487 surface->MoveTo(rcSegment.left, rcSegment.bottom - 1);
2488 surface->LineTo(rcSegment.right, rcSegment.bottom - 1);
2489 }
2490 }
7e0c58e9 2491 }
7e0c58e9
RD
2492}
2493
d134f170 2494void Editor::DrawLine(Surface *surface, ViewStyle &vsDraw, int line, int lineVisible, int xStart,
7e0c58e9 2495 PRectangle rcLine, LineLayout *ll, int subLine) {
d134f170 2496
9ce192d4 2497 PRectangle rcSegment = rcLine;
d134f170 2498
9ce192d4
RD
2499 // Using one font for all control characters so it can be controlled independently to ensure
2500 // the box goes around the characters tightly. Seems to be no way to work out what height
2501 // is taken by an individual character - internal leading gives varying results.
2502 Font &ctrlCharsFont = vsDraw.styles[STYLE_CONTROLCHAR].font;
2503
1a2fb4cd
RD
2504 // See if something overrides the line background color: Either if caret is on the line
2505 // and background color is set for that, or if a marker is defined that forces its background
2506 // color onto the line, or if a marker is defined but has no selection margin in which to
a33203cb
RD
2507 // display itself (as long as it's not an SC_MARK_EMPTY marker). These are checked in order
2508 // with the earlier taking precedence. When multiple markers cause background override,
2509 // the color for the highest numbered one is used.
65ec6247 2510 bool overrideBackground = false;
1a2fb4cd 2511 ColourAllocated background;
b8193d80 2512 if (caret.active && vsDraw.showCaretLineBackground && (vsDraw.caretLineAlpha == SC_ALPHA_NOALPHA) && ll->containsCaret) {
65ec6247
RD
2513 overrideBackground = true;
2514 background = vsDraw.caretLineBackground.allocated;
2515 }
1a2fb4cd
RD
2516 if (!overrideBackground) {
2517 int marks = pdoc->GetMark(line);
2518 for (int markBit = 0; (markBit < 32) && marks; markBit++) {
b8193d80 2519 if ((marks & 1) && (vsDraw.markers[markBit].markType == SC_MARK_BACKGROUND) &&
7e0c58e9 2520 (vsDraw.markers[markBit].alpha == SC_ALPHA_NOALPHA)) {
1a2fb4cd
RD
2521 background = vsDraw.markers[markBit].back.allocated;
2522 overrideBackground = true;
2523 }
2524 marks >>= 1;
2525 }
2526 }
2527 if (!overrideBackground) {
2528 if (vsDraw.maskInLine) {
b8193d80
RD
2529 int marksMasked = pdoc->GetMark(line) & vsDraw.maskInLine;
2530 if (marksMasked) {
2531 for (int markBit = 0; (markBit < 32) && marksMasked; markBit++) {
2532 if ((marksMasked & 1) && (vsDraw.markers[markBit].markType != SC_MARK_EMPTY) &&
7e0c58e9 2533 (vsDraw.markers[markBit].alpha == SC_ALPHA_NOALPHA)) {
a33203cb 2534 overrideBackground = true;
1a2fb4cd
RD
2535 background = vsDraw.markers[markBit].back.allocated;
2536 }
b8193d80 2537 marksMasked >>= 1;
9ce192d4 2538 }
9ce192d4
RD
2539 }
2540 }
9ce192d4
RD
2541 }
2542
9e730a78 2543 bool drawWhitespaceBackground = (vsDraw.viewWhitespace != wsInvisible) &&
7e0c58e9 2544 (!overrideBackground) && (vsDraw.whitespaceBackgroundSet);
9e730a78 2545
1a2fb4cd 2546 bool inIndentation = subLine == 0; // Do not handle indentation except on first subline.
591d01be 2547 int indentWidth = pdoc->IndentSize() * vsDraw.spaceWidth;
d134f170 2548
9ce192d4 2549 int posLineStart = pdoc->LineStart(line);
9ce192d4 2550
1a2fb4cd
RD
2551 int startseg = ll->LineStart(subLine);
2552 int subLineStart = ll->positions[startseg];
9e96e16f
RD
2553 if (subLine >= ll->lines) {
2554 DrawAnnotation(surface, vsDraw, line, xStart, rcLine, ll, subLine);
2555 return; // No further drawing
2556 }
1a2fb4cd
RD
2557 int lineStart = 0;
2558 int lineEnd = 0;
2559 if (subLine < ll->lines) {
2560 lineStart = ll->LineStart(subLine);
9e730a78 2561 lineEnd = ll->LineStart(subLine + 1);
9e96e16f
RD
2562 if (subLine == ll->lines - 1) {
2563 lineEnd = ll->numCharsBeforeEOL;
2564 }
9e730a78 2565 }
591d01be 2566
7e0c58e9
RD
2567 ColourAllocated wrapColour = vsDraw.styles[STYLE_DEFAULT].fore.allocated;
2568 if (vsDraw.whitespaceForegroundSet)
2569 wrapColour = vsDraw.whitespaceForeground.allocated;
2570
591d01be
RD
2571 bool drawWrapMarkEnd = false;
2572
2573 if (wrapVisualFlags & SC_WRAPVISUALFLAG_END) {
2574 if (subLine + 1 < ll->lines) {
2575 drawWrapMarkEnd = ll->LineStart(subLine + 1) != 0;
2576 }
2577 }
2578
9e96e16f 2579 if (ll->wrapIndent != 0) {
591d01be
RD
2580
2581 bool continuedWrapLine = false;
2582 if (subLine < ll->lines) {
2583 continuedWrapLine = ll->LineStart(subLine) != 0;
2584 }
2585
2586 if (continuedWrapLine) {
2587 // draw continuation rect
2588 PRectangle rcPlace = rcSegment;
2589
2590 rcPlace.left = ll->positions[startseg] + xStart - subLineStart;
9e96e16f 2591 rcPlace.right = rcPlace.left + ll->wrapIndent;
591d01be
RD
2592
2593 // default bgnd here..
7e0c58e9
RD
2594 surface->FillRectangle(rcSegment, overrideBackground ? background :
2595 vsDraw.styles[STYLE_DEFAULT].back.allocated);
591d01be
RD
2596
2597 // main line style would be below but this would be inconsistent with end markers
2598 // also would possibly not be the style at wrap point
2599 //int styleMain = ll->styles[lineStart];
2600 //surface->FillRectangle(rcPlace, vsDraw.styles[styleMain].back.allocated);
2601
2602 if (wrapVisualFlags & SC_WRAPVISUALFLAG_START) {
2603
2604 if (wrapVisualFlagsLocation & SC_WRAPVISUALFLAGLOC_START_BY_TEXT)
2605 rcPlace.left = rcPlace.right - vsDraw.aveCharWidth;
2606 else
2607 rcPlace.right = rcPlace.left + vsDraw.aveCharWidth;
2608
7e0c58e9 2609 DrawWrapMarker(surface, rcPlace, false, wrapColour);
591d01be
RD
2610 }
2611
9e96e16f 2612 xStart += ll->wrapIndent;
591d01be
RD
2613 }
2614 }
2615
9e96e16f
RD
2616 bool selBackDrawn = vsDraw.selbackset &&
2617 ((vsDraw.selAlpha == SC_ALPHA_NOALPHA) || (vsDraw.selAdditionalAlpha == SC_ALPHA_NOALPHA));
2618
7e0c58e9
RD
2619 // Does not take margin into account but not significant
2620 int xStartVisible = subLineStart - xStart;
2621
9e96e16f
RD
2622 ll->psel = &sel;
2623
2624 BreakFinder bfBack(ll, lineStart, lineEnd, posLineStart, IsUnicodeMode(), xStartVisible, selBackDrawn);
7e0c58e9 2625 int next = bfBack.First();
9e730a78
RD
2626
2627 // Background drawing loop
7e0c58e9 2628 while (twoPhaseDraw && (next < lineEnd)) {
9e730a78 2629
7e0c58e9
RD
2630 startseg = next;
2631 next = bfBack.Next();
2632 int i = next - 1;
9e730a78 2633 int iDoc = i + posLineStart;
7e0c58e9
RD
2634
2635 rcSegment.left = ll->positions[startseg] + xStart - subLineStart;
2636 rcSegment.right = ll->positions[i + 1] + xStart - subLineStart;
2637 // Only try to draw if really visible - enhances performance by not calling environment to
2638 // draw strings that are completely past the right side of the window.
2639 if ((rcSegment.left <= rcLine.right) && (rcSegment.right >= rcLine.left)) {
2640 // Clip to line rectangle, since may have a huge position which will not work with some platforms
2641 rcSegment.left = Platform::Maximum(rcSegment.left, rcLine.left);
2642 rcSegment.right = Platform::Minimum(rcSegment.right, rcLine.right);
2643
2644 int styleMain = ll->styles[i];
9e96e16f 2645 const int inSelection = hideSelection ? 0 : sel.CharacterInSelection(iDoc);
7e0c58e9
RD
2646 bool inHotspot = (ll->hsStart != -1) && (iDoc >= ll->hsStart) && (iDoc < ll->hsEnd);
2647 ColourAllocated textBack = TextBackground(vsDraw, overrideBackground, background, inSelection, inHotspot, styleMain, i, ll);
2648 if (ll->chars[i] == '\t') {
2649 // Tab display
2650 if (drawWhitespaceBackground &&
2651 (!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways))
2652 textBack = vsDraw.whitespaceBackground.allocated;
2653 surface->FillRectangle(rcSegment, textBack);
2654 } else if (IsControlCharacter(ll->chars[i])) {
2655 // Control character display
2656 inIndentation = false;
2657 surface->FillRectangle(rcSegment, textBack);
2658 } else {
2659 // Normal text display
2660 surface->FillRectangle(rcSegment, textBack);
2661 if (vsDraw.viewWhitespace != wsInvisible ||
2662 (inIndentation && vsDraw.viewIndentationGuides == ivReal)) {
2663 for (int cpos = 0; cpos <= i - startseg; cpos++) {
2664 if (ll->chars[cpos + startseg] == ' ') {
2665 if (drawWhitespaceBackground &&
2666 (!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways)) {
9e96e16f
RD
2667 PRectangle rcSpace(ll->positions[cpos + startseg] + xStart - subLineStart,
2668 rcSegment.top,
2669 ll->positions[cpos + startseg + 1] + xStart - subLineStart,
2670 rcSegment.bottom);
7e0c58e9 2671 surface->FillRectangle(rcSpace, vsDraw.whitespaceBackground.allocated);
9e730a78 2672 }
7e0c58e9
RD
2673 } else {
2674 inIndentation = false;
9e730a78
RD
2675 }
2676 }
2677 }
2678 }
7e0c58e9
RD
2679 } else if (rcSegment.left > rcLine.right) {
2680 break;
9e730a78 2681 }
1a2fb4cd 2682 }
9e730a78
RD
2683
2684 if (twoPhaseDraw) {
2685 DrawEOL(surface, vsDraw, rcLine, ll, line, lineEnd,
591d01be 2686 xStart, subLine, subLineStart, overrideBackground, background,
7e0c58e9
RD
2687 drawWrapMarkEnd, wrapColour);
2688 }
2689
2690 DrawIndicators(surface, vsDraw, line, xStart, rcLine, ll, subLine, lineEnd, true);
2691
2692 if (vsDraw.edgeState == EDGE_LINE) {
2693 int edgeX = theEdge * vsDraw.spaceWidth;
2694 rcSegment.left = edgeX + xStart;
2695 rcSegment.right = rcSegment.left + 1;
2696 surface->FillRectangle(rcSegment, vsDraw.edgecolour.allocated);
9e730a78
RD
2697 }
2698
9e96e16f
RD
2699 // Draw underline mark as part of background if not transparent
2700 int marks = pdoc->GetMark(line);
2701 int markBit;
2702 for (markBit = 0; (markBit < 32) && marks; markBit++) {
2703 if ((marks & 1) && (vsDraw.markers[markBit].markType == SC_MARK_UNDERLINE) &&
2704 (vsDraw.markers[markBit].alpha == SC_ALPHA_NOALPHA)) {
2705 PRectangle rcUnderline = rcLine;
2706 rcUnderline.top = rcUnderline.bottom - 2;
2707 surface->FillRectangle(rcUnderline, vsDraw.markers[markBit].back.allocated);
2708 }
2709 marks >>= 1;
2710 }
2711
9e730a78 2712 inIndentation = subLine == 0; // Do not handle indentation except on first subline.
9e730a78 2713 // Foreground drawing loop
9e96e16f
RD
2714 BreakFinder bfFore(ll, lineStart, lineEnd, posLineStart, IsUnicodeMode(), xStartVisible,
2715 ((!twoPhaseDraw && selBackDrawn) || vsDraw.selforeset));
7e0c58e9
RD
2716 next = bfFore.First();
2717
2718 while (next < lineEnd) {
2719
2720 startseg = next;
2721 next = bfFore.Next();
2722 int i = next - 1;
9ce192d4
RD
2723
2724 int iDoc = i + posLineStart;
7e0c58e9
RD
2725
2726 rcSegment.left = ll->positions[startseg] + xStart - subLineStart;
2727 rcSegment.right = ll->positions[i + 1] + xStart - subLineStart;
2728 // Only try to draw if really visible - enhances performance by not calling environment to
2729 // draw strings that are completely past the right side of the window.
2730 if ((rcSegment.left <= rcLine.right) && (rcSegment.right >= rcLine.left)) {
2731 int styleMain = ll->styles[i];
2732 ColourAllocated textFore = vsDraw.styles[styleMain].fore.allocated;
2733 Font &textFont = vsDraw.styles[styleMain].font;
2734 //hotspot foreground
2735 if (ll->hsStart != -1 && iDoc >= ll->hsStart && iDoc < hsEnd) {
2736 if (vsDraw.hotspotForegroundSet)
2737 textFore = vsDraw.hotspotForeground.allocated;
2738 }
9e96e16f 2739 const int inSelection = hideSelection ? 0 : sel.CharacterInSelection(iDoc);
7e0c58e9 2740 if (inSelection && (vsDraw.selforeset)) {
9e96e16f 2741 textFore = (inSelection == 1) ? vsDraw.selforeground.allocated : vsDraw.selAdditionalForeground.allocated;
7e0c58e9
RD
2742 }
2743 bool inHotspot = (ll->hsStart != -1) && (iDoc >= ll->hsStart) && (iDoc < ll->hsEnd);
2744 ColourAllocated textBack = TextBackground(vsDraw, overrideBackground, background, inSelection, inHotspot, styleMain, i, ll);
2745 if (ll->chars[i] == '\t') {
2746 // Tab display
2747 if (!twoPhaseDraw) {
2748 if (drawWhitespaceBackground &&
2749 (!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways))
2750 textBack = vsDraw.whitespaceBackground.allocated;
2751 surface->FillRectangle(rcSegment, textBack);
9e730a78 2752 }
7e0c58e9
RD
2753 if ((vsDraw.viewWhitespace != wsInvisible) ||
2754 (inIndentation && vsDraw.viewIndentationGuides != ivNone)) {
2755 if (vsDraw.whitespaceForegroundSet)
2756 textFore = vsDraw.whitespaceForeground.allocated;
2757 surface->PenColour(textFore);
9ce192d4 2758 }
7e0c58e9
RD
2759 if (inIndentation && vsDraw.viewIndentationGuides == ivReal) {
2760 for (int xIG = ll->positions[i] / indentWidth * indentWidth; xIG < ll->positions[i + 1]; xIG += indentWidth) {
2761 if (xIG >= ll->positions[i] && xIG > 0) {
2762 DrawIndentGuide(surface, lineVisible, vsDraw.lineHeight, xIG + xStart, rcSegment,
2763 (ll->xHighlightGuide == xIG));
1a2fb4cd 2764 }
d134f170 2765 }
7e0c58e9
RD
2766 }
2767 if (vsDraw.viewWhitespace != wsInvisible) {
2768 if (!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways) {
2769 PRectangle rcTab(rcSegment.left + 1, rcSegment.top + 4,
2770 rcSegment.right - 1, rcSegment.bottom - vsDraw.maxDescent);
2771 DrawTabArrow(surface, rcTab, rcSegment.top + vsDraw.lineHeight / 2);
1a2fb4cd 2772 }
7e0c58e9
RD
2773 }
2774 } else if (IsControlCharacter(ll->chars[i])) {
2775 // Control character display
2776 inIndentation = false;
2777 if (controlCharSymbol < 32) {
2778 // Draw the character
2779 const char *ctrlChar = ControlCharacterString(ll->chars[i]);
2780 DrawTextBlob(surface, vsDraw, rcSegment, ctrlChar, textBack, textFore, twoPhaseDraw);
1a2fb4cd 2781 } else {
7e0c58e9
RD
2782 char cc[2] = { static_cast<char>(controlCharSymbol), '\0' };
2783 surface->DrawTextNoClip(rcSegment, ctrlCharsFont,
2784 rcSegment.top + vsDraw.maxAscent,
2785 cc, 1, textBack, textFore);
2786 }
2787 } else if ((i == startseg) && (static_cast<unsigned char>(ll->chars[i]) >= 0x80) && IsUnicodeMode()) {
2788 char hexits[3];
2789 sprintf(hexits, "%2X", ll->chars[i] & 0xff);
2790 DrawTextBlob(surface, vsDraw, rcSegment, hexits, textBack, textFore, twoPhaseDraw);
2791 } else {
2792 // Normal text display
2793 if (vsDraw.styles[styleMain].visible) {
2794 if (twoPhaseDraw) {
2795 surface->DrawTextTransparent(rcSegment, textFont,
2796 rcSegment.top + vsDraw.maxAscent, ll->chars + startseg,
2797 i - startseg + 1, textFore);
2798 } else {
2799 surface->DrawTextNoClip(rcSegment, textFont,
2800 rcSegment.top + vsDraw.maxAscent, ll->chars + startseg,
2801 i - startseg + 1, textFore, textBack);
9e730a78 2802 }
7e0c58e9
RD
2803 }
2804 if (vsDraw.viewWhitespace != wsInvisible ||
2805 (inIndentation && vsDraw.viewIndentationGuides != ivNone)) {
2806 for (int cpos = 0; cpos <= i - startseg; cpos++) {
2807 if (ll->chars[cpos + startseg] == ' ') {
2808 if (vsDraw.viewWhitespace != wsInvisible) {
2809 if (vsDraw.whitespaceForegroundSet)
2810 textFore = vsDraw.whitespaceForeground.allocated;
2811 if (!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways) {
2812 int xmid = (ll->positions[cpos + startseg] + ll->positions[cpos + startseg + 1]) / 2;
2813 if (!twoPhaseDraw && drawWhitespaceBackground &&
2814 (!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways)) {
2815 textBack = vsDraw.whitespaceBackground.allocated;
9e96e16f
RD
2816 PRectangle rcSpace(ll->positions[cpos + startseg] + xStart - subLineStart,
2817 rcSegment.top,
2818 ll->positions[cpos + startseg + 1] + xStart - subLineStart,
2819 rcSegment.bottom);
7e0c58e9 2820 surface->FillRectangle(rcSpace, textBack);
d134f170 2821 }
7e0c58e9 2822 PRectangle rcDot(xmid + xStart - subLineStart, rcSegment.top + vsDraw.lineHeight / 2, 0, 0);
9e96e16f
RD
2823 rcDot.right = rcDot.left + vs.whitespaceSize;
2824 rcDot.bottom = rcDot.top + vs.whitespaceSize;
7e0c58e9 2825 surface->FillRectangle(rcDot, textFore);
d134f170 2826 }
7e0c58e9
RD
2827 }
2828 if (inIndentation && vsDraw.viewIndentationGuides == ivReal) {
2829 int startSpace = ll->positions[cpos + startseg];
2830 if (startSpace > 0 && (startSpace % indentWidth == 0)) {
2831 DrawIndentGuide(surface, lineVisible, vsDraw.lineHeight, startSpace + xStart, rcSegment,
2832 (ll->xHighlightGuide == ll->positions[cpos + startseg]));
d134f170 2833 }
9ce192d4 2834 }
7e0c58e9
RD
2835 } else {
2836 inIndentation = false;
9ce192d4
RD
2837 }
2838 }
2839 }
7e0c58e9
RD
2840 }
2841 if (ll->hsStart != -1 && vsDraw.hotspotUnderline && iDoc >= ll->hsStart && iDoc < ll->hsEnd ) {
2842 PRectangle rcUL = rcSegment;
2843 rcUL.top = rcUL.top + vsDraw.maxAscent + 1;
2844 rcUL.bottom = rcUL.top + 1;
2845 if (vsDraw.hotspotForegroundSet)
2846 surface->FillRectangle(rcUL, vsDraw.hotspotForeground.allocated);
2847 else
f6bcfd97 2848 surface->FillRectangle(rcUL, textFore);
7e0c58e9
RD
2849 } else if (vsDraw.styles[styleMain].underline) {
2850 PRectangle rcUL = rcSegment;
2851 rcUL.top = rcUL.top + vsDraw.maxAscent + 1;
2852 rcUL.bottom = rcUL.top + 1;
2853 surface->FillRectangle(rcUL, textFore);
9ce192d4 2854 }
7e0c58e9
RD
2855 } else if (rcSegment.left > rcLine.right) {
2856 break;
9ce192d4
RD
2857 }
2858 }
7e0c58e9
RD
2859 if ((vsDraw.viewIndentationGuides == ivLookForward || vsDraw.viewIndentationGuides == ivLookBoth)
2860 && (subLine == 0)) {
2861 int indentSpace = pdoc->GetLineIndentation(line);
9e96e16f
RD
2862 int xStartText = ll->positions[pdoc->GetLineIndentPosition(line) - posLineStart];
2863
7e0c58e9 2864 // Find the most recent line with some text
9ce192d4 2865
7e0c58e9 2866 int lineLastWithText = line;
9e96e16f 2867 while (lineLastWithText > Platform::Maximum(line-20, 0) && pdoc->IsWhiteLine(lineLastWithText)) {
7e0c58e9
RD
2868 lineLastWithText--;
2869 }
2870 if (lineLastWithText < line) {
9e96e16f 2871 xStartText = 100000; // Don't limit to visible indentation on empty line
7e0c58e9
RD
2872 // This line is empty, so use indentation of last line with text
2873 int indentLastWithText = pdoc->GetLineIndentation(lineLastWithText);
2874 int isFoldHeader = pdoc->GetLevel(lineLastWithText) & SC_FOLDLEVELHEADERFLAG;
2875 if (isFoldHeader) {
2876 // Level is one more level than parent
2877 indentLastWithText += pdoc->IndentSize();
1e9bafca 2878 }
7e0c58e9
RD
2879 if (vsDraw.viewIndentationGuides == ivLookForward) {
2880 // In viLookForward mode, previous line only used if it is a fold header
2881 if (isFoldHeader) {
2882 indentSpace = Platform::Maximum(indentSpace, indentLastWithText);
9ce192d4 2883 }
7e0c58e9
RD
2884 } else { // viLookBoth
2885 indentSpace = Platform::Maximum(indentSpace, indentLastWithText);
9ce192d4
RD
2886 }
2887 }
7e0c58e9
RD
2888
2889 int lineNextWithText = line;
9e96e16f 2890 while (lineNextWithText < Platform::Minimum(line+20, pdoc->LinesTotal()) && pdoc->IsWhiteLine(lineNextWithText)) {
7e0c58e9
RD
2891 lineNextWithText++;
2892 }
2893 if (lineNextWithText > line) {
2894 // This line is empty, so use indentation of last line with text
2895 indentSpace = Platform::Maximum(indentSpace,
2896 pdoc->GetLineIndentation(lineNextWithText));
2897 }
2898
2899 for (int indentPos = pdoc->IndentSize(); indentPos < indentSpace; indentPos += pdoc->IndentSize()) {
2900 int xIndent = indentPos * vsDraw.spaceWidth;
9e96e16f
RD
2901 if (xIndent < xStartText) {
2902 DrawIndentGuide(surface, lineVisible, vsDraw.lineHeight, xIndent + xStart, rcSegment,
2903 (ll->xHighlightGuide == xIndent));
2904 }
7e0c58e9 2905 }
9ce192d4 2906 }
7e0c58e9
RD
2907
2908 DrawIndicators(surface, vsDraw, line, xStart, rcLine, ll, subLine, lineEnd, false);
2909
9ce192d4 2910 // End of the drawing of the current line
9e730a78
RD
2911 if (!twoPhaseDraw) {
2912 DrawEOL(surface, vsDraw, rcLine, ll, line, lineEnd,
591d01be 2913 xStart, subLine, subLineStart, overrideBackground, background,
7e0c58e9 2914 drawWrapMarkEnd, wrapColour);
9ce192d4 2915 }
9e96e16f
RD
2916 if (!hideSelection && ((vsDraw.selAlpha != SC_ALPHA_NOALPHA) || (vsDraw.selAdditionalAlpha != SC_ALPHA_NOALPHA))) {
2917 // For each selection draw
2918 int virtualSpaces = 0;
2919 if (subLine == (ll->lines - 1)) {
2920 virtualSpaces = sel.VirtualSpaceFor(pdoc->LineEnd(line));
2921 }
2922 SelectionPosition posStart(posLineStart);
2923 SelectionPosition posEnd(posLineStart + lineEnd, virtualSpaces);
2924 SelectionSegment virtualSpaceRange(posStart, posEnd);
2925 for (size_t r=0; r<sel.Count(); r++) {
2926 int alpha = (r == sel.Main()) ? vsDraw.selAlpha : vsDraw.selAdditionalAlpha;
2927 if (alpha != SC_ALPHA_NOALPHA) {
2928 SelectionSegment portion = sel.Range(r).Intersect(virtualSpaceRange);
2929 if (!portion.Empty()) {
2930 const int spaceWidth = static_cast<int>(vsDraw.styles[ll->EndLineStyle()].spaceWidth);
2931 rcSegment.left = xStart + ll->positions[portion.start.Position() - posLineStart] - subLineStart + portion.start.VirtualSpace() * spaceWidth;
2932 rcSegment.right = xStart + ll->positions[portion.end.Position() - posLineStart] - subLineStart + portion.end.VirtualSpace() * spaceWidth;
2933 rcSegment.left = Platform::Maximum(rcSegment.left, rcLine.left);
2934 rcSegment.right = Platform::Minimum(rcSegment.right, rcLine.right);
2935 SimpleAlphaRectangle(surface, rcSegment, SelectionBackground(vsDraw, r == sel.Main()), alpha);
2936 }
2937 }
b8193d80
RD
2938 }
2939 }
d134f170 2940
b8193d80
RD
2941 // Draw any translucent whole line states
2942 rcSegment.left = xStart;
2943 rcSegment.right = rcLine.right - 1;
2944 if (caret.active && vsDraw.showCaretLineBackground && ll->containsCaret) {
2945 SimpleAlphaRectangle(surface, rcSegment, vsDraw.caretLineBackground.allocated, vsDraw.caretLineAlpha);
2946 }
9e96e16f
RD
2947 marks = pdoc->GetMark(line);
2948 for (markBit = 0; (markBit < 32) && marks; markBit++) {
b8193d80
RD
2949 if ((marks & 1) && (vsDraw.markers[markBit].markType == SC_MARK_BACKGROUND)) {
2950 SimpleAlphaRectangle(surface, rcSegment, vsDraw.markers[markBit].back.allocated, vsDraw.markers[markBit].alpha);
9e96e16f
RD
2951 } else if ((marks & 1) && (vsDraw.markers[markBit].markType == SC_MARK_UNDERLINE)) {
2952 PRectangle rcUnderline = rcSegment;
2953 rcUnderline.top = rcUnderline.bottom - 2;
2954 SimpleAlphaRectangle(surface, rcUnderline, vsDraw.markers[markBit].back.allocated, vsDraw.markers[markBit].alpha);
b8193d80
RD
2955 }
2956 marks >>= 1;
2957 }
2958 if (vsDraw.maskInLine) {
2959 int marksMasked = pdoc->GetMark(line) & vsDraw.maskInLine;
2960 if (marksMasked) {
9e96e16f 2961 for (markBit = 0; (markBit < 32) && marksMasked; markBit++) {
b8193d80
RD
2962 if ((marksMasked & 1) && (vsDraw.markers[markBit].markType != SC_MARK_EMPTY)) {
2963 SimpleAlphaRectangle(surface, rcSegment, vsDraw.markers[markBit].back.allocated, vsDraw.markers[markBit].alpha);
2964 }
2965 marksMasked >>= 1;
2966 }
2967 }
2968 }
9ce192d4
RD
2969}
2970
9e96e16f
RD
2971void Editor::DrawBlockCaret(Surface *surface, ViewStyle &vsDraw, LineLayout *ll, int subLine,
2972 int xStart, int offset, int posCaret, PRectangle rcCaret, ColourAllocated caretColour) {
7e0c58e9
RD
2973
2974 int lineStart = ll->LineStart(subLine);
2975 int posBefore = posCaret;
2976 int posAfter = MovePositionOutsideChar(posCaret + 1, 1);
2977 int numCharsToDraw = posAfter - posCaret;
2978
2979 // Work out where the starting and ending offsets are. We need to
2980 // see if the previous character shares horizontal space, such as a
2981 // glyph / combining character. If so we'll need to draw that too.
2982 int offsetFirstChar = offset;
2983 int offsetLastChar = offset + (posAfter - posCaret);
2984 while ((offsetLastChar - numCharsToDraw) >= lineStart) {
2985 if ((ll->positions[offsetLastChar] - ll->positions[offsetLastChar - numCharsToDraw]) > 0) {
2986 // The char does not share horizontal space
2987 break;
2988 }
2989 // Char shares horizontal space, update the numChars to draw
2990 // Update posBefore to point to the prev char
2991 posBefore = MovePositionOutsideChar(posBefore - 1, -1);
2992 numCharsToDraw = posAfter - posBefore;
2993 offsetFirstChar = offset - (posCaret - posBefore);
2994 }
2995
2996 // See if the next character shares horizontal space, if so we'll
2997 // need to draw that too.
2998 numCharsToDraw = offsetLastChar - offsetFirstChar;
2999 while ((offsetLastChar < ll->LineStart(subLine + 1)) && (offsetLastChar <= ll->numCharsInLine)) {
3000 // Update posAfter to point to the 2nd next char, this is where
3001 // the next character ends, and 2nd next begins. We'll need
3002 // to compare these two
3003 posBefore = posAfter;
3004 posAfter = MovePositionOutsideChar(posAfter + 1, 1);
3005 offsetLastChar = offset + (posAfter - posCaret);
3006 if ((ll->positions[offsetLastChar] - ll->positions[offsetLastChar - (posAfter - posBefore)]) > 0) {
3007 // The char does not share horizontal space
3008 break;
3009 }
3010 // Char shares horizontal space, update the numChars to draw
3011 numCharsToDraw = offsetLastChar - offsetFirstChar;
3012 }
3013
3014 // We now know what to draw, update the caret drawing rectangle
9e96e16f
RD
3015 rcCaret.left = ll->positions[offsetFirstChar] - ll->positions[lineStart] + xStart;
3016 rcCaret.right = ll->positions[offsetFirstChar+numCharsToDraw] - ll->positions[lineStart] + xStart;
3017
3018 // Adjust caret position to take into account any word wrapping symbols.
3019 if ((ll->wrapIndent != 0) && (lineStart != 0)) {
3020 int wordWrapCharWidth = ll->wrapIndent;
3021 rcCaret.left += wordWrapCharWidth;
3022 rcCaret.right += wordWrapCharWidth;
3023 }
7e0c58e9
RD
3024
3025 // This character is where the caret block is, we override the colours
3026 // (inversed) for drawing the caret here.
3027 int styleMain = ll->styles[offsetFirstChar];
3028 surface->DrawTextClipped(rcCaret, vsDraw.styles[styleMain].font,
3029 rcCaret.top + vsDraw.maxAscent, ll->chars + offsetFirstChar,
3030 numCharsToDraw, vsDraw.styles[styleMain].back.allocated,
9e96e16f 3031 caretColour);
7e0c58e9
RD
3032}
3033
9e730a78 3034void Editor::RefreshPixMaps(Surface *surfaceWindow) {
1a2fb4cd 3035 if (!pixmapSelPattern->Initialised()) {
9e730a78
RD
3036 const int patternSize = 8;
3037 pixmapSelPattern->InitPixMap(patternSize, patternSize, surfaceWindow, wMain.GetID());
3038 // This complex procedure is to reproduce the checkerboard dithered pattern used by windows
9ce192d4
RD
3039 // for scroll bars and Visual Studio for its selection margin. The colour of this pattern is half
3040 // way between the chrome colour and the chrome highlight colour making a nice transition
3041 // between the window chrome and the content area. And it works in low colour depths.
9e730a78
RD
3042 PRectangle rcPattern(0, 0, patternSize, patternSize);
3043
3044 // Initialize default colours based on the chrome colour scheme. Typically the highlight is white.
3045 ColourAllocated colourFMFill = vs.selbar.allocated;
3046 ColourAllocated colourFMStripes = vs.selbarlight.allocated;
3047
3048 if (!(vs.selbarlight.desired == ColourDesired(0xff, 0xff, 0xff))) {
9ce192d4 3049 // User has chosen an unusual chrome colour scheme so just use the highlight edge colour.
9e730a78
RD
3050 // (Typically, the highlight colour is white.)
3051 colourFMFill = vs.selbarlight.allocated;
3052 }
3053
3054 if (vs.foldmarginColourSet) {
3055 // override default fold margin colour
3056 colourFMFill = vs.foldmarginColour.allocated;
3057 }
3058 if (vs.foldmarginHighlightColourSet) {
3059 // override default fold margin highlight colour
3060 colourFMStripes = vs.foldmarginHighlightColour.allocated;
3061 }
3062
3063 pixmapSelPattern->FillRectangle(rcPattern, colourFMFill);
3064 pixmapSelPattern->PenColour(colourFMStripes);
3065 for (int stripe = 0; stripe < patternSize; stripe++) {
3066 // Alternating 1 pixel stripes is same as checkerboard.
3067 pixmapSelPattern->MoveTo(0, stripe * 2);
3068 pixmapSelPattern->LineTo(patternSize, stripe * 2 - patternSize);
9ce192d4
RD
3069 }
3070 }
9e730a78 3071
1a2fb4cd 3072 if (!pixmapIndentGuide->Initialised()) {
d134f170 3073 // 1 extra pixel in height so can handle odd/even positions and so produce a continuous line
9e730a78
RD
3074 pixmapIndentGuide->InitPixMap(1, vs.lineHeight + 1, surfaceWindow, wMain.GetID());
3075 pixmapIndentGuideHighlight->InitPixMap(1, vs.lineHeight + 1, surfaceWindow, wMain.GetID());
d134f170 3076 PRectangle rcIG(0, 0, 1, vs.lineHeight);
1a2fb4cd
RD
3077 pixmapIndentGuide->FillRectangle(rcIG, vs.styles[STYLE_INDENTGUIDE].back.allocated);
3078 pixmapIndentGuide->PenColour(vs.styles[STYLE_INDENTGUIDE].fore.allocated);
3079 pixmapIndentGuideHighlight->FillRectangle(rcIG, vs.styles[STYLE_BRACELIGHT].back.allocated);
3080 pixmapIndentGuideHighlight->PenColour(vs.styles[STYLE_BRACELIGHT].fore.allocated);
d134f170 3081 for (int stripe = 1; stripe < vs.lineHeight + 1; stripe += 2) {
1a2fb4cd
RD
3082 pixmapIndentGuide->MoveTo(0, stripe);
3083 pixmapIndentGuide->LineTo(2, stripe);
3084 pixmapIndentGuideHighlight->MoveTo(0, stripe);
3085 pixmapIndentGuideHighlight->LineTo(2, stripe);
d134f170
RD
3086 }
3087 }
9ce192d4
RD
3088
3089 if (bufferedDraw) {
1a2fb4cd 3090 if (!pixmapLine->Initialised()) {
9e730a78 3091 PRectangle rcClient = GetClientRectangle();
1e9bafca 3092 pixmapLine->InitPixMap(rcClient.Width(), vs.lineHeight,
7e0c58e9 3093 surfaceWindow, wMain.GetID());
1a2fb4cd 3094 pixmapSelMargin->InitPixMap(vs.fixedColumnWidth,
7e0c58e9 3095 rcClient.Height(), surfaceWindow, wMain.GetID());
9ce192d4
RD
3096 }
3097 }
9e730a78
RD
3098}
3099
9e96e16f
RD
3100void Editor::DrawCarets(Surface *surface, ViewStyle &vsDraw, int lineDoc, int xStart,
3101 PRectangle rcLine, LineLayout *ll, int subLine) {
3102 // When drag is active it is the only caret drawn
3103 bool drawDrag = posDrag.IsValid();
3104 if (hideSelection && !drawDrag)
3105 return;
3106 const int posLineStart = pdoc->LineStart(lineDoc);
3107 // For each selection draw
3108 for (size_t r=0; (r<sel.Count()) || drawDrag; r++) {
3109 const bool mainCaret = r == sel.Main();
3110 const SelectionPosition posCaret = (drawDrag ? posDrag : sel.Range(r).caret);
3111 const int offset = posCaret.Position() - posLineStart;
3112 const int spaceWidth = static_cast<int>(vsDraw.styles[ll->EndLineStyle()].spaceWidth);
3113 const int virtualOffset = posCaret.VirtualSpace() * spaceWidth;
3114 if (ll->InLine(offset, subLine) && offset <= ll->numCharsBeforeEOL) {
3115 int xposCaret = ll->positions[offset] + virtualOffset - ll->positions[ll->LineStart(subLine)];
3116 if (ll->wrapIndent != 0) {
3117 int lineStart = ll->LineStart(subLine);
3118 if (lineStart != 0) // Wrapped
3119 xposCaret += ll->wrapIndent;
3120 }
3121 bool caretBlinkState = (caret.active && caret.on) || (!additionalCaretsBlink && !mainCaret);
3122 bool caretVisibleState = additionalCaretsVisible || mainCaret;
3123 if ((xposCaret >= 0) && (vsDraw.caretWidth > 0) && (vsDraw.caretStyle != CARETSTYLE_INVISIBLE) &&
3124 ((posDrag.IsValid()) || (caretBlinkState && caretVisibleState))) {
3125 bool caretAtEOF = false;
3126 bool caretAtEOL = false;
3127 bool drawBlockCaret = false;
3128 int widthOverstrikeCaret;
3129 int caretWidthOffset = 0;
3130 PRectangle rcCaret = rcLine;
3131
3132 if (posCaret.Position() == pdoc->Length()) { // At end of document
3133 caretAtEOF = true;
3134 widthOverstrikeCaret = vsDraw.aveCharWidth;
3135 } else if ((posCaret.Position() - posLineStart) >= ll->numCharsInLine) { // At end of line
3136 caretAtEOL = true;
3137 widthOverstrikeCaret = vsDraw.aveCharWidth;
3138 } else {
3139 widthOverstrikeCaret = ll->positions[offset + 1] - ll->positions[offset];
3140 }
3141 if (widthOverstrikeCaret < 3) // Make sure its visible
3142 widthOverstrikeCaret = 3;
3143
3144 if (xposCaret > 0)
3145 caretWidthOffset = 1; // Move back so overlaps both character cells.
3146 xposCaret += xStart;
3147 if (posDrag.IsValid()) {
3148 /* Dragging text, use a line caret */
3149 rcCaret.left = xposCaret - caretWidthOffset;
3150 rcCaret.right = rcCaret.left + vsDraw.caretWidth;
3151 } else if (inOverstrike) {
3152 /* Overstrike (insert mode), use a modified bar caret */
3153 rcCaret.top = rcCaret.bottom - 2;
3154 rcCaret.left = xposCaret + 1;
3155 rcCaret.right = rcCaret.left + widthOverstrikeCaret - 1;
3156 } else if (vsDraw.caretStyle == CARETSTYLE_BLOCK) {
3157 /* Block caret */
3158 rcCaret.left = xposCaret;
3159 if (!caretAtEOL && !caretAtEOF && (ll->chars[offset] != '\t') && !(IsControlCharacter(ll->chars[offset]))) {
3160 drawBlockCaret = true;
3161 rcCaret.right = xposCaret + widthOverstrikeCaret;
3162 } else {
3163 rcCaret.right = xposCaret + vsDraw.aveCharWidth;
3164 }
3165 } else {
3166 /* Line caret */
3167 rcCaret.left = xposCaret - caretWidthOffset;
3168 rcCaret.right = rcCaret.left + vsDraw.caretWidth;
3169 }
3170 ColourAllocated caretColour = mainCaret ? vsDraw.caretcolour.allocated : vsDraw.additionalCaretColour.allocated;
3171 if (drawBlockCaret) {
3172 DrawBlockCaret(surface, vsDraw, ll, subLine, xStart, offset, posCaret.Position(), rcCaret, caretColour);
3173 } else {
3174 surface->FillRectangle(rcCaret, caretColour);
3175 }
3176 }
3177 }
3178 if (drawDrag)
3179 break;
3180 }
3181}
3182
9e730a78
RD
3183void Editor::Paint(Surface *surfaceWindow, PRectangle rcArea) {
3184 //Platform::DebugPrintf("Paint:%1d (%3d,%3d) ... (%3d,%3d)\n",
3185 // paintingAllText, rcArea.left, rcArea.top, rcArea.right, rcArea.bottom);
3186
9e96e16f 3187 pixmapLine->Release();
9e730a78 3188 RefreshStyleData();
9e730a78
RD
3189 RefreshPixMaps(surfaceWindow);
3190
3191 PRectangle rcClient = GetClientRectangle();
3192 //Platform::DebugPrintf("Client: (%3d,%3d) ... (%3d,%3d) %d\n",
3193 // rcClient.left, rcClient.top, rcClient.right, rcClient.bottom);
9ce192d4
RD
3194
3195 surfaceWindow->SetPalette(&palette, true);
1a2fb4cd 3196 pixmapLine->SetPalette(&palette, !hasFocus);
9ce192d4 3197
9ce192d4
RD
3198 int screenLinePaintFirst = rcArea.top / vs.lineHeight;
3199 // The area to be painted plus one extra line is styled.
65ec6247 3200 // The extra line is to determine when a style change, such as starting a comment flows on to other lines.
d134f170 3201 int lineStyleLast = topLine + (rcArea.bottom - 1) / vs.lineHeight + 1;
9ce192d4
RD
3202 //Platform::DebugPrintf("Paint lines = %d .. %d\n", topLine + screenLinePaintFirst, lineStyleLast);
3203 int endPosPaint = pdoc->Length();
3204 if (lineStyleLast < cs.LinesDisplayed())
9e96e16f 3205 endPosPaint = pdoc->LineStart(cs.DocFromDisplay(lineStyleLast) + 1);
9ce192d4
RD
3206
3207 int xStart = vs.fixedColumnWidth - xOffset;
3208 int ypos = 0;
3209 if (!bufferedDraw)
3210 ypos += screenLinePaintFirst * vs.lineHeight;
3211 int yposScreen = screenLinePaintFirst * vs.lineHeight;
3212
f6bcfd97
BP
3213 // Ensure we are styled as far as we are painting.
3214 pdoc->EnsureStyledTo(endPosPaint);
1a2fb4cd 3215 bool paintAbandonedByStyling = paintState == paintAbandoned;
9ce192d4 3216 if (needUpdateUI) {
9e96e16f
RD
3217 // Deselect palette by selecting a temporary palette
3218 Palette palTemp;
3219 surfaceWindow->SetPalette(&palTemp, true);
3220
9ce192d4
RD
3221 NotifyUpdateUI();
3222 needUpdateUI = false;
9e96e16f 3223
b8193d80
RD
3224 RefreshStyleData();
3225 RefreshPixMaps(surfaceWindow);
9e96e16f
RD
3226 surfaceWindow->SetPalette(&palette, true);
3227 pixmapLine->SetPalette(&palette, !hasFocus);
9ce192d4 3228 }
d134f170 3229
8e54aaed
RD
3230 // Call priority lines wrap on a window of lines which are likely
3231 // to rendered with the following paint (that is wrap the visible
3232 // lines first).
3233 int startLineToWrap = cs.DocFromDisplay(topLine) - 5;
3234 if (startLineToWrap < 0)
9e96e16f 3235 startLineToWrap = 0;
8e54aaed
RD
3236 if (WrapLines(false, startLineToWrap)) {
3237 // The wrapping process has changed the height of some lines so
3238 // abandon this paint for a complete repaint.
9e730a78
RD
3239 if (AbandonPaint()) {
3240 return;
3241 }
88a8b04e 3242 RefreshPixMaps(surfaceWindow); // In case pixmaps invalidated by scrollbar change
9e730a78 3243 }
88a8b04e 3244 PLATFORM_ASSERT(pixmapSelPattern->Initialised());
9e730a78 3245
8e54aaed
RD
3246 PaintSelMargin(surfaceWindow, rcArea);
3247
9ce192d4
RD
3248 PRectangle rcRightMargin = rcClient;
3249 rcRightMargin.left = rcRightMargin.right - vs.rightMarginWidth;
3250 if (rcArea.Intersects(rcRightMargin)) {
d134f170 3251 surfaceWindow->FillRectangle(rcRightMargin, vs.styles[STYLE_DEFAULT].back.allocated);
9ce192d4
RD
3252 }
3253
3254 if (paintState == paintAbandoned) {
f6bcfd97 3255 // Either styling or NotifyUpdateUI noticed that painting is needed
9ce192d4
RD
3256 // outside the current painting rectangle
3257 //Platform::DebugPrintf("Abandoning paint\n");
1a2fb4cd
RD
3258 if (wrapState != eWrapNone) {
3259 if (paintAbandonedByStyling) {
e4f0b986 3260 // Styling has spilled over a line end, such as occurs by starting a multiline
1a2fb4cd
RD
3261 // comment. The width of subsequent text may have changed, so rewrap.
3262 NeedWrapping(cs.DocFromDisplay(topLine));
3263 }
3264 }
65ec6247 3265 return;
9ce192d4
RD
3266 }
3267 //Platform::DebugPrintf("start display %d, offset = %d\n", pdoc->Length(), xOffset);
3268
65ec6247 3269 // Do the painting
9ce192d4
RD
3270 if (rcArea.right > vs.fixedColumnWidth) {
3271
f6bcfd97 3272 Surface *surface = surfaceWindow;
9ce192d4 3273 if (bufferedDraw) {
1a2fb4cd 3274 surface = pixmapLine;
88a8b04e 3275 PLATFORM_ASSERT(pixmapLine->Initialised());
9ce192d4 3276 }
1a2fb4cd 3277 surface->SetUnicodeMode(IsUnicodeMode());
9e730a78 3278 surface->SetDBCSMode(CodePage());
9ce192d4
RD
3279
3280 int visibleLine = topLine + screenLinePaintFirst;
9ce192d4 3281
9e96e16f
RD
3282 SelectionPosition posCaret = sel.RangeMain().caret;
3283 if (posDrag.IsValid())
9ce192d4 3284 posCaret = posDrag;
9e96e16f 3285 int lineCaret = pdoc->LineFromPosition(posCaret.Position());
9ce192d4
RD
3286
3287 // Remove selection margin from drawing area so text will not be drawn
3288 // on it in unbuffered mode.
3289 PRectangle rcTextArea = rcClient;
3290 rcTextArea.left = vs.fixedColumnWidth;
3291 rcTextArea.right -= vs.rightMarginWidth;
3292 surfaceWindow->SetClip(rcTextArea);
65ec6247
RD
3293
3294 // Loop on visible lines
1a2fb4cd
RD
3295 //double durLayout = 0.0;
3296 //double durPaint = 0.0;
3297 //double durCopy = 0.0;
3298 //ElapsedTime etWhole;
3299 int lineDocPrevious = -1; // Used to avoid laying out one document line multiple times
9e730a78 3300 AutoLineLayout ll(llc, 0);
d134f170 3301 while (visibleLine < cs.LinesDisplayed() && yposScreen < rcArea.bottom) {
1a2fb4cd
RD
3302
3303 int lineDoc = cs.DocFromDisplay(visibleLine);
1a2fb4cd
RD
3304 // Only visible lines should be handled by the code within the loop
3305 PLATFORM_ASSERT(cs.GetVisible(lineDoc));
3306 int lineStartSet = cs.DisplayFromDoc(lineDoc);
3307 int subLine = visibleLine - lineStartSet;
9ce192d4 3308
65ec6247
RD
3309 // Copy this line and its styles from the document into local arrays
3310 // and determine the x position at which each character starts.
1a2fb4cd
RD
3311 //ElapsedTime et;
3312 if (lineDoc != lineDocPrevious) {
591d01be 3313 ll.Set(0);
9e730a78 3314 ll.Set(RetrieveLineLayout(lineDoc));
1a2fb4cd
RD
3315 LayoutLine(lineDoc, surface, vs, ll, wrapWidth);
3316 lineDocPrevious = lineDoc;
65ec6247 3317 }
1a2fb4cd
RD
3318 //durLayout += et.Duration(true);
3319
3320 if (ll) {
1a2fb4cd
RD
3321 ll->containsCaret = lineDoc == lineCaret;
3322 if (hideSelection) {
1a2fb4cd 3323 ll->containsCaret = false;
9ce192d4 3324 }
e4f0b986 3325
9e730a78
RD
3326 GetHotSpotRange(ll->hsStart, ll->hsEnd);
3327
1a2fb4cd
RD
3328 PRectangle rcLine = rcClient;
3329 rcLine.top = ypos;
3330 rcLine.bottom = ypos + vs.lineHeight;
3331
3332 Range rangeLine(pdoc->LineStart(lineDoc), pdoc->LineStart(lineDoc + 1));
3333 // Highlight the current braces if any
3334 ll->SetBracesHighlight(rangeLine, braces, static_cast<char>(bracesMatchStyle),
7e0c58e9 3335 highlightGuideColumn * vs.spaceWidth);
e4f0b986 3336
1a2fb4cd
RD
3337 // Draw the line
3338 DrawLine(surface, vs, lineDoc, visibleLine, xStart, rcLine, ll, subLine);
3339 //durPaint += et.Duration(true);
e4f0b986 3340
8e54aaed 3341 // Restore the previous styles for the brace highlights in case layout is in cache.
1a2fb4cd
RD
3342 ll->RestoreBracesHighlight(rangeLine, braces);
3343
3344 bool expanded = cs.GetExpanded(lineDoc);
9e96e16f
RD
3345 // Paint the line above the fold
3346 if ((expanded && (foldFlags & SC_FOLDFLAG_LINEBEFORE_EXPANDED))
3347 ||
3348 (!expanded && (foldFlags & SC_FOLDFLAG_LINEBEFORE_CONTRACTED))) {
3349 if (pdoc->GetLevel(lineDoc) & SC_FOLDLEVELHEADERFLAG) {
1a2fb4cd
RD
3350 PRectangle rcFoldLine = rcLine;
3351 rcFoldLine.bottom = rcFoldLine.top + 1;
3352 surface->FillRectangle(rcFoldLine, vs.styles[STYLE_DEFAULT].fore.allocated);
3353 }
9e96e16f
RD
3354 }
3355 // Paint the line below the fold
3356 if ((expanded && (foldFlags & SC_FOLDFLAG_LINEAFTER_EXPANDED))
3357 ||
3358 (!expanded && (foldFlags & SC_FOLDFLAG_LINEAFTER_CONTRACTED))) {
3359 if (pdoc->GetLevel(lineDoc) & SC_FOLDLEVELHEADERFLAG) {
1a2fb4cd
RD
3360 PRectangle rcFoldLine = rcLine;
3361 rcFoldLine.top = rcFoldLine.bottom - 1;
3362 surface->FillRectangle(rcFoldLine, vs.styles[STYLE_DEFAULT].fore.allocated);
3363 }
d134f170 3364 }
e4f0b986 3365
9e96e16f 3366 DrawCarets(surface, vs, lineDoc, xStart, rcLine, ll, subLine);
e4f0b986 3367
9ce192d4
RD
3368 if (bufferedDraw) {
3369 Point from(vs.fixedColumnWidth, 0);
3370 PRectangle rcCopyArea(vs.fixedColumnWidth, yposScreen,
7e0c58e9 3371 rcClient.right, yposScreen + vs.lineHeight);
1a2fb4cd 3372 surfaceWindow->Copy(rcCopyArea, from, *pixmapLine);
9ce192d4 3373 }
9e96e16f
RD
3374
3375 lineWidthMaxSeen = Platform::Maximum(
3376 lineWidthMaxSeen, ll->positions[ll->numCharsInLine]);
1a2fb4cd 3377 //durCopy += et.Duration(true);
9ce192d4 3378 }
e4f0b986 3379
9ce192d4
RD
3380 if (!bufferedDraw) {
3381 ypos += vs.lineHeight;
3382 }
3383
3384 yposScreen += vs.lineHeight;
3385 visibleLine++;
7e0c58e9 3386
9ce192d4 3387 //gdk_flush();
9ce192d4 3388 }
1e9bafca 3389 ll.Set(0);
1a2fb4cd
RD
3390 //if (durPaint < 0.00000001)
3391 // durPaint = 0.00000001;
d134f170 3392
65ec6247 3393 // Right column limit indicator
9ce192d4
RD
3394 PRectangle rcBeyondEOF = rcClient;
3395 rcBeyondEOF.left = vs.fixedColumnWidth;
3396 rcBeyondEOF.right = rcBeyondEOF.right;
3397 rcBeyondEOF.top = (cs.LinesDisplayed() - topLine) * vs.lineHeight;
3398 if (rcBeyondEOF.top < rcBeyondEOF.bottom) {
3399 surfaceWindow->FillRectangle(rcBeyondEOF, vs.styles[STYLE_DEFAULT].back.allocated);
d134f170 3400 if (vs.edgeState == EDGE_LINE) {
9ce192d4
RD
3401 int edgeX = theEdge * vs.spaceWidth;
3402 rcBeyondEOF.left = edgeX + xStart;
3403 rcBeyondEOF.right = rcBeyondEOF.left + 1;
3404 surfaceWindow->FillRectangle(rcBeyondEOF, vs.edgecolour.allocated);
3405 }
3406 }
1a2fb4cd 3407 //Platform::DebugPrintf(
e4f0b986 3408 //"Layout:%9.6g Paint:%9.6g Ratio:%9.6g Copy:%9.6g Total:%9.6g\n",
1a2fb4cd 3409 //durLayout, durPaint, durLayout / durPaint, durCopy, etWhole.Duration());
65ec6247 3410 NotifyPainted();
9ce192d4
RD
3411 }
3412}
3413
3414// Space (3 space characters) between line numbers and text when printing.
3415#define lineNumberPrintSpace " "
3416
1a2fb4cd 3417ColourDesired InvertedLight(ColourDesired orig) {
d134f170
RD
3418 unsigned int r = orig.GetRed();
3419 unsigned int g = orig.GetGreen();
3420 unsigned int b = orig.GetBlue();
3421 unsigned int l = (r + g + b) / 3; // There is a better calculation for this that matches human eye
3422 unsigned int il = 0xff - l;
3423 if (l == 0)
1a2fb4cd 3424 return ColourDesired(0xff, 0xff, 0xff);
d134f170
RD
3425 r = r * il / l;
3426 g = g * il / l;
3427 b = b * il / l;
1a2fb4cd 3428 return ColourDesired(Platform::Minimum(r, 0xff), Platform::Minimum(g, 0xff), Platform::Minimum(b, 0xff));
d134f170
RD
3429}
3430
9ce192d4
RD
3431// This is mostly copied from the Paint method but with some things omitted
3432// such as the margin markers, line numbers, selection and caret
3433// Should be merged back into a combined Draw method.
9e96e16f 3434long Editor::FormatRange(bool draw, Sci_RangeToFormat *pfr) {
9ce192d4
RD
3435 if (!pfr)
3436 return 0;
3437
9e730a78 3438 AutoSurface surface(pfr->hdc, this);
1a2fb4cd
RD
3439 if (!surface)
3440 return 0;
9e730a78 3441 AutoSurface surfaceMeasure(pfr->hdcTarget, this);
1a2fb4cd
RD
3442 if (!surfaceMeasure) {
3443 return 0;
3444 }
d134f170 3445
7e0c58e9
RD
3446 // Can't use measurements cached for screen
3447 posCache.Clear();
3448
9ce192d4 3449 ViewStyle vsPrint(vs);
d134f170 3450
9ce192d4
RD
3451 // Modify the view style for printing as do not normally want any of the transient features to be printed
3452 // Printing supports only the line number margin.
3453 int lineNumberIndex = -1;
d134f170 3454 for (int margin = 0; margin < ViewStyle::margins; margin++) {
b8193d80 3455 if ((vsPrint.ms[margin].style == SC_MARGIN_NUMBER) && (vsPrint.ms[margin].width > 0)) {
9ce192d4
RD
3456 lineNumberIndex = margin;
3457 } else {
3458 vsPrint.ms[margin].width = 0;
3459 }
3460 }
3461 vsPrint.showMarkedLines = false;
3462 vsPrint.fixedColumnWidth = 0;
d134f170 3463 vsPrint.zoomLevel = printMagnification;
7e0c58e9 3464 vsPrint.viewIndentationGuides = ivNone;
9ce192d4
RD
3465 // Don't show the selection when printing
3466 vsPrint.selbackset = false;
3467 vsPrint.selforeset = false;
b8193d80 3468 vsPrint.selAlpha = SC_ALPHA_NOALPHA;
9e96e16f 3469 vsPrint.selAdditionalAlpha = SC_ALPHA_NOALPHA;
f114b858
RD
3470 vsPrint.whitespaceBackgroundSet = false;
3471 vsPrint.whitespaceForegroundSet = false;
65ec6247
RD
3472 vsPrint.showCaretLineBackground = false;
3473
3474 // Set colours for printing according to users settings
7e0c58e9 3475 for (size_t sty = 0;sty < vsPrint.stylesSize;sty++) {
d134f170
RD
3476 if (printColourMode == SC_PRINT_INVERTLIGHT) {
3477 vsPrint.styles[sty].fore.desired = InvertedLight(vsPrint.styles[sty].fore.desired);
3478 vsPrint.styles[sty].back.desired = InvertedLight(vsPrint.styles[sty].back.desired);
3479 } else if (printColourMode == SC_PRINT_BLACKONWHITE) {
1a2fb4cd
RD
3480 vsPrint.styles[sty].fore.desired = ColourDesired(0, 0, 0);
3481 vsPrint.styles[sty].back.desired = ColourDesired(0xff, 0xff, 0xff);
65ec6247 3482 } else if (printColourMode == SC_PRINT_COLOURONWHITE) {
1a2fb4cd 3483 vsPrint.styles[sty].back.desired = ColourDesired(0xff, 0xff, 0xff);
65ec6247
RD
3484 } else if (printColourMode == SC_PRINT_COLOURONWHITEDEFAULTBG) {
3485 if (sty <= STYLE_DEFAULT) {
1a2fb4cd 3486 vsPrint.styles[sty].back.desired = ColourDesired(0xff, 0xff, 0xff);
65ec6247 3487 }
d134f170
RD
3488 }
3489 }
65ec6247 3490 // White background for the line numbers
1a2fb4cd 3491 vsPrint.styles[STYLE_LINENUMBER].back.desired = ColourDesired(0xff, 0xff, 0xff);
d134f170 3492
9ce192d4 3493 vsPrint.Refresh(*surfaceMeasure);
9ce192d4
RD
3494 // Determining width must hapen after fonts have been realised in Refresh
3495 int lineNumberWidth = 0;
3496 if (lineNumberIndex >= 0) {
9e730a78 3497 lineNumberWidth = surfaceMeasure->WidthText(vsPrint.styles[STYLE_LINENUMBER].font,
7e0c58e9 3498 "99999" lineNumberPrintSpace, 5 + istrlen(lineNumberPrintSpace));
9ce192d4 3499 vsPrint.ms[lineNumberIndex].width = lineNumberWidth;
7e0c58e9 3500 vsPrint.Refresh(*surfaceMeasure); // Recalculate fixedColumnWidth
9ce192d4 3501 }
7e0c58e9
RD
3502 // Ensure colours are set up
3503 vsPrint.RefreshColourPalette(palette, true);
3504 vsPrint.RefreshColourPalette(palette, false);
9ce192d4
RD
3505
3506 int linePrintStart = pdoc->LineFromPosition(pfr->chrg.cpMin);
3507 int linePrintLast = linePrintStart + (pfr->rc.bottom - pfr->rc.top) / vsPrint.lineHeight - 1;
3508 if (linePrintLast < linePrintStart)
3509 linePrintLast = linePrintStart;
9e730a78 3510 int linePrintMax = pdoc->LineFromPosition(pfr->chrg.cpMax);
9ce192d4
RD
3511 if (linePrintLast > linePrintMax)
3512 linePrintLast = linePrintMax;
3513 //Platform::DebugPrintf("Formatting lines=[%0d,%0d,%0d] top=%0d bottom=%0d line=%0d %0d\n",
9e730a78
RD
3514 // linePrintStart, linePrintLast, linePrintMax, pfr->rc.top, pfr->rc.bottom, vsPrint.lineHeight,
3515 // surfaceMeasure->Height(vsPrint.styles[STYLE_LINENUMBER].font));
9ce192d4
RD
3516 int endPosPrint = pdoc->Length();
3517 if (linePrintLast < pdoc->LinesTotal())
3518 endPosPrint = pdoc->LineStart(linePrintLast + 1);
3519
f6bcfd97
BP
3520 // Ensure we are styled to where we are formatting.
3521 pdoc->EnsureStyledTo(endPosPrint);
3522
7e0c58e9 3523 int xStart = vsPrint.fixedColumnWidth + pfr->rc.left;
9ce192d4 3524 int ypos = pfr->rc.top;
9ce192d4 3525
9e730a78
RD
3526 int lineDoc = linePrintStart;
3527
3528 int nPrintPos = pfr->chrg.cpMin;
3529 int visibleLine = 0;
7e0c58e9 3530 int widthPrint = pfr->rc.Width() - vsPrint.fixedColumnWidth;
9e730a78
RD
3531 if (printWrapState == eWrapNone)
3532 widthPrint = LineLayout::wrapWidthInfinite;
3533
3534 while (lineDoc <= linePrintLast && ypos < pfr->rc.bottom) {
3535
3536 // When printing, the hdc and hdcTarget may be the same, so
3537 // changing the state of surfaceMeasure may change the underlying
3538 // state of surface. Therefore, any cached state is discarded before
3539 // using each surface.
3540 surfaceMeasure->FlushCachedState();
3541
3542 // Copy this line and its styles from the document into local arrays
3543 // and determine the x position at which each character starts.
3544 LineLayout ll(8000);
3545 LayoutLine(lineDoc, surfaceMeasure, vsPrint, &ll, widthPrint);
3546
9e730a78
RD
3547 ll.containsCaret = false;
3548
3549 PRectangle rcLine;
7e0c58e9 3550 rcLine.left = pfr->rc.left;
9e730a78
RD
3551 rcLine.top = ypos;
3552 rcLine.right = pfr->rc.right - 1;
3553 rcLine.bottom = ypos + vsPrint.lineHeight;
3554
3555 // When document line is wrapped over multiple display lines, find where
3556 // to start printing from to ensure a particular position is on the first
3557 // line of the page.
3558 if (visibleLine == 0) {
3559 int startWithinLine = nPrintPos - pdoc->LineStart(lineDoc);
3560 for (int iwl = 0; iwl < ll.lines - 1; iwl++) {
3561 if (ll.LineStart(iwl) <= startWithinLine && ll.LineStart(iwl + 1) >= startWithinLine) {
3562 visibleLine = -iwl;
3563 }
3564 }
65ec6247 3565
9e730a78
RD
3566 if (ll.lines > 1 && startWithinLine >= ll.LineStart(ll.lines - 1)) {
3567 visibleLine = -(ll.lines - 1);
9ce192d4 3568 }
9e730a78 3569 }
f6bcfd97 3570
9e730a78
RD
3571 if (draw && lineNumberWidth &&
3572 (ypos + vsPrint.lineHeight <= pfr->rc.bottom) &&
3573 (visibleLine >= 0)) {
3574 char number[100];
3575 sprintf(number, "%d" lineNumberPrintSpace, lineDoc + 1);
3576 PRectangle rcNumber = rcLine;
3577 rcNumber.right = rcNumber.left + lineNumberWidth;
3578 // Right justify
7e0c58e9
RD
3579 rcNumber.left = rcNumber.right - surfaceMeasure->WidthText(
3580 vsPrint.styles[STYLE_LINENUMBER].font, number, istrlen(number));
f6bcfd97 3581 surface->FlushCachedState();
9e730a78 3582 surface->DrawTextNoClip(rcNumber, vsPrint.styles[STYLE_LINENUMBER].font,
7e0c58e9
RD
3583 ypos + vsPrint.maxAscent, number, istrlen(number),
3584 vsPrint.styles[STYLE_LINENUMBER].fore.allocated,
3585 vsPrint.styles[STYLE_LINENUMBER].back.allocated);
9e730a78
RD
3586 }
3587
3588 // Draw the line
3589 surface->FlushCachedState();
3590
3591 for (int iwl = 0; iwl < ll.lines; iwl++) {
3592 if (ypos + vsPrint.lineHeight <= pfr->rc.bottom) {
3593 if (visibleLine >= 0) {
3594 if (draw) {
3595 rcLine.top = ypos;
3596 rcLine.bottom = ypos + vsPrint.lineHeight;
3597 DrawLine(surface, vsPrint, lineDoc, visibleLine, xStart, rcLine, &ll, iwl);
3598 }
3599 ypos += vsPrint.lineHeight;
3600 }
3601 visibleLine++;
3602 if (iwl == ll.lines - 1)
3603 nPrintPos = pdoc->LineStart(lineDoc + 1);
3604 else
3605 nPrintPos += ll.LineStart(iwl + 1) - ll.LineStart(iwl);
3606 }
9ce192d4 3607 }
9e730a78
RD
3608
3609 ++lineDoc;
9ce192d4
RD
3610 }
3611
7e0c58e9
RD
3612 // Clear cache so measurements are not used for screen
3613 posCache.Clear();
3614
9e730a78 3615 return nPrintPos;
9ce192d4
RD
3616}
3617
a834585d
RD
3618int Editor::TextWidth(int style, const char *text) {
3619 RefreshStyleData();
9e730a78 3620 AutoSurface surface(this);
a834585d 3621 if (surface) {
88a8b04e 3622 return surface->WidthText(vs.styles[style].font, text, istrlen(text));
a834585d
RD
3623 } else {
3624 return 1;
3625 }
3626}
3627
f6bcfd97 3628// Empty method is overridden on GTK+ to show / hide scrollbars
9e730a78 3629void Editor::ReconfigureScrollBars() {}
d134f170 3630
1a2fb4cd 3631void Editor::SetScrollBars() {
9ce192d4
RD
3632 RefreshStyleData();
3633
a834585d
RD
3634 int nMax = MaxScrollPos();
3635 int nPage = LinesOnScreen();
3636 bool modified = ModifyScrollBars(nMax + nPage - 1, nPage);
1e9bafca
RD
3637 if (modified) {
3638 DwellEnd(true);
3639 }
9ce192d4
RD
3640
3641 // TODO: ensure always showing as many lines as possible
3642 // May not be, if, for example, window made larger
3643 if (topLine > MaxScrollPos()) {
3644 SetTopLine(Platform::Clamp(topLine, 0, MaxScrollPos()));
3645 SetVerticalScrollPos();
3646 Redraw();
3647 }
a834585d
RD
3648 if (modified) {
3649 if (!AbandonPaint())
3650 Redraw();
3651 }
9ce192d4
RD
3652 //Platform::DebugPrintf("end max = %d page = %d\n", nMax, nPage);
3653}
3654
1a2fb4cd
RD
3655void Editor::ChangeSize() {
3656 DropGraphics();
3657 SetScrollBars();
3658 if (wrapState != eWrapNone) {
3659 PRectangle rcTextArea = GetClientRectangle();
3660 rcTextArea.left = vs.fixedColumnWidth;
3661 rcTextArea.right -= vs.rightMarginWidth;
3662 if (wrapWidth != rcTextArea.Width()) {
3663 NeedWrapping();
3664 Redraw();
3665 }
3666 }
9ce192d4
RD
3667}
3668
9e96e16f
RD
3669int Editor::InsertSpace(int position, unsigned int spaces) {
3670 if (spaces > 0) {
3671 std::string spaceText(spaces, ' ');
3672 pdoc->InsertString(position, spaceText.c_str(), spaces);
3673 position += spaces;
3674 }
3675 return position;
3676}
3677
9ce192d4 3678void Editor::AddChar(char ch) {
f6bcfd97
BP
3679 char s[2];
3680 s[0] = ch;
3681 s[1] = '\0';
3682 AddCharUTF(s, 1);
3683}
3684
9e96e16f
RD
3685void Editor::FilterSelections() {
3686 if (!additionalSelectionTyping && (sel.Count() > 1)) {
3687 SelectionRange rangeOnly = sel.RangeMain();
3688 InvalidateSelection(rangeOnly, true);
3689 sel.SetSelection(rangeOnly);
3690 }
3691}
3692
7e0c58e9 3693// AddCharUTF inserts an array of bytes which may or may not be in UTF-8.
1a2fb4cd 3694void Editor::AddCharUTF(char *s, unsigned int len, bool treatAsDBCS) {
9e96e16f
RD
3695 FilterSelections();
3696 {
3697 UndoGroup ug(pdoc, (sel.Count() > 1) || !sel.Empty() || inOverstrike);
3698 for (size_t r=0; r<sel.Count(); r++) {
3699 if (!RangeContainsProtected(sel.Range(r).Start().Position(),
3700 sel.Range(r).End().Position())) {
3701 int positionInsert = sel.Range(r).Start().Position();
3702 if (!sel.Range(r).Empty()) {
3703 if (sel.Range(r).Length()) {
3704 pdoc->DeleteChars(positionInsert, sel.Range(r).Length());
3705 sel.Range(r).ClearVirtualSpace();
3706 } else {
3707 // Range is all virtual so collapse to start of virtual space
3708 sel.Range(r).MinimizeVirtualSpace();
3709 }
3710 } else if (inOverstrike) {
3711 if (positionInsert < pdoc->Length()) {
3712 if (!IsEOLChar(pdoc->CharAt(positionInsert))) {
3713 pdoc->DelChar(positionInsert);
3714 sel.Range(r).ClearVirtualSpace();
3715 }
3716 }
3717 }
3718 positionInsert = InsertSpace(positionInsert, sel.Range(r).caret.VirtualSpace());
3719 if (pdoc->InsertString(positionInsert, s, len)) {
3720 sel.Range(r).caret.SetPosition(positionInsert + len);
3721 sel.Range(r).anchor.SetPosition(positionInsert + len);
3722 }
3723 sel.Range(r).ClearVirtualSpace();
3724 // If in wrap mode rewrap current line so EnsureCaretVisible has accurate information
3725 if (wrapState != eWrapNone) {
3726 AutoSurface surface(this);
3727 if (surface) {
3728 WrapOneLine(surface, pdoc->LineFromPosition(positionInsert));
3729 }
3730 }
9ce192d4
RD
3731 }
3732 }
3733 }
7e0c58e9 3734 if (wrapState != eWrapNone) {
7e0c58e9
RD
3735 SetScrollBars();
3736 }
9e96e16f
RD
3737 ThinRectangularRange();
3738 // If in wrap mode rewrap current line so EnsureCaretVisible has accurate information
9ce192d4 3739 EnsureCaretVisible();
d134f170
RD
3740 // Avoid blinking during rapid typing:
3741 ShowCaretAtCurrentPosition();
1e9bafca
RD
3742 if (!caretSticky) {
3743 SetLastXChosen();
3744 }
65ec6247 3745
1a2fb4cd 3746 if (treatAsDBCS) {
e4f0b986 3747 NotifyChar((static_cast<unsigned char>(s[0]) << 8) |
7e0c58e9 3748 static_cast<unsigned char>(s[1]));
65ec6247 3749 } else {
1a2fb4cd
RD
3750 int byte = static_cast<unsigned char>(s[0]);
3751 if ((byte < 0xC0) || (1 == len)) {
3752 // Handles UTF-8 characters between 0x01 and 0x7F and single byte
3753 // characters when not in UTF-8 mode.
3754 // Also treats \0 and naked trail bytes 0x80 to 0xBF as valid
3755 // characters representing themselves.
3756 } else {
3757 // Unroll 1 to 3 byte UTF-8 sequences. See reference data at:
3758 // http://www.cl.cam.ac.uk/~mgk25/unicode.html
3759 // http://www.cl.cam.ac.uk/~mgk25/ucs/examples/UTF-8-test.txt
3760 if (byte < 0xE0) {
3761 int byte2 = static_cast<unsigned char>(s[1]);
3762 if ((byte2 & 0xC0) == 0x80) {
3763 // Two-byte-character lead-byte followed by a trail-byte.
3764 byte = (((byte & 0x1F) << 6) | (byte2 & 0x3F));
3765 }
3766 // A two-byte-character lead-byte not followed by trail-byte
3767 // represents itself.
3768 } else if (byte < 0xF0) {
3769 int byte2 = static_cast<unsigned char>(s[1]);
3770 int byte3 = static_cast<unsigned char>(s[2]);
3771 if (((byte2 & 0xC0) == 0x80) && ((byte3 & 0xC0) == 0x80)) {
3772 // Three-byte-character lead byte followed by two trail bytes.
3773 byte = (((byte & 0x0F) << 12) | ((byte2 & 0x3F) << 6) |
9e730a78 3774 (byte3 & 0x3F));
1a2fb4cd
RD
3775 }
3776 // A three-byte-character lead-byte not followed by two trail-bytes
3777 // represents itself.
65ec6247 3778 }
65ec6247 3779 }
1a2fb4cd 3780 NotifyChar(byte);
65ec6247 3781 }
9e96e16f
RD
3782
3783 if (recordingMacro) {
3784 NotifyMacroRecord(SCI_REPLACESEL, 0, reinterpret_cast<sptr_t>(s));
3785 }
9ce192d4
RD
3786}
3787
3788void Editor::ClearSelection() {
9e96e16f
RD
3789 if (!sel.IsRectangular())
3790 FilterSelections();
3791 UndoGroup ug(pdoc);
3792 for (size_t r=0; r<sel.Count(); r++) {
3793 if (!sel.Range(r).Empty()) {
3794 if (!RangeContainsProtected(sel.Range(r).Start().Position(),
3795 sel.Range(r).End().Position())) {
3796 pdoc->DeleteChars(sel.Range(r).Start().Position(),
3797 sel.Range(r).Length());
3798 sel.Range(r) = sel.Range(r).Start();
8e54aaed 3799 }
9ce192d4 3800 }
9ce192d4 3801 }
9e96e16f
RD
3802 ThinRectangularRange();
3803 sel.RemoveDuplicates();
3804 ClaimSelection();
9ce192d4
RD
3805}
3806
3807void Editor::ClearAll() {
9e96e16f
RD
3808 {
3809 UndoGroup ug(pdoc);
3810 if (0 != pdoc->Length()) {
3811 pdoc->DeleteChars(0, pdoc->Length());
3812 }
3813 if (!pdoc->IsReadOnly()) {
3814 cs.Clear();
3815 pdoc->AnnotationClearAll();
3816 pdoc->MarginClearAll();
3817 }
a834585d 3818 }
9e96e16f 3819 sel.Clear();
9ce192d4
RD
3820 SetTopLine(0);
3821 SetVerticalScrollPos();
1e9bafca 3822 InvalidateStyleRedraw();
9ce192d4
RD
3823}
3824
d134f170 3825void Editor::ClearDocumentStyle() {
9e96e16f
RD
3826 Decoration *deco = pdoc->decorations.root;
3827 while (deco) {
3828 // Save next in case deco deleted
3829 Decoration *decoNext = deco->next;
3830 if (deco->indicator < INDIC_CONTAINER) {
3831 pdoc->decorations.SetCurrentIndicator(deco->indicator);
3832 pdoc->DecorationFillRange(0, 0, pdoc->Length());
3833 }
3834 deco = decoNext;
3835 }
d134f170
RD
3836 pdoc->StartStyling(0, '\377');
3837 pdoc->SetStyleFor(pdoc->Length(), 0);
3838 cs.ShowAll();
3839 pdoc->ClearLevels();
3840}
3841
9e96e16f
RD
3842void Editor::CopyAllowLine() {
3843 SelectionText selectedText;
3844 CopySelectionRange(&selectedText, true);
3845 CopyToClipboard(selectedText);
3846}
3847
9ce192d4 3848void Editor::Cut() {
7e0c58e9 3849 pdoc->CheckReadOnly();
9e730a78 3850 if (!pdoc->IsReadOnly() && !SelectionContainsProtected()) {
65ec6247
RD
3851 Copy();
3852 ClearSelection();
3853 }
9ce192d4
RD
3854}
3855
9e96e16f 3856void Editor::PasteRectangular(SelectionPosition pos, const char *ptr, int len) {
9e730a78 3857 if (pdoc->IsReadOnly() || SelectionContainsProtected()) {
65ec6247
RD
3858 return;
3859 }
9e96e16f
RD
3860 sel.Clear();
3861 sel.RangeMain() = SelectionRange(pos);
3862 int line = pdoc->LineFromPosition(sel.MainCaret());
3863 UndoGroup ug(pdoc);
3864 sel.RangeMain().caret = SelectionPosition(
3865 InsertSpace(sel.RangeMain().caret.Position(), sel.RangeMain().caret.VirtualSpace()));
3866 int xInsert = XFromPosition(sel.RangeMain().caret);
9ce192d4 3867 bool prevCr = false;
9e96e16f
RD
3868 while ((len > 0) && IsEOLChar(ptr[len-1]))
3869 len--;
d134f170 3870 for (int i = 0; i < len; i++) {
9e730a78 3871 if (IsEOLChar(ptr[i])) {
d134f170 3872 if ((ptr[i] == '\r') || (!prevCr))
9ce192d4
RD
3873 line++;
3874 if (line >= pdoc->LinesTotal()) {
3875 if (pdoc->eolMode != SC_EOL_LF)
3876 pdoc->InsertChar(pdoc->Length(), '\r');
3877 if (pdoc->eolMode != SC_EOL_CR)
3878 pdoc->InsertChar(pdoc->Length(), '\n');
3879 }
65ec6247 3880 // Pad the end of lines with spaces if required
9e96e16f
RD
3881 sel.RangeMain().caret.SetPosition(PositionFromLineX(line, xInsert));
3882 if ((XFromPosition(sel.MainCaret()) < xInsert) && (i + 1 < len)) {
3883 while (XFromPosition(sel.MainCaret()) < xInsert) {
3884 pdoc->InsertChar(sel.MainCaret(), ' ');
3885 sel.RangeMain().caret.Add(1);
65ec6247 3886 }
65ec6247 3887 }
9ce192d4
RD
3888 prevCr = ptr[i] == '\r';
3889 } else {
9e96e16f
RD
3890 pdoc->InsertString(sel.MainCaret(), ptr + i, 1);
3891 sel.RangeMain().caret.Add(1);
9ce192d4
RD
3892 prevCr = false;
3893 }
3894 }
1a2fb4cd 3895 SetEmptySelection(pos);
9ce192d4
RD
3896}
3897
65ec6247 3898bool Editor::CanPaste() {
9e730a78 3899 return !pdoc->IsReadOnly() && !SelectionContainsProtected();
65ec6247
RD
3900}
3901
9ce192d4 3902void Editor::Clear() {
9e96e16f
RD
3903 UndoGroup ug(pdoc);
3904 // If multiple selections, don't delete EOLS
3905 if (sel.Empty()) {
3906 for (size_t r=0; r<sel.Count(); r++) {
3907 if (!RangeContainsProtected(sel.Range(r).caret.Position(), sel.Range(r).caret.Position() + 1)) {
3908 if (sel.Range(r).Start().VirtualSpace()) {
3909 if (sel.Range(r).anchor < sel.Range(r).caret)
3910 sel.Range(r) = SelectionPosition(InsertSpace(sel.Range(r).anchor.Position(), sel.Range(r).anchor.VirtualSpace()));
3911 else
3912 sel.Range(r) = SelectionPosition(InsertSpace(sel.Range(r).caret.Position(), sel.Range(r).caret.VirtualSpace()));
3913 }
3914 if ((sel.Count() == 1) || !IsEOLChar(pdoc->CharAt(sel.Range(r).caret.Position()))) {
3915 pdoc->DelChar(sel.Range(r).caret.Position());
3916 sel.Range(r).ClearVirtualSpace();
3917 } // else multiple selection so don't eat line ends
3918 } else {
3919 sel.Range(r).ClearVirtualSpace();
3920 }
9e730a78 3921 }
9ce192d4
RD
3922 } else {
3923 ClearSelection();
3924 }
9e96e16f 3925 sel.RemoveDuplicates();
9ce192d4
RD
3926}
3927
3928void Editor::SelectAll() {
9e96e16f 3929 sel.Clear();
9ce192d4
RD
3930 SetSelection(0, pdoc->Length());
3931 Redraw();
3932}
3933
3934void Editor::Undo() {
3935 if (pdoc->CanUndo()) {
65ec6247 3936 InvalidateCaret();
9ce192d4 3937 int newPos = pdoc->Undo();
1e9bafca
RD
3938 if (newPos >= 0)
3939 SetEmptySelection(newPos);
9ce192d4
RD
3940 EnsureCaretVisible();
3941 }
3942}
3943
3944void Editor::Redo() {
3945 if (pdoc->CanRedo()) {
3946 int newPos = pdoc->Redo();
1e9bafca
RD
3947 if (newPos >= 0)
3948 SetEmptySelection(newPos);
9ce192d4
RD
3949 EnsureCaretVisible();
3950 }
3951}
3952
3953void Editor::DelChar() {
9e96e16f
RD
3954 if (!RangeContainsProtected(sel.MainCaret(), sel.MainCaret() + 1)) {
3955 pdoc->DelChar(sel.MainCaret());
9e730a78 3956 }
d134f170
RD
3957 // Avoid blinking during rapid typing:
3958 ShowCaretAtCurrentPosition();
9ce192d4
RD
3959}
3960
1a2fb4cd 3961void Editor::DelCharBack(bool allowLineStartDeletion) {
9e96e16f
RD
3962 if (!sel.IsRectangular())
3963 FilterSelections();
3964 if (sel.IsRectangular())
3965 allowLineStartDeletion = false;
3966 UndoGroup ug(pdoc, (sel.Count() > 1) || !sel.Empty());
3967 if (sel.Empty()) {
3968 for (size_t r=0; r<sel.Count(); r++) {
3969 if (!RangeContainsProtected(sel.Range(r).caret.Position(), sel.Range(r).caret.Position() + 1)) {
3970 if (sel.Range(r).caret.VirtualSpace()) {
3971 sel.Range(r).caret.SetVirtualSpace(sel.Range(r).caret.VirtualSpace() - 1);
3972 sel.Range(r).anchor.SetVirtualSpace(sel.Range(r).caret.VirtualSpace());
1a2fb4cd 3973 } else {
9e96e16f
RD
3974 int lineCurrentPos = pdoc->LineFromPosition(sel.Range(r).caret.Position());
3975 if (allowLineStartDeletion || (pdoc->LineStart(lineCurrentPos) != sel.Range(r).caret.Position())) {
3976 if (pdoc->GetColumn(sel.Range(r).caret.Position()) <= pdoc->GetLineIndentation(lineCurrentPos) &&
3977 pdoc->GetColumn(sel.Range(r).caret.Position()) > 0 && pdoc->backspaceUnindents) {
3978 UndoGroup ugInner(pdoc, !ug.Needed());
3979 int indentation = pdoc->GetLineIndentation(lineCurrentPos);
3980 int indentationStep = pdoc->IndentSize();
3981 if (indentation % indentationStep == 0) {
3982 pdoc->SetLineIndentation(lineCurrentPos, indentation - indentationStep);
3983 } else {
3984 pdoc->SetLineIndentation(lineCurrentPos, indentation - (indentation % indentationStep));
3985 }
3986 // SetEmptySelection
3987 sel.Range(r) = SelectionRange(pdoc->GetLineIndentPosition(lineCurrentPos),
3988 pdoc->GetLineIndentPosition(lineCurrentPos));
3989 } else {
3990 pdoc->DelCharBack(sel.Range(r).caret.Position());
3991 }
3992 }
1a2fb4cd 3993 }
9e96e16f
RD
3994 } else {
3995 sel.Range(r).ClearVirtualSpace();
65ec6247 3996 }
65ec6247 3997 }
9ce192d4
RD
3998 } else {
3999 ClearSelection();
9ce192d4 4000 }
9e96e16f 4001 sel.RemoveDuplicates();
d134f170
RD
4002 // Avoid blinking during rapid typing:
4003 ShowCaretAtCurrentPosition();
9ce192d4
RD
4004}
4005
d134f170
RD
4006void Editor::NotifyFocus(bool) {}
4007
f6bcfd97 4008void Editor::NotifyStyleToNeeded(int endStyleNeeded) {
1e9bafca 4009 SCNotification scn = {0};
9ce192d4
RD
4010 scn.nmhdr.code = SCN_STYLENEEDED;
4011 scn.position = endStyleNeeded;
4012 NotifyParent(scn);
4013}
4014
f6bcfd97
BP
4015void Editor::NotifyStyleNeeded(Document*, void *, int endStyleNeeded) {
4016 NotifyStyleToNeeded(endStyleNeeded);
4017}
4018
65ec6247 4019void Editor::NotifyChar(int ch) {
1e9bafca 4020 SCNotification scn = {0};
9ce192d4
RD
4021 scn.nmhdr.code = SCN_CHARADDED;
4022 scn.ch = ch;
4023 NotifyParent(scn);
9ce192d4
RD
4024}
4025
4026void Editor::NotifySavePoint(bool isSavePoint) {
1e9bafca 4027 SCNotification scn = {0};
9ce192d4
RD
4028 if (isSavePoint) {
4029 scn.nmhdr.code = SCN_SAVEPOINTREACHED;
4030 } else {
4031 scn.nmhdr.code = SCN_SAVEPOINTLEFT;
4032 }
4033 NotifyParent(scn);
4034}
4035
4036void Editor::NotifyModifyAttempt() {
1e9bafca 4037 SCNotification scn = {0};
9ce192d4
RD
4038 scn.nmhdr.code = SCN_MODIFYATTEMPTRO;
4039 NotifyParent(scn);
4040}
4041
7e0c58e9 4042void Editor::NotifyDoubleClick(Point pt, bool shift, bool ctrl, bool alt) {
1e9bafca 4043 SCNotification scn = {0};
9ce192d4 4044 scn.nmhdr.code = SCN_DOUBLECLICK;
7e0c58e9 4045 scn.line = LineFromLocation(pt);
9e96e16f 4046 scn.position = PositionFromLocation(pt, true);
7e0c58e9
RD
4047 scn.modifiers = (shift ? SCI_SHIFT : 0) | (ctrl ? SCI_CTRL : 0) |
4048 (alt ? SCI_ALT : 0);
9ce192d4
RD
4049 NotifyParent(scn);
4050}
4051
9e730a78 4052void Editor::NotifyHotSpotDoubleClicked(int position, bool shift, bool ctrl, bool alt) {
1e9bafca 4053 SCNotification scn = {0};
9e730a78
RD
4054 scn.nmhdr.code = SCN_HOTSPOTDOUBLECLICK;
4055 scn.position = position;
4056 scn.modifiers = (shift ? SCI_SHIFT : 0) | (ctrl ? SCI_CTRL : 0) |
7e0c58e9 4057 (alt ? SCI_ALT : 0);
9e730a78
RD
4058 NotifyParent(scn);
4059}
4060
4061void Editor::NotifyHotSpotClicked(int position, bool shift, bool ctrl, bool alt) {
1e9bafca 4062 SCNotification scn = {0};
9e730a78
RD
4063 scn.nmhdr.code = SCN_HOTSPOTCLICK;
4064 scn.position = position;
4065 scn.modifiers = (shift ? SCI_SHIFT : 0) | (ctrl ? SCI_CTRL : 0) |
7e0c58e9 4066 (alt ? SCI_ALT : 0);
9e730a78
RD
4067 NotifyParent(scn);
4068}
4069
9ce192d4 4070void Editor::NotifyUpdateUI() {
1e9bafca 4071 SCNotification scn = {0};
9ce192d4
RD
4072 scn.nmhdr.code = SCN_UPDATEUI;
4073 NotifyParent(scn);
4074}
4075
65ec6247 4076void Editor::NotifyPainted() {
1e9bafca 4077 SCNotification scn = {0};
65ec6247
RD
4078 scn.nmhdr.code = SCN_PAINTED;
4079 NotifyParent(scn);
4080}
4081
7e0c58e9
RD
4082void Editor::NotifyIndicatorClick(bool click, int position, bool shift, bool ctrl, bool alt) {
4083 int mask = pdoc->decorations.AllOnFor(position);
4084 if ((click && mask) || pdoc->decorations.clickNotified) {
4085 SCNotification scn = {0};
4086 pdoc->decorations.clickNotified = click;
4087 scn.nmhdr.code = click ? SCN_INDICATORCLICK : SCN_INDICATORRELEASE;
4088 scn.modifiers = (shift ? SCI_SHIFT : 0) | (ctrl ? SCI_CTRL : 0) | (alt ? SCI_ALT : 0);
4089 scn.position = position;
4090 NotifyParent(scn);
4091 }
4092}
4093
9ce192d4
RD
4094bool Editor::NotifyMarginClick(Point pt, bool shift, bool ctrl, bool alt) {
4095 int marginClicked = -1;
4096 int x = 0;
d134f170 4097 for (int margin = 0; margin < ViewStyle::margins; margin++) {
9ce192d4
RD
4098 if ((pt.x > x) && (pt.x < x + vs.ms[margin].width))
4099 marginClicked = margin;
4100 x += vs.ms[margin].width;
4101 }
4102 if ((marginClicked >= 0) && vs.ms[marginClicked].sensitive) {
1e9bafca 4103 SCNotification scn = {0};
9ce192d4
RD
4104 scn.nmhdr.code = SCN_MARGINCLICK;
4105 scn.modifiers = (shift ? SCI_SHIFT : 0) | (ctrl ? SCI_CTRL : 0) |
7e0c58e9 4106 (alt ? SCI_ALT : 0);
9ce192d4
RD
4107 scn.position = pdoc->LineStart(LineFromLocation(pt));
4108 scn.margin = marginClicked;
4109 NotifyParent(scn);
4110 return true;
4111 } else {
4112 return false;
4113 }
4114}
4115
4116void Editor::NotifyNeedShown(int pos, int len) {
1e9bafca 4117 SCNotification scn = {0};
9ce192d4
RD
4118 scn.nmhdr.code = SCN_NEEDSHOWN;
4119 scn.position = pos;
4120 scn.length = len;
4121 NotifyParent(scn);
4122}
4123
65ec6247 4124void Editor::NotifyDwelling(Point pt, bool state) {
1e9bafca 4125 SCNotification scn = {0};
65ec6247 4126 scn.nmhdr.code = state ? SCN_DWELLSTART : SCN_DWELLEND;
9e96e16f 4127 scn.position = PositionFromLocation(pt, true);
65ec6247
RD
4128 scn.x = pt.x;
4129 scn.y = pt.y;
4130 NotifyParent(scn);
4131}
4132
a834585d 4133void Editor::NotifyZoom() {
1e9bafca 4134 SCNotification scn = {0};
a834585d
RD
4135 scn.nmhdr.code = SCN_ZOOM;
4136 NotifyParent(scn);
4137}
4138
9ce192d4
RD
4139// Notifications from document
4140void Editor::NotifyModifyAttempt(Document*, void *) {
4141 //Platform::DebugPrintf("** Modify Attempt\n");
4142 NotifyModifyAttempt();
4143}
4144
4145void Editor::NotifySavePoint(Document*, void *, bool atSavePoint) {
4146 //Platform::DebugPrintf("** Save Point %s\n", atSavePoint ? "On" : "Off");
4147 NotifySavePoint(atSavePoint);
4148}
4149
1a2fb4cd 4150void Editor::CheckModificationForWrap(DocModification mh) {
7e0c58e9 4151 if (mh.modificationType & (SC_MOD_INSERTTEXT | SC_MOD_DELETETEXT)) {
a834585d 4152 llc.Invalidate(LineLayout::llCheckTextAndStyle);
1a2fb4cd
RD
4153 if (wrapState != eWrapNone) {
4154 int lineDoc = pdoc->LineFromPosition(mh.position);
b8193d80
RD
4155 int lines = Platform::Maximum(0, mh.linesAdded);
4156 NeedWrapping(lineDoc, lineDoc + lines + 1);
1a2fb4cd 4157 }
9e96e16f
RD
4158 // Fix up annotation heights
4159 int lineDoc = pdoc->LineFromPosition(mh.position);
4160 int lines = Platform::Maximum(0, mh.linesAdded);
4161 SetAnnotationHeights(lineDoc, lineDoc + lines + 2);
1a2fb4cd
RD
4162 }
4163}
4164
4165// Move a position so it is still after the same character as before the insertion.
4166static inline int MovePositionForInsertion(int position, int startInsertion, int length) {
4167 if (position > startInsertion) {
4168 return position + length;
4169 }
4170 return position;
4171}
4172
4173// Move a position so it is still after the same character as before the deletion if that
4174// character is still present else after the previous surviving character.
4175static inline int MovePositionForDeletion(int position, int startDeletion, int length) {
4176 if (position > startDeletion) {
4177 int endDeletion = startDeletion + length;
4178 if (position > endDeletion) {
4179 return position - length;
4180 } else {
4181 return startDeletion;
4182 }
4183 } else {
4184 return position;
4185 }
4186}
4187
9ce192d4
RD
4188void Editor::NotifyModified(Document*, DocModification mh, void *) {
4189 needUpdateUI = true;
4190 if (paintState == painting) {
4191 CheckForChangeOutsidePaint(Range(mh.position, mh.position + mh.length));
a834585d 4192 }
7e0c58e9
RD
4193 if (mh.modificationType & SC_MOD_CHANGELINESTATE) {
4194 if (paintState == painting) {
4195 CheckForChangeOutsidePaint(
4196 Range(pdoc->LineStart(mh.line), pdoc->LineStart(mh.line + 1)));
4197 } else {
4198 // Could check that change is before last visible line.
4199 Redraw();
4200 }
4201 }
4202 if (mh.modificationType & (SC_MOD_CHANGESTYLE | SC_MOD_CHANGEINDICATOR)) {
4203 if (mh.modificationType & SC_MOD_CHANGESTYLE) {
4204 pdoc->IncrementStyleClock();
4205 }
a834585d 4206 if (paintState == notPainting) {
9ce192d4
RD
4207 if (mh.position < pdoc->LineStart(topLine)) {
4208 // Styling performed before this view
4209 Redraw();
4210 } else {
4211 InvalidateRange(mh.position, mh.position + mh.length);
4212 }
a834585d 4213 }
7e0c58e9
RD
4214 if (mh.modificationType & SC_MOD_CHANGESTYLE) {
4215 llc.Invalidate(LineLayout::llCheckTextAndStyle);
4216 }
a834585d
RD
4217 } else {
4218 // Move selection and brace highlights
4219 if (mh.modificationType & SC_MOD_INSERTTEXT) {
9e96e16f 4220 sel.MovePositions(true, mh.position, mh.length);
a834585d
RD
4221 braces[0] = MovePositionForInsertion(braces[0], mh.position, mh.length);
4222 braces[1] = MovePositionForInsertion(braces[1], mh.position, mh.length);
4223 } else if (mh.modificationType & SC_MOD_DELETETEXT) {
9e96e16f 4224 sel.MovePositions(false, mh.position, mh.length);
a834585d
RD
4225 braces[0] = MovePositionForDeletion(braces[0], mh.position, mh.length);
4226 braces[1] = MovePositionForDeletion(braces[1], mh.position, mh.length);
4227 }
4228 if (cs.LinesDisplayed() < cs.LinesInDoc()) {
4229 // Some lines are hidden so may need shown.
4230 // TODO: check if the modified area is hidden.
4231 if (mh.modificationType & SC_MOD_BEFOREINSERT) {
1e9bafca 4232 NotifyNeedShown(mh.position, 0);
a834585d
RD
4233 } else if (mh.modificationType & SC_MOD_BEFOREDELETE) {
4234 NotifyNeedShown(mh.position, mh.length);
9ce192d4 4235 }
a834585d
RD
4236 }
4237 if (mh.linesAdded != 0) {
4238 // Update contraction state for inserted and removed lines
4239 // lineOfPos should be calculated in context of state before modification, shouldn't it
4240 int lineOfPos = pdoc->LineFromPosition(mh.position);
4241 if (mh.linesAdded > 0) {
4242 cs.InsertLines(lineOfPos, mh.linesAdded);
4243 } else {
4244 cs.DeleteLines(lineOfPos, -mh.linesAdded);
d134f170 4245 }
8e54aaed 4246 }
9e96e16f
RD
4247 if (mh.modificationType & SC_MOD_CHANGEANNOTATION) {
4248 int lineDoc = pdoc->LineFromPosition(mh.position);
4249 if (vs.annotationVisible) {
4250 cs.SetHeight(lineDoc, cs.GetHeight(lineDoc) + mh.annotationLinesAdded);
4251 }
4252 }
8e54aaed
RD
4253 CheckModificationForWrap(mh);
4254 if (mh.linesAdded != 0) {
a834585d 4255 // Avoid scrolling of display if change before current display
1e9bafca 4256 if (mh.position < posTopLine && !CanDeferToLastStep(mh)) {
a834585d
RD
4257 int newTop = Platform::Clamp(topLine + mh.linesAdded, 0, MaxScrollPos());
4258 if (newTop != topLine) {
4259 SetTopLine(newTop);
4260 SetVerticalScrollPos();
9ce192d4 4261 }
a834585d 4262 }
d134f170 4263
a834585d
RD
4264 //Platform::DebugPrintf("** %x Doc Changed\n", this);
4265 // TODO: could invalidate from mh.startModification to end of screen
4266 //InvalidateRange(mh.position, mh.position + mh.length);
1e9bafca 4267 if (paintState == notPainting && !CanDeferToLastStep(mh)) {
9ce192d4 4268 Redraw();
a834585d
RD
4269 }
4270 } else {
4271 //Platform::DebugPrintf("** %x Line Changed %d .. %d\n", this,
4272 // mh.position, mh.position + mh.length);
1e9bafca 4273 if (paintState == notPainting && mh.length && !CanEliminate(mh)) {
9ce192d4
RD
4274 InvalidateRange(mh.position, mh.position + mh.length);
4275 }
4276 }
a834585d 4277 }
9ce192d4 4278
1e9bafca 4279 if (mh.linesAdded != 0 && !CanDeferToLastStep(mh)) {
9ce192d4
RD
4280 SetScrollBars();
4281 }
4282
9e96e16f 4283 if ((mh.modificationType & SC_MOD_CHANGEMARKER) || (mh.modificationType & SC_MOD_CHANGEMARGIN)) {
1e9bafca
RD
4284 if ((paintState == notPainting) || !PaintContainsMargin()) {
4285 if (mh.modificationType & SC_MOD_CHANGEFOLD) {
4286 // Fold changes can affect the drawing of following lines so redraw whole margin
4287 RedrawSelMargin();
4288 } else {
4289 RedrawSelMargin(mh.line);
4290 }
a834585d 4291 }
f6bcfd97 4292 }
d134f170 4293
1e9bafca
RD
4294 // NOW pay the piper WRT "deferred" visual updates
4295 if (IsLastStep(mh)) {
4296 SetScrollBars();
4297 Redraw();
4298 }
4299
9ce192d4
RD
4300 // If client wants to see this modification
4301 if (mh.modificationType & modEventMask) {
7e0c58e9 4302 if ((mh.modificationType & (SC_MOD_CHANGESTYLE | SC_MOD_CHANGEINDICATOR)) == 0) {
9ce192d4
RD
4303 // Real modification made to text of document.
4304 NotifyChange(); // Send EN_CHANGE
4305 }
d134f170 4306
1e9bafca 4307 SCNotification scn = {0};
9ce192d4
RD
4308 scn.nmhdr.code = SCN_MODIFIED;
4309 scn.position = mh.position;
4310 scn.modificationType = mh.modificationType;
4311 scn.text = mh.text;
4312 scn.length = mh.length;
4313 scn.linesAdded = mh.linesAdded;
4314 scn.line = mh.line;
4315 scn.foldLevelNow = mh.foldLevelNow;
4316 scn.foldLevelPrev = mh.foldLevelPrev;
9e96e16f
RD
4317 scn.token = mh.token;
4318 scn.annotationLinesAdded = mh.annotationLinesAdded;
9ce192d4
RD
4319 NotifyParent(scn);
4320 }
4321}
4322
f6bcfd97 4323void Editor::NotifyDeleted(Document *, void *) {
9ce192d4
RD
4324 /* Do nothing */
4325}
4326
1e9bafca 4327void Editor::NotifyMacroRecord(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
9ce192d4
RD
4328
4329 // Enumerates all macroable messages
4330 switch (iMessage) {
d134f170
RD
4331 case SCI_CUT:
4332 case SCI_COPY:
4333 case SCI_PASTE:
4334 case SCI_CLEAR:
4335 case SCI_REPLACESEL:
9ce192d4
RD
4336 case SCI_ADDTEXT:
4337 case SCI_INSERTTEXT:
9e730a78 4338 case SCI_APPENDTEXT:
9ce192d4
RD
4339 case SCI_CLEARALL:
4340 case SCI_SELECTALL:
4341 case SCI_GOTOLINE:
4342 case SCI_GOTOPOS:
4343 case SCI_SEARCHANCHOR:
4344 case SCI_SEARCHNEXT:
4345 case SCI_SEARCHPREV:
4346 case SCI_LINEDOWN:
4347 case SCI_LINEDOWNEXTEND:
9e730a78
RD
4348 case SCI_PARADOWN:
4349 case SCI_PARADOWNEXTEND:
9ce192d4
RD
4350 case SCI_LINEUP:
4351 case SCI_LINEUPEXTEND:
9e730a78
RD
4352 case SCI_PARAUP:
4353 case SCI_PARAUPEXTEND:
9ce192d4
RD
4354 case SCI_CHARLEFT:
4355 case SCI_CHARLEFTEXTEND:
4356 case SCI_CHARRIGHT:
4357 case SCI_CHARRIGHTEXTEND:
4358 case SCI_WORDLEFT:
4359 case SCI_WORDLEFTEXTEND:
4360 case SCI_WORDRIGHT:
4361 case SCI_WORDRIGHTEXTEND:
65ec6247
RD
4362 case SCI_WORDPARTLEFT:
4363 case SCI_WORDPARTLEFTEXTEND:
4364 case SCI_WORDPARTRIGHT:
4365 case SCI_WORDPARTRIGHTEXTEND:
8e54aaed
RD
4366 case SCI_WORDLEFTEND:
4367 case SCI_WORDLEFTENDEXTEND:
4368 case SCI_WORDRIGHTEND:
4369 case SCI_WORDRIGHTENDEXTEND:
9ce192d4
RD
4370 case SCI_HOME:
4371 case SCI_HOMEEXTEND:
4372 case SCI_LINEEND:
4373 case SCI_LINEENDEXTEND:
9e730a78
RD
4374 case SCI_HOMEWRAP:
4375 case SCI_HOMEWRAPEXTEND:
4376 case SCI_LINEENDWRAP:
4377 case SCI_LINEENDWRAPEXTEND:
9ce192d4
RD
4378 case SCI_DOCUMENTSTART:
4379 case SCI_DOCUMENTSTARTEXTEND:
4380 case SCI_DOCUMENTEND:
4381 case SCI_DOCUMENTENDEXTEND:
8e54aaed
RD
4382 case SCI_STUTTEREDPAGEUP:
4383 case SCI_STUTTEREDPAGEUPEXTEND:
4384 case SCI_STUTTEREDPAGEDOWN:
4385 case SCI_STUTTEREDPAGEDOWNEXTEND:
9ce192d4
RD
4386 case SCI_PAGEUP:
4387 case SCI_PAGEUPEXTEND:
4388 case SCI_PAGEDOWN:
4389 case SCI_PAGEDOWNEXTEND:
4390 case SCI_EDITTOGGLEOVERTYPE:
4391 case SCI_CANCEL:
4392 case SCI_DELETEBACK:
4393 case SCI_TAB:
4394 case SCI_BACKTAB:
9ce192d4
RD
4395 case SCI_FORMFEED:
4396 case SCI_VCHOME:
4397 case SCI_VCHOMEEXTEND:
9e730a78
RD
4398 case SCI_VCHOMEWRAP:
4399 case SCI_VCHOMEWRAPEXTEND:
9ce192d4
RD
4400 case SCI_DELWORDLEFT:
4401 case SCI_DELWORDRIGHT:
7e0c58e9 4402 case SCI_DELWORDRIGHTEND:
65ec6247
RD
4403 case SCI_DELLINELEFT:
4404 case SCI_DELLINERIGHT:
e14d10b0 4405 case SCI_LINECOPY:
f6bcfd97
BP
4406 case SCI_LINECUT:
4407 case SCI_LINEDELETE:
4408 case SCI_LINETRANSPOSE:
9e730a78 4409 case SCI_LINEDUPLICATE:
f6bcfd97
BP
4410 case SCI_LOWERCASE:
4411 case SCI_UPPERCASE:
1a2fb4cd
RD
4412 case SCI_LINESCROLLDOWN:
4413 case SCI_LINESCROLLUP:
4414 case SCI_DELETEBACKNOTLINE:
f114b858
RD
4415 case SCI_HOMEDISPLAY:
4416 case SCI_HOMEDISPLAYEXTEND:
4417 case SCI_LINEENDDISPLAY:
4418 case SCI_LINEENDDISPLAYEXTEND:
8e54aaed
RD
4419 case SCI_SETSELECTIONMODE:
4420 case SCI_LINEDOWNRECTEXTEND:
4421 case SCI_LINEUPRECTEXTEND:
4422 case SCI_CHARLEFTRECTEXTEND:
4423 case SCI_CHARRIGHTRECTEXTEND:
4424 case SCI_HOMERECTEXTEND:
4425 case SCI_VCHOMERECTEXTEND:
4426 case SCI_LINEENDRECTEXTEND:
4427 case SCI_PAGEUPRECTEXTEND:
4428 case SCI_PAGEDOWNRECTEXTEND:
1e9bafca 4429 case SCI_SELECTIONDUPLICATE:
9e96e16f 4430 case SCI_COPYALLOWLINE:
8e54aaed
RD
4431 break;
4432
7e0c58e9
RD
4433 // Filter out all others like display changes. Also, newlines are redundant
4434 // with char insert messages.
65ec6247 4435 case SCI_NEWLINE:
9ce192d4 4436 default:
d134f170 4437 // printf("Filtered out %ld of macro recording\n", iMessage);
9e730a78 4438 return ;
9ce192d4
RD
4439 }
4440
4441 // Send notification
1e9bafca 4442 SCNotification scn = {0};
9ce192d4
RD
4443 scn.nmhdr.code = SCN_MACRORECORD;
4444 scn.message = iMessage;
4445 scn.wParam = wParam;
4446 scn.lParam = lParam;
4447 NotifyParent(scn);
4448}
9ce192d4 4449
8e54aaed
RD
4450/**
4451 * Force scroll and keep position relative to top of window.
4452 *
4453 * If stuttered = true and not already at first/last row, move to first/last row of window.
4454 * If stuttered = true and already at first/last row, scroll as normal.
4455 */
9e96e16f 4456void Editor::PageMove(int direction, Selection::selTypes selt, bool stuttered) {
8e54aaed
RD
4457 int topLineNew, newPos;
4458
4459 // I consider only the caretYSlop, and ignore the caretYPolicy-- is that a problem?
9e96e16f 4460 int currentLine = pdoc->LineFromPosition(sel.MainCaret());
8e54aaed 4461 int topStutterLine = topLine + caretYSlop;
7e0c58e9
RD
4462 int bottomStutterLine =
4463 pdoc->LineFromPosition(PositionFromLocation(
4464 Point(lastXChosen, direction * vs.lineHeight * LinesToScroll())))
4465 - caretYSlop - 1;
8e54aaed
RD
4466
4467 if (stuttered && (direction < 0 && currentLine > topStutterLine)) {
4468 topLineNew = topLine;
4469 newPos = PositionFromLocation(Point(lastXChosen, vs.lineHeight * caretYSlop));
4470
4471 } else if (stuttered && (direction > 0 && currentLine < bottomStutterLine)) {
4472 topLineNew = topLine;
4473 newPos = PositionFromLocation(Point(lastXChosen, vs.lineHeight * (LinesToScroll() - caretYSlop)));
4474
4475 } else {
9e96e16f 4476 Point pt = LocationFromPosition(sel.MainCaret());
8e54aaed
RD
4477
4478 topLineNew = Platform::Clamp(
7e0c58e9 4479 topLine + direction * LinesToScroll(), 0, MaxScrollPos());
8e54aaed 4480 newPos = PositionFromLocation(
7e0c58e9 4481 Point(lastXChosen, pt.y + direction * (vs.lineHeight * LinesToScroll())));
8e54aaed
RD
4482 }
4483
9ce192d4
RD
4484 if (topLineNew != topLine) {
4485 SetTopLine(topLineNew);
9e96e16f 4486 MovePositionTo(SelectionPosition(newPos), selt);
9ce192d4
RD
4487 Redraw();
4488 SetVerticalScrollPos();
4489 } else {
9e96e16f 4490 MovePositionTo(SelectionPosition(newPos), selt);
9ce192d4
RD
4491 }
4492}
4493
f6bcfd97 4494void Editor::ChangeCaseOfSelection(bool makeUpperCase) {
9e96e16f
RD
4495 UndoGroup ug(pdoc);
4496 for (size_t r=0; r<sel.Count(); r++) {
4497 SelectionRange current = sel.Range(r);
4498 pdoc->ChangeCase(Range(current.Start().Position(), current.End().Position()),
4499 makeUpperCase);
4500 // Automatic movement cuts off last character so reset to exactly the same as it was.
4501 sel.Range(r) = current;
f6bcfd97 4502 }
f6bcfd97
BP
4503}
4504
f6bcfd97 4505void Editor::LineTranspose() {
9e96e16f 4506 int line = pdoc->LineFromPosition(sel.MainCaret());
f6bcfd97 4507 if (line > 0) {
9e96e16f 4508 UndoGroup ug(pdoc);
d134f170
RD
4509 int startPrev = pdoc->LineStart(line - 1);
4510 int endPrev = pdoc->LineEnd(line - 1);
f6bcfd97
BP
4511 int start = pdoc->LineStart(line);
4512 int end = pdoc->LineEnd(line);
7e0c58e9
RD
4513 char *line1 = CopyRange(startPrev, endPrev);
4514 int len1 = endPrev - startPrev;
4515 char *line2 = CopyRange(start, end);
4516 int len2 = end - start;
4517 pdoc->DeleteChars(start, len2);
4518 pdoc->DeleteChars(startPrev, len1);
4519 pdoc->InsertString(startPrev, line2, len2);
4520 pdoc->InsertString(start - len1 + len2, line1, len1);
9e96e16f 4521 MovePositionTo(SelectionPosition(start - len1 + len2));
7e0c58e9
RD
4522 delete []line1;
4523 delete []line2;
f6bcfd97
BP
4524 }
4525}
4526
1e9bafca 4527void Editor::Duplicate(bool forLine) {
9e96e16f 4528 if (sel.Empty()) {
1e9bafca
RD
4529 forLine = true;
4530 }
9e96e16f
RD
4531 UndoGroup ug(pdoc, sel.Count() > 1);
4532 SelectionPosition last;
4533 const char *eol = "";
4534 int eolLen = 0;
1e9bafca 4535 if (forLine) {
9e96e16f
RD
4536 eol = StringFromEOLMode(pdoc->eolMode);
4537 eolLen = istrlen(eol);
4538 }
4539 for (size_t r=0; r<sel.Count(); r++) {
4540 SelectionPosition start = sel.Range(r).Start();
4541 SelectionPosition end = sel.Range(r).End();
4542 if (forLine) {
4543 int line = pdoc->LineFromPosition(sel.Range(r).caret.Position());
4544 start = SelectionPosition(pdoc->LineStart(line));
4545 end = SelectionPosition(pdoc->LineEnd(line));
4546 }
4547 char *text = CopyRange(start.Position(), end.Position());
4548 if (forLine)
4549 pdoc->InsertString(end.Position(), eol, eolLen);
4550 pdoc->InsertString(end.Position() + eolLen, text, SelectionRange(end, start).Length());
4551 delete []text;
1e9bafca 4552 }
9e96e16f
RD
4553 if (sel.Count() && sel.IsRectangular()) {
4554 SelectionPosition last = sel.Last();
4555 if (forLine) {
4556 int line = pdoc->LineFromPosition(last.Position());
4557 last = SelectionPosition(last.Position() + pdoc->LineStart(line+1) - pdoc->LineStart(line));
4558 }
4559 if (sel.Rectangular().anchor > sel.Rectangular().caret)
4560 sel.Rectangular().anchor = last;
4561 else
4562 sel.Rectangular().caret = last;
4563 SetRectangularRange();
1e9bafca 4564 }
9e730a78
RD
4565}
4566
8e54aaed 4567void Editor::CancelModes() {
9e96e16f 4568 sel.SetMoveExtends(false);
8e54aaed 4569}
d134f170 4570
a834585d
RD
4571void Editor::NewLine() {
4572 ClearSelection();
4573 const char *eol = "\n";
4574 if (pdoc->eolMode == SC_EOL_CRLF) {
4575 eol = "\r\n";
4576 } else if (pdoc->eolMode == SC_EOL_CR) {
4577 eol = "\r";
4578 } // else SC_EOL_LF -> "\n" already set
9e96e16f
RD
4579 if (pdoc->InsertCString(sel.MainCaret(), eol)) {
4580 SetEmptySelection(sel.MainCaret() + istrlen(eol));
a834585d
RD
4581 while (*eol) {
4582 NotifyChar(*eol);
9e96e16f
RD
4583 if (recordingMacro) {
4584 char txt[2];
4585 txt[0] = *eol;
4586 txt[1] = '\0';
4587 NotifyMacroRecord(SCI_REPLACESEL, 0, reinterpret_cast<sptr_t>(txt));
4588 }
a834585d
RD
4589 eol++;
4590 }
4591 }
4592 SetLastXChosen();
7e0c58e9 4593 SetScrollBars();
a834585d 4594 EnsureCaretVisible();
1e9bafca
RD
4595 // Avoid blinking during rapid typing:
4596 ShowCaretAtCurrentPosition();
a834585d
RD
4597}
4598
9e96e16f
RD
4599void Editor::CursorUpOrDown(int direction, Selection::selTypes selt) {
4600 SelectionPosition caretToUse = sel.Range(sel.Main()).caret;
4601 if (sel.IsRectangular()) {
4602 if (selt == Selection::noSel) {
4603 caretToUse = (direction > 0) ? sel.Limits().end : sel.Limits().start;
4604 } else {
4605 caretToUse = sel.Rectangular().caret;
4606 }
4607 }
4608 Point pt = LocationFromPosition(caretToUse);
4609 int lineDoc = pdoc->LineFromPosition(caretToUse.Position());
4610 Point ptStartLine = LocationFromPosition(pdoc->LineStart(lineDoc));
4611 int subLine = (pt.y - ptStartLine.y) / vs.lineHeight;
4612 int commentLines = vs.annotationVisible ? pdoc->AnnotationLines(lineDoc) : 0;
4613 SelectionPosition posNew = SPositionFromLocation(
4614 Point(lastXChosen, pt.y + direction * vs.lineHeight), false, false, UserVirtualSpace());
4615 if ((direction > 0) && (subLine >= (cs.GetHeight(lineDoc) - 1 - commentLines))) {
4616 posNew = SPositionFromLocation(
4617 Point(lastXChosen, pt.y + (commentLines + 1) * vs.lineHeight), false, false, UserVirtualSpace());
4618 }
a834585d
RD
4619 if (direction < 0) {
4620 // Line wrapping may lead to a location on the same line, so
4621 // seek back if that is the case.
4622 // There is an equivalent case when moving down which skips
4623 // over a line but as that does not trap the user it is fine.
9e96e16f
RD
4624 Point ptNew = LocationFromPosition(posNew.Position());
4625 while ((posNew.Position() > 0) && (pt.y == ptNew.y)) {
4626 posNew.Add(- 1);
4627 posNew.SetVirtualSpace(0);
4628 ptNew = LocationFromPosition(posNew.Position());
a834585d
RD
4629 }
4630 }
9e96e16f 4631 MovePositionTo(posNew, selt);
a834585d 4632}
9ce192d4 4633
9e96e16f
RD
4634void Editor::ParaUpOrDown(int direction, Selection::selTypes selt) {
4635 int lineDoc, savedPos = sel.MainCaret();
1e9bafca 4636 do {
9e96e16f
RD
4637 MovePositionTo(SelectionPosition(direction > 0 ? pdoc->ParaDown(sel.MainCaret()) : pdoc->ParaUp(sel.MainCaret())), selt);
4638 lineDoc = pdoc->LineFromPosition(sel.MainCaret());
1e9bafca 4639 if (direction > 0) {
9e96e16f
RD
4640 if (sel.MainCaret() >= pdoc->Length() && !cs.GetVisible(lineDoc)) {
4641 if (selt == Selection::noSel) {
4642 MovePositionTo(SelectionPosition(pdoc->LineEndPosition(savedPos)));
1e9bafca
RD
4643 }
4644 break;
4645 }
4646 }
4647 } while (!cs.GetVisible(lineDoc));
4648}
4649
f114b858
RD
4650int Editor::StartEndDisplayLine(int pos, bool start) {
4651 RefreshStyleData();
4652 int line = pdoc->LineFromPosition(pos);
9e730a78
RD
4653 AutoSurface surface(this);
4654 AutoLineLayout ll(llc, RetrieveLineLayout(line));
f114b858
RD
4655 int posRet = INVALID_POSITION;
4656 if (surface && ll) {
4657 unsigned int posLineStart = pdoc->LineStart(line);
4658 LayoutLine(line, surface, vs, ll, wrapWidth);
4659 int posInLine = pos - posLineStart;
4660 if (posInLine <= ll->maxLineLength) {
9e730a78
RD
4661 for (int subLine = 0; subLine < ll->lines; subLine++) {
4662 if ((posInLine >= ll->LineStart(subLine)) && (posInLine <= ll->LineStart(subLine + 1))) {
f114b858
RD
4663 if (start) {
4664 posRet = ll->LineStart(subLine) + posLineStart;
4665 } else {
4666 if (subLine == ll->lines - 1)
9e730a78 4667 posRet = ll->LineStart(subLine + 1) + posLineStart;
f114b858 4668 else
9e730a78 4669 posRet = ll->LineStart(subLine + 1) + posLineStart - 1;
f114b858
RD
4670 }
4671 }
4672 }
4673 }
4674 }
f114b858
RD
4675 if (posRet == INVALID_POSITION) {
4676 return pos;
4677 } else {
4678 return posRet;
4679 }
4680}
4681
a834585d 4682int Editor::KeyCommand(unsigned int iMessage) {
9ce192d4
RD
4683 switch (iMessage) {
4684 case SCI_LINEDOWN:
a834585d 4685 CursorUpOrDown(1);
9ce192d4
RD
4686 break;
4687 case SCI_LINEDOWNEXTEND:
9e96e16f 4688 CursorUpOrDown(1, Selection::selStream);
8e54aaed
RD
4689 break;
4690 case SCI_LINEDOWNRECTEXTEND:
9e96e16f 4691 CursorUpOrDown(1, Selection::selRectangle);
9ce192d4 4692 break;
9e730a78 4693 case SCI_PARADOWN:
1e9bafca 4694 ParaUpOrDown(1);
9e730a78
RD
4695 break;
4696 case SCI_PARADOWNEXTEND:
9e96e16f 4697 ParaUpOrDown(1, Selection::selStream);
9e730a78 4698 break;
f6bcfd97
BP
4699 case SCI_LINESCROLLDOWN:
4700 ScrollTo(topLine + 1);
f114b858 4701 MoveCaretInsideView(false);
f6bcfd97 4702 break;
9ce192d4 4703 case SCI_LINEUP:
a834585d 4704 CursorUpOrDown(-1);
9ce192d4
RD
4705 break;
4706 case SCI_LINEUPEXTEND:
9e96e16f 4707 CursorUpOrDown(-1, Selection::selStream);
8e54aaed
RD
4708 break;
4709 case SCI_LINEUPRECTEXTEND:
9e96e16f 4710 CursorUpOrDown(-1, Selection::selRectangle);
9ce192d4 4711 break;
9e730a78 4712 case SCI_PARAUP:
1e9bafca 4713 ParaUpOrDown(-1);
9e730a78
RD
4714 break;
4715 case SCI_PARAUPEXTEND:
9e96e16f 4716 ParaUpOrDown(-1, Selection::selStream);
9e730a78 4717 break;
f6bcfd97
BP
4718 case SCI_LINESCROLLUP:
4719 ScrollTo(topLine - 1);
f114b858 4720 MoveCaretInsideView(false);
f6bcfd97 4721 break;
9ce192d4 4722 case SCI_CHARLEFT:
9e96e16f
RD
4723 if (SelectionEmpty() || sel.MoveExtends()) {
4724 if ((sel.Count() == 1) && pdoc->IsLineEndPosition(sel.MainCaret()) && sel.RangeMain().caret.VirtualSpace()) {
4725 SelectionPosition spCaret = sel.RangeMain().caret;
4726 spCaret.SetVirtualSpace(spCaret.VirtualSpace() - 1);
4727 MovePositionTo(spCaret);
4728 } else {
4729 MovePositionTo(MovePositionSoVisible(
4730 SelectionPosition((sel.LimitsForRectangularElseMain().start).Position() - 1), -1));
4731 }
9ce192d4 4732 } else {
9e96e16f 4733 MovePositionTo(sel.LimitsForRectangularElseMain().start);
9ce192d4
RD
4734 }
4735 SetLastXChosen();
4736 break;
4737 case SCI_CHARLEFTEXTEND:
9e96e16f
RD
4738 if (pdoc->IsLineEndPosition(sel.MainCaret()) && sel.RangeMain().caret.VirtualSpace()) {
4739 SelectionPosition spCaret = sel.RangeMain().caret;
4740 spCaret.SetVirtualSpace(spCaret.VirtualSpace() - 1);
4741 MovePositionTo(spCaret, Selection::selStream);
4742 } else {
4743 MovePositionTo(MovePositionSoVisible(SelectionPosition(sel.MainCaret() - 1), -1), Selection::selStream);
4744 }
8e54aaed
RD
4745 SetLastXChosen();
4746 break;
4747 case SCI_CHARLEFTRECTEXTEND:
9e96e16f
RD
4748 if (pdoc->IsLineEndPosition(sel.MainCaret()) && sel.RangeMain().caret.VirtualSpace()) {
4749 SelectionPosition spCaret = sel.RangeMain().caret;
4750 spCaret.SetVirtualSpace(spCaret.VirtualSpace() - 1);
4751 MovePositionTo(spCaret, Selection::selRectangle);
4752 } else {
4753 MovePositionTo(MovePositionSoVisible(SelectionPosition(sel.MainCaret() - 1), -1), Selection::selRectangle);
4754 }
9ce192d4
RD
4755 SetLastXChosen();
4756 break;
4757 case SCI_CHARRIGHT:
9e96e16f
RD
4758 if (SelectionEmpty() || sel.MoveExtends()) {
4759 if ((virtualSpaceOptions & SCVS_USERACCESSIBLE) && pdoc->IsLineEndPosition(sel.MainCaret())) {
4760 SelectionPosition spCaret = sel.RangeMain().caret;
4761 spCaret.SetVirtualSpace(spCaret.VirtualSpace() + 1);
4762 MovePositionTo(spCaret);
4763 } else {
4764 MovePositionTo(MovePositionSoVisible(
4765 SelectionPosition((sel.LimitsForRectangularElseMain().end).Position() + 1), 1));
4766 }
9ce192d4 4767 } else {
9e96e16f 4768 MovePositionTo(sel.LimitsForRectangularElseMain().end);
9ce192d4
RD
4769 }
4770 SetLastXChosen();
4771 break;
4772 case SCI_CHARRIGHTEXTEND:
9e96e16f
RD
4773 if ((virtualSpaceOptions & SCVS_USERACCESSIBLE) && pdoc->IsLineEndPosition(sel.MainCaret())) {
4774 SelectionPosition spCaret = sel.RangeMain().caret;
4775 spCaret.SetVirtualSpace(spCaret.VirtualSpace() + 1);
4776 MovePositionTo(spCaret, Selection::selStream);
4777 } else {
4778 MovePositionTo(MovePositionSoVisible(SelectionPosition(sel.MainCaret() + 1), 1), Selection::selStream);
4779 }
8e54aaed
RD
4780 SetLastXChosen();
4781 break;
4782 case SCI_CHARRIGHTRECTEXTEND:
9e96e16f
RD
4783 if ((virtualSpaceOptions & SCVS_RECTANGULARSELECTION) && pdoc->IsLineEndPosition(sel.MainCaret())) {
4784 SelectionPosition spCaret = sel.RangeMain().caret;
4785 spCaret.SetVirtualSpace(spCaret.VirtualSpace() + 1);
4786 MovePositionTo(spCaret, Selection::selRectangle);
4787 } else {
4788 MovePositionTo(MovePositionSoVisible(SelectionPosition(sel.MainCaret() + 1), 1), Selection::selRectangle);
4789 }
9ce192d4
RD
4790 SetLastXChosen();
4791 break;
4792 case SCI_WORDLEFT:
9e96e16f 4793 MovePositionTo(MovePositionSoVisible(pdoc->NextWordStart(sel.MainCaret(), -1), -1));
9ce192d4
RD
4794 SetLastXChosen();
4795 break;
4796 case SCI_WORDLEFTEXTEND:
9e96e16f 4797 MovePositionTo(MovePositionSoVisible(pdoc->NextWordStart(sel.MainCaret(), -1), -1), Selection::selStream);
9ce192d4
RD
4798 SetLastXChosen();
4799 break;
4800 case SCI_WORDRIGHT:
9e96e16f 4801 MovePositionTo(MovePositionSoVisible(pdoc->NextWordStart(sel.MainCaret(), 1), 1));
9ce192d4
RD
4802 SetLastXChosen();
4803 break;
4804 case SCI_WORDRIGHTEXTEND:
9e96e16f 4805 MovePositionTo(MovePositionSoVisible(pdoc->NextWordStart(sel.MainCaret(), 1), 1), Selection::selStream);
8e54aaed
RD
4806 SetLastXChosen();
4807 break;
4808
4809 case SCI_WORDLEFTEND:
9e96e16f 4810 MovePositionTo(MovePositionSoVisible(pdoc->NextWordEnd(sel.MainCaret(), -1), -1));
8e54aaed
RD
4811 SetLastXChosen();
4812 break;
4813 case SCI_WORDLEFTENDEXTEND:
9e96e16f 4814 MovePositionTo(MovePositionSoVisible(pdoc->NextWordEnd(sel.MainCaret(), -1), -1), Selection::selStream);
8e54aaed
RD
4815 SetLastXChosen();
4816 break;
4817 case SCI_WORDRIGHTEND:
9e96e16f 4818 MovePositionTo(MovePositionSoVisible(pdoc->NextWordEnd(sel.MainCaret(), 1), 1));
9ce192d4
RD
4819 SetLastXChosen();
4820 break;
8e54aaed 4821 case SCI_WORDRIGHTENDEXTEND:
9e96e16f 4822 MovePositionTo(MovePositionSoVisible(pdoc->NextWordEnd(sel.MainCaret(), 1), 1), Selection::selStream);
8e54aaed
RD
4823 SetLastXChosen();
4824 break;
4825
9ce192d4 4826 case SCI_HOME:
9e96e16f 4827 MovePositionTo(pdoc->LineStart(pdoc->LineFromPosition(sel.MainCaret())));
9ce192d4
RD
4828 SetLastXChosen();
4829 break;
4830 case SCI_HOMEEXTEND:
9e96e16f 4831 MovePositionTo(pdoc->LineStart(pdoc->LineFromPosition(sel.MainCaret())), Selection::selStream);
8e54aaed
RD
4832 SetLastXChosen();
4833 break;
4834 case SCI_HOMERECTEXTEND:
9e96e16f 4835 MovePositionTo(pdoc->LineStart(pdoc->LineFromPosition(sel.MainCaret())), Selection::selRectangle);
9ce192d4
RD
4836 SetLastXChosen();
4837 break;
4838 case SCI_LINEEND:
9e96e16f 4839 MovePositionTo(pdoc->LineEndPosition(sel.MainCaret()));
9ce192d4
RD
4840 SetLastXChosen();
4841 break;
4842 case SCI_LINEENDEXTEND:
9e96e16f 4843 MovePositionTo(pdoc->LineEndPosition(sel.MainCaret()), Selection::selStream);
8e54aaed
RD
4844 SetLastXChosen();
4845 break;
4846 case SCI_LINEENDRECTEXTEND:
9e96e16f 4847 MovePositionTo(pdoc->LineEndPosition(sel.MainCaret()), Selection::selRectangle);
9ce192d4
RD
4848 SetLastXChosen();
4849 break;
9e730a78 4850 case SCI_HOMEWRAP: {
9e96e16f
RD
4851 SelectionPosition homePos = MovePositionSoVisible(StartEndDisplayLine(sel.MainCaret(), true), -1);
4852 if (sel.RangeMain().caret <= homePos)
4853 homePos = SelectionPosition(pdoc->LineStart(pdoc->LineFromPosition(sel.MainCaret())));
9e730a78
RD
4854 MovePositionTo(homePos);
4855 SetLastXChosen();
4856 }
4857 break;
4858 case SCI_HOMEWRAPEXTEND: {
9e96e16f
RD
4859 SelectionPosition homePos = MovePositionSoVisible(StartEndDisplayLine(sel.MainCaret(), true), -1);
4860 if (sel.RangeMain().caret <= homePos)
4861 homePos = SelectionPosition(pdoc->LineStart(pdoc->LineFromPosition(sel.MainCaret())));
4862 MovePositionTo(homePos, Selection::selStream);
9e730a78
RD
4863 SetLastXChosen();
4864 }
4865 break;
4866 case SCI_LINEENDWRAP: {
9e96e16f
RD
4867 SelectionPosition endPos = MovePositionSoVisible(StartEndDisplayLine(sel.MainCaret(), false), 1);
4868 SelectionPosition realEndPos = SelectionPosition(pdoc->LineEndPosition(sel.MainCaret()));
a33203cb 4869 if (endPos > realEndPos // if moved past visible EOLs
9e96e16f 4870 || sel.RangeMain().caret >= endPos) // if at end of display line already
a33203cb 4871 endPos = realEndPos;
9e730a78
RD
4872 MovePositionTo(endPos);
4873 SetLastXChosen();
4874 }
4875 break;
4876 case SCI_LINEENDWRAPEXTEND: {
9e96e16f
RD
4877 SelectionPosition endPos = MovePositionSoVisible(StartEndDisplayLine(sel.MainCaret(), false), 1);
4878 SelectionPosition realEndPos = SelectionPosition(pdoc->LineEndPosition(sel.MainCaret()));
a33203cb 4879 if (endPos > realEndPos // if moved past visible EOLs
9e96e16f 4880 || sel.RangeMain().caret >= endPos) // if at end of display line already
a33203cb 4881 endPos = realEndPos;
9e96e16f 4882 MovePositionTo(endPos, Selection::selStream);
9e730a78
RD
4883 SetLastXChosen();
4884 }
4885 break;
9ce192d4
RD
4886 case SCI_DOCUMENTSTART:
4887 MovePositionTo(0);
4888 SetLastXChosen();
4889 break;
4890 case SCI_DOCUMENTSTARTEXTEND:
9e96e16f 4891 MovePositionTo(0, Selection::selStream);
9ce192d4
RD
4892 SetLastXChosen();
4893 break;
4894 case SCI_DOCUMENTEND:
4895 MovePositionTo(pdoc->Length());
4896 SetLastXChosen();
4897 break;
4898 case SCI_DOCUMENTENDEXTEND:
9e96e16f 4899 MovePositionTo(pdoc->Length(), Selection::selStream);
9ce192d4
RD
4900 SetLastXChosen();
4901 break;
8e54aaed 4902 case SCI_STUTTEREDPAGEUP:
9e96e16f 4903 PageMove(-1, Selection::noSel, true);
8e54aaed
RD
4904 break;
4905 case SCI_STUTTEREDPAGEUPEXTEND:
9e96e16f 4906 PageMove(-1, Selection::selStream, true);
8e54aaed
RD
4907 break;
4908 case SCI_STUTTEREDPAGEDOWN:
9e96e16f 4909 PageMove(1, Selection::noSel, true);
8e54aaed
RD
4910 break;
4911 case SCI_STUTTEREDPAGEDOWNEXTEND:
9e96e16f 4912 PageMove(1, Selection::selStream, true);
8e54aaed 4913 break;
9ce192d4 4914 case SCI_PAGEUP:
9e730a78 4915 PageMove(-1);
9ce192d4
RD
4916 break;
4917 case SCI_PAGEUPEXTEND:
9e96e16f 4918 PageMove(-1, Selection::selStream);
8e54aaed
RD
4919 break;
4920 case SCI_PAGEUPRECTEXTEND:
9e96e16f 4921 PageMove(-1, Selection::selRectangle);
9ce192d4
RD
4922 break;
4923 case SCI_PAGEDOWN:
4924 PageMove(1);
4925 break;
4926 case SCI_PAGEDOWNEXTEND:
9e96e16f 4927 PageMove(1, Selection::selStream);
8e54aaed
RD
4928 break;
4929 case SCI_PAGEDOWNRECTEXTEND:
9e96e16f 4930 PageMove(1, Selection::selRectangle);
9ce192d4
RD
4931 break;
4932 case SCI_EDITTOGGLEOVERTYPE:
4933 inOverstrike = !inOverstrike;
4934 DropCaret();
4935 ShowCaretAtCurrentPosition();
d134f170 4936 NotifyUpdateUI();
9ce192d4 4937 break;
9e730a78 4938 case SCI_CANCEL: // Cancel any modes - handled in subclass
9ce192d4 4939 // Also unselect text
d134f170 4940 CancelModes();
9ce192d4
RD
4941 break;
4942 case SCI_DELETEBACK:
1a2fb4cd 4943 DelCharBack(true);
1e9bafca
RD
4944 if (!caretSticky) {
4945 SetLastXChosen();
4946 }
1a2fb4cd
RD
4947 EnsureCaretVisible();
4948 break;
4949 case SCI_DELETEBACKNOTLINE:
4950 DelCharBack(false);
1e9bafca
RD
4951 if (!caretSticky) {
4952 SetLastXChosen();
4953 }
9ce192d4
RD
4954 EnsureCaretVisible();
4955 break;
4956 case SCI_TAB:
4957 Indent(true);
1e9bafca
RD
4958 if (!caretSticky) {
4959 SetLastXChosen();
4960 }
d134f170 4961 EnsureCaretVisible();
9e96e16f 4962 ShowCaretAtCurrentPosition(); // Avoid blinking
9ce192d4
RD
4963 break;
4964 case SCI_BACKTAB:
4965 Indent(false);
1e9bafca
RD
4966 if (!caretSticky) {
4967 SetLastXChosen();
4968 }
d134f170 4969 EnsureCaretVisible();
9e96e16f 4970 ShowCaretAtCurrentPosition(); // Avoid blinking
9ce192d4
RD
4971 break;
4972 case SCI_NEWLINE:
a834585d 4973 NewLine();
9ce192d4
RD
4974 break;
4975 case SCI_FORMFEED:
4976 AddChar('\f');
4977 break;
4978 case SCI_VCHOME:
9e96e16f 4979 MovePositionTo(pdoc->VCHomePosition(sel.MainCaret()));
9ce192d4
RD
4980 SetLastXChosen();
4981 break;
4982 case SCI_VCHOMEEXTEND:
9e96e16f 4983 MovePositionTo(pdoc->VCHomePosition(sel.MainCaret()), Selection::selStream);
8e54aaed
RD
4984 SetLastXChosen();
4985 break;
4986 case SCI_VCHOMERECTEXTEND:
9e96e16f 4987 MovePositionTo(pdoc->VCHomePosition(sel.MainCaret()), Selection::selRectangle);
9ce192d4
RD
4988 SetLastXChosen();
4989 break;
9e730a78 4990 case SCI_VCHOMEWRAP: {
9e96e16f
RD
4991 SelectionPosition homePos = SelectionPosition(pdoc->VCHomePosition(sel.MainCaret()));
4992 SelectionPosition viewLineStart = MovePositionSoVisible(StartEndDisplayLine(sel.MainCaret(), true), -1);
4993 if ((viewLineStart < sel.RangeMain().caret) && (viewLineStart > homePos))
9e730a78
RD
4994 homePos = viewLineStart;
4995
4996 MovePositionTo(homePos);
4997 SetLastXChosen();
4998 }
4999 break;
5000 case SCI_VCHOMEWRAPEXTEND: {
9e96e16f
RD
5001 SelectionPosition homePos = SelectionPosition(pdoc->VCHomePosition(sel.MainCaret()));
5002 SelectionPosition viewLineStart = MovePositionSoVisible(StartEndDisplayLine(sel.MainCaret(), true), -1);
5003 if ((viewLineStart < sel.RangeMain().caret) && (viewLineStart > homePos))
9e730a78
RD
5004 homePos = viewLineStart;
5005
9e96e16f 5006 MovePositionTo(homePos, Selection::selStream);
9e730a78
RD
5007 SetLastXChosen();
5008 }
5009 break;
9ce192d4 5010 case SCI_ZOOMIN:
a834585d 5011 if (vs.zoomLevel < 20) {
9ce192d4 5012 vs.zoomLevel++;
a834585d
RD
5013 InvalidateStyleRedraw();
5014 NotifyZoom();
5015 }
9ce192d4
RD
5016 break;
5017 case SCI_ZOOMOUT:
a834585d 5018 if (vs.zoomLevel > -10) {
9ce192d4 5019 vs.zoomLevel--;
a834585d
RD
5020 InvalidateStyleRedraw();
5021 NotifyZoom();
5022 }
9ce192d4
RD
5023 break;
5024 case SCI_DELWORDLEFT: {
9e96e16f
RD
5025 int startWord = pdoc->NextWordStart(sel.MainCaret(), -1);
5026 pdoc->DeleteChars(startWord, sel.MainCaret() - startWord);
5027 sel.RangeMain().ClearVirtualSpace();
f6bcfd97 5028 SetLastXChosen();
9ce192d4
RD
5029 }
5030 break;
5031 case SCI_DELWORDRIGHT: {
9e96e16f
RD
5032 UndoGroup ug(pdoc);
5033 sel.RangeMain().caret = SelectionPosition(
5034 InsertSpace(sel.RangeMain().caret.Position(), sel.RangeMain().caret.VirtualSpace()));
5035 int endWord = pdoc->NextWordStart(sel.MainCaret(), 1);
5036 pdoc->DeleteChars(sel.MainCaret(), endWord - sel.MainCaret());
f6bcfd97
BP
5037 }
5038 break;
7e0c58e9 5039 case SCI_DELWORDRIGHTEND: {
9e96e16f
RD
5040 UndoGroup ug(pdoc);
5041 sel.RangeMain().caret = SelectionPosition(
5042 InsertSpace(sel.RangeMain().caret.Position(), sel.RangeMain().caret.VirtualSpace()));
5043 int endWord = pdoc->NextWordEnd(sel.MainCaret(), 1);
5044 pdoc->DeleteChars(sel.MainCaret(), endWord - sel.MainCaret());
7e0c58e9
RD
5045 }
5046 break;
65ec6247 5047 case SCI_DELLINELEFT: {
9e96e16f 5048 int line = pdoc->LineFromPosition(sel.MainCaret());
65ec6247 5049 int start = pdoc->LineStart(line);
9e96e16f
RD
5050 pdoc->DeleteChars(start, sel.MainCaret() - start);
5051 sel.RangeMain().ClearVirtualSpace();
65ec6247
RD
5052 SetLastXChosen();
5053 }
5054 break;
5055 case SCI_DELLINERIGHT: {
9e96e16f 5056 int line = pdoc->LineFromPosition(sel.MainCaret());
65ec6247 5057 int end = pdoc->LineEnd(line);
9e96e16f 5058 pdoc->DeleteChars(sel.MainCaret(), end - sel.MainCaret());
65ec6247
RD
5059 }
5060 break;
e14d10b0 5061 case SCI_LINECOPY: {
9e96e16f
RD
5062 int lineStart = pdoc->LineFromPosition(SelectionStart().Position());
5063 int lineEnd = pdoc->LineFromPosition(SelectionEnd().Position());
e14d10b0 5064 CopyRangeToClipboard(pdoc->LineStart(lineStart),
7e0c58e9 5065 pdoc->LineStart(lineEnd + 1));
e14d10b0
RD
5066 }
5067 break;
f6bcfd97 5068 case SCI_LINECUT: {
9e96e16f
RD
5069 int lineStart = pdoc->LineFromPosition(SelectionStart().Position());
5070 int lineEnd = pdoc->LineFromPosition(SelectionEnd().Position());
f6bcfd97 5071 int start = pdoc->LineStart(lineStart);
d134f170
RD
5072 int end = pdoc->LineStart(lineEnd + 1);
5073 SetSelection(start, end);
f6bcfd97 5074 Cut();
e14d10b0 5075 SetLastXChosen();
f6bcfd97
BP
5076 }
5077 break;
5078 case SCI_LINEDELETE: {
9e96e16f 5079 int line = pdoc->LineFromPosition(sel.MainCaret());
f6bcfd97 5080 int start = pdoc->LineStart(line);
d134f170
RD
5081 int end = pdoc->LineStart(line + 1);
5082 pdoc->DeleteChars(start, end - start);
9ce192d4
RD
5083 }
5084 break;
f6bcfd97
BP
5085 case SCI_LINETRANSPOSE:
5086 LineTranspose();
5087 break;
9e730a78 5088 case SCI_LINEDUPLICATE:
1e9bafca
RD
5089 Duplicate(true);
5090 break;
5091 case SCI_SELECTIONDUPLICATE:
5092 Duplicate(false);
9e730a78 5093 break;
f6bcfd97
BP
5094 case SCI_LOWERCASE:
5095 ChangeCaseOfSelection(false);
5096 break;
5097 case SCI_UPPERCASE:
5098 ChangeCaseOfSelection(true);
5099 break;
65ec6247 5100 case SCI_WORDPARTLEFT:
9e96e16f 5101 MovePositionTo(MovePositionSoVisible(pdoc->WordPartLeft(sel.MainCaret()), -1));
65ec6247
RD
5102 SetLastXChosen();
5103 break;
5104 case SCI_WORDPARTLEFTEXTEND:
9e96e16f 5105 MovePositionTo(MovePositionSoVisible(pdoc->WordPartLeft(sel.MainCaret()), -1), Selection::selStream);
65ec6247
RD
5106 SetLastXChosen();
5107 break;
5108 case SCI_WORDPARTRIGHT:
9e96e16f 5109 MovePositionTo(MovePositionSoVisible(pdoc->WordPartRight(sel.MainCaret()), 1));
65ec6247
RD
5110 SetLastXChosen();
5111 break;
5112 case SCI_WORDPARTRIGHTEXTEND:
9e96e16f 5113 MovePositionTo(MovePositionSoVisible(pdoc->WordPartRight(sel.MainCaret()), 1), Selection::selStream);
65ec6247
RD
5114 SetLastXChosen();
5115 break;
f114b858
RD
5116 case SCI_HOMEDISPLAY:
5117 MovePositionTo(MovePositionSoVisible(
9e96e16f 5118 StartEndDisplayLine(sel.MainCaret(), true), -1));
f114b858
RD
5119 SetLastXChosen();
5120 break;
5121 case SCI_HOMEDISPLAYEXTEND:
5122 MovePositionTo(MovePositionSoVisible(
9e96e16f 5123 StartEndDisplayLine(sel.MainCaret(), true), -1), Selection::selStream);
f114b858
RD
5124 SetLastXChosen();
5125 break;
5126 case SCI_LINEENDDISPLAY:
5127 MovePositionTo(MovePositionSoVisible(
9e96e16f 5128 StartEndDisplayLine(sel.MainCaret(), false), 1));
f114b858
RD
5129 SetLastXChosen();
5130 break;
5131 case SCI_LINEENDDISPLAYEXTEND:
5132 MovePositionTo(MovePositionSoVisible(
9e96e16f 5133 StartEndDisplayLine(sel.MainCaret(), false), 1), Selection::selStream);
f114b858
RD
5134 SetLastXChosen();
5135 break;
9ce192d4
RD
5136 }
5137 return 0;
5138}
5139
5140int Editor::KeyDefault(int, int) {
5141 return 0;
5142}
5143
65ec6247
RD
5144int Editor::KeyDown(int key, bool shift, bool ctrl, bool alt, bool *consumed) {
5145 DwellEnd(false);
9ce192d4 5146 int modifiers = (shift ? SCI_SHIFT : 0) | (ctrl ? SCI_CTRL : 0) |
7e0c58e9 5147 (alt ? SCI_ALT : 0);
9ce192d4 5148 int msg = kmap.Find(key, modifiers);
65ec6247
RD
5149 if (msg) {
5150 if (consumed)
5151 *consumed = true;
9ce192d4 5152 return WndProc(msg, 0, 0);
65ec6247
RD
5153 } else {
5154 if (consumed)
5155 *consumed = false;
9ce192d4 5156 return KeyDefault(key, modifiers);
65ec6247 5157 }
9ce192d4
RD
5158}
5159
d134f170
RD
5160void Editor::SetWhitespaceVisible(int view) {
5161 vs.viewWhitespace = static_cast<WhiteSpaceVisibility>(view);
9ce192d4
RD
5162}
5163
d134f170 5164int Editor::GetWhitespaceVisible() {
9ce192d4
RD
5165 return vs.viewWhitespace;
5166}
5167
5168void Editor::Indent(bool forwards) {
9e96e16f
RD
5169 for (size_t r=0; r<sel.Count(); r++) {
5170 int lineOfAnchor = pdoc->LineFromPosition(sel.Range(r).anchor.Position());
5171 int caretPosition = sel.Range(r).caret.Position();
5172 int lineCurrentPos = pdoc->LineFromPosition(caretPosition);
5173 if (lineOfAnchor == lineCurrentPos) {
5174 if (forwards) {
5175 UndoGroup ug(pdoc);
5176 pdoc->DeleteChars(sel.Range(r).Start().Position(), sel.Range(r).Length());
5177 caretPosition = sel.Range(r).caret.Position();
5178 if (pdoc->GetColumn(caretPosition) <= pdoc->GetColumn(pdoc->GetLineIndentPosition(lineCurrentPos)) &&
5179 pdoc->tabIndents) {
5180 int indentation = pdoc->GetLineIndentation(lineCurrentPos);
5181 int indentationStep = pdoc->IndentSize();
5182 pdoc->SetLineIndentation(lineCurrentPos, indentation + indentationStep - indentation % indentationStep);
5183 sel.Range(r) = SelectionRange(pdoc->GetLineIndentPosition(lineCurrentPos));
65ec6247 5184 } else {
9e96e16f
RD
5185 if (pdoc->useTabs) {
5186 pdoc->InsertChar(caretPosition, '\t');
5187 sel.Range(r) = SelectionRange(caretPosition+1);
5188 } else {
5189 int numSpaces = (pdoc->tabInChars) -
5190 (pdoc->GetColumn(caretPosition) % (pdoc->tabInChars));
5191 if (numSpaces < 1)
5192 numSpaces = pdoc->tabInChars;
5193 for (int i = 0; i < numSpaces; i++) {
5194 pdoc->InsertChar(caretPosition + i, ' ');
5195 }
5196 sel.Range(r) = SelectionRange(caretPosition+numSpaces);
65ec6247 5197 }
9e96e16f
RD
5198 }
5199 } else {
5200 if (pdoc->GetColumn(caretPosition) <= pdoc->GetLineIndentation(lineCurrentPos) &&
5201 pdoc->tabIndents) {
5202 UndoGroup ug(pdoc);
5203 int indentation = pdoc->GetLineIndentation(lineCurrentPos);
5204 int indentationStep = pdoc->IndentSize();
5205 pdoc->SetLineIndentation(lineCurrentPos, indentation - indentationStep);
5206 SetEmptySelection(pdoc->GetLineIndentPosition(lineCurrentPos));
5207 } else {
5208 int newColumn = ((pdoc->GetColumn(caretPosition) - 1) / pdoc->tabInChars) *
5209 pdoc->tabInChars;
5210 if (newColumn < 0)
5211 newColumn = 0;
5212 int newPos = caretPosition;
5213 while (pdoc->GetColumn(newPos) > newColumn)
5214 newPos--;
5215 sel.Range(r) = SelectionRange(newPos);
65ec6247
RD
5216 }
5217 }
9e96e16f
RD
5218 } else { // Multiline
5219 int anchorPosOnLine = sel.Range(r).anchor.Position() - pdoc->LineStart(lineOfAnchor);
5220 int currentPosPosOnLine = caretPosition - pdoc->LineStart(lineCurrentPos);
5221 // Multiple lines selected so indent / dedent
5222 int lineTopSel = Platform::Minimum(lineOfAnchor, lineCurrentPos);
5223 int lineBottomSel = Platform::Maximum(lineOfAnchor, lineCurrentPos);
5224 if (pdoc->LineStart(lineBottomSel) == sel.Range(r).anchor.Position() || pdoc->LineStart(lineBottomSel) == caretPosition)
5225 lineBottomSel--; // If not selecting any characters on a line, do not indent
5226 {
5227 UndoGroup ug(pdoc);
5228 pdoc->Indent(forwards, lineBottomSel, lineTopSel);
5229 }
5230 if (lineOfAnchor < lineCurrentPos) {
5231 if (currentPosPosOnLine == 0)
5232 sel.Range(r) = SelectionRange(pdoc->LineStart(lineCurrentPos), pdoc->LineStart(lineOfAnchor));
5233 else
5234 sel.Range(r) = SelectionRange(pdoc->LineStart(lineCurrentPos + 1), pdoc->LineStart(lineOfAnchor));
65ec6247 5235 } else {
9e96e16f
RD
5236 if (anchorPosOnLine == 0)
5237 sel.Range(r) = SelectionRange(pdoc->LineStart(lineCurrentPos), pdoc->LineStart(lineOfAnchor));
5238 else
5239 sel.Range(r) = SelectionRange(pdoc->LineStart(lineCurrentPos), pdoc->LineStart(lineOfAnchor + 1));
d134f170 5240 }
d134f170 5241 }
9ce192d4
RD
5242 }
5243}
5244
65ec6247
RD
5245/**
5246 * Search of a text in the document, in the given range.
5247 * @return The position of the found text, -1 if not found.
5248 */
5249long Editor::FindText(
7e0c58e9
RD
5250 uptr_t wParam, ///< Search modes : @c SCFIND_MATCHCASE, @c SCFIND_WHOLEWORD,
5251 ///< @c SCFIND_WORDSTART, @c SCFIND_REGEXP or @c SCFIND_POSIX.
5252 sptr_t lParam) { ///< @c TextToFind structure: The text to search for in the given range.
65ec6247 5253
9e96e16f 5254 Sci_TextToFind *ft = reinterpret_cast<Sci_TextToFind *>(lParam);
88a8b04e 5255 int lengthFound = istrlen(ft->lpstrText);
9ce192d4 5256 int pos = pdoc->FindText(ft->chrg.cpMin, ft->chrg.cpMax, ft->lpstrText,
7e0c58e9
RD
5257 (wParam & SCFIND_MATCHCASE) != 0,
5258 (wParam & SCFIND_WHOLEWORD) != 0,
5259 (wParam & SCFIND_WORDSTART) != 0,
5260 (wParam & SCFIND_REGEXP) != 0,
9e96e16f 5261 wParam,
7e0c58e9 5262 &lengthFound);
9ce192d4 5263 if (pos != -1) {
b8b0e402
RD
5264 ft->chrgText.cpMin = pos;
5265 ft->chrgText.cpMax = pos + lengthFound;
9ce192d4
RD
5266 }
5267 return pos;
5268}
5269
65ec6247
RD
5270/**
5271 * Relocatable search support : Searches relative to current selection
5272 * point and sets the selection to the found text range with
5273 * each search.
5274 */
5275/**
5276 * Anchor following searches at current selection start: This allows
5277 * multiple incremental interactive searches to be macro recorded
5278 * while still setting the selection to found text so the find/select
5279 * operation is self-contained.
5280 */
9ce192d4 5281void Editor::SearchAnchor() {
9e96e16f 5282 searchAnchor = SelectionStart().Position();
9ce192d4
RD
5283}
5284
65ec6247
RD
5285/**
5286 * Find text from current search anchor: Must call @c SearchAnchor first.
5287 * Used for next text and previous text requests.
5288 * @return The position of the found text, -1 if not found.
5289 */
5290long Editor::SearchText(
8e54aaed
RD
5291 unsigned int iMessage, ///< Accepts both @c SCI_SEARCHNEXT and @c SCI_SEARCHPREV.
5292 uptr_t wParam, ///< Search modes : @c SCFIND_MATCHCASE, @c SCFIND_WHOLEWORD,
7e0c58e9 5293 ///< @c SCFIND_WORDSTART, @c SCFIND_REGEXP or @c SCFIND_POSIX.
a834585d 5294 sptr_t lParam) { ///< The text to search for.
65ec6247 5295
9ce192d4
RD
5296 const char *txt = reinterpret_cast<char *>(lParam);
5297 int pos;
88a8b04e 5298 int lengthFound = istrlen(txt);
9ce192d4 5299 if (iMessage == SCI_SEARCHNEXT) {
d134f170 5300 pos = pdoc->FindText(searchAnchor, pdoc->Length(), txt,
7e0c58e9
RD
5301 (wParam & SCFIND_MATCHCASE) != 0,
5302 (wParam & SCFIND_WHOLEWORD) != 0,
5303 (wParam & SCFIND_WORDSTART) != 0,
5304 (wParam & SCFIND_REGEXP) != 0,
9e96e16f 5305 wParam,
7e0c58e9 5306 &lengthFound);
9ce192d4 5307 } else {
d134f170 5308 pos = pdoc->FindText(searchAnchor, 0, txt,
7e0c58e9
RD
5309 (wParam & SCFIND_MATCHCASE) != 0,
5310 (wParam & SCFIND_WHOLEWORD) != 0,
5311 (wParam & SCFIND_WORDSTART) != 0,
5312 (wParam & SCFIND_REGEXP) != 0,
9e96e16f 5313 wParam,
7e0c58e9 5314 &lengthFound);
9ce192d4
RD
5315 }
5316
5317 if (pos != -1) {
65ec6247 5318 SetSelection(pos, pos + lengthFound);
9ce192d4
RD
5319 }
5320
5321 return pos;
5322}
5323
65ec6247
RD
5324/**
5325 * Search for text in the target range of the document.
5326 * @return The position of the found text, -1 if not found.
5327 */
5328long Editor::SearchInTarget(const char *text, int length) {
5329 int lengthFound = length;
5330 int pos = pdoc->FindText(targetStart, targetEnd, text,
7e0c58e9
RD
5331 (searchFlags & SCFIND_MATCHCASE) != 0,
5332 (searchFlags & SCFIND_WHOLEWORD) != 0,
5333 (searchFlags & SCFIND_WORDSTART) != 0,
5334 (searchFlags & SCFIND_REGEXP) != 0,
9e96e16f 5335 searchFlags,
7e0c58e9 5336 &lengthFound);
65ec6247
RD
5337 if (pos != -1) {
5338 targetStart = pos;
5339 targetEnd = pos + lengthFound;
5340 }
5341 return pos;
5342}
5343
9ce192d4
RD
5344void Editor::GoToLine(int lineNo) {
5345 if (lineNo > pdoc->LinesTotal())
5346 lineNo = pdoc->LinesTotal();
5347 if (lineNo < 0)
5348 lineNo = 0;
5349 SetEmptySelection(pdoc->LineStart(lineNo));
5350 ShowCaretAtCurrentPosition();
5351 EnsureCaretVisible();
5352}
5353
5354static bool Close(Point pt1, Point pt2) {
5355 if (abs(pt1.x - pt2.x) > 3)
5356 return false;
5357 if (abs(pt1.y - pt2.y) > 3)
5358 return false;
5359 return true;
5360}
5361
5362char *Editor::CopyRange(int start, int end) {
5363 char *text = 0;
5364 if (start < end) {
5365 int len = end - start;
5366 text = new char[len + 1];
9e96e16f
RD
5367 for (int i = 0; i < len; i++) {
5368 text[i] = pdoc->CharAt(start + i);
9ce192d4 5369 }
9e96e16f 5370 text[len] = '\0';
9ce192d4
RD
5371 }
5372 return text;
5373}
5374
9e96e16f
RD
5375void Editor::CopySelectionRange(SelectionText *ss, bool allowLineCopy) {
5376 if (sel.Empty()) {
5377 if (allowLineCopy) {
5378 int currentLine = pdoc->LineFromPosition(sel.MainCaret());
5379 int start = pdoc->LineStart(currentLine);
5380 int end = pdoc->LineEnd(currentLine);
5381
5382 char *text = CopyRange(start, end);
5383 int textLen = text ? strlen(text) : 0;
5384 // include room for \r\n\0
5385 textLen += 3;
5386 char *textWithEndl = new char[textLen];
5387 textWithEndl[0] = '\0';
5388 if (text)
5389 strncat(textWithEndl, text, textLen);
5390 if (pdoc->eolMode != SC_EOL_LF)
5391 strncat(textWithEndl, "\r", textLen);
5392 if (pdoc->eolMode != SC_EOL_CR)
5393 strncat(textWithEndl, "\n", textLen);
5394 ss->Set(textWithEndl, strlen(textWithEndl) + 1,
5395 pdoc->dbcsCodePage, vs.styles[STYLE_DEFAULT].characterSet, false, true);
5396 delete []text;
5397 }
8e54aaed 5398 } else {
9e96e16f
RD
5399 int delimiterLength = 0;
5400 if (sel.selType == Selection::selRectangle) {
5401 if (pdoc->eolMode == SC_EOL_CRLF) {
5402 delimiterLength = 2;
5403 } else {
5404 delimiterLength = 1;
8e54aaed 5405 }
9ce192d4 5406 }
9e96e16f
RD
5407 int size = sel.Length() + delimiterLength * sel.Count();
5408 char *text = new char[size + 1];
5409 int j = 0;
b5fe65c8 5410 wxVector<SelectionRange> rangesInOrder = sel.RangesCopy();
9e96e16f 5411 if (sel.selType == Selection::selRectangle)
b5fe65c8 5412 wxVectorSort(rangesInOrder);
9e96e16f
RD
5413 for (size_t r=0; r<rangesInOrder.size(); r++) {
5414 SelectionRange current = rangesInOrder[r];
5415 for (int i = current.Start().Position();
5416 i < current.End().Position();
5417 i++) {
5418 text[j++] = pdoc->CharAt(i);
5419 }
5420 if (sel.selType == Selection::selRectangle) {
5421 if (pdoc->eolMode != SC_EOL_LF) {
5422 text[j++] = '\r';
5423 }
5424 if (pdoc->eolMode != SC_EOL_CR) {
5425 text[j++] = '\n';
9ce192d4 5426 }
9ce192d4
RD
5427 }
5428 }
9e96e16f 5429 text[size] = '\0';
b8193d80 5430 ss->Set(text, size + 1, pdoc->dbcsCodePage,
9e96e16f 5431 vs.styles[STYLE_DEFAULT].characterSet, sel.IsRectangular(), sel.selType == Selection::selLines);
9ce192d4 5432 }
e14d10b0
RD
5433}
5434
5435void Editor::CopyRangeToClipboard(int start, int end) {
5436 start = pdoc->ClampPositionIntoDocument(start);
5437 end = pdoc->ClampPositionIntoDocument(end);
5438 SelectionText selectedText;
591d01be 5439 selectedText.Set(CopyRange(start, end), end - start + 1,
9e96e16f 5440 pdoc->dbcsCodePage, vs.styles[STYLE_DEFAULT].characterSet, false, false);
e14d10b0
RD
5441 CopyToClipboard(selectedText);
5442}
5443
5444void Editor::CopyText(int length, const char *text) {
5445 SelectionText selectedText;
b8193d80 5446 selectedText.Copy(text, length + 1,
9e96e16f 5447 pdoc->dbcsCodePage, vs.styles[STYLE_DEFAULT].characterSet, false, false);
e14d10b0 5448 CopyToClipboard(selectedText);
9ce192d4
RD
5449}
5450
9e96e16f
RD
5451void Editor::SetDragPosition(SelectionPosition newPos) {
5452 if (newPos.Position() >= 0) {
d134f170 5453 newPos = MovePositionOutsideChar(newPos, 1);
9ce192d4
RD
5454 posDrop = newPos;
5455 }
9e96e16f 5456 if (!(posDrag == newPos)) {
9ce192d4
RD
5457 caret.on = true;
5458 SetTicking(true);
5459 InvalidateCaret();
5460 posDrag = newPos;
5461 InvalidateCaret();
5462 }
5463}
5464
65ec6247
RD
5465void Editor::DisplayCursor(Window::Cursor c) {
5466 if (cursorMode == SC_CURSORNORMAL)
5467 wMain.SetCursor(c);
5468 else
5469 wMain.SetCursor(static_cast<Window::Cursor>(cursorMode));
5470}
5471
7e0c58e9
RD
5472bool Editor::DragThreshold(Point ptStart, Point ptNow) {
5473 int xMove = ptStart.x - ptNow.x;
5474 int yMove = ptStart.y - ptNow.y;
5475 int distanceSquared = xMove * xMove + yMove * yMove;
5476 return distanceSquared > 16;
5477}
5478
9ce192d4
RD
5479void Editor::StartDrag() {
5480 // Always handled by subclasses
5481 //SetMouseCapture(true);
65ec6247 5482 //DisplayCursor(Window::cursorArrow);
9ce192d4
RD
5483}
5484
9e96e16f 5485void Editor::DropAt(SelectionPosition position, const char *value, bool moving, bool rectangular) {
7e0c58e9
RD
5486 //Platform::DebugPrintf("DropAt %d %d\n", inDragDrop, position);
5487 if (inDragDrop == ddDragging)
9ce192d4
RD
5488 dropWentOutside = false;
5489
9e96e16f 5490 bool positionWasInSelection = PositionInSelection(position.Position());
9ce192d4 5491
d134f170
RD
5492 bool positionOnEdgeOfSelection =
5493 (position == SelectionStart()) || (position == SelectionEnd());
f6bcfd97 5494
9e96e16f 5495 if ((inDragDrop != ddDragging) || !(positionWasInSelection) ||
d134f170 5496 (positionOnEdgeOfSelection && !moving)) {
9ce192d4 5497
9e96e16f
RD
5498 SelectionPosition selStart = SelectionStart();
5499 SelectionPosition selEnd = SelectionEnd();
d134f170 5500
9e96e16f 5501 UndoGroup ug(pdoc);
9ce192d4 5502
9e96e16f 5503 SelectionPosition positionAfterDeletion = position;
7e0c58e9 5504 if ((inDragDrop == ddDragging) && moving) {
9ce192d4 5505 // Remove dragged out text
9e96e16f
RD
5506 if (rectangular || sel.selType == Selection::selLines) {
5507 for (size_t r=0; r<sel.Count(); r++) {
5508 if (position >= sel.Range(r).Start()) {
5509 if (position > sel.Range(r).End()) {
5510 positionAfterDeletion.Add(-sel.Range(r).Length());
9ce192d4 5511 } else {
9e96e16f 5512 positionAfterDeletion.Add(-SelectionRange(position, sel.Range(r).Start()).Length());
9ce192d4
RD
5513 }
5514 }
5515 }
5516 } else {
5517 if (position > selStart) {
9e96e16f 5518 positionAfterDeletion.Add(-SelectionRange(selEnd, selStart).Length());
9ce192d4
RD
5519 }
5520 }
5521 ClearSelection();
5522 }
5523 position = positionAfterDeletion;
d134f170 5524
9ce192d4 5525 if (rectangular) {
88a8b04e 5526 PasteRectangular(position, value, istrlen(value));
9ce192d4 5527 // Should try to select new rectangle but it may not be a rectangle now so just select the drop position
9e96e16f 5528 SetEmptySelection(position);
8e54aaed 5529 } else {
9e96e16f
RD
5530 position = MovePositionOutsideChar(position, sel.MainCaret() - position.Position());
5531 position = SelectionPosition(InsertSpace(position.Position(), position.VirtualSpace()));
5532 if (pdoc->InsertCString(position.Position(), value)) {
5533 SelectionPosition posAfterInsertion = position;
5534 posAfterInsertion.Add(istrlen(value));
5535 SetSelection(posAfterInsertion, position);
5536 }
9ce192d4 5537 }
9e96e16f
RD
5538 } else if (inDragDrop == ddDragging) {
5539 SetEmptySelection(position);
5540 }
5541}
5542
5543/**
5544 * @return true if given position is inside the selection,
5545 */
5546bool Editor::PositionInSelection(int pos) {
5547 pos = MovePositionOutsideChar(pos, sel.MainCaret() - pos);
5548 for (size_t r=0; r<sel.Count(); r++) {
5549 if (sel.Range(r).Contains(pos))
5550 return true;
9ce192d4 5551 }
9e96e16f 5552 return false;
9ce192d4
RD
5553}
5554
5555bool Editor::PointInSelection(Point pt) {
9e96e16f
RD
5556 SelectionPosition pos = SPositionFromLocation(pt);
5557 int xPos = XFromPosition(pos);
5558 for (size_t r=0; r<sel.Count(); r++) {
5559 SelectionRange range = sel.Range(r);
5560 if (range.Contains(pos)) {
5561 bool hit = true;
5562 if (pos == range.Start()) {
5563 // see if just before selection
5564 if (pt.x < xPos) {
5565 hit = false;
5566 }
8e54aaed 5567 }
9e96e16f
RD
5568 if (pos == range.End()) {
5569 // see if just after selection
5570 if (pt.x > xPos) {
5571 hit = false;
5572 }
8e54aaed 5573 }
9e96e16f
RD
5574 if (hit)
5575 return true;
9ce192d4 5576 }
9ce192d4
RD
5577 }
5578 return false;
5579}
5580
5581bool Editor::PointInSelMargin(Point pt) {
5582 // Really means: "Point in a margin"
5583 if (vs.fixedColumnWidth > 0) { // There is a margin
5584 PRectangle rcSelMargin = GetClientRectangle();
5585 rcSelMargin.right = vs.fixedColumnWidth - vs.leftMarginWidth;
5586 return rcSelMargin.Contains(pt);
5587 } else {
5588 return false;
5589 }
5590}
5591
65ec6247
RD
5592void Editor::LineSelection(int lineCurrent_, int lineAnchor_) {
5593 if (lineAnchor_ < lineCurrent_) {
5594 SetSelection(pdoc->LineStart(lineCurrent_ + 1),
7e0c58e9 5595 pdoc->LineStart(lineAnchor_));
65ec6247
RD
5596 } else if (lineAnchor_ > lineCurrent_) {
5597 SetSelection(pdoc->LineStart(lineCurrent_),
7e0c58e9 5598 pdoc->LineStart(lineAnchor_ + 1));
65ec6247
RD
5599 } else { // Same line, select it
5600 SetSelection(pdoc->LineStart(lineAnchor_ + 1),
7e0c58e9 5601 pdoc->LineStart(lineAnchor_));
65ec6247
RD
5602 }
5603}
5604
5605void Editor::DwellEnd(bool mouseMoved) {
5606 if (mouseMoved)
5607 ticksToDwell = dwellDelay;
5608 else
5609 ticksToDwell = SC_TIME_FOREVER;
5610 if (dwelling && (dwellDelay < SC_TIME_FOREVER)) {
5611 dwelling = false;
5612 NotifyDwelling(ptMouseLast, dwelling);
5613 }
5614}
5615
9e96e16f
RD
5616static bool AllowVirtualSpace(int virtualSpaceOptions, bool rectangular) {
5617 return ((virtualSpaceOptions & SCVS_USERACCESSIBLE) != 0)
5618 || (rectangular && ((virtualSpaceOptions & SCVS_RECTANGULARSELECTION) != 0));
5619}
5620
9ce192d4 5621void Editor::ButtonDown(Point pt, unsigned int curTime, bool shift, bool ctrl, bool alt) {
7e0c58e9 5622 //Platform::DebugPrintf("ButtonDown %d %d = %d alt=%d %d\n", curTime, lastClickTime, curTime - lastClickTime, alt, inDragDrop);
9ce192d4 5623 ptMouseLast = pt;
9e96e16f
RD
5624 SelectionPosition newPos = SPositionFromLocation(pt, false, false, AllowVirtualSpace(virtualSpaceOptions, alt));
5625 newPos = MovePositionOutsideChar(newPos, sel.MainCaret() - newPos.Position());
7e0c58e9 5626 inDragDrop = ddNone;
9e96e16f 5627 sel.SetMoveExtends(false);
d134f170 5628
9ce192d4
RD
5629 bool processed = NotifyMarginClick(pt, shift, ctrl, alt);
5630 if (processed)
65ec6247 5631 return;
d134f170 5632
9e96e16f 5633 NotifyIndicatorClick(true, newPos.Position(), shift, ctrl, alt);
7e0c58e9 5634
65ec6247
RD
5635 bool inSelMargin = PointInSelMargin(pt);
5636 if (shift & !inSelMargin) {
9e96e16f 5637 SetSelection(newPos.Position());
9ce192d4
RD
5638 }
5639 if (((curTime - lastClickTime) < Platform::DoubleClickTime()) && Close(pt, lastClick)) {
5640 //Platform::DebugPrintf("Double click %d %d = %d\n", curTime, lastClickTime, curTime - lastClickTime);
5641 SetMouseCapture(true);
9e96e16f 5642 SetEmptySelection(newPos.Position());
9ce192d4
RD
5643 bool doubleClick = false;
5644 // Stop mouse button bounce changing selection type
9e730a78 5645 if (!Platform::MouseButtonBounce() || curTime != lastClickTime) {
9ce192d4
RD
5646 if (selectionType == selChar) {
5647 selectionType = selWord;
5648 doubleClick = true;
5649 } else if (selectionType == selWord) {
5650 selectionType = selLine;
5651 } else {
5652 selectionType = selChar;
9e96e16f 5653 originalAnchorPos = sel.MainCaret();
9ce192d4
RD
5654 }
5655 }
5656
5657 if (selectionType == selWord) {
9e96e16f
RD
5658 if (sel.MainCaret() >= originalAnchorPos) { // Moved forward
5659 SetSelection(pdoc->ExtendWordSelect(sel.MainCaret(), 1),
7e0c58e9 5660 pdoc->ExtendWordSelect(originalAnchorPos, -1));
9ce192d4 5661 } else { // Moved backward
9e96e16f 5662 SetSelection(pdoc->ExtendWordSelect(sel.MainCaret(), -1),
7e0c58e9 5663 pdoc->ExtendWordSelect(originalAnchorPos, 1));
9ce192d4
RD
5664 }
5665 } else if (selectionType == selLine) {
5666 lineAnchor = LineFromLocation(pt);
5667 SetSelection(pdoc->LineStart(lineAnchor + 1), pdoc->LineStart(lineAnchor));
5668 //Platform::DebugPrintf("Triple click: %d - %d\n", anchor, currentPos);
9e730a78 5669 } else {
9e96e16f 5670 SetEmptySelection(sel.MainCaret());
9ce192d4
RD
5671 }
5672 //Platform::DebugPrintf("Double click: %d - %d\n", anchor, currentPos);
9e730a78 5673 if (doubleClick) {
7e0c58e9 5674 NotifyDoubleClick(pt, shift, ctrl, alt);
9e96e16f
RD
5675 if (PositionIsHotspot(newPos.Position()))
5676 NotifyHotSpotDoubleClicked(newPos.Position(), shift, ctrl, alt);
9e730a78 5677 }
9ce192d4 5678 } else { // Single click
65ec6247 5679 if (inSelMargin) {
9e96e16f 5680 sel.selType = Selection::selStream;
9ce192d4
RD
5681 if (ctrl) {
5682 SelectAll();
5683 lastClickTime = curTime;
65ec6247 5684 return;
9ce192d4 5685 }
9ce192d4 5686 if (!shift) {
65ec6247 5687 lineAnchor = LineFromLocation(pt);
9ce192d4 5688 // Single click in margin: select whole line
65ec6247
RD
5689 LineSelection(lineAnchor, lineAnchor);
5690 SetSelection(pdoc->LineStart(lineAnchor + 1),
7e0c58e9 5691 pdoc->LineStart(lineAnchor));
9ce192d4 5692 } else {
65ec6247 5693 // Single shift+click in margin: select from line anchor to clicked line
9e96e16f
RD
5694 if (sel.MainAnchor() > sel.MainCaret())
5695 lineAnchor = pdoc->LineFromPosition(sel.MainAnchor() - 1);
65ec6247 5696 else
9e96e16f 5697 lineAnchor = pdoc->LineFromPosition(sel.MainAnchor());
65ec6247
RD
5698 int lineStart = LineFromLocation(pt);
5699 LineSelection(lineStart, lineAnchor);
5700 //lineAnchor = lineStart; // Keep the same anchor for ButtonMove
9ce192d4 5701 }
65ec6247 5702
9e96e16f 5703 SetDragPosition(SelectionPosition(invalidPosition));
9ce192d4
RD
5704 SetMouseCapture(true);
5705 selectionType = selLine;
5706 } else {
8e54aaed 5707 if (PointIsHotspot(pt)) {
9e96e16f 5708 NotifyHotSpotClicked(newPos.Position(), shift, ctrl, alt);
9e730a78 5709 }
9ce192d4 5710 if (!shift) {
7e0c58e9
RD
5711 if (PointInSelection(pt) && !SelectionEmpty())
5712 inDragDrop = ddInitial;
5713 else
5714 inDragDrop = ddNone;
9ce192d4 5715 }
7e0c58e9
RD
5716 SetMouseCapture(true);
5717 if (inDragDrop != ddInitial) {
9e96e16f 5718 SetDragPosition(SelectionPosition(invalidPosition));
8e54aaed 5719 if (!shift) {
9e96e16f
RD
5720 if (ctrl && multipleSelection) {
5721 SelectionRange range(newPos);
5722 sel.TentativeSelection(range);
5723 InvalidateSelection(range, true);
5724 } else {
5725 InvalidateSelection(SelectionRange(newPos), true);
5726 if (sel.Count() > 1)
5727 Redraw();
5728 sel.Clear();
5729 sel.selType = alt ? Selection::selRectangle : Selection::selStream;
5730 SetSelection(newPos, newPos);
5731 }
8e54aaed 5732 }
9e96e16f
RD
5733 SelectionPosition anchorCurrent = newPos;
5734 if (shift)
5735 anchorCurrent = sel.IsRectangular() ?
5736 sel.Rectangular().anchor : sel.RangeMain().anchor;
5737 sel.selType = alt ? Selection::selRectangle : Selection::selStream;
9ce192d4 5738 selectionType = selChar;
9e96e16f
RD
5739 originalAnchorPos = sel.MainCaret();
5740 sel.Rectangular() = SelectionRange(newPos, anchorCurrent);
1e9bafca 5741 SetRectangularRange();
9ce192d4
RD
5742 }
5743 }
5744 }
5745 lastClickTime = curTime;
5746 lastXChosen = pt.x;
5747 ShowCaretAtCurrentPosition();
5748}
5749
9e730a78 5750bool Editor::PositionIsHotspot(int position) {
8e54aaed 5751 return vs.styles[pdoc->StyleAt(position) & pdoc->stylingBitsMask].hotspot;
9e730a78
RD
5752}
5753
5754bool Editor::PointIsHotspot(Point pt) {
9e96e16f 5755 int pos = PositionFromLocation(pt, true);
8e54aaed
RD
5756 if (pos == INVALID_POSITION)
5757 return false;
9e730a78
RD
5758 return PositionIsHotspot(pos);
5759}
5760
5761void Editor::SetHotSpotRange(Point *pt) {
5762 if (pt) {
5763 int pos = PositionFromLocation(*pt);
5764
5765 // If we don't limit this to word characters then the
5766 // range can encompass more than the run range and then
5767 // the underline will not be drawn properly.
8e54aaed
RD
5768 int hsStart_ = pdoc->ExtendStyleRange(pos, -1, vs.hotspotSingleLine);
5769 int hsEnd_ = pdoc->ExtendStyleRange(pos, 1, vs.hotspotSingleLine);
9e730a78
RD
5770
5771 // Only invalidate the range if the hotspot range has changed...
5772 if (hsStart_ != hsStart || hsEnd_ != hsEnd) {
5773 if (hsStart != -1) {
5774 InvalidateRange(hsStart, hsEnd);
5775 }
5776 hsStart = hsStart_;
5777 hsEnd = hsEnd_;
5778 InvalidateRange(hsStart, hsEnd);
5779 }
5780 } else {
5781 if (hsStart != -1) {
5782 int hsStart_ = hsStart;
5783 int hsEnd_ = hsEnd;
5784 hsStart = -1;
5785 hsEnd = -1;
5786 InvalidateRange(hsStart_, hsEnd_);
5787 } else {
5788 hsStart = -1;
5789 hsEnd = -1;
5790 }
5791 }
5792}
5793
5794void Editor::GetHotSpotRange(int& hsStart_, int& hsEnd_) {
5795 hsStart_ = hsStart;
5796 hsEnd_ = hsEnd;
5797}
5798
9ce192d4 5799void Editor::ButtonMove(Point pt) {
65ec6247
RD
5800 if ((ptMouseLast.x != pt.x) || (ptMouseLast.y != pt.y)) {
5801 DwellEnd(true);
5802 }
7e0c58e9 5803
9e96e16f
RD
5804 SelectionPosition movePos = SPositionFromLocation(pt, false, false,
5805 AllowVirtualSpace(virtualSpaceOptions, sel.IsRectangular()));
5806 movePos = MovePositionOutsideChar(movePos, sel.MainCaret() - movePos.Position());
7e0c58e9
RD
5807
5808 if (inDragDrop == ddInitial) {
5809 if (DragThreshold(ptMouseLast, pt)) {
5810 SetMouseCapture(false);
5811 SetDragPosition(movePos);
5812 CopySelectionRange(&drag);
5813 StartDrag();
5814 }
5815 return;
5816 }
5817
65ec6247 5818 ptMouseLast = pt;
9ce192d4
RD
5819 //Platform::DebugPrintf("Move %d %d\n", pt.x, pt.y);
5820 if (HaveMouseCapture()) {
65ec6247
RD
5821
5822 // Slow down autoscrolling/selection
5823 autoScrollTimer.ticksToWait -= timer.tickSize;
5824 if (autoScrollTimer.ticksToWait > 0)
5825 return;
5826 autoScrollTimer.ticksToWait = autoScrollDelay;
5827
5828 // Adjust selection
9e96e16f 5829 if (posDrag.IsValid()) {
9ce192d4
RD
5830 SetDragPosition(movePos);
5831 } else {
5832 if (selectionType == selChar) {
9e96e16f
RD
5833 if (sel.IsRectangular()) {
5834 sel.Rectangular() = SelectionRange(movePos, sel.Rectangular().anchor);
5835 SetSelection(movePos, sel.RangeMain().anchor);
5836 } else if (sel.Count() > 1) {
5837 SelectionRange range(movePos, sel.RangeMain().anchor);
5838 sel.TentativeSelection(range);
5839 InvalidateSelection(range, true);
5840 } else {
5841 SetSelection(movePos, sel.RangeMain().anchor);
5842 }
9ce192d4
RD
5843 } else if (selectionType == selWord) {
5844 // Continue selecting by word
9e96e16f 5845 if (movePos.Position() == originalAnchorPos) { // Didn't move
8e54aaed
RD
5846 // No need to do anything. Previously this case was lumped
5847 // in with "Moved forward", but that can be harmful in this
5848 // case: a handler for the NotifyDoubleClick re-adjusts
5849 // the selection for a fancier definition of "word" (for
5850 // example, in Perl it is useful to include the leading
5851 // '$', '%' or '@' on variables for word selection). In this
5852 // the ButtonMove() called via Tick() for auto-scrolling
5853 // could result in the fancier word selection adjustment
5854 // being unmade.
9e96e16f
RD
5855 } else if (movePos.Position() > originalAnchorPos) { // Moved forward
5856 SetSelection(pdoc->ExtendWordSelect(movePos.Position(), 1),
7e0c58e9 5857 pdoc->ExtendWordSelect(originalAnchorPos, -1));
9ce192d4 5858 } else { // Moved backward
9e96e16f 5859 SetSelection(pdoc->ExtendWordSelect(movePos.Position(), -1),
7e0c58e9 5860 pdoc->ExtendWordSelect(originalAnchorPos, 1));
9ce192d4
RD
5861 }
5862 } else {
5863 // Continue selecting by line
5864 int lineMove = LineFromLocation(pt);
65ec6247 5865 LineSelection(lineMove, lineAnchor);
9ce192d4
RD
5866 }
5867 }
65ec6247
RD
5868
5869 // Autoscroll
5870 PRectangle rcClient = GetClientRectangle();
5871 if (pt.y > rcClient.bottom) {
5872 int lineMove = cs.DisplayFromDoc(LineFromLocation(pt));
9e730a78
RD
5873 if (lineMove < 0) {
5874 lineMove = cs.DisplayFromDoc(pdoc->LinesTotal() - 1);
5875 }
9e96e16f 5876 ScrollTo(lineMove - LinesOnScreen() + 1);
65ec6247
RD
5877 Redraw();
5878 } else if (pt.y < rcClient.top) {
5879 int lineMove = cs.DisplayFromDoc(LineFromLocation(pt));
9e96e16f 5880 ScrollTo(lineMove - 1);
65ec6247
RD
5881 Redraw();
5882 }
5883 EnsureCaretVisible(false, false, true);
5884
9e96e16f 5885 if (hsStart != -1 && !PositionIsHotspot(movePos.Position()))
9e730a78
RD
5886 SetHotSpotRange(NULL);
5887
9ce192d4
RD
5888 } else {
5889 if (vs.fixedColumnWidth > 0) { // There is a margin
5890 if (PointInSelMargin(pt)) {
65ec6247
RD
5891 DisplayCursor(Window::cursorReverseArrow);
5892 return; // No need to test for selection
9ce192d4
RD
5893 }
5894 }
5895 // Display regular (drag) cursor over selection
1e9bafca 5896 if (PointInSelection(pt) && !SelectionEmpty()) {
65ec6247 5897 DisplayCursor(Window::cursorArrow);
9e730a78
RD
5898 } else if (PointIsHotspot(pt)) {
5899 DisplayCursor(Window::cursorHand);
5900 SetHotSpotRange(&pt);
5901 } else {
65ec6247 5902 DisplayCursor(Window::cursorText);
9e730a78
RD
5903 SetHotSpotRange(NULL);
5904 }
9ce192d4 5905 }
9ce192d4
RD
5906}
5907
5908void Editor::ButtonUp(Point pt, unsigned int curTime, bool ctrl) {
7e0c58e9 5909 //Platform::DebugPrintf("ButtonUp %d %d\n", HaveMouseCapture(), inDragDrop);
9e96e16f
RD
5910 SelectionPosition newPos = SPositionFromLocation(pt, false, false,
5911 AllowVirtualSpace(virtualSpaceOptions, sel.IsRectangular()));
5912 newPos = MovePositionOutsideChar(newPos, sel.MainCaret() - newPos.Position());
7e0c58e9
RD
5913 if (inDragDrop == ddInitial) {
5914 inDragDrop = ddNone;
9e96e16f 5915 SetEmptySelection(newPos.Position());
7e0c58e9 5916 }
9ce192d4
RD
5917 if (HaveMouseCapture()) {
5918 if (PointInSelMargin(pt)) {
65ec6247 5919 DisplayCursor(Window::cursorReverseArrow);
9ce192d4 5920 } else {
65ec6247 5921 DisplayCursor(Window::cursorText);
9e730a78 5922 SetHotSpotRange(NULL);
9ce192d4 5923 }
9ce192d4
RD
5924 ptMouseLast = pt;
5925 SetMouseCapture(false);
9e96e16f 5926 NotifyIndicatorClick(false, newPos.Position(), false, false, false);
7e0c58e9 5927 if (inDragDrop == ddDragging) {
9e96e16f
RD
5928 SelectionPosition selStart = SelectionStart();
5929 SelectionPosition selEnd = SelectionEnd();
9ce192d4 5930 if (selStart < selEnd) {
b8b0e402 5931 if (drag.len) {
9ce192d4 5932 if (ctrl) {
9e96e16f
RD
5933 if (pdoc->InsertString(newPos.Position(), drag.s, drag.len)) {
5934 SetSelection(newPos.Position(), newPos.Position() + drag.len);
a834585d 5935 }
9ce192d4 5936 } else if (newPos < selStart) {
9e96e16f
RD
5937 pdoc->DeleteChars(selStart.Position(), drag.len);
5938 if (pdoc->InsertString(newPos.Position(), drag.s, drag.len)) {
5939 SetSelection(newPos.Position(), newPos.Position() + drag.len);
a834585d 5940 }
9ce192d4 5941 } else if (newPos > selEnd) {
9e96e16f
RD
5942 pdoc->DeleteChars(selStart.Position(), drag.len);
5943 newPos.Add(-drag.len);
5944 if (pdoc->InsertString(newPos.Position(), drag.s, drag.len)) {
5945 SetSelection(newPos.Position(), newPos.Position() + drag.len);
a834585d 5946 }
9ce192d4 5947 } else {
9e96e16f 5948 SetEmptySelection(newPos.Position());
9ce192d4 5949 }
591d01be 5950 drag.Free();
9ce192d4
RD
5951 }
5952 selectionType = selChar;
5953 }
5954 } else {
5955 if (selectionType == selChar) {
9e96e16f
RD
5956 if (sel.Count() > 1) {
5957 sel.RangeMain() =
5958 SelectionRange(newPos, sel.Range(sel.Count() - 1).anchor);
5959 InvalidateSelection(sel.RangeMain(), true);
5960 } else {
5961 SetSelection(newPos, sel.RangeMain().anchor);
5962 }
9ce192d4 5963 }
9e96e16f 5964 sel.CommitTentative();
9ce192d4 5965 }
1e9bafca 5966 SetRectangularRange();
9ce192d4
RD
5967 lastClickTime = curTime;
5968 lastClick = pt;
5969 lastXChosen = pt.x;
9e96e16f 5970 if (sel.selType == Selection::selStream) {
1a2fb4cd
RD
5971 SetLastXChosen();
5972 }
7e0c58e9 5973 inDragDrop = ddNone;
9ce192d4
RD
5974 EnsureCaretVisible(false);
5975 }
5976}
5977
5978// Called frequently to perform background UI including
5979// caret blinking and automatic scrolling.
5980void Editor::Tick() {
5981 if (HaveMouseCapture()) {
5982 // Auto scroll
5983 ButtonMove(ptMouseLast);
5984 }
5985 if (caret.period > 0) {
5986 timer.ticksToWait -= timer.tickSize;
5987 if (timer.ticksToWait <= 0) {
5988 caret.on = !caret.on;
5989 timer.ticksToWait = caret.period;
1e9bafca
RD
5990 if (caret.active) {
5991 InvalidateCaret();
5992 }
9ce192d4
RD
5993 }
5994 }
7e0c58e9
RD
5995 if (horizontalScrollBarVisible && trackLineWidth && (lineWidthMaxSeen > scrollWidth)) {
5996 scrollWidth = lineWidthMaxSeen;
5997 SetScrollBars();
5998 }
1a2fb4cd 5999 if ((dwellDelay < SC_TIME_FOREVER) &&
9e730a78
RD
6000 (ticksToDwell > 0) &&
6001 (!HaveMouseCapture())) {
65ec6247
RD
6002 ticksToDwell -= timer.tickSize;
6003 if (ticksToDwell <= 0) {
6004 dwelling = true;
6005 NotifyDwelling(ptMouseLast, dwelling);
6006 }
6007 }
6008}
6009
8e54aaed
RD
6010bool Editor::Idle() {
6011
6012 bool idleDone;
6013
b8193d80 6014 bool wrappingDone = wrapState == eWrapNone;
8e54aaed
RD
6015
6016 if (!wrappingDone) {
6017 // Wrap lines during idle.
6018 WrapLines(false, -1);
6019 // No more wrapping
b8193d80 6020 if (wrapStart == wrapEnd)
8e54aaed
RD
6021 wrappingDone = true;
6022 }
6023
6024 // Add more idle things to do here, but make sure idleDone is
6025 // set correctly before the function returns. returning
6026 // false will stop calling this idle funtion until SetIdle() is
6027 // called again.
6028
6029 idleDone = wrappingDone; // && thatDone && theOtherThingDone...
6030
6031 return !idleDone;
6032}
6033
65ec6247
RD
6034void Editor::SetFocusState(bool focusState) {
6035 hasFocus = focusState;
6036 NotifyFocus(hasFocus);
6037 if (hasFocus) {
6038 ShowCaretAtCurrentPosition();
65ec6247 6039 } else {
9e730a78 6040 CancelModes();
65ec6247
RD
6041 DropCaret();
6042 }
9ce192d4
RD
6043}
6044
1e9bafca 6045bool Editor::PaintContains(PRectangle rc) {
7e0c58e9
RD
6046 if (rc.Empty()) {
6047 return true;
6048 } else {
6049 return rcPaint.Contains(rc);
6050 }
9ce192d4
RD
6051}
6052
1e9bafca
RD
6053bool Editor::PaintContainsMargin() {
6054 PRectangle rcSelMargin = GetClientRectangle();
6055 rcSelMargin.right = vs.fixedColumnWidth;
6056 return PaintContains(rcSelMargin);
9ce192d4
RD
6057}
6058
6059void Editor::CheckForChangeOutsidePaint(Range r) {
6060 if (paintState == painting && !paintingAllText) {
6061 //Platform::DebugPrintf("Checking range in paint %d-%d\n", r.start, r.end);
6062 if (!r.Valid())
65ec6247 6063 return;
d134f170 6064
1e9bafca 6065 PRectangle rcRange = RectangleFromRange(r.start, r.end);
9ce192d4 6066 PRectangle rcText = GetTextRectangle();
1e9bafca
RD
6067 if (rcRange.top < rcText.top) {
6068 rcRange.top = rcText.top;
9ce192d4 6069 }
1e9bafca
RD
6070 if (rcRange.bottom > rcText.bottom) {
6071 rcRange.bottom = rcText.bottom;
9ce192d4 6072 }
1e9bafca
RD
6073
6074 if (!PaintContains(rcRange)) {
6075 AbandonPaint();
9ce192d4
RD
6076 }
6077 }
6078}
6079
9ce192d4
RD
6080void Editor::SetBraceHighlight(Position pos0, Position pos1, int matchStyle) {
6081 if ((pos0 != braces[0]) || (pos1 != braces[1]) || (matchStyle != bracesMatchStyle)) {
d134f170 6082 if ((braces[0] != pos0) || (matchStyle != bracesMatchStyle)) {
9ce192d4
RD
6083 CheckForChangeOutsidePaint(Range(braces[0]));
6084 CheckForChangeOutsidePaint(Range(pos0));
6085 braces[0] = pos0;
6086 }
d134f170 6087 if ((braces[1] != pos1) || (matchStyle != bracesMatchStyle)) {
9ce192d4
RD
6088 CheckForChangeOutsidePaint(Range(braces[1]));
6089 CheckForChangeOutsidePaint(Range(pos1));
6090 braces[1] = pos1;
6091 }
6092 bracesMatchStyle = matchStyle;
6093 if (paintState == notPainting) {
6094 Redraw();
6095 }
6096 }
6097}
6098
9e96e16f
RD
6099void Editor::SetAnnotationHeights(int start, int end) {
6100 if (vs.annotationVisible) {
6101 for (int line=start; line<end; line++) {
6102 cs.SetHeight(line, pdoc->AnnotationLines(line) + 1);
6103 }
6104 }
6105}
6106
9ce192d4
RD
6107void Editor::SetDocPointer(Document *document) {
6108 //Platform::DebugPrintf("** %x setdoc to %x\n", pdoc, document);
6109 pdoc->RemoveWatcher(this, 0);
6110 pdoc->Release();
6111 if (document == NULL) {
6112 pdoc = new Document();
6113 } else {
6114 pdoc = document;
6115 }
6116 pdoc->AddRef();
9e730a78
RD
6117
6118 // Ensure all positions within document
9e96e16f 6119 sel.Clear();
9e730a78
RD
6120 targetStart = 0;
6121 targetEnd = 0;
6122
591d01be
RD
6123 braces[0] = invalidPosition;
6124 braces[1] = invalidPosition;
6125
f6bcfd97
BP
6126 // Reset the contraction state to fully shown.
6127 cs.Clear();
d134f170 6128 cs.InsertLines(0, pdoc->LinesTotal() - 1);
9e96e16f 6129 SetAnnotationHeights(0, pdoc->LinesTotal());
1a2fb4cd
RD
6130 llc.Deallocate();
6131 NeedWrapping();
f6bcfd97 6132
9ce192d4 6133 pdoc->AddWatcher(this, 0);
e14d10b0 6134 SetScrollBars();
1e9bafca 6135 Redraw();
9ce192d4
RD
6136}
6137
9e96e16f
RD
6138void Editor::SetAnnotationVisible(int visible) {
6139 if (vs.annotationVisible != visible) {
6140 bool changedFromOrToHidden = ((vs.annotationVisible != 0) != (visible != 0));
6141 vs.annotationVisible = visible;
6142 if (changedFromOrToHidden) {
6143 int dir = vs.annotationVisible ? 1 : -1;
6144 for (int line=0; line<pdoc->LinesTotal(); line++) {
6145 int annotationLines = pdoc->AnnotationLines(line);
6146 if (annotationLines > 0) {
6147 cs.SetHeight(line, cs.GetHeight(line) + annotationLines * dir);
6148 }
6149 }
6150 }
6151 }
6152}
6153
8e54aaed
RD
6154/**
6155 * Recursively expand a fold, making lines visible except where they have an unexpanded parent.
6156 */
9ce192d4
RD
6157void Editor::Expand(int &line, bool doExpand) {
6158 int lineMaxSubord = pdoc->GetLastChild(line);
6159 line++;
6160 while (line <= lineMaxSubord) {
6161 if (doExpand)
6162 cs.SetVisible(line, line, true);
6163 int level = pdoc->GetLevel(line);
6164 if (level & SC_FOLDLEVELHEADERFLAG) {
6165 if (doExpand && cs.GetExpanded(line)) {
6166 Expand(line, true);
6167 } else {
6168 Expand(line, false);
6169 }
6170 } else {
6171 line++;
6172 }
6173 }
6174}
6175
6176void Editor::ToggleContraction(int line) {
591d01be
RD
6177 if (line >= 0) {
6178 if ((pdoc->GetLevel(line) & SC_FOLDLEVELHEADERFLAG) == 0) {
6179 line = pdoc->GetFoldParent(line);
6180 if (line < 0)
6181 return;
6182 }
6183
9ce192d4
RD
6184 if (cs.GetExpanded(line)) {
6185 int lineMaxSubord = pdoc->GetLastChild(line);
6186 cs.SetExpanded(line, 0);
6187 if (lineMaxSubord > line) {
d134f170 6188 cs.SetVisible(line + 1, lineMaxSubord, false);
591d01be 6189
9e96e16f 6190 int lineCurrent = pdoc->LineFromPosition(sel.MainCaret());
591d01be
RD
6191 if (lineCurrent > line && lineCurrent <= lineMaxSubord) {
6192 // This does not re-expand the fold
6193 EnsureCaretVisible();
6194 }
6195
9ce192d4
RD
6196 SetScrollBars();
6197 Redraw();
6198 }
591d01be 6199
9ce192d4 6200 } else {
591d01be
RD
6201 if (!(cs.GetVisible(line))) {
6202 EnsureLineVisible(line, false);
6203 GoToLine(line);
6204 }
9ce192d4
RD
6205 cs.SetExpanded(line, 1);
6206 Expand(line, true);
6207 SetScrollBars();
6208 Redraw();
6209 }
6210 }
6211}
6212
8e54aaed
RD
6213/**
6214 * Recurse up from this line to find any folds that prevent this line from being visible
6215 * and unfold them all.
6216 */
65ec6247 6217void Editor::EnsureLineVisible(int lineDoc, bool enforcePolicy) {
1a2fb4cd
RD
6218
6219 // In case in need of wrapping to ensure DisplayFromDoc works.
8e54aaed 6220 WrapLines(true, -1);
1a2fb4cd 6221
65ec6247
RD
6222 if (!cs.GetVisible(lineDoc)) {
6223 int lineParent = pdoc->GetFoldParent(lineDoc);
9ce192d4 6224 if (lineParent >= 0) {
65ec6247
RD
6225 if (lineDoc != lineParent)
6226 EnsureLineVisible(lineParent, enforcePolicy);
9ce192d4
RD
6227 if (!cs.GetExpanded(lineParent)) {
6228 cs.SetExpanded(lineParent, 1);
6229 Expand(lineParent, true);
6230 }
6231 }
6232 SetScrollBars();
6233 Redraw();
6234 }
65ec6247
RD
6235 if (enforcePolicy) {
6236 int lineDisplay = cs.DisplayFromDoc(lineDoc);
6237 if (visiblePolicy & VISIBLE_SLOP) {
6238 if ((topLine > lineDisplay) || ((visiblePolicy & VISIBLE_STRICT) && (topLine + visibleSlop > lineDisplay))) {
6239 SetTopLine(Platform::Clamp(lineDisplay - visibleSlop, 0, MaxScrollPos()));
6240 SetVerticalScrollPos();
6241 Redraw();
6242 } else if ((lineDisplay > topLine + LinesOnScreen() - 1) ||
7e0c58e9 6243 ((visiblePolicy & VISIBLE_STRICT) && (lineDisplay > topLine + LinesOnScreen() - 1 - visibleSlop))) {
65ec6247
RD
6244 SetTopLine(Platform::Clamp(lineDisplay - LinesOnScreen() + 1 + visibleSlop, 0, MaxScrollPos()));
6245 SetVerticalScrollPos();
6246 Redraw();
6247 }
6248 } else {
6249 if ((topLine > lineDisplay) || (lineDisplay > topLine + LinesOnScreen() - 1) || (visiblePolicy & VISIBLE_STRICT)) {
6250 SetTopLine(Platform::Clamp(lineDisplay - LinesOnScreen() / 2 + 1, 0, MaxScrollPos()));
6251 SetVerticalScrollPos();
6252 Redraw();
6253 }
6254 }
6255 }
6256}
6257
6258int Editor::ReplaceTarget(bool replacePatterns, const char *text, int length) {
9e96e16f 6259 UndoGroup ug(pdoc);
65ec6247 6260 if (length == -1)
88a8b04e 6261 length = istrlen(text);
65ec6247
RD
6262 if (replacePatterns) {
6263 text = pdoc->SubstituteByPosition(text, &length);
7e0c58e9 6264 if (!text) {
65ec6247 6265 return 0;
7e0c58e9 6266 }
65ec6247
RD
6267 }
6268 if (targetStart != targetEnd)
6269 pdoc->DeleteChars(targetStart, targetEnd - targetStart);
6270 targetEnd = targetStart;
6271 pdoc->InsertString(targetStart, text, length);
6272 targetEnd = targetStart + length;
65ec6247 6273 return length;
9ce192d4
RD
6274}
6275
1a2fb4cd
RD
6276bool Editor::IsUnicodeMode() const {
6277 return pdoc && (SC_CP_UTF8 == pdoc->dbcsCodePage);
6278}
6279
9e730a78
RD
6280int Editor::CodePage() const {
6281 if (pdoc)
6282 return pdoc->dbcsCodePage;
6283 else
6284 return 0;
6285}
6286
1e9bafca
RD
6287int Editor::WrapCount(int line) {
6288 AutoSurface surface(this);
6289 AutoLineLayout ll(llc, RetrieveLineLayout(line));
6290
6291 if (surface && ll) {
6292 LayoutLine(line, surface, vs, ll, wrapWidth);
6293 return ll->lines;
6294 } else {
6295 return 1;
6296 }
6297}
6298
7e0c58e9
RD
6299void Editor::AddStyledText(char *buffer, int appendLength) {
6300 // The buffer consists of alternating character bytes and style bytes
6301 size_t textLength = appendLength / 2;
6302 char *text = new char[textLength];
9e96e16f
RD
6303 size_t i;
6304 for (i = 0;i < textLength;i++) {
6305 text[i] = buffer[i*2];
7e0c58e9 6306 }
9e96e16f
RD
6307 pdoc->InsertString(CurrentPosition(), text, textLength);
6308 for (i = 0;i < textLength;i++) {
6309 text[i] = buffer[i*2+1];
6310 }
6311 pdoc->StartStyling(CurrentPosition(), static_cast<char>(0xff));
6312 pdoc->SetStyles(textLength, text);
6313 delete []text;
6314 SetEmptySelection(sel.MainCaret() + textLength);
7e0c58e9
RD
6315}
6316
d134f170 6317static bool ValidMargin(unsigned long wParam) {
f6bcfd97
BP
6318 return wParam < ViewStyle::margins;
6319}
6320
a834585d
RD
6321static char *CharPtrFromSPtr(sptr_t lParam) {
6322 return reinterpret_cast<char *>(lParam);
6323}
6324
7e0c58e9
RD
6325void Editor::StyleSetMessage(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
6326 vs.EnsureStyle(wParam);
6327 switch (iMessage) {
6328 case SCI_STYLESETFORE:
6329 vs.styles[wParam].fore.desired = ColourDesired(lParam);
6330 break;
6331 case SCI_STYLESETBACK:
6332 vs.styles[wParam].back.desired = ColourDesired(lParam);
6333 break;
6334 case SCI_STYLESETBOLD:
6335 vs.styles[wParam].bold = lParam != 0;
6336 break;
6337 case SCI_STYLESETITALIC:
6338 vs.styles[wParam].italic = lParam != 0;
6339 break;
6340 case SCI_STYLESETEOLFILLED:
6341 vs.styles[wParam].eolFilled = lParam != 0;
6342 break;
6343 case SCI_STYLESETSIZE:
6344 vs.styles[wParam].size = lParam;
6345 break;
6346 case SCI_STYLESETFONT:
6347 if (lParam != 0) {
6348 vs.SetStyleFontName(wParam, CharPtrFromSPtr(lParam));
6349 }
6350 break;
6351 case SCI_STYLESETUNDERLINE:
6352 vs.styles[wParam].underline = lParam != 0;
6353 break;
6354 case SCI_STYLESETCASE:
6355 vs.styles[wParam].caseForce = static_cast<Style::ecaseForced>(lParam);
6356 break;
6357 case SCI_STYLESETCHARACTERSET:
6358 vs.styles[wParam].characterSet = lParam;
6359 break;
6360 case SCI_STYLESETVISIBLE:
6361 vs.styles[wParam].visible = lParam != 0;
6362 break;
6363 case SCI_STYLESETCHANGEABLE:
6364 vs.styles[wParam].changeable = lParam != 0;
6365 break;
6366 case SCI_STYLESETHOTSPOT:
6367 vs.styles[wParam].hotspot = lParam != 0;
6368 break;
6369 }
6370 InvalidateStyleRedraw();
6371}
6372
6373sptr_t Editor::StyleGetMessage(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
6374 vs.EnsureStyle(wParam);
6375 switch (iMessage) {
6376 case SCI_STYLEGETFORE:
6377 return vs.styles[wParam].fore.desired.AsLong();
6378 case SCI_STYLEGETBACK:
6379 return vs.styles[wParam].back.desired.AsLong();
6380 case SCI_STYLEGETBOLD:
6381 return vs.styles[wParam].bold ? 1 : 0;
6382 case SCI_STYLEGETITALIC:
6383 return vs.styles[wParam].italic ? 1 : 0;
6384 case SCI_STYLEGETEOLFILLED:
6385 return vs.styles[wParam].eolFilled ? 1 : 0;
6386 case SCI_STYLEGETSIZE:
6387 return vs.styles[wParam].size;
6388 case SCI_STYLEGETFONT:
9e96e16f
RD
6389 if (!vs.styles[wParam].fontName)
6390 return 0;
7e0c58e9
RD
6391 if (lParam != 0)
6392 strcpy(CharPtrFromSPtr(lParam), vs.styles[wParam].fontName);
6393 return strlen(vs.styles[wParam].fontName);
6394 case SCI_STYLEGETUNDERLINE:
6395 return vs.styles[wParam].underline ? 1 : 0;
6396 case SCI_STYLEGETCASE:
6397 return static_cast<int>(vs.styles[wParam].caseForce);
6398 case SCI_STYLEGETCHARACTERSET:
6399 return vs.styles[wParam].characterSet;
6400 case SCI_STYLEGETVISIBLE:
6401 return vs.styles[wParam].visible ? 1 : 0;
6402 case SCI_STYLEGETCHANGEABLE:
6403 return vs.styles[wParam].changeable ? 1 : 0;
6404 case SCI_STYLEGETHOTSPOT:
6405 return vs.styles[wParam].hotspot ? 1 : 0;
6406 }
6407 return 0;
6408}
6409
9e96e16f
RD
6410sptr_t Editor::StringResult(sptr_t lParam, const char *val) {
6411 const int n = strlen(val);
6412 if (lParam != 0) {
6413 char *ptr = reinterpret_cast<char *>(lParam);
6414 strcpy(ptr, val);
6415 }
6416 return n; // Not including NUL
6417}
6418
65ec6247 6419sptr_t Editor::WndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
9ce192d4
RD
6420 //Platform::DebugPrintf("S start wnd proc %d %d %d\n",iMessage, wParam, lParam);
6421
6422 // Optional macro recording hook
9ce192d4
RD
6423 if (recordingMacro)
6424 NotifyMacroRecord(iMessage, wParam, lParam);
9ce192d4
RD
6425
6426 switch (iMessage) {
6427
9e730a78 6428 case SCI_GETTEXT: {
9ce192d4 6429 if (lParam == 0)
591d01be 6430 return pdoc->Length() + 1;
a834585d
RD
6431 if (wParam == 0)
6432 return 0;
6433 char *ptr = CharPtrFromSPtr(lParam);
9ce192d4 6434 unsigned int iChar = 0;
d134f170 6435 for (; iChar < wParam - 1; iChar++)
9ce192d4
RD
6436 ptr[iChar] = pdoc->CharAt(iChar);
6437 ptr[iChar] = '\0';
6438 return iChar;
6439 }
6440
9e730a78 6441 case SCI_SETTEXT: {
9ce192d4 6442 if (lParam == 0)
1a2fb4cd 6443 return 0;
9e96e16f 6444 UndoGroup ug(pdoc);
9ce192d4
RD
6445 pdoc->DeleteChars(0, pdoc->Length());
6446 SetEmptySelection(0);
7e0c58e9 6447 pdoc->InsertCString(0, CharPtrFromSPtr(lParam));
1a2fb4cd 6448 return 1;
9ce192d4
RD
6449 }
6450
d134f170 6451 case SCI_GETTEXTLENGTH:
9ce192d4
RD
6452 return pdoc->Length();
6453
d134f170 6454 case SCI_CUT:
9ce192d4
RD
6455 Cut();
6456 SetLastXChosen();
6457 break;
6458
d134f170 6459 case SCI_COPY:
9ce192d4
RD
6460 Copy();
6461 break;
6462
9e96e16f
RD
6463 case SCI_COPYALLOWLINE:
6464 CopyAllowLine();
6465 break;
6466
e14d10b0
RD
6467 case SCI_COPYRANGE:
6468 CopyRangeToClipboard(wParam, lParam);
6469 break;
6470
6471 case SCI_COPYTEXT:
6472 CopyText(wParam, CharPtrFromSPtr(lParam));
6473 break;
6474
d134f170 6475 case SCI_PASTE:
9ce192d4 6476 Paste();
1e9bafca
RD
6477 if (!caretSticky) {
6478 SetLastXChosen();
6479 }
f6bcfd97 6480 EnsureCaretVisible();
9ce192d4
RD
6481 break;
6482
d134f170 6483 case SCI_CLEAR:
9ce192d4
RD
6484 Clear();
6485 SetLastXChosen();
a834585d 6486 EnsureCaretVisible();
9ce192d4
RD
6487 break;
6488
d134f170 6489 case SCI_UNDO:
9ce192d4
RD
6490 Undo();
6491 SetLastXChosen();
6492 break;
6493
d134f170 6494 case SCI_CANUNDO:
1e9bafca 6495 return (pdoc->CanUndo() && !pdoc->IsReadOnly()) ? 1 : 0;
9ce192d4 6496
d134f170 6497 case SCI_EMPTYUNDOBUFFER:
9ce192d4
RD
6498 pdoc->DeleteUndoHistory();
6499 return 0;
6500
d134f170 6501 case SCI_GETFIRSTVISIBLELINE:
9ce192d4
RD
6502 return topLine;
6503
9e96e16f
RD
6504 case SCI_SETFIRSTVISIBLELINE:
6505 ScrollTo(wParam);
6506 break;
6507
b8b0e402 6508 case SCI_GETLINE: { // Risk of overwriting the end of the buffer
d134f170
RD
6509 int lineStart = pdoc->LineStart(wParam);
6510 int lineEnd = pdoc->LineStart(wParam + 1);
591d01be
RD
6511 if (lParam == 0) {
6512 return lineEnd - lineStart;
6513 }
a834585d 6514 char *ptr = CharPtrFromSPtr(lParam);
d134f170 6515 int iPlace = 0;
65ec6247 6516 for (int iChar = lineStart; iChar < lineEnd; iChar++) {
d134f170 6517 ptr[iPlace++] = pdoc->CharAt(iChar);
65ec6247 6518 }
d134f170
RD
6519 return iPlace;
6520 }
6521
d134f170 6522 case SCI_GETLINECOUNT:
9ce192d4
RD
6523 if (pdoc->LinesTotal() == 0)
6524 return 1;
6525 else
6526 return pdoc->LinesTotal();
6527
d134f170 6528 case SCI_GETMODIFY:
9ce192d4
RD
6529 return !pdoc->IsSavePoint();
6530
d134f170 6531 case SCI_SETSEL: {
9ce192d4
RD
6532 int nStart = static_cast<int>(wParam);
6533 int nEnd = static_cast<int>(lParam);
6534 if (nEnd < 0)
6535 nEnd = pdoc->Length();
6536 if (nStart < 0)
6537 nStart = nEnd; // Remove selection
9e96e16f
RD
6538 InvalidateSelection(SelectionRange(nStart, nEnd));
6539 sel.Clear();
6540 sel.selType = Selection::selStream;
9ce192d4
RD
6541 SetSelection(nEnd, nStart);
6542 EnsureCaretVisible();
6543 }
6544 break;
6545
d134f170 6546 case SCI_GETSELTEXT: {
b8b0e402
RD
6547 SelectionText selectedText;
6548 CopySelectionRange(&selectedText);
9e96e16f
RD
6549 if (lParam == 0) {
6550 return selectedText.len ? selectedText.len : 1;
65ec6247 6551 } else {
9e96e16f
RD
6552 char *ptr = CharPtrFromSPtr(lParam);
6553 int iChar = 0;
6554 if (selectedText.len) {
6555 for (; iChar < selectedText.len; iChar++)
6556 ptr[iChar] = selectedText.s[iChar];
6557 } else {
6558 ptr[0] = '\0';
6559 }
6560 return iChar;
9ce192d4 6561 }
9ce192d4
RD
6562 }
6563
d134f170
RD
6564 case SCI_LINEFROMPOSITION:
6565 if (static_cast<int>(wParam) < 0)
6566 return 0;
6567 return pdoc->LineFromPosition(wParam);
6568
d134f170 6569 case SCI_POSITIONFROMLINE:
9ce192d4 6570 if (static_cast<int>(wParam) < 0)
9e96e16f 6571 wParam = pdoc->LineFromPosition(SelectionStart().Position());
9ce192d4
RD
6572 if (wParam == 0)
6573 return 0; // Even if there is no text, there is a first line that starts at 0
6574 if (static_cast<int>(wParam) > pdoc->LinesTotal())
65ec6247 6575 return -1;
9ce192d4
RD
6576 //if (wParam > pdoc->LineFromPosition(pdoc->Length())) // Useful test, anyway...
6577 // return -1;
6578 return pdoc->LineStart(wParam);
6579
9e730a78 6580 // Replacement of the old Scintilla interpretation of EM_LINELENGTH
9ce192d4
RD
6581 case SCI_LINELENGTH:
6582 if ((static_cast<int>(wParam) < 0) ||
6583 (static_cast<int>(wParam) > pdoc->LineFromPosition(pdoc->Length())))
6584 return 0;
6585 return pdoc->LineStart(wParam + 1) - pdoc->LineStart(wParam);
6586
d134f170 6587 case SCI_REPLACESEL: {
9ce192d4
RD
6588 if (lParam == 0)
6589 return 0;
9e96e16f 6590 UndoGroup ug(pdoc);
9ce192d4 6591 ClearSelection();
a834585d 6592 char *replacement = CharPtrFromSPtr(lParam);
9e96e16f
RD
6593 pdoc->InsertCString(sel.MainCaret(), replacement);
6594 SetEmptySelection(sel.MainCaret() + istrlen(replacement));
9ce192d4
RD
6595 EnsureCaretVisible();
6596 }
6597 break;
6598
65ec6247
RD
6599 case SCI_SETTARGETSTART:
6600 targetStart = wParam;
6601 break;
6602
6603 case SCI_GETTARGETSTART:
6604 return targetStart;
6605
6606 case SCI_SETTARGETEND:
6607 targetEnd = wParam;
6608 break;
6609
6610 case SCI_GETTARGETEND:
6611 return targetEnd;
6612
9e730a78 6613 case SCI_TARGETFROMSELECTION:
9e96e16f
RD
6614 if (sel.MainCaret() < sel.MainAnchor()) {
6615 targetStart = sel.MainCaret();
6616 targetEnd = sel.MainAnchor();
9e730a78 6617 } else {
9e96e16f
RD
6618 targetStart = sel.MainAnchor();
6619 targetEnd = sel.MainCaret();
9e730a78
RD
6620 }
6621 break;
6622
65ec6247
RD
6623 case SCI_REPLACETARGET:
6624 PLATFORM_ASSERT(lParam);
a834585d 6625 return ReplaceTarget(false, CharPtrFromSPtr(lParam), wParam);
65ec6247
RD
6626
6627 case SCI_REPLACETARGETRE:
6628 PLATFORM_ASSERT(lParam);
a834585d 6629 return ReplaceTarget(true, CharPtrFromSPtr(lParam), wParam);
65ec6247
RD
6630
6631 case SCI_SEARCHINTARGET:
6632 PLATFORM_ASSERT(lParam);
a834585d 6633 return SearchInTarget(CharPtrFromSPtr(lParam), wParam);
65ec6247
RD
6634
6635 case SCI_SETSEARCHFLAGS:
6636 searchFlags = wParam;
6637 break;
6638
6639 case SCI_GETSEARCHFLAGS:
6640 return searchFlags;
6641
e14d10b0 6642 case SCI_POSITIONBEFORE:
7e0c58e9 6643 return pdoc->MovePositionOutsideChar(wParam - 1, -1, true);
e14d10b0
RD
6644
6645 case SCI_POSITIONAFTER:
7e0c58e9 6646 return pdoc->MovePositionOutsideChar(wParam + 1, 1, true);
e14d10b0 6647
d134f170 6648 case SCI_LINESCROLL:
9ce192d4
RD
6649 ScrollTo(topLine + lParam);
6650 HorizontalScrollTo(xOffset + wParam * vs.spaceWidth);
1a2fb4cd
RD
6651 return 1;
6652
6653 case SCI_SETXOFFSET:
6654 xOffset = wParam;
9e730a78 6655 SetHorizontalScrollPos();
1a2fb4cd
RD
6656 Redraw();
6657 break;
6658
6659 case SCI_GETXOFFSET:
6660 return xOffset;
9ce192d4 6661
9e730a78
RD
6662 case SCI_CHOOSECARETX:
6663 SetLastXChosen();
6664 break;
6665
d134f170 6666 case SCI_SCROLLCARET:
9ce192d4
RD
6667 EnsureCaretVisible();
6668 break;
6669
d134f170 6670 case SCI_SETREADONLY:
1a2fb4cd
RD
6671 pdoc->SetReadOnly(wParam != 0);
6672 return 1;
9ce192d4 6673
d134f170
RD
6674 case SCI_GETREADONLY:
6675 return pdoc->IsReadOnly();
6676
d134f170 6677 case SCI_CANPASTE:
65ec6247 6678 return CanPaste();
9ce192d4 6679
d134f170
RD
6680 case SCI_POINTXFROMPOSITION:
6681 if (lParam < 0) {
6682 return 0;
6683 } else {
6684 Point pt = LocationFromPosition(lParam);
6685 return pt.x;
6686 }
6687
6688 case SCI_POINTYFROMPOSITION:
6689 if (lParam < 0) {
6690 return 0;
6691 } else {
6692 Point pt = LocationFromPosition(lParam);
6693 return pt.y;
6694 }
6695
d134f170 6696 case SCI_FINDTEXT:
b8b0e402 6697 return FindText(wParam, lParam);
9ce192d4 6698
d134f170 6699 case SCI_GETTEXTRANGE: {
9ce192d4
RD
6700 if (lParam == 0)
6701 return 0;
9e96e16f 6702 Sci_TextRange *tr = reinterpret_cast<Sci_TextRange *>(lParam);
9ce192d4
RD
6703 int cpMax = tr->chrg.cpMax;
6704 if (cpMax == -1)
6705 cpMax = pdoc->Length();
8e54aaed 6706 PLATFORM_ASSERT(cpMax <= pdoc->Length());
9ce192d4
RD
6707 int len = cpMax - tr->chrg.cpMin; // No -1 as cpMin and cpMax are referring to inter character positions
6708 pdoc->GetCharRange(tr->lpstrText, tr->chrg.cpMin, len);
6709 // Spec says copied text is terminated with a NUL
6710 tr->lpstrText[len] = '\0';
6711 return len; // Not including NUL
6712 }
6713
b8b0e402 6714 case SCI_HIDESELECTION:
1a2fb4cd 6715 hideSelection = wParam != 0;
9ce192d4
RD
6716 Redraw();
6717 break;
6718
d134f170 6719 case SCI_FORMATRANGE:
9e96e16f 6720 return FormatRange(wParam != 0, reinterpret_cast<Sci_RangeToFormat *>(lParam));
9ce192d4 6721
d134f170
RD
6722 case SCI_GETMARGINLEFT:
6723 return vs.leftMarginWidth;
6724
6725 case SCI_GETMARGINRIGHT:
6726 return vs.rightMarginWidth;
6727
d134f170
RD
6728 case SCI_SETMARGINLEFT:
6729 vs.leftMarginWidth = lParam;
6730 InvalidateStyleRedraw();
6731 break;
6732
6733 case SCI_SETMARGINRIGHT:
6734 vs.rightMarginWidth = lParam;
6735 InvalidateStyleRedraw();
6736 break;
6737
9ce192d4
RD
6738 // Control specific mesages
6739
6740 case SCI_ADDTEXT: {
6741 if (lParam == 0)
6742 return 0;
a834585d 6743 pdoc->InsertString(CurrentPosition(), CharPtrFromSPtr(lParam), wParam);
9e96e16f 6744 SetEmptySelection(sel.MainCaret() + wParam);
9ce192d4
RD
6745 return 0;
6746 }
6747
7e0c58e9
RD
6748 case SCI_ADDSTYLEDTEXT:
6749 if (lParam)
6750 AddStyledText(CharPtrFromSPtr(lParam), wParam);
6751 return 0;
9ce192d4
RD
6752
6753 case SCI_INSERTTEXT: {
6754 if (lParam == 0)
6755 return 0;
6756 int insertPos = wParam;
8e54aaed 6757 if (static_cast<int>(wParam) == -1)
9ce192d4
RD
6758 insertPos = CurrentPosition();
6759 int newCurrent = CurrentPosition();
a834585d 6760 char *sz = CharPtrFromSPtr(lParam);
7e0c58e9 6761 pdoc->InsertCString(insertPos, sz);
9ce192d4 6762 if (newCurrent > insertPos)
88a8b04e 6763 newCurrent += istrlen(sz);
9ce192d4
RD
6764 SetEmptySelection(newCurrent);
6765 return 0;
6766 }
6767
9e730a78
RD
6768 case SCI_APPENDTEXT:
6769 pdoc->InsertString(pdoc->Length(), CharPtrFromSPtr(lParam), wParam);
6770 return 0;
6771
9ce192d4
RD
6772 case SCI_CLEARALL:
6773 ClearAll();
6774 return 0;
6775
d134f170
RD
6776 case SCI_CLEARDOCUMENTSTYLE:
6777 ClearDocumentStyle();
9ce192d4
RD
6778 return 0;
6779
d134f170 6780 case SCI_SETUNDOCOLLECTION:
1a2fb4cd 6781 pdoc->SetUndoCollection(wParam != 0);
9ce192d4 6782 return 0;
d134f170
RD
6783
6784 case SCI_GETUNDOCOLLECTION:
6785 return pdoc->IsCollectingUndo();
9ce192d4
RD
6786
6787 case SCI_BEGINUNDOACTION:
6788 pdoc->BeginUndoAction();
6789 return 0;
6790
6791 case SCI_ENDUNDOACTION:
6792 pdoc->EndUndoAction();
6793 return 0;
6794
6795 case SCI_GETCARETPERIOD:
6796 return caret.period;
6797
6798 case SCI_SETCARETPERIOD:
6799 caret.period = wParam;
6800 break;
6801
6802 case SCI_SETWORDCHARS: {
591d01be 6803 pdoc->SetDefaultCharClasses(false);
9ce192d4
RD
6804 if (lParam == 0)
6805 return 0;
b8193d80 6806 pdoc->SetCharClasses(reinterpret_cast<unsigned char *>(lParam), CharClassify::ccWord);
9ce192d4
RD
6807 }
6808 break;
6809
8e54aaed
RD
6810 case SCI_SETWHITESPACECHARS: {
6811 if (lParam == 0)
6812 return 0;
b8193d80 6813 pdoc->SetCharClasses(reinterpret_cast<unsigned char *>(lParam), CharClassify::ccSpace);
8e54aaed
RD
6814 }
6815 break;
6816
6817 case SCI_SETCHARSDEFAULT:
591d01be 6818 pdoc->SetDefaultCharClasses(true);
8e54aaed
RD
6819 break;
6820
9ce192d4
RD
6821 case SCI_GETLENGTH:
6822 return pdoc->Length();
6823
591d01be
RD
6824 case SCI_ALLOCATE:
6825 pdoc->Allocate(wParam);
6826 break;
6827
9ce192d4
RD
6828 case SCI_GETCHARAT:
6829 return pdoc->CharAt(wParam);
6830
d134f170 6831 case SCI_SETCURRENTPOS:
9e96e16f
RD
6832 if (sel.IsRectangular()) {
6833 sel.Rectangular().caret.SetPosition(wParam);
6834 SetRectangularRange();
6835 Redraw();
6836 } else {
6837 SetSelection(wParam, sel.MainAnchor());
6838 }
d134f170
RD
6839 break;
6840
9ce192d4 6841 case SCI_GETCURRENTPOS:
9e96e16f 6842 return sel.IsRectangular() ? sel.Rectangular().caret.Position() : sel.MainCaret();
9ce192d4 6843
d134f170 6844 case SCI_SETANCHOR:
9e96e16f
RD
6845 if (sel.IsRectangular()) {
6846 sel.Rectangular().anchor.SetPosition(wParam);
6847 SetRectangularRange();
6848 Redraw();
6849 } else {
6850 SetSelection(sel.MainCaret(), wParam);
6851 }
d134f170
RD
6852 break;
6853
9ce192d4 6854 case SCI_GETANCHOR:
9e96e16f 6855 return sel.IsRectangular() ? sel.Rectangular().anchor.Position() : sel.MainAnchor();
9ce192d4 6856
d134f170 6857 case SCI_SETSELECTIONSTART:
9e96e16f 6858 SetSelection(Platform::Maximum(sel.MainCaret(), wParam), wParam);
d134f170
RD
6859 break;
6860
6861 case SCI_GETSELECTIONSTART:
9e96e16f 6862 return Platform::Minimum(sel.MainAnchor(), sel.MainCaret());
d134f170
RD
6863
6864 case SCI_SETSELECTIONEND:
9e96e16f 6865 SetSelection(wParam, Platform::Minimum(sel.MainAnchor(), wParam));
d134f170
RD
6866 break;
6867
6868 case SCI_GETSELECTIONEND:
9e96e16f 6869 return Platform::Maximum(sel.MainAnchor(), sel.MainCaret());
d134f170
RD
6870
6871 case SCI_SETPRINTMAGNIFICATION:
6872 printMagnification = wParam;
6873 break;
6874
6875 case SCI_GETPRINTMAGNIFICATION:
6876 return printMagnification;
6877
6878 case SCI_SETPRINTCOLOURMODE:
6879 printColourMode = wParam;
6880 break;
6881
6882 case SCI_GETPRINTCOLOURMODE:
6883 return printColourMode;
6884
9e730a78
RD
6885 case SCI_SETPRINTWRAPMODE:
6886 printWrapState = (wParam == SC_WRAP_WORD) ? eWrapWord : eWrapNone;
6887 break;
6888
6889 case SCI_GETPRINTWRAPMODE:
6890 return printWrapState;
6891
9ce192d4 6892 case SCI_GETSTYLEAT:
8e54aaed 6893 if (static_cast<int>(wParam) >= pdoc->Length())
9ce192d4
RD
6894 return 0;
6895 else
6896 return pdoc->StyleAt(wParam);
6897
6898 case SCI_REDO:
6899 Redo();
6900 break;
6901
6902 case SCI_SELECTALL:
6903 SelectAll();
6904 break;
6905
6906 case SCI_SETSAVEPOINT:
6907 pdoc->SetSavePoint();
9ce192d4
RD
6908 break;
6909
6910 case SCI_GETSTYLEDTEXT: {
6911 if (lParam == 0)
6912 return 0;
9e96e16f 6913 Sci_TextRange *tr = reinterpret_cast<Sci_TextRange *>(lParam);
9ce192d4
RD
6914 int iPlace = 0;
6915 for (int iChar = tr->chrg.cpMin; iChar < tr->chrg.cpMax; iChar++) {
6916 tr->lpstrText[iPlace++] = pdoc->CharAt(iChar);
6917 tr->lpstrText[iPlace++] = pdoc->StyleAt(iChar);
6918 }
6919 tr->lpstrText[iPlace] = '\0';
6920 tr->lpstrText[iPlace + 1] = '\0';
6921 return iPlace;
6922 }
6923
6924 case SCI_CANREDO:
1e9bafca 6925 return (pdoc->CanRedo() && !pdoc->IsReadOnly()) ? 1 : 0;
9ce192d4
RD
6926
6927 case SCI_MARKERLINEFROMHANDLE:
6928 return pdoc->LineFromHandle(wParam);
6929
6930 case SCI_MARKERDELETEHANDLE:
6931 pdoc->DeleteMarkFromHandle(wParam);
6932 break;
6933
6934 case SCI_GETVIEWWS:
6935 return vs.viewWhitespace;
6936
6937 case SCI_SETVIEWWS:
d134f170 6938 vs.viewWhitespace = static_cast<WhiteSpaceVisibility>(wParam);
9ce192d4
RD
6939 Redraw();
6940 break;
6941
9e96e16f
RD
6942 case SCI_GETWHITESPACESIZE:
6943 return vs.whitespaceSize;
6944
6945 case SCI_SETWHITESPACESIZE:
6946 vs.whitespaceSize = static_cast<int>(wParam);
6947 Redraw();
6948 break;
6949
d134f170 6950 case SCI_POSITIONFROMPOINT:
9e96e16f 6951 return PositionFromLocation(Point(wParam, lParam), false, false);
d134f170 6952
65ec6247 6953 case SCI_POSITIONFROMPOINTCLOSE:
9e96e16f
RD
6954 return PositionFromLocation(Point(wParam, lParam), true, false);
6955
6956 case SCI_CHARPOSITIONFROMPOINT:
6957 return PositionFromLocation(Point(wParam, lParam), false, true);
6958
6959 case SCI_CHARPOSITIONFROMPOINTCLOSE:
6960 return PositionFromLocation(Point(wParam, lParam), true, true);
65ec6247 6961
9ce192d4
RD
6962 case SCI_GOTOLINE:
6963 GoToLine(wParam);
6964 break;
6965
6966 case SCI_GOTOPOS:
6967 SetEmptySelection(wParam);
6968 EnsureCaretVisible();
6969 Redraw();
6970 break;
6971
9ce192d4 6972 case SCI_GETCURLINE: {
9e96e16f 6973 int lineCurrentPos = pdoc->LineFromPosition(sel.MainCaret());
9ce192d4
RD
6974 int lineStart = pdoc->LineStart(lineCurrentPos);
6975 unsigned int lineEnd = pdoc->LineStart(lineCurrentPos + 1);
591d01be
RD
6976 if (lParam == 0) {
6977 return 1 + lineEnd - lineStart;
6978 }
1e9bafca 6979 PLATFORM_ASSERT(wParam > 0);
a834585d 6980 char *ptr = CharPtrFromSPtr(lParam);
9ce192d4 6981 unsigned int iPlace = 0;
65ec6247 6982 for (unsigned int iChar = lineStart; iChar < lineEnd && iPlace < wParam - 1; iChar++) {
9ce192d4 6983 ptr[iPlace++] = pdoc->CharAt(iChar);
65ec6247 6984 }
9ce192d4 6985 ptr[iPlace] = '\0';
9e96e16f 6986 return sel.MainCaret() - lineStart;
9ce192d4
RD
6987 }
6988
6989 case SCI_GETENDSTYLED:
6990 return pdoc->GetEndStyled();
6991
6992 case SCI_GETEOLMODE:
6993 return pdoc->eolMode;
6994
6995 case SCI_SETEOLMODE:
6996 pdoc->eolMode = wParam;
6997 break;
6998
6999 case SCI_STARTSTYLING:
f6bcfd97 7000 pdoc->StartStyling(wParam, static_cast<char>(lParam));
9ce192d4
RD
7001 break;
7002
7003 case SCI_SETSTYLING:
f6bcfd97 7004 pdoc->SetStyleFor(wParam, static_cast<char>(lParam));
9ce192d4
RD
7005 break;
7006
9e730a78 7007 case SCI_SETSTYLINGEX: // Specify a complete styling buffer
9ce192d4
RD
7008 if (lParam == 0)
7009 return 0;
a834585d 7010 pdoc->SetStyles(wParam, CharPtrFromSPtr(lParam));
9ce192d4
RD
7011 break;
7012
9ce192d4 7013 case SCI_SETBUFFEREDDRAW:
1a2fb4cd 7014 bufferedDraw = wParam != 0;
9ce192d4
RD
7015 break;
7016
d134f170
RD
7017 case SCI_GETBUFFEREDDRAW:
7018 return bufferedDraw;
7019
9e730a78
RD
7020 case SCI_GETTWOPHASEDRAW:
7021 return twoPhaseDraw;
7022
7023 case SCI_SETTWOPHASEDRAW:
7024 twoPhaseDraw = wParam != 0;
7025 InvalidateStyleRedraw();
7026 break;
7027
9e96e16f
RD
7028 case SCI_SETFONTQUALITY:
7029 vs.extraFontFlag &= ~SC_EFF_QUALITY_MASK;
7030 vs.extraFontFlag |= (wParam & SC_EFF_QUALITY_MASK);
7031 InvalidateStyleRedraw();
7032 break;
7033
7034 case SCI_GETFONTQUALITY:
7035 return (vs.extraFontFlag & SC_EFF_QUALITY_MASK);
7036
9ce192d4 7037 case SCI_SETTABWIDTH:
591d01be 7038 if (wParam > 0) {
9ce192d4 7039 pdoc->tabInChars = wParam;
591d01be
RD
7040 if (pdoc->indentInChars == 0)
7041 pdoc->actualIndentInChars = pdoc->tabInChars;
7042 }
9ce192d4
RD
7043 InvalidateStyleRedraw();
7044 break;
7045
f6bcfd97
BP
7046 case SCI_GETTABWIDTH:
7047 return pdoc->tabInChars;
d134f170 7048
f6bcfd97
BP
7049 case SCI_SETINDENT:
7050 pdoc->indentInChars = wParam;
591d01be
RD
7051 if (pdoc->indentInChars != 0)
7052 pdoc->actualIndentInChars = pdoc->indentInChars;
7053 else
7054 pdoc->actualIndentInChars = pdoc->tabInChars;
f6bcfd97
BP
7055 InvalidateStyleRedraw();
7056 break;
d134f170 7057
f6bcfd97
BP
7058 case SCI_GETINDENT:
7059 return pdoc->indentInChars;
7060
7061 case SCI_SETUSETABS:
1a2fb4cd 7062 pdoc->useTabs = wParam != 0;
f6bcfd97
BP
7063 InvalidateStyleRedraw();
7064 break;
7065
7066 case SCI_GETUSETABS:
7067 return pdoc->useTabs;
d134f170 7068
f6bcfd97
BP
7069 case SCI_SETLINEINDENTATION:
7070 pdoc->SetLineIndentation(wParam, lParam);
7071 break;
d134f170 7072
f6bcfd97
BP
7073 case SCI_GETLINEINDENTATION:
7074 return pdoc->GetLineIndentation(wParam);
d134f170 7075
f6bcfd97
BP
7076 case SCI_GETLINEINDENTPOSITION:
7077 return pdoc->GetLineIndentPosition(wParam);
d134f170 7078
65ec6247 7079 case SCI_SETTABINDENTS:
1a2fb4cd 7080 pdoc->tabIndents = wParam != 0;
65ec6247
RD
7081 break;
7082
7083 case SCI_GETTABINDENTS:
7084 return pdoc->tabIndents;
7085
7086 case SCI_SETBACKSPACEUNINDENTS:
1a2fb4cd 7087 pdoc->backspaceUnindents = wParam != 0;
65ec6247
RD
7088 break;
7089
7090 case SCI_GETBACKSPACEUNINDENTS:
7091 return pdoc->backspaceUnindents;
7092
7093 case SCI_SETMOUSEDWELLTIME:
7094 dwellDelay = wParam;
7095 ticksToDwell = dwellDelay;
7096 break;
1a2fb4cd 7097
65ec6247
RD
7098 case SCI_GETMOUSEDWELLTIME:
7099 return dwellDelay;
1a2fb4cd
RD
7100
7101 case SCI_WORDSTARTPOSITION:
7102 return pdoc->ExtendWordSelect(wParam, -1, lParam != 0);
7103
7104 case SCI_WORDENDPOSITION:
7105 return pdoc->ExtendWordSelect(wParam, 1, lParam != 0);
7106
7107 case SCI_SETWRAPMODE:
b8193d80
RD
7108 switch (wParam) {
7109 case SC_WRAP_WORD:
7110 wrapState = eWrapWord;
7111 break;
7112 case SC_WRAP_CHAR:
7113 wrapState = eWrapChar;
7114 break;
7115 default:
7116 wrapState = eWrapNone;
7117 break;
1e9bafca 7118 }
1a2fb4cd
RD
7119 xOffset = 0;
7120 InvalidateStyleRedraw();
7121 ReconfigureScrollBars();
7122 break;
7123
7124 case SCI_GETWRAPMODE:
7125 return wrapState;
7126
591d01be
RD
7127 case SCI_SETWRAPVISUALFLAGS:
7128 wrapVisualFlags = wParam;
591d01be
RD
7129 InvalidateStyleRedraw();
7130 ReconfigureScrollBars();
7131 break;
7132
7133 case SCI_GETWRAPVISUALFLAGS:
7134 return wrapVisualFlags;
7135
7136 case SCI_SETWRAPVISUALFLAGSLOCATION:
7137 wrapVisualFlagsLocation = wParam;
7138 InvalidateStyleRedraw();
7139 break;
7140
7141 case SCI_GETWRAPVISUALFLAGSLOCATION:
7142 return wrapVisualFlagsLocation;
7143
7144 case SCI_SETWRAPSTARTINDENT:
7145 wrapVisualStartIndent = wParam;
591d01be
RD
7146 InvalidateStyleRedraw();
7147 ReconfigureScrollBars();
7148 break;
7149
7150 case SCI_GETWRAPSTARTINDENT:
7151 return wrapVisualStartIndent;
7152
9e96e16f
RD
7153 case SCI_SETWRAPINDENTMODE:
7154 wrapIndentMode = wParam;
7155 InvalidateStyleRedraw();
7156 ReconfigureScrollBars();
7157 break;
7158
7159 case SCI_GETWRAPINDENTMODE:
7160 return wrapIndentMode;
7161
1a2fb4cd
RD
7162 case SCI_SETLAYOUTCACHE:
7163 llc.SetLevel(wParam);
7164 break;
7165
7166 case SCI_GETLAYOUTCACHE:
7167 return llc.GetLevel();
7168
7e0c58e9
RD
7169 case SCI_SETPOSITIONCACHE:
7170 posCache.SetSize(wParam);
7171 break;
7172
7173 case SCI_GETPOSITIONCACHE:
7174 return posCache.GetSize();
7175
a834585d
RD
7176 case SCI_SETSCROLLWIDTH:
7177 PLATFORM_ASSERT(wParam > 0);
7178 if ((wParam > 0) && (wParam != static_cast<unsigned int >(scrollWidth))) {
7e0c58e9 7179 lineWidthMaxSeen = 0;
a834585d
RD
7180 scrollWidth = wParam;
7181 SetScrollBars();
7182 }
7183 break;
7184
7185 case SCI_GETSCROLLWIDTH:
7186 return scrollWidth;
7187
7e0c58e9
RD
7188 case SCI_SETSCROLLWIDTHTRACKING:
7189 trackLineWidth = wParam != 0;
7190 break;
7191
7192 case SCI_GETSCROLLWIDTHTRACKING:
7193 return trackLineWidth;
7194
9e730a78
RD
7195 case SCI_LINESJOIN:
7196 LinesJoin();
7197 break;
7198
7199 case SCI_LINESSPLIT:
7200 LinesSplit(wParam);
7201 break;
7202
a834585d 7203 case SCI_TEXTWIDTH:
7e0c58e9 7204 PLATFORM_ASSERT(wParam < vs.stylesSize);
a834585d
RD
7205 PLATFORM_ASSERT(lParam);
7206 return TextWidth(wParam, CharPtrFromSPtr(lParam));
7207
7208 case SCI_TEXTHEIGHT:
7209 return vs.lineHeight;
7210
7211 case SCI_SETENDATLASTLINE:
9e730a78 7212 PLATFORM_ASSERT((wParam == 0) || (wParam == 1));
a834585d
RD
7213 if (endAtLastLine != (wParam != 0)) {
7214 endAtLastLine = wParam != 0;
7215 SetScrollBars();
7216 }
7217 break;
7218
7219 case SCI_GETENDATLASTLINE:
7220 return endAtLastLine;
7221
1e9bafca
RD
7222 case SCI_SETCARETSTICKY:
7223 PLATFORM_ASSERT((wParam == 0) || (wParam == 1));
7224 if (caretSticky != (wParam != 0)) {
7225 caretSticky = wParam != 0;
7226 }
7227 break;
7228
7229 case SCI_GETCARETSTICKY:
7230 return caretSticky;
7231
7232 case SCI_TOGGLECARETSTICKY:
7233 caretSticky = !caretSticky;
7234 break;
7235
d134f170
RD
7236 case SCI_GETCOLUMN:
7237 return pdoc->GetColumn(wParam);
7238
a33203cb
RD
7239 case SCI_FINDCOLUMN:
7240 return pdoc->FindColumn(wParam, lParam);
7241
f6bcfd97 7242 case SCI_SETHSCROLLBAR :
1a2fb4cd
RD
7243 if (horizontalScrollBarVisible != (wParam != 0)) {
7244 horizontalScrollBarVisible = wParam != 0;
7245 SetScrollBars();
7246 ReconfigureScrollBars();
7247 }
f6bcfd97 7248 break;
d134f170 7249
f6bcfd97
BP
7250 case SCI_GETHSCROLLBAR:
7251 return horizontalScrollBarVisible;
d134f170 7252
9e730a78
RD
7253 case SCI_SETVSCROLLBAR:
7254 if (verticalScrollBarVisible != (wParam != 0)) {
7255 verticalScrollBarVisible = wParam != 0;
7256 SetScrollBars();
7257 ReconfigureScrollBars();
7258 }
7259 break;
7260
7261 case SCI_GETVSCROLLBAR:
7262 return verticalScrollBarVisible;
7263
d134f170 7264 case SCI_SETINDENTATIONGUIDES:
7e0c58e9 7265 vs.viewIndentationGuides = IndentView(wParam);
d134f170 7266 Redraw();
9ce192d4
RD
7267 break;
7268
d134f170
RD
7269 case SCI_GETINDENTATIONGUIDES:
7270 return vs.viewIndentationGuides;
7271
7272 case SCI_SETHIGHLIGHTGUIDE:
7273 if ((highlightGuideColumn != static_cast<int>(wParam)) || (wParam > 0)) {
7274 highlightGuideColumn = wParam;
7275 Redraw();
9ce192d4 7276 }
9ce192d4 7277 break;
d134f170
RD
7278
7279 case SCI_GETHIGHLIGHTGUIDE:
7280 return highlightGuideColumn;
7281
7282 case SCI_GETLINEENDPOSITION:
7283 return pdoc->LineEnd(wParam);
7284
7285 case SCI_SETCODEPAGE:
b8193d80
RD
7286 if (ValidCodePage(wParam)) {
7287 pdoc->dbcsCodePage = wParam;
7288 InvalidateStyleRedraw();
7289 }
d134f170
RD
7290 break;
7291
7292 case SCI_GETCODEPAGE:
7293 return pdoc->dbcsCodePage;
9ce192d4
RD
7294
7295 case SCI_SETUSEPALETTE:
1a2fb4cd 7296 palette.allowRealization = wParam != 0;
9ce192d4
RD
7297 InvalidateStyleRedraw();
7298 break;
7299
d134f170
RD
7300 case SCI_GETUSEPALETTE:
7301 return palette.allowRealization;
7302
9ce192d4
RD
7303 // Marker definition and setting
7304 case SCI_MARKERDEFINE:
7305 if (wParam <= MARKER_MAX)
7306 vs.markers[wParam].markType = lParam;
7307 InvalidateStyleData();
7308 RedrawSelMargin();
7309 break;
9e96e16f
RD
7310
7311 case SCI_MARKERSYMBOLDEFINED:
7312 if (wParam <= MARKER_MAX)
7313 return vs.markers[wParam].markType;
7314 else
7315 return 0;
7316
9ce192d4
RD
7317 case SCI_MARKERSETFORE:
7318 if (wParam <= MARKER_MAX)
1a2fb4cd 7319 vs.markers[wParam].fore.desired = ColourDesired(lParam);
9ce192d4
RD
7320 InvalidateStyleData();
7321 RedrawSelMargin();
7322 break;
7323 case SCI_MARKERSETBACK:
7324 if (wParam <= MARKER_MAX)
1a2fb4cd 7325 vs.markers[wParam].back.desired = ColourDesired(lParam);
9ce192d4
RD
7326 InvalidateStyleData();
7327 RedrawSelMargin();
7328 break;
b8193d80
RD
7329 case SCI_MARKERSETALPHA:
7330 if (wParam <= MARKER_MAX)
7331 vs.markers[wParam].alpha = lParam;
7332 InvalidateStyleRedraw();
7333 break;
9ce192d4
RD
7334 case SCI_MARKERADD: {
7335 int markerID = pdoc->AddMark(wParam, lParam);
9ce192d4
RD
7336 return markerID;
7337 }
1e9bafca
RD
7338 case SCI_MARKERADDSET:
7339 if (lParam != 0)
7340 pdoc->AddMarkSet(wParam, lParam);
7341 break;
9ce192d4
RD
7342
7343 case SCI_MARKERDELETE:
7344 pdoc->DeleteMark(wParam, lParam);
9ce192d4
RD
7345 break;
7346
7347 case SCI_MARKERDELETEALL:
7348 pdoc->DeleteAllMarks(static_cast<int>(wParam));
9ce192d4
RD
7349 break;
7350
7351 case SCI_MARKERGET:
7352 return pdoc->GetMark(wParam);
7353
7354 case SCI_MARKERNEXT: {
7355 int lt = pdoc->LinesTotal();
7356 for (int iLine = wParam; iLine < lt; iLine++) {
7357 if ((pdoc->GetMark(iLine) & lParam) != 0)
7358 return iLine;
7359 }
7360 }
7361 return -1;
7362
7363 case SCI_MARKERPREVIOUS: {
7364 for (int iLine = wParam; iLine >= 0; iLine--) {
7365 if ((pdoc->GetMark(iLine) & lParam) != 0)
7366 return iLine;
7367 }
7368 }
7369 return -1;
7370
9e730a78
RD
7371 case SCI_MARKERDEFINEPIXMAP:
7372 if (wParam <= MARKER_MAX) {
7373 vs.markers[wParam].SetXPM(CharPtrFromSPtr(lParam));
7374 };
7375 InvalidateStyleData();
7376 RedrawSelMargin();
7377 break;
7378
9ce192d4 7379 case SCI_SETMARGINTYPEN:
f6bcfd97 7380 if (ValidMargin(wParam)) {
b8193d80 7381 vs.ms[wParam].style = lParam;
9ce192d4
RD
7382 InvalidateStyleRedraw();
7383 }
7384 break;
d134f170 7385
9ce192d4 7386 case SCI_GETMARGINTYPEN:
d134f170 7387 if (ValidMargin(wParam))
b8193d80 7388 return vs.ms[wParam].style;
9ce192d4
RD
7389 else
7390 return 0;
d134f170 7391
9ce192d4 7392 case SCI_SETMARGINWIDTHN:
f6bcfd97 7393 if (ValidMargin(wParam)) {
8e54aaed
RD
7394 // Short-circuit if the width is unchanged, to avoid unnecessary redraw.
7395 if (vs.ms[wParam].width != lParam) {
7396 vs.ms[wParam].width = lParam;
7397 InvalidateStyleRedraw();
7398 }
9ce192d4
RD
7399 }
7400 break;
d134f170 7401
9ce192d4 7402 case SCI_GETMARGINWIDTHN:
d134f170 7403 if (ValidMargin(wParam))
9ce192d4
RD
7404 return vs.ms[wParam].width;
7405 else
7406 return 0;
d134f170 7407
9ce192d4 7408 case SCI_SETMARGINMASKN:
f6bcfd97 7409 if (ValidMargin(wParam)) {
9ce192d4
RD
7410 vs.ms[wParam].mask = lParam;
7411 InvalidateStyleRedraw();
7412 }
7413 break;
d134f170 7414
9ce192d4 7415 case SCI_GETMARGINMASKN:
d134f170 7416 if (ValidMargin(wParam))
9ce192d4
RD
7417 return vs.ms[wParam].mask;
7418 else
7419 return 0;
d134f170 7420
9ce192d4 7421 case SCI_SETMARGINSENSITIVEN:
f6bcfd97 7422 if (ValidMargin(wParam)) {
1a2fb4cd 7423 vs.ms[wParam].sensitive = lParam != 0;
9ce192d4
RD
7424 InvalidateStyleRedraw();
7425 }
7426 break;
d134f170 7427
9ce192d4 7428 case SCI_GETMARGINSENSITIVEN:
d134f170 7429 if (ValidMargin(wParam))
9ce192d4
RD
7430 return vs.ms[wParam].sensitive ? 1 : 0;
7431 else
7432 return 0;
7433
7434 case SCI_STYLECLEARALL:
7435 vs.ClearStyles();
7436 InvalidateStyleRedraw();
7437 break;
7438
7439 case SCI_STYLESETFORE:
9ce192d4 7440 case SCI_STYLESETBACK:
9ce192d4 7441 case SCI_STYLESETBOLD:
9ce192d4 7442 case SCI_STYLESETITALIC:
9ce192d4 7443 case SCI_STYLESETEOLFILLED:
9ce192d4 7444 case SCI_STYLESETSIZE:
9ce192d4 7445 case SCI_STYLESETFONT:
f6bcfd97 7446 case SCI_STYLESETUNDERLINE:
65ec6247 7447 case SCI_STYLESETCASE:
f6bcfd97 7448 case SCI_STYLESETCHARACTERSET:
d134f170 7449 case SCI_STYLESETVISIBLE:
1a2fb4cd 7450 case SCI_STYLESETCHANGEABLE:
9e730a78 7451 case SCI_STYLESETHOTSPOT:
7e0c58e9
RD
7452 StyleSetMessage(iMessage, wParam, lParam);
7453 break;
7454
7455 case SCI_STYLEGETFORE:
7456 case SCI_STYLEGETBACK:
7457 case SCI_STYLEGETBOLD:
7458 case SCI_STYLEGETITALIC:
7459 case SCI_STYLEGETEOLFILLED:
7460 case SCI_STYLEGETSIZE:
7461 case SCI_STYLEGETFONT:
7462 case SCI_STYLEGETUNDERLINE:
7463 case SCI_STYLEGETCASE:
7464 case SCI_STYLEGETCHARACTERSET:
7465 case SCI_STYLEGETVISIBLE:
7466 case SCI_STYLEGETCHANGEABLE:
7467 case SCI_STYLEGETHOTSPOT:
7468 return StyleGetMessage(iMessage, wParam, lParam);
d134f170 7469
9ce192d4
RD
7470 case SCI_STYLERESETDEFAULT:
7471 vs.ResetDefaultStyle();
7472 InvalidateStyleRedraw();
7473 break;
9ce192d4 7474 case SCI_SETSTYLEBITS:
7e0c58e9 7475 vs.EnsureStyle((1 << wParam) - 1);
9ce192d4
RD
7476 pdoc->SetStylingBits(wParam);
7477 break;
d134f170 7478
9ce192d4
RD
7479 case SCI_GETSTYLEBITS:
7480 return pdoc->stylingBits;
d134f170 7481
9ce192d4
RD
7482 case SCI_SETLINESTATE:
7483 return pdoc->SetLineState(wParam, lParam);
d134f170 7484
9ce192d4
RD
7485 case SCI_GETLINESTATE:
7486 return pdoc->GetLineState(wParam);
d134f170 7487
9ce192d4
RD
7488 case SCI_GETMAXLINESTATE:
7489 return pdoc->GetMaxLineState();
d134f170 7490
65ec6247
RD
7491 case SCI_GETCARETLINEVISIBLE:
7492 return vs.showCaretLineBackground;
7493 case SCI_SETCARETLINEVISIBLE:
1a2fb4cd 7494 vs.showCaretLineBackground = wParam != 0;
65ec6247
RD
7495 InvalidateStyleRedraw();
7496 break;
7497 case SCI_GETCARETLINEBACK:
7498 return vs.caretLineBackground.desired.AsLong();
7499 case SCI_SETCARETLINEBACK:
7500 vs.caretLineBackground.desired = wParam;
7501 InvalidateStyleRedraw();
7502 break;
b8193d80
RD
7503 case SCI_GETCARETLINEBACKALPHA:
7504 return vs.caretLineAlpha;
7505 case SCI_SETCARETLINEBACKALPHA:
7506 vs.caretLineAlpha = wParam;
7507 InvalidateStyleRedraw();
7508 break;
65ec6247 7509
d134f170
RD
7510 // Folding messages
7511
9ce192d4
RD
7512 case SCI_VISIBLEFROMDOCLINE:
7513 return cs.DisplayFromDoc(wParam);
d134f170 7514
9ce192d4
RD
7515 case SCI_DOCLINEFROMVISIBLE:
7516 return cs.DocFromDisplay(wParam);
7517
1e9bafca
RD
7518 case SCI_WRAPCOUNT:
7519 return WrapCount(wParam);
7520
9ce192d4
RD
7521 case SCI_SETFOLDLEVEL: {
7522 int prev = pdoc->SetLevel(wParam, lParam);
7523 if (prev != lParam)
7524 RedrawSelMargin();
7525 return prev;
7526 }
d134f170 7527
9ce192d4
RD
7528 case SCI_GETFOLDLEVEL:
7529 return pdoc->GetLevel(wParam);
d134f170 7530
9ce192d4
RD
7531 case SCI_GETLASTCHILD:
7532 return pdoc->GetLastChild(wParam, lParam);
d134f170 7533
9ce192d4
RD
7534 case SCI_GETFOLDPARENT:
7535 return pdoc->GetFoldParent(wParam);
d134f170 7536
9ce192d4
RD
7537 case SCI_SHOWLINES:
7538 cs.SetVisible(wParam, lParam, true);
7539 SetScrollBars();
7540 Redraw();
7541 break;
d134f170 7542
9ce192d4 7543 case SCI_HIDELINES:
9e96e16f
RD
7544 if (wParam > 0)
7545 cs.SetVisible(wParam, lParam, false);
9ce192d4
RD
7546 SetScrollBars();
7547 Redraw();
7548 break;
d134f170 7549
9ce192d4
RD
7550 case SCI_GETLINEVISIBLE:
7551 return cs.GetVisible(wParam);
d134f170 7552
9ce192d4 7553 case SCI_SETFOLDEXPANDED:
1a2fb4cd 7554 if (cs.SetExpanded(wParam, lParam != 0)) {
9ce192d4
RD
7555 RedrawSelMargin();
7556 }
7557 break;
d134f170 7558
9ce192d4
RD
7559 case SCI_GETFOLDEXPANDED:
7560 return cs.GetExpanded(wParam);
d134f170 7561
9ce192d4
RD
7562 case SCI_SETFOLDFLAGS:
7563 foldFlags = wParam;
7564 Redraw();
7565 break;
7566
7567 case SCI_TOGGLEFOLD:
7568 ToggleContraction(wParam);
7569 break;
d134f170 7570
9ce192d4 7571 case SCI_ENSUREVISIBLE:
65ec6247
RD
7572 EnsureLineVisible(wParam, false);
7573 break;
7574
7575 case SCI_ENSUREVISIBLEENFORCEPOLICY:
7576 EnsureLineVisible(wParam, true);
9ce192d4 7577 break;
d134f170 7578
9ce192d4
RD
7579 case SCI_SEARCHANCHOR:
7580 SearchAnchor();
7581 break;
7582
7583 case SCI_SEARCHNEXT:
7584 case SCI_SEARCHPREV:
7585 return SearchText(iMessage, wParam, lParam);
9ce192d4 7586
a834585d
RD
7587 case SCI_SETXCARETPOLICY:
7588 caretXPolicy = wParam;
7589 caretXSlop = lParam;
7590 break;
7591
7592 case SCI_SETYCARETPOLICY:
7593 caretYPolicy = wParam;
7594 caretYSlop = lParam;
9ce192d4
RD
7595 break;
7596
65ec6247
RD
7597 case SCI_SETVISIBLEPOLICY:
7598 visiblePolicy = wParam;
7599 visibleSlop = lParam;
7600 break;
7601
f6bcfd97
BP
7602 case SCI_LINESONSCREEN:
7603 return LinesOnScreen();
7604
9ce192d4 7605 case SCI_SETSELFORE:
1a2fb4cd
RD
7606 vs.selforeset = wParam != 0;
7607 vs.selforeground.desired = ColourDesired(lParam);
9e96e16f 7608 vs.selAdditionalForeground.desired = ColourDesired(lParam);
9ce192d4
RD
7609 InvalidateStyleRedraw();
7610 break;
7611
7612 case SCI_SETSELBACK:
1a2fb4cd
RD
7613 vs.selbackset = wParam != 0;
7614 vs.selbackground.desired = ColourDesired(lParam);
9e96e16f 7615 vs.selAdditionalBackground.desired = ColourDesired(lParam);
9ce192d4
RD
7616 InvalidateStyleRedraw();
7617 break;
7618
b8193d80
RD
7619 case SCI_SETSELALPHA:
7620 vs.selAlpha = wParam;
9e96e16f 7621 vs.selAdditionalAlpha = wParam;
b8193d80
RD
7622 InvalidateStyleRedraw();
7623 break;
7624
7625 case SCI_GETSELALPHA:
7626 return vs.selAlpha;
7627
7e0c58e9
RD
7628 case SCI_GETSELEOLFILLED:
7629 return vs.selEOLFilled;
7630
7631 case SCI_SETSELEOLFILLED:
7632 vs.selEOLFilled = wParam != 0;
7633 InvalidateStyleRedraw();
7634 break;
7635
f114b858
RD
7636 case SCI_SETWHITESPACEFORE:
7637 vs.whitespaceForegroundSet = wParam != 0;
7638 vs.whitespaceForeground.desired = ColourDesired(lParam);
7639 InvalidateStyleRedraw();
7640 break;
7641
7642 case SCI_SETWHITESPACEBACK:
7643 vs.whitespaceBackgroundSet = wParam != 0;
7644 vs.whitespaceBackground.desired = ColourDesired(lParam);
7645 InvalidateStyleRedraw();
7646 break;
7647
9ce192d4 7648 case SCI_SETCARETFORE:
1a2fb4cd 7649 vs.caretcolour.desired = ColourDesired(wParam);
9ce192d4
RD
7650 InvalidateStyleRedraw();
7651 break;
7652
d134f170
RD
7653 case SCI_GETCARETFORE:
7654 return vs.caretcolour.desired.AsLong();
7655
7e0c58e9
RD
7656 case SCI_SETCARETSTYLE:
7657 if (wParam >= CARETSTYLE_INVISIBLE && wParam <= CARETSTYLE_BLOCK)
7658 vs.caretStyle = wParam;
7659 else
7660 /* Default to the line caret */
7661 vs.caretStyle = CARETSTYLE_LINE;
7662 InvalidateStyleRedraw();
7663 break;
7664
7665 case SCI_GETCARETSTYLE:
7666 return vs.caretStyle;
7667
65ec6247 7668 case SCI_SETCARETWIDTH:
9e730a78
RD
7669 if (wParam <= 0)
7670 vs.caretWidth = 0;
65ec6247
RD
7671 else if (wParam >= 3)
7672 vs.caretWidth = 3;
7673 else
7674 vs.caretWidth = wParam;
7675 InvalidateStyleRedraw();
7676 break;
7677
7678 case SCI_GETCARETWIDTH:
7679 return vs.caretWidth;
7680
9ce192d4 7681 case SCI_ASSIGNCMDKEY:
d134f170 7682 kmap.AssignCmdKey(Platform::LowShortFromLong(wParam),
7e0c58e9 7683 Platform::HighShortFromLong(wParam), lParam);
9ce192d4
RD
7684 break;
7685
7686 case SCI_CLEARCMDKEY:
d134f170 7687 kmap.AssignCmdKey(Platform::LowShortFromLong(wParam),
7e0c58e9 7688 Platform::HighShortFromLong(wParam), SCI_NULL);
9ce192d4
RD
7689 break;
7690
7691 case SCI_CLEARALLCMDKEYS:
7692 kmap.Clear();
7693 break;
7694
7695 case SCI_INDICSETSTYLE:
7696 if (wParam <= INDIC_MAX) {
7697 vs.indicators[wParam].style = lParam;
7698 InvalidateStyleRedraw();
7699 }
7700 break;
7701
7702 case SCI_INDICGETSTYLE:
7703 return (wParam <= INDIC_MAX) ? vs.indicators[wParam].style : 0;
7704
7705 case SCI_INDICSETFORE:
7706 if (wParam <= INDIC_MAX) {
1a2fb4cd 7707 vs.indicators[wParam].fore.desired = ColourDesired(lParam);
9ce192d4
RD
7708 InvalidateStyleRedraw();
7709 }
7710 break;
7711
7712 case SCI_INDICGETFORE:
7713 return (wParam <= INDIC_MAX) ? vs.indicators[wParam].fore.desired.AsLong() : 0;
7714
7e0c58e9
RD
7715 case SCI_INDICSETUNDER:
7716 if (wParam <= INDIC_MAX) {
7717 vs.indicators[wParam].under = lParam != 0;
7718 InvalidateStyleRedraw();
7719 }
7720 break;
7721
7722 case SCI_INDICGETUNDER:
7723 return (wParam <= INDIC_MAX) ? vs.indicators[wParam].under : 0;
7724
9e96e16f
RD
7725 case SCI_INDICSETALPHA:
7726 if (wParam <= INDIC_MAX && lParam >=0 && lParam <= 100) {
7727 vs.indicators[wParam].fillAlpha = lParam;
7728 InvalidateStyleRedraw();
7729 }
7730 break;
7731
7732 case SCI_INDICGETALPHA:
7733 return (wParam <= INDIC_MAX) ? vs.indicators[wParam].fillAlpha : 0;
7734
7e0c58e9
RD
7735 case SCI_SETINDICATORCURRENT:
7736 pdoc->decorations.SetCurrentIndicator(wParam);
7737 break;
7738 case SCI_GETINDICATORCURRENT:
7739 return pdoc->decorations.GetCurrentIndicator();
7740 case SCI_SETINDICATORVALUE:
7741 pdoc->decorations.SetCurrentValue(wParam);
7742 break;
7743 case SCI_GETINDICATORVALUE:
7744 return pdoc->decorations.GetCurrentValue();
7745
7746 case SCI_INDICATORFILLRANGE:
7747 pdoc->DecorationFillRange(wParam, pdoc->decorations.GetCurrentValue(), lParam);
7748 break;
7749
7750 case SCI_INDICATORCLEARRANGE:
7751 pdoc->DecorationFillRange(wParam, 0, lParam);
7752 break;
7753
7754 case SCI_INDICATORALLONFOR:
7755 return pdoc->decorations.AllOnFor(wParam);
7756
7757 case SCI_INDICATORVALUEAT:
7758 return pdoc->decorations.ValueAt(wParam, lParam);
7759
7760 case SCI_INDICATORSTART:
7761 return pdoc->decorations.Start(wParam, lParam);
7762
7763 case SCI_INDICATOREND:
7764 return pdoc->decorations.End(wParam, lParam);
7765
9ce192d4
RD
7766 case SCI_LINEDOWN:
7767 case SCI_LINEDOWNEXTEND:
9e730a78
RD
7768 case SCI_PARADOWN:
7769 case SCI_PARADOWNEXTEND:
9ce192d4
RD
7770 case SCI_LINEUP:
7771 case SCI_LINEUPEXTEND:
9e730a78
RD
7772 case SCI_PARAUP:
7773 case SCI_PARAUPEXTEND:
9ce192d4
RD
7774 case SCI_CHARLEFT:
7775 case SCI_CHARLEFTEXTEND:
7776 case SCI_CHARRIGHT:
7777 case SCI_CHARRIGHTEXTEND:
7778 case SCI_WORDLEFT:
7779 case SCI_WORDLEFTEXTEND:
7780 case SCI_WORDRIGHT:
7781 case SCI_WORDRIGHTEXTEND:
8e54aaed
RD
7782 case SCI_WORDLEFTEND:
7783 case SCI_WORDLEFTENDEXTEND:
7784 case SCI_WORDRIGHTEND:
7785 case SCI_WORDRIGHTENDEXTEND:
9ce192d4
RD
7786 case SCI_HOME:
7787 case SCI_HOMEEXTEND:
7788 case SCI_LINEEND:
7789 case SCI_LINEENDEXTEND:
9e730a78
RD
7790 case SCI_HOMEWRAP:
7791 case SCI_HOMEWRAPEXTEND:
7792 case SCI_LINEENDWRAP:
7793 case SCI_LINEENDWRAPEXTEND:
9ce192d4
RD
7794 case SCI_DOCUMENTSTART:
7795 case SCI_DOCUMENTSTARTEXTEND:
7796 case SCI_DOCUMENTEND:
7797 case SCI_DOCUMENTENDEXTEND:
8e54aaed
RD
7798
7799 case SCI_STUTTEREDPAGEUP:
7800 case SCI_STUTTEREDPAGEUPEXTEND:
7801 case SCI_STUTTEREDPAGEDOWN:
7802 case SCI_STUTTEREDPAGEDOWNEXTEND:
7803
9ce192d4
RD
7804 case SCI_PAGEUP:
7805 case SCI_PAGEUPEXTEND:
7806 case SCI_PAGEDOWN:
7807 case SCI_PAGEDOWNEXTEND:
7808 case SCI_EDITTOGGLEOVERTYPE:
7809 case SCI_CANCEL:
7810 case SCI_DELETEBACK:
7811 case SCI_TAB:
7812 case SCI_BACKTAB:
7813 case SCI_NEWLINE:
7814 case SCI_FORMFEED:
7815 case SCI_VCHOME:
7816 case SCI_VCHOMEEXTEND:
9e730a78
RD
7817 case SCI_VCHOMEWRAP:
7818 case SCI_VCHOMEWRAPEXTEND:
9ce192d4
RD
7819 case SCI_ZOOMIN:
7820 case SCI_ZOOMOUT:
7821 case SCI_DELWORDLEFT:
7822 case SCI_DELWORDRIGHT:
7e0c58e9 7823 case SCI_DELWORDRIGHTEND:
65ec6247
RD
7824 case SCI_DELLINELEFT:
7825 case SCI_DELLINERIGHT:
e14d10b0 7826 case SCI_LINECOPY:
f6bcfd97
BP
7827 case SCI_LINECUT:
7828 case SCI_LINEDELETE:
7829 case SCI_LINETRANSPOSE:
9e730a78 7830 case SCI_LINEDUPLICATE:
f6bcfd97
BP
7831 case SCI_LOWERCASE:
7832 case SCI_UPPERCASE:
7833 case SCI_LINESCROLLDOWN:
7834 case SCI_LINESCROLLUP:
65ec6247
RD
7835 case SCI_WORDPARTLEFT:
7836 case SCI_WORDPARTLEFTEXTEND:
7837 case SCI_WORDPARTRIGHT:
7838 case SCI_WORDPARTRIGHTEXTEND:
1a2fb4cd 7839 case SCI_DELETEBACKNOTLINE:
f114b858
RD
7840 case SCI_HOMEDISPLAY:
7841 case SCI_HOMEDISPLAYEXTEND:
7842 case SCI_LINEENDDISPLAY:
7843 case SCI_LINEENDDISPLAYEXTEND:
8e54aaed
RD
7844 case SCI_LINEDOWNRECTEXTEND:
7845 case SCI_LINEUPRECTEXTEND:
7846 case SCI_CHARLEFTRECTEXTEND:
7847 case SCI_CHARRIGHTRECTEXTEND:
7848 case SCI_HOMERECTEXTEND:
7849 case SCI_VCHOMERECTEXTEND:
7850 case SCI_LINEENDRECTEXTEND:
7851 case SCI_PAGEUPRECTEXTEND:
7852 case SCI_PAGEDOWNRECTEXTEND:
1e9bafca 7853 case SCI_SELECTIONDUPLICATE:
9ce192d4
RD
7854 return KeyCommand(iMessage);
7855
7856 case SCI_BRACEHIGHLIGHT:
7857 SetBraceHighlight(static_cast<int>(wParam), lParam, STYLE_BRACELIGHT);
7858 break;
d134f170 7859
9ce192d4
RD
7860 case SCI_BRACEBADLIGHT:
7861 SetBraceHighlight(static_cast<int>(wParam), -1, STYLE_BRACEBAD);
7862 break;
7863
7864 case SCI_BRACEMATCH:
7865 // wParam is position of char to find brace for,
7866 // lParam is maximum amount of text to restyle to find it
1e9bafca 7867 return pdoc->BraceMatch(wParam, lParam);
9ce192d4
RD
7868
7869 case SCI_GETVIEWEOL:
7870 return vs.viewEOL;
7871
7872 case SCI_SETVIEWEOL:
1a2fb4cd 7873 vs.viewEOL = wParam != 0;
a834585d 7874 InvalidateStyleRedraw();
9ce192d4
RD
7875 break;
7876
f6bcfd97
BP
7877 case SCI_SETZOOM:
7878 vs.zoomLevel = wParam;
7879 InvalidateStyleRedraw();
a834585d 7880 NotifyZoom();
f6bcfd97
BP
7881 break;
7882
7883 case SCI_GETZOOM:
7884 return vs.zoomLevel;
d134f170 7885
9ce192d4
RD
7886 case SCI_GETEDGECOLUMN:
7887 return theEdge;
d134f170 7888
9ce192d4
RD
7889 case SCI_SETEDGECOLUMN:
7890 theEdge = wParam;
7891 InvalidateStyleRedraw();
7892 break;
d134f170 7893
9ce192d4 7894 case SCI_GETEDGEMODE:
d134f170
RD
7895 return vs.edgeState;
7896
9ce192d4 7897 case SCI_SETEDGEMODE:
d134f170 7898 vs.edgeState = wParam;
9ce192d4
RD
7899 InvalidateStyleRedraw();
7900 break;
d134f170 7901
9ce192d4
RD
7902 case SCI_GETEDGECOLOUR:
7903 return vs.edgecolour.desired.AsLong();
d134f170 7904
9ce192d4 7905 case SCI_SETEDGECOLOUR:
1a2fb4cd 7906 vs.edgecolour.desired = ColourDesired(wParam);
9ce192d4
RD
7907 InvalidateStyleRedraw();
7908 break;
d134f170 7909
9ce192d4 7910 case SCI_GETDOCPOINTER:
1a2fb4cd 7911 return reinterpret_cast<sptr_t>(pdoc);
9ce192d4
RD
7912
7913 case SCI_SETDOCPOINTER:
9e730a78 7914 CancelModes();
9ce192d4
RD
7915 SetDocPointer(reinterpret_cast<Document *>(lParam));
7916 return 0;
7917
d134f170
RD
7918 case SCI_CREATEDOCUMENT: {
7919 Document *doc = new Document();
9e730a78
RD
7920 if (doc) {
7921 doc->AddRef();
7922 }
1a2fb4cd 7923 return reinterpret_cast<sptr_t>(doc);
d134f170
RD
7924 }
7925
7926 case SCI_ADDREFDOCUMENT:
f6bcfd97
BP
7927 (reinterpret_cast<Document *>(lParam))->AddRef();
7928 break;
d134f170
RD
7929
7930 case SCI_RELEASEDOCUMENT:
f6bcfd97
BP
7931 (reinterpret_cast<Document *>(lParam))->Release();
7932 break;
d134f170 7933
9ce192d4
RD
7934 case SCI_SETMODEVENTMASK:
7935 modEventMask = wParam;
7936 return 0;
d134f170
RD
7937
7938 case SCI_GETMODEVENTMASK:
7939 return modEventMask;
7940
9ce192d4
RD
7941 case SCI_CONVERTEOLS:
7942 pdoc->ConvertLineEnds(wParam);
9e96e16f 7943 SetSelection(sel.MainCaret(), sel.MainAnchor()); // Ensure selection inside document
9ce192d4
RD
7944 return 0;
7945
a33203cb
RD
7946 case SCI_SETLENGTHFORENCODE:
7947 lengthForEncode = wParam;
7948 return 0;
7949
f6bcfd97 7950 case SCI_SELECTIONISRECTANGLE:
9e96e16f 7951 return sel.selType == Selection::selRectangle ? 1 : 0;
8e54aaed
RD
7952
7953 case SCI_SETSELECTIONMODE: {
7954 switch (wParam) {
7955 case SC_SEL_STREAM:
9e96e16f
RD
7956 sel.SetMoveExtends(!sel.MoveExtends() || (sel.selType != Selection::selStream));
7957 sel.selType = Selection::selStream;
8e54aaed
RD
7958 break;
7959 case SC_SEL_RECTANGLE:
9e96e16f
RD
7960 sel.SetMoveExtends(!sel.MoveExtends() || (sel.selType != Selection::selRectangle));
7961 sel.selType = Selection::selRectangle;
8e54aaed
RD
7962 break;
7963 case SC_SEL_LINES:
9e96e16f
RD
7964 sel.SetMoveExtends(!sel.MoveExtends() || (sel.selType != Selection::selLines));
7965 sel.selType = Selection::selLines;
7966 break;
7967 case SC_SEL_THIN:
7968 sel.SetMoveExtends(!sel.MoveExtends() || (sel.selType != Selection::selThin));
7969 sel.selType = Selection::selThin;
8e54aaed
RD
7970 break;
7971 default:
9e96e16f
RD
7972 sel.SetMoveExtends(!sel.MoveExtends() || (sel.selType != Selection::selStream));
7973 sel.selType = Selection::selStream;
8e54aaed 7974 }
9e96e16f 7975 InvalidateSelection(sel.RangeMain(), true);
8e54aaed
RD
7976 }
7977 case SCI_GETSELECTIONMODE:
9e96e16f
RD
7978 switch (sel.selType) {
7979 case Selection::selStream:
8e54aaed 7980 return SC_SEL_STREAM;
9e96e16f 7981 case Selection::selRectangle:
8e54aaed 7982 return SC_SEL_RECTANGLE;
9e96e16f 7983 case Selection::selLines:
8e54aaed 7984 return SC_SEL_LINES;
9e96e16f
RD
7985 case Selection::selThin:
7986 return SC_SEL_THIN;
8e54aaed
RD
7987 default: // ?!
7988 return SC_SEL_STREAM;
7989 }
9e96e16f 7990 case SCI_GETLINESELSTARTPOSITION:
8e54aaed 7991 case SCI_GETLINESELENDPOSITION: {
9e96e16f
RD
7992 SelectionSegment segmentLine(SelectionPosition(pdoc->LineStart(wParam)),
7993 SelectionPosition(pdoc->LineEnd(wParam)));
7994 for (size_t r=0; r<sel.Count(); r++) {
7995 SelectionSegment portion = sel.Range(r).Intersect(segmentLine);
7996 if (portion.start.IsValid()) {
7997 return (iMessage == SCI_GETLINESELSTARTPOSITION) ? portion.start.Position() : portion.end.Position();
7998 }
7999 }
8000 return INVALID_POSITION;
8e54aaed 8001 }
f6bcfd97 8002
d134f170 8003 case SCI_SETOVERTYPE:
1a2fb4cd 8004 inOverstrike = wParam != 0;
d134f170 8005 break;
ce1ecc6d 8006
d134f170 8007 case SCI_GETOVERTYPE:
1a2fb4cd 8008 return inOverstrike ? 1 : 0;
ce1ecc6d 8009
65ec6247 8010 case SCI_SETFOCUS:
1a2fb4cd 8011 SetFocusState(wParam != 0);
65ec6247
RD
8012 break;
8013
8014 case SCI_GETFOCUS:
8015 return hasFocus;
8016
8017 case SCI_SETSTATUS:
8018 errorStatus = wParam;
8019 break;
8020
8021 case SCI_GETSTATUS:
8022 return errorStatus;
8023
8024 case SCI_SETMOUSEDOWNCAPTURES:
1a2fb4cd 8025 mouseDownCaptures = wParam != 0;
65ec6247
RD
8026 break;
8027
8028 case SCI_GETMOUSEDOWNCAPTURES:
8029 return mouseDownCaptures;
8030
8031 case SCI_SETCURSOR:
8032 cursorMode = wParam;
8033 DisplayCursor(Window::cursorText);
8034 break;
8035
8036 case SCI_GETCURSOR:
8037 return cursorMode;
8038
1a2fb4cd
RD
8039 case SCI_SETCONTROLCHARSYMBOL:
8040 controlCharSymbol = wParam;
8041 break;
8042
8043 case SCI_GETCONTROLCHARSYMBOL:
8044 return controlCharSymbol;
8045
9ce192d4 8046 case SCI_STARTRECORD:
b8b0e402 8047 recordingMacro = true;
9ce192d4
RD
8048 return 0;
8049
8050 case SCI_STOPRECORD:
b8b0e402 8051 recordingMacro = false;
9ce192d4 8052 return 0;
65ec6247
RD
8053
8054 case SCI_MOVECARETINSIDEVIEW:
8055 MoveCaretInsideView();
8056 break;
d134f170 8057
9e730a78
RD
8058 case SCI_SETFOLDMARGINCOLOUR:
8059 vs.foldmarginColourSet = wParam != 0;
8060 vs.foldmarginColour.desired = ColourDesired(lParam);
8061 InvalidateStyleRedraw();
8062 break;
8063
8064 case SCI_SETFOLDMARGINHICOLOUR:
8065 vs.foldmarginHighlightColourSet = wParam != 0;
8066 vs.foldmarginHighlightColour.desired = ColourDesired(lParam);
8067 InvalidateStyleRedraw();
8068 break;
8069
8070 case SCI_SETHOTSPOTACTIVEFORE:
8071 vs.hotspotForegroundSet = wParam != 0;
8072 vs.hotspotForeground.desired = ColourDesired(lParam);
8073 InvalidateStyleRedraw();
8074 break;
8075
7e0c58e9
RD
8076 case SCI_GETHOTSPOTACTIVEFORE:
8077 return vs.hotspotForeground.desired.AsLong();
8078
9e730a78
RD
8079 case SCI_SETHOTSPOTACTIVEBACK:
8080 vs.hotspotBackgroundSet = wParam != 0;
8081 vs.hotspotBackground.desired = ColourDesired(lParam);
8082 InvalidateStyleRedraw();
8083 break;
8084
7e0c58e9
RD
8085 case SCI_GETHOTSPOTACTIVEBACK:
8086 return vs.hotspotBackground.desired.AsLong();
8087
9e730a78
RD
8088 case SCI_SETHOTSPOTACTIVEUNDERLINE:
8089 vs.hotspotUnderline = wParam != 0;
8090 InvalidateStyleRedraw();
8091 break;
8092
7e0c58e9
RD
8093 case SCI_GETHOTSPOTACTIVEUNDERLINE:
8094 return vs.hotspotUnderline ? 1 : 0;
8095
8e54aaed
RD
8096 case SCI_SETHOTSPOTSINGLELINE:
8097 vs.hotspotSingleLine = wParam != 0;
8098 InvalidateStyleRedraw();
8099 break;
8100
7e0c58e9
RD
8101 case SCI_GETHOTSPOTSINGLELINE:
8102 return vs.hotspotSingleLine ? 1 : 0;
8103
1e9bafca
RD
8104 case SCI_SETPASTECONVERTENDINGS:
8105 convertPastes = wParam != 0;
8106 break;
8107
8108 case SCI_GETPASTECONVERTENDINGS:
8109 return convertPastes ? 1 : 0;
8110
9e96e16f
RD
8111 case SCI_GETCHARACTERPOINTER:
8112 return reinterpret_cast<sptr_t>(pdoc->BufferPointer());
8113
8114 case SCI_SETEXTRAASCENT:
8115 vs.extraAscent = wParam;
8116 InvalidateStyleRedraw();
8117 break;
8118
8119 case SCI_GETEXTRAASCENT:
8120 return vs.extraAscent;
8121
8122 case SCI_SETEXTRADESCENT:
8123 vs.extraDescent = wParam;
8124 InvalidateStyleRedraw();
8125 break;
8126
8127 case SCI_GETEXTRADESCENT:
8128 return vs.extraDescent;
8129
8130 case SCI_MARGINSETSTYLEOFFSET:
8131 vs.marginStyleOffset = wParam;
8132 InvalidateStyleRedraw();
8133 break;
8134
8135 case SCI_MARGINGETSTYLEOFFSET:
8136 return vs.marginStyleOffset;
8137
8138 case SCI_MARGINSETTEXT:
8139 pdoc->MarginSetText(wParam, CharPtrFromSPtr(lParam));
8140 break;
8141
8142 case SCI_MARGINGETTEXT: {
8143 const StyledText st = pdoc->MarginStyledText(wParam);
8144 if (lParam) {
8145 if (st.text)
8146 memcpy(CharPtrFromSPtr(lParam), st.text, st.length);
8147 else
8148 strcpy(CharPtrFromSPtr(lParam), "");
8149 }
8150 return st.length;
8151 }
8152
8153 case SCI_MARGINSETSTYLE:
8154 pdoc->MarginSetStyle(wParam, lParam);
8155 break;
8156
8157 case SCI_MARGINGETSTYLE: {
8158 const StyledText st = pdoc->MarginStyledText(wParam);
8159 return st.style;
8160 }
8161
8162 case SCI_MARGINSETSTYLES:
8163 pdoc->MarginSetStyles(wParam, reinterpret_cast<const unsigned char *>(lParam));
8164 break;
8165
8166 case SCI_MARGINGETSTYLES: {
8167 const StyledText st = pdoc->MarginStyledText(wParam);
8168 if (lParam) {
8169 if (st.styles)
8170 memcpy(CharPtrFromSPtr(lParam), st.styles, st.length);
8171 else
8172 strcpy(CharPtrFromSPtr(lParam), "");
8173 }
8174 return st.styles ? st.length : 0;
8175 }
8176
8177 case SCI_MARGINTEXTCLEARALL:
8178 pdoc->MarginClearAll();
8179 break;
8180
8181 case SCI_ANNOTATIONSETTEXT:
8182 pdoc->AnnotationSetText(wParam, CharPtrFromSPtr(lParam));
8183 break;
8184
8185 case SCI_ANNOTATIONGETTEXT: {
8186 const StyledText st = pdoc->AnnotationStyledText(wParam);
8187 if (lParam) {
8188 if (st.text)
8189 memcpy(CharPtrFromSPtr(lParam), st.text, st.length);
8190 else
8191 strcpy(CharPtrFromSPtr(lParam), "");
8192 }
8193 return st.length;
8194 }
8195
8196 case SCI_ANNOTATIONGETSTYLE: {
8197 const StyledText st = pdoc->AnnotationStyledText(wParam);
8198 return st.style;
8199 }
8200
8201 case SCI_ANNOTATIONSETSTYLE:
8202 pdoc->AnnotationSetStyle(wParam, lParam);
8203 break;
8204
8205 case SCI_ANNOTATIONSETSTYLES:
8206 pdoc->AnnotationSetStyles(wParam, reinterpret_cast<const unsigned char *>(lParam));
8207 break;
8208
8209 case SCI_ANNOTATIONGETSTYLES: {
8210 const StyledText st = pdoc->AnnotationStyledText(wParam);
8211 if (lParam) {
8212 if (st.styles)
8213 memcpy(CharPtrFromSPtr(lParam), st.styles, st.length);
8214 else
8215 strcpy(CharPtrFromSPtr(lParam), "");
8216 }
8217 return st.styles ? st.length : 0;
8218 }
8219
8220 case SCI_ANNOTATIONGETLINES:
8221 return pdoc->AnnotationLines(wParam);
8222
8223 case SCI_ANNOTATIONCLEARALL:
8224 pdoc->AnnotationClearAll();
8225 break;
8226
8227 case SCI_ANNOTATIONSETVISIBLE:
8228 SetAnnotationVisible(wParam);
8229 break;
8230
8231 case SCI_ANNOTATIONGETVISIBLE:
8232 return vs.annotationVisible;
8233
8234 case SCI_ANNOTATIONSETSTYLEOFFSET:
8235 vs.annotationStyleOffset = wParam;
8236 InvalidateStyleRedraw();
8237 break;
8238
8239 case SCI_ANNOTATIONGETSTYLEOFFSET:
8240 return vs.annotationStyleOffset;
8241
8242 case SCI_ADDUNDOACTION:
8243 pdoc->AddUndoAction(wParam, lParam & UNDO_MAY_COALESCE);
8244 break;
8245
8246 case SCI_SETMULTIPLESELECTION:
8247 multipleSelection = wParam != 0;
8248 InvalidateCaret();
8249 break;
8250
8251 case SCI_GETMULTIPLESELECTION:
8252 return multipleSelection;
8253
8254 case SCI_SETADDITIONALSELECTIONTYPING:
8255 additionalSelectionTyping = wParam != 0;
8256 InvalidateCaret();
8257 break;
8258
8259 case SCI_GETADDITIONALSELECTIONTYPING:
8260 return additionalSelectionTyping;
8261
8262 case SCI_SETADDITIONALCARETSBLINK:
8263 additionalCaretsBlink = wParam != 0;
8264 InvalidateCaret();
8265 break;
8266
8267 case SCI_GETADDITIONALCARETSBLINK:
8268 return additionalCaretsBlink;
8269
8270 case SCI_SETADDITIONALCARETSVISIBLE:
8271 additionalCaretsVisible = wParam != 0;
8272 InvalidateCaret();
8273 break;
8274
8275 case SCI_GETADDITIONALCARETSVISIBLE:
8276 return additionalCaretsVisible;
8277
8278 case SCI_GETSELECTIONS:
8279 return sel.Count();
8280
8281 case SCI_CLEARSELECTIONS:
8282 sel.Clear();
8283 Redraw();
8284 break;
8285
8286 case SCI_SETSELECTION:
8287 sel.SetSelection(SelectionRange(wParam, lParam));
8288 Redraw();
8289 break;
8290
8291 case SCI_ADDSELECTION:
8292 sel.AddSelection(SelectionRange(wParam, lParam));
8293 Redraw();
8294 break;
8295
8296 case SCI_SETMAINSELECTION:
8297 sel.SetMain(wParam);
8298 Redraw();
8299 break;
8300
8301 case SCI_GETMAINSELECTION:
8302 return sel.Main();
8303
8304 case SCI_SETSELECTIONNCARET:
8305 sel.Range(wParam).caret.SetPosition(lParam);
8306 Redraw();
8307 break;
8308
8309 case SCI_GETSELECTIONNCARET:
8310 return sel.Range(wParam).caret.Position();
8311
8312 case SCI_SETSELECTIONNANCHOR:
8313 sel.Range(wParam).anchor.SetPosition(lParam);
8314 Redraw();
8315 break;
8316 case SCI_GETSELECTIONNANCHOR:
8317 return sel.Range(wParam).anchor.Position();
8318
8319 case SCI_SETSELECTIONNCARETVIRTUALSPACE:
8320 sel.Range(wParam).caret.SetVirtualSpace(lParam);
8321 Redraw();
8322 break;
8323
8324 case SCI_GETSELECTIONNCARETVIRTUALSPACE:
8325 return sel.Range(wParam).caret.VirtualSpace();
8326
8327 case SCI_SETSELECTIONNANCHORVIRTUALSPACE:
8328 sel.Range(wParam).anchor.SetVirtualSpace(lParam);
8329 Redraw();
8330 break;
8331
8332 case SCI_GETSELECTIONNANCHORVIRTUALSPACE:
8333 return sel.Range(wParam).anchor.VirtualSpace();
8334
8335 case SCI_SETSELECTIONNSTART:
8336 sel.Range(wParam).anchor.SetPosition(lParam);
8337 Redraw();
8338 break;
8339
8340 case SCI_GETSELECTIONNSTART:
8341 return sel.Range(wParam).Start().Position();
8342
8343 case SCI_SETSELECTIONNEND:
8344 sel.Range(wParam).caret.SetPosition(lParam);
8345 Redraw();
8346 break;
8347
8348 case SCI_GETSELECTIONNEND:
8349 return sel.Range(wParam).End().Position();
8350
8351 case SCI_SETRECTANGULARSELECTIONCARET:
8352 if (!sel.IsRectangular())
8353 sel.Clear();
8354 sel.selType = Selection::selRectangle;
8355 sel.Rectangular().caret.SetPosition(wParam);
8356 SetRectangularRange();
8357 Redraw();
8358 break;
8359
8360 case SCI_GETRECTANGULARSELECTIONCARET:
8361 return sel.Rectangular().caret.Position();
8362
8363 case SCI_SETRECTANGULARSELECTIONANCHOR:
8364 if (!sel.IsRectangular())
8365 sel.Clear();
8366 sel.selType = Selection::selRectangle;
8367 sel.Rectangular().anchor.SetPosition(wParam);
8368 SetRectangularRange();
8369 Redraw();
8370 break;
8371
8372 case SCI_GETRECTANGULARSELECTIONANCHOR:
8373 return sel.Rectangular().anchor.Position();
8374
8375 case SCI_SETRECTANGULARSELECTIONCARETVIRTUALSPACE:
8376 if (!sel.IsRectangular())
8377 sel.Clear();
8378 sel.selType = Selection::selRectangle;
8379 sel.Rectangular().caret.SetVirtualSpace(wParam);
8380 SetRectangularRange();
8381 Redraw();
8382 break;
8383
8384 case SCI_GETRECTANGULARSELECTIONCARETVIRTUALSPACE:
8385 return sel.Rectangular().caret.VirtualSpace();
8386
8387 case SCI_SETRECTANGULARSELECTIONANCHORVIRTUALSPACE:
8388 if (!sel.IsRectangular())
8389 sel.Clear();
8390 sel.selType = Selection::selRectangle;
8391 sel.Rectangular().anchor.SetVirtualSpace(wParam);
8392 SetRectangularRange();
8393 Redraw();
8394 break;
8395
8396 case SCI_GETRECTANGULARSELECTIONANCHORVIRTUALSPACE:
8397 return sel.Rectangular().anchor.VirtualSpace();
8398
8399 case SCI_SETVIRTUALSPACEOPTIONS:
8400 virtualSpaceOptions = wParam;
8401 break;
8402
8403 case SCI_GETVIRTUALSPACEOPTIONS:
8404 return virtualSpaceOptions;
8405
8406 case SCI_SETADDITIONALSELFORE:
8407 vs.selAdditionalForeground.desired = ColourDesired(wParam);
8408 InvalidateStyleRedraw();
8409 break;
8410
8411 case SCI_SETADDITIONALSELBACK:
8412 vs.selAdditionalBackground.desired = ColourDesired(wParam);
8413 InvalidateStyleRedraw();
8414 break;
8415
8416 case SCI_SETADDITIONALSELALPHA:
8417 vs.selAdditionalAlpha = wParam;
8418 InvalidateStyleRedraw();
8419 break;
8420
8421 case SCI_GETADDITIONALSELALPHA:
8422 return vs.selAdditionalAlpha;
8423
8424 case SCI_SETADDITIONALCARETFORE:
8425 vs.additionalCaretColour.desired = ColourDesired(wParam);
8426 InvalidateStyleRedraw();
8427 break;
8428
8429 case SCI_GETADDITIONALCARETFORE:
8430 return vs.additionalCaretColour.desired.AsLong();
8431
8432 case SCI_ROTATESELECTION:
8433 sel.RotateMain();
8434 InvalidateSelection(sel.RangeMain(), true);
8435 break;
8436
8437 case SCI_SWAPMAINANCHORCARET:
8438 InvalidateSelection(sel.RangeMain());
8439 sel.RangeMain() = SelectionRange(sel.RangeMain().anchor, sel.RangeMain().caret);
8440 break;
8441
9ce192d4
RD
8442 default:
8443 return DefWndProc(iMessage, wParam, lParam);
8444 }
8445 //Platform::DebugPrintf("end wnd proc\n");
8446 return 0l;
8447}