1 // Scintilla source code edit control
2 /** @file ScintillaWin.cxx
3  ** Windows specific subclass of ScintillaBase.
4  **/
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.
7 
8 #include <cstddef>
9 #include <cstdlib>
10 #include <cassert>
11 #include <cstring>
12 #include <cstdio>
13 #include <cmath>
14 #include <climits>
15 
16 #include <stdexcept>
17 #include <new>
18 #include <string>
19 #include <string_view>
20 #include <vector>
21 #include <map>
22 #include <algorithm>
23 #include <memory>
24 #include <chrono>
25 #include <mutex>
26 
27 // Want to use std::min and std::max so don't want Windows.h version of min and max
28 #if !defined(NOMINMAX)
29 #define NOMINMAX
30 #endif
31 #undef _WIN32_WINNT
32 #define _WIN32_WINNT 0x0500
33 #undef WINVER
34 #define WINVER 0x0500
35 #include <windows.h>
36 #include <commctrl.h>
37 #include <richedit.h>
38 #include <windowsx.h>
39 #include <zmouse.h>
40 #include <ole2.h>
41 
42 #if !defined(DISABLE_D2D)
43 #define USE_D2D 1
44 #endif
45 
46 #if defined(USE_D2D)
47 #include <d2d1.h>
48 #include <dwrite.h>
49 #endif
50 
51 #include "Platform.h"
52 
53 #include "ILoader.h"
54 #include "ILexer.h"
55 #include "Scintilla.h"
56 
57 #include "CharacterCategory.h"
58 #include "Position.h"
59 #include "UniqueString.h"
60 #include "SplitVector.h"
61 #include "Partitioning.h"
62 #include "RunStyles.h"
63 #include "ContractionState.h"
64 #include "CellBuffer.h"
65 #include "CallTip.h"
66 #include "KeyMap.h"
67 #include "Indicator.h"
68 #include "LineMarker.h"
69 #include "Style.h"
70 #include "ViewStyle.h"
71 #include "CharClassify.h"
72 #include "Decoration.h"
73 #include "CaseFolder.h"
74 #include "Document.h"
75 #include "CaseConvert.h"
76 #include "UniConversion.h"
77 #include "Selection.h"
78 #include "PositionCache.h"
79 #include "EditModel.h"
80 #include "MarginView.h"
81 #include "EditView.h"
82 #include "Editor.h"
83 #include "ElapsedPeriod.h"
84 
85 #include "AutoComplete.h"
86 #include "ScintillaBase.h"
87 
88 #include "PlatWin.h"
89 #include "HanjaDic.h"
90 #include "ScintillaWin.h"
91 
92 #ifndef SPI_GETWHEELSCROLLLINES
93 #define SPI_GETWHEELSCROLLLINES   104
94 #endif
95 
96 #ifndef WM_UNICHAR
97 #define WM_UNICHAR                      0x0109
98 #endif
99 
100 #ifndef WM_DPICHANGED
101 #define WM_DPICHANGED 0x02E0
102 #endif
103 #ifndef WM_DPICHANGED_AFTERPARENT
104 #define WM_DPICHANGED_AFTERPARENT 0x02E3
105 #endif
106 
107 #ifndef UNICODE_NOCHAR
108 #define UNICODE_NOCHAR                  0xFFFF
109 #endif
110 
111 #ifndef IS_HIGH_SURROGATE
112 #define IS_HIGH_SURROGATE(x)            ((x) >= SURROGATE_LEAD_FIRST && (x) <= SURROGATE_LEAD_LAST)
113 #endif
114 
115 #ifndef IS_LOW_SURROGATE
116 #define IS_LOW_SURROGATE(x)             ((x) >= SURROGATE_TRAIL_FIRST && (x) <= SURROGATE_TRAIL_LAST)
117 #endif
118 
119 #ifndef MK_ALT
120 #define MK_ALT 32
121 #endif
122 
123 // Two idle messages SC_WIN_IDLE and SC_WORK_IDLE.
124 
125 // SC_WIN_IDLE is low priority so should occur after the next WM_PAINT
126 // It is for lengthy actions like wrapping and background styling
127 constexpr UINT SC_WIN_IDLE = 5001;
128 // SC_WORK_IDLE is high priority and should occur before the next WM_PAINT
129 // It is for shorter actions like restyling the text just inserted
130 // and delivering SCN_UPDATEUI
131 constexpr UINT SC_WORK_IDLE = 5002;
132 
133 #define SC_INDICATOR_INPUT INDICATOR_IME
134 #define SC_INDICATOR_TARGET INDICATOR_IME+1
135 #define SC_INDICATOR_CONVERTED INDICATOR_IME+2
136 #define SC_INDICATOR_UNKNOWN INDICATOR_IME_MAX
137 
138 #ifndef SCS_CAP_SETRECONVERTSTRING
139 #define SCS_CAP_SETRECONVERTSTRING 0x00000004
140 #define SCS_QUERYRECONVERTSTRING 0x00020000
141 #define SCS_SETRECONVERTSTRING 0x00010000
142 #endif
143 
144 typedef UINT_PTR (WINAPI *SetCoalescableTimerSig)(HWND hwnd, UINT_PTR nIDEvent,
145 	UINT uElapse, TIMERPROC lpTimerFunc, ULONG uToleranceDelay);
146 
147 // GCC has trouble with the standard COM ABI so do it the old C way with explicit vtables.
148 
149 using namespace Scintilla;
150 
151 namespace {
152 
153 const TCHAR callClassName[] = TEXT("CallTip");
154 
SetWindowID(HWND hWnd,int identifier)155 void SetWindowID(HWND hWnd, int identifier) noexcept {
156 	::SetWindowLongPtr(hWnd, GWLP_ID, identifier);
157 }
158 
PointFromLParam(sptr_t lpoint)159 Point PointFromLParam(sptr_t lpoint) noexcept {
160 	return Point::FromInts(GET_X_LPARAM(lpoint), GET_Y_LPARAM(lpoint));
161 }
162 
KeyboardIsKeyDown(int key)163 bool KeyboardIsKeyDown(int key) noexcept {
164 	return (::GetKeyState(key) & 0x80000000) != 0;
165 }
166 
KeyboardIsNumericKeypadFunction(uptr_t wParam,sptr_t lParam)167 constexpr bool KeyboardIsNumericKeypadFunction(uptr_t wParam, sptr_t lParam) {
168 	// Bit 24 is the extended keyboard flag and the numeric keypad is non-extended
169 	if ((lParam & (1 << 24)) != 0) {
170 		// Not from the numeric keypad
171 		return false;
172 	}
173 
174 	switch (wParam) {
175 	case VK_INSERT:	// 0
176 	case VK_END:	// 1
177 	case VK_DOWN:	// 2
178 	case VK_NEXT:	// 3
179 	case VK_LEFT:	// 4
180 	case VK_CLEAR:	// 5
181 	case VK_RIGHT:	// 6
182 	case VK_HOME:	// 7
183 	case VK_UP:		// 8
184 	case VK_PRIOR:	// 9
185 		return true;
186 	default:
187 		return false;
188 	}
189 }
190 
191 }
192 
193 class ScintillaWin; 	// Forward declaration for COM interface subobjects
194 
195 typedef void VFunction(void);
196 
197 
198 /**
199  */
200 class FormatEnumerator {
201 public:
202 	VFunction **vtbl;
203 	ULONG ref;
204 	ULONG pos;
205 	std::vector<CLIPFORMAT> formats;
206 	FormatEnumerator(ULONG pos_, const CLIPFORMAT formats_[], size_t formatsLen_);
207 };
208 
209 /**
210  */
211 class DropSource {
212 public:
213 	VFunction **vtbl;
214 	ScintillaWin *sci;
215 	DropSource() noexcept;
216 };
217 
218 /**
219  */
220 class DataObject {
221 public:
222 	VFunction **vtbl;
223 	ScintillaWin *sci;
224 	DataObject() noexcept;
225 };
226 
227 /**
228  */
229 class DropTarget {
230 public:
231 	VFunction **vtbl;
232 	ScintillaWin *sci;
233 	DropTarget() noexcept;
234 };
235 
236 namespace {
237 
238 class IMContext {
239 	HWND hwnd;
240 public:
241 	HIMC hIMC;
IMContext(HWND hwnd_)242 	IMContext(HWND hwnd_) noexcept :
243 		hwnd(hwnd_), hIMC(::ImmGetContext(hwnd_)) {
244 	}
245 	// Deleted so IMContext objects can not be copied.
246 	IMContext(const IMContext &) = delete;
247 	IMContext(IMContext &&) = delete;
248 	IMContext &operator=(const IMContext &) = delete;
249 	IMContext &operator=(IMContext &&) = delete;
~IMContext()250 	~IMContext() {
251 		if (hIMC)
252 			::ImmReleaseContext(hwnd, hIMC);
253 	}
254 
GetImeCaretPos() const255 	unsigned int GetImeCaretPos() const noexcept {
256 		return ImmGetCompositionStringW(hIMC, GCS_CURSORPOS, nullptr, 0);
257 	}
258 
GetImeAttributes()259 	std::vector<BYTE> GetImeAttributes() {
260 		const int attrLen = ::ImmGetCompositionStringW(hIMC, GCS_COMPATTR, nullptr, 0);
261 		std::vector<BYTE> attr(attrLen, 0);
262 		::ImmGetCompositionStringW(hIMC, GCS_COMPATTR, &attr[0], static_cast<DWORD>(attr.size()));
263 		return attr;
264 	}
265 
GetCompositionString(DWORD dwIndex)266 	std::wstring GetCompositionString(DWORD dwIndex) {
267 		const LONG byteLen = ::ImmGetCompositionStringW(hIMC, dwIndex, nullptr, 0);
268 		std::wstring wcs(byteLen / 2, 0);
269 		::ImmGetCompositionStringW(hIMC, dwIndex, &wcs[0], byteLen);
270 		return wcs;
271 	}
272 };
273 
274 class GlobalMemory;
275 
276 class ReverseArrowCursor {
277 	UINT dpi = USER_DEFAULT_SCREEN_DPI;
278 	HCURSOR cursor {};
279 
280 public:
ReverseArrowCursor()281 	ReverseArrowCursor() noexcept {}
282 	// Deleted so ReverseArrowCursor objects can not be copied.
283 	ReverseArrowCursor(const ReverseArrowCursor &) = delete;
284 	ReverseArrowCursor(ReverseArrowCursor &&) = delete;
285 	ReverseArrowCursor &operator=(const ReverseArrowCursor &) = delete;
286 	ReverseArrowCursor &operator=(ReverseArrowCursor &&) = delete;
~ReverseArrowCursor()287 	~ReverseArrowCursor() {
288 		if (cursor) {
289 			::DestroyCursor(cursor);
290 		}
291 	}
292 
Load(UINT dpi_)293 	HCURSOR Load(UINT dpi_) noexcept {
294 		if (cursor)	 {
295 			if (dpi == dpi_) {
296 				return cursor;
297 			}
298 			::DestroyCursor(cursor);
299 		}
300 
301 		dpi = dpi_;
302 		cursor = LoadReverseArrowCursor(dpi_);
303 		return cursor ? cursor : ::LoadCursor({}, IDC_ARROW);
304 	}
305 };
306 
307 }
308 
309 /**
310  */
311 class ScintillaWin :
312 	public ScintillaBase {
313 
314 	bool lastKeyDownConsumed;
315 	wchar_t lastHighSurrogateChar;
316 
317 	bool capturedMouse;
318 	bool trackedMouseLeave;
319 	SetCoalescableTimerSig SetCoalescableTimerFn;
320 
321 	unsigned int linesPerScroll;	///< Intellimouse support
322 	int wheelDelta; ///< Wheel delta from roll
323 
324 	UINT dpi = USER_DEFAULT_SCREEN_DPI;
325 	ReverseArrowCursor reverseArrowCursor;
326 
327 	HRGN hRgnUpdate;
328 
329 	bool hasOKText;
330 
331 	CLIPFORMAT cfColumnSelect;
332 	CLIPFORMAT cfBorlandIDEBlockType;
333 	CLIPFORMAT cfLineSelect;
334 	CLIPFORMAT cfVSLineTag;
335 
336 	HRESULT hrOle;
337 	DropSource ds;
338 	DataObject dob;
339 	DropTarget dt;
340 
341 	static HINSTANCE hInstance;
342 	static ATOM scintillaClassAtom;
343 	static ATOM callClassAtom;
344 
345 #if defined(USE_D2D)
346 	ID2D1RenderTarget *pRenderTarget;
347 	bool renderTargetValid;
348 #endif
349 
350 	explicit ScintillaWin(HWND hwnd);
351 	// Deleted so ScintillaWin objects can not be copied.
352 	ScintillaWin(const ScintillaWin &) = delete;
353 	ScintillaWin(ScintillaWin &&) = delete;
354 	ScintillaWin &operator=(const ScintillaWin &) = delete;
355 	ScintillaWin &operator=(ScintillaWin &&) = delete;
356 	// ~ScintillaWin() in public section
357 
358 	void Init();
359 	void Finalise() override;
360 #if defined(USE_D2D)
361 	void EnsureRenderTarget(HDC hdc);
362 	void DropRenderTarget();
363 #endif
364 	HWND MainHWND() const noexcept;
365 
366 	static sptr_t DirectFunction(
367 		    sptr_t ptr, UINT iMessage, uptr_t wParam, sptr_t lParam);
368 	static LRESULT PASCAL SWndProc(
369 		    HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam);
370 	static LRESULT PASCAL CTWndProc(
371 		    HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam);
372 
373 	enum : UINT_PTR { invalidTimerID, standardTimerID, idleTimerID, fineTimerStart };
374 
375 	void DisplayCursor(Window::Cursor c) override;
376 	bool DragThreshold(Point ptStart, Point ptNow) override;
377 	void StartDrag() override;
378 	static int MouseModifiers(uptr_t wParam) noexcept;
379 
380 	Sci::Position TargetAsUTF8(char *text) const;
381 	Sci::Position EncodedFromUTF8(const char *utf8, char *encoded) const;
382 
383 	bool PaintDC(HDC hdc);
384 	sptr_t WndPaint();
385 
386 	// DBCS
387 	void ImeStartComposition();
388 	void ImeEndComposition();
389 	LRESULT ImeOnReconvert(LPARAM lParam);
390 	sptr_t HandleCompositionWindowed(uptr_t wParam, sptr_t lParam);
391 	sptr_t HandleCompositionInline(uptr_t wParam, sptr_t lParam);
392 	static bool KoreanIME() noexcept;
393 	void MoveImeCarets(Sci::Position offset);
394 	void DrawImeIndicator(int indicator, Sci::Position len);
395 	void SetCandidateWindowPos();
396 	void SelectionToHangul();
397 	void EscapeHanja();
398 	void ToggleHanja();
399 	void AddWString(std::wstring_view wsv, CharacterSource charSource);
400 
401 	UINT CodePageOfDocument() const noexcept;
402 	bool ValidCodePage(int codePage) const override;
403 	std::string EncodeWString(std::wstring_view wsv);
404 	sptr_t DefWndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam) override;
405 	void IdleWork() override;
406 	void QueueIdleWork(WorkNeeded::workItems items, Sci::Position upTo) override;
407 	bool SetIdle(bool on) override;
408 	UINT_PTR timers[tickDwell+1] {};
409 	bool FineTickerRunning(TickReason reason) override;
410 	void FineTickerStart(TickReason reason, int millis, int tolerance) override;
411 	void FineTickerCancel(TickReason reason) override;
412 	void SetMouseCapture(bool on) override;
413 	bool HaveMouseCapture() override;
414 	void SetTrackMouseLeaveEvent(bool on) noexcept;
415 	bool PaintContains(PRectangle rc) override;
416 	void ScrollText(Sci::Line linesToMove) override;
417 	void NotifyCaretMove() override;
418 	void UpdateSystemCaret() override;
419 	void SetVerticalScrollPos() override;
420 	void SetHorizontalScrollPos() override;
421 	bool ModifyScrollBars(Sci::Line nMax, Sci::Line nPage) override;
422 	void NotifyChange() override;
423 	void NotifyFocus(bool focus) override;
424 	void SetCtrlID(int identifier) override;
425 	int GetCtrlID() override;
426 	void NotifyParent(SCNotification scn) override;
427 	void NotifyDoubleClick(Point pt, int modifiers) override;
428 	CaseFolder *CaseFolderForEncoding() override;
429 	std::string CaseMapString(const std::string &s, int caseMapping) override;
430 	void Copy() override;
431 	bool CanPaste() override;
432 	void Paste() override;
433 	void CreateCallTipWindow(PRectangle rc) override;
434 	void AddToPopUp(const char *label, int cmd = 0, bool enabled = true) override;
435 	void ClaimSelection() override;
436 
437 	void GetIntelliMouseParameters() noexcept;
438 	void CopyToGlobal(GlobalMemory &gmUnicode, const SelectionText &selectedText);
439 	void CopyToClipboard(const SelectionText &selectedText) override;
440 	void ScrollMessage(WPARAM wParam);
441 	void HorizontalScrollMessage(WPARAM wParam);
442 	void FullPaint();
443 	void FullPaintDC(HDC hdc);
444 	bool IsCompatibleDC(HDC hOtherDC) noexcept;
445 	DWORD EffectFromState(DWORD grfKeyState) const noexcept;
446 
447 	int SetScrollInfo(int nBar, LPCSCROLLINFO lpsi, BOOL bRedraw) noexcept;
448 	bool GetScrollInfo(int nBar, LPSCROLLINFO lpsi) noexcept;
449 	void ChangeScrollPos(int barType, Sci::Position pos);
450 	sptr_t GetTextLength();
451 	sptr_t GetText(uptr_t wParam, sptr_t lParam);
452 	Window::Cursor ContextCursor(Point pt);
453 	sptr_t ShowContextMenu(unsigned int iMessage, uptr_t wParam, sptr_t lParam);
454 	void SizeWindow();
455 	sptr_t MouseMessage(unsigned int iMessage, uptr_t wParam, sptr_t lParam);
456 	sptr_t KeyMessage(unsigned int iMessage, uptr_t wParam, sptr_t lParam);
457 	sptr_t FocusMessage(unsigned int iMessage, uptr_t wParam, sptr_t lParam);
458 	sptr_t IMEMessage(unsigned int iMessage, uptr_t wParam, sptr_t lParam);
459 	sptr_t EditMessage(unsigned int iMessage, uptr_t wParam, sptr_t lParam);
460 	sptr_t IdleMessage(unsigned int iMessage, uptr_t wParam, sptr_t lParam);
461 	sptr_t SciMessage(unsigned int iMessage, uptr_t wParam, sptr_t lParam);
462 
463 public:
464 	~ScintillaWin() override;
465 
466 	// Public for benefit of Scintilla_DirectFunction
467 	sptr_t WndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam) override;
468 
469 	/// Implement IUnknown
470 	STDMETHODIMP QueryInterface(REFIID riid, PVOID *ppv);
471 	STDMETHODIMP_(ULONG)AddRef();
472 	STDMETHODIMP_(ULONG)Release();
473 
474 	/// Implement IDropTarget
475 	STDMETHODIMP DragEnter(LPDATAOBJECT pIDataSource, DWORD grfKeyState,
476 	                       POINTL pt, PDWORD pdwEffect);
477 	STDMETHODIMP DragOver(DWORD grfKeyState, POINTL pt, PDWORD pdwEffect);
478 	STDMETHODIMP DragLeave();
479 	STDMETHODIMP Drop(LPDATAOBJECT pIDataSource, DWORD grfKeyState,
480 	                  POINTL pt, PDWORD pdwEffect);
481 
482 	/// Implement important part of IDataObject
483 	STDMETHODIMP GetData(FORMATETC *pFEIn, STGMEDIUM *pSTM);
484 
485 	static void Prepare() noexcept;
486 	static bool Register(HINSTANCE hInstance_) noexcept;
487 	static bool Unregister() noexcept;
488 
489 	friend class DropSource;
490 	friend class DataObject;
491 	friend class DropTarget;
DragIsRectangularOK(CLIPFORMAT fmt) const492 	bool DragIsRectangularOK(CLIPFORMAT fmt) const noexcept {
493 		return drag.rectangular && (fmt == cfColumnSelect);
494 	}
495 
496 private:
497 	// For use in creating a system caret
498 	bool HasCaretSizeChanged() const noexcept;
499 	BOOL CreateSystemCaret();
500 	BOOL DestroySystemCaret() noexcept;
501 	HBITMAP sysCaretBitmap;
502 	int sysCaretWidth;
503 	int sysCaretHeight;
504 	bool styleIdleInQueue;
505 };
506 
507 HINSTANCE ScintillaWin::hInstance {};
508 ATOM ScintillaWin::scintillaClassAtom = 0;
509 ATOM ScintillaWin::callClassAtom = 0;
510 
ScintillaWin(HWND hwnd)511 ScintillaWin::ScintillaWin(HWND hwnd) {
512 
513 	lastKeyDownConsumed = false;
514 	lastHighSurrogateChar = 0;
515 
516 	capturedMouse = false;
517 	trackedMouseLeave = false;
518 	SetCoalescableTimerFn = nullptr;
519 
520 	linesPerScroll = 0;
521 	wheelDelta = 0;   // Wheel delta from roll
522 
523 	dpi = DpiForWindow(hwnd);
524 
525 	hRgnUpdate = {};
526 
527 	hasOKText = false;
528 
529 	// There does not seem to be a real standard for indicating that the clipboard
530 	// contains a rectangular selection, so copy Developer Studio and Borland Delphi.
531 	cfColumnSelect = static_cast<CLIPFORMAT>(
532 		::RegisterClipboardFormat(TEXT("MSDEVColumnSelect")));
533 	cfBorlandIDEBlockType = static_cast<CLIPFORMAT>(
534 		::RegisterClipboardFormat(TEXT("Borland IDE Block Type")));
535 
536 	// Likewise for line-copy (copies a full line when no text is selected)
537 	cfLineSelect = static_cast<CLIPFORMAT>(
538 		::RegisterClipboardFormat(TEXT("MSDEVLineSelect")));
539 	cfVSLineTag = static_cast<CLIPFORMAT>(
540 		::RegisterClipboardFormat(TEXT("VisualStudioEditorOperationsLineCutCopyClipboardTag")));
541 	hrOle = E_FAIL;
542 
543 	wMain = hwnd;
544 
545 	dob.sci = this;
546 	ds.sci = this;
547 	dt.sci = this;
548 
549 	sysCaretBitmap = {};
550 	sysCaretWidth = 0;
551 	sysCaretHeight = 0;
552 
553 	styleIdleInQueue = false;
554 
555 #if defined(USE_D2D)
556 	pRenderTarget = nullptr;
557 	renderTargetValid = true;
558 #endif
559 
560 	caret.period = ::GetCaretBlinkTime();
561 	if (caret.period < 0)
562 		caret.period = 0;
563 
564 	Init();
565 }
566 
~ScintillaWin()567 ScintillaWin::~ScintillaWin() {}
568 
Init()569 void ScintillaWin::Init() {
570 	// Initialize COM.  If the app has already done this it will have
571 	// no effect.  If the app hasn't, we really shouldn't ask them to call
572 	// it just so this internal feature works.
573 	hrOle = ::OleInitialize(nullptr);
574 
575 	// Find SetCoalescableTimer which is only available from Windows 8+
576 	HMODULE user32 = ::GetModuleHandleW(L"user32.dll");
577 	SetCoalescableTimerFn = DLLFunction<SetCoalescableTimerSig>(user32, "SetCoalescableTimer");
578 
579 	vs.indicators[SC_INDICATOR_UNKNOWN] = Indicator(INDIC_HIDDEN, ColourDesired(0, 0, 0xff));
580 	vs.indicators[SC_INDICATOR_INPUT] = Indicator(INDIC_DOTS, ColourDesired(0, 0, 0xff));
581 	vs.indicators[SC_INDICATOR_CONVERTED] = Indicator(INDIC_COMPOSITIONTHICK, ColourDesired(0, 0, 0xff));
582 	vs.indicators[SC_INDICATOR_TARGET] = Indicator(INDIC_STRAIGHTBOX, ColourDesired(0, 0, 0xff));
583 }
584 
Finalise()585 void ScintillaWin::Finalise() {
586 	ScintillaBase::Finalise();
587 	for (TickReason tr = tickCaret; tr <= tickDwell; tr = static_cast<TickReason>(tr + 1)) {
588 		FineTickerCancel(tr);
589 	}
590 	SetIdle(false);
591 #if defined(USE_D2D)
592 	DropRenderTarget();
593 #endif
594 	::RevokeDragDrop(MainHWND());
595 	if (SUCCEEDED(hrOle)) {
596 		::OleUninitialize();
597 	}
598 }
599 
600 #if defined(USE_D2D)
601 
EnsureRenderTarget(HDC hdc)602 void ScintillaWin::EnsureRenderTarget(HDC hdc) {
603 	if (!renderTargetValid) {
604 		DropRenderTarget();
605 		renderTargetValid = true;
606 	}
607 	if (pD2DFactory && !pRenderTarget) {
608 		HWND hw = MainHWND();
609 		RECT rc;
610 		GetClientRect(hw, &rc);
611 
612 		const D2D1_SIZE_U size = D2D1::SizeU(rc.right - rc.left, rc.bottom - rc.top);
613 
614 		// Create a Direct2D render target.
615 #if 1
616 		D2D1_RENDER_TARGET_PROPERTIES drtp;
617 		drtp.type = D2D1_RENDER_TARGET_TYPE_DEFAULT;
618 		drtp.pixelFormat.format = DXGI_FORMAT_UNKNOWN;
619 		drtp.pixelFormat.alphaMode = D2D1_ALPHA_MODE_UNKNOWN;
620 		drtp.dpiX = 96.0;
621 		drtp.dpiY = 96.0;
622 		drtp.usage = D2D1_RENDER_TARGET_USAGE_NONE;
623 		drtp.minLevel = D2D1_FEATURE_LEVEL_DEFAULT;
624 
625 		if (technology == SC_TECHNOLOGY_DIRECTWRITEDC) {
626 			// Explicit pixel format needed.
627 			drtp.pixelFormat = D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM,
628 				D2D1_ALPHA_MODE_IGNORE);
629 
630 			ID2D1DCRenderTarget *pDCRT = nullptr;
631 			const HRESULT hr = pD2DFactory->CreateDCRenderTarget(&drtp, &pDCRT);
632 			if (SUCCEEDED(hr)) {
633 				pRenderTarget = pDCRT;
634 			} else {
635 				Platform::DebugPrintf("Failed CreateDCRenderTarget 0x%lx\n", hr);
636 				pRenderTarget = nullptr;
637 			}
638 
639 		} else {
640 			D2D1_HWND_RENDER_TARGET_PROPERTIES dhrtp;
641 			dhrtp.hwnd = hw;
642 			dhrtp.pixelSize = size;
643 			dhrtp.presentOptions = (technology == SC_TECHNOLOGY_DIRECTWRITERETAIN) ?
644 			D2D1_PRESENT_OPTIONS_RETAIN_CONTENTS : D2D1_PRESENT_OPTIONS_NONE;
645 
646 			ID2D1HwndRenderTarget *pHwndRenderTarget = nullptr;
647 			const HRESULT hr = pD2DFactory->CreateHwndRenderTarget(drtp, dhrtp, &pHwndRenderTarget);
648 			if (SUCCEEDED(hr)) {
649 				pRenderTarget = pHwndRenderTarget;
650 			} else {
651 				Platform::DebugPrintf("Failed CreateHwndRenderTarget 0x%lx\n", hr);
652 				pRenderTarget = nullptr;
653 			}
654 		}
655 #else
656 		pD2DFactory->CreateHwndRenderTarget(
657 			D2D1::RenderTargetProperties(
658 				D2D1_RENDER_TARGET_TYPE_DEFAULT ,
659 				D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_PREMULTIPLIED),
660 				96.0f, 96.0f, D2D1_RENDER_TARGET_USAGE_NONE, D2D1_FEATURE_LEVEL_DEFAULT),
661 			D2D1::HwndRenderTargetProperties(hw, size),
662 			&pRenderTarget);
663 #endif
664 		// Pixmaps were created to be compatible with previous render target so
665 		// need to be recreated.
666 		DropGraphics(false);
667 	}
668 
669 	if ((technology == SC_TECHNOLOGY_DIRECTWRITEDC) && pRenderTarget) {
670 		RECT rcWindow;
671 		GetClientRect(MainHWND(), &rcWindow);
672 		const HRESULT hr = static_cast<ID2D1DCRenderTarget*>(pRenderTarget)->BindDC(hdc, &rcWindow);
673 		if (FAILED(hr)) {
674 			Platform::DebugPrintf("BindDC failed 0x%lx\n", hr);
675 			DropRenderTarget();
676 		}
677 	}
678 }
679 
DropRenderTarget()680 void ScintillaWin::DropRenderTarget() {
681 	ReleaseUnknown(pRenderTarget);
682 }
683 
684 #endif
685 
MainHWND() const686 HWND ScintillaWin::MainHWND() const noexcept {
687 	return HwndFromWindow(wMain);
688 }
689 
DisplayCursor(Window::Cursor c)690 void ScintillaWin::DisplayCursor(Window::Cursor c) {
691 	if (cursorMode != SC_CURSORNORMAL) {
692 		c = static_cast<Window::Cursor>(cursorMode);
693 	}
694 	if (c == Window::cursorReverseArrow) {
695 		::SetCursor(reverseArrowCursor.Load(dpi));
696 	} else {
697 		wMain.SetCursor(c);
698 	}
699 }
700 
DragThreshold(Point ptStart,Point ptNow)701 bool ScintillaWin::DragThreshold(Point ptStart, Point ptNow) {
702 	const Point ptDifference = ptStart - ptNow;
703 	const XYPOSITION xMove = std::trunc(std::abs(ptDifference.x));
704 	const XYPOSITION yMove = std::trunc(std::abs(ptDifference.y));
705 	return (xMove > SystemMetricsForDpi(SM_CXDRAG, dpi)) ||
706 		(yMove > SystemMetricsForDpi(SM_CYDRAG, dpi));
707 }
708 
StartDrag()709 void ScintillaWin::StartDrag() {
710 	inDragDrop = ddDragging;
711 	DWORD dwEffect = 0;
712 	dropWentOutside = true;
713 	IDataObject *pDataObject = reinterpret_cast<IDataObject *>(&dob);
714 	IDropSource *pDropSource = reinterpret_cast<IDropSource *>(&ds);
715 	//Platform::DebugPrintf("About to DoDragDrop %x %x\n", pDataObject, pDropSource);
716 	const HRESULT hr = ::DoDragDrop(
717 	                 pDataObject,
718 	                 pDropSource,
719 	                 DROPEFFECT_COPY | DROPEFFECT_MOVE, &dwEffect);
720 	//Platform::DebugPrintf("DoDragDrop = %x\n", hr);
721 	if (SUCCEEDED(hr)) {
722 		if ((hr == DRAGDROP_S_DROP) && (dwEffect == DROPEFFECT_MOVE) && dropWentOutside) {
723 			// Remove dragged out text
724 			ClearSelection();
725 		}
726 	}
727 	inDragDrop = ddNone;
728 	SetDragPosition(SelectionPosition(Sci::invalidPosition));
729 }
730 
MouseModifiers(uptr_t wParam)731 int ScintillaWin::MouseModifiers(uptr_t wParam) noexcept {
732 	return ModifierFlags((wParam & MK_SHIFT) != 0,
733 		(wParam & MK_CONTROL) != 0,
734 		KeyboardIsKeyDown(VK_MENU));
735 }
736 
737 namespace {
738 
InputCodePage()739 int InputCodePage() noexcept {
740 	HKL inputLocale = ::GetKeyboardLayout(0);
741 	const LANGID inputLang = LOWORD(inputLocale);
742 	char sCodePage[10];
743 	const int res = ::GetLocaleInfoA(MAKELCID(inputLang, SORT_DEFAULT),
744 	  LOCALE_IDEFAULTANSICODEPAGE, sCodePage, sizeof(sCodePage));
745 	if (!res)
746 		return 0;
747 	return atoi(sCodePage);
748 }
749 
750 /** Map the key codes to their equivalent SCK_ form. */
KeyTranslate(int keyIn)751 int KeyTranslate(int keyIn) noexcept {
752 //PLATFORM_ASSERT(!keyIn);
753 	switch (keyIn) {
754 		case VK_DOWN:		return SCK_DOWN;
755 		case VK_UP:		return SCK_UP;
756 		case VK_LEFT:		return SCK_LEFT;
757 		case VK_RIGHT:		return SCK_RIGHT;
758 		case VK_HOME:		return SCK_HOME;
759 		case VK_END:		return SCK_END;
760 		case VK_PRIOR:		return SCK_PRIOR;
761 		case VK_NEXT:		return SCK_NEXT;
762 		case VK_DELETE:	return SCK_DELETE;
763 		case VK_INSERT:		return SCK_INSERT;
764 		case VK_ESCAPE:	return SCK_ESCAPE;
765 		case VK_BACK:		return SCK_BACK;
766 		case VK_TAB:		return SCK_TAB;
767 		case VK_RETURN:	return SCK_RETURN;
768 		case VK_ADD:		return SCK_ADD;
769 		case VK_SUBTRACT:	return SCK_SUBTRACT;
770 		case VK_DIVIDE:		return SCK_DIVIDE;
771 		case VK_LWIN:		return SCK_WIN;
772 		case VK_RWIN:		return SCK_RWIN;
773 		case VK_APPS:		return SCK_MENU;
774 		case VK_OEM_2:		return '/';
775 		case VK_OEM_3:		return '`';
776 		case VK_OEM_4:		return '[';
777 		case VK_OEM_5:		return '\\';
778 		case VK_OEM_6:		return ']';
779 		default:			return keyIn;
780 	}
781 }
782 
BoundsContains(PRectangle rcBounds,HRGN hRgnBounds,PRectangle rcCheck)783 bool BoundsContains(PRectangle rcBounds, HRGN hRgnBounds, PRectangle rcCheck) noexcept {
784 	bool contains = true;
785 	if (!rcCheck.Empty()) {
786 		if (!rcBounds.Contains(rcCheck)) {
787 			contains = false;
788 		} else if (hRgnBounds) {
789 			// In bounding rectangle so check more accurately using region
790 			const RECT rcw = RectFromPRectangle(rcCheck);
791 			HRGN hRgnCheck = ::CreateRectRgnIndirect(&rcw);
792 			if (hRgnCheck) {
793 				HRGN hRgnDifference = ::CreateRectRgn(0, 0, 0, 0);
794 				if (hRgnDifference) {
795 					const int combination = ::CombineRgn(hRgnDifference, hRgnCheck, hRgnBounds, RGN_DIFF);
796 					if (combination != NULLREGION) {
797 						contains = false;
798 					}
799 					::DeleteRgn(hRgnDifference);
800 				}
801 				::DeleteRgn(hRgnCheck);
802 			}
803 		}
804 	}
805 	return contains;
806 }
807 
808 // Simplify calling WideCharToMultiByte and MultiByteToWideChar by providing default parameters and using string view.
809 
MultiByteFromWideChar(UINT codePage,std::wstring_view wsv,LPSTR lpMultiByteStr,ptrdiff_t cbMultiByte)810 int MultiByteFromWideChar(UINT codePage, std::wstring_view wsv, LPSTR lpMultiByteStr, ptrdiff_t cbMultiByte) noexcept {
811 	return ::WideCharToMultiByte(codePage, 0, wsv.data(), static_cast<int>(wsv.length()), lpMultiByteStr, static_cast<int>(cbMultiByte), nullptr, nullptr);
812 }
813 
MultiByteLenFromWideChar(UINT codePage,std::wstring_view wsv)814 int MultiByteLenFromWideChar(UINT codePage, std::wstring_view wsv) noexcept {
815 	return MultiByteFromWideChar(codePage, wsv, nullptr, 0);
816 }
817 
WideCharFromMultiByte(UINT codePage,std::string_view sv,LPWSTR lpWideCharStr,ptrdiff_t cchWideChar)818 int WideCharFromMultiByte(UINT codePage, std::string_view sv, LPWSTR lpWideCharStr, ptrdiff_t cchWideChar) noexcept {
819 	return ::MultiByteToWideChar(codePage, 0, sv.data(), static_cast<int>(sv.length()), lpWideCharStr, static_cast<int>(cchWideChar));
820 }
821 
WideCharLenFromMultiByte(UINT codePage,std::string_view sv)822 int WideCharLenFromMultiByte(UINT codePage, std::string_view sv) noexcept {
823 	return WideCharFromMultiByte(codePage, sv, nullptr, 0);
824 }
825 
StringEncode(std::wstring_view wsv,int codePage)826 std::string StringEncode(std::wstring_view wsv, int codePage) {
827 	const int cchMulti = wsv.length() ? MultiByteLenFromWideChar(codePage, wsv) : 0;
828 	std::string sMulti(cchMulti, 0);
829 	if (cchMulti) {
830 		MultiByteFromWideChar(codePage, wsv, sMulti.data(), cchMulti);
831 	}
832 	return sMulti;
833 }
834 
StringDecode(std::string_view sv,int codePage)835 std::wstring StringDecode(std::string_view sv, int codePage) {
836 	const int cchWide = sv.length() ? WideCharLenFromMultiByte(codePage, sv) : 0;
837 	std::wstring sWide(cchWide, 0);
838 	if (cchWide) {
839 		WideCharFromMultiByte(codePage, sv, sWide.data(), cchWide);
840 	}
841 	return sWide;
842 }
843 
StringMapCase(std::wstring_view wsv,DWORD mapFlags)844 std::wstring StringMapCase(std::wstring_view wsv, DWORD mapFlags) {
845 	const int charsConverted = ::LCMapStringW(LOCALE_SYSTEM_DEFAULT, mapFlags,
846 		wsv.data(), static_cast<int>(wsv.length()), nullptr, 0);
847 	std::wstring wsConverted(charsConverted, 0);
848 	if (charsConverted) {
849 		::LCMapStringW(LOCALE_SYSTEM_DEFAULT, mapFlags,
850 			wsv.data(), static_cast<int>(wsv.length()), wsConverted.data(), charsConverted);
851 	}
852 	return wsConverted;
853 }
854 
855 }
856 
857 // Returns the target converted to UTF8.
858 // Return the length in bytes.
TargetAsUTF8(char * text) const859 Sci::Position ScintillaWin::TargetAsUTF8(char *text) const {
860 	const Sci::Position targetLength = targetRange.Length();
861 	if (IsUnicodeMode()) {
862 		if (text) {
863 			pdoc->GetCharRange(text, targetRange.start.Position(), targetLength);
864 		}
865 	} else {
866 		// Need to convert
867 		const std::string s = RangeText(targetRange.start.Position(), targetRange.end.Position());
868 		const std::wstring characters = StringDecode(s, CodePageOfDocument());
869 		const int utf8Len = MultiByteLenFromWideChar(CP_UTF8, characters);
870 		if (text) {
871 			MultiByteFromWideChar(CP_UTF8, characters, text, utf8Len);
872 			text[utf8Len] = '\0';
873 		}
874 		return utf8Len;
875 	}
876 	return targetLength;
877 }
878 
879 // Translates a nul terminated UTF8 string into the document encoding.
880 // Return the length of the result in bytes.
EncodedFromUTF8(const char * utf8,char * encoded) const881 Sci::Position ScintillaWin::EncodedFromUTF8(const char *utf8, char *encoded) const {
882 	const Sci::Position inputLength = (lengthForEncode >= 0) ? lengthForEncode : strlen(utf8);
883 	if (IsUnicodeMode()) {
884 		if (encoded) {
885 			memcpy(encoded, utf8, inputLength);
886 		}
887 		return inputLength;
888 	} else {
889 		// Need to convert
890 		const std::string_view utf8Input(utf8, inputLength);
891 		const int charsLen = WideCharLenFromMultiByte(CP_UTF8, utf8Input);
892 		std::wstring characters(charsLen, L'\0');
893 		WideCharFromMultiByte(CP_UTF8, utf8Input, &characters[0], charsLen);
894 
895 		const int encodedLen = MultiByteLenFromWideChar(CodePageOfDocument(), characters);
896 		if (encoded) {
897 			MultiByteFromWideChar(CodePageOfDocument(), characters, encoded, encodedLen);
898 			encoded[encodedLen] = '\0';
899 		}
900 		return encodedLen;
901 	}
902 }
903 
PaintDC(HDC hdc)904 bool ScintillaWin::PaintDC(HDC hdc) {
905 	if (technology == SC_TECHNOLOGY_DEFAULT) {
906 		AutoSurface surfaceWindow(hdc, this);
907 		if (surfaceWindow) {
908 			Paint(surfaceWindow, rcPaint);
909 			surfaceWindow->Release();
910 		}
911 	} else {
912 #if defined(USE_D2D)
913 		EnsureRenderTarget(hdc);
914 		if (pRenderTarget) {
915 			AutoSurface surfaceWindow(pRenderTarget, this);
916 			if (surfaceWindow) {
917 				pRenderTarget->BeginDraw();
918 				Paint(surfaceWindow, rcPaint);
919 				surfaceWindow->Release();
920 				const HRESULT hr = pRenderTarget->EndDraw();
921 				if (hr == static_cast<HRESULT>(D2DERR_RECREATE_TARGET)) {
922 					DropRenderTarget();
923 					return false;
924 				}
925 			}
926 		}
927 #endif
928 	}
929 
930 	return true;
931 }
932 
WndPaint()933 sptr_t ScintillaWin::WndPaint() {
934 	//ElapsedPeriod ep;
935 
936 	// Redirect assertions to debug output and save current state
937 	const bool assertsPopup = Platform::ShowAssertionPopUps(false);
938 	paintState = painting;
939 	PAINTSTRUCT ps = {};
940 
941 	// Removed since this interferes with reporting other assertions as it occurs repeatedly
942 	//PLATFORM_ASSERT(hRgnUpdate == NULL);
943 	hRgnUpdate = ::CreateRectRgn(0, 0, 0, 0);
944 	::GetUpdateRgn(MainHWND(), hRgnUpdate, FALSE);
945 	::BeginPaint(MainHWND(), &ps);
946 	rcPaint = PRectangle::FromInts(ps.rcPaint.left, ps.rcPaint.top, ps.rcPaint.right, ps.rcPaint.bottom);
947 	const PRectangle rcClient = GetClientRectangle();
948 	paintingAllText = BoundsContains(rcPaint, hRgnUpdate, rcClient);
949 	if (!PaintDC(ps.hdc)) {
950 		paintState = paintAbandoned;
951 	}
952 	if (hRgnUpdate) {
953 		::DeleteRgn(hRgnUpdate);
954 		hRgnUpdate = {};
955 	}
956 
957 	::EndPaint(MainHWND(), &ps);
958 	if (paintState == paintAbandoned) {
959 		// Painting area was insufficient to cover new styling or brace highlight positions
960 		FullPaint();
961 		::ValidateRect(MainHWND(), nullptr);
962 	}
963 	paintState = notPainting;
964 
965 	// Restore debug output state
966 	Platform::ShowAssertionPopUps(assertsPopup);
967 
968 	//Platform::DebugPrintf("Paint took %g\n", ep.Duration());
969 	return 0;
970 }
971 
HandleCompositionWindowed(uptr_t wParam,sptr_t lParam)972 sptr_t ScintillaWin::HandleCompositionWindowed(uptr_t wParam, sptr_t lParam) {
973 	if (lParam & GCS_RESULTSTR) {
974 		IMContext imc(MainHWND());
975 		if (imc.hIMC) {
976 			AddWString(imc.GetCompositionString(GCS_RESULTSTR), CharacterSource::imeResult);
977 
978 			// Set new position after converted
979 			const Point pos = PointMainCaret();
980 			COMPOSITIONFORM CompForm;
981 			CompForm.dwStyle = CFS_POINT;
982 			CompForm.ptCurrentPos = POINTFromPoint(pos);
983 			::ImmSetCompositionWindow(imc.hIMC, &CompForm);
984 		}
985 		return 0;
986 	}
987 	return ::DefWindowProc(MainHWND(), WM_IME_COMPOSITION, wParam, lParam);
988 }
989 
KoreanIME()990 bool ScintillaWin::KoreanIME() noexcept {
991 	const int codePage = InputCodePage();
992 	return codePage == 949 || codePage == 1361;
993 }
994 
MoveImeCarets(Sci::Position offset)995 void ScintillaWin::MoveImeCarets(Sci::Position offset) {
996 	// Move carets relatively by bytes.
997 	for (size_t r=0; r<sel.Count(); r++) {
998 		const Sci::Position positionInsert = sel.Range(r).Start().Position();
999 		sel.Range(r).caret.SetPosition(positionInsert + offset);
1000 		sel.Range(r).anchor.SetPosition(positionInsert + offset);
1001 	}
1002 }
1003 
DrawImeIndicator(int indicator,Sci::Position len)1004 void ScintillaWin::DrawImeIndicator(int indicator, Sci::Position len) {
1005 	// Emulate the visual style of IME characters with indicators.
1006 	// Draw an indicator on the character before caret by the character bytes of len
1007 	// so it should be called after InsertCharacter().
1008 	// It does not affect caret positions.
1009 	if (indicator < 8 || indicator > INDICATOR_MAX) {
1010 		return;
1011 	}
1012 	pdoc->DecorationSetCurrentIndicator(indicator);
1013 	for (size_t r=0; r<sel.Count(); r++) {
1014 		const Sci::Position positionInsert = sel.Range(r).Start().Position();
1015 		pdoc->DecorationFillRange(positionInsert - len, 1, len);
1016 	}
1017 }
1018 
SetCandidateWindowPos()1019 void ScintillaWin::SetCandidateWindowPos() {
1020 	IMContext imc(MainHWND());
1021 	if (imc.hIMC) {
1022 		const Point pos = PointMainCaret();
1023 		const PRectangle rcClient = GetTextRectangle();
1024 		CANDIDATEFORM CandForm{};
1025 		CandForm.dwIndex = 0;
1026 		CandForm.dwStyle = CFS_EXCLUDE;
1027 		CandForm.ptCurrentPos.x = static_cast<int>(pos.x);
1028 		CandForm.ptCurrentPos.y = static_cast<int>(pos.y + std::max(4, vs.lineHeight/4));
1029 		// Exclude the area of the whole caret line
1030 		CandForm.rcArea.top = static_cast<int>(pos.y);
1031 		CandForm.rcArea.bottom = static_cast<int>(pos.y + vs.lineHeight);
1032 		CandForm.rcArea.left = static_cast<int>(rcClient.left);
1033 		CandForm.rcArea.right = static_cast<int>(rcClient.right);
1034 		::ImmSetCandidateWindow(imc.hIMC, &CandForm);
1035 	}
1036 }
1037 
SelectionToHangul()1038 void ScintillaWin::SelectionToHangul() {
1039 	// Convert every hanja to hangul within the main range.
1040 	const Sci::Position selStart = sel.RangeMain().Start().Position();
1041 	const Sci::Position documentStrLen = sel.RangeMain().Length();
1042 	const Sci::Position selEnd = selStart + documentStrLen;
1043 	const Sci::Position utf16Len = pdoc->CountUTF16(selStart, selEnd);
1044 
1045 	if (utf16Len > 0) {
1046 		std::string documentStr(documentStrLen, '\0');
1047 		pdoc->GetCharRange(&documentStr[0], selStart, documentStrLen);
1048 
1049 		std::wstring uniStr = StringDecode(documentStr, CodePageOfDocument());
1050 		const int converted = HanjaDict::GetHangulOfHanja(&uniStr[0]);
1051 		documentStr = StringEncode(uniStr, CodePageOfDocument());
1052 
1053 		if (converted > 0) {
1054 			pdoc->BeginUndoAction();
1055 			ClearSelection();
1056 			InsertPaste(&documentStr[0], documentStr.size());
1057 			pdoc->EndUndoAction();
1058 		}
1059 	}
1060 }
1061 
EscapeHanja()1062 void ScintillaWin::EscapeHanja() {
1063 	// The candidate box pops up to user to select a hanja.
1064 	// It comes into WM_IME_COMPOSITION with GCS_RESULTSTR.
1065 	// The existing hangul or hanja is replaced with it.
1066 	if (sel.Count() > 1) {
1067 		return; // Do not allow multi carets.
1068 	}
1069 	const Sci::Position currentPos = CurrentPosition();
1070 	const int oneCharLen = pdoc->LenChar(currentPos);
1071 
1072 	if (oneCharLen < 2) {
1073 		return; // No need to handle SBCS.
1074 	}
1075 
1076 	// ImmEscapeW() may overwrite uniChar[] with a null terminated string.
1077 	// So enlarge it enough to Maximum 4 as in UTF-8.
1078 	constexpr size_t safeLength = UTF8MaxBytes + 1;
1079 	std::string oneChar(safeLength, '\0');
1080 	pdoc->GetCharRange(&oneChar[0], currentPos, oneCharLen);
1081 
1082 	std::wstring uniChar = StringDecode(oneChar, CodePageOfDocument());
1083 
1084 	IMContext imc(MainHWND());
1085 	if (imc.hIMC) {
1086 		// Set the candidate box position since IME may show it.
1087 		SetCandidateWindowPos();
1088 		// IME_ESC_HANJA_MODE appears to receive the first character only.
1089 		if (::ImmEscapeW(GetKeyboardLayout(0), imc.hIMC, IME_ESC_HANJA_MODE, &uniChar[0])) {
1090 			SetSelection(currentPos, currentPos + oneCharLen);
1091 		}
1092 	}
1093 }
1094 
ToggleHanja()1095 void ScintillaWin::ToggleHanja() {
1096 	// If selection, convert every hanja to hangul within the main range.
1097 	// If no selection, commit to IME.
1098 	if (sel.Count() > 1) {
1099 		return; // Do not allow multi carets.
1100 	}
1101 
1102 	if (sel.Empty()) {
1103 		EscapeHanja();
1104 	} else {
1105 		SelectionToHangul();
1106 	}
1107 }
1108 
1109 namespace {
1110 
MapImeIndicators(std::vector<BYTE> inputStyle)1111 std::vector<int> MapImeIndicators(std::vector<BYTE> inputStyle) {
1112 	std::vector<int> imeIndicator(inputStyle.size(), SC_INDICATOR_UNKNOWN);
1113 	for (size_t i = 0; i < inputStyle.size(); i++) {
1114 		switch (static_cast<int>(inputStyle.at(i))) {
1115 		case ATTR_INPUT:
1116 			imeIndicator[i] = SC_INDICATOR_INPUT;
1117 			break;
1118 		case ATTR_TARGET_NOTCONVERTED:
1119 		case ATTR_TARGET_CONVERTED:
1120 			imeIndicator[i] = SC_INDICATOR_TARGET;
1121 			break;
1122 		case ATTR_CONVERTED:
1123 			imeIndicator[i] = SC_INDICATOR_CONVERTED;
1124 			break;
1125 		default:
1126 			imeIndicator[i] = SC_INDICATOR_UNKNOWN;
1127 			break;
1128 		}
1129 	}
1130 	return imeIndicator;
1131 }
1132 
1133 }
1134 
AddWString(std::wstring_view wsv,CharacterSource charSource)1135 void ScintillaWin::AddWString(std::wstring_view wsv, CharacterSource charSource) {
1136 	if (wsv.empty())
1137 		return;
1138 
1139 	const int codePage = CodePageOfDocument();
1140 	for (size_t i = 0; i < wsv.size(); ) {
1141 		const size_t ucWidth = UTF16CharLength(wsv[i]);
1142 		const std::string docChar = StringEncode(wsv.substr(i, ucWidth), codePage);
1143 
1144 		InsertCharacter(docChar, charSource);
1145 		i += ucWidth;
1146 	}
1147 }
1148 
HandleCompositionInline(uptr_t,sptr_t lParam)1149 sptr_t ScintillaWin::HandleCompositionInline(uptr_t, sptr_t lParam) {
1150 	// Copy & paste by johnsonj with a lot of helps of Neil.
1151 	// Great thanks for my foreruners, jiniya and BLUEnLIVE.
1152 
1153 	IMContext imc(MainHWND());
1154 	if (!imc.hIMC)
1155 		return 0;
1156 	if (pdoc->IsReadOnly() || SelectionContainsProtected()) {
1157 		::ImmNotifyIME(imc.hIMC, NI_COMPOSITIONSTR, CPS_CANCEL, 0);
1158 		return 0;
1159 	}
1160 
1161 	bool initialCompose = false;
1162 	if (pdoc->TentativeActive()) {
1163 		pdoc->TentativeUndo();
1164 	} else {
1165 		// No tentative undo means start of this composition so
1166 		// fill in any virtual spaces.
1167 		initialCompose = true;
1168 	}
1169 
1170 	view.imeCaretBlockOverride = false;
1171 
1172 	if (lParam & GCS_RESULTSTR) {
1173 		AddWString(imc.GetCompositionString(GCS_RESULTSTR), CharacterSource::imeResult);
1174 	}
1175 
1176 	if (lParam & GCS_COMPSTR) {
1177 		const std::wstring wcs = imc.GetCompositionString(GCS_COMPSTR);
1178 		if (wcs.empty()) {
1179 			ShowCaretAtCurrentPosition();
1180 			return 0;
1181 		}
1182 
1183 		if (initialCompose) {
1184 			ClearBeforeTentativeStart();
1185 		}
1186 
1187 		// Set candidate window left aligned to beginning of preedit string.
1188 		SetCandidateWindowPos();
1189 		pdoc->TentativeStart(); // TentativeActive from now on.
1190 
1191 		std::vector<int> imeIndicator = MapImeIndicators(imc.GetImeAttributes());
1192 
1193 		const int codePage = CodePageOfDocument();
1194 		const std::wstring_view wsv = wcs;
1195 		for (size_t i = 0; i < wsv.size(); ) {
1196 			const size_t ucWidth = UTF16CharLength(wsv[i]);
1197 			const std::string docChar = StringEncode(wsv.substr(i, ucWidth), codePage);
1198 
1199 			InsertCharacter(docChar, CharacterSource::tentativeInput);
1200 
1201 			DrawImeIndicator(imeIndicator[i], docChar.size());
1202 			i += ucWidth;
1203 		}
1204 
1205 		// Japanese IME after pressing Tab replaces input string with first candidate item (target string);
1206 		// when selecting other candidate item, previous item will be replaced with current one.
1207 		// After candidate item been added, it's looks like been full selected, it's better to keep caret
1208 		// at end of "selection" (end of input) instead of jump to beginning of input ("selection").
1209 		const bool onlyTarget = std::all_of(imeIndicator.begin(), imeIndicator.end(), [](int i) noexcept {
1210 			return i == SC_INDICATOR_TARGET;
1211 		});
1212 		if (!onlyTarget) {
1213 			// CS_NOMOVECARET: keep caret at beginning of composition string which already moved in InsertCharacter().
1214 			// GCS_CURSORPOS: current caret position is provided by IME.
1215 			Sci::Position imeEndToImeCaretU16 = -static_cast<Sci::Position>(wcs.size());
1216 			if (!(lParam & CS_NOMOVECARET) && (lParam & GCS_CURSORPOS)) {
1217 				imeEndToImeCaretU16 += imc.GetImeCaretPos();
1218 			}
1219 			if (imeEndToImeCaretU16 != 0) {
1220 				// Move back IME caret from current last position to imeCaretPos.
1221 				const Sci::Position currentPos = CurrentPosition();
1222 				const Sci::Position imeCaretPosDoc = pdoc->GetRelativePositionUTF16(currentPos, imeEndToImeCaretU16);
1223 
1224 				MoveImeCarets(-currentPos + imeCaretPosDoc);
1225 
1226 				if (std::find(imeIndicator.begin(), imeIndicator.end(), SC_INDICATOR_TARGET) != imeIndicator.end()) {
1227 					// set candidate window left aligned to beginning of target string.
1228 					SetCandidateWindowPos();
1229 				}
1230 			}
1231 		}
1232 
1233 		if (KoreanIME()) {
1234 			view.imeCaretBlockOverride = true;
1235 		}
1236 	}
1237 	EnsureCaretVisible();
1238 	ShowCaretAtCurrentPosition();
1239 	return 0;
1240 }
1241 
1242 namespace {
1243 
1244 // Translate message IDs from WM_* and EM_* to SCI_* so can partly emulate Windows Edit control
SciMessageFromEM(unsigned int iMessage)1245 unsigned int SciMessageFromEM(unsigned int iMessage) noexcept {
1246 	switch (iMessage) {
1247 	case EM_CANPASTE: return SCI_CANPASTE;
1248 	case EM_CANUNDO: return SCI_CANUNDO;
1249 	case EM_EMPTYUNDOBUFFER: return SCI_EMPTYUNDOBUFFER;
1250 	case EM_FINDTEXTEX: return SCI_FINDTEXT;
1251 	case EM_FORMATRANGE: return SCI_FORMATRANGE;
1252 	case EM_GETFIRSTVISIBLELINE: return SCI_GETFIRSTVISIBLELINE;
1253 	case EM_GETLINECOUNT: return SCI_GETLINECOUNT;
1254 	case EM_GETSELTEXT: return SCI_GETSELTEXT;
1255 	case EM_GETTEXTRANGE: return SCI_GETTEXTRANGE;
1256 	case EM_HIDESELECTION: return SCI_HIDESELECTION;
1257 	case EM_LINEINDEX: return SCI_POSITIONFROMLINE;
1258 	case EM_LINESCROLL: return SCI_LINESCROLL;
1259 	case EM_REPLACESEL: return SCI_REPLACESEL;
1260 	case EM_SCROLLCARET: return SCI_SCROLLCARET;
1261 	case EM_SETREADONLY: return SCI_SETREADONLY;
1262 	case WM_CLEAR: return SCI_CLEAR;
1263 	case WM_COPY: return SCI_COPY;
1264 	case WM_CUT: return SCI_CUT;
1265 	case WM_SETTEXT: return SCI_SETTEXT;
1266 	case WM_PASTE: return SCI_PASTE;
1267 	case WM_UNDO: return SCI_UNDO;
1268 	}
1269 	return iMessage;
1270 }
1271 
1272 }
1273 
1274 namespace Scintilla {
1275 
CodePageFromCharSet(DWORD characterSet,UINT documentCodePage)1276 UINT CodePageFromCharSet(DWORD characterSet, UINT documentCodePage) noexcept {
1277 	if (documentCodePage == SC_CP_UTF8) {
1278 		return SC_CP_UTF8;
1279 	}
1280 	switch (characterSet) {
1281 	case SC_CHARSET_ANSI: return 1252;
1282 	case SC_CHARSET_DEFAULT: return documentCodePage ? documentCodePage : 1252;
1283 	case SC_CHARSET_BALTIC: return 1257;
1284 	case SC_CHARSET_CHINESEBIG5: return 950;
1285 	case SC_CHARSET_EASTEUROPE: return 1250;
1286 	case SC_CHARSET_GB2312: return 936;
1287 	case SC_CHARSET_GREEK: return 1253;
1288 	case SC_CHARSET_HANGUL: return 949;
1289 	case SC_CHARSET_MAC: return 10000;
1290 	case SC_CHARSET_OEM: return 437;
1291 	case SC_CHARSET_RUSSIAN: return 1251;
1292 	case SC_CHARSET_SHIFTJIS: return 932;
1293 	case SC_CHARSET_TURKISH: return 1254;
1294 	case SC_CHARSET_JOHAB: return 1361;
1295 	case SC_CHARSET_HEBREW: return 1255;
1296 	case SC_CHARSET_ARABIC: return 1256;
1297 	case SC_CHARSET_VIETNAMESE: return 1258;
1298 	case SC_CHARSET_THAI: return 874;
1299 	case SC_CHARSET_8859_15: return 28605;
1300 	// Not supported
1301 	case SC_CHARSET_CYRILLIC: return documentCodePage;
1302 	case SC_CHARSET_SYMBOL: return documentCodePage;
1303 	}
1304 	return documentCodePage;
1305 }
1306 
1307 }
1308 
CodePageOfDocument() const1309 UINT ScintillaWin::CodePageOfDocument() const noexcept {
1310 	return CodePageFromCharSet(vs.styles[STYLE_DEFAULT].characterSet, pdoc->dbcsCodePage);
1311 }
1312 
EncodeWString(std::wstring_view wsv)1313 std::string ScintillaWin::EncodeWString(std::wstring_view wsv) {
1314 	if (IsUnicodeMode()) {
1315 		const size_t len = UTF8Length(wsv);
1316 		std::string putf(len, 0);
1317 		UTF8FromUTF16(wsv, putf.data(), len);
1318 		return putf;
1319 	} else {
1320 		// Not in Unicode mode so convert from Unicode to current Scintilla code page
1321 		return StringEncode(wsv, CodePageOfDocument());
1322 	}
1323 }
1324 
GetTextLength()1325 sptr_t ScintillaWin::GetTextLength() {
1326 	return pdoc->CountUTF16(0, pdoc->Length());
1327 }
1328 
GetText(uptr_t wParam,sptr_t lParam)1329 sptr_t ScintillaWin::GetText(uptr_t wParam, sptr_t lParam) {
1330 	if (lParam == 0) {
1331 		return pdoc->CountUTF16(0, pdoc->Length());
1332 	}
1333 	if (wParam == 0) {
1334 		return 0;
1335 	}
1336 	wchar_t *ptr = static_cast<wchar_t *>(PtrFromSPtr(lParam));
1337 	if (pdoc->Length() == 0) {
1338 		*ptr = L'\0';
1339 		return 0;
1340 	}
1341 	const Sci::Position lengthWanted = wParam - 1;
1342 	Sci::Position sizeRequestedRange = pdoc->GetRelativePositionUTF16(0, lengthWanted);
1343 	if (sizeRequestedRange < 0) {
1344 		// Requested more text than there is in the document.
1345 		sizeRequestedRange = pdoc->Length();
1346 	}
1347 	std::string docBytes(sizeRequestedRange, '\0');
1348 	pdoc->GetCharRange(&docBytes[0], 0, sizeRequestedRange);
1349 	if (IsUnicodeMode()) {
1350 		const size_t uLen = UTF16FromUTF8(docBytes, ptr, lengthWanted);
1351 		ptr[uLen] = L'\0';
1352 		return uLen;
1353 	} else {
1354 		// Not Unicode mode
1355 		// Convert to Unicode using the current Scintilla code page
1356 		const UINT cpSrc = CodePageOfDocument();
1357 		int lengthUTF16 = WideCharLenFromMultiByte(cpSrc, docBytes);
1358 		if (lengthUTF16 > lengthWanted)
1359 			lengthUTF16 = static_cast<int>(lengthWanted);
1360 		WideCharFromMultiByte(cpSrc, docBytes, ptr, lengthUTF16);
1361 		ptr[lengthUTF16] = L'\0';
1362 		return lengthUTF16;
1363 	}
1364 }
1365 
ContextCursor(Point pt)1366 Window::Cursor ScintillaWin::ContextCursor(Point pt) {
1367 	if (inDragDrop == ddDragging) {
1368 		return Window::cursorUp;
1369 	} else {
1370 		// Display regular (drag) cursor over selection
1371 		if (PointInSelMargin(pt)) {
1372 			return GetMarginCursor(pt);
1373 		} else if (!SelectionEmpty() && PointInSelection(pt)) {
1374 			return Window::cursorArrow;
1375 		} else if (PointIsHotspot(pt)) {
1376 			return Window::cursorHand;
1377 		} else if (hoverIndicatorPos != Sci::invalidPosition) {
1378 			const Sci::Position pos = PositionFromLocation(pt, true, true);
1379 			if (pos != Sci::invalidPosition) {
1380 				return Window::cursorHand;
1381 			}
1382 		}
1383 	}
1384 	return Window::cursorText;
1385 }
1386 
ShowContextMenu(unsigned int iMessage,uptr_t wParam,sptr_t lParam)1387 sptr_t ScintillaWin::ShowContextMenu(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
1388 	Point pt = PointFromLParam(lParam);
1389 	POINT rpt = POINTFromPoint(pt);
1390 	::ScreenToClient(MainHWND(), &rpt);
1391 	const Point ptClient = PointFromPOINT(rpt);
1392 	if (ShouldDisplayPopup(ptClient)) {
1393 		if ((pt.x == -1) && (pt.y == -1)) {
1394 			// Caused by keyboard so display menu near caret
1395 			pt = PointMainCaret();
1396 			POINT spt = POINTFromPoint(pt);
1397 			::ClientToScreen(MainHWND(), &spt);
1398 			pt = PointFromPOINT(spt);
1399 		}
1400 		ContextMenu(pt);
1401 		return 0;
1402 	}
1403 	return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1404 }
1405 
SizeWindow()1406 void ScintillaWin::SizeWindow() {
1407 #if defined(USE_D2D)
1408 	if (paintState == notPainting) {
1409 		DropRenderTarget();
1410 	} else {
1411 		renderTargetValid = false;
1412 	}
1413 #endif
1414 	//Platform::DebugPrintf("Scintilla WM_SIZE %d %d\n", LOWORD(lParam), HIWORD(lParam));
1415 	ChangeSize();
1416 }
1417 
MouseMessage(unsigned int iMessage,uptr_t wParam,sptr_t lParam)1418 sptr_t ScintillaWin::MouseMessage(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
1419 	switch (iMessage) {
1420 	case WM_LBUTTONDOWN: {
1421 			// For IME, set the composition string as the result string.
1422 			IMContext imc(MainHWND());
1423 			if (imc.hIMC) {
1424 				::ImmNotifyIME(imc.hIMC, NI_COMPOSITIONSTR, CPS_COMPLETE, 0);
1425 			}
1426 			//
1427 			//Platform::DebugPrintf("Buttdown %d %x %x %x %x %x\n",iMessage, wParam, lParam,
1428 			//	KeyboardIsKeyDown(VK_SHIFT),
1429 			//	KeyboardIsKeyDown(VK_CONTROL),
1430 			//	KeyboardIsKeyDown(VK_MENU));
1431 			::SetFocus(MainHWND());
1432 			ButtonDownWithModifiers(PointFromLParam(lParam), ::GetMessageTime(),
1433 						MouseModifiers(wParam));
1434 		}
1435 		break;
1436 
1437 	case WM_LBUTTONUP:
1438 		ButtonUpWithModifiers(PointFromLParam(lParam),
1439 				      ::GetMessageTime(), MouseModifiers(wParam));
1440 		break;
1441 
1442 	case WM_RBUTTONDOWN: {
1443 			::SetFocus(MainHWND());
1444 			const Point pt = PointFromLParam(lParam);
1445 			if (!PointInSelection(pt)) {
1446 				CancelModes();
1447 				SetEmptySelection(PositionFromLocation(PointFromLParam(lParam)));
1448 			}
1449 
1450 			RightButtonDownWithModifiers(pt, ::GetMessageTime(), MouseModifiers(wParam));
1451 		}
1452 		break;
1453 
1454 	case WM_MOUSEMOVE: {
1455 			const Point pt = PointFromLParam(lParam);
1456 
1457 			// Windows might send WM_MOUSEMOVE even though the mouse has not been moved:
1458 			// http://blogs.msdn.com/b/oldnewthing/archive/2003/10/01/55108.aspx
1459 			if (ptMouseLast != pt) {
1460 				SetTrackMouseLeaveEvent(true);
1461 				ButtonMoveWithModifiers(pt, ::GetMessageTime(), MouseModifiers(wParam));
1462 			}
1463 		}
1464 		break;
1465 
1466 	case WM_MOUSELEAVE:
1467 		SetTrackMouseLeaveEvent(false);
1468 		MouseLeave();
1469 		return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1470 
1471 	case WM_MOUSEWHEEL:
1472 		if (!mouseWheelCaptures) {
1473 			// if the mouse wheel is not captured, test if the mouse
1474 			// pointer is over the editor window and if not, don't
1475 			// handle the message but pass it on.
1476 			RECT rc;
1477 			GetWindowRect(MainHWND(), &rc);
1478 			const POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
1479 			if (!PtInRect(&rc, pt))
1480 				return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1481 		}
1482 		// if autocomplete list active then send mousewheel message to it
1483 		if (ac.Active()) {
1484 			HWND hWnd = HwndFromWindow(*(ac.lb));
1485 			::SendMessage(hWnd, iMessage, wParam, lParam);
1486 			break;
1487 		}
1488 
1489 		// Don't handle datazoom.
1490 		// (A good idea for datazoom would be to "fold" or "unfold" details.
1491 		// i.e. if datazoomed out only class structures are visible, when datazooming in the control
1492 		// structures appear, then eventually the individual statements...)
1493 		if (wParam & MK_SHIFT) {
1494 			return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1495 		}
1496 		// Either SCROLL or ZOOM. We handle the wheel steppings calculation
1497 		wheelDelta -= GET_WHEEL_DELTA_WPARAM(wParam);
1498 		if (std::abs(wheelDelta) >= WHEEL_DELTA && linesPerScroll > 0) {
1499 			Sci::Line linesToScroll = linesPerScroll;
1500 			if (linesPerScroll == WHEEL_PAGESCROLL)
1501 				linesToScroll = LinesOnScreen() - 1;
1502 			if (linesToScroll == 0) {
1503 				linesToScroll = 1;
1504 			}
1505 			linesToScroll *= (wheelDelta / WHEEL_DELTA);
1506 			if (wheelDelta >= 0)
1507 				wheelDelta = wheelDelta % WHEEL_DELTA;
1508 			else
1509 				wheelDelta = -(-wheelDelta % WHEEL_DELTA);
1510 
1511 			if (wParam & MK_CONTROL) {
1512 				// Zoom! We play with the font sizes in the styles.
1513 				// Number of steps/line is ignored, we just care if sizing up or down
1514 				if (linesToScroll < 0) {
1515 					KeyCommand(SCI_ZOOMIN);
1516 				} else {
1517 					KeyCommand(SCI_ZOOMOUT);
1518 				}
1519 			} else {
1520 				// Scroll
1521 				ScrollTo(topLine + linesToScroll);
1522 			}
1523 		}
1524 		return 0;
1525 	}
1526 	return 0;
1527 }
1528 
KeyMessage(unsigned int iMessage,uptr_t wParam,sptr_t lParam)1529 sptr_t ScintillaWin::KeyMessage(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
1530 	switch (iMessage) {
1531 
1532 	case WM_SYSKEYDOWN:
1533 	case WM_KEYDOWN: {
1534 			// Platform::DebugPrintf("Keydown %c %c%c%c%c %x %x\n",
1535 			// iMessage == WM_KEYDOWN ? 'K' : 'S',
1536 			// (lParam & (1 << 24)) ? 'E' : '-',
1537 			// KeyboardIsKeyDown(VK_SHIFT) ? 'S' : '-',
1538 			// KeyboardIsKeyDown(VK_CONTROL) ? 'C' : '-',
1539 			// KeyboardIsKeyDown(VK_MENU) ? 'A' : '-',
1540 			// wParam, lParam);
1541 			lastKeyDownConsumed = false;
1542 			const bool altDown = KeyboardIsKeyDown(VK_MENU);
1543 			if (altDown && KeyboardIsNumericKeypadFunction(wParam, lParam)) {
1544 				// Don't interpret these as they may be characters entered by number.
1545 				return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1546 			}
1547 			const int ret = KeyDownWithModifiers(KeyTranslate(static_cast<int>(wParam)),
1548 							     ModifierFlags(KeyboardIsKeyDown(VK_SHIFT),
1549 									     KeyboardIsKeyDown(VK_CONTROL),
1550 									     altDown),
1551 							     &lastKeyDownConsumed);
1552 			if (!ret && !lastKeyDownConsumed) {
1553 				return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1554 			}
1555 			break;
1556 		}
1557 
1558 	case WM_KEYUP:
1559 		//Platform::DebugPrintf("S keyup %d %x %x\n",iMessage, wParam, lParam);
1560 		return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1561 
1562 	case WM_CHAR:
1563 		if (((wParam >= 128) || !iscntrl(static_cast<int>(wParam))) || !lastKeyDownConsumed) {
1564 			wchar_t wcs[3] = { static_cast<wchar_t>(wParam), 0 };
1565 			unsigned int wclen = 1;
1566 			if (IS_HIGH_SURROGATE(wcs[0])) {
1567 				// If this is a high surrogate character, we need a second one
1568 				lastHighSurrogateChar = wcs[0];
1569 				return 0;
1570 			} else if (IS_LOW_SURROGATE(wcs[0])) {
1571 				wcs[1] = wcs[0];
1572 				wcs[0] = lastHighSurrogateChar;
1573 				lastHighSurrogateChar = 0;
1574 				wclen = 2;
1575 			}
1576 			AddWString(std::wstring_view(wcs, wclen), CharacterSource::directInput);
1577 		}
1578 		return 0;
1579 
1580 	case WM_UNICHAR:
1581 		if (wParam == UNICODE_NOCHAR) {
1582 			return TRUE;
1583 		} else if (lastKeyDownConsumed) {
1584 			return 1;
1585 		} else {
1586 			wchar_t wcs[3] = { 0 };
1587 			const size_t wclen = UTF16FromUTF32Character(static_cast<unsigned int>(wParam), wcs);
1588 			AddWString(std::wstring_view(wcs, wclen), CharacterSource::directInput);
1589 			return FALSE;
1590 		}
1591 	}
1592 
1593 	return 0;
1594 }
1595 
FocusMessage(unsigned int iMessage,uptr_t wParam,sptr_t)1596 sptr_t ScintillaWin::FocusMessage(unsigned int iMessage, uptr_t wParam, sptr_t) {
1597 	switch (iMessage) {
1598 	case WM_KILLFOCUS: {
1599 		HWND wOther = reinterpret_cast<HWND>(wParam);
1600 		HWND wThis = MainHWND();
1601 		const HWND wCT = HwndFromWindow(ct.wCallTip);
1602 		if (!wParam ||
1603 			!(::IsChild(wThis, wOther) || (wOther == wCT))) {
1604 			SetFocusState(false);
1605 			DestroySystemCaret();
1606 		}
1607 		// Explicitly complete any IME composition
1608 		IMContext imc(MainHWND());
1609 		if (imc.hIMC) {
1610 			::ImmNotifyIME(imc.hIMC, NI_COMPOSITIONSTR, CPS_COMPLETE, 0);
1611 		}
1612 		break;
1613 	}
1614 
1615 	case WM_SETFOCUS:
1616 		SetFocusState(true);
1617 		DestroySystemCaret();
1618 		CreateSystemCaret();
1619 		break;
1620 	}
1621 	return 0;
1622 }
1623 
IMEMessage(unsigned int iMessage,uptr_t wParam,sptr_t lParam)1624 sptr_t ScintillaWin::IMEMessage(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
1625 	switch (iMessage) {
1626 
1627 	case WM_INPUTLANGCHANGE:
1628 		return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1629 
1630 	case WM_INPUTLANGCHANGEREQUEST:
1631 		return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1632 
1633 	case WM_IME_KEYDOWN: {
1634 			if (wParam == VK_HANJA) {
1635 				ToggleHanja();
1636 			}
1637 			return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1638 		}
1639 
1640 	case WM_IME_REQUEST: {
1641 			if (wParam == IMR_RECONVERTSTRING) {
1642 				return ImeOnReconvert(lParam);
1643 			}
1644 			return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1645 		}
1646 
1647 	case WM_IME_STARTCOMPOSITION:
1648 		if (KoreanIME() || imeInteraction == imeInline) {
1649 			return 0;
1650 		} else {
1651 			ImeStartComposition();
1652 			return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1653 		}
1654 
1655 	case WM_IME_ENDCOMPOSITION:
1656 		ImeEndComposition();
1657 		return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1658 
1659 	case WM_IME_COMPOSITION:
1660 		if (KoreanIME() || imeInteraction == imeInline) {
1661 			return HandleCompositionInline(wParam, lParam);
1662 		} else {
1663 			return HandleCompositionWindowed(wParam, lParam);
1664 		}
1665 
1666 	case WM_IME_SETCONTEXT:
1667 		if (KoreanIME() || imeInteraction == imeInline) {
1668 			if (wParam) {
1669 				LPARAM NoImeWin = lParam;
1670 				NoImeWin = NoImeWin & (~ISC_SHOWUICOMPOSITIONWINDOW);
1671 				return ::DefWindowProc(MainHWND(), iMessage, wParam, NoImeWin);
1672 			}
1673 		}
1674 		return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1675 
1676 	case WM_IME_NOTIFY:
1677 		return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1678 
1679 	}
1680 	return 0;
1681 }
1682 
EditMessage(unsigned int iMessage,uptr_t wParam,sptr_t lParam)1683 sptr_t ScintillaWin::EditMessage(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
1684 	switch (iMessage) {
1685 
1686 	case EM_LINEFROMCHAR:
1687 		if (static_cast<Sci::Position>(wParam) < 0) {
1688 			wParam = SelectionStart().Position();
1689 		}
1690 		return pdoc->LineFromPosition(wParam);
1691 
1692 	case EM_EXLINEFROMCHAR:
1693 		return pdoc->LineFromPosition(lParam);
1694 
1695 	case EM_GETSEL:
1696 		if (wParam) {
1697 			*reinterpret_cast<DWORD *>(wParam) = static_cast<DWORD>(SelectionStart().Position());
1698 		}
1699 		if (lParam) {
1700 			*reinterpret_cast<DWORD *>(lParam) = static_cast<DWORD>(SelectionEnd().Position());
1701 		}
1702 		return MAKELRESULT(SelectionStart().Position(), SelectionEnd().Position());
1703 
1704 	case EM_EXGETSEL: {
1705 			if (lParam == 0) {
1706 				return 0;
1707 			}
1708 			CHARRANGE *pCR = reinterpret_cast<CHARRANGE *>(lParam);
1709 			pCR->cpMin = static_cast<LONG>(SelectionStart().Position());
1710 			pCR->cpMax = static_cast<LONG>(SelectionEnd().Position());
1711 		}
1712 		break;
1713 
1714 	case EM_SETSEL: {
1715 			Sci::Position nStart = wParam;
1716 			Sci::Position nEnd = lParam;
1717 			if (nStart == 0 && nEnd == -1) {
1718 				nEnd = pdoc->Length();
1719 			}
1720 			if (nStart == -1) {
1721 				nStart = nEnd;	// Remove selection
1722 			}
1723 			SetSelection(nEnd, nStart);
1724 			EnsureCaretVisible();
1725 		}
1726 		break;
1727 
1728 	case EM_EXSETSEL: {
1729 			if (lParam == 0) {
1730 				return 0;
1731 			}
1732 			const CHARRANGE *pCR = reinterpret_cast<const CHARRANGE *>(lParam);
1733 			sel.selType = Selection::selStream;
1734 			if (pCR->cpMin == 0 && pCR->cpMax == -1) {
1735 				SetSelection(pCR->cpMin, pdoc->Length());
1736 			} else {
1737 				SetSelection(pCR->cpMin, pCR->cpMax);
1738 			}
1739 			EnsureCaretVisible();
1740 			return pdoc->LineFromPosition(SelectionStart().Position());
1741 		}
1742 	}
1743 	return 0;
1744 }
1745 
IdleMessage(unsigned int iMessage,uptr_t wParam,sptr_t lParam)1746 sptr_t ScintillaWin::IdleMessage(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
1747 	switch (iMessage) {
1748 	case SC_WIN_IDLE:
1749 		// wParam=dwTickCountInitial, or 0 to initialize.  lParam=bSkipUserInputTest
1750 		if (idler.state) {
1751 			if (lParam || (WAIT_TIMEOUT == MsgWaitForMultipleObjects(0, nullptr, 0, 0, QS_INPUT | QS_HOTKEY))) {
1752 				if (Idle()) {
1753 					// User input was given priority above, but all events do get a turn.  Other
1754 					// messages, notifications, etc. will get interleaved with the idle messages.
1755 
1756 					// However, some things like WM_PAINT are a lower priority, and will not fire
1757 					// when there's a message posted.  So, several times a second, we stop and let
1758 					// the low priority events have a turn (after which the timer will fire again).
1759 
1760 					// Suppress a warning from Code Analysis that the GetTickCount function
1761 					// wraps after 49 days. The WM_TIMER will kick off another SC_WIN_IDLE
1762 					// after the wrap.
1763 #ifdef _MSC_VER
1764 #pragma warning(suppress: 28159)
1765 #endif
1766 					const DWORD dwCurrent = GetTickCount();
1767 					const DWORD dwStart = wParam ? static_cast<DWORD>(wParam) : dwCurrent;
1768 					constexpr DWORD maxWorkTime = 50;
1769 
1770 					if (dwCurrent >= dwStart && dwCurrent > maxWorkTime &&dwCurrent - maxWorkTime < dwStart)
1771 						PostMessage(MainHWND(), SC_WIN_IDLE, dwStart, 0);
1772 				} else {
1773 					SetIdle(false);
1774 				}
1775 			}
1776 		}
1777 		break;
1778 
1779 	case SC_WORK_IDLE:
1780 		IdleWork();
1781 		break;
1782 	}
1783 	return 0;
1784 }
1785 
SciMessage(unsigned int iMessage,uptr_t wParam,sptr_t lParam)1786 sptr_t ScintillaWin::SciMessage(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
1787 	switch (iMessage) {
1788 	case SCI_GETDIRECTFUNCTION:
1789 		return reinterpret_cast<sptr_t>(DirectFunction);
1790 
1791 	case SCI_GETDIRECTPOINTER:
1792 		return reinterpret_cast<sptr_t>(this);
1793 
1794 	case SCI_GRABFOCUS:
1795 		::SetFocus(MainHWND());
1796 		break;
1797 
1798 #ifdef INCLUDE_DEPRECATED_FEATURES
1799 	case SCI_SETKEYSUNICODE:
1800 		break;
1801 
1802 	case SCI_GETKEYSUNICODE:
1803 		return true;
1804 #endif
1805 
1806 	case SCI_SETTECHNOLOGY:
1807 		if ((wParam == SC_TECHNOLOGY_DEFAULT) ||
1808 			(wParam == SC_TECHNOLOGY_DIRECTWRITERETAIN) ||
1809 			(wParam == SC_TECHNOLOGY_DIRECTWRITEDC) ||
1810 			(wParam == SC_TECHNOLOGY_DIRECTWRITE)) {
1811 			const int technologyNew = static_cast<int>(wParam);
1812 			if (technology != technologyNew) {
1813 				if (technologyNew > SC_TECHNOLOGY_DEFAULT) {
1814 #if defined(USE_D2D)
1815 					if (!LoadD2D())
1816 						// Failed to load Direct2D or DirectWrite so no effect
1817 						return 0;
1818 #else
1819 					return 0;
1820 #endif
1821 				} else {
1822 					bidirectional = EditModel::Bidirectional::bidiDisabled;
1823 				}
1824 #if defined(USE_D2D)
1825 				DropRenderTarget();
1826 #endif
1827 				technology = technologyNew;
1828 				// Invalidate all cached information including layout.
1829 				DropGraphics(true);
1830 				InvalidateStyleRedraw();
1831 			}
1832 		}
1833 		break;
1834 
1835 	case SCI_SETBIDIRECTIONAL:
1836 		if (technology == SC_TECHNOLOGY_DEFAULT) {
1837 			bidirectional = EditModel::Bidirectional::bidiDisabled;
1838 		} else if (wParam <= SC_BIDIRECTIONAL_R2L) {
1839 			bidirectional = static_cast<EditModel::Bidirectional>(wParam);
1840 		}
1841 		// Invalidate all cached information including layout.
1842 		DropGraphics(true);
1843 		InvalidateStyleRedraw();
1844 		break;
1845 
1846 	case SCI_TARGETASUTF8:
1847 		return TargetAsUTF8(CharPtrFromSPtr(lParam));
1848 
1849 	case SCI_ENCODEDFROMUTF8:
1850 		return EncodedFromUTF8(ConstCharPtrFromUPtr(wParam),
1851 			CharPtrFromSPtr(lParam));
1852 
1853 	}
1854 	return 0;
1855 }
1856 
WndProc(unsigned int iMessage,uptr_t wParam,sptr_t lParam)1857 sptr_t ScintillaWin::WndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
1858 	try {
1859 		//Platform::DebugPrintf("S M:%x WP:%x L:%x\n", iMessage, wParam, lParam);
1860 		iMessage = SciMessageFromEM(iMessage);
1861 		switch (iMessage) {
1862 
1863 		case WM_CREATE:
1864 			ctrlID = ::GetDlgCtrlID(HwndFromWindow(wMain));
1865 			// Get Intellimouse scroll line parameters
1866 			GetIntelliMouseParameters();
1867 			::RegisterDragDrop(MainHWND(), reinterpret_cast<IDropTarget *>(&dt));
1868 			break;
1869 
1870 		case WM_COMMAND:
1871 			Command(LOWORD(wParam));
1872 			break;
1873 
1874 		case WM_PAINT:
1875 			return WndPaint();
1876 
1877 		case WM_PRINTCLIENT: {
1878 				HDC hdc = reinterpret_cast<HDC>(wParam);
1879 				if (!IsCompatibleDC(hdc)) {
1880 					return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1881 				}
1882 				FullPaintDC(hdc);
1883 			}
1884 			break;
1885 
1886 		case WM_VSCROLL:
1887 			ScrollMessage(wParam);
1888 			break;
1889 
1890 		case WM_HSCROLL:
1891 			HorizontalScrollMessage(wParam);
1892 			break;
1893 
1894 		case WM_SIZE:
1895 			SizeWindow();
1896 			break;
1897 
1898 		case WM_TIMER:
1899 			if (wParam == idleTimerID && idler.state) {
1900 				SendMessage(MainHWND(), SC_WIN_IDLE, 0, 1);
1901 			} else {
1902 				TickFor(static_cast<TickReason>(wParam - fineTimerStart));
1903 			}
1904 			break;
1905 
1906 		case SC_WIN_IDLE:
1907 		case SC_WORK_IDLE:
1908 			return IdleMessage(iMessage, wParam, lParam);
1909 
1910 		case WM_GETMINMAXINFO:
1911 			return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1912 
1913 		case WM_LBUTTONDOWN:
1914 		case WM_LBUTTONUP:
1915 		case WM_RBUTTONDOWN:
1916 		case WM_MOUSEMOVE:
1917 		case WM_MOUSELEAVE:
1918 		case WM_MOUSEWHEEL:
1919 			return MouseMessage(iMessage, wParam, lParam);
1920 
1921 		case WM_SETCURSOR:
1922 			if (LOWORD(lParam) == HTCLIENT) {
1923 				POINT pt;
1924 				if (::GetCursorPos(&pt)) {
1925 					::ScreenToClient(MainHWND(), &pt);
1926 					DisplayCursor(ContextCursor(PointFromPOINT(pt)));
1927 				}
1928 				return TRUE;
1929 			} else {
1930 				return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1931 			}
1932 
1933 		case WM_SYSKEYDOWN:
1934 		case WM_KEYDOWN:
1935 		case WM_KEYUP:
1936 		case WM_CHAR:
1937 		case WM_UNICHAR:
1938 			return KeyMessage(iMessage, wParam, lParam);
1939 
1940 		case WM_SETTINGCHANGE:
1941 			//Platform::DebugPrintf("Setting Changed\n");
1942 			InvalidateStyleData();
1943 			// Get Intellimouse scroll line parameters
1944 			GetIntelliMouseParameters();
1945 			break;
1946 
1947 		case WM_GETDLGCODE:
1948 			return DLGC_HASSETSEL | DLGC_WANTALLKEYS;
1949 
1950 		case WM_KILLFOCUS:
1951 		case WM_SETFOCUS:
1952 			return FocusMessage(iMessage, wParam, lParam);
1953 
1954 		case WM_SYSCOLORCHANGE:
1955 			//Platform::DebugPrintf("Setting Changed\n");
1956 			InvalidateStyleData();
1957 			break;
1958 
1959 		case WM_DPICHANGED:
1960 			dpi = HIWORD(wParam);
1961 			InvalidateStyleRedraw();
1962 			break;
1963 
1964 		case WM_DPICHANGED_AFTERPARENT: {
1965 				const UINT dpiNow = DpiForWindow(wMain.GetID());
1966 				if (dpi != dpiNow) {
1967 					dpi = dpiNow;
1968 					InvalidateStyleRedraw();
1969 				}
1970 			}
1971 			break;
1972 
1973 		case WM_CONTEXTMENU:
1974 			return ShowContextMenu(iMessage, wParam, lParam);
1975 
1976 		case WM_ERASEBKGND:
1977 			return 1;   // Avoid any background erasure as whole window painted.
1978 
1979 		case WM_CAPTURECHANGED:
1980 			capturedMouse = false;
1981 			return 0;
1982 
1983 		// These are not handled in Scintilla and its faster to dispatch them here.
1984 		// Also moves time out to here so profile doesn't count lots of empty message calls.
1985 
1986 		case WM_MOVE:
1987 		case WM_MOUSEACTIVATE:
1988 		case WM_NCHITTEST:
1989 		case WM_NCCALCSIZE:
1990 		case WM_NCPAINT:
1991 		case WM_NCMOUSEMOVE:
1992 		case WM_NCLBUTTONDOWN:
1993 		case WM_SYSCOMMAND:
1994 		case WM_WINDOWPOSCHANGING:
1995 		case WM_WINDOWPOSCHANGED:
1996 			return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1997 
1998 		case WM_GETTEXTLENGTH:
1999 			return GetTextLength();
2000 
2001 		case WM_GETTEXT:
2002 			return GetText(wParam, lParam);
2003 
2004 		case WM_INPUTLANGCHANGE:
2005 		case WM_INPUTLANGCHANGEREQUEST:
2006 		case WM_IME_KEYDOWN:
2007 		case WM_IME_REQUEST:
2008 		case WM_IME_STARTCOMPOSITION:
2009 		case WM_IME_ENDCOMPOSITION:
2010 		case WM_IME_COMPOSITION:
2011 		case WM_IME_SETCONTEXT:
2012 		case WM_IME_NOTIFY:
2013 			return IMEMessage(iMessage, wParam, lParam);
2014 
2015 		case EM_LINEFROMCHAR:
2016 		case EM_EXLINEFROMCHAR:
2017 		case EM_GETSEL:
2018 		case EM_EXGETSEL:
2019 		case EM_SETSEL:
2020 		case EM_EXSETSEL:
2021 			return EditMessage(iMessage, wParam, lParam);
2022 
2023 		case SCI_GETDIRECTFUNCTION:
2024 		case SCI_GETDIRECTPOINTER:
2025 		case SCI_GRABFOCUS:
2026 #ifdef INCLUDE_DEPRECATED_FEATURES
2027 		case SCI_SETKEYSUNICODE:
2028 		case SCI_GETKEYSUNICODE:
2029 #endif
2030 		case SCI_SETTECHNOLOGY:
2031 		case SCI_SETBIDIRECTIONAL:
2032 		case SCI_TARGETASUTF8:
2033 		case SCI_ENCODEDFROMUTF8:
2034 			return SciMessage(iMessage, wParam, lParam);
2035 
2036 		default:
2037 			return ScintillaBase::WndProc(iMessage, wParam, lParam);
2038 		}
2039 	} catch (std::bad_alloc &) {
2040 		errorStatus = SC_STATUS_BADALLOC;
2041 	} catch (...) {
2042 		errorStatus = SC_STATUS_FAILURE;
2043 	}
2044 	return 0;
2045 }
2046 
ValidCodePage(int codePage) const2047 bool ScintillaWin::ValidCodePage(int codePage) const {
2048 	return codePage == 0 || codePage == SC_CP_UTF8 ||
2049 	       codePage == 932 || codePage == 936 || codePage == 949 ||
2050 	       codePage == 950 || codePage == 1361;
2051 }
2052 
DefWndProc(unsigned int iMessage,uptr_t wParam,sptr_t lParam)2053 sptr_t ScintillaWin::DefWndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
2054 	return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
2055 }
2056 
FineTickerRunning(TickReason reason)2057 bool ScintillaWin::FineTickerRunning(TickReason reason) {
2058 	return timers[reason] != 0;
2059 }
2060 
FineTickerStart(TickReason reason,int millis,int tolerance)2061 void ScintillaWin::FineTickerStart(TickReason reason, int millis, int tolerance) {
2062 	FineTickerCancel(reason);
2063 	const UINT_PTR eventID = static_cast<UINT_PTR>(fineTimerStart) + reason;
2064 	if (SetCoalescableTimerFn && tolerance) {
2065 		timers[reason] = SetCoalescableTimerFn(MainHWND(), eventID, millis, nullptr, tolerance);
2066 	} else {
2067 		timers[reason] = ::SetTimer(MainHWND(), eventID, millis, nullptr);
2068 	}
2069 }
2070 
FineTickerCancel(TickReason reason)2071 void ScintillaWin::FineTickerCancel(TickReason reason) {
2072 	if (timers[reason]) {
2073 		::KillTimer(MainHWND(), timers[reason]);
2074 		timers[reason] = 0;
2075 	}
2076 }
2077 
2078 
SetIdle(bool on)2079 bool ScintillaWin::SetIdle(bool on) {
2080 	// On Win32 the Idler is implemented as a Timer on the Scintilla window.  This
2081 	// takes advantage of the fact that WM_TIMER messages are very low priority,
2082 	// and are only posted when the message queue is empty, i.e. during idle time.
2083 	if (idler.state != on) {
2084 		if (on) {
2085 			idler.idlerID = ::SetTimer(MainHWND(), idleTimerID, 10, nullptr)
2086 				? reinterpret_cast<IdlerID>(idleTimerID) : 0;
2087 		} else {
2088 			::KillTimer(MainHWND(), reinterpret_cast<uptr_t>(idler.idlerID));
2089 			idler.idlerID = 0;
2090 		}
2091 		idler.state = idler.idlerID != 0;
2092 	}
2093 	return idler.state;
2094 }
2095 
IdleWork()2096 void ScintillaWin::IdleWork() {
2097 	styleIdleInQueue = false;
2098 	Editor::IdleWork();
2099 }
2100 
QueueIdleWork(WorkNeeded::workItems items,Sci::Position upTo)2101 void ScintillaWin::QueueIdleWork(WorkNeeded::workItems items, Sci::Position upTo) {
2102 	Editor::QueueIdleWork(items, upTo);
2103 	if (!styleIdleInQueue) {
2104 		if (PostMessage(MainHWND(), SC_WORK_IDLE, 0, 0)) {
2105 			styleIdleInQueue = true;
2106 		}
2107 	}
2108 }
2109 
SetMouseCapture(bool on)2110 void ScintillaWin::SetMouseCapture(bool on) {
2111 	if (mouseDownCaptures) {
2112 		if (on) {
2113 			::SetCapture(MainHWND());
2114 		} else {
2115 			::ReleaseCapture();
2116 		}
2117 	}
2118 	capturedMouse = on;
2119 }
2120 
HaveMouseCapture()2121 bool ScintillaWin::HaveMouseCapture() {
2122 	// Cannot just see if GetCapture is this window as the scroll bar also sets capture for the window
2123 	return capturedMouse;
2124 	//return capturedMouse && (::GetCapture() == MainHWND());
2125 }
2126 
SetTrackMouseLeaveEvent(bool on)2127 void ScintillaWin::SetTrackMouseLeaveEvent(bool on) noexcept {
2128 	if (on && !trackedMouseLeave) {
2129 		TRACKMOUSEEVENT tme;
2130 		tme.cbSize = sizeof(tme);
2131 		tme.dwFlags = TME_LEAVE;
2132 		tme.hwndTrack = MainHWND();
2133 		tme.dwHoverTime = HOVER_DEFAULT;	// Unused but triggers Dr. Memory if not initialized
2134 		TrackMouseEvent(&tme);
2135 	}
2136 	trackedMouseLeave = on;
2137 }
2138 
PaintContains(PRectangle rc)2139 bool ScintillaWin::PaintContains(PRectangle rc) {
2140 	if (paintState == painting) {
2141 		return BoundsContains(rcPaint, hRgnUpdate, rc);
2142 	}
2143 	return true;
2144 }
2145 
ScrollText(Sci::Line)2146 void ScintillaWin::ScrollText(Sci::Line /* linesToMove */) {
2147 	//Platform::DebugPrintf("ScintillaWin::ScrollText %d\n", linesToMove);
2148 	//::ScrollWindow(MainHWND(), 0,
2149 	//	vs.lineHeight * linesToMove, 0, 0);
2150 	//::UpdateWindow(MainHWND());
2151 	Redraw();
2152 	UpdateSystemCaret();
2153 }
2154 
NotifyCaretMove()2155 void ScintillaWin::NotifyCaretMove() {
2156 	NotifyWinEvent(EVENT_OBJECT_LOCATIONCHANGE, MainHWND(), OBJID_CARET, CHILDID_SELF);
2157 }
2158 
UpdateSystemCaret()2159 void ScintillaWin::UpdateSystemCaret() {
2160 	if (hasFocus) {
2161 		if (pdoc->TentativeActive()) {
2162 			// ongoing inline mode IME composition, don't inform IME of system caret position.
2163 			// fix candidate window for Google Japanese IME moved on typing on Win7.
2164 			return;
2165 		}
2166 		if (HasCaretSizeChanged()) {
2167 			DestroySystemCaret();
2168 			CreateSystemCaret();
2169 		}
2170 		const Point pos = PointMainCaret();
2171 		::SetCaretPos(static_cast<int>(pos.x), static_cast<int>(pos.y));
2172 	}
2173 }
2174 
SetScrollInfo(int nBar,LPCSCROLLINFO lpsi,BOOL bRedraw)2175 int ScintillaWin::SetScrollInfo(int nBar, LPCSCROLLINFO lpsi, BOOL bRedraw) noexcept {
2176 	return ::SetScrollInfo(MainHWND(), nBar, lpsi, bRedraw);
2177 }
2178 
GetScrollInfo(int nBar,LPSCROLLINFO lpsi)2179 bool ScintillaWin::GetScrollInfo(int nBar, LPSCROLLINFO lpsi) noexcept {
2180 	return ::GetScrollInfo(MainHWND(), nBar, lpsi) ? true : false;
2181 }
2182 
2183 // Change the scroll position but avoid repaint if changing to same value
ChangeScrollPos(int barType,Sci::Position pos)2184 void ScintillaWin::ChangeScrollPos(int barType, Sci::Position pos) {
2185 	SCROLLINFO sci = {
2186 		sizeof(sci), 0, 0, 0, 0, 0, 0
2187 	};
2188 	sci.fMask = SIF_POS;
2189 	GetScrollInfo(barType, &sci);
2190 	if (sci.nPos != pos) {
2191 		DwellEnd(true);
2192 		sci.nPos = static_cast<int>(pos);
2193 		SetScrollInfo(barType, &sci, TRUE);
2194 	}
2195 }
2196 
SetVerticalScrollPos()2197 void ScintillaWin::SetVerticalScrollPos() {
2198 	ChangeScrollPos(SB_VERT, topLine);
2199 }
2200 
SetHorizontalScrollPos()2201 void ScintillaWin::SetHorizontalScrollPos() {
2202 	ChangeScrollPos(SB_HORZ, xOffset);
2203 }
2204 
ModifyScrollBars(Sci::Line nMax,Sci::Line nPage)2205 bool ScintillaWin::ModifyScrollBars(Sci::Line nMax, Sci::Line nPage) {
2206 	bool modified = false;
2207 	SCROLLINFO sci = {
2208 		sizeof(sci), 0, 0, 0, 0, 0, 0
2209 	};
2210 	sci.fMask = SIF_PAGE | SIF_RANGE;
2211 	GetScrollInfo(SB_VERT, &sci);
2212 	const Sci::Line vertEndPreferred = nMax;
2213 	if (!verticalScrollBarVisible)
2214 		nPage = vertEndPreferred + 1;
2215 	if ((sci.nMin != 0) ||
2216 		(sci.nMax != vertEndPreferred) ||
2217 	        (sci.nPage != static_cast<unsigned int>(nPage)) ||
2218 	        (sci.nPos != 0)) {
2219 		sci.fMask = SIF_PAGE | SIF_RANGE;
2220 		sci.nMin = 0;
2221 		sci.nMax = static_cast<int>(vertEndPreferred);
2222 		sci.nPage = static_cast<UINT>(nPage);
2223 		sci.nPos = 0;
2224 		sci.nTrackPos = 1;
2225 		SetScrollInfo(SB_VERT, &sci, TRUE);
2226 		modified = true;
2227 	}
2228 
2229 	const PRectangle rcText = GetTextRectangle();
2230 	int horizEndPreferred = scrollWidth;
2231 	if (horizEndPreferred < 0)
2232 		horizEndPreferred = 0;
2233 	int pageWidth = static_cast<int>(rcText.Width());
2234 	if (!horizontalScrollBarVisible || Wrapping())
2235 		pageWidth = horizEndPreferred + 1;
2236 	sci.fMask = SIF_PAGE | SIF_RANGE;
2237 	GetScrollInfo(SB_HORZ, &sci);
2238 	if ((sci.nMin != 0) ||
2239 		(sci.nMax != horizEndPreferred) ||
2240 		(sci.nPage != static_cast<unsigned int>(pageWidth)) ||
2241 	        (sci.nPos != 0)) {
2242 		sci.fMask = SIF_PAGE | SIF_RANGE;
2243 		sci.nMin = 0;
2244 		sci.nMax = horizEndPreferred;
2245 		sci.nPage = pageWidth;
2246 		sci.nPos = 0;
2247 		sci.nTrackPos = 1;
2248 		SetScrollInfo(SB_HORZ, &sci, TRUE);
2249 		modified = true;
2250 		if (scrollWidth < pageWidth) {
2251 			HorizontalScrollTo(0);
2252 		}
2253 	}
2254 	return modified;
2255 }
2256 
NotifyChange()2257 void ScintillaWin::NotifyChange() {
2258 	::SendMessage(::GetParent(MainHWND()), WM_COMMAND,
2259 	        MAKEWPARAM(GetCtrlID(), SCEN_CHANGE),
2260 		reinterpret_cast<LPARAM>(MainHWND()));
2261 }
2262 
NotifyFocus(bool focus)2263 void ScintillaWin::NotifyFocus(bool focus) {
2264 	if (commandEvents) {
2265 		::SendMessage(::GetParent(MainHWND()), WM_COMMAND,
2266 			MAKEWPARAM(GetCtrlID(), focus ? SCEN_SETFOCUS : SCEN_KILLFOCUS),
2267 			reinterpret_cast<LPARAM>(MainHWND()));
2268 	}
2269 	Editor::NotifyFocus(focus);
2270 }
2271 
SetCtrlID(int identifier)2272 void ScintillaWin::SetCtrlID(int identifier) {
2273 	::SetWindowID(HwndFromWindow(wMain), identifier);
2274 }
2275 
GetCtrlID()2276 int ScintillaWin::GetCtrlID() {
2277 	return ::GetDlgCtrlID(HwndFromWindow(wMain));
2278 }
2279 
NotifyParent(SCNotification scn)2280 void ScintillaWin::NotifyParent(SCNotification scn) {
2281 	scn.nmhdr.hwndFrom = MainHWND();
2282 	scn.nmhdr.idFrom = GetCtrlID();
2283 	::SendMessage(::GetParent(MainHWND()), WM_NOTIFY,
2284 	              GetCtrlID(), reinterpret_cast<LPARAM>(&scn));
2285 }
2286 
NotifyDoubleClick(Point pt,int modifiers)2287 void ScintillaWin::NotifyDoubleClick(Point pt, int modifiers) {
2288 	//Platform::DebugPrintf("ScintillaWin Double click 0\n");
2289 	ScintillaBase::NotifyDoubleClick(pt, modifiers);
2290 	// Send myself a WM_LBUTTONDBLCLK, so the container can handle it too.
2291 	::SendMessage(MainHWND(),
2292 			  WM_LBUTTONDBLCLK,
2293 			  (modifiers & SCI_SHIFT) ? MK_SHIFT : 0,
2294 			  MAKELPARAM(pt.x, pt.y));
2295 }
2296 
2297 class CaseFolderDBCS : public CaseFolderTable {
2298 	// Allocate the expandable storage here so that it does not need to be reallocated
2299 	// for each call to Fold.
2300 	std::vector<wchar_t> utf16Mixed;
2301 	std::vector<wchar_t> utf16Folded;
2302 	UINT cp;
2303 public:
CaseFolderDBCS(UINT cp_)2304 	explicit CaseFolderDBCS(UINT cp_) : cp(cp_) {
2305 		StandardASCII();
2306 	}
Fold(char * folded,size_t sizeFolded,const char * mixed,size_t lenMixed)2307 	size_t Fold(char *folded, size_t sizeFolded, const char *mixed, size_t lenMixed) override {
2308 		if ((lenMixed == 1) && (sizeFolded > 0)) {
2309 			folded[0] = mapping[static_cast<unsigned char>(mixed[0])];
2310 			return 1;
2311 		} else {
2312 			if (lenMixed > utf16Mixed.size()) {
2313 				utf16Mixed.resize(lenMixed + 8);
2314 			}
2315 			const size_t nUtf16Mixed = WideCharFromMultiByte(cp,
2316 				std::string_view(mixed, lenMixed),
2317 				&utf16Mixed[0],
2318 				utf16Mixed.size());
2319 
2320 			if (nUtf16Mixed == 0) {
2321 				// Failed to convert -> bad input
2322 				folded[0] = '\0';
2323 				return 1;
2324 			}
2325 
2326 			size_t lenFlat = 0;
2327 			for (size_t mixIndex=0; mixIndex < nUtf16Mixed; mixIndex++) {
2328 				if ((lenFlat + 20) > utf16Folded.size())
2329 					utf16Folded.resize(lenFlat + 60);
2330 				const char *foldedUTF8 = CaseConvert(utf16Mixed[mixIndex], CaseConversionFold);
2331 				if (foldedUTF8) {
2332 					// Maximum length of a case conversion is 6 bytes, 3 characters
2333 					wchar_t wFolded[20];
2334 					const size_t charsConverted = UTF16FromUTF8(std::string_view(foldedUTF8),
2335 							wFolded, std::size(wFolded));
2336 					for (size_t j=0; j<charsConverted; j++)
2337 						utf16Folded[lenFlat++] = wFolded[j];
2338 				} else {
2339 					utf16Folded[lenFlat++] = utf16Mixed[mixIndex];
2340 				}
2341 			}
2342 
2343 			const std::wstring_view wsvFolded(&utf16Folded[0], lenFlat);
2344 			const size_t lenOut = MultiByteLenFromWideChar(cp, wsvFolded);
2345 
2346 			if (lenOut < sizeFolded) {
2347 				MultiByteFromWideChar(cp, wsvFolded, folded, lenOut);
2348 				return lenOut;
2349 			} else {
2350 				return 0;
2351 			}
2352 		}
2353 	}
2354 };
2355 
CaseFolderForEncoding()2356 CaseFolder *ScintillaWin::CaseFolderForEncoding() {
2357 	const UINT cpDest = CodePageOfDocument();
2358 	if (cpDest == SC_CP_UTF8) {
2359 		return new CaseFolderUnicode();
2360 	} else {
2361 		if (pdoc->dbcsCodePage == 0) {
2362 			CaseFolderTable *pcf = new CaseFolderTable();
2363 			pcf->StandardASCII();
2364 			// Only for single byte encodings
2365 			for (int i=0x80; i<0x100; i++) {
2366 				char sCharacter[2] = "A";
2367 				sCharacter[0] = static_cast<char>(i);
2368 				wchar_t wCharacter[20];
2369 				const unsigned int lengthUTF16 = WideCharFromMultiByte(cpDest, sCharacter,
2370 					wCharacter, std::size(wCharacter));
2371 				if (lengthUTF16 == 1) {
2372 					const char *caseFolded = CaseConvert(wCharacter[0], CaseConversionFold);
2373 					if (caseFolded) {
2374 						wchar_t wLower[20];
2375 						const size_t charsConverted = UTF16FromUTF8(std::string_view(caseFolded),
2376 							wLower, std::size(wLower));
2377 						if (charsConverted == 1) {
2378 							char sCharacterLowered[20];
2379 							const unsigned int lengthConverted = MultiByteFromWideChar(cpDest,
2380 								std::wstring_view(wLower, charsConverted),
2381 								sCharacterLowered, std::size(sCharacterLowered));
2382 							if ((lengthConverted == 1) && (sCharacter[0] != sCharacterLowered[0])) {
2383 								pcf->SetTranslation(sCharacter[0], sCharacterLowered[0]);
2384 							}
2385 						}
2386 					}
2387 				}
2388 			}
2389 			return pcf;
2390 		} else {
2391 			return new CaseFolderDBCS(cpDest);
2392 		}
2393 	}
2394 }
2395 
CaseMapString(const std::string & s,int caseMapping)2396 std::string ScintillaWin::CaseMapString(const std::string &s, int caseMapping) {
2397 	if ((s.size() == 0) || (caseMapping == cmSame))
2398 		return s;
2399 
2400 	const UINT cpDoc = CodePageOfDocument();
2401 	if (cpDoc == SC_CP_UTF8) {
2402 		return CaseConvertString(s, (caseMapping == cmUpper) ? CaseConversionUpper : CaseConversionLower);
2403 	}
2404 
2405 	// Change text to UTF-16
2406 	const std::wstring wsText = StringDecode(s, cpDoc);
2407 
2408 	const DWORD mapFlags = LCMAP_LINGUISTIC_CASING |
2409 		((caseMapping == cmUpper) ? LCMAP_UPPERCASE : LCMAP_LOWERCASE);
2410 
2411 	// Change case
2412 	const std::wstring wsConverted = StringMapCase(wsText, mapFlags);
2413 
2414 	// Change back to document encoding
2415 	std::string sConverted = StringEncode(wsConverted, cpDoc);
2416 
2417 	return sConverted;
2418 }
2419 
Copy()2420 void ScintillaWin::Copy() {
2421 	//Platform::DebugPrintf("Copy\n");
2422 	if (!sel.Empty()) {
2423 		SelectionText selectedText;
2424 		CopySelectionRange(&selectedText);
2425 		CopyToClipboard(selectedText);
2426 	}
2427 }
2428 
CanPaste()2429 bool ScintillaWin::CanPaste() {
2430 	if (!Editor::CanPaste())
2431 		return false;
2432 	return ::IsClipboardFormatAvailable(CF_UNICODETEXT) != FALSE;
2433 }
2434 
2435 namespace {
2436 
2437 class GlobalMemory {
2438 	HGLOBAL hand {};
2439 public:
2440 	void *ptr {};
GlobalMemory()2441 	GlobalMemory() noexcept {
2442 	}
GlobalMemory(HGLOBAL hand_)2443 	explicit GlobalMemory(HGLOBAL hand_) noexcept : hand(hand_) {
2444 		if (hand) {
2445 			ptr = ::GlobalLock(hand);
2446 		}
2447 	}
2448 	// Deleted so GlobalMemory objects can not be copied.
2449 	GlobalMemory(const GlobalMemory &) = delete;
2450 	GlobalMemory(GlobalMemory &&) = delete;
2451 	GlobalMemory &operator=(const GlobalMemory &) = delete;
2452 	GlobalMemory &operator=(GlobalMemory &&) = delete;
~GlobalMemory()2453 	~GlobalMemory() {
2454 		assert(!ptr);
2455 		assert(!hand);
2456 	}
Allocate(size_t bytes)2457 	void Allocate(size_t bytes) noexcept {
2458 		assert(!hand);
2459 		hand = ::GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT, bytes);
2460 		if (hand) {
2461 			ptr = ::GlobalLock(hand);
2462 		}
2463 	}
Unlock()2464 	HGLOBAL Unlock() noexcept {
2465 		assert(ptr);
2466 		HGLOBAL handCopy = hand;
2467 		::GlobalUnlock(hand);
2468 		ptr = nullptr;
2469 		hand = {};
2470 		return handCopy;
2471 	}
SetClip(UINT uFormat)2472 	void SetClip(UINT uFormat) noexcept {
2473 		::SetClipboardData(uFormat, Unlock());
2474 	}
operator bool() const2475 	operator bool() const noexcept {
2476 		return ptr != nullptr;
2477 	}
Size() const2478 	SIZE_T Size() const noexcept {
2479 		return ::GlobalSize(hand);
2480 	}
2481 };
2482 
2483 // OpenClipboard may fail if another application has opened the clipboard.
2484 // Try up to 8 times, with an initial delay of 1 ms and an exponential back off
2485 // for a maximum total delay of 127 ms (1+2+4+8+16+32+64).
OpenClipboardRetry(HWND hwnd)2486 bool OpenClipboardRetry(HWND hwnd) noexcept {
2487 	for (int attempt=0; attempt<8; attempt++) {
2488 		if (attempt > 0) {
2489 			::Sleep(1 << (attempt-1));
2490 		}
2491 		if (::OpenClipboard(hwnd)) {
2492 			return true;
2493 		}
2494 	}
2495 	return false;
2496 }
2497 
IsValidFormatEtc(const FORMATETC * pFE)2498 bool IsValidFormatEtc(const FORMATETC *pFE) noexcept {
2499 	return pFE->ptd == nullptr &&
2500 		(pFE->dwAspect & DVASPECT_CONTENT) != 0 &&
2501 		pFE->lindex == -1 &&
2502 		(pFE->tymed & TYMED_HGLOBAL) != 0;
2503 }
2504 
SupportedFormat(const FORMATETC * pFE)2505 bool SupportedFormat(const FORMATETC *pFE) noexcept {
2506 	return pFE->cfFormat == CF_UNICODETEXT &&
2507 		IsValidFormatEtc(pFE);
2508 }
2509 
2510 }
2511 
Paste()2512 void ScintillaWin::Paste() {
2513 	if (!::OpenClipboardRetry(MainHWND())) {
2514 		return;
2515 	}
2516 	UndoGroup ug(pdoc);
2517 	const bool isLine = SelectionEmpty() &&
2518 		(::IsClipboardFormatAvailable(cfLineSelect) || ::IsClipboardFormatAvailable(cfVSLineTag));
2519 	ClearSelection(multiPasteMode == SC_MULTIPASTE_EACH);
2520 	bool isRectangular = (::IsClipboardFormatAvailable(cfColumnSelect) != 0);
2521 
2522 	if (!isRectangular) {
2523 		// Evaluate "Borland IDE Block Type" explicitly
2524 		GlobalMemory memBorlandSelection(::GetClipboardData(cfBorlandIDEBlockType));
2525 		if (memBorlandSelection) {
2526 			isRectangular = (memBorlandSelection.Size() == 1) && (static_cast<BYTE *>(memBorlandSelection.ptr)[0] == 0x02);
2527 			memBorlandSelection.Unlock();
2528 		}
2529 	}
2530 	const PasteShape pasteShape = isRectangular ? pasteRectangular : (isLine ? pasteLine : pasteStream);
2531 
2532 	// Use CF_UNICODETEXT if available
2533 	GlobalMemory memUSelection(::GetClipboardData(CF_UNICODETEXT));
2534 	if (const wchar_t *uptr = static_cast<const wchar_t *>(memUSelection.ptr)) {
2535 		const std::string putf = EncodeWString(uptr);
2536 		InsertPasteShape(putf.c_str(), putf.length(), pasteShape);
2537 		memUSelection.Unlock();
2538 	}
2539 	::CloseClipboard();
2540 	Redraw();
2541 }
2542 
CreateCallTipWindow(PRectangle)2543 void ScintillaWin::CreateCallTipWindow(PRectangle) {
2544 	if (!ct.wCallTip.Created()) {
2545 		HWND wnd = ::CreateWindow(callClassName, TEXT("ACallTip"),
2546 					     WS_POPUP, 100, 100, 150, 20,
2547 					     MainHWND(), 0,
2548 					     GetWindowInstance(MainHWND()),
2549 					     this);
2550 		ct.wCallTip = wnd;
2551 		ct.wDraw = wnd;
2552 	}
2553 }
2554 
AddToPopUp(const char * label,int cmd,bool enabled)2555 void ScintillaWin::AddToPopUp(const char *label, int cmd, bool enabled) {
2556 	HMENU hmenuPopup = static_cast<HMENU>(popup.GetID());
2557 	if (!label[0])
2558 		::AppendMenuA(hmenuPopup, MF_SEPARATOR, 0, "");
2559 	else if (enabled)
2560 		::AppendMenuA(hmenuPopup, MF_STRING, cmd, label);
2561 	else
2562 		::AppendMenuA(hmenuPopup, MF_STRING | MF_DISABLED | MF_GRAYED, cmd, label);
2563 }
2564 
ClaimSelection()2565 void ScintillaWin::ClaimSelection() {
2566 	// Windows does not have a primary selection
2567 }
2568 
2569 /// Implement IUnknown
2570 
2571 STDMETHODIMP_(ULONG)FormatEnumerator_AddRef(FormatEnumerator *fe);
FormatEnumerator_QueryInterface(FormatEnumerator * fe,REFIID riid,PVOID * ppv)2572 STDMETHODIMP FormatEnumerator_QueryInterface(FormatEnumerator *fe, REFIID riid, PVOID *ppv) {
2573 	//Platform::DebugPrintf("EFE QI");
2574 	*ppv = nullptr;
2575 	if (riid == IID_IUnknown)
2576 		*ppv = reinterpret_cast<IEnumFORMATETC *>(fe);
2577 	if (riid == IID_IEnumFORMATETC)
2578 		*ppv = reinterpret_cast<IEnumFORMATETC *>(fe);
2579 	if (!*ppv)
2580 		return E_NOINTERFACE;
2581 	FormatEnumerator_AddRef(fe);
2582 	return S_OK;
2583 }
FormatEnumerator_AddRef(FormatEnumerator * fe)2584 STDMETHODIMP_(ULONG)FormatEnumerator_AddRef(FormatEnumerator *fe) {
2585 	return ++fe->ref;
2586 }
FormatEnumerator_Release(FormatEnumerator * fe)2587 STDMETHODIMP_(ULONG)FormatEnumerator_Release(FormatEnumerator *fe) {
2588 	fe->ref--;
2589 	if (fe->ref > 0)
2590 		return fe->ref;
2591 	delete fe;
2592 	return 0;
2593 }
2594 /// Implement IEnumFORMATETC
FormatEnumerator_Next(FormatEnumerator * fe,ULONG celt,FORMATETC * rgelt,ULONG * pceltFetched)2595 STDMETHODIMP FormatEnumerator_Next(FormatEnumerator *fe, ULONG celt, FORMATETC *rgelt, ULONG *pceltFetched) {
2596 	if (!rgelt) return E_POINTER;
2597 	ULONG putPos = 0;
2598 	while ((fe->pos < fe->formats.size()) && (putPos < celt)) {
2599 		rgelt->cfFormat = fe->formats[fe->pos];
2600 		rgelt->ptd = nullptr;
2601 		rgelt->dwAspect = DVASPECT_CONTENT;
2602 		rgelt->lindex = -1;
2603 		rgelt->tymed = TYMED_HGLOBAL;
2604 		rgelt++;
2605 		fe->pos++;
2606 		putPos++;
2607 	}
2608 	if (pceltFetched)
2609 		*pceltFetched = putPos;
2610 	return putPos ? S_OK : S_FALSE;
2611 }
FormatEnumerator_Skip(FormatEnumerator * fe,ULONG celt)2612 STDMETHODIMP FormatEnumerator_Skip(FormatEnumerator *fe, ULONG celt) {
2613 	fe->pos += celt;
2614 	return S_OK;
2615 }
FormatEnumerator_Reset(FormatEnumerator * fe)2616 STDMETHODIMP FormatEnumerator_Reset(FormatEnumerator *fe) {
2617 	fe->pos = 0;
2618 	return S_OK;
2619 }
FormatEnumerator_Clone(FormatEnumerator * fe,IEnumFORMATETC ** ppenum)2620 STDMETHODIMP FormatEnumerator_Clone(FormatEnumerator *fe, IEnumFORMATETC **ppenum) {
2621 	FormatEnumerator *pfe;
2622 	try {
2623 		pfe = new FormatEnumerator(fe->pos, &fe->formats[0], fe->formats.size());
2624 	} catch (...) {
2625 		return E_OUTOFMEMORY;
2626 	}
2627 	return FormatEnumerator_QueryInterface(pfe, IID_IEnumFORMATETC,
2628 	                                       reinterpret_cast<void **>(ppenum));
2629 }
2630 
2631 static VFunction *vtFormatEnumerator[] = {
2632 	(VFunction *)(FormatEnumerator_QueryInterface),
2633 	(VFunction *)(FormatEnumerator_AddRef),
2634 	(VFunction *)(FormatEnumerator_Release),
2635 	(VFunction *)(FormatEnumerator_Next),
2636 	(VFunction *)(FormatEnumerator_Skip),
2637 	(VFunction *)(FormatEnumerator_Reset),
2638 	(VFunction *)(FormatEnumerator_Clone)
2639 };
2640 
FormatEnumerator(ULONG pos_,const CLIPFORMAT formats_[],size_t formatsLen_)2641 FormatEnumerator::FormatEnumerator(ULONG pos_, const CLIPFORMAT formats_[], size_t formatsLen_) {
2642 	vtbl = vtFormatEnumerator;
2643 	ref = 0;   // First QI adds first reference...
2644 	pos = pos_;
2645 	formats.insert(formats.begin(), formats_, formats_+formatsLen_);
2646 }
2647 
2648 /// Implement IUnknown
DropSource_QueryInterface(DropSource * ds,REFIID riid,PVOID * ppv)2649 STDMETHODIMP DropSource_QueryInterface(DropSource *ds, REFIID riid, PVOID *ppv) {
2650 	return ds->sci->QueryInterface(riid, ppv);
2651 }
DropSource_AddRef(DropSource * ds)2652 STDMETHODIMP_(ULONG)DropSource_AddRef(DropSource *ds) {
2653 	return ds->sci->AddRef();
2654 }
DropSource_Release(DropSource * ds)2655 STDMETHODIMP_(ULONG)DropSource_Release(DropSource *ds) {
2656 	return ds->sci->Release();
2657 }
2658 
2659 /// Implement IDropSource
DropSource_QueryContinueDrag(DropSource *,BOOL fEsc,DWORD grfKeyState)2660 STDMETHODIMP DropSource_QueryContinueDrag(DropSource *, BOOL fEsc, DWORD grfKeyState) {
2661 	if (fEsc)
2662 		return DRAGDROP_S_CANCEL;
2663 	if (!(grfKeyState & MK_LBUTTON))
2664 		return DRAGDROP_S_DROP;
2665 	return S_OK;
2666 }
2667 
DropSource_GiveFeedback(DropSource *,DWORD)2668 STDMETHODIMP DropSource_GiveFeedback(DropSource *, DWORD) {
2669 	return DRAGDROP_S_USEDEFAULTCURSORS;
2670 }
2671 
2672 static VFunction *vtDropSource[] = {
2673 	(VFunction *)(DropSource_QueryInterface),
2674 	(VFunction *)(DropSource_AddRef),
2675 	(VFunction *)(DropSource_Release),
2676 	(VFunction *)(DropSource_QueryContinueDrag),
2677 	(VFunction *)(DropSource_GiveFeedback)
2678 };
2679 
DropSource()2680 DropSource::DropSource() noexcept {
2681 	vtbl = vtDropSource;
2682 	sci = nullptr;
2683 }
2684 
2685 /// Implement IUnkown
DataObject_QueryInterface(DataObject * pd,REFIID riid,PVOID * ppv)2686 STDMETHODIMP DataObject_QueryInterface(DataObject *pd, REFIID riid, PVOID *ppv) {
2687 	//Platform::DebugPrintf("DO QI %x\n", pd);
2688 	return pd->sci->QueryInterface(riid, ppv);
2689 }
DataObject_AddRef(DataObject * pd)2690 STDMETHODIMP_(ULONG)DataObject_AddRef(DataObject *pd) {
2691 	return pd->sci->AddRef();
2692 }
DataObject_Release(DataObject * pd)2693 STDMETHODIMP_(ULONG)DataObject_Release(DataObject *pd) {
2694 	return pd->sci->Release();
2695 }
2696 /// Implement IDataObject
DataObject_GetData(DataObject * pd,FORMATETC * pFEIn,STGMEDIUM * pSTM)2697 STDMETHODIMP DataObject_GetData(DataObject *pd, FORMATETC *pFEIn, STGMEDIUM *pSTM) {
2698 	return pd->sci->GetData(pFEIn, pSTM);
2699 }
2700 
DataObject_GetDataHere(DataObject *,FORMATETC *,STGMEDIUM *)2701 STDMETHODIMP DataObject_GetDataHere(DataObject *, FORMATETC *, STGMEDIUM *) {
2702 	//Platform::DebugPrintf("DOB GetDataHere\n");
2703 	return E_NOTIMPL;
2704 }
2705 
DataObject_QueryGetData(DataObject * pd,FORMATETC * pFE)2706 STDMETHODIMP DataObject_QueryGetData(DataObject *pd, FORMATETC *pFE) {
2707 	if (pd->sci->DragIsRectangularOK(pFE->cfFormat) && IsValidFormatEtc(pFE)) {
2708 		return S_OK;
2709 	}
2710 
2711 	if (SupportedFormat(pFE)) {
2712 		return S_OK;
2713 	} else {
2714 		return S_FALSE;
2715 	}
2716 }
2717 
DataObject_GetCanonicalFormatEtc(DataObject *,FORMATETC *,FORMATETC * pFEOut)2718 STDMETHODIMP DataObject_GetCanonicalFormatEtc(DataObject *, FORMATETC *, FORMATETC *pFEOut) {
2719 	//Platform::DebugPrintf("DOB GetCanon\n");
2720 	pFEOut->cfFormat = CF_UNICODETEXT;
2721 	pFEOut->ptd = nullptr;
2722 	pFEOut->dwAspect = DVASPECT_CONTENT;
2723 	pFEOut->lindex = -1;
2724 	pFEOut->tymed = TYMED_HGLOBAL;
2725 	return S_OK;
2726 }
2727 
DataObject_SetData(DataObject *,FORMATETC *,STGMEDIUM *,BOOL)2728 STDMETHODIMP DataObject_SetData(DataObject *, FORMATETC *, STGMEDIUM *, BOOL) {
2729 	//Platform::DebugPrintf("DOB SetData\n");
2730 	return E_FAIL;
2731 }
2732 
DataObject_EnumFormatEtc(DataObject * pd,DWORD dwDirection,IEnumFORMATETC ** ppEnum)2733 STDMETHODIMP DataObject_EnumFormatEtc(DataObject *pd, DWORD dwDirection, IEnumFORMATETC **ppEnum) {
2734 	try {
2735 		//Platform::DebugPrintf("DOB EnumFormatEtc %d\n", dwDirection);
2736 		if (dwDirection != DATADIR_GET) {
2737 			*ppEnum = nullptr;
2738 			return E_FAIL;
2739 		}
2740 
2741 		const CLIPFORMAT formats[] = {CF_UNICODETEXT};
2742 		FormatEnumerator *pfe = new FormatEnumerator(0, formats, std::size(formats));
2743 		return FormatEnumerator_QueryInterface(pfe, IID_IEnumFORMATETC,
2744 											   reinterpret_cast<void **>(ppEnum));
2745 	} catch (std::bad_alloc &) {
2746 		pd->sci->errorStatus = SC_STATUS_BADALLOC;
2747 		return E_OUTOFMEMORY;
2748 	} catch (...) {
2749 		pd->sci->errorStatus = SC_STATUS_FAILURE;
2750 		return E_FAIL;
2751 	}
2752 }
2753 
DataObject_DAdvise(DataObject *,FORMATETC *,DWORD,IAdviseSink *,PDWORD)2754 STDMETHODIMP DataObject_DAdvise(DataObject *, FORMATETC *, DWORD, IAdviseSink *, PDWORD) {
2755 	//Platform::DebugPrintf("DOB DAdvise\n");
2756 	return E_FAIL;
2757 }
2758 
DataObject_DUnadvise(DataObject *,DWORD)2759 STDMETHODIMP DataObject_DUnadvise(DataObject *, DWORD) {
2760 	//Platform::DebugPrintf("DOB DUnadvise\n");
2761 	return E_FAIL;
2762 }
2763 
DataObject_EnumDAdvise(DataObject *,IEnumSTATDATA **)2764 STDMETHODIMP DataObject_EnumDAdvise(DataObject *, IEnumSTATDATA **) {
2765 	//Platform::DebugPrintf("DOB EnumDAdvise\n");
2766 	return E_FAIL;
2767 }
2768 
2769 static VFunction *vtDataObject[] = {
2770 	(VFunction *)(DataObject_QueryInterface),
2771 	(VFunction *)(DataObject_AddRef),
2772 	(VFunction *)(DataObject_Release),
2773 	(VFunction *)(DataObject_GetData),
2774 	(VFunction *)(DataObject_GetDataHere),
2775 	(VFunction *)(DataObject_QueryGetData),
2776 	(VFunction *)(DataObject_GetCanonicalFormatEtc),
2777 	(VFunction *)(DataObject_SetData),
2778 	(VFunction *)(DataObject_EnumFormatEtc),
2779 	(VFunction *)(DataObject_DAdvise),
2780 	(VFunction *)(DataObject_DUnadvise),
2781 	(VFunction *)(DataObject_EnumDAdvise)
2782 };
2783 
DataObject()2784 DataObject::DataObject() noexcept {
2785 	vtbl = vtDataObject;
2786 	sci = nullptr;
2787 }
2788 
2789 /// Implement IUnknown
DropTarget_QueryInterface(DropTarget * dt,REFIID riid,PVOID * ppv)2790 STDMETHODIMP DropTarget_QueryInterface(DropTarget *dt, REFIID riid, PVOID *ppv) {
2791 	//Platform::DebugPrintf("DT QI %x\n", dt);
2792 	return dt->sci->QueryInterface(riid, ppv);
2793 }
DropTarget_AddRef(DropTarget * dt)2794 STDMETHODIMP_(ULONG)DropTarget_AddRef(DropTarget *dt) {
2795 	return dt->sci->AddRef();
2796 }
DropTarget_Release(DropTarget * dt)2797 STDMETHODIMP_(ULONG)DropTarget_Release(DropTarget *dt) {
2798 	return dt->sci->Release();
2799 }
2800 
2801 /// Implement IDropTarget by forwarding to Scintilla
DropTarget_DragEnter(DropTarget * dt,LPDATAOBJECT pIDataSource,DWORD grfKeyState,POINTL pt,PDWORD pdwEffect)2802 STDMETHODIMP DropTarget_DragEnter(DropTarget *dt, LPDATAOBJECT pIDataSource, DWORD grfKeyState,
2803                                   POINTL pt, PDWORD pdwEffect) {
2804 	try {
2805 		return dt->sci->DragEnter(pIDataSource, grfKeyState, pt, pdwEffect);
2806 	} catch (...) {
2807 		dt->sci->errorStatus = SC_STATUS_FAILURE;
2808 	}
2809 	return E_FAIL;
2810 }
DropTarget_DragOver(DropTarget * dt,DWORD grfKeyState,POINTL pt,PDWORD pdwEffect)2811 STDMETHODIMP DropTarget_DragOver(DropTarget *dt, DWORD grfKeyState, POINTL pt, PDWORD pdwEffect) {
2812 	try {
2813 		return dt->sci->DragOver(grfKeyState, pt, pdwEffect);
2814 	} catch (...) {
2815 		dt->sci->errorStatus = SC_STATUS_FAILURE;
2816 	}
2817 	return E_FAIL;
2818 }
DropTarget_DragLeave(DropTarget * dt)2819 STDMETHODIMP DropTarget_DragLeave(DropTarget *dt) {
2820 	try {
2821 		return dt->sci->DragLeave();
2822 	} catch (...) {
2823 		dt->sci->errorStatus = SC_STATUS_FAILURE;
2824 	}
2825 	return E_FAIL;
2826 }
DropTarget_Drop(DropTarget * dt,LPDATAOBJECT pIDataSource,DWORD grfKeyState,POINTL pt,PDWORD pdwEffect)2827 STDMETHODIMP DropTarget_Drop(DropTarget *dt, LPDATAOBJECT pIDataSource, DWORD grfKeyState,
2828                              POINTL pt, PDWORD pdwEffect) {
2829 	try {
2830 		return dt->sci->Drop(pIDataSource, grfKeyState, pt, pdwEffect);
2831 	} catch (...) {
2832 		dt->sci->errorStatus = SC_STATUS_FAILURE;
2833 	}
2834 	return E_FAIL;
2835 }
2836 
2837 static VFunction *vtDropTarget[] = {
2838 	(VFunction *)(DropTarget_QueryInterface),
2839 	(VFunction *)(DropTarget_AddRef),
2840 	(VFunction *)(DropTarget_Release),
2841 	(VFunction *)(DropTarget_DragEnter),
2842 	(VFunction *)(DropTarget_DragOver),
2843 	(VFunction *)(DropTarget_DragLeave),
2844 	(VFunction *)(DropTarget_Drop)
2845 };
2846 
DropTarget()2847 DropTarget::DropTarget() noexcept {
2848 	vtbl = vtDropTarget;
2849 	sci = nullptr;
2850 }
2851 
2852 /**
2853  * DBCS: support Input Method Editor (IME).
2854  * Called when IME Window opened.
2855  */
ImeStartComposition()2856 void ScintillaWin::ImeStartComposition() {
2857 	if (caret.active) {
2858 		// Move IME Window to current caret position
2859 		IMContext imc(MainHWND());
2860 		const Point pos = PointMainCaret();
2861 		COMPOSITIONFORM CompForm;
2862 		CompForm.dwStyle = CFS_POINT;
2863 		CompForm.ptCurrentPos = POINTFromPoint(pos);
2864 
2865 		::ImmSetCompositionWindow(imc.hIMC, &CompForm);
2866 
2867 		// Set font of IME window to same as surrounded text.
2868 		if (stylesValid) {
2869 			// Since the style creation code has been made platform independent,
2870 			// The logfont for the IME is recreated here.
2871 			const int styleHere = pdoc->StyleIndexAt(sel.MainCaret());
2872 			LOGFONTW lf = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, L""};
2873 			int sizeZoomed = vs.styles[styleHere].size + vs.zoomLevel * SC_FONT_SIZE_MULTIPLIER;
2874 			if (sizeZoomed <= 2 * SC_FONT_SIZE_MULTIPLIER)	// Hangs if sizeZoomed <= 1
2875 				sizeZoomed = 2 * SC_FONT_SIZE_MULTIPLIER;
2876 			// The negative is to allow for leading
2877 			lf.lfHeight = -::MulDiv(sizeZoomed, dpi, 72*SC_FONT_SIZE_MULTIPLIER);
2878 			lf.lfWeight = vs.styles[styleHere].weight;
2879 			lf.lfItalic = vs.styles[styleHere].italic ? 1 : 0;
2880 			lf.lfCharSet = DEFAULT_CHARSET;
2881 			lf.lfFaceName[0] = L'\0';
2882 			if (vs.styles[styleHere].fontName) {
2883 				const char* fontName = vs.styles[styleHere].fontName;
2884 				UTF16FromUTF8(std::string_view(fontName), lf.lfFaceName, LF_FACESIZE);
2885 			}
2886 
2887 			::ImmSetCompositionFontW(imc.hIMC, &lf);
2888 		}
2889 		// Caret is displayed in IME window. So, caret in Scintilla is useless.
2890 		DropCaret();
2891 	}
2892 }
2893 
2894 /** Called when IME Window closed. */
ImeEndComposition()2895 void ScintillaWin::ImeEndComposition() {
2896 	// clear IME composition state.
2897 	view.imeCaretBlockOverride = false;
2898 	pdoc->TentativeUndo();
2899 	ShowCaretAtCurrentPosition();
2900 }
2901 
ImeOnReconvert(LPARAM lParam)2902 LRESULT ScintillaWin::ImeOnReconvert(LPARAM lParam) {
2903 	// Reconversion on windows limits within one line without eol.
2904 	// Look around:   baseStart  <--  (|mainStart|  -- mainEnd)  --> baseEnd.
2905 	const Sci::Position mainStart = sel.RangeMain().Start().Position();
2906 	const Sci::Position mainEnd = sel.RangeMain().End().Position();
2907 	const Sci::Line curLine = pdoc->SciLineFromPosition(mainStart);
2908 	if (curLine != pdoc->LineFromPosition(mainEnd))
2909 		return 0;
2910 	const Sci::Position baseStart = pdoc->LineStart(curLine);
2911 	const Sci::Position baseEnd = pdoc->LineEnd(curLine);
2912 	if ((baseStart == baseEnd) || (mainEnd > baseEnd))
2913 		return 0;
2914 
2915 	const int codePage = CodePageOfDocument();
2916 	const std::wstring rcFeed = StringDecode(RangeText(baseStart, baseEnd), codePage);
2917 	const int rcFeedLen = static_cast<int>(rcFeed.length()) * sizeof(wchar_t);
2918 	const int rcSize = sizeof(RECONVERTSTRING) + rcFeedLen + sizeof(wchar_t);
2919 
2920 	RECONVERTSTRING *rc = static_cast<RECONVERTSTRING *>(PtrFromSPtr(lParam));
2921 	if (!rc)
2922 		return rcSize; // Immediately be back with rcSize of memory block.
2923 
2924 	wchar_t *rcFeedStart = reinterpret_cast<wchar_t*>(rc + 1);
2925 	memcpy(rcFeedStart, &rcFeed[0], rcFeedLen);
2926 
2927 	std::string rcCompString = RangeText(mainStart, mainEnd);
2928 	std::wstring rcCompWstring = StringDecode(rcCompString, codePage);
2929 	std::string rcCompStart = RangeText(baseStart, mainStart);
2930 	std::wstring rcCompWstart = StringDecode(rcCompStart, codePage);
2931 
2932 	// Map selection to dwCompStr.
2933 	// No selection assumes current caret as rcCompString without length.
2934 	rc->dwVersion = 0; // It should be absolutely 0.
2935 	rc->dwStrLen = static_cast<DWORD>(rcFeed.length());
2936 	rc->dwStrOffset = sizeof(RECONVERTSTRING);
2937 	rc->dwCompStrLen = static_cast<DWORD>(rcCompWstring.length());
2938 	rc->dwCompStrOffset = static_cast<DWORD>(rcCompWstart.length()) * sizeof(wchar_t);
2939 	rc->dwTargetStrLen = rc->dwCompStrLen;
2940 	rc->dwTargetStrOffset =rc->dwCompStrOffset;
2941 
2942 	IMContext imc(MainHWND());
2943 	if (!imc.hIMC)
2944 		return 0;
2945 
2946 	if (!::ImmSetCompositionStringW(imc.hIMC, SCS_QUERYRECONVERTSTRING, rc, rcSize, nullptr, 0))
2947 		return 0;
2948 
2949 	// No selection asks IME to fill target fields with its own value.
2950 	const int tgWlen = rc->dwTargetStrLen;
2951 	const int tgWstart = rc->dwTargetStrOffset / sizeof(wchar_t);
2952 
2953 	std::string tgCompStart = StringEncode(rcFeed.substr(0, tgWstart), codePage);
2954 	std::string tgComp = StringEncode(rcFeed.substr(tgWstart, tgWlen), codePage);
2955 
2956 	// No selection needs to adjust reconvert start position for IME set.
2957 	const int adjust = static_cast<int>(tgCompStart.length() - rcCompStart.length());
2958 	const int docCompLen = static_cast<int>(tgComp.length());
2959 
2960 	// Make place for next composition string to sit in.
2961 	for (size_t r=0; r<sel.Count(); r++) {
2962 		const Sci::Position rBase = sel.Range(r).Start().Position();
2963 		const Sci::Position docCompStart = rBase + adjust;
2964 
2965 		if (inOverstrike) { // the docCompLen of bytes will be overstriked.
2966 			sel.Range(r).caret.SetPosition(docCompStart);
2967 			sel.Range(r).anchor.SetPosition(docCompStart);
2968 		} else {
2969 			// Ensure docCompStart+docCompLen be not beyond lineEnd.
2970 			// since docCompLen by byte might break eol.
2971 			const Sci::Position lineEnd = pdoc->LineEnd(pdoc->LineFromPosition(rBase));
2972 			const Sci::Position overflow = (docCompStart + docCompLen) - lineEnd;
2973 			if (overflow > 0) {
2974 				pdoc->DeleteChars(docCompStart, docCompLen - overflow);
2975 			} else {
2976 				pdoc->DeleteChars(docCompStart, docCompLen);
2977 			}
2978 		}
2979 	}
2980 	// Immediately Target Input or candidate box choice with GCS_COMPSTR.
2981 	return rcSize;
2982 }
2983 
GetIntelliMouseParameters()2984 void ScintillaWin::GetIntelliMouseParameters() noexcept {
2985 	// This retrieves the number of lines per scroll as configured in the Mouse Properties sheet in Control Panel
2986 	::SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, &linesPerScroll, 0);
2987 }
2988 
CopyToGlobal(GlobalMemory & gmUnicode,const SelectionText & selectedText)2989 void ScintillaWin::CopyToGlobal(GlobalMemory &gmUnicode, const SelectionText &selectedText) {
2990 	const std::string_view svSelected(selectedText.Data(), selectedText.LengthWithTerminator());
2991 	if (IsUnicodeMode()) {
2992 		const size_t uchars = UTF16Length(svSelected);
2993 		gmUnicode.Allocate(2 * uchars);
2994 		if (gmUnicode) {
2995 			UTF16FromUTF8(svSelected,
2996 				static_cast<wchar_t *>(gmUnicode.ptr), uchars);
2997 		}
2998 	} else {
2999 		// Not Unicode mode
3000 		// Convert to Unicode using the current Scintilla code page
3001 		const UINT cpSrc = CodePageFromCharSet(
3002 			selectedText.characterSet, selectedText.codePage);
3003 		const size_t uLen = WideCharLenFromMultiByte(cpSrc, svSelected);
3004 		gmUnicode.Allocate(2 * uLen);
3005 		if (gmUnicode) {
3006 			WideCharFromMultiByte(cpSrc, svSelected,
3007 				static_cast<wchar_t *>(gmUnicode.ptr), uLen);
3008 		}
3009 	}
3010 }
3011 
CopyToClipboard(const SelectionText & selectedText)3012 void ScintillaWin::CopyToClipboard(const SelectionText &selectedText) {
3013 	if (!::OpenClipboardRetry(MainHWND())) {
3014 		return;
3015 	}
3016 	::EmptyClipboard();
3017 
3018 	GlobalMemory uniText;
3019 	CopyToGlobal(uniText, selectedText);
3020 	if (uniText) {
3021 		uniText.SetClip(CF_UNICODETEXT);
3022 	}
3023 
3024 	if (selectedText.rectangular) {
3025 		::SetClipboardData(cfColumnSelect, 0);
3026 
3027 		GlobalMemory borlandSelection;
3028 		borlandSelection.Allocate(1);
3029 		if (borlandSelection) {
3030 			static_cast<BYTE *>(borlandSelection.ptr)[0] = 0x02;
3031 			borlandSelection.SetClip(cfBorlandIDEBlockType);
3032 		}
3033 	}
3034 
3035 	if (selectedText.lineCopy) {
3036 		::SetClipboardData(cfLineSelect, 0);
3037 		::SetClipboardData(cfVSLineTag, 0);
3038 	}
3039 
3040 	::CloseClipboard();
3041 }
3042 
ScrollMessage(WPARAM wParam)3043 void ScintillaWin::ScrollMessage(WPARAM wParam) {
3044 	//DWORD dwStart = timeGetTime();
3045 	//Platform::DebugPrintf("Scroll %x %d\n", wParam, lParam);
3046 
3047 	SCROLLINFO sci = {};
3048 	sci.cbSize = sizeof(sci);
3049 	sci.fMask = SIF_ALL;
3050 
3051 	GetScrollInfo(SB_VERT, &sci);
3052 
3053 	//Platform::DebugPrintf("ScrollInfo %d mask=%x min=%d max=%d page=%d pos=%d track=%d\n", b,sci.fMask,
3054 	//sci.nMin, sci.nMax, sci.nPage, sci.nPos, sci.nTrackPos);
3055 	Sci::Line topLineNew = topLine;
3056 	switch (LOWORD(wParam)) {
3057 	case SB_LINEUP:
3058 		topLineNew -= 1;
3059 		break;
3060 	case SB_LINEDOWN:
3061 		topLineNew += 1;
3062 		break;
3063 	case SB_PAGEUP:
3064 		topLineNew -= LinesToScroll(); break;
3065 	case SB_PAGEDOWN: topLineNew += LinesToScroll(); break;
3066 	case SB_TOP: topLineNew = 0; break;
3067 	case SB_BOTTOM: topLineNew = MaxScrollPos(); break;
3068 	case SB_THUMBPOSITION: topLineNew = sci.nTrackPos; break;
3069 	case SB_THUMBTRACK: topLineNew = sci.nTrackPos; break;
3070 	}
3071 	ScrollTo(topLineNew);
3072 }
3073 
HorizontalScrollMessage(WPARAM wParam)3074 void ScintillaWin::HorizontalScrollMessage(WPARAM wParam) {
3075 	int xPos = xOffset;
3076 	const PRectangle rcText = GetTextRectangle();
3077 	const int pageWidth = static_cast<int>(rcText.Width() * 2 / 3);
3078 	switch (LOWORD(wParam)) {
3079 	case SB_LINEUP:
3080 		xPos -= 20;
3081 		break;
3082 	case SB_LINEDOWN:	// May move past the logical end
3083 		xPos += 20;
3084 		break;
3085 	case SB_PAGEUP:
3086 		xPos -= pageWidth;
3087 		break;
3088 	case SB_PAGEDOWN:
3089 		xPos += pageWidth;
3090 		if (xPos > scrollWidth - rcText.Width()) {	// Hit the end exactly
3091 			xPos = scrollWidth - static_cast<int>(rcText.Width());
3092 		}
3093 		break;
3094 	case SB_TOP:
3095 		xPos = 0;
3096 		break;
3097 	case SB_BOTTOM:
3098 		xPos = scrollWidth;
3099 		break;
3100 	case SB_THUMBPOSITION:
3101 	case SB_THUMBTRACK: {
3102 			// 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 =]
3103 			SCROLLINFO si;
3104 			si.cbSize = sizeof(si);
3105 			si.fMask = SIF_TRACKPOS;
3106 			if (GetScrollInfo(SB_HORZ, &si)) {
3107 				xPos = si.nTrackPos;
3108 			}
3109 		}
3110 		break;
3111 	}
3112 	HorizontalScrollTo(xPos);
3113 }
3114 
3115 /**
3116  * Redraw all of text area.
3117  * This paint will not be abandoned.
3118  */
FullPaint()3119 void ScintillaWin::FullPaint() {
3120 	if ((technology == SC_TECHNOLOGY_DEFAULT) || (technology == SC_TECHNOLOGY_DIRECTWRITEDC)) {
3121 		HDC hdc = ::GetDC(MainHWND());
3122 		FullPaintDC(hdc);
3123 		::ReleaseDC(MainHWND(), hdc);
3124 	} else {
3125 		FullPaintDC({});
3126 	}
3127 }
3128 
3129 /**
3130  * Redraw all of text area on the specified DC.
3131  * This paint will not be abandoned.
3132  */
FullPaintDC(HDC hdc)3133 void ScintillaWin::FullPaintDC(HDC hdc) {
3134 	paintState = painting;
3135 	rcPaint = GetClientRectangle();
3136 	paintingAllText = true;
3137 	PaintDC(hdc);
3138 	paintState = notPainting;
3139 }
3140 
3141 namespace {
3142 
CompareDevCap(HDC hdc,HDC hOtherDC,int nIndex)3143 bool CompareDevCap(HDC hdc, HDC hOtherDC, int nIndex) noexcept {
3144 	return ::GetDeviceCaps(hdc, nIndex) == ::GetDeviceCaps(hOtherDC, nIndex);
3145 }
3146 
3147 }
3148 
IsCompatibleDC(HDC hOtherDC)3149 bool ScintillaWin::IsCompatibleDC(HDC hOtherDC) noexcept {
3150 	HDC hdc = ::GetDC(MainHWND());
3151 	const bool isCompatible =
3152 		CompareDevCap(hdc, hOtherDC, TECHNOLOGY) &&
3153 		CompareDevCap(hdc, hOtherDC, LOGPIXELSY) &&
3154 		CompareDevCap(hdc, hOtherDC, LOGPIXELSX) &&
3155 		CompareDevCap(hdc, hOtherDC, BITSPIXEL) &&
3156 		CompareDevCap(hdc, hOtherDC, PLANES);
3157 	::ReleaseDC(MainHWND(), hdc);
3158 	return isCompatible;
3159 }
3160 
EffectFromState(DWORD grfKeyState) const3161 DWORD ScintillaWin::EffectFromState(DWORD grfKeyState) const noexcept {
3162 	// These are the Wordpad semantics.
3163 	DWORD dwEffect;
3164 	if (inDragDrop == ddDragging)	// Internal defaults to move
3165 		dwEffect = DROPEFFECT_MOVE;
3166 	else
3167 		dwEffect = DROPEFFECT_COPY;
3168 	if (grfKeyState & MK_ALT)
3169 		dwEffect = DROPEFFECT_MOVE;
3170 	if (grfKeyState & MK_CONTROL)
3171 		dwEffect = DROPEFFECT_COPY;
3172 	return dwEffect;
3173 }
3174 
3175 /// Implement IUnknown
QueryInterface(REFIID riid,PVOID * ppv)3176 STDMETHODIMP ScintillaWin::QueryInterface(REFIID riid, PVOID *ppv) {
3177 	*ppv = nullptr;
3178 	if (riid == IID_IUnknown)
3179 		*ppv = reinterpret_cast<IDropTarget *>(&dt);
3180 	if (riid == IID_IDropSource)
3181 		*ppv = reinterpret_cast<IDropSource *>(&ds);
3182 	if (riid == IID_IDropTarget)
3183 		*ppv = reinterpret_cast<IDropTarget *>(&dt);
3184 	if (riid == IID_IDataObject)
3185 		*ppv = reinterpret_cast<IDataObject *>(&dob);
3186 	if (!*ppv)
3187 		return E_NOINTERFACE;
3188 	return S_OK;
3189 }
3190 
STDMETHODIMP_(ULONG)3191 STDMETHODIMP_(ULONG) ScintillaWin::AddRef() {
3192 	return 1;
3193 }
3194 
STDMETHODIMP_(ULONG)3195 STDMETHODIMP_(ULONG) ScintillaWin::Release() {
3196 	return 1;
3197 }
3198 
3199 /// Implement IDropTarget
DragEnter(LPDATAOBJECT pIDataSource,DWORD grfKeyState,POINTL,PDWORD pdwEffect)3200 STDMETHODIMP ScintillaWin::DragEnter(LPDATAOBJECT pIDataSource, DWORD grfKeyState,
3201                                      POINTL, PDWORD pdwEffect) {
3202 	if (!pIDataSource )
3203 		return E_POINTER;
3204 	FORMATETC fmtu = {CF_UNICODETEXT, nullptr, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
3205 	const HRESULT hrHasUText = pIDataSource->QueryGetData(&fmtu);
3206 	hasOKText = (hrHasUText == S_OK);
3207 	if (hasOKText) {
3208 		*pdwEffect = EffectFromState(grfKeyState);
3209 	} else {
3210 		*pdwEffect = DROPEFFECT_NONE;
3211 	}
3212 
3213 	return S_OK;
3214 }
3215 
DragOver(DWORD grfKeyState,POINTL pt,PDWORD pdwEffect)3216 STDMETHODIMP ScintillaWin::DragOver(DWORD grfKeyState, POINTL pt, PDWORD pdwEffect) {
3217 	try {
3218 		if (!hasOKText || pdoc->IsReadOnly()) {
3219 			*pdwEffect = DROPEFFECT_NONE;
3220 			return S_OK;
3221 		}
3222 
3223 		*pdwEffect = EffectFromState(grfKeyState);
3224 
3225 		// Update the cursor.
3226 		POINT rpt = {pt.x, pt.y};
3227 		::ScreenToClient(MainHWND(), &rpt);
3228 		SetDragPosition(SPositionFromLocation(PointFromPOINT(rpt), false, false, UserVirtualSpace()));
3229 
3230 		return S_OK;
3231 	} catch (...) {
3232 		errorStatus = SC_STATUS_FAILURE;
3233 	}
3234 	return E_FAIL;
3235 }
3236 
DragLeave()3237 STDMETHODIMP ScintillaWin::DragLeave() {
3238 	try {
3239 		SetDragPosition(SelectionPosition(Sci::invalidPosition));
3240 		return S_OK;
3241 	} catch (...) {
3242 		errorStatus = SC_STATUS_FAILURE;
3243 	}
3244 	return E_FAIL;
3245 }
3246 
Drop(LPDATAOBJECT pIDataSource,DWORD grfKeyState,POINTL pt,PDWORD pdwEffect)3247 STDMETHODIMP ScintillaWin::Drop(LPDATAOBJECT pIDataSource, DWORD grfKeyState,
3248                                 POINTL pt, PDWORD pdwEffect) {
3249 	try {
3250 		*pdwEffect = EffectFromState(grfKeyState);
3251 
3252 		if (!pIDataSource)
3253 			return E_POINTER;
3254 
3255 		SetDragPosition(SelectionPosition(Sci::invalidPosition));
3256 
3257 		std::string putf;
3258 		FORMATETC fmtu = {CF_UNICODETEXT, nullptr, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
3259 		STGMEDIUM medium{};
3260 		const HRESULT hr = pIDataSource->GetData(&fmtu, &medium);
3261 		if (!SUCCEEDED(hr)) {
3262 			return hr;
3263 		}
3264 		if (medium.hGlobal) {
3265 			GlobalMemory memUDrop(medium.hGlobal);
3266 			if (const wchar_t *uptr = static_cast<const wchar_t *>(memUDrop.ptr)) {
3267 				putf = EncodeWString(uptr);
3268 			}
3269 			memUDrop.Unlock();
3270 		}
3271 
3272 		if (putf.empty()) {
3273 			return S_OK;
3274 		}
3275 
3276 		FORMATETC fmtr = {cfColumnSelect, nullptr, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
3277 		const bool isRectangular = S_OK == pIDataSource->QueryGetData(&fmtr);
3278 
3279 		POINT rpt = {pt.x, pt.y};
3280 		::ScreenToClient(MainHWND(), &rpt);
3281 		const SelectionPosition movePos = SPositionFromLocation(PointFromPOINT(rpt), false, false, UserVirtualSpace());
3282 
3283 		DropAt(movePos, putf.c_str(), putf.size(), *pdwEffect == DROPEFFECT_MOVE, isRectangular);
3284 
3285 		// Free data
3286 		::ReleaseStgMedium(&medium);
3287 
3288 		return S_OK;
3289 	} catch (...) {
3290 		errorStatus = SC_STATUS_FAILURE;
3291 	}
3292 	return E_FAIL;
3293 }
3294 
3295 /// Implement important part of IDataObject
GetData(FORMATETC * pFEIn,STGMEDIUM * pSTM)3296 STDMETHODIMP ScintillaWin::GetData(FORMATETC *pFEIn, STGMEDIUM *pSTM) {
3297 	if (!SupportedFormat(pFEIn)) {
3298 		//Platform::DebugPrintf("DOB GetData No %d %x %x fmt=%x\n", lenDrag, pFEIn, pSTM, pFEIn->cfFormat);
3299 		return DATA_E_FORMATETC;
3300 	}
3301 
3302 	pSTM->tymed = TYMED_HGLOBAL;
3303 	//Platform::DebugPrintf("DOB GetData OK %d %x %x\n", lenDrag, pFEIn, pSTM);
3304 
3305 	GlobalMemory uniText;
3306 	CopyToGlobal(uniText, drag);
3307 	pSTM->hGlobal = uniText ? uniText.Unlock() : 0;
3308 	pSTM->pUnkForRelease = nullptr;
3309 	return S_OK;
3310 }
3311 
Prepare()3312 void ScintillaWin::Prepare() noexcept {
3313 	Platform_Initialise(hInstance);
3314 
3315 	// Register the CallTip class
3316 	WNDCLASSEX wndclassc{};
3317 	wndclassc.cbSize = sizeof(wndclassc);
3318 	wndclassc.style = CS_GLOBALCLASS | CS_HREDRAW | CS_VREDRAW;
3319 	wndclassc.cbWndExtra = sizeof(ScintillaWin *);
3320 	wndclassc.hInstance = hInstance;
3321 	wndclassc.lpfnWndProc = ScintillaWin::CTWndProc;
3322 	wndclassc.hCursor = ::LoadCursor(NULL, IDC_ARROW);
3323 	wndclassc.lpszClassName = callClassName;
3324 
3325 	callClassAtom = ::RegisterClassEx(&wndclassc);
3326 }
3327 
Register(HINSTANCE hInstance_)3328 bool ScintillaWin::Register(HINSTANCE hInstance_) noexcept {
3329 
3330 	hInstance = hInstance_;
3331 
3332 	// Register the Scintilla class
3333 	// Register Scintilla as a wide character window
3334 	WNDCLASSEXW wndclass {};
3335 	wndclass.cbSize = sizeof(wndclass);
3336 	wndclass.style = CS_GLOBALCLASS | CS_HREDRAW | CS_VREDRAW;
3337 	wndclass.lpfnWndProc = ScintillaWin::SWndProc;
3338 	wndclass.cbWndExtra = sizeof(ScintillaWin *);
3339 	wndclass.hInstance = hInstance;
3340 	wndclass.lpszClassName = L"Scintilla";
3341 	scintillaClassAtom = ::RegisterClassExW(&wndclass);
3342 	const bool result = 0 != scintillaClassAtom;
3343 
3344 	return result;
3345 }
3346 
Unregister()3347 bool ScintillaWin::Unregister() noexcept {
3348 	bool result = true;
3349 	if (0 != scintillaClassAtom) {
3350 		if (::UnregisterClass(MAKEINTATOM(scintillaClassAtom), hInstance) == 0) {
3351 			result = false;
3352 		}
3353 		scintillaClassAtom = 0;
3354 	}
3355 	if (0 != callClassAtom) {
3356 		if (::UnregisterClass(MAKEINTATOM(callClassAtom), hInstance) == 0) {
3357 			result = false;
3358 		}
3359 		callClassAtom = 0;
3360 	}
3361 	return result;
3362 }
3363 
HasCaretSizeChanged() const3364 bool ScintillaWin::HasCaretSizeChanged() const noexcept {
3365 	if (
3366 		( (0 != vs.caretWidth) && (sysCaretWidth != vs.caretWidth) )
3367 		|| ((0 != vs.lineHeight) && (sysCaretHeight != vs.lineHeight))
3368 		) {
3369 		return true;
3370 	}
3371 	return false;
3372 }
3373 
CreateSystemCaret()3374 BOOL ScintillaWin::CreateSystemCaret() {
3375 	sysCaretWidth = vs.caretWidth;
3376 	if (0 == sysCaretWidth) {
3377 		sysCaretWidth = 1;
3378 	}
3379 	sysCaretHeight = vs.lineHeight;
3380 	const int bitmapSize = (((sysCaretWidth + 15) & ~15) >> 3) *
3381 		sysCaretHeight;
3382 	std::vector<BYTE> bits(bitmapSize);
3383 	sysCaretBitmap = ::CreateBitmap(sysCaretWidth, sysCaretHeight, 1,
3384 		1, &bits[0]);
3385 	const BOOL retval = ::CreateCaret(
3386 		MainHWND(), sysCaretBitmap,
3387 		sysCaretWidth, sysCaretHeight);
3388 	if (technology == SC_TECHNOLOGY_DEFAULT) {
3389 		// System caret interferes with Direct2D drawing so only show it for GDI.
3390 		::ShowCaret(MainHWND());
3391 	}
3392 	return retval;
3393 }
3394 
DestroySystemCaret()3395 BOOL ScintillaWin::DestroySystemCaret() noexcept {
3396 	::HideCaret(MainHWND());
3397 	const BOOL retval = ::DestroyCaret();
3398 	if (sysCaretBitmap) {
3399 		::DeleteObject(sysCaretBitmap);
3400 		sysCaretBitmap = {};
3401 	}
3402 	return retval;
3403 }
3404 
CTWndProc(HWND hWnd,UINT iMessage,WPARAM wParam,LPARAM lParam)3405 LRESULT PASCAL ScintillaWin::CTWndProc(
3406 	HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam) {
3407 	// Find C++ object associated with window.
3408 	ScintillaWin *sciThis = static_cast<ScintillaWin *>(PointerFromWindow(hWnd));
3409 	try {
3410 		// ctp will be zero if WM_CREATE not seen yet
3411 		if (sciThis == nullptr) {
3412 			if (iMessage == WM_CREATE) {
3413 				// Associate CallTip object with window
3414 				CREATESTRUCT *pCreate = static_cast<CREATESTRUCT *>(PtrFromSPtr(lParam));
3415 				SetWindowPointer(hWnd, pCreate->lpCreateParams);
3416 				return 0;
3417 			} else {
3418 				return ::DefWindowProc(hWnd, iMessage, wParam, lParam);
3419 			}
3420 		} else {
3421 			if (iMessage == WM_NCDESTROY) {
3422 				SetWindowPointer(hWnd, nullptr);
3423 				return ::DefWindowProc(hWnd, iMessage, wParam, lParam);
3424 			} else if (iMessage == WM_PAINT) {
3425 				PAINTSTRUCT ps;
3426 				::BeginPaint(hWnd, &ps);
3427 				std::unique_ptr<Surface> surfaceWindow(Surface::Allocate(sciThis->technology));
3428 #if defined(USE_D2D)
3429 				ID2D1HwndRenderTarget *pCTRenderTarget = nullptr;
3430 #endif
3431 				RECT rc;
3432 				GetClientRect(hWnd, &rc);
3433 				// Create a Direct2D render target.
3434 				if (sciThis->technology == SC_TECHNOLOGY_DEFAULT) {
3435 					surfaceWindow->Init(ps.hdc, hWnd);
3436 				} else {
3437 #if defined(USE_D2D)
3438 					D2D1_HWND_RENDER_TARGET_PROPERTIES dhrtp;
3439 					dhrtp.hwnd = hWnd;
3440 					dhrtp.pixelSize = D2D1::SizeU(rc.right - rc.left, rc.bottom - rc.top);
3441 					dhrtp.presentOptions = (sciThis->technology == SC_TECHNOLOGY_DIRECTWRITERETAIN) ?
3442 						D2D1_PRESENT_OPTIONS_RETAIN_CONTENTS : D2D1_PRESENT_OPTIONS_NONE;
3443 
3444 					D2D1_RENDER_TARGET_PROPERTIES drtp;
3445 					drtp.type = D2D1_RENDER_TARGET_TYPE_DEFAULT;
3446 					drtp.pixelFormat.format = DXGI_FORMAT_UNKNOWN;
3447 					drtp.pixelFormat.alphaMode = D2D1_ALPHA_MODE_UNKNOWN;
3448 					drtp.dpiX = 96.0;
3449 					drtp.dpiY = 96.0;
3450 					drtp.usage = D2D1_RENDER_TARGET_USAGE_NONE;
3451 					drtp.minLevel = D2D1_FEATURE_LEVEL_DEFAULT;
3452 
3453 					if (!SUCCEEDED(pD2DFactory->CreateHwndRenderTarget(drtp, dhrtp, &pCTRenderTarget))) {
3454 						surfaceWindow->Release();
3455 						::EndPaint(hWnd, &ps);
3456 						return 0;
3457 					}
3458 					// If above SUCCEEDED, then pCTRenderTarget not nullptr
3459 					assert(pCTRenderTarget);
3460 					if (pCTRenderTarget) {
3461 						surfaceWindow->Init(pCTRenderTarget, hWnd);
3462 						pCTRenderTarget->BeginDraw();
3463 					}
3464 #endif
3465 				}
3466 				surfaceWindow->SetUnicodeMode(SC_CP_UTF8 == sciThis->ct.codePage);
3467 				surfaceWindow->SetDBCSMode(sciThis->ct.codePage);
3468 				surfaceWindow->SetBidiR2L(sciThis->BidirectionalR2L());
3469 				sciThis->ct.PaintCT(surfaceWindow.get());
3470 #if defined(USE_D2D)
3471 				if (pCTRenderTarget)
3472 					pCTRenderTarget->EndDraw();
3473 #endif
3474 				surfaceWindow->Release();
3475 #if defined(USE_D2D)
3476 				ReleaseUnknown(pCTRenderTarget);
3477 #endif
3478 				::EndPaint(hWnd, &ps);
3479 				return 0;
3480 			} else if ((iMessage == WM_NCLBUTTONDOWN) || (iMessage == WM_NCLBUTTONDBLCLK)) {
3481 				POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
3482 				ScreenToClient(hWnd, &pt);
3483 				sciThis->ct.MouseClick(PointFromPOINT(pt));
3484 				sciThis->CallTipClick();
3485 				return 0;
3486 			} else if (iMessage == WM_LBUTTONDOWN) {
3487 				// This does not fire due to the hit test code
3488 				sciThis->ct.MouseClick(PointFromLParam(lParam));
3489 				sciThis->CallTipClick();
3490 				return 0;
3491 			} else if (iMessage == WM_SETCURSOR) {
3492 				::SetCursor(::LoadCursor(NULL, IDC_ARROW));
3493 				return 0;
3494 			} else if (iMessage == WM_NCHITTEST) {
3495 				return HTCAPTION;
3496 			} else {
3497 				return ::DefWindowProc(hWnd, iMessage, wParam, lParam);
3498 			}
3499 		}
3500 	} catch (...) {
3501 		sciThis->errorStatus = SC_STATUS_FAILURE;
3502 	}
3503 	return ::DefWindowProc(hWnd, iMessage, wParam, lParam);
3504 }
3505 
DirectFunction(sptr_t ptr,UINT iMessage,uptr_t wParam,sptr_t lParam)3506 sptr_t ScintillaWin::DirectFunction(
3507     sptr_t ptr, UINT iMessage, uptr_t wParam, sptr_t lParam) {
3508 	PLATFORM_ASSERT(::GetCurrentThreadId() == ::GetWindowThreadProcessId(reinterpret_cast<ScintillaWin *>(ptr)->MainHWND(), nullptr));
3509 	return reinterpret_cast<ScintillaWin *>(ptr)->WndProc(iMessage, wParam, lParam);
3510 }
3511 
3512 namespace Scintilla {
3513 
DirectFunction(ScintillaWin * sci,UINT iMessage,uptr_t wParam,sptr_t lParam)3514 sptr_t DirectFunction(
3515     ScintillaWin *sci, UINT iMessage, uptr_t wParam, sptr_t lParam) {
3516 	return sci->WndProc(iMessage, wParam, lParam);
3517 }
3518 
3519 }
3520 
SWndProc(HWND hWnd,UINT iMessage,WPARAM wParam,LPARAM lParam)3521 LRESULT PASCAL ScintillaWin::SWndProc(
3522 	HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam) {
3523 	//Platform::DebugPrintf("S W:%x M:%x WP:%x L:%x\n", hWnd, iMessage, wParam, lParam);
3524 
3525 	// Find C++ object associated with window.
3526 	ScintillaWin *sci = static_cast<ScintillaWin *>(PointerFromWindow(hWnd));
3527 	// sci will be zero if WM_CREATE not seen yet
3528 	if (sci == nullptr) {
3529 		try {
3530 			if (iMessage == WM_CREATE) {
3531 				static std::once_flag once;
3532 				std::call_once(once, Prepare);
3533 				// Create C++ object associated with window
3534 				sci = new ScintillaWin(hWnd);
3535 				SetWindowPointer(hWnd, sci);
3536 				return sci->WndProc(iMessage, wParam, lParam);
3537 			}
3538 		} catch (...) {
3539 		}
3540 		return ::DefWindowProc(hWnd, iMessage, wParam, lParam);
3541 	} else {
3542 		if (iMessage == WM_NCDESTROY) {
3543 			try {
3544 				sci->Finalise();
3545 				delete sci;
3546 			} catch (...) {
3547 			}
3548 			SetWindowPointer(hWnd, nullptr);
3549 			return ::DefWindowProc(hWnd, iMessage, wParam, lParam);
3550 		} else {
3551 			return sci->WndProc(iMessage, wParam, lParam);
3552 		}
3553 	}
3554 }
3555 
3556 // This function is externally visible so it can be called from container when building statically.
3557 // Must be called once only.
Scintilla_RegisterClasses(void * hInstance)3558 int Scintilla_RegisterClasses(void *hInstance) {
3559 	const bool result = ScintillaWin::Register(static_cast<HINSTANCE>(hInstance));
3560 	return result;
3561 }
3562 
3563 namespace Scintilla {
3564 
ResourcesRelease(bool fromDllMain)3565 int ResourcesRelease(bool fromDllMain) noexcept {
3566 	const bool result = ScintillaWin::Unregister();
3567 	Platform_Finalise(fromDllMain);
3568 	return result;
3569 }
3570 
3571 }
3572 
3573 // This function is externally visible so it can be called from container when building statically.
Scintilla_ReleaseResources()3574 int Scintilla_ReleaseResources() {
3575 	return Scintilla::ResourcesRelease(false);
3576 }
3577