chore: bump scintilla and lexilla version

This commit is contained in:
2025-10-12 13:51:32 +08:00
parent 9fb3681e3a
commit db20417ce7
1093 changed files with 138943 additions and 128144 deletions

View File

@ -0,0 +1,35 @@
#!/usr/bin/env python3
# DepGen.py - produce a make dependencies file for Scintilla
# Copyright 2019 by Neil Hodgson <neilh@scintilla.org>
# The License.txt file describes the conditions under which this software may be distributed.
# Requires Python 3.6 or later
import sys
sys.path.append("..")
from scripts import Dependencies
topComment = "# Created by DepGen.py. To recreate, run DepGen.py.\n"
def Generate():
sources = ["../src/*.cxx"]
includes = ["../include", "../src"]
# Create the dependencies file for g++
deps = Dependencies.FindDependencies(["../win32/*.cxx"] + sources, ["../win32"] + includes, ".o", "../win32/")
# Place the objects in $(DIR_O)
deps = [["$(DIR_O)/"+obj, headers] for obj, headers in deps]
Dependencies.UpdateDependencies("../win32/deps.mak", deps, topComment)
# Create the dependencies file for MSVC
# Place the objects in $(DIR_O) and change extension from ".o" to ".obj"
deps = [["$(DIR_O)/"+Dependencies.PathStem(obj)+".obj", headers] for obj, headers in deps]
Dependencies.UpdateDependencies("../win32/nmdeps.mak", deps, topComment)
if __name__ == "__main__":
Generate()

View File

@ -0,0 +1,159 @@
// Scintilla source code edit control
/** @file HanjaDic.cxx
** Korean Hanja Dictionary
** Convert between Korean Hanja and Hangul by COM interface.
**/
// Copyright 2015 by Neil Hodgson <neilh@scintilla.org>
// The License.txt file describes the conditions under which this software may be distributed.
#include <string>
#include <string_view>
#include <memory>
#define WIN32_LEAN_AND_MEAN 1
#include <windows.h>
#include <ole2.h>
#include "WinTypes.h"
#include "HanjaDic.h"
namespace Scintilla::Internal::HanjaDict {
interface IRadical;
interface IHanja;
interface IStrokes;
enum HANJA_TYPE { HANJA_UNKNOWN = 0, HANJA_K0 = 1, HANJA_K1 = 2, HANJA_OTHER = 3 };
interface IHanjaDic : IUnknown {
STDMETHOD(OpenMainDic)();
STDMETHOD(CloseMainDic)();
STDMETHOD(GetHanjaWords)(BSTR bstrHangul, SAFEARRAY* ppsaHanja, VARIANT_BOOL* pfFound);
STDMETHOD(GetHanjaChars)(unsigned short wchHangul, BSTR* pbstrHanjaChars, VARIANT_BOOL* pfFound);
STDMETHOD(HanjaToHangul)(BSTR bstrHanja, BSTR* pbstrHangul);
STDMETHOD(GetHanjaType)(unsigned short wchHanja, HANJA_TYPE* pHanjaType);
STDMETHOD(GetHanjaSense)(unsigned short wchHanja, BSTR* pbstrSense);
STDMETHOD(GetRadicalID)(short SeqNumOfRadical, short* pRadicalID, unsigned short* pwchRadical);
STDMETHOD(GetRadical)(short nRadicalID, IRadical** ppIRadical);
STDMETHOD(RadicalIDToHanja)(short nRadicalID, unsigned short* pwchRadical);
STDMETHOD(GetHanja)(unsigned short wchHanja, IHanja** ppIHanja);
STDMETHOD(GetStrokes)(short nStrokes, IStrokes** ppIStrokes);
STDMETHOD(OpenDefaultCustomDic)();
STDMETHOD(OpenCustomDic)(BSTR bstrPath, long* plUdr);
STDMETHOD(CloseDefaultCustomDic)();
STDMETHOD(CloseCustomDic)(long lUdr);
STDMETHOD(CloseAllCustomDics)();
STDMETHOD(GetDefaultCustomHanjaWords)(BSTR bstrHangul, SAFEARRAY** ppsaHanja, VARIANT_BOOL* pfFound);
STDMETHOD(GetCustomHanjaWords)(long lUdr, BSTR bstrHangul, SAFEARRAY** ppsaHanja, VARIANT_BOOL* pfFound);
STDMETHOD(PutDefaultCustomHanjaWord)(BSTR bstrHangul, BSTR bstrHanja);
STDMETHOD(PutCustomHanjaWord)(long lUdr, BSTR bstrHangul, BSTR bstrHanja);
STDMETHOD(MaxNumOfRadicals)(short* pVal);
STDMETHOD(MaxNumOfStrokes)(short* pVal);
STDMETHOD(DefaultCustomDic)(long* pVal);
STDMETHOD(DefaultCustomDic)(long pVal);
STDMETHOD(MaxHanjaType)(HANJA_TYPE* pHanjaType);
STDMETHOD(MaxHanjaType)(HANJA_TYPE pHanjaType);
};
extern "C" const GUID __declspec(selectany) IID_IHanjaDic =
{ 0xad75f3ac, 0x18cd, 0x48c6, { 0xa2, 0x7d, 0xf1, 0xe9, 0xa7, 0xdc, 0xe4, 0x32 } };
class ScopedBSTR {
BSTR bstr = nullptr;
public:
ScopedBSTR() noexcept = default;
explicit ScopedBSTR(const OLECHAR *psz) noexcept :
bstr(SysAllocString(psz)) {
}
explicit ScopedBSTR(OLECHAR character) noexcept :
bstr(SysAllocStringLen(&character, 1)) {
}
// Deleted so ScopedBSTR objects can not be copied. Moves are OK.
ScopedBSTR(const ScopedBSTR &) = delete;
ScopedBSTR &operator=(const ScopedBSTR &) = delete;
// Moves are OK.
ScopedBSTR(ScopedBSTR &&) = default;
ScopedBSTR &operator=(ScopedBSTR &&) = default;
~ScopedBSTR() {
SysFreeString(bstr);
}
BSTR get() const noexcept {
return bstr;
}
void reset(BSTR value=nullptr) noexcept {
// https://en.cppreference.com/w/cpp/memory/unique_ptr/reset
BSTR const old = bstr;
bstr = value;
SysFreeString(old);
}
};
class HanjaDic {
std::unique_ptr<IHanjaDic, UnknownReleaser> HJinterface;
bool OpenHanjaDic(LPCOLESTR lpszProgID) noexcept {
CLSID CLSID_HanjaDic;
HRESULT hr = CLSIDFromProgID(lpszProgID, &CLSID_HanjaDic);
if (SUCCEEDED(hr)) {
IHanjaDic *instance = nullptr;
hr = CoCreateInstance(CLSID_HanjaDic, nullptr,
CLSCTX_INPROC_SERVER, IID_IHanjaDic,
reinterpret_cast<LPVOID *>(&instance));
if (SUCCEEDED(hr) && instance) {
HJinterface.reset(instance);
hr = instance->OpenMainDic();
return SUCCEEDED(hr);
}
}
return false;
}
public:
bool Open() noexcept {
return OpenHanjaDic(OLESTR("imkrhjd.hanjadic"))
|| OpenHanjaDic(OLESTR("mshjdic.hanjadic"));
}
void Close() const noexcept {
HJinterface->CloseMainDic();
}
bool IsHanja(wchar_t hanja) const noexcept {
HANJA_TYPE hanjaType = HANJA_UNKNOWN;
const HRESULT hr = HJinterface->GetHanjaType(hanja, &hanjaType);
return SUCCEEDED(hr) && hanjaType > HANJA_UNKNOWN;
}
bool HanjaToHangul(const ScopedBSTR &bstrHanja, ScopedBSTR &bstrHangul) const noexcept {
BSTR result = nullptr;
const HRESULT hr = HJinterface->HanjaToHangul(bstrHanja.get(), &result);
bstrHangul.reset(result);
return SUCCEEDED(hr);
}
};
bool GetHangulOfHanja(std::wstring &inout) noexcept {
// Convert every hanja to hangul.
// Return whether any character been converted.
// Hanja linked to different notes in Hangul have different codes,
// so current character based conversion is enough.
// great thanks for BLUEnLIVE.
bool changed = false;
HanjaDic dict;
if (dict.Open()) {
for (wchar_t &character : inout) {
if (dict.IsHanja(character)) { // Pass hanja only!
ScopedBSTR bstrHangul;
if (dict.HanjaToHangul(ScopedBSTR(character), bstrHangul)) {
changed = true;
character = *(bstrHangul.get());
}
}
}
dict.Close();
}
return changed;
}
}

View File

@ -0,0 +1,22 @@
// Scintilla source code edit control
/** @file HanjaDic.h
** Korean Hanja Dictionary
** Convert between Korean Hanja and Hangul by COM interface.
**/
// Copyright 2015 by Neil Hodgson <neilh@scintilla.org>
// The License.txt file describes the conditions under which this software may be distributed.
#ifndef HANJADIC_H
#define HANJADIC_H
namespace Scintilla::Internal {
namespace HanjaDict {
bool GetHangulOfHanja(std::wstring &inout) noexcept;
}
}
#endif

View File

