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)
{
auto udlDoc = new TiXmlDocument(i);
TiXmlDocument* udlDoc = new TiXmlDocument(i);
loadOkay = udlDoc->LoadFile();
if (!loadOkay)
{

View File

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

View File

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

View File

@ -70,12 +70,14 @@ variableScope:lexilla/lexers/LexCmake.cxx
knownConditionTrueFalse:lexilla/lexers/LexCmake.cxx
constParameterReference:lexilla/lexers/LexCmake.cxx
constParameterReference:lexilla/lexers/LexCOBOL.cxx
constVariablePointer:lexilla/lexers/LexCOBOL.cxx
constParameterReference:lexilla/lexers/LexCoffeeScript.cxx
constParameterPointer:lexilla/lexers/LexCoffeeScript.cxx
knownConditionTrueFalse:lexilla/lexers/LexCoffeeScript.cxx
constVariableReference:lexilla/lexers/LexConf.cxx
constParameterReference:lexilla/lexers/LexCPP.cxx
variableScope:lexilla/lexers/LexCSS.cxx
constVariablePointer:lexilla/lexers/LexCSS.cxx
knownConditionTrueFalse:lexilla/lexers/LexDataflex.cxx
constParameterReference: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/LexMetapost.cxx
constParameterReference:lexilla/lexers/LexModula.cxx
duplicateBreak:lexilla/lexers/LexModula.cxx
variableScope:lexilla/lexers/LexModula.cxx
constParameterReference:lexilla/lexers/LexMPT.cxx
variableScope:lexilla/lexers/LexMSSQL.cxx
@ -140,6 +143,7 @@ constVariableReference:lexilla/lexers/LexPerl.cxx
knownConditionTrueFalse:lexilla/lexers/LexPerl.cxx
constParameterReference:lexilla/lexers/LexPLM.cxx
constParameterReference:lexilla/lexers/LexPO.cxx
constVariablePointer:lexilla/lexers/LexPOV.cxx
constParameterReference:lexilla/lexers/LexPython.cxx
shadowVariable:lexilla/lexers/LexPowerPro.cxx
knownConditionTrueFalse:lexilla/lexers/LexPowerPro.cxx
@ -188,7 +192,6 @@ constVariableReference:lexilla/lexers/LexX12.cxx
constParameterPointer:lexilla/lexers/LexX12.cxx
uselessCallsSubstr:lexilla/lexers/LexX12.cxx
constParameterReference:lexilla/lexers/LexYAML.cxx
constParameterPointer:lexilla/lexers/LexYAML.cxx
knownConditionTrueFalse:lexilla/lexers/LexYAML.cxx
// 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="Description"
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" />
<style type="text/css">
.logo {
@ -51,6 +51,7 @@
<title>
Lexilla
</title>
<link rel="canonical" href="https://scintilla.org/Lexilla.html" />
</head>
<body bgcolor="#FFFFFF" text="#000000">
<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>
</td>
<td width="40%" align="right">
<font color="#FFCC99" size="3">Release version 5.4.3<br />
Site last modified February 25 2025</font>
<font color="#FFCC99" size="3">Release version 5.4.4<br />
Site last modified April 2 2025</font>
</td>
<td width="20%">
&nbsp;
@ -77,11 +78,11 @@
</tr>
</table>
<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.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.0 adds a TOML lexer.</li>
<li>Version 5.3.3 improves HTML, JavaScript, Lua, PHP, and XML.</li>
</ul>
<ul id="menu">
<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" />
<title>Lexilla Documentation</title>
<link rel="canonical" href="https://scintilla.org/LexillaDoc.html" />
<style type="text/css">
<!--

View File

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

View File

@ -590,9 +590,22 @@
<td>Henrik S. Johansen</td>
<td>Ekopalypse</td>
<td>HoTschir</td>
<td>Ahmet Sait</td>
</tr>
</table>
<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>
<a href="https://www.scintilla.org/lexilla543.zip">Release 5.4.3</a>
</h3>

View File

@ -109,7 +109,7 @@ constexpr int translateBashDigit(int ch) noexcept {
return BASH_BASE_ERROR;
}
int getBashNumberBase(char *s) noexcept {
int getBashNumberBase(const char *s) noexcept {
int i = 0;
int base = 0;
while (*s) {
@ -164,7 +164,7 @@ bool IsCommentLine(Sci_Position line, LexAccessor &styler) {
const char ch = styler[i];
if (ch == '#')
return true;
else if (ch != ' ' && ch != '\t')
if (ch != ' ' && ch != '\t')
return false;
}
return false;
@ -698,7 +698,7 @@ void SCI_METHOD LexerBash::Lex(Sci_PositionU startPos, Sci_Position length, int
identifierStyle = subStyle | insideCommand;
}
// 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[1] = '\0';
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 '[]'
if (cmdState < CmdState::DoubleBracket) {
char s[10];
char s[10]{};
s[0] = static_cast<char>(sc.ch);
if (setBashOperator.Contains(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';
}
unsigned int SpaceCount(char* lineBuffer) noexcept {
unsigned int SpaceCount(const char* lineBuffer) noexcept {
if (lineBuffer == nullptr)
return 0;
char* headBuffer = lineBuffer;
const char* headBuffer = lineBuffer;
while (*headBuffer == ' ')
headBuffer++;
@ -60,10 +60,10 @@ unsigned int SpaceCount(char* lineBuffer) noexcept {
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)
return false;
char* endValue = startComment - 1;
const char* endValue = startComment - 1;
while (endValue >= lineBuffer && *endValue == ' ')
endValue--;
Sci_PositionU len = static_cast<Sci_PositionU>(endValue - lineBuffer) + 1;

View File

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

View File

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

View File

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

View File

@ -15,7 +15,7 @@
.SUFFIXES: .cxx
DIR_O=.
DIR_O=obj
DIR_BIN=..\bin
LEXILLA=$(DIR_BIN)\lexilla.dll
@ -23,6 +23,10 @@ LIBLEXILLA=$(DIR_BIN)\liblexilla.lib
LD=link
!IF "$(PLATFORM:64=)" == "arm"
ARM64=1
!ENDIF
!IFDEF SUPPORT_XP
ADD_DEFINE=-D_USING_V110_SDK71_
# 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
!ENDIF
!ELSE
CETCOMPAT=-CETCOMPAT
!IFDEF ARM64
ADD_DEFINE=-D_ARM64_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE=1
SUBSYSTEM=-SUBSYSTEM:WINDOWS,10.00
!ELSE
CETCOMPAT=-CETCOMPAT
!ENDIF
!ENDIF
@ -68,7 +73,10 @@ SCINTILLA_INCLUDE = ../../scintilla/include
INCLUDEDIRS=-I../include -I$(SCINTILLA_INCLUDE) -I../lexlib
CXXFLAGS=$(CXXFLAGS) $(INCLUDEDIRS)
all: $(SCINTILLA_INCLUDE) $(LEXILLA) $(LIBLEXILLA)
all: $(SCINTILLA_INCLUDE) $(DIR_O) $(LEXILLA) $(LIBLEXILLA)
$(DIR_O):
mkdir "$(DIR_O)" 2>NUL || cd .
clean:
-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
!ENDIF
CXXFLAGS = /EHsc /std:c++latest $(DEBUG_OPTIONS) $(INCLUDEDIRS)
CXXFLAGS = /EHsc /std:c++20 $(DEBUG_OPTIONS) $(INCLUDEDIRS)
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));
}
int ScintillaCall::MarkerSymbolDefined(int markerNumber) {
return static_cast<int>(Call(Message::MarkerSymbolDefined, markerNumber));
MarkerSymbol ScintillaCall::MarkerSymbolDefined(int markerNumber) {
return static_cast<Scintilla::MarkerSymbol>(Call(Message::MarkerSymbolDefined, markerNumber));
}
void ScintillaCall::MarginSetText(Line line, const char *text) {

View File

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

View File

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

View File

@ -130,7 +130,7 @@
<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
<a href="Lexilla.html">Lexilla</a> project.<br />
@ -3583,117 +3583,140 @@ struct Sci_TextToFindFull {
<table class="standard" summary="Character Sets supported"><tbody>
<tr>
<th>Character Set</th>
<th>Value</th>
<th>Windows</th>
<th>GTK</th>
<th>Cocoa</th></tr></tbody>
<tbody>
<tr>
<td><code>SC_CHARSET_ANSI</code></td>
<tr class="section">
<th align="left"><code>SC_CHARSET_ANSI</code></th>
<td>0</td>
<td>&check;</td>
<td>&check;</td>
<td>&check; (8859-1)</td></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></td>
<td>&check;</td></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></td>
<td>&check;</td></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></td>
<td>&check;</td></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; (8859-1)</td>
<td>&check; (8859-1)</td></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></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></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></td>
<td>&check;</td></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></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></td>
<td>&check;</td></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></td>
<td>&check;</td></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></td>
<td>&check;</td></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></td>
<td>&check;</td></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; (koi8-r)</td>
<td>&check; (cp1251)</td></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></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></td>
<td>&check;</td></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></td>
<td>&check;</td></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></td>
<td>&check;</td></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></td>
<td>&check;</td></tr>
<tr>
<td><code>SC_CHARSET_OEM866</code></td>
<th align="left"><code>SC_CHARSET_OEM866</code></th>
<td>866</td>
<td></td>
<td>&check; (cp866)</td>
<td></td></tr>
<tr>
<td><code>SC_CHARSET_CYRILLIC</code></td>
<th align="left"><code>SC_CHARSET_CYRILLIC</code></th>
<td>1251</td>
<td></td>
<td>&check; (cp1251)</td>
<td>&check; (cp1251)</td></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>&check;</td>
<td>&check;</td></tr>
@ -4960,6 +4983,8 @@ struct Sci_TextToFindFull {
</table>
<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>
<p><img src="StadiumVariants.png" alt="Different shapes available for EOL annotations" /></p>
@ -9043,21 +9068,24 @@ struct SCNotification {
Sci_Position position;
/* SCN_STYLENEEDED, SCN_DOUBLECLICK, SCN_MODIFIED, SCN_MARGINCLICK, */
/* SCN_MARGINRIGHTCLICK, SCN_NEEDSHOWN, SCN_DWELLSTART, SCN_DWELLEND, */
/* SCN_CALLTIPCLICK, SCN_HOTSPOTCLICK, SCN_HOTSPOTDOUBLECLICK, */
/* SCN_HOTSPOTRELEASECLICK, SCN_INDICATORCLICK, SCN_INDICATORRELEASE, */
/* SCN_USERLISTSELECTION, SCN_AUTOCSELECTION, SCN_AUTOCSELECTIONCHANGE */
/* SCN_CALLTIPCLICK, */
/* SCN_HOTSPOTCLICK, SCN_HOTSPOTDOUBLECLICK, SCN_HOTSPOTRELEASECLICK, */
/* SCN_INDICATORCLICK, SCN_INDICATORRELEASE, */
/* SCN_USERLISTSELECTION, SCN_AUTOCCOMPLETED, SCN_AUTOCSELECTION, */
/* SCN_AUTOCSELECTIONCHANGE */
int ch;
/* SCN_CHARADDED, SCN_KEY, SCN_AUTOCCOMPLETE, SCN_AUTOCSELECTION, */
/* SCN_CHARADDED, SCN_KEY, SCN_AUTOCCOMPLETED, SCN_AUTOCSELECTION, */
/* SCN_USERLISTSELECTION */
int modifiers;
/* SCN_KEY, SCN_DOUBLECLICK, SCN_HOTSPOTCLICK, SCN_HOTSPOTDOUBLECLICK, */
/* SCN_HOTSPOTRELEASECLICK, SCN_INDICATORCLICK, SCN_INDICATORRELEASE, */
/* SCN_MARGINCLICK, SCN_MARGINRIGHTCLICK */
int modificationType; /* SCN_MODIFIED */
const char *text;
/* SCN_MODIFIED, SCN_USERLISTSELECTION, SCN_AUTOCSELECTION, SCN_URIDROPPED, */
/* SCN_AUTOCSELECTIONCHANGE */
/* SCN_MODIFIED, SCN_USERLISTSELECTION, SCN_URIDROPPED, */
/* SCN_AUTOCCOMPLETED, SCN_AUTOCSELECTION, SCN_AUTOCSELECTIONCHANGE */
Sci_Position length; /* SCN_MODIFIED */
Sci_Position linesAdded; /* SCN_MODIFIED */

View File

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

View File

@ -585,9 +585,56 @@
<td>Martijn Laan</td>
</tr><tr>
<td>Pawel Z Wronek</td>
<td>Joachim Mairboeck</td>
<td>Gianluca Vaccari</td>
<td>8day</td>
</tr><tr>
<td>Ahmet Sait</td>
</tr>
</table>
<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>
<a href="https://www.scintilla.org/scintilla555.zip">Release 5.5.5</a>
</h3>

View File

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

View File

@ -9,7 +9,7 @@
<meta name="keywords" content="Scintilla, SciTE, Editing Component, Text Editor" />
<meta name="Description"
content="www.scintilla.org is the home of the Scintilla editing component and SciTE text editor application." />
<meta name="Date.Modified" content="20250225" />
<meta name="Date.Modified" content="20250402" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<style type="text/css">
.logo {
@ -61,8 +61,8 @@
GTK, and macOS</font>
</td>
<td width="40%" align="right">
<font color="#FFCC99" size="3"> Release version 5.5.5<br />
Site last modified February 25 2025</font>
<font color="#FFCC99" size="3"> Release version 5.5.6<br />
Site last modified April 2 2025</font>
</td>
<td width="20%">
&nbsp;
@ -77,11 +77,11 @@
</tr>
</table>
<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.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.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 id="menu">
<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_Position position;
/* 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_INDICATORCLICK, SCN_INDICATORRELEASE, */
/* SCN_USERLISTSELECTION, SCN_AUTOCSELECTION */
/* SCN_USERLISTSELECTION, SCN_AUTOCCOMPLETED, SCN_AUTOCSELECTION, */
/* SCN_AUTOCSELECTIONCHANGE */
int ch;
/* SCN_CHARADDED, SCN_KEY, SCN_AUTOCCOMPLETED, SCN_AUTOCSELECTION, */
@ -1435,10 +1437,12 @@ struct SCNotification {
int modifiers;
/* SCN_KEY, SCN_DOUBLECLICK, SCN_HOTSPOTCLICK, SCN_HOTSPOTDOUBLECLICK, */
/* SCN_HOTSPOTRELEASECLICK, SCN_INDICATORCLICK, SCN_INDICATORRELEASE, */
/* SCN_MARGINCLICK, SCN_MARGINRIGHTCLICK */
int modificationType; /* SCN_MODIFIED */
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 linesAdded; /* SCN_MODIFIED */
@ -1448,15 +1452,15 @@ struct SCNotification {
Sci_Position line; /* SCN_MODIFIED */
int foldLevelNow; /* SCN_MODIFIED */
int foldLevelPrev; /* SCN_MODIFIED */
int margin; /* SCN_MARGINCLICK */
int listType; /* SCN_USERLISTSELECTION */
int margin; /* SCN_MARGINCLICK, SCN_MARGINRIGHTCLICK */
int listType; /* SCN_USERLISTSELECTION, SCN_AUTOCSELECTIONCHANGE */
int x; /* SCN_DWELLSTART, SCN_DWELLEND */
int y; /* SCN_DWELLSTART, SCN_DWELLEND */
int token; /* SCN_MODIFIED with SC_MOD_CONTAINER */
Sci_Position annotationLinesAdded; /* SCN_MODIFIED with SC_MOD_CHANGEANNOTATION */
int updated; /* SCN_UPDATEUI */
int listCompletionMethod;
/* SCN_AUTOCSELECTION, SCN_AUTOCCOMPLETED, SCN_USERLISTSELECTION, */
/* SCN_AUTOCSELECTION, SCN_AUTOCCOMPLETED, SCN_USERLISTSELECTION */
int characterSource; /* SCN_CHARADDED */
};

View File

@ -2755,7 +2755,7 @@ set void SetExtraDescent=2527(int extraDescent,)
get int GetExtraDescent=2528(,)
# 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 void MarginSetText=2530(line line, string text)

View File

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

View File

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

View File

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

View File

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

View File

@ -373,6 +373,12 @@ void ScintillaEditBase::mouseMoveEvent(QMouseEvent *event)
sqt->ButtonMoveWithModifiers(pos, TimeOfEvent(time), modifiers);
}
void ScintillaEditBase::leaveEvent(QEvent *event)
{
QWidget::leaveEvent(event);
sqt->MouseLeave();
}
void ScintillaEditBase::contextMenuEvent(QContextMenuEvent *event)
{
const Point pos = PointFromQPoint(event->globalPos());
@ -810,7 +816,7 @@ void ScintillaEditBase::notifyParent(NotificationData scn)
break;
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;
case Notification::AutoCCancelled:

View File

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

View File

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

View File

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

View File

@ -605,7 +605,7 @@ class CaseConverter final : public ICaseConverter {
return character < other.character;
}
};
typedef std::vector<CharacterConversion> CharacterToConversion;
using CharacterToConversion = std::vector<CharacterConversion>;
CharacterToConversion characterToConversion;
// The parallel arrays
std::vector<int> characters;
@ -613,7 +613,7 @@ class CaseConverter final : public ICaseConverter {
public:
CaseConverter() noexcept = default;
bool Initialised() const noexcept {
[[nodiscard]] bool Initialised() const noexcept {
return !characters.empty();
}
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 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);
retMapped.resize(lenMapped);
return retMapped;

View File

@ -5,6 +5,11 @@
// Copyright 2017 by Neil Hodgson <neilh@scintilla.org>
// The License.txt file describes the conditions under which this software may be distributed.
#include <cstdint>
#include <array>
#include <map>
#include "DBCS.h"
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
const unsigned char uch = ch;
switch (codePage) {
case 932:
case cp932:
// Shift_jis
return ((uch >= 0x81) && (uch <= 0x9F)) ||
((uch >= 0xE0) && (uch <= 0xFC));
// Lead bytes F0 to FC may be a Microsoft addition.
case 936:
case cp936:
// GBK
return (uch >= 0x81) && (uch <= 0xFE);
case 949:
case cp949:
// Korean Wansung KS C-5601-1987
return (uch >= 0x81) && (uch <= 0xFE);
case 950:
case cp950:
// Big5
return (uch >= 0x81) && (uch <= 0xFE);
case 1361:
case cp1361:
// Korean Johab KS C-5601-1992
return
((uch >= 0x84) && (uch <= 0xD3)) ||
((uch >= 0xD8) && (uch <= 0xDE)) ||
((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;
}
bool IsDBCSValidSingleByte(int codePage, int ch) noexcept {
switch (codePage) {
case 932:
case cp932:
return ch == 0x80
|| (ch >= 0xA0 && ch <= 0xDF)
|| (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 {
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 {
return codePage == 932
|| codePage == 936
|| codePage == 949
|| codePage == 950
|| codePage == 1361;
return codePage == cp932
|| codePage == cp936
|| codePage == cp949
|| codePage == cp950
|| codePage == cp1361;
}
bool DBCSIsLeadByte(int codePage, char ch) noexcept;
bool DBCSIsTrailByte(int codePage, char 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

View File

@ -620,11 +620,14 @@ void Document::ClearLevels() {
Levels()->ClearLevels();
}
static bool IsSubordinate(FoldLevel levelStart, FoldLevel levelTry) noexcept {
namespace {
constexpr bool IsSubordinate(FoldLevel levelStart, FoldLevel levelTry) noexcept {
if (LevelIsWhitespace(levelTry))
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) {
@ -1229,8 +1232,8 @@ void DiscardEndFragment(std::string_view &text) noexcept {
}
constexpr bool IsBaseOfGrapheme(CharacterCategory cc) {
// \p{L}\p{N}\p{P}\p{Sm}\p{Sc}\p{So}\p{Zs}
switch (cc) {
// \p{L}\p{N}\p{P}\p{Sm}\p{Sc}\p{So}\p{Zs}
case ccLu:
case ccLl:
case ccLt:
@ -1250,12 +1253,16 @@ constexpr bool IsBaseOfGrapheme(CharacterCategory cc) {
case ccSc:
case ccSo:
case ccZs:
// Control
case ccCc:
case ccCs:
case ccCo:
return true;
default:
// ccMn, ccMc, ccMe,
// ccSk,
// ccZl, ccZp,
// ccCc, ccCf, ccCs, ccCo, ccCn
// ccCf, ccCn
return false;
}
}
@ -1276,6 +1283,25 @@ CharacterExtracted LastCharacter(std::string_view text) noexcept {
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 {
@ -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-extend := [\p{M}\p{Join_Control}]
// 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;
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 indent = 0;
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;
std::vector<char> searchThing((lengthFind+1) * UTF8MaxBytes * maxFoldingExpansion + 1);
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)) {
int widthFirstCharacter = 1;
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
assert((indexSearch + lenFlat) <= searchThing.size());
// Does folded match the buffer
characterMatches = 0 == memcmp(folded, &searchThing[0] + indexSearch, lenFlat);
characterMatches = 0 == memcmp(folded, searchThing.data() + indexSearch, lenFlat);
}
if (!characterMatches) {
break;
@ -2423,7 +2432,7 @@ Sci::Position Document::FindText(Sci::Position minPos, Sci::Position maxPos, con
constexpr size_t maxBytesCharacter = 2;
constexpr size_t maxFoldingExpansion = 4;
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)) {
int widthFirstCharacter = 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
assert((indexSearch + lenFlat) <= searchThing.size());
// Does folded match the buffer
characterMatches = 0 == memcmp(folded, &searchThing[0] + indexSearch, lenFlat);
characterMatches = 0 == memcmp(folded, searchThing.data() + indexSearch, lenFlat);
}
if (!characterMatches) {
break;
@ -2477,7 +2486,7 @@ Sci::Position Document::FindText(Sci::Position minPos, Sci::Position maxPos, con
} else {
const Sci::Position endSearch = (startPos <= endPos) ? endPos - lengthFind + 1 : endPos;
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)) {
bool found = (pos + lengthFind) <= limitPos;
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;
}
static char BraceOpposite(char ch) noexcept {
namespace {
constexpr char BraceOpposite(char ch) noexcept {
switch (ch) {
case '(':
return ')';
@ -2998,6 +3009,8 @@ static char BraceOpposite(char ch) noexcept {
}
}
}
// 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 {
const unsigned char chBrace = CharAt(position);
@ -3075,7 +3088,7 @@ public:
lineRangeEnd = doc->SciLineFromPosition(endPos);
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);
if (increment == 1) {
if (line == lineRangeStart)
@ -3101,13 +3114,13 @@ public:
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)
return 0;
else
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);
}
};
@ -3150,10 +3163,10 @@ public:
bool operator!=(const ByteIterator &other) const noexcept {
return doc != other.doc || position != other.position;
}
Sci::Position Pos() const noexcept {
[[nodiscard]] Sci::Position Pos() const noexcept {
return position;
}
Sci::Position PosRoundUp() const noexcept {
[[nodiscard]] Sci::Position PosRoundUp() const noexcept {
return position;
}
};
@ -3178,11 +3191,11 @@ class UTF8Iterator {
// These 3 fields determine the iterator position and are used for comparisons
const Document *doc;
Sci::Position position;
size_t characterIndex;
size_t characterIndex = 0;
// Remaining fields are derived from the determining fields so are excluded in comparisons
unsigned int lenBytes;
size_t lenCharacters;
wchar_t buffered[2];
unsigned int lenBytes = 0;
size_t lenCharacters = 0;
wchar_t buffered[2]{};
public:
using iterator_category = std::bidirectional_iterator_tag;
using value_type = wchar_t;
@ -3191,9 +3204,7 @@ public:
using reference = wchar_t&;
explicit UTF8Iterator(const Document *doc_=nullptr, Sci::Position position_=0) noexcept :
doc(doc_), position(position_), characterIndex(0), lenBytes(0), lenCharacters(0), buffered{} {
buffered[0] = 0;
buffered[1] = 0;
doc(doc_), position(position_) {
if (doc) {
ReadCharacter();
}
@ -3245,10 +3256,10 @@ public:
position != other.position ||
characterIndex != other.characterIndex;
}
Sci::Position Pos() const noexcept {
[[nodiscard]] Sci::Position Pos() const noexcept {
return position;
}
Sci::Position PosRoundUp() const noexcept {
[[nodiscard]] Sci::Position PosRoundUp() const noexcept {
if (characterIndex)
return position + lenBytes; // Force to end of character
else

View File

@ -106,15 +106,7 @@ constexpr bool IsLastStep(const DocModification &mh) noexcept {
&& ((mh.modificationType & finalMask) == finalMask);
}
}
Timer::Timer() noexcept :
ticking(false), ticksToWait(0), tickerID{} {}
Idler::Idler() noexcept :
state(false), idlerID(nullptr) {}
static constexpr bool IsAllSpacesOrTabs(std::string_view sv) noexcept {
constexpr bool IsAllSpacesOrTabs(std::string_view sv) noexcept {
for (const char ch : sv) {
// This is safe because IsSpaceOrTab() will return false for null terminators
if (!IsSpaceOrTab(ch))
@ -123,6 +115,14 @@ static constexpr bool IsAllSpacesOrTabs(std::string_view sv) noexcept {
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) {
ctrlID = 0;
@ -3143,7 +3143,7 @@ void Editor::ChangeCaseOfSelection(CaseMapping caseMapping) {
SelectionRange currentNoVS = current;
currentNoVS.ClearVirtualSpace();
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 sMapped = CaseMapString(sText, caseMapping);
@ -4402,7 +4402,9 @@ void Editor::GoToLine(Sci::Line lineNo) {
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;
if (std::abs(ptDifference.x) > threshold.x)
return false;
@ -4411,6 +4413,12 @@ static bool Close(Point pt1, Point pt2, Point threshold) noexcept {
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 {
if (start < end) {
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) {
SetHoverIndicatorPoint(pt);
//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 {
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 len = 0;
for (size_t i = 0; i < wsv.length() && wsv[i];) {
const unsigned int uch = wsv[i];
if (uch < 0x80) {
const wchar_t uch = wsv[i];
if (uch < first2Byte) {
len++;
} else if (uch < 0x800) {
} else if (uch < first3Byte) {
len += 2;
} else if ((uch >= SURROGATE_LEAD_FIRST) &&
(uch <= SURROGATE_TRAIL_LAST)) {
} else if (IsSurrogate(uch)) {
len += 4;
i++;
} 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 {
size_t k = 0;
for (size_t i = 0; i < wsv.length() && wsv[i];) {
const unsigned int uch = wsv[i];
if (uch < 0x80) {
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)) {
unsigned int uch = wsv[i];
if (IsSurrogate(wsv[i])) {
// Half a surrogate pair
i++;
const unsigned int xch = 0x10000 + ((uch & 0x3ff) << 10) + (wsv[i] & 0x3ff);
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));
uch = SUPPLEMENTAL_PLANE_FIRST + ((uch & maskSurrogate) << shiftSurrogate) + (wsv[i] & maskSurrogate);
}
UTF8AppendCharacter(uch, putf, k);
i++;
}
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 {
size_t k = 0;
if (uch < 0x80) {
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));
}
UTF8AppendCharacter(uch, putf, k);
putf[k] = '\0';
}
@ -108,18 +148,12 @@ size_t UTF16Length(std::string_view svu8) noexcept {
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 ui = 0;
for (size_t i = 0; i < svu8.length();) {
unsigned char ch = svu8[i];
const unsigned int byteCount = UTF8BytesOfLead[ch];
unsigned int value;
unsigned int value = 0;
if (i + byteCount > svu8.length()) {
// 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;
break;
case 2:
value = (ch & 0x1F) << 6;
value = (ch & leadBits2) << shiftByte2;
ch = svu8[i++];
value += TrailByteValue(ch);
tbuf[ui] = static_cast<wchar_t>(value);
break;
case 3:
value = (ch & 0xF) << 12;
value = (ch & leadBits3) << shiftByte3;
ch = svu8[i++];
value += (TrailByteValue(ch) << 6);
value += (TrailByteValue(ch) << shiftByte2);
ch = svu8[i++];
value += TrailByteValue(ch);
tbuf[ui] = static_cast<wchar_t>(value);
break;
default:
// Outside the BMP so need two surrogates
value = (ch & 0x7) << 18;
value = (ch & leadBits4) << shiftByte4;
ch = svu8[i++];
value += TrailByteValue(ch) << 12;
value += TrailByteValue(ch) << shiftByte3;
ch = svu8[i++];
value += TrailByteValue(ch) << 6;
value += TrailByteValue(ch) << shiftByte2;
ch = svu8[i++];
value += TrailByteValue(ch);
tbuf[ui] = static_cast<wchar_t>(((value - 0x10000) >> 10) + SURROGATE_LEAD_FIRST);
tbuf[ui] = SurrogateLead(value);
ui++;
tbuf[ui] = static_cast<wchar_t>((value & 0x3ff) + SURROGATE_TRAIL_FIRST);
tbuf[ui] = SurrogateTrail(value);
break;
}
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();) {
unsigned char ch = svu8[i];
const unsigned int byteCount = UTF8BytesOfLead[ch];
unsigned int value;
unsigned int value = 0;
if (i + byteCount > svu8.length()) {
// 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;
break;
case 2:
value = (ch & 0x1F) << 6;
value = (ch & leadBits2) << shiftByte2;
ch = svu8[i++];
value += TrailByteValue(ch);
break;
case 3:
value = (ch & 0xF) << 12;
value = (ch & leadBits3) << shiftByte3;
ch = svu8[i++];
value += TrailByteValue(ch) << 6;
value += TrailByteValue(ch) << shiftByte2;
ch = svu8[i++];
value += TrailByteValue(ch);
break;
default:
value = (ch & 0x7) << 18;
value = (ch & leadBits4) << shiftByte4;
ch = svu8[i++];
value += TrailByteValue(ch) << 12;
value += TrailByteValue(ch) << shiftByte3;
ch = svu8[i++];
value += TrailByteValue(ch) << 6;
value += TrailByteValue(ch) << shiftByte2;
ch = svu8[i++];
value += TrailByteValue(ch);
break;
@ -256,8 +290,8 @@ unsigned int UTF16FromUTF32Character(unsigned int val, wchar_t *tbuf) noexcept {
tbuf[0] = static_cast<wchar_t>(val);
return 1;
}
tbuf[0] = static_cast<wchar_t>(((val - SUPPLEMENTAL_PLANE_FIRST) >> 10) + SURROGATE_LEAD_FIRST);
tbuf[1] = static_cast<wchar_t>((val & 0x3ff) + SURROGATE_TRAIL_FIRST);
tbuf[0] = SurrogateLead(val);
tbuf[1] = SurrogateTrail(val);
return 2;
}

View File

@ -99,8 +99,12 @@ enum { SURROGATE_TRAIL_FIRST = 0xDC00 };
enum { SURROGATE_TRAIL_LAST = 0xDFFF };
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 {
return ((uch >= SURROGATE_LEAD_FIRST) && (uch <= SURROGATE_LEAD_LAST)) ? 2 : 1;
return IsSurrogate(uch) ? 2 : 1;
}
constexpr unsigned int UTF16LengthFromUTF8ByteCount(unsigned int byteCount) noexcept {

View File

@ -145,5 +145,27 @@ class TestPerformance(unittest.TestCase):
print("%6.3f testUTF8AsciiSearches" % duration)
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__':
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") {
SECTION("LineMarkers") {
DocPlus doc("1\n2\n", CpUtf8);

View File

@ -121,6 +121,9 @@ TEST_CASE("UniConversion") {
const size_t tlen = UTF16FromUTF8(s, tbuf, 1);
REQUIRE(tlen == 1U);
REQUIRE(tbuf[0] == 'a');
char back[4]{};
UTF8FromUTF16(std::wstring_view(tbuf, tlen), back, sizeof(back));
REQUIRE(strcmp(s, back) == 0);
}
SECTION("UTF16FromUTF8 Example1") {
@ -129,6 +132,9 @@ TEST_CASE("UniConversion") {
const size_t tlen = UTF16FromUTF8(s, tbuf, 1);
REQUIRE(tlen == 1U);
REQUIRE(tbuf[0] == 0x24);
char back[4]{};
UTF8FromUTF16(std::wstring_view(tbuf, tlen), back, sizeof(back));
REQUIRE(strcmp(s, back) == 0);
}
SECTION("UTF16FromUTF8 Example2") {
@ -137,6 +143,9 @@ TEST_CASE("UniConversion") {
const size_t tlen = UTF16FromUTF8(s, tbuf, 1);
REQUIRE(tlen == 1U);
REQUIRE(tbuf[0] == 0xA2);
char back[4]{};
UTF8FromUTF16(std::wstring_view(tbuf, tlen), back, sizeof(back));
REQUIRE(strcmp(s, back) == 0);
}
SECTION("UTF16FromUTF8 Example3") {
@ -145,6 +154,9 @@ TEST_CASE("UniConversion") {
const size_t tlen = UTF16FromUTF8(s, tbuf, 1);;
REQUIRE(tlen == 1U);
REQUIRE(tbuf[0] == 0x20AC);
char back[4]{};
UTF8FromUTF16(std::wstring_view(tbuf, tlen), back, sizeof(back));
REQUIRE(strcmp(s, back) == 0);
}
SECTION("UTF16FromUTF8 Example4") {
@ -154,6 +166,9 @@ TEST_CASE("UniConversion") {
REQUIRE(tlen == 2U);
REQUIRE(tbuf[0] == 0xD800);
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") {
@ -165,6 +180,7 @@ TEST_CASE("UniConversion") {
REQUIRE(tbuf[1] == 0xB5);
REQUIRE(tbuf[2] == 'y');
REQUIRE(tbuf[3] == 'z');
// Invalid so can't round trip
}
SECTION("UTF16FromUTF8 Invalid Lead byte at end") {
@ -174,6 +190,7 @@ TEST_CASE("UniConversion") {
REQUIRE(tlen == 2U);
REQUIRE(tbuf[0] == 'a');
REQUIRE(tbuf[1] == 0xC2);
// Invalid so can't round trip
}
SECTION("UTF16FromUTF8 Invalid Lead byte implies 3 trails but only 2") {
@ -183,6 +200,7 @@ TEST_CASE("UniConversion") {
REQUIRE(tlen == 2U);
REQUIRE(tbuf[0] == 'a');
REQUIRE(tbuf[1] == 0xF1);
// Invalid so can't round trip
}
// UTF32FromUTF8
@ -193,6 +211,9 @@ TEST_CASE("UniConversion") {
const size_t tlen = UTF32FromUTF8(s, tbuf, 1);
REQUIRE(tlen == 1U);
REQUIRE(tbuf[0] == static_cast<unsigned int>('a'));
char back[5]{};
UTF8FromUTF32Character(tbuf[0], back);
REQUIRE(strcmp(s, back) == 0);
}
SECTION("UTF32FromUTF8 Example1") {
@ -201,6 +222,9 @@ TEST_CASE("UniConversion") {
const size_t tlen = UTF32FromUTF8(s, tbuf, 1);
REQUIRE(tlen == 1U);
REQUIRE(tbuf[0] == 0x24);
char back[5]{};
UTF8FromUTF32Character(tbuf[0], back);
REQUIRE(strcmp(s, back) == 0);
}
SECTION("UTF32FromUTF8 Example2") {
@ -209,6 +233,9 @@ TEST_CASE("UniConversion") {
const size_t tlen = UTF32FromUTF8(s, tbuf, 1);
REQUIRE(tlen == 1U);
REQUIRE(tbuf[0] == 0xA2);
char back[5]{};
UTF8FromUTF32Character(tbuf[0], back);
REQUIRE(strcmp(s, back) == 0);
}
SECTION("UTF32FromUTF8 Example3") {
@ -217,6 +244,9 @@ TEST_CASE("UniConversion") {
const size_t tlen = UTF32FromUTF8(s, tbuf, 1);
REQUIRE(tlen == 1U);
REQUIRE(tbuf[0] == 0x20AC);
char back[5]{};
UTF8FromUTF32Character(tbuf[0], back);
REQUIRE(strcmp(s, back) == 0);
}
SECTION("UTF32FromUTF8 Example4") {
@ -225,6 +255,9 @@ TEST_CASE("UniConversion") {
const size_t tlen = UTF32FromUTF8(s, tbuf, 1);
REQUIRE(tlen == 1U);
REQUIRE(tbuf[0] == 0x10348);
char back[5]{};
UTF8FromUTF32Character(tbuf[0], back);
REQUIRE(strcmp(s, back) == 0);
}
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
#define USER_DEFAULT_SCREEN_DPI 96
#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;
@ -24,6 +28,10 @@ constexpr RECT RectFromPRectangle(PRectangle prc) noexcept {
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 {
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 };
}
ColourRGBA ColourFromSys(int nIndex) noexcept;
constexpr HWND HwndFromWindowID(WindowID wid) noexcept {
return static_cast<HWND>(wid);
}
@ -44,6 +54,10 @@ inline HWND HwndFromWindow(const Window &w) noexcept {
return HwndFromWindowID(w.GetID());
}
extern HINSTANCE hinstPlatformRes;
UINT CodePageFromCharSet(CharacterSet characterSet, UINT documentCodePage) noexcept;
void *PointerFromWindow(HWND hWnd) noexcept;
void SetWindowPointer(HWND hWnd, void *ptr) noexcept;
@ -54,8 +68,22 @@ float GetDeviceScaleFactorWhenGdiScalingActive(HWND hWnd) noexcept;
int SystemMetricsForDpi(int nIndex, UINT dpi) noexcept;
void AdjustWindowRectForDpi(LPRECT lpRect, DWORD dwStyle, 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 {
int wheelDelta = 0;
public:
@ -70,29 +98,89 @@ public:
}
};
#if defined(USE_D2D)
extern bool LoadD2D() 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;
// Both GDI and DirectWrite can produce a HFONT for use in list boxes
struct FontWin : public Font {
[[nodiscard]] virtual HFONT HFont() const noexcept = 0;
[[nodiscard]] virtual std::unique_ptr<FontWin> Duplicate() const = 0;
[[nodiscard]] virtual CharacterSet GetCharacterSet() const noexcept = 0;
};
struct ISetRenderingParams {
virtual void SetRenderingParams(std::shared_ptr<RenderingParams> renderingParams_) = 0;
// Buffer to hold strings and string position arrays without always allocating on heap.
// 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>
#define VERSION_SCINTILLA "5.5.5"
#define VERSION_WORDS 5, 5, 5, 0
#define VERSION_SCINTILLA "5.5.6"
#define VERSION_WORDS 5, 5, 6, 0
VS_VERSION_INFO VERSIONINFO
FILEVERSION VERSION_WORDS

View File

@ -195,6 +195,9 @@
<ClCompile Include="..\..\boostregex\*.cxx" />
<ClCompile Include="..\win32\HanjaDic.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\ScintillaDLL.cxx" />
</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;
}
inline void ReleaseLibrary(HMODULE &hLib) noexcept {
if (hLib) {
FreeLibrary(hLib);
hLib = {};
}
}
}
#endif

View File

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

View File

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

View File

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

View File

@ -10,7 +10,7 @@
.SUFFIXES: .cxx
DIR_O=.
DIR_O=obj
DIR_BIN=..\bin
COMPONENT=$(DIR_BIN)\Scintilla.dll
@ -18,6 +18,10 @@ LIBSCI=$(DIR_BIN)\libscintilla.lib
LD=link
!IF "$(PLATFORM:64=)" == "arm"
ARM64=1
!ENDIF
!IFDEF SUPPORT_XP
ADD_DEFINE=-D_USING_V110_SDK71_
# 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
!ENDIF
!ELSE
CETCOMPAT=-CETCOMPAT
!IFDEF ARM64
ADD_DEFINE=-D_ARM64_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE=1
SUBSYSTEM=-SUBSYSTEM:WINDOWS,10.00
!ELSE
CETCOMPAT=-CETCOMPAT
!ENDIF
!ENDIF
@ -65,7 +70,10 @@ CXXFLAGS=$(CXXFLAGS) $(CXXNDEBUG)
INCLUDES=-I../include -I../src
CXXFLAGS=$(CXXFLAGS) $(INCLUDES)
all: $(COMPONENT) $(LIBSCI)
all: $(DIR_O) $(COMPONENT) $(LIBSCI)
$(DIR_O):
mkdir "$(DIR_O)" 2>NUL || cd .
clean:
-del /q $(DIR_O)\*.obj $(DIR_O)\*.pdb $(DIR_O)\*.asm $(COMPONENT) \
@ -117,6 +125,9 @@ SRC_OBJS=\
COMPONENT_OBJS = \
$(DIR_O)\HanjaDic.obj \
$(DIR_O)\PlatWin.obj \
$(DIR_O)\ListBox.obj \
$(DIR_O)\SurfaceGDI.obj \
$(DIR_O)\SurfaceD2D.obj \
$(DIR_O)\ScintillaBase.obj \
$(DIR_O)\ScintillaWin.obj \
$(SRC_OBJS)