]> git.saurik.com Git - wxWidgets.git/blob - contrib/src/stc/scintilla/src/Editor.cxx
Undoing Eric's changes on the main branch
[wxWidgets.git] / contrib / src / stc / scintilla / src / Editor.cxx
1 // Scintilla source code edit control
2 // Editor.cxx - main code for the edit control
3 // Copyright 1998-2000 by Neil Hodgson <neilh@scintilla.org>
4 // The License.txt file describes the conditions under which this software may be distributed.
5
6 #include <stdlib.h>
7 #include <string.h>
8 #include <stdio.h>
9 #include <ctype.h>
10
11 #include "Platform.h"
12
13 #include "Scintilla.h"
14 #include "ContractionState.h"
15 #include "SVector.h"
16 #include "CellBuffer.h"
17 #include "KeyMap.h"
18 #include "Indicator.h"
19 #include "LineMarker.h"
20 #include "Style.h"
21 #include "ViewStyle.h"
22 #include "Document.h"
23 #include "Editor.h"
24
25 Caret::Caret() :
26 active(true), on(true), period(500) {}
27
28 Timer::Timer() :
29 ticking(false), ticksToWait(0), tickerID(0) {}
30
31 Editor::Editor() {
32 ctrlID = 0;
33
34 stylesValid = false;
35
36 hideSelection = false;
37 inOverstrike = false;
38
39 bufferedDraw = true;
40
41 lastClickTime = 0;
42 ptMouseLast.x = 0;
43 ptMouseLast.y = 0;
44 firstExpose = true;
45 inDragDrop = false;
46 dropWentOutside = false;
47 posDrag = invalidPosition;
48 posDrop = invalidPosition;
49 selectionType = selChar;
50
51 lastXChosen = 0;
52 lineAnchor = 0;
53 originalAnchorPos = 0;
54
55 dragChars = 0;
56 lenDrag = 0;
57 dragIsRectangle = false;
58 selType = selStream;
59 xStartSelect = 0;
60 xEndSelect = 0;
61
62 caretPolicy = CARET_SLOP;
63 caretSlop = 0;
64
65 searchAnchor = 0;
66
67 ucWheelScrollLines = 0;
68 cWheelDelta = 0; //wheel delta from roll
69
70 xOffset = 0;
71 xCaretMargin = 50;
72
73 currentPos = 0;
74 anchor = 0;
75
76 topLine = 0;
77 posTopLine = 0;
78
79 needUpdateUI = true;
80 braces[0]=invalidPosition;
81 braces[1]=invalidPosition;
82 bracesMatchStyle = STYLE_BRACEBAD;
83
84 edgeState = EDGE_NONE;
85 theEdge = 0;
86
87 paintState = notPainting;
88
89 modEventMask = SC_MODEVENTMASKALL;
90
91 pdoc = new Document();
92 pdoc ->AddRef();
93 pdoc->AddWatcher(this, 0);
94
95 #ifdef MACRO_SUPPORT
96 recordingMacro = 0;
97 #endif
98 foldFlags = 0;
99 }
100
101 Editor::~Editor() {
102 pdoc->RemoveWatcher(this, 0);
103 pdoc->Release();
104 pdoc = 0;
105 DropGraphics();
106
107 delete []dragChars;
108 dragChars = 0;
109 lenDrag = 0;
110 }
111
112 void Editor::Finalise() {
113 }
114
115 void Editor::DropGraphics() {
116 pixmapLine.Release();
117 pixmapSelMargin.Release();
118 pixmapSelPattern.Release();
119 }
120
121 void Editor::InvalidateStyleData() {
122 stylesValid = false;
123 palette.Release();
124 DropGraphics();
125 }
126
127 void Editor::InvalidateStyleRedraw() {
128 InvalidateStyleData();
129 Redraw();
130 }
131
132 void Editor::RefreshColourPalette(Palette &pal, bool want) {
133 vs.RefreshColourPalette(pal, want);
134 }
135
136 void Editor::RefreshStyleData() {
137 if (!stylesValid) {
138 stylesValid = true;
139 Surface surface;
140 surface.Init();
141 vs.Refresh(surface);
142 RefreshColourPalette(palette, true);
143 palette.Allocate(wMain);
144 RefreshColourPalette(palette, false);
145 SetScrollBars();
146 }
147 }
148
149 PRectangle Editor::GetClientRectangle() {
150 return wDraw.GetClientPosition();
151 }
152
153 PRectangle Editor::GetTextRectangle() {
154 PRectangle rc = GetClientRectangle();
155 rc.left += vs.fixedColumnWidth;
156 rc.right -= vs.rightMarginWidth;
157 return rc;
158 }
159
160 int Editor::LinesOnScreen() {
161 PRectangle rcClient = GetClientRectangle();
162 int htClient = rcClient.bottom - rcClient.top;
163 //Platform::DebugPrintf("lines on screen = %d\n", htClient / lineHeight + 1);
164 return htClient / vs.lineHeight;
165 }
166
167 int Editor::LinesToScroll() {
168 int retVal = LinesOnScreen() - 1;
169 if (retVal < 1)
170 return 1;
171 else
172 return retVal;
173 }
174
175 int Editor::MaxScrollPos() {
176 //Platform::DebugPrintf("Lines %d screen = %d maxScroll = %d\n",
177 //LinesTotal(), LinesOnScreen(), LinesTotal() - LinesOnScreen() + 1);
178 int retVal = cs.LinesDisplayed() - LinesOnScreen();
179 if (retVal < 0)
180 return 0;
181 else
182 return retVal;
183 }
184
185 bool IsControlCharacter(char ch) {
186 // iscntrl returns true for lots of chars > 127 which are displayable
187 return ch >= 0 && ch < ' ';
188 }
189
190 const char *ControlCharacterString(char ch) {
191 const char *reps[] = {
192 "NUL", "SOH", "STX", "ETX", "EOT", "ENQ", "ACK", "BEL",
193 "BS", "HT", "LF", "VT", "FF", "CR", "SO", "SI",
194 "DLE", "DC1", "DC2", "DC3", "DC4", "NAK", "SYN", "ETB",
195 "CAN", "EM", "SUB", "ESC", "FS", "GS", "RS", "US"
196 };
197 if (ch < (sizeof(reps) / sizeof(reps[0]))) {
198 return reps[ch];
199 } else {
200 return "BAD";
201 }
202 }
203
204 Point Editor::LocationFromPosition(unsigned int pos) {
205 RefreshStyleData();
206 int line = pdoc->LineFromPosition(pos);
207 int lineVisible = cs.DisplayFromDoc(line);
208 //Platform::DebugPrintf("line=%d\n", line);
209 Surface surface;
210 surface.Init();
211 Point pt;
212 pt.y = (lineVisible - topLine) * vs.lineHeight; // + half a lineheight?
213 unsigned int posLineStart = pdoc->LineStart(line);
214 if ((pos - posLineStart) > LineLayout::maxLineLength) {
215 // very long line so put x at arbitrary large position
216 pt.x = 30000 + vs.fixedColumnWidth - xOffset;
217 } else {
218 LineLayout ll;
219 LayoutLine(line, &surface, vs, ll);
220 pt.x = ll.positions[pos - posLineStart] + vs.fixedColumnWidth - xOffset;
221 }
222 return pt;
223 }
224
225 int Editor::XFromPosition(unsigned int pos) {
226 Point pt = LocationFromPosition(pos);
227 return pt.x - vs.fixedColumnWidth + xOffset;
228 }
229
230 int Editor::LineFromLocation(Point pt) {
231 return cs.DocFromDisplay(pt.y / vs.lineHeight + topLine);
232 }
233
234 void Editor::SetTopLine(int topLineNew) {
235 topLine = topLineNew;
236 posTopLine = pdoc->LineStart(topLine);
237 }
238
239 int Editor::PositionFromLocation(Point pt) {
240 RefreshStyleData();
241 pt.x = pt.x - vs.fixedColumnWidth + xOffset;
242 int line = cs.DocFromDisplay(pt.y / vs.lineHeight + topLine);
243 if (pt.y < 0) { // Division rounds towards 0
244 line = cs.DocFromDisplay((pt.y - (vs.lineHeight - 1)) / vs.lineHeight + topLine);
245 }
246 if (line < 0)
247 return 0;
248 if (line >= pdoc->LinesTotal())
249 return pdoc->Length();
250 //Platform::DebugPrintf("Position of (%d,%d) line = %d top=%d\n", pt.x, pt.y, line, topLine);
251 Surface surface;
252 surface.Init();
253 unsigned int posLineStart = pdoc->LineStart(line);
254
255 LineLayout ll;
256 LayoutLine(line, &surface, vs, ll);
257 for (int i = 0; i < ll.numCharsInLine; i++) {
258 if (pt.x < ((ll.positions[i] + ll.positions[i + 1]) / 2) ||
259 ll.chars[i] == '\r' || ll.chars[i] == '\n') {
260 return i + posLineStart;
261 }
262 }
263
264 return ll.numCharsInLine + posLineStart;
265 }
266
267 int Editor::PositionFromLineX(int line, int x) {
268 RefreshStyleData();
269 if (line >= pdoc->LinesTotal())
270 return pdoc->Length();
271 //Platform::DebugPrintf("Position of (%d,%d) line = %d top=%d\n", pt.x, pt.y, line, topLine);
272 Surface surface;
273 surface.Init();
274 unsigned int posLineStart = pdoc->LineStart(line);
275
276 LineLayout ll;
277 LayoutLine(line, &surface, vs, ll);
278 for (int i = 0; i < ll.numCharsInLine; i++) {
279 if (x < ((ll.positions[i] + ll.positions[i + 1]) / 2) ||
280 ll.chars[i] == '\r' || ll.chars[i] == '\n') {
281 return i + posLineStart;
282 }
283 }
284
285 return ll.numCharsInLine + posLineStart;
286 }
287
288 void Editor::RedrawRect(PRectangle rc) {
289 //Platform::DebugPrintf("Redraw %d %d - %d %d\n", rc.left, rc.top, rc.right, rc.bottom);
290 wDraw.InvalidateRectangle(rc);
291 }
292
293 void Editor::Redraw() {
294 //Platform::DebugPrintf("Redraw all\n");
295 wDraw.InvalidateAll();
296 }
297
298 void Editor::RedrawSelMargin() {
299 if (vs.maskInLine) {
300 Redraw();
301 } else {
302 PRectangle rcSelMargin = GetClientRectangle();
303 rcSelMargin.right = vs.fixedColumnWidth;
304 wDraw.InvalidateRectangle(rcSelMargin);
305 }
306 }
307
308 PRectangle Editor::RectangleFromRange(int start, int end) {
309 int minPos = start;
310 if (minPos > end)
311 minPos = end;
312 int maxPos = start;
313 if (maxPos < end)
314 maxPos = end;
315 int minLine = cs.DisplayFromDoc(pdoc->LineFromPosition(minPos));
316 int maxLine = cs.DisplayFromDoc(pdoc->LineFromPosition(maxPos));
317 PRectangle rcClient = GetTextRectangle();
318 PRectangle rc;
319 rc.left = vs.fixedColumnWidth;
320 rc.top = (minLine - topLine) * vs.lineHeight;
321 if (rc.top < 0)
322 rc.top = 0;
323 rc.right = rcClient.right;
324 rc.bottom = (maxLine - topLine + 1) * vs.lineHeight;
325 // Ensure PRectangle is within 16 bit space
326 rc.top = Platform::Clamp(rc.top, -32000, 32000);
327 rc.bottom = Platform::Clamp(rc.bottom, -32000, 32000);
328
329 return rc;
330 }
331
332 void Editor::InvalidateRange(int start, int end) {
333 RedrawRect(RectangleFromRange(start, end));
334 }
335
336 int Editor::CurrentPosition() {
337 return currentPos;
338 }
339
340 bool Editor::SelectionEmpty() {
341 return anchor == currentPos;
342 }
343
344 int Editor::SelectionStart(int line) {
345 if ((line == -1) || (selType == selStream)) {
346 return Platform::Minimum(currentPos, anchor);
347 } else { // selType == selRectangle
348 int selStart = SelectionStart();
349 int selEnd = SelectionEnd();
350 int lineStart = pdoc->LineFromPosition(selStart);
351 int lineEnd = pdoc->LineFromPosition(selEnd);
352 if (line < lineStart || line > lineEnd) {
353 return -1;
354 } else {
355 int minX = Platform::Minimum(xStartSelect, xEndSelect);
356 //return PositionFromLineX(line, minX + vs.fixedColumnWidth - xOffset);
357 return PositionFromLineX(line, minX);
358 }
359 }
360 }
361
362 int Editor::SelectionEnd(int line) {
363 if ((line == -1) || (selType == selStream)) {
364 return Platform::Maximum(currentPos, anchor);
365 } else { // selType == selRectangle
366 int selStart = SelectionStart();
367 int selEnd = SelectionEnd();
368 int lineStart = pdoc->LineFromPosition(selStart);
369 int lineEnd = pdoc->LineFromPosition(selEnd);
370 if (line < lineStart || line > lineEnd) {
371 return -1;
372 } else {
373 int maxX = Platform::Maximum(xStartSelect, xEndSelect);
374 // measure line and return character closest to minx
375 return PositionFromLineX(line, maxX);
376 }
377 }
378 }
379
380 void Editor::SetSelection(int currentPos_, int anchor_) {
381 currentPos_ = pdoc->ClampPositionIntoDocument(currentPos_);
382 anchor_ = pdoc->ClampPositionIntoDocument(anchor_);
383 if ((currentPos != currentPos_) || (anchor != anchor_)) {
384 int firstAffected = anchor;
385 if (firstAffected > currentPos)
386 firstAffected = currentPos;
387 if (firstAffected > anchor_)
388 firstAffected = anchor_;
389 if (firstAffected > currentPos_)
390 firstAffected = currentPos_;
391 int lastAffected = anchor;
392 if (lastAffected < currentPos)
393 lastAffected = currentPos;
394 if (lastAffected < anchor_)
395 lastAffected = anchor_;
396 if (lastAffected < (currentPos_ + 1)) // +1 ensures caret repainted
397 lastAffected = (currentPos_ + 1);
398 currentPos = currentPos_;
399 anchor = anchor_;
400 needUpdateUI = true;
401 InvalidateRange(firstAffected, lastAffected);
402 }
403 ClaimSelection();
404 }
405
406 void Editor::SetSelection(int currentPos_) {
407 currentPos_ = pdoc->ClampPositionIntoDocument(currentPos_);
408 if (currentPos != currentPos_) {
409 int firstAffected = anchor;
410 if (firstAffected > currentPos)
411 firstAffected = currentPos;
412 if (firstAffected > currentPos_)
413 firstAffected = currentPos_;
414 int lastAffected = anchor;
415 if (lastAffected < currentPos)
416 lastAffected = currentPos;
417 if (lastAffected < (currentPos_ + 1)) // +1 ensures caret repainted
418 lastAffected = (currentPos_ + 1);
419 currentPos = currentPos_;
420 needUpdateUI = true;
421 InvalidateRange(firstAffected, lastAffected);
422 }
423 ClaimSelection();
424 }
425
426 void Editor::SetEmptySelection(int currentPos_) {
427 SetSelection(currentPos_, currentPos_);
428 }
429
430 int Editor::MovePositionTo(int newPos, bool extend) {
431 int delta = newPos - currentPos;
432 newPos = pdoc->ClampPositionIntoDocument(newPos);
433 newPos = pdoc->MovePositionOutsideChar(newPos, delta);
434 if (extend) {
435 SetSelection(newPos);
436 } else {
437 SetEmptySelection(newPos);
438 }
439 EnsureCaretVisible();
440 ShowCaretAtCurrentPosition();
441 return 0;
442 }
443
444 int Editor::MovePositionSoVisible(int pos, int moveDir) {
445 pos = pdoc->ClampPositionIntoDocument(pos);
446 pos = pdoc->MovePositionOutsideChar(pos, moveDir);
447 int lineDoc = pdoc->LineFromPosition(pos);
448 if (cs.GetVisible(lineDoc)) {
449 return pos;
450 } else {
451 int lineDisplay = cs.DisplayFromDoc(lineDoc);
452 if (moveDir > 0) {
453 lineDisplay = Platform::Clamp(lineDisplay + 1, 0, cs.LinesDisplayed());
454 return pdoc->LineStart(cs.DocFromDisplay(lineDisplay));
455 } else {
456 // lineDisplay is already line before fold as lines in fold use display line of line before fold
457 lineDisplay = Platform::Clamp(lineDisplay, 0, cs.LinesDisplayed());
458 return pdoc->LineEndPosition(pdoc->LineStart(cs.DocFromDisplay(lineDisplay)));
459 }
460 }
461 }
462
463 // Choose the x position that the caret will try to stick to as it is moves up and down
464 void Editor::SetLastXChosen() {
465 Point pt = LocationFromPosition(currentPos);
466 lastXChosen = pt.x;
467 }
468
469 void Editor::ScrollTo(int line) {
470 int topLineNew = Platform::Clamp(line, 0, MaxScrollPos());
471 if (topLineNew != topLine) {
472 // Try to optimise small scrolls
473 int linesToMove = topLine - topLineNew;
474 SetTopLine(topLineNew);
475 ShowCaretAtCurrentPosition();
476 // Perform redraw rather than scroll if many lines would be redrawn anyway.
477 if (abs(linesToMove) <= 10) {
478 ScrollText(linesToMove);
479 } else {
480 Redraw();
481 }
482 SetVerticalScrollPos();
483 }
484 }
485
486 void Editor::ScrollText(int linesToMove) {
487 //Platform::DebugPrintf("Editor::ScrollText %d\n", linesToMove);
488 Redraw();
489 }
490
491 void Editor::HorizontalScrollTo(int xPos) {
492 //Platform::DebugPrintf("HorizontalScroll %d\n", xPos);
493 xOffset = xPos;
494 if (xOffset < 0)
495 xOffset = 0;
496 SetHorizontalScrollPos();
497 Redraw();
498 }
499
500 void Editor::EnsureCaretVisible(bool useMargin) {
501 //Platform::DebugPrintf("EnsureCaretVisible %d\n", xOffset);
502 PRectangle rcClient = GetTextRectangle();
503 int posCaret = currentPos;
504 if (posDrag >= 0)
505 posCaret = posDrag;
506 Point pt = LocationFromPosition(posCaret);
507 Point ptEOL = LocationFromPosition(pdoc->LineEndPosition(posCaret));
508 Point ptBottomCaret = pt;
509 int lineCaret = cs.DisplayFromDoc(pdoc->LineFromPosition(posCaret));
510 ptBottomCaret.y += vs.lineHeight - 1;
511
512 // Ensure the caret is reasonably visible in context.
513 int xMargin = Platform::Clamp(xCaretMargin, 2, Platform::Maximum(rcClient.Width() - 10, 4) / 2);
514 if (!useMargin)
515 xMargin = 2;
516
517 // Ensure certain amount of text visible on both sides of caretSo move if caret just on edge
518 rcClient.left = rcClient.left + xMargin;
519 rcClient.right = rcClient.right - xMargin;
520
521 if (!rcClient.Contains(pt) || !rcClient.Contains(ptBottomCaret) || (caretPolicy & CARET_STRICT)) {
522 //Platform::DebugPrintf("EnsureCaretVisible move, (%d,%d) (%d,%d)\n", pt.x, pt.y, rcClient.left, rcClient.right);
523 // It should be possible to scroll the window to show the caret,
524 // but this fails to remove the caret on GTK+
525 if (caretPolicy & CARET_SLOP) {
526 if ((topLine > lineCaret) || ((caretPolicy & CARET_STRICT) && (topLine + caretSlop > lineCaret))) {
527 SetTopLine(Platform::Clamp(lineCaret - caretSlop, 0, MaxScrollPos()));
528 SetVerticalScrollPos();
529 Redraw();
530 } else if ((lineCaret > topLine + LinesOnScreen() - 1) ||
531 ((caretPolicy & CARET_STRICT) && (lineCaret > topLine + LinesOnScreen() - 1 - caretSlop))) {
532 SetTopLine(Platform::Clamp(lineCaret - LinesOnScreen() + 1 + caretSlop, 0, MaxScrollPos()));
533 SetVerticalScrollPos();
534 Redraw();
535 }
536 } else {
537 if ((topLine > lineCaret) || (lineCaret > topLine + LinesOnScreen() - 1) || (caretPolicy & CARET_STRICT)) {
538 SetTopLine(Platform::Clamp(lineCaret - LinesOnScreen() / 2 + 1, 0, MaxScrollPos()));
539 SetVerticalScrollPos();
540 Redraw();
541 }
542 }
543 int xOffsetNew = xOffset;
544 if (pt.x < rcClient.left) {
545 xOffsetNew = xOffset - (rcClient.left - pt.x);
546 } else if (pt.x >= rcClient.right) {
547 xOffsetNew = xOffset + (pt.x - rcClient.right);
548 int xOffsetEOL = xOffset + (ptEOL.x - rcClient.right) - xMargin + 2;
549 //Platform::DebugPrintf("Margin %d %d\n", xOffsetNew, xOffsetEOL);
550 // Ensure don't scroll out into empty space
551 if (xOffsetNew > xOffsetEOL)
552 xOffsetNew = xOffsetEOL;
553 }
554 if (xOffsetNew < 0)
555 xOffsetNew = 0;
556 if (xOffset != xOffsetNew) {
557 xOffset = xOffsetNew;
558 SetHorizontalScrollPos();
559 Redraw();
560 }
561 }
562 }
563
564 void Editor::ShowCaretAtCurrentPosition() {
565 if (!wMain.HasFocus()) {
566 caret.active = false;
567 caret.on = false;
568 return;
569 }
570 caret.active = true;
571 caret.on = true;
572 SetTicking(true);
573 }
574
575 void Editor::DropCaret() {
576 caret.active = false;
577 InvalidateCaret();
578 }
579
580 void Editor::InvalidateCaret() {
581 if (posDrag >= 0)
582 InvalidateRange(posDrag, posDrag + 1);
583 else
584 InvalidateRange(currentPos, currentPos + 1);
585 }
586
587 void Editor::PaintSelMargin(Surface *surfWindow, PRectangle &rc) {
588 if (vs.fixedColumnWidth == 0)
589 return;
590
591 PRectangle rcMargin = GetClientRectangle();
592 rcMargin.right = vs.fixedColumnWidth;
593
594 if (!rc.Intersects(rcMargin))
595 return;
596
597 Surface *surface;
598 if (bufferedDraw) {
599 surface = &pixmapSelMargin;
600 } else {
601 surface = surfWindow;
602 }
603
604 PRectangle rcSelMargin = rcMargin;
605 rcSelMargin.right = rcMargin.left;
606
607 for (int margin=0; margin < vs.margins; margin++) {
608 if (vs.ms[margin].width > 0) {
609
610 rcSelMargin.left = rcSelMargin.right;
611 rcSelMargin.right = rcSelMargin.left + vs.ms[margin].width;
612
613 if (vs.ms[margin].symbol) {
614 /* alternate scheme:
615 if (vs.ms[margin].mask & SC_MASK_FOLDERS)
616 surface->FillRectangle(rcSelMargin, vs.styles[STYLE_DEFAULT].back.allocated);
617 else
618 // Required because of special way brush is created for selection margin
619 surface->FillRectangle(rcSelMargin, pixmapSelPattern);
620 */
621 if (vs.ms[margin].mask & SC_MASK_FOLDERS)
622 // Required because of special way brush is created for selection margin
623 surface->FillRectangle(rcSelMargin, pixmapSelPattern);
624 else
625 surface->FillRectangle(rcSelMargin, vs.styles[STYLE_LINENUMBER].back.allocated);
626 } else {
627 surface->FillRectangle(rcSelMargin, vs.styles[STYLE_LINENUMBER].back.allocated);
628 }
629
630 int visibleLine = topLine;
631 int line = cs.DocFromDisplay(visibleLine);
632 int yposScreen = 0;
633
634 while (line < pdoc->LinesTotal() && yposScreen < rcMargin.bottom) {
635 int marks = pdoc->GetMark(line);
636 if (pdoc->GetLevel(line) & SC_FOLDLEVELHEADERFLAG) {
637 if (cs.GetExpanded(line)) {
638 marks |= 1 << SC_MARKNUM_FOLDEROPEN;
639 } else {
640 marks |= 1 << SC_MARKNUM_FOLDER;
641 }
642 }
643 marks &= vs.ms[margin].mask;
644 PRectangle rcMarker = rcSelMargin;
645 rcMarker.top = yposScreen;
646 rcMarker.bottom = yposScreen + vs.lineHeight;
647 if (!vs.ms[margin].symbol) {
648 char number[100];
649 number[0] = '\0';
650 sprintf(number, "%d", line + 1);
651 if (foldFlags & 8)
652 sprintf(number, "%X", pdoc->GetLevel(line));
653 int xpos = 0;
654 PRectangle rcNumber=rcMarker;
655 // Right justify
656 int width = surface->WidthText(vs.styles[STYLE_LINENUMBER].font, number, strlen(number));
657 xpos = rcNumber.right - width - 3;
658 rcNumber.left = xpos;
659 if ((visibleLine < cs.LinesDisplayed()) && cs.GetVisible(line)) {
660 surface->DrawText(rcNumber, vs.styles[STYLE_LINENUMBER].font,
661 rcNumber.top + vs.maxAscent, number, strlen(number),
662 vs.styles[STYLE_LINENUMBER].fore.allocated,
663 vs.styles[STYLE_LINENUMBER].back.allocated);
664 }
665 }
666
667 if (marks) {
668 for (int markBit = 0; (markBit < 32) && marks; markBit++) {
669 if (marks & 1) {
670 rcMarker.top++;
671 rcMarker.bottom--;
672 vs.markers[markBit].Draw(surface, rcMarker);
673 }
674 marks >>= 1;
675 }
676 }
677
678 visibleLine++;
679 line = cs.DocFromDisplay(visibleLine);
680 yposScreen += vs.lineHeight;
681 }
682 }
683 }
684
685 PRectangle rcBlankMargin = rcMargin;
686 rcBlankMargin.left = rcSelMargin.right;
687 surface->FillRectangle(rcBlankMargin, vs.styles[STYLE_DEFAULT].back.allocated);
688
689 if (bufferedDraw) {
690 surfWindow->Copy(rcMargin, Point(), pixmapSelMargin);
691 }
692 }
693
694 void DrawTabArrow(Surface *surface, PRectangle rcTab, int ymid) {
695 int ydiff = (rcTab.bottom - rcTab.top) / 2;
696 int xhead = rcTab.right - 1 - ydiff;
697 if ((rcTab.left + 2) < (rcTab.right - 1))
698 surface->MoveTo(rcTab.left + 2, ymid);
699 else
700 surface->MoveTo(rcTab.right - 1, ymid);
701 surface->LineTo(rcTab.right - 1, ymid);
702 surface->LineTo(xhead, ymid - ydiff);
703 surface->MoveTo(rcTab.right - 1, ymid);
704 surface->LineTo(xhead, ymid + ydiff);
705 }
706
707 void Editor::LayoutLine(int line, Surface *surface, ViewStyle &vstyle, LineLayout &ll) {
708 int numCharsInLine = 0;
709 int posLineStart = pdoc->LineStart(line);
710 int posLineEnd = pdoc->LineStart(line + 1);
711 Font &ctrlCharsFont = vstyle.styles[STYLE_CONTROLCHAR].font;
712 char styleByte = 0;
713 int styleMask = pdoc->stylingBitsMask;
714 for (int charInDoc = posLineStart;
715 charInDoc < posLineEnd && numCharsInLine < LineLayout::maxLineLength - 1;
716 charInDoc++) {
717 char chDoc = pdoc->CharAt(charInDoc);
718 styleByte = pdoc->StyleAt(charInDoc);
719 if (vstyle.viewEOL || ((chDoc != '\r') && (chDoc != '\n'))) {
720 ll.chars[numCharsInLine] = chDoc;
721 ll.styles[numCharsInLine] = styleByte & styleMask;
722 ll.indicators[numCharsInLine] = styleByte & ~styleMask;
723 numCharsInLine++;
724 }
725 }
726 ll.chars[numCharsInLine] = 0;
727 ll.styles[numCharsInLine] = styleByte; // For eolFilled
728 ll.indicators[numCharsInLine] = 0;
729
730 // Layout the line, determining the position of each character
731 int startseg = 0;
732 int startsegx = 0;
733 ll.positions[0] = 0;
734 unsigned int tabWidth = vstyle.spaceWidth * pdoc->tabInChars;
735
736 for (int charInLine = 0; charInLine < numCharsInLine; charInLine++) {
737 if ((ll.styles[charInLine] != ll.styles[charInLine + 1]) ||
738 IsControlCharacter(ll.chars[charInLine]) || IsControlCharacter(ll.chars[charInLine + 1])) {
739 ll.positions[startseg] = 0;
740 if (IsControlCharacter(ll.chars[charInLine])) {
741 if (ll.chars[charInLine] == '\t') {
742 ll.positions[charInLine + 1] = ((((startsegx + 2) /
743 tabWidth) + 1) * tabWidth) - startsegx;
744 } else {
745 const char *ctrlChar = ControlCharacterString(ll.chars[charInLine]);
746 // +3 For a blank on front and rounded edge each side:
747 ll.positions[charInLine + 1] = surface->WidthText(ctrlCharsFont, ctrlChar, strlen(ctrlChar)) + 3;
748 }
749 } else {
750 surface->MeasureWidths(vstyle.styles[ll.styles[charInLine]].font, ll.chars + startseg,
751 charInLine - startseg + 1, ll.positions + startseg + 1);
752 }
753 for (int posToIncrease = startseg; posToIncrease <= (charInLine + 1); posToIncrease++) {
754 ll.positions[posToIncrease] += startsegx;
755 }
756 startsegx = ll.positions[charInLine + 1];
757 startseg = charInLine + 1;
758 }
759 }
760 ll.numCharsInLine = numCharsInLine;
761 }
762
763 void Editor::DrawLine(Surface *surface, ViewStyle &vsDraw, int line, int xStart, PRectangle rcLine, LineLayout &ll) {
764
765 PRectangle rcSegment = rcLine;
766
767 // Using one font for all control characters so it can be controlled independently to ensure
768 // the box goes around the characters tightly. Seems to be no way to work out what height
769 // is taken by an individual character - internal leading gives varying results.
770 Font &ctrlCharsFont = vsDraw.styles[STYLE_CONTROLCHAR].font;
771
772 int marks = 0;
773 Colour markBack = Colour(0, 0, 0);
774 if (vsDraw.maskInLine) {
775 marks = pdoc->GetMark(line) & vsDraw.maskInLine;
776 if (marks) {
777 for (int markBit = 0; (markBit < 32) && marks; markBit++) {
778 if (marks & 1) {
779 markBack = vsDraw.markers[markBit].back.allocated;
780 }
781 marks >>= 1;
782 }
783 }
784 marks = pdoc->GetMark(line) & vsDraw.maskInLine;
785 }
786
787 int posLineStart = pdoc->LineStart(line);
788 int posLineEnd = pdoc->LineStart(line + 1);
789
790 int selStart = SelectionStart(line);
791 int selEnd = SelectionEnd(line);
792
793 int styleMask = pdoc->stylingBitsMask;
794 int startseg = 0;
795 for (int i = 0; i < ll.numCharsInLine; i++) {
796
797 int iDoc = i + posLineStart;
798 // If there is the end of a style run for any reason
799 if ((ll.styles[i] != ll.styles[i + 1]) ||
800 IsControlCharacter(ll.chars[i]) || IsControlCharacter(ll.chars[i + 1]) ||
801 ((selStart != selEnd) && ((iDoc + 1 == selStart) || (iDoc + 1 == selEnd))) ||
802 (i == (theEdge-1))) {
803 int styleMain = ll.styles[i];
804 Colour textBack = vsDraw.styles[styleMain].back.allocated;
805 Colour textFore = vsDraw.styles[styleMain].fore.allocated;
806 Font &textFont = vsDraw.styles[styleMain].font;
807 bool inSelection = (iDoc >= selStart) && (iDoc < selEnd) && (selStart != selEnd);
808 if (inSelection && !hideSelection) {
809 if (vsDraw.selbackset)
810 textBack = vsDraw.selbackground.allocated;
811 if (vsDraw.selforeset)
812 textFore = vsDraw.selforeground.allocated;
813 } else {
814 if (marks)
815 textBack = markBack;
816 if ((edgeState == EDGE_BACKGROUND) && (i >= theEdge) && (ll.chars[i] != '\n') && (ll.chars[i] != '\r'))
817 textBack = vs.edgecolour.allocated;
818 }
819 // Manage tab display
820 if (ll.chars[i] == '\t') {
821 rcSegment.left = ll.positions[i] + xStart;
822 rcSegment.right = ll.positions[i + 1] + xStart;
823 surface->FillRectangle(rcSegment, textBack);
824 if (vsDraw.viewWhitespace) {
825 surface->PenColour(textFore);
826 PRectangle rcTab(rcSegment.left + 1, rcSegment.top + 4,
827 rcSegment.right - 1, rcSegment.bottom - vsDraw.maxDescent);
828 DrawTabArrow(surface, rcTab, rcSegment.top + vsDraw.lineHeight / 2);
829 }
830 // Manage control character display
831 } else if (IsControlCharacter(ll.chars[i])) {
832 const char *ctrlChar = ControlCharacterString(ll.chars[i]);
833 rcSegment.left = ll.positions[i] + xStart;
834 rcSegment.right = ll.positions[i + 1] + xStart;
835 surface->FillRectangle(rcSegment, textBack);
836 int normalCharHeight = surface->Ascent(ctrlCharsFont) -
837 surface->InternalLeading(ctrlCharsFont);
838 PRectangle rcCChar = rcSegment;
839 rcCChar.left = rcCChar.left + 1;
840 rcCChar.top = rcSegment.top + vsDraw.maxAscent - normalCharHeight;
841 rcCChar.bottom = rcSegment.top + vsDraw.maxAscent + 1;
842 PRectangle rcCentral = rcCChar;
843 rcCentral.top++;
844 rcCentral.bottom--;
845 surface->FillRectangle(rcCentral, textFore);
846 PRectangle rcChar = rcCChar;
847 rcChar.left++;
848 rcChar.right--;
849 surface->DrawTextClipped(rcChar, ctrlCharsFont,
850 rcSegment.top + vsDraw.maxAscent, ctrlChar, strlen(ctrlChar),
851 textBack, textFore);
852 // Manage normal display
853 } else {
854 rcSegment.left = ll.positions[startseg] + xStart;
855 rcSegment.right = ll.positions[i + 1] + xStart;
856 // Only try do draw if really visible - enhances performance by not calling environment to
857 // draw strings that are completely past the right side of the window.
858 if (rcSegment.left <= rcLine.right) {
859 surface->DrawText(rcSegment, textFont,
860 rcSegment.top + vsDraw.maxAscent, ll.chars + startseg,
861 i - startseg + 1, textFore, textBack);
862 if (vsDraw.viewWhitespace) {
863 for (int cpos = 0; cpos <= i - startseg; cpos++) {
864 if (ll.chars[cpos + startseg] == ' ') {
865 int xmid = (ll.positions[cpos + startseg] + ll.positions[cpos + startseg + 1]) / 2;
866 PRectangle rcDot(xmid + xStart, rcSegment.top + vsDraw.lineHeight / 2, 0, 0);
867 rcDot.right = rcDot.left + 1;
868 rcDot.bottom = rcDot.top + 1;
869 surface->FillRectangle(rcDot, textFore);
870 }
871 }
872 }
873 }
874 }
875 startseg = i + 1;
876 }
877 }
878
879 // Draw indicators
880 int indStart[INDIC_MAX + 1] = {0};
881 for (int indica = 0; indica <= INDIC_MAX; indica++)
882 indStart[indica] = 0;
883
884 for (int indicPos = 0; indicPos <= ll.numCharsInLine; indicPos++) {
885 if (ll.indicators[indicPos] != ll.indicators[indicPos + 1]) {
886 int mask = 1 << pdoc->stylingBits;
887 for (int indicnum = 0; mask <= 0x100; indicnum++) {
888 if ((ll.indicators[indicPos + 1] & mask) && !(ll.indicators[indicPos] & mask)) {
889 indStart[indicnum] = ll.positions[indicPos + 1];
890 }
891 if (!(ll.indicators[indicPos + 1] & mask) && (ll.indicators[indicPos] & mask)) {
892 PRectangle rcIndic(
893 indStart[indicnum] + xStart,
894 rcLine.top + vsDraw.maxAscent,
895 ll.positions[indicPos + 1] + xStart,
896 rcLine.top + vsDraw.maxAscent + 3);
897 vsDraw.indicators[indicnum].Draw(surface, rcIndic);
898 }
899 mask = mask << 1;
900 }
901 }
902 }
903 // End of the drawing of the current line
904
905 // Fill in a PRectangle representing the end of line characters
906 int xEol = ll.positions[ll.numCharsInLine];
907 rcSegment.left = xEol + xStart;
908 rcSegment.right = xEol + vsDraw.aveCharWidth + xStart;
909 bool eolInSelection = (posLineEnd > selStart) && (posLineEnd <= selEnd) && (selStart != selEnd);
910 if (eolInSelection && !hideSelection && vsDraw.selbackset && (line < pdoc->LinesTotal()-1)) {
911 surface->FillRectangle(rcSegment, vsDraw.selbackground.allocated);
912 } else if (marks) {
913 surface->FillRectangle(rcSegment, markBack);
914 } else {
915 surface->FillRectangle(rcSegment, vsDraw.styles[ll.styles[ll.numCharsInLine] & styleMask].back.allocated);
916 }
917
918 rcSegment.left = xEol + vsDraw.aveCharWidth + xStart;
919 rcSegment.right = rcLine.right;
920 if (marks) {
921 surface->FillRectangle(rcSegment, markBack);
922 } else if (vsDraw.styles[ll.styles[ll.numCharsInLine] & styleMask].eolFilled) {
923 surface->FillRectangle(rcSegment, vsDraw.styles[ll.styles[ll.numCharsInLine] & styleMask].back.allocated);
924 } else {
925 surface->FillRectangle(rcSegment, vsDraw.styles[STYLE_DEFAULT].back.allocated);
926 }
927
928 if (edgeState == EDGE_LINE) {
929 int edgeX = theEdge * vsDraw.spaceWidth;
930 rcSegment.left = edgeX + xStart;
931 rcSegment.right = rcSegment.left + 1;
932 surface->FillRectangle(rcSegment, vs.edgecolour.allocated);
933 }
934 }
935
936 void Editor::Paint(Surface *surfaceWindow, PRectangle rcArea) {
937 //Platform::DebugPrintf("Paint %d %d - %d %d\n", rcArea.left, rcArea.top, rcArea.right, rcArea.bottom);
938 RefreshStyleData();
939
940 PRectangle rcClient = GetClientRectangle();
941 //Platform::DebugPrintf("Client: (%3d,%3d) ... (%3d,%3d) %d\n",
942 // rcClient.left, rcClient.top, rcClient.right, rcClient.bottom);
943
944 if (!pixmapSelPattern.Initialised()) {
945 pixmapSelPattern.InitPixMap(8, 8, surfaceWindow);
946 // This complex procedure is to reproduce the checker board dithered pattern used by windows
947 // for scroll bars and Visual Studio for its selection margin. The colour of this pattern is half
948 // way between the chrome colour and the chrome highlight colour making a nice transition
949 // between the window chrome and the content area. And it works in low colour depths.
950 PRectangle rcPattern(0, 0, 8, 8);
951 if (vs.selbarlight.desired == Colour(0xff, 0xff, 0xff)) {
952 pixmapSelPattern.FillRectangle(rcPattern, vs.selbar.allocated);
953 pixmapSelPattern.PenColour(vs.selbarlight.allocated);
954 for (int stripe = 0; stripe < 8; stripe++) {
955 pixmapSelPattern.MoveTo(0, stripe * 2);
956 pixmapSelPattern.LineTo(8, stripe * 2 - 8);
957 }
958 } else {
959 // User has chosen an unusual chrome colour scheme so just use the highlight edge colour.
960 pixmapSelPattern.FillRectangle(rcPattern, vs.selbarlight.allocated);
961 }
962 }
963
964 if (bufferedDraw) {
965 if (!pixmapLine.Initialised()) {
966 pixmapLine.InitPixMap(rcClient.Width(), rcClient.Height(),
967 surfaceWindow);
968 pixmapSelMargin.InitPixMap(vs.fixedColumnWidth,
969 rcClient.Height(), surfaceWindow);
970 }
971 }
972
973 surfaceWindow->SetPalette(&palette, true);
974 pixmapLine.SetPalette(&palette, !wMain.HasFocus());
975
976 //Platform::DebugPrintf("Paint: (%3d,%3d) ... (%3d,%3d)\n",
977 // rcArea.left, rcArea.top, rcArea.right, rcArea.bottom);
978
979 int screenLinePaintFirst = rcArea.top / vs.lineHeight;
980 // The area to be painted plus one extra line is styled.
981 // The extra line is to determine when a style change, such as statrting a comment flows on to other lines.
982 int lineStyleLast = topLine + (rcArea.bottom-1) / vs.lineHeight + 1;
983 //Platform::DebugPrintf("Paint lines = %d .. %d\n", topLine + screenLinePaintFirst, lineStyleLast);
984 int endPosPaint = pdoc->Length();
985 if (lineStyleLast < cs.LinesDisplayed())
986 endPosPaint = pdoc->LineStart(cs.DocFromDisplay(lineStyleLast + 1));
987
988 int xStart = vs.fixedColumnWidth - xOffset;
989 int ypos = 0;
990 if (!bufferedDraw)
991 ypos += screenLinePaintFirst * vs.lineHeight;
992 int yposScreen = screenLinePaintFirst * vs.lineHeight;
993
994 if (endPosPaint > pdoc->GetEndStyled()) {
995 // Notify container to do some more styling
996 NotifyStyleNeeded(endPosPaint);
997 }
998 if (needUpdateUI) {
999 NotifyUpdateUI();
1000 needUpdateUI = false;
1001 }
1002
1003 PaintSelMargin(surfaceWindow, rcArea);
1004
1005 PRectangle rcRightMargin = rcClient;
1006 rcRightMargin.left = rcRightMargin.right - vs.rightMarginWidth;
1007 if (rcArea.Intersects(rcRightMargin)) {
1008 surfaceWindow->FillRectangle(rcRightMargin, vs.styles[STYLE_DEFAULT].back.allocated);
1009 }
1010
1011 if (paintState == paintAbandoned) {
1012 // Either NotifyStyleNeeded or NotifyUpdateUI noticed that painting is needed
1013 // outside the current painting rectangle
1014 //Platform::DebugPrintf("Abandoning paint\n");
1015 return;
1016 }
1017 //Platform::DebugPrintf("start display %d, offset = %d\n", pdoc->Length(), xOffset);
1018
1019 Surface *surface = 0;
1020 if (rcArea.right > vs.fixedColumnWidth) {
1021
1022 if (bufferedDraw) {
1023 surface = &pixmapLine;
1024 } else {
1025 surface = surfaceWindow;
1026 }
1027
1028 int visibleLine = topLine + screenLinePaintFirst;
1029 int line = cs.DocFromDisplay(visibleLine);
1030
1031 int posCaret = currentPos;
1032 if (posDrag >= 0)
1033 posCaret = posDrag;
1034 int lineCaret = pdoc->LineFromPosition(posCaret);
1035
1036 // Remove selection margin from drawing area so text will not be drawn
1037 // on it in unbuffered mode.
1038 PRectangle rcTextArea = rcClient;
1039 rcTextArea.left = vs.fixedColumnWidth;
1040 rcTextArea.right -= vs.rightMarginWidth;
1041 surfaceWindow->SetClip(rcTextArea);
1042 //GTimer *tim=g_timer_new();
1043 while (visibleLine <= cs.LinesDisplayed() && yposScreen < rcArea.bottom) {
1044 //g_timer_start(tim);
1045 //Platform::DebugPrintf("Painting line %d\n", line);
1046
1047 int posLineStart = pdoc->LineStart(line);
1048 int posLineEnd = pdoc->LineStart(line + 1);
1049 //Platform::DebugPrintf("line %d %d - %d\n", line, posLineStart, posLineEnd);
1050
1051 PRectangle rcLine = rcClient;
1052 rcLine.top = ypos;
1053 rcLine.bottom = ypos + vs.lineHeight;
1054
1055 // Copy this line and its styles from the document into local arrays
1056 // and determine the x position at which each character starts.
1057 LineLayout ll;
1058 LayoutLine(line, surface, vs, ll);
1059
1060 // Highlight the current braces if any
1061 if ((braces[0] >= posLineStart) && (braces[0] < posLineEnd))
1062 ll.styles[braces[0] - posLineStart] = bracesMatchStyle;
1063 if ((braces[1] >= posLineStart) && (braces[1] < posLineEnd))
1064 ll.styles[braces[1] - posLineStart] = bracesMatchStyle;
1065
1066 // Draw the line
1067 if (cs.GetVisible(line))
1068 DrawLine(surface, vs, line, xStart, rcLine, ll);
1069
1070 if (foldFlags & 2) {
1071 if (pdoc->GetLevel(line) & SC_FOLDLEVELHEADERFLAG) {
1072 PRectangle rcFoldLine = rcLine;
1073 rcFoldLine.bottom = rcFoldLine.top + 1;
1074 surface->FillRectangle(rcFoldLine, vs.styles[STYLE_DEFAULT].fore.allocated);
1075 }
1076 }
1077
1078 // Draw the Caret
1079 if (line == lineCaret) {
1080 int xposCaret = ll.positions[posCaret - posLineStart] + xStart;
1081 int widthOverstrikeCaret =
1082 ll.positions[posCaret - posLineStart + 1] - ll.positions[posCaret - posLineStart];
1083 if (posCaret == pdoc->Length()) // At end of document
1084 widthOverstrikeCaret = vs.aveCharWidth;
1085 if ((posCaret - posLineStart) >= ll.numCharsInLine) // At end of line
1086 widthOverstrikeCaret = vs.aveCharWidth;
1087 if (widthOverstrikeCaret < 3) // Make sure its visible
1088 widthOverstrikeCaret = 3;
1089 if (((caret.active && caret.on) || (posDrag >= 0)) && xposCaret >= 0) {
1090 PRectangle rcCaret = rcLine;
1091 if (posDrag >= 0) {
1092 rcCaret.left = xposCaret;
1093 rcCaret.right = xposCaret + 1;
1094 } else {
1095 if (inOverstrike) {
1096 rcCaret.top = rcCaret.bottom - 2;
1097 rcCaret.left = xposCaret + 1;
1098 rcCaret.right = rcCaret.left + widthOverstrikeCaret - 1;
1099 } else {
1100 rcCaret.left = xposCaret;
1101 rcCaret.right = xposCaret + 1;
1102 }
1103 }
1104 surface->FillRectangle(rcCaret, vs.caretcolour.allocated);
1105 }
1106 }
1107
1108 if (cs.GetVisible(line)) {
1109 if (bufferedDraw) {
1110 Point from(vs.fixedColumnWidth, 0);
1111 PRectangle rcCopyArea(vs.fixedColumnWidth, yposScreen,
1112 rcClient.right, yposScreen + vs.lineHeight);
1113 surfaceWindow->Copy(rcCopyArea, from, pixmapLine);
1114 }
1115 }
1116
1117 if (!bufferedDraw) {
1118 ypos += vs.lineHeight;
1119 }
1120
1121 yposScreen += vs.lineHeight;
1122 visibleLine++;
1123 line = cs.DocFromDisplay(visibleLine);
1124 //gdk_flush();
1125 //g_timer_stop(tim);
1126 //Platform::DebugPrintf("Paint [%0d] took %g\n", line, g_timer_elapsed(tim, 0));
1127 }
1128 //g_timer_destroy(tim);
1129 PRectangle rcBeyondEOF = rcClient;
1130 rcBeyondEOF.left = vs.fixedColumnWidth;
1131 rcBeyondEOF.right = rcBeyondEOF.right;
1132 rcBeyondEOF.top = (cs.LinesDisplayed() - topLine) * vs.lineHeight;
1133 if (rcBeyondEOF.top < rcBeyondEOF.bottom) {
1134 surfaceWindow->FillRectangle(rcBeyondEOF, vs.styles[STYLE_DEFAULT].back.allocated);
1135 if (edgeState == EDGE_LINE) {
1136 int edgeX = theEdge * vs.spaceWidth;
1137 rcBeyondEOF.left = edgeX + xStart;
1138 rcBeyondEOF.right = rcBeyondEOF.left + 1;
1139 surfaceWindow->FillRectangle(rcBeyondEOF, vs.edgecolour.allocated);
1140 }
1141 }
1142 }
1143 }
1144
1145 // Space (3 space characters) between line numbers and text when printing.
1146 #define lineNumberPrintSpace " "
1147
1148 // This is mostly copied from the Paint method but with some things omitted
1149 // such as the margin markers, line numbers, selection and caret
1150 // Should be merged back into a combined Draw method.
1151 long Editor::FormatRange(bool draw, FORMATRANGE *pfr) {
1152 if (!pfr)
1153 return 0;
1154
1155 Surface *surface = new Surface();
1156 surface->Init(pfr->hdc);
1157 Surface *surfaceMeasure = new Surface();
1158 surfaceMeasure->Init(pfr->hdcTarget);
1159
1160 ViewStyle vsPrint(vs);
1161
1162 // Modify the view style for printing as do not normally want any of the transient features to be printed
1163 // Printing supports only the line number margin.
1164 int lineNumberIndex = -1;
1165 for (int margin=0; margin < ViewStyle::margins; margin++) {
1166 if ((!vsPrint.ms[margin].symbol) && (vsPrint.ms[margin].width > 0)) {
1167 lineNumberIndex = margin;
1168 } else {
1169 vsPrint.ms[margin].width = 0;
1170 }
1171 }
1172 vsPrint.showMarkedLines = false;
1173 vsPrint.fixedColumnWidth = 0;
1174 vsPrint.zoomLevel = 0;
1175 // Don't show the selection when printing
1176 vsPrint.selbackset = false;
1177 vsPrint.selforeset = false;
1178 // White background for the line numbers
1179 vsPrint.styles[STYLE_LINENUMBER].back.desired = Colour(0xff,0xff,0xff);
1180
1181 vsPrint.Refresh(*surfaceMeasure);
1182 // Ensure colours are set up
1183 vsPrint.RefreshColourPalette(palette, true);
1184 vsPrint.RefreshColourPalette(palette, false);
1185 // Determining width must hapen after fonts have been realised in Refresh
1186 int lineNumberWidth = 0;
1187 if (lineNumberIndex >= 0) {
1188 lineNumberWidth = surface->WidthText(vsPrint.styles[STYLE_LINENUMBER].font,
1189 "9999" lineNumberPrintSpace, 4 + strlen(lineNumberPrintSpace));
1190 vsPrint.ms[lineNumberIndex].width = lineNumberWidth;
1191 }
1192
1193 int linePrintStart = pdoc->LineFromPosition(pfr->chrg.cpMin);
1194 int linePrintLast = linePrintStart + (pfr->rc.bottom - pfr->rc.top) / vsPrint.lineHeight - 1;
1195 if (linePrintLast < linePrintStart)
1196 linePrintLast = linePrintStart;
1197 int linePrintMax = pdoc->LineFromPosition(pfr->chrg.cpMax - 1);
1198 if (linePrintLast > linePrintMax)
1199 linePrintLast = linePrintMax;
1200 //Platform::DebugPrintf("Formatting lines=[%0d,%0d,%0d] top=%0d bottom=%0d line=%0d %0d\n",
1201 // linePrintStart, linePrintLast, linePrintMax, pfr->rc.top, pfr->rc.bottom, vsPrint.lineHeight,
1202 // surfaceMeasure->Height(vsPrint.styles[STYLE_LINENUMBER].font));
1203 int endPosPrint = pdoc->Length();
1204 if (linePrintLast < pdoc->LinesTotal())
1205 endPosPrint = pdoc->LineStart(linePrintLast + 1);
1206
1207 if (endPosPrint > pdoc->GetEndStyled()) {
1208 // Notify container to do some more styling
1209 NotifyStyleNeeded(endPosPrint);
1210 }
1211 int xStart = vsPrint.fixedColumnWidth + pfr->rc.left + lineNumberWidth;
1212 int ypos = pfr->rc.top;
1213 int line = linePrintStart;
1214
1215 if (draw) { // Otherwise just measuring
1216
1217 while (line <= linePrintLast && ypos < pfr->rc.bottom) {
1218
1219 PRectangle rcLine;
1220 rcLine.left = pfr->rc.left + lineNumberWidth;
1221 rcLine.top = ypos;
1222 rcLine.right = pfr->rc.right;
1223 rcLine.bottom = ypos + vsPrint.lineHeight;
1224
1225 if (lineNumberWidth) {
1226 char number[100];
1227 sprintf(number, "%d" lineNumberPrintSpace, line + 1);
1228 PRectangle rcNumber = rcLine;
1229 rcNumber.right = rcNumber.left + lineNumberWidth;
1230 // Right justify
1231 rcNumber.left += lineNumberWidth -
1232 surface->WidthText(vsPrint.styles[STYLE_LINENUMBER].font, number, strlen(number));
1233 surface->DrawText(rcNumber, vsPrint.styles[STYLE_LINENUMBER].font,
1234 ypos + vsPrint.maxAscent, number, strlen(number),
1235 vsPrint.styles[STYLE_LINENUMBER].fore.allocated,
1236 vsPrint.styles[STYLE_LINENUMBER].back.allocated);
1237 }
1238
1239 // Copy this line and its styles from the document into local arrays
1240 // and determine the x position at which each character starts.
1241 LineLayout ll;
1242 LayoutLine(line, surfaceMeasure, vsPrint, ll);
1243
1244 // Draw the line
1245 DrawLine(surface, vsPrint, line, xStart, rcLine, ll);
1246
1247 ypos += vsPrint.lineHeight;
1248 line++;
1249 }
1250 }
1251
1252 delete surface;
1253 delete surfaceMeasure;
1254
1255 return endPosPrint;
1256 }
1257
1258 void Editor::SetScrollBarsTo(PRectangle) {
1259 RefreshStyleData();
1260
1261 int nMax = cs.LinesDisplayed();
1262 int nPage = cs.LinesDisplayed() - MaxScrollPos() + 1;
1263 bool modified = ModifyScrollBars(nMax, nPage);
1264
1265 // TODO: ensure always showing as many lines as possible
1266 // May not be, if, for example, window made larger
1267 if (topLine > MaxScrollPos()) {
1268 SetTopLine(Platform::Clamp(topLine, 0, MaxScrollPos()));
1269 SetVerticalScrollPos();
1270 Redraw();
1271 }
1272 if (modified)
1273 Redraw();
1274 //Platform::DebugPrintf("end max = %d page = %d\n", nMax, nPage);
1275 }
1276
1277 void Editor::SetScrollBars() {
1278 PRectangle rsClient = GetClientRectangle();
1279 SetScrollBarsTo(rsClient);
1280 }
1281
1282 void Editor::AddChar(char ch) {
1283 bool wasSelection = currentPos != anchor;
1284 ClearSelection();
1285 if (inOverstrike && !wasSelection) {
1286 if (currentPos < (pdoc->Length() - 1)) {
1287 if ((pdoc->CharAt(currentPos) != '\r') && (pdoc->CharAt(currentPos) != '\n')) {
1288 pdoc->DelChar(currentPos);
1289 }
1290 }
1291 }
1292 pdoc->InsertChar(currentPos, ch);
1293 SetEmptySelection(currentPos + 1);
1294 EnsureCaretVisible();
1295 SetLastXChosen();
1296 NotifyChar(ch);
1297 }
1298
1299 void Editor::ClearSelection() {
1300 if (selType == selRectangle) {
1301 pdoc->BeginUndoAction();
1302 int lineStart = pdoc->LineFromPosition(SelectionStart());
1303 int lineEnd = pdoc->LineFromPosition(SelectionEnd());
1304 int startPos = SelectionStart();
1305 int line;
1306 for (line=lineStart; line <= lineEnd; line++) {
1307 startPos = SelectionStart(line);
1308 unsigned int chars = SelectionEnd(line) - startPos;
1309 if (0 != chars) {
1310 pdoc->DeleteChars(startPos, chars);
1311 }
1312 }
1313 SetEmptySelection(startPos);
1314 selType = selStream;
1315 pdoc->EndUndoAction();
1316 } else {
1317 int startPos = SelectionStart();
1318 unsigned int chars = SelectionEnd() - startPos;
1319 SetEmptySelection(startPos);
1320 if (0 != chars) {
1321 pdoc->DeleteChars(startPos, chars);
1322 }
1323 }
1324 }
1325
1326 void Editor::ClearAll() {
1327 if (0 != pdoc->Length()) {
1328 pdoc->DeleteChars(0, pdoc->Length());
1329 }
1330 cs.Clear();
1331 anchor = 0;
1332 currentPos = 0;
1333 SetTopLine(0);
1334 SetVerticalScrollPos();
1335 }
1336
1337 void Editor::Cut() {
1338 Copy();
1339 ClearSelection();
1340 }
1341
1342 void Editor::PasteRectangular(int pos, const char *ptr, int len) {
1343 currentPos = pos;
1344 int insertPos = currentPos;
1345 int xInsert = XFromPosition(currentPos);
1346 int line = pdoc->LineFromPosition(currentPos);
1347 bool prevCr = false;
1348 for (int i=0; i<len; i++) {
1349 if ((ptr[i] == '\r') || (ptr[i] == '\n')) {
1350 if ((ptr[i] == '\r') || (!prevCr))
1351 line++;
1352 if (line >= pdoc->LinesTotal()) {
1353 if (pdoc->eolMode != SC_EOL_LF)
1354 pdoc->InsertChar(pdoc->Length(), '\r');
1355 if (pdoc->eolMode != SC_EOL_CR)
1356 pdoc->InsertChar(pdoc->Length(), '\n');
1357 }
1358 currentPos = PositionFromLineX(line, xInsert);
1359 prevCr = ptr[i] == '\r';
1360 } else {
1361 pdoc->InsertString(currentPos, ptr+i, 1);
1362 currentPos++;
1363 insertPos = currentPos;
1364 prevCr = false;
1365 }
1366 }
1367 SetEmptySelection(insertPos);
1368 }
1369
1370 void Editor::Clear() {
1371 if (currentPos == anchor) {
1372 DelChar();
1373 } else {
1374 ClearSelection();
1375 }
1376 SetEmptySelection(currentPos);
1377 }
1378
1379 void Editor::SelectAll() {
1380 SetSelection(0, pdoc->Length());
1381 Redraw();
1382 }
1383
1384 void Editor::Undo() {
1385 if (pdoc->CanUndo()) {
1386 int newPos = pdoc->Undo();
1387 SetEmptySelection(newPos);
1388 EnsureCaretVisible();
1389 }
1390 }
1391
1392 void Editor::Redo() {
1393 if (pdoc->CanRedo()) {
1394 int newPos = pdoc->Redo();
1395 SetEmptySelection(newPos);
1396 EnsureCaretVisible();
1397 }
1398 }
1399
1400 void Editor::DelChar() {
1401 pdoc->DelChar(currentPos);
1402 }
1403
1404 void Editor::DelCharBack() {
1405 if (currentPos == anchor) {
1406 int newPos = pdoc->DelCharBack(currentPos);
1407 SetEmptySelection(newPos);
1408 } else {
1409 ClearSelection();
1410 SetEmptySelection(currentPos);
1411 }
1412 }
1413
1414 void Editor::NotifyFocus(bool) {
1415 }
1416
1417 void Editor::NotifyStyleNeeded(int endStyleNeeded) {
1418 SCNotification scn;
1419 scn.nmhdr.code = SCN_STYLENEEDED;
1420 scn.position = endStyleNeeded;
1421 NotifyParent(scn);
1422 }
1423
1424 void Editor::NotifyChar(char ch) {
1425 SCNotification scn;
1426 scn.nmhdr.code = SCN_CHARADDED;
1427 scn.ch = ch;
1428 NotifyParent(scn);
1429 #ifdef MACRO_SUPPORT
1430 if (recordingMacro) {
1431 char txt[2];
1432 txt[0] = ch;
1433 txt[1] = '\0';
1434 NotifyMacroRecord(EM_REPLACESEL, 0, (LPARAM) txt);
1435 }
1436 #endif
1437 }
1438
1439 void Editor::NotifySavePoint(bool isSavePoint) {
1440 SCNotification scn;
1441 if (isSavePoint) {
1442 scn.nmhdr.code = SCN_SAVEPOINTREACHED;
1443 } else {
1444 scn.nmhdr.code = SCN_SAVEPOINTLEFT;
1445 }
1446 NotifyParent(scn);
1447 }
1448
1449 void Editor::NotifyModifyAttempt() {
1450 SCNotification scn;
1451 scn.nmhdr.code = SCN_MODIFYATTEMPTRO;
1452 NotifyParent(scn);
1453 }
1454
1455 void Editor::NotifyDoubleClick(Point, bool) {
1456 SCNotification scn;
1457 scn.nmhdr.code = SCN_DOUBLECLICK;
1458 NotifyParent(scn);
1459 }
1460
1461 void Editor::NotifyUpdateUI() {
1462 SCNotification scn;
1463 scn.nmhdr.code = SCN_UPDATEUI;
1464 NotifyParent(scn);
1465 }
1466
1467 bool Editor::NotifyMarginClick(Point pt, bool shift, bool ctrl, bool alt) {
1468 int marginClicked = -1;
1469 int x = 0;
1470 for (int margin=0; margin < ViewStyle::margins; margin++) {
1471 if ((pt.x > x) && (pt.x < x + vs.ms[margin].width))
1472 marginClicked = margin;
1473 x += vs.ms[margin].width;
1474 }
1475 if ((marginClicked >= 0) && vs.ms[marginClicked].sensitive) {
1476 SCNotification scn;
1477 scn.nmhdr.code = SCN_MARGINCLICK;
1478 scn.modifiers = (shift ? SCI_SHIFT : 0) | (ctrl ? SCI_CTRL : 0) |
1479 (alt ? SCI_ALT : 0);
1480 scn.position = pdoc->LineStart(LineFromLocation(pt));
1481 scn.margin = marginClicked;
1482 NotifyParent(scn);
1483 return true;
1484 } else {
1485 return false;
1486 }
1487 }
1488
1489 void Editor::NotifyNeedShown(int pos, int len) {
1490 SCNotification scn;
1491 scn.nmhdr.code = SCN_NEEDSHOWN;
1492 scn.position = pos;
1493 scn.length = len;
1494 NotifyParent(scn);
1495 }
1496
1497 // Notifications from document
1498 void Editor::NotifyModifyAttempt(Document*, void *) {
1499 //Platform::DebugPrintf("** Modify Attempt\n");
1500 NotifyModifyAttempt();
1501 }
1502
1503 void Editor::NotifySavePoint(Document*, void *, bool atSavePoint) {
1504 //Platform::DebugPrintf("** Save Point %s\n", atSavePoint ? "On" : "Off");
1505 NotifySavePoint(atSavePoint);
1506 }
1507
1508 void Editor::NotifyModified(Document*, DocModification mh, void *) {
1509 needUpdateUI = true;
1510 if (paintState == painting) {
1511 CheckForChangeOutsidePaint(Range(mh.position, mh.position + mh.length));
1512 } else if (paintState == notPainting) {
1513 if (mh.modificationType & SC_MOD_CHANGESTYLE) {
1514 if (mh.position < pdoc->LineStart(topLine)) {
1515 // Styling performed before this view
1516 Redraw();
1517 } else {
1518 InvalidateRange(mh.position, mh.position + mh.length);
1519 }
1520 } else {
1521 // Move selection and brace highlights
1522 if (mh.modificationType & SC_MOD_INSERTTEXT) {
1523 if (currentPos > mh.position) {
1524 currentPos += mh.length;
1525 }
1526 if (anchor > mh.position) {
1527 anchor += mh.length;
1528 }
1529 if (braces[0] > mh.position) {
1530 braces[0] += mh.length;
1531 }
1532 if (braces[1] > mh.position) {
1533 braces[1] += mh.length;
1534 }
1535 } else { // SC_MOD_DELETETEXT
1536 int endPos = mh.position + mh.length;
1537 if (currentPos > mh.position) {
1538 if (currentPos > endPos) {
1539 currentPos -= mh.length;
1540 } else {
1541 currentPos = endPos;
1542 }
1543 }
1544 if (anchor > mh.position) {
1545 if (anchor > endPos) {
1546 anchor -= mh.length;
1547 } else {
1548 anchor = endPos;
1549 }
1550 }
1551 if (braces[0] > mh.position) {
1552 if (braces[0] > endPos) {
1553 braces[0] -= mh.length;
1554 } else {
1555 braces[0] = endPos;
1556 }
1557 }
1558 if (braces[1] > mh.position) {
1559 if (braces[1] > endPos) {
1560 braces[1] -= mh.length;
1561 } else {
1562 braces[1] = endPos;
1563 }
1564 }
1565 }
1566 if (mh.linesAdded != 0) {
1567
1568 // Update contraction state for inserted and removed lines
1569 // lineOfPos should be calculated in context of state before modification, shouldn't it
1570 int lineOfPos = pdoc->LineFromPosition(mh.position);
1571 if (mh.linesAdded > 0) {
1572 NotifyNeedShown(mh.position, mh.length);
1573 cs.InsertLines(lineOfPos, mh.linesAdded);
1574 } else {
1575 cs.DeleteLines(lineOfPos, -mh.linesAdded);
1576 }
1577 // Avoid scrolling of display if change before current display
1578 if (mh.position < posTopLine) {
1579 int newTop = Platform::Clamp(topLine + mh.linesAdded, 0, MaxScrollPos());
1580 if (newTop != topLine) {
1581 SetTopLine(newTop);
1582 SetVerticalScrollPos();
1583 }
1584 }
1585
1586 //Platform::DebugPrintf("** %x Doc Changed\n", this);
1587 // TODO: could invalidate from mh.startModification to end of screen
1588 //InvalidateRange(mh.position, mh.position + mh.length);
1589 Redraw();
1590 } else {
1591 //Platform::DebugPrintf("** %x Line Changed %d .. %d\n", this,
1592 // mh.position, mh.position + mh.length);
1593 InvalidateRange(mh.position, mh.position + mh.length);
1594 }
1595 }
1596 } // else paintState == paintAbandoned so no need to do anything
1597
1598 if (mh.linesAdded != 0) {
1599 SetScrollBars();
1600 }
1601
1602 // If client wants to see this modification
1603 if (mh.modificationType & modEventMask) {
1604 if ((mh.modificationType & SC_MOD_CHANGESTYLE) == 0) {
1605 // Real modification made to text of document.
1606 NotifyChange(); // Send EN_CHANGE
1607 }
1608 SCNotification scn;
1609 scn.nmhdr.code = SCN_MODIFIED;
1610 scn.position = mh.position;
1611 scn.modificationType = mh.modificationType;
1612 scn.text = mh.text;
1613 scn.length = mh.length;
1614 scn.linesAdded = mh.linesAdded;
1615 scn.line = mh.line;
1616 scn.foldLevelNow = mh.foldLevelNow;
1617 scn.foldLevelPrev = mh.foldLevelPrev;
1618 NotifyParent(scn);
1619 }
1620 }
1621
1622 void Editor::NotifyDeleted(Document *document, void *userData) {
1623 /* Do nothing */
1624 }
1625
1626 #ifdef MACRO_SUPPORT
1627 void Editor::NotifyMacroRecord(UINT iMessage, WPARAM wParam, LPARAM lParam) {
1628
1629 // Enumerates all macroable messages
1630 switch (iMessage) {
1631 case WM_CUT:
1632 case WM_COPY:
1633 case WM_PASTE:
1634 case WM_CLEAR:
1635 case EM_REPLACESEL:
1636 case SCI_ADDTEXT:
1637 case SCI_INSERTTEXT:
1638 case SCI_CLEARALL:
1639 case SCI_SELECTALL:
1640 case SCI_GOTOLINE:
1641 case SCI_GOTOPOS:
1642 case SCI_SEARCHANCHOR:
1643 case SCI_SEARCHNEXT:
1644 case SCI_SEARCHPREV:
1645 case SCI_LINEDOWN:
1646 case SCI_LINEDOWNEXTEND:
1647 case SCI_LINEUP:
1648 case SCI_LINEUPEXTEND:
1649 case SCI_CHARLEFT:
1650 case SCI_CHARLEFTEXTEND:
1651 case SCI_CHARRIGHT:
1652 case SCI_CHARRIGHTEXTEND:
1653 case SCI_WORDLEFT:
1654 case SCI_WORDLEFTEXTEND:
1655 case SCI_WORDRIGHT:
1656 case SCI_WORDRIGHTEXTEND:
1657 case SCI_HOME:
1658 case SCI_HOMEEXTEND:
1659 case SCI_LINEEND:
1660 case SCI_LINEENDEXTEND:
1661 case SCI_DOCUMENTSTART:
1662 case SCI_DOCUMENTSTARTEXTEND:
1663 case SCI_DOCUMENTEND:
1664 case SCI_DOCUMENTENDEXTEND:
1665 case SCI_PAGEUP:
1666 case SCI_PAGEUPEXTEND:
1667 case SCI_PAGEDOWN:
1668 case SCI_PAGEDOWNEXTEND:
1669 case SCI_EDITTOGGLEOVERTYPE:
1670 case SCI_CANCEL:
1671 case SCI_DELETEBACK:
1672 case SCI_TAB:
1673 case SCI_BACKTAB:
1674 case SCI_NEWLINE:
1675 case SCI_FORMFEED:
1676 case SCI_VCHOME:
1677 case SCI_VCHOMEEXTEND:
1678 case SCI_DELWORDLEFT:
1679 case SCI_DELWORDRIGHT:
1680 break;
1681
1682 // Filter out all others (display changes, etc)
1683 default:
1684 // printf("Filtered out %ld of macro recording\n", iMessage);
1685 return;
1686 }
1687
1688 // Send notification
1689 SCNotification scn;
1690 scn.nmhdr.code = SCN_MACRORECORD;
1691 scn.message = iMessage;
1692 scn.wParam = wParam;
1693 scn.lParam = lParam;
1694 NotifyParent(scn);
1695 }
1696 #endif
1697
1698 // Force scroll and keep position relative to top of window
1699 void Editor::PageMove(int direction, bool extend) {
1700 Point pt = LocationFromPosition(currentPos);
1701 int topLineNew = Platform::Clamp(
1702 topLine + direction * LinesToScroll(), 0, MaxScrollPos());
1703 int newPos = PositionFromLocation(
1704 Point(lastXChosen, pt.y + direction * (vs.lineHeight * LinesToScroll())));
1705 if (topLineNew != topLine) {
1706 SetTopLine(topLineNew);
1707 MovePositionTo(newPos, extend);
1708 Redraw();
1709 SetVerticalScrollPos();
1710 } else {
1711 MovePositionTo(newPos, extend);
1712 }
1713 }
1714
1715 int Editor::KeyCommand(UINT iMessage) {
1716 Point pt = LocationFromPosition(currentPos);
1717
1718 switch (iMessage) {
1719 case SCI_LINEDOWN:
1720 MovePositionTo(PositionFromLocation(
1721 Point(lastXChosen, pt.y + vs.lineHeight)));
1722 break;
1723 case SCI_LINEDOWNEXTEND:
1724 MovePositionTo(PositionFromLocation(
1725 Point(lastXChosen, pt.y + vs.lineHeight)), true);
1726 break;
1727 case SCI_LINEUP:
1728 MovePositionTo(PositionFromLocation(
1729 Point(lastXChosen, pt.y - vs.lineHeight)));
1730 break;
1731 case SCI_LINEUPEXTEND:
1732 MovePositionTo(PositionFromLocation(
1733 Point(lastXChosen, pt.y - vs.lineHeight)), true);
1734 break;
1735 case SCI_CHARLEFT:
1736 if (SelectionEmpty()) {
1737 MovePositionTo(MovePositionSoVisible(currentPos - 1, -1));
1738 } else {
1739 MovePositionTo(SelectionStart());
1740 }
1741 SetLastXChosen();
1742 break;
1743 case SCI_CHARLEFTEXTEND:
1744 MovePositionTo(MovePositionSoVisible(currentPos - 1, -1), true);
1745 SetLastXChosen();
1746 break;
1747 case SCI_CHARRIGHT:
1748 if (SelectionEmpty()) {
1749 MovePositionTo(MovePositionSoVisible(currentPos + 1, 1));
1750 } else {
1751 MovePositionTo(SelectionEnd());
1752 }
1753 SetLastXChosen();
1754 break;
1755 case SCI_CHARRIGHTEXTEND:
1756 MovePositionTo(MovePositionSoVisible(currentPos + 1, 1), true);
1757 SetLastXChosen();
1758 break;
1759 case SCI_WORDLEFT:
1760 MovePositionTo(MovePositionSoVisible(pdoc->NextWordStart(currentPos, -1), -1));
1761 SetLastXChosen();
1762 break;
1763 case SCI_WORDLEFTEXTEND:
1764 MovePositionTo(MovePositionSoVisible(pdoc->NextWordStart(currentPos, -1), -1), true);
1765 SetLastXChosen();
1766 break;
1767 case SCI_WORDRIGHT:
1768 MovePositionTo(MovePositionSoVisible(pdoc->NextWordStart(currentPos, 1), 1));
1769 SetLastXChosen();
1770 break;
1771 case SCI_WORDRIGHTEXTEND:
1772 MovePositionTo(MovePositionSoVisible(pdoc->NextWordStart(currentPos, 1), 1), true);
1773 SetLastXChosen();
1774 break;
1775 case SCI_HOME:
1776 MovePositionTo(pdoc->LineStart(pdoc->LineFromPosition(currentPos)));
1777 SetLastXChosen();
1778 break;
1779 case SCI_HOMEEXTEND:
1780 MovePositionTo(pdoc->LineStart(pdoc->LineFromPosition(currentPos)), true);
1781 SetLastXChosen();
1782 break;
1783 case SCI_LINEEND:
1784 MovePositionTo(pdoc->LineEndPosition(currentPos));
1785 SetLastXChosen();
1786 break;
1787 case SCI_LINEENDEXTEND:
1788 MovePositionTo(pdoc->LineEndPosition(currentPos), true);
1789 SetLastXChosen();
1790 break;
1791 case SCI_DOCUMENTSTART:
1792 MovePositionTo(0);
1793 SetLastXChosen();
1794 break;
1795 case SCI_DOCUMENTSTARTEXTEND:
1796 MovePositionTo(0, true);
1797 SetLastXChosen();
1798 break;
1799 case SCI_DOCUMENTEND:
1800 MovePositionTo(pdoc->Length());
1801 SetLastXChosen();
1802 break;
1803 case SCI_DOCUMENTENDEXTEND:
1804 MovePositionTo(pdoc->Length(), true);
1805 SetLastXChosen();
1806 break;
1807 case SCI_PAGEUP:
1808 PageMove( -1);
1809 break;
1810 case SCI_PAGEUPEXTEND:
1811 PageMove( -1, true);
1812 break;
1813 case SCI_PAGEDOWN:
1814 PageMove(1);
1815 break;
1816 case SCI_PAGEDOWNEXTEND:
1817 PageMove(1, true);
1818 break;
1819 case SCI_EDITTOGGLEOVERTYPE:
1820 inOverstrike = !inOverstrike;
1821 DropCaret();
1822 ShowCaretAtCurrentPosition();
1823 break;
1824 case SCI_CANCEL: // Cancel any modes - handled in subclass
1825 // Also unselect text
1826 SetEmptySelection(currentPos);
1827 break;
1828 case SCI_DELETEBACK:
1829 DelCharBack();
1830 EnsureCaretVisible();
1831 break;
1832 case SCI_TAB:
1833 Indent(true);
1834 break;
1835 case SCI_BACKTAB:
1836 Indent(false);
1837 break;
1838 case SCI_NEWLINE:
1839 ClearSelection();
1840 if (pdoc->eolMode == SC_EOL_CRLF) {
1841 pdoc->InsertString(currentPos, "\r\n");
1842 SetEmptySelection(currentPos + 2);
1843 NotifyChar('\r');
1844 NotifyChar('\n');
1845 } else if (pdoc->eolMode == SC_EOL_CR) {
1846 pdoc->InsertChar(currentPos, '\r');
1847 SetEmptySelection(currentPos + 1);
1848 NotifyChar('\r');
1849 } else if (pdoc->eolMode == SC_EOL_LF) {
1850 pdoc->InsertChar(currentPos, '\n');
1851 SetEmptySelection(currentPos + 1);
1852 NotifyChar('\n');
1853 }
1854 SetLastXChosen();
1855 EnsureCaretVisible();
1856 break;
1857 case SCI_FORMFEED:
1858 AddChar('\f');
1859 break;
1860 case SCI_VCHOME:
1861 MovePositionTo(pdoc->VCHomePosition(currentPos));
1862 SetLastXChosen();
1863 break;
1864 case SCI_VCHOMEEXTEND:
1865 MovePositionTo(pdoc->VCHomePosition(currentPos), true);
1866 SetLastXChosen();
1867 break;
1868 case SCI_ZOOMIN:
1869 if (vs.zoomLevel < 20)
1870 vs.zoomLevel++;
1871 InvalidateStyleRedraw();
1872 break;
1873 case SCI_ZOOMOUT:
1874 if (vs.zoomLevel > -10)
1875 vs.zoomLevel--;
1876 InvalidateStyleRedraw();
1877 break;
1878 case SCI_DELWORDLEFT: {
1879 int startWord = pdoc->NextWordStart(currentPos, -1);
1880 pdoc->DeleteChars(startWord, currentPos - startWord);
1881 MovePositionTo(startWord);
1882 }
1883 break;
1884 case SCI_DELWORDRIGHT: {
1885 int endWord = pdoc->NextWordStart(currentPos, 1);
1886 pdoc->DeleteChars(currentPos, endWord - currentPos);
1887 }
1888 break;
1889 }
1890 return 0;
1891 }
1892
1893 int Editor::KeyDefault(int, int) {
1894 return 0;
1895 }
1896
1897 int Editor::KeyDown(int key, bool shift, bool ctrl, bool alt) {
1898 int modifiers = (shift ? SCI_SHIFT : 0) | (ctrl ? SCI_CTRL : 0) |
1899 (alt ? SCI_ALT : 0);
1900 int msg = kmap.Find(key, modifiers);
1901 if (msg)
1902 return WndProc(msg, 0, 0);
1903 else
1904 return KeyDefault(key, modifiers);
1905 }
1906
1907 void Editor::SetWhitespaceVisible(bool view) {
1908 vs.viewWhitespace = view;
1909 }
1910
1911 bool Editor::GetWhitespaceVisible() {
1912 return vs.viewWhitespace;
1913 }
1914
1915 void Editor::Indent(bool forwards) {
1916 //Platform::DebugPrintf("INdent %d\n", forwards);
1917 int lineOfAnchor = pdoc->LineFromPosition(anchor);
1918 int lineCurrentPos = pdoc->LineFromPosition(currentPos);
1919 if (lineOfAnchor == lineCurrentPos) {
1920 ClearSelection();
1921 pdoc->InsertChar(currentPos, '\t');
1922 //pdoc->InsertChar(currentPos++, '\t');
1923 SetEmptySelection(currentPos + 1);
1924 } else {
1925 int anchorPosOnLine = anchor - pdoc->LineStart(lineOfAnchor);
1926 int currentPosPosOnLine = currentPos - pdoc->LineStart(lineCurrentPos);
1927 // Multiple lines selected so indent / dedent
1928 int lineTopSel = Platform::Minimum(lineOfAnchor, lineCurrentPos);
1929 int lineBottomSel = Platform::Maximum(lineOfAnchor, lineCurrentPos);
1930 if (pdoc->LineStart(lineBottomSel) == anchor || pdoc->LineStart(lineBottomSel) == currentPos)
1931 lineBottomSel--; // If not selecting any characters on a line, do not indent
1932 pdoc->BeginUndoAction();
1933 pdoc->Indent(forwards, lineBottomSel, lineTopSel);
1934 pdoc->EndUndoAction();
1935 if (lineOfAnchor < lineCurrentPos) {
1936 if (currentPosPosOnLine == 0)
1937 SetSelection(pdoc->LineStart(lineCurrentPos), pdoc->LineStart(lineOfAnchor));
1938 else
1939 SetSelection(pdoc->LineStart(lineCurrentPos + 1), pdoc->LineStart(lineOfAnchor));
1940 } else {
1941 if (anchorPosOnLine == 0)
1942 SetSelection(pdoc->LineStart(lineCurrentPos), pdoc->LineStart(lineOfAnchor));
1943 else
1944 SetSelection(pdoc->LineStart(lineCurrentPos), pdoc->LineStart(lineOfAnchor + 1));
1945 }
1946 }
1947 }
1948
1949 long Editor::FindText(UINT iMessage, WPARAM wParam, LPARAM lParam) {
1950 FINDTEXTEX *ft = reinterpret_cast<FINDTEXTEX *>(lParam);
1951 int pos = pdoc->FindText(ft->chrg.cpMin, ft->chrg.cpMax, ft->lpstrText,
1952 wParam & FR_MATCHCASE, wParam & FR_WHOLEWORD);
1953 if (pos != -1) {
1954 if (iMessage == EM_FINDTEXTEX) {
1955 ft->chrgText.cpMin = pos;
1956 ft->chrgText.cpMax = pos + strlen(ft->lpstrText);
1957 }
1958 }
1959 return pos;
1960 }
1961
1962 // Relocatable search support : Searches relative to current selection
1963 // point and sets the selection to the found text range with
1964 // each search.
1965
1966 // Anchor following searches at current selection start: This allows
1967 // multiple incremental interactive searches to be macro recorded
1968 // while still setting the selection to found text so the find/select
1969 // operation is self-contained.
1970 void Editor::SearchAnchor() {
1971 searchAnchor = SelectionStart();
1972 }
1973
1974 // Find text from current search anchor: Must call SearchAnchor first.
1975 // Accepts both SCI_SEARCHNEXT and SCI_SEARCHPREV.
1976 // wParam contains search modes : ORed FR_MATCHCASE and FR_WHOLEWORD.
1977 // lParam contains the text to search for.
1978 long Editor::SearchText(UINT iMessage, WPARAM wParam, LPARAM lParam) {
1979 const char *txt = reinterpret_cast<char *>(lParam);
1980 int pos;
1981
1982 if (iMessage == SCI_SEARCHNEXT) {
1983 pos = pdoc->FindText(searchAnchor, pdoc->Length(), txt,
1984 wParam & FR_MATCHCASE,
1985 wParam & FR_WHOLEWORD);
1986 } else {
1987 pos = pdoc->FindText(searchAnchor, 0, txt,
1988 wParam & FR_MATCHCASE,
1989 wParam & FR_WHOLEWORD);
1990 }
1991
1992 if (pos != -1) {
1993 SetSelection(pos, pos + strlen(txt));
1994 }
1995
1996 return pos;
1997 }
1998
1999 void Editor::GoToLine(int lineNo) {
2000 if (lineNo > pdoc->LinesTotal())
2001 lineNo = pdoc->LinesTotal();
2002 if (lineNo < 0)
2003 lineNo = 0;
2004 SetEmptySelection(pdoc->LineStart(lineNo));
2005 ShowCaretAtCurrentPosition();
2006 EnsureCaretVisible();
2007 }
2008
2009 static bool Close(Point pt1, Point pt2) {
2010 if (abs(pt1.x - pt2.x) > 3)
2011 return false;
2012 if (abs(pt1.y - pt2.y) > 3)
2013 return false;
2014 return true;
2015 }
2016
2017 char *Editor::CopyRange(int start, int end) {
2018 char *text = 0;
2019 if (start < end) {
2020 int len = end - start;
2021 text = new char[len + 1];
2022 if (text) {
2023 for (int i = 0; i < len; i++) {
2024 text[i] = pdoc->CharAt(start + i);
2025 }
2026 text[len] = '\0';
2027 }
2028 }
2029 return text;
2030 }
2031
2032 int Editor::SelectionRangeLength() {
2033 if (selType == selRectangle) {
2034 int lineStart = pdoc->LineFromPosition(SelectionStart());
2035 int lineEnd = pdoc->LineFromPosition(SelectionEnd());
2036 int totalSize = 0;
2037 for (int line=lineStart; line <= lineEnd; line++) {
2038 totalSize += SelectionEnd(line) - SelectionStart(line) + 1;
2039 if (pdoc->eolMode == SC_EOL_CRLF)
2040 totalSize++;
2041 }
2042 return totalSize;
2043 } else {
2044 return SelectionEnd() - SelectionStart();
2045 }
2046 }
2047
2048 char *Editor::CopySelectionRange() {
2049 if (selType == selRectangle) {
2050 char *text = 0;
2051 int lineStart = pdoc->LineFromPosition(SelectionStart());
2052 int lineEnd = pdoc->LineFromPosition(SelectionEnd());
2053 int totalSize = SelectionRangeLength();
2054 if (totalSize > 0) {
2055 text = new char[totalSize + 1];
2056 if (text) {
2057 int j = 0;
2058 for (int line=lineStart; line <= lineEnd; line++) {
2059 for (int i=SelectionStart(line);i<SelectionEnd(line);i++) {
2060 text[j++] = pdoc->CharAt(i);
2061 }
2062 if (pdoc->eolMode != SC_EOL_LF)
2063 text[j++] = '\r';
2064 if (pdoc->eolMode != SC_EOL_CR)
2065 text[j++] = '\n';
2066 }
2067 text[totalSize] = '\0';
2068 }
2069 }
2070 return text;
2071 } else {
2072 return CopyRange(SelectionStart(), SelectionEnd());
2073 }
2074 }
2075
2076 void Editor::CopySelectionIntoDrag() {
2077 delete []dragChars;
2078 dragChars = 0;
2079 lenDrag = SelectionRangeLength();
2080 dragChars = CopySelectionRange();
2081 dragIsRectangle = selType == selRectangle;
2082 if (!dragChars) {
2083 lenDrag = 0;
2084 }
2085 }
2086
2087 void Editor::SetDragPosition(int newPos) {
2088 if (newPos >= 0) {
2089 newPos = pdoc->MovePositionOutsideChar(newPos, 1);
2090 posDrop = newPos;
2091 }
2092 if (posDrag != newPos) {
2093 caret.on = true;
2094 SetTicking(true);
2095 InvalidateCaret();
2096 posDrag = newPos;
2097 InvalidateCaret();
2098 }
2099 }
2100
2101 void Editor::StartDrag() {
2102 // Always handled by subclasses
2103 //SetMouseCapture(true);
2104 //wDraw.SetCursor(Window::cursorArrow);
2105 }
2106
2107 void Editor::DropAt(int position, const char *value, bool moving, bool rectangular) {
2108 //Platform::DebugPrintf("DropAt %d\n", inDragDrop);
2109 if (inDragDrop)
2110 dropWentOutside = false;
2111
2112 int positionWasInSelection = PositionInSelection(position);
2113
2114 if ((!inDragDrop) || !(0 == positionWasInSelection)) {
2115
2116 int selStart = SelectionStart();
2117 int selEnd = SelectionEnd();
2118
2119 pdoc->BeginUndoAction();
2120
2121 int positionAfterDeletion = position;
2122 if (inDragDrop && moving) {
2123 // Remove dragged out text
2124 if (rectangular) {
2125 int lineStart = pdoc->LineFromPosition(SelectionStart());
2126 int lineEnd = pdoc->LineFromPosition(SelectionEnd());
2127 for (int line=lineStart; line <= lineEnd; line++) {
2128 int startPos = SelectionStart(line);
2129 int endPos = SelectionEnd(line);
2130 if (position >= startPos) {
2131 if (position > endPos) {
2132 positionAfterDeletion -= endPos - startPos;
2133 } else {
2134 positionAfterDeletion -= position - startPos;
2135 }
2136 }
2137 }
2138 } else {
2139 if (position > selStart) {
2140 positionAfterDeletion -= selEnd - selStart;
2141 }
2142 }
2143 ClearSelection();
2144 }
2145 position = positionAfterDeletion;
2146
2147 if (rectangular) {
2148 PasteRectangular(position, value, strlen(value));
2149 pdoc->EndUndoAction();
2150 // Should try to select new rectangle but it may not be a rectangle now so just select the drop position
2151 SetSelection(position, position);
2152 } else {
2153 position = pdoc->MovePositionOutsideChar(position, currentPos - position);
2154 pdoc->InsertString(position, value);
2155 pdoc->EndUndoAction();
2156 SetSelection(position + strlen(value), position);
2157 }
2158 } else if (inDragDrop) {
2159 SetSelection(position, position);
2160 }
2161 }
2162
2163 static int BeforeInOrAfter(int val, int minim, int maxim) {
2164 if (val < minim)
2165 return -1;
2166 else if (val > maxim)
2167 return 1;
2168 else
2169 return 0;
2170 }
2171
2172 int Editor::PositionInSelection(int pos) {
2173 pos = pdoc->MovePositionOutsideChar(pos, currentPos - pos);
2174 if (selType == selRectangle) {
2175 if (pos < SelectionStart())
2176 return -1;
2177 if (pos > SelectionEnd())
2178 return 1;
2179 int linePos = pdoc->LineFromPosition(pos);
2180 return BeforeInOrAfter(pos, SelectionStart(linePos), SelectionEnd(linePos));
2181 } else {
2182 if (currentPos > anchor) {
2183 return BeforeInOrAfter(pos, anchor, currentPos);
2184 } else if (currentPos < anchor) {
2185 return BeforeInOrAfter(pos, currentPos, anchor);
2186 }
2187 }
2188 return 1;
2189 }
2190
2191 bool Editor::PointInSelection(Point pt) {
2192 // TODO: fix up for rectangular selection
2193 int pos = PositionFromLocation(pt);
2194 if (0 == PositionInSelection(pos)) {
2195 if (pos == SelectionStart()) {
2196 // see if just before selection
2197 Point locStart = LocationFromPosition(pos);
2198 if (pt.x < locStart.x)
2199 return false;
2200 }
2201 if (pos == SelectionEnd()) {
2202 // see if just after selection
2203 Point locEnd = LocationFromPosition(pos);
2204 if (pt.x > locEnd.x)
2205 return false;
2206 }
2207 return true;
2208 }
2209 return false;
2210 }
2211
2212 bool Editor::PointInSelMargin(Point pt) {
2213 // Really means: "Point in a margin"
2214 if (vs.fixedColumnWidth > 0) { // There is a margin
2215 PRectangle rcSelMargin = GetClientRectangle();
2216 rcSelMargin.right = vs.fixedColumnWidth - vs.leftMarginWidth;
2217 return rcSelMargin.Contains(pt);
2218 } else {
2219 return false;
2220 }
2221 }
2222
2223 void Editor::ButtonDown(Point pt, unsigned int curTime, bool shift, bool ctrl, bool alt) {
2224 //Platform::DebugPrintf("Scintilla:ButtonDown %d %d = %d alt=%d\n", curTime, lastClickTime, curTime - lastClickTime, alt);
2225 ptMouseLast = pt;
2226 int newPos = PositionFromLocation(pt);
2227 newPos = pdoc->MovePositionOutsideChar(newPos, currentPos - newPos);
2228 inDragDrop = false;
2229
2230 bool processed = NotifyMarginClick(pt, shift, ctrl, alt);
2231 if (processed)
2232 return;
2233
2234 if (shift) {
2235 SetSelection(newPos);
2236 }
2237 if (((curTime - lastClickTime) < Platform::DoubleClickTime()) && Close(pt, lastClick)) {
2238 //Platform::DebugPrintf("Double click %d %d = %d\n", curTime, lastClickTime, curTime - lastClickTime);
2239 SetMouseCapture(true);
2240 SetEmptySelection(newPos);
2241 bool doubleClick = false;
2242 // Stop mouse button bounce changing selection type
2243 if (curTime != lastClickTime) {
2244 if (selectionType == selChar) {
2245 selectionType = selWord;
2246 doubleClick = true;
2247 } else if (selectionType == selWord) {
2248 selectionType = selLine;
2249 } else {
2250 selectionType = selChar;
2251 originalAnchorPos = currentPos;
2252 }
2253 }
2254
2255 if (selectionType == selWord) {
2256 if (currentPos >= originalAnchorPos) { // Moved forward
2257 SetSelection(pdoc->ExtendWordSelect(currentPos, 1),
2258 pdoc->ExtendWordSelect(originalAnchorPos, -1));
2259 } else { // Moved backward
2260 SetSelection(pdoc->ExtendWordSelect(currentPos, -1),
2261 pdoc->ExtendWordSelect(originalAnchorPos, 1));
2262 }
2263 } else if (selectionType == selLine) {
2264 lineAnchor = LineFromLocation(pt);
2265 SetSelection(pdoc->LineStart(lineAnchor + 1), pdoc->LineStart(lineAnchor));
2266 //Platform::DebugPrintf("Triple click: %d - %d\n", anchor, currentPos);
2267 }
2268 else {
2269 SetEmptySelection(currentPos);
2270 }
2271 //Platform::DebugPrintf("Double click: %d - %d\n", anchor, currentPos);
2272 if (doubleClick)
2273 NotifyDoubleClick(pt, shift);
2274 } else { // Single click
2275 if (PointInSelMargin(pt)) {
2276 if (ctrl) {
2277 SelectAll();
2278 lastClickTime = curTime;
2279 return;
2280 }
2281 lineAnchor = LineFromLocation(pt);
2282 // While experimenting with folding turn off line selection
2283 if (!shift) {
2284 // Single click in margin: select whole line
2285 SetSelection(pdoc->LineStart(lineAnchor + 1), pdoc->LineStart(lineAnchor));
2286 } else {
2287 // Single shift+click in margin: select from anchor to beginning of clicked line
2288 SetSelection(pdoc->LineStart(lineAnchor), anchor);
2289 }
2290 SetDragPosition(invalidPosition);
2291 SetMouseCapture(true);
2292 selectionType = selLine;
2293 } else {
2294 if (!shift) {
2295 inDragDrop = PointInSelection(pt);
2296 }
2297 if (inDragDrop) {
2298 SetMouseCapture(false);
2299 SetDragPosition(newPos);
2300 CopySelectionIntoDrag();
2301 StartDrag();
2302 } else {
2303 selType = alt ? selRectangle : selStream;
2304 xStartSelect = pt.x - vs.fixedColumnWidth + xOffset;
2305 xEndSelect = pt.x - vs.fixedColumnWidth + xOffset;
2306 SetDragPosition(invalidPosition);
2307 SetMouseCapture(true);
2308 if (!shift)
2309 SetEmptySelection(newPos);
2310 selectionType = selChar;
2311 originalAnchorPos = currentPos;
2312 }
2313 }
2314 }
2315 lastClickTime = curTime;
2316 lastXChosen = pt.x;
2317 ShowCaretAtCurrentPosition();
2318 }
2319
2320 void Editor::ButtonMove(Point pt) {
2321 //Platform::DebugPrintf("Move %d %d\n", pt.x, pt.y);
2322 if (HaveMouseCapture()) {
2323 xEndSelect = pt.x - vs.fixedColumnWidth + xOffset;
2324 ptMouseLast = pt;
2325 int movePos = PositionFromLocation(pt);
2326 movePos = pdoc->MovePositionOutsideChar(movePos, currentPos - movePos);
2327 if (posDrag >= 0) {
2328 SetDragPosition(movePos);
2329 } else {
2330 if (selectionType == selChar) {
2331 SetSelection(movePos);
2332 } else if (selectionType == selWord) {
2333 // Continue selecting by word
2334 if (currentPos > originalAnchorPos) { // Moved forward
2335 SetSelection(pdoc->ExtendWordSelect(movePos, 1),
2336 pdoc->ExtendWordSelect(originalAnchorPos, -1));
2337 } else { // Moved backward
2338 SetSelection(pdoc->ExtendWordSelect(movePos, -1),
2339 pdoc->ExtendWordSelect(originalAnchorPos, 1));
2340 }
2341 } else {
2342 // Continue selecting by line
2343 int lineMove = LineFromLocation(pt);
2344 if (lineAnchor < lineMove) {
2345 SetSelection(pdoc->LineStart(lineMove + 1),
2346 pdoc->LineStart(lineAnchor));
2347 } else {
2348 SetSelection(pdoc->LineStart(lineMove),
2349 pdoc->LineStart(lineAnchor + 1));
2350 }
2351 }
2352 }
2353 EnsureCaretVisible(false);
2354 } else {
2355 if (vs.fixedColumnWidth > 0) { // There is a margin
2356 if (PointInSelMargin(pt)) {
2357 wDraw.SetCursor(Window::cursorReverseArrow);
2358 return; // No need to test for selection
2359 }
2360 }
2361 // Display regular (drag) cursor over selection
2362 if (PointInSelection(pt))
2363 wDraw.SetCursor(Window::cursorArrow);
2364 else
2365 wDraw.SetCursor(Window::cursorText);
2366 }
2367
2368 }
2369
2370 void Editor::ButtonUp(Point pt, unsigned int curTime, bool ctrl) {
2371 //Platform::DebugPrintf("ButtonUp %d\n", HaveMouseCapture());
2372 if (HaveMouseCapture()) {
2373 if (PointInSelMargin(pt)) {
2374 wDraw.SetCursor(Window::cursorReverseArrow);
2375 } else {
2376 wDraw.SetCursor(Window::cursorText);
2377 }
2378 xEndSelect = pt.x - vs.fixedColumnWidth + xOffset;
2379 ptMouseLast = pt;
2380 SetMouseCapture(false);
2381 int newPos = PositionFromLocation(pt);
2382 newPos = pdoc->MovePositionOutsideChar(newPos, currentPos - newPos);
2383 if (inDragDrop) {
2384 int selStart = SelectionStart();
2385 int selEnd = SelectionEnd();
2386 if (selStart < selEnd) {
2387 if (dragChars && lenDrag) {
2388 if (ctrl) {
2389 pdoc->InsertString(newPos, dragChars, lenDrag);
2390 SetSelection(newPos, newPos + lenDrag);
2391 } else if (newPos < selStart) {
2392 pdoc->DeleteChars(selStart, lenDrag);
2393 pdoc->InsertString(newPos, dragChars, lenDrag);
2394 SetSelection(newPos, newPos + lenDrag);
2395 } else if (newPos > selEnd) {
2396 pdoc->DeleteChars(selStart, lenDrag);
2397 newPos -= lenDrag;
2398 pdoc->InsertString(newPos, dragChars, lenDrag);
2399 SetSelection(newPos, newPos + lenDrag);
2400 } else {
2401 SetEmptySelection(newPos);
2402 }
2403 delete []dragChars;
2404 dragChars = 0;
2405 lenDrag = 0;
2406 }
2407 selectionType = selChar;
2408 }
2409 } else {
2410 if (selectionType == selChar) {
2411 SetSelection(newPos);
2412 }
2413 }
2414 lastClickTime = curTime;
2415 lastClick = pt;
2416 lastXChosen = pt.x;
2417 inDragDrop = false;
2418 EnsureCaretVisible(false);
2419 }
2420 }
2421
2422 // Called frequently to perform background UI including
2423 // caret blinking and automatic scrolling.
2424 void Editor::Tick() {
2425 if (HaveMouseCapture()) {
2426 // Auto scroll
2427 ButtonMove(ptMouseLast);
2428 }
2429 if (caret.period > 0) {
2430 timer.ticksToWait -= timer.tickSize;
2431 if (timer.ticksToWait <= 0) {
2432 caret.on = !caret.on;
2433 timer.ticksToWait = caret.period;
2434 InvalidateCaret();
2435 }
2436 }
2437 }
2438
2439 static bool IsIn(int a, int minimum, int maximum) {
2440 return (a >= minimum) && (a <= maximum);
2441 }
2442
2443 static bool IsOverlap(int mina, int maxa, int minb, int maxb) {
2444 return
2445 IsIn(mina, minb, maxb) ||
2446 IsIn(maxa, minb, maxb) ||
2447 IsIn(minb, mina, maxa) ||
2448 IsIn(maxb, mina, maxa);
2449 }
2450
2451 void Editor::CheckForChangeOutsidePaint(Range r) {
2452 if (paintState == painting && !paintingAllText) {
2453 //Platform::DebugPrintf("Checking range in paint %d-%d\n", r.start, r.end);
2454 if (!r.Valid())
2455 return;
2456
2457 PRectangle rcText = GetTextRectangle();
2458 // Determine number of lines displayed including a possible partially displayed last line
2459 int linesDisplayed = (rcText.bottom - rcText.top - 1) / vs.lineHeight + 1;
2460 int bottomLine = topLine + linesDisplayed - 1;
2461
2462 int lineRangeStart = cs.DisplayFromDoc(pdoc->LineFromPosition(r.start));
2463 int lineRangeEnd = cs.DisplayFromDoc(pdoc->LineFromPosition(r.end));
2464 if (!IsOverlap(topLine, bottomLine, lineRangeStart, lineRangeEnd)) {
2465 //Platform::DebugPrintf("No overlap (%d-%d) with window(%d-%d)\n",
2466 // lineRangeStart, lineRangeEnd, topLine, bottomLine);
2467 return;
2468 }
2469
2470 // Assert rcPaint contained within or equal to rcText
2471 if (rcPaint.top > rcText.top) {
2472 // does range intersect rcText.top .. rcPaint.top
2473 int paintTopLine = ((rcPaint.top - rcText.top-1) / vs.lineHeight) + topLine;
2474 // paintTopLine is the top line of the paint rectangle or the line just above if that line is completely inside the paint rectangle
2475 if (IsOverlap(topLine, paintTopLine, lineRangeStart, lineRangeEnd)) {
2476 //Platform::DebugPrintf("Change (%d-%d) in top npv(%d-%d)\n",
2477 // lineRangeStart, lineRangeEnd, topLine, paintTopLine);
2478 paintState = paintAbandoned;
2479 return;
2480 }
2481 }
2482 if (rcPaint.bottom < rcText.bottom) {
2483 // does range intersect rcPaint.bottom .. rcText.bottom
2484 int paintBottomLine = ((rcPaint.bottom - rcText.top-1) / vs.lineHeight + 1) + topLine;
2485 // paintTopLine is the bottom line of the paint rectangle or the line just below if that line is completely inside the paint rectangle
2486 if (IsOverlap(paintBottomLine, bottomLine, lineRangeStart, lineRangeEnd)) {
2487 //Platform::DebugPrintf("Change (%d-%d) in bottom npv(%d-%d)\n",
2488 // lineRangeStart, lineRangeEnd, paintBottomLine, bottomLine);
2489 paintState = paintAbandoned;
2490 return;
2491 }
2492 }
2493 }
2494 }
2495
2496 char BraceOpposite(char ch) {
2497 switch (ch) {
2498 case '(': return ')';
2499 case ')': return '(';
2500 case '[': return ']';
2501 case ']': return '[';
2502 case '{': return '}';
2503 case '}': return '{';
2504 case '<': return '>';
2505 case '>': return '<';
2506 default: return '\0';
2507 }
2508 }
2509
2510 // TODO: should be able to extend styled region to find matching brace
2511 // TODO: may need to make DBCS safe
2512 // so should be moved into Document
2513 int Editor::BraceMatch(int position, int maxReStyle) {
2514 char chBrace = pdoc->CharAt(position);
2515 char chSeek = BraceOpposite(chBrace);
2516 if (!chSeek)
2517 return - 1;
2518 char styBrace = pdoc->StyleAt(position) & pdoc->stylingBitsMask;
2519 int direction = -1;
2520 if (chBrace == '(' || chBrace == '[' || chBrace == '{' || chBrace == '<')
2521 direction = 1;
2522 int depth = 1;
2523 position = position + direction;
2524 while ((position >= 0) && (position < pdoc->Length())) {
2525 char chAtPos = pdoc->CharAt(position);
2526 char styAtPos = pdoc->StyleAt(position) & pdoc->stylingBitsMask;
2527 if ((position > pdoc->GetEndStyled()) || (styAtPos == styBrace)) {
2528 if (chAtPos == chBrace)
2529 depth++;
2530 if (chAtPos == chSeek)
2531 depth--;
2532 if (depth == 0)
2533 return position;
2534 }
2535 position = position + direction;
2536 }
2537 return - 1;
2538 }
2539
2540 void Editor::SetBraceHighlight(Position pos0, Position pos1, int matchStyle) {
2541 if ((pos0 != braces[0]) || (pos1 != braces[1]) || (matchStyle != bracesMatchStyle)) {
2542 if ((braces[0] != pos0) || (matchStyle != bracesMatchStyle)) {
2543 CheckForChangeOutsidePaint(Range(braces[0]));
2544 CheckForChangeOutsidePaint(Range(pos0));
2545 braces[0] = pos0;
2546 }
2547 if ((braces[1] != pos1) || (matchStyle != bracesMatchStyle)) {
2548 CheckForChangeOutsidePaint(Range(braces[1]));
2549 CheckForChangeOutsidePaint(Range(pos1));
2550 braces[1] = pos1;
2551 }
2552 bracesMatchStyle = matchStyle;
2553 if (paintState == notPainting) {
2554 Redraw();
2555 }
2556 }
2557 }
2558
2559 void Editor::SetDocPointer(Document *document) {
2560 //Platform::DebugPrintf("** %x setdoc to %x\n", pdoc, document);
2561 pdoc->RemoveWatcher(this, 0);
2562 pdoc->Release();
2563 if (document == NULL) {
2564 pdoc = new Document();
2565 } else {
2566 pdoc = document;
2567 }
2568 pdoc->AddRef();
2569 pdoc->AddWatcher(this, 0);
2570 Redraw();
2571 SetScrollBars();
2572 }
2573
2574 // Recursively expand a fold, making lines visible except where they have an unexpanded parent
2575 void Editor::Expand(int &line, bool doExpand) {
2576 int lineMaxSubord = pdoc->GetLastChild(line);
2577 line++;
2578 while (line <= lineMaxSubord) {
2579 if (doExpand)
2580 cs.SetVisible(line, line, true);
2581 int level = pdoc->GetLevel(line);
2582 if (level & SC_FOLDLEVELHEADERFLAG) {
2583 if (doExpand && cs.GetExpanded(line)) {
2584 Expand(line, true);
2585 } else {
2586 Expand(line, false);
2587 }
2588 } else {
2589 line++;
2590 }
2591 }
2592 }
2593
2594 void Editor::ToggleContraction(int line) {
2595 if (pdoc->GetLevel(line) & SC_FOLDLEVELHEADERFLAG) {
2596 if (cs.GetExpanded(line)) {
2597 int lineMaxSubord = pdoc->GetLastChild(line);
2598 cs.SetExpanded(line, 0);
2599 if (lineMaxSubord > line) {
2600 cs.SetVisible(line+1, lineMaxSubord, false);
2601 SetScrollBars();
2602 Redraw();
2603 }
2604 } else {
2605 cs.SetExpanded(line, 1);
2606 Expand(line, true);
2607 SetScrollBars();
2608 Redraw();
2609 }
2610 }
2611 }
2612
2613 // Recurse up from this line to find any folds that prevent this line from being visible
2614 // and unfold them all.
2615 void Editor::EnsureLineVisible(int line) {
2616 if (!cs.GetVisible(line)) {
2617 int lineParent = pdoc->GetFoldParent(line);
2618 if (lineParent >= 0) {
2619 if (line != lineParent)
2620 EnsureLineVisible(lineParent);
2621 if (!cs.GetExpanded(lineParent)) {
2622 cs.SetExpanded(lineParent, 1);
2623 Expand(lineParent, true);
2624 }
2625 }
2626 SetScrollBars();
2627 Redraw();
2628 }
2629 }
2630
2631 LRESULT Editor::WndProc(UINT iMessage, WPARAM wParam, LPARAM lParam) {
2632 //Platform::DebugPrintf("S start wnd proc %d %d %d\n",iMessage, wParam, lParam);
2633
2634 // Optional macro recording hook
2635 #ifdef MACRO_SUPPORT
2636 if (recordingMacro)
2637 NotifyMacroRecord(iMessage, wParam, lParam);
2638 #endif
2639
2640 switch (iMessage) {
2641
2642 case WM_GETTEXT:
2643 {
2644 if (lParam == 0)
2645 return 0;
2646 char *ptr = reinterpret_cast<char *>(lParam);
2647 unsigned int iChar = 0;
2648 for (; iChar < wParam-1; iChar++)
2649 ptr[iChar] = pdoc->CharAt(iChar);
2650 ptr[iChar] = '\0';
2651 return iChar;
2652 }
2653
2654 case WM_SETTEXT:
2655 {
2656 if (lParam == 0)
2657 return FALSE;
2658 pdoc->DeleteChars(0, pdoc->Length());
2659 SetEmptySelection(0);
2660 pdoc->InsertString(0, reinterpret_cast<char *>(lParam));
2661 return TRUE;
2662 }
2663
2664 case WM_GETTEXTLENGTH:
2665 return pdoc->Length();
2666
2667 case WM_NOTIFY:
2668 //Platform::DebugPrintf("S notify %d %d\n", wParam, lParam);
2669 break;
2670
2671 case WM_CUT:
2672 Cut();
2673 SetLastXChosen();
2674 break;
2675
2676 case WM_COPY:
2677 Copy();
2678 break;
2679
2680 case WM_PASTE:
2681 Paste();
2682 SetLastXChosen();
2683 break;
2684
2685 case WM_CLEAR:
2686 Clear();
2687 SetLastXChosen();
2688 break;
2689
2690 case WM_UNDO:
2691 Undo();
2692 SetLastXChosen();
2693 break;
2694
2695 // Edit control mesages
2696
2697 // Not supported (no-ops):
2698 // EM_GETWORDBREAKPROC
2699 // EM_GETWORDBREAKPROCEX
2700 // EM_SETWORDBREAKPROC
2701 // EM_SETWORDBREAKPROCEX
2702 // EM_GETWORDWRAPMODE
2703 // EM_SETWORDWRAPMODE
2704 // EM_LIMITTEXT
2705 // EM_EXLIMITTEXT
2706 // EM_SETRECT
2707 // EM_SETRECTNP
2708 // EM_FMTLINES
2709 // EM_GETHANDLE
2710 // EM_SETHANDLE
2711 // EM_GETPASSWORDCHAR
2712 // EM_SETPASSWORDCHAR
2713 // EM_SETTABSTOPS
2714 // EM_FINDWORDBREAK
2715 // EM_GETCHARFORMAT
2716 // EM_SETCHARFORMAT
2717 // EM_GETOLEINTERFACE
2718 // EM_SETOLEINTERFACE
2719 // EM_SETOLECALLBACK
2720 // EM_GETPARAFORMAT
2721 // EM_SETPARAFORMAT
2722 // EM_PASTESPECIAL
2723 // EM_REQUESTRESIZE
2724 // EM_GETBKGNDCOLOR
2725 // EM_SETBKGNDCOLOR
2726 // EM_STREAMIN
2727 // EM_STREAMOUT
2728 // EM_GETIMECOLOR
2729 // EM_SETIMECOLOR
2730 // EM_GETIMEOPTIONS
2731 // EM_SETIMEOPTIONS
2732 // EM_GETOPTIONS
2733 // EM_SETOPTIONS
2734 // EM_GETPUNCTUATION
2735 // EM_SETPUNCTUATION
2736 // EM_GETTHUMB
2737
2738 // Not supported but should be:
2739 // EM_GETEVENTMASK
2740 // EM_SETEVENTMASK
2741 // For printing:
2742 // EM_DISPLAYBAND
2743 // EM_SETTARGETDEVICE
2744
2745 case EM_CANUNDO:
2746 return pdoc->CanUndo() ? TRUE : FALSE;
2747
2748 case EM_UNDO:
2749 Undo();
2750 break;
2751
2752 case EM_EMPTYUNDOBUFFER:
2753 pdoc->DeleteUndoHistory();
2754 return 0;
2755
2756 case EM_GETFIRSTVISIBLELINE:
2757 return topLine;
2758
2759 case EM_GETLINE: {
2760 if (lParam == 0)
2761 return 0;
2762 int lineStart = pdoc->LineStart(wParam);
2763 int lineEnd = pdoc->LineStart(wParam + 1);
2764 char *ptr = reinterpret_cast<char *>(lParam);
2765 WORD *pBufSize = reinterpret_cast<WORD *>(lParam);
2766 if (*pBufSize < lineEnd - lineStart) {
2767 ptr[0] = '\0'; // If no characters copied have to put a NUL into buffer
2768 return 0;
2769 }
2770 int iPlace = 0;
2771 for (int iChar = lineStart; iChar < lineEnd; iChar++)
2772 ptr[iPlace++] = pdoc->CharAt(iChar);
2773 return iPlace;
2774 }
2775
2776 case EM_GETLINECOUNT:
2777 if (pdoc->LinesTotal() == 0)
2778 return 1;
2779 else
2780 return pdoc->LinesTotal();
2781
2782 case EM_GETMODIFY:
2783 return !pdoc->IsSavePoint();
2784
2785 case EM_SETMODIFY:
2786 // Not really supported now that there is the save point stuff
2787 //pdoc->isModified = wParam;
2788 //return pdoc->isModified;
2789 return false;
2790
2791 case EM_GETRECT:
2792 if (lParam == 0)
2793 return 0;
2794 *(reinterpret_cast<PRectangle *>(lParam)) = GetClientRectangle();
2795 break;
2796
2797 case EM_GETSEL:
2798 if (wParam)
2799 *reinterpret_cast<int *>(wParam) = SelectionStart();
2800 if (lParam)
2801 *reinterpret_cast<int *>(lParam) = SelectionEnd();
2802 return MAKELONG(SelectionStart(), SelectionEnd());
2803
2804 case EM_EXGETSEL: {
2805 if (lParam == 0)
2806 return 0;
2807 CHARRANGE *pCR = reinterpret_cast<CHARRANGE *>(lParam);
2808 pCR->cpMin = SelectionStart();
2809 pCR->cpMax = SelectionEnd();
2810 }
2811 break;
2812
2813 case EM_SETSEL: {
2814 int nStart = static_cast<int>(wParam);
2815 int nEnd = static_cast<int>(lParam);
2816 if (nEnd < 0)
2817 nEnd = pdoc->Length();
2818 if (nStart < 0)
2819 nStart = nEnd; // Remove selection
2820 SetSelection(nEnd, nStart);
2821 EnsureCaretVisible();
2822 }
2823 break;
2824
2825 case EM_EXSETSEL: {
2826 if (lParam == 0)
2827 return 0;
2828 CHARRANGE *pCR = reinterpret_cast<CHARRANGE *>(lParam);
2829 if (pCR->cpMax == -1) {
2830 SetSelection(pCR->cpMin, pdoc->Length());
2831 } else {
2832 SetSelection(pCR->cpMin, pCR->cpMax);
2833 }
2834 EnsureCaretVisible();
2835 return pdoc->LineFromPosition(SelectionStart());
2836 }
2837
2838 case EM_GETSELTEXT: {
2839 if (lParam == 0)
2840 return 0;
2841 char *ptr = reinterpret_cast<char *>(lParam);
2842 int selSize = SelectionRangeLength();
2843 char *text = CopySelectionRange();
2844 int iChar = 0;
2845 if (text) {
2846 for (; iChar < selSize; iChar++)
2847 ptr[iChar] = text[iChar];
2848 ptr[iChar] = '\0';
2849 delete []text;
2850 }
2851 return iChar;
2852 }
2853
2854 case EM_GETWORDBREAKPROC:
2855 return 0;
2856
2857 case EM_SETWORDBREAKPROC:
2858 break;
2859
2860 case EM_LIMITTEXT:
2861 // wParam holds the number of characters control should be limited to
2862 break;
2863
2864 case EM_GETLIMITTEXT:
2865 return 0xffffffff;
2866
2867 case EM_GETOLEINTERFACE:
2868 return 0;
2869
2870 case EM_LINEFROMCHAR:
2871 if (static_cast<int>(wParam) < 0)
2872 wParam = SelectionStart();
2873 return pdoc->LineFromPosition(wParam);
2874
2875 case EM_EXLINEFROMCHAR:
2876 if (static_cast<int>(lParam) < 0)
2877 lParam = SelectionStart(); // Not specified, but probably OK
2878 return pdoc->LineFromPosition(lParam);
2879
2880 case EM_LINEINDEX:
2881 if (static_cast<int>(wParam) < 0)
2882 wParam = pdoc->LineFromPosition(SelectionStart());
2883 if (wParam == 0)
2884 return 0; // Even if there is no text, there is a first line that starts at 0
2885 if (static_cast<int>(wParam) > pdoc->LinesTotal())
2886 return - 1;
2887 //if (wParam > pdoc->LineFromPosition(pdoc->Length())) // Useful test, anyway...
2888 // return -1;
2889 return pdoc->LineStart(wParam);
2890
2891 case EM_LINELENGTH:
2892 {
2893 if (static_cast<int>(wParam) < 0) // Who use this anyway?
2894 return 0; // Should be... Too complex to describe here, see MS specs!
2895 if (static_cast<int>(wParam) > pdoc->Length()) // Useful test, anyway...
2896 return 0;
2897 int line = pdoc->LineFromPosition(wParam);
2898 int charsOnLine = 0;
2899 for (int pos = pdoc->LineStart(line); pos < pdoc->LineStart(line + 1); pos++) {
2900 if ((pdoc->CharAt(pos) != '\r') && (pdoc->CharAt(pos) != '\n'))
2901 charsOnLine++;
2902 }
2903 return charsOnLine;
2904 }
2905
2906 // Replacement of the old Scintilla interpretation of EM_LINELENGTH
2907 case SCI_LINELENGTH:
2908 if ((static_cast<int>(wParam) < 0) ||
2909 (static_cast<int>(wParam) > pdoc->LineFromPosition(pdoc->Length())))
2910 return 0;
2911 return pdoc->LineStart(wParam + 1) - pdoc->LineStart(wParam);
2912
2913 case EM_REPLACESEL: {
2914 if (lParam == 0)
2915 return 0;
2916 pdoc->BeginUndoAction();
2917 ClearSelection();
2918 char *replacement = reinterpret_cast<char *>(lParam);
2919 pdoc->InsertString(currentPos, replacement);
2920 pdoc->EndUndoAction();
2921 SetEmptySelection(currentPos + strlen(replacement));
2922 EnsureCaretVisible();
2923 }
2924 break;
2925
2926 case EM_LINESCROLL:
2927 ScrollTo(topLine + lParam);
2928 HorizontalScrollTo(xOffset + wParam * vs.spaceWidth);
2929 return TRUE;
2930
2931 case EM_SCROLLCARET:
2932 EnsureCaretVisible();
2933 break;
2934
2935 case EM_SETREADONLY:
2936 pdoc->SetReadOnly(wParam);
2937 return TRUE;
2938
2939 case EM_SETRECT:
2940 break;
2941
2942 case EM_CANPASTE:
2943 return 1;
2944
2945 case EM_CHARFROMPOS: {
2946 if (lParam == 0)
2947 return 0;
2948 Point *ppt = reinterpret_cast<Point *>(lParam);
2949 int pos = PositionFromLocation(*ppt);
2950 int line = pdoc->LineFromPosition(pos);
2951 return MAKELONG(pos, line);
2952 }
2953
2954 case EM_POSFROMCHAR: {
2955 // The MS specs for this have changed 3 times: using the RichEdit 3 version
2956 if (wParam == 0)
2957 return 0;
2958 Point *ppt = reinterpret_cast<Point *>(wParam);
2959 if (lParam < 0) {
2960 *ppt = Point(0, 0);
2961 } else {
2962 *ppt = LocationFromPosition(lParam);
2963 }
2964 return 0;
2965 }
2966
2967 case EM_FINDTEXT:
2968 return FindText(iMessage, wParam, lParam);
2969
2970 case EM_FINDTEXTEX:
2971 return FindText(iMessage, wParam, lParam);
2972
2973 case EM_GETTEXTRANGE: {
2974 if (lParam == 0)
2975 return 0;
2976 TEXTRANGE *tr = reinterpret_cast<TEXTRANGE *>(lParam);
2977 int cpMax = tr->chrg.cpMax;
2978 if (cpMax == -1)
2979 cpMax = pdoc->Length();
2980 int len = cpMax - tr->chrg.cpMin; // No -1 as cpMin and cpMax are referring to inter character positions
2981 pdoc->GetCharRange(tr->lpstrText, tr->chrg.cpMin, len);
2982 // Spec says copied text is terminated with a NUL
2983 tr->lpstrText[len] = '\0';
2984 return len; // Not including NUL
2985 }
2986
2987 case EM_SELECTIONTYPE:
2988 if (currentPos == anchor)
2989 return SEL_EMPTY;
2990 else
2991 return SEL_TEXT;
2992
2993 case EM_HIDESELECTION:
2994 hideSelection = wParam;
2995 Redraw();
2996 break;
2997
2998 case EM_FORMATRANGE:
2999 return FormatRange(wParam, reinterpret_cast<FORMATRANGE *>(lParam));
3000
3001 case EM_GETMARGINS:
3002 return MAKELONG(vs.leftMarginWidth, vs.rightMarginWidth);
3003
3004 case EM_SETMARGINS:
3005 if (wParam & EC_LEFTMARGIN) {
3006 vs.leftMarginWidth = LOWORD(lParam);
3007 }
3008 if (wParam & EC_RIGHTMARGIN) {
3009 vs.rightMarginWidth = HIWORD(lParam);
3010 }
3011 if (wParam == EC_USEFONTINFO) {
3012 vs.leftMarginWidth = vs.aveCharWidth / 2;
3013 vs.rightMarginWidth = vs.aveCharWidth / 2;
3014 }
3015 InvalidateStyleRedraw();
3016 break;
3017
3018 // Control specific mesages
3019
3020 case SCI_ADDTEXT: {
3021 if (lParam == 0)
3022 return 0;
3023 pdoc->InsertString(CurrentPosition(), reinterpret_cast<char *>(lParam), wParam);
3024 SetEmptySelection(currentPos + wParam);
3025 return 0;
3026 }
3027
3028 case SCI_ADDSTYLEDTEXT: {
3029 if (lParam == 0)
3030 return 0;
3031 pdoc->InsertStyledString(CurrentPosition() * 2, reinterpret_cast<char *>(lParam), wParam);
3032 SetEmptySelection(currentPos + wParam / 2);
3033 return 0;
3034 }
3035
3036 case SCI_INSERTTEXT: {
3037 if (lParam == 0)
3038 return 0;
3039 int insertPos = wParam;
3040 if (static_cast<short>(wParam) == -1)
3041 insertPos = CurrentPosition();
3042 int newCurrent = CurrentPosition();
3043 int newAnchor = anchor;
3044 char *sz = reinterpret_cast<char *>(lParam);
3045 pdoc->InsertString(insertPos, sz);
3046 if (newCurrent > insertPos)
3047 newCurrent += strlen(sz);
3048 if (newAnchor > insertPos)
3049 newAnchor += strlen(sz);
3050 SetEmptySelection(newCurrent);
3051 return 0;
3052 }
3053
3054 case SCI_CLEARALL:
3055 ClearAll();
3056 return 0;
3057
3058 case SCI_SETUNDOCOLLECTION:
3059 pdoc->SetUndoCollection(static_cast<enum undoCollectionType>(wParam));
3060 return 0;
3061
3062 #ifdef INCLUDE_DEPRECATED_FEATURES
3063 case SCI_APPENDUNDOSTARTACTION:
3064 pdoc->AppendUndoStartAction();
3065 return 0;
3066 #endif
3067
3068 case SCI_BEGINUNDOACTION:
3069 pdoc->BeginUndoAction();
3070 return 0;
3071
3072 case SCI_ENDUNDOACTION:
3073 pdoc->EndUndoAction();
3074 return 0;
3075
3076 case SCI_GETCARETPERIOD:
3077 return caret.period;
3078
3079 case SCI_SETCARETPERIOD:
3080 caret.period = wParam;
3081 break;
3082
3083 case SCI_SETWORDCHARS: {
3084 if (lParam == 0)
3085 return 0;
3086 pdoc->SetWordChars(reinterpret_cast<unsigned char *>(lParam));
3087 }
3088 break;
3089
3090 case SCI_GETLENGTH:
3091 return pdoc->Length();
3092
3093 case SCI_GETCHARAT:
3094 return pdoc->CharAt(wParam);
3095
3096 case SCI_GETCURRENTPOS:
3097 return currentPos;
3098
3099 case SCI_GETANCHOR:
3100 return anchor;
3101
3102 case SCI_GETSTYLEAT:
3103 if (static_cast<short>(wParam) >= pdoc->Length())
3104 return 0;
3105 else
3106 return pdoc->StyleAt(wParam);
3107
3108 case SCI_REDO:
3109 Redo();
3110 break;
3111
3112 case SCI_SELECTALL:
3113 SelectAll();
3114 break;
3115
3116 case SCI_SETSAVEPOINT:
3117 pdoc->SetSavePoint();
3118 NotifySavePoint(true);
3119 break;
3120
3121 case SCI_GETSTYLEDTEXT: {
3122 if (lParam == 0)
3123 return 0;
3124 TEXTRANGE *tr = reinterpret_cast<TEXTRANGE *>(lParam);
3125 int iPlace = 0;
3126 for (int iChar = tr->chrg.cpMin; iChar < tr->chrg.cpMax; iChar++) {
3127 tr->lpstrText[iPlace++] = pdoc->CharAt(iChar);
3128 tr->lpstrText[iPlace++] = pdoc->StyleAt(iChar);
3129 }
3130 tr->lpstrText[iPlace] = '\0';
3131 tr->lpstrText[iPlace + 1] = '\0';
3132 return iPlace;
3133 }
3134
3135 case SCI_CANREDO:
3136 return pdoc->CanRedo() ? TRUE : FALSE;
3137
3138 case SCI_MARKERLINEFROMHANDLE:
3139 return pdoc->LineFromHandle(wParam);
3140
3141 case SCI_MARKERDELETEHANDLE:
3142 pdoc->DeleteMarkFromHandle(wParam);
3143 break;
3144
3145 case SCI_GETVIEWWS:
3146 return vs.viewWhitespace;
3147
3148 case SCI_SETVIEWWS:
3149 vs.viewWhitespace = wParam;
3150 Redraw();
3151 break;
3152
3153 case SCI_GOTOLINE:
3154 GoToLine(wParam);
3155 break;
3156
3157 case SCI_GOTOPOS:
3158 SetEmptySelection(wParam);
3159 EnsureCaretVisible();
3160 Redraw();
3161 break;
3162
3163 case SCI_SETANCHOR:
3164 SetSelection(currentPos, wParam);
3165 break;
3166
3167 case SCI_GETCURLINE: {
3168 if (lParam == 0)
3169 return 0;
3170 int lineCurrentPos = pdoc->LineFromPosition(currentPos);
3171 int lineStart = pdoc->LineStart(lineCurrentPos);
3172 unsigned int lineEnd = pdoc->LineStart(lineCurrentPos + 1);
3173 char *ptr = reinterpret_cast<char *>(lParam);
3174 unsigned int iPlace = 0;
3175 for (unsigned int iChar = lineStart; iChar < lineEnd && iPlace < wParam; iChar++)
3176 ptr[iPlace++] = pdoc->CharAt(iChar);
3177 ptr[iPlace] = '\0';
3178 return currentPos - lineStart;
3179 }
3180
3181 case SCI_GETENDSTYLED:
3182 return pdoc->GetEndStyled();
3183
3184 case SCI_GETEOLMODE:
3185 return pdoc->eolMode;
3186
3187 case SCI_SETEOLMODE:
3188 pdoc->eolMode = wParam;
3189 break;
3190
3191 case SCI_STARTSTYLING:
3192 pdoc->StartStyling(wParam, lParam);
3193 break;
3194
3195 case SCI_SETSTYLING:
3196 pdoc->SetStyleFor(wParam, lParam);
3197 break;
3198
3199 case SCI_SETSTYLINGEX: // Specify a complete styling buffer
3200 if (lParam == 0)
3201 return 0;
3202 pdoc->SetStyles(wParam, reinterpret_cast<char *>(lParam));
3203 break;
3204
3205 #ifdef INCLUDE_DEPRECATED_FEATURES
3206 case SCI_SETMARGINWIDTH:
3207 if (wParam < 100) {
3208 vs.ms[1].width = wParam;
3209 }
3210 InvalidateStyleRedraw();
3211 break;
3212 #endif
3213
3214 case SCI_SETBUFFEREDDRAW:
3215 bufferedDraw = wParam;
3216 break;
3217
3218 case SCI_SETTABWIDTH:
3219 if (wParam > 0)
3220 pdoc->tabInChars = wParam;
3221 InvalidateStyleRedraw();
3222 break;
3223
3224 case SCI_SETCODEPAGE:
3225 pdoc->dbcsCodePage = wParam;
3226 break;
3227
3228 #ifdef INCLUDE_DEPRECATED_FEATURES
3229 case SCI_SETLINENUMBERWIDTH:
3230 if (wParam < 200) {
3231 vs.ms[0].width = wParam;
3232 }
3233 InvalidateStyleRedraw();
3234 break;
3235 #endif
3236
3237 case SCI_SETUSEPALETTE:
3238 palette.allowRealization = wParam;
3239 InvalidateStyleRedraw();
3240 break;
3241
3242 // Marker definition and setting
3243 case SCI_MARKERDEFINE:
3244 if (wParam <= MARKER_MAX)
3245 vs.markers[wParam].markType = lParam;
3246 InvalidateStyleData();
3247 RedrawSelMargin();
3248 break;
3249 case SCI_MARKERSETFORE:
3250 if (wParam <= MARKER_MAX)
3251 vs.markers[wParam].fore.desired = Colour(lParam);
3252 InvalidateStyleData();
3253 RedrawSelMargin();
3254 break;
3255 case SCI_MARKERSETBACK:
3256 if (wParam <= MARKER_MAX)
3257 vs.markers[wParam].back.desired = Colour(lParam);
3258 InvalidateStyleData();
3259 RedrawSelMargin();
3260 break;
3261 case SCI_MARKERADD: {
3262 int markerID = pdoc->AddMark(wParam, lParam);
3263 RedrawSelMargin();
3264 return markerID;
3265 }
3266
3267 case SCI_MARKERDELETE:
3268 pdoc->DeleteMark(wParam, lParam);
3269 RedrawSelMargin();
3270 break;
3271
3272 case SCI_MARKERDELETEALL:
3273 pdoc->DeleteAllMarks(static_cast<int>(wParam));
3274 RedrawSelMargin();
3275 break;
3276
3277 case SCI_MARKERGET:
3278 return pdoc->GetMark(wParam);
3279
3280 case SCI_MARKERNEXT: {
3281 int lt = pdoc->LinesTotal();
3282 for (int iLine = wParam; iLine < lt; iLine++) {
3283 if ((pdoc->GetMark(iLine) & lParam) != 0)
3284 return iLine;
3285 }
3286 }
3287 return -1;
3288
3289 case SCI_MARKERPREVIOUS: {
3290 for (int iLine = wParam; iLine >= 0; iLine--) {
3291 if ((pdoc->GetMark(iLine) & lParam) != 0)
3292 return iLine;
3293 }
3294 }
3295 return -1;
3296
3297 case SCI_SETMARGINTYPEN:
3298 if (wParam >= 0 && wParam < ViewStyle::margins) {
3299 vs.ms[wParam].symbol = (lParam == SC_MARGIN_SYMBOL);
3300 InvalidateStyleRedraw();
3301 }
3302 break;
3303
3304 case SCI_GETMARGINTYPEN:
3305 if (wParam >= 0 && wParam < ViewStyle::margins)
3306 return vs.ms[wParam].symbol ? SC_MARGIN_SYMBOL : SC_MARGIN_NUMBER;
3307 else
3308 return 0;
3309
3310 case SCI_SETMARGINWIDTHN:
3311 if (wParam >= 0 && wParam < ViewStyle::margins) {
3312 vs.ms[wParam].width = lParam;
3313 InvalidateStyleRedraw();
3314 }
3315 break;
3316
3317 case SCI_GETMARGINWIDTHN:
3318 if (wParam >= 0 && wParam < ViewStyle::margins)
3319 return vs.ms[wParam].width;
3320 else
3321 return 0;
3322
3323 case SCI_SETMARGINMASKN:
3324 if (wParam >= 0 && wParam < ViewStyle::margins) {
3325 vs.ms[wParam].mask = lParam;
3326 InvalidateStyleRedraw();
3327 }
3328 break;
3329
3330 case SCI_GETMARGINMASKN:
3331 if (wParam >= 0 && wParam < ViewStyle::margins)
3332 return vs.ms[wParam].mask;
3333 else
3334 return 0;
3335
3336 case SCI_SETMARGINSENSITIVEN:
3337 if (wParam >= 0 && wParam < ViewStyle::margins) {
3338 vs.ms[wParam].sensitive = lParam;
3339 InvalidateStyleRedraw();
3340 }
3341 break;
3342
3343 case SCI_GETMARGINSENSITIVEN:
3344 if (wParam >= 0 && wParam < ViewStyle::margins)
3345 return vs.ms[wParam].sensitive ? 1 : 0;
3346 else
3347 return 0;
3348
3349 case SCI_STYLECLEARALL:
3350 vs.ClearStyles();
3351 InvalidateStyleRedraw();
3352 break;
3353
3354 case SCI_STYLESETFORE:
3355 if (wParam <= STYLE_MAX) {
3356 vs.styles[wParam].fore.desired = Colour(lParam);
3357 InvalidateStyleRedraw();
3358 }
3359 break;
3360 case SCI_STYLESETBACK:
3361 if (wParam <= STYLE_MAX) {
3362 vs.styles[wParam].back.desired = Colour(lParam);
3363 InvalidateStyleRedraw();
3364 }
3365 break;
3366 case SCI_STYLESETBOLD:
3367 if (wParam <= STYLE_MAX) {
3368 vs.styles[wParam].bold = lParam;
3369 InvalidateStyleRedraw();
3370 }
3371 break;
3372 case SCI_STYLESETITALIC:
3373 if (wParam <= STYLE_MAX) {
3374 vs.styles[wParam].italic = lParam;
3375 InvalidateStyleRedraw();
3376 }
3377 break;
3378 case SCI_STYLESETEOLFILLED:
3379 if (wParam <= STYLE_MAX) {
3380 vs.styles[wParam].eolFilled = lParam;
3381 InvalidateStyleRedraw();
3382 }
3383 break;
3384 case SCI_STYLESETSIZE:
3385 if (wParam <= STYLE_MAX) {
3386 vs.styles[wParam].size = lParam;
3387 InvalidateStyleRedraw();
3388 }
3389 break;
3390 case SCI_STYLESETFONT:
3391 if (lParam == 0)
3392 return 0;
3393 if (wParam <= STYLE_MAX) {
3394 strcpy(vs.styles[wParam].fontName, reinterpret_cast<char *>(lParam));
3395 InvalidateStyleRedraw();
3396 }
3397 break;
3398
3399 case SCI_STYLERESETDEFAULT:
3400 vs.ResetDefaultStyle();
3401 InvalidateStyleRedraw();
3402 break;
3403
3404 case SCI_SETSTYLEBITS:
3405 pdoc->SetStylingBits(wParam);
3406 break;
3407
3408 case SCI_GETSTYLEBITS:
3409 return pdoc->stylingBits;
3410
3411 case SCI_SETLINESTATE:
3412 return pdoc->SetLineState(wParam, lParam);
3413
3414 case SCI_GETLINESTATE:
3415 return pdoc->GetLineState(wParam);
3416
3417 case SCI_GETMAXLINESTATE:
3418 return pdoc->GetMaxLineState();
3419
3420 // Folding messages
3421
3422 case SCI_VISIBLEFROMDOCLINE:
3423 return cs.DisplayFromDoc(wParam);
3424
3425 case SCI_DOCLINEFROMVISIBLE:
3426 return cs.DocFromDisplay(wParam);
3427
3428 case SCI_SETFOLDLEVEL: {
3429 int prev = pdoc->SetLevel(wParam, lParam);
3430 if (prev != lParam)
3431 RedrawSelMargin();
3432 return prev;
3433 }
3434
3435 case SCI_GETFOLDLEVEL:
3436 return pdoc->GetLevel(wParam);
3437
3438 case SCI_GETLASTCHILD:
3439 return pdoc->GetLastChild(wParam, lParam);
3440
3441 case SCI_GETFOLDPARENT:
3442 return pdoc->GetFoldParent(wParam);
3443
3444 case SCI_SHOWLINES:
3445 cs.SetVisible(wParam, lParam, true);
3446 SetScrollBars();
3447 Redraw();
3448 break;
3449
3450 case SCI_HIDELINES:
3451 cs.SetVisible(wParam, lParam, false);
3452 SetScrollBars();
3453 Redraw();
3454 break;
3455
3456 case SCI_GETLINEVISIBLE:
3457 return cs.GetVisible(wParam);
3458
3459 case SCI_SETFOLDEXPANDED:
3460 if (cs.SetExpanded(wParam, lParam)) {
3461 RedrawSelMargin();
3462 }
3463 break;
3464
3465 case SCI_GETFOLDEXPANDED:
3466 return cs.GetExpanded(wParam);
3467
3468 case SCI_SETFOLDFLAGS:
3469 foldFlags = wParam;
3470 Redraw();
3471 break;
3472
3473 case SCI_TOGGLEFOLD:
3474 ToggleContraction(wParam);
3475 break;
3476
3477 case SCI_ENSUREVISIBLE:
3478 EnsureLineVisible(wParam);
3479 break;
3480
3481 case SCI_SEARCHANCHOR:
3482 SearchAnchor();
3483 break;
3484
3485 case SCI_SEARCHNEXT:
3486 case SCI_SEARCHPREV:
3487 return SearchText(iMessage, wParam, lParam);
3488 break;
3489
3490 case SCI_SETCARETPOLICY:
3491 caretPolicy = wParam;
3492 caretSlop = lParam;
3493 break;
3494
3495 #ifdef INCLUDE_DEPRECATED_FEATURES
3496 case SCI_SETFORE:
3497 vs.styles[STYLE_DEFAULT].fore.desired = Colour(wParam);
3498 InvalidateStyleRedraw();
3499 break;
3500
3501 case SCI_SETBACK:
3502 vs.styles[STYLE_DEFAULT].back.desired = Colour(wParam);
3503 InvalidateStyleRedraw();
3504 break;
3505
3506 case SCI_SETBOLD:
3507 vs.styles[STYLE_DEFAULT].bold = wParam;
3508 InvalidateStyleRedraw();
3509 break;
3510
3511 case SCI_SETITALIC:
3512 vs.styles[STYLE_DEFAULT].italic = wParam;
3513 InvalidateStyleRedraw();
3514 break;
3515
3516 case SCI_SETSIZE:
3517 vs.styles[STYLE_DEFAULT].size = wParam;
3518 InvalidateStyleRedraw();
3519 break;
3520
3521 case SCI_SETFONT:
3522 if (wParam == 0)
3523 return 0;
3524 strcpy(vs.styles[STYLE_DEFAULT].fontName, reinterpret_cast<char *>(wParam));
3525 InvalidateStyleRedraw();
3526 break;
3527 #endif
3528
3529 case SCI_SETSELFORE:
3530 vs.selforeset = wParam;
3531 vs.selforeground.desired = Colour(lParam);
3532 InvalidateStyleRedraw();
3533 break;
3534
3535 case SCI_SETSELBACK:
3536 vs.selbackset = wParam;
3537 vs.selbackground.desired = Colour(lParam);
3538 InvalidateStyleRedraw();
3539 break;
3540
3541 case SCI_SETCARETFORE:
3542 vs.caretcolour.desired = Colour(wParam);
3543 InvalidateStyleRedraw();
3544 break;
3545
3546 case SCI_ASSIGNCMDKEY:
3547 kmap.AssignCmdKey(LOWORD(wParam), HIWORD(wParam), lParam);
3548 break;
3549
3550 case SCI_CLEARCMDKEY:
3551 kmap.AssignCmdKey(LOWORD(wParam), HIWORD(wParam), WM_NULL);
3552 break;
3553
3554 case SCI_CLEARALLCMDKEYS:
3555 kmap.Clear();
3556 break;
3557
3558 case SCI_INDICSETSTYLE:
3559 if (wParam <= INDIC_MAX) {
3560 vs.indicators[wParam].style = lParam;
3561 InvalidateStyleRedraw();
3562 }
3563 break;
3564
3565 case SCI_INDICGETSTYLE:
3566 return (wParam <= INDIC_MAX) ? vs.indicators[wParam].style : 0;
3567
3568 case SCI_INDICSETFORE:
3569 if (wParam <= INDIC_MAX) {
3570 vs.indicators[wParam].fore.desired = Colour(lParam);
3571 InvalidateStyleRedraw();
3572 }
3573 break;
3574
3575 case SCI_INDICGETFORE:
3576 return (wParam <= INDIC_MAX) ? vs.indicators[wParam].fore.desired.AsLong() : 0;
3577
3578 case SCI_LINEDOWN:
3579 case SCI_LINEDOWNEXTEND:
3580 case SCI_LINEUP:
3581 case SCI_LINEUPEXTEND:
3582 case SCI_CHARLEFT:
3583 case SCI_CHARLEFTEXTEND:
3584 case SCI_CHARRIGHT:
3585 case SCI_CHARRIGHTEXTEND:
3586 case SCI_WORDLEFT:
3587 case SCI_WORDLEFTEXTEND:
3588 case SCI_WORDRIGHT:
3589 case SCI_WORDRIGHTEXTEND:
3590 case SCI_HOME:
3591 case SCI_HOMEEXTEND:
3592 case SCI_LINEEND:
3593 case SCI_LINEENDEXTEND:
3594 case SCI_DOCUMENTSTART:
3595 case SCI_DOCUMENTSTARTEXTEND:
3596 case SCI_DOCUMENTEND:
3597 case SCI_DOCUMENTENDEXTEND:
3598 case SCI_PAGEUP:
3599 case SCI_PAGEUPEXTEND:
3600 case SCI_PAGEDOWN:
3601 case SCI_PAGEDOWNEXTEND:
3602 case SCI_EDITTOGGLEOVERTYPE:
3603 case SCI_CANCEL:
3604 case SCI_DELETEBACK:
3605 case SCI_TAB:
3606 case SCI_BACKTAB:
3607 case SCI_NEWLINE:
3608 case SCI_FORMFEED:
3609 case SCI_VCHOME:
3610 case SCI_VCHOMEEXTEND:
3611 case SCI_ZOOMIN:
3612 case SCI_ZOOMOUT:
3613 case SCI_DELWORDLEFT:
3614 case SCI_DELWORDRIGHT:
3615 return KeyCommand(iMessage);
3616
3617 case SCI_BRACEHIGHLIGHT:
3618 SetBraceHighlight(static_cast<int>(wParam), lParam, STYLE_BRACELIGHT);
3619 break;
3620
3621 case SCI_BRACEBADLIGHT:
3622 SetBraceHighlight(static_cast<int>(wParam), -1, STYLE_BRACEBAD);
3623 break;
3624
3625 case SCI_BRACEMATCH:
3626 // wParam is position of char to find brace for,
3627 // lParam is maximum amount of text to restyle to find it
3628 return BraceMatch(wParam, lParam);
3629
3630 case SCI_GETVIEWEOL:
3631 return vs.viewEOL;
3632
3633 case SCI_SETVIEWEOL:
3634 vs.viewEOL = wParam;
3635 Redraw();
3636 break;
3637
3638 case SCI_GETEDGECOLUMN:
3639 return theEdge;
3640
3641 case SCI_SETEDGECOLUMN:
3642 theEdge = wParam;
3643 InvalidateStyleRedraw();
3644 break;
3645
3646 case SCI_GETEDGEMODE:
3647 return edgeState;
3648
3649 case SCI_SETEDGEMODE:
3650 edgeState = wParam;
3651 InvalidateStyleRedraw();
3652 break;
3653
3654 case SCI_GETEDGECOLOUR:
3655 return vs.edgecolour.desired.AsLong();
3656
3657 case SCI_SETEDGECOLOUR:
3658 vs.edgecolour.desired = Colour(wParam);
3659 InvalidateStyleRedraw();
3660 break;
3661
3662 case SCI_GETDOCPOINTER:
3663 return reinterpret_cast<LRESULT>(pdoc);
3664
3665 case SCI_SETDOCPOINTER:
3666 SetDocPointer(reinterpret_cast<Document *>(lParam));
3667 return 0;
3668
3669 case SCI_SETMODEVENTMASK:
3670 modEventMask = wParam;
3671 return 0;
3672
3673 case SCI_CONVERTEOLS:
3674 pdoc->ConvertLineEnds(wParam);
3675 SetSelection(currentPos, anchor); // Ensure selection inside document
3676 return 0;
3677
3678 #ifdef MACRO_SUPPORT
3679 case SCI_STARTRECORD:
3680 recordingMacro = 1;
3681 return 0;
3682
3683 case SCI_STOPRECORD:
3684 recordingMacro = 0;
3685 return 0;
3686 #endif
3687
3688 default:
3689 return DefWndProc(iMessage, wParam, lParam);
3690 }
3691 //Platform::DebugPrintf("end wnd proc\n");
3692 return 0l;
3693 }