]> git.saurik.com Git - wxWidgets.git/blob - contrib/src/stc/scintilla/src/Editor.cxx
2171924f0fd5eea90a496b6d8b5a139b573c0ba0
[wxWidgets.git] / contrib / src / stc / scintilla / src / Editor.cxx
1 // Scintilla source code edit control
2 /** @file Editor.cxx
3 ** Main code for the edit control.
4 **/
5 // Copyright 1998-2001 by Neil Hodgson <neilh@scintilla.org>
6 // The License.txt file describes the conditions under which this software may be distributed.
7
8 #include <stdlib.h>
9 #include <string.h>
10 #include <stdio.h>
11 #include <ctype.h>
12
13 #include "Platform.h"
14
15 #include "Scintilla.h"
16
17 #include "ContractionState.h"
18 #include "SVector.h"
19 #include "CellBuffer.h"
20 #include "KeyMap.h"
21 #include "Indicator.h"
22 #include "LineMarker.h"
23 #include "Style.h"
24 #include "ViewStyle.h"
25 #include "Document.h"
26 #include "Editor.h"
27
28 Caret::Caret() :
29 active(false), on(false), period(500) {}
30
31 Timer::Timer() :
32 ticking(false), ticksToWait(0), tickerID(0) {}
33
34 Editor::Editor() {
35 ctrlID = 0;
36
37 stylesValid = false;
38
39 printMagnification = 0;
40 printColourMode = SC_PRINT_NORMAL;
41 cursorMode = SC_CURSORNORMAL;
42
43 hasFocus = false;
44 hideSelection = false;
45 inOverstrike = false;
46 errorStatus = 0;
47 mouseDownCaptures = true;
48
49 bufferedDraw = true;
50
51 lastClickTime = 0;
52 dwellDelay = SC_TIME_FOREVER;
53 ticksToDwell = SC_TIME_FOREVER;
54 dwelling = false;
55 ptMouseLast.x = 0;
56 ptMouseLast.y = 0;
57 inDragDrop = false;
58 dropWentOutside = false;
59 posDrag = invalidPosition;
60 posDrop = invalidPosition;
61 selectionType = selChar;
62
63 lastXChosen = 0;
64 lineAnchor = 0;
65 originalAnchorPos = 0;
66
67 selType = selStream;
68 xStartSelect = 0;
69 xEndSelect = 0;
70 primarySelection = true;
71
72 caretPolicy = CARET_SLOP;
73 caretSlop = 0;
74
75 visiblePolicy = VISIBLE_SLOP;
76 visibleSlop = 0;
77
78 searchAnchor = 0;
79
80 xOffset = 0;
81 xCaretMargin = 50;
82 horizontalScrollBarVisible = true;
83
84 currentPos = 0;
85 anchor = 0;
86
87 targetStart = 0;
88 targetEnd = 0;
89 searchFlags = 0;
90
91 topLine = 0;
92 posTopLine = 0;
93
94 needUpdateUI = true;
95 braces[0] = invalidPosition;
96 braces[1] = invalidPosition;
97 bracesMatchStyle = STYLE_BRACEBAD;
98 highlightGuideColumn = 0;
99
100 theEdge = 0;
101
102 paintState = notPainting;
103
104 modEventMask = SC_MODEVENTMASKALL;
105
106 pdoc = new Document();
107 pdoc ->AddRef();
108 pdoc->AddWatcher(this, 0);
109
110 recordingMacro = false;
111 foldFlags = 0;
112 }
113
114 Editor::~Editor() {
115 pdoc->RemoveWatcher(this, 0);
116 pdoc->Release();
117 pdoc = 0;
118 DropGraphics();
119 }
120
121 void Editor::Finalise() {
122 CancelModes();
123 }
124
125 void Editor::DropGraphics() {
126 pixmapLine.Release();
127 pixmapSelMargin.Release();
128 pixmapSelPattern.Release();
129 pixmapIndentGuide.Release();
130 }
131
132 void Editor::InvalidateStyleData() {
133 stylesValid = false;
134 palette.Release();
135 DropGraphics();
136 }
137
138 void Editor::InvalidateStyleRedraw() {
139 InvalidateStyleData();
140 Redraw();
141 }
142
143 void Editor::RefreshColourPalette(Palette &pal, bool want) {
144 vs.RefreshColourPalette(pal, want);
145 }
146
147 void Editor::RefreshStyleData() {
148 if (!stylesValid) {
149 stylesValid = true;
150 Surface surface;
151 surface.Init();
152 vs.Refresh(surface);
153 RefreshColourPalette(palette, true);
154 palette.Allocate(wMain);
155 RefreshColourPalette(palette, false);
156 SetScrollBars();
157 }
158 }
159
160 PRectangle Editor::GetClientRectangle() {
161 return wMain.GetClientPosition();
162 }
163
164 PRectangle Editor::GetTextRectangle() {
165 PRectangle rc = GetClientRectangle();
166 rc.left += vs.fixedColumnWidth;
167 rc.right -= vs.rightMarginWidth;
168 return rc;
169 }
170
171 int Editor::LinesOnScreen() {
172 PRectangle rcClient = GetClientRectangle();
173 int htClient = rcClient.bottom - rcClient.top;
174 //Platform::DebugPrintf("lines on screen = %d\n", htClient / lineHeight + 1);
175 return htClient / vs.lineHeight;
176 }
177
178 int Editor::LinesToScroll() {
179 int retVal = LinesOnScreen() - 1;
180 if (retVal < 1)
181 return 1;
182 else
183 return retVal;
184 }
185
186 int Editor::MaxScrollPos() {
187 //Platform::DebugPrintf("Lines %d screen = %d maxScroll = %d\n",
188 //LinesTotal(), LinesOnScreen(), LinesTotal() - LinesOnScreen() + 1);
189 int retVal = cs.LinesDisplayed() - LinesOnScreen();
190 if (retVal < 0)
191 return 0;
192 else
193 return retVal;
194 }
195
196 static inline bool IsControlCharacter(char ch) {
197 // iscntrl returns true for lots of chars > 127 which are displayable
198 return ch >= 0 && ch < ' ';
199 }
200
201 const char *ControlCharacterString(unsigned char ch) {
202 const char *reps[] = {
203 "NUL", "SOH", "STX", "ETX", "EOT", "ENQ", "ACK", "BEL",
204 "BS", "HT", "LF", "VT", "FF", "CR", "SO", "SI",
205 "DLE", "DC1", "DC2", "DC3", "DC4", "NAK", "SYN", "ETB",
206 "CAN", "EM", "SUB", "ESC", "FS", "GS", "RS", "US"
207 };
208 if (ch < (sizeof(reps) / sizeof(reps[0]))) {
209 return reps[ch];
210 } else {
211 return "BAD";
212 }
213 }
214
215 Point Editor::LocationFromPosition(int pos) {
216 Point pt;
217 RefreshStyleData();
218 if (pos == INVALID_POSITION)
219 return pt;
220 int line = pdoc->LineFromPosition(pos);
221 int lineVisible = cs.DisplayFromDoc(line);
222 //Platform::DebugPrintf("line=%d\n", line);
223 Surface surface;
224 surface.Init();
225 surface.SetUnicodeMode(SC_CP_UTF8 == pdoc->dbcsCodePage);
226 pt.y = (lineVisible - topLine) * vs.lineHeight; // + half a lineheight?
227 unsigned int posLineStart = pdoc->LineStart(line);
228 LineLayout ll;
229 LayoutLine(line, &surface, vs, ll);
230 if ((pos - posLineStart) > LineLayout::maxLineLength) {
231 // very long line so put x at arbitrary large position
232 pt.x = ll.positions[LineLayout::maxLineLength] + vs.fixedColumnWidth - xOffset;
233 } else {
234 pt.x = ll.positions[pos - posLineStart] + vs.fixedColumnWidth - xOffset;
235 }
236 return pt;
237 }
238
239 int Editor::XFromPosition(int pos) {
240 Point pt = LocationFromPosition(pos);
241 return pt.x - vs.fixedColumnWidth + xOffset;
242 }
243
244 int Editor::LineFromLocation(Point pt) {
245 return cs.DocFromDisplay(pt.y / vs.lineHeight + topLine);
246 }
247
248 void Editor::SetTopLine(int topLineNew) {
249 topLine = topLineNew;
250 posTopLine = pdoc->LineStart(topLine);
251 }
252
253 int Editor::PositionFromLocation(Point pt) {
254 RefreshStyleData();
255 pt.x = pt.x - vs.fixedColumnWidth + xOffset;
256 int line = cs.DocFromDisplay(pt.y / vs.lineHeight + topLine);
257 if (pt.y < 0) { // Division rounds towards 0
258 line = cs.DocFromDisplay((pt.y - (vs.lineHeight - 1)) / vs.lineHeight + topLine);
259 }
260 if (line < 0)
261 return 0;
262 if (line >= pdoc->LinesTotal())
263 return pdoc->Length();
264 Surface surface;
265 surface.Init();
266 surface.SetUnicodeMode(SC_CP_UTF8 == pdoc->dbcsCodePage);
267 unsigned int posLineStart = pdoc->LineStart(line);
268
269 LineLayout ll;
270 LayoutLine(line, &surface, vs, ll);
271 for (int i = 0; i < ll.numCharsInLine; i++) {
272 if (pt.x < ((ll.positions[i] + ll.positions[i + 1]) / 2) ||
273 ll.chars[i] == '\r' || ll.chars[i] == '\n') {
274 return i + posLineStart;
275 }
276 }
277
278 return ll.numCharsInLine + posLineStart;
279 }
280
281 // Like PositionFromLocation but INVALID_POSITION returned when not near any text.
282 int Editor::PositionFromLocationClose(Point pt) {
283 RefreshStyleData();
284 PRectangle rcClient = GetTextRectangle();
285 if (!rcClient.Contains(pt))
286 return INVALID_POSITION;
287 if (pt.x < vs.fixedColumnWidth)
288 return INVALID_POSITION;
289 if (pt.y < 0)
290 return INVALID_POSITION;
291 pt.x = pt.x - vs.fixedColumnWidth + xOffset;
292 int line = cs.DocFromDisplay(pt.y / vs.lineHeight + topLine);
293 if (pt.y < 0) { // Division rounds towards 0
294 line = cs.DocFromDisplay((pt.y - (vs.lineHeight - 1)) / vs.lineHeight + topLine);
295 }
296 if (line < 0)
297 return INVALID_POSITION;
298 if (line >= pdoc->LinesTotal())
299 return INVALID_POSITION;
300 Surface surface;
301 surface.Init();
302 surface.SetUnicodeMode(SC_CP_UTF8 == pdoc->dbcsCodePage);
303 unsigned int posLineStart = pdoc->LineStart(line);
304
305 LineLayout ll;
306 LayoutLine(line, &surface, vs, ll);
307 for (int i = 0; i < ll.numCharsInLine; i++) {
308 if (pt.x < ((ll.positions[i] + ll.positions[i + 1]) / 2) ||
309 ll.chars[i] == '\r' || ll.chars[i] == '\n') {
310 return i + posLineStart;
311 }
312 }
313
314 return INVALID_POSITION;
315 }
316
317 int Editor::PositionFromLineX(int line, int x) {
318 RefreshStyleData();
319 if (line >= pdoc->LinesTotal())
320 return pdoc->Length();
321 //Platform::DebugPrintf("Position of (%d,%d) line = %d top=%d\n", pt.x, pt.y, line, topLine);
322 Surface surface;
323 surface.Init();
324 surface.SetUnicodeMode(SC_CP_UTF8 == pdoc->dbcsCodePage);
325 unsigned int posLineStart = pdoc->LineStart(line);
326
327 LineLayout ll;
328 LayoutLine(line, &surface, vs, ll);
329 for (int i = 0; i < ll.numCharsInLine; i++) {
330 if (x < ((ll.positions[i] + ll.positions[i + 1]) / 2) ||
331 ll.chars[i] == '\r' || ll.chars[i] == '\n') {
332 return i + posLineStart;
333 }
334 }
335
336 return ll.numCharsInLine + posLineStart;
337 }
338
339 void Editor::RedrawRect(PRectangle rc) {
340 //Platform::DebugPrintf("Redraw %0d,%0d - %0d,%0d\n", rc.left, rc.top, rc.right, rc.bottom);
341
342 // Clip the redraw rectangle into the client area
343 PRectangle rcClient = GetClientRectangle();
344 if (rc.top < rcClient.top)
345 rc.top = rcClient.top;
346 if (rc.bottom > rcClient.bottom)
347 rc.bottom = rcClient.bottom;
348 if (rc.left < rcClient.left)
349 rc.left = rcClient.left;
350 if (rc.right > rcClient.right)
351 rc.right = rcClient.right;
352
353 if ((rc.bottom > rc.top) && (rc.right > rc.left)) {
354 wMain.InvalidateRectangle(rc);
355 }
356 }
357
358 void Editor::Redraw() {
359 //Platform::DebugPrintf("Redraw all\n");
360 wMain.InvalidateAll();
361 }
362
363 void Editor::RedrawSelMargin() {
364 if (vs.maskInLine) {
365 Redraw();
366 } else {
367 PRectangle rcSelMargin = GetClientRectangle();
368 rcSelMargin.right = vs.fixedColumnWidth;
369 wMain.InvalidateRectangle(rcSelMargin);
370 }
371 }
372
373 PRectangle Editor::RectangleFromRange(int start, int end) {
374 int minPos = start;
375 if (minPos > end)
376 minPos = end;
377 int maxPos = start;
378 if (maxPos < end)
379 maxPos = end;
380 int minLine = cs.DisplayFromDoc(pdoc->LineFromPosition(minPos));
381 int maxLine = cs.DisplayFromDoc(pdoc->LineFromPosition(maxPos));
382 PRectangle rcClient = GetTextRectangle();
383 PRectangle rc;
384 rc.left = vs.fixedColumnWidth;
385 rc.top = (minLine - topLine) * vs.lineHeight;
386 if (rc.top < 0)
387 rc.top = 0;
388 rc.right = rcClient.right;
389 rc.bottom = (maxLine - topLine + 1) * vs.lineHeight;
390 // Ensure PRectangle is within 16 bit space
391 rc.top = Platform::Clamp(rc.top, -32000, 32000);
392 rc.bottom = Platform::Clamp(rc.bottom, -32000, 32000);
393
394 return rc;
395 }
396
397 void Editor::InvalidateRange(int start, int end) {
398 RedrawRect(RectangleFromRange(start, end));
399 }
400
401 int Editor::CurrentPosition() {
402 return currentPos;
403 }
404
405 bool Editor::SelectionEmpty() {
406 return anchor == currentPos;
407 }
408
409 int Editor::SelectionStart(int line) {
410 if ((line == -1) || (selType == selStream)) {
411 return Platform::Minimum(currentPos, anchor);
412 } else { // selType == selRectangle
413 int selStart = SelectionStart();
414 int selEnd = SelectionEnd();
415 int lineStart = pdoc->LineFromPosition(selStart);
416 int lineEnd = pdoc->LineFromPosition(selEnd);
417 if (line < lineStart || line > lineEnd) {
418 return -1;
419 } else {
420 int minX = Platform::Minimum(xStartSelect, xEndSelect);
421 return PositionFromLineX(line, minX);
422 }
423 }
424 }
425
426 int Editor::SelectionEnd(int line) {
427 if ((line == -1) || (selType == selStream)) {
428 return Platform::Maximum(currentPos, anchor);
429 } else { // selType == selRectangle
430 int selStart = SelectionStart();
431 int selEnd = SelectionEnd();
432 int lineStart = pdoc->LineFromPosition(selStart);
433 int lineEnd = pdoc->LineFromPosition(selEnd);
434 if (line < lineStart || line > lineEnd) {
435 return -1;
436 } else {
437 int maxX = Platform::Maximum(xStartSelect, xEndSelect);
438 // measure line and return character closest to minx
439 return PositionFromLineX(line, maxX);
440 }
441 }
442 }
443
444 void Editor::SetSelection(int currentPos_, int anchor_) {
445 currentPos_ = pdoc->ClampPositionIntoDocument(currentPos_);
446 anchor_ = pdoc->ClampPositionIntoDocument(anchor_);
447 if ((currentPos != currentPos_) || (anchor != anchor_)) {
448 int firstAffected = anchor;
449 if (firstAffected > currentPos)
450 firstAffected = currentPos;
451 if (firstAffected > anchor_)
452 firstAffected = anchor_;
453 if (firstAffected > currentPos_)
454 firstAffected = currentPos_;
455 int lastAffected = anchor;
456 if (lastAffected < currentPos)
457 lastAffected = currentPos;
458 if (lastAffected < anchor_)
459 lastAffected = anchor_;
460 if (lastAffected < (currentPos_ + 1)) // +1 ensures caret repainted
461 lastAffected = (currentPos_ + 1);
462 currentPos = currentPos_;
463 anchor = anchor_;
464 needUpdateUI = true;
465 InvalidateRange(firstAffected, lastAffected);
466 }
467 ClaimSelection();
468 }
469
470 void Editor::SetSelection(int currentPos_) {
471 currentPos_ = pdoc->ClampPositionIntoDocument(currentPos_);
472 if (currentPos != currentPos_) {
473 int firstAffected = anchor;
474 if (firstAffected > currentPos)
475 firstAffected = currentPos;
476 if (firstAffected > currentPos_)
477 firstAffected = currentPos_;
478 int lastAffected = anchor;
479 if (lastAffected < currentPos)
480 lastAffected = currentPos;
481 if (lastAffected < (currentPos_ + 1)) // +1 ensures caret repainted
482 lastAffected = (currentPos_ + 1);
483 currentPos = currentPos_;
484 needUpdateUI = true;
485 InvalidateRange(firstAffected, lastAffected);
486 }
487 ClaimSelection();
488 }
489
490 void Editor::SetEmptySelection(int currentPos_) {
491 selType = selStream;
492 SetSelection(currentPos_, currentPos_);
493 }
494
495 int Editor::MovePositionOutsideChar(int pos, int moveDir, bool checkLineEnd) {
496 // Asks document to find a good position and then moves out of any invisible positions
497 pos = pdoc->MovePositionOutsideChar(pos, moveDir, checkLineEnd);
498 int mask = pdoc->stylingBitsMask;
499 if (moveDir > 0) {
500 while ((pos < pdoc->Length()) &&
501 (!vs.styles[pdoc->StyleAt(pos - 1) & mask].visible))
502 pos++;
503 } else {
504 while ((pos > 0) &&
505 (!vs.styles[pdoc->StyleAt(pos - 1) & mask].visible))
506 pos--;
507 }
508 return pos;
509 }
510
511 int Editor::MovePositionTo(int newPos, bool extend) {
512 int delta = newPos - currentPos;
513 newPos = pdoc->ClampPositionIntoDocument(newPos);
514 newPos = MovePositionOutsideChar(newPos, delta);
515 if (extend) {
516 SetSelection(newPos);
517 } else {
518 SetEmptySelection(newPos);
519 }
520 EnsureCaretVisible();
521 ShowCaretAtCurrentPosition();
522 NotifyMove(newPos);
523 return 0;
524 }
525
526 int Editor::MovePositionSoVisible(int pos, int moveDir) {
527 pos = pdoc->ClampPositionIntoDocument(pos);
528 pos = MovePositionOutsideChar(pos, moveDir);
529 int lineDoc = pdoc->LineFromPosition(pos);
530 if (cs.GetVisible(lineDoc)) {
531 return pos;
532 } else {
533 int lineDisplay = cs.DisplayFromDoc(lineDoc);
534 if (moveDir > 0) {
535 lineDisplay = Platform::Clamp(lineDisplay + 1, 0, cs.LinesDisplayed());
536 return pdoc->LineStart(cs.DocFromDisplay(lineDisplay));
537 } else {
538 // lineDisplay is already line before fold as lines in fold use display line of line before fold
539 lineDisplay = Platform::Clamp(lineDisplay, 0, cs.LinesDisplayed());
540 return pdoc->LineEndPosition(pdoc->LineStart(cs.DocFromDisplay(lineDisplay)));
541 }
542 }
543 }
544
545 // Choose the x position that the caret will try to stick to as it is moves up and down
546 void Editor::SetLastXChosen() {
547 Point pt = LocationFromPosition(currentPos);
548 lastXChosen = pt.x;
549 }
550
551 void Editor::ScrollTo(int line) {
552 int topLineNew = Platform::Clamp(line, 0, MaxScrollPos());
553 if (topLineNew != topLine) {
554 // Try to optimise small scrolls
555 int linesToMove = topLine - topLineNew;
556 SetTopLine(topLineNew);
557 ShowCaretAtCurrentPosition();
558 // Perform redraw rather than scroll if many lines would be redrawn anyway.
559 if (abs(linesToMove) <= 10) {
560 ScrollText(linesToMove);
561 } else {
562 Redraw();
563 }
564 SetVerticalScrollPos();
565 }
566 }
567
568 void Editor::ScrollText(int /* linesToMove */) {
569 //Platform::DebugPrintf("Editor::ScrollText %d\n", linesToMove);
570 Redraw();
571 }
572
573 void Editor::HorizontalScrollTo(int xPos) {
574 //Platform::DebugPrintf("HorizontalScroll %d\n", xPos);
575 xOffset = xPos;
576 if (xOffset < 0)
577 xOffset = 0;
578 SetHorizontalScrollPos();
579 RedrawRect(GetClientRectangle());
580 }
581
582 void Editor::MoveCaretInsideView() {
583 PRectangle rcClient = GetTextRectangle();
584 Point pt = LocationFromPosition(currentPos);
585 if (pt.y < rcClient.top) {
586 MovePositionTo(PositionFromLocation(
587 Point(lastXChosen, rcClient.top)));
588 } else if ((pt.y + vs.lineHeight - 1) > rcClient.bottom) {
589 int yOfLastLineFullyDisplayed = rcClient.top + (LinesOnScreen() - 1) * vs.lineHeight;
590 MovePositionTo(PositionFromLocation(
591 Point(lastXChosen, rcClient.top + yOfLastLineFullyDisplayed)));
592 }
593 }
594
595 void Editor::EnsureCaretVisible(bool useMargin, bool vert, bool horiz) {
596 //Platform::DebugPrintf("EnsureCaretVisible %d %s\n", xOffset, useMargin ? " margin" : " ");
597 PRectangle rcClient = GetTextRectangle();
598 //int rcClientFullWidth = rcClient.Width();
599 int posCaret = currentPos;
600 if (posDrag >= 0)
601 posCaret = posDrag;
602 Point pt = LocationFromPosition(posCaret);
603 Point ptEOL = LocationFromPosition(pdoc->LineEndPosition(posCaret));
604 Point ptBottomCaret = pt;
605 int lineCaret = cs.DisplayFromDoc(pdoc->LineFromPosition(posCaret));
606 ptBottomCaret.y += vs.lineHeight - 1;
607
608 // Ensure the caret is reasonably visible in context:
609 // xMargin must equal to xCaretMargin, with a minimum of 2 and a maximum of
610 // slightly less than half the width of the text area.
611 int xMargin = Platform::Clamp(xCaretMargin, 2, Platform::Maximum(rcClient.Width() - 10, 4) / 2);
612 if (!useMargin)
613 xMargin = 2;
614
615 // If we scroll the display, we use a minimum amount of xMargin.
616 int offsetLeft = rcClient.left + xMargin;
617 int offsetRight = rcClient.right - xMargin;
618 // If we are in XJUMPS mode, then when the margin is reached, the
619 // offset jumps so that it won't need to move agin for a while.
620 if (!(caretPolicy & CARET_XJUMPS)) {
621 rcClient.left = offsetLeft;
622 rcClient.right = offsetRight;
623 }
624
625 // Vertical positioning
626 if (vert && (!rcClient.Contains(pt) || !rcClient.Contains(ptBottomCaret) || (caretPolicy & CARET_STRICT))) {
627 //Platform::DebugPrintf("EnsureCaretVisible move, (%d,%d)(%d,%d)\n", pt.x, pt.y, rcClient.left, rcClient.right);
628 // It should be possible to scroll the window to show the caret,
629 // but this fails to remove the caret on GTK+
630 if (caretPolicy & CARET_SLOP) {
631 if ((topLine > lineCaret) || ((caretPolicy & CARET_STRICT) && (topLine + caretSlop > lineCaret))) {
632 SetTopLine(Platform::Clamp(lineCaret - caretSlop, 0, MaxScrollPos()));
633 SetVerticalScrollPos();
634 Redraw();
635 } else if ((lineCaret > topLine + LinesOnScreen() - 1) ||
636 ((caretPolicy & CARET_STRICT) && (lineCaret > topLine + LinesOnScreen() - 1 - caretSlop))) {
637 SetTopLine(Platform::Clamp(lineCaret - LinesOnScreen() + 1 + caretSlop, 0, MaxScrollPos()));
638 SetVerticalScrollPos();
639 Redraw();
640 }
641 } else {
642 if ((topLine > lineCaret) || (lineCaret > topLine + LinesOnScreen() - 1) || (caretPolicy & CARET_STRICT)) {
643 SetTopLine(Platform::Clamp(lineCaret - LinesOnScreen() / 2 + 1, 0, MaxScrollPos()));
644 SetVerticalScrollPos();
645 Redraw();
646 }
647 }
648 }
649
650 // Horizontal positioning
651 if (horiz) {
652 int xOffsetNew = xOffset;
653 if (pt.x < rcClient.left) {
654 xOffsetNew = xOffset - (offsetLeft - pt.x);
655 } else if ((!(caretPolicy & CARET_XEVEN) && ((xOffset > 0) && useMargin)) || pt.x >= rcClient.right) {
656 xOffsetNew = xOffset + (pt.x - offsetRight);
657 int xOffsetEOL = xOffset + (ptEOL.x - offsetRight) - xMargin + 2;
658 //Platform::DebugPrintf("Margin %d %d\n", xOffsetNew, xOffsetEOL);
659 // Ensure don't scroll out into empty space
660 if (xOffsetNew > xOffsetEOL)
661 xOffsetNew = xOffsetEOL;
662 }
663 if (xOffsetNew < 0)
664 xOffsetNew = 0;
665 if (xOffset != xOffsetNew) {
666 xOffset = xOffsetNew;
667 SetHorizontalScrollPos();
668 Redraw();
669 }
670 }
671 }
672
673 void Editor::ShowCaretAtCurrentPosition() {
674 if (!hasFocus) {
675 caret.active = false;
676 caret.on = false;
677 return;
678 }
679 caret.active = true;
680 caret.on = true;
681 SetTicking(true);
682 }
683
684 void Editor::DropCaret() {
685 caret.active = false;
686 InvalidateCaret();
687 }
688
689 void Editor::InvalidateCaret() {
690 if (posDrag >= 0)
691 InvalidateRange(posDrag, posDrag + 1);
692 else
693 InvalidateRange(currentPos, currentPos + 1);
694 }
695
696 int Editor::SubstituteMarkerIfEmpty(int markerCheck, int markerDefault) {
697 if (vs.markers[markerCheck].markType == SC_MARK_EMPTY)
698 return markerDefault;
699 return markerCheck;
700 }
701
702 void Editor::PaintSelMargin(Surface *surfWindow, PRectangle &rc) {
703 if (vs.fixedColumnWidth == 0)
704 return;
705
706 PRectangle rcMargin = GetClientRectangle();
707 rcMargin.right = vs.fixedColumnWidth;
708
709 if (!rc.Intersects(rcMargin))
710 return;
711
712 Surface *surface;
713 if (bufferedDraw) {
714 surface = &pixmapSelMargin;
715 } else {
716 surface = surfWindow;
717 }
718
719 PRectangle rcSelMargin = rcMargin;
720 rcSelMargin.right = rcMargin.left;
721
722 for (int margin = 0; margin < vs.margins; margin++) {
723 if (vs.ms[margin].width > 0) {
724
725 rcSelMargin.left = rcSelMargin.right;
726 rcSelMargin.right = rcSelMargin.left + vs.ms[margin].width;
727
728 if (vs.ms[margin].symbol) {
729 /* alternate scheme:
730 if (vs.ms[margin].mask & SC_MASK_FOLDERS)
731 surface->FillRectangle(rcSelMargin, vs.styles[STYLE_DEFAULT].back.allocated);
732 else
733 // Required because of special way brush is created for selection margin
734 surface->FillRectangle(rcSelMargin, pixmapSelPattern);
735 */
736 if (vs.ms[margin].mask & SC_MASK_FOLDERS)
737 // Required because of special way brush is created for selection margin
738 surface->FillRectangle(rcSelMargin, pixmapSelPattern);
739 else
740 surface->FillRectangle(rcSelMargin, vs.styles[STYLE_LINENUMBER].back.allocated);
741 } else {
742 surface->FillRectangle(rcSelMargin, vs.styles[STYLE_LINENUMBER].back.allocated);
743 }
744
745 int visibleLine = topLine;
746 int line = cs.DocFromDisplay(visibleLine);
747 int yposScreen = 0;
748
749 // Work out whether the top line is whitespace located after a
750 // lessening of fold level which implies a 'fold tail' but which should not
751 // be displayed until the last of a sequence of whitespace.
752 bool needWhiteClosure = false;
753 int level = pdoc->GetLevel(line);
754 if (level & SC_FOLDLEVELWHITEFLAG) {
755 int lineBack = line;
756 int levelPrev = level;
757 while ((lineBack > 0) && (levelPrev & SC_FOLDLEVELWHITEFLAG)) {
758 lineBack--;
759 levelPrev = pdoc->GetLevel(lineBack);
760 }
761 if (!(levelPrev & SC_FOLDLEVELHEADERFLAG)) {
762 if ((level & SC_FOLDLEVELNUMBERMASK) < (levelPrev & SC_FOLDLEVELNUMBERMASK))
763 needWhiteClosure = true;
764 }
765 }
766
767 // Old code does not know about new markers needed to distinguish all cases
768 int folderOpenMid = SubstituteMarkerIfEmpty(SC_MARKNUM_FOLDEROPENMID,
769 SC_MARKNUM_FOLDEROPEN);
770 int folderEnd = SubstituteMarkerIfEmpty(SC_MARKNUM_FOLDEREND,
771 SC_MARKNUM_FOLDER);
772
773 while ((visibleLine < cs.LinesDisplayed()) && yposScreen < rcMargin.bottom) {
774
775 // Decide which fold indicator should be displayed
776 level = pdoc->GetLevel(line);
777 int levelNext = pdoc->GetLevel(line+1);
778 int marks = pdoc->GetMark(line);
779 int levelNum = level & SC_FOLDLEVELNUMBERMASK;
780 int levelNextNum = levelNext & SC_FOLDLEVELNUMBERMASK;
781 if (level & SC_FOLDLEVELHEADERFLAG) {
782 if (cs.GetExpanded(line)) {
783 if (levelNum == SC_FOLDLEVELBASE)
784 marks |= 1 << SC_MARKNUM_FOLDEROPEN;
785 else
786 marks |= 1 << folderOpenMid;
787 } else {
788 if (levelNum == SC_FOLDLEVELBASE)
789 marks |= 1 << SC_MARKNUM_FOLDER;
790 else
791 marks |= 1 << folderEnd;
792 }
793 needWhiteClosure = false;
794 } else if (level & SC_FOLDLEVELWHITEFLAG) {
795 if (needWhiteClosure) {
796 if (levelNext & SC_FOLDLEVELWHITEFLAG) {
797 marks |= 1 << SC_MARKNUM_FOLDERSUB;
798 } else if (levelNum > SC_FOLDLEVELBASE) {
799 marks |= 1 << SC_MARKNUM_FOLDERMIDTAIL;
800 needWhiteClosure = false;
801 } else {
802 marks |= 1 << SC_MARKNUM_FOLDERTAIL;
803 needWhiteClosure = false;
804 }
805 } else if (levelNum > SC_FOLDLEVELBASE) {
806 if (levelNextNum < levelNum) {
807 if (levelNextNum > SC_FOLDLEVELBASE) {
808 marks |= 1 << SC_MARKNUM_FOLDERMIDTAIL;
809 } else {
810 marks |= 1 << SC_MARKNUM_FOLDERTAIL;
811 }
812 } else {
813 marks |= 1 << SC_MARKNUM_FOLDERSUB;
814 }
815 }
816 } else if (levelNum > SC_FOLDLEVELBASE) {
817 if (levelNextNum < levelNum) {
818 needWhiteClosure = false;
819 if (levelNext & SC_FOLDLEVELWHITEFLAG) {
820 marks |= 1 << SC_MARKNUM_FOLDERSUB;
821 needWhiteClosure = true;
822 } else if (levelNextNum > SC_FOLDLEVELBASE) {
823 marks |= 1 << SC_MARKNUM_FOLDERMIDTAIL;
824 } else {
825 marks |= 1 << SC_MARKNUM_FOLDERTAIL;
826 }
827 } else {
828 marks |= 1 << SC_MARKNUM_FOLDERSUB;
829 }
830 }
831
832 marks &= vs.ms[margin].mask;
833 PRectangle rcMarker = rcSelMargin;
834 rcMarker.top = yposScreen;
835 rcMarker.bottom = yposScreen + vs.lineHeight;
836 if (!vs.ms[margin].symbol) {
837 char number[100];
838 number[0] = '\0';
839 sprintf(number, "%d", line + 1);
840 if (foldFlags & 64)
841 sprintf(number, "%X", pdoc->GetLevel(line));
842 PRectangle rcNumber = rcMarker;
843 // Right justify
844 int width = surface->WidthText(vs.styles[STYLE_LINENUMBER].font, number, strlen(number));
845 int xpos = rcNumber.right - width - 3;
846 rcNumber.left = xpos;
847 if ((visibleLine < cs.LinesDisplayed()) && cs.GetVisible(line)) {
848 surface->DrawText(rcNumber, vs.styles[STYLE_LINENUMBER].font,
849 rcNumber.top + vs.maxAscent, number, strlen(number),
850 vs.styles[STYLE_LINENUMBER].fore.allocated,
851 vs.styles[STYLE_LINENUMBER].back.allocated);
852 }
853 }
854
855 if (marks) {
856 for (int markBit = 0; (markBit < 32) && marks; markBit++) {
857 if (marks & 1) {
858 vs.markers[markBit].Draw(surface, rcMarker, vs.styles[STYLE_LINENUMBER].font);
859 }
860 marks >>= 1;
861 }
862 }
863
864 visibleLine++;
865 line = cs.DocFromDisplay(visibleLine);
866 yposScreen += vs.lineHeight;
867 }
868 }
869 }
870
871 PRectangle rcBlankMargin = rcMargin;
872 rcBlankMargin.left = rcSelMargin.right;
873 surface->FillRectangle(rcBlankMargin, vs.styles[STYLE_DEFAULT].back.allocated);
874
875 if (bufferedDraw) {
876 surfWindow->Copy(rcMargin, Point(), pixmapSelMargin);
877 }
878 }
879
880 void DrawTabArrow(Surface *surface, PRectangle rcTab, int ymid) {
881 int ydiff = (rcTab.bottom - rcTab.top) / 2;
882 int xhead = rcTab.right - 1 - ydiff;
883 if ((rcTab.left + 2) < (rcTab.right - 1))
884 surface->MoveTo(rcTab.left + 2, ymid);
885 else
886 surface->MoveTo(rcTab.right - 1, ymid);
887 surface->LineTo(rcTab.right - 1, ymid);
888 surface->LineTo(xhead, ymid - ydiff);
889 surface->MoveTo(rcTab.right - 1, ymid);
890 surface->LineTo(xhead, ymid + ydiff);
891 }
892
893 /**
894 * Fill in the LineLayout data for the given line.
895 * Copy the given @a line and its styles from the document into local arrays.
896 * Also determine the x position at which each character starts.
897 */
898 void Editor::LayoutLine(int line, Surface *surface, ViewStyle &vstyle, LineLayout &ll) {
899 int numCharsInLine = 0;
900 int posLineStart = pdoc->LineStart(line);
901 int posLineEnd = pdoc->LineStart(line + 1);
902 Font &ctrlCharsFont = vstyle.styles[STYLE_CONTROLCHAR].font;
903 char styleByte = 0;
904 int styleMask = pdoc->stylingBitsMask;
905 ll.xHighlightGuide = 0;
906 // If the line is very long, limit the treatment to a length that should fit in the viewport
907 if (posLineEnd > (posLineStart + LineLayout::maxLineLength)) {
908 posLineEnd = posLineStart + LineLayout::maxLineLength;
909 }
910 // Fill base line layout
911 for (int charInDoc = posLineStart; charInDoc < posLineEnd; charInDoc++) {
912 char chDoc = pdoc->CharAt(charInDoc);
913 styleByte = pdoc->StyleAt(charInDoc);
914 if (vstyle.viewEOL || ((chDoc != '\r') && (chDoc != '\n'))) {
915 ll.chars[numCharsInLine] = chDoc;
916 ll.styles[numCharsInLine] = static_cast<char>(styleByte & styleMask);
917 ll.indicators[numCharsInLine] = static_cast<char>(styleByte & ~styleMask);
918 if (vstyle.styles[ll.styles[numCharsInLine]].caseForce == Style::caseUpper)
919 ll.chars[numCharsInLine] = static_cast<char>(toupper(chDoc));
920 else if (vstyle.styles[ll.styles[numCharsInLine]].caseForce == Style::caseLower)
921 ll.chars[numCharsInLine] = static_cast<char>(tolower(chDoc));
922 numCharsInLine++;
923 }
924 }
925 // Extra element at the end of the line to hold end x position and act as
926 ll.chars[numCharsInLine] = 0; // Also triggers processing in the loops as this is a control character
927 ll.styles[numCharsInLine] = styleByte; // For eolFilled
928 ll.indicators[numCharsInLine] = 0;
929
930 // Layout the line, determining the position of each character,
931 // with an extra element at the end for the end of the line.
932 int startseg = 0; // Start of the current segment, in char. number
933 int startsegx = 0; // Start of the current segment, in pixels
934 ll.positions[0] = 0;
935 unsigned int tabWidth = vstyle.spaceWidth * pdoc->tabInChars;
936 bool lastSegItalics = false;
937
938 for (int charInLine = 0; charInLine < numCharsInLine; charInLine++) {
939 if ((ll.styles[charInLine] != ll.styles[charInLine + 1]) ||
940 IsControlCharacter(ll.chars[charInLine]) || IsControlCharacter(ll.chars[charInLine + 1])) {
941 ll.positions[startseg] = 0;
942 if (vstyle.styles[ll.styles[charInLine]].visible) {
943 if (IsControlCharacter(ll.chars[charInLine])) {
944 if (ll.chars[charInLine] == '\t') {
945 ll.positions[charInLine + 1] = ((((startsegx + 2) /
946 tabWidth) + 1) * tabWidth) - startsegx;
947 } else {
948 const char *ctrlChar = ControlCharacterString(ll.chars[charInLine]);
949 // +3 For a blank on front and rounded edge each side:
950 ll.positions[charInLine + 1] = surface->WidthText(ctrlCharsFont, ctrlChar, strlen(ctrlChar)) + 3;
951 }
952 lastSegItalics = false;
953 } else { // Regular character
954 lastSegItalics = vstyle.styles[ll.styles[charInLine]].italic;
955 int lenSeg = charInLine - startseg + 1;
956 if ((lenSeg == 1) && (' ' == ll.chars[startseg])) {
957 // Over half the segments are single characters and of these about half are space characters.
958 ll.positions[charInLine + 1] = vstyle.styles[ll.styles[charInLine]].spaceWidth;
959 } else {
960 surface->MeasureWidths(vstyle.styles[ll.styles[charInLine]].font, ll.chars + startseg,
961 lenSeg, ll.positions + startseg + 1);
962 }
963 }
964 } else { // invisible
965 for (int posToZero = startseg; posToZero <= (charInLine + 1); posToZero++) {
966 ll.positions[posToZero] = 0;
967 }
968 }
969 for (int posToIncrease = startseg; posToIncrease <= (charInLine + 1); posToIncrease++) {
970 ll.positions[posToIncrease] += startsegx;
971 }
972 startsegx = ll.positions[charInLine + 1];
973 startseg = charInLine + 1;
974 }
975 }
976 // Small hack to make lines that end with italics not cut off the edge of the last character
977 if ((startseg > 0) && lastSegItalics) {
978 ll.positions[startseg] += 2;
979 }
980 ll.numCharsInLine = numCharsInLine;
981 }
982
983 void Editor::DrawLine(Surface *surface, ViewStyle &vsDraw, int line, int lineVisible, int xStart,
984 PRectangle rcLine, LineLayout &ll) {
985
986 PRectangle rcSegment = rcLine;
987
988 // Using one font for all control characters so it can be controlled independently to ensure
989 // the box goes around the characters tightly. Seems to be no way to work out what height
990 // is taken by an individual character - internal leading gives varying results.
991 Font &ctrlCharsFont = vsDraw.styles[STYLE_CONTROLCHAR].font;
992
993 bool overrideBackground = false;
994 Colour background = Colour(0, 0, 0);
995 if (caret.active && vsDraw.showCaretLineBackground && ll.containsCaret) {
996 overrideBackground = true;
997 background = vsDraw.caretLineBackground.allocated;
998 }
999 if (vsDraw.maskInLine) {
1000 int marks = pdoc->GetMark(line) & vsDraw.maskInLine;
1001 if (marks) {
1002 overrideBackground = true;
1003 for (int markBit = 0; (markBit < 32) && marks; markBit++) {
1004 if (marks & 1) {
1005 background = vsDraw.markers[markBit].back.allocated;
1006 }
1007 marks >>= 1;
1008 }
1009 }
1010 }
1011
1012 bool inIndentation = true;
1013 int indentWidth = pdoc->indentInChars * vsDraw.spaceWidth;
1014 if (indentWidth == 0)
1015 indentWidth = pdoc->tabInChars * vsDraw.spaceWidth;
1016
1017 int posLineStart = pdoc->LineStart(line);
1018 int posLineEnd = pdoc->LineStart(line + 1);
1019
1020 int styleMask = pdoc->stylingBitsMask;
1021 int startseg = 0;
1022 for (int i = 0; i < ll.numCharsInLine; i++) {
1023
1024 int iDoc = i + posLineStart;
1025 // If there is the end of a style run for any reason
1026 if ((ll.styles[i] != ll.styles[i + 1]) ||
1027 IsControlCharacter(ll.chars[i]) || IsControlCharacter(ll.chars[i + 1]) ||
1028 ((ll.selStart != ll.selEnd) && ((iDoc + 1 == ll.selStart) || (iDoc + 1 == ll.selEnd))) ||
1029 (i == (ll.edgeColumn - 1))) {
1030 int styleMain = ll.styles[i];
1031 Colour textBack = vsDraw.styles[styleMain].back.allocated;
1032 Colour textFore = vsDraw.styles[styleMain].fore.allocated;
1033 Font &textFont = vsDraw.styles[styleMain].font;
1034 bool inSelection = (iDoc >= ll.selStart) && (iDoc < ll.selEnd) && (ll.selStart != ll.selEnd);
1035 if (inSelection) {
1036 if (vsDraw.selbackset) {
1037 if (primarySelection)
1038 textBack = vsDraw.selbackground.allocated;
1039 else
1040 textBack = vsDraw.selbackground2.allocated;
1041 }
1042 if (vsDraw.selforeset)
1043 textFore = vsDraw.selforeground.allocated;
1044 } else {
1045 if (overrideBackground)
1046 textBack = background;
1047 if ((vsDraw.edgeState == EDGE_BACKGROUND) && (i >= ll.edgeColumn) && (ll.chars[i] != '\n') && (ll.chars[i] != '\r'))
1048 textBack = vsDraw.edgecolour.allocated;
1049 }
1050 // Manage tab display
1051 if (ll.chars[i] == '\t') {
1052 rcSegment.left = ll.positions[i] + xStart;
1053 rcSegment.right = ll.positions[i + 1] + xStart;
1054 surface->FillRectangle(rcSegment, textBack);
1055 if ((vsDraw.viewWhitespace != wsInvisible) || ((inIndentation && vsDraw.viewIndentationGuides))) {
1056 surface->PenColour(textFore);
1057 }
1058 if (inIndentation && vsDraw.viewIndentationGuides) {
1059 for (int xIG = ll.positions[i] / indentWidth * indentWidth; xIG < ll.positions[i + 1]; xIG += indentWidth) {
1060 if (xIG >= ll.positions[i] && xIG > 0) {
1061 Point from(0, ((lineVisible & 1) && (vsDraw.lineHeight & 1)) ? 1 : 0);
1062 PRectangle rcCopyArea(xIG + xStart + 1, rcSegment.top, xIG + xStart + 2, rcSegment.bottom);
1063 surface->Copy(rcCopyArea, from, (ll.xHighlightGuide == xIG) ?
1064 pixmapIndentGuideHighlight : pixmapIndentGuide);
1065 }
1066 }
1067 }
1068 if (vsDraw.viewWhitespace != wsInvisible) {
1069 if (!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways) {
1070 PRectangle rcTab(rcSegment.left + 1, rcSegment.top + 4,
1071 rcSegment.right - 1, rcSegment.bottom - vsDraw.maxDescent);
1072 DrawTabArrow(surface, rcTab, rcSegment.top + vsDraw.lineHeight / 2);
1073 }
1074 }
1075 // Manage control character display
1076 }
1077 else if (IsControlCharacter(ll.chars[i])) {
1078 inIndentation = false;
1079 const char *ctrlChar = ControlCharacterString(ll.chars[i]);
1080 rcSegment.left = ll.positions[i] + xStart;
1081 rcSegment.right = ll.positions[i + 1] + xStart;
1082 surface->FillRectangle(rcSegment, textBack);
1083 int normalCharHeight = surface->Ascent(ctrlCharsFont) -
1084 surface->InternalLeading(ctrlCharsFont);
1085 PRectangle rcCChar = rcSegment;
1086 rcCChar.left = rcCChar.left + 1;
1087 rcCChar.top = rcSegment.top + vsDraw.maxAscent - normalCharHeight;
1088 rcCChar.bottom = rcSegment.top + vsDraw.maxAscent + 1;
1089 PRectangle rcCentral = rcCChar;
1090 rcCentral.top++;
1091 rcCentral.bottom--;
1092 surface->FillRectangle(rcCentral, textFore);
1093 PRectangle rcChar = rcCChar;
1094 rcChar.left++;
1095 rcChar.right--;
1096 surface->DrawTextClipped(rcChar, ctrlCharsFont,
1097 rcSegment.top + vsDraw.maxAscent, ctrlChar, strlen(ctrlChar),
1098 textBack, textFore);
1099 // Manage normal display
1100 }
1101 else {
1102 rcSegment.left = ll.positions[startseg] + xStart;
1103 rcSegment.right = ll.positions[i + 1] + xStart;
1104 // Only try to draw if really visible - enhances performance by not calling environment to
1105 // draw strings that are completely past the right side of the window.
1106 if (rcSegment.left <= rcLine.right) {
1107 surface->DrawText(rcSegment, textFont,
1108 rcSegment.top + vsDraw.maxAscent, ll.chars + startseg,
1109 i - startseg + 1, textFore, textBack);
1110 if (vsDraw.viewWhitespace != wsInvisible ||
1111 (inIndentation && vsDraw.viewIndentationGuides)) {
1112 for (int cpos = 0; cpos <= i - startseg; cpos++) {
1113 if (ll.chars[cpos + startseg] == ' ') {
1114 if (vsDraw.viewWhitespace != wsInvisible) {
1115 if (!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways) {
1116 int xmid = (ll.positions[cpos + startseg] + ll.positions[cpos + startseg + 1]) / 2;
1117 PRectangle rcDot(xmid + xStart, rcSegment.top + vsDraw.lineHeight / 2, 0, 0);
1118 rcDot.right = rcDot.left + 1;
1119 rcDot.bottom = rcDot.top + 1;
1120 surface->FillRectangle(rcDot, textFore);
1121 }
1122 }
1123 if (inIndentation && vsDraw.viewIndentationGuides) {
1124 int startSpace = ll.positions[cpos + startseg];
1125 if (startSpace > 0 && (startSpace % indentWidth == 0)) {
1126 Point from(0, ((lineVisible & 1) && (vsDraw.lineHeight & 1)) ? 1 : 0);
1127 PRectangle rcCopyArea(startSpace + xStart + 1, rcSegment.top, startSpace + xStart + 2, rcSegment.bottom);
1128 surface->Copy(rcCopyArea, from, (ll.xHighlightGuide == ll.positions[cpos + startseg]) ?
1129 pixmapIndentGuideHighlight : pixmapIndentGuide);
1130 }
1131 }
1132 } else {
1133 inIndentation = false;
1134 }
1135 }
1136 }
1137 }
1138 if (vsDraw.styles[styleMain].underline) {
1139 PRectangle rcUL = rcSegment;
1140 rcUL.top = rcUL.top + vsDraw.maxAscent + 1;
1141 rcUL.bottom = rcUL.top + 1;
1142 surface->FillRectangle(rcUL, textFore);
1143 }
1144 }
1145 startseg = i + 1;
1146 }
1147 }
1148
1149 // Draw indicators
1150 int indStart[INDIC_MAX + 1] = {0};
1151 for (int indica = 0; indica <= INDIC_MAX; indica++)
1152 indStart[indica] = 0;
1153
1154 for (int indicPos = 0; indicPos < ll.numCharsInLine; indicPos++) {
1155 if (ll.indicators[indicPos] != ll.indicators[indicPos + 1]) {
1156 int mask = 1 << pdoc->stylingBits;
1157 for (int indicnum = 0; mask < 0x100; indicnum++) {
1158 if ((ll.indicators[indicPos + 1] & mask) && !(ll.indicators[indicPos] & mask)) {
1159 indStart[indicnum] = ll.positions[indicPos + 1];
1160 }
1161 if (!(ll.indicators[indicPos + 1] & mask) && (ll.indicators[indicPos] & mask)) {
1162 PRectangle rcIndic(
1163 indStart[indicnum] + xStart,
1164 rcLine.top + vsDraw.maxAscent,
1165 ll.positions[indicPos + 1] + xStart,
1166 rcLine.top + vsDraw.maxAscent + 3);
1167 vsDraw.indicators[indicnum].Draw(surface, rcIndic);
1168 }
1169 mask = mask << 1;
1170 }
1171 }
1172 }
1173 // End of the drawing of the current line
1174
1175 // Fill in a PRectangle representing the end of line characters
1176 int xEol = ll.positions[ll.numCharsInLine];
1177 rcSegment.left = xEol + xStart;
1178 rcSegment.right = xEol + vsDraw.aveCharWidth + xStart;
1179 bool eolInSelection = (posLineEnd > ll.selStart) && (posLineEnd <= ll.selEnd) && (ll.selStart != ll.selEnd);
1180 if (eolInSelection && vsDraw.selbackset && (line < pdoc->LinesTotal() - 1)) {
1181 if (primarySelection)
1182 surface->FillRectangle(rcSegment, vsDraw.selbackground.allocated);
1183 else
1184 surface->FillRectangle(rcSegment, vsDraw.selbackground2.allocated);
1185 } else if (overrideBackground) {
1186 surface->FillRectangle(rcSegment, background);
1187 } else {
1188 surface->FillRectangle(rcSegment, vsDraw.styles[ll.styles[ll.numCharsInLine] & styleMask].back.allocated);
1189 }
1190
1191 rcSegment.left = xEol + vsDraw.aveCharWidth + xStart;
1192 rcSegment.right = rcLine.right;
1193 if (overrideBackground) {
1194 surface->FillRectangle(rcSegment, background);
1195 } else if (vsDraw.styles[ll.styles[ll.numCharsInLine] & styleMask].eolFilled) {
1196 surface->FillRectangle(rcSegment, vsDraw.styles[ll.styles[ll.numCharsInLine] & styleMask].back.allocated);
1197 } else {
1198 surface->FillRectangle(rcSegment, vsDraw.styles[STYLE_DEFAULT].back.allocated);
1199 }
1200
1201 if (vsDraw.edgeState == EDGE_LINE) {
1202 int edgeX = ll.edgeColumn * vsDraw.spaceWidth;
1203 rcSegment.left = edgeX + xStart;
1204 rcSegment.right = rcSegment.left + 1;
1205 surface->FillRectangle(rcSegment, vsDraw.edgecolour.allocated);
1206 }
1207 }
1208
1209 void Editor::Paint(Surface *surfaceWindow, PRectangle rcArea) {
1210 //Platform::DebugPrintf("Paint %d %d - %d %d\n", rcArea.left, rcArea.top, rcArea.right, rcArea.bottom);
1211 RefreshStyleData();
1212
1213 PRectangle rcClient = GetClientRectangle();
1214 //Platform::DebugPrintf("Client: (%3d,%3d) ... (%3d,%3d) %d\n",
1215 // rcClient.left, rcClient.top, rcClient.right, rcClient.bottom);
1216
1217 if (!pixmapSelPattern.Initialised()) {
1218 pixmapSelPattern.InitPixMap(8, 8, surfaceWindow);
1219 // This complex procedure is to reproduce the checker board dithered pattern used by windows
1220 // for scroll bars and Visual Studio for its selection margin. The colour of this pattern is half
1221 // way between the chrome colour and the chrome highlight colour making a nice transition
1222 // between the window chrome and the content area. And it works in low colour depths.
1223 PRectangle rcPattern(0, 0, 8, 8);
1224 if (vs.selbarlight.desired == Colour(0xff, 0xff, 0xff)) {
1225 pixmapSelPattern.FillRectangle(rcPattern, vs.selbar.allocated);
1226 pixmapSelPattern.PenColour(vs.selbarlight.allocated);
1227 for (int stripe = 0; stripe < 8; stripe++) {
1228 pixmapSelPattern.MoveTo(0, stripe * 2);
1229 pixmapSelPattern.LineTo(8, stripe * 2 - 8);
1230 }
1231 } else {
1232 // User has chosen an unusual chrome colour scheme so just use the highlight edge colour.
1233 pixmapSelPattern.FillRectangle(rcPattern, vs.selbarlight.allocated);
1234 }
1235 }
1236 if (!pixmapIndentGuide.Initialised()) {
1237 // 1 extra pixel in height so can handle odd/even positions and so produce a continuous line
1238 pixmapIndentGuide.InitPixMap(1, vs.lineHeight + 1, surfaceWindow);
1239 pixmapIndentGuideHighlight.InitPixMap(1, vs.lineHeight + 1, surfaceWindow);
1240 PRectangle rcIG(0, 0, 1, vs.lineHeight);
1241 pixmapIndentGuide.FillRectangle(rcIG, vs.styles[STYLE_INDENTGUIDE].back.allocated);
1242 pixmapIndentGuide.PenColour(vs.styles[STYLE_INDENTGUIDE].fore.allocated);
1243 pixmapIndentGuideHighlight.FillRectangle(rcIG, vs.styles[STYLE_BRACELIGHT].back.allocated);
1244 pixmapIndentGuideHighlight.PenColour(vs.styles[STYLE_BRACELIGHT].fore.allocated);
1245 for (int stripe = 1; stripe < vs.lineHeight + 1; stripe += 2) {
1246 pixmapIndentGuide.MoveTo(0, stripe);
1247 pixmapIndentGuide.LineTo(2, stripe);
1248 pixmapIndentGuideHighlight.MoveTo(0, stripe);
1249 pixmapIndentGuideHighlight.LineTo(2, stripe);
1250 }
1251 }
1252
1253 if (bufferedDraw) {
1254 if (!pixmapLine.Initialised()) {
1255 pixmapLine.InitPixMap(rcClient.Width(), rcClient.Height(),
1256 surfaceWindow);
1257 pixmapSelMargin.InitPixMap(vs.fixedColumnWidth,
1258 rcClient.Height(), surfaceWindow);
1259 }
1260 }
1261
1262 surfaceWindow->SetPalette(&palette, true);
1263 pixmapLine.SetPalette(&palette, !hasFocus);
1264
1265 //Platform::DebugPrintf("Paint: (%3d,%3d) ... (%3d,%3d)\n",
1266 // rcArea.left, rcArea.top, rcArea.right, rcArea.bottom);
1267
1268 int screenLinePaintFirst = rcArea.top / vs.lineHeight;
1269 // The area to be painted plus one extra line is styled.
1270 // The extra line is to determine when a style change, such as starting a comment flows on to other lines.
1271 int lineStyleLast = topLine + (rcArea.bottom - 1) / vs.lineHeight + 1;
1272 //Platform::DebugPrintf("Paint lines = %d .. %d\n", topLine + screenLinePaintFirst, lineStyleLast);
1273 int endPosPaint = pdoc->Length();
1274 if (lineStyleLast < cs.LinesDisplayed())
1275 endPosPaint = pdoc->LineStart(cs.DocFromDisplay(lineStyleLast + 1));
1276
1277 int xStart = vs.fixedColumnWidth - xOffset;
1278 int ypos = 0;
1279 if (!bufferedDraw)
1280 ypos += screenLinePaintFirst * vs.lineHeight;
1281 int yposScreen = screenLinePaintFirst * vs.lineHeight;
1282
1283 // Ensure we are styled as far as we are painting.
1284 pdoc->EnsureStyledTo(endPosPaint);
1285
1286 if (needUpdateUI) {
1287 NotifyUpdateUI();
1288 needUpdateUI = false;
1289 }
1290
1291 PaintSelMargin(surfaceWindow, rcArea);
1292
1293 PRectangle rcRightMargin = rcClient;
1294 rcRightMargin.left = rcRightMargin.right - vs.rightMarginWidth;
1295 if (rcArea.Intersects(rcRightMargin)) {
1296 surfaceWindow->FillRectangle(rcRightMargin, vs.styles[STYLE_DEFAULT].back.allocated);
1297 }
1298
1299 if (paintState == paintAbandoned) {
1300 // Either styling or NotifyUpdateUI noticed that painting is needed
1301 // outside the current painting rectangle
1302 //Platform::DebugPrintf("Abandoning paint\n");
1303 return;
1304 }
1305 //Platform::DebugPrintf("start display %d, offset = %d\n", pdoc->Length(), xOffset);
1306
1307 // Do the painting
1308 if (rcArea.right > vs.fixedColumnWidth) {
1309
1310 Surface *surface = surfaceWindow;
1311 if (bufferedDraw) {
1312 surface = &pixmapLine;
1313 }
1314 surface->SetUnicodeMode(SC_CP_UTF8 == pdoc->dbcsCodePage);
1315
1316 int visibleLine = topLine + screenLinePaintFirst;
1317 int line = cs.DocFromDisplay(visibleLine);
1318
1319 int posCaret = currentPos;
1320 if (posDrag >= 0)
1321 posCaret = posDrag;
1322 int lineCaret = pdoc->LineFromPosition(posCaret);
1323
1324 // Remove selection margin from drawing area so text will not be drawn
1325 // on it in unbuffered mode.
1326 PRectangle rcTextArea = rcClient;
1327 rcTextArea.left = vs.fixedColumnWidth;
1328 rcTextArea.right -= vs.rightMarginWidth;
1329 surfaceWindow->SetClip(rcTextArea);
1330
1331 // Loop on visible lines
1332 //GTimer *tim=g_timer_new();
1333 while (visibleLine < cs.LinesDisplayed() && yposScreen < rcArea.bottom) {
1334 //g_timer_start(tim);
1335 //Platform::DebugPrintf("Painting line %d\n", line);
1336
1337 // Copy this line and its styles from the document into local arrays
1338 // and determine the x position at which each character starts.
1339 LineLayout ll;
1340 LayoutLine(line, surface, vs, ll);
1341
1342 ll.selStart = SelectionStart(line);
1343 ll.selEnd = SelectionEnd(line);
1344 ll.containsCaret = line == lineCaret;
1345 if (hideSelection) {
1346 ll.selStart = -1;
1347 ll.selEnd = -1;
1348 ll.containsCaret = false;
1349 }
1350 // Need to fix this up so takes account of Unicode and DBCS
1351 ll.edgeColumn = theEdge;
1352
1353 int posLineStart = pdoc->LineStart(line);
1354 int posLineEnd = pdoc->LineStart(line + 1);
1355 //Platform::DebugPrintf("line %d %d - %d\n", line, posLineStart, posLineEnd);
1356
1357 PRectangle rcLine = rcClient;
1358 rcLine.top = ypos;
1359 rcLine.bottom = ypos + vs.lineHeight;
1360
1361 // Highlight the current braces if any
1362 if ((braces[0] >= posLineStart) && (braces[0] < posLineEnd)) {
1363 int braceOffset = braces[0] - posLineStart;
1364 if (braceOffset < ll.numCharsInLine)
1365 ll.styles[braceOffset] = static_cast<char>(bracesMatchStyle);
1366 }
1367 if ((braces[1] >= posLineStart) && (braces[1] < posLineEnd)) {
1368 int braceOffset = braces[1] - posLineStart;
1369 if (braceOffset < ll.numCharsInLine)
1370 ll.styles[braceOffset] = static_cast<char>(bracesMatchStyle);
1371 }
1372 if ((braces[0] >= posLineStart && braces[1] <= posLineEnd) ||
1373 (braces[1] >= posLineStart && braces[0] <= posLineEnd)) {
1374 ll.xHighlightGuide = highlightGuideColumn * vs.spaceWidth;
1375 }
1376
1377 // Draw the line
1378 if (cs.GetVisible(line))
1379 DrawLine(surface, vs, line, visibleLine, xStart, rcLine, ll);
1380
1381 bool expanded = cs.GetExpanded(line);
1382 if ( (expanded && (foldFlags & 2)) || (!expanded && (foldFlags & 4)) ) {
1383 if (pdoc->GetLevel(line) & SC_FOLDLEVELHEADERFLAG) {
1384 PRectangle rcFoldLine = rcLine;
1385 rcFoldLine.bottom = rcFoldLine.top + 1;
1386 surface->FillRectangle(rcFoldLine, vs.styles[STYLE_DEFAULT].fore.allocated);
1387 }
1388 }
1389 if ( (expanded && (foldFlags & 8)) || (!expanded && (foldFlags & 16)) ) {
1390 if (pdoc->GetLevel(line) & SC_FOLDLEVELHEADERFLAG) {
1391 PRectangle rcFoldLine = rcLine;
1392 rcFoldLine.top = rcFoldLine.bottom - 1;
1393 surface->FillRectangle(rcFoldLine, vs.styles[STYLE_DEFAULT].fore.allocated);
1394 }
1395 }
1396
1397 // Draw the Caret
1398 if (line == lineCaret) {
1399 int offset = Platform::Minimum(posCaret - posLineStart, LineLayout::maxLineLength);
1400 int xposCaret = ll.positions[offset] + xStart;
1401 int widthOverstrikeCaret;
1402 if (posCaret == pdoc->Length()) { // At end of document
1403 widthOverstrikeCaret = vs.aveCharWidth;
1404 } else if ((posCaret - posLineStart) >= ll.numCharsInLine) { // At end of line
1405 widthOverstrikeCaret = vs.aveCharWidth;
1406 } else {
1407 widthOverstrikeCaret = ll.positions[offset + 1] - ll.positions[offset];
1408 }
1409 if (widthOverstrikeCaret < 3) // Make sure its visible
1410 widthOverstrikeCaret = 3;
1411 if (((caret.active && caret.on) || (posDrag >= 0)) && xposCaret >= 0) {
1412 PRectangle rcCaret = rcLine;
1413 int caretWidthOffset = 0;
1414 if ((offset > 0) && (vs.caretWidth > 1))
1415 caretWidthOffset = 1; // Move back so overlaps both character cells.
1416 if (posDrag >= 0) {
1417 rcCaret.left = xposCaret - caretWidthOffset;
1418 rcCaret.right = rcCaret.left + vs.caretWidth;
1419 } else {
1420 if (inOverstrike) {
1421 rcCaret.top = rcCaret.bottom - 2;
1422 rcCaret.left = xposCaret + 1;
1423 rcCaret.right = rcCaret.left + widthOverstrikeCaret - 1;
1424 } else {
1425 rcCaret.left = xposCaret - caretWidthOffset;
1426 rcCaret.right = rcCaret.left + vs.caretWidth;
1427 }
1428 }
1429 surface->FillRectangle(rcCaret, vs.caretcolour.allocated);
1430 }
1431 }
1432
1433 if (cs.GetVisible(line)) {
1434 if (bufferedDraw) {
1435 Point from(vs.fixedColumnWidth, 0);
1436 PRectangle rcCopyArea(vs.fixedColumnWidth, yposScreen,
1437 rcClient.right, yposScreen + vs.lineHeight);
1438 surfaceWindow->Copy(rcCopyArea, from, pixmapLine);
1439 }
1440 }
1441
1442 if (!bufferedDraw) {
1443 ypos += vs.lineHeight;
1444 }
1445
1446 yposScreen += vs.lineHeight;
1447 visibleLine++;
1448 line = cs.DocFromDisplay(visibleLine);
1449 //gdk_flush();
1450 //g_timer_stop(tim);
1451 //Platform::DebugPrintf("Paint [%0d] took %g\n", line, g_timer_elapsed(tim, 0));
1452 }
1453 //g_timer_destroy(tim);
1454
1455 // Right column limit indicator
1456
1457
1458
1459 PRectangle rcBeyondEOF = rcClient;
1460 rcBeyondEOF.left = vs.fixedColumnWidth;
1461 rcBeyondEOF.right = rcBeyondEOF.right;
1462 rcBeyondEOF.top = (cs.LinesDisplayed() - topLine) * vs.lineHeight;
1463 if (rcBeyondEOF.top < rcBeyondEOF.bottom) {
1464 surfaceWindow->FillRectangle(rcBeyondEOF, vs.styles[STYLE_DEFAULT].back.allocated);
1465 if (vs.edgeState == EDGE_LINE) {
1466 int edgeX = theEdge * vs.spaceWidth;
1467 rcBeyondEOF.left = edgeX + xStart;
1468 rcBeyondEOF.right = rcBeyondEOF.left + 1;
1469 surfaceWindow->FillRectangle(rcBeyondEOF, vs.edgecolour.allocated);
1470 }
1471 }
1472 NotifyPainted();
1473 }
1474 }
1475
1476 // Space (3 space characters) between line numbers and text when printing.
1477 #define lineNumberPrintSpace " "
1478
1479 Colour InvertedLight(Colour orig) {
1480 unsigned int r = orig.GetRed();
1481 unsigned int g = orig.GetGreen();
1482 unsigned int b = orig.GetBlue();
1483 unsigned int l = (r + g + b) / 3; // There is a better calculation for this that matches human eye
1484 unsigned int il = 0xff - l;
1485 if (l == 0)
1486 return Colour(0xff, 0xff, 0xff);
1487 r = r * il / l;
1488 g = g * il / l;
1489 b = b * il / l;
1490 return Colour(Platform::Minimum(r, 0xff), Platform::Minimum(g, 0xff), Platform::Minimum(b, 0xff));
1491 }
1492
1493 // This is mostly copied from the Paint method but with some things omitted
1494 // such as the margin markers, line numbers, selection and caret
1495 // Should be merged back into a combined Draw method.
1496 long Editor::FormatRange(bool draw, RangeToFormat *pfr) {
1497 if (!pfr)
1498 return 0;
1499
1500 Surface *surface = new Surface();
1501 surface->Init(pfr->hdc);
1502 surface->SetUnicodeMode(SC_CP_UTF8 == pdoc->dbcsCodePage);
1503 Surface *surfaceMeasure = new Surface();
1504 surfaceMeasure->Init(pfr->hdcTarget);
1505 surfaceMeasure->SetUnicodeMode(SC_CP_UTF8 == pdoc->dbcsCodePage);
1506
1507 ViewStyle vsPrint(vs);
1508
1509 // Modify the view style for printing as do not normally want any of the transient features to be printed
1510 // Printing supports only the line number margin.
1511 int lineNumberIndex = -1;
1512 for (int margin = 0; margin < ViewStyle::margins; margin++) {
1513 if ((!vsPrint.ms[margin].symbol) && (vsPrint.ms[margin].width > 0)) {
1514 lineNumberIndex = margin;
1515 } else {
1516 vsPrint.ms[margin].width = 0;
1517 }
1518 }
1519 vsPrint.showMarkedLines = false;
1520 vsPrint.fixedColumnWidth = 0;
1521 vsPrint.zoomLevel = printMagnification;
1522 vsPrint.viewIndentationGuides = false;
1523 // Don't show the selection when printing
1524 vsPrint.selbackset = false;
1525 vsPrint.selforeset = false;
1526 vsPrint.showCaretLineBackground = false;
1527
1528 // Set colours for printing according to users settings
1529 for (int sty = 0;sty <= STYLE_MAX;sty++) {
1530 if (printColourMode == SC_PRINT_INVERTLIGHT) {
1531 vsPrint.styles[sty].fore.desired = InvertedLight(vsPrint.styles[sty].fore.desired);
1532 vsPrint.styles[sty].back.desired = InvertedLight(vsPrint.styles[sty].back.desired);
1533 } else if (printColourMode == SC_PRINT_BLACKONWHITE) {
1534 vsPrint.styles[sty].fore.desired = Colour(0, 0, 0);
1535 vsPrint.styles[sty].back.desired = Colour(0xff, 0xff, 0xff);
1536 } else if (printColourMode == SC_PRINT_COLOURONWHITE) {
1537 vsPrint.styles[sty].back.desired = Colour(0xff, 0xff, 0xff);
1538 } else if (printColourMode == SC_PRINT_COLOURONWHITEDEFAULTBG) {
1539 if (sty <= STYLE_DEFAULT) {
1540 vsPrint.styles[sty].back.desired = Colour(0xff, 0xff, 0xff);
1541 }
1542 }
1543 }
1544 // White background for the line numbers
1545 vsPrint.styles[STYLE_LINENUMBER].back.desired = Colour(0xff, 0xff, 0xff);
1546
1547 vsPrint.Refresh(*surfaceMeasure);
1548 // Ensure colours are set up
1549 vsPrint.RefreshColourPalette(palette, true);
1550 vsPrint.RefreshColourPalette(palette, false);
1551 // Determining width must hapen after fonts have been realised in Refresh
1552 int lineNumberWidth = 0;
1553 if (lineNumberIndex >= 0) {
1554 lineNumberWidth = surface->WidthText(vsPrint.styles[STYLE_LINENUMBER].font,
1555 "99999" lineNumberPrintSpace, 5 + strlen(lineNumberPrintSpace));
1556 vsPrint.ms[lineNumberIndex].width = lineNumberWidth;
1557 }
1558
1559 int linePrintStart = pdoc->LineFromPosition(pfr->chrg.cpMin);
1560 int linePrintLast = linePrintStart + (pfr->rc.bottom - pfr->rc.top) / vsPrint.lineHeight - 1;
1561 if (linePrintLast < linePrintStart)
1562 linePrintLast = linePrintStart;
1563 int linePrintMax = pdoc->LineFromPosition(pfr->chrg.cpMax - 1);
1564 if (linePrintLast > linePrintMax)
1565 linePrintLast = linePrintMax;
1566 //Platform::DebugPrintf("Formatting lines=[%0d,%0d,%0d] top=%0d bottom=%0d line=%0d %0d\n",
1567 // linePrintStart, linePrintLast, linePrintMax, pfr->rc.top, pfr->rc.bottom, vsPrint.lineHeight,
1568 // surfaceMeasure->Height(vsPrint.styles[STYLE_LINENUMBER].font));
1569 int endPosPrint = pdoc->Length();
1570 if (linePrintLast < pdoc->LinesTotal())
1571 endPosPrint = pdoc->LineStart(linePrintLast + 1);
1572
1573 // Ensure we are styled to where we are formatting.
1574 pdoc->EnsureStyledTo(endPosPrint);
1575
1576 int xStart = vsPrint.fixedColumnWidth + pfr->rc.left + lineNumberWidth;
1577 int ypos = pfr->rc.top;
1578 int line = linePrintStart;
1579
1580 if (draw) { // Otherwise just measuring
1581
1582 while (line <= linePrintLast && ypos < pfr->rc.bottom) {
1583
1584 // When printing, the hdc and hdcTarget may be the same, so
1585 // changing the state of surfaceMeasure may change the underlying
1586 // state of surface. Therefore, any cached state is discarded before
1587 // using each surface.
1588 surfaceMeasure->FlushCachedState();
1589
1590 // Copy this line and its styles from the document into local arrays
1591 // and determine the x position at which each character starts.
1592 LineLayout ll;
1593 LayoutLine(line, surfaceMeasure, vsPrint, ll);
1594 ll.selStart = -1;
1595 ll.selEnd = -1;
1596 ll.containsCaret = false;
1597 // Need to fix this up so takes account of Unicode and DBCS
1598 ll.edgeColumn = theEdge;
1599
1600 PRectangle rcLine;
1601 rcLine.left = pfr->rc.left + lineNumberWidth;
1602 rcLine.top = ypos;
1603 rcLine.right = pfr->rc.right;
1604 rcLine.bottom = ypos + vsPrint.lineHeight;
1605
1606 if (lineNumberWidth) {
1607 char number[100];
1608 sprintf(number, "%d" lineNumberPrintSpace, line + 1);
1609 PRectangle rcNumber = rcLine;
1610 rcNumber.right = rcNumber.left + lineNumberWidth;
1611 // Right justify
1612 rcNumber.left -=
1613 surface->WidthText(vsPrint.styles[STYLE_LINENUMBER].font, number, strlen(number));
1614 surface->DrawText(rcNumber, vsPrint.styles[STYLE_LINENUMBER].font,
1615 ypos + vsPrint.maxAscent, number, strlen(number),
1616 vsPrint.styles[STYLE_LINENUMBER].fore.allocated,
1617 vsPrint.styles[STYLE_LINENUMBER].back.allocated);
1618 }
1619
1620 // Draw the line
1621 surface->FlushCachedState();
1622 DrawLine(surface, vsPrint, line, line, xStart, rcLine, ll);
1623
1624 ypos += vsPrint.lineHeight;
1625 line++;
1626 }
1627 }
1628
1629 delete surface;
1630 delete surfaceMeasure;
1631
1632 return endPosPrint;
1633 }
1634
1635 // Empty method is overridden on GTK+ to show / hide scrollbars
1636 void Editor::ReconfigureScrollBars() {}
1637
1638 void Editor::SetScrollBarsTo(PRectangle) {
1639 RefreshStyleData();
1640
1641 int nMax = cs.LinesDisplayed();
1642 int nPage = cs.LinesDisplayed() - MaxScrollPos() + 1;
1643 bool modified = ModifyScrollBars(nMax, nPage);
1644
1645 // TODO: ensure always showing as many lines as possible
1646 // May not be, if, for example, window made larger
1647 if (topLine > MaxScrollPos()) {
1648 SetTopLine(Platform::Clamp(topLine, 0, MaxScrollPos()));
1649 SetVerticalScrollPos();
1650 Redraw();
1651 }
1652 if (modified)
1653 Redraw();
1654 //Platform::DebugPrintf("end max = %d page = %d\n", nMax, nPage);
1655 }
1656
1657 void Editor::SetScrollBars() {
1658 PRectangle rsClient = GetClientRectangle();
1659 SetScrollBarsTo(rsClient);
1660 }
1661
1662 void Editor::AddChar(char ch) {
1663 char s[2];
1664 s[0] = ch;
1665 s[1] = '\0';
1666 AddCharUTF(s, 1);
1667 }
1668
1669 void Editor::AddCharUTF(char *s, unsigned int len) {
1670 bool wasSelection = currentPos != anchor;
1671 ClearSelection();
1672 if (inOverstrike && !wasSelection) {
1673 if (currentPos < (pdoc->Length() - 1)) {
1674 if ((pdoc->CharAt(currentPos) != '\r') && (pdoc->CharAt(currentPos) != '\n')) {
1675 pdoc->DelChar(currentPos);
1676 }
1677 }
1678 }
1679 pdoc->InsertString(currentPos, s, len);
1680 SetEmptySelection(currentPos + len);
1681 EnsureCaretVisible();
1682 // Avoid blinking during rapid typing:
1683 ShowCaretAtCurrentPosition();
1684 SetLastXChosen();
1685
1686 int byte = static_cast<unsigned char>(s[0]);
1687 if ((byte < 0xC0) || (1 == len)) {
1688 // Handles UTF-8 characters between 0x01 and 0x7F and single byte
1689 // characters when not in UTF-8 mode.
1690 // Also treats \0 and naked trail bytes 0x80 to 0xBF as valid
1691 // characters representing themselves.
1692 } else {
1693 // Unroll 1 to 3 byte UTF-8 sequences. See reference data at:
1694 // http://www.cl.cam.ac.uk/~mgk25/unicode.html
1695 // http://www.cl.cam.ac.uk/~mgk25/ucs/examples/UTF-8-test.txt
1696 if (byte < 0xE0) {
1697 int byte2 = static_cast<unsigned char>(s[1]);
1698 if ((byte2 & 0xC0) == 0x80) {
1699 // Two-byte-character lead-byte followed by a trail-byte.
1700 byte = (((byte & 0x1F) << 6) | (byte2 & 0x3F));
1701 }
1702 // A two-byte-character lead-byte not followed by trail-byte
1703 // represents itself.
1704 } else if (byte < 0xF0) {
1705 int byte2 = static_cast<unsigned char>(s[1]);
1706 int byte3 = static_cast<unsigned char>(s[2]);
1707 if (((byte2 & 0xC0) == 0x80) && ((byte3 & 0xC0) == 0x80)) {
1708 // Three-byte-character lead byte followed by two trail bytes.
1709 byte = (((byte & 0x0F) << 12) | ((byte2 & 0x3F) << 6) |
1710 (byte3 & 0x3F));
1711 }
1712 // A three-byte-character lead-byte not followed by two trail-bytes
1713 // represents itself.
1714 }
1715 }
1716 NotifyChar(byte);
1717 }
1718
1719 void Editor::ClearSelection() {
1720 if (selType == selRectangle) {
1721 pdoc->BeginUndoAction();
1722 int lineStart = pdoc->LineFromPosition(SelectionStart());
1723 int lineEnd = pdoc->LineFromPosition(SelectionEnd());
1724 int startPos = SelectionStart();
1725 for (int line = lineEnd; line >= lineStart; line--) {
1726 startPos = SelectionStart(line);
1727 unsigned int chars = SelectionEnd(line) - startPos;
1728 if (0 != chars) {
1729 pdoc->DeleteChars(startPos, chars);
1730 }
1731 }
1732 SetEmptySelection(startPos);
1733 pdoc->EndUndoAction();
1734 selType = selStream;
1735 } else {
1736 int startPos = SelectionStart();
1737 unsigned int chars = SelectionEnd() - startPos;
1738 SetEmptySelection(startPos);
1739 if (0 != chars) {
1740 pdoc->BeginUndoAction();
1741 pdoc->DeleteChars(startPos, chars);
1742 pdoc->EndUndoAction();
1743 }
1744 }
1745 }
1746
1747 void Editor::ClearAll() {
1748 pdoc->BeginUndoAction();
1749 if (0 != pdoc->Length()) {
1750 pdoc->DeleteChars(0, pdoc->Length());
1751 }
1752 cs.Clear();
1753 pdoc->EndUndoAction();
1754 anchor = 0;
1755 currentPos = 0;
1756 SetTopLine(0);
1757 SetVerticalScrollPos();
1758 }
1759
1760 void Editor::ClearDocumentStyle() {
1761 pdoc->StartStyling(0, '\377');
1762 pdoc->SetStyleFor(pdoc->Length(), 0);
1763 cs.ShowAll();
1764 pdoc->ClearLevels();
1765 }
1766
1767 void Editor::Cut() {
1768 if (!pdoc->IsReadOnly()) {
1769 Copy();
1770 ClearSelection();
1771 }
1772 }
1773
1774 void Editor::PasteRectangular(int pos, const char *ptr, int len) {
1775 if (pdoc->IsReadOnly()) {
1776 return;
1777 }
1778 currentPos = pos;
1779 int insertPos = currentPos;
1780 int xInsert = XFromPosition(currentPos);
1781 int line = pdoc->LineFromPosition(currentPos);
1782 bool prevCr = false;
1783 pdoc->BeginUndoAction();
1784 for (int i = 0; i < len; i++) {
1785 if ((ptr[i] == '\r') || (ptr[i] == '\n')) {
1786 if ((ptr[i] == '\r') || (!prevCr))
1787 line++;
1788 if (line >= pdoc->LinesTotal()) {
1789 if (pdoc->eolMode != SC_EOL_LF)
1790 pdoc->InsertChar(pdoc->Length(), '\r');
1791 if (pdoc->eolMode != SC_EOL_CR)
1792 pdoc->InsertChar(pdoc->Length(), '\n');
1793 }
1794 // Pad the end of lines with spaces if required
1795 currentPos = PositionFromLineX(line, xInsert);
1796 if ((XFromPosition(currentPos) < xInsert) && (i + 1 < len)) {
1797 for (int i = 0; i < xInsert - XFromPosition(currentPos); i++) {
1798 pdoc->InsertChar(currentPos, ' ');
1799 currentPos++;
1800 }
1801 insertPos = currentPos;
1802 }
1803 prevCr = ptr[i] == '\r';
1804 } else {
1805 pdoc->InsertString(currentPos, ptr + i, 1);
1806 currentPos++;
1807 insertPos = currentPos;
1808 prevCr = false;
1809 }
1810 }
1811 pdoc->EndUndoAction();
1812 SetEmptySelection(insertPos);
1813 }
1814
1815 bool Editor::CanPaste() {
1816 return !pdoc->IsReadOnly();
1817 }
1818
1819 void Editor::Clear() {
1820 if (currentPos == anchor) {
1821 DelChar();
1822 } else {
1823 ClearSelection();
1824 }
1825 SetEmptySelection(currentPos);
1826 }
1827
1828 void Editor::SelectAll() {
1829 SetSelection(0, pdoc->Length());
1830 Redraw();
1831 }
1832
1833 void Editor::Undo() {
1834 if (pdoc->CanUndo()) {
1835 InvalidateCaret();
1836 int newPos = pdoc->Undo();
1837 SetEmptySelection(newPos);
1838 EnsureCaretVisible();
1839 }
1840 }
1841
1842 void Editor::Redo() {
1843 if (pdoc->CanRedo()) {
1844 int newPos = pdoc->Redo();
1845 SetEmptySelection(newPos);
1846 EnsureCaretVisible();
1847 }
1848 }
1849
1850 void Editor::DelChar() {
1851 pdoc->DelChar(currentPos);
1852 // Avoid blinking during rapid typing:
1853 ShowCaretAtCurrentPosition();
1854 }
1855
1856 void Editor::DelCharBack() {
1857 if (currentPos == anchor) {
1858 int lineCurrentPos = pdoc->LineFromPosition(currentPos);
1859 if (pdoc->GetColumn(currentPos) <= pdoc->GetLineIndentation(lineCurrentPos) &&
1860 pdoc->GetColumn(currentPos) > 0 && pdoc->backspaceUnindents) {
1861 pdoc->BeginUndoAction();
1862 int indentation = pdoc->GetLineIndentation(lineCurrentPos);
1863 int indentationStep = (pdoc->indentInChars ? pdoc->indentInChars : pdoc->tabInChars);
1864 if (indentation % indentationStep == 0) {
1865 pdoc->SetLineIndentation(lineCurrentPos, indentation - indentationStep);
1866 } else {
1867 pdoc->SetLineIndentation(lineCurrentPos, indentation - (indentation % indentationStep));
1868 }
1869 SetEmptySelection(pdoc->GetLineIndentPosition(lineCurrentPos));
1870 pdoc->EndUndoAction();
1871 } else {
1872 int newPos = pdoc->DelCharBack(currentPos);
1873 SetEmptySelection(newPos);
1874 }
1875 } else {
1876 ClearSelection();
1877 SetEmptySelection(currentPos);
1878 }
1879 // Avoid blinking during rapid typing:
1880 ShowCaretAtCurrentPosition();
1881 }
1882
1883 void Editor::NotifyFocus(bool) {}
1884
1885 void Editor::NotifyStyleToNeeded(int endStyleNeeded) {
1886 SCNotification scn;
1887 scn.nmhdr.code = SCN_STYLENEEDED;
1888 scn.position = endStyleNeeded;
1889 NotifyParent(scn);
1890 }
1891
1892 void Editor::NotifyStyleNeeded(Document*, void *, int endStyleNeeded) {
1893 NotifyStyleToNeeded(endStyleNeeded);
1894 }
1895
1896 void Editor::NotifyChar(int ch) {
1897 SCNotification scn;
1898 scn.nmhdr.code = SCN_CHARADDED;
1899 scn.ch = ch;
1900 NotifyParent(scn);
1901 if (recordingMacro) {
1902 char txt[2];
1903 txt[0] = static_cast<char>(ch);
1904 txt[1] = '\0';
1905 NotifyMacroRecord(SCI_REPLACESEL, 0, reinterpret_cast<long>(txt));
1906 }
1907 }
1908
1909 void Editor::NotifySavePoint(bool isSavePoint) {
1910 SCNotification scn;
1911 if (isSavePoint) {
1912 scn.nmhdr.code = SCN_SAVEPOINTREACHED;
1913 } else {
1914 scn.nmhdr.code = SCN_SAVEPOINTLEFT;
1915 }
1916 NotifyParent(scn);
1917 }
1918
1919 void Editor::NotifyModifyAttempt() {
1920 SCNotification scn;
1921 scn.nmhdr.code = SCN_MODIFYATTEMPTRO;
1922 NotifyParent(scn);
1923 }
1924
1925 void Editor::NotifyDoubleClick(Point, bool) {
1926 SCNotification scn;
1927 scn.nmhdr.code = SCN_DOUBLECLICK;
1928 NotifyParent(scn);
1929 }
1930
1931 void Editor::NotifyUpdateUI() {
1932 SCNotification scn;
1933 scn.nmhdr.code = SCN_UPDATEUI;
1934 NotifyParent(scn);
1935 }
1936
1937 void Editor::NotifyPainted() {
1938 SCNotification scn;
1939 scn.nmhdr.code = SCN_PAINTED;
1940 NotifyParent(scn);
1941 }
1942
1943 bool Editor::NotifyMarginClick(Point pt, bool shift, bool ctrl, bool alt) {
1944 int marginClicked = -1;
1945 int x = 0;
1946 for (int margin = 0; margin < ViewStyle::margins; margin++) {
1947 if ((pt.x > x) && (pt.x < x + vs.ms[margin].width))
1948 marginClicked = margin;
1949 x += vs.ms[margin].width;
1950 }
1951 if ((marginClicked >= 0) && vs.ms[marginClicked].sensitive) {
1952 SCNotification scn;
1953 scn.nmhdr.code = SCN_MARGINCLICK;
1954 scn.modifiers = (shift ? SCI_SHIFT : 0) | (ctrl ? SCI_CTRL : 0) |
1955 (alt ? SCI_ALT : 0);
1956 scn.position = pdoc->LineStart(LineFromLocation(pt));
1957 scn.margin = marginClicked;
1958 NotifyParent(scn);
1959 return true;
1960 } else {
1961 return false;
1962 }
1963 }
1964
1965 void Editor::NotifyNeedShown(int pos, int len) {
1966 SCNotification scn;
1967 scn.nmhdr.code = SCN_NEEDSHOWN;
1968 scn.position = pos;
1969 scn.length = len;
1970 NotifyParent(scn);
1971 }
1972
1973 void Editor::NotifyDwelling(Point pt, bool state) {
1974 SCNotification scn;
1975 scn.nmhdr.code = state ? SCN_DWELLSTART : SCN_DWELLEND;
1976 scn.position = PositionFromLocationClose(pt);
1977 scn.x = pt.x;
1978 scn.y = pt.y;
1979 NotifyParent(scn);
1980 }
1981
1982 // Notifications from document
1983 void Editor::NotifyModifyAttempt(Document*, void *) {
1984 //Platform::DebugPrintf("** Modify Attempt\n");
1985 NotifyModifyAttempt();
1986 }
1987
1988 void Editor::NotifyMove(int position) {
1989 SCNotification scn;
1990 scn.nmhdr.code = SCN_POSCHANGED;
1991 scn.position = position;
1992 NotifyParent(scn);
1993 }
1994
1995 void Editor::NotifySavePoint(Document*, void *, bool atSavePoint) {
1996 //Platform::DebugPrintf("** Save Point %s\n", atSavePoint ? "On" : "Off");
1997 NotifySavePoint(atSavePoint);
1998 }
1999
2000 void Editor::NotifyModified(Document*, DocModification mh, void *) {
2001 needUpdateUI = true;
2002 if (paintState == painting) {
2003 CheckForChangeOutsidePaint(Range(mh.position, mh.position + mh.length));
2004 } else if (paintState == notPainting) {
2005 if (mh.modificationType & SC_MOD_CHANGESTYLE) {
2006 if (mh.position < pdoc->LineStart(topLine)) {
2007 // Styling performed before this view
2008 Redraw();
2009 } else {
2010 InvalidateRange(mh.position, mh.position + mh.length);
2011 }
2012 } else {
2013 // Move selection and brace highlights
2014 if (mh.modificationType & SC_MOD_INSERTTEXT) {
2015 if (currentPos > mh.position) {
2016 currentPos += mh.length;
2017 }
2018 if (anchor > mh.position) {
2019 anchor += mh.length;
2020 }
2021 if (braces[0] > mh.position) {
2022 braces[0] += mh.length;
2023 }
2024 if (braces[1] > mh.position) {
2025 braces[1] += mh.length;
2026 }
2027 } else { // SC_MOD_DELETETEXT
2028 int endPos = mh.position + mh.length;
2029 if (currentPos > mh.position) {
2030 if (currentPos > endPos) {
2031 currentPos -= mh.length;
2032 } else {
2033 currentPos = endPos;
2034 }
2035 }
2036 if (anchor > mh.position) {
2037 if (anchor > endPos) {
2038 anchor -= mh.length;
2039 } else {
2040 anchor = endPos;
2041 }
2042 }
2043 if (braces[0] > mh.position) {
2044 if (braces[0] > endPos) {
2045 braces[0] -= mh.length;
2046 } else {
2047 braces[0] = endPos;
2048 }
2049 }
2050 if (braces[1] > mh.position) {
2051 if (braces[1] > endPos) {
2052 braces[1] -= mh.length;
2053 } else {
2054 braces[1] = endPos;
2055 }
2056 }
2057 }
2058 if (cs.LinesDisplayed() < cs.LinesInDoc()) {
2059 // Some lines are hidden so may need shown.
2060 // TODO: check if the modified area is hidden.
2061 if (mh.modificationType & SC_MOD_BEFOREINSERT) {
2062 NotifyNeedShown(mh.position, mh.length);
2063 } else if (mh.modificationType & SC_MOD_BEFOREDELETE) {
2064 NotifyNeedShown(mh.position, mh.length);
2065 }
2066 }
2067 if (mh.linesAdded != 0) {
2068 // Update contraction state for inserted and removed lines
2069 // lineOfPos should be calculated in context of state before modification, shouldn't it
2070 int lineOfPos = pdoc->LineFromPosition(mh.position);
2071 if (mh.linesAdded > 0) {
2072 cs.InsertLines(lineOfPos, mh.linesAdded);
2073 } else {
2074 cs.DeleteLines(lineOfPos, -mh.linesAdded);
2075 }
2076 // Avoid scrolling of display if change before current display
2077 if (mh.position < posTopLine) {
2078 int newTop = Platform::Clamp(topLine + mh.linesAdded, 0, MaxScrollPos());
2079 if (newTop != topLine) {
2080 SetTopLine(newTop);
2081 SetVerticalScrollPos();
2082 }
2083 }
2084
2085 //Platform::DebugPrintf("** %x Doc Changed\n", this);
2086 // TODO: could invalidate from mh.startModification to end of screen
2087 //InvalidateRange(mh.position, mh.position + mh.length);
2088 Redraw();
2089 } else {
2090 //Platform::DebugPrintf("** %x Line Changed %d .. %d\n", this,
2091 // mh.position, mh.position + mh.length);
2092 InvalidateRange(mh.position, mh.position + mh.length);
2093 }
2094 }
2095 } // else paintState == paintAbandoned so no need to do anything
2096
2097 if (mh.linesAdded != 0) {
2098 SetScrollBars();
2099 }
2100
2101 if (mh.modificationType & SC_MOD_CHANGEMARKER) {
2102 RedrawSelMargin();
2103 }
2104
2105 // If client wants to see this modification
2106 if (mh.modificationType & modEventMask) {
2107 if ((mh.modificationType & SC_MOD_CHANGESTYLE) == 0) {
2108 // Real modification made to text of document.
2109 NotifyChange(); // Send EN_CHANGE
2110 }
2111
2112
2113
2114 SCNotification scn;
2115 scn.nmhdr.code = SCN_MODIFIED;
2116 scn.position = mh.position;
2117 scn.modificationType = mh.modificationType;
2118 scn.text = mh.text;
2119 scn.length = mh.length;
2120 scn.linesAdded = mh.linesAdded;
2121 scn.line = mh.line;
2122 scn.foldLevelNow = mh.foldLevelNow;
2123 scn.foldLevelPrev = mh.foldLevelPrev;
2124 NotifyParent(scn);
2125 }
2126 }
2127
2128 void Editor::NotifyDeleted(Document *, void *) {
2129 /* Do nothing */
2130 }
2131
2132 void Editor::NotifyMacroRecord(unsigned int iMessage, unsigned long wParam, long lParam) {
2133
2134 // Enumerates all macroable messages
2135 switch (iMessage) {
2136 case SCI_CUT:
2137 case SCI_COPY:
2138 case SCI_PASTE:
2139 case SCI_CLEAR:
2140 case SCI_REPLACESEL:
2141 case SCI_ADDTEXT:
2142 case SCI_INSERTTEXT:
2143 case SCI_CLEARALL:
2144 case SCI_SELECTALL:
2145 case SCI_GOTOLINE:
2146 case SCI_GOTOPOS:
2147 case SCI_SEARCHANCHOR:
2148 case SCI_SEARCHNEXT:
2149 case SCI_SEARCHPREV:
2150 case SCI_LINEDOWN:
2151 case SCI_LINEDOWNEXTEND:
2152 case SCI_LINEUP:
2153 case SCI_LINEUPEXTEND:
2154 case SCI_CHARLEFT:
2155 case SCI_CHARLEFTEXTEND:
2156 case SCI_CHARRIGHT:
2157 case SCI_CHARRIGHTEXTEND:
2158 case SCI_WORDLEFT:
2159 case SCI_WORDLEFTEXTEND:
2160 case SCI_WORDRIGHT:
2161 case SCI_WORDRIGHTEXTEND:
2162 case SCI_WORDPARTLEFT:
2163 case SCI_WORDPARTLEFTEXTEND:
2164 case SCI_WORDPARTRIGHT:
2165 case SCI_WORDPARTRIGHTEXTEND:
2166 case SCI_HOME:
2167 case SCI_HOMEEXTEND:
2168 case SCI_LINEEND:
2169 case SCI_LINEENDEXTEND:
2170 case SCI_DOCUMENTSTART:
2171 case SCI_DOCUMENTSTARTEXTEND:
2172 case SCI_DOCUMENTEND:
2173 case SCI_DOCUMENTENDEXTEND:
2174 case SCI_PAGEUP:
2175 case SCI_PAGEUPEXTEND:
2176 case SCI_PAGEDOWN:
2177 case SCI_PAGEDOWNEXTEND:
2178 case SCI_EDITTOGGLEOVERTYPE:
2179 case SCI_CANCEL:
2180 case SCI_DELETEBACK:
2181 case SCI_TAB:
2182 case SCI_BACKTAB:
2183 case SCI_FORMFEED:
2184 case SCI_VCHOME:
2185 case SCI_VCHOMEEXTEND:
2186 case SCI_DELWORDLEFT:
2187 case SCI_DELWORDRIGHT:
2188 case SCI_DELLINELEFT:
2189 case SCI_DELLINERIGHT:
2190 case SCI_LINECUT:
2191 case SCI_LINEDELETE:
2192 case SCI_LINETRANSPOSE:
2193 case SCI_LOWERCASE:
2194 case SCI_UPPERCASE:
2195 break;
2196
2197 // Filter out all others like display changes. Also, newlines are redundant
2198 // with char insert messages.
2199 case SCI_NEWLINE:
2200 default:
2201 // printf("Filtered out %ld of macro recording\n", iMessage);
2202 return;
2203 }
2204
2205 // Send notification
2206 SCNotification scn;
2207 scn.nmhdr.code = SCN_MACRORECORD;
2208 scn.message = iMessage;
2209 scn.wParam = wParam;
2210 scn.lParam = lParam;
2211 NotifyParent(scn);
2212 }
2213
2214 // Force scroll and keep position relative to top of window
2215 void Editor::PageMove(int direction, bool extend) {
2216 Point pt = LocationFromPosition(currentPos);
2217 int topLineNew = Platform::Clamp(
2218 topLine + direction * LinesToScroll(), 0, MaxScrollPos());
2219 int newPos = PositionFromLocation(
2220 Point(lastXChosen, pt.y + direction * (vs.lineHeight * LinesToScroll())));
2221 if (topLineNew != topLine) {
2222 SetTopLine(topLineNew);
2223 MovePositionTo(newPos, extend);
2224 Redraw();
2225 SetVerticalScrollPos();
2226 } else {
2227 MovePositionTo(newPos, extend);
2228 }
2229 }
2230
2231 void Editor::ChangeCaseOfSelection(bool makeUpperCase) {
2232 pdoc->BeginUndoAction();
2233 int startCurrent = currentPos;
2234 int startAnchor = anchor;
2235 if (selType == selRectangle) {
2236 int lineStart = pdoc->LineFromPosition(SelectionStart());
2237 int lineEnd = pdoc->LineFromPosition(SelectionEnd());
2238 for (int line = lineEnd; line >= lineStart; line--) {
2239 pdoc->ChangeCase(
2240 Range(SelectionStart(line), SelectionEnd(line)),
2241 makeUpperCase);
2242 }
2243 // Would be nicer to keep the rectangular selection but this is complex
2244 selType = selStream;
2245 SetSelection(startCurrent, startCurrent);
2246 } else {
2247 pdoc->ChangeCase(Range(SelectionStart(), SelectionEnd()),
2248 makeUpperCase);
2249 SetSelection(startCurrent, startAnchor);
2250 }
2251 pdoc->EndUndoAction();
2252 }
2253
2254 void Editor::LineTranspose() {
2255 int line = pdoc->LineFromPosition(currentPos);
2256 if (line > 0) {
2257 int startPrev = pdoc->LineStart(line - 1);
2258 int endPrev = pdoc->LineEnd(line - 1);
2259 int start = pdoc->LineStart(line);
2260 int end = pdoc->LineEnd(line);
2261 int startNext = pdoc->LineStart(line + 1);
2262 if (end < pdoc->Length()) {
2263 end = startNext;
2264 char *thisLine = CopyRange(start, end);
2265 pdoc->DeleteChars(start, end - start);
2266 pdoc->InsertString(startPrev, thisLine, end - start);
2267 MovePositionTo(startPrev + end - start);
2268 delete []thisLine;
2269 } else {
2270 // Last line so line has no line end
2271 char *thisLine = CopyRange(start, end);
2272 char *prevEnd = CopyRange(endPrev, start);
2273 pdoc->DeleteChars(endPrev, end - endPrev);
2274 pdoc->InsertString(startPrev, thisLine, end - start);
2275 pdoc->InsertString(startPrev + end - start, prevEnd, start - endPrev);
2276 MovePositionTo(startPrev + end - endPrev);
2277 delete []thisLine;
2278 delete []prevEnd;
2279 }
2280
2281 }
2282 }
2283
2284 void Editor::CancelModes() {}
2285
2286 int Editor::KeyCommand(unsigned int iMessage) {
2287 Point pt = LocationFromPosition(currentPos);
2288
2289 switch (iMessage) {
2290 case SCI_LINEDOWN:
2291 MovePositionTo(PositionFromLocation(
2292 Point(lastXChosen, pt.y + vs.lineHeight)));
2293 break;
2294 case SCI_LINEDOWNEXTEND:
2295 MovePositionTo(PositionFromLocation(
2296 Point(lastXChosen, pt.y + vs.lineHeight)), true);
2297 break;
2298 case SCI_LINESCROLLDOWN:
2299 ScrollTo(topLine + 1);
2300 MoveCaretInsideView();
2301 break;
2302 case SCI_LINEUP:
2303 MovePositionTo(PositionFromLocation(
2304 Point(lastXChosen, pt.y - vs.lineHeight)));
2305 break;
2306 case SCI_LINEUPEXTEND:
2307 MovePositionTo(PositionFromLocation(
2308 Point(lastXChosen, pt.y - vs.lineHeight)), true);
2309 break;
2310 case SCI_LINESCROLLUP:
2311 ScrollTo(topLine - 1);
2312 MoveCaretInsideView();
2313 break;
2314 case SCI_CHARLEFT:
2315 if (SelectionEmpty()) {
2316 MovePositionTo(MovePositionSoVisible(currentPos - 1, -1));
2317 } else {
2318 MovePositionTo(SelectionStart());
2319 }
2320 SetLastXChosen();
2321 break;
2322 case SCI_CHARLEFTEXTEND:
2323 MovePositionTo(MovePositionSoVisible(currentPos - 1, -1), true);
2324 SetLastXChosen();
2325 break;
2326 case SCI_CHARRIGHT:
2327 if (SelectionEmpty()) {
2328 MovePositionTo(MovePositionSoVisible(currentPos + 1, 1));
2329 } else {
2330 MovePositionTo(SelectionEnd());
2331 }
2332 SetLastXChosen();
2333 break;
2334 case SCI_CHARRIGHTEXTEND:
2335 MovePositionTo(MovePositionSoVisible(currentPos + 1, 1), true);
2336 SetLastXChosen();
2337 break;
2338 case SCI_WORDLEFT:
2339 MovePositionTo(MovePositionSoVisible(pdoc->NextWordStart(currentPos, -1), -1));
2340 SetLastXChosen();
2341 break;
2342 case SCI_WORDLEFTEXTEND:
2343 MovePositionTo(MovePositionSoVisible(pdoc->NextWordStart(currentPos, -1), -1), true);
2344 SetLastXChosen();
2345 break;
2346 case SCI_WORDRIGHT:
2347 MovePositionTo(MovePositionSoVisible(pdoc->NextWordStart(currentPos, 1), 1));
2348 SetLastXChosen();
2349 break;
2350 case SCI_WORDRIGHTEXTEND:
2351 MovePositionTo(MovePositionSoVisible(pdoc->NextWordStart(currentPos, 1), 1), true);
2352 SetLastXChosen();
2353 break;
2354 case SCI_HOME:
2355 MovePositionTo(pdoc->LineStart(pdoc->LineFromPosition(currentPos)));
2356 SetLastXChosen();
2357 break;
2358 case SCI_HOMEEXTEND:
2359 MovePositionTo(pdoc->LineStart(pdoc->LineFromPosition(currentPos)), true);
2360 SetLastXChosen();
2361 break;
2362 case SCI_LINEEND:
2363 MovePositionTo(pdoc->LineEndPosition(currentPos));
2364 SetLastXChosen();
2365 break;
2366 case SCI_LINEENDEXTEND:
2367 MovePositionTo(pdoc->LineEndPosition(currentPos), true);
2368 SetLastXChosen();
2369 break;
2370 case SCI_DOCUMENTSTART:
2371 MovePositionTo(0);
2372 SetLastXChosen();
2373 break;
2374 case SCI_DOCUMENTSTARTEXTEND:
2375 MovePositionTo(0, true);
2376 SetLastXChosen();
2377 break;
2378 case SCI_DOCUMENTEND:
2379 MovePositionTo(pdoc->Length());
2380 SetLastXChosen();
2381 break;
2382 case SCI_DOCUMENTENDEXTEND:
2383 MovePositionTo(pdoc->Length(), true);
2384 SetLastXChosen();
2385 break;
2386 case SCI_PAGEUP:
2387 PageMove( -1);
2388 break;
2389 case SCI_PAGEUPEXTEND:
2390 PageMove( -1, true);
2391 break;
2392 case SCI_PAGEDOWN:
2393 PageMove(1);
2394 break;
2395 case SCI_PAGEDOWNEXTEND:
2396 PageMove(1, true);
2397 break;
2398 case SCI_EDITTOGGLEOVERTYPE:
2399 inOverstrike = !inOverstrike;
2400 DropCaret();
2401 ShowCaretAtCurrentPosition();
2402 NotifyUpdateUI();
2403 break;
2404 case SCI_CANCEL: // Cancel any modes - handled in subclass
2405 // Also unselect text
2406 CancelModes();
2407 break;
2408 case SCI_DELETEBACK:
2409 DelCharBack();
2410 SetLastXChosen();
2411 EnsureCaretVisible();
2412 break;
2413 case SCI_TAB:
2414 Indent(true);
2415 SetLastXChosen();
2416 EnsureCaretVisible();
2417 break;
2418 case SCI_BACKTAB:
2419 Indent(false);
2420 SetLastXChosen();
2421 EnsureCaretVisible();
2422 break;
2423 case SCI_NEWLINE:
2424 ClearSelection();
2425 if (pdoc->eolMode == SC_EOL_CRLF) {
2426 pdoc->InsertString(currentPos, "\r\n");
2427 SetEmptySelection(currentPos + 2);
2428 NotifyChar('\r');
2429 NotifyChar('\n');
2430 } else if (pdoc->eolMode == SC_EOL_CR) {
2431 pdoc->InsertChar(currentPos, '\r');
2432 SetEmptySelection(currentPos + 1);
2433 NotifyChar('\r');
2434 } else if (pdoc->eolMode == SC_EOL_LF) {
2435 pdoc->InsertChar(currentPos, '\n');
2436 SetEmptySelection(currentPos + 1);
2437 NotifyChar('\n');
2438 }
2439 SetLastXChosen();
2440 EnsureCaretVisible();
2441 break;
2442 case SCI_FORMFEED:
2443 AddChar('\f');
2444 break;
2445 case SCI_VCHOME:
2446 MovePositionTo(pdoc->VCHomePosition(currentPos));
2447 SetLastXChosen();
2448 break;
2449 case SCI_VCHOMEEXTEND:
2450 MovePositionTo(pdoc->VCHomePosition(currentPos), true);
2451 SetLastXChosen();
2452 break;
2453 case SCI_ZOOMIN:
2454 if (vs.zoomLevel < 20)
2455 vs.zoomLevel++;
2456 InvalidateStyleRedraw();
2457 break;
2458 case SCI_ZOOMOUT:
2459 if (vs.zoomLevel > -10)
2460 vs.zoomLevel--;
2461 InvalidateStyleRedraw();
2462 break;
2463 case SCI_DELWORDLEFT: {
2464 int startWord = pdoc->NextWordStart(currentPos, -1);
2465 pdoc->DeleteChars(startWord, currentPos - startWord);
2466 MovePositionTo(startWord);
2467 SetLastXChosen();
2468 }
2469 break;
2470 case SCI_DELWORDRIGHT: {
2471 int endWord = pdoc->NextWordStart(currentPos, 1);
2472 pdoc->DeleteChars(currentPos, endWord - currentPos);
2473 MovePositionTo(currentPos);
2474 }
2475 break;
2476 case SCI_DELLINELEFT: {
2477 int line = pdoc->LineFromPosition(currentPos);
2478 int start = pdoc->LineStart(line);
2479 pdoc->DeleteChars(start, currentPos - start);
2480 MovePositionTo(start);
2481 SetLastXChosen();
2482 }
2483 break;
2484 case SCI_DELLINERIGHT: {
2485 int line = pdoc->LineFromPosition(currentPos);
2486 int end = pdoc->LineEnd(line);
2487 pdoc->DeleteChars(currentPos, end - currentPos);
2488 MovePositionTo(currentPos);
2489 }
2490 break;
2491 case SCI_LINECUT: {
2492 int lineStart = pdoc->LineFromPosition(currentPos);
2493 int lineEnd = pdoc->LineFromPosition(anchor);
2494 if (lineStart > lineEnd) {
2495 int t = lineEnd;
2496 lineEnd = lineStart;
2497 lineStart = t;
2498 }
2499 int start = pdoc->LineStart(lineStart);
2500 int end = pdoc->LineStart(lineEnd + 1);
2501 SetSelection(start, end);
2502 Cut();
2503 }
2504 break;
2505 case SCI_LINEDELETE: {
2506 int line = pdoc->LineFromPosition(currentPos);
2507 int start = pdoc->LineStart(line);
2508 int end = pdoc->LineStart(line + 1);
2509 pdoc->DeleteChars(start, end - start);
2510 MovePositionTo(start);
2511 }
2512 break;
2513 case SCI_LINETRANSPOSE:
2514 LineTranspose();
2515 break;
2516 case SCI_LOWERCASE:
2517 ChangeCaseOfSelection(false);
2518 break;
2519 case SCI_UPPERCASE:
2520 ChangeCaseOfSelection(true);
2521 break;
2522 case SCI_WORDPARTLEFT:
2523 MovePositionTo(MovePositionSoVisible(pdoc->WordPartLeft(currentPos), -1));
2524 SetLastXChosen();
2525 break;
2526 case SCI_WORDPARTLEFTEXTEND:
2527 MovePositionTo(MovePositionSoVisible(pdoc->WordPartLeft(currentPos), -1), true);
2528 SetLastXChosen();
2529 break;
2530 case SCI_WORDPARTRIGHT:
2531 MovePositionTo(MovePositionSoVisible(pdoc->WordPartRight(currentPos), 1));
2532 SetLastXChosen();
2533 break;
2534 case SCI_WORDPARTRIGHTEXTEND:
2535 MovePositionTo(MovePositionSoVisible(pdoc->WordPartRight(currentPos), 1), true);
2536 SetLastXChosen();
2537 break;
2538 }
2539 return 0;
2540 }
2541
2542 int Editor::KeyDefault(int, int) {
2543 return 0;
2544 }
2545
2546 int Editor::KeyDown(int key, bool shift, bool ctrl, bool alt, bool *consumed) {
2547 DwellEnd(false);
2548 int modifiers = (shift ? SCI_SHIFT : 0) | (ctrl ? SCI_CTRL : 0) |
2549 (alt ? SCI_ALT : 0);
2550 int msg = kmap.Find(key, modifiers);
2551 if (msg) {
2552 if (consumed)
2553 *consumed = true;
2554 return WndProc(msg, 0, 0);
2555 } else {
2556 if (consumed)
2557 *consumed = false;
2558 return KeyDefault(key, modifiers);
2559 }
2560 }
2561
2562 void Editor::SetWhitespaceVisible(int view) {
2563 vs.viewWhitespace = static_cast<WhiteSpaceVisibility>(view);
2564 }
2565
2566 int Editor::GetWhitespaceVisible() {
2567 return vs.viewWhitespace;
2568 }
2569
2570 void Editor::Indent(bool forwards) {
2571 //Platform::DebugPrintf("INdent %d\n", forwards);
2572 int lineOfAnchor = pdoc->LineFromPosition(anchor);
2573 int lineCurrentPos = pdoc->LineFromPosition(currentPos);
2574 if (lineOfAnchor == lineCurrentPos) {
2575 if (forwards) {
2576 ClearSelection();
2577 if (pdoc->GetColumn(currentPos) <= pdoc->GetColumn(pdoc->GetLineIndentPosition(lineCurrentPos)) &&
2578 pdoc->tabIndents) {
2579 pdoc->BeginUndoAction();
2580 int indentation = pdoc->GetLineIndentation(lineCurrentPos);
2581 int indentationStep = (pdoc->indentInChars ? pdoc->indentInChars : pdoc->tabInChars);
2582 pdoc->SetLineIndentation(lineCurrentPos, indentation + indentationStep);
2583 SetEmptySelection(pdoc->GetLineIndentPosition(lineCurrentPos));
2584 pdoc->EndUndoAction();
2585 } else {
2586 if (pdoc->useTabs) {
2587 pdoc->InsertChar(currentPos, '\t');
2588 SetEmptySelection(currentPos + 1);
2589 } else {
2590 int numSpaces = (pdoc->tabInChars) -
2591 (pdoc->GetColumn(currentPos) % (pdoc->tabInChars));
2592 if (numSpaces < 1)
2593 numSpaces = pdoc->tabInChars;
2594 for (int i = 0; i < numSpaces; i++) {
2595 pdoc->InsertChar(currentPos, ' ');
2596 }
2597 SetEmptySelection(currentPos + numSpaces);
2598 }
2599 }
2600 } else {
2601 if (pdoc->GetColumn(currentPos) <= pdoc->GetLineIndentation(lineCurrentPos) &&
2602 pdoc->tabIndents) {
2603 pdoc->BeginUndoAction();
2604 int indentation = pdoc->GetLineIndentation(lineCurrentPos);
2605 int indentationStep = (pdoc->indentInChars ? pdoc->indentInChars : pdoc->tabInChars);
2606 pdoc->SetLineIndentation(lineCurrentPos, indentation - indentationStep);
2607 SetEmptySelection(pdoc->GetLineIndentPosition(lineCurrentPos));
2608 pdoc->EndUndoAction();
2609 } else {
2610 int newColumn = ((pdoc->GetColumn(currentPos) - 1) / pdoc->tabInChars) *
2611 pdoc->tabInChars;
2612 if (newColumn < 0)
2613 newColumn = 0;
2614 int newPos = currentPos;
2615 while (pdoc->GetColumn(newPos) > newColumn)
2616 newPos--;
2617 SetEmptySelection(newPos);
2618 }
2619 }
2620 } else {
2621 int anchorPosOnLine = anchor - pdoc->LineStart(lineOfAnchor);
2622 int currentPosPosOnLine = currentPos - pdoc->LineStart(lineCurrentPos);
2623 // Multiple lines selected so indent / dedent
2624 int lineTopSel = Platform::Minimum(lineOfAnchor, lineCurrentPos);
2625 int lineBottomSel = Platform::Maximum(lineOfAnchor, lineCurrentPos);
2626 if (pdoc->LineStart(lineBottomSel) == anchor || pdoc->LineStart(lineBottomSel) == currentPos)
2627 lineBottomSel--; // If not selecting any characters on a line, do not indent
2628 pdoc->BeginUndoAction();
2629 pdoc->Indent(forwards, lineBottomSel, lineTopSel);
2630 pdoc->EndUndoAction();
2631 if (lineOfAnchor < lineCurrentPos) {
2632 if (currentPosPosOnLine == 0)
2633 SetSelection(pdoc->LineStart(lineCurrentPos), pdoc->LineStart(lineOfAnchor));
2634 else
2635 SetSelection(pdoc->LineStart(lineCurrentPos + 1), pdoc->LineStart(lineOfAnchor));
2636 } else {
2637 if (anchorPosOnLine == 0)
2638 SetSelection(pdoc->LineStart(lineCurrentPos), pdoc->LineStart(lineOfAnchor));
2639 else
2640 SetSelection(pdoc->LineStart(lineCurrentPos), pdoc->LineStart(lineOfAnchor + 1));
2641 }
2642 }
2643 }
2644
2645 /**
2646 * Search of a text in the document, in the given range.
2647 * @return The position of the found text, -1 if not found.
2648 */
2649 long Editor::FindText(
2650 unsigned long wParam, ///< Search modes : @c SCFIND_MATCHCASE, @c SCFIND_WHOLEWORD,
2651 ///< @c SCFIND_WORDSTART or @c SCFIND_REGEXP.
2652 long lParam) { ///< @c TextToFind structure: The text to search for in the given range.
2653
2654 TextToFind *ft = reinterpret_cast<TextToFind *>(lParam);
2655 int lengthFound = strlen(ft->lpstrText);
2656 int pos = pdoc->FindText(ft->chrg.cpMin, ft->chrg.cpMax, ft->lpstrText,
2657 wParam & SCFIND_MATCHCASE,
2658 wParam & SCFIND_WHOLEWORD,
2659 wParam & SCFIND_WORDSTART,
2660 wParam & SCFIND_REGEXP,
2661 &lengthFound);
2662 if (pos != -1) {
2663 ft->chrgText.cpMin = pos;
2664 ft->chrgText.cpMax = pos + lengthFound;
2665 }
2666 return pos;
2667 }
2668
2669 /**
2670 * Relocatable search support : Searches relative to current selection
2671 * point and sets the selection to the found text range with
2672 * each search.
2673 */
2674 /**
2675 * Anchor following searches at current selection start: This allows
2676 * multiple incremental interactive searches to be macro recorded
2677 * while still setting the selection to found text so the find/select
2678 * operation is self-contained.
2679 */
2680 void Editor::SearchAnchor() {
2681 searchAnchor = SelectionStart();
2682 }
2683
2684 /**
2685 * Find text from current search anchor: Must call @c SearchAnchor first.
2686 * Used for next text and previous text requests.
2687 * @return The position of the found text, -1 if not found.
2688 */
2689 long Editor::SearchText(
2690 unsigned int iMessage, ///< Accepts both @c SCI_SEARCHNEXT and @c SCI_SEARCHPREV.
2691 unsigned long wParam, ///< Search modes : @c SCFIND_MATCHCASE, @c SCFIND_WHOLEWORD,
2692 ///< @c SCFIND_WORDSTART or @c SCFIND_REGEXP.
2693 long lParam) { ///< The text to search for.
2694
2695 const char *txt = reinterpret_cast<char *>(lParam);
2696 int pos;
2697 int lengthFound = strlen(txt);
2698 if (iMessage == SCI_SEARCHNEXT) {
2699 pos = pdoc->FindText(searchAnchor, pdoc->Length(), txt,
2700 wParam & SCFIND_MATCHCASE,
2701 wParam & SCFIND_WHOLEWORD,
2702 wParam & SCFIND_WORDSTART,
2703 wParam & SCFIND_REGEXP,
2704 &lengthFound);
2705 } else {
2706 pos = pdoc->FindText(searchAnchor, 0, txt,
2707 wParam & SCFIND_MATCHCASE,
2708 wParam & SCFIND_WHOLEWORD,
2709 wParam & SCFIND_WORDSTART,
2710 wParam & SCFIND_REGEXP,
2711 &lengthFound);
2712 }
2713
2714 if (pos != -1) {
2715 SetSelection(pos, pos + lengthFound);
2716 }
2717
2718 return pos;
2719 }
2720
2721 /**
2722 * Search for text in the target range of the document.
2723 * @return The position of the found text, -1 if not found.
2724 */
2725 long Editor::SearchInTarget(const char *text, int length) {
2726 int lengthFound = length;
2727 int pos = pdoc->FindText(targetStart, targetEnd, text,
2728 searchFlags & SCFIND_MATCHCASE,
2729 searchFlags & SCFIND_WHOLEWORD,
2730 searchFlags & SCFIND_WORDSTART,
2731 searchFlags & SCFIND_REGEXP,
2732 &lengthFound);
2733 if (pos != -1) {
2734 targetStart = pos;
2735 targetEnd = pos + lengthFound;
2736 }
2737 return pos;
2738 }
2739
2740 void Editor::GoToLine(int lineNo) {
2741 if (lineNo > pdoc->LinesTotal())
2742 lineNo = pdoc->LinesTotal();
2743 if (lineNo < 0)
2744 lineNo = 0;
2745 SetEmptySelection(pdoc->LineStart(lineNo));
2746 ShowCaretAtCurrentPosition();
2747 EnsureCaretVisible();
2748 }
2749
2750 static bool Close(Point pt1, Point pt2) {
2751 if (abs(pt1.x - pt2.x) > 3)
2752 return false;
2753 if (abs(pt1.y - pt2.y) > 3)
2754 return false;
2755 return true;
2756 }
2757
2758 char *Editor::CopyRange(int start, int end) {
2759 char *text = 0;
2760 if (start < end) {
2761 int len = end - start;
2762 text = new char[len + 1];
2763 if (text) {
2764 for (int i = 0; i < len; i++) {
2765 text[i] = pdoc->CharAt(start + i);
2766 }
2767 text[len] = '\0';
2768 }
2769 }
2770 return text;
2771 }
2772
2773 void Editor::CopySelectionRange(SelectionText *ss) {
2774 char *text = 0;
2775 int size = 0;
2776 if (selType == selRectangle) {
2777 int lineStart = pdoc->LineFromPosition(SelectionStart());
2778 int lineEnd = pdoc->LineFromPosition(SelectionEnd());
2779 int line;
2780 for (line = lineStart; line <= lineEnd; line++) {
2781 size += SelectionEnd(line) - SelectionStart(line) + 1;
2782 if (pdoc->eolMode == SC_EOL_CRLF)
2783 size++;
2784 }
2785 if (size > 0) {
2786 text = new char[size + 1];
2787 if (text) {
2788 int j = 0;
2789 for (line = lineStart; line <= lineEnd; line++) {
2790 for (int i = SelectionStart(line);i < SelectionEnd(line);i++) {
2791 text[j++] = pdoc->CharAt(i);
2792 }
2793 if (pdoc->eolMode != SC_EOL_LF)
2794 text[j++] = '\r';
2795 if (pdoc->eolMode != SC_EOL_CR)
2796 text[j++] = '\n';
2797 }
2798 text[size] = '\0';
2799 }
2800 }
2801 } else {
2802 size = SelectionEnd() - SelectionStart();
2803 text = CopyRange(SelectionStart(), SelectionEnd());
2804 }
2805 ss->Set(text, size, selType == selRectangle);
2806 }
2807
2808 void Editor::SetDragPosition(int newPos) {
2809 if (newPos >= 0) {
2810 newPos = MovePositionOutsideChar(newPos, 1);
2811 posDrop = newPos;
2812 }
2813 if (posDrag != newPos) {
2814 caret.on = true;
2815 SetTicking(true);
2816 InvalidateCaret();
2817 posDrag = newPos;
2818 InvalidateCaret();
2819 }
2820 }
2821
2822 void Editor::DisplayCursor(Window::Cursor c) {
2823 if (cursorMode == SC_CURSORNORMAL)
2824 wMain.SetCursor(c);
2825 else
2826 wMain.SetCursor(static_cast<Window::Cursor>(cursorMode));
2827 }
2828
2829 void Editor::StartDrag() {
2830 // Always handled by subclasses
2831 //SetMouseCapture(true);
2832 //DisplayCursor(Window::cursorArrow);
2833 }
2834
2835 void Editor::DropAt(int position, const char *value, bool moving, bool rectangular) {
2836 //Platform::DebugPrintf("DropAt %d\n", inDragDrop);
2837 if (inDragDrop)
2838 dropWentOutside = false;
2839
2840 int positionWasInSelection = PositionInSelection(position);
2841
2842 bool positionOnEdgeOfSelection =
2843 (position == SelectionStart()) || (position == SelectionEnd());
2844
2845 if ((!inDragDrop) || !(0 == positionWasInSelection) ||
2846 (positionOnEdgeOfSelection && !moving)) {
2847
2848 int selStart = SelectionStart();
2849 int selEnd = SelectionEnd();
2850
2851 pdoc->BeginUndoAction();
2852
2853 int positionAfterDeletion = position;
2854 if (inDragDrop && moving) {
2855 // Remove dragged out text
2856 if (rectangular) {
2857 int lineStart = pdoc->LineFromPosition(SelectionStart());
2858 int lineEnd = pdoc->LineFromPosition(SelectionEnd());
2859 for (int line = lineStart; line <= lineEnd; line++) {
2860 int startPos = SelectionStart(line);
2861 int endPos = SelectionEnd(line);
2862 if (position >= startPos) {
2863 if (position > endPos) {
2864 positionAfterDeletion -= endPos - startPos;
2865 } else {
2866 positionAfterDeletion -= position - startPos;
2867 }
2868 }
2869 }
2870 } else {
2871 if (position > selStart) {
2872 positionAfterDeletion -= selEnd - selStart;
2873 }
2874 }
2875 ClearSelection();
2876 }
2877 position = positionAfterDeletion;
2878
2879 if (rectangular) {
2880 PasteRectangular(position, value, strlen(value));
2881 pdoc->EndUndoAction();
2882 // Should try to select new rectangle but it may not be a rectangle now so just select the drop position
2883 SetSelection(position, position);
2884 } else {
2885 position = MovePositionOutsideChar(position, currentPos - position);
2886 pdoc->InsertString(position, value);
2887 pdoc->EndUndoAction();
2888 SetSelection(position + strlen(value), position);
2889 }
2890 } else if (inDragDrop) {
2891 SetSelection(position, position);
2892 }
2893 }
2894
2895 static int BeforeInOrAfter(int val, int minim, int maxim) {
2896 if (val < minim)
2897 return -1;
2898 else if (val > maxim)
2899 return 1;
2900 else
2901 return 0;
2902 }
2903
2904 int Editor::PositionInSelection(int pos) {
2905 pos = MovePositionOutsideChar(pos, currentPos - pos);
2906 if (selType == selRectangle) {
2907 if (pos < SelectionStart())
2908 return -1;
2909 if (pos > SelectionEnd())
2910 return 1;
2911 int linePos = pdoc->LineFromPosition(pos);
2912 return BeforeInOrAfter(pos, SelectionStart(linePos), SelectionEnd(linePos));
2913 } else {
2914 if (currentPos > anchor) {
2915 return BeforeInOrAfter(pos, anchor, currentPos);
2916 } else if (currentPos < anchor) {
2917 return BeforeInOrAfter(pos, currentPos, anchor);
2918 }
2919 }
2920 return 1;
2921 }
2922
2923 bool Editor::PointInSelection(Point pt) {
2924 // TODO: fix up for rectangular selection
2925 int pos = PositionFromLocation(pt);
2926 if (0 == PositionInSelection(pos)) {
2927 if (pos == SelectionStart()) {
2928 // see if just before selection
2929 Point locStart = LocationFromPosition(pos);
2930 if (pt.x < locStart.x)
2931 return false;
2932 }
2933 if (pos == SelectionEnd()) {
2934 // see if just after selection
2935 Point locEnd = LocationFromPosition(pos);
2936 if (pt.x > locEnd.x)
2937 return false;
2938 }
2939 return true;
2940 }
2941 return false;
2942 }
2943
2944 bool Editor::PointInSelMargin(Point pt) {
2945 // Really means: "Point in a margin"
2946 if (vs.fixedColumnWidth > 0) { // There is a margin
2947 PRectangle rcSelMargin = GetClientRectangle();
2948 rcSelMargin.right = vs.fixedColumnWidth - vs.leftMarginWidth;
2949 return rcSelMargin.Contains(pt);
2950 } else {
2951 return false;
2952 }
2953 }
2954
2955 void Editor::LineSelection(int lineCurrent_, int lineAnchor_) {
2956 if (lineAnchor_ < lineCurrent_) {
2957 SetSelection(pdoc->LineStart(lineCurrent_ + 1),
2958 pdoc->LineStart(lineAnchor_));
2959 } else if (lineAnchor_ > lineCurrent_) {
2960 SetSelection(pdoc->LineStart(lineCurrent_),
2961 pdoc->LineStart(lineAnchor_ + 1));
2962 } else { // Same line, select it
2963 SetSelection(pdoc->LineStart(lineAnchor_ + 1),
2964 pdoc->LineStart(lineAnchor_));
2965 }
2966 }
2967
2968 void Editor::DwellEnd(bool mouseMoved) {
2969 if (mouseMoved)
2970 ticksToDwell = dwellDelay;
2971 else
2972 ticksToDwell = SC_TIME_FOREVER;
2973 if (dwelling && (dwellDelay < SC_TIME_FOREVER)) {
2974 dwelling = false;
2975 NotifyDwelling(ptMouseLast, dwelling);
2976 }
2977 }
2978
2979 void Editor::ButtonDown(Point pt, unsigned int curTime, bool shift, bool ctrl, bool alt) {
2980 //Platform::DebugPrintf("Scintilla:ButtonDown %d %d = %d alt=%d\n", curTime, lastClickTime, curTime - lastClickTime, alt);
2981 ptMouseLast = pt;
2982 int newPos = PositionFromLocation(pt);
2983 newPos = MovePositionOutsideChar(newPos, currentPos - newPos);
2984 inDragDrop = false;
2985
2986 bool processed = NotifyMarginClick(pt, shift, ctrl, alt);
2987 if (processed)
2988 return;
2989
2990 bool inSelMargin = PointInSelMargin(pt);
2991 if (shift & !inSelMargin) {
2992 SetSelection(newPos);
2993 }
2994 if (((curTime - lastClickTime) < Platform::DoubleClickTime()) && Close(pt, lastClick)) {
2995 //Platform::DebugPrintf("Double click %d %d = %d\n", curTime, lastClickTime, curTime - lastClickTime);
2996 SetMouseCapture(true);
2997 SetEmptySelection(newPos);
2998 bool doubleClick = false;
2999 // Stop mouse button bounce changing selection type
3000 if (curTime != lastClickTime) {
3001 if (selectionType == selChar) {
3002 selectionType = selWord;
3003 doubleClick = true;
3004 } else if (selectionType == selWord) {
3005 selectionType = selLine;
3006 } else {
3007 selectionType = selChar;
3008 originalAnchorPos = currentPos;
3009 }
3010 }
3011
3012 if (selectionType == selWord) {
3013 if (currentPos >= originalAnchorPos) { // Moved forward
3014 SetSelection(pdoc->ExtendWordSelect(currentPos, 1),
3015 pdoc->ExtendWordSelect(originalAnchorPos, -1));
3016 } else { // Moved backward
3017 SetSelection(pdoc->ExtendWordSelect(currentPos, -1),
3018 pdoc->ExtendWordSelect(originalAnchorPos, 1));
3019 }
3020 } else if (selectionType == selLine) {
3021 lineAnchor = LineFromLocation(pt);
3022 SetSelection(pdoc->LineStart(lineAnchor + 1), pdoc->LineStart(lineAnchor));
3023 //Platform::DebugPrintf("Triple click: %d - %d\n", anchor, currentPos);
3024 }
3025 else {
3026 SetEmptySelection(currentPos);
3027 }
3028 //Platform::DebugPrintf("Double click: %d - %d\n", anchor, currentPos);
3029 if (doubleClick)
3030 NotifyDoubleClick(pt, shift);
3031 } else { // Single click
3032 if (inSelMargin) {
3033 selType = selStream;
3034 if (ctrl) {
3035 SelectAll();
3036 lastClickTime = curTime;
3037 return;
3038 }
3039 if (!shift) {
3040 lineAnchor = LineFromLocation(pt);
3041 // Single click in margin: select whole line
3042 LineSelection(lineAnchor, lineAnchor);
3043 SetSelection(pdoc->LineStart(lineAnchor + 1),
3044 pdoc->LineStart(lineAnchor));
3045 } else {
3046 // Single shift+click in margin: select from line anchor to clicked line
3047 if (anchor > currentPos)
3048 lineAnchor = pdoc->LineFromPosition(anchor - 1);
3049 else
3050 lineAnchor = pdoc->LineFromPosition(anchor);
3051 int lineStart = LineFromLocation(pt);
3052 LineSelection(lineStart, lineAnchor);
3053 //lineAnchor = lineStart; // Keep the same anchor for ButtonMove
3054 }
3055
3056 SetDragPosition(invalidPosition);
3057 SetMouseCapture(true);
3058 selectionType = selLine;
3059 } else {
3060 if (!shift) {
3061 inDragDrop = PointInSelection(pt);
3062 }
3063 if (inDragDrop) {
3064 SetMouseCapture(false);
3065 SetDragPosition(newPos);
3066 CopySelectionRange(&drag);
3067 StartDrag();
3068 } else {
3069 xStartSelect = pt.x - vs.fixedColumnWidth + xOffset;
3070 xEndSelect = pt.x - vs.fixedColumnWidth + xOffset;
3071 SetDragPosition(invalidPosition);
3072 SetMouseCapture(true);
3073 if (!shift)
3074 SetEmptySelection(newPos);
3075 selType = alt ? selRectangle : selStream;
3076 selectionType = selChar;
3077 originalAnchorPos = currentPos;
3078 }
3079 }
3080 }
3081 lastClickTime = curTime;
3082 lastXChosen = pt.x;
3083 ShowCaretAtCurrentPosition();
3084 }
3085
3086 void Editor::ButtonMove(Point pt) {
3087 if ((ptMouseLast.x != pt.x) || (ptMouseLast.y != pt.y)) {
3088 DwellEnd(true);
3089 }
3090 ptMouseLast = pt;
3091 //Platform::DebugPrintf("Move %d %d\n", pt.x, pt.y);
3092 if (HaveMouseCapture()) {
3093
3094 // Slow down autoscrolling/selection
3095 autoScrollTimer.ticksToWait -= timer.tickSize;
3096 if (autoScrollTimer.ticksToWait > 0)
3097 return;
3098 autoScrollTimer.ticksToWait = autoScrollDelay;
3099
3100 // Adjust selection
3101 xEndSelect = pt.x - vs.fixedColumnWidth + xOffset;
3102 int movePos = PositionFromLocation(pt);
3103 movePos = MovePositionOutsideChar(movePos, currentPos - movePos);
3104 if (posDrag >= 0) {
3105 SetDragPosition(movePos);
3106 } else {
3107 if (selectionType == selChar) {
3108 SetSelection(movePos);
3109 } else if (selectionType == selWord) {
3110 // Continue selecting by word
3111 if (currentPos > originalAnchorPos) { // Moved forward
3112 SetSelection(pdoc->ExtendWordSelect(movePos, 1),
3113 pdoc->ExtendWordSelect(originalAnchorPos, -1));
3114 } else { // Moved backward
3115 SetSelection(pdoc->ExtendWordSelect(movePos, -1),
3116 pdoc->ExtendWordSelect(originalAnchorPos, 1));
3117 }
3118 } else {
3119 // Continue selecting by line
3120 int lineMove = LineFromLocation(pt);
3121 LineSelection(lineMove, lineAnchor);
3122 }
3123 }
3124
3125 // Autoscroll
3126 PRectangle rcClient = GetClientRectangle();
3127 if (pt.y > rcClient.bottom) {
3128 int lineMove = cs.DisplayFromDoc(LineFromLocation(pt));
3129 ScrollTo(lineMove - LinesOnScreen() + 5);
3130 Redraw();
3131 } else if (pt.y < rcClient.top) {
3132 int lineMove = cs.DisplayFromDoc(LineFromLocation(pt));
3133 ScrollTo(lineMove - 5);
3134 Redraw();
3135 }
3136 EnsureCaretVisible(false, false, true);
3137
3138 } else {
3139 if (vs.fixedColumnWidth > 0) { // There is a margin
3140 if (PointInSelMargin(pt)) {
3141 DisplayCursor(Window::cursorReverseArrow);
3142 return; // No need to test for selection
3143 }
3144 }
3145 // Display regular (drag) cursor over selection
3146 if (PointInSelection(pt))
3147 DisplayCursor(Window::cursorArrow);
3148 else
3149 DisplayCursor(Window::cursorText);
3150 }
3151
3152 }
3153
3154 void Editor::ButtonUp(Point pt, unsigned int curTime, bool ctrl) {
3155 //Platform::DebugPrintf("ButtonUp %d\n", HaveMouseCapture());
3156 if (HaveMouseCapture()) {
3157 if (PointInSelMargin(pt)) {
3158 DisplayCursor(Window::cursorReverseArrow);
3159 } else {
3160 DisplayCursor(Window::cursorText);
3161 }
3162 xEndSelect = pt.x - vs.fixedColumnWidth + xOffset;
3163 ptMouseLast = pt;
3164 SetMouseCapture(false);
3165 int newPos = PositionFromLocation(pt);
3166 newPos = MovePositionOutsideChar(newPos, currentPos - newPos);
3167 if (inDragDrop) {
3168 int selStart = SelectionStart();
3169 int selEnd = SelectionEnd();
3170 if (selStart < selEnd) {
3171 if (drag.len) {
3172 if (ctrl) {
3173 pdoc->InsertString(newPos, drag.s, drag.len);
3174 SetSelection(newPos, newPos + drag.len);
3175 } else if (newPos < selStart) {
3176 pdoc->DeleteChars(selStart, drag.len);
3177 pdoc->InsertString(newPos, drag.s, drag.len);
3178 SetSelection(newPos, newPos + drag.len);
3179 } else if (newPos > selEnd) {
3180 pdoc->DeleteChars(selStart, drag.len);
3181 newPos -= drag.len;
3182 pdoc->InsertString(newPos, drag.s, drag.len);
3183 SetSelection(newPos, newPos + drag.len);
3184 } else {
3185 SetEmptySelection(newPos);
3186 }
3187 drag.Set(0, 0);
3188 }
3189 selectionType = selChar;
3190 }
3191 } else {
3192 if (selectionType == selChar) {
3193 SetSelection(newPos);
3194 }
3195 }
3196 lastClickTime = curTime;
3197 lastClick = pt;
3198 lastXChosen = pt.x;
3199 inDragDrop = false;
3200 EnsureCaretVisible(false);
3201 }
3202 }
3203
3204 // Called frequently to perform background UI including
3205 // caret blinking and automatic scrolling.
3206 void Editor::Tick() {
3207 if (HaveMouseCapture()) {
3208 // Auto scroll
3209 ButtonMove(ptMouseLast);
3210 }
3211 if (caret.period > 0) {
3212 timer.ticksToWait -= timer.tickSize;
3213 if (timer.ticksToWait <= 0) {
3214 caret.on = !caret.on;
3215 timer.ticksToWait = caret.period;
3216 InvalidateCaret();
3217 }
3218 }
3219 if ((dwellDelay < SC_TIME_FOREVER) &&
3220 (ticksToDwell > 0) &&
3221 (!HaveMouseCapture())) {
3222 ticksToDwell -= timer.tickSize;
3223 if (ticksToDwell <= 0) {
3224 dwelling = true;
3225 NotifyDwelling(ptMouseLast, dwelling);
3226 }
3227 }
3228 }
3229
3230 void Editor::SetFocusState(bool focusState) {
3231 hasFocus = focusState;
3232 NotifyFocus(hasFocus);
3233 if (hasFocus) {
3234 ShowCaretAtCurrentPosition();
3235 InvalidateCaret();
3236 } else {
3237 DropCaret();
3238 }
3239 }
3240
3241 static bool IsIn(int a, int minimum, int maximum) {
3242 return (a >= minimum) && (a <= maximum);
3243 }
3244
3245 static bool IsOverlap(int mina, int maxa, int minb, int maxb) {
3246 return
3247 IsIn(mina, minb, maxb) ||
3248 IsIn(maxa, minb, maxb) ||
3249 IsIn(minb, mina, maxa) ||
3250 IsIn(maxb, mina, maxa);
3251 }
3252
3253 void Editor::CheckForChangeOutsidePaint(Range r) {
3254 if (paintState == painting && !paintingAllText) {
3255 //Platform::DebugPrintf("Checking range in paint %d-%d\n", r.start, r.end);
3256 if (!r.Valid())
3257 return;
3258
3259 PRectangle rcText = GetTextRectangle();
3260 // Determine number of lines displayed including a possible partially displayed last line
3261 int linesDisplayed = (rcText.bottom - rcText.top - 1) / vs.lineHeight + 1;
3262 int bottomLine = topLine + linesDisplayed - 1;
3263
3264 int lineRangeStart = cs.DisplayFromDoc(pdoc->LineFromPosition(r.start));
3265 int lineRangeEnd = cs.DisplayFromDoc(pdoc->LineFromPosition(r.end));
3266 if (!IsOverlap(topLine, bottomLine, lineRangeStart, lineRangeEnd)) {
3267 //Platform::DebugPrintf("No overlap (%d-%d) with window(%d-%d)\n",
3268 // lineRangeStart, lineRangeEnd, topLine, bottomLine);
3269 return;
3270 }
3271
3272 // Assert rcPaint contained within or equal to rcText
3273 if (rcPaint.top > rcText.top) {
3274 // does range intersect rcText.top .. rcPaint.top
3275 int paintTopLine = ((rcPaint.top - rcText.top - 1) / vs.lineHeight) + topLine;
3276 // paintTopLine is the top line of the paint rectangle or the line just above if that line is completely inside the paint rectangle
3277 if (IsOverlap(topLine, paintTopLine, lineRangeStart, lineRangeEnd)) {
3278 //Platform::DebugPrintf("Change (%d-%d) in top npv(%d-%d)\n",
3279 // lineRangeStart, lineRangeEnd, topLine, paintTopLine);
3280 paintState = paintAbandoned;
3281 return;
3282 }
3283 }
3284 if (rcPaint.bottom < rcText.bottom) {
3285 // does range intersect rcPaint.bottom .. rcText.bottom
3286 int paintBottomLine = ((rcPaint.bottom - rcText.top - 1) / vs.lineHeight + 1) + topLine;
3287 // paintTopLine is the bottom line of the paint rectangle or the line just below if that line is completely inside the paint rectangle
3288 if (IsOverlap(paintBottomLine, bottomLine, lineRangeStart, lineRangeEnd)) {
3289 //Platform::DebugPrintf("Change (%d-%d) in bottom npv(%d-%d)\n",
3290 // lineRangeStart, lineRangeEnd, paintBottomLine, bottomLine);
3291 paintState = paintAbandoned;
3292 return;
3293 }
3294 }
3295 }
3296 }
3297
3298 char BraceOpposite(char ch) {
3299 switch (ch) {
3300 case '(':
3301 return ')';
3302 case ')':
3303 return '(';
3304 case '[':
3305 return ']';
3306 case ']':
3307 return '[';
3308 case '{':
3309 return '}';
3310 case '}':
3311 return '{';
3312 case '<':
3313 return '>';
3314 case '>':
3315 return '<';
3316 default:
3317 return '\0';
3318 }
3319 }
3320
3321 // TODO: should be able to extend styled region to find matching brace
3322 // TODO: may need to make DBCS safe
3323 // so should be moved into Document
3324 int Editor::BraceMatch(int position, int /*maxReStyle*/) {
3325 char chBrace = pdoc->CharAt(position);
3326 char chSeek = BraceOpposite(chBrace);
3327 if (chSeek == '\0')
3328 return - 1;
3329 char styBrace = static_cast<char>(
3330 pdoc->StyleAt(position) & pdoc->stylingBitsMask);
3331 int direction = -1;
3332 if (chBrace == '(' || chBrace == '[' || chBrace == '{' || chBrace == '<')
3333 direction = 1;
3334 int depth = 1;
3335 position = position + direction;
3336 while ((position >= 0) && (position < pdoc->Length())) {
3337 char chAtPos = pdoc->CharAt(position);
3338 char styAtPos = static_cast<char>(pdoc->StyleAt(position) & pdoc->stylingBitsMask);
3339 if ((position > pdoc->GetEndStyled()) || (styAtPos == styBrace)) {
3340 if (chAtPos == chBrace)
3341 depth++;
3342 if (chAtPos == chSeek)
3343 depth--;
3344 if (depth == 0)
3345 return position;
3346 }
3347 position = position + direction;
3348 }
3349 return - 1;
3350 }
3351
3352 void Editor::SetBraceHighlight(Position pos0, Position pos1, int matchStyle) {
3353 if ((pos0 != braces[0]) || (pos1 != braces[1]) || (matchStyle != bracesMatchStyle)) {
3354 if ((braces[0] != pos0) || (matchStyle != bracesMatchStyle)) {
3355 CheckForChangeOutsidePaint(Range(braces[0]));
3356 CheckForChangeOutsidePaint(Range(pos0));
3357 braces[0] = pos0;
3358 }
3359 if ((braces[1] != pos1) || (matchStyle != bracesMatchStyle)) {
3360 CheckForChangeOutsidePaint(Range(braces[1]));
3361 CheckForChangeOutsidePaint(Range(pos1));
3362 braces[1] = pos1;
3363 }
3364 bracesMatchStyle = matchStyle;
3365 if (paintState == notPainting) {
3366 Redraw();
3367 }
3368 }
3369 }
3370
3371 void Editor::SetDocPointer(Document *document) {
3372 //Platform::DebugPrintf("** %x setdoc to %x\n", pdoc, document);
3373 pdoc->RemoveWatcher(this, 0);
3374 pdoc->Release();
3375 if (document == NULL) {
3376 pdoc = new Document();
3377 } else {
3378 pdoc = document;
3379 }
3380 pdoc->AddRef();
3381 // Reset the contraction state to fully shown.
3382 cs.Clear();
3383 cs.InsertLines(0, pdoc->LinesTotal() - 1);
3384
3385 pdoc->AddWatcher(this, 0);
3386 Redraw();
3387 SetScrollBars();
3388 }
3389
3390 // Recursively expand a fold, making lines visible except where they have an unexpanded parent
3391 void Editor::Expand(int &line, bool doExpand) {
3392 int lineMaxSubord = pdoc->GetLastChild(line);
3393 line++;
3394 while (line <= lineMaxSubord) {
3395 if (doExpand)
3396 cs.SetVisible(line, line, true);
3397 int level = pdoc->GetLevel(line);
3398 if (level & SC_FOLDLEVELHEADERFLAG) {
3399 if (doExpand && cs.GetExpanded(line)) {
3400 Expand(line, true);
3401 } else {
3402 Expand(line, false);
3403 }
3404 } else {
3405 line++;
3406 }
3407 }
3408 }
3409
3410 void Editor::ToggleContraction(int line) {
3411 if (pdoc->GetLevel(line) & SC_FOLDLEVELHEADERFLAG) {
3412 if (cs.GetExpanded(line)) {
3413 int lineMaxSubord = pdoc->GetLastChild(line);
3414 cs.SetExpanded(line, 0);
3415 if (lineMaxSubord > line) {
3416 cs.SetVisible(line + 1, lineMaxSubord, false);
3417 SetScrollBars();
3418 Redraw();
3419 }
3420 } else {
3421 cs.SetExpanded(line, 1);
3422 Expand(line, true);
3423 SetScrollBars();
3424 Redraw();
3425 }
3426 }
3427 }
3428
3429 // Recurse up from this line to find any folds that prevent this line from being visible
3430 // and unfold them all.
3431 void Editor::EnsureLineVisible(int lineDoc, bool enforcePolicy) {
3432 if (!cs.GetVisible(lineDoc)) {
3433 int lineParent = pdoc->GetFoldParent(lineDoc);
3434 if (lineParent >= 0) {
3435 if (lineDoc != lineParent)
3436 EnsureLineVisible(lineParent, enforcePolicy);
3437 if (!cs.GetExpanded(lineParent)) {
3438 cs.SetExpanded(lineParent, 1);
3439 Expand(lineParent, true);
3440 }
3441 }
3442 SetScrollBars();
3443 Redraw();
3444 }
3445 if (enforcePolicy) {
3446 int lineDisplay = cs.DisplayFromDoc(lineDoc);
3447 if (visiblePolicy & VISIBLE_SLOP) {
3448 if ((topLine > lineDisplay) || ((visiblePolicy & VISIBLE_STRICT) && (topLine + visibleSlop > lineDisplay))) {
3449 SetTopLine(Platform::Clamp(lineDisplay - visibleSlop, 0, MaxScrollPos()));
3450 SetVerticalScrollPos();
3451 Redraw();
3452 } else if ((lineDisplay > topLine + LinesOnScreen() - 1) ||
3453 ((visiblePolicy & VISIBLE_STRICT) && (lineDisplay > topLine + LinesOnScreen() - 1 - visibleSlop))) {
3454 SetTopLine(Platform::Clamp(lineDisplay - LinesOnScreen() + 1 + visibleSlop, 0, MaxScrollPos()));
3455 SetVerticalScrollPos();
3456 Redraw();
3457 }
3458 } else {
3459 if ((topLine > lineDisplay) || (lineDisplay > topLine + LinesOnScreen() - 1) || (visiblePolicy & VISIBLE_STRICT)) {
3460 SetTopLine(Platform::Clamp(lineDisplay - LinesOnScreen() / 2 + 1, 0, MaxScrollPos()));
3461 SetVerticalScrollPos();
3462 Redraw();
3463 }
3464 }
3465 }
3466 }
3467
3468 int Editor::ReplaceTarget(bool replacePatterns, const char *text, int length) {
3469 pdoc->BeginUndoAction();
3470 if (length == -1)
3471 length = strlen(text);
3472 if (replacePatterns) {
3473 text = pdoc->SubstituteByPosition(text, &length);
3474 if (!text)
3475 return 0;
3476 }
3477 if (targetStart != targetEnd)
3478 pdoc->DeleteChars(targetStart, targetEnd - targetStart);
3479 targetEnd = targetStart;
3480 pdoc->InsertString(targetStart, text, length);
3481 targetEnd = targetStart + length;
3482 pdoc->EndUndoAction();
3483 return length;
3484 }
3485
3486 static bool ValidMargin(unsigned long wParam) {
3487 return wParam < ViewStyle::margins;
3488 }
3489
3490 sptr_t Editor::WndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
3491 //Platform::DebugPrintf("S start wnd proc %d %d %d\n",iMessage, wParam, lParam);
3492
3493 // Optional macro recording hook
3494 if (recordingMacro)
3495 NotifyMacroRecord(iMessage, wParam, lParam);
3496
3497 switch (iMessage) {
3498
3499 case SCI_GETTEXT:
3500 {
3501 if (lParam == 0)
3502 return 0;
3503 char *ptr = reinterpret_cast<char *>(lParam);
3504 unsigned int iChar = 0;
3505 for (; iChar < wParam - 1; iChar++)
3506 ptr[iChar] = pdoc->CharAt(iChar);
3507 ptr[iChar] = '\0';
3508 return iChar;
3509 }
3510
3511 case SCI_SETTEXT:
3512 {
3513 if (lParam == 0)
3514 return FALSE;
3515 pdoc->DeleteChars(0, pdoc->Length());
3516 SetEmptySelection(0);
3517 pdoc->InsertString(0, reinterpret_cast<char *>(lParam));
3518 return TRUE;
3519 }
3520
3521 case SCI_GETTEXTLENGTH:
3522 return pdoc->Length();
3523
3524 case SCI_CUT:
3525 Cut();
3526 SetLastXChosen();
3527 break;
3528
3529 case SCI_COPY:
3530 Copy();
3531 break;
3532
3533 case SCI_PASTE:
3534 Paste();
3535 SetLastXChosen();
3536 EnsureCaretVisible();
3537 break;
3538
3539 case SCI_CLEAR:
3540 Clear();
3541 SetLastXChosen();
3542 break;
3543
3544 case SCI_UNDO:
3545 Undo();
3546 SetLastXChosen();
3547 break;
3548
3549 case SCI_CANUNDO:
3550 return pdoc->CanUndo() ? TRUE : FALSE;
3551
3552 case SCI_EMPTYUNDOBUFFER:
3553 pdoc->DeleteUndoHistory();
3554 return 0;
3555
3556 case SCI_GETFIRSTVISIBLELINE:
3557 return topLine;
3558
3559 case SCI_GETLINE: { // Risk of overwriting the end of the buffer
3560 if (lParam == 0) {
3561 return 0;
3562 }
3563 int lineStart = pdoc->LineStart(wParam);
3564 int lineEnd = pdoc->LineStart(wParam + 1);
3565 char *ptr = reinterpret_cast<char *>(lParam);
3566 int iPlace = 0;
3567 for (int iChar = lineStart; iChar < lineEnd; iChar++) {
3568 ptr[iPlace++] = pdoc->CharAt(iChar);
3569 }
3570 return iPlace;
3571 }
3572
3573 case SCI_GETLINECOUNT:
3574 if (pdoc->LinesTotal() == 0)
3575 return 1;
3576 else
3577 return pdoc->LinesTotal();
3578
3579 case SCI_GETMODIFY:
3580 return !pdoc->IsSavePoint();
3581
3582 case SCI_SETSEL: {
3583 int nStart = static_cast<int>(wParam);
3584 int nEnd = static_cast<int>(lParam);
3585 if (nEnd < 0)
3586 nEnd = pdoc->Length();
3587 if (nStart < 0)
3588 nStart = nEnd; // Remove selection
3589 selType = selStream;
3590 SetSelection(nEnd, nStart);
3591 EnsureCaretVisible();
3592 }
3593 break;
3594
3595 case SCI_GETSELTEXT: {
3596 if (lParam == 0)
3597 return 0;
3598 SelectionText selectedText;
3599 CopySelectionRange(&selectedText);
3600 char *ptr = reinterpret_cast<char *>(lParam);
3601 int iChar = 0;
3602 if (selectedText.len) {
3603 for (; iChar < selectedText.len; iChar++)
3604 ptr[iChar] = selectedText.s[iChar];
3605 ptr[iChar] = '\0';
3606 } else {
3607 ptr[0] = '\0';
3608 }
3609 return iChar;
3610 }
3611
3612 case SCI_LINEFROMPOSITION:
3613 if (static_cast<int>(wParam) < 0)
3614 return 0;
3615 return pdoc->LineFromPosition(wParam);
3616
3617 case SCI_POSITIONFROMLINE:
3618 if (static_cast<int>(wParam) < 0)
3619 wParam = pdoc->LineFromPosition(SelectionStart());
3620 if (wParam == 0)
3621 return 0; // Even if there is no text, there is a first line that starts at 0
3622 if (static_cast<int>(wParam) > pdoc->LinesTotal())
3623 return -1;
3624 //if (wParam > pdoc->LineFromPosition(pdoc->Length())) // Useful test, anyway...
3625 // return -1;
3626 return pdoc->LineStart(wParam);
3627
3628 // Replacement of the old Scintilla interpretation of EM_LINELENGTH
3629 case SCI_LINELENGTH:
3630 if ((static_cast<int>(wParam) < 0) ||
3631 (static_cast<int>(wParam) > pdoc->LineFromPosition(pdoc->Length())))
3632 return 0;
3633 return pdoc->LineStart(wParam + 1) - pdoc->LineStart(wParam);
3634
3635 case SCI_REPLACESEL: {
3636 if (lParam == 0)
3637 return 0;
3638 pdoc->BeginUndoAction();
3639 ClearSelection();
3640 char *replacement = reinterpret_cast<char *>(lParam);
3641 pdoc->InsertString(currentPos, replacement);
3642 pdoc->EndUndoAction();
3643 SetEmptySelection(currentPos + strlen(replacement));
3644 EnsureCaretVisible();
3645 }
3646 break;
3647
3648 case SCI_SETTARGETSTART:
3649 targetStart = wParam;
3650 break;
3651
3652 case SCI_GETTARGETSTART:
3653 return targetStart;
3654
3655 case SCI_SETTARGETEND:
3656 targetEnd = wParam;
3657 break;
3658
3659 case SCI_GETTARGETEND:
3660 return targetEnd;
3661
3662 case SCI_REPLACETARGET:
3663 PLATFORM_ASSERT(lParam);
3664 return ReplaceTarget(false, reinterpret_cast<char *>(lParam), wParam);
3665
3666 case SCI_REPLACETARGETRE:
3667 PLATFORM_ASSERT(lParam);
3668 return ReplaceTarget(true, reinterpret_cast<char *>(lParam), wParam);
3669
3670 case SCI_SEARCHINTARGET:
3671 PLATFORM_ASSERT(lParam);
3672 return SearchInTarget(reinterpret_cast<char *>(lParam), wParam);
3673
3674 case SCI_SETSEARCHFLAGS:
3675 searchFlags = wParam;
3676 break;
3677
3678 case SCI_GETSEARCHFLAGS:
3679 return searchFlags;
3680
3681 case SCI_LINESCROLL:
3682 ScrollTo(topLine + lParam);
3683 HorizontalScrollTo(xOffset + wParam * vs.spaceWidth);
3684 return TRUE;
3685
3686 case SCI_SCROLLCARET:
3687 EnsureCaretVisible();
3688 break;
3689
3690 case SCI_SETREADONLY:
3691 pdoc->SetReadOnly(wParam);
3692 return TRUE;
3693
3694 case SCI_GETREADONLY:
3695 return pdoc->IsReadOnly();
3696
3697 case SCI_CANPASTE:
3698 return CanPaste();
3699
3700 case SCI_POINTXFROMPOSITION:
3701 if (lParam < 0) {
3702 return 0;
3703 } else {
3704 Point pt = LocationFromPosition(lParam);
3705 return pt.x;
3706 }
3707
3708 case SCI_POINTYFROMPOSITION:
3709 if (lParam < 0) {
3710 return 0;
3711 } else {
3712 Point pt = LocationFromPosition(lParam);
3713 return pt.y;
3714 }
3715
3716 case SCI_FINDTEXT:
3717 return FindText(wParam, lParam);
3718
3719 case SCI_GETTEXTRANGE: {
3720 if (lParam == 0)
3721 return 0;
3722 TextRange *tr = reinterpret_cast<TextRange *>(lParam);
3723 int cpMax = tr->chrg.cpMax;
3724 if (cpMax == -1)
3725 cpMax = pdoc->Length();
3726 int len = cpMax - tr->chrg.cpMin; // No -1 as cpMin and cpMax are referring to inter character positions
3727 pdoc->GetCharRange(tr->lpstrText, tr->chrg.cpMin, len);
3728 // Spec says copied text is terminated with a NUL
3729 tr->lpstrText[len] = '\0';
3730 return len; // Not including NUL
3731 }
3732
3733 case SCI_HIDESELECTION:
3734 hideSelection = wParam;
3735 Redraw();
3736 break;
3737
3738 case SCI_FORMATRANGE:
3739 return FormatRange(wParam, reinterpret_cast<RangeToFormat *>(lParam));
3740
3741 case SCI_GETMARGINLEFT:
3742 return vs.leftMarginWidth;
3743
3744 case SCI_GETMARGINRIGHT:
3745 return vs.rightMarginWidth;
3746
3747 case SCI_SETMARGINLEFT:
3748 vs.leftMarginWidth = lParam;
3749 InvalidateStyleRedraw();
3750 break;
3751
3752 case SCI_SETMARGINRIGHT:
3753 vs.rightMarginWidth = lParam;
3754 InvalidateStyleRedraw();
3755 break;
3756
3757 // Control specific mesages
3758
3759 case SCI_ADDTEXT: {
3760 if (lParam == 0)
3761 return 0;
3762 pdoc->InsertString(CurrentPosition(), reinterpret_cast<char *>(lParam), wParam);
3763 SetEmptySelection(currentPos + wParam);
3764 return 0;
3765 }
3766
3767 case SCI_ADDSTYLEDTEXT: {
3768 if (lParam == 0)
3769 return 0;
3770 pdoc->InsertStyledString(CurrentPosition() * 2, reinterpret_cast<char *>(lParam), wParam);
3771 SetEmptySelection(currentPos + wParam / 2);
3772 return 0;
3773 }
3774
3775 case SCI_INSERTTEXT: {
3776 if (lParam == 0)
3777 return 0;
3778 int insertPos = wParam;
3779 if (static_cast<short>(wParam) == -1)
3780 insertPos = CurrentPosition();
3781 int newCurrent = CurrentPosition();
3782 int newAnchor = anchor;
3783 char *sz = reinterpret_cast<char *>(lParam);
3784 pdoc->InsertString(insertPos, sz);
3785 if (newCurrent > insertPos)
3786 newCurrent += strlen(sz);
3787 if (newAnchor > insertPos)
3788 newAnchor += strlen(sz);
3789 SetEmptySelection(newCurrent);
3790 return 0;
3791 }
3792
3793 case SCI_CLEARALL:
3794 ClearAll();
3795 return 0;
3796
3797 case SCI_CLEARDOCUMENTSTYLE:
3798 ClearDocumentStyle();
3799 return 0;
3800
3801 case SCI_SETUNDOCOLLECTION:
3802 pdoc->SetUndoCollection(wParam);
3803 return 0;
3804
3805 case SCI_GETUNDOCOLLECTION:
3806 return pdoc->IsCollectingUndo();
3807
3808 case SCI_BEGINUNDOACTION:
3809 pdoc->BeginUndoAction();
3810 return 0;
3811
3812 case SCI_ENDUNDOACTION:
3813 pdoc->EndUndoAction();
3814 return 0;
3815
3816 case SCI_GETCARETPERIOD:
3817 return caret.period;
3818
3819 case SCI_SETCARETPERIOD:
3820 caret.period = wParam;
3821 break;
3822
3823 case SCI_SETWORDCHARS: {
3824 if (lParam == 0)
3825 return 0;
3826 pdoc->SetWordChars(reinterpret_cast<unsigned char *>(lParam));
3827 }
3828 break;
3829
3830 case SCI_GETLENGTH:
3831 return pdoc->Length();
3832
3833 case SCI_GETCHARAT:
3834 return pdoc->CharAt(wParam);
3835
3836 case SCI_SETCURRENTPOS:
3837 SetSelection(wParam, anchor);
3838 break;
3839
3840 case SCI_GETCURRENTPOS:
3841 return currentPos;
3842
3843 case SCI_SETANCHOR:
3844 SetSelection(currentPos, wParam);
3845 break;
3846
3847 case SCI_GETANCHOR:
3848 return anchor;
3849
3850 case SCI_SETSELECTIONSTART:
3851 SetSelection(Platform::Maximum(currentPos, wParam), wParam);
3852 break;
3853
3854 case SCI_GETSELECTIONSTART:
3855 return Platform::Minimum(anchor, currentPos);
3856
3857 case SCI_SETSELECTIONEND:
3858 SetSelection(wParam, Platform::Minimum(anchor, wParam));
3859 break;
3860
3861 case SCI_GETSELECTIONEND:
3862 return Platform::Maximum(anchor, currentPos);
3863
3864 case SCI_SETPRINTMAGNIFICATION:
3865 printMagnification = wParam;
3866 break;
3867
3868 case SCI_GETPRINTMAGNIFICATION:
3869 return printMagnification;
3870
3871 case SCI_SETPRINTCOLOURMODE:
3872 printColourMode = wParam;
3873 break;
3874
3875 case SCI_GETPRINTCOLOURMODE:
3876 return printColourMode;
3877
3878 case SCI_GETSTYLEAT:
3879 if (static_cast<short>(wParam) >= pdoc->Length())
3880 return 0;
3881 else
3882 return pdoc->StyleAt(wParam);
3883
3884 case SCI_REDO:
3885 Redo();
3886 break;
3887
3888 case SCI_SELECTALL:
3889 SelectAll();
3890 break;
3891
3892 case SCI_SETSAVEPOINT:
3893 pdoc->SetSavePoint();
3894 break;
3895
3896 case SCI_GETSTYLEDTEXT: {
3897 if (lParam == 0)
3898 return 0;
3899 TextRange *tr = reinterpret_cast<TextRange *>(lParam);
3900 int iPlace = 0;
3901 for (int iChar = tr->chrg.cpMin; iChar < tr->chrg.cpMax; iChar++) {
3902 tr->lpstrText[iPlace++] = pdoc->CharAt(iChar);
3903 tr->lpstrText[iPlace++] = pdoc->StyleAt(iChar);
3904 }
3905 tr->lpstrText[iPlace] = '\0';
3906 tr->lpstrText[iPlace + 1] = '\0';
3907 return iPlace;
3908 }
3909
3910 case SCI_CANREDO:
3911 return pdoc->CanRedo() ? TRUE : FALSE;
3912
3913 case SCI_MARKERLINEFROMHANDLE:
3914 return pdoc->LineFromHandle(wParam);
3915
3916 case SCI_MARKERDELETEHANDLE:
3917 pdoc->DeleteMarkFromHandle(wParam);
3918 break;
3919
3920 case SCI_GETVIEWWS:
3921 return vs.viewWhitespace;
3922
3923 case SCI_SETVIEWWS:
3924 vs.viewWhitespace = static_cast<WhiteSpaceVisibility>(wParam);
3925 Redraw();
3926 break;
3927
3928 case SCI_POSITIONFROMPOINT:
3929 return PositionFromLocation(Point(wParam, lParam));
3930
3931 case SCI_POSITIONFROMPOINTCLOSE:
3932 return PositionFromLocationClose(Point(wParam, lParam));
3933
3934 case SCI_GOTOLINE:
3935 GoToLine(wParam);
3936 break;
3937
3938 case SCI_GOTOPOS:
3939 SetEmptySelection(wParam);
3940 EnsureCaretVisible();
3941 Redraw();
3942 break;
3943
3944 case SCI_GETCURLINE: {
3945 if (lParam == 0) {
3946 return 0;
3947 }
3948 int lineCurrentPos = pdoc->LineFromPosition(currentPos);
3949 int lineStart = pdoc->LineStart(lineCurrentPos);
3950 unsigned int lineEnd = pdoc->LineStart(lineCurrentPos + 1);
3951 char *ptr = reinterpret_cast<char *>(lParam);
3952 unsigned int iPlace = 0;
3953 for (unsigned int iChar = lineStart; iChar < lineEnd && iPlace < wParam - 1; iChar++) {
3954 ptr[iPlace++] = pdoc->CharAt(iChar);
3955 }
3956 ptr[iPlace] = '\0';
3957 return currentPos - lineStart;
3958 }
3959
3960 case SCI_GETENDSTYLED:
3961 return pdoc->GetEndStyled();
3962
3963 case SCI_GETEOLMODE:
3964 return pdoc->eolMode;
3965
3966 case SCI_SETEOLMODE:
3967 pdoc->eolMode = wParam;
3968 break;
3969
3970 case SCI_STARTSTYLING:
3971 pdoc->StartStyling(wParam, static_cast<char>(lParam));
3972 break;
3973
3974 case SCI_SETSTYLING:
3975 pdoc->SetStyleFor(wParam, static_cast<char>(lParam));
3976 break;
3977
3978 case SCI_SETSTYLINGEX: // Specify a complete styling buffer
3979 if (lParam == 0)
3980 return 0;
3981 pdoc->SetStyles(wParam, reinterpret_cast<char *>(lParam));
3982 break;
3983
3984 case SCI_SETBUFFEREDDRAW:
3985 bufferedDraw = wParam;
3986 break;
3987
3988 case SCI_GETBUFFEREDDRAW:
3989 return bufferedDraw;
3990
3991 case SCI_SETTABWIDTH:
3992 if (wParam > 0)
3993 pdoc->tabInChars = wParam;
3994 InvalidateStyleRedraw();
3995 break;
3996
3997 case SCI_GETTABWIDTH:
3998 return pdoc->tabInChars;
3999
4000 case SCI_SETINDENT:
4001 pdoc->indentInChars = wParam;
4002 InvalidateStyleRedraw();
4003 break;
4004
4005 case SCI_GETINDENT:
4006 return pdoc->indentInChars;
4007
4008 case SCI_SETUSETABS:
4009 pdoc->useTabs = wParam;
4010 InvalidateStyleRedraw();
4011 break;
4012
4013 case SCI_GETUSETABS:
4014 return pdoc->useTabs;
4015
4016 case SCI_SETLINEINDENTATION:
4017 pdoc->SetLineIndentation(wParam, lParam);
4018 break;
4019
4020 case SCI_GETLINEINDENTATION:
4021 return pdoc->GetLineIndentation(wParam);
4022
4023 case SCI_GETLINEINDENTPOSITION:
4024 return pdoc->GetLineIndentPosition(wParam);
4025
4026 case SCI_SETTABINDENTS:
4027 pdoc->tabIndents = wParam;
4028 break;
4029
4030 case SCI_GETTABINDENTS:
4031 return pdoc->tabIndents;
4032
4033 case SCI_SETBACKSPACEUNINDENTS:
4034 pdoc->backspaceUnindents = wParam;
4035 break;
4036
4037 case SCI_GETBACKSPACEUNINDENTS:
4038 return pdoc->backspaceUnindents;
4039
4040 case SCI_SETMOUSEDWELLTIME:
4041 dwellDelay = wParam;
4042 ticksToDwell = dwellDelay;
4043 break;
4044
4045 case SCI_GETMOUSEDWELLTIME:
4046 return dwellDelay;
4047
4048 case SCI_GETCOLUMN:
4049 return pdoc->GetColumn(wParam);
4050
4051 case SCI_SETHSCROLLBAR :
4052 horizontalScrollBarVisible = wParam;
4053 SetScrollBars();
4054 ReconfigureScrollBars();
4055 break;
4056
4057 case SCI_GETHSCROLLBAR:
4058 return horizontalScrollBarVisible;
4059
4060 case SCI_SETINDENTATIONGUIDES:
4061 vs.viewIndentationGuides = wParam;
4062 Redraw();
4063 break;
4064
4065 case SCI_GETINDENTATIONGUIDES:
4066 return vs.viewIndentationGuides;
4067
4068 case SCI_SETHIGHLIGHTGUIDE:
4069 if ((highlightGuideColumn != static_cast<int>(wParam)) || (wParam > 0)) {
4070 highlightGuideColumn = wParam;
4071 Redraw();
4072 }
4073 break;
4074
4075 case SCI_GETHIGHLIGHTGUIDE:
4076 return highlightGuideColumn;
4077
4078 case SCI_GETLINEENDPOSITION:
4079 return pdoc->LineEnd(wParam);
4080
4081 case SCI_SETCODEPAGE:
4082 pdoc->dbcsCodePage = wParam;
4083 break;
4084
4085 case SCI_GETCODEPAGE:
4086 return pdoc->dbcsCodePage;
4087
4088 case SCI_SETUSEPALETTE:
4089 palette.allowRealization = wParam;
4090 InvalidateStyleRedraw();
4091 break;
4092
4093 case SCI_GETUSEPALETTE:
4094 return palette.allowRealization;
4095
4096 // Marker definition and setting
4097 case SCI_MARKERDEFINE:
4098 if (wParam <= MARKER_MAX)
4099 vs.markers[wParam].markType = lParam;
4100 InvalidateStyleData();
4101 RedrawSelMargin();
4102 break;
4103 case SCI_MARKERSETFORE:
4104 if (wParam <= MARKER_MAX)
4105 vs.markers[wParam].fore.desired = Colour(lParam);
4106 InvalidateStyleData();
4107 RedrawSelMargin();
4108 break;
4109 case SCI_MARKERSETBACK:
4110 if (wParam <= MARKER_MAX)
4111 vs.markers[wParam].back.desired = Colour(lParam);
4112 InvalidateStyleData();
4113 RedrawSelMargin();
4114 break;
4115 case SCI_MARKERADD: {
4116 int markerID = pdoc->AddMark(wParam, lParam);
4117 return markerID;
4118 }
4119
4120 case SCI_MARKERDELETE:
4121 pdoc->DeleteMark(wParam, lParam);
4122 break;
4123
4124 case SCI_MARKERDELETEALL:
4125 pdoc->DeleteAllMarks(static_cast<int>(wParam));
4126 break;
4127
4128 case SCI_MARKERGET:
4129 return pdoc->GetMark(wParam);
4130
4131 case SCI_MARKERNEXT: {
4132 int lt = pdoc->LinesTotal();
4133 for (int iLine = wParam; iLine < lt; iLine++) {
4134 if ((pdoc->GetMark(iLine) & lParam) != 0)
4135 return iLine;
4136 }
4137 }
4138 return -1;
4139
4140 case SCI_MARKERPREVIOUS: {
4141 for (int iLine = wParam; iLine >= 0; iLine--) {
4142 if ((pdoc->GetMark(iLine) & lParam) != 0)
4143 return iLine;
4144 }
4145 }
4146 return -1;
4147
4148 case SCI_SETMARGINTYPEN:
4149 if (ValidMargin(wParam)) {
4150 vs.ms[wParam].symbol = (lParam == SC_MARGIN_SYMBOL);
4151 InvalidateStyleRedraw();
4152 }
4153 break;
4154
4155 case SCI_GETMARGINTYPEN:
4156 if (ValidMargin(wParam))
4157 return vs.ms[wParam].symbol ? SC_MARGIN_SYMBOL : SC_MARGIN_NUMBER;
4158 else
4159 return 0;
4160
4161 case SCI_SETMARGINWIDTHN:
4162 if (ValidMargin(wParam)) {
4163 vs.ms[wParam].width = lParam;
4164 InvalidateStyleRedraw();
4165 }
4166 break;
4167
4168 case SCI_GETMARGINWIDTHN:
4169 if (ValidMargin(wParam))
4170 return vs.ms[wParam].width;
4171 else
4172 return 0;
4173
4174 case SCI_SETMARGINMASKN:
4175 if (ValidMargin(wParam)) {
4176 vs.ms[wParam].mask = lParam;
4177 InvalidateStyleRedraw();
4178 }
4179 break;
4180
4181 case SCI_GETMARGINMASKN:
4182 if (ValidMargin(wParam))
4183 return vs.ms[wParam].mask;
4184 else
4185 return 0;
4186
4187 case SCI_SETMARGINSENSITIVEN:
4188 if (ValidMargin(wParam)) {
4189 vs.ms[wParam].sensitive = lParam;
4190 InvalidateStyleRedraw();
4191 }
4192 break;
4193
4194 case SCI_GETMARGINSENSITIVEN:
4195 if (ValidMargin(wParam))
4196 return vs.ms[wParam].sensitive ? 1 : 0;
4197 else
4198 return 0;
4199
4200 case SCI_STYLECLEARALL:
4201 vs.ClearStyles();
4202 InvalidateStyleRedraw();
4203 break;
4204
4205 case SCI_STYLESETFORE:
4206 if (wParam <= STYLE_MAX) {
4207 vs.styles[wParam].fore.desired = Colour(lParam);
4208 InvalidateStyleRedraw();
4209 }
4210 break;
4211 case SCI_STYLESETBACK:
4212 if (wParam <= STYLE_MAX) {
4213 vs.styles[wParam].back.desired = Colour(lParam);
4214 InvalidateStyleRedraw();
4215 }
4216 break;
4217 case SCI_STYLESETBOLD:
4218 if (wParam <= STYLE_MAX) {
4219 vs.styles[wParam].bold = lParam;
4220 InvalidateStyleRedraw();
4221 }
4222 break;
4223 case SCI_STYLESETITALIC:
4224 if (wParam <= STYLE_MAX) {
4225 vs.styles[wParam].italic = lParam;
4226 InvalidateStyleRedraw();
4227 }
4228 break;
4229 case SCI_STYLESETEOLFILLED:
4230 if (wParam <= STYLE_MAX) {
4231 vs.styles[wParam].eolFilled = lParam;
4232 InvalidateStyleRedraw();
4233 }
4234 break;
4235 case SCI_STYLESETSIZE:
4236 if (wParam <= STYLE_MAX) {
4237 vs.styles[wParam].size = lParam;
4238 InvalidateStyleRedraw();
4239 }
4240 break;
4241 case SCI_STYLESETFONT:
4242 if (lParam == 0)
4243 return 0;
4244 if (wParam <= STYLE_MAX) {
4245 vs.SetStyleFontName(wParam, reinterpret_cast<const char *>(lParam));
4246 InvalidateStyleRedraw();
4247 }
4248 break;
4249 case SCI_STYLESETUNDERLINE:
4250 if (wParam <= STYLE_MAX) {
4251 vs.styles[wParam].underline = lParam;
4252 InvalidateStyleRedraw();
4253 }
4254 break;
4255 case SCI_STYLESETCASE:
4256 if (wParam <= STYLE_MAX) {
4257 vs.styles[wParam].caseForce = static_cast<Style::ecaseForced>(lParam);
4258 InvalidateStyleRedraw();
4259 }
4260 break;
4261 case SCI_STYLESETCHARACTERSET:
4262 if (wParam <= STYLE_MAX) {
4263 vs.styles[wParam].characterSet = lParam;
4264 InvalidateStyleRedraw();
4265 }
4266 break;
4267 case SCI_STYLESETVISIBLE:
4268 if (wParam <= STYLE_MAX) {
4269 vs.styles[wParam].visible = lParam;
4270 InvalidateStyleRedraw();
4271 }
4272 break;
4273
4274 case SCI_STYLERESETDEFAULT:
4275 vs.ResetDefaultStyle();
4276 InvalidateStyleRedraw();
4277 break;
4278 case SCI_SETSTYLEBITS:
4279 pdoc->SetStylingBits(wParam);
4280 break;
4281
4282 case SCI_GETSTYLEBITS:
4283 return pdoc->stylingBits;
4284
4285 case SCI_SETLINESTATE:
4286 return pdoc->SetLineState(wParam, lParam);
4287
4288 case SCI_GETLINESTATE:
4289 return pdoc->GetLineState(wParam);
4290
4291 case SCI_GETMAXLINESTATE:
4292 return pdoc->GetMaxLineState();
4293
4294 case SCI_GETCARETLINEVISIBLE:
4295 return vs.showCaretLineBackground;
4296 case SCI_SETCARETLINEVISIBLE:
4297 vs.showCaretLineBackground = wParam;
4298 InvalidateStyleRedraw();
4299 break;
4300 case SCI_GETCARETLINEBACK:
4301 return vs.caretLineBackground.desired.AsLong();
4302 case SCI_SETCARETLINEBACK:
4303 vs.caretLineBackground.desired = wParam;
4304 InvalidateStyleRedraw();
4305 break;
4306
4307 // Folding messages
4308
4309 case SCI_VISIBLEFROMDOCLINE:
4310 return cs.DisplayFromDoc(wParam);
4311
4312 case SCI_DOCLINEFROMVISIBLE:
4313 return cs.DocFromDisplay(wParam);
4314
4315 case SCI_SETFOLDLEVEL: {
4316 int prev = pdoc->SetLevel(wParam, lParam);
4317 if (prev != lParam)
4318 RedrawSelMargin();
4319 return prev;
4320 }
4321
4322 case SCI_GETFOLDLEVEL:
4323 return pdoc->GetLevel(wParam);
4324
4325 case SCI_GETLASTCHILD:
4326 return pdoc->GetLastChild(wParam, lParam);
4327
4328 case SCI_GETFOLDPARENT:
4329 return pdoc->GetFoldParent(wParam);
4330
4331 case SCI_SHOWLINES:
4332 cs.SetVisible(wParam, lParam, true);
4333 SetScrollBars();
4334 Redraw();
4335 break;
4336
4337 case SCI_HIDELINES:
4338 cs.SetVisible(wParam, lParam, false);
4339 SetScrollBars();
4340 Redraw();
4341 break;
4342
4343 case SCI_GETLINEVISIBLE:
4344 return cs.GetVisible(wParam);
4345
4346 case SCI_SETFOLDEXPANDED:
4347 if (cs.SetExpanded(wParam, lParam)) {
4348 RedrawSelMargin();
4349 }
4350 break;
4351
4352 case SCI_GETFOLDEXPANDED:
4353 return cs.GetExpanded(wParam);
4354
4355 case SCI_SETFOLDFLAGS:
4356 foldFlags = wParam;
4357 Redraw();
4358 break;
4359
4360 case SCI_TOGGLEFOLD:
4361 ToggleContraction(wParam);
4362 break;
4363
4364 case SCI_ENSUREVISIBLE:
4365 EnsureLineVisible(wParam, false);
4366 break;
4367
4368 case SCI_ENSUREVISIBLEENFORCEPOLICY:
4369 EnsureLineVisible(wParam, true);
4370 break;
4371
4372 case SCI_SEARCHANCHOR:
4373 SearchAnchor();
4374 break;
4375
4376 case SCI_SEARCHNEXT:
4377 case SCI_SEARCHPREV:
4378 return SearchText(iMessage, wParam, lParam);
4379
4380 case SCI_SETCARETPOLICY:
4381 caretPolicy = wParam;
4382 caretSlop = lParam;
4383 break;
4384
4385 case SCI_SETVISIBLEPOLICY:
4386 visiblePolicy = wParam;
4387 visibleSlop = lParam;
4388 break;
4389
4390 case SCI_LINESONSCREEN:
4391 return LinesOnScreen();
4392
4393 case SCI_SETSELFORE:
4394 vs.selforeset = wParam;
4395 vs.selforeground.desired = Colour(lParam);
4396 InvalidateStyleRedraw();
4397 break;
4398
4399 case SCI_SETSELBACK:
4400 vs.selbackset = wParam;
4401 vs.selbackground.desired = Colour(lParam);
4402 InvalidateStyleRedraw();
4403 break;
4404
4405 case SCI_SETCARETFORE:
4406 vs.caretcolour.desired = Colour(wParam);
4407 InvalidateStyleRedraw();
4408 break;
4409
4410 case SCI_GETCARETFORE:
4411 return vs.caretcolour.desired.AsLong();
4412
4413 case SCI_SETCARETWIDTH:
4414 if (wParam <= 1)
4415 vs.caretWidth = 1;
4416 else if (wParam >= 3)
4417 vs.caretWidth = 3;
4418 else
4419 vs.caretWidth = wParam;
4420 InvalidateStyleRedraw();
4421 break;
4422
4423 case SCI_GETCARETWIDTH:
4424 return vs.caretWidth;
4425
4426 case SCI_ASSIGNCMDKEY:
4427 kmap.AssignCmdKey(Platform::LowShortFromLong(wParam),
4428 Platform::HighShortFromLong(wParam), lParam);
4429 break;
4430
4431 case SCI_CLEARCMDKEY:
4432 kmap.AssignCmdKey(Platform::LowShortFromLong(wParam),
4433 Platform::HighShortFromLong(wParam), SCI_NULL);
4434 break;
4435
4436 case SCI_CLEARALLCMDKEYS:
4437 kmap.Clear();
4438 break;
4439
4440 case SCI_INDICSETSTYLE:
4441 if (wParam <= INDIC_MAX) {
4442 vs.indicators[wParam].style = lParam;
4443 InvalidateStyleRedraw();
4444 }
4445 break;
4446
4447 case SCI_INDICGETSTYLE:
4448 return (wParam <= INDIC_MAX) ? vs.indicators[wParam].style : 0;
4449
4450 case SCI_INDICSETFORE:
4451 if (wParam <= INDIC_MAX) {
4452 vs.indicators[wParam].fore.desired = Colour(lParam);
4453 InvalidateStyleRedraw();
4454 }
4455 break;
4456
4457 case SCI_INDICGETFORE:
4458 return (wParam <= INDIC_MAX) ? vs.indicators[wParam].fore.desired.AsLong() : 0;
4459
4460 case SCI_LINEDOWN:
4461 case SCI_LINEDOWNEXTEND:
4462 case SCI_LINEUP:
4463 case SCI_LINEUPEXTEND:
4464 case SCI_CHARLEFT:
4465 case SCI_CHARLEFTEXTEND:
4466 case SCI_CHARRIGHT:
4467 case SCI_CHARRIGHTEXTEND:
4468 case SCI_WORDLEFT:
4469 case SCI_WORDLEFTEXTEND:
4470 case SCI_WORDRIGHT:
4471 case SCI_WORDRIGHTEXTEND:
4472 case SCI_HOME:
4473 case SCI_HOMEEXTEND:
4474 case SCI_LINEEND:
4475 case SCI_LINEENDEXTEND:
4476 case SCI_DOCUMENTSTART:
4477 case SCI_DOCUMENTSTARTEXTEND:
4478 case SCI_DOCUMENTEND:
4479 case SCI_DOCUMENTENDEXTEND:
4480 case SCI_PAGEUP:
4481 case SCI_PAGEUPEXTEND:
4482 case SCI_PAGEDOWN:
4483 case SCI_PAGEDOWNEXTEND:
4484 case SCI_EDITTOGGLEOVERTYPE:
4485 case SCI_CANCEL:
4486 case SCI_DELETEBACK:
4487 case SCI_TAB:
4488 case SCI_BACKTAB:
4489 case SCI_NEWLINE:
4490 case SCI_FORMFEED:
4491 case SCI_VCHOME:
4492 case SCI_VCHOMEEXTEND:
4493 case SCI_ZOOMIN:
4494 case SCI_ZOOMOUT:
4495 case SCI_DELWORDLEFT:
4496 case SCI_DELWORDRIGHT:
4497 case SCI_DELLINELEFT:
4498 case SCI_DELLINERIGHT:
4499 case SCI_LINECUT:
4500 case SCI_LINEDELETE:
4501 case SCI_LINETRANSPOSE:
4502 case SCI_LOWERCASE:
4503 case SCI_UPPERCASE:
4504 case SCI_LINESCROLLDOWN:
4505 case SCI_LINESCROLLUP:
4506 case SCI_WORDPARTLEFT:
4507 case SCI_WORDPARTLEFTEXTEND:
4508 case SCI_WORDPARTRIGHT:
4509 case SCI_WORDPARTRIGHTEXTEND:
4510 return KeyCommand(iMessage);
4511
4512 case SCI_BRACEHIGHLIGHT:
4513 SetBraceHighlight(static_cast<int>(wParam), lParam, STYLE_BRACELIGHT);
4514 break;
4515
4516 case SCI_BRACEBADLIGHT:
4517 SetBraceHighlight(static_cast<int>(wParam), -1, STYLE_BRACEBAD);
4518 break;
4519
4520 case SCI_BRACEMATCH:
4521 // wParam is position of char to find brace for,
4522 // lParam is maximum amount of text to restyle to find it
4523 return BraceMatch(wParam, lParam);
4524
4525 case SCI_GETVIEWEOL:
4526 return vs.viewEOL;
4527
4528 case SCI_SETVIEWEOL:
4529 vs.viewEOL = wParam;
4530 Redraw();
4531 break;
4532
4533 case SCI_SETZOOM:
4534 vs.zoomLevel = wParam;
4535 InvalidateStyleRedraw();
4536 break;
4537
4538 case SCI_GETZOOM:
4539 return vs.zoomLevel;
4540
4541 case SCI_GETEDGECOLUMN:
4542 return theEdge;
4543
4544 case SCI_SETEDGECOLUMN:
4545 theEdge = wParam;
4546 InvalidateStyleRedraw();
4547 break;
4548
4549 case SCI_GETEDGEMODE:
4550 return vs.edgeState;
4551
4552 case SCI_SETEDGEMODE:
4553 vs.edgeState = wParam;
4554 InvalidateStyleRedraw();
4555 break;
4556
4557 case SCI_GETEDGECOLOUR:
4558 return vs.edgecolour.desired.AsLong();
4559
4560 case SCI_SETEDGECOLOUR:
4561 vs.edgecolour.desired = Colour(wParam);
4562 InvalidateStyleRedraw();
4563 break;
4564
4565 case SCI_GETDOCPOINTER:
4566 return reinterpret_cast<long>(pdoc);
4567
4568 case SCI_SETDOCPOINTER:
4569 SetDocPointer(reinterpret_cast<Document *>(lParam));
4570 return 0;
4571
4572 case SCI_CREATEDOCUMENT: {
4573 Document *doc = new Document();
4574 doc->AddRef();
4575 return reinterpret_cast<long>(doc);
4576 }
4577
4578 case SCI_ADDREFDOCUMENT:
4579 (reinterpret_cast<Document *>(lParam))->AddRef();
4580 break;
4581
4582 case SCI_RELEASEDOCUMENT:
4583 (reinterpret_cast<Document *>(lParam))->Release();
4584 break;
4585
4586 case SCI_SETMODEVENTMASK:
4587 modEventMask = wParam;
4588 return 0;
4589
4590 case SCI_GETMODEVENTMASK:
4591 return modEventMask;
4592
4593 case SCI_CONVERTEOLS:
4594 pdoc->ConvertLineEnds(wParam);
4595 SetSelection(currentPos, anchor); // Ensure selection inside document
4596 return 0;
4597
4598 case SCI_SELECTIONISRECTANGLE:
4599 return (selType == selRectangle) ? 1 : 0;
4600
4601 case SCI_SETOVERTYPE:
4602 inOverstrike = wParam;
4603 break;
4604
4605 case SCI_GETOVERTYPE:
4606 return inOverstrike ? TRUE : FALSE;
4607
4608 case SCI_SETFOCUS:
4609 SetFocusState(wParam);
4610 break;
4611
4612 case SCI_GETFOCUS:
4613 return hasFocus;
4614
4615 case SCI_SETSTATUS:
4616 errorStatus = wParam;
4617 break;
4618
4619 case SCI_GETSTATUS:
4620 return errorStatus;
4621
4622 case SCI_SETMOUSEDOWNCAPTURES:
4623 mouseDownCaptures = wParam;
4624 break;
4625
4626 case SCI_GETMOUSEDOWNCAPTURES:
4627 return mouseDownCaptures;
4628
4629 case SCI_SETCURSOR:
4630 cursorMode = wParam;
4631 DisplayCursor(Window::cursorText);
4632 break;
4633
4634 case SCI_GETCURSOR:
4635 return cursorMode;
4636
4637 case SCI_STARTRECORD:
4638 recordingMacro = true;
4639 return 0;
4640
4641 case SCI_STOPRECORD:
4642 recordingMacro = false;
4643 return 0;
4644
4645 case SCI_MOVECARETINSIDEVIEW:
4646 MoveCaretInsideView();
4647 break;
4648
4649 default:
4650 return DefWndProc(iMessage, wParam, lParam);
4651 }
4652 //Platform::DebugPrintf("end wnd proc\n");
4653 return 0l;
4654 }