diff options
45 files changed, 733 insertions, 427 deletions
@@ -216,3 +216,6 @@ c171b756efc76359f0795ca0a1bfb7eb16d4c04b rel-5-4-3 5cb9af9b3103ffa3c8c4e3c15887191486de260f rel-5-5-5 68ddcf0a91ee2763cfc148925917bf609b355948 rel-5-5-6 ba0e2f317940a65f8f16325cbe4234fd75a6d47b rel-5-5-7 +afc75d68d333f449ad52fce35ff40a5eddadf5c8 rel-5-5-8 +76c8666d51407d156417e4b4a293a5e9ed29087f rel-5-5-9 +a951931386960b1c5045652c9b93e79b669e9728 rel-5-6-0 diff --git a/call/ScintillaCall.cxx b/call/ScintillaCall.cxx index d1d4012b2..093cf9a0d 100644 --- a/call/ScintillaCall.cxx +++ b/call/ScintillaCall.cxx @@ -3391,6 +3391,14 @@ Position ScintillaCall::IndexPositionFromLine(Line line, Scintilla::LineCharacte return Call(Message::IndexPositionFromLine, line, static_cast<intptr_t>(lineCharacterIndex)); } +bool ScintillaCall::DragDropEnabled() { + return Call(Message::GetDragDropEnabled); +} + +void ScintillaCall::SetDragDropEnabled(bool dragDropEnabled) { + Call(Message::SetDragDropEnabled, dragDropEnabled); +} + void ScintillaCall::StartRecord() { Call(Message::StartRecord); } diff --git a/cocoa/Scintilla/Info.plist b/cocoa/Scintilla/Info.plist index f5ce998da..195a18222 100644 --- a/cocoa/Scintilla/Info.plist +++ b/cocoa/Scintilla/Info.plist @@ -15,7 +15,7 @@ <key>CFBundlePackageType</key> <string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string> <key>CFBundleShortVersionString</key> - <string>5.5.7</string> + <string>5.6.0</string> <key>CFBundleVersion</key> <string>$(CURRENT_PROJECT_VERSION)</string> <key>NSHumanReadableCopyright</key> diff --git a/cocoa/Scintilla/Scintilla.xcodeproj/project.pbxproj b/cocoa/Scintilla/Scintilla.xcodeproj/project.pbxproj index 00b77029b..eb28817b1 100644 --- a/cocoa/Scintilla/Scintilla.xcodeproj/project.pbxproj +++ b/cocoa/Scintilla/Scintilla.xcodeproj/project.pbxproj @@ -586,7 +586,7 @@ CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 5.5.7; + CURRENT_PROJECT_VERSION = 5.6.0; DEAD_CODE_STRIPPING = YES; DEBUG_INFORMATION_FORMAT = dwarf; ENABLE_STRICT_OBJC_MSGSEND = YES; @@ -650,7 +650,7 @@ CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 5.5.7; + CURRENT_PROJECT_VERSION = 5.6.0; DEAD_CODE_STRIPPING = YES; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; @@ -682,7 +682,7 @@ CODE_SIGN_IDENTITY = "-"; CODE_SIGN_STYLE = Manual; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 5.5.7; + CURRENT_PROJECT_VERSION = 5.6.0; DEAD_CODE_STRIPPING = YES; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = ""; @@ -717,7 +717,7 @@ CODE_SIGN_IDENTITY = "-"; CODE_SIGN_STYLE = Manual; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 5.5.7; + CURRENT_PROJECT_VERSION = 5.6.0; DEAD_CODE_STRIPPING = YES; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = ""; diff --git a/cppcheck.suppress b/cppcheck.suppress index b8483fedf..812b65277 100644 --- a/cppcheck.suppress +++ b/cppcheck.suppress @@ -47,6 +47,9 @@ constParameterPointer:scintilla/win32/ScintillaWin.cxx knownConditionTrueFalse:scintilla/src/Editor.cxx
knownConditionTrueFalse:scintilla/src/EditView.cxx
+// This makes no sense
+CastIntegerToAddressAtReturn:scintilla\src\DBCS.cxx
+
// G_DEFINE_TYPE is too complex to pass to cppcheck
unknownMacro:scintilla/gtk/PlatGTK.cxx
// G_END_DECLS
diff --git a/doc/ScintillaDoc.html b/doc/ScintillaDoc.html index 0dfe986f4..3189a001a 100644 --- a/doc/ScintillaDoc.html +++ b/doc/ScintillaDoc.html @@ -482,6 +482,8 @@ *text)</a><br /> <a class="message" href="#SCI_SETREADONLY">SCI_SETREADONLY(bool readOnly)</a><br /> <a class="message" href="#SCI_GETREADONLY">SCI_GETREADONLY → bool</a><br /> + <a class="message" href="#SCI_GETDRAGDROPENABLED">SCI_GETDRAGDROPENABLED → bool</a><br /> + <a class="message" href="#SCI_SETDRAGDROPENABLED">SCI_SETDRAGDROPENABLED(bool dragDropEnabled)</a><br /> <a class="message" href="#SCI_GETTEXTRANGE">SCI_GETTEXTRANGE(<unused>, Sci_TextRange *tr) → position</a><br /> <a class="message" href="#SCI_GETTEXTRANGEFULL">SCI_GETTEXTRANGEFULL(<unused>, Sci_TextRangeFull *tr) → position</a><br /> <a class="message" href="#SCI_ALLOCATE">SCI_ALLOCATE(position bytes)</a><br /> @@ -568,6 +570,10 @@ only, attempts to modify the text cause the <a class="message" href="#SCN_MODIFYATTEMPTRO"><code>SCN_MODIFYATTEMPTRO</code></a> notification.</p> + <p><b id="SCI_GETDRAGDROPENABLED">SCI_GETDRAGDROPENABLED → bool</b><br /> + <b id="SCI_SETDRAGDROPENABLED">SCI_SETDRAGDROPENABLED(bool dragDropEnabled)</b><br /> + These messages get and set the flag controlling whether drag-and-drop is enabled or not.</p> + <p> <b id="SCI_GETTEXTRANGE">SCI_GETTEXTRANGE(<unused>, <a class="jump" href="#Sci_TextRange">Sci_TextRange</a> *tr) → position</b><br /> <b id="SCI_GETTEXTRANGEFULL">SCI_GETTEXTRANGEFULL(<unused>, <a class="jump" href="#Sci_TextRangeFull">Sci_TextRangeFull</a> *tr) → position</b><br /> @@ -2884,6 +2890,16 @@ struct Sci_TextToFindFull { <td>A horizontal line stretching until the tabstop.</td> </tr> + + <tr> + <th align="left"><code>SCTD_CONTROLCHAR</code></th> + + <td>2</td> + + <td>Will be drawn as a control code according to the configured + <a href="#CharacterRepresentations">character representation</a> + without any indentation.</td> + </tr> </tbody> </table> diff --git a/doc/ScintillaDownload.html b/doc/ScintillaDownload.html index 8d142b0ff..59ef4b721 100644 --- a/doc/ScintillaDownload.html +++ b/doc/ScintillaDownload.html @@ -26,9 +26,9 @@ <table bgcolor="#CCCCCC" width="100%" cellspacing="0" cellpadding="8" border="0"> <tr> <td> - <font size="4"> <a href="https://www.scintilla.org/scintilla557.zip"> + <font size="4"> <a href="https://www.scintilla.org/scintilla560.zip"> Windows</a> - <a href="https://www.scintilla.org/scintilla557.tgz"> + <a href="https://www.scintilla.org/scintilla560.tgz"> GTK/Linux</a> </font> </td> @@ -42,7 +42,7 @@ containing very few restrictions. </p> <h3> - Release 5.5.7 + Release 5.6.0 </h3> <h4> Source Code @@ -50,8 +50,8 @@ The source code package contains all of the source code for Scintilla but no binary executable code and is available in <ul> - <li><a href="https://www.scintilla.org/scintilla557.zip">zip format</a> (1.8M) commonly used on Windows</li> - <li><a href="https://www.scintilla.org/scintilla557.tgz">tgz format</a> (1.7M) commonly used on Linux and compatible operating systems</li> + <li><a href="https://www.scintilla.org/scintilla560.zip">zip format</a> (1.8M) commonly used on Windows</li> + <li><a href="https://www.scintilla.org/scintilla560.tgz">tgz format</a> (1.7M) commonly used on Linux and compatible operating systems</li> </ul> Instructions for building on both Windows and Linux are included in the readme file. <h4> diff --git a/doc/ScintillaHistory.html b/doc/ScintillaHistory.html index b63c9851d..26333e1be 100644 --- a/doc/ScintillaHistory.html +++ b/doc/ScintillaHistory.html @@ -591,15 +591,75 @@ </tr><tr> <td>Ahmet Sait</td> <td>Sven Ritter</td> + <td>Stefan Löffler</td> + <td>Nathaniel Braun</td> </tr> </table> <h2 id="Releases">Releases</h2> <h3> + <a href="https://www.scintilla.org/scintilla561.zip">Release 5.6.1</a> + </h3> + <ul> + <li> + Released 25 February 2026. + </li> + <li> + Add mode to draw tabs as [HT] blobs with SCI_SETTABDRAWMODE(SCTD_CONTROLCHAR). + </li> + </ul> + <h3> + <a href="https://www.scintilla.org/scintilla560.zip">Release 5.6.0</a> + </h3> + <ul> + <li> + Released 25 February 2026. + </li> + <li> + Fix crash when window is too narrow to show any text. + </li> + </ul> + <h3> + <a href="https://www.scintilla.org/scintilla559.zip">Release 5.5.9</a> + </h3> + <ul> + <li> + Released 25 February 2026. + </li> + <li> + Add option to disable drag/drop editing. + <a href="https://sourceforge.net/p/scintilla/feature-requests/184/">Feature #184</a>. + </li> + <li> + Allow Euro '€' in code page 936 for 0x80. + <a href="https://sourceforge.net/p/scintilla/feature-requests/1575/">Feature #1575</a>. + </li> + <li> + Fix SCI_SETSELECTIONNSTART and SCI_SETSELECTIONNEND to behave more sensibly. + <a href="https://sourceforge.net/p/scintilla/bugs/2488/">Bug #2488</a>. + </li> + <li> + Fix lexing after undo at end of document. + <a href="https://sourceforge.net/p/scintilla/bugs/2491/">Bug #2491</a>. + </li> + <li> + When a line end is not selected, change the colour of the rectangle that indicates the line end is selected + to white space background instead of previous character's background except for EOL filled style. + </li> + <li> + On Qt, add const to ScintillaDocument and ScintillaEdit methods. + <a href="https://sourceforge.net/p/scintilla/bugs/2494/">Bug #2494</a>. + </li> + <li> + On Qt, prevent crash when using ScintillaDocument object. + <a href="https://sourceforge.net/p/scintilla/bugs/2495/">Bug #2495</a>. + </li> + </ul> + <h3> <a href="https://www.scintilla.org/scintilla558.zip">Release 5.5.8</a> </h3> <ul> <li> - Released 8 June 2025. + Released 10 November 2025. </li> <li> Change format for SCI_GETSELECTIONSERIALIZED. diff --git a/doc/index.html b/doc/index.html index aed89ac40..69fe4a270 100644 --- a/doc/index.html +++ b/doc/index.html @@ -9,7 +9,7 @@ <meta name="keywords" content="Scintilla, SciTE, Editing Component, Text Editor" /> <meta name="Description" content="www.scintilla.org is the home of the Scintilla editing component and SciTE text editor application." /> - <meta name="Date.Modified" content="20250608" /> + <meta name="Date.Modified" content="20260225" /> <meta name="viewport" content="width=device-width, initial-scale=1" /> <style type="text/css"> .logo { @@ -61,8 +61,8 @@ GTK, and macOS</font> </td> <td width="40%" align="right"> - <font color="#FFCC99" size="3"> Release version 5.5.7<br /> - Site last modified June 8 2025</font> + <font color="#FFCC99" size="3"> Release version 5.6.0<br /> + Site last modified February 25 2026</font> </td> <td width="20%"> @@ -77,11 +77,12 @@ </tr> </table> <ul id="versionlist"> + <li>Version 5.6.0 fixes crash when window is too narrow to show any text.</li> + <li>Version 5.5.9 adds an option to disable drag & drop. Fixes colour after line end.</li> + <li>Version 5.5.8 changes format of SCI_GETSELECTIONSERIALIZED and fixes redraw after undo.</li> <li>Version 5.5.7 can prevent storing scroll position in undo selection history and adds a wrap-aware SCI_SCROLLVERTICAL API.</li> <li>Version 5.5.6 improves DBCS and autocompletion drawing on Win32 and improves dwell, encoding and text clipping on Qt.</li> <li>Version 5.5.5 can remember selection with undo and redo. Update to use DirectWrite 1.1.</li> - <li>Version 5.5.4 fixes wrapping of removed lines and edge cases for moving lines. On GTK, middle click can be repeated with one value.</li> - <li>Version 5.5.3 fixes horizontal scrolling with a touchpad on Win32.</li> </ul> <ul id="menu"> <li id="remote1"><a href="https://www.scintilla.org/SciTEImage.html">Screenshot</a></li> diff --git a/include/Scintilla.h b/include/Scintilla.h index f7afa4f50..94a66d4f8 100644 --- a/include/Scintilla.h +++ b/include/Scintilla.h @@ -78,6 +78,7 @@ typedef sptr_t (*SciFnDirectStatus)(sptr_t ptr, unsigned int iMessage, uptr_t wP #define SCI_SETVIEWWS 2021 #define SCTD_LONGARROW 0 #define SCTD_STRIKEOUT 1 +#define SCTD_CONTROLCHAR 2 #define SCI_GETTABDRAWMODE 2698 #define SCI_SETTABDRAWMODE 2699 #define SCI_POSITIONFROMPOINT 2022 @@ -1194,6 +1195,8 @@ typedef sptr_t (*SciFnDirectStatus)(sptr_t ptr, unsigned int iMessage, uptr_t wP #define SCI_RELEASELINECHARACTERINDEX 2712 #define SCI_LINEFROMINDEXPOSITION 2713 #define SCI_INDEXPOSITIONFROMLINE 2714 +#define SCI_GETDRAGDROPENABLED 2818 +#define SCI_SETDRAGDROPENABLED 2819 #define SCI_STARTRECORD 3001 #define SCI_STOPRECORD 3002 #define SCI_GETLEXER 4002 diff --git a/include/Scintilla.iface b/include/Scintilla.iface index 9ba834aed..d98ac8358 100644 --- a/include/Scintilla.iface +++ b/include/Scintilla.iface @@ -196,9 +196,11 @@ set void SetViewWS=2021(WhiteSpace viewWS,) enu TabDrawMode=SCTD_ val SCTD_LONGARROW=0 val SCTD_STRIKEOUT=1 +val SCTD_CONTROLCHAR=2 ali SCTD_LONGARROW=LONG_ARROW ali SCTD_STRIKEOUT=STRIKE_OUT +ali SCTD_CONTROLCHAR=CONTROL_CHAR # Retrieve the current tab draw mode. # Returns one of SCTD_* constants. @@ -3258,6 +3260,12 @@ fun line LineFromIndexPosition=2713(position pos, LineCharacterIndexType lineCha # Retrieve the position measured in index units at the start of a document line. fun position IndexPositionFromLine=2714(line line, LineCharacterIndexType lineCharacterIndex) +# Get whether drag-and-drop is enabled or disabled +get bool GetDragDropEnabled=2818(,) + +# Enable or disable drag-and-drop +set void SetDragDropEnabled=2819(bool dragDropEnabled,) + # Start notifying the container of all key presses and commands. fun void StartRecord=3001(,) diff --git a/include/ScintillaCall.h b/include/ScintillaCall.h index 7a98c7e3d..2c065c03f 100644 --- a/include/ScintillaCall.h +++ b/include/ScintillaCall.h @@ -893,6 +893,8 @@ public: void ReleaseLineCharacterIndex(Scintilla::LineCharacterIndexType lineCharacterIndex); Line LineFromIndexPosition(Position pos, Scintilla::LineCharacterIndexType lineCharacterIndex); Position IndexPositionFromLine(Line line, Scintilla::LineCharacterIndexType lineCharacterIndex); + bool DragDropEnabled(); + void SetDragDropEnabled(bool dragDropEnabled); void StartRecord(); void StopRecord(); int Lexer(); diff --git a/include/ScintillaMessages.h b/include/ScintillaMessages.h index 6a33f5c6d..d327bf7c1 100644 --- a/include/ScintillaMessages.h +++ b/include/ScintillaMessages.h @@ -799,6 +799,8 @@ enum class Message { ReleaseLineCharacterIndex = 2712, LineFromIndexPosition = 2713, IndexPositionFromLine = 2714, + GetDragDropEnabled = 2818, + SetDragDropEnabled = 2819, StartRecord = 3001, StopRecord = 3002, GetLexer = 4002, diff --git a/include/ScintillaTypes.h b/include/ScintillaTypes.h index 0991a1480..a1e55c203 100644 --- a/include/ScintillaTypes.h +++ b/include/ScintillaTypes.h @@ -26,6 +26,7 @@ enum class WhiteSpace { enum class TabDrawMode { LongArrow = 0, StrikeOut = 1, + ControlChar = 2, }; enum class EndOfLine { diff --git a/qt/ScintillaEdit/ScintillaDocument.cpp b/qt/ScintillaEdit/ScintillaDocument.cpp index fc5ef2ec3..f532f2726 100644 --- a/qt/ScintillaEdit/ScintillaDocument.cpp +++ b/qt/ScintillaEdit/ScintillaDocument.cpp @@ -2,6 +2,8 @@ // Wrapper for Scintilla document object so it can be manipulated independently. // Copyright (c) 2011 Archaeopteryx Software, Inc. d/b/a Wingware +#include <cstdint> + #include <stdexcept> #include <string_view> #include <vector> @@ -99,8 +101,9 @@ ScintillaDocument::ScintillaDocument(QObject *parent, void *pdoc_) : pdoc = new Document(DocumentOption::Default); } docWatcher = new WatcherHelper(this); - (static_cast<Document *>(pdoc))->AddRef(); - (static_cast<Document *>(pdoc))->AddWatcher(docWatcher, pdoc); + Document *doc = static_cast<Document *>(pdoc); + doc->AddRef(); + doc->AddWatcher(docWatcher, doc); } ScintillaDocument::~ScintillaDocument() { @@ -114,15 +117,15 @@ ScintillaDocument::~ScintillaDocument() { docWatcher = nullptr; } -void *ScintillaDocument::pointer() { +void *ScintillaDocument::pointer() const { return pdoc; } -int ScintillaDocument::line_from_position(int pos) { +int ScintillaDocument::line_from_position(int pos) const { return (static_cast<Document *>(pdoc))->LineFromPosition(pos); } -bool ScintillaDocument::is_cr_lf(int pos) { +bool ScintillaDocument::is_cr_lf(int pos) const { return (static_cast<Document *>(pdoc))->IsCrLf(pos); } @@ -138,11 +141,11 @@ int ScintillaDocument::redo() { return (static_cast<Document *>(pdoc))->Redo(); } -bool ScintillaDocument::can_undo() { +bool ScintillaDocument::can_undo() const { return (static_cast<Document *>(pdoc))->CanUndo(); } -bool ScintillaDocument::can_redo() { +bool ScintillaDocument::can_redo() const { return (static_cast<Document *>(pdoc))->CanRedo(); } @@ -154,7 +157,7 @@ bool ScintillaDocument::set_undo_collection(bool collect_undo) { return (static_cast<Document *>(pdoc))->SetUndoCollection(collect_undo); } -bool ScintillaDocument::is_collecting_undo() { +bool ScintillaDocument::is_collecting_undo() const { return (static_cast<Document *>(pdoc))->IsCollectingUndo(); } @@ -170,7 +173,7 @@ void ScintillaDocument::set_save_point() { (static_cast<Document *>(pdoc))->SetSavePoint(); } -bool ScintillaDocument::is_save_point() { +bool ScintillaDocument::is_save_point() const { return (static_cast<Document *>(pdoc))->IsSavePoint(); } @@ -178,15 +181,15 @@ void ScintillaDocument::set_read_only(bool read_only) { (static_cast<Document *>(pdoc))->SetReadOnly(read_only); } -bool ScintillaDocument::is_read_only() { +bool ScintillaDocument::is_read_only() const { return (static_cast<Document *>(pdoc))->IsReadOnly(); } -void ScintillaDocument::insert_string(int position, QByteArray &str) { +void ScintillaDocument::insert_string(int position, const QByteArray &str) { (static_cast<Document *>(pdoc))->InsertString(position, str.data(), str.size()); } -QByteArray ScintillaDocument::get_char_range(int position, int length) { +QByteArray ScintillaDocument::get_char_range(int position, int length) const { const Document *doc = static_cast<Document *>(pdoc); if (position < 0 || length <= 0 || position + length > doc->Length()) @@ -197,27 +200,27 @@ QByteArray ScintillaDocument::get_char_range(int position, int length) { return ba; } -char ScintillaDocument::style_at(int position) { +char ScintillaDocument::style_at(int position) const { return (static_cast<Document *>(pdoc))->StyleAt(position); } -int ScintillaDocument::line_start(int lineno) { +int ScintillaDocument::line_start(int lineno) const { return (static_cast<Document *>(pdoc))->LineStart(lineno); } -int ScintillaDocument::line_end(int lineno) { +int ScintillaDocument::line_end(int lineno) const { return (static_cast<Document *>(pdoc))->LineEnd(lineno); } -int ScintillaDocument::line_end_position(int pos) { +int ScintillaDocument::line_end_position(int pos) const { return (static_cast<Document *>(pdoc))->LineEndPosition(pos); } -int ScintillaDocument::length() { +int ScintillaDocument::length() const { return (static_cast<Document *>(pdoc))->Length(); } -int ScintillaDocument::lines_total() { +int ScintillaDocument::lines_total() const { return (static_cast<Document *>(pdoc))->LinesTotal(); } @@ -229,7 +232,7 @@ bool ScintillaDocument::set_style_for(int length, char style) { return (static_cast<Document *>(pdoc))->SetStyleFor(length, style); } -int ScintillaDocument::get_end_styled() { +int ScintillaDocument::get_end_styled() const { return (static_cast<Document *>(pdoc))->GetEndStyled(); } @@ -257,7 +260,7 @@ int ScintillaDocument::decorations_end(int indic, int position) { return (static_cast<Document *>(pdoc))->decorations->End(indic, position); } -int ScintillaDocument::get_code_page() { +int ScintillaDocument::get_code_page() const { return (static_cast<Document *>(pdoc))->CodePage(); } @@ -265,7 +268,7 @@ void ScintillaDocument::set_code_page(int code_page) { (static_cast<Document *>(pdoc))->dbcsCodePage = code_page; } -int ScintillaDocument::get_eol_mode() { +int ScintillaDocument::get_eol_mode() const { return static_cast<int>((static_cast<Document *>(pdoc))->eolMode); } @@ -277,6 +280,6 @@ int ScintillaDocument::move_position_outside_char(int pos, int move_dir, bool ch return (static_cast<Document *>(pdoc))->MovePositionOutsideChar(pos, move_dir, check_line_end); } -int ScintillaDocument::get_character(int pos) { +int ScintillaDocument::get_character(int pos) const { return (static_cast<Document *>(pdoc))->GetCharacterAndWidth(pos, nullptr); } diff --git a/qt/ScintillaEdit/ScintillaDocument.h b/qt/ScintillaEdit/ScintillaDocument.h index fa129d53b..2e68b8aa1 100644 --- a/qt/ScintillaEdit/ScintillaDocument.h +++ b/qt/ScintillaEdit/ScintillaDocument.h @@ -38,48 +38,48 @@ class EXPORT_IMPORT_API ScintillaDocument : public QObject public: explicit ScintillaDocument(QObject *parent = 0, void *pdoc_=0); virtual ~ScintillaDocument(); - void *pointer(); + void *pointer() const; - int line_from_position(int pos); - bool is_cr_lf(int pos); + int line_from_position(int pos) const; + bool is_cr_lf(int pos) const; bool delete_chars(int pos, int len); int undo(); int redo(); - bool can_undo(); - bool can_redo(); + bool can_undo() const; + bool can_redo() const; void delete_undo_history(); bool set_undo_collection(bool collect_undo); - bool is_collecting_undo(); + bool is_collecting_undo() const; void begin_undo_action(bool coalesceWithPrior = false); void end_undo_action(); void set_save_point(); - bool is_save_point(); + bool is_save_point() const; void set_read_only(bool read_only); - bool is_read_only(); - void insert_string(int position, QByteArray &str); - QByteArray get_char_range(int position, int length); - char style_at(int position); - int line_start(int lineno); - int line_end(int lineno); - int line_end_position(int pos); - int length(); - int lines_total(); + bool is_read_only() const; + void insert_string(int position, const QByteArray &str); + QByteArray get_char_range(int position, int length) const; + char style_at(int position) const; + int line_start(int lineno) const; + int line_end(int lineno) const; + int line_end_position(int pos) const; + int length() const; + int lines_total() const; void start_styling(int position); bool set_style_for(int length, char style); - int get_end_styled(); + int get_end_styled() const; void ensure_styled_to(int position); void set_current_indicator(int indic); void decoration_fill_range(int position, int value, int fillLength); int decorations_value_at(int indic, int position); int decorations_start(int indic, int position); int decorations_end(int indic, int position); - int get_code_page(); + int get_code_page() const; void set_code_page(int code_page); - int get_eol_mode(); + int get_eol_mode() const; void set_eol_mode(int eol_mode); int move_position_outside_char(int pos, int move_dir, bool check_line_end); - int get_character(int pos); // Calls GetCharacterAndWidth(pos, NULL) + int get_character(int pos) const; // Calls GetCharacterAndWidth(pos, NULL) signals: void modify_attempt(); diff --git a/qt/ScintillaEdit/ScintillaEdit.cpp.template b/qt/ScintillaEdit/ScintillaEdit.cpp.template index a46a1d12d..a2deb91de 100644 --- a/qt/ScintillaEdit/ScintillaEdit.cpp.template +++ b/qt/ScintillaEdit/ScintillaEdit.cpp.template @@ -23,7 +23,7 @@ QByteArray ScintillaEdit::TextReturner(int message, uptr_t wParam) const { return ba; } -QPair<int, int>ScintillaEdit::find_text(int flags, const char *text, int cpMin, int cpMax) { +QPair<int, int>ScintillaEdit::find_text(int flags, const char *text, int cpMin, int cpMax) const { struct Sci_TextToFind ft = {{0, 0}, 0, {0, 0}}; ft.chrg.cpMin = cpMin; ft.chrg.cpMax = cpMax; @@ -36,7 +36,7 @@ QPair<int, int>ScintillaEdit::find_text(int flags, const char *text, int cpMin, return QPair<int,int>(start, ft.chrgText.cpMax); } -QByteArray ScintillaEdit::get_text_range(int start, int end) { +QByteArray ScintillaEdit::get_text_range(int start, int end) const { if (start > end) start = end; @@ -50,7 +50,7 @@ QByteArray ScintillaEdit::get_text_range(int start, int end) { return ba; } -ScintillaDocument *ScintillaEdit::get_doc() { +ScintillaDocument *ScintillaEdit::get_doc() const { return new ScintillaDocument(0, (void *)send(SCI_GETDOCPOINTER, 0, 0)); } @@ -60,7 +60,7 @@ void ScintillaEdit::set_doc(ScintillaDocument *pdoc_) { long ScintillaEdit::format_range(bool draw, QPaintDevice* target, QPaintDevice* measure, const QRect& print_rect, const QRect& page_rect, - long range_start, long range_end) + long range_start, long range_end) const { Sci_RangeToFormat to_format; diff --git a/qt/ScintillaEdit/ScintillaEdit.h.template b/qt/ScintillaEdit/ScintillaEdit.h.template index 1444e907c..869788f0d 100644 --- a/qt/ScintillaEdit/ScintillaEdit.h.template +++ b/qt/ScintillaEdit/ScintillaEdit.h.template @@ -33,27 +33,27 @@ public: QByteArray TextReturner(int message, uptr_t wParam) const; - QPair<int, int>find_text(int flags, const char *text, int cpMin, int cpMax); - QByteArray get_text_range(int start, int end); - ScintillaDocument *get_doc(); + QPair<int, int>find_text(int flags, const char *text, int cpMin, int cpMax) const; + QByteArray get_text_range(int start, int end) const; + ScintillaDocument *get_doc() const; void set_doc(ScintillaDocument *pdoc_); // Same as previous two methods but with Qt style names - QPair<int, int>findText(int flags, const char *text, int cpMin, int cpMax) { + QPair<int, int>findText(int flags, const char *text, int cpMin, int cpMax) const { return find_text(flags, text, cpMin, cpMax); } - QByteArray textRange(int start, int end) { + QByteArray textRange(int start, int end) const { return get_text_range(start, end); } // Exposing the FORMATRANGE api with both underscore & qt style names long format_range(bool draw, QPaintDevice* target, QPaintDevice* measure, const QRect& print_rect, const QRect& page_rect, - long range_start, long range_end); + long range_start, long range_end) const; long formatRange(bool draw, QPaintDevice* target, QPaintDevice* measure, const QRect& print_rect, const QRect& page_rect, - long range_start, long range_end) { + long range_start, long range_end) const { return format_range(draw, target, measure, print_rect, page_rect, range_start, range_end); } diff --git a/qt/ScintillaEdit/ScintillaEdit.pro b/qt/ScintillaEdit/ScintillaEdit.pro index ebfc65ebe..295678d3c 100644 --- a/qt/ScintillaEdit/ScintillaEdit.pro +++ b/qt/ScintillaEdit/ScintillaEdit.pro @@ -13,7 +13,7 @@ TEMPLATE = lib CONFIG += lib_bundle CONFIG += c++1z -VERSION = 5.5.7 +VERSION = 5.6.0 SOURCES += \ ScintillaEdit.cpp \ diff --git a/qt/ScintillaEditBase/ScintillaEditBase.pro b/qt/ScintillaEditBase/ScintillaEditBase.pro index 17c350f12..d2ab750df 100644 --- a/qt/ScintillaEditBase/ScintillaEditBase.pro +++ b/qt/ScintillaEditBase/ScintillaEditBase.pro @@ -13,7 +13,7 @@ TEMPLATE = lib CONFIG += lib_bundle CONFIG += c++1z -VERSION = 5.5.7 +VERSION = 5.6.0 SOURCES += \ PlatQt.cpp \ diff --git a/src/DBCS.cxx b/src/DBCS.cxx index f53b8e9ed..062c30a51 100644 --- a/src/DBCS.cxx +++ b/src/DBCS.cxx @@ -7,8 +7,10 @@ #include <cstdint> +#include <vector> #include <array> #include <map> +#include <algorithm> #include "DBCS.h" @@ -88,10 +90,13 @@ bool DBCSIsTrailByte(int codePage, char ch) noexcept { bool IsDBCSValidSingleByte(int codePage, int ch) noexcept { switch (codePage) { case cp932: + // Shift_jis return ch == 0x80 || (ch >= 0xA0 && ch <= 0xDF) || (ch >= 0xFD); - + case cp936: + // GBK + return ch == 0x80; default: return false; } @@ -99,25 +104,31 @@ bool IsDBCSValidSingleByte(int codePage, int ch) noexcept { // NOLINTEND(*-magic-numbers) -using CodePageToFoldMap = std::map<int, FoldMap>; -CodePageToFoldMap cpToFoldMap; +namespace { -bool DBCSHasFoldMap(int codePage) { - const CodePageToFoldMap::const_iterator it = cpToFoldMap.find(codePage); - return it != cpToFoldMap.end(); -} +struct CodePageFoldMap { + int codePage = 0; + FoldMap foldMap; + explicit CodePageFoldMap(int codePage_) noexcept : codePage {codePage_} {} +}; + +using CodePageToFoldMap = std::vector<CodePageFoldMap>; +CodePageToFoldMap cpToFoldMap; -void DBCSSetFoldMap(int codePage, const FoldMap &foldMap) { - cpToFoldMap[codePage] = foldMap; } -FoldMap *DBCSGetMutableFoldMap(int codePage) { - // Constructs if needed - return &cpToFoldMap[codePage]; +FoldMap *DBCSCreateFoldMap(int codePage) { + cpToFoldMap.emplace_back(codePage); + return &(cpToFoldMap.back().foldMap); } const FoldMap *DBCSGetFoldMap(int codePage) { - return &cpToFoldMap[codePage]; + const CodePageToFoldMap::iterator it = std::find_if(cpToFoldMap.begin(), cpToFoldMap.end(), + [codePage](const CodePageFoldMap &cpfm) -> bool {return cpfm.codePage == codePage; }); + if (it != cpToFoldMap.end()) { + return &(it->foldMap); + } + return nullptr; } } diff --git a/src/DBCS.h b/src/DBCS.h index 96d282446..466d70ec7 100644 --- a/src/DBCS.h +++ b/src/DBCS.h @@ -37,13 +37,11 @@ constexpr uint16_t DBCSIndex(char ch1, char ch2) noexcept { } struct DBCSPair { - char chars[2]; + char chars[2]{}; }; using FoldMap = std::array<DBCSPair, 0x8000>; -bool DBCSHasFoldMap(int codePage); -void DBCSSetFoldMap(int codePage, const FoldMap &foldMap); -FoldMap *DBCSGetMutableFoldMap(int codePage); +FoldMap *DBCSCreateFoldMap(int codePage); const FoldMap *DBCSGetFoldMap(int codePage); } diff --git a/src/Document.cxx b/src/Document.cxx index c79c5002b..458c1e93f 100644 --- a/src/Document.cxx +++ b/src/Document.cxx @@ -864,8 +864,9 @@ Sci::Position Document::NextPosition(Sci::Position pos, int moveDir) const noexc const int increment = (moveDir > 0) ? 1 : -1; if (pos + increment <= 0) return 0; - if (pos + increment >= cb.Length()) - return cb.Length(); + const Sci::Position length = LengthNoExcept(); + if (pos + increment >= length) + return length; if (dbcsCodePage) { if (CpUtf8 == dbcsCodePage) { @@ -904,9 +905,7 @@ Sci::Position Document::NextPosition(Sci::Position pos, int moveDir) const noexc } else { if (moveDir > 0) { const int mbsize = IsDBCSDualByteAt(pos) ? 2 : 1; - pos += mbsize; - if (pos > cb.Length()) - pos = cb.Length(); + pos = std::min(pos + mbsize, length); } else { // How to Go Backward in a DBCS String // https://msdn.microsoft.com/en-us/library/cc194792.aspx @@ -1590,7 +1589,10 @@ Sci::Position Document::Undo() { } cb.PerformUndoStep(); if (action.at != ActionType::container) { - ModifiedAt(action.position); + if ((action.at == ActionType::insert) && (action.position >= LengthNoExcept()) && (action.position > 0)) + ModifiedAt(action.position - 1); + else + ModifiedAt(action.position); newPos = action.position; } @@ -1761,22 +1763,23 @@ Sci::Position Document::SetLineIndentation(Sci::Line line, Sci::Position indent) } } -Sci::Position Document::GetLineIndentPosition(Sci::Line line) const { +Sci::Position Document::GetLineIndentPosition(Sci::Line line) const noexcept { if (line < 0) return 0; - Sci::Position pos = LineStart(line); - const Sci::Position length = Length(); + Sci::Position pos = cb.LineStart(line); + const Sci::Position length = LengthNoExcept(); while ((pos < length) && IsSpaceOrTab(cb.CharAt(pos))) { pos++; } return pos; } -Sci::Position Document::GetColumn(Sci::Position pos) const { +Sci::Position Document::GetColumn(Sci::Position pos) const noexcept { Sci::Position column = 0; const Sci::Line line = SciLineFromPosition(pos); if ((line >= 0) && (line < LinesTotal())) { - for (Sci::Position i = LineStart(line); i < pos;) { + const Sci::Position length = LengthNoExcept(); + for (Sci::Position i = cb.LineStart(line); i < pos;) { const char ch = cb.CharAt(i); if (ch == '\t') { column = NextTab(column, tabInChars); @@ -1785,7 +1788,7 @@ Sci::Position Document::GetColumn(Sci::Position pos) const { return column; } else if (ch == '\n') { return column; - } else if (i >= Length()) { + } else if (i >= length) { return column; } else if (UTF8IsAscii(ch)) { column++; @@ -1826,11 +1829,12 @@ Sci::Position Document::CountUTF16(Sci::Position startPos, Sci::Position endPos) return count; } -Sci::Position Document::FindColumn(Sci::Line line, Sci::Position column) { - Sci::Position position = LineStart(line); +Sci::Position Document::FindColumn(Sci::Line line, Sci::Position column) const noexcept { + Sci::Position position = cb.LineStart(line); if ((line >= 0) && (line < LinesTotal())) { + const Sci::Position length = LengthNoExcept(); Sci::Position columnCurrent = 0; - while ((columnCurrent < column) && (position < Length())) { + while ((columnCurrent < column) && (position < length)) { const char ch = cb.CharAt(position); if (ch == '\t') { columnCurrent = NextTab(columnCurrent, tabInChars); @@ -1841,6 +1845,9 @@ Sci::Position Document::FindColumn(Sci::Line line, Sci::Position column) { return position; } else if (ch == '\n') { return position; + } else if (UTF8IsAscii(ch)) { + columnCurrent++; + position++; } else { columnCurrent++; position = NextPosition(position, 1); @@ -1900,7 +1907,8 @@ std::string Document::TransformLineEnds(const char *s, size_t len, EndOfLine eol void Document::ConvertLineEnds(EndOfLine eolModeSet) { UndoGroup ug(this); - for (Sci::Position pos = 0; pos < Length(); pos++) { + const Sci::Position length = Length(); + for (Sci::Position pos = 0; pos < length; pos++) { const char ch = cb.CharAt(pos); if (ch == '\r') { if (cb.CharAt(pos + 1) == '\n') { @@ -2819,7 +2827,7 @@ void SCI_METHOD Document::DecorationFillRange(Sci_Position position, int value, bool Document::AddWatcher(DocWatcher *watcher, void *userData) { const WatcherWithUserData wwud(watcher, userData); - std::vector<WatcherWithUserData>::iterator it = + const std::vector<WatcherWithUserData>::iterator it = std::find(watchers.begin(), watchers.end(), wwud); if (it != watchers.end()) return false; @@ -2831,7 +2839,7 @@ bool Document::RemoveWatcher(DocWatcher *watcher, void *userData) noexcept { try { // This can never fail as WatcherWithUserData constructor and == are noexcept // but std::find is not noexcept. - std::vector<WatcherWithUserData>::iterator it = + const std::vector<WatcherWithUserData>::iterator it = std::find(watchers.begin(), watchers.end(), WatcherWithUserData(watcher, userData)); if (it != watchers.end()) { watchers.erase(it); diff --git a/src/Document.h b/src/Document.h index 7655d5290..7fd150f37 100644 --- a/src/Document.h +++ b/src/Document.h @@ -455,11 +455,11 @@ public: int SCI_METHOD GetLineIndentation(Sci_Position line) override; Sci::Position SetLineIndentation(Sci::Line line, Sci::Position indent); - Sci::Position GetLineIndentPosition(Sci::Line line) const; - Sci::Position GetColumn(Sci::Position pos) const; + Sci::Position GetLineIndentPosition(Sci::Line line) const noexcept; + Sci::Position GetColumn(Sci::Position pos) const noexcept; Sci::Position CountCharacters(Sci::Position startPos, Sci::Position endPos) const noexcept; Sci::Position CountUTF16(Sci::Position startPos, Sci::Position endPos) const noexcept; - Sci::Position FindColumn(Sci::Line line, Sci::Position column); + Sci::Position FindColumn(Sci::Line line, Sci::Position column) const noexcept; void Indent(bool forwards, Sci::Line lineBottom, Sci::Line lineTop); static std::string TransformLineEnds(const char *s, size_t len, Scintilla::EndOfLine eolModeWanted); void ConvertLineEnds(Scintilla::EndOfLine eolModeSet); diff --git a/src/EditModel.cxx b/src/EditModel.cxx index 65d39f9e7..cad0c3ade 100644 --- a/src/EditModel.cxx +++ b/src/EditModel.cxx @@ -167,6 +167,10 @@ InSelection EditModel::LineEndInSelection(Sci::Line lineDoc) const { return sel.InSelectionForEOL(posAfterLineEnd); } +Sci::Position EditModel::VirtualSpaceForLine(Sci::Line lineDoc) const { + return sel.VirtualSpaceFor(pdoc->LineEnd(lineDoc)); +} + int EditModel::GetMark(Sci::Line line) const { return pdoc->GetMark(line, FlagSet(changeHistoryOption, ChangeHistoryOption::Markers)); } diff --git a/src/EditModel.h b/src/EditModel.h index dcaf99613..b23169f78 100644 --- a/src/EditModel.h +++ b/src/EditModel.h @@ -108,6 +108,7 @@ public: const char *GetDefaultFoldDisplayText() const noexcept; const char *GetFoldDisplayText(Sci::Line lineDoc) const noexcept; InSelection LineEndInSelection(Sci::Line lineDoc) const; + [[nodiscard]] Sci::Position VirtualSpaceForLine(Sci::Line lineDoc) const; [[nodiscard]] int GetMark(Sci::Line line) const; void EnsureModelState(); diff --git a/src/EditView.cxx b/src/EditView.cxx index 3bf0a1fbb..bec07fe95 100644 --- a/src/EditView.cxx +++ b/src/EditView.cxx @@ -17,6 +17,7 @@ #include <string> #include <string_view> #include <vector> +#include <array> #include <map> #include <set> #include <forward_list> @@ -124,8 +125,7 @@ int WidestLineWidth(Surface *surface, const ViewStyle &vs, int styleOffset, cons const std::string_view text(st.text + start, lenLine); widthSubLine = static_cast<int>(surface->WidthText(fontText, text)); } - if (widthSubLine > widthMax) - widthMax = widthSubLine; + widthMax = std::max(widthMax, widthSubLine); start += lenLine + 1; } return widthMax; @@ -191,7 +191,7 @@ EditView::EditView() { imeCaretBlockOverride = false; llc.SetLevel(LineCache::Caret); posCache = CreatePositionCache(); - posCache->SetSize(0x400); + posCache->SetSize(positionCacheDefaultSize); maxLayoutThreads = 1; tabArrowHeight = 4; customDrawTabArrow = nullptr; @@ -251,9 +251,8 @@ bool EditView::AddTabstop(Sci::Line line, int x) { int EditView::GetNextTabstop(Sci::Line line, int x) const noexcept { if (ldTabstops) { return ldTabstops->GetNextTabstop(line, x); - } else { - return 0; } + return 0; } void EditView::LinesAddedOrRemoved(Sci::Line lineOfPos, Sci::Line linesAdded) { @@ -353,14 +352,14 @@ void LayoutSegments(IPositionCache *pCache, XYPOSITION representationWidth = 0.0; // Tab is a special case of representation, taking a variable amount of space // which will be filled in later. - if (ll->chars[ts.start] != '\t') { + if (ll->chars[ts.start] != '\t' || vstyle.tabDrawMode == TabDrawMode::ControlChar) { representationWidth = vstyle.controlCharWidth; if (representationWidth <= 0.0) { assert(ts.representation->stringRep.length() <= Representation::maxLength); - XYPOSITION positionsRepr[Representation::maxLength + 1]; + std::array<XYPOSITION, Representation::maxLength + 1> positionsRepr; // ts.representation->stringRep is UTF-8. pCache->MeasureWidths(surface, vstyle, StyleControlChar, true, ts.representation->stringRep, - positionsRepr, multiThreaded); + positionsRepr.data(), multiThreaded); representationWidth = positionsRepr[ts.representation->stringRep.length() - 1]; if (FlagSet(ts.representation->appearance, RepresentationAppearance::Blob)) { representationWidth += vstyle.ctrlCharPadding; @@ -379,9 +378,9 @@ void LayoutSegments(IPositionCache *pCache, } } else if (vstyle.styles[styleSegment].invisibleRepresentation[0]) { const std::string_view text = vstyle.styles[styleSegment].invisibleRepresentation; - XYPOSITION positionsRepr[Representation::maxLength + 1]; + std::array<XYPOSITION, Representation::maxLength + 1> positionsRepr; // invisibleRepresentation is UTF-8. - pCache->MeasureWidths(surface, vstyle, styleSegment, true, text, positionsRepr, multiThreaded); + pCache->MeasureWidths(surface, vstyle, styleSegment, true, text, positionsRepr.data(), multiThreaded); const XYPOSITION representationWidth = positionsRepr[text.length() - 1]; std::fill(positions, positions + ts.length, representationWidth); } @@ -402,13 +401,10 @@ void EditView::LayoutLine(const EditModel &model, Surface *surface, const ViewSt PLATFORM_ASSERT(line < model.pdoc->LinesTotal()); PLATFORM_ASSERT(ll->chars); const Sci::Position posLineStart = model.pdoc->LineStart(line); - Sci::Position posLineEnd = model.pdoc->LineStart(line + 1); - // If the line is very long, limit the treatment to a length that should fit in the viewport - if (posLineEnd >(posLineStart + ll->maxLineLength)) { - posLineEnd = posLineStart + ll->maxLineLength; - } + const Sci::Position posLineEnd = std::min(model.pdoc->LineStart(line + 1), posLineStart + ll->maxLineLength); // Hard to cope when too narrow, so just assume there is space - width = std::max(width, 20); + constexpr int minimumWidth = 20; + width = std::max(width, minimumWidth); if (ll->validity == LineLayout::ValidLevel::checkTextAndStyle) { Sci::Position lineLength = posLineEnd - posLineStart; @@ -526,7 +522,7 @@ void EditView::LayoutLine(const EditModel &model, Surface *surface, const ViewSt for (const TextSegment &ts : segments) { if (vstyle.styles[ll->styles[ts.start]].visible && ts.representation && - (ll->chars[ts.start] == '\t')) { + ll->chars[ts.start] == '\t' && vstyle.tabDrawMode != TabDrawMode::ControlChar) { // Simple visible tab, go to next tab stop const XYPOSITION startTab = ll->positions[ts.start]; const XYPOSITION nextTab = NextTabstopPos(line, startTab, vstyle.tabWidth); @@ -614,7 +610,7 @@ void EditView::UpdateBidiData(const EditModel &model, const ViewStyle &vstyle, L const Representation *repr = model.reprs->RepresentationFromCharacter(std::string_view(&ll->chars[charsInLine], charWidth)); ll->bidiData->widthReprs[charsInLine] = 0.0f; - if (repr && ll->chars[charsInLine] != '\t') { + if (repr && (ll->chars[charsInLine] != '\t' || vstyle.tabDrawMode == TabDrawMode::ControlChar)) { ll->bidiData->widthReprs[charsInLine] = ll->positions[charsInLine + charWidth] - ll->positions[charsInLine]; } if (charWidth > 1) { @@ -745,7 +741,8 @@ SelectionPosition EditView::SPositionFromLocation(Surface *surface, const EditMo const int spaceOffset = static_cast<int>( (pt.x + subLineStart - ll->positions[rangeSubLine.end] + spaceWidth / 2) / spaceWidth); return SelectionPosition(rangeSubLine.end + posLineStart, spaceOffset); - } else if (canReturnInvalid) { + } + if (canReturnInvalid) { if (pt.x < (ll->positions[rangeSubLine.end] - subLineStart)) { return SelectionPosition(model.pdoc->MovePositionOutsideChar(rangeSubLine.end + posLineStart, 1)); } @@ -892,11 +889,11 @@ ColourRGBA TextBackground(const EditModel &model, const ViewStyle &vsDraw, const } if (background && (styleMain != StyleBraceLight) && (styleMain != StyleBraceBad)) { return *background; - } else { - return vsDraw.styles[styleMain].back; } + return vsDraw.styles[styleMain].back; } +// Draw inverted text in a filled rectangle but omit the 4 corner pixels so appears rounded. void DrawTextBlob(Surface *surface, const ViewStyle &vsDraw, PRectangle rcSegment, std::string_view text, ColourRGBA textBack, ColourRGBA textFore, bool fillBackground) { if (rcSegment.Empty()) @@ -910,42 +907,95 @@ void DrawTextBlob(Surface *surface, const ViewStyle &vsDraw, PRectangle rcSegmen rcCChar.left = rcCChar.left + 1; rcCChar.top = rcSegment.top + vsDraw.maxAscent - normalCharHeight; rcCChar.bottom = rcSegment.top + vsDraw.maxAscent + 1; - PRectangle rcCentral = rcCChar; - rcCentral.top++; - rcCentral.bottom--; + + // Ensure pixels to left and right coloured for central part avoiding top and bottom + // pixels which will be drawn by DrawTextClippedUTF8. + const PRectangle rcCentral = rcCChar.Inset(Point(0, 1)); surface->FillRectangleAligned(rcCentral, Fill(textFore)); - PRectangle rcChar = rcCChar; - rcChar.left++; - rcChar.right--; + + const PRectangle rcChar = rcCChar.Inset(Point(1, 0)); + // NOLINTNEXTLINE(readability-suspicious-call-argument) Inverted text surface->DrawTextClippedUTF8(rcChar, ctrlCharsFont, rcSegment.top + vsDraw.maxAscent, text, textBack, textFore); } void FillLineRemainder(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll, - Sci::Line line, PRectangle rcArea, int subLine) { + Sci::Line line, PRectangle rcLine, XYPOSITION left, int subLine) { + if (rcLine.Empty()) { + return; + } InSelection eolInSelection = InSelection::inNone; if (vsDraw.selection.visible && (subLine == (ll->lines - 1))) { eolInSelection = model.LineEndInSelection(line); } + const bool drawEOLSelection = eolInSelection && vsDraw.selection.eolFilled && (line < model.pdoc->LinesTotal() - 1); - if (eolInSelection && vsDraw.selection.eolFilled && (line < model.pdoc->LinesTotal() - 1) && (vsDraw.selection.layer == Layer::Base)) { - surface->FillRectangleAligned(rcArea, Fill(SelectionBackground(model, vsDraw, eolInSelection).Opaque())); + const PRectangle rcArea = Clamp(rcLine, Edge::left, left); // Limit to right side of line from 'left' + + const ColourRGBA selectionBack = drawEOLSelection ? SelectionBackground(model, vsDraw, eolInSelection) : ColourRGBA{}; + ColourRGBA base = vsDraw.styles[StyleDefault].back; + if (drawEOLSelection && (vsDraw.selection.layer == Layer::Base)) { + base = selectionBack; } else { const ColourOptional background = vsDraw.Background(model.GetMark(line), model.caret.active, ll->containsCaret); if (background) { - surface->FillRectangleAligned(rcArea, Fill(*background)); - } else if (vsDraw.styles[ll->styles[ll->numCharsInLine]].eolFilled) { - surface->FillRectangleAligned(rcArea, Fill(vsDraw.styles[ll->styles[ll->numCharsInLine]].back)); - } else { - surface->FillRectangleAligned(rcArea, Fill(vsDraw.styles[StyleDefault].back)); + base = *background; + } else if (vsDraw.styles[ll->LastStyle()].eolFilled) { + base = vsDraw.styles[ll->LastStyle()].back; } - if (eolInSelection && vsDraw.selection.eolFilled && (line < model.pdoc->LinesTotal() - 1) && (vsDraw.selection.layer != Layer::Base)) { - surface->FillRectangleAligned(rcArea, SelectionBackground(model, vsDraw, eolInSelection)); + } + surface->FillRectangleAligned(rcArea, Fill(base.Opaque())); + if (drawEOLSelection && (vsDraw.selection.layer != Layer::Base)) { + // This may be translucent + surface->FillRectangleAligned(rcArea, selectionBack); + } +} + +// Stadium ends require different amounts of padding to look reasonable around text. +constexpr XYPOSITION divisorAngle = 2.0; // <text> +constexpr XYPOSITION divisorCircle = 3.0; // (text) +constexpr XYPOSITION EndPadding(Surface::Ends side, XYPOSITION height) noexcept { + switch (side) { + case Surface::Ends::leftFlat: + case Surface::Ends::rightFlat: + return 1; + case Surface::Ends::leftAngle: + case Surface::Ends::rightAngle: + return height / divisorAngle; + default: + // semiCircles + return height / divisorCircle; + } +} + +struct HorizontalPadding { + XYPOSITION left = 0; + XYPOSITION right = 0; +}; + +[[nodiscard]] constexpr PRectangle TextPart(const PRectangle &rc, const HorizontalPadding &padding) noexcept { + return PRectangle(rc.left + padding.left, rc.top, rc.right - padding.right, rc.bottom); +} + +HorizontalPadding StadiumPadding(Scintilla::EOLAnnotationVisible eolAnnotationVisible, XYPOSITION height) noexcept { + if (eolAnnotationVisible >= EOLAnnotationVisible::Boxed) { + if (eolAnnotationVisible == EOLAnnotationVisible::Boxed) { + return { 1, 1 }; } + const Surface::Ends leftSide = static_cast<Surface::Ends>(static_cast<int>(eolAnnotationVisible) & 0xf); + const Surface::Ends rightSide = static_cast<Surface::Ends>(static_cast<int>(eolAnnotationVisible) & 0xf0); + const XYPOSITION leftSpace = EndPadding(leftSide, height); + const XYPOSITION rightSpace = EndPadding(rightSide, height); + return { leftSpace , rightSpace }; } + return {}; +} + } +void EditView::UpdateMaxWidth(XYPOSITION width) noexcept { + lineWidthMaxSeen = std::max(lineWidthMaxSeen, static_cast<int>(width)); } void EditView::DrawEOL(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll, @@ -955,27 +1005,23 @@ void EditView::DrawEOL(Surface *surface, const EditModel &model, const ViewStyle PRectangle rcSegment = rcLine; const bool lastSubLine = subLine == (ll->lines - 1); - XYPOSITION virtualSpace = 0; - if (lastSubLine) { - const XYPOSITION spaceWidth = vsDraw.styles[ll->EndLineStyle()].spaceWidth; - virtualSpace = static_cast<XYPOSITION>(model.sel.VirtualSpaceFor(model.pdoc->LineEnd(line))) * spaceWidth; - } + const Sci::Position virtualSpaces = lastSubLine ? model.VirtualSpaceForLine(line) : 0; + const XYPOSITION spaceWidth = lastSubLine ? vsDraw.styles[ll->EndLineStyle()].spaceWidth : 0; + const XYPOSITION virtualSpace = static_cast<XYPOSITION>(virtualSpaces) * spaceWidth; const XYPOSITION xEol = ll->positions[lineEnd] - subLineStart; // Fill the virtual space and show selections within it if (virtualSpace > 0.0f) { rcSegment.left = xEol + xStart; rcSegment.right = xEol + xStart + virtualSpace; - const ColourRGBA backgroundFill = background.value_or(vsDraw.styles[ll->styles[ll->numCharsInLine]].back); + const ColourRGBA backgroundFill = background.value_or(vsDraw.styles[ll->LastStyle()].back); surface->FillRectangleAligned(rcSegment, backgroundFill); if (vsDraw.selection.visible && (vsDraw.selection.layer == Layer::Base)) { const SelectionSegment virtualSpaceRange(SelectionPosition(model.pdoc->LineEnd(line)), - SelectionPosition(model.pdoc->LineEnd(line), - model.sel.VirtualSpaceFor(model.pdoc->LineEnd(line)))); + SelectionPosition(model.pdoc->LineEnd(line), virtualSpaces)); for (size_t r = 0; r<model.sel.Count(); r++) { const SelectionSegment portion = model.sel.Range(r).Intersect(virtualSpaceRange); if (!portion.Empty()) { - const XYPOSITION spaceWidth = vsDraw.styles[ll->EndLineStyle()].spaceWidth; rcSegment.left = xStart + ll->positions[portion.start.Position() - posLineStart] - subLineStart + portion.start.VirtualSpaceWidth(spaceWidth); rcSegment.right = xStart + ll->positions[portion.end.Position() - posLineStart] - @@ -989,30 +1035,29 @@ void EditView::DrawEOL(Surface *surface, const EditModel &model, const ViewStyle } } - InSelection eolInSelection = InSelection::inNone; - if (vsDraw.selection.visible && lastSubLine) { - eolInSelection = model.LineEndInSelection(line); - } + const InSelection eolInSelection = (vsDraw.selection.visible && lastSubLine) ? + model.LineEndInSelection(line) : InSelection::inNone; + const bool lastLine = line >= (model.pdoc->LinesTotal() - 1); + const bool drawEOLSelection = eolInSelection && !lastLine; const ColourRGBA selectionBack = SelectionBackground(model, vsDraw, eolInSelection); // Draw the [CR], [LF], or [CR][LF] blobs if visible line ends are on XYPOSITION blobsWidth = 0; if (lastSubLine) { - for (Sci::Position eolPos = ll->numCharsBeforeEOL; eolPos<ll->numCharsInLine;) { + for (int eolPos = ll->numCharsBeforeEOL; eolPos<ll->numCharsInLine;) { const int styleMain = ll->styles[eolPos]; const ColourOptional selectionFore = SelectionForeground(model, vsDraw, eolInSelection); ColourRGBA textFore = selectionFore.value_or(vsDraw.styles[styleMain].fore); char hexits[4] = ""; std::string_view ctrlChar; - Sci::Position widthBytes = 1; RepresentationAppearance appearance = RepresentationAppearance::Blob; - const Representation *repr = model.reprs->RepresentationFromCharacter(std::string_view(&ll->chars[eolPos], ll->numCharsInLine - eolPos)); - if (repr) { - // Representation of whole text - widthBytes = ll->numCharsInLine - eolPos; - } else { - repr = model.reprs->RepresentationFromCharacter(std::string_view(&ll->chars[eolPos], 1)); + const std::string_view rest(&ll->chars[eolPos], ll->numCharsInLine - eolPos); + const Representation *repr = model.reprs->RepresentationFromCharacter(rest); + const int widthBytes = repr ? static_cast<int>(rest.length()) : 1; + if (!repr) { + // No representation of whole line end so try first byte. + repr = model.reprs->RepresentationFromCharacter(rest.substr(0, 1)); } if (repr) { ctrlChar = repr->stringRep; @@ -1021,7 +1066,7 @@ void EditView::DrawEOL(Surface *surface, const EditModel &model, const ViewStyle textFore = repr->colour; } } else { - const unsigned char chEOL = ll->chars[eolPos]; + const unsigned char chEOL = rest.front(); if (UTF8IsAscii(chEOL)) { ctrlChar = ControlCharacterString(chEOL); } else { @@ -1030,69 +1075,54 @@ void EditView::DrawEOL(Surface *surface, const EditModel &model, const ViewStyle } } - rcSegment.left = xStart + ll->positions[eolPos] - subLineStart + virtualSpace; - rcSegment.right = xStart + ll->positions[eolPos + widthBytes] - subLineStart + virtualSpace; - blobsWidth += rcSegment.Width(); + const PRectangle rcBlob = rcLine.WithHorizontalBounds( + ll->Span(eolPos, eolPos + widthBytes).Offset(xStart - subLineStart + virtualSpace)); + blobsWidth += rcBlob.Width(); const ColourRGBA textBack = TextBackground(model, vsDraw, ll, background, eolInSelection, false, styleMain, eolPos); - if (eolInSelection && (line < model.pdoc->LinesTotal() - 1)) { - if (vsDraw.selection.layer == Layer::Base) { - surface->FillRectangleAligned(rcSegment, Fill(selectionBack.Opaque())); - } else { - surface->FillRectangleAligned(rcSegment, Fill(textBack)); - } + if (drawEOLSelection && (vsDraw.selection.layer == Layer::Base)) { + surface->FillRectangleAligned(rcBlob, Fill(selectionBack.Opaque())); } else { - surface->FillRectangleAligned(rcSegment, Fill(textBack)); + surface->FillRectangleAligned(rcBlob, Fill(textBack)); } - const bool drawEOLSelection = eolInSelection && (line < model.pdoc->LinesTotal() - 1); ColourRGBA blobText = textBack; if (drawEOLSelection && (vsDraw.selection.layer == Layer::UnderText)) { - surface->FillRectangleAligned(rcSegment, selectionBack); + surface->FillRectangleAligned(rcBlob, selectionBack); blobText = textBack.MixedWith(selectionBack, selectionBack.GetAlphaComponent()); } if (FlagSet(appearance, RepresentationAppearance::Blob)) { - DrawTextBlob(surface, vsDraw, rcSegment, ctrlChar, blobText, textFore, phasesDraw == PhasesDraw::One); + DrawTextBlob(surface, vsDraw, rcBlob, ctrlChar, blobText, textFore, phasesDraw == PhasesDraw::One); } else { - surface->DrawTextTransparentUTF8(rcSegment, vsDraw.styles[StyleControlChar].font.get(), - rcSegment.top + vsDraw.maxAscent, ctrlChar, textFore); + surface->DrawTextTransparentUTF8(rcBlob, vsDraw.styles[StyleControlChar].font.get(), + rcBlob.top + vsDraw.maxAscent, ctrlChar, textFore); } if (drawEOLSelection && (vsDraw.selection.layer == Layer::OverText)) { - surface->FillRectangleAligned(rcSegment, selectionBack); + surface->FillRectangleAligned(rcBlob, selectionBack); } eolPos += widthBytes; } } // Draw the eol-is-selected rectangle - rcSegment.left = xEol + xStart + virtualSpace + blobsWidth; - rcSegment.right = rcSegment.left + vsDraw.aveCharWidth; - - if (eolInSelection && (line < model.pdoc->LinesTotal() - 1) && (vsDraw.selection.layer == Layer::Base)) { - surface->FillRectangleAligned(rcSegment, Fill(selectionBack.Opaque())); - } else { - if (background) { - surface->FillRectangleAligned(rcSegment, Fill(*background)); - } else if (line < model.pdoc->LinesTotal() - 1) { - surface->FillRectangleAligned(rcSegment, Fill(vsDraw.styles[ll->styles[ll->numCharsInLine]].back)); - } else if (vsDraw.styles[ll->styles[ll->numCharsInLine]].eolFilled) { - surface->FillRectangleAligned(rcSegment, Fill(vsDraw.styles[ll->styles[ll->numCharsInLine]].back)); - } else { - surface->FillRectangleAligned(rcSegment, Fill(vsDraw.styles[StyleDefault].back)); - } - if (eolInSelection && (line < model.pdoc->LinesTotal() - 1) && (vsDraw.selection.layer != Layer::Base)) { - surface->FillRectangleAligned(rcSegment, selectionBack); - } + const PRectangle rcEOLIsSelected = rcLine.WithHorizontalBounds( + Interval::FromLeftAndWidth(xEol + xStart + virtualSpace + blobsWidth, vsDraw.aveCharWidth)); + ColourRGBA base = vsDraw.styles[StyleDefault].back; + if (drawEOLSelection && (vsDraw.selection.layer == Layer::Base)) { + base = selectionBack; + } else if (background) { + base = *background; + } else if (const Style &styleLast = vsDraw.styles[ll->LastStyle()]; styleLast.eolFilled) { + base = styleLast.back; + } + surface->FillRectangleAligned(rcEOLIsSelected, Fill(base.Opaque())); + if (drawEOLSelection && (vsDraw.selection.layer != Layer::Base)) { + surface->FillRectangleAligned(rcEOLIsSelected, selectionBack); } - - rcSegment.left = rcSegment.right; - if (rcSegment.left < rcLine.left) - rcSegment.left = rcLine.left; - rcSegment.right = rcLine.right; const bool drawEOLAnnotationStyledText = (vsDraw.eolAnnotationVisible != EOLAnnotationVisible::Hidden) && model.pdoc->EOLAnnotationStyledText(line).text; const bool fillRemainder = (!lastSubLine || (!model.GetFoldDisplayText(line) && !drawEOLAnnotationStyledText)); if (fillRemainder) { // Fill the remainder of the line - FillLineRemainder(surface, model, vsDraw, ll, line, rcSegment, subLine); + FillLineRemainder(surface, model, vsDraw, ll, line, rcLine, rcEOLIsSelected.right, subLine); } bool drawWrapMarkEnd = false; @@ -1109,8 +1139,8 @@ void EditView::DrawEOL(Surface *surface, const EditModel &model, const ViewStyle } if (drawWrapMarkEnd) { - PRectangle rcPlace = rcSegment; - const XYPOSITION maxLeft = rcPlace.right - vsDraw.aveCharWidth; + PRectangle rcPlace = rcLine; + const XYPOSITION maxLeft = rcLine.right - vsDraw.aveCharWidth; if (FlagSet(vsDraw.wrap.visualFlagsLocation, WrapVisualLocation::EndByText)) { rcPlace.left = std::min(xEol + xStart + virtualSpace, maxLeft); @@ -1149,8 +1179,7 @@ void EditView::DrawFoldDisplayText(Surface *surface, const EditModel &model, con } const XYPOSITION spaceWidth = vsDraw.styles[ll->EndLineStyle()].spaceWidth; - const XYPOSITION virtualSpace = static_cast<XYPOSITION>(model.sel.VirtualSpaceFor( - model.pdoc->LineEnd(line))) * spaceWidth; + const XYPOSITION virtualSpace = static_cast<XYPOSITION>(model.VirtualSpaceForLine(line)) * spaceWidth; rcSegment.left = xStart + ll->positions[ll->numCharsInLine] - subLineStart + virtualSpace + vsDraw.aveCharWidth; rcSegment.right = rcSegment.left + static_cast<XYPOSITION>(widthFoldDisplayText); @@ -1161,22 +1190,15 @@ void EditView::DrawFoldDisplayText(Surface *surface, const EditModel &model, con false, StyleFoldDisplayText, -1); if (model.trackLineWidth) { - if (rcSegment.right + 1> lineWidthMaxSeen) { - // Fold display text border drawn on rcSegment.right with width 1 is the last visible object of the line - lineWidthMaxSeen = static_cast<int>(rcSegment.right + 1); - } + // Fold display text border drawn on rcSegment.right with width 1 is the last visible object of the line + UpdateMaxWidth(rcSegment.right + 1); } if (FlagSet(phase, DrawPhase::back)) { surface->FillRectangleAligned(rcSegment, Fill(textBack)); // Fill Remainder of the line - PRectangle rcRemainder = rcSegment; - rcRemainder.left = rcRemainder.right; - if (rcRemainder.left < rcLine.left) - rcRemainder.left = rcLine.left; - rcRemainder.right = rcLine.right; - FillLineRemainder(surface, model, vsDraw, ll, line, rcRemainder, subLine); + FillLineRemainder(surface, model, vsDraw, ll, line, rcLine, rcSegment.right, subLine); } if (FlagSet(phase, DrawPhase::text)) { @@ -1228,47 +1250,14 @@ void EditView::DrawEOLAnnotationText(Surface *surface, const EditModel &model, c const Font *fontText = vsDraw.styles[style].font.get(); const Surface::Ends ends = static_cast<Surface::Ends>(static_cast<int>(vsDraw.eolAnnotationVisible) & 0xff); - const Surface::Ends leftSide = static_cast<Surface::Ends>(static_cast<int>(ends) & 0xf); - const Surface::Ends rightSide = static_cast<Surface::Ends>(static_cast<int>(ends) & 0xf0); - - XYPOSITION leftBoxSpace = 0; - XYPOSITION rightBoxSpace = 0; - if (vsDraw.eolAnnotationVisible >= EOLAnnotationVisible::Boxed) { - leftBoxSpace = 1; - rightBoxSpace = 1; - if (vsDraw.eolAnnotationVisible != EOLAnnotationVisible::Boxed) { - switch (leftSide) { - case Surface::Ends::leftFlat: - leftBoxSpace = 1; - break; - case Surface::Ends::leftAngle: - leftBoxSpace = rcLine.Height() / 2.0; - break; - case Surface::Ends::semiCircles: - default: - leftBoxSpace = rcLine.Height() / 3.0; - break; - } - switch (rightSide) { - case Surface::Ends::rightFlat: - rightBoxSpace = 1; - break; - case Surface::Ends::rightAngle: - rightBoxSpace = rcLine.Height() / 2.0; - break; - case Surface::Ends::semiCircles: - default: - rightBoxSpace = rcLine.Height() / 3.0; - break; - } - } - } + + const HorizontalPadding padding = StadiumPadding(vsDraw.eolAnnotationVisible, rcLine.Height()); + const int widthEOLAnnotationText = static_cast<int>(surface->WidthTextUTF8(fontText, eolAnnotationText) + - leftBoxSpace + rightBoxSpace); + padding.left + padding.right); const XYPOSITION spaceWidth = vsDraw.styles[ll->EndLineStyle()].spaceWidth; - const XYPOSITION virtualSpace = static_cast<XYPOSITION>(model.sel.VirtualSpaceFor( - model.pdoc->LineEnd(line))) * spaceWidth; + const XYPOSITION virtualSpace = static_cast<XYPOSITION>(model.VirtualSpaceForLine(line)) * spaceWidth; rcSegment.left = xStart + ll->positions[ll->numCharsInLine] - subLineStart + virtualSpace + vsDraw.aveCharWidth; @@ -1288,10 +1277,8 @@ void EditView::DrawEOLAnnotationText(Surface *surface, const EditModel &model, c false, static_cast<int>(style), -1); if (model.trackLineWidth) { - if (rcSegment.right + 1> lineWidthMaxSeen) { - // EOL Annotation text border drawn on rcSegment.right with width 1 is the last visible object of the line - lineWidthMaxSeen = static_cast<int>(rcSegment.right + 1); - } + // EOL Annotation text border drawn on rcSegment.right with width 1 is the last visible object of the line + UpdateMaxWidth(rcSegment.right + 1); } if (FlagSet(phase, DrawPhase::back)) { @@ -1299,14 +1286,10 @@ void EditView::DrawEOLAnnotationText(Surface *surface, const EditModel &model, c // it may be double drawing. This is to allow stadiums with // curved or angled ends to have the area outside in the correct // background colour. - PRectangle rcRemainder = rcSegment; - rcRemainder.right = rcLine.right; - FillLineRemainder(surface, model, vsDraw, ll, line, rcRemainder, subLine); + FillLineRemainder(surface, model, vsDraw, ll, line, rcLine, rcSegment.right, subLine); } - PRectangle rcText = rcSegment; - rcText.left += leftBoxSpace; - rcText.right -= rightBoxSpace; + const PRectangle rcText = TextPart(rcSegment, padding); // For single phase drawing, draw the text then any box over it if (FlagSet(phase, DrawPhase::text)) { @@ -1369,7 +1352,7 @@ constexpr bool AnnotationBoxedOrIndented(AnnotationVisible annotationVisible) no } void EditView::DrawAnnotation(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll, - Sci::Line line, int xStart, PRectangle rcLine, int subLine, DrawPhase phase) { + Sci::Line line, int xOrigin, PRectangle rcLine, int subLine, DrawPhase phase) { const int indent = static_cast<int>(model.pdoc->GetLineIndentation(line) * vsDraw.spaceWidth); PRectangle rcSegment = rcLine; const int annotationLine = subLine - ll->lines; @@ -1378,17 +1361,16 @@ void EditView::DrawAnnotation(Surface *surface, const EditModel &model, const Vi if (FlagSet(phase, DrawPhase::back)) { surface->FillRectangleAligned(rcSegment, Fill(vsDraw.styles[0].back)); } - rcSegment.left = static_cast<XYPOSITION>(xStart); + rcSegment.left = static_cast<XYPOSITION>(xOrigin); if (model.trackLineWidth || AnnotationBoxedOrIndented(vsDraw.annotationVisible)) { // Only care about calculating width if tracking or need to draw indented box int widthAnnotation = WidestLineWidth(surface, vsDraw, vsDraw.annotationStyleOffset, stAnnotation); if (AnnotationBoxedOrIndented(vsDraw.annotationVisible)) { widthAnnotation += static_cast<int>(vsDraw.spaceWidth * 2); // Margins - rcSegment.left = static_cast<XYPOSITION>(xStart + indent); + rcSegment.left = static_cast<XYPOSITION>(xOrigin + indent); rcSegment.right = rcSegment.left + widthAnnotation; } - if (widthAnnotation > lineWidthMaxSeen) - lineWidthMaxSeen = widthAnnotation; + UpdateMaxWidth(widthAnnotation); } const int annotationLines = model.pdoc->AnnotationLines(line); size_t start = 0; @@ -1430,7 +1412,7 @@ void EditView::DrawAnnotation(Surface *surface, const EditModel &model, const Vi namespace { void DrawBlockCaret(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll, - int subLine, int xStart, Sci::Position offset, Sci::Position posCaret, PRectangle rcCaret, ColourRGBA caretColour) { + int subLine, int xOrigin, Sci::Position offset, Sci::Position posCaret, PRectangle rcCaret, ColourRGBA caretColour) { const Sci::Position lineStart = ll->LineStart(subLine); Sci::Position posBefore = posCaret; @@ -1456,8 +1438,7 @@ void DrawBlockCaret(Surface *surface, const EditModel &model, const ViewStyle &v // See if the next character shares horizontal space, if so we'll // need to draw that too. - if (offsetFirstChar < 0) - offsetFirstChar = 0; + offsetFirstChar = std::max<Sci::Position>(offsetFirstChar, 0); numCharsToDraw = offsetLastChar - offsetFirstChar; while ((offsetLastChar < ll->LineStart(subLine + 1)) && (offsetLastChar <= ll->numCharsInLine)) { // Update posAfter to point to the 2nd next char, this is where @@ -1475,8 +1456,8 @@ void DrawBlockCaret(Surface *surface, const EditModel &model, const ViewStyle &v } // We now know what to draw, update the caret drawing rectangle - rcCaret.left = ll->positions[offsetFirstChar] - ll->positions[lineStart] + xStart; - rcCaret.right = ll->positions[offsetFirstChar + numCharsToDraw] - ll->positions[lineStart] + xStart; + rcCaret.left = ll->positions[offsetFirstChar] - ll->positions[lineStart] + xOrigin; + rcCaret.right = ll->positions[offsetFirstChar + numCharsToDraw] - ll->positions[lineStart] + xOrigin; // Adjust caret position to take into account any word wrapping symbols. if ((ll->wrapIndent != 0) && (lineStart != 0)) { @@ -1498,7 +1479,7 @@ void DrawBlockCaret(Surface *surface, const EditModel &model, const ViewStyle &v } void EditView::DrawCarets(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll, - Sci::Line lineDoc, int xStart, PRectangle rcLine, int subLine) const { + Sci::Line lineDoc, int xOrigin, PRectangle rcLine, int subLine) const { // When drag is active it is the only caret drawn const bool drawDrag = model.posDrag.IsValid(); if (!vsDraw.selection.visible && !drawDrag) @@ -1545,7 +1526,6 @@ void EditView::DrawCarets(Surface *surface, const EditModel &model, const ViewSt bool canDrawBlockCaret = true; bool drawBlockCaret = false; XYPOSITION widthOverstrikeCaret; - XYPOSITION caretWidthOffset = 0; PRectangle rcCaret = rcLine; if (posCaret.Position() == model.pdoc->Length()) { // At end of document @@ -1558,12 +1538,15 @@ void EditView::DrawCarets(Surface *surface, const EditModel &model, const ViewSt const int widthChar = model.pdoc->LenChar(posCaret.Position()); widthOverstrikeCaret = ll->positions[offset + widthChar] - ll->positions[offset]; } - if (widthOverstrikeCaret < 3) // Make sure its visible - widthOverstrikeCaret = 3; + // Make sure block caret visible + constexpr XYPOSITION minimumBlockCaretWidth = 3.0f; + widthOverstrikeCaret = std::max(widthOverstrikeCaret, minimumBlockCaretWidth); - if (xposCaret > 0) - caretWidthOffset = 0.51f; // Move back so overlaps both character cells. - xposCaret += xStart; + // Move back slightly so line caret overlaps both character cells unless at start of text area. + constexpr XYPOSITION justOverHalf = 0.51f; + const XYPOSITION caretWidthOffset = (xposCaret > 0) ? justOverHalf : 0; + + xposCaret += xOrigin; const ViewStyle::CaretShape caretShape = drawDrag ? ViewStyle::CaretShape::line : vsDraw.CaretShapeForMode(model.inOverstrike, mainCaret); if (drawDrag) { @@ -1593,7 +1576,7 @@ void EditView::DrawCarets(Surface *surface, const EditModel &model, const ViewSt const ColourRGBA caretColour = vsDraw.ElementColourForced(elementCaret); //assert(caretColour.IsOpaque()); if (drawBlockCaret) { - DrawBlockCaret(surface, model, vsDraw, ll, subLine, xStart, offset, posCaret.Position(), rcCaret, caretColour); + DrawBlockCaret(surface, model, vsDraw, ll, subLine, xOrigin, offset, posCaret.Position(), rcCaret, caretColour); } else { surface->FillRectangleAligned(rcCaret, Fill(caretColour)); } @@ -1690,7 +1673,7 @@ void DrawBackground(Surface *surface, const EditModel &model, const ViewStyle &v ColourRGBA textBack = TextBackground(model, vsDraw, ll, background, inSelection, inHotspot, ll->styles[i], i); if (ts.representation) { - if (ll->chars[i] == '\t') { + if (ll->chars[i] == '\t' && vsDraw.tabDrawMode != TabDrawMode::ControlChar) { // Tab display if (drawWhitespaceBackground && vsDraw.WhiteSpaceVisible(inIndentation)) { textBack = vsDraw.ElementColourForced(Element::WhiteSpaceBack).Opaque(); @@ -1771,10 +1754,8 @@ void DrawTranslucentSelection(Surface *surface, const EditModel &model, const Vi const XYPOSITION subLineStart = ll->positions[lineRange.start]; const XYPOSITION horizontalOffset = xStart - subLineStart; // For each selection draw - Sci::Position virtualSpaces = 0; - if (subLine == (ll->lines - 1)) { - virtualSpaces = model.sel.VirtualSpaceFor(model.pdoc->LineEnd(line)); - } + const Sci::Position virtualSpaces = (subLine == (ll->lines - 1)) ? + model.VirtualSpaceForLine(line) : 0; const SelectionPosition posStart(posLineStart + lineRange.start); const SelectionPosition posEnd(posLineStart + lineRange.end, virtualSpaces); const SelectionSegment virtualSpaceRange(posStart, posEnd); @@ -1912,7 +1893,7 @@ void DrawTabArrow(Surface *surface, PRectangle rcTab, int ymid, // Draw the arrow head if needed if (vsDraw.tabDrawMode == TabDrawMode::LongArrow) { - XYPOSITION ydiff = std::floor(rcTab.Height() / 2.0f); + XYPOSITION ydiff = std::floor(rcTab.Height() / 2); XYPOSITION xhead = rightStroke - ydiff; if (xhead <= rcTab.left) { ydiff -= rcTab.left - xhead; @@ -2031,7 +2012,7 @@ void DrawIndicators(Surface *surface, const EditModel &model, const ViewStyle &v const Sci::Position endPos = std::min(rangeRun.end, posLineEnd); const int edition = model.pdoc->EditionAt(startPos); if (edition != 0) { - const int indicator = (edition - 1) * 2 + indexHistory; + const int indicator = ((edition - 1) * 2) + indexHistory; const Sci::Position posSecond = model.pdoc->MovePositionOutsideChar(rangeRun.First() + 1, 1); DrawIndicator(indicator, startPos - posLineStart, endPos - posLineStart, surface, vsDraw, ll, xStart, rcLine, posSecond - posLineStart, subLine, Indicator::State::normal, @@ -2048,7 +2029,7 @@ void DrawIndicators(Surface *surface, const EditModel &model, const ViewStyle &v const Sci::Position posSecond = model.pdoc->MovePositionOutsideChar(startPos + 1, 1); for (unsigned int edition = 0; edition < 4; edition++) { if (editions & (1 << edition)) { - const int indicator = edition * 2 + indexHistory + 1; + const int indicator = (edition * 2) + indexHistory + 1; DrawIndicator(indicator, startPos - posLineStart, posSecond - posLineStart, surface, vsDraw, ll, xStart, rcLine, posSecond - posLineStart, subLine, Indicator::State::normal, 1, model.BidirectionalEnabled(), tabWidthMinimumPixels); @@ -2102,12 +2083,12 @@ ColourRGBA InvertedLight(ColourRGBA orig) noexcept { r = r * il / l; g = g * il / l; b = b * il / l; - return ColourRGBA(std::min(r, 0xffu), std::min(g, 0xffu), std::min(b, 0xffu)); + return ColourRGBA(std::min(r, maximumByte), std::min(g, maximumByte), std::min(b, maximumByte)); } } -void EditView::DrawIndentGuide(Surface *surface, XYPOSITION start, PRectangle rcSegment, bool highlight, bool offset) { +void EditView::DrawIndentGuide(Surface *surface, XYPOSITION start, PRectangle rcSegment, bool highlight, bool offset) const { const Point from = Point::FromInts(0, offset ? 1 : 0); const PRectangle rcCopyArea(start + 1, rcSegment.top, start + 2, rcSegment.bottom); @@ -2197,7 +2178,7 @@ void EditView::DrawForeground(Surface *surface, const EditModel &model, const Vi } ColourRGBA textBack = TextBackground(model, vsDraw, ll, background, inSelection, inHotspot, styleMain, i); if (ts.representation) { - if (ll->chars[i] == '\t') { + if (ll->chars[i] == '\t' && vsDraw.tabDrawMode != TabDrawMode::ControlChar) { // Tab display if (phasesDraw == PhasesDraw::One) { if (drawWhitespaceBackground && vsDraw.WhiteSpaceVisible(inIndentation)) @@ -2219,7 +2200,7 @@ void EditView::DrawForeground(Surface *surface, const EditModel &model, const Vi if (vsDraw.WhiteSpaceVisible(inIndentation)) { const PRectangle rcTab(rcSegment.left + 1, rcSegment.top + tabArrowHeight, rcSegment.right - 1, rcSegment.bottom - vsDraw.maxDescent); - const int segmentTop = static_cast<int>(rcSegment.top) + vsDraw.lineHeight / 2; + const int segmentTop = static_cast<int>(rcSegment.top) + (vsDraw.lineHeight / 2); const ColourRGBA whiteSpaceFore = vsDraw.ElementColour(Element::WhiteSpace).value_or(textFore); if (!customDrawTabArrow) DrawTabArrow(surface, rcTab, segmentTop, vsDraw, Stroke(whiteSpaceFore, 1.0f)); @@ -2229,12 +2210,13 @@ void EditView::DrawForeground(Surface *surface, const EditModel &model, const Vi } } else { inIndentation = false; - if (vsDraw.controlCharSymbol >= 32) { + if (vsDraw.controlCharSymbol >= ' ') { // Using one font for all control characters so it can be controlled independently to ensure // the box goes around the characters tightly. Seems to be no way to work out what height // is taken by an individual character - internal leading gives varying results. const Font *ctrlCharsFont = vsDraw.styles[StyleControlChar].font.get(); const char cc[2] = { static_cast<char>(vsDraw.controlCharSymbol), '\0' }; + // NOLINTNEXTLINE(readability-suspicious-call-argument) Inverted text surface->DrawTextNoClip(rcSegment, ctrlCharsFont, ybase, cc, textBack, textFore); } else { @@ -2286,7 +2268,7 @@ void EditView::DrawForeground(Surface *surface, const EditModel &model, const Vi } const int halfDotWidth = vsDraw.whitespaceSize / 2; PRectangle rcDot(xmid - halfDotWidth, - rcSegment.top + vsDraw.lineHeight / 2, 0.0f, 0.0f); + rcSegment.top + (vsDraw.lineHeight / 2), 0.0f, 0.0f); rcDot.right = rcDot.left + vsDraw.whitespaceSize; rcDot.bottom = rcDot.top + vsDraw.whitespaceSize; const ColourRGBA whiteSpaceFore = vsDraw.ElementColour(Element::WhiteSpace).value_or(textFore); @@ -2336,12 +2318,16 @@ void EditView::DrawIndentGuidesOverEmpty(Surface *surface, const EditModel &mode // Find the most recent line with some text + constexpr int largeIndent = 100000; + + constexpr Sci::Line maxCheck = 20; + Sci::Line lineLastWithText = line; - while (lineLastWithText > std::max(line - 20, static_cast<Sci::Line>(0)) && model.pdoc->IsWhiteLine(lineLastWithText)) { + while (lineLastWithText > std::max(line - maxCheck, static_cast<Sci::Line>(0)) && model.pdoc->IsWhiteLine(lineLastWithText)) { lineLastWithText--; } if (lineLastWithText < line) { - xStartText = 100000; // Don't limit to visible indentation on empty line + xStartText = largeIndent; // Don't limit to visible indentation on empty line // This line is empty, so use indentation of last line with text int indentLastWithText = model.pdoc->GetLineIndentation(lineLastWithText); const int isFoldHeader = LevelIsHeader(model.pdoc->GetFoldLevel(lineLastWithText)); @@ -2350,21 +2336,21 @@ void EditView::DrawIndentGuidesOverEmpty(Surface *surface, const EditModel &mode indentLastWithText += model.pdoc->IndentSize(); } if (vsDraw.viewIndentationGuides == IndentView::LookForward) { - // In viLookForward mode, previous line only used if it is a fold header + // In LookForward mode, previous line only used if it is a fold header if (isFoldHeader) { indentSpace = std::max(indentSpace, indentLastWithText); } - } else { // viLookBoth + } else { // LookBoth indentSpace = std::max(indentSpace, indentLastWithText); } } Sci::Line lineNextWithText = line; - while (lineNextWithText < std::min(line + 20, model.pdoc->LinesTotal()) && model.pdoc->IsWhiteLine(lineNextWithText)) { + while (lineNextWithText < std::min(line + maxCheck, model.pdoc->LinesTotal()) && model.pdoc->IsWhiteLine(lineNextWithText)) { lineNextWithText++; } if (lineNextWithText > line) { - xStartText = 100000; // Don't limit to visible indentation on empty line + xStartText = largeIndent; // Don't limit to visible indentation on empty line // This line is empty, so use indentation of first next line with text indentSpace = std::max(indentSpace, model.pdoc->GetLineIndentation(lineNextWithText)); @@ -2381,10 +2367,10 @@ void EditView::DrawIndentGuidesOverEmpty(Surface *surface, const EditModel &mode } void EditView::DrawLine(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll, - Sci::Line line, Sci::Line lineVisible, int xStart, PRectangle rcLine, int subLine, DrawPhase phase) { + Sci::Line line, Sci::Line lineVisible, int xOrigin, PRectangle rcLine, int subLine, DrawPhase phase) { if (subLine >= ll->lines) { - DrawAnnotation(surface, model, vsDraw, ll, line, xStart, rcLine, subLine, phase); + DrawAnnotation(surface, model, vsDraw, ll, line, xOrigin, rcLine, subLine, phase); return; // No further drawing } @@ -2404,10 +2390,10 @@ void EditView::DrawLine(Surface *surface, const EditModel &model, const ViewStyl if ((ll->wrapIndent != 0) && (subLine > 0)) { if (FlagSet(phase, DrawPhase::back)) { - DrawWrapIndentAndMarker(surface, vsDraw, ll, xStart, rcLine, background, customDrawWrapMarker, model.caret.active); + DrawWrapIndentAndMarker(surface, vsDraw, ll, xOrigin, rcLine, background, customDrawWrapMarker, model.caret.active); } - xStart += static_cast<int>(ll->wrapIndent); } + const int xStart = xOrigin + ((subLine > 0) ? static_cast<int>(ll->wrapIndent) : 0); if (phasesDraw != PhasesDraw::One) { if (FlagSet(phase, DrawPhase::back)) { @@ -2497,7 +2483,7 @@ void EditView::PaintText(Surface *surfaceWindow, const EditModel &model, const V const Point ptOrigin = model.GetVisibleOriginInMain(); const int screenLinePaintFirst = static_cast<int>(rcArea.top) / vsDraw.lineHeight; - const int xStart = vsDraw.textStart - model.xOffset + static_cast<int>(ptOrigin.x); + const int xOrigin = vsDraw.textStart - model.xOffset + static_cast<int>(ptOrigin.x); const SelectionPosition posCaret = model.posDrag.IsValid() ? model.posDrag : model.sel.RangeMain().caret; const Sci::Line lineCaret = model.pdoc->SciLineFromPosition(posCaret.Position()); @@ -2588,7 +2574,7 @@ void EditView::PaintText(Surface *surfaceWindow, const EditModel &model, const V surface->FillRectangleAligned(rcSpacer, Fill(vsDraw.styles[StyleDefault].back)); } - DrawLine(surface, model, vsDraw, ll.get(), lineDoc, visibleLine, xStart, rcLine, subLine, phase); + DrawLine(surface, model, vsDraw, ll.get(), lineDoc, visibleLine, xOrigin, rcLine, subLine, phase); #if defined(TIME_PAINTING) durPaint += ep.Duration(true); #endif @@ -2600,7 +2586,7 @@ void EditView::PaintText(Surface *surfaceWindow, const EditModel &model, const V } if (FlagSet(phase, DrawPhase::carets)) { - DrawCarets(surface, model, vsDraw, ll.get(), lineDoc, xStart, rcLine, subLine); + DrawCarets(surface, model, vsDraw, ll.get(), lineDoc, xOrigin, rcLine, subLine); } if (bufferedDraw) { @@ -2612,8 +2598,7 @@ void EditView::PaintText(Surface *surfaceWindow, const EditModel &model, const V surfaceWindow->Copy(rcCopyArea, from, *pixmapLine); } - lineWidthMaxSeen = std::max( - lineWidthMaxSeen, static_cast<int>(ll->positions[ll->numCharsInLine])); + UpdateMaxWidth(ll->positions[ll->numCharsInLine]); #if defined(TIME_PAINTING) durCopy += ep.Duration(true); #endif @@ -2646,14 +2631,14 @@ void EditView::PaintText(Surface *surfaceWindow, const EditModel &model, const V surfaceWindow->FillRectangleAligned(rcBeyondEOF, Fill(vsDraw.styles[StyleDefault].back)); if (vsDraw.edgeState == EdgeVisualStyle::Line) { const int edgeX = static_cast<int>(vsDraw.theEdge.column * vsDraw.spaceWidth); - rcBeyondEOF.left = static_cast<XYPOSITION>(edgeX + xStart); + rcBeyondEOF.left = static_cast<XYPOSITION>(edgeX + xOrigin); rcBeyondEOF.right = rcBeyondEOF.left + 1; surfaceWindow->FillRectangleAligned(rcBeyondEOF, Fill(vsDraw.theEdge.colour)); } else if (vsDraw.edgeState == EdgeVisualStyle::MultiLine) { for (size_t edge = 0; edge < vsDraw.theMultiEdge.size(); edge++) { if (vsDraw.theMultiEdge[edge].column >= 0) { const int edgeX = static_cast<int>(vsDraw.theMultiEdge[edge].column * vsDraw.spaceWidth); - rcBeyondEOF.left = static_cast<XYPOSITION>(edgeX + xStart); + rcBeyondEOF.left = static_cast<XYPOSITION>(edgeX + xOrigin); rcBeyondEOF.right = rcBeyondEOF.left + 1; surfaceWindow->FillRectangleAligned(rcBeyondEOF, Fill(vsDraw.theMultiEdge[edge].colour)); } @@ -2750,13 +2735,11 @@ Sci::Position EditView::FormatRange(bool draw, CharacterRangeFull chrg, Rectangl 1u << static_cast<unsigned int>(MarkerOutline::HistoryRevertedToModified); vsPrint.maskInLine &= ~changeMarkers; + const int linesInArea = (rc.bottom - rc.top) / vsPrint.lineHeight; const Sci::Line linePrintStart = model.pdoc->SciLineFromPosition(chrg.cpMin); - Sci::Line linePrintLast = linePrintStart + (rc.bottom - rc.top) / vsPrint.lineHeight - 1; - if (linePrintLast < linePrintStart) - linePrintLast = linePrintStart; const Sci::Line linePrintMax = model.pdoc->SciLineFromPosition(chrg.cpMax); - if (linePrintLast > linePrintMax) - linePrintLast = linePrintMax; + const Sci::Line linePrintLast = std::min(std::max(linePrintStart + linesInArea - 1, linePrintStart), linePrintMax); + //Platform::DebugPrintf("Formatting lines=[%0d,%0d,%0d] top=%0d bottom=%0d line=%0d %0d\n", // linePrintStart, linePrintLast, linePrintMax, rc.top, rc.bottom, vsPrint.lineHeight, // surfaceMeasure->Height(vsPrint.styles[StyleLineNumber].font)); @@ -2767,7 +2750,7 @@ Sci::Position EditView::FormatRange(bool draw, CharacterRangeFull chrg, Rectangl // Ensure we are styled to where we are formatting. model.pdoc->EnsureStyledTo(endPosPrint); - const int xStart = vsPrint.fixedColumnWidth + rc.left; + const int xOrigin = vsPrint.fixedColumnWidth + rc.left; int ypos = rc.top; Sci::Line lineDoc = linePrintStart; @@ -2841,7 +2824,7 @@ Sci::Position EditView::FormatRange(bool draw, CharacterRangeFull chrg, Rectangl if (draw) { rcLine.top = static_cast<XYPOSITION>(ypos); rcLine.bottom = static_cast<XYPOSITION>(ypos + vsPrint.lineHeight); - DrawLine(surface, model, vsPrint, &ll, lineDoc, visibleLine, xStart, rcLine, iwl, DrawPhase::all); + DrawLine(surface, model, vsPrint, &ll, lineDoc, visibleLine, xOrigin, rcLine, iwl, DrawPhase::all); } ypos += vsPrint.lineHeight; } diff --git a/src/EditView.h b/src/EditView.h index 1e28d25c5..58e9c3d80 100644 --- a/src/EditView.h +++ b/src/EditView.h @@ -133,6 +133,7 @@ public: Sci::Position StartEndDisplayLine(Surface *surface, const EditModel &model, Sci::Position pos, bool start, const ViewStyle &vs); private: + void UpdateMaxWidth(XYPOSITION width) noexcept; void DrawEOL(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll, Sci::Line line, int xStart, PRectangle rcLine, int subLine, Sci::Position lineEnd, XYPOSITION subLineStart, ColourOptional background); void DrawFoldDisplayText(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll, @@ -140,17 +141,17 @@ private: void DrawEOLAnnotationText(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll, Sci::Line line, int xStart, PRectangle rcLine, int subLine, XYPOSITION subLineStart, DrawPhase phase); void DrawAnnotation(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll, - Sci::Line line, int xStart, PRectangle rcLine, int subLine, DrawPhase phase); + Sci::Line line, int xOrigin, PRectangle rcLine, int subLine, DrawPhase phase); void DrawCarets(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll, - Sci::Line lineDoc, int xStart, PRectangle rcLine, int subLine) const; - void DrawIndentGuide(Surface *surface, XYPOSITION start, PRectangle rcSegment, bool highlight, bool offset); + Sci::Line lineDoc, int xOrigin, PRectangle rcLine, int subLine) const; + void DrawIndentGuide(Surface *surface, XYPOSITION start, PRectangle rcSegment, bool highlight, bool offset) const; void DrawForeground(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll, int xStart, PRectangle rcLine, int subLine, Sci::Line lineVisible, Range lineRange, Sci::Position posLineStart, ColourOptional background); void DrawIndentGuidesOverEmpty(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll, Sci::Line line, int xStart, PRectangle rcLine, int subLine, Sci::Line lineVisible); void DrawLine(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll, - Sci::Line line, Sci::Line lineVisible, int xStart, PRectangle rcLine, int subLine, DrawPhase phase); + Sci::Line line, Sci::Line lineVisible, int xOrigin, PRectangle rcLine, int subLine, DrawPhase phase); public: void PaintText(Surface *surfaceWindow, const EditModel &model, const ViewStyle &vsDraw, diff --git a/src/Editor.cxx b/src/Editor.cxx index e7a309e67..0289fa5e5 100644 --- a/src/Editor.cxx +++ b/src/Editor.cxx @@ -143,6 +143,7 @@ Editor::Editor() : durationWrapOneByte(0.000001, 0.00000001, 0.00001) { dwelling = false; ptMouseLast.x = 0; ptMouseLast.y = 0; + dragDropEnabled = true; inDragDrop = DragDrop::none; dropWentOutside = false; posDrop = SelectionPosition(Sci::invalidPosition); @@ -4167,42 +4168,37 @@ void Editor::Indent(bool forwards, bool lineIndent) { Sci::Position caretPosition = sel.Range(r).caret.Position(); const Sci::Line lineCurrentPos = pdoc->SciLineFromPosition(caretPosition); if (lineOfAnchor == lineCurrentPos && !lineIndent) { + const int indentationStep = pdoc->IndentSize(); if (forwards) { pdoc->DeleteChars(sel.Range(r).Start().Position(), sel.Range(r).Length()); caretPosition = sel.Range(r).caret.Position(); - if (pdoc->GetColumn(caretPosition) <= pdoc->GetColumn(pdoc->GetLineIndentPosition(lineCurrentPos)) && - pdoc->tabIndents) { - const int indentation = pdoc->GetLineIndentation(lineCurrentPos); - const int indentationStep = pdoc->IndentSize(); + const int indentation = pdoc->GetLineIndentation(lineCurrentPos); + const Sci::Position column = pdoc->GetColumn(caretPosition); + if (column <= indentation && pdoc->tabIndents) { + // Inside initial whitespace const Sci::Position posSelect = pdoc->SetLineIndentation( - lineCurrentPos, indentation + indentationStep - indentation % indentationStep); + lineCurrentPos, indentation + indentationStep - (indentation % indentationStep)); sel.Range(r) = SelectionRange(posSelect); } else { if (pdoc->useTabs) { - const Sci::Position lengthInserted = pdoc->InsertString(caretPosition, "\t", 1); + const Sci::Position lengthInserted = pdoc->InsertString(caretPosition, "\t"); sel.Range(r) = SelectionRange(caretPosition + lengthInserted); } else { - int numSpaces = (pdoc->tabInChars) - - static_cast<int>((pdoc->GetColumn(caretPosition) % (pdoc->tabInChars))); - if (numSpaces < 1) - numSpaces = pdoc->tabInChars; + const Sci::Position numSpaces = pdoc->tabInChars - (column % pdoc->tabInChars); const std::string spaceText(numSpaces, ' '); const Sci::Position lengthInserted = pdoc->InsertString(caretPosition, spaceText); sel.Range(r) = SelectionRange(caretPosition + lengthInserted); } } } else { - if (pdoc->GetColumn(caretPosition) <= pdoc->GetLineIndentation(lineCurrentPos) && - pdoc->tabIndents) { - const int indentation = pdoc->GetLineIndentation(lineCurrentPos); - const int indentationStep = pdoc->IndentSize(); + const int indentation = pdoc->GetLineIndentation(lineCurrentPos); + const Sci::Position column = pdoc->GetColumn(caretPosition); + if (column <= indentation && pdoc->tabIndents) { const Sci::Position posSelect = pdoc->SetLineIndentation(lineCurrentPos, indentation - indentationStep); sel.Range(r) = SelectionRange(posSelect); } else { - Sci::Position newColumn = ((pdoc->GetColumn(caretPosition) - 1) / pdoc->tabInChars) * - pdoc->tabInChars; - if (newColumn < 0) - newColumn = 0; + const Sci::Position newColumn = std::max<Sci::Position>(0, + ((column - 1) / pdoc->tabInChars) * pdoc->tabInChars); Sci::Position newPos = caretPosition; while (pdoc->GetColumn(newPos) > newColumn) newPos--; @@ -5041,7 +5037,7 @@ void Editor::ButtonMoveWithModifiers(Point pt, unsigned int, KeyMod modifiers) { AllowVirtualSpace(virtualSpaceOptions, sel.IsRectangular())); movePos = MovePositionOutsideChar(movePos, sel.MainCaret() - movePos.Position()); - if (inDragDrop == DragDrop::initial) { + if (dragDropEnabled && inDragDrop == DragDrop::initial) { if (DragThreshold(ptMouseLast, pt)) { ChangeMouseCapture(false); SetDragPosition(movePos); @@ -5139,7 +5135,7 @@ void Editor::ButtonMoveWithModifiers(Point pt, unsigned int, KeyMod modifiers) { } } // Display regular (drag) cursor over selection - if (PointInSelection(pt) && !SelectionEmpty()) { + if (dragDropEnabled && PointInSelection(pt) && !SelectionEmpty()) { DisplayCursor(Window::Cursor::arrow); SetHoverIndicatorPosition(Sci::invalidPosition); } else { @@ -6178,11 +6174,11 @@ void Editor::SetSelectionNMessage(Message iMessage, uptr_t wParam, sptr_t lParam break; case Message::SetSelectionNStart: - sel.Range(wParam).anchor.SetPosition(lParam); + sel.Range(wParam).StartSet(SelectionPosition(lParam)); break; case Message::SetSelectionNEnd: - sel.Range(wParam).caret.SetPosition(lParam); + sel.Range(wParam).EndSet(SelectionPosition(lParam)); break; default: @@ -6988,7 +6984,7 @@ sptr_t Editor::WndProc(Message iMessage, uptr_t wParam, sptr_t lParam) { return static_cast<sptr_t>(vs.tabDrawMode); case Message::SetTabDrawMode: - vs.tabDrawMode = static_cast<TabDrawMode>(wParam); + SetAppearance(vs.tabDrawMode, static_cast<TabDrawMode>(wParam)); Redraw(); break; @@ -7084,6 +7080,13 @@ sptr_t Editor::WndProc(Message iMessage, uptr_t wParam, sptr_t lParam) { case Message::GetBufferedDraw: return view.bufferedDraw; + case Message::GetDragDropEnabled: + return dragDropEnabled; + + case Message::SetDragDropEnabled: + dragDropEnabled = wParam != 0; + break; + #ifdef INCLUDE_DEPRECATED_FEATURES case SCI_GETTWOPHASEDRAW: return view.phasesDraw == EditView::phasesTwo; diff --git a/src/Editor.h b/src/Editor.h index f8bc18970..95b83f1f7 100644 --- a/src/Editor.h +++ b/src/Editor.h @@ -238,6 +238,7 @@ protected: // ScintillaBase subclass needs access to much of Editor bool dwelling; enum class TextUnit { character, word, subLine, wholeLine } selectionUnit; Point ptMouseLast; + bool dragDropEnabled; enum class DragDrop { none, initial, dragging } inDragDrop; bool dropWentOutside; SelectionPosition posDrop; diff --git a/src/Geometry.h b/src/Geometry.h index d02a25bbb..a08c5c8f0 100644 --- a/src/Geometry.h +++ b/src/Geometry.h @@ -69,6 +69,9 @@ public: constexpr Interval Offset(XYPOSITION offset) const noexcept { return {left + offset, right + offset}; } + [[nodiscard]] static constexpr Interval FromLeftAndWidth(XYPOSITION left_, XYPOSITION width) { + return {left_, left_+width}; + } }; /** diff --git a/src/PositionCache.cxx b/src/PositionCache.cxx index 16bc10f5a..1f05ca752 100644 --- a/src/PositionCache.cxx +++ b/src/PositionCache.cxx @@ -318,6 +318,10 @@ int LineLayout::EndLineStyle() const noexcept { return styles[std::max(numCharsBeforeEOL - 1, 0)]; } +int LineLayout::LastStyle() const noexcept { + return styles[numCharsInLine]; +} + void LineLayout::WrapLine(const Document *pdoc, Sci::Position posLineStart, Wrap wrapState, XYPOSITION wrapWidth) { // Document wants document positions but simpler to work in line positions // so take care of adding and subtracting line start in a lambda. @@ -996,14 +1000,13 @@ public: }; class PositionCache : public IPositionCache { - static constexpr size_t defaultCacheSize = 0x400; - std::vector<PositionCacheEntry> pces{ defaultCacheSize }; + std::vector<PositionCacheEntry> pces{ positionCacheDefaultSize }; std::mutex mutex; uint16_t clock = 1; bool allClear = true; public: PositionCache(); - // Deleted so LineAnnotation objects can not be copied. + // Deleted so PositionCache objects can not be copied. PositionCache(const PositionCache &) = delete; PositionCache(PositionCache &&) = delete; void operator=(const PositionCache &) = delete; diff --git a/src/PositionCache.h b/src/PositionCache.h index b912c2fcc..9138d18e7 100644 --- a/src/PositionCache.h +++ b/src/PositionCache.h @@ -101,6 +101,7 @@ public: Interval Span(int start, int end) const noexcept; Interval SpanByte(int index) const noexcept; int EndLineStyle() const noexcept; + [[nodiscard]] int LastStyle() const noexcept; void WrapLine(const Document *pdoc, Sci::Position posLineStart, Wrap wrapState, XYPOSITION wrapWidth); }; @@ -256,6 +257,8 @@ public: bool More() const noexcept; }; +constexpr size_t positionCacheDefaultSize = 0x400; + class IPositionCache { public: virtual ~IPositionCache() = default; diff --git a/src/Selection.cxx b/src/Selection.cxx index 967033bff..e8d0148f5 100644 --- a/src/Selection.cxx +++ b/src/Selection.cxx @@ -182,6 +182,34 @@ SelectionSegment SelectionRange::Intersect(SelectionSegment check) const noexcep }; } +void SelectionRange::StartSet(SelectionPosition sp) noexcept { + if (anchor <= caret) { + anchor = sp; + if (caret < anchor) { + caret = anchor; + } + } else { + caret = sp; + if (anchor < caret) { + anchor = caret; + } + } +} + +void SelectionRange::EndSet(SelectionPosition sp) noexcept { + if (caret >= anchor) { + caret = sp; + if (anchor > caret) { + anchor = caret; + } + } else { + anchor = sp; + if (caret > anchor) { + caret = anchor; + } + } +} + void SelectionRange::Swap() noexcept { std::swap(caret, anchor); } diff --git a/src/Selection.h b/src/Selection.h index 1a57d2e78..0b3bb9159 100644 --- a/src/Selection.h +++ b/src/Selection.h @@ -156,6 +156,8 @@ struct SelectionRange { SelectionPosition End() const noexcept { return (anchor < caret) ? caret : anchor; } + void StartSet(SelectionPosition sp) noexcept; + void EndSet(SelectionPosition sp) noexcept; void Swap() noexcept; bool Trim(SelectionRange range) noexcept; void Truncate(Sci::Position length) noexcept; diff --git a/src/Style.cxx b/src/Style.cxx index 2c78c5241..a080f452e 100644 --- a/src/Style.cxx +++ b/src/Style.cxx @@ -62,7 +62,9 @@ int DefaultFontSize() noexcept { try { return Platform::DefaultFontSize(); } catch (...) { - return 10; + // Should never happen + constexpr int sensibleFontSize = 10; + return sensibleFontSize; } } @@ -83,5 +85,5 @@ Style::Style(const char *fontName_) noexcept : void Style::Copy(std::shared_ptr<Font> font_, const FontMeasurements &fm_) noexcept { font = std::move(font_); - (FontMeasurements &)(*this) = fm_; + static_cast<FontMeasurements &>(*this) = fm_; } diff --git a/src/ViewStyle.cxx b/src/ViewStyle.cxx index 92d084ee6..767e019f4 100644 --- a/src/ViewStyle.cxx +++ b/src/ViewStyle.cxx @@ -49,6 +49,8 @@ constexpr unsigned int mid = 0x80U; constexpr unsigned int half = 0x7fU; constexpr unsigned int quarter = 0x3fU; +constexpr int startExtendedStyles = 0x100; + } MarginStyle::MarginStyle(MarginType style_, int width_, int mask_) noexcept : @@ -61,9 +63,8 @@ bool MarginStyle::ShowsFolding() const noexcept { void FontRealised::Realise(Surface &surface, int zoomLevel, Technology technology, const FontSpecification &fs, const char *localeName) { PLATFORM_ASSERT(fs.fontName); - measurements.sizeZoomed = fs.size + zoomLevel * FontSizeMultiplier; - if (measurements.sizeZoomed <= FontSizeMultiplier) // May fail if sizeZoomed < 1 - measurements.sizeZoomed = FontSizeMultiplier; + // If negative zoomLevel, ensure sizeZoomed at least minimum positive size + measurements.sizeZoomed = std::max(fs.size + (zoomLevel * FontSizeMultiplier), FontSizeMultiplier); const float deviceHeight = static_cast<float>(surface.DeviceHeightFont(measurements.sizeZoomed)); const FontParameters fp(fs.fontName, deviceHeight / FontSizeMultiplier, fs.weight, @@ -73,10 +74,11 @@ void FontRealised::Realise(Surface &surface, int zoomLevel, Technology technolog // floor here is historical as platform layers have tweaked their values to match. // ceil would likely be better to ensure (nearly) all of the ink of a character is seen // but that would require platform layer changes. - measurements.ascent = std::floor(surface.Ascent(font.get())); + const XYPOSITION ascent = surface.Ascent(font.get()); + measurements.ascent = std::floor(ascent); measurements.descent = std::floor(surface.Descent(font.get())); - measurements.capitalHeight = surface.Ascent(font.get()) - surface.InternalLeading(font.get()); + measurements.capitalHeight = ascent - surface.InternalLeading(font.get()); measurements.aveCharWidth = surface.AverageCharWidth(font.get()); measurements.monospaceCharacterWidth = measurements.aveCharWidth; measurements.spaceWidth = surface.WidthText(font.get(), " "); @@ -92,9 +94,9 @@ void FontRealised::Realise(Surface &surface, int zoomLevel, Technology technolog const XYPOSITION maxWidth = *std::max_element(positions.begin(), positions.end()); const XYPOSITION minWidth = *std::min_element(positions.begin(), positions.end()); const XYPOSITION variance = maxWidth - minWidth; - const XYPOSITION scaledVariance = variance / measurements.aveCharWidth; constexpr XYPOSITION monospaceWidthEpsilon = 0.000001; // May need tweaking if monospace fonts vary more - measurements.monospaceASCII = scaledVariance < monospaceWidthEpsilon; + const XYPOSITION scaledVariance = monospaceWidthEpsilon * minWidth; + measurements.monospaceASCII = variance < scaledVariance; measurements.monospaceCharacterWidth = minWidth; } else { measurements.monospaceASCII = false; @@ -107,7 +109,7 @@ ViewStyle::ViewStyle(size_t stylesSize_) : indicators(static_cast<size_t>(IndicatorNumbers::Max) + 1), ms(MaxMargin + 1) { - nextExtendedStyle = 256; + nextExtendedStyle = startExtendedStyles; ResetDefaultStyle(); // There are no image markers by default, so no need for calling CalcLargestMarkerHeight() @@ -170,9 +172,11 @@ ViewStyle::ViewStyle(size_t stylesSize_) : lineOverlap = 0; maxAscent = 1; maxDescent = 1; - aveCharWidth = 8; - spaceWidth = 8; - tabWidth = spaceWidth * 8; + constexpr XYPOSITION defaultWidthChar = 8.F; // Reasonable initial approximation + aveCharWidth = defaultWidthChar; + spaceWidth = defaultWidthChar; + constexpr int defaultTabSpaces = 8; + tabWidth = spaceWidth * defaultTabSpaces; // Default is for no selection foregrounds // Shades of grey for selection backgrounds @@ -220,7 +224,8 @@ ViewStyle::ViewStyle(size_t stylesSize_) : leftMarginWidth = 1; rightMarginWidth = 1; ms[0] = MarginStyle(MarginType::Number); - ms[1] = MarginStyle(MarginType::Symbol, 16, ~MaskFolders); + constexpr int widthMarks = 16; + ms[1] = MarginStyle(MarginType::Symbol, widthMarks, ~MaskFolders); ms[2] = MarginStyle(MarginType::Symbol); marginInside = true; CalculateMarginWidthAndMask(); @@ -335,7 +340,7 @@ ViewStyle::~ViewStyle() = default; void ViewStyle::CalculateMarginWidthAndMask() noexcept { fixedColumnWidth = marginInside ? leftMarginWidth : 0; - maskInLine = 0xffffffff; + maskInLine = UINT32_MAX; int maskDefinedMarkers = 0; for (const MarginStyle &m : ms) { fixedColumnWidth += m.width; @@ -413,11 +418,9 @@ void ViewStyle::Refresh(Surface &surface, int tabInChars) { maxAscent = std::max(1.0, maxAscent + extraAscent); maxDescent = std::max(0.0, maxDescent + extraDescent); lineHeight = static_cast<int>(std::lround(maxAscent + maxDescent)); - lineOverlap = lineHeight / 10; - if (lineOverlap < 2) - lineOverlap = 2; - if (lineOverlap > lineHeight) - lineOverlap = lineHeight; + // lineHeight may rarely be less than 2, so can't use std::clamp + constexpr int overlapFraction = 10; // Allow up to a tenth of a line overlap + lineOverlap = std::min(std::max(lineHeight / overlapFraction, 2), lineHeight); someStylesProtected = std::any_of(styles.cbegin(), styles.cend(), [](const Style &style) noexcept { return style.IsProtected(); }); @@ -430,7 +433,7 @@ void ViewStyle::Refresh(Surface &surface, int tabInChars) { tabWidth = spaceWidth * tabInChars; controlCharWidth = 0.0; - if (controlCharSymbol >= 32) { + if (controlCharSymbol >= ' ') { const char cc[2] = { static_cast<char>(controlCharSymbol), '\0' }; controlCharWidth = surface.WidthText(styles[StyleControlChar].font.get(), cc); } @@ -440,7 +443,7 @@ void ViewStyle::Refresh(Surface &surface, int tabInChars) { } void ViewStyle::ReleaseAllExtendedStyles() noexcept { - nextExtendedStyle = 256; + nextExtendedStyle = startExtendedStyles; } int ViewStyle::AllocateExtendedStyles(int numberStyles) { @@ -571,9 +574,8 @@ ColourOptional ViewStyle::Background(int marksOfLine, bool caretActive, bool lin } if (background) { return background->Opaque(); - } else { - return {}; } + return {}; } bool ViewStyle::SelectionBackgroundDrawn() const noexcept { @@ -671,9 +673,8 @@ bool ViewStyle::SetElementColour(Element element, ColourRGBA colour) { bool ViewStyle::SetElementColourOptional(Element element, uptr_t wParam, sptr_t lParam) { if (wParam) { return SetElementColour(element, ColourRGBA::FromIpRGB(lParam)); - } else { - return ResetElement(element); } + return ResetElement(element); } void ViewStyle::SetElementRGB(Element element, int rgb) { @@ -801,9 +802,7 @@ void ViewStyle::FindMaxAscentDescent() noexcept { const auto &style = styles[i]; - if (maxAscent < style.ascent) - maxAscent = style.ascent; - if (maxDescent < style.descent) - maxDescent = style.descent; + maxAscent = std::max(style.ascent, maxAscent); + maxDescent = std::max(style.descent, maxDescent); } } diff --git a/test/simpleTests.py b/test/simpleTests.py index 020b72e99..0a14a4bdb 100644 --- a/test/simpleTests.py +++ b/test/simpleTests.py @@ -260,6 +260,13 @@ class TestSimple(unittest.TestCase): self.assertEqual(self.ed.CanUndo(), 0) self.ed.UndoCollection = 1 + def testDragDrop(self): + self.assertEqual(self.ed.DragDropEnabled, 1) + self.ed.DragDropEnabled = 0 + self.assertEqual(self.ed.DragDropEnabled, 0) + self.ed.DragDropEnabled = 1 + self.assertEqual(self.ed.DragDropEnabled, 1) + def testGetColumn(self): self.ed.AddText(1, b"x") self.assertEqual(self.ed.GetColumn(0), 0) @@ -1967,6 +1974,28 @@ class TestMultiSelection(unittest.TestCase): self.assertEqual(self.ed.GetSelectionNStart(0), 2) self.assertEqual(self.ed.GetSelectionNEnd(0), 3) + self.ed.SetSelectionNStart(0, 1) + self.assertEqual(self.ed.GetSelectionNAnchor(0), 1) + self.assertEqual(self.ed.GetSelectionNCaret(0), 3) + self.assertEqual(self.ed.GetSelectionNStart(0), 1) + self.assertEqual(self.ed.GetSelectionNEnd(0), 3) + + self.ed.SetSelectionNAnchor(0, 2) + self.ed.SetSelectionNCaret(0, 2) + self.ed.SetSelectionNStart(0, 9) + self.assertEqual(self.ed.GetSelectionNAnchor(0), 9) + self.assertEqual(self.ed.GetSelectionNCaret(0), 9) + self.assertEqual(self.ed.GetSelectionNStart(0), 9) + self.assertEqual(self.ed.GetSelectionNEnd(0), 9) + + self.ed.SetSelectionNAnchor(0, 2) + self.ed.SetSelectionNCaret(0, 3) + self.ed.SetSelectionNStart(0, 9) + self.assertEqual(self.ed.GetSelectionNAnchor(0), 9) + self.assertEqual(self.ed.GetSelectionNCaret(0), 9) + self.assertEqual(self.ed.GetSelectionNStart(0), 9) + self.assertEqual(self.ed.GetSelectionNEnd(0), 9) + def test2Selections(self): self.ed.SetSelection(1, 2) self.ed.AddSelection(4, 5) diff --git a/test/unit/testSelection.cxx b/test/unit/testSelection.cxx index ab0065624..76eebca21 100644 --- a/test/unit/testSelection.cxx +++ b/test/unit/testSelection.cxx @@ -198,6 +198,96 @@ TEST_CASE("SelectionRange") { REQUIRE(thin == single); } + SECTION("StartEndSet") { + { + SelectionRange range; + + range.StartSet(SelectionPosition(2)); + range.EndSet(SelectionPosition(3)); + REQUIRE(range.Start() == SelectionPosition(2)); + REQUIRE(range.End() == SelectionPosition(3)); + REQUIRE(range == SelectionRange(3, 2)); + + range.StartSet(SelectionPosition(1)); + REQUIRE(range.Start() == SelectionPosition(1)); + REQUIRE(range.End() == SelectionPosition(3)); + REQUIRE(range == SelectionRange(3, 1)); + } + + { + // Outside after + SelectionRange range(2, 1); + range.StartSet(SelectionPosition(3)); + REQUIRE(range.Start() == SelectionPosition(3)); + REQUIRE(range.End() == SelectionPosition(3)); + REQUIRE(range == SelectionRange(3, 3)); + } + + { + // Outside after + SelectionRange range(2, 1); + range.EndSet(SelectionPosition(3)); + REQUIRE(range.Start() == SelectionPosition(1)); + REQUIRE(range.End() == SelectionPosition(3)); + REQUIRE(range == SelectionRange(3, 1)); + } + + { + // Outside before + SelectionRange range(2, 1); + range.StartSet(SelectionPosition(0)); + REQUIRE(range.Start() == SelectionPosition(0)); + REQUIRE(range.End() == SelectionPosition(2)); + REQUIRE(range == SelectionRange(2, 0)); + } + + { + // Outside before + SelectionRange range(2, 1); + range.EndSet(SelectionPosition(0)); + REQUIRE(range.Start() == SelectionPosition(0)); + REQUIRE(range.End() == SelectionPosition(0)); + REQUIRE(range == SelectionRange(0, 0)); + } + + { + // Inside + SelectionRange range(3, 1); + range.EndSet(SelectionPosition(2)); + REQUIRE(range.Start() == SelectionPosition(1)); + REQUIRE(range.End() == SelectionPosition(2)); + REQUIRE(range == SelectionRange(2, 1)); + } + + { + // Inside + SelectionRange range(3, 1); + range.StartSet(SelectionPosition(2)); + REQUIRE(range.Start() == SelectionPosition(2)); + REQUIRE(range.End() == SelectionPosition(3)); + REQUIRE(range == SelectionRange(3, 2)); + } + + { + // Empty then outside + SelectionRange range(2); + range.StartSet(SelectionPosition(9)); + REQUIRE(range.Start() == SelectionPosition(9)); + REQUIRE(range.End() == SelectionPosition(9)); + REQUIRE(range == SelectionRange(9, 9)); + } + + { + // Empty then outside + SelectionRange range(2); + range.StartSet(SelectionPosition(0)); + REQUIRE(range.Start() == SelectionPosition(0)); + REQUIRE(range.End() == SelectionPosition(2)); + REQUIRE(range == SelectionRange(2, 0)); + } + + } + } TEST_CASE("Selection") { diff --git a/version.txt b/version.txt index 0ee983639..c7884c020 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -557 +560 diff --git a/win32/ListBox.cxx b/win32/ListBox.cxx index 0548ea5bc..b5ce7782f 100644 --- a/win32/ListBox.cxx +++ b/win32/ListBox.cxx @@ -195,8 +195,8 @@ class ListBoxX : public ListBox { LRESULT NcHitTest(WPARAM, LPARAM) const; void CentreItem(int n); void AllocateBitMap(); - LRESULT PASCAL ListProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam); - static LRESULT PASCAL ControlWndProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam); + LRESULT CALLBACK ListProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam); + static LRESULT CALLBACK ControlWndProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam); static constexpr POINT ItemInset {0, 0}; // Padding around whole item static constexpr POINT TextInset {2, 0}; // Padding around text @@ -237,7 +237,7 @@ public: void SetOptions(ListOptions options_) override; void Draw(DRAWITEMSTRUCT *pDrawItem); LRESULT WndProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam); - static LRESULT PASCAL StaticWndProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam); + static LRESULT CALLBACK StaticWndProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam); }; std::unique_ptr<ListBox> ListBox::Allocate() { @@ -787,7 +787,7 @@ void ListBoxX::AllocateBitMap() { graphics.pixmapLine->Init(graphics.bm.DC(), GetID()); } -LRESULT PASCAL ListBoxX::ListProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam) { +LRESULT CALLBACK ListBoxX::ListProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam) { try { switch (iMessage) { case WM_ERASEBKGND: @@ -831,7 +831,7 @@ LRESULT PASCAL ListBoxX::ListProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARA return ::DefWindowProc(hWnd, iMessage, wParam, lParam); } -LRESULT PASCAL ListBoxX::ControlWndProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam) { +LRESULT CALLBACK ListBoxX::ControlWndProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam) { if (ListBoxX *lbx = static_cast<ListBoxX *>(PointerFromWindow(::GetParent(hWnd)))) { return lbx->ListProc(hWnd, iMessage, wParam, lParam); } @@ -949,7 +949,7 @@ LRESULT ListBoxX::WndProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam return 0; } -LRESULT PASCAL ListBoxX::StaticWndProc( +LRESULT CALLBACK ListBoxX::StaticWndProc( HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam) { if (iMessage == WM_CREATE) { CREATESTRUCT *pCreate = static_cast<CREATESTRUCT *>(PtrFromLParam(lParam)); diff --git a/win32/PlatWin.cxx b/win32/PlatWin.cxx index ff47c28ab..01b41f5cc 100644 --- a/win32/PlatWin.cxx +++ b/win32/PlatWin.cxx @@ -628,13 +628,14 @@ HCURSOR LoadReverseArrowCursor(UINT dpi) noexcept { COLORREF strokeColour = RGB(0, 0, 1); status = ::RegOpenKeyExW(HKEY_CURRENT_USER, L"Software\\Microsoft\\Accessibility", 0, KEY_QUERY_VALUE, &hKey); if (status == ERROR_SUCCESS) { + constexpr DWORD customColour = 6; if (std::optional<DWORD> cursorType = RegGetDWORD(hKey, L"CursorType")) { switch (*cursorType) { case 1: // black case 4: // black std::swap(fillColour, strokeColour); break; - case 6: // custom + case customColour: // custom if (std::optional<DWORD> cursorColor = RegGetDWORD(hKey, L"CursorColor")) { fillColour = *cursorColor; } @@ -696,6 +697,15 @@ void Menu::Show(Point pt, const Window &w) { Destroy(); } +namespace { + +bool assertionPopUps = true; + +constexpr int defaultFontSize = 8; +constexpr size_t lengthDiagnostic = 2000; + +} + ColourRGBA ColourFromSys(int nIndex) noexcept { const DWORD colourValue = ::GetSysColor(nIndex); return ColourRGBA::FromRGB(colourValue); @@ -714,7 +724,7 @@ const char *Platform::DefaultFont() { } int Platform::DefaultFontSize() { - return 8; + return defaultFontSize; } unsigned int Platform::DoubleClickTime() { @@ -729,7 +739,7 @@ void Platform::DebugDisplay(const char *s) noexcept { #ifdef TRACE void Platform::DebugPrintf(const char *format, ...) noexcept { - char buffer[2000]; + char buffer[lengthDiagnostic]; va_list pArguments; va_start(pArguments, format); vsnprintf(buffer, std::size(buffer), format, pArguments); @@ -741,12 +751,6 @@ void Platform::DebugPrintf(const char *, ...) noexcept { } #endif -namespace { - -bool assertionPopUps = true; - -} - bool Platform::ShowAssertionPopUps(bool assertionPopUps_) noexcept { const bool ret = assertionPopUps; assertionPopUps = assertionPopUps_; @@ -754,7 +758,7 @@ bool Platform::ShowAssertionPopUps(bool assertionPopUps_) noexcept { } void Platform::Assert(const char *c, const char *file, int line) noexcept { - char buffer[2000] {}; + char buffer[lengthDiagnostic] {}; snprintf(buffer, std::size(buffer), "Assertion [%s] failed at %s %d%s", c, file, line, assertionPopUps ? "" : "\r\n"); if (assertionPopUps) { const int idButton = ::MessageBoxA({}, buffer, "Assertion failure", diff --git a/win32/PlatWin.h b/win32/PlatWin.h index 2ffc4a5fb..3db343587 100644 --- a/win32/PlatWin.h +++ b/win32/PlatWin.h @@ -81,6 +81,11 @@ struct Painter { explicit Painter(HWND hWnd_) noexcept : hWnd(hWnd_) { ::BeginPaint(hWnd, &ps); } + // Deleted so Painter objects can not be copied. + Painter(const Painter &) = delete; + Painter(Painter &&) = delete; + Painter &operator=(const Painter &) = delete; + Painter &operator=(Painter &&) = delete; ~Painter() { ::EndPaint(hWnd, &ps); } diff --git a/win32/ScintRes.rc b/win32/ScintRes.rc index 069195ea9..007b0ead9 100644 --- a/win32/ScintRes.rc +++ b/win32/ScintRes.rc @@ -4,8 +4,8 @@ #include <windows.h> -#define VERSION_SCINTILLA "5.5.7" -#define VERSION_WORDS 5, 5, 7, 0 +#define VERSION_SCINTILLA "5.6.0" +#define VERSION_WORDS 5, 6, 0, 0 VS_VERSION_INFO VERSIONINFO FILEVERSION VERSION_WORDS diff --git a/win32/ScintillaWin.cxx b/win32/ScintillaWin.cxx index fbec35f76..f59a5e28e 100644 --- a/win32/ScintillaWin.cxx +++ b/win32/ScintillaWin.cxx @@ -152,7 +152,7 @@ constexpr Point PointFromLParam(sptr_t lpoint) noexcept { } bool KeyboardIsKeyDown(int key) noexcept { - return (::GetKeyState(key) & 0x80000000) != 0; + return ::GetKeyState(key) < 0; } // Bit 24 is the extended keyboard flag and the numeric keypad is non-extended @@ -406,7 +406,8 @@ CLIPFORMAT RegisterClipboardType(LPCWSTR lpszFormat) noexcept { // Registered clipboard format values are 0xC000 through 0xFFFF. // RegisterClipboardFormatW returns 32-bit unsigned and CLIPFORMAT is 16-bit // unsigned so choose the low 16-bits with &. - return ::RegisterClipboardFormatW(lpszFormat) & 0xFFFF; + constexpr CLIPFORMAT LowBits = 0xFFFF; + return ::RegisterClipboardFormatW(lpszFormat) & LowBits; } RECT GetClientRect(HWND hwnd) noexcept { @@ -572,12 +573,12 @@ class ScintillaWin : sptr_t ptr, UINT iMessage, uptr_t wParam, sptr_t lParam); static sptr_t DirectStatusFunction( sptr_t ptr, UINT iMessage, uptr_t wParam, sptr_t lParam, int *pStatus); - static LRESULT PASCAL SWndProc( + static LRESULT CALLBACK SWndProc( HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam); void CTPaint(HWND hWnd); LRESULT CTProcessMessage(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam); - static LRESULT PASCAL CTWndProc( + static LRESULT CALLBACK CTWndProc( HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam); enum : UINT_PTR { invalidTimerID, standardTimerID, idleTimerID, fineTimerStart }; @@ -856,7 +857,9 @@ bool ScintillaWin::UpdateRenderingParams(bool force) noexcept { UINT clearTypeContrast = 0; if (SUCCEEDED(hr) && monitorRenderingParams && ::SystemParametersInfo(SPI_GETFONTSMOOTHINGCONTRAST, 0, &clearTypeContrast, 0) != 0) { - if (clearTypeContrast >= 1000 && clearTypeContrast <= 2200) { + constexpr UINT minContrast = 1000; + constexpr UINT maxContrast = 2200; + if (clearTypeContrast >= minContrast && clearTypeContrast <= maxContrast) { const FLOAT gamma = static_cast<FLOAT>(clearTypeContrast) / 1000.0f; pIDWriteFactory->CreateCustomRenderingParams(gamma, monitorRenderingParams->GetEnhancedContrast(), @@ -1112,7 +1115,8 @@ namespace { int InputCodePage() noexcept { HKL inputLocale = ::GetKeyboardLayout(0); const LANGID inputLang = LOWORD(inputLocale); - char sCodePage[10]; + constexpr size_t lengthCodePage = 10; + char sCodePage[lengthCodePage]; const int res = ::GetLocaleInfoA(MAKELCID(inputLang, SORT_DEFAULT), LOCALE_IDEFAULTANSICODEPAGE, sCodePage, sizeof(sCodePage)); if (!res) @@ -1750,7 +1754,7 @@ Window::Cursor ScintillaWin::ContextCursor(Point pt) { // Display regular (drag) cursor over selection if (PointInSelMargin(pt)) { return GetMarginCursor(pt); - } else if (!SelectionEmpty() && PointInSelection(pt)) { + } else if (dragDropEnabled && !SelectionEmpty() && PointInSelection(pt)) { return Window::Cursor::arrow; } else if (PointIsHotspot(pt)) { return Window::Cursor::hand; @@ -2813,10 +2817,10 @@ void CreateFoldMap(int codePage, FoldMap *foldingMap) { if (DBCSIsLeadByte(codePage, ch1)) { for (unsigned char byte2 = highByteLast; byte2 >= minTrailByte; byte2--) { const char ch2 = byte2; + const DBCSPair pair{ ch1, ch2 }; + const uint16_t index = DBCSIndex(ch1, ch2); + (*foldingMap)[index] = pair; if (DBCSIsTrailByte(codePage, ch2)) { - const DBCSPair pair{ ch1, ch2 }; - const uint16_t index = DBCSIndex(ch1, ch2); - (*foldingMap)[index] = pair; const std::string_view svBytes(pair.chars, 2); const int lenUni = WideCharLenFromMultiByte(codePage, svBytes); if (lenUni == 1) { @@ -2848,36 +2852,41 @@ void CreateFoldMap(int codePage, FoldMap *foldingMap) { class CaseFolderDBCS : public CaseFolderTable { // Allocate the expandable storage here so that it does not need to be reallocated // for each call to Fold. - const FoldMap *foldingMap; - UINT cp; + const FoldMap *foldingMap = nullptr; + UINT cp = 0; public: explicit CaseFolderDBCS(UINT cp_) : cp(cp_) { - if (!DBCSHasFoldMap(cp)) { - CreateFoldMap(cp, DBCSGetMutableFoldMap(cp)); - } foldingMap = DBCSGetFoldMap(cp); + if (!foldingMap) { + FoldMap *pfm = DBCSCreateFoldMap(cp); + CreateFoldMap(cp, pfm); + foldingMap = pfm; + } } size_t Fold(char *folded, size_t sizeFolded, const char *mixed, size_t lenMixed) override; }; -size_t CaseFolderDBCS::Fold(char *folded, size_t sizeFolded, const char *mixed, size_t lenMixed) { +size_t CaseFolderDBCS::Fold(char *folded, [[maybe_unused]] size_t sizeFolded, const char *mixed, size_t lenMixed) { // This loop outputs the same length as input as for each char 1-byte -> 1-byte; 2-byte -> 2-byte + assert(lenMixed <= sizeFolded); size_t lenOut = 0; for (size_t i = 0; i < lenMixed; i++) { - const ptrdiff_t lenLeft = lenMixed - i; const char ch = mixed[i]; - if ((lenLeft >= 2) && DBCSIsLeadByte(cp, ch) && ((lenOut + 2) <= sizeFolded)) { + if (((i+1) < lenMixed) && DBCSIsLeadByte(cp, ch)) { i++; const char ch2 = mixed[i]; const uint16_t ind = DBCSIndex(ch, ch2); const char *pair = foldingMap->at(ind).chars; + assert(pair[0]); + assert(pair[1]); folded[lenOut++] = pair[0]; folded[lenOut++] = pair[1]; - } else if ((lenOut + 1) <= sizeFolded) { + } else { const unsigned char uch = ch; folded[lenOut++] = mapping[uch]; } } + assert(lenOut == lenMixed); return lenOut; } @@ -3712,6 +3721,10 @@ STDMETHODIMP_(ULONG) ScintillaWin::Release() { /// Implement IDropTarget STDMETHODIMP ScintillaWin::DragEnter(LPDATAOBJECT pIDataSource, DWORD grfKeyState, POINTL, PDWORD pdwEffect) { + if (!dragDropEnabled) { + *pdwEffect = DROPEFFECT_NONE; + return S_OK; + } if (!pIDataSource ) return E_POINTER; FORMATETC fmtu = {CF_UNICODETEXT, nullptr, DVASPECT_CONTENT, -1, TYMED_HGLOBAL }; @@ -3728,7 +3741,7 @@ STDMETHODIMP ScintillaWin::DragEnter(LPDATAOBJECT pIDataSource, DWORD grfKeyStat STDMETHODIMP ScintillaWin::DragOver(DWORD grfKeyState, POINTL pt, PDWORD pdwEffect) { try { - if (!hasOKText || pdoc->IsReadOnly()) { + if (!dragDropEnabled || !hasOKText || pdoc->IsReadOnly()) { *pdwEffect = DROPEFFECT_NONE; return S_OK; } @@ -3762,6 +3775,11 @@ STDMETHODIMP ScintillaWin::Drop(LPDATAOBJECT pIDataSource, DWORD grfKeyState, try { *pdwEffect = EffectFromState(grfKeyState); + if (!dragDropEnabled) { + *pdwEffect = DROPEFFECT_NONE; + return S_OK; + } + if (!pIDataSource) return E_POINTER; @@ -4001,7 +4019,7 @@ LRESULT ScintillaWin::CTProcessMessage(HWND hWnd, UINT iMessage, WPARAM wParam, return ::DefWindowProc(hWnd, iMessage, wParam, lParam); } -LRESULT PASCAL ScintillaWin::CTWndProc( +LRESULT CALLBACK ScintillaWin::CTWndProc( HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam) { // Find C++ object associated with window. ScintillaWin *sciThis = static_cast<ScintillaWin *>(PointerFromWindow(hWnd)); @@ -4043,7 +4061,7 @@ sptr_t DirectFunction( } -LRESULT PASCAL ScintillaWin::SWndProc( +LRESULT CALLBACK ScintillaWin::SWndProc( HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam) { //Platform::DebugPrintf("S W:%x M:%x WP:%x L:%x\n", hWnd, iMessage, wParam, lParam); |