@ -0,0 +1,991 @@
// 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 {
void *PtrFromLParam(Scintilla::sptr_t lParam) noexcept {
return reinterpret_cast<void *>(lParam);
}
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;
WNDPROC prevWndProc{};
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();
LRESULT PASCAL ListProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam);
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::ListProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam) {
try {
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));
OnSelChange();
}
}
return 0;
case WM_LBUTTONUP:
return 0;
case WM_LBUTTONDBLCLK:
OnDoubleClick();
return 0;
case WM_MBUTTONDOWN:
// disable the scroll wheel button click action
return 0;
default:
break;
}
if (prevWndProc) {
return ::CallWindowProc(prevWndProc, hWnd, iMessage, wParam, lParam);
}
} catch (...) {
}
return ::DefWindowProc(hWnd, iMessage, wParam, lParam);
}
LRESULT PASCAL ListBoxX::ControlWndProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam) {
if (ListBoxX *lbx = static_cast<ListBoxX *>(PointerFromWindow(::GetParent(hWnd)))) {
return lbx->ListProc(hWnd, iMessage, wParam, lParam);
}
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 = ::CreateWindowExW(
0, WC_LISTBOXW, L"",
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);
prevWndProc = SubclassWindow(lb, ControlWndProc);
}
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 = static_cast<MEASUREITEMSTRUCT *>(PtrFromLParam(lParam));
pMeasureItem->itemHeight = ItemHeight();
}
break;
case WM_DRAWITEM:
Draw(static_cast<DRAWITEMSTRUCT *>(PtrFromLParam(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 = static_cast<MINMAXINFO*>(PtrFromLParam(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 = static_cast<CREATESTRUCT *>(PtrFromLParam(lParam));
SetWindowPointer(hWnd, pCreate->lpCreateParams);
}
// Find C++ object associated with window.
if (ListBoxX *lbx = static_cast<ListBoxX *>(PointerFromWindow(hWnd))) {
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;
}

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

View File

@ -0,0 +1,792 @@
// Scintilla source code edit control
/** @file PlatWin.cxx
** Implementation of platform facilities 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;
namespace Scintilla::Internal {
HINSTANCE hinstPlatformRes{};
void *PointerFromWindow(HWND hWnd) noexcept {
return reinterpret_cast<void *>(::GetWindowLongPtr(hWnd, 0));
}
void SetWindowPointer(HWND hWnd, void *ptr) noexcept {
::SetWindowLongPtr(hWnd, 0, reinterpret_cast<LONG_PTR>(ptr));
}
namespace {
// system DPI, same for all monitor.
UINT uSystemDPI = USER_DEFAULT_SCREEN_DPI;
using GetDpiForWindowSig = UINT(WINAPI *)(HWND hwnd);
GetDpiForWindowSig fnGetDpiForWindow = nullptr;
HMODULE hDLLShcore{};
using GetDpiForMonitorSig = HRESULT(WINAPI *)(HMONITOR hmonitor, /*MONITOR_DPI_TYPE*/int dpiType, UINT *dpiX, UINT *dpiY);
GetDpiForMonitorSig fnGetDpiForMonitor = nullptr;
using GetSystemMetricsForDpiSig = int(WINAPI *)(int nIndex, UINT dpi);
GetSystemMetricsForDpiSig fnGetSystemMetricsForDpi = nullptr;
using AdjustWindowRectExForDpiSig = BOOL(WINAPI *)(LPRECT lpRect, DWORD dwStyle, BOOL bMenu, DWORD dwExStyle, UINT dpi);
AdjustWindowRectExForDpiSig fnAdjustWindowRectExForDpi = nullptr;
using AreDpiAwarenessContextsEqualSig = BOOL(WINAPI *)(DPI_AWARENESS_CONTEXT, DPI_AWARENESS_CONTEXT);
AreDpiAwarenessContextsEqualSig fnAreDpiAwarenessContextsEqual = nullptr;
using GetWindowDpiAwarenessContextSig = DPI_AWARENESS_CONTEXT(WINAPI *)(HWND);
GetWindowDpiAwarenessContextSig fnGetWindowDpiAwarenessContext = nullptr;
using GetScaleFactorForMonitorSig = HRESULT(WINAPI *)(HMONITOR, DEVICE_SCALE_FACTOR *);
GetScaleFactorForMonitorSig fnGetScaleFactorForMonitor = nullptr;
using SetThreadDpiAwarenessContextSig = DPI_AWARENESS_CONTEXT(WINAPI *)(DPI_AWARENESS_CONTEXT);
SetThreadDpiAwarenessContextSig fnSetThreadDpiAwarenessContext = nullptr;
void LoadDpiForWindow() noexcept {
HMODULE user32 = ::GetModuleHandleW(L"user32.dll");
fnGetDpiForWindow = DLLFunction<GetDpiForWindowSig>(user32, "GetDpiForWindow");
fnGetSystemMetricsForDpi = DLLFunction<GetSystemMetricsForDpiSig>(user32, "GetSystemMetricsForDpi");
fnAdjustWindowRectExForDpi = DLLFunction<AdjustWindowRectExForDpiSig>(user32, "AdjustWindowRectExForDpi");
fnSetThreadDpiAwarenessContext = DLLFunction<SetThreadDpiAwarenessContextSig>(user32, "SetThreadDpiAwarenessContext");
using GetDpiForSystemSig = UINT(WINAPI *)(void);
GetDpiForSystemSig fnGetDpiForSystem = DLLFunction<GetDpiForSystemSig>(user32, "GetDpiForSystem");
if (fnGetDpiForSystem) {
uSystemDPI = fnGetDpiForSystem();
} else {
HDC hdcMeasure = ::CreateCompatibleDC({});
uSystemDPI = ::GetDeviceCaps(hdcMeasure, LOGPIXELSY);
::DeleteDC(hdcMeasure);
}
fnGetWindowDpiAwarenessContext = DLLFunction<GetWindowDpiAwarenessContextSig>(user32, "GetWindowDpiAwarenessContext");
fnAreDpiAwarenessContextsEqual = DLLFunction<AreDpiAwarenessContextsEqualSig>(user32, "AreDpiAwarenessContextsEqual");
hDLLShcore = ::LoadLibraryExW(L"shcore.dll", {}, LOAD_LIBRARY_SEARCH_SYSTEM32);
if (hDLLShcore) {
fnGetScaleFactorForMonitor = DLLFunction<GetScaleFactorForMonitorSig>(hDLLShcore, "GetScaleFactorForMonitor");
fnGetDpiForMonitor = DLLFunction<GetDpiForMonitorSig>(hDLLShcore, "GetDpiForMonitor");
}
}
}
HMONITOR MonitorFromWindowHandleScaling(HWND hWnd) noexcept {
constexpr DWORD monitorFlags = MONITOR_DEFAULTTONEAREST;
if (!fnSetThreadDpiAwarenessContext) {
return ::MonitorFromWindow(hWnd, monitorFlags);
}
// Temporarily switching to PerMonitorV2 to retrieve correct monitor via MonitorFromRect() in case of active GDI scaling.
const DPI_AWARENESS_CONTEXT oldContext = fnSetThreadDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2);
PLATFORM_ASSERT(oldContext != nullptr);
RECT rect;
::GetWindowRect(hWnd, &rect);
const HMONITOR monitor = ::MonitorFromRect(&rect, monitorFlags);
fnSetThreadDpiAwarenessContext(oldContext);
return monitor;
}
float GetDeviceScaleFactorWhenGdiScalingActive(HWND hWnd) noexcept {
if (fnAreDpiAwarenessContextsEqual) {
PLATFORM_ASSERT(fnGetWindowDpiAwarenessContext && fnGetScaleFactorForMonitor);
if (fnAreDpiAwarenessContextsEqual(DPI_AWARENESS_CONTEXT_UNAWARE_GDISCALED, fnGetWindowDpiAwarenessContext(hWnd))) {
const HWND hRootWnd = ::GetAncestor(hWnd, GA_ROOT); // Scale factor applies to entire (root) window.
const HMONITOR hMonitor = MonitorFromWindowHandleScaling(hRootWnd);
DEVICE_SCALE_FACTOR deviceScaleFactor;
if (S_OK == fnGetScaleFactorForMonitor(hMonitor, &deviceScaleFactor))
return static_cast<float>(static_cast<int>(deviceScaleFactor)) / 100.f;
}
}
return 1.f;
}
UINT DpiForWindow(WindowID wid) noexcept {
if (fnGetDpiForWindow) {
return fnGetDpiForWindow(HwndFromWindowID(wid));
}
if (fnGetDpiForMonitor) {
HMONITOR hMonitor = ::MonitorFromWindow(HwndFromWindowID(wid), MONITOR_DEFAULTTONEAREST);
UINT dpiX = 0;
UINT dpiY = 0;
if (fnGetDpiForMonitor(hMonitor, 0 /*MDT_EFFECTIVE_DPI*/, &dpiX, &dpiY) == S_OK) {
return dpiY;
}
}
return uSystemDPI;
}
int SystemMetricsForDpi(int nIndex, UINT dpi) noexcept {
if (fnGetSystemMetricsForDpi) {
return fnGetSystemMetricsForDpi(nIndex, dpi);
}
int value = ::GetSystemMetrics(nIndex);
value = (dpi == uSystemDPI) ? value : ::MulDiv(value, dpi, uSystemDPI);
return value;
}
void AdjustWindowRectForDpi(LPRECT lpRect, DWORD dwStyle, UINT dpi) noexcept {
if (fnAdjustWindowRectExForDpi) {
fnAdjustWindowRectExForDpi(lpRect, dwStyle, false, WS_EX_WINDOWEDGE, dpi);
} else {
::AdjustWindowRectEx(lpRect, dwStyle, false, WS_EX_WINDOWEDGE);
}
}
namespace {
constexpr BITMAPV5HEADER BitMapHeader(int width, int height) noexcept {
constexpr int pixelBits = 32;
// Divide each pixel up in the expected BGRA manner.
// Compatible with DXGI_FORMAT_B8G8R8A8_UNORM.
constexpr DWORD maskRed = 0x00FF0000U;
constexpr DWORD maskGreen = 0x0000FF00U;
constexpr DWORD maskBlue = 0x000000FFU;
constexpr DWORD maskAlpha = 0xFF000000U;
BITMAPV5HEADER bi{};
bi.bV5Size = sizeof(BITMAPV5HEADER);
bi.bV5Width = width;
bi.bV5Height = height;
bi.bV5Planes = 1;
bi.bV5BitCount = pixelBits;
bi.bV5Compression = BI_BITFIELDS;
// The following mask specification specifies a supported 32 BPP alpha format for Windows XP.
bi.bV5RedMask = maskRed;
bi.bV5GreenMask = maskGreen;
bi.bV5BlueMask = maskBlue;
bi.bV5AlphaMask = maskAlpha;
return bi;
}
HBITMAP BitMapSection(HDC hdc, int width, int height, DWORD **pixels) noexcept {
const BITMAPV5HEADER bi = BitMapHeader(width, height);
void *image = nullptr;
HBITMAP hbm = ::CreateDIBSection(hdc, reinterpret_cast<const BITMAPINFO *>(&bi), DIB_RGB_COLORS, &image, {}, 0);
if (pixels) {
*pixels = static_cast<DWORD *>(image);
}
return hbm;
}
}
GDIBitMap::~GDIBitMap() noexcept {
Release();
}
void GDIBitMap::Create(HDC hdcBase, int width, int height, DWORD **pixels) noexcept {
Release();
hdc = CreateCompatibleDC(hdcBase);
if (!hdc) {
return;
}
hbm = BitMapSection(hdc, width, height, pixels);
if (!hbm) {
return;
}
hbmOriginal = SelectBitmap(hdc, hbm);
}
void GDIBitMap::Release() noexcept {
if (hbmOriginal) {
// Deselect HBITMAP from HDC so it may be deleted.
SelectBitmap(hdc, hbmOriginal);
}
hbmOriginal = {};
if (hbm) {
::DeleteObject(hbm);
}
hbm = {};
if (hdc) {
::DeleteDC(hdc);
}
hdc = {};
}
HBITMAP GDIBitMap::Extract() noexcept {
// Deselect HBITMAP from HDC but keep so can delete.
// The caller will make a copy, not take ownership.
HBITMAP ret = hbm;
if (hbmOriginal) {
SelectBitmap(hdc, hbmOriginal);
hbmOriginal = {};
}
return ret;
}
Window::~Window() noexcept = default;
void Window::Destroy() noexcept {
if (wid)
::DestroyWindow(HwndFromWindowID(wid));
wid = nullptr;
}
PRectangle Window::GetPosition() const {
RECT rc;
::GetWindowRect(HwndFromWindowID(wid), &rc);
return PRectangleFromRECT(rc);
}
void Window::SetPosition(PRectangle rc) {
::SetWindowPos(HwndFromWindowID(wid),
{}, static_cast<int>(rc.left), static_cast<int>(rc.top),
static_cast<int>(rc.Width()), static_cast<int>(rc.Height()), SWP_NOZORDER | SWP_NOACTIVATE);
}
namespace {
RECT RectFromMonitor(HMONITOR hMonitor) noexcept {
MONITORINFO mi = {};
mi.cbSize = sizeof(mi);
if (GetMonitorInfo(hMonitor, &mi)) {
return mi.rcWork;
}
RECT rc = {0, 0, 0, 0};
if (::SystemParametersInfoA(SPI_GETWORKAREA, 0, &rc, 0) == 0) {
rc.left = 0;
rc.top = 0;
rc.right = 0;
rc.bottom = 0;
}
return rc;
}
}
void Window::SetPositionRelative(PRectangle rc, const Window *relativeTo) {
const DWORD style = GetWindowStyle(HwndFromWindowID(wid));
if (style & WS_POPUP) {
POINT ptOther = {0, 0};
::ClientToScreen(HwndFromWindow(*relativeTo), &ptOther);
rc.Move(static_cast<XYPOSITION>(ptOther.x), static_cast<XYPOSITION>(ptOther.y));
const RECT rcMonitor = RectFromPRectangle(rc);
HMONITOR hMonitor = MonitorFromRect(&rcMonitor, MONITOR_DEFAULTTONEAREST);
// If hMonitor is NULL, that's just the main screen anyways.
const RECT rcWork = RectFromMonitor(hMonitor);
if (rcWork.left < rcWork.right) {
// Now clamp our desired rectangle to fit inside the work area
// This way, the menu will fit wholly on one screen. An improvement even
// if you don't have a second monitor on the left... Menu's appears half on
// one screen and half on the other are just U.G.L.Y.!
if (rc.right > rcWork.right)
rc.Move(rcWork.right - rc.right, 0);
if (rc.bottom > rcWork.bottom)
rc.Move(0, rcWork.bottom - rc.bottom);
if (rc.left < rcWork.left)
rc.Move(rcWork.left - rc.left, 0);
if (rc.top < rcWork.top)
rc.Move(0, rcWork.top - rc.top);
}
}
SetPosition(rc);
}
PRectangle Window::GetClientPosition() const {
RECT rc={0,0,0,0};
if (wid)
::GetClientRect(HwndFromWindowID(wid), &rc);
return PRectangleFromRECT(rc);
}
void Window::Show(bool show) {
if (show)
::ShowWindow(HwndFromWindowID(wid), SW_SHOWNOACTIVATE);
else
::ShowWindow(HwndFromWindowID(wid), SW_HIDE);
}
void Window::InvalidateAll() {
::InvalidateRect(HwndFromWindowID(wid), nullptr, FALSE);
}
void Window::InvalidateRectangle(PRectangle rc) {
const RECT rcw = RectFromPRectangle(rc);
::InvalidateRect(HwndFromWindowID(wid), &rcw, FALSE);
}
namespace {
std::optional<DWORD> RegGetDWORD(HKEY hKey, LPCWSTR valueName) noexcept {
DWORD value = 0;
DWORD type = REG_NONE;
DWORD size = sizeof(DWORD);
const LSTATUS status = ::RegQueryValueExW(hKey, valueName, nullptr, &type, reinterpret_cast<LPBYTE>(&value), &size);
if (status == ERROR_SUCCESS && type == REG_DWORD) {
return value;
}
return {};
}
class CursorHelper {
GDIBitMap bm;
DWORD *pixels = nullptr;
const int width;
const int height;
const float scale;
static constexpr float baseSize = 32.0f;
static constexpr float arrow[][2] = {
{ 32.0f - 12.73606f,32.0f - 19.04075f },
{ 32.0f - 7.80159f, 32.0f - 19.04075f },
{ 32.0f - 9.82813f, 32.0f - 14.91828f },
{ 32.0f - 6.88341f, 32.0f - 13.42515f },
{ 32.0f - 4.62301f, 32.0f - 18.05872f },
{ 32.0f - 1.26394f, 32.0f - 14.78295f },
{ 32.0f - 1.26394f, 32.0f - 30.57485f },
};
public:
~CursorHelper() = default;
CursorHelper(int width_, int height_) noexcept : width{width_}, height{height_}, scale{ static_cast<float>(width) / baseSize } {
// https://learn.microsoft.com/en-us/windows/win32/menurc/using-cursors#creating-a-cursor
bm.Create({}, width, height, &pixels);
}
[[nodiscard]] explicit operator bool() const noexcept {
return static_cast<bool>(bm);
}
HCURSOR Create() noexcept {
HCURSOR cursor {};
// Create an empty mask bitmap.
HBITMAP hMonoBitmap = ::CreateBitmap(width, height, 1, 1, nullptr);
if (hMonoBitmap) {
// https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-createiconindirect
// hBitmap should not already be selected into a device context
HBITMAP hBitmap = bm.Extract();
ICONINFO info = {false, static_cast<DWORD>(width - 1), 0, hMonoBitmap, hBitmap};
cursor = ::CreateIconIndirect(&info);
::DeleteObject(hMonoBitmap);
}
return cursor;
}
#if defined(USE_D2D)
bool DrawD2D(COLORREF fillColour, COLORREF strokeColour) noexcept {
if (!LoadD2D()) {
return false;
}
const D2D1_RENDER_TARGET_PROPERTIES drtp = D2D1::RenderTargetProperties(
D2D1_RENDER_TARGET_TYPE_DEFAULT,
{ DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_PREMULTIPLIED });
DCRenderTarget pTarget;
HRESULT hr = CreateDCRenderTarget(&drtp, pTarget);
if (FAILED(hr) || !pTarget) {
return false;
}
const RECT rc = {0, 0, width, height};
hr = pTarget->BindDC(bm.DC(), &rc);
if (FAILED(hr)) {
return false;
}
pTarget->BeginDraw();
// Draw something on the bitmap section.
constexpr size_t nPoints = std::size(arrow);
D2D1_POINT_2F points[nPoints]{};
for (size_t i = 0; i < nPoints; i++) {
points[i].x = arrow[i][0] * scale;
points[i].y = arrow[i][1] * scale;
}
const Geometry geometry = GeometryCreate();
if (!geometry) {
return false;
}
const GeometrySink sink = GeometrySinkCreate(geometry.Get());
if (!sink) {
return false;
}
sink->BeginFigure(points[0], D2D1_FIGURE_BEGIN_FILLED);
for (size_t i = 1; i < nPoints; i++) {
sink->AddLine(points[i]);
}
sink->EndFigure(D2D1_FIGURE_END_CLOSED);
hr = sink->Close();
if (FAILED(hr)) {
return false;
}
if (const BrushSolid pBrushFill = BrushSolidCreate(pTarget.Get(), fillColour)) {
pTarget->FillGeometry(geometry.Get(), pBrushFill.Get());
}
if (const BrushSolid pBrushStroke = BrushSolidCreate(pTarget.Get(), strokeColour)) {
pTarget->DrawGeometry(geometry.Get(), pBrushStroke.Get(), scale);
}
hr = pTarget->EndDraw();
return SUCCEEDED(hr);
}
#endif
void Draw(COLORREF fillColour, COLORREF strokeColour) noexcept {
#if defined(USE_D2D)
if (DrawD2D(fillColour, strokeColour)) {
return;
}
#endif
// Draw something on the DIB section.
constexpr size_t nPoints = std::size(arrow);
POINT points[nPoints]{};
for (size_t i = 0; i < nPoints; i++) {
points[i].x = std::lround(arrow[i][0] * scale);
points[i].y = std::lround(arrow[i][1] * scale);
}
const DWORD penWidth = std::lround(scale);
HPEN pen{};
if (penWidth > 1) {
const LOGBRUSH brushParameters { BS_SOLID, strokeColour, 0 };
pen = ::ExtCreatePen(PS_GEOMETRIC | PS_ENDCAP_ROUND | PS_JOIN_MITER,
penWidth,
&brushParameters,
0,
nullptr);
} else {
pen = ::CreatePen(PS_INSIDEFRAME, 1, strokeColour);
}
HPEN penOld = SelectPen(bm.DC(), pen);
HBRUSH brush = ::CreateSolidBrush(fillColour);
HBRUSH brushOld = SelectBrush(bm.DC(), brush);
::Polygon(bm.DC(), points, static_cast<int>(nPoints));
SelectPen(bm.DC(), penOld);
SelectBrush(bm.DC(), brushOld);
::DeleteObject(pen);
::DeleteObject(brush);
// Set the alpha values for each pixel in the cursor.
constexpr DWORD opaque = 0xFF000000U;
for (int i = 0; i < width*height; i++) {
if (*pixels != 0) {
*pixels |= opaque;
}
pixels++;
}
}
};
void ChooseCursor(LPCTSTR cursor) noexcept {
::SetCursor(::LoadCursor({}, cursor));
}
void ChooseCursor(Window::Cursor curs) noexcept {
switch (curs) {
case Window::Cursor::text:
ChooseCursor(IDC_IBEAM);
break;
case Window::Cursor::up:
ChooseCursor(IDC_UPARROW);
break;
case Window::Cursor::wait:
ChooseCursor(IDC_WAIT);
break;
case Window::Cursor::horizontal:
ChooseCursor(IDC_SIZEWE);
break;
case Window::Cursor::vertical:
ChooseCursor(IDC_SIZENS);
break;
case Window::Cursor::hand:
ChooseCursor(IDC_HAND);
break;
case Window::Cursor::reverseArrow:
case Window::Cursor::arrow:
case Window::Cursor::invalid: // Should not occur, but just in case.
default:
ChooseCursor(IDC_ARROW);
break;
}
}
}
HCURSOR LoadReverseArrowCursor(UINT dpi) noexcept {
// https://learn.microsoft.com/en-us/answers/questions/815036/windows-cursor-size
constexpr DWORD defaultCursorBaseSize = 32;
constexpr DWORD maxCursorBaseSize = 16*(1 + 15); // 16*(1 + CursorSize)
DWORD cursorBaseSize = 0;
HKEY hKey {};
LSTATUS status = ::RegOpenKeyExW(HKEY_CURRENT_USER, L"Control Panel\\Cursors", 0, KEY_QUERY_VALUE, &hKey);
if (status == ERROR_SUCCESS) {
if (std::optional<DWORD> baseSize = RegGetDWORD(hKey, L"CursorBaseSize")) {
// CursorBaseSize is multiple of 16
cursorBaseSize = std::min(*baseSize & ~15, maxCursorBaseSize);
}
::RegCloseKey(hKey);
}
int width = 0;
int height = 0;
if (cursorBaseSize > defaultCursorBaseSize) {
width = ::MulDiv(cursorBaseSize, dpi, USER_DEFAULT_SCREEN_DPI);
height = width;
} else {
width = SystemMetricsForDpi(SM_CXCURSOR, dpi);
height = SystemMetricsForDpi(SM_CYCURSOR, dpi);
PLATFORM_ASSERT(width == height);
}
CursorHelper cursorHelper(width, height);
if (!cursorHelper) {
return {};
}
COLORREF fillColour = RGB(0xff, 0xff, 0xfe);
COLORREF strokeColour = RGB(0, 0, 1);
status = ::RegOpenKeyExW(HKEY_CURRENT_USER, L"Software\\Microsoft\\Accessibility", 0, KEY_QUERY_VALUE, &hKey);
if (status == ERROR_SUCCESS) {
if (std::optional<DWORD> cursorType = RegGetDWORD(hKey, L"CursorType")) {
switch (*cursorType) {
case 1: // black
case 4: // black
std::swap(fillColour, strokeColour);
break;
case 6: // custom
if (std::optional<DWORD> cursorColor = RegGetDWORD(hKey, L"CursorColor")) {
fillColour = *cursorColor;
}
break;
default: // 0, 3 white, 2, 5 invert
break;
}
}
::RegCloseKey(hKey);
}
cursorHelper.Draw(fillColour, strokeColour);
HCURSOR cursor = cursorHelper.Create();
return cursor;
}
void Window::SetCursor(Cursor curs) {
ChooseCursor(curs);
}
/* Returns rectangle of monitor pt is on, both rect and pt are in Window's
coordinates */
PRectangle Window::GetMonitorRect(Point pt) {
const PRectangle rcPosition = GetPosition();
const POINT ptDesktop = {static_cast<LONG>(pt.x + rcPosition.left),
static_cast<LONG>(pt.y + rcPosition.top)};
HMONITOR hMonitor = MonitorFromPoint(ptDesktop, MONITOR_DEFAULTTONEAREST);
const RECT rcWork = RectFromMonitor(hMonitor);
if (rcWork.left < rcWork.right) {
PRectangle rcMonitor(
rcWork.left - rcPosition.left,
rcWork.top - rcPosition.top,
rcWork.right - rcPosition.left,
rcWork.bottom - rcPosition.top);
return rcMonitor;
}
return PRectangle();
}
Menu::Menu() noexcept : mid{} {
}
void Menu::CreatePopUp() {
Destroy();
mid = ::CreatePopupMenu();
}
void Menu::Destroy() noexcept {
if (mid)
::DestroyMenu(static_cast<HMENU>(mid));
mid = {};
}
void Menu::Show(Point pt, const Window &w) {
::TrackPopupMenu(static_cast<HMENU>(mid),
TPM_RIGHTBUTTON, static_cast<int>(pt.x - 4), static_cast<int>(pt.y), 0,
HwndFromWindow(w), nullptr);
Destroy();
}
ColourRGBA ColourFromSys(int nIndex) noexcept {
const DWORD colourValue = ::GetSysColor(nIndex);
return ColourRGBA::FromRGB(colourValue);
}
ColourRGBA Platform::Chrome() {
return ColourFromSys(COLOR_3DFACE);
}
ColourRGBA Platform::ChromeHighlight() {
return ColourFromSys(COLOR_3DHIGHLIGHT);
}
const char *Platform::DefaultFont() {
return "Verdana";
}
int Platform::DefaultFontSize() {
return 8;
}
unsigned int Platform::DoubleClickTime() {
return ::GetDoubleClickTime();
}
void Platform::DebugDisplay(const char *s) noexcept {
::OutputDebugStringA(s);
}
//#define TRACE
#ifdef TRACE
void Platform::DebugPrintf(const char *format, ...) noexcept {
char buffer[2000];
va_list pArguments;
va_start(pArguments, format);
vsnprintf(buffer, std::size(buffer), format, pArguments);
va_end(pArguments);
Platform::DebugDisplay(buffer);
}
#else
void Platform::DebugPrintf(const char *, ...) noexcept {
}
#endif
namespace {
bool assertionPopUps = true;
}
bool Platform::ShowAssertionPopUps(bool assertionPopUps_) noexcept {
const bool ret = assertionPopUps;
assertionPopUps = assertionPopUps_;
return ret;
}
void Platform::Assert(const char *c, const char *file, int line) noexcept {
char buffer[2000] {};
snprintf(buffer, std::size(buffer), "Assertion [%s] failed at %s %d%s", c, file, line, assertionPopUps ? "" : "\r\n");
if (assertionPopUps) {
const int idButton = ::MessageBoxA({}, buffer, "Assertion failure",
MB_ABORTRETRYIGNORE|MB_ICONHAND|MB_SETFOREGROUND|MB_TASKMODAL);
if (idButton == IDRETRY) {
::DebugBreak();
} else if (idButton == IDIGNORE) {
// all OK
} else {
abort();
}
} else {
Platform::DebugDisplay(buffer);
::DebugBreak();
abort();
}
}
void Platform_Initialise(void *hInstance) noexcept {
hinstPlatformRes = static_cast<HINSTANCE>(hInstance);
LoadDpiForWindow();
ListBoxX_Register();
}
void Platform_Finalise(bool fromDllMain) noexcept {
if (!fromDllMain) {
#if defined(USE_D2D)
ReleaseD2D();
#endif
ReleaseLibrary(hDLLShcore);
}
ListBoxX_Unregister();
}
}

