Update to scintilla 5.5.6 & Lexilla 5.4.4

Release 5.5.6 (https://www.scintilla.org/scintilla556.zip)

    Released 2 April 2025.
*   Disallow changing case of protected text. Bug #2463.
*   Return enumeration type from MarkerSymbolDefined to match MarkerDefine. Bug #2469.
*   On Win32, use DirectWrite for autocompletion lists when DirectWrite chosen for document text.
*   On Win32, optimize case-insensitive DBCS search to be around 5 times faster by using 64K memory to cache folding data for each DBCS code page used.
*   On Win32, fix a crash with bidirectional text.
*   When using Visual C++ through nmake, fix building for ARM64. Feature #1546.
*   On Qt, draw clipped UTF-8 text correctly. Bug #2464.
*   On Qt, avoid a dwell start when the mouse is moved outside the Scintilla widget. Bug #2466.
*   On Qt, autoCompleteSelection converts from local encoding when not in Unicode mode. Bug #2465.

Release 5.4.4 (https://www.scintilla.org/lexilla544.zip)

    Released 2 April 2025.
*   Fix building for ARM64. Pull request #308.

Close #16373
This commit is contained in:
Christian Grasser 2025-04-04 01:29:26 +02:00 committed by Don Ho
parent f127ba02d0
commit 5c1813185a
63 changed files with 5124 additions and 4159 deletions

View File

@ -1429,7 +1429,7 @@ bool NppParameters::load()
for (const auto& i : udlFiles) for (const auto& i : udlFiles)
{ {
auto udlDoc = new TiXmlDocument(i); TiXmlDocument* udlDoc = new TiXmlDocument(i);
loadOkay = udlDoc->LoadFile(); loadOkay = udlDoc->LoadFile();
if (!loadOkay) if (!loadOkay)
{ {

View File

@ -3,16 +3,24 @@ name: "Build and check Lexilla on Win32 with Visual C++"
on: [push] on: [push]
jobs: jobs:
# Compile for amd64 and cross-compile for arm64. Tests run only for amd64.
build: build:
runs-on: windows-latest runs-on: windows-latest
strategy:
matrix:
arch:
- amd64
- amd64_arm64
env:
TEST: ${{ matrix.arch == 'amd64' && 'test' || '' }}
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- name: Preparing nmake - name: Preparing nmake
uses: ilammy/msvc-dev-cmd@v1 uses: ilammy/msvc-dev-cmd@v1
with: with:
arch: x64 arch: ${{ matrix.arch }}
- name: Install Scintilla source - name: Install Scintilla source
run: | run: |
pwd pwd
@ -24,7 +32,7 @@ jobs:
- name: Unit Test - name: Unit Test
run: | run: |
cd test/unit cd test/unit
nmake -f test.mak DEBUG=1 test nmake -f test.mak DEBUG=1 $env:TEST
cd ../.. cd ../..
- name: Build Lexilla - name: Build Lexilla
run: | run: |
@ -33,14 +41,15 @@ jobs:
cd .. cd ..
- uses: actions/upload-artifact@v4 - uses: actions/upload-artifact@v4
with: with:
name: lexilla.dll name: lexilla${{ matrix.arch == 'amd64_arm64' && '-arm64' || '' }}.dll
path: bin/lexilla.dll path: bin/lexilla.dll
- name: Test lexing and folding - name: Test lexing and folding
run: | run: |
cd test cd test
nmake -f testlexers.mak DEBUG=1 test nmake -f testlexers.mak DEBUG=1 $env:TEST
cd .. cd ..
- name: CheckLexilla C Example - name: CheckLexilla C Example
if: matrix.arch == 'amd64'
run: | run: |
cd examples/CheckLexilla cd examples/CheckLexilla
cl -MP CheckLexilla.c -I ../../include -Fe: CheckLexilla cl -MP CheckLexilla.c -I ../../include -Fe: CheckLexilla

View File

@ -30,12 +30,12 @@
namespace { namespace {
#if defined(_WIN32) #if defined(_WIN32)
typedef FARPROC Function; using Function = FARPROC;
typedef HMODULE Module; using Module = HMODULE;
constexpr const char *pathSeparator = "\\"; constexpr const char *pathSeparator = "\\";
#else #else
typedef void *Function; using Function = void *;
typedef void *Module; using Module = void *;
constexpr const char *pathSeparator = "/"; constexpr const char *pathSeparator = "/";
#endif #endif
@ -164,9 +164,10 @@ bool Lexilla::Load(std::string_view sharedLibraryPaths) {
if (fnLexerCount && fnLexerName) { if (fnLexerCount && fnLexerName) {
const int nLexers = fnLexerCount(); const int nLexers = fnLexerCount();
for (int i = 0; i < nLexers; i++) { for (int i = 0; i < nLexers; i++) {
char name[100] = ""; constexpr size_t lengthName = 200;
char name[lengthName]{};
fnLexerName(i, name, sizeof(name)); fnLexerName(i, name, sizeof(name));
lexers.push_back(name); lexers.emplace_back(name);
} }
} }
CreateLexerFn fnCL = FunctionPointer<CreateLexerFn>( CreateLexerFn fnCL = FunctionPointer<CreateLexerFn>(
@ -268,7 +269,7 @@ std::string Lexilla::NameFromID(int identifier) {
} }
} }
} }
return std::string(); return {};
} }
std::vector<std::string> Lexilla::LibraryProperties() { std::vector<std::string> Lexilla::LibraryProperties() {

View File

@ -70,12 +70,14 @@ variableScope:lexilla/lexers/LexCmake.cxx
knownConditionTrueFalse:lexilla/lexers/LexCmake.cxx knownConditionTrueFalse:lexilla/lexers/LexCmake.cxx
constParameterReference:lexilla/lexers/LexCmake.cxx constParameterReference:lexilla/lexers/LexCmake.cxx
constParameterReference:lexilla/lexers/LexCOBOL.cxx constParameterReference:lexilla/lexers/LexCOBOL.cxx
constVariablePointer:lexilla/lexers/LexCOBOL.cxx
constParameterReference:lexilla/lexers/LexCoffeeScript.cxx constParameterReference:lexilla/lexers/LexCoffeeScript.cxx
constParameterPointer:lexilla/lexers/LexCoffeeScript.cxx constParameterPointer:lexilla/lexers/LexCoffeeScript.cxx
knownConditionTrueFalse:lexilla/lexers/LexCoffeeScript.cxx knownConditionTrueFalse:lexilla/lexers/LexCoffeeScript.cxx
constVariableReference:lexilla/lexers/LexConf.cxx constVariableReference:lexilla/lexers/LexConf.cxx
constParameterReference:lexilla/lexers/LexCPP.cxx constParameterReference:lexilla/lexers/LexCPP.cxx
variableScope:lexilla/lexers/LexCSS.cxx variableScope:lexilla/lexers/LexCSS.cxx
constVariablePointer:lexilla/lexers/LexCSS.cxx
knownConditionTrueFalse:lexilla/lexers/LexDataflex.cxx knownConditionTrueFalse:lexilla/lexers/LexDataflex.cxx
constParameterReference:lexilla/lexers/LexDataflex.cxx constParameterReference:lexilla/lexers/LexDataflex.cxx
variableScope:lexilla/lexers/LexDataflex.cxx variableScope:lexilla/lexers/LexDataflex.cxx
@ -118,6 +120,7 @@ unreadVariable:lexilla/lexers/LexMatlab.cxx
variableScope:lexilla/lexers/LexMatlab.cxx variableScope:lexilla/lexers/LexMatlab.cxx
variableScope:lexilla/lexers/LexMetapost.cxx variableScope:lexilla/lexers/LexMetapost.cxx
constParameterReference:lexilla/lexers/LexModula.cxx constParameterReference:lexilla/lexers/LexModula.cxx
duplicateBreak:lexilla/lexers/LexModula.cxx
variableScope:lexilla/lexers/LexModula.cxx variableScope:lexilla/lexers/LexModula.cxx
constParameterReference:lexilla/lexers/LexMPT.cxx constParameterReference:lexilla/lexers/LexMPT.cxx
variableScope:lexilla/lexers/LexMSSQL.cxx variableScope:lexilla/lexers/LexMSSQL.cxx
@ -140,6 +143,7 @@ constVariableReference:lexilla/lexers/LexPerl.cxx
knownConditionTrueFalse:lexilla/lexers/LexPerl.cxx knownConditionTrueFalse:lexilla/lexers/LexPerl.cxx
constParameterReference:lexilla/lexers/LexPLM.cxx constParameterReference:lexilla/lexers/LexPLM.cxx
constParameterReference:lexilla/lexers/LexPO.cxx constParameterReference:lexilla/lexers/LexPO.cxx
constVariablePointer:lexilla/lexers/LexPOV.cxx
constParameterReference:lexilla/lexers/LexPython.cxx constParameterReference:lexilla/lexers/LexPython.cxx
shadowVariable:lexilla/lexers/LexPowerPro.cxx shadowVariable:lexilla/lexers/LexPowerPro.cxx
knownConditionTrueFalse:lexilla/lexers/LexPowerPro.cxx knownConditionTrueFalse:lexilla/lexers/LexPowerPro.cxx
@ -188,7 +192,6 @@ constVariableReference:lexilla/lexers/LexX12.cxx
constParameterPointer:lexilla/lexers/LexX12.cxx constParameterPointer:lexilla/lexers/LexX12.cxx
uselessCallsSubstr:lexilla/lexers/LexX12.cxx uselessCallsSubstr:lexilla/lexers/LexX12.cxx
constParameterReference:lexilla/lexers/LexYAML.cxx constParameterReference:lexilla/lexers/LexYAML.cxx
constParameterPointer:lexilla/lexers/LexYAML.cxx
knownConditionTrueFalse:lexilla/lexers/LexYAML.cxx knownConditionTrueFalse:lexilla/lexers/LexYAML.cxx
// These are due to Accessor::IndentAmount not declaring the callback as taking a const. // These are due to Accessor::IndentAmount not declaring the callback as taking a const.

View File

@ -9,7 +9,7 @@
<meta name="keywords" content="Scintilla, SciTE, Editing Component, Text Editor" /> <meta name="keywords" content="Scintilla, SciTE, Editing Component, Text Editor" />
<meta name="Description" <meta name="Description"
content="www.scintilla.org is the home of the Scintilla editing component and SciTE text editor application." /> content="www.scintilla.org is the home of the Scintilla editing component and SciTE text editor application." />
<meta name="Date.Modified" content="20250225" /> <meta name="Date.Modified" content="20250402" />
<meta name="viewport" content="width=device-width, initial-scale=1" /> <meta name="viewport" content="width=device-width, initial-scale=1" />
<style type="text/css"> <style type="text/css">
.logo { .logo {
@ -51,6 +51,7 @@
<title> <title>
Lexilla Lexilla
</title> </title>
<link rel="canonical" href="https://scintilla.org/Lexilla.html" />
</head> </head>
<body bgcolor="#FFFFFF" text="#000000"> <body bgcolor="#FFFFFF" text="#000000">
<table bgcolor="#000000" width="100%" cellspacing="0" cellpadding="0" border="0"> <table bgcolor="#000000" width="100%" cellspacing="0" cellpadding="0" border="0">
@ -61,8 +62,8 @@
<font color="#FFCC99" size="4"> A library of language lexers for use with Scintilla</font> <font color="#FFCC99" size="4"> A library of language lexers for use with Scintilla</font>
</td> </td>
<td width="40%" align="right"> <td width="40%" align="right">
<font color="#FFCC99" size="3">Release version 5.4.3<br /> <font color="#FFCC99" size="3">Release version 5.4.4<br />
Site last modified February 25 2025</font> Site last modified April 2 2025</font>
</td> </td>
<td width="20%"> <td width="20%">
&nbsp; &nbsp;
@ -77,11 +78,11 @@
</tr> </tr>
</table> </table>
<ul id="versionlist"> <ul id="versionlist">
<li>Version 5.4.4 fixes a problem when building for ARM654 on Windows.</li>
<li>Version 5.4.3 improves C++, Modula 3, Pascal, Python, and Ruby.</li> <li>Version 5.4.3 improves C++, Modula 3, Pascal, Python, and Ruby.</li>
<li>Version 5.4.2 adds Nix lexer. Improves JavaScript, PHP, Rust, TOML, and Zig.</li> <li>Version 5.4.2 adds Nix lexer. Improves JavaScript, PHP, Rust, TOML, and Zig.</li>
<li>Version 5.4.1 adds Dart, troff, and Zig lexers. Improves C++, F#, HTML, and Smalltalk.</li> <li>Version 5.4.1 adds Dart, troff, and Zig lexers. Improves C++, F#, HTML, and Smalltalk.</li>
<li>Version 5.4.0 adds a TOML lexer.</li> <li>Version 5.4.0 adds a TOML lexer.</li>
<li>Version 5.3.3 improves HTML, JavaScript, Lua, PHP, and XML.</li>
</ul> </ul>
<ul id="menu"> <ul id="menu">
<li id="remote1"><a href="https://www.scintilla.org/SciTEImage.html">Screenshot</a></li> <li id="remote1"><a href="https://www.scintilla.org/SciTEImage.html">Screenshot</a></li>

View File

@ -10,6 +10,7 @@
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" /> <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
<title>Lexilla Documentation</title> <title>Lexilla Documentation</title>
<link rel="canonical" href="https://scintilla.org/LexillaDoc.html" />
<style type="text/css"> <style type="text/css">
<!-- <!--

View File

@ -26,9 +26,9 @@
<table bgcolor="#CCCCCC" width="100%" cellspacing="0" cellpadding="8" border="0"> <table bgcolor="#CCCCCC" width="100%" cellspacing="0" cellpadding="8" border="0">
<tr> <tr>
<td> <td>
<font size="4"> <a href="https://www.scintilla.org/lexilla543.zip"> <font size="4"> <a href="https://www.scintilla.org/lexilla544.zip">
Windows</a>&nbsp;&nbsp; Windows</a>&nbsp;&nbsp;
<a href="https://www.scintilla.org/lexilla543.tgz"> <a href="https://www.scintilla.org/lexilla544.tgz">
GTK/Linux</a>&nbsp;&nbsp; GTK/Linux</a>&nbsp;&nbsp;
</font> </font>
</td> </td>
@ -42,7 +42,7 @@
containing very few restrictions. containing very few restrictions.
</p> </p>
<h3> <h3>
Release 5.4.3 Release 5.4.4
</h3> </h3>
<h4> <h4>
Source Code Source Code
@ -50,8 +50,8 @@
The source code package contains all of the source code for Lexilla but no binary The source code package contains all of the source code for Lexilla but no binary
executable code and is available in executable code and is available in
<ul> <ul>
<li><a href="https://www.scintilla.org/lexilla543.zip">zip format</a> (1.4M) commonly used on Windows</li> <li><a href="https://www.scintilla.org/lexilla544.zip">zip format</a> (1.4M) commonly used on Windows</li>
<li><a href="https://www.scintilla.org/lexilla543.tgz">tgz format</a> (1.0M) commonly used on Linux and compatible operating systems</li> <li><a href="https://www.scintilla.org/lexilla544.tgz">tgz format</a> (1.0M) commonly used on Linux and compatible operating systems</li>
</ul> </ul>
Instructions for building on both Windows and Linux are included in the readme file. Instructions for building on both Windows and Linux are included in the readme file.
<h4> <h4>

View File

@ -590,9 +590,22 @@
<td>Henrik S. Johansen</td> <td>Henrik S. Johansen</td>
<td>Ekopalypse</td> <td>Ekopalypse</td>
<td>HoTschir</td> <td>HoTschir</td>
<td>Ahmet Sait</td>
</tr> </tr>
</table> </table>
<h2 id="Releases">Releases</h2> <h2 id="Releases">Releases</h2>
<h3>
<a href="https://www.scintilla.org/lexilla544.zip">Release 5.4.4</a>
</h3>
<ul>
<li>
Released 2 April 2025.
</li>
<li>
Fix building for ARM64.
<a href="https://github.com/ScintillaOrg/lexilla/pull/308">Pull request #308</a>.
</li>
</ul>
<h3> <h3>
<a href="https://www.scintilla.org/lexilla543.zip">Release 5.4.3</a> <a href="https://www.scintilla.org/lexilla543.zip">Release 5.4.3</a>
</h3> </h3>

View File

@ -109,7 +109,7 @@ constexpr int translateBashDigit(int ch) noexcept {
return BASH_BASE_ERROR; return BASH_BASE_ERROR;
} }
int getBashNumberBase(char *s) noexcept { int getBashNumberBase(const char *s) noexcept {
int i = 0; int i = 0;
int base = 0; int base = 0;
while (*s) { while (*s) {
@ -164,7 +164,7 @@ bool IsCommentLine(Sci_Position line, LexAccessor &styler) {
const char ch = styler[i]; const char ch = styler[i];
if (ch == '#') if (ch == '#')
return true; return true;
else if (ch != ' ' && ch != '\t') if (ch != ' ' && ch != '\t')
return false; return false;
} }
return false; return false;
@ -698,7 +698,7 @@ void SCI_METHOD LexerBash::Lex(Sci_PositionU startPos, Sci_Position length, int
identifierStyle = subStyle | insideCommand; identifierStyle = subStyle | insideCommand;
} }
// allow keywords ending in a whitespace, meta character or command delimiter // allow keywords ending in a whitespace, meta character or command delimiter
char s2[10]; char s2[10]{};
s2[0] = static_cast<char>(sc.ch); s2[0] = static_cast<char>(sc.ch);
s2[1] = '\0'; s2[1] = '\0';
const bool keywordEnds = IsASpace(sc.ch) || setMetaCharacter.Contains(sc.ch) || cmdDelimiter.InList(s2); const bool keywordEnds = IsASpace(sc.ch) || setMetaCharacter.Contains(sc.ch) || cmdDelimiter.InList(s2);
@ -1140,7 +1140,7 @@ void SCI_METHOD LexerBash::Lex(Sci_PositionU startPos, Sci_Position length, int
} }
// handle command delimiters in command Start|Body|Word state, also Test if 'test' or '[]' // handle command delimiters in command Start|Body|Word state, also Test if 'test' or '[]'
if (cmdState < CmdState::DoubleBracket) { if (cmdState < CmdState::DoubleBracket) {
char s[10]; char s[10]{};
s[0] = static_cast<char>(sc.ch); s[0] = static_cast<char>(sc.ch);
if (setBashOperator.Contains(sc.chNext)) { if (setBashOperator.Contains(sc.chNext)) {
s[1] = static_cast<char>(sc.chNext); s[1] = static_cast<char>(sc.chNext);

View File

@ -48,11 +48,11 @@ constexpr bool IsWhiteSpaceOrEOL(char ch) noexcept {
return ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r'; return ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r';
} }
unsigned int SpaceCount(char* lineBuffer) noexcept { unsigned int SpaceCount(const char* lineBuffer) noexcept {
if (lineBuffer == nullptr) if (lineBuffer == nullptr)
return 0; return 0;
char* headBuffer = lineBuffer; const char* headBuffer = lineBuffer;
while (*headBuffer == ' ') while (*headBuffer == ' ')
headBuffer++; headBuffer++;
@ -60,10 +60,10 @@ unsigned int SpaceCount(char* lineBuffer) noexcept {
return static_cast<unsigned int>(headBuffer - lineBuffer); return static_cast<unsigned int>(headBuffer - lineBuffer);
} }
bool KeywordAtChar(const char* lineBuffer, char* startComment, const WordList &keywords) noexcept { bool KeywordAtChar(const char* lineBuffer, const char* startComment, const WordList &keywords) noexcept {
if (lineBuffer == nullptr || startComment <= lineBuffer) if (lineBuffer == nullptr || startComment <= lineBuffer)
return false; return false;
char* endValue = startComment - 1; const char* endValue = startComment - 1;
while (endValue >= lineBuffer && *endValue == ' ') while (endValue >= lineBuffer && *endValue == ' ')
endValue--; endValue--;
Sci_PositionU len = static_cast<Sci_PositionU>(endValue - lineBuffer) + 1; Sci_PositionU len = static_cast<Sci_PositionU>(endValue - lineBuffer) + 1;

View File

@ -15,7 +15,7 @@
<key>CFBundlePackageType</key> <key>CFBundlePackageType</key>
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string> <string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
<key>CFBundleShortVersionString</key> <key>CFBundleShortVersionString</key>
<string>5.4.3</string> <string>5.4.4</string>
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string> <string>$(CURRENT_PROJECT_VERSION)</string>
<key>NSHumanReadableCopyright</key> <key>NSHumanReadableCopyright</key>

View File

@ -876,7 +876,7 @@
buildSettings = { buildSettings = {
CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 5.4.3; CURRENT_PROJECT_VERSION = 5.4.4;
DEAD_CODE_STRIPPING = YES; DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_TEAM = 4F446KW87E; DEVELOPMENT_TEAM = 4F446KW87E;
DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_COMPATIBILITY_VERSION = 1;
@ -904,7 +904,7 @@
buildSettings = { buildSettings = {
CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 5.4.3; CURRENT_PROJECT_VERSION = 5.4.4;
DEAD_CODE_STRIPPING = YES; DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_TEAM = 4F446KW87E; DEVELOPMENT_TEAM = 4F446KW87E;
DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_COMPATIBILITY_VERSION = 1;

View File

@ -4,8 +4,8 @@
#include <windows.h> #include <windows.h>
#define VERSION_LEXILLA "5.4.3" #define VERSION_LEXILLA "5.4.4"
#define VERSION_WORDS 5, 4, 3, 0 #define VERSION_WORDS 5, 4, 4, 0
VS_VERSION_INFO VERSIONINFO VS_VERSION_INFO VERSIONINFO
FILEVERSION VERSION_WORDS FILEVERSION VERSION_WORDS

View File

@ -15,7 +15,7 @@
.SUFFIXES: .cxx .SUFFIXES: .cxx
DIR_O=. DIR_O=obj
DIR_BIN=..\bin DIR_BIN=..\bin
LEXILLA=$(DIR_BIN)\lexilla.dll LEXILLA=$(DIR_BIN)\lexilla.dll
@ -23,6 +23,10 @@ LIBLEXILLA=$(DIR_BIN)\liblexilla.lib
LD=link LD=link
!IF "$(PLATFORM:64=)" == "arm"
ARM64=1
!ENDIF
!IFDEF SUPPORT_XP !IFDEF SUPPORT_XP
ADD_DEFINE=-D_USING_V110_SDK71_ ADD_DEFINE=-D_USING_V110_SDK71_
# Different subsystems for 32-bit and 64-bit Windows XP so detect based on Platform # Different subsystems for 32-bit and 64-bit Windows XP so detect based on Platform
@ -33,10 +37,11 @@ SUBSYSTEM=-SUBSYSTEM:WINDOWS,5.02
SUBSYSTEM=-SUBSYSTEM:WINDOWS,5.01 SUBSYSTEM=-SUBSYSTEM:WINDOWS,5.01
!ENDIF !ENDIF
!ELSE !ELSE
CETCOMPAT=-CETCOMPAT
!IFDEF ARM64 !IFDEF ARM64
ADD_DEFINE=-D_ARM64_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE=1 ADD_DEFINE=-D_ARM64_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE=1
SUBSYSTEM=-SUBSYSTEM:WINDOWS,10.00 SUBSYSTEM=-SUBSYSTEM:WINDOWS,10.00
!ELSE
CETCOMPAT=-CETCOMPAT
!ENDIF !ENDIF
!ENDIF !ENDIF
@ -68,7 +73,10 @@ SCINTILLA_INCLUDE = ../../scintilla/include
INCLUDEDIRS=-I../include -I$(SCINTILLA_INCLUDE) -I../lexlib INCLUDEDIRS=-I../include -I$(SCINTILLA_INCLUDE) -I../lexlib
CXXFLAGS=$(CXXFLAGS) $(INCLUDEDIRS) CXXFLAGS=$(CXXFLAGS) $(INCLUDEDIRS)
all: $(SCINTILLA_INCLUDE) $(LEXILLA) $(LIBLEXILLA) all: $(SCINTILLA_INCLUDE) $(DIR_O) $(LEXILLA) $(LIBLEXILLA)
$(DIR_O):
mkdir "$(DIR_O)" 2>NUL || cd .
clean: clean:
-del /q $(DIR_O)\*.obj $(DIR_O)\*.o $(DIR_O)\*.pdb \ -del /q $(DIR_O)\*.obj $(DIR_O)\*.o $(DIR_O)\*.pdb \

View File

@ -17,7 +17,7 @@ DEBUG_OPTIONS = -Zi -DEBUG -Od -MTd -DDEBUG $(STATIC_FLAG)
DEBUG_OPTIONS = -O2 -MT -DNDEBUG $(STATIC_FLAG) -GL DEBUG_OPTIONS = -O2 -MT -DNDEBUG $(STATIC_FLAG) -GL
!ENDIF !ENDIF
CXXFLAGS = /EHsc /std:c++latest $(DEBUG_OPTIONS) $(INCLUDEDIRS) CXXFLAGS = /EHsc /std:c++20 $(DEBUG_OPTIONS) $(INCLUDEDIRS)
OBJS = TestLexers.obj TestDocument.obj LexillaAccess.obj OBJS = TestLexers.obj TestDocument.obj LexillaAccess.obj

View File

@ -1 +1 @@
543 544

View File

@ -2819,8 +2819,8 @@ int ScintillaCall::ExtraDescent() {
return static_cast<int>(Call(Message::GetExtraDescent)); return static_cast<int>(Call(Message::GetExtraDescent));
} }
int ScintillaCall::MarkerSymbolDefined(int markerNumber) { MarkerSymbol ScintillaCall::MarkerSymbolDefined(int markerNumber) {
return static_cast<int>(Call(Message::MarkerSymbolDefined, markerNumber)); return static_cast<Scintilla::MarkerSymbol>(Call(Message::MarkerSymbolDefined, markerNumber));
} }
void ScintillaCall::MarginSetText(Line line, const char *text) { void ScintillaCall::MarginSetText(Line line, const char *text) {

View File

@ -15,7 +15,7 @@
<key>CFBundlePackageType</key> <key>CFBundlePackageType</key>
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string> <string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
<key>CFBundleShortVersionString</key> <key>CFBundleShortVersionString</key>
<string>5.5.5</string> <string>5.5.6</string>
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string> <string>$(CURRENT_PROJECT_VERSION)</string>
<key>NSHumanReadableCopyright</key> <key>NSHumanReadableCopyright</key>

View File

@ -586,7 +586,7 @@
CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO; COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 5.5.5; CURRENT_PROJECT_VERSION = 5.5.6;
DEAD_CODE_STRIPPING = YES; DEAD_CODE_STRIPPING = YES;
DEBUG_INFORMATION_FORMAT = dwarf; DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_STRICT_OBJC_MSGSEND = YES;
@ -650,7 +650,7 @@
CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO; COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 5.5.5; CURRENT_PROJECT_VERSION = 5.5.6;
DEAD_CODE_STRIPPING = YES; DEAD_CODE_STRIPPING = YES;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO; ENABLE_NS_ASSERTIONS = NO;
@ -682,7 +682,7 @@
CODE_SIGN_IDENTITY = "-"; CODE_SIGN_IDENTITY = "-";
CODE_SIGN_STYLE = Manual; CODE_SIGN_STYLE = Manual;
COMBINE_HIDPI_IMAGES = YES; COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 5.5.5; CURRENT_PROJECT_VERSION = 5.5.6;
DEAD_CODE_STRIPPING = YES; DEAD_CODE_STRIPPING = YES;
DEFINES_MODULE = YES; DEFINES_MODULE = YES;
DEVELOPMENT_TEAM = ""; DEVELOPMENT_TEAM = "";
@ -717,7 +717,7 @@
CODE_SIGN_IDENTITY = "-"; CODE_SIGN_IDENTITY = "-";
CODE_SIGN_STYLE = Manual; CODE_SIGN_STYLE = Manual;
COMBINE_HIDPI_IMAGES = YES; COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 5.5.5; CURRENT_PROJECT_VERSION = 5.5.6;
DEAD_CODE_STRIPPING = YES; DEAD_CODE_STRIPPING = YES;
DEFINES_MODULE = YES; DEFINES_MODULE = YES;
DEVELOPMENT_TEAM = ""; DEVELOPMENT_TEAM = "";

View File

@ -130,7 +130,7 @@
<h1>Scintilla Documentation</h1> <h1>Scintilla Documentation</h1>
<p>Last edited 12 February 2025 NH</p> <p>Last edited 29 March 2025 NH</p>
<p style="background:#90F0C0">Scintilla 5 has moved the lexers from Scintilla into a new <p style="background:#90F0C0">Scintilla 5 has moved the lexers from Scintilla into a new
<a href="Lexilla.html">Lexilla</a> project.<br /> <a href="Lexilla.html">Lexilla</a> project.<br />
@ -3583,117 +3583,140 @@ struct Sci_TextToFindFull {
<table class="standard" summary="Character Sets supported"><tbody> <table class="standard" summary="Character Sets supported"><tbody>
<tr> <tr>
<th>Character Set</th> <th>Character Set</th>
<th>Value</th>
<th>Windows</th> <th>Windows</th>
<th>GTK</th> <th>GTK</th>
<th>Cocoa</th></tr></tbody> <th>Cocoa</th></tr></tbody>
<tbody> <tbody>
<tr> <tr class="section">
<td><code>SC_CHARSET_ANSI</code></td> <th align="left"><code>SC_CHARSET_ANSI</code></th>
<td>0</td>
<td>&check;</td> <td>&check;</td>
<td>&check;</td> <td>&check;</td>
<td>&check; (8859-1)</td></tr> <td>&check; (8859-1)</td></tr>
<tr> <tr>
<td><code>SC_CHARSET_ARABIC</code></td> <th align="left"><code>SC_CHARSET_ARABIC</code></th>
<td>178</td>
<td>&check;</td> <td>&check;</td>
<td></td> <td></td>
<td>&check;</td></tr> <td>&check;</td></tr>
<tr> <tr>
<td><code>SC_CHARSET_BALTIC</code></td> <th align="left"><code>SC_CHARSET_BALTIC</code></th>
<td>186</td>
<td>&check;</td> <td>&check;</td>
<td></td> <td></td>
<td>&check;</td></tr> <td>&check;</td></tr>
<tr> <tr>
<td><code>SC_CHARSET_CHINESEBIG5</code></td> <th align="left"><code>SC_CHARSET_CHINESEBIG5</code></th>
<td>136</td>
<td>&check;</td> <td>&check;</td>
<td></td> <td></td>
<td>&check;</td></tr> <td>&check;</td></tr>
<tr> <tr>
<td><code>SC_CHARSET_DEFAULT</code></td> <th align="left"><code>SC_CHARSET_DEFAULT</code></th>
<td>1</td>
<td>&check;</td> <td>&check;</td>
<td>&check; (8859-1)</td> <td>&check; (8859-1)</td>
<td>&check; (8859-1)</td></tr> <td>&check; (8859-1)</td></tr>
<tr> <tr>
<td><code>SC_CHARSET_EASTEUROPE</code></td> <th align="left"><code>SC_CHARSET_EASTEUROPE</code></th>
<td>238</td>
<td>&check;</td> <td>&check;</td>
<td>&check;</td> <td>&check;</td>
<td>&check;</td></tr> <td>&check;</td></tr>
<tr> <tr>
<td><code>SC_CHARSET_GB2312</code></td> <th align="left"><code>SC_CHARSET_GB2312</code></th>
<td>134</td>
<td>&check;</td> <td>&check;</td>
<td>&check;</td> <td>&check;</td>
<td>&check;</td></tr> <td>&check;</td></tr>
<tr> <tr>
<td><code>SC_CHARSET_GREEK</code></td> <th align="left"><code>SC_CHARSET_GREEK</code></td>
<td>161</td>
<td>&check;</td> <td>&check;</td>
<td></td> <td></td>
<td>&check;</td></tr> <td>&check;</td></tr>
<tr> <tr>
<td><code>SC_CHARSET_HANGUL</code></td> <th align="left"><code>SC_CHARSET_HANGUL</code></th>
<td>129</td>
<td>&check;</td> <td>&check;</td>
<td>&check;</td> <td>&check;</td>
<td>&check;</td></tr> <td>&check;</td></tr>
<tr> <tr>
<td><code>SC_CHARSET_HEBREW</code></td> <th align="left"><code>SC_CHARSET_HEBREW</code></th>
<td>177</td>
<td>&check;</td> <td>&check;</td>
<td></td> <td></td>
<td>&check;</td></tr> <td>&check;</td></tr>
<tr> <tr>
<td><code>SC_CHARSET_JOHAB</code></td> <th align="left"><code>SC_CHARSET_JOHAB</code></th>
<td>130</td>
<td>&check;</td> <td>&check;</td>
<td></td> <td></td>
<td>&check;</td></tr> <td>&check;</td></tr>
<tr> <tr>
<td><code>SC_CHARSET_MAC</code></td> <th align="left"><code>SC_CHARSET_MAC</code></th>
<td>77</td>
<td>&check;</td> <td>&check;</td>
<td></td> <td></td>
<td>&check;</td></tr> <td>&check;</td></tr>
<tr> <tr>
<td><code>SC_CHARSET_OEM</code></td> <th align="left"><code>SC_CHARSET_OEM</code></th>
<td>255</td>
<td>&check;</td> <td>&check;</td>
<td></td> <td></td>
<td>&check;</td></tr> <td>&check;</td></tr>
<tr> <tr>
<td><code>SC_CHARSET_RUSSIAN</code></td> <th align="left"><code>SC_CHARSET_RUSSIAN</code></th>
<td>204</td>
<td>&check; (cp1251)</td> <td>&check; (cp1251)</td>
<td>&check; (koi8-r)</td> <td>&check; (koi8-r)</td>
<td>&check; (cp1251)</td></tr> <td>&check; (cp1251)</td></tr>
<tr> <tr>
<td><code>SC_CHARSET_SHIFTJIS</code></td> <th align="left"><code>SC_CHARSET_SHIFTJIS</code></th>
<td>128</td>
<td>&check;</td> <td>&check;</td>
<td>&check;</td> <td>&check;</td>
<td>&check;</td></tr> <td>&check;</td></tr>
<tr> <tr>
<td><code>SC_CHARSET_SYMBOL</code></td> <th align="left"><code>SC_CHARSET_SYMBOL</code></th>
<td>2</td>
<td>&check;</td> <td>&check;</td>
<td></td> <td></td>
<td>&check;</td></tr> <td>&check;</td></tr>
<tr> <tr>
<td><code>SC_CHARSET_THAI</code></td> <th align="left"><code>SC_CHARSET_THAI</code></th>
<td>222</td>
<td>&check;</td> <td>&check;</td>
<td></td> <td></td>
<td>&check;</td></tr> <td>&check;</td></tr>
<tr> <tr>
<td><code>SC_CHARSET_TURKISH</code></td> <th align="left"><code>SC_CHARSET_TURKISH</code></th>
<td>162</td>
<td>&check;</td> <td>&check;</td>
<td></td> <td></td>
<td>&check;</td></tr> <td>&check;</td></tr>
<tr> <tr>
<td><code>SC_CHARSET_VIETNAMESE</code></td> <th align="left"><code>SC_CHARSET_VIETNAMESE</code></th>
<td>163</td>
<td>&check;</td> <td>&check;</td>
<td></td> <td></td>
<td>&check;</td></tr> <td>&check;</td></tr>
<tr> <tr>
<td><code>SC_CHARSET_OEM866</code></td> <th align="left"><code>SC_CHARSET_OEM866</code></th>
<td>866</td>
<td></td> <td></td>
<td>&check; (cp866)</td> <td>&check; (cp866)</td>
<td></td></tr> <td></td></tr>
<tr> <tr>
<td><code>SC_CHARSET_CYRILLIC</code></td> <th align="left"><code>SC_CHARSET_CYRILLIC</code></th>
<td>1251</td>
<td></td> <td></td>
<td>&check; (cp1251)</td> <td>&check; (cp1251)</td>
<td>&check; (cp1251)</td></tr> <td>&check; (cp1251)</td></tr>
<tr> <tr>
<td><code>SC_CHARSET_8859_15</code></td> <th align="left"><code>SC_CHARSET_8859_15</code></th>
<td>1000</td>
<td></td> <td></td>
<td>&check;</td> <td>&check;</td>
<td>&check;</td></tr> <td>&check;</td></tr>
@ -4960,6 +4983,8 @@ struct Sci_TextToFindFull {
</table> </table>
<p>Different shapes available for EOL annotations. <p>Different shapes available for EOL annotations.
Different shapes depend on drawing API so <code>SC_TECHNOLOGY_DEFAULT</code> on Win32
only draws a rectangle.
Only one shape can currently be used for the whole document - this illustration was produced by trickery.</p> Only one shape can currently be used for the whole document - this illustration was produced by trickery.</p>
<p><img src="StadiumVariants.png" alt="Different shapes available for EOL annotations" /></p> <p><img src="StadiumVariants.png" alt="Different shapes available for EOL annotations" /></p>
@ -9043,21 +9068,24 @@ struct SCNotification {
Sci_Position position; Sci_Position position;
/* SCN_STYLENEEDED, SCN_DOUBLECLICK, SCN_MODIFIED, SCN_MARGINCLICK, */ /* SCN_STYLENEEDED, SCN_DOUBLECLICK, SCN_MODIFIED, SCN_MARGINCLICK, */
/* SCN_MARGINRIGHTCLICK, SCN_NEEDSHOWN, SCN_DWELLSTART, SCN_DWELLEND, */ /* SCN_MARGINRIGHTCLICK, SCN_NEEDSHOWN, SCN_DWELLSTART, SCN_DWELLEND, */
/* SCN_CALLTIPCLICK, SCN_HOTSPOTCLICK, SCN_HOTSPOTDOUBLECLICK, */ /* SCN_CALLTIPCLICK, */
/* SCN_HOTSPOTRELEASECLICK, SCN_INDICATORCLICK, SCN_INDICATORRELEASE, */ /* SCN_HOTSPOTCLICK, SCN_HOTSPOTDOUBLECLICK, SCN_HOTSPOTRELEASECLICK, */
/* SCN_USERLISTSELECTION, SCN_AUTOCSELECTION, SCN_AUTOCSELECTIONCHANGE */ /* SCN_INDICATORCLICK, SCN_INDICATORRELEASE, */
/* SCN_USERLISTSELECTION, SCN_AUTOCCOMPLETED, SCN_AUTOCSELECTION, */
/* SCN_AUTOCSELECTIONCHANGE */
int ch; int ch;
/* SCN_CHARADDED, SCN_KEY, SCN_AUTOCCOMPLETE, SCN_AUTOCSELECTION, */ /* SCN_CHARADDED, SCN_KEY, SCN_AUTOCCOMPLETED, SCN_AUTOCSELECTION, */
/* SCN_USERLISTSELECTION */ /* SCN_USERLISTSELECTION */
int modifiers; int modifiers;
/* SCN_KEY, SCN_DOUBLECLICK, SCN_HOTSPOTCLICK, SCN_HOTSPOTDOUBLECLICK, */ /* SCN_KEY, SCN_DOUBLECLICK, SCN_HOTSPOTCLICK, SCN_HOTSPOTDOUBLECLICK, */
/* SCN_HOTSPOTRELEASECLICK, SCN_INDICATORCLICK, SCN_INDICATORRELEASE, */ /* SCN_HOTSPOTRELEASECLICK, SCN_INDICATORCLICK, SCN_INDICATORRELEASE, */
/* SCN_MARGINCLICK, SCN_MARGINRIGHTCLICK */
int modificationType; /* SCN_MODIFIED */ int modificationType; /* SCN_MODIFIED */
const char *text; const char *text;
/* SCN_MODIFIED, SCN_USERLISTSELECTION, SCN_AUTOCSELECTION, SCN_URIDROPPED, */ /* SCN_MODIFIED, SCN_USERLISTSELECTION, SCN_URIDROPPED, */
/* SCN_AUTOCSELECTIONCHANGE */ /* SCN_AUTOCCOMPLETED, SCN_AUTOCSELECTION, SCN_AUTOCSELECTIONCHANGE */
Sci_Position length; /* SCN_MODIFIED */ Sci_Position length; /* SCN_MODIFIED */
Sci_Position linesAdded; /* SCN_MODIFIED */ Sci_Position linesAdded; /* SCN_MODIFIED */

View File

@ -26,9 +26,9 @@
<table bgcolor="#CCCCCC" width="100%" cellspacing="0" cellpadding="8" border="0"> <table bgcolor="#CCCCCC" width="100%" cellspacing="0" cellpadding="8" border="0">
<tr> <tr>
<td> <td>
<font size="4"> <a href="https://www.scintilla.org/scintilla555.zip"> <font size="4"> <a href="https://www.scintilla.org/scintilla556.zip">
Windows</a>&nbsp;&nbsp; Windows</a>&nbsp;&nbsp;
<a href="https://www.scintilla.org/scintilla555.tgz"> <a href="https://www.scintilla.org/scintilla556.tgz">
GTK/Linux</a>&nbsp;&nbsp; GTK/Linux</a>&nbsp;&nbsp;
</font> </font>
</td> </td>
@ -42,7 +42,7 @@
containing very few restrictions. containing very few restrictions.
</p> </p>
<h3> <h3>
Release 5.5.5 Release 5.5.6
</h3> </h3>
<h4> <h4>
Source Code Source Code
@ -50,8 +50,8 @@
The source code package contains all of the source code for Scintilla but no binary The source code package contains all of the source code for Scintilla but no binary
executable code and is available in executable code and is available in
<ul> <ul>
<li><a href="https://www.scintilla.org/scintilla555.zip">zip format</a> (1.8M) commonly used on Windows</li> <li><a href="https://www.scintilla.org/scintilla556.zip">zip format</a> (1.8M) commonly used on Windows</li>
<li><a href="https://www.scintilla.org/scintilla555.tgz">tgz format</a> (1.7M) commonly used on Linux and compatible operating systems</li> <li><a href="https://www.scintilla.org/scintilla556.tgz">tgz format</a> (1.7M) commonly used on Linux and compatible operating systems</li>
</ul> </ul>
Instructions for building on both Windows and Linux are included in the readme file. Instructions for building on both Windows and Linux are included in the readme file.
<h4> <h4>

View File

@ -585,9 +585,56 @@
<td>Martijn Laan</td> <td>Martijn Laan</td>
</tr><tr> </tr><tr>
<td>Pawel Z Wronek</td> <td>Pawel Z Wronek</td>
<td>Joachim Mairboeck</td>
<td>Gianluca Vaccari</td>
<td>8day</td>
</tr><tr>
<td>Ahmet Sait</td>
</tr> </tr>
</table> </table>
<h2 id="Releases">Releases</h2> <h2 id="Releases">Releases</h2>
<h3>
<a href="https://www.scintilla.org/scintilla556.zip">Release 5.5.6</a>
</h3>
<ul>
<li>
Released 2 April 2025.
</li>
<li>
Disallow changing case of protected text.
<a href="https://sourceforge.net/p/scintilla/bugs/2463/">Bug #2463</a>.
</li>
<li>
Return enumeration type from MarkerSymbolDefined to match MarkerDefine.
<a href="https://sourceforge.net/p/scintilla/bugs/2469/">Bug #2469</a>.
</li>
<li>
On Win32, use DirectWrite for autocompletion lists when DirectWrite chosen for document text.
</li>
<li>
On Win32, optimize case-insensitive DBCS search to be around 5 times faster
by using 64K memory to cache folding data for each DBCS code page used.
</li>
<li>
On Win32, fix a crash with bidirectional text.
</li>
<li>
When using Visual C++ through nmake, fix building for ARM64.
<a href="https://sourceforge.net/p/scintilla/feature-requests/1546/">Feature #1546</a>.
</li>
<li>
On Qt, draw clipped UTF-8 text correctly.
<a href="https://sourceforge.net/p/scintilla/bugs/2464/">Bug #2464</a>.
</li>
<li>
On Qt, avoid a dwell start when the mouse is moved outside the Scintilla widget.
<a href="https://sourceforge.net/p/scintilla/bugs/2466/">Bug #2466</a>.
</li>
<li>
On Qt, autoCompleteSelection converts from local encoding when not in Unicode mode.
<a href="https://sourceforge.net/p/scintilla/bugs/2465/">Bug #2465</a>.
</li>
</ul>
<h3> <h3>
<a href="https://www.scintilla.org/scintilla555.zip">Release 5.5.5</a> <a href="https://www.scintilla.org/scintilla555.zip">Release 5.5.5</a>
</h3> </h3>

View File

@ -10,6 +10,7 @@
<title> <title>
Scintilla Usage Notes Scintilla Usage Notes
</title> </title>
<link rel="canonical" href="https://scintilla.org/ScintillaUsage.html" />
<style type="text/css"> <style type="text/css">
SPAN { SPAN {
font-family: Verdana, Arial, Helvetica; font-family: Verdana, Arial, Helvetica;

View File

@ -9,7 +9,7 @@
<meta name="keywords" content="Scintilla, SciTE, Editing Component, Text Editor" /> <meta name="keywords" content="Scintilla, SciTE, Editing Component, Text Editor" />
<meta name="Description" <meta name="Description"
content="www.scintilla.org is the home of the Scintilla editing component and SciTE text editor application." /> content="www.scintilla.org is the home of the Scintilla editing component and SciTE text editor application." />
<meta name="Date.Modified" content="20250225" /> <meta name="Date.Modified" content="20250402" />
<meta name="viewport" content="width=device-width, initial-scale=1" /> <meta name="viewport" content="width=device-width, initial-scale=1" />
<style type="text/css"> <style type="text/css">
.logo { .logo {
@ -61,8 +61,8 @@
GTK, and macOS</font> GTK, and macOS</font>
</td> </td>
<td width="40%" align="right"> <td width="40%" align="right">
<font color="#FFCC99" size="3"> Release version 5.5.5<br /> <font color="#FFCC99" size="3"> Release version 5.5.6<br />
Site last modified February 25 2025</font> Site last modified April 2 2025</font>
</td> </td>
<td width="20%"> <td width="20%">
&nbsp; &nbsp;
@ -77,11 +77,11 @@
</tr> </tr>
</table> </table>
<ul id="versionlist"> <ul id="versionlist">
<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.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.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> <li>Version 5.5.3 fixes horizontal scrolling with a touchpad on Win32.</li>
<li>Version 5.5.2 adds multiple selection copy with separator, a font stretch setting, and access to whether an undo sequence is active.</li> <li>Version 5.5.2 adds multiple selection copy with separator, a font stretch setting, and access to whether an undo sequence is active.</li>
<li>Version 5.5.1 adds SCI_CUTALLOWLINE and fixes a Win32 bug that caused the cursor to flicker.</li>
</ul> </ul>
<ul id="menu"> <ul id="menu">
<li id="remote1"><a href="https://www.scintilla.org/SciTEImage.html">Screenshot</a></li> <li id="remote1"><a href="https://www.scintilla.org/SciTEImage.html">Screenshot</a></li>

View File

@ -1424,10 +1424,12 @@ struct SCNotification {
Sci_NotifyHeader nmhdr; Sci_NotifyHeader nmhdr;
Sci_Position position; Sci_Position position;
/* SCN_STYLENEEDED, SCN_DOUBLECLICK, SCN_MODIFIED, SCN_MARGINCLICK, */ /* SCN_STYLENEEDED, SCN_DOUBLECLICK, SCN_MODIFIED, SCN_MARGINCLICK, */
/* SCN_NEEDSHOWN, SCN_DWELLSTART, SCN_DWELLEND, SCN_CALLTIPCLICK, */ /* SCN_MARGINRIGHTCLICK, SCN_NEEDSHOWN, SCN_DWELLSTART, SCN_DWELLEND, */
/* SCN_CALLTIPCLICK, */
/* SCN_HOTSPOTCLICK, SCN_HOTSPOTDOUBLECLICK, SCN_HOTSPOTRELEASECLICK, */ /* SCN_HOTSPOTCLICK, SCN_HOTSPOTDOUBLECLICK, SCN_HOTSPOTRELEASECLICK, */
/* SCN_INDICATORCLICK, SCN_INDICATORRELEASE, */ /* SCN_INDICATORCLICK, SCN_INDICATORRELEASE, */
/* SCN_USERLISTSELECTION, SCN_AUTOCSELECTION */ /* SCN_USERLISTSELECTION, SCN_AUTOCCOMPLETED, SCN_AUTOCSELECTION, */
/* SCN_AUTOCSELECTIONCHANGE */
int ch; int ch;
/* SCN_CHARADDED, SCN_KEY, SCN_AUTOCCOMPLETED, SCN_AUTOCSELECTION, */ /* SCN_CHARADDED, SCN_KEY, SCN_AUTOCCOMPLETED, SCN_AUTOCSELECTION, */
@ -1435,10 +1437,12 @@ struct SCNotification {
int modifiers; int modifiers;
/* SCN_KEY, SCN_DOUBLECLICK, SCN_HOTSPOTCLICK, SCN_HOTSPOTDOUBLECLICK, */ /* SCN_KEY, SCN_DOUBLECLICK, SCN_HOTSPOTCLICK, SCN_HOTSPOTDOUBLECLICK, */
/* SCN_HOTSPOTRELEASECLICK, SCN_INDICATORCLICK, SCN_INDICATORRELEASE, */ /* SCN_HOTSPOTRELEASECLICK, SCN_INDICATORCLICK, SCN_INDICATORRELEASE, */
/* SCN_MARGINCLICK, SCN_MARGINRIGHTCLICK */
int modificationType; /* SCN_MODIFIED */ int modificationType; /* SCN_MODIFIED */
const char *text; const char *text;
/* SCN_MODIFIED, SCN_USERLISTSELECTION, SCN_AUTOCSELECTION, SCN_URIDROPPED */ /* SCN_MODIFIED, SCN_USERLISTSELECTION, SCN_URIDROPPED, */
/* SCN_AUTOCCOMPLETED, SCN_AUTOCSELECTION, SCN_AUTOCSELECTIONCHANGE */
Sci_Position length; /* SCN_MODIFIED */ Sci_Position length; /* SCN_MODIFIED */
Sci_Position linesAdded; /* SCN_MODIFIED */ Sci_Position linesAdded; /* SCN_MODIFIED */
@ -1448,15 +1452,15 @@ struct SCNotification {
Sci_Position line; /* SCN_MODIFIED */ Sci_Position line; /* SCN_MODIFIED */
int foldLevelNow; /* SCN_MODIFIED */ int foldLevelNow; /* SCN_MODIFIED */
int foldLevelPrev; /* SCN_MODIFIED */ int foldLevelPrev; /* SCN_MODIFIED */
int margin; /* SCN_MARGINCLICK */ int margin; /* SCN_MARGINCLICK, SCN_MARGINRIGHTCLICK */
int listType; /* SCN_USERLISTSELECTION */ int listType; /* SCN_USERLISTSELECTION, SCN_AUTOCSELECTIONCHANGE */
int x; /* SCN_DWELLSTART, SCN_DWELLEND */ int x; /* SCN_DWELLSTART, SCN_DWELLEND */
int y; /* SCN_DWELLSTART, SCN_DWELLEND */ int y; /* SCN_DWELLSTART, SCN_DWELLEND */
int token; /* SCN_MODIFIED with SC_MOD_CONTAINER */ int token; /* SCN_MODIFIED with SC_MOD_CONTAINER */
Sci_Position annotationLinesAdded; /* SCN_MODIFIED with SC_MOD_CHANGEANNOTATION */ Sci_Position annotationLinesAdded; /* SCN_MODIFIED with SC_MOD_CHANGEANNOTATION */
int updated; /* SCN_UPDATEUI */ int updated; /* SCN_UPDATEUI */
int listCompletionMethod; int listCompletionMethod;
/* SCN_AUTOCSELECTION, SCN_AUTOCCOMPLETED, SCN_USERLISTSELECTION, */ /* SCN_AUTOCSELECTION, SCN_AUTOCCOMPLETED, SCN_USERLISTSELECTION */
int characterSource; /* SCN_CHARADDED */ int characterSource; /* SCN_CHARADDED */
}; };

View File

@ -2755,7 +2755,7 @@ set void SetExtraDescent=2527(int extraDescent,)
get int GetExtraDescent=2528(,) get int GetExtraDescent=2528(,)
# Which symbol was defined for markerNumber with MarkerDefine # Which symbol was defined for markerNumber with MarkerDefine
fun int MarkerSymbolDefined=2529(int markerNumber,) fun MarkerSymbol MarkerSymbolDefined=2529(int markerNumber,)
# Set the text in the text margin for a line # Set the text in the text margin for a line
set void MarginSetText=2530(line line, string text) set void MarginSetText=2530(line line, string text)

View File

@ -750,7 +750,7 @@ public:
int ExtraAscent(); int ExtraAscent();
void SetExtraDescent(int extraDescent); void SetExtraDescent(int extraDescent);
int ExtraDescent(); int ExtraDescent();
int MarkerSymbolDefined(int markerNumber); Scintilla::MarkerSymbol MarkerSymbolDefined(int markerNumber);
void MarginSetText(Line line, const char *text); void MarginSetText(Line line, const char *text);
int MarginGetText(Line line, char *text); int MarginGetText(Line line, char *text);
std::string MarginGetText(Line line); std::string MarginGetText(Line line);

View File

@ -88,10 +88,12 @@ struct NotificationData {
NotifyHeader nmhdr; NotifyHeader nmhdr;
Position position; Position position;
/* SCN_STYLENEEDED, SCN_DOUBLECLICK, SCN_MODIFIED, SCN_MARGINCLICK, */ /* SCN_STYLENEEDED, SCN_DOUBLECLICK, SCN_MODIFIED, SCN_MARGINCLICK, */
/* SCN_NEEDSHOWN, SCN_DWELLSTART, SCN_DWELLEND, SCN_CALLTIPCLICK, */ /* SCN_MARGINRIGHTCLICK, SCN_NEEDSHOWN, SCN_DWELLSTART, SCN_DWELLEND, */
/* SCN_CALLTIPCLICK, */
/* SCN_HOTSPOTCLICK, SCN_HOTSPOTDOUBLECLICK, SCN_HOTSPOTRELEASECLICK, */ /* SCN_HOTSPOTCLICK, SCN_HOTSPOTDOUBLECLICK, SCN_HOTSPOTRELEASECLICK, */
/* SCN_INDICATORCLICK, SCN_INDICATORRELEASE, */ /* SCN_INDICATORCLICK, SCN_INDICATORRELEASE, */
/* SCN_USERLISTSELECTION, SCN_AUTOCSELECTION */ /* SCN_USERLISTSELECTION, SCN_AUTOCCOMPLETED, SCN_AUTOCSELECTION, */
/* SCN_AUTOCSELECTIONCHANGE */
int ch; int ch;
/* SCN_CHARADDED, SCN_KEY, SCN_AUTOCCOMPLETED, SCN_AUTOCSELECTION, */ /* SCN_CHARADDED, SCN_KEY, SCN_AUTOCCOMPLETED, SCN_AUTOCSELECTION, */
@ -99,10 +101,12 @@ struct NotificationData {
KeyMod modifiers; KeyMod modifiers;
/* SCN_KEY, SCN_DOUBLECLICK, SCN_HOTSPOTCLICK, SCN_HOTSPOTDOUBLECLICK, */ /* SCN_KEY, SCN_DOUBLECLICK, SCN_HOTSPOTCLICK, SCN_HOTSPOTDOUBLECLICK, */
/* SCN_HOTSPOTRELEASECLICK, SCN_INDICATORCLICK, SCN_INDICATORRELEASE, */ /* SCN_HOTSPOTRELEASECLICK, SCN_INDICATORCLICK, SCN_INDICATORRELEASE, */
/* SCN_MARGINCLICK, SCN_MARGINRIGHTCLICK */
ModificationFlags modificationType; /* SCN_MODIFIED */ ModificationFlags modificationType; /* SCN_MODIFIED */
const char *text; const char *text;
/* SCN_MODIFIED, SCN_USERLISTSELECTION, SCN_AUTOCSELECTION, SCN_URIDROPPED */ /* SCN_MODIFIED, SCN_USERLISTSELECTION, SCN_URIDROPPED, */
/* SCN_AUTOCCOMPLETED, SCN_AUTOCSELECTION, SCN_AUTOCSELECTIONCHANGE */
Position length; /* SCN_MODIFIED */ Position length; /* SCN_MODIFIED */
Position linesAdded; /* SCN_MODIFIED */ Position linesAdded; /* SCN_MODIFIED */
@ -112,15 +116,15 @@ struct NotificationData {
Position line; /* SCN_MODIFIED */ Position line; /* SCN_MODIFIED */
FoldLevel foldLevelNow; /* SCN_MODIFIED */ FoldLevel foldLevelNow; /* SCN_MODIFIED */
FoldLevel foldLevelPrev; /* SCN_MODIFIED */ FoldLevel foldLevelPrev; /* SCN_MODIFIED */
int margin; /* SCN_MARGINCLICK */ int margin; /* SCN_MARGINCLICK, SCN_MARGINRIGHTCLICK */
int listType; /* SCN_USERLISTSELECTION */ int listType; /* SCN_USERLISTSELECTION, SCN_AUTOCSELECTIONCHANGE */
int x; /* SCN_DWELLSTART, SCN_DWELLEND */ int x; /* SCN_DWELLSTART, SCN_DWELLEND */
int y; /* SCN_DWELLSTART, SCN_DWELLEND */ int y; /* SCN_DWELLSTART, SCN_DWELLEND */
int token; /* SCN_MODIFIED with SC_MOD_CONTAINER */ int token; /* SCN_MODIFIED with SC_MOD_CONTAINER */
Position annotationLinesAdded; /* SCN_MODIFIED with SC_MOD_CHANGEANNOTATION */ Position annotationLinesAdded; /* SCN_MODIFIED with SC_MOD_CHANGEANNOTATION */
Update updated; /* SCN_UPDATEUI */ Update updated; /* SCN_UPDATEUI */
CompletionMethods listCompletionMethod; CompletionMethods listCompletionMethod;
/* SCN_AUTOCSELECTION, SCN_AUTOCCOMPLETED, SCN_USERLISTSELECTION, */ /* SCN_AUTOCSELECTION, SCN_AUTOCCOMPLETED, SCN_USERLISTSELECTION */
CharacterSource characterSource; /* SCN_CHARADDED */ CharacterSource characterSource; /* SCN_CHARADDED */
}; };

View File

@ -13,7 +13,7 @@ TEMPLATE = lib
CONFIG += lib_bundle CONFIG += lib_bundle
CONFIG += c++1z CONFIG += c++1z
VERSION = 5.5.5 VERSION = 5.5.6
SOURCES += \ SOURCES += \
ScintillaEdit.cpp \ ScintillaEdit.cpp \

View File

@ -667,7 +667,7 @@ void SurfaceImpl::DrawTextClippedUTF8(PRectangle rc,
ColourRGBA back) ColourRGBA back)
{ {
SetClip(rc); SetClip(rc);
DrawTextNoClip(rc, font, ybase, text, fore, back); DrawTextNoClipUTF8(rc, font, ybase, text, fore, back);
PopClip(); PopClip();
} }

View File

@ -373,6 +373,12 @@ void ScintillaEditBase::mouseMoveEvent(QMouseEvent *event)
sqt->ButtonMoveWithModifiers(pos, TimeOfEvent(time), modifiers); sqt->ButtonMoveWithModifiers(pos, TimeOfEvent(time), modifiers);
} }
void ScintillaEditBase::leaveEvent(QEvent *event)
{
QWidget::leaveEvent(event);
sqt->MouseLeave();
}
void ScintillaEditBase::contextMenuEvent(QContextMenuEvent *event) void ScintillaEditBase::contextMenuEvent(QContextMenuEvent *event)
{ {
const Point pos = PointFromQPoint(event->globalPos()); const Point pos = PointFromQPoint(event->globalPos());
@ -810,7 +816,7 @@ void ScintillaEditBase::notifyParent(NotificationData scn)
break; break;
case Notification::AutoCSelection: case Notification::AutoCSelection:
emit autoCompleteSelection(scn.lParam, QString::fromUtf8(scn.text)); emit autoCompleteSelection(scn.lParam, sqt->IsUnicodeMode() ? QString::fromUtf8(scn.text) : QString::fromLocal8Bit(scn.text));
break; break;
case Notification::AutoCCancelled: case Notification::AutoCCancelled:

View File

@ -141,6 +141,7 @@ protected:
void mouseReleaseEvent(QMouseEvent *event) override; void mouseReleaseEvent(QMouseEvent *event) override;
void mouseDoubleClickEvent(QMouseEvent *event) override; void mouseDoubleClickEvent(QMouseEvent *event) override;
void mouseMoveEvent(QMouseEvent *event) override; void mouseMoveEvent(QMouseEvent *event) override;
void leaveEvent(QEvent *event) override;
void contextMenuEvent(QContextMenuEvent *event) override; void contextMenuEvent(QContextMenuEvent *event) override;
void dragEnterEvent(QDragEnterEvent *event) override; void dragEnterEvent(QDragEnterEvent *event) override;
void dragLeaveEvent(QDragLeaveEvent *event) override; void dragLeaveEvent(QDragLeaveEvent *event) override;

View File

@ -13,7 +13,7 @@ TEMPLATE = lib
CONFIG += lib_bundle CONFIG += lib_bundle
CONFIG += c++1z CONFIG += c++1z
VERSION = 5.5.5 VERSION = 5.5.6
SOURCES += \ SOURCES += \
PlatQt.cpp \ PlatQt.cpp \

View File

@ -158,6 +158,9 @@
// win32 // win32
#include "WinTypes.h" #include "WinTypes.h"
#include "PlatWin.h" #include "PlatWin.h"
#include "ListBox.h"
#include "SurfaceGDI.h"
#include "SurfaceD2D.h"
#include "HanjaDic.h" #include "HanjaDic.h"
#include "ScintillaWin.h" #include "ScintillaWin.h"

View File

@ -605,7 +605,7 @@ class CaseConverter final : public ICaseConverter {
return character < other.character; return character < other.character;
} }
}; };
typedef std::vector<CharacterConversion> CharacterToConversion; using CharacterToConversion = std::vector<CharacterConversion>;
CharacterToConversion characterToConversion; CharacterToConversion characterToConversion;
// The parallel arrays // The parallel arrays
std::vector<int> characters; std::vector<int> characters;
@ -613,7 +613,7 @@ class CaseConverter final : public ICaseConverter {
public: public:
CaseConverter() noexcept = default; CaseConverter() noexcept = default;
bool Initialised() const noexcept { [[nodiscard]] bool Initialised() const noexcept {
return !characters.empty(); return !characters.empty();
} }
void Add(int character, std::string_view conversion_) { void Add(int character, std::string_view conversion_) {
@ -787,7 +787,7 @@ size_t CaseConvertString(char *converted, size_t sizeConverted, const char *mixe
std::string CaseConvertString(const std::string &s, CaseConversion conversion) { std::string CaseConvertString(const std::string &s, CaseConversion conversion) {
std::string retMapped(s.length() * maxExpansionCaseConversion, 0); std::string retMapped(s.length() * maxExpansionCaseConversion, 0);
const size_t lenMapped = CaseConvertString(&retMapped[0], retMapped.length(), s.c_str(), s.length(), const size_t lenMapped = CaseConvertString(retMapped.data(), retMapped.length(), s.c_str(), s.length(),
conversion); conversion);
retMapped.resize(lenMapped); retMapped.resize(lenMapped);
return retMapped; return retMapped;

View File

@ -5,6 +5,11 @@
// Copyright 2017 by Neil Hodgson <neilh@scintilla.org> // Copyright 2017 by Neil Hodgson <neilh@scintilla.org>
// The License.txt file describes the conditions under which this software may be distributed. // The License.txt file describes the conditions under which this software may be distributed.
#include <cstdint>
#include <array>
#include <map>
#include "DBCS.h" #include "DBCS.h"
using namespace Scintilla::Internal; using namespace Scintilla::Internal;
@ -15,33 +20,68 @@ bool DBCSIsLeadByte(int codePage, char ch) noexcept {
// Byte ranges found in Wikipedia articles with relevant search strings in each case // Byte ranges found in Wikipedia articles with relevant search strings in each case
const unsigned char uch = ch; const unsigned char uch = ch;
switch (codePage) { switch (codePage) {
case 932: case cp932:
// Shift_jis // Shift_jis
return ((uch >= 0x81) && (uch <= 0x9F)) || return ((uch >= 0x81) && (uch <= 0x9F)) ||
((uch >= 0xE0) && (uch <= 0xFC)); ((uch >= 0xE0) && (uch <= 0xFC));
// Lead bytes F0 to FC may be a Microsoft addition. // Lead bytes F0 to FC may be a Microsoft addition.
case 936: case cp936:
// GBK // GBK
return (uch >= 0x81) && (uch <= 0xFE); return (uch >= 0x81) && (uch <= 0xFE);
case 949: case cp949:
// Korean Wansung KS C-5601-1987 // Korean Wansung KS C-5601-1987
return (uch >= 0x81) && (uch <= 0xFE); return (uch >= 0x81) && (uch <= 0xFE);
case 950: case cp950:
// Big5 // Big5
return (uch >= 0x81) && (uch <= 0xFE); return (uch >= 0x81) && (uch <= 0xFE);
case 1361: case cp1361:
// Korean Johab KS C-5601-1992 // Korean Johab KS C-5601-1992
return return
((uch >= 0x84) && (uch <= 0xD3)) || ((uch >= 0x84) && (uch <= 0xD3)) ||
((uch >= 0xD8) && (uch <= 0xDE)) || ((uch >= 0xD8) && (uch <= 0xDE)) ||
((uch >= 0xE0) && (uch <= 0xF9)); ((uch >= 0xE0) && (uch <= 0xF9));
default:
break;
}
return false;
}
bool DBCSIsTrailByte(int codePage, char ch) noexcept {
const unsigned char trail = ch;
switch (codePage) {
case cp932:
// Shift_jis
return (trail != 0x7F) &&
((trail >= 0x40) && (trail <= 0xFC));
case cp936:
// GBK
return (trail != 0x7F) &&
((trail >= 0x40) && (trail <= 0xFE));
case cp949:
// Korean Wansung KS C-5601-1987
return
((trail >= 0x41) && (trail <= 0x5A)) ||
((trail >= 0x61) && (trail <= 0x7A)) ||
((trail >= 0x81) && (trail <= 0xFE));
case cp950:
// Big5
return
((trail >= 0x40) && (trail <= 0x7E)) ||
((trail >= 0xA1) && (trail <= 0xFE));
case cp1361:
// Korean Johab KS C-5601-1992
return
((trail >= 0x31) && (trail <= 0x7E)) ||
((trail >= 0x81) && (trail <= 0xFE));
default:
break;
} }
return false; return false;
} }
bool IsDBCSValidSingleByte(int codePage, int ch) noexcept { bool IsDBCSValidSingleByte(int codePage, int ch) noexcept {
switch (codePage) { switch (codePage) {
case 932: case cp932:
return ch == 0x80 return ch == 0x80
|| (ch >= 0xA0 && ch <= 0xDF) || (ch >= 0xA0 && ch <= 0xDF)
|| (ch >= 0xFD); || (ch >= 0xFD);
@ -51,4 +91,25 @@ bool IsDBCSValidSingleByte(int codePage, int ch) noexcept {
} }
} }
using CodePageToFoldMap = std::map<int, FoldMap>;
CodePageToFoldMap cpToFoldMap;
bool DBCSHasFoldMap(int codePage) {
const CodePageToFoldMap::const_iterator it = cpToFoldMap.find(codePage);
return it != cpToFoldMap.end();
}
void DBCSSetFoldMap(int codePage, const FoldMap &foldMap) {
cpToFoldMap[codePage] = foldMap;
}
FoldMap *DBCSGetMutableFoldMap(int codePage) {
// Constructs if needed
return &cpToFoldMap[codePage];
}
const FoldMap *DBCSGetFoldMap(int codePage) {
return &cpToFoldMap[codePage];
}
} }

View File

@ -10,17 +10,44 @@
namespace Scintilla::Internal { namespace Scintilla::Internal {
constexpr int cp932 = 932;
constexpr int cp936 = 936;
constexpr int cp949 = 949;
constexpr int cp950 = 950;
constexpr int cp1361 = 1361;
constexpr bool IsDBCSCodePage(int codePage) noexcept { constexpr bool IsDBCSCodePage(int codePage) noexcept {
return codePage == 932 return codePage == cp932
|| codePage == 936 || codePage == cp936
|| codePage == 949 || codePage == cp949
|| codePage == 950 || codePage == cp950
|| codePage == 1361; || codePage == cp1361;
} }
bool DBCSIsLeadByte(int codePage, char ch) noexcept; bool DBCSIsLeadByte(int codePage, char ch) noexcept;
bool DBCSIsTrailByte(int codePage, char ch) noexcept;
bool IsDBCSValidSingleByte(int codePage, int ch) noexcept; bool IsDBCSValidSingleByte(int codePage, int ch) noexcept;
// Calculate a number from a DBCS byte pair that can be used to index into an array or map.
// Should only be called with genuine DBCS character pairs which means that ch1 has top bit set.
constexpr uint16_t DBCSIndex(char ch1, char ch2) noexcept {
constexpr unsigned int highStart = 0x80U;
constexpr unsigned int byteMultiply = 0x100U;
const unsigned char uch1 = ch1;
const unsigned char uch2 = ch2;
return ((uch1 - highStart) * byteMultiply) + uch2;
}
struct DBCSPair {
char chars[2];
};
using FoldMap = std::array<DBCSPair, 0x8000>;
bool DBCSHasFoldMap(int codePage);
void DBCSSetFoldMap(int codePage, const FoldMap &foldMap);
FoldMap *DBCSGetMutableFoldMap(int codePage);
const FoldMap *DBCSGetFoldMap(int codePage);
} }
#endif #endif

View File

@ -620,11 +620,14 @@ void Document::ClearLevels() {
Levels()->ClearLevels(); Levels()->ClearLevels();
} }
static bool IsSubordinate(FoldLevel levelStart, FoldLevel levelTry) noexcept { namespace {
constexpr bool IsSubordinate(FoldLevel levelStart, FoldLevel levelTry) noexcept {
if (LevelIsWhitespace(levelTry)) if (LevelIsWhitespace(levelTry))
return true; return true;
else return LevelNumber(levelStart) < LevelNumber(levelTry);
return LevelNumber(levelStart) < LevelNumber(levelTry); }
} }
Sci::Line Document::GetLastChild(Sci::Line lineParent, std::optional<FoldLevel> level, Sci::Line lastLine) { Sci::Line Document::GetLastChild(Sci::Line lineParent, std::optional<FoldLevel> level, Sci::Line lastLine) {
@ -1229,8 +1232,8 @@ void DiscardEndFragment(std::string_view &text) noexcept {
} }
constexpr bool IsBaseOfGrapheme(CharacterCategory cc) { constexpr bool IsBaseOfGrapheme(CharacterCategory cc) {
// \p{L}\p{N}\p{P}\p{Sm}\p{Sc}\p{So}\p{Zs}
switch (cc) { switch (cc) {
// \p{L}\p{N}\p{P}\p{Sm}\p{Sc}\p{So}\p{Zs}
case ccLu: case ccLu:
case ccLl: case ccLl:
case ccLt: case ccLt:
@ -1250,12 +1253,16 @@ constexpr bool IsBaseOfGrapheme(CharacterCategory cc) {
case ccSc: case ccSc:
case ccSo: case ccSo:
case ccZs: case ccZs:
// Control
case ccCc:
case ccCs:
case ccCo:
return true; return true;
default: default:
// ccMn, ccMc, ccMe, // ccMn, ccMc, ccMe,
// ccSk, // ccSk,
// ccZl, ccZp, // ccZl, ccZp,
// ccCc, ccCf, ccCs, ccCo, ccCn // ccCf, ccCn
return false; return false;
} }
} }
@ -1276,6 +1283,25 @@ CharacterExtracted LastCharacter(std::string_view text) noexcept {
static_cast<unsigned int>(utf8status & UTF8MaskWidth) }; static_cast<unsigned int>(utf8status & UTF8MaskWidth) };
} }
constexpr Sci::Position NextTab(Sci::Position pos, Sci::Position tabSize) noexcept {
return ((pos / tabSize) + 1) * tabSize;
}
std::string CreateIndentation(Sci::Position indent, int tabSize, bool insertSpaces) {
std::string indentation;
if (!insertSpaces) {
while (indent >= tabSize) {
indentation += '\t';
indent -= tabSize;
}
}
while (indent > 0) {
indentation += ' ';
indent--;
}
return indentation;
}
} }
bool Scintilla::Internal::DiscardLastCombinedCharacter(std::string_view &text) noexcept { bool Scintilla::Internal::DiscardLastCombinedCharacter(std::string_view &text) noexcept {
@ -1287,6 +1313,8 @@ bool Scintilla::Internal::DiscardLastCombinedCharacter(std::string_view &text) n
// ccs-base := [\p{L}\p{N}\p{P}\p{S}\p{Zs}] // ccs-base := [\p{L}\p{N}\p{P}\p{S}\p{Zs}]
// ccs-extend := [\p{M}\p{Join_Control}] // ccs-extend := [\p{M}\p{Join_Control}]
// Modified to move Sk (Symbol Modifier) from ccs-base to ccs-extend to preserve modified emoji // Modified to move Sk (Symbol Modifier) from ccs-base to ccs-extend to preserve modified emoji
// May break before and after Control which is defined as most of ccC? but not some of ccCf and ccCn
// so treat ccCc, ccCs, ccCo as base for now.
std::string_view truncated = text; std::string_view truncated = text;
while (truncated.length() > UTF8MaxBytes) { while (truncated.length() > UTF8MaxBytes) {
@ -1702,25 +1730,6 @@ void Document::DelCharBack(Sci::Position pos) {
} }
} }
static constexpr Sci::Position NextTab(Sci::Position pos, Sci::Position tabSize) noexcept {
return ((pos / tabSize) + 1) * tabSize;
}
static std::string CreateIndentation(Sci::Position indent, int tabSize, bool insertSpaces) {
std::string indentation;
if (!insertSpaces) {
while (indent >= tabSize) {
indentation += '\t';
indent -= tabSize;
}
}
while (indent > 0) {
indentation += ' ';
indent--;
}
return indentation;
}
int SCI_METHOD Document::GetLineIndentation(Sci_Position line) { int SCI_METHOD Document::GetLineIndentation(Sci_Position line) {
int indent = 0; int indent = 0;
if ((line >= 0) && (line < LinesTotal())) { if ((line >= 0) && (line < LinesTotal())) {
@ -2364,7 +2373,7 @@ Sci::Position Document::FindText(Sci::Position minPos, Sci::Position maxPos, con
constexpr size_t maxFoldingExpansion = 4; constexpr size_t maxFoldingExpansion = 4;
std::vector<char> searchThing((lengthFind+1) * UTF8MaxBytes * maxFoldingExpansion + 1); std::vector<char> searchThing((lengthFind+1) * UTF8MaxBytes * maxFoldingExpansion + 1);
const size_t lenSearch = const size_t lenSearch =
pcf->Fold(&searchThing[0], searchThing.size(), search, lengthFind); pcf->Fold(searchThing.data(), searchThing.size(), search, lengthFind);
while (forward ? (pos < endPos) : (pos >= endPos)) { while (forward ? (pos < endPos) : (pos >= endPos)) {
int widthFirstCharacter = 1; int widthFirstCharacter = 1;
Sci::Position posIndexDocument = pos; Sci::Position posIndexDocument = pos;
@ -2397,7 +2406,7 @@ Sci::Position Document::FindText(Sci::Position minPos, Sci::Position maxPos, con
// memcmp may examine lenFlat bytes in both arguments so assert it doesn't read past end of searchThing // memcmp may examine lenFlat bytes in both arguments so assert it doesn't read past end of searchThing
assert((indexSearch + lenFlat) <= searchThing.size()); assert((indexSearch + lenFlat) <= searchThing.size());
// Does folded match the buffer // Does folded match the buffer
characterMatches = 0 == memcmp(folded, &searchThing[0] + indexSearch, lenFlat); characterMatches = 0 == memcmp(folded, searchThing.data() + indexSearch, lenFlat);
} }
if (!characterMatches) { if (!characterMatches) {
break; break;
@ -2423,7 +2432,7 @@ Sci::Position Document::FindText(Sci::Position minPos, Sci::Position maxPos, con
constexpr size_t maxBytesCharacter = 2; constexpr size_t maxBytesCharacter = 2;
constexpr size_t maxFoldingExpansion = 4; constexpr size_t maxFoldingExpansion = 4;
std::vector<char> searchThing((lengthFind+1) * maxBytesCharacter * maxFoldingExpansion + 1); std::vector<char> searchThing((lengthFind+1) * maxBytesCharacter * maxFoldingExpansion + 1);
const size_t lenSearch = pcf->Fold(&searchThing[0], searchThing.size(), search, lengthFind); const size_t lenSearch = pcf->Fold(searchThing.data(), searchThing.size(), search, lengthFind);
while (forward ? (pos < endPos) : (pos >= endPos)) { while (forward ? (pos < endPos) : (pos >= endPos)) {
int widthFirstCharacter = 0; int widthFirstCharacter = 0;
Sci::Position indexDocument = 0; Sci::Position indexDocument = 0;
@ -2452,7 +2461,7 @@ Sci::Position Document::FindText(Sci::Position minPos, Sci::Position maxPos, con
// memcmp may examine lenFlat bytes in both arguments so assert it doesn't read past end of searchThing // memcmp may examine lenFlat bytes in both arguments so assert it doesn't read past end of searchThing
assert((indexSearch + lenFlat) <= searchThing.size()); assert((indexSearch + lenFlat) <= searchThing.size());
// Does folded match the buffer // Does folded match the buffer
characterMatches = 0 == memcmp(folded, &searchThing[0] + indexSearch, lenFlat); characterMatches = 0 == memcmp(folded, searchThing.data() + indexSearch, lenFlat);
} }
if (!characterMatches) { if (!characterMatches) {
break; break;
@ -2477,7 +2486,7 @@ Sci::Position Document::FindText(Sci::Position minPos, Sci::Position maxPos, con
} else { } else {
const Sci::Position endSearch = (startPos <= endPos) ? endPos - lengthFind + 1 : endPos; const Sci::Position endSearch = (startPos <= endPos) ? endPos - lengthFind + 1 : endPos;
std::vector<char> searchThing(lengthFind + 1); std::vector<char> searchThing(lengthFind + 1);
pcf->Fold(&searchThing[0], searchThing.size(), search, lengthFind); pcf->Fold(searchThing.data(), searchThing.size(), search, lengthFind);
while (forward ? (pos < endSearch) : (pos >= endSearch)) { while (forward ? (pos < endSearch) : (pos >= endSearch)) {
bool found = (pos + lengthFind) <= limitPos; bool found = (pos + lengthFind) <= limitPos;
for (int indexSearch = 0; (indexSearch < lengthFind) && found; indexSearch++) { for (int indexSearch = 0; (indexSearch < lengthFind) && found; indexSearch++) {
@ -2975,7 +2984,9 @@ Sci::Position Document::ExtendStyleRange(Sci::Position pos, int delta, bool sing
return pos; return pos;
} }
static char BraceOpposite(char ch) noexcept { namespace {
constexpr char BraceOpposite(char ch) noexcept {
switch (ch) { switch (ch) {
case '(': case '(':
return ')'; return ')';
@ -2998,6 +3009,8 @@ static char BraceOpposite(char ch) noexcept {
} }
} }
}
// TODO: should be able to extend styled region to find matching brace // TODO: should be able to extend styled region to find matching brace
Sci::Position Document::BraceMatch(Sci::Position position, Sci::Position /*maxReStyle*/, Sci::Position startPos, bool useStartPos) noexcept { Sci::Position Document::BraceMatch(Sci::Position position, Sci::Position /*maxReStyle*/, Sci::Position startPos, bool useStartPos) noexcept {
const unsigned char chBrace = CharAt(position); const unsigned char chBrace = CharAt(position);
@ -3075,7 +3088,7 @@ public:
lineRangeEnd = doc->SciLineFromPosition(endPos); lineRangeEnd = doc->SciLineFromPosition(endPos);
lineRangeBreak = lineRangeEnd + increment; lineRangeBreak = lineRangeEnd + increment;
} }
Range LineRange(Sci::Line line, Sci::Position lineStartPos, Sci::Position lineEndPos) const noexcept { [[nodiscard]] Range LineRange(Sci::Line line, Sci::Position lineStartPos, Sci::Position lineEndPos) const noexcept {
Range range(lineStartPos, lineEndPos); Range range(lineStartPos, lineEndPos);
if (increment == 1) { if (increment == 1) {
if (line == lineRangeStart) if (line == lineRangeStart)
@ -3101,13 +3114,13 @@ public:
pdoc(pdoc_), end(end_) { pdoc(pdoc_), end(end_) {
} }
char CharAt(Sci::Position index) const noexcept override { [[nodiscard]] char CharAt(Sci::Position index) const noexcept override {
if (index < 0 || index >= end) if (index < 0 || index >= end)
return 0; return 0;
else else
return pdoc->CharAt(index); return pdoc->CharAt(index);
} }
Sci::Position MovePositionOutsideChar(Sci::Position pos, Sci::Position moveDir) const noexcept override { [[nodiscard]] Sci::Position MovePositionOutsideChar(Sci::Position pos, Sci::Position moveDir) const noexcept override {
return pdoc->MovePositionOutsideChar(pos, moveDir, false); return pdoc->MovePositionOutsideChar(pos, moveDir, false);
} }
}; };
@ -3150,10 +3163,10 @@ public:
bool operator!=(const ByteIterator &other) const noexcept { bool operator!=(const ByteIterator &other) const noexcept {
return doc != other.doc || position != other.position; return doc != other.doc || position != other.position;
} }
Sci::Position Pos() const noexcept { [[nodiscard]] Sci::Position Pos() const noexcept {
return position; return position;
} }
Sci::Position PosRoundUp() const noexcept { [[nodiscard]] Sci::Position PosRoundUp() const noexcept {
return position; return position;
} }
}; };
@ -3178,11 +3191,11 @@ class UTF8Iterator {
// These 3 fields determine the iterator position and are used for comparisons // These 3 fields determine the iterator position and are used for comparisons
const Document *doc; const Document *doc;
Sci::Position position; Sci::Position position;
size_t characterIndex; size_t characterIndex = 0;
// Remaining fields are derived from the determining fields so are excluded in comparisons // Remaining fields are derived from the determining fields so are excluded in comparisons
unsigned int lenBytes; unsigned int lenBytes = 0;
size_t lenCharacters; size_t lenCharacters = 0;
wchar_t buffered[2]; wchar_t buffered[2]{};
public: public:
using iterator_category = std::bidirectional_iterator_tag; using iterator_category = std::bidirectional_iterator_tag;
using value_type = wchar_t; using value_type = wchar_t;
@ -3191,9 +3204,7 @@ public:
using reference = wchar_t&; using reference = wchar_t&;
explicit UTF8Iterator(const Document *doc_=nullptr, Sci::Position position_=0) noexcept : explicit UTF8Iterator(const Document *doc_=nullptr, Sci::Position position_=0) noexcept :
doc(doc_), position(position_), characterIndex(0), lenBytes(0), lenCharacters(0), buffered{} { doc(doc_), position(position_) {
buffered[0] = 0;
buffered[1] = 0;
if (doc) { if (doc) {
ReadCharacter(); ReadCharacter();
} }
@ -3245,10 +3256,10 @@ public:
position != other.position || position != other.position ||
characterIndex != other.characterIndex; characterIndex != other.characterIndex;
} }
Sci::Position Pos() const noexcept { [[nodiscard]] Sci::Position Pos() const noexcept {
return position; return position;
} }
Sci::Position PosRoundUp() const noexcept { [[nodiscard]] Sci::Position PosRoundUp() const noexcept {
if (characterIndex) if (characterIndex)
return position + lenBytes; // Force to end of character return position + lenBytes; // Force to end of character
else else

View File

@ -106,15 +106,7 @@ constexpr bool IsLastStep(const DocModification &mh) noexcept {
&& ((mh.modificationType & finalMask) == finalMask); && ((mh.modificationType & finalMask) == finalMask);
} }
} constexpr bool IsAllSpacesOrTabs(std::string_view sv) noexcept {
Timer::Timer() noexcept :
ticking(false), ticksToWait(0), tickerID{} {}
Idler::Idler() noexcept :
state(false), idlerID(nullptr) {}
static constexpr bool IsAllSpacesOrTabs(std::string_view sv) noexcept {
for (const char ch : sv) { for (const char ch : sv) {
// This is safe because IsSpaceOrTab() will return false for null terminators // This is safe because IsSpaceOrTab() will return false for null terminators
if (!IsSpaceOrTab(ch)) if (!IsSpaceOrTab(ch))
@ -123,6 +115,14 @@ static constexpr bool IsAllSpacesOrTabs(std::string_view sv) noexcept {
return true; return true;
} }
}
Timer::Timer() noexcept :
ticking(false), ticksToWait(0), tickerID{} {}
Idler::Idler() noexcept :
state(false), idlerID(nullptr) {}
Editor::Editor() : durationWrapOneByte(0.000001, 0.00000001, 0.00001) { Editor::Editor() : durationWrapOneByte(0.000001, 0.00000001, 0.00001) {
ctrlID = 0; ctrlID = 0;
@ -3143,7 +3143,7 @@ void Editor::ChangeCaseOfSelection(CaseMapping caseMapping) {
SelectionRange currentNoVS = current; SelectionRange currentNoVS = current;
currentNoVS.ClearVirtualSpace(); currentNoVS.ClearVirtualSpace();
const size_t rangeBytes = currentNoVS.Length(); const size_t rangeBytes = currentNoVS.Length();
if (rangeBytes > 0) { if (rangeBytes > 0 && !RangeContainsProtected(currentNoVS)) {
std::string sText = RangeText(currentNoVS.Start().Position(), currentNoVS.End().Position()); std::string sText = RangeText(currentNoVS.Start().Position(), currentNoVS.End().Position());
std::string sMapped = CaseMapString(sText, caseMapping); std::string sMapped = CaseMapString(sText, caseMapping);
@ -4402,7 +4402,9 @@ void Editor::GoToLine(Sci::Line lineNo) {
EnsureCaretVisible(); EnsureCaretVisible();
} }
static bool Close(Point pt1, Point pt2, Point threshold) noexcept { namespace {
bool Close(Point pt1, Point pt2, Point threshold) noexcept {
const Point ptDifference = pt2 - pt1; const Point ptDifference = pt2 - pt1;
if (std::abs(ptDifference.x) > threshold.x) if (std::abs(ptDifference.x) > threshold.x)
return false; return false;
@ -4411,6 +4413,12 @@ static bool Close(Point pt1, Point pt2, Point threshold) noexcept {
return true; return true;
} }
constexpr bool AllowVirtualSpace(VirtualSpace virtualSpaceOptions, bool rectangular) noexcept {
return FlagSet(virtualSpaceOptions, (rectangular ? VirtualSpace::RectangularSelection : VirtualSpace::UserAccessible));
}
}
std::string Editor::RangeText(Sci::Position start, Sci::Position end) const { std::string Editor::RangeText(Sci::Position start, Sci::Position end) const {
if (start < end) { if (start < end) {
const Sci::Position len = end - start; const Sci::Position len = end - start;
@ -4756,10 +4764,6 @@ void Editor::MouseLeave() {
} }
} }
static constexpr bool AllowVirtualSpace(VirtualSpace virtualSpaceOptions, bool rectangular) noexcept {
return FlagSet(virtualSpaceOptions, (rectangular ? VirtualSpace::RectangularSelection : VirtualSpace::UserAccessible));
}
void Editor::ButtonDownWithModifiers(Point pt, unsigned int curTime, KeyMod modifiers) { void Editor::ButtonDownWithModifiers(Point pt, unsigned int curTime, KeyMod modifiers) {
SetHoverIndicatorPoint(pt); SetHoverIndicatorPoint(pt);
//Platform::DebugPrintf("ButtonDown %d %d = %d alt=%d %d\n", curTime, lastClickTime, curTime - lastClickTime, alt, inDragDrop); //Platform::DebugPrintf("ButtonDown %d %d = %d alt=%d %d\n", curTime, lastClickTime, curTime - lastClickTime, alt, inDragDrop);

View File

@ -15,16 +15,83 @@
namespace Scintilla::Internal { namespace Scintilla::Internal {
namespace {
constexpr int first2Byte = 0x80;
constexpr int first3Byte = 0x800;
constexpr unsigned int maskSurrogate = 0x3FF;
constexpr unsigned int shiftSurrogate = 10;
// The top 2 bits are 0b10 to indicate a trail byte.
// The lower 6 bits contain the value.
constexpr unsigned int trailByteFlag = 0b1000'0000;
constexpr unsigned int trailByteMask = 0b0011'1111;
// With each UTF-8 length the bits are divided into length indicator
// bits and value bits separated by a 0 bit.
constexpr unsigned int leadByte2 = 0b1100'0000;
constexpr unsigned int leadBits2 = 0b0001'1111;
constexpr unsigned int leadByte3 = 0b1110'0000;
constexpr unsigned int leadBits3 = 0b0000'1111;
constexpr unsigned int leadByte4 = 0b1111'0000;
constexpr unsigned int leadBits4 = 0b0000'0111;
constexpr unsigned int shiftByte2 = 6;
constexpr unsigned int shiftByte3 = 12;
constexpr unsigned int shiftByte4 = 18;
constexpr char LeadByte(unsigned int lengthValue, unsigned int uch) noexcept {
return static_cast<unsigned char>(lengthValue | uch);
}
constexpr char TrailByte(unsigned int uch) noexcept {
return trailByteFlag | (uch & trailByteMask);
}
constexpr unsigned char TrailByteValue(unsigned char c) noexcept {
return c & trailByteMask;
}
constexpr wchar_t SurrogateLead(int val) noexcept {
return static_cast<wchar_t>(((val - SUPPLEMENTAL_PLANE_FIRST) >> shiftSurrogate) + SURROGATE_LEAD_FIRST);
}
constexpr wchar_t SurrogateTrail(int val) noexcept {
return (val & maskSurrogate) + SURROGATE_TRAIL_FIRST;
}
void UTF8AppendCharacter(int uch, char *putf, size_t &k) noexcept {
if (uch < first2Byte) {
putf[k++] = LeadByte(0, uch);
} else if (uch < first3Byte) {
putf[k++] = LeadByte(leadByte2, uch >> shiftByte2);
putf[k++] = TrailByte(uch);
} else if (uch < SUPPLEMENTAL_PLANE_FIRST) {
putf[k++] = LeadByte(leadByte3, uch >> shiftByte3);
putf[k++] = TrailByte(uch >> shiftByte2);
putf[k++] = TrailByte(uch);
} else {
putf[k++] = LeadByte(leadByte4, uch >> shiftByte4);
putf[k++] = TrailByte(uch >> shiftByte3);
putf[k++] = TrailByte(uch >> shiftByte2);
putf[k++] = TrailByte(uch);
}
}
}
size_t UTF8Length(std::wstring_view wsv) noexcept { size_t UTF8Length(std::wstring_view wsv) noexcept {
size_t len = 0; size_t len = 0;
for (size_t i = 0; i < wsv.length() && wsv[i];) { for (size_t i = 0; i < wsv.length() && wsv[i];) {
const unsigned int uch = wsv[i]; const wchar_t uch = wsv[i];
if (uch < 0x80) { if (uch < first2Byte) {
len++; len++;
} else if (uch < 0x800) { } else if (uch < first3Byte) {
len += 2; len += 2;
} else if ((uch >= SURROGATE_LEAD_FIRST) && } else if (IsSurrogate(uch)) {
(uch <= SURROGATE_TRAIL_LAST)) {
len += 4; len += 4;
i++; i++;
} else { } else {
@ -50,26 +117,13 @@ size_t UTF8PositionFromUTF16Position(std::string_view u8Text, size_t positionUTF
void UTF8FromUTF16(std::wstring_view wsv, char *putf, size_t len) noexcept { void UTF8FromUTF16(std::wstring_view wsv, char *putf, size_t len) noexcept {
size_t k = 0; size_t k = 0;
for (size_t i = 0; i < wsv.length() && wsv[i];) { for (size_t i = 0; i < wsv.length() && wsv[i];) {
const unsigned int uch = wsv[i]; unsigned int uch = wsv[i];
if (uch < 0x80) { if (IsSurrogate(wsv[i])) {
putf[k++] = static_cast<char>(uch);
} else if (uch < 0x800) {
putf[k++] = static_cast<char>(0xC0 | (uch >> 6));
putf[k++] = static_cast<char>(0x80 | (uch & 0x3f));
} else if ((uch >= SURROGATE_LEAD_FIRST) &&
(uch <= SURROGATE_TRAIL_LAST)) {
// Half a surrogate pair // Half a surrogate pair
i++; i++;
const unsigned int xch = 0x10000 + ((uch & 0x3ff) << 10) + (wsv[i] & 0x3ff); uch = SUPPLEMENTAL_PLANE_FIRST + ((uch & maskSurrogate) << shiftSurrogate) + (wsv[i] & maskSurrogate);
putf[k++] = static_cast<char>(0xF0 | (xch >> 18));
putf[k++] = static_cast<char>(0x80 | ((xch >> 12) & 0x3f));
putf[k++] = static_cast<char>(0x80 | ((xch >> 6) & 0x3f));
putf[k++] = static_cast<char>(0x80 | (xch & 0x3f));
} else {
putf[k++] = static_cast<char>(0xE0 | (uch >> 12));
putf[k++] = static_cast<char>(0x80 | ((uch >> 6) & 0x3f));
putf[k++] = static_cast<char>(0x80 | (uch & 0x3f));
} }
UTF8AppendCharacter(uch, putf, k);
i++; i++;
} }
if (k < len) if (k < len)
@ -78,21 +132,7 @@ void UTF8FromUTF16(std::wstring_view wsv, char *putf, size_t len) noexcept {
void UTF8FromUTF32Character(int uch, char *putf) noexcept { void UTF8FromUTF32Character(int uch, char *putf) noexcept {
size_t k = 0; size_t k = 0;
if (uch < 0x80) { UTF8AppendCharacter(uch, putf, k);
putf[k++] = static_cast<char>(uch);
} else if (uch < 0x800) {
putf[k++] = static_cast<char>(0xC0 | (uch >> 6));
putf[k++] = static_cast<char>(0x80 | (uch & 0x3f));
} else if (uch < 0x10000) {
putf[k++] = static_cast<char>(0xE0 | (uch >> 12));
putf[k++] = static_cast<char>(0x80 | ((uch >> 6) & 0x3f));
putf[k++] = static_cast<char>(0x80 | (uch & 0x3f));
} else {
putf[k++] = static_cast<char>(0xF0 | (uch >> 18));
putf[k++] = static_cast<char>(0x80 | ((uch >> 12) & 0x3f));
putf[k++] = static_cast<char>(0x80 | ((uch >> 6) & 0x3f));
putf[k++] = static_cast<char>(0x80 | (uch & 0x3f));
}
putf[k] = '\0'; putf[k] = '\0';
} }
@ -108,18 +148,12 @@ size_t UTF16Length(std::string_view svu8) noexcept {
return ulen; return ulen;
} }
constexpr unsigned char TrailByteValue(unsigned char c) {
// The top 2 bits are 0b10 to indicate a trail byte.
// The lower 6 bits contain the value.
return c & 0b0011'1111;
}
size_t UTF16FromUTF8(std::string_view svu8, wchar_t *tbuf, size_t tlen) { size_t UTF16FromUTF8(std::string_view svu8, wchar_t *tbuf, size_t tlen) {
size_t ui = 0; size_t ui = 0;
for (size_t i = 0; i < svu8.length();) { for (size_t i = 0; i < svu8.length();) {
unsigned char ch = svu8[i]; unsigned char ch = svu8[i];
const unsigned int byteCount = UTF8BytesOfLead[ch]; const unsigned int byteCount = UTF8BytesOfLead[ch];
unsigned int value; unsigned int value = 0;
if (i + byteCount > svu8.length()) { if (i + byteCount > svu8.length()) {
// Trying to read past end but still have space to write // Trying to read past end but still have space to write
@ -141,31 +175,31 @@ size_t UTF16FromUTF8(std::string_view svu8, wchar_t *tbuf, size_t tlen) {
tbuf[ui] = ch; tbuf[ui] = ch;
break; break;
case 2: case 2:
value = (ch & 0x1F) << 6; value = (ch & leadBits2) << shiftByte2;
ch = svu8[i++]; ch = svu8[i++];
value += TrailByteValue(ch); value += TrailByteValue(ch);
tbuf[ui] = static_cast<wchar_t>(value); tbuf[ui] = static_cast<wchar_t>(value);
break; break;
case 3: case 3:
value = (ch & 0xF) << 12; value = (ch & leadBits3) << shiftByte3;
ch = svu8[i++]; ch = svu8[i++];
value += (TrailByteValue(ch) << 6); value += (TrailByteValue(ch) << shiftByte2);
ch = svu8[i++]; ch = svu8[i++];
value += TrailByteValue(ch); value += TrailByteValue(ch);
tbuf[ui] = static_cast<wchar_t>(value); tbuf[ui] = static_cast<wchar_t>(value);
break; break;
default: default:
// Outside the BMP so need two surrogates // Outside the BMP so need two surrogates
value = (ch & 0x7) << 18; value = (ch & leadBits4) << shiftByte4;
ch = svu8[i++]; ch = svu8[i++];
value += TrailByteValue(ch) << 12; value += TrailByteValue(ch) << shiftByte3;
ch = svu8[i++]; ch = svu8[i++];
value += TrailByteValue(ch) << 6; value += TrailByteValue(ch) << shiftByte2;
ch = svu8[i++]; ch = svu8[i++];
value += TrailByteValue(ch); value += TrailByteValue(ch);
tbuf[ui] = static_cast<wchar_t>(((value - 0x10000) >> 10) + SURROGATE_LEAD_FIRST); tbuf[ui] = SurrogateLead(value);
ui++; ui++;
tbuf[ui] = static_cast<wchar_t>((value & 0x3ff) + SURROGATE_TRAIL_FIRST); tbuf[ui] = SurrogateTrail(value);
break; break;
} }
ui++; ui++;
@ -189,7 +223,7 @@ size_t UTF32FromUTF8(std::string_view svu8, unsigned int *tbuf, size_t tlen) {
for (size_t i = 0; i < svu8.length();) { for (size_t i = 0; i < svu8.length();) {
unsigned char ch = svu8[i]; unsigned char ch = svu8[i];
const unsigned int byteCount = UTF8BytesOfLead[ch]; const unsigned int byteCount = UTF8BytesOfLead[ch];
unsigned int value; unsigned int value = 0;
if (i + byteCount > svu8.length()) { if (i + byteCount > svu8.length()) {
// Trying to read past end but still have space to write // Trying to read past end but still have space to write
@ -210,23 +244,23 @@ size_t UTF32FromUTF8(std::string_view svu8, unsigned int *tbuf, size_t tlen) {
value = ch; value = ch;
break; break;
case 2: case 2:
value = (ch & 0x1F) << 6; value = (ch & leadBits2) << shiftByte2;
ch = svu8[i++]; ch = svu8[i++];
value += TrailByteValue(ch); value += TrailByteValue(ch);
break; break;
case 3: case 3:
value = (ch & 0xF) << 12; value = (ch & leadBits3) << shiftByte3;
ch = svu8[i++]; ch = svu8[i++];
value += TrailByteValue(ch) << 6; value += TrailByteValue(ch) << shiftByte2;
ch = svu8[i++]; ch = svu8[i++];
value += TrailByteValue(ch); value += TrailByteValue(ch);
break; break;
default: default:
value = (ch & 0x7) << 18; value = (ch & leadBits4) << shiftByte4;
ch = svu8[i++]; ch = svu8[i++];
value += TrailByteValue(ch) << 12; value += TrailByteValue(ch) << shiftByte3;
ch = svu8[i++]; ch = svu8[i++];
value += TrailByteValue(ch) << 6; value += TrailByteValue(ch) << shiftByte2;
ch = svu8[i++]; ch = svu8[i++];
value += TrailByteValue(ch); value += TrailByteValue(ch);
break; break;
@ -256,8 +290,8 @@ unsigned int UTF16FromUTF32Character(unsigned int val, wchar_t *tbuf) noexcept {
tbuf[0] = static_cast<wchar_t>(val); tbuf[0] = static_cast<wchar_t>(val);
return 1; return 1;
} }
tbuf[0] = static_cast<wchar_t>(((val - SUPPLEMENTAL_PLANE_FIRST) >> 10) + SURROGATE_LEAD_FIRST); tbuf[0] = SurrogateLead(val);
tbuf[1] = static_cast<wchar_t>((val & 0x3ff) + SURROGATE_TRAIL_FIRST); tbuf[1] = SurrogateTrail(val);
return 2; return 2;
} }

View File

@ -99,8 +99,12 @@ enum { SURROGATE_TRAIL_FIRST = 0xDC00 };
enum { SURROGATE_TRAIL_LAST = 0xDFFF }; enum { SURROGATE_TRAIL_LAST = 0xDFFF };
enum { SUPPLEMENTAL_PLANE_FIRST = 0x10000 }; enum { SUPPLEMENTAL_PLANE_FIRST = 0x10000 };
constexpr bool IsSurrogate(wchar_t uch) noexcept {
return (uch >= SURROGATE_LEAD_FIRST) && (uch <= SURROGATE_TRAIL_LAST);
}
constexpr unsigned int UTF16CharLength(wchar_t uch) noexcept { constexpr unsigned int UTF16CharLength(wchar_t uch) noexcept {
return ((uch >= SURROGATE_LEAD_FIRST) && (uch <= SURROGATE_LEAD_LAST)) ? 2 : 1; return IsSurrogate(uch) ? 2 : 1;
} }
constexpr unsigned int UTF16LengthFromUTF8ByteCount(unsigned int byteCount) noexcept { constexpr unsigned int UTF16LengthFromUTF8ByteCount(unsigned int byteCount) noexcept {

View File

@ -145,5 +145,27 @@ class TestPerformance(unittest.TestCase):
print("%6.3f testUTF8AsciiSearches" % duration) print("%6.3f testUTF8AsciiSearches" % duration)
self.xite.DoEvents() self.xite.DoEvents()
def testShiftJISSearches(self):
self.ed.SetCodePage(932)
self.ed.StyleSetCharacterSet(32, 128)
oneLine = "Fold Margin=折りたたみ表示用の余白(&F)\n".encode('cp932')
manyLines = oneLine * 100000
manyLines = manyLines + "φ\n".encode('cp932')
#~ with open("932.txt", "wb") as fp:
#~ fp.write(manyLines)
self.ed.AddText(len(manyLines), manyLines)
searchString = "φ".encode('cp932')
start = timer()
for i in range(20):
self.ed.TargetStart = 0
self.ed.TargetEnd = self.ed.Length-1
self.ed.SearchFlags = 0
pos = self.ed.SearchInTarget(len(searchString), searchString)
self.assertTrue(pos > 0)
end = timer()
duration = end - start
print("%6.3f testShiftJISSearches" % duration)
self.xite.DoEvents()
if __name__ == '__main__': if __name__ == '__main__':
Xite.main("performanceTests") Xite.main("performanceTests")

View File

@ -1042,6 +1042,137 @@ TEST_CASE("SafeSegment") {
} }
} }
TEST_CASE("DiscardLastCombinedCharacter") {
SECTION("Short") {
const std::string_view base = "12345";
// Short strings (up to 4 bytes) aren't changed to avoid null and problematic results
for (size_t len = 0; len < 5; len++) {
std::string_view text = base.substr(0, len);
REQUIRE(text.length() == len);
const bool changed = DiscardLastCombinedCharacter(text);
REQUIRE(!changed);
REQUIRE(text.length() == len);
}
}
SECTION("ASCII") {
std::string_view text = "12345";
REQUIRE(text.length() == 5);
const bool changed = DiscardLastCombinedCharacter(text);
REQUIRE(changed);
REQUIRE(text.length() == 4);
}
SECTION("Control") {
{
std::string_view text = "12345\007";
REQUIRE(text.length() == 6);
const bool changed = DiscardLastCombinedCharacter(text);
REQUIRE(changed);
REQUIRE(text.length() == 5);
}
{
std::string_view text = "12345\007Z";
REQUIRE(text.length() == 7);
const bool changed = DiscardLastCombinedCharacter(text);
REQUIRE(changed);
REQUIRE(text.length() == 6);
}
}
SECTION("Japanese") {
std::string_view text = "Japanese\xe6\x97\xa5\xe6\x9c\xac\xe8\xaa\x9e";
REQUIRE(text.length() == 17);
const bool changed = DiscardLastCombinedCharacter(text);
REQUIRE(changed);
REQUIRE(text.length() == 14);
}
SECTION("Thai Combining") {
// Ends with two combined characters
// 7 characters, 5 base characters and 2 combining
// HO HIP, SARA AA, KHO KHAI, MAI THO, O ANG, MO MA, SARA UU
std::string_view text = "\xe0\xb8\xab\xe0\xb8\xb2\xe0\xb8\x82\xe0\xb9\x89\xe0\xb8\xad\xe0\xb8\xa1\xe0\xb8\xb9";
REQUIRE(text.length() == 21);
const bool changed = DiscardLastCombinedCharacter(text);
REQUIRE(changed);
// Discarded 2 x 3-byte characters
REQUIRE(text.length() == 15);
}
SECTION("Invalid UTF-8") {
{
// Ends with isolated lead byte
std::string_view text = "1234\xe0";
REQUIRE(text.length() == 5);
const bool changed = DiscardLastCombinedCharacter(text);
REQUIRE(changed);
// Discarded final invalid byte
REQUIRE(text.length() == 4);
}
{
// Ends with isolated trail byte
std::string_view text = "1234\xb8";
REQUIRE(text.length() == 5);
const bool changed = DiscardLastCombinedCharacter(text);
REQUIRE(changed);
// Discarded final invalid byte
REQUIRE(text.length() == 4);
}
{
// Ends with lead byte and only one of two required trail bytes
std::string_view text = "1234\xe0\xb8";
REQUIRE(text.length() == 6);
const bool changed = DiscardLastCombinedCharacter(text);
REQUIRE(changed);
// Discarded final invalid byte
REQUIRE(text.length() == 5);
}
}
SECTION("Private Use UTF-8") {
{
// Ends with private use area U+F8FF - Apple uses for apple symbol.
std::string_view text = "1234\xEF\xA3\xBF";
REQUIRE(text.length() == 7);
const bool changed = DiscardLastCombinedCharacter(text);
REQUIRE(changed);
// Discarded whole final character
REQUIRE(text.length() == 4);
}
{
// At end: PUA + letter: PUA acts as base
std::string_view text = "1234\xEF\xA3\xBFZ";
REQUIRE(text.length() == 8);
const bool changed = DiscardLastCombinedCharacter(text);
REQUIRE(changed);
// Discarded just final character
REQUIRE(text.length() == 7);
}
}
SECTION("Surrogates") {
{
// Ends with surrogate U+D800.
std::string_view text = "1234\xED\xA0\x80";
REQUIRE(text.length() == 7);
const bool changed = DiscardLastCombinedCharacter(text);
REQUIRE(changed);
// Discarded final invalid byte
REQUIRE(text.length() == 6);
}
{
// Ends with surrogate U+DC00.
std::string_view text = "1234\xED\xB0\x80";
REQUIRE(text.length() == 7);
const bool changed = DiscardLastCombinedCharacter(text);
REQUIRE(changed);
// Discarded final invalid byte
REQUIRE(text.length() == 6);
}
}
}
TEST_CASE("PerLine") { TEST_CASE("PerLine") {
SECTION("LineMarkers") { SECTION("LineMarkers") {
DocPlus doc("1\n2\n", CpUtf8); DocPlus doc("1\n2\n", CpUtf8);

View File

@ -121,6 +121,9 @@ TEST_CASE("UniConversion") {
const size_t tlen = UTF16FromUTF8(s, tbuf, 1); const size_t tlen = UTF16FromUTF8(s, tbuf, 1);
REQUIRE(tlen == 1U); REQUIRE(tlen == 1U);
REQUIRE(tbuf[0] == 'a'); REQUIRE(tbuf[0] == 'a');
char back[4]{};
UTF8FromUTF16(std::wstring_view(tbuf, tlen), back, sizeof(back));
REQUIRE(strcmp(s, back) == 0);
} }
SECTION("UTF16FromUTF8 Example1") { SECTION("UTF16FromUTF8 Example1") {
@ -129,6 +132,9 @@ TEST_CASE("UniConversion") {
const size_t tlen = UTF16FromUTF8(s, tbuf, 1); const size_t tlen = UTF16FromUTF8(s, tbuf, 1);
REQUIRE(tlen == 1U); REQUIRE(tlen == 1U);
REQUIRE(tbuf[0] == 0x24); REQUIRE(tbuf[0] == 0x24);
char back[4]{};
UTF8FromUTF16(std::wstring_view(tbuf, tlen), back, sizeof(back));
REQUIRE(strcmp(s, back) == 0);
} }
SECTION("UTF16FromUTF8 Example2") { SECTION("UTF16FromUTF8 Example2") {
@ -137,6 +143,9 @@ TEST_CASE("UniConversion") {
const size_t tlen = UTF16FromUTF8(s, tbuf, 1); const size_t tlen = UTF16FromUTF8(s, tbuf, 1);
REQUIRE(tlen == 1U); REQUIRE(tlen == 1U);
REQUIRE(tbuf[0] == 0xA2); REQUIRE(tbuf[0] == 0xA2);
char back[4]{};
UTF8FromUTF16(std::wstring_view(tbuf, tlen), back, sizeof(back));
REQUIRE(strcmp(s, back) == 0);
} }
SECTION("UTF16FromUTF8 Example3") { SECTION("UTF16FromUTF8 Example3") {
@ -145,6 +154,9 @@ TEST_CASE("UniConversion") {
const size_t tlen = UTF16FromUTF8(s, tbuf, 1);; const size_t tlen = UTF16FromUTF8(s, tbuf, 1);;
REQUIRE(tlen == 1U); REQUIRE(tlen == 1U);
REQUIRE(tbuf[0] == 0x20AC); REQUIRE(tbuf[0] == 0x20AC);
char back[4]{};
UTF8FromUTF16(std::wstring_view(tbuf, tlen), back, sizeof(back));
REQUIRE(strcmp(s, back) == 0);
} }
SECTION("UTF16FromUTF8 Example4") { SECTION("UTF16FromUTF8 Example4") {
@ -154,6 +166,9 @@ TEST_CASE("UniConversion") {
REQUIRE(tlen == 2U); REQUIRE(tlen == 2U);
REQUIRE(tbuf[0] == 0xD800); REQUIRE(tbuf[0] == 0xD800);
REQUIRE(tbuf[1] == 0xDF48); REQUIRE(tbuf[1] == 0xDF48);
char back[5]{};
UTF8FromUTF16(std::wstring_view(tbuf, tlen), back, sizeof(back));
REQUIRE(strcmp(s, back) == 0);
} }
SECTION("UTF16FromUTF8 Invalid Trail byte in lead position") { SECTION("UTF16FromUTF8 Invalid Trail byte in lead position") {
@ -165,6 +180,7 @@ TEST_CASE("UniConversion") {
REQUIRE(tbuf[1] == 0xB5); REQUIRE(tbuf[1] == 0xB5);
REQUIRE(tbuf[2] == 'y'); REQUIRE(tbuf[2] == 'y');
REQUIRE(tbuf[3] == 'z'); REQUIRE(tbuf[3] == 'z');
// Invalid so can't round trip
} }
SECTION("UTF16FromUTF8 Invalid Lead byte at end") { SECTION("UTF16FromUTF8 Invalid Lead byte at end") {
@ -174,6 +190,7 @@ TEST_CASE("UniConversion") {
REQUIRE(tlen == 2U); REQUIRE(tlen == 2U);
REQUIRE(tbuf[0] == 'a'); REQUIRE(tbuf[0] == 'a');
REQUIRE(tbuf[1] == 0xC2); REQUIRE(tbuf[1] == 0xC2);
// Invalid so can't round trip
} }
SECTION("UTF16FromUTF8 Invalid Lead byte implies 3 trails but only 2") { SECTION("UTF16FromUTF8 Invalid Lead byte implies 3 trails but only 2") {
@ -183,6 +200,7 @@ TEST_CASE("UniConversion") {
REQUIRE(tlen == 2U); REQUIRE(tlen == 2U);
REQUIRE(tbuf[0] == 'a'); REQUIRE(tbuf[0] == 'a');
REQUIRE(tbuf[1] == 0xF1); REQUIRE(tbuf[1] == 0xF1);
// Invalid so can't round trip
} }
// UTF32FromUTF8 // UTF32FromUTF8
@ -193,6 +211,9 @@ TEST_CASE("UniConversion") {
const size_t tlen = UTF32FromUTF8(s, tbuf, 1); const size_t tlen = UTF32FromUTF8(s, tbuf, 1);
REQUIRE(tlen == 1U); REQUIRE(tlen == 1U);
REQUIRE(tbuf[0] == static_cast<unsigned int>('a')); REQUIRE(tbuf[0] == static_cast<unsigned int>('a'));
char back[5]{};
UTF8FromUTF32Character(tbuf[0], back);
REQUIRE(strcmp(s, back) == 0);
} }
SECTION("UTF32FromUTF8 Example1") { SECTION("UTF32FromUTF8 Example1") {
@ -201,6 +222,9 @@ TEST_CASE("UniConversion") {
const size_t tlen = UTF32FromUTF8(s, tbuf, 1); const size_t tlen = UTF32FromUTF8(s, tbuf, 1);
REQUIRE(tlen == 1U); REQUIRE(tlen == 1U);
REQUIRE(tbuf[0] == 0x24); REQUIRE(tbuf[0] == 0x24);
char back[5]{};
UTF8FromUTF32Character(tbuf[0], back);
REQUIRE(strcmp(s, back) == 0);
} }
SECTION("UTF32FromUTF8 Example2") { SECTION("UTF32FromUTF8 Example2") {
@ -209,6 +233,9 @@ TEST_CASE("UniConversion") {
const size_t tlen = UTF32FromUTF8(s, tbuf, 1); const size_t tlen = UTF32FromUTF8(s, tbuf, 1);
REQUIRE(tlen == 1U); REQUIRE(tlen == 1U);
REQUIRE(tbuf[0] == 0xA2); REQUIRE(tbuf[0] == 0xA2);
char back[5]{};
UTF8FromUTF32Character(tbuf[0], back);
REQUIRE(strcmp(s, back) == 0);
} }
SECTION("UTF32FromUTF8 Example3") { SECTION("UTF32FromUTF8 Example3") {
@ -217,6 +244,9 @@ TEST_CASE("UniConversion") {
const size_t tlen = UTF32FromUTF8(s, tbuf, 1); const size_t tlen = UTF32FromUTF8(s, tbuf, 1);
REQUIRE(tlen == 1U); REQUIRE(tlen == 1U);
REQUIRE(tbuf[0] == 0x20AC); REQUIRE(tbuf[0] == 0x20AC);
char back[5]{};
UTF8FromUTF32Character(tbuf[0], back);
REQUIRE(strcmp(s, back) == 0);
} }
SECTION("UTF32FromUTF8 Example4") { SECTION("UTF32FromUTF8 Example4") {
@ -225,6 +255,9 @@ TEST_CASE("UniConversion") {
const size_t tlen = UTF32FromUTF8(s, tbuf, 1); const size_t tlen = UTF32FromUTF8(s, tbuf, 1);
REQUIRE(tlen == 1U); REQUIRE(tlen == 1U);
REQUIRE(tbuf[0] == 0x10348); REQUIRE(tbuf[0] == 0x10348);
char back[5]{};
UTF8FromUTF32Character(tbuf[0], back);
REQUIRE(strcmp(s, back) == 0);
} }
SECTION("UTF32FromUTF8 Invalid Trail byte in lead position") { SECTION("UTF32FromUTF8 Invalid Trail byte in lead position") {

View File

@ -1 +1 @@
555 556

988
scintilla/win32/ListBox.cxx Normal file
View File

@ -0,0 +1,988 @@
// Scintilla source code edit control
/** @file ListBox.cxx
** Implementation of list box on Windows.
**/
// Copyright 1998-2003 by Neil Hodgson <neilh@scintilla.org>
// The License.txt file describes the conditions under which this software may be distributed.
#include <cstddef>
#include <cstdlib>
#include <cstdint>
#include <cstring>
#include <cstdio>
#include <cstdarg>
#include <ctime>
#include <cmath>
#include <climits>
#include <string_view>
#include <vector>
#include <map>
#include <optional>
#include <algorithm>
#include <iterator>
#include <memory>
#include <mutex>
// Want to use std::min and std::max so don't want Windows.h version of min and max
#if !defined(NOMINMAX)
#define NOMINMAX
#endif
#undef _WIN32_WINNT
#define _WIN32_WINNT 0x0A00
#undef WINVER
#define WINVER 0x0A00
#define WIN32_LEAN_AND_MEAN 1
#include <windows.h>
#include <commctrl.h>
#include <richedit.h>
#include <windowsx.h>
#include <shellscalingapi.h>
#include <wrl.h>
using Microsoft::WRL::ComPtr;
#if !defined(DISABLE_D2D)
#define USE_D2D 1
#endif
#if defined(USE_D2D)
#include <d2d1_1.h>
#include <d3d11_1.h>
#include <dwrite_1.h>
#endif
#include "ScintillaTypes.h"
#include "Debugging.h"
#include "Geometry.h"
#include "Platform.h"
#include "XPM.h"
#include "UniConversion.h"
#include "DBCS.h"
#include "WinTypes.h"
#include "PlatWin.h"
#include "ListBox.h"
#if defined(USE_D2D)
#include "SurfaceD2D.h"
#endif
using namespace Scintilla;
using namespace Scintilla::Internal;
namespace {
struct ListItemData {
const char *text;
int pixId;
};
class LineToItem {
std::vector<char> words;
std::vector<ListItemData> data;
public:
void Clear() noexcept {
words.clear();
data.clear();
}
[[nodiscard]] ListItemData Get(size_t index) const noexcept {
if (index < data.size()) {
return data[index];
}
ListItemData missing = {"", -1};
return missing;
}
[[nodiscard]] int Count() const noexcept {
return static_cast<int>(data.size());
}
void AllocItem(const char *text, int pixId) {
const ListItemData lid = { text, pixId };
data.push_back(lid);
}
char *SetWords(const char *s) {
words = std::vector<char>(s, s+strlen(s)+1);
return words.data();
}
};
const TCHAR ListBoxX_ClassName[] = TEXT("ListBoxX");
ColourRGBA ColourElement(std::optional<ColourRGBA> colour, int nIndex) {
if (colour.has_value()) {
return colour.value();
}
return ColourFromSys(nIndex);
}
struct LBGraphics {
GDIBitMap bm;
std::unique_ptr<Surface> pixmapLine;
#if defined(USE_D2D)
DCRenderTarget pBMDCTarget;
#endif
void Release() noexcept {
pixmapLine.reset();
#if defined(USE_D2D)
pBMDCTarget = nullptr;
#endif
bm.Release();
}
};
}
class ListBoxX : public ListBox {
int lineHeight = 10;
HFONT fontCopy {};
std::unique_ptr<FontWin> fontWin;
Technology technology = Technology::Default;
RGBAImageSet images;
LineToItem lti;
HWND lb {};
bool unicodeMode = false;
int codePage = 0;
int desiredVisibleRows = 9;
int maxItemCharacters = 0;
unsigned int aveCharWidth = 8;
Window *parent = nullptr;
int ctrlID = 0;
UINT dpi = USER_DEFAULT_SCREEN_DPI;
IListBoxDelegate *delegate = nullptr;
unsigned int maxCharWidth = 1;
WPARAM resizeHit = 0;
PRectangle rcPreSize;
Point dragOffset;
Point location; // Caret location at which the list is opened
MouseWheelDelta wheelDelta;
ListOptions options;
DWORD frameStyle = WS_THICKFRAME;
LBGraphics graphics;
HWND GetHWND() const noexcept;
void AppendListItem(const char *text, const char *numword);
void AdjustWindowRect(PRectangle *rc, UINT dpiAdjust) const noexcept;
int ItemHeight() const noexcept;
int MinClientWidth() const noexcept;
int TextOffset() const noexcept;
POINT GetClientExtent() const noexcept;
POINT MinTrackSize() const noexcept;
POINT MaxTrackSize() const noexcept;
void SetRedraw(bool on) noexcept;
void OnDoubleClick();
void OnSelChange();
void ResizeToCursor();
void StartResize(WPARAM);
LRESULT NcHitTest(WPARAM, LPARAM) const;
void CentreItem(int n);
void AllocateBitMap();
static LRESULT PASCAL 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
static constexpr POINT ImageInset {1, 0}; // Padding around image
public:
ListBoxX() = default;
ListBoxX(const ListBoxX &) = delete;
ListBoxX(ListBoxX &&) = delete;
ListBoxX &operator=(const ListBoxX &) = delete;
ListBoxX &operator=(ListBoxX &&) = delete;
~ListBoxX() noexcept override {
if (fontCopy) {
::DeleteObject(fontCopy);
fontCopy = {};
}
graphics.Release();
}
void SetFont(const Font *font) override;
void Create(Window &parent_, int ctrlID_, Point location_, int lineHeight_, bool unicodeMode_, Technology technology_) override;
void SetAverageCharWidth(int width) override;
void SetVisibleRows(int rows) override;
int GetVisibleRows() const override;
PRectangle GetDesiredRect() override;
int CaretFromEdge() override;
void Clear() noexcept override;
void Append(char *s, int type) override;
int Length() override;
void Select(int n) override;
int GetSelection() override;
int Find(const char *prefix) override;
std::string GetValue(int n) override;
void RegisterImage(int type, const char *xpm_data) override;
void RegisterRGBAImage(int type, int width, int height, const unsigned char *pixelsImage) override;
void ClearRegisteredImages() override;
void SetDelegate(IListBoxDelegate *lbDelegate) override;
void SetList(const char *list, char separator, char typesep) override;
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);
};
std::unique_ptr<ListBox> ListBox::Allocate() {
return std::make_unique<ListBoxX>();
}
void ListBoxX::Create(Window &parent_, int ctrlID_, Point location_, int lineHeight_, bool unicodeMode_, Technology technology_) {
parent = &parent_;
ctrlID = ctrlID_;
location = location_;
lineHeight = lineHeight_;
unicodeMode = unicodeMode_;
codePage = unicodeMode ? CpUtf8 : 0;
technology = technology_;
HWND hwndParent = HwndFromWindow(*parent);
HINSTANCE hinstanceParent = GetWindowInstance(hwndParent);
// Window created as popup so not clipped within parent client area
wid = ::CreateWindowEx(
WS_EX_WINDOWEDGE, ListBoxX_ClassName, TEXT(""),
WS_POPUP | frameStyle,
100,100, 150,80, hwndParent,
{},
hinstanceParent,
this);
dpi = DpiForWindow(hwndParent);
POINT locationw = POINTFromPoint(location);
::MapWindowPoints(hwndParent, {}, &locationw, 1);
location = PointFromPOINT(locationw);
}
void ListBoxX::SetFont(const Font *font) {
const FontWin *pfm = dynamic_cast<const FontWin *>(font);
if (pfm) {
if (fontCopy) {
::DeleteObject(fontCopy);
fontCopy = {};
}
fontCopy = pfm->HFont();
SetWindowFont(lb, fontCopy, 0);
fontWin = pfm->Duplicate();
codePage = unicodeMode ? CpUtf8 : CodePageFromCharSet(fontWin->GetCharacterSet(), 1252);
graphics.Release();
}
}
void ListBoxX::SetAverageCharWidth(int width) {
aveCharWidth = width;
}
void ListBoxX::SetVisibleRows(int rows) {
desiredVisibleRows = rows;
}
int ListBoxX::GetVisibleRows() const {
return desiredVisibleRows;
}
HWND ListBoxX::GetHWND() const noexcept {
return HwndFromWindowID(GetID());
}
PRectangle ListBoxX::GetDesiredRect() {
PRectangle rcDesired = GetPosition();
int rows = Length();
if ((rows == 0) || (rows > desiredVisibleRows))
rows = desiredVisibleRows;
rcDesired.bottom = rcDesired.top + ItemHeight() * rows;
int width = MinClientWidth();
int textSize = 0;
int averageCharWidth = 8;
// Make a measuring surface
std::unique_ptr<Surface> surfaceItem(Surface::Allocate(technology));
surfaceItem->Init(GetID());
surfaceItem->SetMode(SurfaceMode(codePage, false));
// Find the widest item in pixels
const int items = lti.Count();
for (int i = 0; i < items; i++) {
const ListItemData item = lti.Get(i);
const int itemTextSize = static_cast<int>(std::ceil(
surfaceItem->WidthText(fontWin.get(), item.text)));
textSize = std::max(textSize, itemTextSize);
}
maxCharWidth = static_cast<int>(std::ceil(surfaceItem->WidthText(fontWin.get(), "W")));
averageCharWidth = static_cast<int>(surfaceItem->AverageCharWidth(fontWin.get()));
width = std::max({ width, textSize, (maxItemCharacters + 1) * averageCharWidth });
rcDesired.right = rcDesired.left + TextOffset() + width + (TextInset.x * 2);
if (Length() > rows)
rcDesired.right += SystemMetricsForDpi(SM_CXVSCROLL, dpi);
AdjustWindowRect(&rcDesired, dpi);
return rcDesired;
}
int ListBoxX::TextOffset() const noexcept {
const int pixWidth = images.GetWidth();
return pixWidth == 0 ? ItemInset.x : ItemInset.x + pixWidth + (ImageInset.x * 2);
}
int ListBoxX::CaretFromEdge() {
PRectangle rc;
AdjustWindowRect(&rc, dpi);
return TextOffset() + static_cast<int>(TextInset.x + (0 - rc.left) - 1);
}
void ListBoxX::Clear() noexcept {
ListBox_ResetContent(lb);
maxItemCharacters = 0;
lti.Clear();
}
void ListBoxX::Append(char *, int) {
// This method is no longer called in Scintilla
PLATFORM_ASSERT(false);
}
int ListBoxX::Length() {
return lti.Count();
}
void ListBoxX::Select(int n) {
// We are going to scroll to centre on the new selection and then select it, so disable
// redraw to avoid flicker caused by a painting new selection twice in unselected and then
// selected states
SetRedraw(false);
CentreItem(n);
ListBox_SetCurSel(lb, n);
OnSelChange();
SetRedraw(true);
}
int ListBoxX::GetSelection() {
return ListBox_GetCurSel(lb);
}
// This is not actually called at present
int ListBoxX::Find(const char *) {
return LB_ERR;
}
std::string ListBoxX::GetValue(int n) {
const ListItemData item = lti.Get(n);
return item.text;
}
void ListBoxX::RegisterImage(int type, const char *xpm_data) {
XPM xpmImage(xpm_data);
images.AddImage(type, std::make_unique<RGBAImage>(xpmImage));
}
void ListBoxX::RegisterRGBAImage(int type, int width, int height, const unsigned char *pixelsImage) {
images.AddImage(type, std::make_unique<RGBAImage>(width, height, 1.0f, pixelsImage));
}
void ListBoxX::ClearRegisteredImages() {
images.Clear();
}
void ListBoxX::Draw(DRAWITEMSTRUCT *pDrawItem) {
if ((pDrawItem->itemAction != ODA_SELECT) && (pDrawItem->itemAction != ODA_DRAWENTIRE)) {
return;
}
if (!graphics.pixmapLine) {
AllocateBitMap();
if (!graphics.pixmapLine) {
// Failed to allocate, so release fully and give up
graphics.Release();
return;
}
}
#if defined(USE_D2D)
if (graphics.pBMDCTarget) {
graphics.pBMDCTarget->BeginDraw();
}
#endif
const PRectangle rcItemBase = PRectangleFromRECT(pDrawItem->rcItem);
const PRectangle rcItem(0, 0, rcItemBase.Width(), rcItemBase.Height());
PRectangle rcBox = rcItem;
rcBox.left += TextOffset();
ColourRGBA colourFore;
ColourRGBA colourBack;
if (pDrawItem->itemState & ODS_SELECTED) {
PRectangle rcImage = rcItem;
rcImage.right = rcBox.left;
// The image is not highlighted
graphics.pixmapLine->FillRectangle(rcImage, ColourElement(options.back, COLOR_WINDOW));
colourBack = ColourElement(options.backSelected, COLOR_HIGHLIGHT);
graphics.pixmapLine->FillRectangle(rcBox, colourBack);
colourFore = ColourElement(options.foreSelected, COLOR_HIGHLIGHTTEXT);
} else {
colourBack = ColourElement(options.back, COLOR_WINDOW);
graphics.pixmapLine->FillRectangle(rcItem, colourBack);
colourFore = ColourElement(options.fore, COLOR_WINDOWTEXT);
}
const ListItemData item = lti.Get(pDrawItem->itemID);
const int pixId = item.pixId;
const char *text = item.text;
const PRectangle rcText = rcBox.Inset(Point(TextInset.x, TextInset.y));
const double ascent = graphics.pixmapLine->Ascent(fontWin.get());
graphics.pixmapLine->DrawTextClipped(rcText, fontWin.get(), rcText.top + ascent, text, colourFore, colourBack);
// Draw the image, if any
const RGBAImage *pimage = images.Get(pixId);
if (pimage) {
const XYPOSITION left = rcItem.left + ItemInset.x + ImageInset.x;
PRectangle rcImage = rcItem;
rcImage.left = left;
rcImage.right = rcImage.left + images.GetWidth();
graphics.pixmapLine->DrawRGBAImage(rcImage,
pimage->GetWidth(), pimage->GetHeight(), pimage->Pixels());
}
#if defined(USE_D2D)
if (graphics.pBMDCTarget) {
const HRESULT hrEnd = graphics.pBMDCTarget->EndDraw();
if (FAILED(hrEnd)) {
return;
}
}
#endif
// 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);
}
void ListBoxX::AppendListItem(const char *text, const char *numword) {
int pixId = -1;
if (numword) {
pixId = 0;
char ch;
while ((ch = *++numword) != '\0') {
pixId = 10 * pixId + (ch - '0');
}
}
lti.AllocItem(text, pixId);
maxItemCharacters = std::max(maxItemCharacters, static_cast<int>(strlen(text)));
}
void ListBoxX::SetDelegate(IListBoxDelegate *lbDelegate) {
delegate = lbDelegate;
}
void ListBoxX::SetList(const char *list, char separator, char typesep) {
// Turn off redraw while populating the list - this has a significant effect, even if
// the listbox is not visible.
SetRedraw(false);
Clear();
const size_t size = strlen(list);
char *words = lti.SetWords(list);
const char *startword = words;
char *numword = nullptr;
for (size_t i=0; i < size; i++) {
if (words[i] == separator) {
words[i] = '\0';
if (numword)
*numword = '\0';
AppendListItem(startword, numword);
startword = words + i + 1;
numword = nullptr;
} else if (words[i] == typesep) {
numword = words + i;
}
}
if (startword) {
if (numword)
*numword = '\0';
AppendListItem(startword, numword);
}
// Finally populate the listbox itself with the correct number of items
const int count = lti.Count();
::SendMessage(lb, LB_INITSTORAGE, count, 0);
for (intptr_t j=0; j<count; j++) {
ListBox_AddItemData(lb, j+1);
}
SetRedraw(true);
}
void ListBoxX::SetOptions(ListOptions options_) {
options = options_;
frameStyle = FlagSet(options.options, AutoCompleteOption::FixedSize) ? WS_BORDER : WS_THICKFRAME;
}
void ListBoxX::AdjustWindowRect(PRectangle *rc, UINT dpiAdjust) const noexcept {
RECT rcw = RectFromPRectangle(*rc);
AdjustWindowRectForDpi(&rcw, frameStyle, dpiAdjust);
*rc = PRectangleFromRECT(rcw);
}
int ListBoxX::ItemHeight() const noexcept {
int itemHeight = lineHeight + (TextInset.y * 2);
const int pixHeight = images.GetHeight() + (ImageInset.y * 2);
if (itemHeight < pixHeight) {
itemHeight = pixHeight;
}
return itemHeight;
}
int ListBoxX::MinClientWidth() const noexcept {
return 12 * (aveCharWidth+aveCharWidth/3);
}
POINT ListBoxX::MinTrackSize() const noexcept {
PRectangle rc = PRectangle::FromInts(0, 0, MinClientWidth(), ItemHeight());
AdjustWindowRect(&rc, dpi);
POINT ret = {static_cast<LONG>(rc.Width()), static_cast<LONG>(rc.Height())};
return ret;
}
POINT ListBoxX::MaxTrackSize() const noexcept {
PRectangle rc = PRectangle::FromInts(0, 0,
std::max<int>(static_cast<unsigned int>(MinClientWidth()),
maxCharWidth * maxItemCharacters + TextInset.x * 2 +
TextOffset() + SystemMetricsForDpi(SM_CXVSCROLL, dpi)),
ItemHeight() * lti.Count());
AdjustWindowRect(&rc, dpi);
POINT ret = {static_cast<LONG>(rc.Width()), static_cast<LONG>(rc.Height())};
return ret;
}
void ListBoxX::SetRedraw(bool on) noexcept {
::SendMessage(lb, WM_SETREDRAW, on, 0);
if (on) {
::RedrawWindow(lb, {}, {}, RDW_ERASE | RDW_FRAME | RDW_INVALIDATE | RDW_ALLCHILDREN);
}
}
void ListBoxX::ResizeToCursor() {
PRectangle rc = GetPosition();
POINT ptw;
::GetCursorPos(&ptw);
const Point pt = PointFromPOINT(ptw) + dragOffset;
switch (resizeHit) {
case HTLEFT:
rc.left = pt.x;
break;
case HTRIGHT:
rc.right = pt.x;
break;
case HTTOP:
rc.top = pt.y;
break;
case HTTOPLEFT:
rc.top = pt.y;
rc.left = pt.x;
break;
case HTTOPRIGHT:
rc.top = pt.y;
rc.right = pt.x;
break;
case HTBOTTOM:
rc.bottom = pt.y;
break;
case HTBOTTOMLEFT:
rc.bottom = pt.y;
rc.left = pt.x;
break;
case HTBOTTOMRIGHT:
rc.bottom = pt.y;
rc.right = pt.x;
break;
default:
break;
}
const POINT ptMin = MinTrackSize();
const POINT ptMax = MaxTrackSize();
// We don't allow the left edge to move at present, but just in case
rc.left = std::clamp(rc.left, rcPreSize.right - ptMax.x, rcPreSize.right - ptMin.x);
rc.top = std::clamp(rc.top, rcPreSize.bottom - ptMax.y, rcPreSize.bottom - ptMin.y);
rc.right = std::clamp(rc.right, rcPreSize.left + ptMin.x, rcPreSize.left + ptMax.x);
rc.bottom = std::clamp(rc.bottom, rcPreSize.top + ptMin.y, rcPreSize.top + ptMax.y);
SetPosition(rc);
}
void ListBoxX::StartResize(WPARAM hitCode) {
rcPreSize = GetPosition();
POINT cursorPos;
::GetCursorPos(&cursorPos);
switch (hitCode) {
case HTRIGHT:
case HTBOTTOM:
case HTBOTTOMRIGHT:
dragOffset.x = rcPreSize.right - cursorPos.x;
dragOffset.y = rcPreSize.bottom - cursorPos.y;
break;
case HTTOPRIGHT:
dragOffset.x = rcPreSize.right - cursorPos.x;
dragOffset.y = rcPreSize.top - cursorPos.y;
break;
// Note that the current hit test code prevents the left edge cases ever firing
// as we don't want the left edge to be movable
case HTLEFT:
case HTTOP:
case HTTOPLEFT:
dragOffset.x = rcPreSize.left - cursorPos.x;
dragOffset.y = rcPreSize.top - cursorPos.y;
break;
case HTBOTTOMLEFT:
dragOffset.x = rcPreSize.left - cursorPos.x;
dragOffset.y = rcPreSize.bottom - cursorPos.y;
break;
default:
return;
}
::SetCapture(GetHWND());
resizeHit = hitCode;
}
LRESULT ListBoxX::NcHitTest(WPARAM wParam, LPARAM lParam) const {
const PRectangle rc = GetPosition();
LRESULT hit = ::DefWindowProc(GetHWND(), WM_NCHITTEST, wParam, lParam);
// There is an apparent bug in the DefWindowProc hit test code whereby it will
// return HTTOPXXX if the window in question is shorter than the default
// window caption height + frame, even if one is hovering over the bottom edge of
// the frame, so workaround that here
if (hit >= HTTOP && hit <= HTTOPRIGHT) {
const int minHeight = SystemMetricsForDpi(SM_CYMINTRACK, dpi);
const int yPos = GET_Y_LPARAM(lParam);
if ((rc.Height() < minHeight) && (yPos > ((rc.top + rc.bottom)/2))) {
hit += HTBOTTOM - HTTOP;
}
}
// Never permit resizing that moves the left edge. Allow movement of top or bottom edge
// depending on whether the list is above or below the caret
switch (hit) {
case HTLEFT:
case HTTOPLEFT:
case HTBOTTOMLEFT:
hit = HTERROR;
break;
case HTTOP:
case HTTOPRIGHT: {
// Valid only if caret below list
if (location.y < rc.top)
hit = HTERROR;
}
break;
case HTBOTTOM:
case HTBOTTOMRIGHT: {
// Valid only if caret above list
if (rc.bottom <= location.y)
hit = HTERROR;
}
break;
default:
break;
}
return hit;
}
void ListBoxX::OnDoubleClick() {
if (delegate) {
ListBoxEvent event(ListBoxEvent::EventType::doubleClick);
delegate->ListNotify(&event);
}
}
void ListBoxX::OnSelChange() {
if (delegate) {
ListBoxEvent event(ListBoxEvent::EventType::selectionChange);
delegate->ListNotify(&event);
}
}
POINT ListBoxX::GetClientExtent() const noexcept {
RECT rc;
::GetWindowRect(HwndFromWindowID(wid), &rc);
POINT ret { rc.right - rc.left, rc.bottom - rc.top };
return ret;
}
void ListBoxX::CentreItem(int n) {
// If below mid point, scroll up to centre, but with more items below if uneven
if (n >= 0) {
const POINT extent = GetClientExtent();
const int visible = extent.y/ItemHeight();
if (visible < Length()) {
const int top = ListBox_GetTopIndex(lb);
const int half = (visible - 1) / 2;
if (n > (top + half))
ListBox_SetTopIndex(lb, n - half);
}
}
}
void ListBoxX::AllocateBitMap() {
const SIZE extent { GetClientExtent().x, lineHeight };
graphics.bm.Create({}, extent.cx, -extent.cy, nullptr);
if (!graphics.bm) {
return;
}
// Make a surface
graphics.pixmapLine = Surface::Allocate(technology);
graphics.pixmapLine->SetMode(SurfaceMode(codePage, false));
#if defined(USE_D2D)
if (technology != Technology::Default) {
if (!LoadD2D()) {
return;
}
const D2D1_RENDER_TARGET_PROPERTIES drtp = D2D1::RenderTargetProperties(
D2D1_RENDER_TARGET_TYPE_DEFAULT,
{ DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_PREMULTIPLIED });
HRESULT hr = CreateDCRenderTarget(&drtp, graphics.pBMDCTarget);
if (FAILED(hr) || !graphics.pBMDCTarget) {
return;
}
const RECT rcExtent = { 0, 0, extent.cx, extent.cy };
hr = graphics.pBMDCTarget->BindDC(graphics.bm.DC(), &rcExtent);
if (SUCCEEDED(hr)) {
graphics.pixmapLine->Init(graphics.pBMDCTarget.Get(), GetID());
}
return;
}
#endif
// Either technology == Technology::Default or USE_D2D turned off
graphics.pixmapLine->Init(graphics.bm.DC(), GetID());
}
LRESULT PASCAL ListBoxX::ControlWndProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam) {
try {
ListBoxX *lbx = static_cast<ListBoxX *>(PointerFromWindow(::GetParent(hWnd)));
switch (iMessage) {
case WM_ERASEBKGND:
return TRUE;
case WM_MOUSEACTIVATE:
// This prevents the view activating when the scrollbar is clicked
return MA_NOACTIVATE;
case WM_LBUTTONDOWN: {
// We must take control of selection to prevent the ListBox activating
// the popup
const LRESULT lResult = ::SendMessage(hWnd, LB_ITEMFROMPOINT, 0, lParam);
if (HIWORD(lResult) == 0) {
ListBox_SetCurSel(hWnd, LOWORD(lResult));
if (lbx) {
lbx->OnSelChange();
}
}
}
return 0;
case WM_LBUTTONUP:
return 0;
case WM_LBUTTONDBLCLK: {
if (lbx) {
lbx->OnDoubleClick();
}
}
return 0;
case WM_MBUTTONDOWN:
// disable the scroll wheel button click action
return 0;
default:
break;
}
WNDPROC prevWndProc = reinterpret_cast<WNDPROC>(GetWindowLongPtr(hWnd, GWLP_USERDATA));
if (prevWndProc) {
return ::CallWindowProc(prevWndProc, hWnd, iMessage, wParam, lParam);
}
return ::DefWindowProc(hWnd, iMessage, wParam, lParam);
} catch (...) {
}
return ::DefWindowProc(hWnd, iMessage, wParam, lParam);
}
LRESULT ListBoxX::WndProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam) {
switch (iMessage) {
case WM_CREATE: {
HINSTANCE hinstanceParent = GetWindowInstance(HwndFromWindow(*parent));
// Note that LBS_NOINTEGRALHEIGHT is specified to fix cosmetic issue when resizing the list
// but has useful side effect of speeding up list population significantly
lb = ::CreateWindowEx(
0, TEXT("listbox"), TEXT(""),
WS_CHILD | WS_VSCROLL | WS_VISIBLE |
LBS_OWNERDRAWFIXED | LBS_NODATA | LBS_NOINTEGRALHEIGHT,
0, 0, 150,80, hWnd,
reinterpret_cast<HMENU>(static_cast<ptrdiff_t>(ctrlID)),
hinstanceParent,
nullptr);
WNDPROC prevWndProc = SubclassWindow(lb, ControlWndProc);
::SetWindowLongPtr(lb, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(prevWndProc));
}
break;
case WM_SIZE:
if (lb) {
graphics.Release(); // Bitmap must be reallocated to new size.
SetRedraw(false);
::SetWindowPos(lb, {}, 0, 0, LOWORD(lParam), HIWORD(lParam), SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOMOVE);
// Ensure the selection remains visible
CentreItem(GetSelection());
SetRedraw(true);
}
break;
case WM_PAINT: {
Painter painter(hWnd);
}
break;
case WM_COMMAND:
// This is not actually needed now - the registered double click action is used
// directly to action a choice from the list.
::SendMessage(HwndFromWindow(*parent), iMessage, wParam, lParam);
break;
case WM_MEASUREITEM: {
MEASUREITEMSTRUCT *pMeasureItem = reinterpret_cast<MEASUREITEMSTRUCT *>(lParam);
pMeasureItem->itemHeight = ItemHeight();
}
break;
case WM_DRAWITEM:
Draw(reinterpret_cast<DRAWITEMSTRUCT *>(lParam));
break;
case WM_DESTROY:
lb = {};
SetWindowPointer(hWnd, nullptr);
return ::DefWindowProc(hWnd, iMessage, wParam, lParam);
case WM_ERASEBKGND:
// To reduce flicker we can elide background erasure since this window is
// completely covered by its child.
return TRUE;
case WM_GETMINMAXINFO: {
MINMAXINFO *minMax = reinterpret_cast<MINMAXINFO*>(lParam);
minMax->ptMaxTrackSize = MaxTrackSize();
minMax->ptMinTrackSize = MinTrackSize();
}
break;
case WM_MOUSEACTIVATE:
return MA_NOACTIVATE;
case WM_NCHITTEST:
return NcHitTest(wParam, lParam);
case WM_NCLBUTTONDOWN:
// We have to implement our own window resizing because the DefWindowProc
// implementation insists on activating the resized window
StartResize(wParam);
return 0;
case WM_MOUSEMOVE: {
if (resizeHit == 0) {
return ::DefWindowProc(hWnd, iMessage, wParam, lParam);
}
ResizeToCursor();
}
break;
case WM_LBUTTONUP:
case WM_CANCELMODE:
if (resizeHit != 0) {
resizeHit = 0;
::ReleaseCapture();
}
return ::DefWindowProc(hWnd, iMessage, wParam, lParam);
case WM_MOUSEWHEEL:
if (wheelDelta.Accumulate(wParam)) {
const int nRows = GetVisibleRows();
int linesToScroll = std::clamp(nRows - 1, 1, 3);
linesToScroll *= wheelDelta.Actions();
int top = ListBox_GetTopIndex(lb) + linesToScroll;
if (top < 0) {
top = 0;
}
ListBox_SetTopIndex(lb, top);
}
break;
default:
return ::DefWindowProc(hWnd, iMessage, wParam, lParam);
}
return 0;
}
LRESULT PASCAL ListBoxX::StaticWndProc(
HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam) {
if (iMessage == WM_CREATE) {
CREATESTRUCT *pCreate = reinterpret_cast<CREATESTRUCT *>(lParam);
SetWindowPointer(hWnd, pCreate->lpCreateParams);
}
// Find C++ object associated with window.
ListBoxX *lbx = static_cast<ListBoxX *>(PointerFromWindow(hWnd));
if (lbx) {
return lbx->WndProc(hWnd, iMessage, wParam, lParam);
}
return ::DefWindowProc(hWnd, iMessage, wParam, lParam);
}
namespace Scintilla::Internal {
bool ListBoxX_Register() noexcept {
WNDCLASSEX wndclassc {};
wndclassc.cbSize = sizeof(wndclassc);
// We need CS_HREDRAW and CS_VREDRAW because of the ellipsis that might be drawn for
// truncated items in the list and the appearance/disappearance of the vertical scroll bar.
// The list repaint is double-buffered to avoid the flicker this would otherwise cause.
wndclassc.style = CS_GLOBALCLASS | CS_HREDRAW | CS_VREDRAW;
wndclassc.cbWndExtra = sizeof(ListBoxX *);
wndclassc.hInstance = hinstPlatformRes;
wndclassc.lpfnWndProc = ListBoxX::StaticWndProc;
wndclassc.hCursor = ::LoadCursor({}, IDC_ARROW);
wndclassc.lpszClassName = ListBoxX_ClassName;
return ::RegisterClassEx(&wndclassc) != 0;
}
void ListBoxX_Unregister() noexcept {
if (hinstPlatformRes) {
::UnregisterClass(ListBoxX_ClassName, hinstPlatformRes);
}
}
ListBox::ListBox() noexcept = default;
ListBox::~ListBox() noexcept = default;
}

18
scintilla/win32/ListBox.h Normal file
View File

@ -0,0 +1,18 @@
// Scintilla source code edit control
/** @file ListBox.h
** Definitions for list box on Windows.
**/
// Copyright 2025 by Neil Hodgson <neilh@scintilla.org>
// The License.txt file describes the conditions under which this software may be distributed.
#ifndef LISTBOX_H
#define LISTBOX_H
namespace Scintilla::Internal {
bool ListBoxX_Register() noexcept;
void ListBoxX_Unregister() noexcept;
}
#endif

File diff suppressed because it is too large Load Diff

View File

@ -13,6 +13,10 @@ namespace Scintilla::Internal {
#ifndef USER_DEFAULT_SCREEN_DPI #ifndef USER_DEFAULT_SCREEN_DPI
#define USER_DEFAULT_SCREEN_DPI 96 #define USER_DEFAULT_SCREEN_DPI 96
#endif #endif
constexpr FLOAT dpiDefault = USER_DEFAULT_SCREEN_DPI;
// Used for defining font size with LOGFONT
constexpr int pointsPerInch = 72;
extern void Platform_Initialise(void *hInstance) noexcept; extern void Platform_Initialise(void *hInstance) noexcept;
@ -24,6 +28,10 @@ constexpr RECT RectFromPRectangle(PRectangle prc) noexcept {
return rc; return rc;
} }
constexpr PRectangle PRectangleFromRECT(RECT rc) noexcept {
return PRectangle::FromInts(rc.left, rc.top, rc.right, rc.bottom);
}
constexpr POINT POINTFromPoint(Point pt) noexcept { constexpr POINT POINTFromPoint(Point pt) noexcept {
return POINT{ static_cast<LONG>(pt.x), static_cast<LONG>(pt.y) }; return POINT{ static_cast<LONG>(pt.x), static_cast<LONG>(pt.y) };
} }
@ -36,6 +44,8 @@ constexpr SIZE SizeOfRect(RECT rc) noexcept {
return { rc.right - rc.left, rc.bottom - rc.top }; return { rc.right - rc.left, rc.bottom - rc.top };
} }
ColourRGBA ColourFromSys(int nIndex) noexcept;
constexpr HWND HwndFromWindowID(WindowID wid) noexcept { constexpr HWND HwndFromWindowID(WindowID wid) noexcept {
return static_cast<HWND>(wid); return static_cast<HWND>(wid);
} }
@ -44,6 +54,10 @@ inline HWND HwndFromWindow(const Window &w) noexcept {
return HwndFromWindowID(w.GetID()); return HwndFromWindowID(w.GetID());
} }
extern HINSTANCE hinstPlatformRes;
UINT CodePageFromCharSet(CharacterSet characterSet, UINT documentCodePage) noexcept;
void *PointerFromWindow(HWND hWnd) noexcept; void *PointerFromWindow(HWND hWnd) noexcept;
void SetWindowPointer(HWND hWnd, void *ptr) noexcept; void SetWindowPointer(HWND hWnd, void *ptr) noexcept;
@ -54,8 +68,22 @@ float GetDeviceScaleFactorWhenGdiScalingActive(HWND hWnd) noexcept;
int SystemMetricsForDpi(int nIndex, UINT dpi) noexcept; int SystemMetricsForDpi(int nIndex, UINT dpi) noexcept;
void AdjustWindowRectForDpi(LPRECT lpRect, DWORD dwStyle, UINT dpi) noexcept;
HCURSOR LoadReverseArrowCursor(UINT dpi) noexcept; HCURSOR LoadReverseArrowCursor(UINT dpi) noexcept;
// Encapsulate WM_PAINT handling so that EndPaint is always called even with unexpected returns or exceptions.
struct Painter {
HWND hWnd{};
PAINTSTRUCT ps{};
explicit Painter(HWND hWnd_) noexcept : hWnd(hWnd_) {
::BeginPaint(hWnd, &ps);
}
~Painter() {
::EndPaint(hWnd, &ps);
}
};
class MouseWheelDelta { class MouseWheelDelta {
int wheelDelta = 0; int wheelDelta = 0;
public: public:
@ -70,29 +98,89 @@ public:
} }
}; };
#if defined(USE_D2D) // Both GDI and DirectWrite can produce a HFONT for use in list boxes
extern bool LoadD2D() noexcept; struct FontWin : public Font {
extern ID2D1Factory1 *pD2DFactory; [[nodiscard]] virtual HFONT HFont() const noexcept = 0;
extern IDWriteFactory1 *pIDWriteFactory; [[nodiscard]] virtual std::unique_ptr<FontWin> Duplicate() const = 0;
[[nodiscard]] virtual CharacterSet GetCharacterSet() const noexcept = 0;
using DCRenderTarget = ComPtr<ID2D1DCRenderTarget>;
using D3D11Device = ComPtr<ID3D11Device1>;
HRESULT CreateDCRenderTarget(const D2D1_RENDER_TARGET_PROPERTIES *renderTargetProperties, DCRenderTarget &dcRT) noexcept;
extern HRESULT CreateD3D(D3D11Device &device) noexcept;
using WriteRenderingParams = ComPtr<IDWriteRenderingParams1>;
struct RenderingParams {
WriteRenderingParams defaultRenderingParams;
WriteRenderingParams customRenderingParams;
}; };
struct ISetRenderingParams { // Buffer to hold strings and string position arrays without always allocating on heap.
virtual void SetRenderingParams(std::shared_ptr<RenderingParams> renderingParams_) = 0; // May sometimes have string too long to allocate on stack. So use a fixed stack-allocated buffer
// when less than safe size otherwise allocate on heap and free automatically.
template<typename T, int lengthStandard>
class VarBuffer {
T bufferStandard[lengthStandard];
public:
T *buffer;
explicit VarBuffer(size_t length) : buffer(nullptr) {
if (length > lengthStandard) {
buffer = new T[length];
} else {
buffer = bufferStandard;
}
}
// Deleted so VarBuffer objects can not be copied.
VarBuffer(const VarBuffer &) = delete;
VarBuffer(VarBuffer &&) = delete;
VarBuffer &operator=(const VarBuffer &) = delete;
VarBuffer &operator=(VarBuffer &&) = delete;
~VarBuffer() noexcept {
if (buffer != bufferStandard) {
delete[]buffer;
buffer = nullptr;
}
}
};
constexpr int stackBufferLength = 400;
class TextWide : public VarBuffer<wchar_t, stackBufferLength> {
public:
int tlen; // Using int instead of size_t as most Win32 APIs take int.
TextWide(std::string_view text, int codePage) :
VarBuffer<wchar_t, stackBufferLength>(text.length()) {
if (codePage == CpUtf8) {
tlen = static_cast<int>(UTF16FromUTF8(text, buffer, text.length()));
} else {
// Support Asian string display in 9x English
tlen = ::MultiByteToWideChar(codePage, 0, text.data(), static_cast<int>(text.length()),
buffer, static_cast<int>(text.length()));
}
}
[[nodiscard]] std::wstring_view AsView() const noexcept {
return std::wstring_view(buffer, tlen);
}
};
using TextPositions = VarBuffer<XYPOSITION, stackBufferLength>;
// Manage the lifetime of a memory HBITMAP and its HDC so there are no leaks.
class GDIBitMap {
HDC hdc{};
HBITMAP hbm{};
HBITMAP hbmOriginal{};
public:
GDIBitMap() noexcept = default;
// Deleted so GDIBitMap objects can not be copied.
GDIBitMap(const GDIBitMap &) = delete;
GDIBitMap(GDIBitMap &&) = delete;
// Move would be OK but not needed yet
GDIBitMap &operator=(const GDIBitMap &) = delete;
GDIBitMap &operator=(GDIBitMap &&) = delete;
~GDIBitMap() noexcept;
void Create(HDC hdcBase, int width, int height, DWORD **pixels) noexcept;
void Release() noexcept;
HBITMAP Extract() noexcept;
[[nodiscard]] HDC DC() const noexcept {
return hdc;
}
[[nodiscard]] explicit operator bool() const noexcept {
return hdc && hbm;
}
}; };
#endif
} }

View File

@ -4,8 +4,8 @@
#include <windows.h> #include <windows.h>
#define VERSION_SCINTILLA "5.5.5" #define VERSION_SCINTILLA "5.5.6"
#define VERSION_WORDS 5, 5, 5, 0 #define VERSION_WORDS 5, 5, 6, 0
VS_VERSION_INFO VERSIONINFO VS_VERSION_INFO VERSIONINFO
FILEVERSION VERSION_WORDS FILEVERSION VERSION_WORDS

View File

@ -195,6 +195,9 @@
<ClCompile Include="..\..\boostregex\*.cxx" /> <ClCompile Include="..\..\boostregex\*.cxx" />
<ClCompile Include="..\win32\HanjaDic.cxx" /> <ClCompile Include="..\win32\HanjaDic.cxx" />
<ClCompile Include="..\win32\PlatWin.cxx" /> <ClCompile Include="..\win32\PlatWin.cxx" />
<ClCompile Include="..\win32\ListBox.cxx" />
<ClCompile Include="..\win32\SurfaceGDI.cxx" />
<ClCompile Include="..\win32\SurfaceD2D.cxx" />
<ClCompile Include="..\win32\ScintillaWin.cxx" /> <ClCompile Include="..\win32\ScintillaWin.cxx" />
<ClCompile Include="..\win32\ScintillaDLL.cxx" /> <ClCompile Include="..\win32\ScintillaDLL.cxx" />
</ItemGroup> </ItemGroup>

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,50 @@
// Scintilla source code edit control
/** @file SurfaceD2D.h
** Definitions for drawing to Direct2D on Windows.
**/
// Copyright 1998-2011 by Neil Hodgson <neilh@scintilla.org>
// The License.txt file describes the conditions under which this software may be distributed.
#ifndef SURFACED2D_H
#define SURFACED2D_H
namespace Scintilla::Internal {
extern bool LoadD2D() noexcept;
extern void ReleaseD2D() noexcept;
extern ID2D1Factory1 *pD2DFactory;
extern IDWriteFactory1 *pIDWriteFactory;
using DCRenderTarget = ComPtr<ID2D1DCRenderTarget>;
using D3D11Device = ComPtr<ID3D11Device1>;
HRESULT CreateDCRenderTarget(const D2D1_RENDER_TARGET_PROPERTIES *renderTargetProperties, DCRenderTarget &dcRT) noexcept;
extern HRESULT CreateD3D(D3D11Device &device) noexcept;
using WriteRenderingParams = ComPtr<IDWriteRenderingParams1>;
struct RenderingParams {
WriteRenderingParams defaultRenderingParams;
WriteRenderingParams customRenderingParams;
};
struct ISetRenderingParams {
virtual void SetRenderingParams(std::shared_ptr<RenderingParams> renderingParams_) = 0;
};
using BrushSolid = ComPtr<ID2D1SolidColorBrush>;
using Geometry = ComPtr<ID2D1PathGeometry>;
using GeometrySink = ComPtr<ID2D1GeometrySink>;
using StrokeStyle = ComPtr<ID2D1StrokeStyle>;
using TextLayout = ComPtr<IDWriteTextLayout>;
BrushSolid BrushSolidCreate(ID2D1RenderTarget *pTarget, COLORREF colour) noexcept;
Geometry GeometryCreate() noexcept;
GeometrySink GeometrySinkCreate(ID2D1PathGeometry *geometry) noexcept;
StrokeStyle StrokeStyleCreate(const D2D1_STROKE_STYLE_PROPERTIES &strokeStyleProperties) noexcept;
TextLayout LayoutCreate(std::wstring_view wsv, IDWriteTextFormat *pTextFormat, FLOAT maxWidth=10000.0F, FLOAT maxHeight=1000.0F) noexcept;
}
#endif

View File

@ -0,0 +1,889 @@
// Scintilla source code edit control
/** @file SurfaceGDI.cxx
** Implementation of drawing to GDI on Windows.
**/
// Copyright 1998-2003 by Neil Hodgson <neilh@scintilla.org>
// The License.txt file describes the conditions under which this software may be distributed.
#include <cstddef>
#include <cstdlib>
#include <cstdint>
#include <cstring>
#include <cstdio>
#include <cstdarg>
#include <ctime>
#include <cmath>
#include <climits>
#include <string_view>
#include <vector>
#include <map>
#include <optional>
#include <algorithm>
#include <iterator>
#include <memory>
#include <mutex>
// Want to use std::min and std::max so don't want Windows.h version of min and max
#if !defined(NOMINMAX)
#define NOMINMAX
#endif
#undef _WIN32_WINNT
#define _WIN32_WINNT 0x0A00
#undef WINVER
#define WINVER 0x0A00
#define WIN32_LEAN_AND_MEAN 1
#include <windows.h>
#include <commctrl.h>
#include <richedit.h>
#include <windowsx.h>
#include <shellscalingapi.h>
#include "ScintillaTypes.h"
#include "Debugging.h"
#include "Geometry.h"
#include "Platform.h"
#include "XPM.h"
#include "UniConversion.h"
#include "DBCS.h"
#include "WinTypes.h"
#include "PlatWin.h"
#include "SurfaceGDI.h"
using namespace Scintilla;
using namespace Scintilla::Internal;
// All file hidden in unnamed namespace except for FontGDI_Allocate and SurfaceGDI_Allocate
namespace {
constexpr Supports SupportsGDI[] = {
Supports::PixelModification,
};
constexpr BYTE Win32MapFontQuality(FontQuality extraFontFlag) noexcept {
switch (extraFontFlag & FontQuality::QualityMask) {
case FontQuality::QualityNonAntialiased:
return NONANTIALIASED_QUALITY;
case FontQuality::QualityAntialiased:
return ANTIALIASED_QUALITY;
case FontQuality::QualityLcdOptimized:
return CLEARTYPE_QUALITY;
default:
return DEFAULT_QUALITY;
}
}
void SetLogFont(LOGFONTW &lf, const char *faceName, CharacterSet characterSet, XYPOSITION size, FontWeight weight, bool italic, FontQuality extraFontFlag) {
lf = LOGFONTW();
// The negative is to allow for leading
lf.lfHeight = -(std::abs(std::lround(size)));
lf.lfWeight = static_cast<LONG>(weight);
lf.lfItalic = italic ? 1 : 0;
lf.lfCharSet = static_cast<BYTE>(characterSet);
lf.lfQuality = Win32MapFontQuality(extraFontFlag);
UTF16FromUTF8(faceName, lf.lfFaceName, LF_FACESIZE);
}
struct FontGDI : public FontWin {
HFONT hfont = {};
CharacterSet characterSet = CharacterSet::Ansi;
FontGDI(HFONT hfont_, CharacterSet characterSet_) noexcept : hfont(hfont_), characterSet(characterSet_) {
// Takes ownership and deletes the font
}
explicit FontGDI(const FontParameters &fp) : characterSet(fp.characterSet) {
LOGFONTW lf;
SetLogFont(lf, fp.faceName, fp.characterSet, fp.size, fp.weight, fp.italic, fp.extraFontFlag);
hfont = ::CreateFontIndirectW(&lf);
}
// Deleted so FontGDI objects can not be copied.
FontGDI(const FontGDI &) = delete;
FontGDI(FontGDI &&) = delete;
FontGDI &operator=(const FontGDI &) = delete;
FontGDI &operator=(FontGDI &&) = delete;
~FontGDI() noexcept override {
if (hfont)
::DeleteObject(hfont);
}
[[nodiscard]] HFONT HFont() const noexcept override {
// Duplicating hfont
LOGFONTW lf = {};
if (0 == ::GetObjectW(hfont, sizeof(lf), &lf)) {
return {};
}
return ::CreateFontIndirectW(&lf);
}
[[nodiscard]] std::unique_ptr<FontWin> Duplicate() const override {
HFONT hfontCopy = HFont();
return std::make_unique<FontGDI>(hfontCopy, characterSet);
}
[[nodiscard]] CharacterSet GetCharacterSet() const noexcept override {
return characterSet;
}
};
class SurfaceGDI : public Surface {
SurfaceMode mode;
HDC hdc{};
bool hdcOwned = false;
HPEN pen{};
HPEN penOld{};
HBRUSH brush{};
HBRUSH brushOld{};
HFONT fontOld{};
HBITMAP bitmap{};
HBITMAP bitmapOld{};
int logPixelsY = USER_DEFAULT_SCREEN_DPI;
static constexpr int maxWidthMeasure = INT_MAX;
// There appears to be a 16 bit string length limit in GDI on NT.
static constexpr int maxLenText = 65535;
void PenColour(ColourRGBA fore, XYPOSITION widthStroke) noexcept;
void BrushColour(ColourRGBA back) noexcept;
void SetFont(const Font *font_);
void Clear() noexcept;
public:
SurfaceGDI() noexcept = default;
SurfaceGDI(HDC hdcCompatible, int width, int height, SurfaceMode mode_, int logPixelsY_) noexcept;
// Deleted so SurfaceGDI objects can not be copied.
SurfaceGDI(const SurfaceGDI &) = delete;
SurfaceGDI(SurfaceGDI &&) = delete;
SurfaceGDI &operator=(const SurfaceGDI &) = delete;
SurfaceGDI &operator=(SurfaceGDI &&) = delete;
~SurfaceGDI() noexcept override;
void Init(WindowID wid) override;
void Init(SurfaceID sid, WindowID wid) override;
std::unique_ptr<Surface> AllocatePixMap(int width, int height) override;
void SetMode(SurfaceMode mode_) override;
void Release() noexcept override;
int SupportsFeature(Supports feature) noexcept override;
bool Initialised() override;
int LogPixelsY() override;
int PixelDivisions() override;
int DeviceHeightFont(int points) override;
void LineDraw(Point start, Point end, Stroke stroke) override;
void PolyLine(const Point *pts, size_t npts, Stroke stroke) override;
void Polygon(const Point *pts, size_t npts, FillStroke fillStroke) override;
void RectangleDraw(PRectangle rc, FillStroke fillStroke) override;
void RectangleFrame(PRectangle rc, Stroke stroke) override;
void FillRectangle(PRectangle rc, Fill fill) override;
void FillRectangleAligned(PRectangle rc, Fill fill) override;
void FillRectangle(PRectangle rc, Surface &surfacePattern) override;
void RoundedRectangle(PRectangle rc, FillStroke fillStroke) override;
void AlphaRectangle(PRectangle rc, XYPOSITION cornerSize, FillStroke fillStroke) override;
void GradientRectangle(PRectangle rc, const std::vector<ColourStop> &stops, GradientOptions options) override;
void DrawRGBAImage(PRectangle rc, int width, int height, const unsigned char *pixelsImage) override;
void Ellipse(PRectangle rc, FillStroke fillStroke) override;
void Stadium(PRectangle rc, FillStroke fillStroke, Ends ends) override;
void Copy(PRectangle rc, Point from, Surface &surfaceSource) override;
std::unique_ptr<IScreenLineLayout> Layout(const IScreenLine *screenLine) override;
void DrawTextCommon(PRectangle rc, const Font *font_, XYPOSITION ybase, std::string_view text, UINT fuOptions);
void DrawTextNoClip(PRectangle rc, const Font *font_, XYPOSITION ybase, std::string_view text, ColourRGBA fore, ColourRGBA back) override;
void DrawTextClipped(PRectangle rc, const Font *font_, XYPOSITION ybase, std::string_view text, ColourRGBA fore, ColourRGBA back) override;
void DrawTextTransparent(PRectangle rc, const Font *font_, XYPOSITION ybase, std::string_view text, ColourRGBA fore) override;
void MeasureWidths(const Font *font_, std::string_view text, XYPOSITION *positions) override;
XYPOSITION WidthText(const Font *font_, std::string_view text) override;
void DrawTextCommonUTF8(PRectangle rc, const Font *font_, XYPOSITION ybase, std::string_view text, UINT fuOptions);
void DrawTextNoClipUTF8(PRectangle rc, const Font *font_, XYPOSITION ybase, std::string_view text, ColourRGBA fore, ColourRGBA back) override;
void DrawTextClippedUTF8(PRectangle rc, const Font *font_, XYPOSITION ybase, std::string_view text, ColourRGBA fore, ColourRGBA back) override;
void DrawTextTransparentUTF8(PRectangle rc, const Font *font_, XYPOSITION ybase, std::string_view text, ColourRGBA fore) override;
void MeasureWidthsUTF8(const Font *font_, std::string_view text, XYPOSITION *positions) override;
XYPOSITION WidthTextUTF8(const Font *font_, std::string_view text) override;
XYPOSITION Ascent(const Font *font_) override;
XYPOSITION Descent(const Font *font_) override;
XYPOSITION InternalLeading(const Font *font_) override;
XYPOSITION Height(const Font *font_) override;
XYPOSITION AverageCharWidth(const Font *font_) override;
void SetClip(PRectangle rc) override;
void PopClip() override;
void FlushCachedState() override;
void FlushDrawing() override;
};
SurfaceGDI::SurfaceGDI(HDC hdcCompatible, int width, int height, SurfaceMode mode_, int logPixelsY_) noexcept {
hdc = ::CreateCompatibleDC(hdcCompatible);
hdcOwned = true;
bitmap = ::CreateCompatibleBitmap(hdcCompatible, width, height);
bitmapOld = SelectBitmap(hdc, bitmap);
::SetTextAlign(hdc, TA_BASELINE);
mode = mode_;
logPixelsY = logPixelsY_;
}
SurfaceGDI::~SurfaceGDI() noexcept {
Clear();
}
void SurfaceGDI::Clear() noexcept {
if (penOld) {
::SelectObject(hdc, penOld);
::DeleteObject(pen);
penOld = {};
}
pen = {};
if (brushOld) {
::SelectObject(hdc, brushOld);
::DeleteObject(brush);
brushOld = {};
}
brush = {};
if (fontOld) {
// Fonts are not deleted as they are owned by a Font object
::SelectObject(hdc, fontOld);
fontOld = {};
}
if (bitmapOld) {
::SelectObject(hdc, bitmapOld);
::DeleteObject(bitmap);
bitmapOld = {};
}
bitmap = {};
if (hdcOwned) {
::DeleteDC(hdc);
hdc = {};
hdcOwned = false;
}
}
void SurfaceGDI::Release() noexcept {
Clear();
}
int SurfaceGDI::SupportsFeature(Supports feature) noexcept {
for (const Supports f : SupportsGDI) {
if (f == feature)
return 1;
}
return 0;
}
bool SurfaceGDI::Initialised() {
return hdc;
}
void SurfaceGDI::Init(WindowID wid) {
Release();
hdc = ::CreateCompatibleDC({});
hdcOwned = true;
::SetTextAlign(hdc, TA_BASELINE);
logPixelsY = DpiForWindow(wid);
}
void SurfaceGDI::Init(SurfaceID sid, WindowID wid) {
Release();
hdc = static_cast<HDC>(sid);
::SetTextAlign(hdc, TA_BASELINE);
// Windows on screen are scaled but printers are not.
const bool printing = ::GetDeviceCaps(hdc, TECHNOLOGY) != DT_RASDISPLAY;
logPixelsY = printing ? ::GetDeviceCaps(hdc, LOGPIXELSY) : DpiForWindow(wid);
}
std::unique_ptr<Surface> SurfaceGDI::AllocatePixMap(int width, int height) {
return std::make_unique<SurfaceGDI>(hdc, width, height, mode, logPixelsY);
}
void SurfaceGDI::SetMode(SurfaceMode mode_) {
mode = mode_;
}
void SurfaceGDI::PenColour(ColourRGBA fore, XYPOSITION widthStroke) noexcept {
if (pen) {
::SelectObject(hdc, penOld);
::DeleteObject(pen);
pen = {};
penOld = {};
}
const DWORD penWidth = std::lround(widthStroke);
const COLORREF penColour = fore.OpaqueRGB();
if (widthStroke > 1) {
const LOGBRUSH brushParameters{ BS_SOLID, penColour, 0 };
pen = ::ExtCreatePen(PS_GEOMETRIC | PS_ENDCAP_ROUND | PS_JOIN_MITER,
penWidth,
&brushParameters,
0,
nullptr);
} else {
pen = ::CreatePen(PS_INSIDEFRAME, penWidth, penColour);
}
penOld = SelectPen(hdc, pen);
}
void SurfaceGDI::BrushColour(ColourRGBA back) noexcept {
if (brush) {
::SelectObject(hdc, brushOld);
::DeleteObject(brush);
brush = {};
brushOld = {};
}
brush = ::CreateSolidBrush(back.OpaqueRGB());
brushOld = SelectBrush(hdc, brush);
}
void SurfaceGDI::SetFont(const Font *font_) {
const FontGDI *pfm = dynamic_cast<const FontGDI *>(font_);
PLATFORM_ASSERT(pfm);
if (!pfm) {
throw std::runtime_error("SurfaceGDI::SetFont: wrong Font type.");
}
if (fontOld) {
SelectFont(hdc, pfm->hfont);
} else {
fontOld = SelectFont(hdc, pfm->hfont);
}
}
int SurfaceGDI::LogPixelsY() {
return logPixelsY;
}
int SurfaceGDI::PixelDivisions() {
// Win32 uses device pixels.
return 1;
}
int SurfaceGDI::DeviceHeightFont(int points) {
return ::MulDiv(points, LogPixelsY(), pointsPerInch);
}
void SurfaceGDI::LineDraw(Point start, Point end, Stroke stroke) {
PenColour(stroke.colour, stroke.width);
::MoveToEx(hdc, std::lround(std::floor(start.x)), std::lround(std::floor(start.y)), nullptr);
::LineTo(hdc, std::lround(std::floor(end.x)), std::lround(std::floor(end.y)));
}
void SurfaceGDI::PolyLine(const Point *pts, size_t npts, Stroke stroke) {
PLATFORM_ASSERT(npts > 1);
if (npts <= 1) {
return;
}
PenColour(stroke.colour, stroke.width);
std::vector<POINT> outline;
std::transform(pts, pts + npts, std::back_inserter(outline), POINTFromPoint);
::Polyline(hdc, outline.data(), static_cast<int>(npts));
}
void SurfaceGDI::Polygon(const Point *pts, size_t npts, FillStroke fillStroke) {
PenColour(fillStroke.stroke.colour.WithoutAlpha(), fillStroke.stroke.width);
BrushColour(fillStroke.fill.colour.WithoutAlpha());
std::vector<POINT> outline;
std::transform(pts, pts + npts, std::back_inserter(outline), POINTFromPoint);
::Polygon(hdc, outline.data(), static_cast<int>(npts));
}
void SurfaceGDI::RectangleDraw(PRectangle rc, FillStroke fillStroke) {
RectangleFrame(rc, fillStroke.stroke);
FillRectangle(rc.Inset(fillStroke.stroke.width), fillStroke.fill.colour);
}
void SurfaceGDI::RectangleFrame(PRectangle rc, Stroke stroke) {
BrushColour(stroke.colour);
const RECT rcw = RectFromPRectangle(rc);
::FrameRect(hdc, &rcw, brush);
}
void SurfaceGDI::FillRectangle(PRectangle rc, Fill fill) {
if (fill.colour.IsOpaque()) {
// Using ExtTextOut rather than a FillRect ensures that no dithering occurs.
// There is no need to allocate a brush either.
const RECT rcw = RectFromPRectangle(rc);
::SetBkColor(hdc, fill.colour.OpaqueRGB());
::ExtTextOut(hdc, rcw.left, rcw.top, ETO_OPAQUE, &rcw, TEXT(""), 0, nullptr);
} else {
AlphaRectangle(rc, 0, FillStroke(fill.colour));
}
}
void SurfaceGDI::FillRectangleAligned(PRectangle rc, Fill fill) {
FillRectangle(PixelAlign(rc, 1), fill);
}
void SurfaceGDI::FillRectangle(PRectangle rc, Surface &surfacePattern) {
HBRUSH br{};
if (SurfaceGDI *psgdi = dynamic_cast<SurfaceGDI *>(&surfacePattern); psgdi && psgdi->bitmap) {
br = ::CreatePatternBrush(psgdi->bitmap);
} else { // Something is wrong so display in red
br = ::CreateSolidBrush(RGB(0xff, 0, 0));
}
const RECT rcw = RectFromPRectangle(rc);
::FillRect(hdc, &rcw, br);
::DeleteObject(br);
}
void SurfaceGDI::RoundedRectangle(PRectangle rc, FillStroke fillStroke) {
PenColour(fillStroke.stroke.colour, fillStroke.stroke.width);
BrushColour(fillStroke.fill.colour);
const RECT rcw = RectFromPRectangle(rc);
constexpr int cornerSize = 8;
::RoundRect(hdc,
rcw.left + 1, rcw.top,
rcw.right - 1, rcw.bottom,
cornerSize, cornerSize);
}
// DIBSection is bitmap with some drawing operations used by SurfaceGDI.
class DIBSection {
GDIBitMap bm;
SIZE size{};
DWORD *pixels = nullptr;
public:
DIBSection(HDC hdc, SIZE size_) noexcept;
explicit operator bool() const noexcept {
return bm && pixels;
}
[[nodiscard]] DWORD *Pixels() const noexcept {
return pixels;
}
[[nodiscard]] unsigned char *Bytes() const noexcept {
return reinterpret_cast<unsigned char *>(pixels);
}
[[nodiscard]] HDC DC() const noexcept {
return bm.DC();
}
void SetPixel(LONG x, LONG y, DWORD value) noexcept {
PLATFORM_ASSERT(x >= 0);
PLATFORM_ASSERT(y >= 0);
PLATFORM_ASSERT(x < size.cx);
PLATFORM_ASSERT(y < size.cy);
pixels[(y * size.cx) + x] = value;
}
void SetSymmetric(LONG x, LONG y, DWORD value) noexcept;
};
DIBSection::DIBSection(HDC hdc, SIZE size_) noexcept : size(size_) {
// -size.y makes bitmap start from top
bm.Create(hdc, size.cx, -size.cy, &pixels);
}
void DIBSection::SetSymmetric(LONG x, LONG y, DWORD value) noexcept {
// Plot a point symmetrically to all 4 quadrants
const LONG xSymmetric = size.cx - 1 - x;
const LONG ySymmetric = size.cy - 1 - y;
SetPixel(x, y, value);
SetPixel(xSymmetric, y, value);
SetPixel(x, ySymmetric, value);
SetPixel(xSymmetric, ySymmetric, value);
}
ColourRGBA GradientValue(const std::vector<ColourStop> &stops, XYPOSITION proportion) noexcept {
for (size_t stop = 0; stop < stops.size() - 1; stop++) {
// Loop through each pair of stops
const XYPOSITION positionStart = stops[stop].position;
const XYPOSITION positionEnd = stops[stop + 1].position;
if ((proportion >= positionStart) && (proportion <= positionEnd)) {
const XYPOSITION proportionInPair = (proportion - positionStart) /
(positionEnd - positionStart);
return stops[stop].colour.MixedWith(stops[stop + 1].colour, proportionInPair);
}
}
// Loop should always find a value
return ColourRGBA();
}
constexpr DWORD dwordFromBGRA(byte b, byte g, byte r, byte a) noexcept {
constexpr int aShift = 24;
constexpr int rShift = 16;
constexpr int gShift = 8;
return (a << aShift) | (r << rShift) | (g << gShift) | b;
}
constexpr byte AlphaScaled(unsigned char component, unsigned int alpha) noexcept {
constexpr byte maxByte = 0xFFU;
return (component * alpha / maxByte) & maxByte;
}
constexpr DWORD dwordMultiplied(ColourRGBA colour) noexcept {
return dwordFromBGRA(
AlphaScaled(colour.GetBlue(), colour.GetAlpha()),
AlphaScaled(colour.GetGreen(), colour.GetAlpha()),
AlphaScaled(colour.GetRed(), colour.GetAlpha()),
colour.GetAlpha());
}
constexpr BLENDFUNCTION mergeAlpha = { AC_SRC_OVER, 0, 255, AC_SRC_ALPHA };
void SurfaceGDI::AlphaRectangle(PRectangle rc, XYPOSITION cornerSize, FillStroke fillStroke) {
// TODO: Implement strokeWidth
const RECT rcw = RectFromPRectangle(rc);
const SIZE size = SizeOfRect(rcw);
if (size.cx > 0) {
DIBSection section(hdc, size);
if (section) {
// Ensure not distorted too much by corners when small
const LONG corner = std::min(static_cast<LONG>(cornerSize), (std::min(size.cx, size.cy) / 2) - 2);
constexpr DWORD valEmpty = dwordFromBGRA(0, 0, 0, 0);
const DWORD valFill = dwordMultiplied(fillStroke.fill.colour);
const DWORD valOutline = dwordMultiplied(fillStroke.stroke.colour);
// Draw a framed rectangle
for (int y = 0; y < size.cy; y++) {
for (int x = 0; x < size.cx; x++) {
if ((x == 0) || (x == size.cx - 1) || (y == 0) || (y == size.cy - 1)) {
section.SetPixel(x, y, valOutline);
} else {
section.SetPixel(x, y, valFill);
}
}
}
// Make the corners transparent
for (LONG c = 0; c < corner; c++) {
for (LONG x = 0; x < c + 1; x++) {
section.SetSymmetric(x, c - x, valEmpty);
}
}
// Draw the corner frame pieces
for (LONG x = 1; x < corner; x++) {
section.SetSymmetric(x, corner - x, valOutline);
}
GdiAlphaBlend(hdc, rcw.left, rcw.top, size.cx, size.cy, section.DC(), 0, 0, size.cx, size.cy, mergeAlpha);
}
} else {
BrushColour(fillStroke.stroke.colour);
FrameRect(hdc, &rcw, brush);
}
}
void SurfaceGDI::GradientRectangle(PRectangle rc, const std::vector<ColourStop> &stops, GradientOptions options) {
const RECT rcw = RectFromPRectangle(rc);
const SIZE size = SizeOfRect(rcw);
DIBSection section(hdc, size);
if (section) {
if (options == GradientOptions::topToBottom) {
for (LONG y = 0; y < size.cy; y++) {
// Find y/height proportional colour
const XYPOSITION proportion = y / (rc.Height() - 1.0f);
const ColourRGBA mixed = GradientValue(stops, proportion);
const DWORD valFill = dwordMultiplied(mixed);
for (LONG x = 0; x < size.cx; x++) {
section.SetPixel(x, y, valFill);
}
}
} else {
for (LONG x = 0; x < size.cx; x++) {
// Find x/width proportional colour
const XYPOSITION proportion = x / (rc.Width() - 1.0f);
const ColourRGBA mixed = GradientValue(stops, proportion);
const DWORD valFill = dwordMultiplied(mixed);
for (LONG y = 0; y < size.cy; y++) {
section.SetPixel(x, y, valFill);
}
}
}
GdiAlphaBlend(hdc, rcw.left, rcw.top, size.cx, size.cy, section.DC(), 0, 0, size.cx, size.cy, mergeAlpha);
}
}
void SurfaceGDI::DrawRGBAImage(PRectangle rc, int width, int height, const unsigned char *pixelsImage) {
if (rc.Width() > 0) {
if (rc.Width() > width)
rc.left += std::floor((rc.Width() - width) / 2);
rc.right = rc.left + width;
if (rc.Height() > height)
rc.top += std::floor((rc.Height() - height) / 2);
rc.bottom = rc.top + height;
const SIZE size{ width, height };
DIBSection section(hdc, size);
if (section) {
RGBAImage::BGRAFromRGBA(section.Bytes(), pixelsImage, static_cast<size_t>(width) * height);
GdiAlphaBlend(hdc, static_cast<int>(rc.left), static_cast<int>(rc.top),
static_cast<int>(rc.Width()), static_cast<int>(rc.Height()), section.DC(),
0, 0, width, height, mergeAlpha);
}
}
}
void SurfaceGDI::Ellipse(PRectangle rc, FillStroke fillStroke) {
PenColour(fillStroke.stroke.colour, fillStroke.stroke.width);
BrushColour(fillStroke.fill.colour);
const RECT rcw = RectFromPRectangle(rc);
::Ellipse(hdc, rcw.left, rcw.top, rcw.right, rcw.bottom);
}
void SurfaceGDI::Stadium(PRectangle rc, FillStroke fillStroke, [[maybe_unused]] Ends ends) {
// TODO: Implement properly - the rectangle is just a placeholder
RectangleDraw(rc, fillStroke);
}
void SurfaceGDI::Copy(PRectangle rc, Point from, Surface &surfaceSource) {
::BitBlt(hdc,
static_cast<int>(rc.left), static_cast<int>(rc.top),
static_cast<int>(rc.Width()), static_cast<int>(rc.Height()),
dynamic_cast<SurfaceGDI &>(surfaceSource).hdc,
static_cast<int>(from.x), static_cast<int>(from.y), SRCCOPY);
}
std::unique_ptr<IScreenLineLayout> SurfaceGDI::Layout(const IScreenLine *) {
return {};
}
using TextPositionsI = VarBuffer<int, stackBufferLength>;
void SurfaceGDI::DrawTextCommon(PRectangle rc, const Font *font_, XYPOSITION ybase, std::string_view text, UINT fuOptions) {
SetFont(font_);
const RECT rcw = RectFromPRectangle(rc);
const int x = static_cast<int>(rc.left);
const int yBaseInt = static_cast<int>(ybase);
if (mode.codePage == CpUtf8) {
const TextWide tbuf(text, mode.codePage);
::ExtTextOutW(hdc, x, yBaseInt, fuOptions, &rcw, tbuf.buffer, tbuf.tlen, nullptr);
} else {
::ExtTextOutA(hdc, x, yBaseInt, fuOptions, &rcw, text.data(), static_cast<UINT>(text.length()), nullptr);
}
}
void SurfaceGDI::DrawTextNoClip(PRectangle rc, const Font *font_, XYPOSITION ybase, std::string_view text,
ColourRGBA fore, ColourRGBA back) {
::SetTextColor(hdc, fore.OpaqueRGB());
::SetBkColor(hdc, back.OpaqueRGB());
DrawTextCommon(rc, font_, ybase, text, ETO_OPAQUE);
}
void SurfaceGDI::DrawTextClipped(PRectangle rc, const Font *font_, XYPOSITION ybase, std::string_view text,
ColourRGBA fore, ColourRGBA back) {
::SetTextColor(hdc, fore.OpaqueRGB());
::SetBkColor(hdc, back.OpaqueRGB());
DrawTextCommon(rc, font_, ybase, text, ETO_OPAQUE | ETO_CLIPPED);
}
void SurfaceGDI::DrawTextTransparent(PRectangle rc, const Font *font_, XYPOSITION ybase, std::string_view text,
ColourRGBA fore) {
// Avoid drawing spaces in transparent mode
for (const char ch : text) {
if (ch != ' ') {
::SetTextColor(hdc, fore.OpaqueRGB());
::SetBkMode(hdc, TRANSPARENT);
DrawTextCommon(rc, font_, ybase, text, 0);
::SetBkMode(hdc, OPAQUE);
return;
}
}
}
void SurfaceGDI::MeasureWidths(const Font *font_, std::string_view text, XYPOSITION *positions) {
// Zero positions to avoid random behaviour on failure.
std::fill(positions, positions + text.length(), 0.0f);
SetFont(font_);
SIZE sz = { 0,0 };
int fit = 0;
int i = 0;
const int len = static_cast<int>(text.length());
if (mode.codePage == CpUtf8) {
const TextWide tbuf(text, mode.codePage);
TextPositionsI poses(tbuf.tlen);
if (!::GetTextExtentExPointW(hdc, tbuf.buffer, tbuf.tlen, maxWidthMeasure, &fit, poses.buffer, &sz)) {
// Failure
return;
}
// Map the widths given for UTF-16 characters back onto the UTF-8 input string
for (int ui = 0; ui < fit; ui++) {
const unsigned char uch = text[i];
const unsigned int byteCount = UTF8BytesOfLead[uch];
if (byteCount == 4) { // Non-BMP
ui++;
}
for (unsigned int bytePos = 0; (bytePos < byteCount) && (i < len); bytePos++) {
positions[i++] = static_cast<XYPOSITION>(poses.buffer[ui]);
}
}
} else {
TextPositionsI poses(len);
if (!::GetTextExtentExPointA(hdc, text.data(), len, maxWidthMeasure, &fit, poses.buffer, &sz)) {
// Eeek - a NULL DC or other foolishness could cause this.
return;
}
while (i < fit) {
positions[i] = static_cast<XYPOSITION>(poses.buffer[i]);
i++;
}
}
// If any positions not filled in then use the last position for them
const XYPOSITION lastPos = (fit > 0) ? positions[fit - 1] : 0.0f;
std::fill(positions + i, positions + text.length(), lastPos);
}
XYPOSITION SurfaceGDI::WidthText(const Font *font_, std::string_view text) {
SetFont(font_);
SIZE sz = { 0,0 };
if (!(mode.codePage == CpUtf8)) {
::GetTextExtentPoint32A(hdc, text.data(), std::min(static_cast<int>(text.length()), maxLenText), &sz);
} else {
const TextWide tbuf(text, mode.codePage);
::GetTextExtentPoint32W(hdc, tbuf.buffer, tbuf.tlen, &sz);
}
return static_cast<XYPOSITION>(sz.cx);
}
void SurfaceGDI::DrawTextCommonUTF8(PRectangle rc, const Font *font_, XYPOSITION ybase, std::string_view text, UINT fuOptions) {
SetFont(font_);
const RECT rcw = RectFromPRectangle(rc);
const int x = static_cast<int>(rc.left);
const int yBaseInt = static_cast<int>(ybase);
const TextWide tbuf(text, CpUtf8);
::ExtTextOutW(hdc, x, yBaseInt, fuOptions, &rcw, tbuf.buffer, tbuf.tlen, nullptr);
}
void SurfaceGDI::DrawTextNoClipUTF8(PRectangle rc, const Font *font_, XYPOSITION ybase, std::string_view text,
ColourRGBA fore, ColourRGBA back) {
::SetTextColor(hdc, fore.OpaqueRGB());
::SetBkColor(hdc, back.OpaqueRGB());
DrawTextCommonUTF8(rc, font_, ybase, text, ETO_OPAQUE);
}
void SurfaceGDI::DrawTextClippedUTF8(PRectangle rc, const Font *font_, XYPOSITION ybase, std::string_view text,
ColourRGBA fore, ColourRGBA back) {
::SetTextColor(hdc, fore.OpaqueRGB());
::SetBkColor(hdc, back.OpaqueRGB());
DrawTextCommonUTF8(rc, font_, ybase, text, ETO_OPAQUE | ETO_CLIPPED);
}
void SurfaceGDI::DrawTextTransparentUTF8(PRectangle rc, const Font *font_, XYPOSITION ybase, std::string_view text,
ColourRGBA fore) {
// Avoid drawing spaces in transparent mode
for (const char ch : text) {
if (ch != ' ') {
::SetTextColor(hdc, fore.OpaqueRGB());
::SetBkMode(hdc, TRANSPARENT);
DrawTextCommonUTF8(rc, font_, ybase, text, 0);
::SetBkMode(hdc, OPAQUE);
return;
}
}
}
void SurfaceGDI::MeasureWidthsUTF8(const Font *font_, std::string_view text, XYPOSITION *positions) {
// Zero positions to avoid random behaviour on failure.
std::fill(positions, positions + text.length(), 0.0f);
SetFont(font_);
SIZE sz = { 0,0 };
int fit = 0;
int i = 0;
const int len = static_cast<int>(text.length());
const TextWide tbuf(text, CpUtf8);
TextPositionsI poses(tbuf.tlen);
if (!::GetTextExtentExPointW(hdc, tbuf.buffer, tbuf.tlen, maxWidthMeasure, &fit, poses.buffer, &sz)) {
// Failure
return;
}
// Map the widths given for UTF-16 characters back onto the UTF-8 input string
for (int ui = 0; ui < fit; ui++) {
const unsigned char uch = text[i];
const unsigned int byteCount = UTF8BytesOfLead[uch];
if (byteCount == 4) { // Non-BMP
ui++;
}
for (unsigned int bytePos = 0; (bytePos < byteCount) && (i < len); bytePos++) {
positions[i++] = static_cast<XYPOSITION>(poses.buffer[ui]);
}
}
// If any positions not filled in then use the last position for them
const XYPOSITION lastPos = (fit > 0) ? positions[fit - 1] : 0.0f;
std::fill(positions + i, positions + text.length(), lastPos);
}
XYPOSITION SurfaceGDI::WidthTextUTF8(const Font *font_, std::string_view text) {
SetFont(font_);
SIZE sz = { 0,0 };
const TextWide tbuf(text, CpUtf8);
::GetTextExtentPoint32W(hdc, tbuf.buffer, tbuf.tlen, &sz);
return static_cast<XYPOSITION>(sz.cx);
}
XYPOSITION SurfaceGDI::Ascent(const Font *font_) {
SetFont(font_);
TEXTMETRIC tm;
::GetTextMetrics(hdc, &tm);
return static_cast<XYPOSITION>(tm.tmAscent);
}
XYPOSITION SurfaceGDI::Descent(const Font *font_) {
SetFont(font_);
TEXTMETRIC tm;
::GetTextMetrics(hdc, &tm);
return static_cast<XYPOSITION>(tm.tmDescent);
}
XYPOSITION SurfaceGDI::InternalLeading(const Font *font_) {
SetFont(font_);
TEXTMETRIC tm;
::GetTextMetrics(hdc, &tm);
return static_cast<XYPOSITION>(tm.tmInternalLeading);
}
XYPOSITION SurfaceGDI::Height(const Font *font_) {
SetFont(font_);
TEXTMETRIC tm;
::GetTextMetrics(hdc, &tm);
return static_cast<XYPOSITION>(tm.tmHeight);
}
XYPOSITION SurfaceGDI::AverageCharWidth(const Font *font_) {
SetFont(font_);
TEXTMETRIC tm;
::GetTextMetrics(hdc, &tm);
return static_cast<XYPOSITION>(tm.tmAveCharWidth);
}
void SurfaceGDI::SetClip(PRectangle rc) {
::SaveDC(hdc);
::IntersectClipRect(hdc, static_cast<int>(rc.left), static_cast<int>(rc.top),
static_cast<int>(rc.right), static_cast<int>(rc.bottom));
}
void SurfaceGDI::PopClip() {
::RestoreDC(hdc, -1);
}
void SurfaceGDI::FlushCachedState() {
pen = {};
brush = {};
}
void SurfaceGDI::FlushDrawing() {
}
}
namespace Scintilla::Internal {
std::shared_ptr<Font> FontGDI_Allocate(const FontParameters &fp) {
return std::make_shared<FontGDI>(fp);
}
std::unique_ptr<Surface> SurfaceGDI_Allocate() {
return std::make_unique<SurfaceGDI>();
}
}

View File

@ -0,0 +1,18 @@
// Scintilla source code edit control
/** @file SurfaceGDI.h
** Definitions for drawing to GDI on Windows.
**/
// Copyright 2025 by Neil Hodgson <neilh@scintilla.org>
// The License.txt file describes the conditions under which this software may be distributed.
#ifndef SURFACEGDI_H
#define SURFACEGDI_H
namespace Scintilla::Internal {
std::shared_ptr<Font> FontGDI_Allocate(const FontParameters &fp);
std::unique_ptr<Surface> SurfaceGDI_Allocate();
}
#endif

View File

@ -52,6 +52,13 @@ inline T DLLFunction(HMODULE hModule, LPCSTR lpProcName) noexcept {
return fp; return fp;
} }
inline void ReleaseLibrary(HMODULE &hLib) noexcept {
if (hLib) {
FreeLibrary(hLib);
hLib = {};
}
}
} }
#endif #endif

View File

@ -3,6 +3,19 @@ $(DIR_O)/HanjaDic.o: \
HanjaDic.cxx \ HanjaDic.cxx \
WinTypes.h \ WinTypes.h \
HanjaDic.h HanjaDic.h
$(DIR_O)/ListBox.o: \
ListBox.cxx \
../include/ScintillaTypes.h \
../src/Debugging.h \
../src/Geometry.h \
../src/Platform.h \
../src/XPM.h \
../src/UniConversion.h \
../src/DBCS.h \
WinTypes.h \
PlatWin.h \
ListBox.h \
SurfaceD2D.h
$(DIR_O)/PlatWin.o: \ $(DIR_O)/PlatWin.o: \
PlatWin.cxx \ PlatWin.cxx \
../include/ScintillaTypes.h \ ../include/ScintillaTypes.h \
@ -13,7 +26,9 @@ $(DIR_O)/PlatWin.o: \
../src/UniConversion.h \ ../src/UniConversion.h \
../src/DBCS.h \ ../src/DBCS.h \
WinTypes.h \ WinTypes.h \
PlatWin.h PlatWin.h \
ListBox.h \
SurfaceD2D.h
$(DIR_O)/ScintillaDLL.o: \ $(DIR_O)/ScintillaDLL.o: \
ScintillaDLL.cxx \ ScintillaDLL.cxx \
../include/ScintillaTypes.h \ ../include/ScintillaTypes.h \
@ -29,6 +44,7 @@ $(DIR_O)/ScintillaWin.o: \
../src/Debugging.h \ ../src/Debugging.h \
../src/Geometry.h \ ../src/Geometry.h \
../src/Platform.h \ ../src/Platform.h \
../src/CharacterType.h \
../src/CharacterCategoryMap.h \ ../src/CharacterCategoryMap.h \
../src/Position.h \ ../src/Position.h \
../src/UniqueString.h \ ../src/UniqueString.h \
@ -49,6 +65,7 @@ $(DIR_O)/ScintillaWin.o: \
../src/Document.h \ ../src/Document.h \
../src/CaseConvert.h \ ../src/CaseConvert.h \
../src/UniConversion.h \ ../src/UniConversion.h \
../src/DBCS.h \
../src/Selection.h \ ../src/Selection.h \
../src/PositionCache.h \ ../src/PositionCache.h \
../src/EditModel.h \ ../src/EditModel.h \
@ -60,8 +77,34 @@ $(DIR_O)/ScintillaWin.o: \
../src/ScintillaBase.h \ ../src/ScintillaBase.h \
WinTypes.h \ WinTypes.h \
PlatWin.h \ PlatWin.h \
SurfaceD2D.h \
HanjaDic.h \ HanjaDic.h \
ScintillaWin.h ScintillaWin.h
$(DIR_O)/SurfaceD2D.o: \
SurfaceD2D.cxx \
../include/ScintillaTypes.h \
../src/Debugging.h \
../src/Geometry.h \
../src/Platform.h \
../src/XPM.h \
../src/UniConversion.h \
../src/DBCS.h \
WinTypes.h \
PlatWin.h \
SurfaceGDI.h \
SurfaceD2D.h
$(DIR_O)/SurfaceGDI.o: \
SurfaceGDI.cxx \
../include/ScintillaTypes.h \
../src/Debugging.h \
../src/Geometry.h \
../src/Platform.h \
../src/XPM.h \
../src/UniConversion.h \
../src/DBCS.h \
WinTypes.h \
PlatWin.h \
SurfaceGDI.h
$(DIR_O)/AutoComplete.o: \ $(DIR_O)/AutoComplete.o: \
../src/AutoComplete.cxx \ ../src/AutoComplete.cxx \
../include/ScintillaTypes.h \ ../include/ScintillaTypes.h \

View File

@ -118,6 +118,9 @@ COMPONENT_OBJS = \
$(SRC_OBJS) \ $(SRC_OBJS) \
$(DIR_O)/HanjaDic.o \ $(DIR_O)/HanjaDic.o \
$(DIR_O)/PlatWin.o \ $(DIR_O)/PlatWin.o \
$(DIR_O)/ListBox.o \
$(DIR_O)/SurfaceGDI.o \
$(DIR_O)/SurfaceD2D.o \
$(DIR_O)/ScintillaBase.o \ $(DIR_O)/ScintillaBase.o \
$(DIR_O)/ScintillaWin.o $(DIR_O)/ScintillaWin.o

View File

@ -3,6 +3,19 @@ $(DIR_O)/HanjaDic.obj: \
HanjaDic.cxx \ HanjaDic.cxx \
WinTypes.h \ WinTypes.h \
HanjaDic.h HanjaDic.h
$(DIR_O)/ListBox.obj: \
ListBox.cxx \
../include/ScintillaTypes.h \
../src/Debugging.h \
../src/Geometry.h \
../src/Platform.h \
../src/XPM.h \
../src/UniConversion.h \
../src/DBCS.h \
WinTypes.h \
PlatWin.h \
ListBox.h \
SurfaceD2D.h
$(DIR_O)/PlatWin.obj: \ $(DIR_O)/PlatWin.obj: \
PlatWin.cxx \ PlatWin.cxx \
../include/ScintillaTypes.h \ ../include/ScintillaTypes.h \
@ -13,7 +26,9 @@ $(DIR_O)/PlatWin.obj: \
../src/UniConversion.h \ ../src/UniConversion.h \
../src/DBCS.h \ ../src/DBCS.h \
WinTypes.h \ WinTypes.h \
PlatWin.h PlatWin.h \
ListBox.h \
SurfaceD2D.h
$(DIR_O)/ScintillaDLL.obj: \ $(DIR_O)/ScintillaDLL.obj: \
ScintillaDLL.cxx \ ScintillaDLL.cxx \
../include/ScintillaTypes.h \ ../include/ScintillaTypes.h \
@ -29,6 +44,7 @@ $(DIR_O)/ScintillaWin.obj: \
../src/Debugging.h \ ../src/Debugging.h \
../src/Geometry.h \ ../src/Geometry.h \
../src/Platform.h \ ../src/Platform.h \
../src/CharacterType.h \
../src/CharacterCategoryMap.h \ ../src/CharacterCategoryMap.h \
../src/Position.h \ ../src/Position.h \
../src/UniqueString.h \ ../src/UniqueString.h \
@ -49,6 +65,7 @@ $(DIR_O)/ScintillaWin.obj: \
../src/Document.h \ ../src/Document.h \
../src/CaseConvert.h \ ../src/CaseConvert.h \
../src/UniConversion.h \ ../src/UniConversion.h \
../src/DBCS.h \
../src/Selection.h \ ../src/Selection.h \
../src/PositionCache.h \ ../src/PositionCache.h \
../src/EditModel.h \ ../src/EditModel.h \
@ -60,8 +77,34 @@ $(DIR_O)/ScintillaWin.obj: \
../src/ScintillaBase.h \ ../src/ScintillaBase.h \
WinTypes.h \ WinTypes.h \
PlatWin.h \ PlatWin.h \
SurfaceD2D.h \
HanjaDic.h \ HanjaDic.h \
ScintillaWin.h ScintillaWin.h
$(DIR_O)/SurfaceD2D.obj: \
SurfaceD2D.cxx \
../include/ScintillaTypes.h \
../src/Debugging.h \
../src/Geometry.h \
../src/Platform.h \
../src/XPM.h \
../src/UniConversion.h \
../src/DBCS.h \
WinTypes.h \
PlatWin.h \
SurfaceGDI.h \
SurfaceD2D.h
$(DIR_O)/SurfaceGDI.obj: \
SurfaceGDI.cxx \
../include/ScintillaTypes.h \
../src/Debugging.h \
../src/Geometry.h \
../src/Platform.h \
../src/XPM.h \
../src/UniConversion.h \
../src/DBCS.h \
WinTypes.h \
PlatWin.h \
SurfaceGDI.h
$(DIR_O)/AutoComplete.obj: \ $(DIR_O)/AutoComplete.obj: \
../src/AutoComplete.cxx \ ../src/AutoComplete.cxx \
../include/ScintillaTypes.h \ ../include/ScintillaTypes.h \

View File

@ -10,7 +10,7 @@
.SUFFIXES: .cxx .SUFFIXES: .cxx
DIR_O=. DIR_O=obj
DIR_BIN=..\bin DIR_BIN=..\bin
COMPONENT=$(DIR_BIN)\Scintilla.dll COMPONENT=$(DIR_BIN)\Scintilla.dll
@ -18,6 +18,10 @@ LIBSCI=$(DIR_BIN)\libscintilla.lib
LD=link LD=link
!IF "$(PLATFORM:64=)" == "arm"
ARM64=1
!ENDIF
!IFDEF SUPPORT_XP !IFDEF SUPPORT_XP
ADD_DEFINE=-D_USING_V110_SDK71_ ADD_DEFINE=-D_USING_V110_SDK71_
# Different subsystems for 32-bit and 64-bit Windows XP so detect based on Platform # Different subsystems for 32-bit and 64-bit Windows XP so detect based on Platform
@ -28,10 +32,11 @@ SUBSYSTEM=-SUBSYSTEM:WINDOWS,5.02
SUBSYSTEM=-SUBSYSTEM:WINDOWS,5.01 SUBSYSTEM=-SUBSYSTEM:WINDOWS,5.01
!ENDIF !ENDIF
!ELSE !ELSE
CETCOMPAT=-CETCOMPAT
!IFDEF ARM64 !IFDEF ARM64
ADD_DEFINE=-D_ARM64_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE=1 ADD_DEFINE=-D_ARM64_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE=1
SUBSYSTEM=-SUBSYSTEM:WINDOWS,10.00 SUBSYSTEM=-SUBSYSTEM:WINDOWS,10.00
!ELSE
CETCOMPAT=-CETCOMPAT
!ENDIF !ENDIF
!ENDIF !ENDIF
@ -65,7 +70,10 @@ CXXFLAGS=$(CXXFLAGS) $(CXXNDEBUG)
INCLUDES=-I../include -I../src INCLUDES=-I../include -I../src
CXXFLAGS=$(CXXFLAGS) $(INCLUDES) CXXFLAGS=$(CXXFLAGS) $(INCLUDES)
all: $(COMPONENT) $(LIBSCI) all: $(DIR_O) $(COMPONENT) $(LIBSCI)
$(DIR_O):
mkdir "$(DIR_O)" 2>NUL || cd .
clean: clean:
-del /q $(DIR_O)\*.obj $(DIR_O)\*.pdb $(DIR_O)\*.asm $(COMPONENT) \ -del /q $(DIR_O)\*.obj $(DIR_O)\*.pdb $(DIR_O)\*.asm $(COMPONENT) \
@ -117,6 +125,9 @@ SRC_OBJS=\
COMPONENT_OBJS = \ COMPONENT_OBJS = \
$(DIR_O)\HanjaDic.obj \ $(DIR_O)\HanjaDic.obj \
$(DIR_O)\PlatWin.obj \ $(DIR_O)\PlatWin.obj \
$(DIR_O)\ListBox.obj \
$(DIR_O)\SurfaceGDI.obj \
$(DIR_O)\SurfaceD2D.obj \
$(DIR_O)\ScintillaBase.obj \ $(DIR_O)\ScintillaBase.obj \
$(DIR_O)\ScintillaWin.obj \ $(DIR_O)\ScintillaWin.obj \
$(SRC_OBJS) $(SRC_OBJS)