1 // Scintilla source code edit control
2 /** @file ScintillaWin.cxx
3 ** Windows specific subclass of ScintillaBase.
4 **/
5 // Copyright 1998-2003 by Neil Hodgson <[email protected]>
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 wchar_t *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 wchar_t * label,int cmd,bool enabled)2555 void ScintillaWin::AddToPopUp(const wchar_t* label, int cmd, bool enabled) {
2556 HMENU hmenuPopup = static_cast<HMENU>(popup.GetID());
2557 if (!label[0])
2558 ::AppendMenu(hmenuPopup, MF_SEPARATOR, 0, L"");
2559 else if (enabled)
2560 ::AppendMenu(hmenuPopup, MF_STRING, cmd, label);
2561 else
2562 ::AppendMenu(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 const int styleHere = STYLE_DEFAULT;
2873 LOGFONTW lf = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, L""};
2874 int sizeZoomed = vs.styles[styleHere].size + vs.zoomLevel * SC_FONT_SIZE_MULTIPLIER;
2875 if (sizeZoomed <= 2 * SC_FONT_SIZE_MULTIPLIER) // Hangs if sizeZoomed <= 1
2876 sizeZoomed = 2 * SC_FONT_SIZE_MULTIPLIER;
2877 // The negative is to allow for leading
2878 lf.lfHeight = -::MulDiv(sizeZoomed, dpi, 72*SC_FONT_SIZE_MULTIPLIER);
2879 lf.lfWeight = vs.styles[styleHere].weight;
2880 lf.lfItalic = vs.styles[styleHere].italic ? 1 : 0;
2881 lf.lfCharSet = DEFAULT_CHARSET;
2882 lf.lfFaceName[0] = L'\0';
2883 if (vs.styles[styleHere].fontName) {
2884 const char* fontName = vs.styles[styleHere].fontName;
2885 UTF16FromUTF8(std::string_view(fontName), lf.lfFaceName, LF_FACESIZE);
2886 }
2887
2888 ::ImmSetCompositionFontW(imc.hIMC, &lf);
2889 }
2890 // Caret is displayed in IME window. So, caret in Scintilla is useless.
2891 DropCaret();
2892 }
2893 }
2894
2895 /** Called when IME Window closed. */
ImeEndComposition()2896 void ScintillaWin::ImeEndComposition() {
2897 // clear IME composition state.
2898 view.imeCaretBlockOverride = false;
2899 pdoc->TentativeUndo();
2900 ShowCaretAtCurrentPosition();
2901 }
2902
ImeOnReconvert(LPARAM lParam)2903 LRESULT ScintillaWin::ImeOnReconvert(LPARAM lParam) {
2904 // Reconversion on windows limits within one line without eol.
2905 // Look around: baseStart <-- (|mainStart| -- mainEnd) --> baseEnd.
2906 const Sci::Position mainStart = sel.RangeMain().Start().Position();
2907 const Sci::Position mainEnd = sel.RangeMain().End().Position();
2908 const Sci::Line curLine = pdoc->SciLineFromPosition(mainStart);
2909 if (curLine != pdoc->LineFromPosition(mainEnd))
2910 return 0;
2911 const Sci::Position baseStart = pdoc->LineStart(curLine);
2912 const Sci::Position baseEnd = pdoc->LineEnd(curLine);
2913 if ((baseStart == baseEnd) || (mainEnd > baseEnd))
2914 return 0;
2915
2916 const int codePage = CodePageOfDocument();
2917 const std::wstring rcFeed = StringDecode(RangeText(baseStart, baseEnd), codePage);
2918 const int rcFeedLen = static_cast<int>(rcFeed.length()) * sizeof(wchar_t);
2919 const int rcSize = sizeof(RECONVERTSTRING) + rcFeedLen + sizeof(wchar_t);
2920
2921 RECONVERTSTRING *rc = static_cast<RECONVERTSTRING *>(PtrFromSPtr(lParam));
2922 if (!rc)
2923 return rcSize; // Immediately be back with rcSize of memory block.
2924
2925 wchar_t *rcFeedStart = reinterpret_cast<wchar_t*>(rc + 1);
2926 memcpy(rcFeedStart, &rcFeed[0], rcFeedLen);
2927
2928 std::string rcCompString = RangeText(mainStart, mainEnd);
2929 std::wstring rcCompWstring = StringDecode(rcCompString, codePage);
2930 std::string rcCompStart = RangeText(baseStart, mainStart);
2931 std::wstring rcCompWstart = StringDecode(rcCompStart, codePage);
2932
2933 // Map selection to dwCompStr.
2934 // No selection assumes current caret as rcCompString without length.
2935 rc->dwVersion = 0; // It should be absolutely 0.
2936 rc->dwStrLen = static_cast<DWORD>(rcFeed.length());
2937 rc->dwStrOffset = sizeof(RECONVERTSTRING);
2938 rc->dwCompStrLen = static_cast<DWORD>(rcCompWstring.length());
2939 rc->dwCompStrOffset = static_cast<DWORD>(rcCompWstart.length()) * sizeof(wchar_t);
2940 rc->dwTargetStrLen = rc->dwCompStrLen;
2941 rc->dwTargetStrOffset =rc->dwCompStrOffset;
2942
2943 IMContext imc(MainHWND());
2944 if (!imc.hIMC)
2945 return 0;
2946
2947 if (!::ImmSetCompositionStringW(imc.hIMC, SCS_QUERYRECONVERTSTRING, rc, rcSize, nullptr, 0))
2948 return 0;
2949
2950 // No selection asks IME to fill target fields with its own value.
2951 const int tgWlen = rc->dwTargetStrLen;
2952 const int tgWstart = rc->dwTargetStrOffset / sizeof(wchar_t);
2953
2954 std::string tgCompStart = StringEncode(rcFeed.substr(0, tgWstart), codePage);
2955 std::string tgComp = StringEncode(rcFeed.substr(tgWstart, tgWlen), codePage);
2956
2957 // No selection needs to adjust reconvert start position for IME set.
2958 const int adjust = static_cast<int>(tgCompStart.length() - rcCompStart.length());
2959 const int docCompLen = static_cast<int>(tgComp.length());
2960
2961 // Make place for next composition string to sit in.
2962 for (size_t r=0; r<sel.Count(); r++) {
2963 const Sci::Position rBase = sel.Range(r).Start().Position();
2964 const Sci::Position docCompStart = rBase + adjust;
2965
2966 if (inOverstrike) { // the docCompLen of bytes will be overstriked.
2967 sel.Range(r).caret.SetPosition(docCompStart);
2968 sel.Range(r).anchor.SetPosition(docCompStart);
2969 } else {
2970 // Ensure docCompStart+docCompLen be not beyond lineEnd.
2971 // since docCompLen by byte might break eol.
2972 const Sci::Position lineEnd = pdoc->LineEnd(pdoc->LineFromPosition(rBase));
2973 const Sci::Position overflow = (docCompStart + docCompLen) - lineEnd;
2974 if (overflow > 0) {
2975 pdoc->DeleteChars(docCompStart, docCompLen - overflow);
2976 } else {
2977 pdoc->DeleteChars(docCompStart, docCompLen);
2978 }
2979 }
2980 }
2981 // Immediately Target Input or candidate box choice with GCS_COMPSTR.
2982 return rcSize;
2983 }
2984
GetIntelliMouseParameters()2985 void ScintillaWin::GetIntelliMouseParameters() noexcept {
2986 // This retrieves the number of lines per scroll as configured in the Mouse Properties sheet in Control Panel
2987 ::SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, &linesPerScroll, 0);
2988 }
2989
CopyToGlobal(GlobalMemory & gmUnicode,const SelectionText & selectedText)2990 void ScintillaWin::CopyToGlobal(GlobalMemory &gmUnicode, const SelectionText &selectedText) {
2991 const std::string_view svSelected(selectedText.Data(), selectedText.LengthWithTerminator());
2992 if (IsUnicodeMode()) {
2993 const size_t uchars = UTF16Length(svSelected);
2994 gmUnicode.Allocate(2 * uchars);
2995 if (gmUnicode) {
2996 UTF16FromUTF8(svSelected,
2997 static_cast<wchar_t *>(gmUnicode.ptr), uchars);
2998 }
2999 } else {
3000 // Not Unicode mode
3001 // Convert to Unicode using the current Scintilla code page
3002 const UINT cpSrc = CodePageFromCharSet(
3003 selectedText.characterSet, selectedText.codePage);
3004 const size_t uLen = WideCharLenFromMultiByte(cpSrc, svSelected);
3005 gmUnicode.Allocate(2 * uLen);
3006 if (gmUnicode) {
3007 WideCharFromMultiByte(cpSrc, svSelected,
3008 static_cast<wchar_t *>(gmUnicode.ptr), uLen);
3009 }
3010 }
3011 }
3012
CopyToClipboard(const SelectionText & selectedText)3013 void ScintillaWin::CopyToClipboard(const SelectionText &selectedText) {
3014 if (!::OpenClipboardRetry(MainHWND())) {
3015 return;
3016 }
3017 ::EmptyClipboard();
3018
3019 GlobalMemory uniText;
3020 CopyToGlobal(uniText, selectedText);
3021 if (uniText) {
3022 uniText.SetClip(CF_UNICODETEXT);
3023 }
3024
3025 if (selectedText.rectangular) {
3026 ::SetClipboardData(cfColumnSelect, 0);
3027
3028 GlobalMemory borlandSelection;
3029 borlandSelection.Allocate(1);
3030 if (borlandSelection) {
3031 static_cast<BYTE *>(borlandSelection.ptr)[0] = 0x02;
3032 borlandSelection.SetClip(cfBorlandIDEBlockType);
3033 }
3034 }
3035
3036 if (selectedText.lineCopy) {
3037 ::SetClipboardData(cfLineSelect, 0);
3038 ::SetClipboardData(cfVSLineTag, 0);
3039 }
3040
3041 ::CloseClipboard();
3042 }
3043
ScrollMessage(WPARAM wParam)3044 void ScintillaWin::ScrollMessage(WPARAM wParam) {
3045 //DWORD dwStart = timeGetTime();
3046 //Platform::DebugPrintf("Scroll %x %d\n", wParam, lParam);
3047
3048 SCROLLINFO sci = {};
3049 sci.cbSize = sizeof(sci);
3050 sci.fMask = SIF_ALL;
3051
3052 GetScrollInfo(SB_VERT, &sci);
3053
3054 //Platform::DebugPrintf("ScrollInfo %d mask=%x min=%d max=%d page=%d pos=%d track=%d\n", b,sci.fMask,
3055 //sci.nMin, sci.nMax, sci.nPage, sci.nPos, sci.nTrackPos);
3056 Sci::Line topLineNew = topLine;
3057 switch (LOWORD(wParam)) {
3058 case SB_LINEUP:
3059 topLineNew -= 1;
3060 break;
3061 case SB_LINEDOWN:
3062 topLineNew += 1;
3063 break;
3064 case SB_PAGEUP:
3065 topLineNew -= LinesToScroll(); break;
3066 case SB_PAGEDOWN: topLineNew += LinesToScroll(); break;
3067 case SB_TOP: topLineNew = 0; break;
3068 case SB_BOTTOM: topLineNew = MaxScrollPos(); break;
3069 case SB_THUMBPOSITION: topLineNew = sci.nTrackPos; break;
3070 case SB_THUMBTRACK: topLineNew = sci.nTrackPos; break;
3071 }
3072 ScrollTo(topLineNew);
3073 }
3074
HorizontalScrollMessage(WPARAM wParam)3075 void ScintillaWin::HorizontalScrollMessage(WPARAM wParam) {
3076 int xPos = xOffset;
3077 const PRectangle rcText = GetTextRectangle();
3078 const int pageWidth = static_cast<int>(rcText.Width() * 2 / 3);
3079 switch (LOWORD(wParam)) {
3080 case SB_LINEUP:
3081 xPos -= 20;
3082 break;
3083 case SB_LINEDOWN: // May move past the logical end
3084 xPos += 20;
3085 break;
3086 case SB_PAGEUP:
3087 xPos -= pageWidth;
3088 break;
3089 case SB_PAGEDOWN:
3090 xPos += pageWidth;
3091 if (xPos > scrollWidth - rcText.Width()) { // Hit the end exactly
3092 xPos = scrollWidth - static_cast<int>(rcText.Width());
3093 }
3094 break;
3095 case SB_TOP:
3096 xPos = 0;
3097 break;
3098 case SB_BOTTOM:
3099 xPos = scrollWidth;
3100 break;
3101 case SB_THUMBPOSITION:
3102 case SB_THUMBTRACK: {
3103 // 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 =]
3104 SCROLLINFO si;
3105 si.cbSize = sizeof(si);
3106 si.fMask = SIF_TRACKPOS;
3107 if (GetScrollInfo(SB_HORZ, &si)) {
3108 xPos = si.nTrackPos;
3109 }
3110 }
3111 break;
3112 }
3113 HorizontalScrollTo(xPos);
3114 }
3115
3116 /**
3117 * Redraw all of text area.
3118 * This paint will not be abandoned.
3119 */
FullPaint()3120 void ScintillaWin::FullPaint() {
3121 if ((technology == SC_TECHNOLOGY_DEFAULT) || (technology == SC_TECHNOLOGY_DIRECTWRITEDC)) {
3122 HDC hdc = ::GetDC(MainHWND());
3123 FullPaintDC(hdc);
3124 ::ReleaseDC(MainHWND(), hdc);
3125 } else {
3126 FullPaintDC({});
3127 }
3128 }
3129
3130 /**
3131 * Redraw all of text area on the specified DC.
3132 * This paint will not be abandoned.
3133 */
FullPaintDC(HDC hdc)3134 void ScintillaWin::FullPaintDC(HDC hdc) {
3135 paintState = painting;
3136 rcPaint = GetClientRectangle();
3137 paintingAllText = true;
3138 PaintDC(hdc);
3139 paintState = notPainting;
3140 }
3141
3142 namespace {
3143
CompareDevCap(HDC hdc,HDC hOtherDC,int nIndex)3144 bool CompareDevCap(HDC hdc, HDC hOtherDC, int nIndex) noexcept {
3145 return ::GetDeviceCaps(hdc, nIndex) == ::GetDeviceCaps(hOtherDC, nIndex);
3146 }
3147
3148 }
3149
IsCompatibleDC(HDC hOtherDC)3150 bool ScintillaWin::IsCompatibleDC(HDC hOtherDC) noexcept {
3151 HDC hdc = ::GetDC(MainHWND());
3152 const bool isCompatible =
3153 CompareDevCap(hdc, hOtherDC, TECHNOLOGY) &&
3154 CompareDevCap(hdc, hOtherDC, LOGPIXELSY) &&
3155 CompareDevCap(hdc, hOtherDC, LOGPIXELSX) &&
3156 CompareDevCap(hdc, hOtherDC, BITSPIXEL) &&
3157 CompareDevCap(hdc, hOtherDC, PLANES);
3158 ::ReleaseDC(MainHWND(), hdc);
3159 return isCompatible;
3160 }
3161
EffectFromState(DWORD grfKeyState) const3162 DWORD ScintillaWin::EffectFromState(DWORD grfKeyState) const noexcept {
3163 // These are the Wordpad semantics.
3164 DWORD dwEffect;
3165 if (inDragDrop == ddDragging) // Internal defaults to move
3166 dwEffect = DROPEFFECT_MOVE;
3167 else
3168 dwEffect = DROPEFFECT_COPY;
3169 if (grfKeyState & MK_ALT)
3170 dwEffect = DROPEFFECT_MOVE;
3171 if (grfKeyState & MK_CONTROL)
3172 dwEffect = DROPEFFECT_COPY;
3173 return dwEffect;
3174 }
3175
3176 /// Implement IUnknown
QueryInterface(REFIID riid,PVOID * ppv)3177 STDMETHODIMP ScintillaWin::QueryInterface(REFIID riid, PVOID *ppv) {
3178 *ppv = nullptr;
3179 if (riid == IID_IUnknown)
3180 *ppv = reinterpret_cast<IDropTarget *>(&dt);
3181 if (riid == IID_IDropSource)
3182 *ppv = reinterpret_cast<IDropSource *>(&ds);
3183 if (riid == IID_IDropTarget)
3184 *ppv = reinterpret_cast<IDropTarget *>(&dt);
3185 if (riid == IID_IDataObject)
3186 *ppv = reinterpret_cast<IDataObject *>(&dob);
3187 if (!*ppv)
3188 return E_NOINTERFACE;
3189 return S_OK;
3190 }
3191
STDMETHODIMP_(ULONG)3192 STDMETHODIMP_(ULONG) ScintillaWin::AddRef() {
3193 return 1;
3194 }
3195
STDMETHODIMP_(ULONG)3196 STDMETHODIMP_(ULONG) ScintillaWin::Release() {
3197 return 1;
3198 }
3199
3200 /// Implement IDropTarget
DragEnter(LPDATAOBJECT pIDataSource,DWORD grfKeyState,POINTL,PDWORD pdwEffect)3201 STDMETHODIMP ScintillaWin::DragEnter(LPDATAOBJECT pIDataSource, DWORD grfKeyState,
3202 POINTL, PDWORD pdwEffect) {
3203 if (!pIDataSource )
3204 return E_POINTER;
3205 FORMATETC fmtu = {CF_UNICODETEXT, nullptr, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
3206 const HRESULT hrHasUText = pIDataSource->QueryGetData(&fmtu);
3207 hasOKText = (hrHasUText == S_OK);
3208 if (hasOKText) {
3209 *pdwEffect = EffectFromState(grfKeyState);
3210 } else {
3211 *pdwEffect = DROPEFFECT_NONE;
3212 }
3213
3214 return S_OK;
3215 }
3216
DragOver(DWORD grfKeyState,POINTL pt,PDWORD pdwEffect)3217 STDMETHODIMP ScintillaWin::DragOver(DWORD grfKeyState, POINTL pt, PDWORD pdwEffect) {
3218 try {
3219 if (!hasOKText || pdoc->IsReadOnly()) {
3220 *pdwEffect = DROPEFFECT_NONE;
3221 return S_OK;
3222 }
3223
3224 *pdwEffect = EffectFromState(grfKeyState);
3225
3226 // Update the cursor.
3227 POINT rpt = {pt.x, pt.y};
3228 ::ScreenToClient(MainHWND(), &rpt);
3229 SetDragPosition(SPositionFromLocation(PointFromPOINT(rpt), false, false, UserVirtualSpace()));
3230
3231 return S_OK;
3232 } catch (...) {
3233 errorStatus = SC_STATUS_FAILURE;
3234 }
3235 return E_FAIL;
3236 }
3237
DragLeave()3238 STDMETHODIMP ScintillaWin::DragLeave() {
3239 try {
3240 SetDragPosition(SelectionPosition(Sci::invalidPosition));
3241 return S_OK;
3242 } catch (...) {
3243 errorStatus = SC_STATUS_FAILURE;
3244 }
3245 return E_FAIL;
3246 }
3247
Drop(LPDATAOBJECT pIDataSource,DWORD grfKeyState,POINTL pt,PDWORD pdwEffect)3248 STDMETHODIMP ScintillaWin::Drop(LPDATAOBJECT pIDataSource, DWORD grfKeyState,
3249 POINTL pt, PDWORD pdwEffect) {
3250 try {
3251 *pdwEffect = EffectFromState(grfKeyState);
3252
3253 if (!pIDataSource)
3254 return E_POINTER;
3255
3256 SetDragPosition(SelectionPosition(Sci::invalidPosition));
3257
3258 std::string putf;
3259 FORMATETC fmtu = {CF_UNICODETEXT, nullptr, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
3260 STGMEDIUM medium{};
3261 const HRESULT hr = pIDataSource->GetData(&fmtu, &medium);
3262 if (!SUCCEEDED(hr)) {
3263 return hr;
3264 }
3265 if (medium.hGlobal) {
3266 GlobalMemory memUDrop(medium.hGlobal);
3267 if (const wchar_t *uptr = static_cast<const wchar_t *>(memUDrop.ptr)) {
3268 putf = EncodeWString(uptr);
3269 }
3270 memUDrop.Unlock();
3271 }
3272
3273 if (putf.empty()) {
3274 return S_OK;
3275 }
3276
3277 FORMATETC fmtr = {cfColumnSelect, nullptr, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
3278 const bool isRectangular = S_OK == pIDataSource->QueryGetData(&fmtr);
3279
3280 POINT rpt = {pt.x, pt.y};
3281 ::ScreenToClient(MainHWND(), &rpt);
3282 const SelectionPosition movePos = SPositionFromLocation(PointFromPOINT(rpt), false, false, UserVirtualSpace());
3283
3284 DropAt(movePos, putf.c_str(), putf.size(), *pdwEffect == DROPEFFECT_MOVE, isRectangular);
3285
3286 // Free data
3287 ::ReleaseStgMedium(&medium);
3288
3289 return S_OK;
3290 } catch (...) {
3291 errorStatus = SC_STATUS_FAILURE;
3292 }
3293 return E_FAIL;
3294 }
3295
3296 /// Implement important part of IDataObject
GetData(FORMATETC * pFEIn,STGMEDIUM * pSTM)3297 STDMETHODIMP ScintillaWin::GetData(FORMATETC *pFEIn, STGMEDIUM *pSTM) {
3298 if (!SupportedFormat(pFEIn)) {
3299 //Platform::DebugPrintf("DOB GetData No %d %x %x fmt=%x\n", lenDrag, pFEIn, pSTM, pFEIn->cfFormat);
3300 return DATA_E_FORMATETC;
3301 }
3302
3303 pSTM->tymed = TYMED_HGLOBAL;
3304 //Platform::DebugPrintf("DOB GetData OK %d %x %x\n", lenDrag, pFEIn, pSTM);
3305
3306 GlobalMemory uniText;
3307 CopyToGlobal(uniText, drag);
3308 pSTM->hGlobal = uniText ? uniText.Unlock() : 0;
3309 pSTM->pUnkForRelease = nullptr;
3310 return S_OK;
3311 }
3312
Prepare()3313 void ScintillaWin::Prepare() noexcept {
3314 Platform_Initialise(hInstance);
3315
3316 // Register the CallTip class
3317 WNDCLASSEX wndclassc{};
3318 wndclassc.cbSize = sizeof(wndclassc);
3319 wndclassc.style = CS_GLOBALCLASS | CS_HREDRAW | CS_VREDRAW;
3320 wndclassc.cbWndExtra = sizeof(ScintillaWin *);
3321 wndclassc.hInstance = hInstance;
3322 wndclassc.lpfnWndProc = ScintillaWin::CTWndProc;
3323 wndclassc.hCursor = ::LoadCursor(NULL, IDC_ARROW);
3324 wndclassc.lpszClassName = callClassName;
3325
3326 callClassAtom = ::RegisterClassEx(&wndclassc);
3327 }
3328
Register(HINSTANCE hInstance_)3329 bool ScintillaWin::Register(HINSTANCE hInstance_) noexcept {
3330
3331 hInstance = hInstance_;
3332
3333 // Register the Scintilla class
3334 // Register Scintilla as a wide character window
3335 WNDCLASSEXW wndclass {};
3336 wndclass.cbSize = sizeof(wndclass);
3337 wndclass.style = CS_GLOBALCLASS | CS_HREDRAW | CS_VREDRAW;
3338 wndclass.lpfnWndProc = ScintillaWin::SWndProc;
3339 wndclass.cbWndExtra = sizeof(ScintillaWin *);
3340 wndclass.hInstance = hInstance;
3341 wndclass.lpszClassName = L"Scintilla";
3342 scintillaClassAtom = ::RegisterClassExW(&wndclass);
3343 const bool result = 0 != scintillaClassAtom;
3344
3345 return result;
3346 }
3347
Unregister()3348 bool ScintillaWin::Unregister() noexcept {
3349 bool result = true;
3350 if (0 != scintillaClassAtom) {
3351 if (::UnregisterClass(MAKEINTATOM(scintillaClassAtom), hInstance) == 0) {
3352 result = false;
3353 }
3354 scintillaClassAtom = 0;
3355 }
3356 if (0 != callClassAtom) {
3357 if (::UnregisterClass(MAKEINTATOM(callClassAtom), hInstance) == 0) {
3358 result = false;
3359 }
3360 callClassAtom = 0;
3361 }
3362 return result;
3363 }
3364
HasCaretSizeChanged() const3365 bool ScintillaWin::HasCaretSizeChanged() const noexcept {
3366 if (
3367 ( (0 != vs.caretWidth) && (sysCaretWidth != vs.caretWidth) )
3368 || ((0 != vs.lineHeight) && (sysCaretHeight != vs.lineHeight))
3369 ) {
3370 return true;
3371 }
3372 return false;
3373 }
3374
CreateSystemCaret()3375 BOOL ScintillaWin::CreateSystemCaret() {
3376 sysCaretWidth = vs.caretWidth;
3377 if (0 == sysCaretWidth) {
3378 sysCaretWidth = 1;
3379 }
3380 sysCaretHeight = vs.lineHeight;
3381 const int bitmapSize = (((sysCaretWidth + 15) & ~15) >> 3) *
3382 sysCaretHeight;
3383 std::vector<BYTE> bits(bitmapSize);
3384 sysCaretBitmap = ::CreateBitmap(sysCaretWidth, sysCaretHeight, 1,
3385 1, &bits[0]);
3386 const BOOL retval = ::CreateCaret(
3387 MainHWND(), sysCaretBitmap,
3388 sysCaretWidth, sysCaretHeight);
3389 if (technology == SC_TECHNOLOGY_DEFAULT) {
3390 // System caret interferes with Direct2D drawing so only show it for GDI.
3391 ::ShowCaret(MainHWND());
3392 }
3393 return retval;
3394 }
3395
DestroySystemCaret()3396 BOOL ScintillaWin::DestroySystemCaret() noexcept {
3397 ::HideCaret(MainHWND());
3398 const BOOL retval = ::DestroyCaret();
3399 if (sysCaretBitmap) {
3400 ::DeleteObject(sysCaretBitmap);
3401 sysCaretBitmap = {};
3402 }
3403 return retval;
3404 }
3405
CTWndProc(HWND hWnd,UINT iMessage,WPARAM wParam,LPARAM lParam)3406 LRESULT PASCAL ScintillaWin::CTWndProc(
3407 HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam) {
3408 // Find C++ object associated with window.
3409 ScintillaWin *sciThis = static_cast<ScintillaWin *>(PointerFromWindow(hWnd));
3410 try {
3411 // ctp will be zero if WM_CREATE not seen yet
3412 if (sciThis == nullptr) {
3413 if (iMessage == WM_CREATE) {
3414 // Associate CallTip object with window
3415 CREATESTRUCT *pCreate = static_cast<CREATESTRUCT *>(PtrFromSPtr(lParam));
3416 SetWindowPointer(hWnd, pCreate->lpCreateParams);
3417 return 0;
3418 } else {
3419 return ::DefWindowProc(hWnd, iMessage, wParam, lParam);
3420 }
3421 } else {
3422 if (iMessage == WM_NCDESTROY) {
3423 SetWindowPointer(hWnd, nullptr);
3424 return ::DefWindowProc(hWnd, iMessage, wParam, lParam);
3425 } else if (iMessage == WM_PAINT) {
3426 PAINTSTRUCT ps;
3427 ::BeginPaint(hWnd, &ps);
3428 std::unique_ptr<Surface> surfaceWindow(Surface::Allocate(sciThis->technology));
3429 #if defined(USE_D2D)
3430 ID2D1HwndRenderTarget *pCTRenderTarget = nullptr;
3431 #endif
3432 RECT rc;
3433 GetClientRect(hWnd, &rc);
3434 // Create a Direct2D render target.
3435 if (sciThis->technology == SC_TECHNOLOGY_DEFAULT) {
3436 surfaceWindow->Init(ps.hdc, hWnd);
3437 } else {
3438 #if defined(USE_D2D)
3439 D2D1_HWND_RENDER_TARGET_PROPERTIES dhrtp;
3440 dhrtp.hwnd = hWnd;
3441 dhrtp.pixelSize = D2D1::SizeU(rc.right - rc.left, rc.bottom - rc.top);
3442 dhrtp.presentOptions = (sciThis->technology == SC_TECHNOLOGY_DIRECTWRITERETAIN) ?
3443 D2D1_PRESENT_OPTIONS_RETAIN_CONTENTS : D2D1_PRESENT_OPTIONS_NONE;
3444
3445 D2D1_RENDER_TARGET_PROPERTIES drtp;
3446 drtp.type = D2D1_RENDER_TARGET_TYPE_DEFAULT;
3447 drtp.pixelFormat.format = DXGI_FORMAT_UNKNOWN;
3448 drtp.pixelFormat.alphaMode = D2D1_ALPHA_MODE_UNKNOWN;
3449 drtp.dpiX = 96.0;
3450 drtp.dpiY = 96.0;
3451 drtp.usage = D2D1_RENDER_TARGET_USAGE_NONE;
3452 drtp.minLevel = D2D1_FEATURE_LEVEL_DEFAULT;
3453
3454 if (!SUCCEEDED(pD2DFactory->CreateHwndRenderTarget(drtp, dhrtp, &pCTRenderTarget))) {
3455 surfaceWindow->Release();
3456 ::EndPaint(hWnd, &ps);
3457 return 0;
3458 }
3459 // If above SUCCEEDED, then pCTRenderTarget not nullptr
3460 assert(pCTRenderTarget);
3461 if (pCTRenderTarget) {
3462 surfaceWindow->Init(pCTRenderTarget, hWnd);
3463 pCTRenderTarget->BeginDraw();
3464 }
3465 #endif
3466 }
3467 surfaceWindow->SetUnicodeMode(SC_CP_UTF8 == sciThis->ct.codePage);
3468 surfaceWindow->SetDBCSMode(sciThis->ct.codePage);
3469 surfaceWindow->SetBidiR2L(sciThis->BidirectionalR2L());
3470 sciThis->ct.PaintCT(surfaceWindow.get());
3471 #if defined(USE_D2D)
3472 if (pCTRenderTarget)
3473 pCTRenderTarget->EndDraw();
3474 #endif
3475 surfaceWindow->Release();
3476 #if defined(USE_D2D)
3477 ReleaseUnknown(pCTRenderTarget);
3478 #endif
3479 ::EndPaint(hWnd, &ps);
3480 return 0;
3481 } else if ((iMessage == WM_NCLBUTTONDOWN) || (iMessage == WM_NCLBUTTONDBLCLK)) {
3482 POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
3483 ScreenToClient(hWnd, &pt);
3484 sciThis->ct.MouseClick(PointFromPOINT(pt));
3485 sciThis->CallTipClick();
3486 return 0;
3487 } else if (iMessage == WM_LBUTTONDOWN) {
3488 // This does not fire due to the hit test code
3489 sciThis->ct.MouseClick(PointFromLParam(lParam));
3490 sciThis->CallTipClick();
3491 return 0;
3492 } else if (iMessage == WM_SETCURSOR) {
3493 ::SetCursor(::LoadCursor(NULL, IDC_ARROW));
3494 return 0;
3495 } else if (iMessage == WM_NCHITTEST) {
3496 return HTCAPTION;
3497 } else {
3498 return ::DefWindowProc(hWnd, iMessage, wParam, lParam);
3499 }
3500 }
3501 } catch (...) {
3502 sciThis->errorStatus = SC_STATUS_FAILURE;
3503 }
3504 return ::DefWindowProc(hWnd, iMessage, wParam, lParam);
3505 }
3506
DirectFunction(sptr_t ptr,UINT iMessage,uptr_t wParam,sptr_t lParam)3507 sptr_t ScintillaWin::DirectFunction(
3508 sptr_t ptr, UINT iMessage, uptr_t wParam, sptr_t lParam) {
3509 PLATFORM_ASSERT(::GetCurrentThreadId() == ::GetWindowThreadProcessId(reinterpret_cast<ScintillaWin *>(ptr)->MainHWND(), nullptr));
3510 return reinterpret_cast<ScintillaWin *>(ptr)->WndProc(iMessage, wParam, lParam);
3511 }
3512
3513 namespace Scintilla {
3514
DirectFunction(ScintillaWin * sci,UINT iMessage,uptr_t wParam,sptr_t lParam)3515 sptr_t DirectFunction(
3516 ScintillaWin *sci, UINT iMessage, uptr_t wParam, sptr_t lParam) {
3517 return sci->WndProc(iMessage, wParam, lParam);
3518 }
3519
3520 }
3521
SWndProc(HWND hWnd,UINT iMessage,WPARAM wParam,LPARAM lParam)3522 LRESULT PASCAL ScintillaWin::SWndProc(
3523 HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam) {
3524 //Platform::DebugPrintf("S W:%x M:%x WP:%x L:%x\n", hWnd, iMessage, wParam, lParam);
3525
3526 // Find C++ object associated with window.
3527 ScintillaWin *sci = static_cast<ScintillaWin *>(PointerFromWindow(hWnd));
3528 // sci will be zero if WM_CREATE not seen yet
3529 if (sci == nullptr) {
3530 try {
3531 if (iMessage == WM_CREATE) {
3532 static std::once_flag once;
3533 std::call_once(once, Prepare);
3534 // Create C++ object associated with window
3535 sci = new ScintillaWin(hWnd);
3536 SetWindowPointer(hWnd, sci);
3537 return sci->WndProc(iMessage, wParam, lParam);
3538 }
3539 } catch (...) {
3540 }
3541 return ::DefWindowProc(hWnd, iMessage, wParam, lParam);
3542 } else {
3543 if (iMessage == WM_NCDESTROY) {
3544 try {
3545 sci->Finalise();
3546 delete sci;
3547 } catch (...) {
3548 }
3549 SetWindowPointer(hWnd, nullptr);
3550 return ::DefWindowProc(hWnd, iMessage, wParam, lParam);
3551 } else {
3552 return sci->WndProc(iMessage, wParam, lParam);
3553 }
3554 }
3555 }
3556
3557 // This function is externally visible so it can be called from container when building statically.
3558 // Must be called once only.
Scintilla_RegisterClasses(void * hInstance)3559 int Scintilla_RegisterClasses(void *hInstance) {
3560 const bool result = ScintillaWin::Register(static_cast<HINSTANCE>(hInstance));
3561 return result;
3562 }
3563
3564 namespace Scintilla {
3565
ResourcesRelease(bool fromDllMain)3566 int ResourcesRelease(bool fromDllMain) noexcept {
3567 const bool result = ScintillaWin::Unregister();
3568 Platform_Finalise(fromDllMain);
3569 return result;
3570 }
3571
3572 }
3573
3574 // This function is externally visible so it can be called from container when building statically.
Scintilla_ReleaseResources()3575 int Scintilla_ReleaseResources() {
3576 return Scintilla::ResourcesRelease(false);
3577 }
3578