diff options
39 files changed, 556 insertions, 175 deletions
@@ -219,3 +219,5 @@ ba0e2f317940a65f8f16325cbe4234fd75a6d47b rel-5-5-7 afc75d68d333f449ad52fce35ff40a5eddadf5c8 rel-5-5-8 76c8666d51407d156417e4b4a293a5e9ed29087f rel-5-5-9 a951931386960b1c5045652c9b93e79b669e9728 rel-5-6-0 +5ab4c75c3d5d252de41d3481fbcb8212e05f96d0 rel-5-6-1 +4d4244525b8a4327e8a04d5cc2cbb79bea0fc574 rel-5-6-2 diff --git a/cocoa/Scintilla/Info.plist b/cocoa/Scintilla/Info.plist index 195a18222..4e20666ec 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.6.0</string> + <string>5.6.2</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 eb28817b1..58df01a1e 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.6.0; + CURRENT_PROJECT_VERSION = 5.6.2; 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.6.0; + CURRENT_PROJECT_VERSION = 5.6.2; 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.6.0; + CURRENT_PROJECT_VERSION = 5.6.2; 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.6.0; + CURRENT_PROJECT_VERSION = 5.6.2; DEAD_CODE_STRIPPING = YES; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = ""; diff --git a/cocoa/ScintillaCocoa.mm b/cocoa/ScintillaCocoa.mm index 8a9af4058..cc4e01c34 100644 --- a/cocoa/ScintillaCocoa.mm +++ b/cocoa/ScintillaCocoa.mm @@ -963,6 +963,8 @@ sptr_t ScintillaCocoa::WndProc(Message iMessage, uptr_t wParam, sptr_t lParam) { } } catch (std::bad_alloc &) { errorStatus = Status::BadAlloc; + } catch (Failure &failure) { + errorStatus = failure.status; } catch (...) { errorStatus = Status::Failure; } diff --git a/cppcheck.suppress b/cppcheck.suppress index 812b65277..dd0af696b 100644 --- a/cppcheck.suppress +++ b/cppcheck.suppress @@ -1,6 +1,6 @@ // File to suppress cppcheck warnings for files that will not be fixed.
// Does not suppress warnings where an additional occurrence of the warning may be of interest.
-// Configured for cppcheck 2.15
+// Configured for cppcheck 2.20
// Just a report of how many checkers are run
checkersReport
@@ -50,11 +50,31 @@ knownConditionTrueFalse:scintilla/src/EditView.cxx // This makes no sense
CastIntegerToAddressAtReturn:scintilla\src\DBCS.cxx
+// Preprocessor #if with *_CHECK_VERSION(...) *_VERSION_CHECK(...)
+syntaxError:scintilla/gtk/Wrappers.h
+syntaxError:scintilla/gtk/PlatGTK.cxx
+syntaxError:scintilla/gtk/ScintillaGTKAccessible.cxx
+syntaxError:scintilla/qt/ScintillaEditBase/PlatQt.cpp
+syntaxError:scintilla/qt/ScintillaEditBase/ScintillaEditBase.cpp
+syntaxError:scintilla/qt/ScintillaEditBase/ScintillaQt.cpp
+
+noValidConfiguration:scintilla/gtk/PlatGTK.cxx
+noValidConfiguration:scintilla/gtk/ScintillaGTKAccessible.cxx
+
+unmatchedSuppression:scintilla/gtk/PlatGTK.cxx
+unmatchedSuppression:scintilla/gtk/ScintillaGTKAccessible.cxx
+unmatchedSuppression:scintilla/qt/ScintillaEditBase/PlatQt.cpp
+unmatchedSuppression:scintilla/qt/ScintillaEditBase/ScintillaEditBase.cpp
+unmatchedSuppression:scintilla/qt/ScintillaEditBase/ScintillaQt.cpp
+
// G_DEFINE_TYPE is too complex to pass to cppcheck
unknownMacro:scintilla/gtk/PlatGTK.cxx
// G_END_DECLS
unknownMacro:scintilla/gtk/scintilla-marshal.h
+// Marshaling hidden from cppcheck
+unusedFunction:scintilla/gtk/scintilla-marshal.c
+
// Difficult to test accessibility so don't change
constParameterPointer:scintilla/gtk/ScintillaGTKAccessible.cxx
constVariableReference:scintilla/gtk/ScintillaGTKAccessible.cxx
@@ -66,16 +86,48 @@ preprocessorErrorDirective:scintilla/qt/**.cpp // Doesn't understand Qt slots macro
unknownMacro:scintilla/qt/ScintillaEditBase/*.h
+// Provided for other projects
+unusedFunction:scintilla/qt/ScintillaEdit/ScintillaDocument.cpp
+
+// Provided for other projects
+unusedFunction:scintilla/call/ScintillaCall.cxx
+
+// Don't want to unnecessarily allow const pointers to be made mutable through reinterpret_cast
+constParameterPointer:scintilla/call/ScintillaCall.cxx
+constParameterPointer:scintilla/src/Editor.cxx
+constVariablePointer:scintilla/src/Editor.cxx
+
// The performance cost of by-value passing is often small and using a reference decreases
// code legibility.
passedByValue
+// For conciseness, the testing code is much sloppier than the library
+
// Suppress everything in catch.hpp as won't be changing
*:scintilla/test/unit/catch.hpp
-// Checks for moves move to variables that are not read but the moved from is checked
+// unread variables and overwritten without read in tests
unreadVariable:scintilla/test/unit/*.cxx
+
+// Checks for moves move to variables that are not read but the moved from is checked
accessMoved:scintilla/test/unit/*.cxx
+// 'obviously' true tests are still useful
+knownConditionTrueFalse:scintilla/test/unit/*.cxx
+
+// fields in TEST_CASE could be restricted to SECTION but not interesting
+variableScope:scintilla/test/unit/*.cxx
+
+// overwritten without read in tests
+redundantInitialization:scintilla/test/unit/*.cxx
+redundantAssignment:scintilla/test/unit/*.cxx
+unusedVariable:scintilla/test/unit/*.cxx
+
+// doesn't understand cppcheck TEST_CASE
+syntaxError:scintilla/test/unit/*.cxx
+
+// doesn't understand cppcheck generated functions
+unusedFunction:scintilla/test/unit/*.cxx
+
// cppcheck fails REQUIRE from Catch
comparisonOfFuncReturningBoolError:scintilla/test/unit/*.cxx
diff --git a/doc/ScintillaDoc.html b/doc/ScintillaDoc.html index d432043f6..c21bd2e29 100644 --- a/doc/ScintillaDoc.html +++ b/doc/ScintillaDoc.html @@ -959,6 +959,12 @@ struct Sci_TextRangeFull { </tr> <tr> + <th align="left"><code>SC_STATUS_OUTSIDE_DOCUMENT</code></th> + <td>3</td> + <td>An operation was attempted on a position that is outside the document</td> + </tr> + + <tr> <th align="left"><code>SC_STATUS_WARN_REGEX</code></th> <td>1001</td> <td>Regular expression is invalid</td> @@ -967,6 +973,10 @@ struct Sci_TextRangeFull { </tbody> </table> + <p>To more easily check the status of APIs, applications should call the direct status function using + <a class="message" href="#SCI_GETDIRECTSTATUSFUNCTION"><code>SCI_GETDIRECTSTATUSFUNCTION</code></a>. + </p> + <h2 id="Selection">Selection</h2> <p>Scintilla maintains a selection that stretches between two points, the anchor and the @@ -2890,6 +2900,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 59ef4b721..0af09cf9d 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/scintilla560.zip"> + <font size="4"> <a href="https://www.scintilla.org/scintilla562.zip"> Windows</a> - <a href="https://www.scintilla.org/scintilla560.tgz"> + <a href="https://www.scintilla.org/scintilla562.tgz"> GTK/Linux</a> </font> </td> @@ -42,7 +42,7 @@ containing very few restrictions. </p> <h3> - Release 5.6.0 + Release 5.6.2 </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/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> + <li><a href="https://www.scintilla.org/scintilla562.zip">zip format</a> (1.8M) commonly used on Windows</li> + <li><a href="https://www.scintilla.org/scintilla562.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 b91a7e282..bcbfa53a4 100644 --- a/doc/ScintillaHistory.html +++ b/doc/ScintillaHistory.html @@ -593,10 +593,54 @@ <td>Sven Ritter</td> <td>Stefan Löffler</td> <td>Nathaniel Braun</td> + </tr><tr> + <td>Stephan T. Lavavej</td> </tr> </table> <h2 id="Releases">Releases</h2> <h3> + <a href="https://www.scintilla.org/scintilla562.zip">Release 5.6.2</a> + </h3> + <ul> + <li> + Released 29 April 2026. + </li> + <li> + Add error status SC_STATUS_OUTSIDE_DOCUMENT that is set when operations are attempted on a position + outside the document. + Positions are checked earlier to prevent actions partly succeeding. + Incomplete actions could lead to features that are out of synchronization + like change history referring to text that has been deleted. + </li> + <li> + On Win32 with DirectWrite in a GDI scaled application, draw sharper text in autocompletion lists. + <a href="https://sourceforge.net/p/scintilla/bugs/2505/">Bug #2505</a>. + </li> + <li> + On Win32 update scaling when the application moves to another monitor. + <a href="https://sourceforge.net/p/scintilla/bugs/2503/">Bug #2503</a>. + </li> + </ul> + <h3> + <a href="https://www.scintilla.org/scintilla561.zip">Release 5.6.1</a> + </h3> + <ul> + <li> + Released 26 March 2026. + </li> + <li> + Add mode to draw tabs as [HT] blobs with SCI_SETTABDRAWMODE(SCTD_CONTROLCHAR). + </li> + <li> + Improve mouse drag behaviour when drag and drop disabled. + <a href="https://sourceforge.net/p/scintilla/feature-requests/1581/">Feature #1581</a>. + </li> + <li> + Fix regression in SCI_CONVERTEOLS that may not convert whole file. + <a href="https://sourceforge.net/p/scintilla/bugs/2501/">Bug #2501</a>. + </li> + </ul> + <h3> <a href="https://www.scintilla.org/scintilla560.zip">Release 5.6.0</a> </h3> <ul> diff --git a/doc/index.html b/doc/index.html index 69fe4a270..5a67a95a1 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="20260225" /> + <meta name="Date.Modified" content="20260429" /> <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.6.0<br /> - Site last modified February 25 2026</font> + <font color="#FFCC99" size="3"> Release version 5.6.2<br /> + Site last modified April 29 2026</font> </td> <td width="20%"> @@ -77,12 +77,11 @@ </tr> </table> <ul id="versionlist"> + <li>Version 5.6.2 adds error status SC_STATUS_OUTSIDE_DOCUMENT and stops out-of-bounds insertions earlier.</li> + <li>Version 5.6.1 adds mode to draw tabs as HT blobs and fixes a regression in SCI_CONVERTEOLS.</li> <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> </ul> <ul id="menu"> <li id="remote1"><a href="https://www.scintilla.org/SciTEImage.html">Screenshot</a></li> diff --git a/gtk/ScintillaGTK.cxx b/gtk/ScintillaGTK.cxx index 686a8c13f..b8a7c354a 100755 --- a/gtk/ScintillaGTK.cxx +++ b/gtk/ScintillaGTK.cxx @@ -947,6 +947,8 @@ sptr_t ScintillaGTK::WndProc(Message iMessage, uptr_t wParam, sptr_t lParam) { } } catch (std::bad_alloc &) { errorStatus = Status::BadAlloc; + } catch (Failure &failure) { + errorStatus = failure.status; } catch (...) { errorStatus = Status::Failure; } diff --git a/include/Scintilla.h b/include/Scintilla.h index b6786097e..7f12703d3 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 @@ -862,6 +863,7 @@ typedef sptr_t (*SciFnDirectStatus)(sptr_t ptr, unsigned int iMessage, uptr_t wP #define SC_STATUS_OK 0 #define SC_STATUS_FAILURE 1 #define SC_STATUS_BADALLOC 2 +#define SC_STATUS_OUTSIDE_DOCUMENT 3 #define SC_STATUS_WARN_START 1000 #define SC_STATUS_WARN_REGEX 1001 #define SCI_SETSTATUS 2382 diff --git a/include/Scintilla.iface b/include/Scintilla.iface index 6262f7c1b..ddb025c50 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. @@ -2289,6 +2291,7 @@ enu Status=SC_STATUS_ val SC_STATUS_OK=0 val SC_STATUS_FAILURE=1 val SC_STATUS_BADALLOC=2 +val SC_STATUS_OUTSIDE_DOCUMENT=3 val SC_STATUS_WARN_START=1000 val SC_STATUS_WARN_REGEX=1001 diff --git a/include/ScintillaTypes.h b/include/ScintillaTypes.h index 0991a1480..cc5f03815 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 { @@ -431,6 +432,7 @@ enum class Status { Ok = 0, Failure = 1, BadAlloc = 2, + OutsideDocument = 3, WarnStart = 1000, RegEx = 1001, }; diff --git a/qt/ScintillaEdit/ScintillaEdit.pro b/qt/ScintillaEdit/ScintillaEdit.pro index 295678d3c..c636e0659 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.6.0 +VERSION = 5.6.2 SOURCES += \ ScintillaEdit.cpp \ diff --git a/qt/ScintillaEditBase/PlatQt.cpp b/qt/ScintillaEditBase/PlatQt.cpp index 38aec01a0..96e7f2865 100644 --- a/qt/ScintillaEditBase/PlatQt.cpp +++ b/qt/ScintillaEditBase/PlatQt.cpp @@ -1378,8 +1378,13 @@ void Platform::Assert(const char *c, const char *file, int line) noexcept char buffer[2000]; snprintf(buffer, std::size(buffer), "Assertion [%s] failed at %s %d", c, file, line); if (Platform::ShowAssertionPopUps(false)) { +#if QT_VERSION >= QT_VERSION_CHECK(6, 2, 0) + QMessageBox mb(QMessageBox::Icon::NoIcon, "Assertion Failure", buffer, + QMessageBox::StandardButton::Ok); +#else QMessageBox mb("Assertion Failure", buffer, QMessageBox::NoIcon, QMessageBox::Ok, QMessageBox::NoButton, QMessageBox::NoButton); +#endif mb.exec(); } else { strcat(buffer, "\n"); diff --git a/qt/ScintillaEditBase/PlatQt.h b/qt/ScintillaEditBase/PlatQt.h index 08cda9dc4..a4d924014 100644 --- a/qt/ScintillaEditBase/PlatQt.h +++ b/qt/ScintillaEditBase/PlatQt.h @@ -61,13 +61,14 @@ inline Point PointFromQPoint(QPoint qp) return Point(qp.x(), qp.y()); } -inline QPointF QPointFFromPoint(Point qp) +inline Point PointFromQPointF(QPointF qp) { - return QPointF(qp.x, qp.y); + return Point(qp.x(), qp.y()); } -constexpr PRectangle RectangleInset(PRectangle rc, XYPOSITION delta) noexcept { - return PRectangle(rc.left + delta, rc.top + delta, rc.right - delta, rc.bottom - delta); +inline QPointF QPointFFromPoint(Point qp) +{ + return QPointF(qp.x, qp.y); } class SurfaceImpl : public Surface { diff --git a/qt/ScintillaEditBase/ScintillaEditBase.cpp b/qt/ScintillaEditBase/ScintillaEditBase.cpp index 187da9dbe..477dd44cf 100644 --- a/qt/ScintillaEditBase/ScintillaEditBase.cpp +++ b/qt/ScintillaEditBase/ScintillaEditBase.cpp @@ -304,6 +304,16 @@ unsigned int TimeOfEvent(const QElapsedTimer &timer) return static_cast<unsigned int>(timer.elapsed() % maxTime); } +Point PointOfEvent(const QDropEvent *event) +{ +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + PLATFORM_ASSERT(false); + return PointFromQPointF(event->position()); +#else + return PointFromQPoint(event->pos()); +#endif +} + } void ScintillaEditBase::mousePressEvent(QMouseEvent *event) @@ -396,7 +406,7 @@ void ScintillaEditBase::dragEnterEvent(QDragEnterEvent *event) } else if (event->mimeData()->hasText()) { event->acceptProposedAction(); - const Point point = PointFromQPoint(event->pos()); + const Point point = PointOfEvent(event); sqt->DragEnter(point); } else { event->ignore(); @@ -415,7 +425,7 @@ void ScintillaEditBase::dragMoveEvent(QDragMoveEvent *event) } else if (event->mimeData()->hasText()) { event->acceptProposedAction(); - const Point point = PointFromQPoint(event->pos()); + const Point point = PointOfEvent(event); sqt->DragMove(point); } else { event->ignore(); @@ -430,7 +440,7 @@ void ScintillaEditBase::dropEvent(QDropEvent *event) } else if (event->mimeData()->hasText()) { event->acceptProposedAction(); - const Point point = PointFromQPoint(event->pos()); + const Point point = PointOfEvent(event); const bool move = (event->source() == this && event->proposedAction() == Qt::MoveAction); sqt->Drop(point, event->mimeData(), move); diff --git a/qt/ScintillaEditBase/ScintillaEditBase.pro b/qt/ScintillaEditBase/ScintillaEditBase.pro index d2ab750df..6e95cca86 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.6.0 +VERSION = 5.6.2 SOURCES += \ PlatQt.cpp \ diff --git a/qt/ScintillaEditBase/ScintillaQt.cpp b/qt/ScintillaEditBase/ScintillaQt.cpp index d5e6eb7b0..5e9d262a4 100644 --- a/qt/ScintillaEditBase/ScintillaQt.cpp +++ b/qt/ScintillaEditBase/ScintillaQt.cpp @@ -768,6 +768,8 @@ sptr_t ScintillaQt::WndProc(Message iMessage, uptr_t wParam, sptr_t lParam) } } catch (std::bad_alloc &) { errorStatus = Status::BadAlloc; + } catch (Failure &failure) { + errorStatus = failure.status; } catch (...) { errorStatus = Status::Failure; } diff --git a/scripts/CheckMentioned.py b/scripts/CheckMentioned.py index 1e5f6c393..b6d52293e 100644 --- a/scripts/CheckMentioned.py +++ b/scripts/CheckMentioned.py @@ -35,13 +35,13 @@ def depunctuate(s): symbols = {} with open(incFileName, "rt") as incFile: - for line in incFile.readlines(): + for line in incFile: if line.startswith("#define"): identifier = line.split()[1] symbols[identifier] = 0 with open(docFileName, "rt") as docFile: - for line in docFile.readlines(): + for line in docFile: for word in depunctuate(line).split(): if word in symbols.keys(): symbols[word] = 1 diff --git a/scripts/Dependencies.py b/scripts/Dependencies.py index 135c57ac8..51e332c56 100644 --- a/scripts/Dependencies.py +++ b/scripts/Dependencies.py @@ -17,9 +17,10 @@ # Only tested with ASCII file names. # Copyright 2019 by Neil Hodgson <neilh@scintilla.org> # The License.txt file describes the conditions under which this software may be distributed. -# Requires Python 2.7 or later +# Requires Python 3.6 or later import codecs, glob, os, sys +from functools import cache if __name__ == "__main__": import FileGenerator @@ -28,6 +29,7 @@ else: continuationLineEnd = " \\" +@cache def FindPathToHeader(header, includePath): for incDir in includePath: relPath = os.path.join(incDir, header) @@ -35,27 +37,25 @@ def FindPathToHeader(header, includePath): return relPath return "" -fhifCache = {} # Remember the includes in each file. ~5x speed up. +@cache def FindHeadersInFile(filePath): - if filePath not in fhifCache: - headers = [] - with codecs.open(filePath, "r", "utf-8") as f: - for line in f: - if line.strip().startswith("#include"): - parts = line.split() - if len(parts) > 1: - header = parts[1] - if header[0] != '<': # No system headers - headers.append(header.strip('"')) - fhifCache[filePath] = headers - return fhifCache[filePath] + headers = [] + with codecs.open(filePath, "r", "utf-8") as f: + for line in f: + if line.strip().startswith("#include"): + parts = line.split() + if len(parts) > 1: + header = parts[1] + if header[0] != '<': # No system headers + headers.append(header.strip('"')) + return headers def FindHeadersInFileRecursive(filePath, includePath, renames): headerPaths = [] for header in FindHeadersInFile(filePath): if header in renames: header = renames[header] - relPath = FindPathToHeader(header, includePath) + relPath = FindPathToHeader(header, tuple(includePath)) if relPath and relPath not in headerPaths: headerPaths.append(relPath) subHeaders = FindHeadersInFileRecursive(relPath, includePath, renames) diff --git a/scripts/Face.py b/scripts/Face.py index 9814115f0..0278f5ac2 100644 --- a/scripts/Face.py +++ b/scripts/Face.py @@ -2,7 +2,7 @@ # Face.py - module for reading and parsing Scintilla.iface file # Implemented 2000 by Neil Hodgson neilh@scintilla.org # Released to the public domain. -# Requires Python 2.7 or later +# Requires Python 3.6 or later def sanitiseLine(line): line = line.rstrip('\n') @@ -72,7 +72,7 @@ class Face: currentComment = [] currentCommentFinished = 0 file = open(name) - for line in file.readlines(): + for line in file: line = sanitiseLine(line) if line: if line[0] == "#": diff --git a/scripts/FileGenerator.py b/scripts/FileGenerator.py index f798e1d6b..f596e9cbe 100644 --- a/scripts/FileGenerator.py +++ b/scripts/FileGenerator.py @@ -4,7 +4,7 @@ # Generate or regenerate source files based on comments in those files. # May be modified in-place or a template may be generated into a complete file. -# Requires Python 2.7 or later +# Requires Python 3.6 or later # The files are copied to a string apart from sections between a # ++Autogenerated comment and a --Autogenerated comment which is # generated by the CopyWithInsertion function. After the whole string is @@ -143,7 +143,7 @@ def UpdateLineInPlistFile(path, key, value): lines = [] keyCurrent = "" with codecs.open(path, "rb", "utf-8") as f: - for line in f.readlines(): + for line in f: ls = line.strip() if ls.startswith("<key>"): keyCurrent = ls.replace("<key>", "").replace("</key>", "") @@ -160,7 +160,7 @@ def UpdateLineInFile(path, linePrefix, lineReplace): lines = [] updated = False with codecs.open(path, "r", "utf-8") as f: - for line in f.readlines(): + for line in f: line = line.rstrip() if not updated and line.startswith(linePrefix): lines.append(lineReplace) diff --git a/scripts/GenerateCharacterCategory.py b/scripts/GenerateCharacterCategory.py index 806dea2fe..1080cceed 100644 --- a/scripts/GenerateCharacterCategory.py +++ b/scripts/GenerateCharacterCategory.py @@ -2,7 +2,7 @@ # Script to generate scintilla/src/CharacterCategoryMap.cxx and lexilla/lexlib/CharacterCategory.cxx # from Python's Unicode data # Should be run rarely when a Python with a new version of Unicode data is available. -# Requires Python 3.3 or later +# Requires Python 3.6 or later # Should not be run with old versions of Python. import pathlib, platform, sys, unicodedata @@ -11,7 +11,7 @@ from FileGenerator import Regenerate def findCategories(filename): with filename.open(encoding="UTF-8") as infile: - lines = [x.strip() for x in infile.readlines() if "\tcc" in x] + lines = [x.strip() for x in infile if "\tcc" in x] values = "".join(lines).replace(" ","").split(",") print("Categrories:", values) return [v[2:] for v in values] diff --git a/scripts/ScintillaData.py b/scripts/ScintillaData.py index 55534306b..d57898e5e 100644 --- a/scripts/ScintillaData.py +++ b/scripts/ScintillaData.py @@ -25,19 +25,20 @@ import datetime, pathlib, sys def FindCredits(historyFile, removeLinks=True): + """ Return a list of contributors in a history file. """ credits = [] stage = 0 with historyFile.open(encoding="utf-8") as f: - for line in f.readlines(): - line = line.strip() - if stage == 0 and line == "<table>": + for line in f: + s = line.strip() + if stage == 0 and s == "<table>": stage = 1 - elif stage == 1 and line == "</table>": + elif stage == 1 and s == "</table>": stage = 2 - if stage == 1 and line.startswith("<td>"): - credit = line[4:-5] - if removeLinks and "<a" in line: - title, _a, rest = credit.partition("<a href=") + if stage == 1 and s.startswith("<td>"): + credit = s[4:-5] + if removeLinks and "<a" in s: + title, _, rest = credit.partition("<a href=") urlplus, _bracket, end = rest.partition(">") name = end.split("<")[0] url = urlplus[1:-1] @@ -57,14 +58,14 @@ class ScintillaData: self.versionCommad = self.versionDotted.replace(".", ", ") + ', 0' with (scintillaRoot / "doc" / "index.html").open() as f: - self.dateModified = [d for d in f.readlines() if "Date.Modified" in d]\ + self.dateModified = [d for d in f if "Date.Modified" in d]\ [0].split('\"')[3] # 20130602 # index.html, SciTE.html dtModified = datetime.datetime.strptime(self.dateModified, "%Y%m%d") self.yearModified = self.dateModified[0:4] monthModified = dtModified.strftime("%B") - dayModified = "%d" % dtModified.day + dayModified = f"{dtModified.day}" self.mdyModified = monthModified + " " + dayModified + " " + self.yearModified # May 22 2013 # index.html, SciTE.html diff --git a/src/CellBuffer.cxx b/src/CellBuffer.cxx index 3e9deb934..1eac3f9c2 100644 --- a/src/CellBuffer.cxx +++ b/src/CellBuffer.cxx @@ -439,35 +439,90 @@ const char *CellBuffer::InsertString(Sci::Position position, const char *s, Sci: return data; } -bool CellBuffer::SetStyleAt(Sci::Position position, char styleValue) noexcept { - if (!hasStyles) { - return false; - } - const char curVal = style.ValueAt(position); - if (curVal != styleValue) { - style.SetValueAt(position, styleValue); - return true; - } else { - return false; +namespace { + +ChangedRange CopyBytes(char *p, const char *styles, Sci::Position length, Sci::Position offset) noexcept { + for (Sci::Position start = 0; start < length; start++) { + if (p[start] != styles[start]) { + for (Sci::Position end = length - 1; end >= 0; end--) { + if (p[end] != styles[end]) { + memcpy(p+start, styles+start, end-start+1); + return { start + offset, end + offset }; + } + } + } } + return {}; } -bool CellBuffer::SetStyleFor(Sci::Position position, Sci::Position lengthStyle, char styleValue) noexcept { - if (!hasStyles) { - return false; - } - bool changed = false; - PLATFORM_ASSERT(lengthStyle == 0 || - (lengthStyle > 0 && lengthStyle + position <= style.Length())); - while (lengthStyle--) { - const char curVal = style.ValueAt(position); - if (curVal != styleValue) { - style.SetValueAt(position, styleValue); - changed = true; +ChangedRange SetBytes(char *p, char style, Sci::Position length, Sci::Position offset) noexcept { + for (Sci::Position start = 0; start < length; start++) { + if (p[start] != style) { + for (Sci::Position end = length - 1; end >= 0; end--) { + if (p[end] != style) { + memset(p+start, style, end-start+1); + return { start + offset, end + offset }; + } + } } - position++; } - return changed; + return {}; +} + +struct Lengths { + Sci::Position length1; + Sci::Position length2; +}; + +Lengths SplitUpdate(const SplitVector<char> &style, Sci::Position position, Sci::Position length) noexcept { + length = std::min(length, style.Length() - position); + + // Divide length into two parts if it overlaps the gap + const Sci::Position part1Length = style.GapPosition(); + + // If all after gap then place in length1 to avoid second call + if (position >= part1Length) { + return { length, 0 }; + } + // If all before gap then place in length1 + const Sci::Position beforeGap = part1Length - position; + if (length <= beforeGap) { + return { length, 0 }; + } + // Some before gap and some after gap so put portion in length1 and rest in length2 + return { beforeGap, length - beforeGap }; +} + +} + +ChangedRange CellBuffer::SetStyles(Sci::Position position, const char *styles, Sci::Position length) noexcept { + if (!hasStyles || (position < 0) || (length <= 0)) { + return {}; + } + + const Lengths lengths = SplitUpdate(style, position, length); + ChangedRange cr = CopyBytes(&style[position], styles, lengths.length1, position); + if (lengths.length2) { + const ChangedRange cr2 = CopyBytes(&style[position + lengths.length1], styles + lengths.length1, + lengths.length2, position + lengths.length1); + cr.Merge(cr2); + } + return cr; +} + +ChangedRange CellBuffer::SetStyleFor(Sci::Position position, Sci::Position length, char value) noexcept { + if (!hasStyles || (position < 0) || (length <= 0)) { + return {}; + } + + const Lengths lengths = SplitUpdate(style, position, length); + ChangedRange cr = SetBytes(&style[position], value, lengths.length1, position); + if (lengths.length2) { + const ChangedRange cr2 = SetBytes(&style[position + lengths.length1], value, + lengths.length2, position + lengths.length1); + cr.Merge(cr2); + } + return cr; } // The char* returned is to an allocation owned by the undo history diff --git a/src/CellBuffer.h b/src/CellBuffer.h index 1b035597a..11479f14a 100644 --- a/src/CellBuffer.h +++ b/src/CellBuffer.h @@ -10,6 +10,11 @@ namespace Scintilla::Internal { +struct Failure : public std::runtime_error { + Status status; + Failure(Status status_) : std::runtime_error("failure with status"), status(status_) {} +}; + // Interface to per-line data that wants to see each line insertion and deletion class PerLine { public: @@ -66,6 +71,24 @@ struct SplitView { } }; +struct ChangedRange { + Sci::Position start = Sci::invalidPosition; + Sci::Position end = Sci::invalidPosition; + ChangedRange() noexcept = default; + ChangedRange(Sci::Position start_, Sci::Position end_) noexcept : start(start_), end(end_) {} + [[nodiscard]] bool Empty() const noexcept { + return start < 0; + } + void Merge(const ChangedRange &cr2) noexcept { + if (cr2.start >= 0) { + if (start < 0) { + *this = cr2; + } else { + end = cr2.end; + } + } + } +}; /** * Holder for an expandable array of characters that supports undo and line markers. @@ -141,9 +164,9 @@ public: const char *InsertString(Sci::Position position, const char *s, Sci::Position insertLength, bool &startSequence); /// Setting styles for positions outside the range of the buffer is safe and has no effect. - /// @return true if the style of a character is changed. - bool SetStyleAt(Sci::Position position, char styleValue) noexcept; - bool SetStyleFor(Sci::Position position, Sci::Position lengthStyle, char styleValue) noexcept; + /// @return range where style of characters changed. + ChangedRange SetStyles(Sci::Position position, const char *styles, Sci::Position length) noexcept; + ChangedRange SetStyleFor(Sci::Position position, Sci::Position length, char value) noexcept; const char *DeleteChars(Sci::Position position, Sci::Position deleteLength, bool &startSequence); diff --git a/src/CharacterType.h b/src/CharacterType.h index 437fb8c5c..a7732cec9 100644 --- a/src/CharacterType.h +++ b/src/CharacterType.h @@ -12,12 +12,29 @@ namespace Scintilla::Internal { // Functions for classifying characters +template <typename T, typename... Args> +constexpr bool AnyOf(T t, Args... args) noexcept { +#if defined(__clang__) + static_assert(__is_integral(T) || __is_enum(T)); +#endif + return ((t == args) || ...); +} + +// prevent pointer without <type_traits> +template <typename T, typename... Args> +constexpr void AnyOf([[maybe_unused]] T *t, [[maybe_unused]] Args... args) noexcept {} +template <typename T, typename... Args> +constexpr void AnyOf([[maybe_unused]] const T *t, [[maybe_unused]] Args... args) noexcept {} + /** * Check if a character is a space. * This is ASCII specific but is safe with chars >= 0x80. */ +constexpr int charTab = 0x09; +constexpr int charCarriageReturn = 0x0D; + constexpr bool IsASpace(int ch) noexcept { - return (ch == ' ') || ((ch >= 0x09) && (ch <= 0x0d)); + return (ch == ' ') || ((ch >= charTab) && (ch <= charCarriageReturn)); } constexpr bool IsSpaceOrTab(int ch) noexcept { @@ -44,17 +61,18 @@ constexpr bool IsADigit(int ch) noexcept { } constexpr bool IsADigit(int ch, int base) noexcept { - if (base <= 10) { + constexpr int digits = 10; + if (base <= digits) { return (ch >= '0') && (ch < '0' + base); - } else { - return ((ch >= '0') && (ch <= '9')) || - ((ch >= 'A') && (ch < 'A' + base - 10)) || - ((ch >= 'a') && (ch < 'a' + base - 10)); } + return ((ch >= '0') && (ch <= '9')) || + ((ch >= 'A') && (ch < 'A' + base - digits)) || + ((ch >= 'a') && (ch < 'a' + base - digits)); } constexpr bool IsASCII(int ch) noexcept { - return (ch >= 0) && (ch < 0x80); + constexpr int lastASCII = 0x7F; + return (ch >= 0) && (ch <= lastASCII); } constexpr bool IsLowerCase(int ch) noexcept { @@ -122,16 +140,14 @@ template <typename T> constexpr T MakeUpperCase(T ch) noexcept { if (ch < 'a' || ch > 'z') return ch; - else - return ch - 'a' + 'A'; + return ch - 'a' + 'A'; } template <typename T> constexpr T MakeLowerCase(T ch) noexcept { if (ch < 'A' || ch > 'Z') return ch; - else - return ch - 'A' + 'a'; + return ch - 'A' + 'a'; } int CompareCaseInsensitive(const char *a, const char *b) noexcept; diff --git a/src/Document.cxx b/src/Document.cxx index 458c1e93f..289fb6731 100644 --- a/src/Document.cxx +++ b/src/Document.cxx @@ -530,6 +530,13 @@ void SCI_METHOD Document::SetErrorStatus(int status) { } } +void Document::CheckPosition(Sci::Position pos) const { + PLATFORM_ASSERT((pos >= 0) && (pos <= LengthNoExcept())); + if ((pos < 0) || (pos > LengthNoExcept())) { + throw Failure(Status::OutsideDocument); + } +} + Sci_Position SCI_METHOD Document::LineFromPosition(Sci_Position pos) const { return cb.LineFromPosition(pos); } @@ -1485,6 +1492,7 @@ Sci::Position Document::InsertString(Sci::Position position, const char *s, Sci: if (insertLength <= 0) { return 0; } + CheckPosition(position); CheckReadOnly(); // Application may change read only state here if (cb.IsReadOnly()) { return 0; @@ -1907,8 +1915,7 @@ std::string Document::TransformLineEnds(const char *s, size_t len, EndOfLine eol void Document::ConvertLineEnds(EndOfLine eolModeSet) { UndoGroup ug(this); - const Sci::Position length = Length(); - for (Sci::Position pos = 0; pos < length; pos++) { + for (Sci::Position pos = 0; pos < LengthNoExcept(); pos++) { const char ch = cb.CharAt(pos); if (ch == '\r') { if (cb.CharAt(pos + 1) == '\n') { @@ -2572,10 +2579,10 @@ bool SCI_METHOD Document::SetStyleFor(Sci_Position length, char style) { return false; } enteredStyling++; - const Sci::Position prevEndStyled = endStyled; - if (cb.SetStyleFor(endStyled, length, style)) { + const ChangedRange cr = cb.SetStyleFor(endStyled, length, style); + if (!cr.Empty()) { const DocModification mh(ModificationFlags::ChangeStyle | ModificationFlags::User, - prevEndStyled, length); + cr.start, cr.end - cr.start + 1); NotifyModified(mh); } endStyled += length; @@ -2588,22 +2595,11 @@ bool SCI_METHOD Document::SetStyles(Sci_Position length, const char *styles) { return false; } enteredStyling++; - bool didChange = false; - Sci::Position startMod = 0; - Sci::Position endMod = 0; - for (int iPos = 0; iPos < length; iPos++, endStyled++) { - PLATFORM_ASSERT(endStyled < Length()); - if (cb.SetStyleAt(endStyled, styles[iPos])) { - if (!didChange) { - startMod = endStyled; - } - didChange = true; - endMod = endStyled; - } - } - if (didChange) { + const ChangedRange cr = cb.SetStyles(endStyled, styles, length); + endStyled += length; + if (!cr.Empty()) { const DocModification mh(ModificationFlags::ChangeStyle | ModificationFlags::User, - startMod, endMod - startMod + 1); + cr.start, cr.end - cr.start + 1); NotifyModified(mh); } enteredStyling--; @@ -3022,7 +3018,7 @@ Sci::Position Document::BraceMatch(Sci::Position position, Sci::Position /*maxRe return -1; const int styBrace = StyleIndexAt(position); int direction = -1; - if (chBrace == '(' || chBrace == '[' || chBrace == '{' || chBrace == '<') + if (AnyOf(chBrace, '(', '[', '{', '<')) direction = 1; int depth = 1; position = useStartPos ? startPos : position + direction; @@ -3035,7 +3031,7 @@ Sci::Position Document::BraceMatch(Sci::Position position, Sci::Position /*maxRe while ((position >= 0) && (position < LengthNoExcept())) { const unsigned char chAtPos = CharAt(position); - if (chAtPos == chBrace || chAtPos == chSeek) { + if (AnyOf(chAtPos, chBrace, chSeek)) { if (((position > GetEndStyled()) || (StyleIndexAt(position) == styBrace)) && (chAtPos <= maxSafeChar || position == MovePositionOutsideChar(position, direction, false))) { depth += (chAtPos == chBrace) ? 1 : -1; @@ -3141,7 +3137,9 @@ public: const Document *doc; Sci::Position position; - explicit ByteIterator(const Document *doc_=nullptr, Sci::Position position_=0) noexcept : + ByteIterator() noexcept : + ByteIterator(nullptr) {} + explicit ByteIterator(const Document *doc_, Sci::Position position_=0) noexcept : doc(doc_), position(position_) { } char operator*() const noexcept { @@ -3206,7 +3204,9 @@ public: using pointer = wchar_t*; using reference = wchar_t&; - explicit UTF8Iterator(const Document *doc_=nullptr, Sci::Position position_=0) noexcept : + UTF8Iterator() noexcept : + UTF8Iterator(nullptr) {} + explicit UTF8Iterator(const Document *doc_, Sci::Position position_=0) noexcept : doc(doc_), position(position_) { if (doc) { ReadCharacter(); @@ -3295,7 +3295,9 @@ public: using pointer = wchar_t*; using reference = wchar_t&; - explicit UTF8Iterator(const Document *doc_=nullptr, Sci::Position position_=0) noexcept : + UTF8Iterator() noexcept : + UTF8Iterator(nullptr) {} + explicit UTF8Iterator(const Document *doc_, Sci::Position position_=0) noexcept : doc(doc_), position(position_) { } wchar_t operator*() const noexcept { diff --git a/src/Document.h b/src/Document.h index 7fd150f37..dfb0a7ff5 100644 --- a/src/Document.h +++ b/src/Document.h @@ -369,6 +369,7 @@ public: int SCI_METHOD DEVersion() const noexcept override; void SCI_METHOD SetErrorStatus(int status) override; + void CheckPosition(Sci::Position pos) const; Sci_Position SCI_METHOD LineFromPosition(Sci_Position pos) const override; Sci::Line SciLineFromPosition(Sci::Position pos) const noexcept; // Avoids casting LineFromPosition diff --git a/src/EditView.cxx b/src/EditView.cxx index 2608f11e5..b25053845 100644 --- a/src/EditView.cxx +++ b/src/EditView.cxx @@ -352,7 +352,7 @@ 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); @@ -522,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); @@ -610,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) { @@ -1286,6 +1286,7 @@ 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. + surface->FillRectangleAligned(rcSegment, Fill(textBack)); FillLineRemainder(surface, model, vsDraw, ll, line, rcLine, rcSegment.right, subLine); } @@ -1346,7 +1347,7 @@ void EditView::DrawEOLAnnotationText(Surface *surface, const EditModel &model, c namespace { constexpr bool AnnotationBoxedOrIndented(AnnotationVisible annotationVisible) noexcept { - return annotationVisible == AnnotationVisible::Boxed || annotationVisible == AnnotationVisible::Indented; + return AnyOf(annotationVisible, AnnotationVisible::Boxed, AnnotationVisible::Indented); } } @@ -1673,7 +1674,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(); @@ -1682,21 +1683,27 @@ void DrawBackground(Surface *surface, const EditModel &model, const ViewStyle &v // Blob display inIndentation = false; } - surface->FillRectangleAligned(rcSegment, Fill(textBack)); - } else { + } + surface->FillRectangleAligned(rcSegment, Fill(textBack)); + if (!ts.representation) { // Normal text display - surface->FillRectangleAligned(rcSegment, Fill(textBack)); if (vsDraw.viewWhitespace != WhiteSpace::Invisible) { - for (int cpos = 0; cpos <= i - ts.start; cpos++) { - if (ll->chars[cpos + ts.start] == ' ') { + for (int cpos = 0; cpos <= i - ts.start; ) { + int countSpaces = 0; + while ((countSpaces <= i - ts.start - cpos) && (ll->chars[cpos + ts.start + countSpaces] == ' ')) { + countSpaces++; + } + if (countSpaces) { if (drawWhitespaceBackground && vsDraw.WhiteSpaceVisible(inIndentation)) { const PRectangle rcSpace = Intersection(rcLine, - ll->SpanByte(cpos + ts.start).Offset(horizontalOffset)); + ll->Span(cpos + ts.start, cpos + ts.start + countSpaces).Offset(horizontalOffset)); surface->FillRectangleAligned(rcSpace, vsDraw.ElementColourForced(Element::WhiteSpaceBack).Opaque()); } + cpos += countSpaces; } else { inIndentation = false; + cpos++; } } } @@ -2178,7 +2185,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)) @@ -2310,7 +2317,7 @@ void EditView::DrawForeground(Surface *surface, const EditModel &model, const Vi void EditView::DrawIndentGuidesOverEmpty(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll, Sci::Line line, int xStart, PRectangle rcLine, int subLine, Sci::Line lineVisible) { - if ((vsDraw.viewIndentationGuides == IndentView::LookForward || vsDraw.viewIndentationGuides == IndentView::LookBoth) + if (AnyOf(vsDraw.viewIndentationGuides, IndentView::LookForward, IndentView::LookBoth) && (subLine == 0)) { const Sci::Position posLineStart = model.pdoc->LineStart(line); int indentSpace = model.pdoc->GetLineIndentation(line); @@ -2525,14 +2532,14 @@ void EditView::PaintText(Surface *surfaceWindow, const EditModel &model, const V for (;;) { int yposScreen = screenLinePaintFirst * vsDraw.lineHeight; int ypos = bufferedDraw ? 0 : yposScreen; - Sci::Line visibleLine = model.TopLineOfMain() + screenLinePaintFirst; - while (visibleLine < model.pcs->LinesDisplayed() && yposScreen < rcArea.bottom) { + Sci::Line lineVisible = model.TopLineOfMain() + screenLinePaintFirst; + while (lineVisible < model.pcs->LinesDisplayed() && yposScreen < rcArea.bottom) { - const Sci::Line lineDoc = model.pcs->DocFromDisplay(visibleLine); + const Sci::Line lineDoc = model.pcs->DocFromDisplay(lineVisible); // Only visible lines should be handled by the code within the loop PLATFORM_ASSERT(model.pcs->GetVisible(lineDoc)); const Sci::Line lineStartSet = model.pcs->DisplayFromDoc(lineDoc); - const int subLine = static_cast<int>(visibleLine - lineStartSet); + const int subLine = static_cast<int>(lineVisible - lineStartSet); // Copy this line and its styles from the document into local arrays // and determine the x position at which each character starts. @@ -2574,7 +2581,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, xOrigin, rcLine, subLine, phase); + DrawLine(surface, model, vsDraw, ll.get(), lineDoc, lineVisible, xOrigin, rcLine, subLine, phase); #if defined(TIME_PAINTING) durPaint += ep.Duration(true); #endif @@ -2609,7 +2616,7 @@ void EditView::PaintText(Surface *surfaceWindow, const EditModel &model, const V } yposScreen += vsDraw.lineHeight; - visibleLine++; + lineVisible++; } if (phase >= DrawPhase::carets) { @@ -2704,7 +2711,7 @@ Sci::Position EditView::FormatRange(bool draw, CharacterRangeFull chrg, Rectangl } else if (colourMode == PrintOption::BlackOnWhite) { it->fore = black; it->back = white; - } else if (colourMode == PrintOption::ColourOnWhite || colourMode == PrintOption::ColourOnWhiteDefaultBG) { + } else if (AnyOf(colourMode, PrintOption::ColourOnWhite, PrintOption::ColourOnWhiteDefaultBG)) { it->back = white; } } @@ -2756,7 +2763,7 @@ Sci::Position EditView::FormatRange(bool draw, CharacterRangeFull chrg, Rectangl Sci::Line lineDoc = linePrintStart; Sci::Position nPrintPos = chrg.cpMin; - int visibleLine = 0; + int lineVisible = 0; int widthPrint = rc.right - rc.left - vsPrint.fixedColumnWidth; if (printParameters.wrapState == Wrap::None) widthPrint = LineLayout::wrapWidthInfinite; @@ -2785,23 +2792,23 @@ Sci::Position EditView::FormatRange(bool draw, CharacterRangeFull chrg, Rectangl // When document line is wrapped over multiple display lines, find where // to start printing from to ensure a particular position is on the first // line of the page. - if (visibleLine == 0) { + if (lineVisible == 0) { const Sci::Position startWithinLine = nPrintPos - model.pdoc->LineStart(lineDoc); for (int iwl = 0; iwl < ll.lines - 1; iwl++) { if (ll.LineStart(iwl) <= startWithinLine && ll.LineStart(iwl + 1) >= startWithinLine) { - visibleLine = -iwl; + lineVisible = -iwl; } } if (ll.lines > 1 && startWithinLine >= ll.LineStart(ll.lines - 1)) { - visibleLine = -(ll.lines - 1); + lineVisible = -(ll.lines - 1); } } if (draw && lineNumberWidth && (ypos + vsPrint.lineHeight <= rc.bottom) && - (visibleLine >= 0)) { + (lineVisible >= 0)) { const std::string number = std::to_string(lineDoc + 1) + lineNumberPrintSpace; PRectangle rcNumber = rcLine; rcNumber.right = rcNumber.left + lineNumberWidth; @@ -2820,15 +2827,15 @@ Sci::Position EditView::FormatRange(bool draw, CharacterRangeFull chrg, Rectangl for (int iwl = 0; iwl < ll.lines; iwl++) { if (ypos + vsPrint.lineHeight <= rc.bottom) { - if (visibleLine >= 0) { + if (lineVisible >= 0) { if (draw) { rcLine.top = static_cast<XYPOSITION>(ypos); rcLine.bottom = static_cast<XYPOSITION>(ypos + vsPrint.lineHeight); - DrawLine(surface, model, vsPrint, &ll, lineDoc, visibleLine, xOrigin, rcLine, iwl, DrawPhase::all); + DrawLine(surface, model, vsPrint, &ll, lineDoc, lineVisible, xOrigin, rcLine, iwl, DrawPhase::all); } ypos += vsPrint.lineHeight; } - visibleLine++; + lineVisible++; if (iwl == ll.lines - 1) nPrintPos = model.pdoc->LineStart(lineDoc + 1); else diff --git a/src/Editor.cxx b/src/Editor.cxx index 5b42407e3..1dfa8fd6b 100644 --- a/src/Editor.cxx +++ b/src/Editor.cxx @@ -3818,7 +3818,7 @@ int Editor::DelWordOrLine(Message iMessage) { // Rightwards and leftwards deletions differ in treatment of virtual space. // Clear virtual space for leftwards, realise for rightwards. - const bool leftwards = (iMessage == Message::DelWordLeft) || (iMessage == Message::DelLineLeft); + const bool leftwards = AnyOf(iMessage, Message::DelWordLeft, Message::DelLineLeft); if (!additionalSelectionTyping) { InvalidateWholeSelection(); @@ -4029,14 +4029,14 @@ int Editor::KeyCommand(Message iMessage) { break; case Message::DeleteBack: DelCharBack(true); - if ((caretSticky == CaretSticky::Off) || (caretSticky == CaretSticky::WhiteSpace)) { + if (AnyOf(caretSticky, CaretSticky::Off, CaretSticky::WhiteSpace)) { SetLastXChosen(); } EnsureCaretVisible(); break; case Message::DeleteBackNotLine: DelCharBack(false); - if ((caretSticky == CaretSticky::Off) || (caretSticky == CaretSticky::WhiteSpace)) { + if (AnyOf(caretSticky, CaretSticky::Off, CaretSticky::WhiteSpace)) { SetLastXChosen(); } EnsureCaretVisible(); @@ -4053,7 +4053,7 @@ int Editor::KeyCommand(Message iMessage) { case Message::BackTab: case Message::LineDedent: Indent(false, iMessage == Message::LineDedent); - if ((caretSticky == CaretSticky::Off) || (caretSticky == CaretSticky::WhiteSpace)) { + if (AnyOf(caretSticky, CaretSticky::Off, CaretSticky::WhiteSpace)) { SetLastXChosen(); } EnsureCaretVisible(); @@ -4859,7 +4859,7 @@ void Editor::ButtonDownWithModifiers(Point pt, unsigned int curTime, KeyMod modi wordSelectAnchorEndPos = endWord; wordSelectInitialCaretPos = sel.MainCaret(); WordSelection(wordSelectInitialCaretPos); - } else if (selectionUnit == TextUnit::subLine || selectionUnit == TextUnit::wholeLine) { + } else if (AnyOf(selectionUnit, TextUnit::subLine, TextUnit::wholeLine)) { lineAnchorPos = newPos.Position(); LineSelection(lineAnchorPos, lineAnchorPos, selectionUnit == TextUnit::wholeLine); //Platform::DebugPrintf("Triple click: %d - %d\n", anchor, currentPos); @@ -4919,7 +4919,7 @@ void Editor::ButtonDownWithModifiers(Point pt, unsigned int curTime, KeyMod modi // Switch to just the click position SetSelection(newPos, newPos); } - if (!sel.Range(selectionPart).Empty()) { + if (dragDropEnabled && !sel.Range(selectionPart).Empty()) { inDragDrop = DragDrop::initial; } } @@ -5395,7 +5395,7 @@ Sci::Position Editor::PositionAfterMaxStyling(Sci::Position posMax, bool scrolli } void Editor::StartIdleStyling(bool truncatedLastStyling) { - if ((idleStyling == IdleStyling::All) || (idleStyling == IdleStyling::AfterVisible)) { + if (AnyOf(idleStyling, IdleStyling::All, IdleStyling::AfterVisible)) { if (pdoc->GetEndStyled() < pdoc->Length()) { // Style remainder of document in idle time needIdleStyling = true; @@ -5899,6 +5899,8 @@ Sci::Position Editor::GetTag(char *tagValue, int tagNumber) { } Sci::Position Editor::ReplaceTarget(ReplaceType replaceType, std::string_view text) { + pdoc->CheckPosition(targetRange.start.Position()); + UndoGroup ug(pdoc); std::string substituted; // Copy in case of re-entrance @@ -6344,7 +6346,7 @@ sptr_t Editor::WndProc(Message iMessage, uptr_t wParam, sptr_t lParam) { case Message::Paste: Paste(); - if ((caretSticky == CaretSticky::Off) || (caretSticky == CaretSticky::WhiteSpace)) { + if (AnyOf(caretSticky, CaretSticky::Off, CaretSticky::WhiteSpace)) { SetLastXChosen(); } EnsureCaretVisible(); @@ -6697,6 +6699,7 @@ sptr_t Editor::WndProc(Message iMessage, uptr_t wParam, sptr_t lParam) { Sci::Position insertPos = PositionFromUPtr(wParam); if (insertPos == -1) insertPos = CurrentPosition(); + pdoc->CheckPosition(insertPos); Sci::Position newCurrent = CurrentPosition(); const char *sz = ConstCharPtrFromSPtr(lParam); const Sci::Position lengthInserted = pdoc->InsertString(insertPos, sz, strlen(sz)); @@ -6984,7 +6987,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; diff --git a/src/Editor.h b/src/Editor.h index 95b83f1f7..0243d5b63 100644 --- a/src/Editor.h +++ b/src/Editor.h @@ -16,7 +16,7 @@ class Timer { public: bool ticking; int ticksToWait; - enum {tickSize = 100}; + static constexpr int tickSize = 100; TickerID tickerID; Timer() noexcept; @@ -226,7 +226,7 @@ protected: // ScintillaBase subclass needs access to much of Editor Timer timer; Timer autoScrollTimer; - enum { autoScrollDelay = 200 }; + static constexpr int autoScrollDelay = 200; Idler idler; diff --git a/test/simpleTests.py b/test/simpleTests.py index 0a14a4bdb..4205c7b3a 100644 --- a/test/simpleTests.py +++ b/test/simpleTests.py @@ -371,6 +371,12 @@ class TestSimple(unittest.TestCase): self.assertEqual(self.ed.Contents(), b"x" + lineEnds[lineEndType] + b"y") self.assertEqual(self.ed.LineLength(0), 1 + len(lineEnds[lineEndType])) + def testLineEndConversionLengthening(self): + # Bug #2501 + self.ed.AddText(4, b"x\ny\n") + self.ed.ConvertEOLs(self.ed.SC_EOL_CRLF) + self.assertEqual(self.ed.Contents(), b"x\r\ny\r\n") + # Several tests for unicode line ends U+2028 and U+2029 @unittest.skipUnless(unicodeLineEndsAvailable, "can not test Unicode line ends") diff --git a/test/unit/testDocument.cxx b/test/unit/testDocument.cxx index fb8448da4..9811a215c 100644 --- a/test/unit/testDocument.cxx +++ b/test/unit/testDocument.cxx @@ -157,8 +157,30 @@ struct DocPlus { document.GetCharRange(contents.data(), 0, length); return contents; } + + [[nodiscard]] std::string Styles() const { + const Sci::Position length = document.Length(); + std::string contents(length, 0); + document.GetStyleRange(reinterpret_cast<unsigned char *>(contents.data()), 0, length); + return contents; + } +}; + +namespace { + +// Provide an empty watcher to avoid filling in all the virtuals in simple watchers. +struct EmptyWatcher : public DocWatcher { + void NotifyModifyAttempt(Document *, void *) override {}; + void NotifySavePoint(Document *, void *, bool) override {}; + void NotifyModified(Document *, DocModification, void *) override {}; + void NotifyDeleted(Document *, void *) noexcept override {}; + void NotifyStyleNeeded(Document *, void *, Sci::Position) override {}; + void NotifyErrorOccurred(Document *, void *, Scintilla::Status) override {}; + void NotifyGroupCompleted(Document *, void *) noexcept override {}; }; +} + void TimeTrace(std::string_view sv, const Catch::Timer &tikka) { std::cout << sv << std::setw(5) << tikka.getElapsedMilliseconds() << " milliseconds" << std::endl; } @@ -185,6 +207,81 @@ TEST_CASE("Document") { REQUIRE(!doc.document.CanRedo()); } + SECTION("Style") { + // Check that styling calls SetStyles and SetStyleFor modify the correct bytes and + // produce a minimal span in the notification. + // Also checks the notification mechanism. + struct ModificationWatcher : public EmptyWatcher { + Sci::Position modPos = -1; + Sci::Position modLength = -1; + void NotifyModified(Document *, DocModification mh, void *) noexcept final { + modPos = mh.position; + modLength = mh.length; + } + void Clear() noexcept { + modPos = -1; + modLength = -1; + } + }; + DocPlus doc(sText, 0); + // Length of sText is 9 + REQUIRE(doc.Styles() == std::string(9, 0)); + ModificationWatcher mw; + doc.document.AddWatcher(&mw, nullptr); + doc.document.StartStyling(1); + doc.document.SetStyles(5, "\0abc\0"); + REQUIRE(doc.Styles() == std::string("\0\0abc\0\0\0\0", 9)); + // The NULs are not changed so are not included in the modification range + REQUIRE(mw.modPos == 2); + REQUIRE(mw.modLength == 3); + + // No change so no notification so no update + mw.Clear(); + doc.document.StartStyling(1); + doc.document.SetStyles(5, "\0abc\0"); + REQUIRE(mw.modPos == -1); + REQUIRE(mw.modLength == -1); + + // Just change one byte + doc.document.StartStyling(1); + doc.document.SetStyles(5, "\0bbc\0"); + REQUIRE(mw.modPos == 2); + REQUIRE(mw.modLength == 1); + REQUIRE(doc.Styles() == std::string("\0\0bbc\0\0\0\0", 9)); + + // Move the gap + doc.MoveGap(1); + doc.document.StartStyling(1); + doc.document.SetStyles(5, "\0cbc\0"); + REQUIRE(mw.modPos == 2); + REQUIRE(mw.modLength == 1); + REQUIRE(doc.Styles() == std::string("\0\0cbc\0\0\0\0", 9)); + + doc.MoveGap(2); + doc.document.StartStyling(1); + doc.document.SetStyles(5, "\0dcc\0"); + REQUIRE(mw.modPos == 2); + REQUIRE(mw.modLength == 2); + REQUIRE(doc.Styles() == std::string("\0\0dcc\0\0\0\0", 9)); + + // Test SetStyleFor + + doc.MoveGap(3); + doc.document.StartStyling(3); + doc.document.SetStyleFor(1, 0); // After gap + REQUIRE(mw.modPos == 3); + REQUIRE(mw.modLength == 1); + REQUIRE(doc.Styles() == std::string("\0\0d\0c\0\0\0\0", 9)); + + doc.document.StartStyling(2); + doc.document.SetStyleFor(3, 0); // Over gap + REQUIRE(mw.modPos == 2); + REQUIRE(mw.modLength == 3); + REQUIRE(doc.Styles() == std::string("\0\0\0\0\0\0\0\0\0", 9)); + + doc.document.RemoveWatcher(&mw, nullptr); + } + // Search ranges are from first argument to just before second argument // Arguments are expected to be at character boundaries and will be tweaked if // part way through a character. diff --git a/version.txt b/version.txt index c7884c020..3bfc51a6e 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -560 +562 diff --git a/win32/ListBox.cxx b/win32/ListBox.cxx index b5ce7782f..31470b2ed 100644 --- a/win32/ListBox.cxx +++ b/win32/ListBox.cxx @@ -175,6 +175,10 @@ class ListBoxX : public ListBox { MouseWheelDelta wheelDelta; ListOptions options; DWORD frameStyle = WS_THICKFRAME; + float deviceScaleFactor = 1.f; + [[nodiscard]] int GetFirstIntegralMultipleDeviceScaleFactor() const noexcept { + return static_cast<int>(std::ceil(deviceScaleFactor)); + } LBGraphics graphics; @@ -265,6 +269,9 @@ void ListBoxX::Create(Window &parent_, int ctrlID_, Point location_, int lineHei this); dpi = DpiForWindow(hwndParent); + if (technology != Technology::Default) { + deviceScaleFactor = Internal::GetDeviceScaleFactorWhenGdiScalingActive(hwndParent); + } POINT locationw = POINTFromPoint(location); ::MapWindowPoints(hwndParent, {}, &locationw, 1); location = PointFromPOINT(locationw); @@ -423,7 +430,8 @@ void ListBoxX::Draw(DRAWITEMSTRUCT *pDrawItem) { #endif const PRectangle rcItemBase = PRectangleFromRECT(pDrawItem->rcItem); - const PRectangle rcItem(0, 0, rcItemBase.Width(), rcItemBase.Height()); + const int integralDeviceScaleFactor = GetFirstIntegralMultipleDeviceScaleFactor(); + const PRectangle rcItem(0, 0, rcItemBase.Width() * integralDeviceScaleFactor, rcItemBase.Height() * integralDeviceScaleFactor); PRectangle rcBox = rcItem; rcBox.left += TextOffset(); ColourRGBA colourFore; @@ -458,6 +466,7 @@ void ListBoxX::Draw(DRAWITEMSTRUCT *pDrawItem) { PRectangle rcImage = rcItem; rcImage.left = left; rcImage.right = rcImage.left + images.GetWidth(); + rcImage.bottom = rcImage.top + rcItemBase.Height(); graphics.pixmapLine->DrawRGBAImage(rcImage, pimage->GetWidth(), pimage->GetHeight(), pimage->Pixels()); } @@ -473,7 +482,11 @@ void ListBoxX::Draw(DRAWITEMSTRUCT *pDrawItem) { // Blit from hMemDC to hDC const SIZE extent = SizeOfRect(pDrawItem->rcItem); - ::BitBlt(pDrawItem->hDC, pDrawItem->rcItem.left, pDrawItem->rcItem.top, extent.cx, extent.cy, graphics.bm.DC(), 0, 0, SRCCOPY); + if (integralDeviceScaleFactor == 1) { + ::BitBlt(pDrawItem->hDC, pDrawItem->rcItem.left, pDrawItem->rcItem.top, extent.cx, extent.cy, graphics.bm.DC(), 0, 0, SRCCOPY); + } else { + ::StretchBlt(pDrawItem->hDC, pDrawItem->rcItem.left, pDrawItem->rcItem.top, extent.cx, extent.cy, graphics.bm.DC(), 0, 0, extent.cx * integralDeviceScaleFactor, extent.cy * integralDeviceScaleFactor, SRCCOPY); + } } void ListBoxX::AppendListItem(const char *text, const char *numword) { @@ -748,7 +761,8 @@ void ListBoxX::CentreItem(int n) { } void ListBoxX::AllocateBitMap() { - const SIZE extent { GetClientExtent().x, ItemHeight() }; + const int integralDeviceScaleFactor = GetFirstIntegralMultipleDeviceScaleFactor(); + const SIZE extent { GetClientExtent().x * integralDeviceScaleFactor, ItemHeight() * integralDeviceScaleFactor }; graphics.bm.Create({}, extent.cx, -extent.cy, nullptr); if (!graphics.bm) { @@ -765,9 +779,12 @@ void ListBoxX::AllocateBitMap() { return; } + const FLOAT dpiTarget = dpiDefault * static_cast<float>(integralDeviceScaleFactor); + const D2D1_RENDER_TARGET_PROPERTIES drtp = D2D1::RenderTargetProperties( D2D1_RENDER_TARGET_TYPE_DEFAULT, - { DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_PREMULTIPLIED }); + { DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_PREMULTIPLIED }, + dpiTarget, dpiTarget); HRESULT hr = CreateDCRenderTarget(&drtp, graphics.pBMDCTarget); if (FAILED(hr) || !graphics.pBMDCTarget) { diff --git a/win32/ScintRes.rc b/win32/ScintRes.rc index 007b0ead9..46526cec8 100644 --- a/win32/ScintRes.rc +++ b/win32/ScintRes.rc @@ -4,8 +4,8 @@ #include <windows.h> -#define VERSION_SCINTILLA "5.6.0" -#define VERSION_WORDS 5, 6, 0, 0 +#define VERSION_SCINTILLA "5.6.2" +#define VERSION_WORDS 5, 6, 2, 0 VS_VERSION_INFO VERSIONINFO FILEVERSION VERSION_WORDS diff --git a/win32/ScintillaWin.cxx b/win32/ScintillaWin.cxx index f59a5e28e..856c66ca9 100644 --- a/win32/ScintillaWin.cxx +++ b/win32/ScintillaWin.cxx @@ -333,9 +333,9 @@ public: void SetCompositionFont(const ViewStyle &vs, int style, UINT dpi) const { LOGFONTW lf{}; - int sizeZoomed = vs.styles[style].size + (vs.zoomLevel * FontSizeMultiplier); - if (sizeZoomed <= 2 * FontSizeMultiplier) // Hangs if sizeZoomed <= 1 - sizeZoomed = 2 * FontSizeMultiplier; + // Hangs if font height <= 1, so force minimum value + const int sizeZoomed = std::max(vs.styles[style].size + (vs.zoomLevel * FontSizeMultiplier), + 2 * FontSizeMultiplier); // The negative is to allow for leading lf.lfHeight = -::MulDiv(sizeZoomed, dpi, pointsPerInch * FontSizeMultiplier); lf.lfWeight = static_cast<LONG>(vs.styles[style].weight); @@ -872,7 +872,11 @@ bool ScintillaWin::UpdateRenderingParams(bool force) noexcept { } hCurrentMonitor = monitor; - deviceScaleFactor = Internal::GetDeviceScaleFactorWhenGdiScalingActive(hRootWnd); + const float newDeviceScaleFactor = Internal::GetDeviceScaleFactorWhenGdiScalingActive(hRootWnd); + if (deviceScaleFactor != newDeviceScaleFactor) { + deviceScaleFactor = newDeviceScaleFactor; + targets.valid = false; + } renderingParams->defaultRenderingParams = std::move(monitorRenderingParams); renderingParams->customRenderingParams = std::move(customClearTypeRenderingParams); return true; @@ -2434,17 +2438,18 @@ sptr_t ScintillaWin::WndProc(Message iMessage, uptr_t wParam, sptr_t lParam) { case WM_MOUSEACTIVATE: case WM_NCHITTEST: case WM_NCCALCSIZE: - case WM_NCPAINT: case WM_NCMOUSEMOVE: case WM_NCLBUTTONDOWN: case WM_SYSCOMMAND: case WM_WINDOWPOSCHANGING: return ::DefWindowProc(MainHWND(), msg, wParam, lParam); + case WM_NCPAINT: case WM_WINDOWPOSCHANGED: #if defined(USE_D2D) if (technology != Technology::Default) { if (UpdateRenderingParams(false)) { + reverseArrowCursor.Invalidate(); DropGraphics(); Redraw(); } @@ -2495,6 +2500,8 @@ sptr_t ScintillaWin::WndProc(Message iMessage, uptr_t wParam, sptr_t lParam) { } } catch (std::bad_alloc &) { errorStatus = Status::BadAlloc; + } catch (Failure &failure) { + errorStatus = failure.status; } catch (...) { errorStatus = Status::Failure; } |
