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