View File

@ -0,0 +1,187 @@
// Scintilla source code edit control
/** @file PlatWin.h
** Implementation of platform facilities 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 PLATWIN_H
#define PLATWIN_H
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;
extern void Platform_Finalise(bool fromDllMain) noexcept;
constexpr RECT RectFromPRectangle(PRectangle prc) noexcept {
const RECT rc = { static_cast<LONG>(prc.left), static_cast<LONG>(prc.top),
static_cast<LONG>(prc.right), static_cast<LONG>(prc.bottom) };
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) };
}
constexpr Point PointFromPOINT(POINT pt) noexcept {
return Point::FromInts(pt.x, pt.y);
}
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);
}
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;
HMONITOR MonitorFromWindowHandleScaling(HWND hWnd) noexcept;
UINT DpiForWindow(WindowID wid) noexcept;
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:
bool Accumulate(WPARAM wParam) noexcept {
wheelDelta -= GET_WHEEL_DELTA_WPARAM(wParam);
return std::abs(wheelDelta) >= WHEEL_DELTA;
}
int Actions() noexcept {
const int actions = wheelDelta / WHEEL_DELTA;
wheelDelta = wheelDelta % WHEEL_DELTA;
return actions;
}
};
// 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;
};
// 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

@ -0,0 +1,21 @@
command.build.SConstruct=scons.bat .
command.name.1.SConstruct=scons clean
command.1.SConstruct=scons.bat --clean .
command.build.*.mak=nmake -f $(FileNameExt) DEBUG=1 QUIET=1
command.name.1.*.mak=nmake clean
command.1.*.mak=nmake -f $(FileNameExt) clean
command.name.2.*.mak=Borland Make
command.2.*.mak=make -f $(FileNameExt)
command.subsystem.2.*.mak=0
command.name.3.*.mak=make clean
command.3.*.mak=make -f $(FileNameExt) clean
command.name.4.*.mak=make debug
command.4.*.mak=make DEBUG=1 -f $(FileNameExt)
command.name.5.*.mak=nmake debug
command.5.*.mak=nmake DEBUG=1 -f $(FileNameExt)
# SciTE.properties is the per directory local options file and can be used to override
# settings made in SciTEGlobal.properties
command.build.*.cxx=nmake -f scintilla.mak DEBUG=1 QUIET=1
command.build.*.h=nmake -f scintilla.mak DEBUG=1 QUIET=1
command.build.*.rc=nmake -f scintilla.mak DEBUG=1 QUIET=1

View File

@ -0,0 +1,37 @@
// Resource file for Scintilla
// Copyright 1998-2010 by Neil Hodgson <neilh@scintilla.org>
// The License.txt file describes the conditions under which this software may be distributed.
#include <windows.h>
#define VERSION_SCINTILLA "5.5.7"
#define VERSION_WORDS 5, 5, 7, 0
VS_VERSION_INFO VERSIONINFO
FILEVERSION VERSION_WORDS
PRODUCTVERSION VERSION_WORDS
FILEFLAGSMASK 0x3fL
FILEFLAGS 0
FILEOS VOS_NT_WINDOWS32
FILETYPE VFT_APP
FILESUBTYPE VFT2_UNKNOWN
BEGIN
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x409, 1200
END
BLOCK "StringFileInfo"
BEGIN
BLOCK "040904b0"
BEGIN
VALUE "CompanyName", "Neil Hodgson neilh@scintilla.org\0"
VALUE "FileDescription", "Scintilla.DLL - a Source Editing Component\0"
VALUE "FileVersion", VERSION_SCINTILLA "\0"
VALUE "InternalName", "Scintilla\0"
VALUE "LegalCopyright", "Copyright 1998-2012 by Neil Hodgson\0"
VALUE "OriginalFilename", "Scintilla.DLL\0"
VALUE "ProductName", "Scintilla\0"
VALUE "ProductVersion", VERSION_SCINTILLA "\0"
END
END
END

View File

@ -0,0 +1,2 @@
EXPORTS
Scintilla_DirectFunction

View File

@ -0,0 +1,189 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|ARM64">
<Configuration>Debug</Configuration>
<Platform>ARM64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|ARM64">
<Configuration>Release</Configuration>
<Platform>ARM64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{19CCA8B8-46B9-4609-B7CE-198DA19F07BD}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<RootNamespace>Scintilla</RootNamespace>
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup>
<ConfigurationType>DynamicLibrary</ConfigurationType>
<CharacterSet>Unicode</CharacterSet>
<PlatformToolset>v143</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<UseDebugLibraries>true</UseDebugLibraries>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<UseDebugLibraries>true</UseDebugLibraries>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'" Label="Configuration">
<UseDebugLibraries>true</UseDebugLibraries>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<UseDebugLibraries>false</UseDebugLibraries>
<WholeProgramOptimization>true</WholeProgramOptimization>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<UseDebugLibraries>false</UseDebugLibraries>
<WholeProgramOptimization>true</WholeProgramOptimization>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'" Label="Configuration">
<UseDebugLibraries>false</UseDebugLibraries>
<WholeProgramOptimization>true</WholeProgramOptimization>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup>
<LinkIncremental>false</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<IntDir>Intermediates\$(Platform)\$(Configuration)\</IntDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<IntDir>Intermediates\$(Platform)\$(Configuration)\</IntDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<IntDir>Intermediates\$(Configuration)\</IntDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<IntDir>Intermediates\$(Configuration)\</IntDir>
</PropertyGroup>
<ItemDefinitionGroup>
<ClCompile>
<WarningLevel>Level4</WarningLevel>
<PreprocessorDefinitions>_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>..\include;..\src;</AdditionalIncludeDirectories>
<BrowseInformation>true</BrowseInformation>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
<MinimalRebuild>false</MinimalRebuild>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<AdditionalOptions>/source-charset:utf-8 %(AdditionalOptions)</AdditionalOptions>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>gdi32.lib;imm32.lib;ole32.lib;oleaut32.lib;advapi32.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<LanguageStandard>stdcpp17</LanguageStandard>
</ClCompile>
<Link>
<LinkTimeCodeGeneration>Default</LinkTimeCodeGeneration>
<CETCompat>true</CETCompat>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<LanguageStandard>stdcpp17</LanguageStandard>
</ClCompile>
<Link>
<LinkTimeCodeGeneration>Default</LinkTimeCodeGeneration>
<CETCompat>true</CETCompat>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">
<ClCompile>
<PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<LanguageStandard>stdcpp17</LanguageStandard>
</ClCompile>
<Link>
<LinkTimeCodeGeneration>Default</LinkTimeCodeGeneration>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<LanguageStandard>stdcpp17</LanguageStandard>
</ClCompile>
<Link>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<CETCompat>true</CETCompat>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<LanguageStandard>stdcpp17</LanguageStandard>
</ClCompile>
<Link>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<CETCompat>true</CETCompat>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">
<ClCompile>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<LanguageStandard>stdcpp17</LanguageStandard>
</ClCompile>
<Link>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="..\src\*.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>
<ItemGroup>
<ClInclude Include="..\include\*.h" />
<ClInclude Include="..\src\*.h" />
<ClInclude Include="..\win32\*.h" />
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="..\win32\ScintRes.rc" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

View File

@ -0,0 +1,39 @@
// Scintilla source code edit control
/** @file ScintillaDLL.cxx
** DLL entry point for Scintilla.
**/
// Copyright 1998-2018 by Neil Hodgson <neilh@scintilla.org>
// The License.txt file describes the conditions under which this software may be distributed.
#include <cstdint>
#undef _WIN32_WINNT
#define _WIN32_WINNT 0x0500
#undef WINVER
#define WINVER 0x0500
#include <windows.h>
#include "ScintillaTypes.h"
#include "ScintillaWin.h"
using namespace Scintilla;
extern "C"
__declspec(dllexport)
sptr_t __stdcall Scintilla_DirectFunction(
Internal::ScintillaWin *sci, UINT iMessage, uptr_t wParam, sptr_t lParam) {
return Internal::DirectFunction(sci, iMessage, wParam, lParam);
}
extern "C" int APIENTRY DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpvReserved) {
//Platform::DebugPrintf("Scintilla::DllMain %d %d\n", hInstance, dwReason);
if (dwReason == DLL_PROCESS_ATTACH) {
if (!Internal::RegisterClasses(hInstance))
return FALSE;
} else if (dwReason == DLL_PROCESS_DETACH) {
if (lpvReserved == NULL) {
Internal::ResourcesRelease(true);
}
}
return TRUE;
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,21 @@
// Scintilla source code edit control
/** @file ScintillaWin.h
** Define functions from ScintillaWin.cxx that can be called from ScintillaDLL.cxx.
**/
// Copyright 1998-2018 by Neil Hodgson <neilh@scintilla.org>
// The License.txt file describes the conditions under which this software may be distributed.
#ifndef SCINTILLAWIN_H
#define SCINTILLAWIN_H
namespace Scintilla::Internal {
class ScintillaWin;
int ResourcesRelease(bool fromDllMain) noexcept;
int RegisterClasses(void *hInstance) noexcept;
Scintilla::sptr_t DirectFunction(ScintillaWin *sci, UINT iMessage, Scintilla::uptr_t wParam, Scintilla::sptr_t lParam);
}
#endif

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

@ -0,0 +1,64 @@
// Scintilla source code edit control
/** @file WinTypes.h
** Implement safe release of COM objects and access to functions in DLLs.
** Header contains all implementation - there is no .cxx file.
**/
// Copyright 2020-2021 by Neil Hodgson <neilh@scintilla.org>
// The License.txt file describes the conditions under which this software may be distributed.
#ifndef WINTYPES_H
#define WINTYPES_H
namespace Scintilla::Internal {
// Release an IUnknown* and set to nullptr.
// While IUnknown::Release must be noexcept, it isn't marked as such so produces
// warnings which are avoided by the catch.
template <class T>
inline void ReleaseUnknown(T *&ppUnknown) noexcept {
if (ppUnknown) {
try {
ppUnknown->Release();
} catch (...) {
// Never occurs
}
ppUnknown = nullptr;
}
}
struct UnknownReleaser {
// Called by unique_ptr to destroy/free the resource
template <class T>
void operator()(T *pUnknown) noexcept {
try {
pUnknown->Release();
} catch (...) {
// IUnknown::Release must not throw, ignore if it does.
}
}
};
/// Find a function in a DLL and convert to a function pointer.
/// This avoids undefined and conditionally defined behaviour.
template<typename T>
inline T DLLFunction(HMODULE hModule, LPCSTR lpProcName) noexcept {
if (!hModule) {
return nullptr;
}
FARPROC function = ::GetProcAddress(hModule, lpProcName);
static_assert(sizeof(T) == sizeof(function));
T fp {};
memcpy(&fp, &function, sizeof(T));
return fp;
}
inline void ReleaseLibrary(HMODULE &hLib) noexcept {
if (hLib) {
FreeLibrary(hLib);
hLib = {};
}
}
}
#endif

View File

@ -0,0 +1,521 @@
# Created by DepGen.py. To recreate, run DepGen.py.
$(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 \
../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)/ScintillaDLL.o: \
ScintillaDLL.cxx \
../include/ScintillaTypes.h \
ScintillaWin.h
$(DIR_O)/ScintillaWin.o: \
ScintillaWin.cxx \
../include/ScintillaTypes.h \
../include/ScintillaMessages.h \
../include/ScintillaStructures.h \
../include/ILoader.h \
../include/Sci_Position.h \
../include/ILexer.h \
../src/Debugging.h \
../src/Geometry.h \
../src/Platform.h \
../src/CharacterType.h \
../src/CharacterCategoryMap.h \
../src/Position.h \
../src/UniqueString.h \
../src/SplitVector.h \
../src/Partitioning.h \
../src/RunStyles.h \
../src/ContractionState.h \
../src/CellBuffer.h \
../src/CallTip.h \
../src/KeyMap.h \
../src/Indicator.h \
../src/LineMarker.h \
../src/Style.h \
../src/ViewStyle.h \
../src/CharClassify.h \
../src/Decoration.h \
../src/CaseFolder.h \
../src/Document.h \
../src/CaseConvert.h \
../src/UniConversion.h \
../src/DBCS.h \
../src/Selection.h \
../src/PositionCache.h \
../src/EditModel.h \
../src/MarginView.h \
../src/EditView.h \
../src/Editor.h \
../src/ElapsedPeriod.h \
../src/AutoComplete.h \
../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 \
../include/ScintillaMessages.h \
../src/Debugging.h \
../src/Geometry.h \
../src/Platform.h \
../src/CharacterType.h \
../src/Position.h \
../src/AutoComplete.h
$(DIR_O)/CallTip.o: \
../src/CallTip.cxx \
../include/ScintillaTypes.h \
../include/ScintillaMessages.h \
../src/Debugging.h \
../src/Geometry.h \
../src/Platform.h \
../src/Position.h \
../src/CallTip.h
$(DIR_O)/CaseConvert.o: \
../src/CaseConvert.cxx \
../src/CaseConvert.h \
../src/UniConversion.h
$(DIR_O)/CaseFolder.o: \
../src/CaseFolder.cxx \
../src/CharacterType.h \
../src/CaseFolder.h \
../src/CaseConvert.h
$(DIR_O)/CellBuffer.o: \
../src/CellBuffer.cxx \
../include/ScintillaTypes.h \
../src/Debugging.h \
../src/Position.h \
../src/SplitVector.h \
../src/Partitioning.h \
../src/RunStyles.h \
../src/SparseVector.h \
../src/ChangeHistory.h \
../src/CellBuffer.h \
../src/UndoHistory.h \
../src/UniConversion.h
$(DIR_O)/ChangeHistory.o: \
../src/ChangeHistory.cxx \
../include/ScintillaTypes.h \
../src/Debugging.h \
../src/Position.h \
../src/SplitVector.h \
../src/Partitioning.h \
../src/RunStyles.h \
../src/SparseVector.h \
../src/ChangeHistory.h
$(DIR_O)/CharacterCategoryMap.o: \
../src/CharacterCategoryMap.cxx \
../src/CharacterCategoryMap.h
$(DIR_O)/CharacterType.o: \
../src/CharacterType.cxx \
../src/CharacterType.h
$(DIR_O)/CharClassify.o: \
../src/CharClassify.cxx \
../src/CharacterType.h \
../src/CharClassify.h
$(DIR_O)/ContractionState.o: \
../src/ContractionState.cxx \
../src/Debugging.h \
../src/Position.h \
../src/UniqueString.h \
../src/SplitVector.h \
../src/Partitioning.h \
../src/RunStyles.h \
../src/SparseVector.h \
../src/ContractionState.h
$(DIR_O)/DBCS.o: \
../src/DBCS.cxx \
../src/DBCS.h
$(DIR_O)/Decoration.o: \
../src/Decoration.cxx \
../include/ScintillaTypes.h \
../src/Debugging.h \
../src/Position.h \
../src/SplitVector.h \
../src/Partitioning.h \
../src/RunStyles.h \
../src/Decoration.h
$(DIR_O)/Document.o: \
../src/Document.cxx \
../include/ScintillaTypes.h \
../include/ILoader.h \
../include/Sci_Position.h \
../include/ILexer.h \
../src/Debugging.h \
../src/CharacterType.h \
../src/CharacterCategoryMap.h \
../src/Position.h \
../src/SplitVector.h \
../src/Partitioning.h \
../src/RunStyles.h \
../src/CellBuffer.h \
../src/PerLine.h \
../src/CharClassify.h \
../src/Decoration.h \
../src/CaseFolder.h \
../src/Document.h \
../src/RESearch.h \
../src/UniConversion.h \
../src/ElapsedPeriod.h
$(DIR_O)/EditModel.o: \
../src/EditModel.cxx \
../include/ScintillaTypes.h \
../include/ILoader.h \
../include/Sci_Position.h \
../include/ILexer.h \
../src/Debugging.h \
../src/Geometry.h \
../src/Platform.h \
../src/CharacterCategoryMap.h \
../src/Position.h \
../src/UniqueString.h \
../src/SplitVector.h \
../src/Partitioning.h \
../src/RunStyles.h \
../src/ContractionState.h \
../src/CellBuffer.h \
../src/Indicator.h \
../src/LineMarker.h \
../src/Style.h \
../src/ViewStyle.h \
../src/CharClassify.h \
../src/Decoration.h \
../src/CaseFolder.h \
../src/Document.h \
../src/UniConversion.h \
../src/Selection.h \
../src/PositionCache.h \
../src/EditModel.h
$(DIR_O)/Editor.o: \
../src/Editor.cxx \
../include/ScintillaTypes.h \
../include/ScintillaMessages.h \
../include/ScintillaStructures.h \
../include/ILoader.h \
../include/Sci_Position.h \
../include/ILexer.h \
../src/Debugging.h \
../src/Geometry.h \
../src/Platform.h \
../src/CharacterType.h \
../src/CharacterCategoryMap.h \
../src/Position.h \
../src/UniqueString.h \
../src/SplitVector.h \
../src/Partitioning.h \
../src/RunStyles.h \
../src/ContractionState.h \
../src/CellBuffer.h \
../src/PerLine.h \
../src/KeyMap.h \
../src/Indicator.h \
../src/LineMarker.h \
../src/Style.h \
../src/ViewStyle.h \
../src/CharClassify.h \
../src/Decoration.h \
../src/CaseFolder.h \
../src/Document.h \
../src/UniConversion.h \
../src/DBCS.h \
../src/Selection.h \
../src/PositionCache.h \
../src/EditModel.h \
../src/MarginView.h \
../src/EditView.h \
../src/Editor.h \
../src/ElapsedPeriod.h
$(DIR_O)/EditView.o: \
../src/EditView.cxx \
../include/ScintillaTypes.h \
../include/ScintillaMessages.h \
../include/ScintillaStructures.h \
../include/ILoader.h \
../include/Sci_Position.h \
../include/ILexer.h \
../src/Debugging.h \
../src/Geometry.h \
../src/Platform.h \
../src/CharacterType.h \
../src/CharacterCategoryMap.h \
../src/Position.h \
../src/UniqueString.h \
../src/SplitVector.h \
../src/Partitioning.h \
../src/RunStyles.h \
../src/ContractionState.h \
../src/CellBuffer.h \
../src/PerLine.h \
../src/KeyMap.h \
../src/Indicator.h \
../src/LineMarker.h \
../src/Style.h \
../src/ViewStyle.h \
../src/CharClassify.h \
../src/Decoration.h \
../src/CaseFolder.h \
../src/Document.h \
../src/UniConversion.h \
../src/Selection.h \
../src/PositionCache.h \
../src/EditModel.h \
../src/MarginView.h \
../src/EditView.h \
../src/ElapsedPeriod.h
$(DIR_O)/Geometry.o: \
../src/Geometry.cxx \
../src/Geometry.h
$(DIR_O)/Indicator.o: \
../src/Indicator.cxx \
../include/ScintillaTypes.h \
../src/Debugging.h \
../src/Geometry.h \
../src/Platform.h \
../src/Indicator.h \
../src/XPM.h
$(DIR_O)/KeyMap.o: \
../src/KeyMap.cxx \
../include/ScintillaTypes.h \
../include/ScintillaMessages.h \
../src/Debugging.h \
../src/Geometry.h \
../src/Platform.h \
../src/KeyMap.h
$(DIR_O)/LineMarker.o: \
../src/LineMarker.cxx \
../include/ScintillaTypes.h \
../src/Debugging.h \
../src/Geometry.h \
../src/Platform.h \
../src/XPM.h \
../src/LineMarker.h \
../src/UniConversion.h
$(DIR_O)/MarginView.o: \
../src/MarginView.cxx \
../include/ScintillaTypes.h \
../include/ScintillaMessages.h \
../include/ScintillaStructures.h \
../include/ILoader.h \
../include/Sci_Position.h \
../include/ILexer.h \
../src/Debugging.h \
../src/Geometry.h \
../src/Platform.h \
../src/CharacterCategoryMap.h \
../src/Position.h \
../src/UniqueString.h \
../src/SplitVector.h \
../src/Partitioning.h \
../src/RunStyles.h \
../src/ContractionState.h \
../src/CellBuffer.h \
../src/KeyMap.h \
../src/Indicator.h \
../src/LineMarker.h \
../src/Style.h \
../src/ViewStyle.h \
../src/CharClassify.h \
../src/Decoration.h \
../src/CaseFolder.h \
../src/Document.h \
../src/UniConversion.h \
../src/Selection.h \
../src/PositionCache.h \
../src/EditModel.h \
../src/MarginView.h \
../src/EditView.h
$(DIR_O)/PerLine.o: \
../src/PerLine.cxx \
../include/ScintillaTypes.h \
../src/Debugging.h \
../src/Geometry.h \
../src/Platform.h \
../src/Position.h \
../src/SplitVector.h \
../src/Partitioning.h \
../src/CellBuffer.h \
../src/PerLine.h
$(DIR_O)/PositionCache.o: \
../src/PositionCache.cxx \
../include/ScintillaTypes.h \
../include/ScintillaMessages.h \
../include/ILoader.h \
../include/Sci_Position.h \
../include/ILexer.h \
../src/Debugging.h \
../src/Geometry.h \
../src/Platform.h \
../src/CharacterType.h \
../src/CharacterCategoryMap.h \
../src/Position.h \
../src/UniqueString.h \
../src/SplitVector.h \
../src/Partitioning.h \
../src/RunStyles.h \
../src/ContractionState.h \
../src/CellBuffer.h \
../src/KeyMap.h \
../src/Indicator.h \
../src/LineMarker.h \
../src/Style.h \
../src/ViewStyle.h \
../src/CharClassify.h \
../src/Decoration.h \
../src/CaseFolder.h \
../src/Document.h \
../src/UniConversion.h \
../src/DBCS.h \
../src/Selection.h \
../src/PositionCache.h
$(DIR_O)/RESearch.o: \
../src/RESearch.cxx \
../src/Position.h \
../src/CharClassify.h \
../src/RESearch.h
$(DIR_O)/RunStyles.o: \
../src/RunStyles.cxx \
../src/Debugging.h \
../src/Position.h \
../src/SplitVector.h \
../src/Partitioning.h \
../src/RunStyles.h
$(DIR_O)/ScintillaBase.o: \
../src/ScintillaBase.cxx \
../include/ScintillaTypes.h \
../include/ScintillaMessages.h \
../include/ScintillaStructures.h \
../include/ILoader.h \
../include/Sci_Position.h \
../include/ILexer.h \
../src/Debugging.h \
../src/Geometry.h \
../src/Platform.h \
../src/CharacterCategoryMap.h \
../src/Position.h \
../src/UniqueString.h \
../src/SplitVector.h \
../src/Partitioning.h \
../src/RunStyles.h \
../src/ContractionState.h \
../src/CellBuffer.h \
../src/CallTip.h \
../src/KeyMap.h \
../src/Indicator.h \
../src/LineMarker.h \
../src/Style.h \
../src/ViewStyle.h \
../src/CharClassify.h \
../src/Decoration.h \
../src/CaseFolder.h \
../src/Document.h \
../src/Selection.h \
../src/PositionCache.h \
../src/EditModel.h \
../src/MarginView.h \
../src/EditView.h \
../src/Editor.h \
../src/AutoComplete.h \
../src/ScintillaBase.h
$(DIR_O)/Selection.o: \
../src/Selection.cxx \
../src/Debugging.h \
../src/Position.h \
../src/Selection.h
$(DIR_O)/Style.o: \
../src/Style.cxx \
../include/ScintillaTypes.h \
../src/Debugging.h \
../src/Geometry.h \
../src/Platform.h \
../src/Style.h
$(DIR_O)/UndoHistory.o: \
../src/UndoHistory.cxx \
../include/ScintillaTypes.h \
../src/Debugging.h \
../src/Position.h \
../src/SplitVector.h \
../src/Partitioning.h \
../src/RunStyles.h \
../src/SparseVector.h \
../src/ChangeHistory.h \
../src/CellBuffer.h \
../src/UndoHistory.h
$(DIR_O)/UniConversion.o: \
../src/UniConversion.cxx \
../src/UniConversion.h
$(DIR_O)/UniqueString.o: \
../src/UniqueString.cxx \
../src/UniqueString.h
$(DIR_O)/ViewStyle.o: \
../src/ViewStyle.cxx \
../include/ScintillaTypes.h \
../src/Debugging.h \
../src/Geometry.h \
../src/Platform.h \
../src/Position.h \
../src/UniqueString.h \
../src/Indicator.h \
../src/XPM.h \
../src/LineMarker.h \
../src/Style.h \
../src/ViewStyle.h
$(DIR_O)/XPM.o: \
../src/XPM.cxx \
../include/ScintillaTypes.h \
../src/Debugging.h \
../src/Geometry.h \
../src/Platform.h \
../src/XPM.h

View File

@ -0,0 +1,144 @@
# Make file for Scintilla on Windows
# @file makefile
# Copyright 1998-2010 by Neil Hodgson <neilh@scintilla.org>
# The License.txt file describes the conditions under which this software may be distributed.
# This makefile assumes Mingw-w64 GCC 9.0+ is used and changes will be needed to use other compilers.
# Clang 9.0+ can be used with CLANG=1 on command line.
.PHONY: all clean analyze depend
.SUFFIXES: .cxx .c .o .h .a
DIR_O=.
DIR_BIN=../bin
COMPONENT = $(DIR_BIN)/Scintilla.dll
LIBSCI = $(DIR_BIN)/libscintilla.a
WARNINGS = -Wpedantic -Wall -Wextra
ifdef CLANG
CXX = clang++
else
# MinGW GCC
LIBSMINGW = -lstdc++
STRIPOPTION = -s
WARNINGS += -Wconversion
endif
ARFLAGS = rc
RANLIB ?= ranlib
WINDRES ?= windres
# Environment variable windir always defined on Win32
# Take care of changing Unix style '/' directory separator to '\' on Windows
normalize = $(if $(windir),$(subst /,\,$1),$1)
PYTHON = $(if $(windir),pyw,python3)
ifdef windir
DEL = $(if $(wildcard $(dir $(SHELL))rm.exe), $(dir $(SHELL))rm.exe -f, del /q)
else
DEL = rm -f
endif
vpath %.h ../src ../include
vpath %.cxx ../src
LDFLAGS=-shared -static -mwindows
LIBS=-lgdi32 -luser32 -limm32 -lole32 -luuid -loleaut32 -ladvapi32 $(LIBSMINGW)
INCLUDES=-I ../include -I ../src
BASE_FLAGS += $(WARNINGS)
ifdef NO_CXX11_REGEX
DEFINES += -DNO_CXX11_REGEX
endif
DEFINES += -D$(if $(DEBUG),DEBUG,NDEBUG)
BASE_FLAGS += $(if $(DEBUG),-g,-O3)
ifndef DEBUG
STRIPFLAG=$(STRIPOPTION)
endif
CXX_BASE_FLAGS =--std=c++17 $(BASE_FLAGS)
CXX_ALL_FLAGS =$(DEFINES) $(INCLUDES) $(CXX_BASE_FLAGS)
all: $(COMPONENT) $(LIBSCI)
clean:
$(DEL) $(call normalize, $(addprefix $(DIR_O)/, *.exe *.o *.a *.obj *.dll *.res *.map *.plist) $(COMPONENT) $(LIBSCI))
$(DIR_O)/%.o: %.cxx
$(CXX) $(CXX_ALL_FLAGS) $(CXXFLAGS) -c $< -o $@
analyze:
$(CXX) --analyze $(CXX_ALL_FLAGS) $(CXXFLAGS) *.cxx ../src/*.cxx
depend deps.mak:
$(PYTHON) DepGen.py
# Required for base Scintilla
SRC_OBJS = \
$(DIR_O)/AutoComplete.o \
$(DIR_O)/CallTip.o \
$(DIR_O)/CaseConvert.o \
$(DIR_O)/CaseFolder.o \
$(DIR_O)/CellBuffer.o \
$(DIR_O)/ChangeHistory.o \
$(DIR_O)/CharacterCategoryMap.o \
$(DIR_O)/CharacterType.o \
$(DIR_O)/CharClassify.o \
$(DIR_O)/ContractionState.o \
$(DIR_O)/DBCS.o \
$(DIR_O)/Decoration.o \
$(DIR_O)/Document.o \
$(DIR_O)/EditModel.o \
$(DIR_O)/Editor.o \
$(DIR_O)/EditView.o \
$(DIR_O)/Geometry.o \
$(DIR_O)/Indicator.o \
$(DIR_O)/KeyMap.o \
$(DIR_O)/LineMarker.o \
$(DIR_O)/MarginView.o \
$(DIR_O)/PerLine.o \
$(DIR_O)/PositionCache.o \
$(DIR_O)/RESearch.o \
$(DIR_O)/RunStyles.o \
$(DIR_O)/Selection.o \
$(DIR_O)/Style.o \
$(DIR_O)/UndoHistory.o \
$(DIR_O)/UniConversion.o \
$(DIR_O)/UniqueString.o \
$(DIR_O)/ViewStyle.o \
$(DIR_O)/XPM.o
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
SHARED_OBJS = \
$(DIR_O)/ScintillaDLL.o \
$(DIR_O)/ScintRes.o
$(COMPONENT): $(COMPONENT_OBJS) $(SHARED_OBJS)
$(CXX) $(LDFLAGS) -o $@ $(STRIPFLAG) $^ $(CXXFLAGS) $(LIBS)
$(LIBSCI): $(COMPONENT_OBJS)
$(AR) $(ARFLAGS) $@ $^
$(RANLIB) $@
# Automatically generate dependencies for most files with "make depend"
include deps.mak
$(DIR_O)/ScintRes.o: ScintRes.rc
$(WINDRES) $< $@

View File

@ -0,0 +1,521 @@
# Created by DepGen.py. To recreate, run DepGen.py.
$(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 \
../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)/ScintillaDLL.obj: \
ScintillaDLL.cxx \
../include/ScintillaTypes.h \
ScintillaWin.h
$(DIR_O)/ScintillaWin.obj: \
ScintillaWin.cxx \
../include/ScintillaTypes.h \
../include/ScintillaMessages.h \
../include/ScintillaStructures.h \
../include/ILoader.h \
../include/Sci_Position.h \
../include/ILexer.h \
../src/Debugging.h \
../src/Geometry.h \
../src/Platform.h \
../src/CharacterType.h \
../src/CharacterCategoryMap.h \
../src/Position.h \
../src/UniqueString.h \
../src/SplitVector.h \
../src/Partitioning.h \
../src/RunStyles.h \
../src/ContractionState.h \
../src/CellBuffer.h \
../src/CallTip.h \
../src/KeyMap.h \
../src/Indicator.h \
../src/LineMarker.h \
../src/Style.h \
../src/ViewStyle.h \
../src/CharClassify.h \
../src/Decoration.h \
../src/CaseFolder.h \
../src/Document.h \
../src/CaseConvert.h \
../src/UniConversion.h \
../src/DBCS.h \
../src/Selection.h \
../src/PositionCache.h \
../src/EditModel.h \
../src/MarginView.h \
../src/EditView.h \
../src/Editor.h \
../src/ElapsedPeriod.h \
../src/AutoComplete.h \
../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 \
../include/ScintillaMessages.h \
../src/Debugging.h \
../src/Geometry.h \
../src/Platform.h \
../src/CharacterType.h \
../src/Position.h \
../src/AutoComplete.h
$(DIR_O)/CallTip.obj: \
../src/CallTip.cxx \
../include/ScintillaTypes.h \
../include/ScintillaMessages.h \
../src/Debugging.h \
../src/Geometry.h \
../src/Platform.h \
../src/Position.h \
../src/CallTip.h
$(DIR_O)/CaseConvert.obj: \
../src/CaseConvert.cxx \
../src/CaseConvert.h \
../src/UniConversion.h
$(DIR_O)/CaseFolder.obj: \
../src/CaseFolder.cxx \
../src/CharacterType.h \
../src/CaseFolder.h \
../src/CaseConvert.h
$(DIR_O)/CellBuffer.obj: \
../src/CellBuffer.cxx \
../include/ScintillaTypes.h \
../src/Debugging.h \
../src/Position.h \
../src/SplitVector.h \
../src/Partitioning.h \
../src/RunStyles.h \
../src/SparseVector.h \
../src/ChangeHistory.h \
../src/CellBuffer.h \
../src/UndoHistory.h \
../src/UniConversion.h
$(DIR_O)/ChangeHistory.obj: \
../src/ChangeHistory.cxx \
../include/ScintillaTypes.h \
../src/Debugging.h \
../src/Position.h \
../src/SplitVector.h \
../src/Partitioning.h \
../src/RunStyles.h \
../src/SparseVector.h \
../src/ChangeHistory.h
$(DIR_O)/CharacterCategoryMap.obj: \
../src/CharacterCategoryMap.cxx \
../src/CharacterCategoryMap.h
$(DIR_O)/CharacterType.obj: \
../src/CharacterType.cxx \
../src/CharacterType.h
$(DIR_O)/CharClassify.obj: \
../src/CharClassify.cxx \
../src/CharacterType.h \
../src/CharClassify.h
$(DIR_O)/ContractionState.obj: \
../src/ContractionState.cxx \
../src/Debugging.h \
../src/Position.h \
../src/UniqueString.h \
../src/SplitVector.h \
../src/Partitioning.h \
../src/RunStyles.h \
../src/SparseVector.h \
../src/ContractionState.h
$(DIR_O)/DBCS.obj: \
../src/DBCS.cxx \
../src/DBCS.h
$(DIR_O)/Decoration.obj: \
../src/Decoration.cxx \
../include/ScintillaTypes.h \
../src/Debugging.h \
../src/Position.h \
../src/SplitVector.h \
../src/Partitioning.h \
../src/RunStyles.h \
../src/Decoration.h
$(DIR_O)/Document.obj: \
../src/Document.cxx \
../include/ScintillaTypes.h \
../include/ILoader.h \
../include/Sci_Position.h \
../include/ILexer.h \
../src/Debugging.h \
../src/CharacterType.h \
../src/CharacterCategoryMap.h \
../src/Position.h \
../src/SplitVector.h \
../src/Partitioning.h \
../src/RunStyles.h \
../src/CellBuffer.h \
../src/PerLine.h \
../src/CharClassify.h \
../src/Decoration.h \
../src/CaseFolder.h \
../src/Document.h \
../src/RESearch.h \
../src/UniConversion.h \
../src/ElapsedPeriod.h
$(DIR_O)/EditModel.obj: \
../src/EditModel.cxx \
../include/ScintillaTypes.h \
../include/ILoader.h \
../include/Sci_Position.h \
../include/ILexer.h \
../src/Debugging.h \
../src/Geometry.h \
../src/Platform.h \
../src/CharacterCategoryMap.h \
../src/Position.h \
../src/UniqueString.h \
../src/SplitVector.h \
../src/Partitioning.h \
../src/RunStyles.h \
../src/ContractionState.h \
../src/CellBuffer.h \
../src/Indicator.h \
../src/LineMarker.h \
../src/Style.h \
../src/ViewStyle.h \
../src/CharClassify.h \
../src/Decoration.h \
../src/CaseFolder.h \
../src/Document.h \
../src/UniConversion.h \
../src/Selection.h \
../src/PositionCache.h \
../src/EditModel.h
$(DIR_O)/Editor.obj: \
../src/Editor.cxx \
../include/ScintillaTypes.h \
../include/ScintillaMessages.h \
../include/ScintillaStructures.h \
../include/ILoader.h \
../include/Sci_Position.h \
../include/ILexer.h \
../src/Debugging.h \
../src/Geometry.h \
../src/Platform.h \
../src/CharacterType.h \
../src/CharacterCategoryMap.h \
../src/Position.h \
../src/UniqueString.h \
../src/SplitVector.h \
../src/Partitioning.h \
../src/RunStyles.h \
../src/ContractionState.h \
../src/CellBuffer.h \
../src/PerLine.h \
../src/KeyMap.h \
../src/Indicator.h \
../src/LineMarker.h \
../src/Style.h \
../src/ViewStyle.h \
../src/CharClassify.h \
../src/Decoration.h \
../src/CaseFolder.h \
../src/Document.h \
../src/UniConversion.h \
../src/DBCS.h \
../src/Selection.h \
../src/PositionCache.h \
../src/EditModel.h \
../src/MarginView.h \
../src/EditView.h \
../src/Editor.h \
../src/ElapsedPeriod.h
$(DIR_O)/EditView.obj: \
../src/EditView.cxx \
../include/ScintillaTypes.h \
../include/ScintillaMessages.h \
../include/ScintillaStructures.h \
../include/ILoader.h \
../include/Sci_Position.h \
../include/ILexer.h \
../src/Debugging.h \
../src/Geometry.h \
../src/Platform.h \
../src/CharacterType.h \
../src/CharacterCategoryMap.h \
../src/Position.h \
../src/UniqueString.h \
../src/SplitVector.h \
../src/Partitioning.h \
../src/RunStyles.h \
../src/ContractionState.h \
../src/CellBuffer.h \
../src/PerLine.h \
../src/KeyMap.h \
../src/Indicator.h \
../src/LineMarker.h \
../src/Style.h \
../src/ViewStyle.h \
../src/CharClassify.h \
../src/Decoration.h \
../src/CaseFolder.h \
../src/Document.h \
../src/UniConversion.h \
../src/Selection.h \
../src/PositionCache.h \
../src/EditModel.h \
../src/MarginView.h \
../src/EditView.h \
../src/ElapsedPeriod.h
$(DIR_O)/Geometry.obj: \
../src/Geometry.cxx \
../src/Geometry.h
$(DIR_O)/Indicator.obj: \
../src/Indicator.cxx \
../include/ScintillaTypes.h \
../src/Debugging.h \
../src/Geometry.h \
../src/Platform.h \
../src/Indicator.h \
../src/XPM.h
$(DIR_O)/KeyMap.obj: \
../src/KeyMap.cxx \
../include/ScintillaTypes.h \
../include/ScintillaMessages.h \
../src/Debugging.h \
../src/Geometry.h \
../src/Platform.h \
../src/KeyMap.h
$(DIR_O)/LineMarker.obj: \
../src/LineMarker.cxx \
../include/ScintillaTypes.h \
../src/Debugging.h \
../src/Geometry.h \
../src/Platform.h \
../src/XPM.h \
../src/LineMarker.h \
../src/UniConversion.h
$(DIR_O)/MarginView.obj: \
../src/MarginView.cxx \
../include/ScintillaTypes.h \
../include/ScintillaMessages.h \
../include/ScintillaStructures.h \
../include/ILoader.h \
../include/Sci_Position.h \
../include/ILexer.h \
../src/Debugging.h \
../src/Geometry.h \
../src/Platform.h \
../src/CharacterCategoryMap.h \
../src/Position.h \
../src/UniqueString.h \
../src/SplitVector.h \
../src/Partitioning.h \
../src/RunStyles.h \
../src/ContractionState.h \
../src/CellBuffer.h \
../src/KeyMap.h \
../src/Indicator.h \
../src/LineMarker.h \
../src/Style.h \
../src/ViewStyle.h \
../src/CharClassify.h \
../src/Decoration.h \
../src/CaseFolder.h \
../src/Document.h \
../src/UniConversion.h \
../src/Selection.h \
../src/PositionCache.h \
../src/EditModel.h \
../src/MarginView.h \
../src/EditView.h
$(DIR_O)/PerLine.obj: \
../src/PerLine.cxx \
../include/ScintillaTypes.h \
../src/Debugging.h \
../src/Geometry.h \
../src/Platform.h \
../src/Position.h \
../src/SplitVector.h \
../src/Partitioning.h \
../src/CellBuffer.h \
../src/PerLine.h
$(DIR_O)/PositionCache.obj: \
../src/PositionCache.cxx \
../include/ScintillaTypes.h \
../include/ScintillaMessages.h \
../include/ILoader.h \
../include/Sci_Position.h \
../include/ILexer.h \
../src/Debugging.h \
../src/Geometry.h \
../src/Platform.h \
../src/CharacterType.h \
../src/CharacterCategoryMap.h \
../src/Position.h \
../src/UniqueString.h \
../src/SplitVector.h \
../src/Partitioning.h \
../src/RunStyles.h \
../src/ContractionState.h \
../src/CellBuffer.h \
../src/KeyMap.h \
../src/Indicator.h \
../src/LineMarker.h \
../src/Style.h \
../src/ViewStyle.h \
../src/CharClassify.h \
../src/Decoration.h \
../src/CaseFolder.h \
../src/Document.h \
../src/UniConversion.h \
../src/DBCS.h \
../src/Selection.h \
../src/PositionCache.h
$(DIR_O)/RESearch.obj: \
../src/RESearch.cxx \
../src/Position.h \
../src/CharClassify.h \
../src/RESearch.h
$(DIR_O)/RunStyles.obj: \
../src/RunStyles.cxx \
../src/Debugging.h \
../src/Position.h \
../src/SplitVector.h \
../src/Partitioning.h \
../src/RunStyles.h
$(DIR_O)/ScintillaBase.obj: \
../src/ScintillaBase.cxx \
../include/ScintillaTypes.h \
../include/ScintillaMessages.h \
../include/ScintillaStructures.h \
../include/ILoader.h \
../include/Sci_Position.h \
../include/ILexer.h \
../src/Debugging.h \
../src/Geometry.h \
../src/Platform.h \
../src/CharacterCategoryMap.h \
../src/Position.h \
../src/UniqueString.h \
../src/SplitVector.h \
../src/Partitioning.h \
../src/RunStyles.h \
../src/ContractionState.h \
../src/CellBuffer.h \
../src/CallTip.h \
../src/KeyMap.h \
../src/Indicator.h \
../src/LineMarker.h \
../src/Style.h \
../src/ViewStyle.h \
../src/CharClassify.h \
../src/Decoration.h \
../src/CaseFolder.h \
../src/Document.h \
../src/Selection.h \
../src/PositionCache.h \
../src/EditModel.h \
../src/MarginView.h \
../src/EditView.h \
../src/Editor.h \
../src/AutoComplete.h \
../src/ScintillaBase.h
$(DIR_O)/Selection.obj: \
../src/Selection.cxx \
../src/Debugging.h \
../src/Position.h \
../src/Selection.h
$(DIR_O)/Style.obj: \
../src/Style.cxx \
../include/ScintillaTypes.h \
../src/Debugging.h \
../src/Geometry.h \
../src/Platform.h \
../src/Style.h
$(DIR_O)/UndoHistory.obj: \
../src/UndoHistory.cxx \
../include/ScintillaTypes.h \
../src/Debugging.h \
../src/Position.h \
../src/SplitVector.h \
../src/Partitioning.h \
../src/RunStyles.h \
../src/SparseVector.h \
../src/ChangeHistory.h \
../src/CellBuffer.h \
../src/UndoHistory.h
$(DIR_O)/UniConversion.obj: \
../src/UniConversion.cxx \
../src/UniConversion.h
$(DIR_O)/UniqueString.obj: \
../src/UniqueString.cxx \
../src/UniqueString.h
$(DIR_O)/ViewStyle.obj: \
../src/ViewStyle.cxx \
../include/ScintillaTypes.h \
../src/Debugging.h \
../src/Geometry.h \
../src/Platform.h \
../src/Position.h \
../src/UniqueString.h \
../src/Indicator.h \
../src/XPM.h \
../src/LineMarker.h \
../src/Style.h \
../src/ViewStyle.h
$(DIR_O)/XPM.obj: \
../src/XPM.cxx \
../include/ScintillaTypes.h \
../src/Debugging.h \
../src/Geometry.h \
../src/Platform.h \
../src/XPM.h

View File

@ -0,0 +1,159 @@
# Make file for Scintilla on Windows Visual C++ version
# Copyright 1998-2010 by Neil Hodgson <neilh@scintilla.org>
# The License.txt file describes the conditions under which this software may be distributed.
# This makefile is for using Visual C++ with nmake.
# Usage for Microsoft:
# nmake -f scintilla.mak
# For debug versions define DEBUG on the command line:
# nmake DEBUG=1 -f scintilla.mak
# The main makefile uses mingw32 gcc and may be more current than this file.
.SUFFIXES: .cxx
DIR_O=obj
DIR_BIN=..\bin
COMPONENT=$(DIR_BIN)\Scintilla.dll
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
# environment variable set by vcvars*.bat to be either x86 or x64
!IF "$(PLATFORM)" == "x64"
SUBSYSTEM=-SUBSYSTEM:WINDOWS,5.02
!ELSE
SUBSYSTEM=-SUBSYSTEM:WINDOWS,5.01
!ENDIF
!ELSE
!IFDEF ARM64
ADD_DEFINE=-D_ARM64_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE=1
SUBSYSTEM=-SUBSYSTEM:WINDOWS,10.00
!ELSE
CETCOMPAT=-CETCOMPAT
!ENDIF
!ENDIF
CRTFLAGS=$(ADD_DEFINE)
CXXFLAGS=-Zi -TP -MP -W4 -EHsc -std:c++17 -utf-8 $(CRTFLAGS)
CXXDEBUG=-Od -MTd -DDEBUG
CXXNDEBUG=-O2 -MT -DNDEBUG -GL
NAME=-Fo
LDFLAGS=-OPT:REF -LTCG -IGNORE:4197 -DEBUG $(SUBSYSTEM) $(CETCOMPAT)
LDDEBUG=
LIBS=KERNEL32.lib USER32.lib GDI32.lib IMM32.lib OLE32.lib OLEAUT32.lib ADVAPI32.lib
NOLOGO=-nologo
!IFDEF QUIET
CXX=@$(CXX)
CXXFLAGS=$(CXXFLAGS) $(NOLOGO)
LDFLAGS=$(LDFLAGS) $(NOLOGO)
!ENDIF
!IFDEF NO_CXX11_REGEX
CXXFLAGS=$(CXXFLAGS) -DNO_CXX11_REGEX
!ENDIF
!IFDEF DEBUG
CXXFLAGS=$(CXXFLAGS) $(CXXDEBUG)
LDFLAGS=$(LDDEBUG) $(LDFLAGS)
!ELSE
CXXFLAGS=$(CXXFLAGS) $(CXXNDEBUG)
!ENDIF
INCLUDES=-I../include -I../src
CXXFLAGS=$(CXXFLAGS) $(INCLUDES)
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) \
$(DIR_O)\*.res $(DIR_BIN)\*.map $(DIR_BIN)\*.exp $(DIR_BIN)\*.pdb \
$(DIR_BIN)\Scintilla.lib $(LIBSCI)
depend:
pyw DepGen.py
# Required for base Scintilla
SRC_OBJS=\
$(DIR_O)\AutoComplete.obj \
$(DIR_O)\CallTip.obj \
$(DIR_O)\CaseConvert.obj \
$(DIR_O)\CaseFolder.obj \
$(DIR_O)\CellBuffer.obj \
$(DIR_O)\ChangeHistory.obj \
$(DIR_O)\CharacterCategoryMap.obj \
$(DIR_O)\CharacterType.obj \
$(DIR_O)\CharClassify.obj \
$(DIR_O)\ContractionState.obj \
$(DIR_O)\DBCS.obj \
$(DIR_O)\Decoration.obj \
$(DIR_O)\Document.obj \
$(DIR_O)\EditModel.obj \
$(DIR_O)\Editor.obj \
$(DIR_O)\EditView.obj \
$(DIR_O)\Geometry.obj \
$(DIR_O)\Indicator.obj \
$(DIR_O)\KeyMap.obj \
$(DIR_O)\LineMarker.obj \
$(DIR_O)\MarginView.obj \
$(DIR_O)\PerLine.obj \
$(DIR_O)\PositionCache.obj \
$(DIR_O)\RESearch.obj \
$(DIR_O)\RunStyles.obj \
$(DIR_O)\Selection.obj \
$(DIR_O)\Style.obj \
$(DIR_O)\UndoHistory.obj \
$(DIR_O)\UniConversion.obj \
$(DIR_O)\UniqueString.obj \
$(DIR_O)\ViewStyle.obj \
$(DIR_O)\XPM.obj
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)
SHARED_OBJS = \
$(DIR_O)\ScintillaDLL.obj
$(DIR_O)\ScintRes.res : ScintRes.rc
$(RC) -fo$@ $**
$(COMPONENT): $(COMPONENT_OBJS) $(SHARED_OBJS) $(DIR_O)\ScintRes.res
$(LD) $(LDFLAGS) -DEF:Scintilla.def -DLL -OUT:$@ $** $(LIBS)
$(LIBSCI): $(COMPONENT_OBJS)
LIB /OUT:$@ $**
# Define how to build all the objects and what they depend on
{..\src}.cxx{$(DIR_O)}.obj::
$(CXX) $(CXXFLAGS) -c $(NAME)$(DIR_O)\ $<
{.}.cxx{$(DIR_O)}.obj::
$(CXX) $(CXXFLAGS) -c $(NAME)$(DIR_O)\ $<
# Dependencies
!IF EXISTS(nmdeps.mak)
# Protect with !IF EXISTS to handle accidental deletion - just 'nmake -f scintilla.mak depend'
!INCLUDE nmdeps.mak
!ENDIF