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