1 // Scintilla source code edit control
2 /** @file ScintillaWin.cxx
3 ** Windows specific subclass of ScintillaBase.
5 // Copyright 1998-2003 by Neil Hodgson <neilh@scintilla.org>
6 // The License.txt file describes the conditions under which this software may be distributed.
20 #include <string_view>
30 // Want to use std::min and std::max so don't want Windows.h version of min and max
31 #if !defined(NOMINMAX)
35 #define _WIN32_WINNT 0x0A00
38 #define WIN32_LEAN_AND_MEAN 1
46 #if !defined(DISABLE_D2D)
55 #include "ScintillaTypes.h"
56 #include "ScintillaMessages.h"
57 #include "ScintillaStructures.h"
61 #include "Debugging.h"
65 #include "CharacterCategoryMap.h"
67 #include "UniqueString.h"
68 #include "SplitVector.h"
69 #include "Partitioning.h"
70 #include "RunStyles.h"
71 #include "ContractionState.h"
72 #include "CellBuffer.h"
75 #include "Indicator.h"
76 #include "LineMarker.h"
78 #include "ViewStyle.h"
79 #include "CharClassify.h"
80 #include "Decoration.h"
81 #include "CaseFolder.h"
83 #include "CaseConvert.h"
84 #include "UniConversion.h"
85 #include "Selection.h"
86 #include "PositionCache.h"
87 #include "EditModel.h"
88 #include "MarginView.h"
91 #include "ElapsedPeriod.h"
93 #include "AutoComplete.h"
94 #include "ScintillaBase.h"
99 #include "ScintillaWin.h"
103 // Two idle messages SC_WIN_IDLE and SC_WORK_IDLE.
105 // SC_WIN_IDLE is low priority so should occur after the next WM_PAINT
106 // It is for lengthy actions like wrapping and background styling
107 constexpr UINT SC_WIN_IDLE
= 5001;
108 // SC_WORK_IDLE is high priority and should occur before the next WM_PAINT
109 // It is for shorter actions like restyling the text just inserted
110 // and delivering SCN_UPDATEUI
111 constexpr UINT SC_WORK_IDLE
= 5002;
113 constexpr int IndicatorInput
= static_cast<int>(Scintilla::IndicatorNumbers::Ime
);
114 constexpr int IndicatorTarget
= IndicatorInput
+ 1;
115 constexpr int IndicatorConverted
= IndicatorInput
+ 2;
116 constexpr int IndicatorUnknown
= IndicatorInput
+ 3;
118 typedef UINT_PTR (WINAPI
*SetCoalescableTimerSig
)(HWND hwnd
, UINT_PTR nIDEvent
,
119 UINT uElapse
, TIMERPROC lpTimerFunc
, ULONG uToleranceDelay
);
123 // GCC has trouble with the standard COM ABI so do it the old C way with explicit vtables.
125 using namespace Scintilla
;
126 using namespace Scintilla::Internal
;
130 const TCHAR callClassName
[] = TEXT("CallTip");
132 void SetWindowID(HWND hWnd
, int identifier
) noexcept
{
133 ::SetWindowLongPtr(hWnd
, GWLP_ID
, identifier
);
136 constexpr POINT
POINTFromLParam(sptr_t lParam
) noexcept
{
137 return { GET_X_LPARAM(lParam
), GET_Y_LPARAM(lParam
) };
140 constexpr Point
PointFromLParam(sptr_t lpoint
) noexcept
{
141 return Point::FromInts(GET_X_LPARAM(lpoint
), GET_Y_LPARAM(lpoint
));
144 bool KeyboardIsKeyDown(int key
) noexcept
{
145 return (::GetKeyState(key
) & 0x80000000) != 0;
148 // Bit 24 is the extended keyboard flag and the numeric keypad is non-extended
149 constexpr sptr_t extendedKeyboard
= 1 << 24;
151 constexpr bool KeyboardIsNumericKeypadFunction(uptr_t wParam
, sptr_t lParam
) {
152 if ((lParam
& extendedKeyboard
) != 0) {
153 // Not from the numeric keypad
176 class FormatEnumerator final
: public IEnumFORMATETC
{
180 std::vector
<CLIPFORMAT
> formats
;
181 FormatEnumerator(ULONG pos_
, const CLIPFORMAT formats_
[], size_t formatsLen_
);
184 STDMETHODIMP
QueryInterface(REFIID riid
, PVOID
*ppv
) override
;
185 STDMETHODIMP_(ULONG
)AddRef() override
;
186 STDMETHODIMP_(ULONG
)Release() override
;
189 STDMETHODIMP
Next(ULONG celt
, FORMATETC
*rgelt
, ULONG
*pceltFetched
) override
;
190 STDMETHODIMP
Skip(ULONG celt
) override
;
191 STDMETHODIMP
Reset() override
;
192 STDMETHODIMP
Clone(IEnumFORMATETC
**ppenum
) override
;
197 class DropSource final
: public IDropSource
{
199 ScintillaWin
*sci
= nullptr;
202 STDMETHODIMP
QueryInterface(REFIID riid
, PVOID
*ppv
) override
;
203 STDMETHODIMP_(ULONG
)AddRef() override
;
204 STDMETHODIMP_(ULONG
)Release() override
;
207 STDMETHODIMP
QueryContinueDrag(BOOL fEsc
, DWORD grfKeyState
) override
;
208 STDMETHODIMP
GiveFeedback(DWORD
) override
;
213 class DataObject final
: public IDataObject
{
215 ScintillaWin
*sci
= nullptr;
218 STDMETHODIMP
QueryInterface(REFIID riid
, PVOID
*ppv
) override
;
219 STDMETHODIMP_(ULONG
)AddRef() override
;
220 STDMETHODIMP_(ULONG
)Release() override
;
223 STDMETHODIMP
GetData(FORMATETC
*pFEIn
, STGMEDIUM
*pSTM
) override
;
224 STDMETHODIMP
GetDataHere(FORMATETC
*, STGMEDIUM
*) override
;
225 STDMETHODIMP
QueryGetData(FORMATETC
*pFE
) override
;
226 STDMETHODIMP
GetCanonicalFormatEtc(FORMATETC
*, FORMATETC
*pFEOut
) override
;
227 STDMETHODIMP
SetData(FORMATETC
*, STGMEDIUM
*, BOOL
) override
;
228 STDMETHODIMP
EnumFormatEtc(DWORD dwDirection
, IEnumFORMATETC
**ppEnum
) override
;
229 STDMETHODIMP
DAdvise(FORMATETC
*, DWORD
, IAdviseSink
*, PDWORD
) override
;
230 STDMETHODIMP
DUnadvise(DWORD
) override
;
231 STDMETHODIMP
EnumDAdvise(IEnumSTATDATA
**) override
;
236 class DropTarget final
: public IDropTarget
{
238 ScintillaWin
*sci
= nullptr;
241 STDMETHODIMP
QueryInterface(REFIID riid
, PVOID
*ppv
) override
;
242 STDMETHODIMP_(ULONG
)AddRef() override
;
243 STDMETHODIMP_(ULONG
)Release() override
;
246 STDMETHODIMP
DragEnter(LPDATAOBJECT pIDataSource
, DWORD grfKeyState
, POINTL pt
, PDWORD pdwEffect
) override
;
247 STDMETHODIMP
DragOver(DWORD grfKeyState
, POINTL pt
, PDWORD pdwEffect
) override
;
248 STDMETHODIMP
DragLeave() override
;
249 STDMETHODIMP
Drop(LPDATAOBJECT pIDataSource
, DWORD grfKeyState
, POINTL pt
, PDWORD pdwEffect
) override
;
256 IMContext(HWND hwnd_
) noexcept
:
257 hwnd(hwnd_
), hIMC(::ImmGetContext(hwnd_
)) {
259 // Deleted so IMContext objects can not be copied.
260 IMContext(const IMContext
&) = delete;
261 IMContext(IMContext
&&) = delete;
262 IMContext
&operator=(const IMContext
&) = delete;
263 IMContext
&operator=(IMContext
&&) = delete;
266 ::ImmReleaseContext(hwnd
, hIMC
);
269 unsigned int GetImeCaretPos() const noexcept
{
270 return ImmGetCompositionStringW(hIMC
, GCS_CURSORPOS
, nullptr, 0);
273 std::vector
<BYTE
> GetImeAttributes() {
274 const int attrLen
= ::ImmGetCompositionStringW(hIMC
, GCS_COMPATTR
, nullptr, 0);
275 std::vector
<BYTE
> attr(attrLen
, 0);
276 ::ImmGetCompositionStringW(hIMC
, GCS_COMPATTR
, &attr
[0], static_cast<DWORD
>(attr
.size()));
280 LONG
GetCompositionStringLength(DWORD dwIndex
) const noexcept
{
281 const LONG byteLen
= ::ImmGetCompositionStringW(hIMC
, dwIndex
, nullptr, 0);
282 return byteLen
/ sizeof(wchar_t);
285 std::wstring
GetCompositionString(DWORD dwIndex
) {
286 const LONG byteLen
= ::ImmGetCompositionStringW(hIMC
, dwIndex
, nullptr, 0);
287 std::wstring
wcs(byteLen
/ 2, 0);
288 ::ImmGetCompositionStringW(hIMC
, dwIndex
, &wcs
[0], byteLen
);
295 class ReverseArrowCursor
{
300 ReverseArrowCursor() noexcept
{}
301 // Deleted so ReverseArrowCursor objects can not be copied.
302 ReverseArrowCursor(const ReverseArrowCursor
&) = delete;
303 ReverseArrowCursor(ReverseArrowCursor
&&) = delete;
304 ReverseArrowCursor
&operator=(const ReverseArrowCursor
&) = delete;
305 ReverseArrowCursor
&operator=(ReverseArrowCursor
&&) = delete;
306 ~ReverseArrowCursor() {
308 ::DestroyCursor(cursor
);
312 void Invalidate() noexcept
{
316 HCURSOR
Load(UINT dpi
) noexcept
{
321 ::DestroyCursor(cursor
);
325 cursor
= LoadReverseArrowCursor(dpi
);
326 return cursor
? cursor
: ::LoadCursor({}, IDC_ARROW
);
330 struct HorizontalScrollRange
{
335 CLIPFORMAT
RegisterClipboardType(LPCWSTR lpszFormat
) noexcept
{
336 // Registered clipboard format values are 0xC000 through 0xFFFF.
337 // RegisterClipboardFormatW returns 32-bit unsigned and CLIPFORMAT is 16-bit
338 // unsigned so choose the low 16-bits with &.
339 return ::RegisterClipboardFormatW(lpszFormat
) & 0xFFFF;
344 namespace Scintilla::Internal
{
349 public ScintillaBase
{
351 bool lastKeyDownConsumed
;
352 wchar_t lastHighSurrogateChar
;
355 bool trackedMouseLeave
;
356 BOOL typingWithoutCursor
;
358 SetCoalescableTimerSig SetCoalescableTimerFn
;
360 unsigned int linesPerScroll
; ///< Intellimouse support
361 unsigned int charsPerScroll
; ///< Intellimouse support
362 MouseWheelDelta verticalWheelDelta
;
363 MouseWheelDelta horizontalWheelDelta
;
365 UINT dpi
= USER_DEFAULT_SCREEN_DPI
;
366 ReverseArrowCursor reverseArrowCursor
;
368 PRectangle rectangleClient
;
373 CLIPFORMAT cfColumnSelect
;
374 CLIPFORMAT cfBorlandIDEBlockType
;
375 CLIPFORMAT cfLineSelect
;
376 CLIPFORMAT cfVSLineTag
;
383 static HINSTANCE hInstance
;
384 static ATOM scintillaClassAtom
;
385 static ATOM callClassAtom
;
387 float deviceScaleFactor
= 1.f
;
388 int GetFirstIntegralMultipleDeviceScaleFactor() const noexcept
{
389 return static_cast<int>(std::ceil(deviceScaleFactor
));
393 ID2D1RenderTarget
*pRenderTarget
;
394 bool renderTargetValid
;
395 // rendering parameters for current monitor
396 HMONITOR hCurrentMonitor
;
397 std::shared_ptr
<RenderingParams
> renderingParams
;
400 explicit ScintillaWin(HWND hwnd
);
401 // Deleted so ScintillaWin objects can not be copied.
402 ScintillaWin(const ScintillaWin
&) = delete;
403 ScintillaWin(ScintillaWin
&&) = delete;
404 ScintillaWin
&operator=(const ScintillaWin
&) = delete;
405 ScintillaWin
&operator=(ScintillaWin
&&) = delete;
406 // ~ScintillaWin() in public section
408 void Finalise() override
;
410 bool UpdateRenderingParams(bool force
) noexcept
;
411 void EnsureRenderTarget(HDC hdc
);
413 void DropRenderTarget() noexcept
;
414 HWND
MainHWND() const noexcept
;
416 static sptr_t
DirectFunction(
417 sptr_t ptr
, UINT iMessage
, uptr_t wParam
, sptr_t lParam
);
418 static sptr_t
DirectStatusFunction(
419 sptr_t ptr
, UINT iMessage
, uptr_t wParam
, sptr_t lParam
, int *pStatus
);
420 static LRESULT PASCAL
SWndProc(
421 HWND hWnd
, UINT iMessage
, WPARAM wParam
, LPARAM lParam
);
422 static LRESULT PASCAL
CTWndProc(
423 HWND hWnd
, UINT iMessage
, WPARAM wParam
, LPARAM lParam
);
425 enum : UINT_PTR
{ invalidTimerID
, standardTimerID
, idleTimerID
, fineTimerStart
};
427 void DisplayCursor(Window::Cursor c
) override
;
428 bool DragThreshold(Point ptStart
, Point ptNow
) override
;
429 void StartDrag() override
;
430 static KeyMod
MouseModifiers(uptr_t wParam
) noexcept
;
432 Sci::Position
TargetAsUTF8(char *text
) const;
433 Sci::Position
EncodedFromUTF8(const char *utf8
, char *encoded
) const;
435 void SetRenderingParams(Surface
*psurf
) const;
437 bool PaintDC(HDC hdc
);
441 void ImeStartComposition();
442 void ImeEndComposition();
443 LRESULT
ImeOnReconvert(LPARAM lParam
);
444 LRESULT
ImeOnDocumentFeed(LPARAM lParam
) const;
445 sptr_t
HandleCompositionWindowed(uptr_t wParam
, sptr_t lParam
);
446 sptr_t
HandleCompositionInline(uptr_t wParam
, sptr_t lParam
);
447 static bool KoreanIME() noexcept
;
448 void MoveImeCarets(Sci::Position offset
) noexcept
;
449 void DrawImeIndicator(int indicator
, Sci::Position len
);
450 void SetCandidateWindowPos();
451 void SelectionToHangul();
454 void AddWString(std::wstring_view wsv
, CharacterSource charSource
);
456 UINT
CodePageOfDocument() const noexcept
;
457 bool ValidCodePage(int codePage
) const override
;
458 std::string
UTF8FromEncoded(std::string_view encoded
) const override
;
459 std::string
EncodedFromUTF8(std::string_view utf8
) const override
;
461 std::string
EncodeWString(std::wstring_view wsv
);
462 sptr_t
DefWndProc(Message iMessage
, uptr_t wParam
, sptr_t lParam
) override
;
463 void IdleWork() override
;
464 void QueueIdleWork(WorkItems items
, Sci::Position upTo
) override
;
465 bool SetIdle(bool on
) override
;
466 UINT_PTR timers
[static_cast<int>(TickReason::dwell
)+1] {};
467 bool FineTickerRunning(TickReason reason
) override
;
468 void FineTickerStart(TickReason reason
, int millis
, int tolerance
) override
;
469 void FineTickerCancel(TickReason reason
) override
;
470 void SetMouseCapture(bool on
) override
;
471 bool HaveMouseCapture() override
;
472 void SetTrackMouseLeaveEvent(bool on
) noexcept
;
473 void HideCursorIfPreferred() noexcept
;
474 void UpdateBaseElements() override
;
475 bool PaintContains(PRectangle rc
) override
;
476 void ScrollText(Sci::Line linesToMove
) override
;
477 void NotifyCaretMove() override
;
478 void UpdateSystemCaret() override
;
479 void SetVerticalScrollPos() override
;
480 void SetHorizontalScrollPos() override
;
481 void HorizontalScrollToClamped(int xPos
);
482 HorizontalScrollRange
GetHorizontalScrollRange() const;
483 bool ModifyScrollBars(Sci::Line nMax
, Sci::Line nPage
) override
;
484 void NotifyChange() override
;
485 void NotifyFocus(bool focus
) override
;
486 void SetCtrlID(int identifier
) override
;
487 int GetCtrlID() override
;
488 void NotifyParent(NotificationData scn
) override
;
489 virtual void NotifyParent(SCNotification
*scn
);
490 void NotifyDoubleClick(Point pt
, KeyMod modifiers
) override
;
491 std::unique_ptr
<CaseFolder
> CaseFolderForEncoding() override
;
492 std::string
CaseMapString(const std::string
&s
, CaseMapping caseMapping
) override
;
493 void Copy() override
;
494 bool CanPaste() override
;
495 void Paste() override
;
496 void CreateCallTipWindow(PRectangle rc
) override
;
497 void AddToPopUp(const char *label
, int cmd
= 0, bool enabled
= true) override
;
498 void ClaimSelection() override
;
500 void GetMouseParameters() noexcept
;
501 void CopyToGlobal(GlobalMemory
&gmUnicode
, const SelectionText
&selectedText
);
502 void CopyToClipboard(const SelectionText
&selectedText
) override
;
503 void ScrollMessage(WPARAM wParam
);
504 void HorizontalScrollMessage(WPARAM wParam
);
506 void FullPaintDC(HDC hdc
);
507 bool IsCompatibleDC(HDC hOtherDC
) noexcept
;
508 DWORD
EffectFromState(DWORD grfKeyState
) const noexcept
;
510 bool IsVisible() const noexcept
;
511 int SetScrollInfo(int nBar
, LPCSCROLLINFO lpsi
, BOOL bRedraw
) noexcept
;
512 bool GetScrollInfo(int nBar
, LPSCROLLINFO lpsi
) noexcept
;
513 bool ChangeScrollRange(int nBar
, int nMin
, int nMax
, UINT nPage
) noexcept
;
514 void ChangeScrollPos(int barType
, Sci::Position pos
);
515 sptr_t
GetTextLength();
516 sptr_t
GetText(uptr_t wParam
, sptr_t lParam
);
517 Window::Cursor
ContextCursor(Point pt
);
518 sptr_t
ShowContextMenu(unsigned int iMessage
, uptr_t wParam
, sptr_t lParam
);
519 PRectangle
GetClientRectangle() const override
;
521 sptr_t
MouseMessage(unsigned int iMessage
, uptr_t wParam
, sptr_t lParam
);
522 sptr_t
KeyMessage(unsigned int iMessage
, uptr_t wParam
, sptr_t lParam
);
523 sptr_t
FocusMessage(unsigned int iMessage
, uptr_t wParam
, sptr_t lParam
);
524 sptr_t
IMEMessage(unsigned int iMessage
, uptr_t wParam
, sptr_t lParam
);
525 sptr_t
EditMessage(unsigned int iMessage
, uptr_t wParam
, sptr_t lParam
);
526 sptr_t
IdleMessage(unsigned int iMessage
, uptr_t wParam
, sptr_t lParam
);
527 sptr_t
SciMessage(Message iMessage
, uptr_t wParam
, sptr_t lParam
);
530 ~ScintillaWin() override
;
532 // Public for benefit of Scintilla_DirectFunction
533 sptr_t
WndProc(Message iMessage
, uptr_t wParam
, sptr_t lParam
) override
;
535 /// Implement IUnknown
536 STDMETHODIMP
QueryInterface(REFIID riid
, PVOID
*ppv
);
537 STDMETHODIMP_(ULONG
)AddRef();
538 STDMETHODIMP_(ULONG
)Release();
540 /// Implement IDropTarget
541 STDMETHODIMP
DragEnter(LPDATAOBJECT pIDataSource
, DWORD grfKeyState
,
542 POINTL pt
, PDWORD pdwEffect
);
543 STDMETHODIMP
DragOver(DWORD grfKeyState
, POINTL pt
, PDWORD pdwEffect
);
544 STDMETHODIMP
DragLeave();
545 STDMETHODIMP
Drop(LPDATAOBJECT pIDataSource
, DWORD grfKeyState
,
546 POINTL pt
, PDWORD pdwEffect
);
548 /// Implement important part of IDataObject
549 STDMETHODIMP
GetData(FORMATETC
*pFEIn
, STGMEDIUM
*pSTM
);
551 static void Prepare() noexcept
;
552 static bool Register(HINSTANCE hInstance_
) noexcept
;
553 static bool Unregister() noexcept
;
555 bool DragIsRectangularOK(CLIPFORMAT fmt
) const noexcept
{
556 return drag
.rectangular
&& (fmt
== cfColumnSelect
);
560 // For use in creating a system caret
561 bool HasCaretSizeChanged() const noexcept
;
562 BOOL
CreateSystemCaret();
563 BOOL
DestroySystemCaret() noexcept
;
564 HBITMAP sysCaretBitmap
;
567 bool styleIdleInQueue
;
570 HINSTANCE
ScintillaWin::hInstance
{};
571 ATOM
ScintillaWin::scintillaClassAtom
= 0;
572 ATOM
ScintillaWin::callClassAtom
= 0;
574 ScintillaWin::ScintillaWin(HWND hwnd
) {
576 lastKeyDownConsumed
= false;
577 lastHighSurrogateChar
= 0;
579 capturedMouse
= false;
580 trackedMouseLeave
= false;
581 typingWithoutCursor
= false;
582 cursorIsHidden
= false;
583 SetCoalescableTimerFn
= nullptr;
588 dpi
= DpiForWindow(hwnd
);
594 // There does not seem to be a real standard for indicating that the clipboard
595 // contains a rectangular selection, so copy Developer Studio and Borland Delphi.
596 cfColumnSelect
= RegisterClipboardType(L
"MSDEVColumnSelect");
597 cfBorlandIDEBlockType
= RegisterClipboardType(L
"Borland IDE Block Type");
599 // Likewise for line-copy or line-cut (copies or cuts a full line when no text is selected)
600 cfLineSelect
= RegisterClipboardType(L
"MSDEVLineSelect");
601 cfVSLineTag
= RegisterClipboardType(L
"VisualStudioEditorOperationsLineCutCopyClipboardTag");
614 styleIdleInQueue
= false;
617 pRenderTarget
= nullptr;
618 renderTargetValid
= true;
619 hCurrentMonitor
= {};
622 caret
.period
= ::GetCaretBlinkTime();
623 if (caret
.period
< 0)
626 // Initialize COM. If the app has already done this it will have
627 // no effect. If the app hasn't, we really shouldn't ask them to call
628 // it just so this internal feature works.
629 hrOle
= ::OleInitialize(nullptr);
631 // Find SetCoalescableTimer which is only available from Windows 8+
632 HMODULE user32
= ::GetModuleHandleW(L
"user32.dll");
633 SetCoalescableTimerFn
= DLLFunction
<SetCoalescableTimerSig
>(user32
, "SetCoalescableTimer");
635 vs
.indicators
[IndicatorUnknown
] = Indicator(IndicatorStyle::Hidden
, colourIME
);
636 vs
.indicators
[IndicatorInput
] = Indicator(IndicatorStyle::Dots
, colourIME
);
637 vs
.indicators
[IndicatorConverted
] = Indicator(IndicatorStyle::CompositionThick
, colourIME
);
638 vs
.indicators
[IndicatorTarget
] = Indicator(IndicatorStyle::StraightBox
, colourIME
);
641 ScintillaWin::~ScintillaWin() {
642 if (sysCaretBitmap
) {
643 ::DeleteObject(sysCaretBitmap
);
648 void ScintillaWin::Finalise() {
649 ScintillaBase::Finalise();
650 for (TickReason tr
= TickReason::caret
; tr
<= TickReason::dwell
;
651 tr
= static_cast<TickReason
>(static_cast<int>(tr
) + 1)) {
652 FineTickerCancel(tr
);
656 ::RevokeDragDrop(MainHWND());
657 if (SUCCEEDED(hrOle
)) {
664 bool ScintillaWin::UpdateRenderingParams(bool force
) noexcept
{
665 if (!renderingParams
) {
667 renderingParams
= std::make_shared
<RenderingParams
>();
668 } catch (const std::bad_alloc
&) {
672 const HWND hRootWnd
= ::GetAncestor(MainHWND(), GA_ROOT
);
673 const HMONITOR monitor
= Internal::MonitorFromWindowHandleScaling(hRootWnd
);
674 if (!force
&& monitor
== hCurrentMonitor
&& renderingParams
->defaultRenderingParams
) {
678 IDWriteRenderingParams
*monitorRenderingParams
= nullptr;
679 IDWriteRenderingParams
*customClearTypeRenderingParams
= nullptr;
680 const HRESULT hr
= pIDWriteFactory
->CreateMonitorRenderingParams(monitor
, &monitorRenderingParams
);
681 UINT clearTypeContrast
= 0;
682 if (SUCCEEDED(hr
) && ::SystemParametersInfo(SPI_GETFONTSMOOTHINGCONTRAST
, 0, &clearTypeContrast
, 0) != 0) {
683 if (clearTypeContrast
>= 1000 && clearTypeContrast
<= 2200) {
684 const FLOAT gamma
= static_cast<FLOAT
>(clearTypeContrast
) / 1000.0f
;
685 pIDWriteFactory
->CreateCustomRenderingParams(gamma
,
686 monitorRenderingParams
->GetEnhancedContrast(),
687 monitorRenderingParams
->GetClearTypeLevel(),
688 monitorRenderingParams
->GetPixelGeometry(),
689 monitorRenderingParams
->GetRenderingMode(),
690 &customClearTypeRenderingParams
);
694 hCurrentMonitor
= monitor
;
695 deviceScaleFactor
= Internal::GetDeviceScaleFactorWhenGdiScalingActive(hRootWnd
);
696 renderingParams
->defaultRenderingParams
.reset(monitorRenderingParams
);
697 renderingParams
->customRenderingParams
.reset(customClearTypeRenderingParams
);
703 D2D1_SIZE_U
GetSizeUFromRect(const RECT
&rc
, const int scaleFactor
) noexcept
{
704 const long width
= rc
.right
- rc
.left
;
705 const long height
= rc
.bottom
- rc
.top
;
706 const UINT32 scaledWidth
= width
* scaleFactor
;
707 const UINT32 scaledHeight
= height
* scaleFactor
;
708 return D2D1::SizeU(scaledWidth
, scaledHeight
);
713 void ScintillaWin::EnsureRenderTarget(HDC hdc
) {
714 if (!renderTargetValid
) {
716 renderTargetValid
= true;
718 if (!pRenderTarget
) {
719 HWND hw
= MainHWND();
721 ::GetClientRect(hw
, &rc
);
723 // Create a Direct2D render target.
724 D2D1_RENDER_TARGET_PROPERTIES drtp
{};
725 drtp
.type
= D2D1_RENDER_TARGET_TYPE_DEFAULT
;
726 drtp
.usage
= D2D1_RENDER_TARGET_USAGE_NONE
;
727 drtp
.minLevel
= D2D1_FEATURE_LEVEL_DEFAULT
;
729 if (technology
== Technology::DirectWriteDC
) {
732 // Explicit pixel format needed.
733 drtp
.pixelFormat
= D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM
,
734 D2D1_ALPHA_MODE_IGNORE
);
736 ID2D1DCRenderTarget
*pDCRT
= nullptr;
737 const HRESULT hr
= pD2DFactory
->CreateDCRenderTarget(&drtp
, &pDCRT
);
739 pRenderTarget
= pDCRT
;
741 Platform::DebugPrintf("Failed CreateDCRenderTarget 0x%lx\n", hr
);
742 pRenderTarget
= nullptr;
746 const int integralDeviceScaleFactor
= GetFirstIntegralMultipleDeviceScaleFactor();
747 drtp
.dpiX
= 96.f
* integralDeviceScaleFactor
;
748 drtp
.dpiY
= 96.f
* integralDeviceScaleFactor
;
749 drtp
.pixelFormat
= D2D1::PixelFormat(DXGI_FORMAT_UNKNOWN
,
750 D2D1_ALPHA_MODE_UNKNOWN
);
752 D2D1_HWND_RENDER_TARGET_PROPERTIES dhrtp
{};
754 dhrtp
.pixelSize
= ::GetSizeUFromRect(rc
, integralDeviceScaleFactor
);
755 dhrtp
.presentOptions
= (technology
== Technology::DirectWriteRetain
) ?
756 D2D1_PRESENT_OPTIONS_RETAIN_CONTENTS
: D2D1_PRESENT_OPTIONS_NONE
;
758 ID2D1HwndRenderTarget
*pHwndRenderTarget
= nullptr;
759 const HRESULT hr
= pD2DFactory
->CreateHwndRenderTarget(drtp
, dhrtp
, &pHwndRenderTarget
);
761 pRenderTarget
= pHwndRenderTarget
;
763 Platform::DebugPrintf("Failed CreateHwndRenderTarget 0x%lx\n", hr
);
764 pRenderTarget
= nullptr;
767 // Pixmaps were created to be compatible with previous render target so
768 // need to be recreated.
772 if ((technology
== Technology::DirectWriteDC
) && pRenderTarget
) {
774 ::GetClientRect(MainHWND(), &rcWindow
);
775 const HRESULT hr
= static_cast<ID2D1DCRenderTarget
*>(pRenderTarget
)->BindDC(hdc
, &rcWindow
);
777 Platform::DebugPrintf("BindDC failed 0x%lx\n", hr
);
784 void ScintillaWin::DropRenderTarget() noexcept
{
786 ReleaseUnknown(pRenderTarget
);
790 HWND
ScintillaWin::MainHWND() const noexcept
{
791 return HwndFromWindow(wMain
);
794 void ScintillaWin::DisplayCursor(Window::Cursor c
) {
795 if (cursorMode
!= CursorShape::Normal
) {
796 c
= static_cast<Window::Cursor
>(cursorMode
);
798 if (c
== Window::Cursor::reverseArrow
) {
799 ::SetCursor(reverseArrowCursor
.Load(static_cast<UINT
>(dpi
* deviceScaleFactor
)));
805 bool ScintillaWin::DragThreshold(Point ptStart
, Point ptNow
) {
806 const Point ptDifference
= ptStart
- ptNow
;
807 const XYPOSITION xMove
= std::trunc(std::abs(ptDifference
.x
));
808 const XYPOSITION yMove
= std::trunc(std::abs(ptDifference
.y
));
809 return (xMove
> SystemMetricsForDpi(SM_CXDRAG
, dpi
)) ||
810 (yMove
> SystemMetricsForDpi(SM_CYDRAG
, dpi
));
813 void ScintillaWin::StartDrag() {
814 inDragDrop
= DragDrop::dragging
;
816 dropWentOutside
= true;
817 IDataObject
*pDataObject
= &dob
;
818 IDropSource
*pDropSource
= &ds
;
819 //Platform::DebugPrintf("About to DoDragDrop %x %x\n", pDataObject, pDropSource);
820 const HRESULT hr
= ::DoDragDrop(
823 DROPEFFECT_COPY
| DROPEFFECT_MOVE
, &dwEffect
);
824 //Platform::DebugPrintf("DoDragDrop = %x\n", hr);
826 if ((hr
== DRAGDROP_S_DROP
) && (dwEffect
== DROPEFFECT_MOVE
) && dropWentOutside
) {
827 // Remove dragged out text
831 inDragDrop
= DragDrop::none
;
832 SetDragPosition(SelectionPosition(Sci::invalidPosition
));
835 KeyMod
ScintillaWin::MouseModifiers(uptr_t wParam
) noexcept
{
836 return ModifierFlags(
837 (wParam
& MK_SHIFT
) != 0,
838 (wParam
& MK_CONTROL
) != 0,
839 KeyboardIsKeyDown(VK_MENU
));
846 int InputCodePage() noexcept
{
847 HKL inputLocale
= ::GetKeyboardLayout(0);
848 const LANGID inputLang
= LOWORD(inputLocale
);
850 const int res
= ::GetLocaleInfoA(MAKELCID(inputLang
, SORT_DEFAULT
),
851 LOCALE_IDEFAULTANSICODEPAGE
, sCodePage
, sizeof(sCodePage
));
854 return atoi(sCodePage
);
857 /** Map the key codes to their equivalent Keys:: form. */
858 Keys
KeyTranslate(uptr_t keyIn
) noexcept
{
860 case VK_DOWN
: return Keys::Down
;
861 case VK_UP
: return Keys::Up
;
862 case VK_LEFT
: return Keys::Left
;
863 case VK_RIGHT
: return Keys::Right
;
864 case VK_HOME
: return Keys::Home
;
865 case VK_END
: return Keys::End
;
866 case VK_PRIOR
: return Keys::Prior
;
867 case VK_NEXT
: return Keys::Next
;
868 case VK_DELETE
: return Keys::Delete
;
869 case VK_INSERT
: return Keys::Insert
;
870 case VK_ESCAPE
: return Keys::Escape
;
871 case VK_BACK
: return Keys::Back
;
872 case VK_TAB
: return Keys::Tab
;
873 case VK_RETURN
: return Keys::Return
;
874 case VK_ADD
: return Keys::Add
;
875 case VK_SUBTRACT
: return Keys::Subtract
;
876 case VK_DIVIDE
: return Keys::Divide
;
877 case VK_LWIN
: return Keys::Win
;
878 case VK_RWIN
: return Keys::RWin
;
879 case VK_APPS
: return Keys::Menu
;
880 case VK_OEM_2
: return static_cast<Keys
>('/');
881 case VK_OEM_3
: return static_cast<Keys
>('`');
882 case VK_OEM_4
: return static_cast<Keys
>('[');
883 case VK_OEM_5
: return static_cast<Keys
>('\\');
884 case VK_OEM_6
: return static_cast<Keys
>(']');
885 default: return static_cast<Keys
>(keyIn
);
889 bool BoundsContains(PRectangle rcBounds
, HRGN hRgnBounds
, PRectangle rcCheck
) noexcept
{
890 bool contains
= true;
891 if (!rcCheck
.Empty()) {
892 if (!rcBounds
.Contains(rcCheck
)) {
894 } else if (hRgnBounds
) {
895 // In bounding rectangle so check more accurately using region
896 const RECT rcw
= RectFromPRectangle(rcCheck
);
897 HRGN hRgnCheck
= ::CreateRectRgnIndirect(&rcw
);
899 HRGN hRgnDifference
= ::CreateRectRgn(0, 0, 0, 0);
900 if (hRgnDifference
) {
901 const int combination
= ::CombineRgn(hRgnDifference
, hRgnCheck
, hRgnBounds
, RGN_DIFF
);
902 if (combination
!= NULLREGION
) {
905 ::DeleteRgn(hRgnDifference
);
907 ::DeleteRgn(hRgnCheck
);
914 // Simplify calling WideCharToMultiByte and MultiByteToWideChar by providing default parameters and using string view.
916 int MultiByteFromWideChar(UINT codePage
, std::wstring_view wsv
, LPSTR lpMultiByteStr
, ptrdiff_t cbMultiByte
) noexcept
{
917 return ::WideCharToMultiByte(codePage
, 0, wsv
.data(), static_cast<int>(wsv
.length()), lpMultiByteStr
, static_cast<int>(cbMultiByte
), nullptr, nullptr);
920 int MultiByteLenFromWideChar(UINT codePage
, std::wstring_view wsv
) noexcept
{
921 return MultiByteFromWideChar(codePage
, wsv
, nullptr, 0);
924 int WideCharFromMultiByte(UINT codePage
, std::string_view sv
, LPWSTR lpWideCharStr
, ptrdiff_t cchWideChar
) noexcept
{
925 return ::MultiByteToWideChar(codePage
, 0, sv
.data(), static_cast<int>(sv
.length()), lpWideCharStr
, static_cast<int>(cchWideChar
));
928 int WideCharLenFromMultiByte(UINT codePage
, std::string_view sv
) noexcept
{
929 return WideCharFromMultiByte(codePage
, sv
, nullptr, 0);
932 std::string
StringEncode(std::wstring_view wsv
, int codePage
) {
933 const int cchMulti
= wsv
.length() ? MultiByteLenFromWideChar(codePage
, wsv
) : 0;
934 std::string
sMulti(cchMulti
, 0);
936 MultiByteFromWideChar(codePage
, wsv
, sMulti
.data(), cchMulti
);
941 std::wstring
StringDecode(std::string_view sv
, int codePage
) {
942 const int cchWide
= sv
.length() ? WideCharLenFromMultiByte(codePage
, sv
) : 0;
943 std::wstring
sWide(cchWide
, 0);
945 WideCharFromMultiByte(codePage
, sv
, sWide
.data(), cchWide
);
950 std::wstring
StringMapCase(std::wstring_view wsv
, DWORD mapFlags
) {
951 const int charsConverted
= ::LCMapStringW(LOCALE_SYSTEM_DEFAULT
, mapFlags
,
952 wsv
.data(), static_cast<int>(wsv
.length()), nullptr, 0);
953 std::wstring
wsConverted(charsConverted
, 0);
954 if (charsConverted
) {
955 ::LCMapStringW(LOCALE_SYSTEM_DEFAULT
, mapFlags
,
956 wsv
.data(), static_cast<int>(wsv
.length()), wsConverted
.data(), charsConverted
);
963 // Returns the target converted to UTF8.
964 // Return the length in bytes.
965 Sci::Position
ScintillaWin::TargetAsUTF8(char *text
) const {
966 const Sci::Position targetLength
= targetRange
.Length();
967 if (IsUnicodeMode()) {
969 pdoc
->GetCharRange(text
, targetRange
.start
.Position(), targetLength
);
973 const std::string s
= RangeText(targetRange
.start
.Position(), targetRange
.end
.Position());
974 const std::wstring characters
= StringDecode(s
, CodePageOfDocument());
975 const int utf8Len
= MultiByteLenFromWideChar(CpUtf8
, characters
);
977 MultiByteFromWideChar(CpUtf8
, characters
, text
, utf8Len
);
978 text
[utf8Len
] = '\0';
985 // Translates a nul terminated UTF8 string into the document encoding.
986 // Return the length of the result in bytes.
987 Sci::Position
ScintillaWin::EncodedFromUTF8(const char *utf8
, char *encoded
) const {
988 const Sci::Position inputLength
= (lengthForEncode
>= 0) ? lengthForEncode
: strlen(utf8
);
989 if (IsUnicodeMode()) {
991 memcpy(encoded
, utf8
, inputLength
);
996 const std::string_view
utf8Input(utf8
, inputLength
);
997 const int charsLen
= WideCharLenFromMultiByte(CpUtf8
, utf8Input
);
998 std::wstring
characters(charsLen
, L
'\0');
999 WideCharFromMultiByte(CpUtf8
, utf8Input
, &characters
[0], charsLen
);
1001 const int encodedLen
= MultiByteLenFromWideChar(CodePageOfDocument(), characters
);
1003 MultiByteFromWideChar(CodePageOfDocument(), characters
, encoded
, encodedLen
);
1004 encoded
[encodedLen
] = '\0';
1010 void ScintillaWin::SetRenderingParams([[maybe_unused
]] Surface
*psurf
) const {
1011 #if defined(USE_D2D)
1013 ISetRenderingParams
*setDrawingParams
= dynamic_cast<ISetRenderingParams
*>(psurf
);
1014 if (setDrawingParams
) {
1015 setDrawingParams
->SetRenderingParams(renderingParams
);
1021 bool ScintillaWin::PaintDC(HDC hdc
) {
1022 if (technology
== Technology::Default
) {
1023 AutoSurface
surfaceWindow(hdc
, this);
1024 if (surfaceWindow
) {
1025 Paint(surfaceWindow
, rcPaint
);
1026 surfaceWindow
->Release();
1029 #if defined(USE_D2D)
1030 EnsureRenderTarget(hdc
);
1031 if (pRenderTarget
) {
1032 AutoSurface
surfaceWindow(pRenderTarget
, this);
1033 if (surfaceWindow
) {
1034 SetRenderingParams(surfaceWindow
);
1035 pRenderTarget
->BeginDraw();
1036 Paint(surfaceWindow
, rcPaint
);
1037 surfaceWindow
->Release();
1038 const HRESULT hr
= pRenderTarget
->EndDraw();
1039 if (hr
== static_cast<HRESULT
>(D2DERR_RECREATE_TARGET
)) {
1051 sptr_t
ScintillaWin::WndPaint() {
1054 // Redirect assertions to debug output and save current state
1055 const bool assertsPopup
= Platform::ShowAssertionPopUps(false);
1056 paintState
= PaintState::painting
;
1057 PAINTSTRUCT ps
= {};
1059 // Removed since this interferes with reporting other assertions as it occurs repeatedly
1060 //PLATFORM_ASSERT(hRgnUpdate == NULL);
1061 hRgnUpdate
= ::CreateRectRgn(0, 0, 0, 0);
1062 ::GetUpdateRgn(MainHWND(), hRgnUpdate
, FALSE
);
1063 ::BeginPaint(MainHWND(), &ps
);
1064 rcPaint
= PRectangle::FromInts(ps
.rcPaint
.left
, ps
.rcPaint
.top
, ps
.rcPaint
.right
, ps
.rcPaint
.bottom
);
1065 const PRectangle rcClient
= GetClientRectangle();
1066 paintingAllText
= BoundsContains(rcPaint
, hRgnUpdate
, rcClient
);
1067 if (!PaintDC(ps
.hdc
)) {
1068 paintState
= PaintState::abandoned
;
1071 ::DeleteRgn(hRgnUpdate
);
1075 ::EndPaint(MainHWND(), &ps
);
1076 if (paintState
== PaintState::abandoned
) {
1077 // Painting area was insufficient to cover new styling or brace highlight positions
1079 ::ValidateRect(MainHWND(), nullptr);
1081 paintState
= PaintState::notPainting
;
1083 // Restore debug output state
1084 Platform::ShowAssertionPopUps(assertsPopup
);
1086 //Platform::DebugPrintf("Paint took %g\n", ep.Duration());
1090 sptr_t
ScintillaWin::HandleCompositionWindowed(uptr_t wParam
, sptr_t lParam
) {
1091 if (lParam
& GCS_RESULTSTR
) {
1092 IMContext
imc(MainHWND());
1094 AddWString(imc
.GetCompositionString(GCS_RESULTSTR
), CharacterSource::ImeResult
);
1096 // Set new position after converted
1097 const Point pos
= PointMainCaret();
1098 COMPOSITIONFORM CompForm
{};
1099 CompForm
.dwStyle
= CFS_POINT
;
1100 CompForm
.ptCurrentPos
= POINTFromPoint(pos
);
1101 ::ImmSetCompositionWindow(imc
.hIMC
, &CompForm
);
1105 return ::DefWindowProc(MainHWND(), WM_IME_COMPOSITION
, wParam
, lParam
);
1108 bool ScintillaWin::KoreanIME() noexcept
{
1109 const int codePage
= InputCodePage();
1110 return codePage
== 949 || codePage
== 1361;
1113 void ScintillaWin::MoveImeCarets(Sci::Position offset
) noexcept
{
1114 // Move carets relatively by bytes.
1115 for (size_t r
=0; r
<sel
.Count(); r
++) {
1116 const Sci::Position positionInsert
= sel
.Range(r
).Start().Position();
1117 sel
.Range(r
).caret
.SetPosition(positionInsert
+ offset
);
1118 sel
.Range(r
).anchor
.SetPosition(positionInsert
+ offset
);
1122 void ScintillaWin::DrawImeIndicator(int indicator
, Sci::Position len
) {
1123 // Emulate the visual style of IME characters with indicators.
1124 // Draw an indicator on the character before caret by the character bytes of len
1125 // so it should be called after InsertCharacter().
1126 // It does not affect caret positions.
1127 if (indicator
< 8 || indicator
> IndicatorMax
) {
1130 pdoc
->DecorationSetCurrentIndicator(indicator
);
1131 for (size_t r
=0; r
<sel
.Count(); r
++) {
1132 const Sci::Position positionInsert
= sel
.Range(r
).Start().Position();
1133 pdoc
->DecorationFillRange(positionInsert
- len
, 1, len
);
1137 void ScintillaWin::SetCandidateWindowPos() {
1138 IMContext
imc(MainHWND());
1140 const Point pos
= PointMainCaret();
1141 const PRectangle rcClient
= GetTextRectangle();
1142 CANDIDATEFORM CandForm
{};
1143 CandForm
.dwIndex
= 0;
1144 CandForm
.dwStyle
= CFS_EXCLUDE
;
1145 CandForm
.ptCurrentPos
.x
= static_cast<int>(pos
.x
);
1146 CandForm
.ptCurrentPos
.y
= static_cast<int>(pos
.y
+ std::max(4, vs
.lineHeight
/4));
1147 // Exclude the area of the whole caret line
1148 CandForm
.rcArea
.top
= static_cast<int>(pos
.y
);
1149 CandForm
.rcArea
.bottom
= static_cast<int>(pos
.y
+ vs
.lineHeight
);
1150 CandForm
.rcArea
.left
= static_cast<int>(rcClient
.left
);
1151 CandForm
.rcArea
.right
= static_cast<int>(rcClient
.right
);
1152 ::ImmSetCandidateWindow(imc
.hIMC
, &CandForm
);
1156 void ScintillaWin::SelectionToHangul() {
1157 // Convert every hanja to hangul within the main range.
1158 const Sci::Position selStart
= sel
.RangeMain().Start().Position();
1159 const Sci::Position documentStrLen
= sel
.RangeMain().Length();
1160 const Sci::Position selEnd
= selStart
+ documentStrLen
;
1161 const Sci::Position utf16Len
= pdoc
->CountUTF16(selStart
, selEnd
);
1164 std::string
documentStr(documentStrLen
, '\0');
1165 pdoc
->GetCharRange(&documentStr
[0], selStart
, documentStrLen
);
1167 std::wstring uniStr
= StringDecode(documentStr
, CodePageOfDocument());
1168 const bool converted
= HanjaDict::GetHangulOfHanja(uniStr
);
1171 documentStr
= StringEncode(uniStr
, CodePageOfDocument());
1172 pdoc
->BeginUndoAction();
1174 InsertPaste(&documentStr
[0], documentStr
.size());
1175 pdoc
->EndUndoAction();
1180 void ScintillaWin::EscapeHanja() {
1181 // The candidate box pops up to user to select a hanja.
1182 // It comes into WM_IME_COMPOSITION with GCS_RESULTSTR.
1183 // The existing hangul or hanja is replaced with it.
1184 if (sel
.Count() > 1) {
1185 return; // Do not allow multi carets.
1187 const Sci::Position currentPos
= CurrentPosition();
1188 const int oneCharLen
= pdoc
->LenChar(currentPos
);
1190 if (oneCharLen
< 2) {
1191 return; // No need to handle SBCS.
1194 // ImmEscapeW() may overwrite uniChar[] with a null terminated string.
1195 // So enlarge it enough to Maximum 4 as in UTF-8.
1196 constexpr size_t safeLength
= UTF8MaxBytes
+ 1;
1197 std::string
oneChar(safeLength
, '\0');
1198 pdoc
->GetCharRange(&oneChar
[0], currentPos
, oneCharLen
);
1200 std::wstring uniChar
= StringDecode(oneChar
, CodePageOfDocument());
1202 IMContext
imc(MainHWND());
1204 // Set the candidate box position since IME may show it.
1205 SetCandidateWindowPos();
1206 // IME_ESC_HANJA_MODE appears to receive the first character only.
1207 if (::ImmEscapeW(GetKeyboardLayout(0), imc
.hIMC
, IME_ESC_HANJA_MODE
, &uniChar
[0])) {
1208 SetSelection(currentPos
, currentPos
+ oneCharLen
);
1213 void ScintillaWin::ToggleHanja() {
1214 // If selection, convert every hanja to hangul within the main range.
1215 // If no selection, commit to IME.
1216 if (sel
.Count() > 1) {
1217 return; // Do not allow multi carets.
1223 SelectionToHangul();
1229 std::vector
<int> MapImeIndicators(std::vector
<BYTE
> inputStyle
) {
1230 std::vector
<int> imeIndicator(inputStyle
.size(), IndicatorUnknown
);
1231 for (size_t i
= 0; i
< inputStyle
.size(); i
++) {
1232 switch (static_cast<int>(inputStyle
.at(i
))) {
1234 imeIndicator
[i
] = IndicatorInput
;
1236 case ATTR_TARGET_NOTCONVERTED
:
1237 case ATTR_TARGET_CONVERTED
:
1238 imeIndicator
[i
] = IndicatorTarget
;
1240 case ATTR_CONVERTED
:
1241 imeIndicator
[i
] = IndicatorConverted
;
1244 imeIndicator
[i
] = IndicatorUnknown
;
1248 return imeIndicator
;
1253 void ScintillaWin::AddWString(std::wstring_view wsv
, CharacterSource charSource
) {
1257 const int codePage
= CodePageOfDocument();
1258 for (size_t i
= 0; i
< wsv
.size(); ) {
1259 const size_t ucWidth
= UTF16CharLength(wsv
[i
]);
1260 const std::string docChar
= StringEncode(wsv
.substr(i
, ucWidth
), codePage
);
1262 InsertCharacter(docChar
, charSource
);
1267 sptr_t
ScintillaWin::HandleCompositionInline(uptr_t
, sptr_t lParam
) {
1268 // Copy & paste by johnsonj with a lot of helps of Neil.
1269 // Great thanks for my foreruners, jiniya and BLUEnLIVE.
1271 IMContext
imc(MainHWND());
1274 if (pdoc
->IsReadOnly() || SelectionContainsProtected()) {
1275 ::ImmNotifyIME(imc
.hIMC
, NI_COMPOSITIONSTR
, CPS_CANCEL
, 0);
1279 bool initialCompose
= false;
1280 if (pdoc
->TentativeActive()) {
1281 pdoc
->TentativeUndo();
1283 // No tentative undo means start of this composition so
1284 // fill in any virtual spaces.
1285 initialCompose
= true;
1288 view
.imeCaretBlockOverride
= false;
1289 HideCursorIfPreferred();
1291 if (lParam
& GCS_RESULTSTR
) {
1292 AddWString(imc
.GetCompositionString(GCS_RESULTSTR
), CharacterSource::ImeResult
);
1295 if (lParam
& GCS_COMPSTR
) {
1296 const std::wstring wcs
= imc
.GetCompositionString(GCS_COMPSTR
);
1298 ShowCaretAtCurrentPosition();
1302 if (initialCompose
) {
1303 ClearBeforeTentativeStart();
1306 // Set candidate window left aligned to beginning of preedit string.
1307 SetCandidateWindowPos();
1308 pdoc
->TentativeStart(); // TentativeActive from now on.
1310 std::vector
<int> imeIndicator
= MapImeIndicators(imc
.GetImeAttributes());
1312 const int codePage
= CodePageOfDocument();
1313 const std::wstring_view wsv
= wcs
;
1314 for (size_t i
= 0; i
< wsv
.size(); ) {
1315 const size_t ucWidth
= UTF16CharLength(wsv
[i
]);
1316 const std::string docChar
= StringEncode(wsv
.substr(i
, ucWidth
), codePage
);
1318 InsertCharacter(docChar
, CharacterSource::TentativeInput
);
1320 DrawImeIndicator(imeIndicator
[i
], docChar
.size());
1324 // Japanese IME after pressing Tab replaces input string with first candidate item (target string);
1325 // when selecting other candidate item, previous item will be replaced with current one.
1326 // After candidate item been added, it's looks like been full selected, it's better to keep caret
1327 // at end of "selection" (end of input) instead of jump to beginning of input ("selection").
1328 const bool onlyTarget
= std::all_of(imeIndicator
.begin(), imeIndicator
.end(), [](int i
) noexcept
{
1329 return i
== IndicatorTarget
;
1332 // CS_NOMOVECARET: keep caret at beginning of composition string which already moved in InsertCharacter().
1333 // GCS_CURSORPOS: current caret position is provided by IME.
1334 Sci::Position imeEndToImeCaretU16
= -static_cast<Sci::Position
>(wcs
.size());
1335 if (!(lParam
& CS_NOMOVECARET
) && (lParam
& GCS_CURSORPOS
)) {
1336 imeEndToImeCaretU16
+= imc
.GetImeCaretPos();
1338 if (imeEndToImeCaretU16
!= 0) {
1339 // Move back IME caret from current last position to imeCaretPos.
1340 const Sci::Position currentPos
= CurrentPosition();
1341 const Sci::Position imeCaretPosDoc
= pdoc
->GetRelativePositionUTF16(currentPos
, imeEndToImeCaretU16
);
1343 MoveImeCarets(-currentPos
+ imeCaretPosDoc
);
1348 view
.imeCaretBlockOverride
= true;
1351 EnsureCaretVisible();
1352 ShowCaretAtCurrentPosition();
1358 // Translate message IDs from WM_* and EM_* to Message::* so can partly emulate Windows Edit control
1359 Message
SciMessageFromEM(unsigned int iMessage
) noexcept
{
1361 case EM_CANPASTE
: return Message::CanPaste
;
1362 case EM_CANUNDO
: return Message::CanUndo
;
1363 case EM_EMPTYUNDOBUFFER
: return Message::EmptyUndoBuffer
;
1364 case EM_FINDTEXTEX
: return Message::FindText
;
1365 case EM_FORMATRANGE
: return Message::FormatRange
;
1366 case EM_GETFIRSTVISIBLELINE
: return Message::GetFirstVisibleLine
;
1367 case EM_GETLINECOUNT
: return Message::GetLineCount
;
1368 case EM_GETSELTEXT
: return Message::GetSelText
;
1369 case EM_GETTEXTRANGE
: return Message::GetTextRange
;
1370 case EM_HIDESELECTION
: return Message::HideSelection
;
1371 case EM_LINEINDEX
: return Message::PositionFromLine
;
1372 case EM_LINESCROLL
: return Message::LineScroll
;
1373 case EM_REPLACESEL
: return Message::ReplaceSel
;
1374 case EM_SCROLLCARET
: return Message::ScrollCaret
;
1375 case EM_SETREADONLY
: return Message::SetReadOnly
;
1376 case WM_CLEAR
: return Message::Clear
;
1377 case WM_COPY
: return Message::Copy
;
1378 case WM_CUT
: return Message::Cut
;
1379 case WM_SETTEXT
: return Message::SetText
;
1380 case WM_PASTE
: return Message::Paste
;
1381 case WM_UNDO
: return Message::Undo
;
1383 return static_cast<Message
>(iMessage
);
1388 namespace Scintilla::Internal
{
1390 UINT
CodePageFromCharSet(CharacterSet characterSet
, UINT documentCodePage
) noexcept
{
1391 if (documentCodePage
== CpUtf8
) {
1394 switch (characterSet
) {
1395 case CharacterSet::Ansi
: return 1252;
1396 case CharacterSet::Default
: return documentCodePage
? documentCodePage
: 1252;
1397 case CharacterSet::Baltic
: return 1257;
1398 case CharacterSet::ChineseBig5
: return 950;
1399 case CharacterSet::EastEurope
: return 1250;
1400 case CharacterSet::GB2312
: return 936;
1401 case CharacterSet::Greek
: return 1253;
1402 case CharacterSet::Hangul
: return 949;
1403 case CharacterSet::Mac
: return 10000;
1404 case CharacterSet::Oem
: return 437;
1405 case CharacterSet::Russian
: return 1251;
1406 case CharacterSet::ShiftJis
: return 932;
1407 case CharacterSet::Turkish
: return 1254;
1408 case CharacterSet::Johab
: return 1361;
1409 case CharacterSet::Hebrew
: return 1255;
1410 case CharacterSet::Arabic
: return 1256;
1411 case CharacterSet::Vietnamese
: return 1258;
1412 case CharacterSet::Thai
: return 874;
1413 case CharacterSet::Iso8859_15
: return 28605;
1415 case CharacterSet::Cyrillic
: return documentCodePage
;
1416 case CharacterSet::Symbol
: return documentCodePage
;
1419 return documentCodePage
;
1424 UINT
ScintillaWin::CodePageOfDocument() const noexcept
{
1425 return CodePageFromCharSet(vs
.styles
[StyleDefault
].characterSet
, pdoc
->dbcsCodePage
);
1428 std::string
ScintillaWin::EncodeWString(std::wstring_view wsv
) {
1429 if (IsUnicodeMode()) {
1430 const size_t len
= UTF8Length(wsv
);
1431 std::string
putf(len
, 0);
1432 UTF8FromUTF16(wsv
, putf
.data(), len
);
1435 // Not in Unicode mode so convert from Unicode to current Scintilla code page
1436 return StringEncode(wsv
, CodePageOfDocument());
1440 sptr_t
ScintillaWin::GetTextLength() {
1441 if (pdoc
->dbcsCodePage
== 0 || pdoc
->dbcsCodePage
== CpUtf8
) {
1442 return pdoc
->CountUTF16(0, pdoc
->Length());
1444 // Count the number of UTF-16 code units line by line
1445 const UINT cpSrc
= CodePageOfDocument();
1446 const Sci::Line lines
= pdoc
->LinesTotal();
1447 Sci::Position codeUnits
= 0;
1448 std::string lineBytes
;
1449 for (Sci::Line line
= 0; line
< lines
; line
++) {
1450 const Sci::Position start
= pdoc
->LineStart(line
);
1451 const Sci::Position width
= pdoc
->LineStart(line
+1) - start
;
1452 lineBytes
.resize(width
);
1453 pdoc
->GetCharRange(lineBytes
.data(), start
, width
);
1454 codeUnits
+= WideCharLenFromMultiByte(cpSrc
, lineBytes
);
1460 sptr_t
ScintillaWin::GetText(uptr_t wParam
, sptr_t lParam
) {
1462 return GetTextLength();
1467 wchar_t *ptr
= static_cast<wchar_t *>(PtrFromSPtr(lParam
));
1468 if (pdoc
->Length() == 0) {
1472 const Sci::Position lengthWanted
= wParam
- 1;
1473 if (IsUnicodeMode()) {
1474 Sci::Position sizeRequestedRange
= pdoc
->GetRelativePositionUTF16(0, lengthWanted
);
1475 if (sizeRequestedRange
< 0) {
1476 // Requested more text than there is in the document.
1477 sizeRequestedRange
= pdoc
->Length();
1479 std::string
docBytes(sizeRequestedRange
, '\0');
1480 pdoc
->GetCharRange(&docBytes
[0], 0, sizeRequestedRange
);
1481 const size_t uLen
= UTF16FromUTF8(docBytes
, ptr
, lengthWanted
);
1486 // Convert to Unicode using the current Scintilla code page
1487 // Retrieve as UTF-16 line by line
1488 const UINT cpSrc
= CodePageOfDocument();
1489 const Sci::Line lines
= pdoc
->LinesTotal();
1490 Sci::Position codeUnits
= 0;
1491 std::string lineBytes
;
1492 std::wstring lineAsUTF16
;
1493 for (Sci::Line line
= 0; line
< lines
&& codeUnits
< lengthWanted
; line
++) {
1494 const Sci::Position start
= pdoc
->LineStart(line
);
1495 const Sci::Position width
= pdoc
->LineStart(line
+ 1) - start
;
1496 lineBytes
.resize(width
);
1497 pdoc
->GetCharRange(lineBytes
.data(), start
, width
);
1498 const Sci::Position codeUnitsLine
= WideCharLenFromMultiByte(cpSrc
, lineBytes
);
1499 lineAsUTF16
.resize(codeUnitsLine
);
1500 const Sci::Position lengthLeft
= lengthWanted
- codeUnits
;
1501 WideCharFromMultiByte(cpSrc
, lineBytes
, lineAsUTF16
.data(), lineAsUTF16
.length());
1502 const Sci::Position lengthToCopy
= std::min(lengthLeft
, codeUnitsLine
);
1503 lineAsUTF16
.copy(ptr
+ codeUnits
, lengthToCopy
);
1504 codeUnits
+= lengthToCopy
;
1506 ptr
[codeUnits
] = L
'\0';
1511 Window::Cursor
ScintillaWin::ContextCursor(Point pt
) {
1512 if (inDragDrop
== DragDrop::dragging
) {
1513 return Window::Cursor::up
;
1515 // Display regular (drag) cursor over selection
1516 if (PointInSelMargin(pt
)) {
1517 return GetMarginCursor(pt
);
1518 } else if (!SelectionEmpty() && PointInSelection(pt
)) {
1519 return Window::Cursor::arrow
;
1520 } else if (PointIsHotspot(pt
)) {
1521 return Window::Cursor::hand
;
1522 } else if (hoverIndicatorPos
!= Sci::invalidPosition
) {
1523 const Sci::Position pos
= PositionFromLocation(pt
, true, true);
1524 if (pos
!= Sci::invalidPosition
) {
1525 return Window::Cursor::hand
;
1529 return Window::Cursor::text
;
1532 sptr_t
ScintillaWin::ShowContextMenu(unsigned int iMessage
, uptr_t wParam
, sptr_t lParam
) {
1533 Point ptScreen
= PointFromLParam(lParam
);
1535 POINT point
= POINTFromLParam(lParam
);
1536 if ((point
.x
== -1) && (point
.y
== -1)) {
1537 // Caused by keyboard so display menu near caret
1538 ptClient
= PointMainCaret();
1539 point
= POINTFromPoint(ptClient
);
1540 ::ClientToScreen(MainHWND(), &point
);
1541 ptScreen
= PointFromPOINT(point
);
1543 ::ScreenToClient(MainHWND(), &point
);
1544 ptClient
= PointFromPOINT(point
);
1546 if (ShouldDisplayPopup(ptClient
)) {
1547 ContextMenu(ptScreen
);
1550 return ::DefWindowProc(MainHWND(), iMessage
, wParam
, lParam
);
1553 PRectangle
ScintillaWin::GetClientRectangle() const {
1554 return rectangleClient
;
1557 void ScintillaWin::SizeWindow() {
1558 #if defined(USE_D2D)
1559 if (paintState
== PaintState::notPainting
) {
1562 renderTargetValid
= false;
1565 //Platform::DebugPrintf("Scintilla WM_SIZE %d %d\n", LOWORD(lParam), HIWORD(lParam));
1566 rectangleClient
= wMain
.GetClientPosition();
1570 sptr_t
ScintillaWin::MouseMessage(unsigned int iMessage
, uptr_t wParam
, sptr_t lParam
) {
1572 case WM_LBUTTONDOWN
: {
1573 // For IME, set the composition string as the result string.
1574 IMContext
imc(MainHWND());
1576 ::ImmNotifyIME(imc
.hIMC
, NI_COMPOSITIONSTR
, CPS_COMPLETE
, 0);
1579 //Platform::DebugPrintf("Buttdown %d %x %x %x %x %x\n",iMessage, wParam, lParam,
1580 // KeyboardIsKeyDown(VK_SHIFT),
1581 // KeyboardIsKeyDown(VK_CONTROL),
1582 // KeyboardIsKeyDown(VK_MENU));
1583 ::SetFocus(MainHWND());
1584 ButtonDownWithModifiers(PointFromLParam(lParam
), ::GetMessageTime(),
1585 MouseModifiers(wParam
));
1590 ButtonUpWithModifiers(PointFromLParam(lParam
),
1591 ::GetMessageTime(), MouseModifiers(wParam
));
1594 case WM_RBUTTONDOWN
: {
1595 ::SetFocus(MainHWND());
1596 const Point pt
= PointFromLParam(lParam
);
1597 if (!PointInSelection(pt
)) {
1599 SetEmptySelection(PositionFromLocation(PointFromLParam(lParam
)));
1602 RightButtonDownWithModifiers(pt
, ::GetMessageTime(), MouseModifiers(wParam
));
1606 case WM_MOUSEMOVE
: {
1607 cursorIsHidden
= false; // to be shown by ButtonMoveWithModifiers
1608 const Point pt
= PointFromLParam(lParam
);
1610 // Windows might send WM_MOUSEMOVE even though the mouse has not been moved:
1611 // http://blogs.msdn.com/b/oldnewthing/archive/2003/10/01/55108.aspx
1612 if (ptMouseLast
!= pt
) {
1613 SetTrackMouseLeaveEvent(true);
1614 ButtonMoveWithModifiers(pt
, ::GetMessageTime(), MouseModifiers(wParam
));
1620 SetTrackMouseLeaveEvent(false);
1622 return ::DefWindowProc(MainHWND(), iMessage
, wParam
, lParam
);
1625 case WM_MOUSEHWHEEL
:
1626 if (!mouseWheelCaptures
) {
1627 // if the mouse wheel is not captured, test if the mouse
1628 // pointer is over the editor window and if not, don't
1629 // handle the message but pass it on.
1631 GetWindowRect(MainHWND(), &rc
);
1632 const POINT pt
= POINTFromLParam(lParam
);
1633 if (!PtInRect(&rc
, pt
))
1634 return ::DefWindowProc(MainHWND(), iMessage
, wParam
, lParam
);
1636 // if autocomplete list active then send mousewheel message to it
1638 HWND hWnd
= HwndFromWindow(*(ac
.lb
));
1639 ::SendMessage(hWnd
, iMessage
, wParam
, lParam
);
1643 // Treat Shift+WM_MOUSEWHEEL as horizontal scrolling, not data-zoom.
1644 if (iMessage
== WM_MOUSEHWHEEL
|| (wParam
& MK_SHIFT
)) {
1645 if (vs
.wrap
.state
!= Wrap::None
|| charsPerScroll
== 0) {
1646 return ::DefWindowProc(MainHWND(), iMessage
, wParam
, lParam
);
1649 MouseWheelDelta
&wheelDelta
= (iMessage
== WM_MOUSEHWHEEL
) ? horizontalWheelDelta
: verticalWheelDelta
;
1650 if (wheelDelta
.Accumulate(wParam
)) {
1651 const int charsToScroll
= charsPerScroll
* wheelDelta
.Actions();
1652 const int widthToScroll
= static_cast<int>(std::lround(charsToScroll
* vs
.aveCharWidth
));
1653 HorizontalScrollToClamped(xOffset
+ widthToScroll
);
1658 // Either SCROLL vertically or ZOOM. We handle the wheel steppings calculation
1659 if (linesPerScroll
!= 0 && verticalWheelDelta
.Accumulate(wParam
)) {
1660 Sci::Line linesToScroll
= linesPerScroll
;
1661 if (linesPerScroll
== WHEEL_PAGESCROLL
)
1662 linesToScroll
= LinesOnScreen() - 1;
1663 if (linesToScroll
== 0) {
1666 linesToScroll
*= verticalWheelDelta
.Actions();
1668 if (wParam
& MK_CONTROL
) {
1669 // Zoom! We play with the font sizes in the styles.
1670 // Number of steps/line is ignored, we just care if sizing up or down
1671 if (linesToScroll
< 0) {
1672 KeyCommand(Message::ZoomIn
);
1674 KeyCommand(Message::ZoomOut
);
1678 ScrollTo(topLine
+ linesToScroll
);
1686 sptr_t
ScintillaWin::KeyMessage(unsigned int iMessage
, uptr_t wParam
, sptr_t lParam
) {
1691 // Platform::DebugPrintf("Keydown %c %c%c%c%c %x %x\n",
1692 // iMessage == WM_KEYDOWN ? 'K' : 'S',
1693 // (lParam & (1 << 24)) ? 'E' : '-',
1694 // KeyboardIsKeyDown(VK_SHIFT) ? 'S' : '-',
1695 // KeyboardIsKeyDown(VK_CONTROL) ? 'C' : '-',
1696 // KeyboardIsKeyDown(VK_MENU) ? 'A' : '-',
1698 lastKeyDownConsumed
= false;
1699 const bool altDown
= KeyboardIsKeyDown(VK_MENU
);
1700 if (altDown
&& KeyboardIsNumericKeypadFunction(wParam
, lParam
)) {
1701 // Don't interpret these as they may be characters entered by number.
1702 return ::DefWindowProc(MainHWND(), iMessage
, wParam
, lParam
);
1704 const int ret
= KeyDownWithModifiers(
1705 KeyTranslate(wParam
),
1706 ModifierFlags(KeyboardIsKeyDown(VK_SHIFT
),
1707 KeyboardIsKeyDown(VK_CONTROL
),
1709 &lastKeyDownConsumed
);
1710 if (!ret
&& !lastKeyDownConsumed
) {
1711 return ::DefWindowProc(MainHWND(), iMessage
, wParam
, lParam
);
1717 //Platform::DebugPrintf("S keyup %d %x %x\n",iMessage, wParam, lParam);
1718 return ::DefWindowProc(MainHWND(), iMessage
, wParam
, lParam
);
1721 HideCursorIfPreferred();
1722 if (((wParam
>= 128) || !iscntrl(static_cast<int>(wParam
))) || !lastKeyDownConsumed
) {
1723 wchar_t wcs
[3] = { static_cast<wchar_t>(wParam
), 0 };
1724 unsigned int wclen
= 1;
1725 if (IS_HIGH_SURROGATE(wcs
[0])) {
1726 // If this is a high surrogate character, we need a second one
1727 lastHighSurrogateChar
= wcs
[0];
1729 } else if (IS_LOW_SURROGATE(wcs
[0])) {
1731 wcs
[0] = lastHighSurrogateChar
;
1732 lastHighSurrogateChar
= 0;
1735 AddWString(std::wstring_view(wcs
, wclen
), CharacterSource::DirectInput
);
1740 if (wParam
== UNICODE_NOCHAR
) {
1742 } else if (lastKeyDownConsumed
) {
1745 wchar_t wcs
[3] = { 0 };
1746 const size_t wclen
= UTF16FromUTF32Character(static_cast<unsigned int>(wParam
), wcs
);
1747 AddWString(std::wstring_view(wcs
, wclen
), CharacterSource::DirectInput
);
1755 sptr_t
ScintillaWin::FocusMessage(unsigned int iMessage
, uptr_t wParam
, sptr_t
) {
1757 case WM_KILLFOCUS
: {
1758 HWND wOther
= reinterpret_cast<HWND
>(wParam
);
1759 HWND wThis
= MainHWND();
1760 const HWND wCT
= HwndFromWindow(ct
.wCallTip
);
1762 !(::IsChild(wThis
, wOther
) || (wOther
== wCT
))) {
1763 SetFocusState(false);
1764 DestroySystemCaret();
1766 // Explicitly complete any IME composition
1767 IMContext
imc(MainHWND());
1769 ::ImmNotifyIME(imc
.hIMC
, NI_COMPOSITIONSTR
, CPS_COMPLETE
, 0);
1775 SetFocusState(true);
1776 DestroySystemCaret();
1777 CreateSystemCaret();
1783 sptr_t
ScintillaWin::IMEMessage(unsigned int iMessage
, uptr_t wParam
, sptr_t lParam
) {
1786 case WM_INPUTLANGCHANGE
:
1787 return ::DefWindowProc(MainHWND(), iMessage
, wParam
, lParam
);
1789 case WM_INPUTLANGCHANGEREQUEST
:
1790 return ::DefWindowProc(MainHWND(), iMessage
, wParam
, lParam
);
1792 case WM_IME_KEYDOWN
: {
1793 if (wParam
== VK_HANJA
) {
1796 return ::DefWindowProc(MainHWND(), iMessage
, wParam
, lParam
);
1799 case WM_IME_REQUEST
: {
1800 if (wParam
== IMR_RECONVERTSTRING
) {
1801 return ImeOnReconvert(lParam
);
1803 if (wParam
== IMR_DOCUMENTFEED
) {
1804 return ImeOnDocumentFeed(lParam
);
1806 return ::DefWindowProc(MainHWND(), iMessage
, wParam
, lParam
);
1809 case WM_IME_STARTCOMPOSITION
:
1810 if (KoreanIME() || imeInteraction
== IMEInteraction::Inline
) {
1813 ImeStartComposition();
1814 return ::DefWindowProc(MainHWND(), iMessage
, wParam
, lParam
);
1817 case WM_IME_ENDCOMPOSITION
:
1818 ImeEndComposition();
1819 return ::DefWindowProc(MainHWND(), iMessage
, wParam
, lParam
);
1821 case WM_IME_COMPOSITION
:
1822 if (KoreanIME() || imeInteraction
== IMEInteraction::Inline
) {
1823 return HandleCompositionInline(wParam
, lParam
);
1825 return HandleCompositionWindowed(wParam
, lParam
);
1828 case WM_IME_SETCONTEXT
:
1829 if (KoreanIME() || imeInteraction
== IMEInteraction::Inline
) {
1831 LPARAM NoImeWin
= lParam
;
1832 NoImeWin
= NoImeWin
& (~ISC_SHOWUICOMPOSITIONWINDOW
);
1833 return ::DefWindowProc(MainHWND(), iMessage
, wParam
, NoImeWin
);
1836 return ::DefWindowProc(MainHWND(), iMessage
, wParam
, lParam
);
1839 return ::DefWindowProc(MainHWND(), iMessage
, wParam
, lParam
);
1845 sptr_t
ScintillaWin::EditMessage(unsigned int iMessage
, uptr_t wParam
, sptr_t lParam
) {
1848 case EM_LINEFROMCHAR
:
1849 if (PositionFromUPtr(wParam
) < 0) {
1850 wParam
= SelectionStart().Position();
1852 return pdoc
->LineFromPosition(wParam
);
1854 case EM_EXLINEFROMCHAR
:
1855 return pdoc
->LineFromPosition(lParam
);
1859 *reinterpret_cast<DWORD
*>(wParam
) = static_cast<DWORD
>(SelectionStart().Position());
1862 *reinterpret_cast<DWORD
*>(lParam
) = static_cast<DWORD
>(SelectionEnd().Position());
1864 return MAKELRESULT(SelectionStart().Position(), SelectionEnd().Position());
1870 CHARRANGE
*pCR
= reinterpret_cast<CHARRANGE
*>(lParam
);
1871 pCR
->cpMin
= static_cast<LONG
>(SelectionStart().Position());
1872 pCR
->cpMax
= static_cast<LONG
>(SelectionEnd().Position());
1877 Sci::Position nStart
= wParam
;
1878 Sci::Position nEnd
= lParam
;
1879 if (nStart
== 0 && nEnd
== -1) {
1880 nEnd
= pdoc
->Length();
1883 nStart
= nEnd
; // Remove selection
1885 SetSelection(nEnd
, nStart
);
1886 EnsureCaretVisible();
1894 const CHARRANGE
*pCR
= reinterpret_cast<const CHARRANGE
*>(lParam
);
1895 sel
.selType
= Selection::SelTypes::stream
;
1896 if (pCR
->cpMin
== 0 && pCR
->cpMax
== -1) {
1897 SetSelection(pCR
->cpMin
, pdoc
->Length());
1899 SetSelection(pCR
->cpMin
, pCR
->cpMax
);
1901 EnsureCaretVisible();
1902 return pdoc
->LineFromPosition(SelectionStart().Position());
1908 sptr_t
ScintillaWin::IdleMessage(unsigned int iMessage
, uptr_t wParam
, sptr_t lParam
) {
1911 // wParam=dwTickCountInitial, or 0 to initialize. lParam=bSkipUserInputTest
1913 if (lParam
|| (WAIT_TIMEOUT
== MsgWaitForMultipleObjects(0, nullptr, 0, 0, QS_INPUT
| QS_HOTKEY
))) {
1915 // User input was given priority above, but all events do get a turn. Other
1916 // messages, notifications, etc. will get interleaved with the idle messages.
1918 // However, some things like WM_PAINT are a lower priority, and will not fire
1919 // when there's a message posted. So, several times a second, we stop and let
1920 // the low priority events have a turn (after which the timer will fire again).
1922 // Suppress a warning from Code Analysis that the GetTickCount function
1923 // wraps after 49 days. The WM_TIMER will kick off another SC_WIN_IDLE
1926 #pragma warning(suppress: 28159)
1928 const DWORD dwCurrent
= GetTickCount();
1929 const DWORD dwStart
= wParam
? static_cast<DWORD
>(wParam
) : dwCurrent
;
1930 constexpr DWORD maxWorkTime
= 50;
1932 if (dwCurrent
>= dwStart
&& dwCurrent
> maxWorkTime
&&dwCurrent
- maxWorkTime
< dwStart
)
1933 PostMessage(MainHWND(), SC_WIN_IDLE
, dwStart
, 0);
1948 sptr_t
ScintillaWin::SciMessage(Message iMessage
, uptr_t wParam
, sptr_t lParam
) {
1950 case Message::GetDirectFunction
:
1951 return reinterpret_cast<sptr_t
>(DirectFunction
);
1953 case Message::GetDirectStatusFunction
:
1954 return reinterpret_cast<sptr_t
>(DirectStatusFunction
);
1956 case Message::GetDirectPointer
:
1957 return reinterpret_cast<sptr_t
>(this);
1959 case Message::GrabFocus
:
1960 ::SetFocus(MainHWND());
1963 #ifdef INCLUDE_DEPRECATED_FEATURES
1964 case Message::SETKEYSUNICODE
:
1967 case Message::GETKEYSUNICODE
:
1971 case Message::SetTechnology
:
1972 if (const Technology technologyNew
= static_cast<Technology
>(wParam
);
1973 (technologyNew
== Technology::Default
) ||
1974 (technologyNew
== Technology::DirectWriteRetain
) ||
1975 (technologyNew
== Technology::DirectWriteDC
) ||
1976 (technologyNew
== Technology::DirectWrite
)) {
1977 if (technology
!= technologyNew
) {
1978 if (technologyNew
> Technology::Default
) {
1979 #if defined(USE_D2D)
1981 // Failed to load Direct2D or DirectWrite so no effect
1984 UpdateRenderingParams(true);
1989 bidirectional
= Bidirectional::Disabled
;
1992 technology
= technologyNew
;
1993 view
.bufferedDraw
= technologyNew
== Technology::Default
;
1994 // Invalidate all cached information including layout.
1995 InvalidateStyleRedraw();
2000 case Message::SetBidirectional
:
2001 if (technology
== Technology::Default
) {
2002 bidirectional
= Bidirectional::Disabled
;
2003 } else if (static_cast<Bidirectional
>(wParam
) <= Bidirectional::R2L
) {
2004 bidirectional
= static_cast<Bidirectional
>(wParam
);
2006 // Invalidate all cached information including layout.
2007 InvalidateStyleRedraw();
2010 case Message::TargetAsUTF8
:
2011 return TargetAsUTF8(CharPtrFromSPtr(lParam
));
2013 case Message::EncodedFromUTF8
:
2014 return EncodedFromUTF8(ConstCharPtrFromUPtr(wParam
),
2015 CharPtrFromSPtr(lParam
));
2024 sptr_t
ScintillaWin::WndProc(Message iMessage
, uptr_t wParam
, sptr_t lParam
) {
2026 //Platform::DebugPrintf("S M:%x WP:%x L:%x\n", iMessage, wParam, lParam);
2027 const unsigned int msg
= static_cast<unsigned int>(iMessage
);
2031 ctrlID
= ::GetDlgCtrlID(HwndFromWindow(wMain
));
2032 UpdateBaseElements();
2033 // Get Intellimouse scroll line parameters
2034 GetMouseParameters();
2035 ::RegisterDragDrop(MainHWND(), &dt
);
2039 Command(LOWORD(wParam
));
2045 case WM_PRINTCLIENT
: {
2046 HDC hdc
= reinterpret_cast<HDC
>(wParam
);
2047 if (!IsCompatibleDC(hdc
)) {
2048 return ::DefWindowProc(MainHWND(), msg
, wParam
, lParam
);
2055 ScrollMessage(wParam
);
2059 HorizontalScrollMessage(wParam
);
2067 if (wParam
== idleTimerID
&& idler
.state
) {
2068 SendMessage(MainHWND(), SC_WIN_IDLE
, 0, 1);
2070 TickFor(static_cast<TickReason
>(wParam
- fineTimerStart
));
2076 return IdleMessage(msg
, wParam
, lParam
);
2078 case WM_GETMINMAXINFO
:
2079 return ::DefWindowProc(MainHWND(), msg
, wParam
, lParam
);
2081 case WM_LBUTTONDOWN
:
2083 case WM_RBUTTONDOWN
:
2087 case WM_MOUSEHWHEEL
:
2088 return MouseMessage(msg
, wParam
, lParam
);
2091 if (LOWORD(lParam
) == HTCLIENT
) {
2092 if (!cursorIsHidden
) {
2094 if (::GetCursorPos(&pt
)) {
2095 ::ScreenToClient(MainHWND(), &pt
);
2096 DisplayCursor(ContextCursor(PointFromPOINT(pt
)));
2101 return ::DefWindowProc(MainHWND(), msg
, wParam
, lParam
);
2109 return KeyMessage(msg
, wParam
, lParam
);
2111 case WM_SETTINGCHANGE
:
2112 //Platform::DebugPrintf("Setting Changed\n");
2113 #if defined(USE_D2D)
2114 if (technology
!= Technology::Default
) {
2115 UpdateRenderingParams(true);
2118 UpdateBaseElements();
2119 // Get Intellimouse scroll line parameters
2120 GetMouseParameters();
2121 InvalidateStyleRedraw();
2125 return DLGC_HASSETSEL
| DLGC_WANTALLKEYS
;
2129 return FocusMessage(msg
, wParam
, lParam
);
2131 case WM_SYSCOLORCHANGE
:
2132 //Platform::DebugPrintf("Setting Changed\n");
2133 UpdateBaseElements();
2134 InvalidateStyleData();
2138 dpi
= HIWORD(wParam
);
2139 reverseArrowCursor
.Invalidate();
2140 InvalidateStyleRedraw();
2143 case WM_DPICHANGED_AFTERPARENT
: {
2144 const UINT dpiNow
= DpiForWindow(wMain
.GetID());
2145 if (dpi
!= dpiNow
) {
2147 reverseArrowCursor
.Invalidate();
2148 InvalidateStyleRedraw();
2153 case WM_CONTEXTMENU
:
2154 return ShowContextMenu(msg
, wParam
, lParam
);
2157 return 1; // Avoid any background erasure as whole window painted.
2160 ::DefWindowProc(MainHWND(), msg
, wParam
, lParam
);
2163 SetVerticalScrollPos();
2164 SetHorizontalScrollPos();
2168 case WM_CAPTURECHANGED
:
2169 capturedMouse
= false;
2172 // These are not handled in Scintilla and its faster to dispatch them here.
2173 // Also moves time out to here so profile doesn't count lots of empty message calls.
2176 case WM_MOUSEACTIVATE
:
2180 case WM_NCMOUSEMOVE
:
2181 case WM_NCLBUTTONDOWN
:
2183 case WM_WINDOWPOSCHANGING
:
2184 return ::DefWindowProc(MainHWND(), msg
, wParam
, lParam
);
2186 case WM_WINDOWPOSCHANGED
:
2187 #if defined(USE_D2D)
2188 if (technology
!= Technology::Default
) {
2189 if (UpdateRenderingParams(false)) {
2195 return ::DefWindowProc(MainHWND(), msg
, wParam
, lParam
);
2197 case WM_GETTEXTLENGTH
:
2198 return GetTextLength();
2201 return GetText(wParam
, lParam
);
2203 case WM_INPUTLANGCHANGE
:
2204 case WM_INPUTLANGCHANGEREQUEST
:
2205 case WM_IME_KEYDOWN
:
2206 case WM_IME_REQUEST
:
2207 case WM_IME_STARTCOMPOSITION
:
2208 case WM_IME_ENDCOMPOSITION
:
2209 case WM_IME_COMPOSITION
:
2210 case WM_IME_SETCONTEXT
:
2212 return IMEMessage(msg
, wParam
, lParam
);
2214 case EM_LINEFROMCHAR
:
2215 case EM_EXLINEFROMCHAR
:
2220 return EditMessage(msg
, wParam
, lParam
);
2223 iMessage
= SciMessageFromEM(msg
);
2225 case Message::GetDirectFunction
:
2226 case Message::GetDirectStatusFunction
:
2227 case Message::GetDirectPointer
:
2228 case Message::GrabFocus
:
2229 case Message::SetTechnology
:
2230 case Message::SetBidirectional
:
2231 case Message::TargetAsUTF8
:
2232 case Message::EncodedFromUTF8
:
2233 return SciMessage(iMessage
, wParam
, lParam
);
2236 return ScintillaBase::WndProc(iMessage
, wParam
, lParam
);
2238 } catch (std::bad_alloc
&) {
2239 errorStatus
= Status::BadAlloc
;
2241 errorStatus
= Status::Failure
;
2246 bool ScintillaWin::ValidCodePage(int codePage
) const {
2247 return codePage
== 0 || codePage
== CpUtf8
||
2248 codePage
== 932 || codePage
== 936 || codePage
== 949 ||
2249 codePage
== 950 || codePage
== 1361;
2252 std::string
ScintillaWin::UTF8FromEncoded(std::string_view encoded
) const {
2253 if (IsUnicodeMode()) {
2254 return std::string(encoded
);
2256 // Pivot through wide string
2257 std::wstring ws
= StringDecode(encoded
, CodePageOfDocument());
2258 return StringEncode(ws
, CpUtf8
);
2262 std::string
ScintillaWin::EncodedFromUTF8(std::string_view utf8
) const {
2263 if (IsUnicodeMode()) {
2264 return std::string(utf8
);
2266 // Pivot through wide string
2267 std::wstring ws
= StringDecode(utf8
, CpUtf8
);
2268 return StringEncode(ws
, CodePageOfDocument());
2272 sptr_t
ScintillaWin::DefWndProc(Message iMessage
, uptr_t wParam
, sptr_t lParam
) {
2273 return ::DefWindowProc(MainHWND(), static_cast<unsigned int>(iMessage
), wParam
, lParam
);
2276 bool ScintillaWin::FineTickerRunning(TickReason reason
) {
2277 return timers
[static_cast<size_t>(reason
)] != 0;
2280 void ScintillaWin::FineTickerStart(TickReason reason
, int millis
, int tolerance
) {
2281 FineTickerCancel(reason
);
2282 const UINT_PTR reasonIndex
= static_cast<UINT_PTR
>(reason
);
2283 const UINT_PTR eventID
= static_cast<UINT_PTR
>(fineTimerStart
) + reasonIndex
;
2284 if (SetCoalescableTimerFn
&& tolerance
) {
2285 timers
[reasonIndex
] = SetCoalescableTimerFn(MainHWND(), eventID
, millis
, nullptr, tolerance
);
2287 timers
[reasonIndex
] = ::SetTimer(MainHWND(), eventID
, millis
, nullptr);
2291 void ScintillaWin::FineTickerCancel(TickReason reason
) {
2292 const UINT_PTR reasonIndex
= static_cast<UINT_PTR
>(reason
);
2293 if (timers
[reasonIndex
]) {
2294 ::KillTimer(MainHWND(), timers
[reasonIndex
]);
2295 timers
[reasonIndex
] = 0;
2300 bool ScintillaWin::SetIdle(bool on
) {
2301 // On Win32 the Idler is implemented as a Timer on the Scintilla window. This
2302 // takes advantage of the fact that WM_TIMER messages are very low priority,
2303 // and are only posted when the message queue is empty, i.e. during idle time.
2304 if (idler
.state
!= on
) {
2306 idler
.idlerID
= ::SetTimer(MainHWND(), idleTimerID
, 10, nullptr)
2307 ? reinterpret_cast<IdlerID
>(idleTimerID
) : 0;
2309 ::KillTimer(MainHWND(), reinterpret_cast<uptr_t
>(idler
.idlerID
));
2312 idler
.state
= idler
.idlerID
!= 0;
2317 void ScintillaWin::IdleWork() {
2318 styleIdleInQueue
= false;
2322 void ScintillaWin::QueueIdleWork(WorkItems items
, Sci::Position upTo
) {
2323 Editor::QueueIdleWork(items
, upTo
);
2324 if (!styleIdleInQueue
) {
2325 if (PostMessage(MainHWND(), SC_WORK_IDLE
, 0, 0)) {
2326 styleIdleInQueue
= true;
2331 void ScintillaWin::SetMouseCapture(bool on
) {
2332 if (mouseDownCaptures
) {
2334 ::SetCapture(MainHWND());
2342 bool ScintillaWin::HaveMouseCapture() {
2343 // Cannot just see if GetCapture is this window as the scroll bar also sets capture for the window
2344 return capturedMouse
;
2345 //return capturedMouse && (::GetCapture() == MainHWND());
2348 void ScintillaWin::SetTrackMouseLeaveEvent(bool on
) noexcept
{
2349 if (on
&& !trackedMouseLeave
) {
2350 TRACKMOUSEEVENT tme
{};
2351 tme
.cbSize
= sizeof(tme
);
2352 tme
.dwFlags
= TME_LEAVE
;
2353 tme
.hwndTrack
= MainHWND();
2354 tme
.dwHoverTime
= HOVER_DEFAULT
; // Unused but triggers Dr. Memory if not initialized
2355 TrackMouseEvent(&tme
);
2357 trackedMouseLeave
= on
;
2360 void ScintillaWin::HideCursorIfPreferred() noexcept
{
2361 // SPI_GETMOUSEVANISH from OS.
2362 if (typingWithoutCursor
&& !cursorIsHidden
) {
2364 cursorIsHidden
= true;
2368 void ScintillaWin::UpdateBaseElements() {
2369 struct ElementToIndex
{ Element element
; int nIndex
; };
2370 const ElementToIndex eti
[] = {
2371 { Element::List
, COLOR_WINDOWTEXT
},
2372 { Element::ListBack
, COLOR_WINDOW
},
2373 { Element::ListSelected
, COLOR_HIGHLIGHTTEXT
},
2374 { Element::ListSelectedBack
, COLOR_HIGHLIGHT
},
2376 bool changed
= false;
2377 for (const ElementToIndex
&ei
: eti
) {
2378 changed
= vs
.SetElementBase(ei
.element
, ColourRGBA::FromRGB(static_cast<int>(::GetSysColor(ei
.nIndex
)))) || changed
;
2385 bool ScintillaWin::PaintContains(PRectangle rc
) {
2386 if (paintState
== PaintState::painting
) {
2387 return BoundsContains(rcPaint
, hRgnUpdate
, rc
);
2392 void ScintillaWin::ScrollText(Sci::Line
/* linesToMove */) {
2393 //Platform::DebugPrintf("ScintillaWin::ScrollText %d\n", linesToMove);
2394 //::ScrollWindow(MainHWND(), 0,
2395 // vs.lineHeight * linesToMove, 0, 0);
2396 //::UpdateWindow(MainHWND());
2398 UpdateSystemCaret();
2401 void ScintillaWin::NotifyCaretMove() {
2402 NotifyWinEvent(EVENT_OBJECT_LOCATIONCHANGE
, MainHWND(), OBJID_CARET
, CHILDID_SELF
);
2405 void ScintillaWin::UpdateSystemCaret() {
2407 if (pdoc
->TentativeActive()) {
2408 // ongoing inline mode IME composition, don't inform IME of system caret position.
2409 // fix candidate window for Google Japanese IME moved on typing on Win7.
2412 if (HasCaretSizeChanged()) {
2413 DestroySystemCaret();
2414 CreateSystemCaret();
2416 const Point pos
= PointMainCaret();
2417 ::SetCaretPos(static_cast<int>(pos
.x
), static_cast<int>(pos
.y
));
2421 bool ScintillaWin::IsVisible() const noexcept
{
2422 return GetWindowStyle(MainHWND()) & WS_VISIBLE
;
2425 int ScintillaWin::SetScrollInfo(int nBar
, LPCSCROLLINFO lpsi
, BOOL bRedraw
) noexcept
{
2426 return ::SetScrollInfo(MainHWND(), nBar
, lpsi
, bRedraw
);
2429 bool ScintillaWin::GetScrollInfo(int nBar
, LPSCROLLINFO lpsi
) noexcept
{
2430 return ::GetScrollInfo(MainHWND(), nBar
, lpsi
) ? true : false;
2433 // Change the scroll position but avoid repaint if changing to same value
2434 void ScintillaWin::ChangeScrollPos(int barType
, Sci::Position pos
) {
2440 sizeof(sci
), 0, 0, 0, 0, 0, 0
2442 sci
.fMask
= SIF_POS
;
2443 GetScrollInfo(barType
, &sci
);
2444 if (sci
.nPos
!= pos
) {
2446 sci
.nPos
= static_cast<int>(pos
);
2447 SetScrollInfo(barType
, &sci
, TRUE
);
2451 void ScintillaWin::SetVerticalScrollPos() {
2452 ChangeScrollPos(SB_VERT
, topLine
);
2455 void ScintillaWin::SetHorizontalScrollPos() {
2456 ChangeScrollPos(SB_HORZ
, xOffset
);
2459 bool ScintillaWin::ChangeScrollRange(int nBar
, int nMin
, int nMax
, UINT nPage
) noexcept
{
2460 SCROLLINFO sci
= { sizeof(sci
), SIF_PAGE
| SIF_RANGE
, 0, 0, 0, 0, 0 };
2461 GetScrollInfo(nBar
, &sci
);
2462 if ((sci
.nMin
!= nMin
) || (sci
.nMax
!= nMax
) || (sci
.nPage
!= nPage
)) {
2466 SetScrollInfo(nBar
, &sci
, TRUE
);
2472 void ScintillaWin::HorizontalScrollToClamped(int xPos
) {
2473 const HorizontalScrollRange range
= GetHorizontalScrollRange();
2474 HorizontalScrollTo(std::clamp(xPos
, 0, range
.documentWidth
- range
.pageWidth
+ 1));
2477 HorizontalScrollRange
ScintillaWin::GetHorizontalScrollRange() const {
2478 const PRectangle rcText
= GetTextRectangle();
2479 int pageWidth
= static_cast<int>(rcText
.Width());
2480 const int horizEndPreferred
= std::max({ scrollWidth
, pageWidth
- 1, 0 });
2481 if (!horizontalScrollBarVisible
|| Wrapping())
2482 pageWidth
= horizEndPreferred
+ 1;
2483 return { pageWidth
, horizEndPreferred
};
2486 bool ScintillaWin::ModifyScrollBars(Sci::Line nMax
, Sci::Line nPage
) {
2491 bool modified
= false;
2492 const Sci::Line vertEndPreferred
= nMax
;
2493 if (!verticalScrollBarVisible
)
2494 nPage
= vertEndPreferred
+ 1;
2495 if (ChangeScrollRange(SB_VERT
, 0, static_cast<int>(vertEndPreferred
), static_cast<unsigned int>(nPage
))) {
2498 const HorizontalScrollRange range
= GetHorizontalScrollRange();
2499 if (ChangeScrollRange(SB_HORZ
, 0, range
.documentWidth
, range
.pageWidth
)) {
2501 if (scrollWidth
< range
.pageWidth
) {
2502 HorizontalScrollTo(0);
2509 void ScintillaWin::NotifyChange() {
2510 ::SendMessage(::GetParent(MainHWND()), WM_COMMAND
,
2511 MAKEWPARAM(GetCtrlID(), FocusChange::Change
),
2512 reinterpret_cast<LPARAM
>(MainHWND()));
2515 void ScintillaWin::NotifyFocus(bool focus
) {
2516 if (commandEvents
) {
2517 ::SendMessage(::GetParent(MainHWND()), WM_COMMAND
,
2518 MAKEWPARAM(GetCtrlID(), focus
? FocusChange::Setfocus
: FocusChange::Killfocus
),
2519 reinterpret_cast<LPARAM
>(MainHWND()));
2521 Editor::NotifyFocus(focus
);
2524 void ScintillaWin::SetCtrlID(int identifier
) {
2525 ::SetWindowID(HwndFromWindow(wMain
), identifier
);
2528 int ScintillaWin::GetCtrlID() {
2529 return ::GetDlgCtrlID(HwndFromWindow(wMain
));
2532 void ScintillaWin::NotifyParent(NotificationData scn
) {
2533 scn
.nmhdr
.hwndFrom
= MainHWND();
2534 scn
.nmhdr
.idFrom
= GetCtrlID();
2535 ::SendMessage(::GetParent(MainHWND()), WM_NOTIFY
,
2536 GetCtrlID(), reinterpret_cast<LPARAM
>(&scn
));
2539 void ScintillaWin::NotifyParent(SCNotification
*scn
) {
2540 scn
->nmhdr
.hwndFrom
= MainHWND();
2541 scn
->nmhdr
.idFrom
= GetCtrlID();
2542 ::SendMessage(::GetParent(MainHWND()), WM_NOTIFY
,
2543 GetCtrlID(), reinterpret_cast<LPARAM
>(scn
));
2546 void ScintillaWin::NotifyDoubleClick(Point pt
, KeyMod modifiers
) {
2547 //Platform::DebugPrintf("ScintillaWin Double click 0\n");
2548 ScintillaBase::NotifyDoubleClick(pt
, modifiers
);
2549 // Send myself a WM_LBUTTONDBLCLK, so the container can handle it too.
2550 const POINT point
= POINTFromPoint(pt
);
2551 ::SendMessage(MainHWND(),
2553 FlagSet(modifiers
, KeyMod::Shift
) ? MK_SHIFT
: 0,
2554 MAKELPARAM(point
.x
, point
.y
));
2559 class CaseFolderDBCS
: public CaseFolderTable
{
2560 // Allocate the expandable storage here so that it does not need to be reallocated
2561 // for each call to Fold.
2562 std::vector
<wchar_t> utf16Mixed
;
2563 std::vector
<wchar_t> utf16Folded
;
2566 explicit CaseFolderDBCS(UINT cp_
) : cp(cp_
) {
2568 size_t Fold(char *folded
, size_t sizeFolded
, const char *mixed
, size_t lenMixed
) override
{
2569 if ((lenMixed
== 1) && (sizeFolded
> 0)) {
2570 folded
[0] = mapping
[static_cast<unsigned char>(mixed
[0])];
2573 if (lenMixed
> utf16Mixed
.size()) {
2574 utf16Mixed
.resize(lenMixed
+ 8);
2576 const size_t nUtf16Mixed
= WideCharFromMultiByte(cp
,
2577 std::string_view(mixed
, lenMixed
),
2581 if (nUtf16Mixed
== 0) {
2582 // Failed to convert -> bad input
2588 for (size_t mixIndex
=0; mixIndex
< nUtf16Mixed
; mixIndex
++) {
2589 if ((lenFlat
+ 20) > utf16Folded
.size())
2590 utf16Folded
.resize(lenFlat
+ 60);
2591 const char *foldedUTF8
= CaseConvert(utf16Mixed
[mixIndex
], CaseConversion::fold
);
2593 // Maximum length of a case conversion is 6 bytes, 3 characters
2594 wchar_t wFolded
[20];
2595 const size_t charsConverted
= UTF16FromUTF8(std::string_view(foldedUTF8
),
2596 wFolded
, std::size(wFolded
));
2597 for (size_t j
=0; j
<charsConverted
; j
++)
2598 utf16Folded
[lenFlat
++] = wFolded
[j
];
2600 utf16Folded
[lenFlat
++] = utf16Mixed
[mixIndex
];
2604 const std::wstring_view
wsvFolded(&utf16Folded
[0], lenFlat
);
2605 const size_t lenOut
= MultiByteLenFromWideChar(cp
, wsvFolded
);
2607 if (lenOut
< sizeFolded
) {
2608 MultiByteFromWideChar(cp
, wsvFolded
, folded
, lenOut
);
2619 std::unique_ptr
<CaseFolder
> ScintillaWin::CaseFolderForEncoding() {
2620 const UINT cpDest
= CodePageOfDocument();
2621 if (cpDest
== CpUtf8
) {
2622 return std::make_unique
<CaseFolderUnicode
>();
2624 if (pdoc
->dbcsCodePage
== 0) {
2625 std::unique_ptr
<CaseFolderTable
> pcf
= std::make_unique
<CaseFolderTable
>();
2626 // Only for single byte encodings
2627 for (int i
=0x80; i
<0x100; i
++) {
2628 char sCharacter
[2] = "A";
2629 sCharacter
[0] = static_cast<char>(i
);
2630 wchar_t wCharacter
[20];
2631 const unsigned int lengthUTF16
= WideCharFromMultiByte(cpDest
, sCharacter
,
2632 wCharacter
, std::size(wCharacter
));
2633 if (lengthUTF16
== 1) {
2634 const char *caseFolded
= CaseConvert(wCharacter
[0], CaseConversion::fold
);
2637 const size_t charsConverted
= UTF16FromUTF8(std::string_view(caseFolded
),
2638 wLower
, std::size(wLower
));
2639 if (charsConverted
== 1) {
2640 char sCharacterLowered
[20];
2641 const unsigned int lengthConverted
= MultiByteFromWideChar(cpDest
,
2642 std::wstring_view(wLower
, charsConverted
),
2643 sCharacterLowered
, std::size(sCharacterLowered
));
2644 if ((lengthConverted
== 1) && (sCharacter
[0] != sCharacterLowered
[0])) {
2645 pcf
->SetTranslation(sCharacter
[0], sCharacterLowered
[0]);
2653 return std::make_unique
<CaseFolderDBCS
>(cpDest
);
2658 std::string
ScintillaWin::CaseMapString(const std::string
&s
, CaseMapping caseMapping
) {
2659 if ((s
.size() == 0) || (caseMapping
== CaseMapping::same
))
2662 const UINT cpDoc
= CodePageOfDocument();
2663 if (cpDoc
== CpUtf8
) {
2664 return CaseConvertString(s
, (caseMapping
== CaseMapping::upper
) ? CaseConversion::upper
: CaseConversion::lower
);
2667 // Change text to UTF-16
2668 const std::wstring wsText
= StringDecode(s
, cpDoc
);
2670 const DWORD mapFlags
= LCMAP_LINGUISTIC_CASING
|
2671 ((caseMapping
== CaseMapping::upper
) ? LCMAP_UPPERCASE
: LCMAP_LOWERCASE
);
2674 const std::wstring wsConverted
= StringMapCase(wsText
, mapFlags
);
2676 // Change back to document encoding
2677 std::string sConverted
= StringEncode(wsConverted
, cpDoc
);
2682 void ScintillaWin::Copy() {
2683 //Platform::DebugPrintf("Copy\n");
2685 SelectionText selectedText
;
2686 CopySelectionRange(&selectedText
);
2687 CopyToClipboard(selectedText
);
2691 bool ScintillaWin::CanPaste() {
2692 if (!Editor::CanPaste())
2694 return ::IsClipboardFormatAvailable(CF_UNICODETEXT
) != FALSE
;
2699 class GlobalMemory
{
2703 GlobalMemory() noexcept
{
2705 explicit GlobalMemory(HGLOBAL hand_
) noexcept
: hand(hand_
) {
2707 ptr
= ::GlobalLock(hand
);
2710 // Deleted so GlobalMemory objects can not be copied.
2711 GlobalMemory(const GlobalMemory
&) = delete;
2712 GlobalMemory(GlobalMemory
&&) = delete;
2713 GlobalMemory
&operator=(const GlobalMemory
&) = delete;
2714 GlobalMemory
&operator=(GlobalMemory
&&) = delete;
2719 void Allocate(size_t bytes
) noexcept
{
2721 hand
= ::GlobalAlloc(GMEM_MOVEABLE
| GMEM_ZEROINIT
, bytes
);
2723 ptr
= ::GlobalLock(hand
);
2726 HGLOBAL
Unlock() noexcept
{
2728 HGLOBAL handCopy
= hand
;
2729 ::GlobalUnlock(hand
);
2734 void SetClip(UINT uFormat
) noexcept
{
2735 ::SetClipboardData(uFormat
, Unlock());
2737 operator bool() const noexcept
{
2738 return ptr
!= nullptr;
2740 SIZE_T
Size() const noexcept
{
2741 return ::GlobalSize(hand
);
2745 // OpenClipboard may fail if another application has opened the clipboard.
2746 // Try up to 8 times, with an initial delay of 1 ms and an exponential back off
2747 // for a maximum total delay of 127 ms (1+2+4+8+16+32+64).
2748 bool OpenClipboardRetry(HWND hwnd
) noexcept
{
2749 for (int attempt
=0; attempt
<8; attempt
++) {
2751 ::Sleep(1 << (attempt
-1));
2753 if (::OpenClipboard(hwnd
)) {
2760 // Ensure every successful OpenClipboard is followed by a CloseClipboard.
2762 bool opened
= false;
2764 Clipboard(HWND hwnd
) noexcept
: opened(::OpenClipboardRetry(hwnd
)) {
2766 // Deleted so Clipboard objects can not be copied.
2767 Clipboard(const Clipboard
&) = delete;
2768 Clipboard(Clipboard
&&) = delete;
2769 Clipboard
&operator=(const Clipboard
&) = delete;
2770 Clipboard
&operator=(Clipboard
&&) = delete;
2771 ~Clipboard() noexcept
{
2776 constexpr operator bool() const noexcept
{
2781 bool IsValidFormatEtc(const FORMATETC
*pFE
) noexcept
{
2782 return pFE
->ptd
== nullptr &&
2783 (pFE
->dwAspect
& DVASPECT_CONTENT
) != 0 &&
2784 pFE
->lindex
== -1 &&
2785 (pFE
->tymed
& TYMED_HGLOBAL
) != 0;
2788 bool SupportedFormat(const FORMATETC
*pFE
) noexcept
{
2789 return pFE
->cfFormat
== CF_UNICODETEXT
&&
2790 IsValidFormatEtc(pFE
);
2795 void ScintillaWin::Paste() {
2796 Clipboard
clipboard(MainHWND());
2801 const bool isLine
= SelectionEmpty() &&
2802 (::IsClipboardFormatAvailable(cfLineSelect
) || ::IsClipboardFormatAvailable(cfVSLineTag
));
2803 ClearSelection(multiPasteMode
== MultiPaste::Each
);
2804 bool isRectangular
= (::IsClipboardFormatAvailable(cfColumnSelect
) != 0);
2806 if (!isRectangular
) {
2807 // Evaluate "Borland IDE Block Type" explicitly
2808 GlobalMemory
memBorlandSelection(::GetClipboardData(cfBorlandIDEBlockType
));
2809 if (memBorlandSelection
) {
2810 isRectangular
= (memBorlandSelection
.Size() == 1) && (static_cast<BYTE
*>(memBorlandSelection
.ptr
)[0] == 0x02);
2811 memBorlandSelection
.Unlock();
2814 const PasteShape pasteShape
= isRectangular
? PasteShape::rectangular
: (isLine
? PasteShape::line
: PasteShape::stream
);
2816 // Use CF_UNICODETEXT if available
2817 GlobalMemory
memUSelection(::GetClipboardData(CF_UNICODETEXT
));
2818 if (const wchar_t *uptr
= static_cast<const wchar_t *>(memUSelection
.ptr
)) {
2819 const std::string putf
= EncodeWString(uptr
);
2820 InsertPasteShape(putf
.c_str(), putf
.length(), pasteShape
);
2821 memUSelection
.Unlock();
2826 void ScintillaWin::CreateCallTipWindow(PRectangle
) {
2827 if (!ct
.wCallTip
.Created()) {
2828 HWND wnd
= ::CreateWindow(callClassName
, TEXT("ACallTip"),
2829 WS_POPUP
, 100, 100, 150, 20,
2831 GetWindowInstance(MainHWND()),
2838 void ScintillaWin::AddToPopUp(const char *label
, int cmd
, bool enabled
) {
2839 HMENU hmenuPopup
= static_cast<HMENU
>(popup
.GetID());
2841 ::AppendMenuA(hmenuPopup
, MF_SEPARATOR
, 0, "");
2843 ::AppendMenuA(hmenuPopup
, MF_STRING
, cmd
, label
);
2845 ::AppendMenuA(hmenuPopup
, MF_STRING
| MF_DISABLED
| MF_GRAYED
, cmd
, label
);
2848 void ScintillaWin::ClaimSelection() {
2849 // Windows does not have a primary selection
2852 /// Implement IUnknown
2853 STDMETHODIMP
FormatEnumerator::QueryInterface(REFIID riid
, PVOID
*ppv
) {
2854 //Platform::DebugPrintf("EFE QI");
2856 if (riid
== IID_IUnknown
|| riid
== IID_IEnumFORMATETC
) {
2859 return E_NOINTERFACE
;
2864 STDMETHODIMP_(ULONG
)FormatEnumerator::AddRef() {
2867 STDMETHODIMP_(ULONG
)FormatEnumerator::Release() {
2868 const ULONG refs
= --ref
;
2874 /// Implement IEnumFORMATETC
2875 STDMETHODIMP
FormatEnumerator::Next(ULONG celt
, FORMATETC
*rgelt
, ULONG
*pceltFetched
) {
2876 if (!rgelt
) return E_POINTER
;
2878 while ((pos
< formats
.size()) && (putPos
< celt
)) {
2879 rgelt
->cfFormat
= formats
[pos
];
2880 rgelt
->ptd
= nullptr;
2881 rgelt
->dwAspect
= DVASPECT_CONTENT
;
2883 rgelt
->tymed
= TYMED_HGLOBAL
;
2889 *pceltFetched
= putPos
;
2890 return putPos
? S_OK
: S_FALSE
;
2892 STDMETHODIMP
FormatEnumerator::Skip(ULONG celt
) {
2896 STDMETHODIMP
FormatEnumerator::Reset() {
2900 STDMETHODIMP
FormatEnumerator::Clone(IEnumFORMATETC
**ppenum
) {
2901 FormatEnumerator
*pfe
;
2903 pfe
= new FormatEnumerator(pos
, &formats
[0], formats
.size());
2905 return E_OUTOFMEMORY
;
2907 return pfe
->QueryInterface(IID_IEnumFORMATETC
, reinterpret_cast<void **>(ppenum
));
2910 FormatEnumerator::FormatEnumerator(ULONG pos_
, const CLIPFORMAT formats_
[], size_t formatsLen_
) {
2911 ref
= 0; // First QI adds first reference...
2913 formats
.insert(formats
.begin(), formats_
, formats_
+formatsLen_
);
2916 /// Implement IUnknown
2917 STDMETHODIMP
DropSource::QueryInterface(REFIID riid
, PVOID
*ppv
) {
2918 return sci
->QueryInterface(riid
, ppv
);
2920 STDMETHODIMP_(ULONG
)DropSource::AddRef() {
2921 return sci
->AddRef();
2923 STDMETHODIMP_(ULONG
)DropSource::Release() {
2924 return sci
->Release();
2927 /// Implement IDropSource
2928 STDMETHODIMP
DropSource::QueryContinueDrag(BOOL fEsc
, DWORD grfKeyState
) {
2930 return DRAGDROP_S_CANCEL
;
2931 if (!(grfKeyState
& MK_LBUTTON
))
2932 return DRAGDROP_S_DROP
;
2936 STDMETHODIMP
DropSource::GiveFeedback(DWORD
) {
2937 return DRAGDROP_S_USEDEFAULTCURSORS
;
2940 /// Implement IUnkown
2941 STDMETHODIMP
DataObject::QueryInterface(REFIID riid
, PVOID
*ppv
) {
2942 //Platform::DebugPrintf("DO QI %p\n", this);
2943 return sci
->QueryInterface(riid
, ppv
);
2945 STDMETHODIMP_(ULONG
)DataObject::AddRef() {
2946 return sci
->AddRef();
2948 STDMETHODIMP_(ULONG
)DataObject::Release() {
2949 return sci
->Release();
2951 /// Implement IDataObject
2952 STDMETHODIMP
DataObject::GetData(FORMATETC
*pFEIn
, STGMEDIUM
*pSTM
) {
2953 return sci
->GetData(pFEIn
, pSTM
);
2956 STDMETHODIMP
DataObject::GetDataHere(FORMATETC
*, STGMEDIUM
*) {
2957 //Platform::DebugPrintf("DOB GetDataHere\n");
2961 STDMETHODIMP
DataObject::QueryGetData(FORMATETC
*pFE
) {
2962 if (sci
->DragIsRectangularOK(pFE
->cfFormat
) && IsValidFormatEtc(pFE
)) {
2966 if (SupportedFormat(pFE
)) {
2973 STDMETHODIMP
DataObject::GetCanonicalFormatEtc(FORMATETC
*, FORMATETC
*pFEOut
) {
2974 //Platform::DebugPrintf("DOB GetCanon\n");
2975 pFEOut
->cfFormat
= CF_UNICODETEXT
;
2976 pFEOut
->ptd
= nullptr;
2977 pFEOut
->dwAspect
= DVASPECT_CONTENT
;
2978 pFEOut
->lindex
= -1;
2979 pFEOut
->tymed
= TYMED_HGLOBAL
;
2983 STDMETHODIMP
DataObject::SetData(FORMATETC
*, STGMEDIUM
*, BOOL
) {
2984 //Platform::DebugPrintf("DOB SetData\n");
2988 STDMETHODIMP
DataObject::EnumFormatEtc(DWORD dwDirection
, IEnumFORMATETC
**ppEnum
) {
2990 //Platform::DebugPrintf("DOB EnumFormatEtc %d\n", dwDirection);
2991 if (dwDirection
!= DATADIR_GET
) {
2996 const CLIPFORMAT formats
[] = {CF_UNICODETEXT
};
2997 FormatEnumerator
*pfe
= new FormatEnumerator(0, formats
, std::size(formats
));
2998 return pfe
->QueryInterface(IID_IEnumFORMATETC
, reinterpret_cast<void **>(ppEnum
));
2999 } catch (std::bad_alloc
&) {
3000 sci
->errorStatus
= Status::BadAlloc
;
3001 return E_OUTOFMEMORY
;
3003 sci
->errorStatus
= Status::Failure
;
3008 STDMETHODIMP
DataObject::DAdvise(FORMATETC
*, DWORD
, IAdviseSink
*, PDWORD
) {
3009 //Platform::DebugPrintf("DOB DAdvise\n");
3013 STDMETHODIMP
DataObject::DUnadvise(DWORD
) {
3014 //Platform::DebugPrintf("DOB DUnadvise\n");
3018 STDMETHODIMP
DataObject::EnumDAdvise(IEnumSTATDATA
**) {
3019 //Platform::DebugPrintf("DOB EnumDAdvise\n");
3023 /// Implement IUnknown
3024 STDMETHODIMP
DropTarget::QueryInterface(REFIID riid
, PVOID
*ppv
) {
3025 //Platform::DebugPrintf("DT QI %p\n", this);
3026 return sci
->QueryInterface(riid
, ppv
);
3028 STDMETHODIMP_(ULONG
)DropTarget::AddRef() {
3029 return sci
->AddRef();
3031 STDMETHODIMP_(ULONG
)DropTarget::Release() {
3032 return sci
->Release();
3035 /// Implement IDropTarget by forwarding to Scintilla
3036 STDMETHODIMP
DropTarget::DragEnter(LPDATAOBJECT pIDataSource
, DWORD grfKeyState
, POINTL pt
, PDWORD pdwEffect
) {
3038 return sci
->DragEnter(pIDataSource
, grfKeyState
, pt
, pdwEffect
);
3040 sci
->errorStatus
= Status::Failure
;
3044 STDMETHODIMP
DropTarget::DragOver(DWORD grfKeyState
, POINTL pt
, PDWORD pdwEffect
) {
3046 return sci
->DragOver(grfKeyState
, pt
, pdwEffect
);
3048 sci
->errorStatus
= Status::Failure
;
3052 STDMETHODIMP
DropTarget::DragLeave() {
3054 return sci
->DragLeave();
3056 sci
->errorStatus
= Status::Failure
;
3060 STDMETHODIMP
DropTarget::Drop(LPDATAOBJECT pIDataSource
, DWORD grfKeyState
, POINTL pt
, PDWORD pdwEffect
) {
3062 return sci
->Drop(pIDataSource
, grfKeyState
, pt
, pdwEffect
);
3064 sci
->errorStatus
= Status::Failure
;
3070 * DBCS: support Input Method Editor (IME).
3071 * Called when IME Window opened.
3073 void ScintillaWin::ImeStartComposition() {
3075 // Move IME Window to current caret position
3076 IMContext
imc(MainHWND());
3077 const Point pos
= PointMainCaret();
3078 COMPOSITIONFORM CompForm
{};
3079 CompForm
.dwStyle
= CFS_POINT
;
3080 CompForm
.ptCurrentPos
= POINTFromPoint(pos
);
3082 ::ImmSetCompositionWindow(imc
.hIMC
, &CompForm
);
3084 // Set font of IME window to same as surrounded text.
3086 // Since the style creation code has been made platform independent,
3087 // The logfont for the IME is recreated here.
3088 const int styleHere
= pdoc
->StyleIndexAt(sel
.MainCaret());
3089 LOGFONTW lf
= {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, L
""};
3090 int sizeZoomed
= vs
.styles
[styleHere
].size
+ vs
.zoomLevel
* FontSizeMultiplier
;
3091 if (sizeZoomed
<= 2 * FontSizeMultiplier
) // Hangs if sizeZoomed <= 1
3092 sizeZoomed
= 2 * FontSizeMultiplier
;
3093 // The negative is to allow for leading
3094 lf
.lfHeight
= -::MulDiv(sizeZoomed
, dpi
, 72*FontSizeMultiplier
);
3095 lf
.lfWeight
= static_cast<LONG
>(vs
.styles
[styleHere
].weight
);
3096 lf
.lfItalic
= vs
.styles
[styleHere
].italic
? 1 : 0;
3097 lf
.lfCharSet
= DEFAULT_CHARSET
;
3098 lf
.lfFaceName
[0] = L
'\0';
3099 if (vs
.styles
[styleHere
].fontName
) {
3100 const char* fontName
= vs
.styles
[styleHere
].fontName
;
3101 UTF16FromUTF8(std::string_view(fontName
), lf
.lfFaceName
, LF_FACESIZE
);
3104 ::ImmSetCompositionFontW(imc
.hIMC
, &lf
);
3106 // Caret is displayed in IME window. So, caret in Scintilla is useless.
3111 /** Called when IME Window closed. */
3112 void ScintillaWin::ImeEndComposition() {
3113 // clear IME composition state.
3114 view
.imeCaretBlockOverride
= false;
3115 pdoc
->TentativeUndo();
3116 ShowCaretAtCurrentPosition();
3119 LRESULT
ScintillaWin::ImeOnReconvert(LPARAM lParam
) {
3120 // Reconversion on windows limits within one line without eol.
3121 // Look around: baseStart <-- (|mainStart| -- mainEnd) --> baseEnd.
3122 const Sci::Position mainStart
= sel
.RangeMain().Start().Position();
3123 const Sci::Position mainEnd
= sel
.RangeMain().End().Position();
3124 const Sci::Line curLine
= pdoc
->SciLineFromPosition(mainStart
);
3125 if (curLine
!= pdoc
->LineFromPosition(mainEnd
))
3127 const Sci::Position baseStart
= pdoc
->LineStart(curLine
);
3128 const Sci::Position baseEnd
= pdoc
->LineEnd(curLine
);
3129 if ((baseStart
== baseEnd
) || (mainEnd
> baseEnd
))
3132 const int codePage
= CodePageOfDocument();
3133 const std::wstring rcFeed
= StringDecode(RangeText(baseStart
, baseEnd
), codePage
);
3134 const int rcFeedLen
= static_cast<int>(rcFeed
.length()) * sizeof(wchar_t);
3135 const int rcSize
= sizeof(RECONVERTSTRING
) + rcFeedLen
+ sizeof(wchar_t);
3137 RECONVERTSTRING
*rc
= static_cast<RECONVERTSTRING
*>(PtrFromSPtr(lParam
));
3139 return rcSize
; // Immediately be back with rcSize of memory block.
3141 wchar_t *rcFeedStart
= reinterpret_cast<wchar_t*>(rc
+ 1);
3142 memcpy(rcFeedStart
, &rcFeed
[0], rcFeedLen
);
3144 std::string rcCompString
= RangeText(mainStart
, mainEnd
);
3145 std::wstring rcCompWstring
= StringDecode(rcCompString
, codePage
);
3146 std::string rcCompStart
= RangeText(baseStart
, mainStart
);
3147 std::wstring rcCompWstart
= StringDecode(rcCompStart
, codePage
);
3149 // Map selection to dwCompStr.
3150 // No selection assumes current caret as rcCompString without length.
3151 rc
->dwVersion
= 0; // It should be absolutely 0.
3152 rc
->dwStrLen
= static_cast<DWORD
>(rcFeed
.length());
3153 rc
->dwStrOffset
= sizeof(RECONVERTSTRING
);
3154 rc
->dwCompStrLen
= static_cast<DWORD
>(rcCompWstring
.length());
3155 rc
->dwCompStrOffset
= static_cast<DWORD
>(rcCompWstart
.length()) * sizeof(wchar_t);
3156 rc
->dwTargetStrLen
= rc
->dwCompStrLen
;
3157 rc
->dwTargetStrOffset
=rc
->dwCompStrOffset
;
3159 IMContext
imc(MainHWND());
3163 if (!::ImmSetCompositionStringW(imc
.hIMC
, SCS_QUERYRECONVERTSTRING
, rc
, rcSize
, nullptr, 0))
3166 // No selection asks IME to fill target fields with its own value.
3167 const int tgWlen
= rc
->dwTargetStrLen
;
3168 const int tgWstart
= rc
->dwTargetStrOffset
/ sizeof(wchar_t);
3170 std::string tgCompStart
= StringEncode(rcFeed
.substr(0, tgWstart
), codePage
);
3171 std::string tgComp
= StringEncode(rcFeed
.substr(tgWstart
, tgWlen
), codePage
);
3173 // No selection needs to adjust reconvert start position for IME set.
3174 const int adjust
= static_cast<int>(tgCompStart
.length() - rcCompStart
.length());
3175 const int docCompLen
= static_cast<int>(tgComp
.length());
3177 // Make place for next composition string to sit in.
3178 for (size_t r
=0; r
<sel
.Count(); r
++) {
3179 const Sci::Position rBase
= sel
.Range(r
).Start().Position();
3180 const Sci::Position docCompStart
= rBase
+ adjust
;
3182 if (inOverstrike
) { // the docCompLen of bytes will be overstriked.
3183 sel
.Range(r
).caret
.SetPosition(docCompStart
);
3184 sel
.Range(r
).anchor
.SetPosition(docCompStart
);
3186 // Ensure docCompStart+docCompLen be not beyond lineEnd.
3187 // since docCompLen by byte might break eol.
3188 const Sci::Position lineEnd
= pdoc
->LineEndPosition(rBase
);
3189 const Sci::Position overflow
= (docCompStart
+ docCompLen
) - lineEnd
;
3191 pdoc
->DeleteChars(docCompStart
, docCompLen
- overflow
);
3193 pdoc
->DeleteChars(docCompStart
, docCompLen
);
3197 // Immediately Target Input or candidate box choice with GCS_COMPSTR.
3201 LRESULT
ScintillaWin::ImeOnDocumentFeed(LPARAM lParam
) const {
3202 // This is called while typing preedit string in.
3203 // So there is no selection.
3204 // Limit feed within one line without EOL.
3205 // Look around: lineStart |<-- |compStart| - caret - compEnd| -->| lineEnd.
3207 const Sci::Position curPos
= CurrentPosition();
3208 const Sci::Line curLine
= pdoc
->SciLineFromPosition(curPos
);
3209 const Sci::Position lineStart
= pdoc
->LineStart(curLine
);
3210 const Sci::Position lineEnd
= pdoc
->LineEnd(curLine
);
3212 const std::wstring rcFeed
= StringDecode(RangeText(lineStart
, lineEnd
), CodePageOfDocument());
3213 const int rcFeedLen
= static_cast<int>(rcFeed
.length()) * sizeof(wchar_t);
3214 const int rcSize
= sizeof(RECONVERTSTRING
) + rcFeedLen
+ sizeof(wchar_t);
3216 RECONVERTSTRING
*rc
= static_cast<RECONVERTSTRING
*>(PtrFromSPtr(lParam
));
3220 wchar_t *rcFeedStart
= reinterpret_cast<wchar_t*>(rc
+ 1);
3221 memcpy(rcFeedStart
, &rcFeed
[0], rcFeedLen
);
3223 IMContext
imc(MainHWND());
3227 DWORD compStrLen
= 0;
3228 Sci::Position compStart
= curPos
;
3229 if (pdoc
->TentativeActive()) {
3230 // rcFeed contains current composition string
3231 compStrLen
= imc
.GetCompositionStringLength(GCS_COMPSTR
);
3232 const int imeCaretPos
= imc
.GetImeCaretPos();
3233 compStart
= pdoc
->GetRelativePositionUTF16(curPos
, -imeCaretPos
);
3235 const Sci::Position compStrOffset
= pdoc
->CountUTF16(lineStart
, compStart
);
3237 // Fill in reconvert structure.
3238 // Let IME to decide what the target is.
3239 rc
->dwVersion
= 0; //constant
3240 rc
->dwStrLen
= static_cast<DWORD
>(rcFeed
.length());
3241 rc
->dwStrOffset
= sizeof(RECONVERTSTRING
); //constant
3242 rc
->dwCompStrLen
= compStrLen
;
3243 rc
->dwCompStrOffset
= static_cast<DWORD
>(compStrOffset
) * sizeof(wchar_t);
3244 rc
->dwTargetStrLen
= rc
->dwCompStrLen
;
3245 rc
->dwTargetStrOffset
= rc
->dwCompStrOffset
;
3247 return rcSize
; // MS API says reconv structure to be returned.
3250 void ScintillaWin::GetMouseParameters() noexcept
{
3251 // mouse pointer size and colour may changed
3252 reverseArrowCursor
.Invalidate();
3253 // This retrieves the number of lines per scroll as configured in the Mouse Properties sheet in Control Panel
3254 ::SystemParametersInfo(SPI_GETWHEELSCROLLLINES
, 0, &linesPerScroll
, 0);
3255 if (!::SystemParametersInfo(SPI_GETWHEELSCROLLCHARS
, 0, &charsPerScroll
, 0)) {
3256 // no horizontal scrolling configuration on Windows XP
3257 charsPerScroll
= (linesPerScroll
== WHEEL_PAGESCROLL
) ? 3 : linesPerScroll
;
3259 ::SystemParametersInfo(SPI_GETMOUSEVANISH
, 0, &typingWithoutCursor
, 0);
3262 void ScintillaWin::CopyToGlobal(GlobalMemory
&gmUnicode
, const SelectionText
&selectedText
) {
3263 const std::string_view
svSelected(selectedText
.Data(), selectedText
.LengthWithTerminator());
3264 if (IsUnicodeMode()) {
3265 const size_t uchars
= UTF16Length(svSelected
);
3266 gmUnicode
.Allocate(2 * uchars
);
3268 UTF16FromUTF8(svSelected
,
3269 static_cast<wchar_t *>(gmUnicode
.ptr
), uchars
);
3273 // Convert to Unicode using the current Scintilla code page
3274 const UINT cpSrc
= CodePageFromCharSet(
3275 selectedText
.characterSet
, selectedText
.codePage
);
3276 const size_t uLen
= WideCharLenFromMultiByte(cpSrc
, svSelected
);
3277 gmUnicode
.Allocate(2 * uLen
);
3279 WideCharFromMultiByte(cpSrc
, svSelected
,
3280 static_cast<wchar_t *>(gmUnicode
.ptr
), uLen
);
3285 void ScintillaWin::CopyToClipboard(const SelectionText
&selectedText
) {
3286 Clipboard
clipboard(MainHWND());
3292 GlobalMemory uniText
;
3293 CopyToGlobal(uniText
, selectedText
);
3295 uniText
.SetClip(CF_UNICODETEXT
);
3298 if (selectedText
.rectangular
) {
3299 ::SetClipboardData(cfColumnSelect
, 0);
3301 GlobalMemory borlandSelection
;
3302 borlandSelection
.Allocate(1);
3303 if (borlandSelection
) {
3304 static_cast<BYTE
*>(borlandSelection
.ptr
)[0] = 0x02;
3305 borlandSelection
.SetClip(cfBorlandIDEBlockType
);
3309 if (selectedText
.lineCopy
) {
3310 ::SetClipboardData(cfLineSelect
, 0);
3311 ::SetClipboardData(cfVSLineTag
, 0);
3315 void ScintillaWin::ScrollMessage(WPARAM wParam
) {
3316 //DWORD dwStart = timeGetTime();
3317 //Platform::DebugPrintf("Scroll %x %d\n", wParam, lParam);
3319 SCROLLINFO sci
= {};
3320 sci
.cbSize
= sizeof(sci
);
3321 sci
.fMask
= SIF_ALL
;
3323 GetScrollInfo(SB_VERT
, &sci
);
3325 //Platform::DebugPrintf("ScrollInfo %d mask=%x min=%d max=%d page=%d pos=%d track=%d\n", b,sci.fMask,
3326 //sci.nMin, sci.nMax, sci.nPage, sci.nPos, sci.nTrackPos);
3327 Sci::Line topLineNew
= topLine
;
3328 switch (LOWORD(wParam
)) {
3336 topLineNew
-= LinesToScroll(); break;
3337 case SB_PAGEDOWN
: topLineNew
+= LinesToScroll(); break;
3338 case SB_TOP
: topLineNew
= 0; break;
3339 case SB_BOTTOM
: topLineNew
= MaxScrollPos(); break;
3340 case SB_THUMBPOSITION
: topLineNew
= sci
.nTrackPos
; break;
3341 case SB_THUMBTRACK
: topLineNew
= sci
.nTrackPos
; break;
3343 ScrollTo(topLineNew
);
3346 void ScintillaWin::HorizontalScrollMessage(WPARAM wParam
) {
3348 const PRectangle rcText
= GetTextRectangle();
3349 const int pageWidth
= static_cast<int>(rcText
.Width() * 2 / 3);
3350 switch (LOWORD(wParam
)) {
3354 case SB_LINEDOWN
: // May move past the logical end
3369 case SB_THUMBPOSITION
:
3370 case SB_THUMBTRACK
: {
3371 // Do NOT use wParam, its 16 bit and not enough for very long lines. Its still possible to overflow the 32 bit but you have to try harder =]
3373 si
.cbSize
= sizeof(si
);
3374 si
.fMask
= SIF_TRACKPOS
;
3375 if (GetScrollInfo(SB_HORZ
, &si
)) {
3376 xPos
= si
.nTrackPos
;
3381 HorizontalScrollToClamped(xPos
);
3385 * Redraw all of text area.
3386 * This paint will not be abandoned.
3388 void ScintillaWin::FullPaint() {
3389 if ((technology
== Technology::Default
) || (technology
== Technology::DirectWriteDC
)) {
3390 HDC hdc
= ::GetDC(MainHWND());
3392 ::ReleaseDC(MainHWND(), hdc
);
3399 * Redraw all of text area on the specified DC.
3400 * This paint will not be abandoned.
3402 void ScintillaWin::FullPaintDC(HDC hdc
) {
3403 paintState
= PaintState::painting
;
3404 rcPaint
= GetClientRectangle();
3405 paintingAllText
= true;
3407 paintState
= PaintState::notPainting
;
3412 bool CompareDevCap(HDC hdc
, HDC hOtherDC
, int nIndex
) noexcept
{
3413 return ::GetDeviceCaps(hdc
, nIndex
) == ::GetDeviceCaps(hOtherDC
, nIndex
);
3418 bool ScintillaWin::IsCompatibleDC(HDC hOtherDC
) noexcept
{
3419 HDC hdc
= ::GetDC(MainHWND());
3420 const bool isCompatible
=
3421 CompareDevCap(hdc
, hOtherDC
, TECHNOLOGY
) &&
3422 CompareDevCap(hdc
, hOtherDC
, LOGPIXELSY
) &&
3423 CompareDevCap(hdc
, hOtherDC
, LOGPIXELSX
) &&
3424 CompareDevCap(hdc
, hOtherDC
, BITSPIXEL
) &&
3425 CompareDevCap(hdc
, hOtherDC
, PLANES
);
3426 ::ReleaseDC(MainHWND(), hdc
);
3427 return isCompatible
;
3430 DWORD
ScintillaWin::EffectFromState(DWORD grfKeyState
) const noexcept
{
3431 // These are the Wordpad semantics.
3433 if (inDragDrop
== DragDrop::dragging
) // Internal defaults to move
3434 dwEffect
= DROPEFFECT_MOVE
;
3436 dwEffect
= DROPEFFECT_COPY
;
3437 if (grfKeyState
& MK_ALT
)
3438 dwEffect
= DROPEFFECT_MOVE
;
3439 if (grfKeyState
& MK_CONTROL
)
3440 dwEffect
= DROPEFFECT_COPY
;
3444 /// Implement IUnknown
3445 STDMETHODIMP
ScintillaWin::QueryInterface(REFIID riid
, PVOID
*ppv
) {
3447 if (riid
== IID_IUnknown
) {
3449 } else if (riid
== IID_IDropSource
) {
3451 } else if (riid
== IID_IDropTarget
) {
3453 } else if (riid
== IID_IDataObject
) {
3457 return E_NOINTERFACE
;
3461 STDMETHODIMP_(ULONG
) ScintillaWin::AddRef() {
3465 STDMETHODIMP_(ULONG
) ScintillaWin::Release() {
3469 /// Implement IDropTarget
3470 STDMETHODIMP
ScintillaWin::DragEnter(LPDATAOBJECT pIDataSource
, DWORD grfKeyState
,
3471 POINTL
, PDWORD pdwEffect
) {
3474 FORMATETC fmtu
= {CF_UNICODETEXT
, nullptr, DVASPECT_CONTENT
, -1, TYMED_HGLOBAL
};
3475 const HRESULT hrHasUText
= pIDataSource
->QueryGetData(&fmtu
);
3476 hasOKText
= (hrHasUText
== S_OK
);
3478 *pdwEffect
= EffectFromState(grfKeyState
);
3480 *pdwEffect
= DROPEFFECT_NONE
;
3486 STDMETHODIMP
ScintillaWin::DragOver(DWORD grfKeyState
, POINTL pt
, PDWORD pdwEffect
) {
3488 if (!hasOKText
|| pdoc
->IsReadOnly()) {
3489 *pdwEffect
= DROPEFFECT_NONE
;
3493 *pdwEffect
= EffectFromState(grfKeyState
);
3495 // Update the cursor.
3496 POINT rpt
= {pt
.x
, pt
.y
};
3497 ::ScreenToClient(MainHWND(), &rpt
);
3498 SetDragPosition(SPositionFromLocation(PointFromPOINT(rpt
), false, false, UserVirtualSpace()));
3502 errorStatus
= Status::Failure
;
3507 STDMETHODIMP
ScintillaWin::DragLeave() {
3509 SetDragPosition(SelectionPosition(Sci::invalidPosition
));
3512 errorStatus
= Status::Failure
;
3517 STDMETHODIMP
ScintillaWin::Drop(LPDATAOBJECT pIDataSource
, DWORD grfKeyState
,
3518 POINTL pt
, PDWORD pdwEffect
) {
3520 *pdwEffect
= EffectFromState(grfKeyState
);
3525 SetDragPosition(SelectionPosition(Sci::invalidPosition
));
3528 FORMATETC fmtu
= {CF_UNICODETEXT
, nullptr, DVASPECT_CONTENT
, -1, TYMED_HGLOBAL
};
3530 const HRESULT hr
= pIDataSource
->GetData(&fmtu
, &medium
);
3531 if (!SUCCEEDED(hr
)) {
3534 if (medium
.hGlobal
) {
3535 GlobalMemory
memUDrop(medium
.hGlobal
);
3536 if (const wchar_t *uptr
= static_cast<const wchar_t *>(memUDrop
.ptr
)) {
3537 putf
= EncodeWString(uptr
);
3546 FORMATETC fmtr
= {cfColumnSelect
, nullptr, DVASPECT_CONTENT
, -1, TYMED_HGLOBAL
};
3547 const bool isRectangular
= S_OK
== pIDataSource
->QueryGetData(&fmtr
);
3549 POINT rpt
= {pt
.x
, pt
.y
};
3550 ::ScreenToClient(MainHWND(), &rpt
);
3551 const SelectionPosition movePos
= SPositionFromLocation(PointFromPOINT(rpt
), false, false, UserVirtualSpace());
3553 DropAt(movePos
, putf
.c_str(), putf
.size(), *pdwEffect
== DROPEFFECT_MOVE
, isRectangular
);
3556 ::ReleaseStgMedium(&medium
);
3560 errorStatus
= Status::Failure
;
3565 /// Implement important part of IDataObject
3566 STDMETHODIMP
ScintillaWin::GetData(FORMATETC
*pFEIn
, STGMEDIUM
*pSTM
) {
3567 if (!SupportedFormat(pFEIn
)) {
3568 //Platform::DebugPrintf("DOB GetData No %d %x %x fmt=%x\n", lenDrag, pFEIn, pSTM, pFEIn->cfFormat);
3569 return DATA_E_FORMATETC
;
3572 pSTM
->tymed
= TYMED_HGLOBAL
;
3573 //Platform::DebugPrintf("DOB GetData OK %d %x %x\n", lenDrag, pFEIn, pSTM);
3575 GlobalMemory uniText
;
3576 CopyToGlobal(uniText
, drag
);
3577 pSTM
->hGlobal
= uniText
? uniText
.Unlock() : 0;
3578 pSTM
->pUnkForRelease
= nullptr;
3582 void ScintillaWin::Prepare() noexcept
{
3583 Platform_Initialise(hInstance
);
3585 // Register the CallTip class
3586 WNDCLASSEX wndclassc
{};
3587 wndclassc
.cbSize
= sizeof(wndclassc
);
3588 wndclassc
.style
= CS_GLOBALCLASS
| CS_HREDRAW
| CS_VREDRAW
;
3589 wndclassc
.cbWndExtra
= sizeof(ScintillaWin
*);
3590 wndclassc
.hInstance
= hInstance
;
3591 wndclassc
.lpfnWndProc
= ScintillaWin::CTWndProc
;
3592 wndclassc
.hCursor
= ::LoadCursor(NULL
, IDC_ARROW
);
3593 wndclassc
.lpszClassName
= callClassName
;
3595 callClassAtom
= ::RegisterClassEx(&wndclassc
);
3598 bool ScintillaWin::Register(HINSTANCE hInstance_
) noexcept
{
3600 hInstance
= hInstance_
;
3602 // Register the Scintilla class
3603 // Register Scintilla as a wide character window
3604 WNDCLASSEXW wndclass
{};
3605 wndclass
.cbSize
= sizeof(wndclass
);
3606 wndclass
.style
= CS_GLOBALCLASS
| CS_HREDRAW
| CS_VREDRAW
;
3607 wndclass
.lpfnWndProc
= ScintillaWin::SWndProc
;
3608 wndclass
.cbWndExtra
= sizeof(ScintillaWin
*);
3609 wndclass
.hInstance
= hInstance
;
3610 wndclass
.lpszClassName
= L
"Scintilla";
3611 scintillaClassAtom
= ::RegisterClassExW(&wndclass
);
3612 const bool result
= 0 != scintillaClassAtom
;
3617 bool ScintillaWin::Unregister() noexcept
{
3619 if (0 != scintillaClassAtom
) {
3620 if (::UnregisterClass(MAKEINTATOM(scintillaClassAtom
), hInstance
) == 0) {
3623 scintillaClassAtom
= 0;
3625 if (0 != callClassAtom
) {
3626 if (::UnregisterClass(MAKEINTATOM(callClassAtom
), hInstance
) == 0) {
3634 bool ScintillaWin::HasCaretSizeChanged() const noexcept
{
3636 ( (0 != vs
.caret
.width
) && (sysCaretWidth
!= vs
.caret
.width
) )
3637 || ((0 != vs
.lineHeight
) && (sysCaretHeight
!= vs
.lineHeight
))
3644 BOOL
ScintillaWin::CreateSystemCaret() {
3645 sysCaretWidth
= vs
.caret
.width
;
3646 if (0 == sysCaretWidth
) {
3649 sysCaretHeight
= vs
.lineHeight
;
3650 const int bitmapSize
= (((sysCaretWidth
+ 15) & ~15) >> 3) *
3652 std::vector
<BYTE
> bits(bitmapSize
);
3653 sysCaretBitmap
= ::CreateBitmap(sysCaretWidth
, sysCaretHeight
, 1,
3655 const BOOL retval
= ::CreateCaret(
3656 MainHWND(), sysCaretBitmap
,
3657 sysCaretWidth
, sysCaretHeight
);
3658 if (technology
== Technology::Default
) {
3659 // System caret interferes with Direct2D drawing so only show it for GDI.
3660 ::ShowCaret(MainHWND());
3665 BOOL
ScintillaWin::DestroySystemCaret() noexcept
{
3666 ::HideCaret(MainHWND());
3667 const BOOL retval
= ::DestroyCaret();
3668 if (sysCaretBitmap
) {
3669 ::DeleteObject(sysCaretBitmap
);
3670 sysCaretBitmap
= {};
3675 LRESULT PASCAL
ScintillaWin::CTWndProc(
3676 HWND hWnd
, UINT iMessage
, WPARAM wParam
, LPARAM lParam
) {
3677 // Find C++ object associated with window.
3678 ScintillaWin
*sciThis
= static_cast<ScintillaWin
*>(PointerFromWindow(hWnd
));
3680 // ctp will be zero if WM_CREATE not seen yet
3681 if (sciThis
== nullptr) {
3682 if (iMessage
== WM_CREATE
) {
3683 // Associate CallTip object with window
3684 CREATESTRUCT
*pCreate
= static_cast<CREATESTRUCT
*>(PtrFromSPtr(lParam
));
3685 SetWindowPointer(hWnd
, pCreate
->lpCreateParams
);
3688 return ::DefWindowProc(hWnd
, iMessage
, wParam
, lParam
);
3691 if (iMessage
== WM_NCDESTROY
) {
3692 SetWindowPointer(hWnd
, nullptr);
3693 return ::DefWindowProc(hWnd
, iMessage
, wParam
, lParam
);
3694 } else if (iMessage
== WM_PAINT
) {
3696 ::BeginPaint(hWnd
, &ps
);
3697 std::unique_ptr
<Surface
> surfaceWindow(Surface::Allocate(sciThis
->technology
));
3698 #if defined(USE_D2D)
3699 ID2D1HwndRenderTarget
*pCTRenderTarget
= nullptr;
3702 GetClientRect(hWnd
, &rc
);
3703 if (sciThis
->technology
== Technology::Default
) {
3704 surfaceWindow
->Init(ps
.hdc
, hWnd
);
3706 #if defined(USE_D2D)
3707 const int scaleFactor
= sciThis
->GetFirstIntegralMultipleDeviceScaleFactor();
3709 // Create a Direct2D render target.
3710 D2D1_HWND_RENDER_TARGET_PROPERTIES dhrtp
{};
3712 dhrtp
.pixelSize
= ::GetSizeUFromRect(rc
, scaleFactor
);
3713 dhrtp
.presentOptions
= (sciThis
->technology
== Technology::DirectWriteRetain
) ?
3714 D2D1_PRESENT_OPTIONS_RETAIN_CONTENTS
: D2D1_PRESENT_OPTIONS_NONE
;
3716 D2D1_RENDER_TARGET_PROPERTIES drtp
{};
3717 drtp
.type
= D2D1_RENDER_TARGET_TYPE_DEFAULT
;
3718 drtp
.pixelFormat
.format
= DXGI_FORMAT_UNKNOWN
;
3719 drtp
.pixelFormat
.alphaMode
= D2D1_ALPHA_MODE_UNKNOWN
;
3720 drtp
.dpiX
= 96.f
* scaleFactor
;
3721 drtp
.dpiY
= 96.f
* scaleFactor
;
3722 drtp
.usage
= D2D1_RENDER_TARGET_USAGE_NONE
;
3723 drtp
.minLevel
= D2D1_FEATURE_LEVEL_DEFAULT
;
3725 if (!SUCCEEDED(pD2DFactory
->CreateHwndRenderTarget(drtp
, dhrtp
, &pCTRenderTarget
))) {
3726 surfaceWindow
->Release();
3727 ::EndPaint(hWnd
, &ps
);
3730 // If above SUCCEEDED, then pCTRenderTarget not nullptr
3731 assert(pCTRenderTarget
);
3732 if (pCTRenderTarget
) {
3733 surfaceWindow
->Init(pCTRenderTarget
, hWnd
);
3734 pCTRenderTarget
->BeginDraw();
3738 surfaceWindow
->SetMode(sciThis
->CurrentSurfaceMode());
3739 sciThis
->SetRenderingParams(surfaceWindow
.get());
3740 sciThis
->ct
.PaintCT(surfaceWindow
.get());
3741 #if defined(USE_D2D)
3742 if (pCTRenderTarget
)
3743 pCTRenderTarget
->EndDraw();
3745 surfaceWindow
->Release();
3746 #if defined(USE_D2D)
3747 ReleaseUnknown(pCTRenderTarget
);
3749 ::EndPaint(hWnd
, &ps
);
3751 } else if ((iMessage
== WM_NCLBUTTONDOWN
) || (iMessage
== WM_NCLBUTTONDBLCLK
)) {
3752 POINT pt
= POINTFromLParam(lParam
);
3753 ::ScreenToClient(hWnd
, &pt
);
3754 sciThis
->ct
.MouseClick(PointFromPOINT(pt
));
3755 sciThis
->CallTipClick();
3757 } else if (iMessage
== WM_LBUTTONDOWN
) {
3758 // This does not fire due to the hit test code
3759 sciThis
->ct
.MouseClick(PointFromLParam(lParam
));
3760 sciThis
->CallTipClick();
3762 } else if (iMessage
== WM_SETCURSOR
) {
3763 ::SetCursor(::LoadCursor(NULL
, IDC_ARROW
));
3765 } else if (iMessage
== WM_NCHITTEST
) {
3768 return ::DefWindowProc(hWnd
, iMessage
, wParam
, lParam
);
3772 sciThis
->errorStatus
= Status::Failure
;
3774 return ::DefWindowProc(hWnd
, iMessage
, wParam
, lParam
);
3777 sptr_t
ScintillaWin::DirectFunction(
3778 sptr_t ptr
, UINT iMessage
, uptr_t wParam
, sptr_t lParam
) {
3779 ScintillaWin
*sci
= reinterpret_cast<ScintillaWin
*>(ptr
);
3780 PLATFORM_ASSERT(::GetCurrentThreadId() == ::GetWindowThreadProcessId(sci
->MainHWND(), nullptr));
3781 return sci
->WndProc(static_cast<Message
>(iMessage
), wParam
, lParam
);
3784 sptr_t
ScintillaWin::DirectStatusFunction(
3785 sptr_t ptr
, UINT iMessage
, uptr_t wParam
, sptr_t lParam
, int *pStatus
) {
3786 ScintillaWin
*sci
= reinterpret_cast<ScintillaWin
*>(ptr
);
3787 PLATFORM_ASSERT(::GetCurrentThreadId() == ::GetWindowThreadProcessId(sci
->MainHWND(), nullptr));
3788 const sptr_t returnValue
= sci
->WndProc(static_cast<Message
>(iMessage
), wParam
, lParam
);
3789 *pStatus
= static_cast<int>(sci
->errorStatus
);
3793 namespace Scintilla::Internal
{
3795 sptr_t
DirectFunction(
3796 ScintillaWin
*sci
, UINT iMessage
, uptr_t wParam
, sptr_t lParam
) {
3797 return sci
->WndProc(static_cast<Message
>(iMessage
), wParam
, lParam
);
3802 LRESULT PASCAL
ScintillaWin::SWndProc(
3803 HWND hWnd
, UINT iMessage
, WPARAM wParam
, LPARAM lParam
) {
3804 //Platform::DebugPrintf("S W:%x M:%x WP:%x L:%x\n", hWnd, iMessage, wParam, lParam);
3806 // Find C++ object associated with window.
3807 ScintillaWin
*sci
= static_cast<ScintillaWin
*>(PointerFromWindow(hWnd
));
3808 // sci will be zero if WM_CREATE not seen yet
3809 if (sci
== nullptr) {
3811 if (iMessage
== WM_CREATE
) {
3812 static std::once_flag once
;
3813 std::call_once(once
, Prepare
);
3814 // Create C++ object associated with window
3815 sci
= new ScintillaWin(hWnd
);
3816 SetWindowPointer(hWnd
, sci
);
3817 return sci
->WndProc(static_cast<Message
>(iMessage
), wParam
, lParam
);
3821 return ::DefWindowProc(hWnd
, iMessage
, wParam
, lParam
);
3823 if (iMessage
== WM_NCDESTROY
) {
3829 SetWindowPointer(hWnd
, nullptr);
3830 return ::DefWindowProc(hWnd
, iMessage
, wParam
, lParam
);
3832 return sci
->WndProc(static_cast<Message
>(iMessage
), wParam
, lParam
);
3837 // This function is externally visible so it can be called from container when building statically.
3838 // Must be called once only.
3839 extern "C" int Scintilla_RegisterClasses(void *hInstance
) {
3840 const bool result
= ScintillaWin::Register(static_cast<HINSTANCE
>(hInstance
));
3844 namespace Scintilla::Internal
{
3846 int ResourcesRelease(bool fromDllMain
) noexcept
{
3847 const bool result
= ScintillaWin::Unregister();
3848 Platform_Finalise(fromDllMain
);
3852 int RegisterClasses(void *hInstance
) noexcept
{
3853 const bool result
= ScintillaWin::Register(static_cast<HINSTANCE
>(hInstance
));
3859 // This function is externally visible so it can be called from container when building statically.
3860 extern "C" int Scintilla_ReleaseResources() {
3861 return Scintilla::Internal::ResourcesRelease(false);