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 155 void SetWindowID(HWND hWnd, int identifier) noexcept { 156 ::SetWindowLongPtr(hWnd, GWLP_ID, identifier); 157 } 158 159 Point PointFromLParam(sptr_t lpoint) noexcept { 160 return Point::FromInts(GET_X_LPARAM(lpoint), GET_Y_LPARAM(lpoint)); 161 } 162 163 bool KeyboardIsKeyDown(int key) noexcept { 164 return (::GetKeyState(key) & 0x80000000) != 0; 165 } 166 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; 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; 250 ~IMContext() { 251 if (hIMC) 252 ::ImmReleaseContext(hwnd, hIMC); 253 } 254 255 unsigned int GetImeCaretPos() const noexcept { 256 return ImmGetCompositionStringW(hIMC, GCS_CURSORPOS, nullptr, 0); 257 } 258 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 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: 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; 287 ~ReverseArrowCursor() { 288 if (cursor) { 289 ::DestroyCursor(cursor); 290 } 291 } 292 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; 492 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 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 567 ScintillaWin::~ScintillaWin() {} 568 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 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 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 680 void ScintillaWin::DropRenderTarget() { 681 ReleaseUnknown(pRenderTarget); 682 } 683 684 #endif 685 686 HWND ScintillaWin::MainHWND() const noexcept { 687 return HwndFromWindow(wMain); 688 } 689 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 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 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 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 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. */ 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 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 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 814 int MultiByteLenFromWideChar(UINT codePage, std::wstring_view wsv) noexcept { 815 return MultiByteFromWideChar(codePage, wsv, nullptr, 0); 816 } 817 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 822 int WideCharLenFromMultiByte(UINT codePage, std::string_view sv) noexcept { 823 return WideCharFromMultiByte(codePage, sv, nullptr, 0); 824 } 825 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 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 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. 859 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. 881 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 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 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 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 990 bool ScintillaWin::KoreanIME() noexcept { 991 const int codePage = InputCodePage(); 992 return codePage == 949 || codePage == 1361; 993 } 994 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 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 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 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 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 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 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 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 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 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 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 1309 UINT ScintillaWin::CodePageOfDocument() const noexcept { 1310 return CodePageFromCharSet(vs.styles[STYLE_DEFAULT].characterSet, pdoc->dbcsCodePage); 1311 } 1312 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 1325 sptr_t ScintillaWin::GetTextLength() { 1326 return pdoc->CountUTF16(0, pdoc->Length()); 1327 } 1328 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 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 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 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 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 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 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 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 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 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 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 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 2047 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 2053 sptr_t ScintillaWin::DefWndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam) { 2054 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam); 2055 } 2056 2057 bool ScintillaWin::FineTickerRunning(TickReason reason) { 2058 return timers[reason] != 0; 2059 } 2060 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 2071 void ScintillaWin::FineTickerCancel(TickReason reason) { 2072 if (timers[reason]) { 2073 ::KillTimer(MainHWND(), timers[reason]); 2074 timers[reason] = 0; 2075 } 2076 } 2077 2078 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 2096 void ScintillaWin::IdleWork() { 2097 styleIdleInQueue = false; 2098 Editor::IdleWork(); 2099 } 2100 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 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 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 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 2139 bool ScintillaWin::PaintContains(PRectangle rc) { 2140 if (paintState == painting) { 2141 return BoundsContains(rcPaint, hRgnUpdate, rc); 2142 } 2143 return true; 2144 } 2145 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 2155 void ScintillaWin::NotifyCaretMove() { 2156 NotifyWinEvent(EVENT_OBJECT_LOCATIONCHANGE, MainHWND(), OBJID_CARET, CHILDID_SELF); 2157 } 2158 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 2175 int ScintillaWin::SetScrollInfo(int nBar, LPCSCROLLINFO lpsi, BOOL bRedraw) noexcept { 2176 return ::SetScrollInfo(MainHWND(), nBar, lpsi, bRedraw); 2177 } 2178 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 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 2197 void ScintillaWin::SetVerticalScrollPos() { 2198 ChangeScrollPos(SB_VERT, topLine); 2199 } 2200 2201 void ScintillaWin::SetHorizontalScrollPos() { 2202 ChangeScrollPos(SB_HORZ, xOffset); 2203 } 2204 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 2257 void ScintillaWin::NotifyChange() { 2258 ::SendMessage(::GetParent(MainHWND()), WM_COMMAND, 2259 MAKEWPARAM(GetCtrlID(), SCEN_CHANGE), 2260 reinterpret_cast<LPARAM>(MainHWND())); 2261 } 2262 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 2272 void ScintillaWin::SetCtrlID(int identifier) { 2273 ::SetWindowID(HwndFromWindow(wMain), identifier); 2274 } 2275 2276 int ScintillaWin::GetCtrlID() { 2277 return ::GetDlgCtrlID(HwndFromWindow(wMain)); 2278 } 2279 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 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: 2304 explicit CaseFolderDBCS(UINT cp_) : cp(cp_) { 2305 StandardASCII(); 2306 } 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 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 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 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 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 {}; 2441 GlobalMemory() noexcept { 2442 } 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; 2453 ~GlobalMemory() { 2454 assert(!ptr); 2455 assert(!hand); 2456 } 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 } 2464 HGLOBAL Unlock() noexcept { 2465 assert(ptr); 2466 HGLOBAL handCopy = hand; 2467 ::GlobalUnlock(hand); 2468 ptr = nullptr; 2469 hand = {}; 2470 return handCopy; 2471 } 2472 void SetClip(UINT uFormat) noexcept { 2473 ::SetClipboardData(uFormat, Unlock()); 2474 } 2475 operator bool() const noexcept { 2476 return ptr != nullptr; 2477 } 2478 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). 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 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 2505 bool SupportedFormat(const FORMATETC *pFE) noexcept { 2506 return pFE->cfFormat == CF_UNICODETEXT && 2507 IsValidFormatEtc(pFE); 2508 } 2509 2510 } 2511 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 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 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 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); 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 } 2584 STDMETHODIMP_(ULONG)FormatEnumerator_AddRef(FormatEnumerator *fe) { 2585 return ++fe->ref; 2586 } 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 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 } 2612 STDMETHODIMP FormatEnumerator_Skip(FormatEnumerator *fe, ULONG celt) { 2613 fe->pos += celt; 2614 return S_OK; 2615 } 2616 STDMETHODIMP FormatEnumerator_Reset(FormatEnumerator *fe) { 2617 fe->pos = 0; 2618 return S_OK; 2619 } 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 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 2649 STDMETHODIMP DropSource_QueryInterface(DropSource *ds, REFIID riid, PVOID *ppv) { 2650 return ds->sci->QueryInterface(riid, ppv); 2651 } 2652 STDMETHODIMP_(ULONG)DropSource_AddRef(DropSource *ds) { 2653 return ds->sci->AddRef(); 2654 } 2655 STDMETHODIMP_(ULONG)DropSource_Release(DropSource *ds) { 2656 return ds->sci->Release(); 2657 } 2658 2659 /// Implement IDropSource 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 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 2680 DropSource::DropSource() noexcept { 2681 vtbl = vtDropSource; 2682 sci = nullptr; 2683 } 2684 2685 /// Implement IUnkown 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 } 2690 STDMETHODIMP_(ULONG)DataObject_AddRef(DataObject *pd) { 2691 return pd->sci->AddRef(); 2692 } 2693 STDMETHODIMP_(ULONG)DataObject_Release(DataObject *pd) { 2694 return pd->sci->Release(); 2695 } 2696 /// Implement IDataObject 2697 STDMETHODIMP DataObject_GetData(DataObject *pd, FORMATETC *pFEIn, STGMEDIUM *pSTM) { 2698 return pd->sci->GetData(pFEIn, pSTM); 2699 } 2700 2701 STDMETHODIMP DataObject_GetDataHere(DataObject *, FORMATETC *, STGMEDIUM *) { 2702 //Platform::DebugPrintf("DOB GetDataHere\n"); 2703 return E_NOTIMPL; 2704 } 2705 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 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 2728 STDMETHODIMP DataObject_SetData(DataObject *, FORMATETC *, STGMEDIUM *, BOOL) { 2729 //Platform::DebugPrintf("DOB SetData\n"); 2730 return E_FAIL; 2731 } 2732 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 2754 STDMETHODIMP DataObject_DAdvise(DataObject *, FORMATETC *, DWORD, IAdviseSink *, PDWORD) { 2755 //Platform::DebugPrintf("DOB DAdvise\n"); 2756 return E_FAIL; 2757 } 2758 2759 STDMETHODIMP DataObject_DUnadvise(DataObject *, DWORD) { 2760 //Platform::DebugPrintf("DOB DUnadvise\n"); 2761 return E_FAIL; 2762 } 2763 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 2784 DataObject::DataObject() noexcept { 2785 vtbl = vtDataObject; 2786 sci = nullptr; 2787 } 2788 2789 /// Implement IUnknown 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 } 2794 STDMETHODIMP_(ULONG)DropTarget_AddRef(DropTarget *dt) { 2795 return dt->sci->AddRef(); 2796 } 2797 STDMETHODIMP_(ULONG)DropTarget_Release(DropTarget *dt) { 2798 return dt->sci->Release(); 2799 } 2800 2801 /// Implement IDropTarget by forwarding to Scintilla 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 } 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 } 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 } 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 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 */ 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. */ 2896 void ScintillaWin::ImeEndComposition() { 2897 // clear IME composition state. 2898 view.imeCaretBlockOverride = false; 2899 pdoc->TentativeUndo(); 2900 ShowCaretAtCurrentPosition(); 2901 } 2902 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 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 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 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 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 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 */ 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 */ 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 3144 bool CompareDevCap(HDC hdc, HDC hOtherDC, int nIndex) noexcept { 3145 return ::GetDeviceCaps(hdc, nIndex) == ::GetDeviceCaps(hOtherDC, nIndex); 3146 } 3147 3148 } 3149 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 3162 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 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 3192 STDMETHODIMP_(ULONG) ScintillaWin::AddRef() { 3193 return 1; 3194 } 3195 3196 STDMETHODIMP_(ULONG) ScintillaWin::Release() { 3197 return 1; 3198 } 3199 3200 /// Implement IDropTarget 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 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 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 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 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 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 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 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 3365 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 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 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 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 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 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 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. 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 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. 3575 int Scintilla_ReleaseResources() { 3576 return Scintilla::ResourcesRelease(false); 3577 } 3578