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