Update Scintilla to 4.0.4
[TortoiseGit.git] / ext / scintilla / win32 / ScintillaWin.cxx
blob52745774b0b462917e08b45f57cf56aa7fba4d41
1 // Scintilla source code edit control
2 /** @file ScintillaWin.cxx
3 ** Windows specific subclass of ScintillaBase.
4 **/
5 // Copyright 1998-2003 by Neil Hodgson <neilh@scintilla.org>
6 // The License.txt file describes the conditions under which this software may be distributed.
8 #include <cstddef>
9 #include <cstdlib>
10 #include <cassert>
11 #include <cstring>
12 #include <cctype>
13 #include <cstdio>
14 #include <cmath>
15 #include <climits>
17 #include <stdexcept>
18 #include <new>
19 #include <string>
20 #include <vector>
21 #include <map>
22 #include <algorithm>
23 #include <memory>
25 #undef _WIN32_WINNT
26 #define _WIN32_WINNT 0x0500
27 #undef WINVER
28 #define WINVER 0x0500
29 #include <windows.h>
30 #include <commctrl.h>
31 #include <richedit.h>
32 #include <windowsx.h>
33 #include <zmouse.h>
34 #include <ole2.h>
36 #if !defined(DISABLE_D2D)
37 #define USE_D2D 1
38 #endif
40 #if defined(USE_D2D)
41 #include <d2d1.h>
42 #include <dwrite.h>
43 #endif
45 #include "Platform.h"
47 #include "ILoader.h"
48 #include "ILexer.h"
49 #include "Scintilla.h"
51 #ifdef SCI_LEXER
52 #include "SciLexer.h"
53 #endif
54 #include "StringCopy.h"
55 #ifdef SCI_LEXER
56 #include "LexerModule.h"
57 #endif
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"
84 #include "AutoComplete.h"
85 #include "ScintillaBase.h"
87 #ifdef SCI_LEXER
88 #include "ExternalLexer.h"
89 #endif
91 #include "PlatWin.h"
92 #include "HanjaDic.h"
93 #include "ScintillaWin.h"
95 #ifndef SPI_GETWHEELSCROLLLINES
96 #define SPI_GETWHEELSCROLLLINES 104
97 #endif
99 #ifndef WM_UNICHAR
100 #define WM_UNICHAR 0x0109
101 #endif
103 #ifndef UNICODE_NOCHAR
104 #define UNICODE_NOCHAR 0xFFFF
105 #endif
107 #ifndef IS_HIGH_SURROGATE
108 #define IS_HIGH_SURROGATE(x) ((x) >= SURROGATE_LEAD_FIRST && (x) <= SURROGATE_LEAD_LAST)
109 #endif
111 #ifndef IS_LOW_SURROGATE
112 #define IS_LOW_SURROGATE(x) ((x) >= SURROGATE_TRAIL_FIRST && (x) <= SURROGATE_TRAIL_LAST)
113 #endif
115 #ifndef MK_ALT
116 #define MK_ALT 32
117 #endif
119 #define SC_WIN_IDLE 5001
121 #define SC_INDICATOR_INPUT INDIC_IME
122 #define SC_INDICATOR_TARGET INDIC_IME+1
123 #define SC_INDICATOR_CONVERTED INDIC_IME+2
124 #define SC_INDICATOR_UNKNOWN INDIC_IME_MAX
126 #ifndef SCS_CAP_SETRECONVERTSTRING
127 #define SCS_CAP_SETRECONVERTSTRING 0x00000004
128 #define SCS_QUERYRECONVERTSTRING 0x00020000
129 #define SCS_SETRECONVERTSTRING 0x00010000
130 #endif
132 typedef BOOL (WINAPI *TrackMouseEventSig)(LPTRACKMOUSEEVENT);
133 typedef UINT_PTR (WINAPI *SetCoalescableTimerSig)(HWND hwnd, UINT_PTR nIDEvent,
134 UINT uElapse, TIMERPROC lpTimerFunc, ULONG uToleranceDelay);
136 // GCC has trouble with the standard COM ABI so do it the old C way with explicit vtables.
138 const TCHAR callClassName[] = TEXT("CallTip");
140 using namespace Scintilla;
142 static void *PointerFromWindow(HWND hWnd) {
143 return reinterpret_cast<void *>(::GetWindowLongPtr(hWnd, 0));
146 static void SetWindowPointer(HWND hWnd, void *ptr) {
147 ::SetWindowLongPtr(hWnd, 0, reinterpret_cast<LONG_PTR>(ptr));
150 static void SetWindowID(HWND hWnd, int identifier) {
151 ::SetWindowLongPtr(hWnd, GWLP_ID, identifier);
154 static Point PointFromPOINT(POINT pt) {
155 return Point::FromInts(pt.x, pt.y);
158 static Point PointFromLParam(sptr_t lpoint) {
159 return Point(static_cast<short>(LOWORD(lpoint)), static_cast<short>(HIWORD(lpoint)));
162 static bool KeyboardIsKeyDown(int key) {
163 return (::GetKeyState(key) & 0x80000000) != 0;
166 class ScintillaWin; // Forward declaration for COM interface subobjects
168 typedef void VFunction(void);
173 class FormatEnumerator {
174 public:
175 VFunction **vtbl;
176 int ref;
177 unsigned int pos;
178 std::vector<CLIPFORMAT> formats;
179 FormatEnumerator(int pos_, CLIPFORMAT formats_[], size_t formatsLen_);
184 class DropSource {
185 public:
186 VFunction **vtbl;
187 ScintillaWin *sci;
188 DropSource();
193 class DataObject {
194 public:
195 VFunction **vtbl;
196 ScintillaWin *sci;
197 DataObject();
202 class DropTarget {
203 public:
204 VFunction **vtbl;
205 ScintillaWin *sci;
206 DropTarget();
209 namespace {
211 class IMContext {
212 HWND hwnd;
213 public:
214 HIMC hIMC;
215 IMContext(HWND hwnd_) :
216 hwnd(hwnd_), hIMC(::ImmGetContext(hwnd_)) {
218 ~IMContext() {
219 if (hIMC)
220 ::ImmReleaseContext(hwnd, hIMC);
223 unsigned int GetImeCaretPos() {
224 return ImmGetCompositionStringW(hIMC, GCS_CURSORPOS, NULL, 0);
227 std::vector<BYTE> GetImeAttributes() {
228 int attrLen = ::ImmGetCompositionStringW(hIMC, GCS_COMPATTR, NULL, 0);
229 std::vector<BYTE> attr(attrLen, 0);
230 ::ImmGetCompositionStringW(hIMC, GCS_COMPATTR, &attr[0], static_cast<DWORD>(attr.size()));
231 return attr;
234 std::wstring GetCompositionString(DWORD dwIndex) {
235 const LONG byteLen = ::ImmGetCompositionStringW(hIMC, dwIndex, NULL, 0);
236 std::wstring wcs(byteLen / 2, 0);
237 ::ImmGetCompositionStringW(hIMC, dwIndex, &wcs[0], byteLen);
238 return wcs;
246 class ScintillaWin :
247 public ScintillaBase {
249 bool lastKeyDownConsumed;
250 wchar_t lastHighSurrogateChar;
252 bool capturedMouse;
253 bool trackedMouseLeave;
254 SetCoalescableTimerSig SetCoalescableTimerFn;
256 unsigned int linesPerScroll; ///< Intellimouse support
257 int wheelDelta; ///< Wheel delta from roll
259 HRGN hRgnUpdate;
261 bool hasOKText;
263 CLIPFORMAT cfColumnSelect;
264 CLIPFORMAT cfBorlandIDEBlockType;
265 CLIPFORMAT cfLineSelect;
266 CLIPFORMAT cfVSLineTag;
268 HRESULT hrOle;
269 DropSource ds;
270 DataObject dob;
271 DropTarget dt;
273 static HINSTANCE hInstance;
274 static ATOM scintillaClassAtom;
275 static ATOM callClassAtom;
277 #if defined(USE_D2D)
278 ID2D1RenderTarget *pRenderTarget;
279 bool renderTargetValid;
280 #endif
282 explicit ScintillaWin(HWND hwnd);
283 // Deleted so ScintillaWin objects can not be copied.
284 ScintillaWin(const ScintillaWin &) = delete;
285 ScintillaWin &operator=(const ScintillaWin &) = delete;
286 ~ScintillaWin() override;
288 void Init();
289 void Finalise() override;
290 #if defined(USE_D2D)
291 void EnsureRenderTarget(HDC hdc);
292 void DropRenderTarget();
293 #endif
294 HWND MainHWND();
296 static sptr_t DirectFunction(
297 sptr_t ptr, UINT iMessage, uptr_t wParam, sptr_t lParam);
298 static LRESULT PASCAL SWndProc(
299 HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam);
300 static LRESULT PASCAL CTWndProc(
301 HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam);
303 enum { invalidTimerID, standardTimerID, idleTimerID, fineTimerStart };
305 bool DragThreshold(Point ptStart, Point ptNow) override;
306 void StartDrag() override;
307 static int MouseModifiers(uptr_t wParam);
309 Sci::Position TargetAsUTF8(char *text);
310 void AddCharUTF16(wchar_t const *wcs, unsigned int wclen);
311 Sci::Position EncodedFromUTF8(const char *utf8, char *encoded) const;
312 sptr_t WndPaint(uptr_t wParam);
314 sptr_t HandleCompositionWindowed(uptr_t wParam, sptr_t lParam);
315 sptr_t HandleCompositionInline(uptr_t wParam, sptr_t lParam);
316 static bool KoreanIME();
317 void MoveImeCarets(Sci::Position offset);
318 void DrawImeIndicator(int indicator, int len);
319 void SetCandidateWindowPos();
320 void SelectionToHangul();
321 void EscapeHanja();
322 void ToggleHanja();
323 void AddWString(std::wstring wcs);
325 UINT CodePageOfDocument() const;
326 bool ValidCodePage(int codePage) const override;
327 sptr_t DefWndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam) override;
328 bool SetIdle(bool on) override;
329 UINT_PTR timers[tickDwell+1];
330 bool FineTickerRunning(TickReason reason) override;
331 void FineTickerStart(TickReason reason, int millis, int tolerance) override;
332 void FineTickerCancel(TickReason reason) override;
333 void SetMouseCapture(bool on) override;
334 bool HaveMouseCapture() override;
335 void SetTrackMouseLeaveEvent(bool on);
336 bool PaintContains(PRectangle rc) override;
337 void ScrollText(Sci::Line linesToMove) override;
338 void NotifyCaretMove() override;
339 void UpdateSystemCaret() override;
340 void SetVerticalScrollPos() override;
341 void SetHorizontalScrollPos() override;
342 bool ModifyScrollBars(Sci::Line nMax, Sci::Line nPage) override;
343 void NotifyChange() override;
344 void NotifyFocus(bool focus) override;
345 void SetCtrlID(int identifier) override;
346 int GetCtrlID() override;
347 void NotifyParent(SCNotification scn) override;
348 void NotifyDoubleClick(Point pt, int modifiers) override;
349 CaseFolder *CaseFolderForEncoding() override;
350 std::string CaseMapString(const std::string &s, int caseMapping) override;
351 void Copy() override;
352 void CopyAllowLine() override;
353 bool CanPaste() override;
354 void Paste() override;
355 void CreateCallTipWindow(PRectangle rc) override;
356 void AddToPopUp(const char *label, int cmd = 0, bool enabled = true) override;
357 void ClaimSelection() override;
359 // DBCS
360 void ImeStartComposition();
361 void ImeEndComposition();
362 LRESULT ImeOnReconvert(LPARAM lParam);
364 void GetIntelliMouseParameters();
365 void CopyToClipboard(const SelectionText &selectedText) override;
366 void ScrollMessage(WPARAM wParam);
367 void HorizontalScrollMessage(WPARAM wParam);
368 void FullPaint();
369 void FullPaintDC(HDC hdc);
370 bool IsCompatibleDC(HDC hOtherDC);
371 DWORD EffectFromState(DWORD grfKeyState) const;
373 int SetScrollInfo(int nBar, LPCSCROLLINFO lpsi, BOOL bRedraw);
374 bool GetScrollInfo(int nBar, LPSCROLLINFO lpsi);
375 void ChangeScrollPos(int barType, Sci::Position pos);
376 sptr_t GetTextLength();
377 sptr_t GetText(uptr_t wParam, sptr_t lParam);
379 public:
380 // Public for benefit of Scintilla_DirectFunction
381 sptr_t WndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam) override;
383 /// Implement IUnknown
384 STDMETHODIMP QueryInterface(REFIID riid, PVOID *ppv);
385 STDMETHODIMP_(ULONG)AddRef();
386 STDMETHODIMP_(ULONG)Release();
388 /// Implement IDropTarget
389 STDMETHODIMP DragEnter(LPDATAOBJECT pIDataSource, DWORD grfKeyState,
390 POINTL pt, PDWORD pdwEffect);
391 STDMETHODIMP DragOver(DWORD grfKeyState, POINTL pt, PDWORD pdwEffect);
392 STDMETHODIMP DragLeave();
393 STDMETHODIMP Drop(LPDATAOBJECT pIDataSource, DWORD grfKeyState,
394 POINTL pt, PDWORD pdwEffect);
396 /// Implement important part of IDataObject
397 STDMETHODIMP GetData(FORMATETC *pFEIn, STGMEDIUM *pSTM);
399 static bool Register(HINSTANCE hInstance_);
400 static bool Unregister();
402 friend class DropSource;
403 friend class DataObject;
404 friend class DropTarget;
405 bool DragIsRectangularOK(CLIPFORMAT fmt) const {
406 return drag.rectangular && (fmt == cfColumnSelect);
409 private:
410 // For use in creating a system caret
411 bool HasCaretSizeChanged() const;
412 BOOL CreateSystemCaret();
413 BOOL DestroySystemCaret();
414 HBITMAP sysCaretBitmap;
415 int sysCaretWidth;
416 int sysCaretHeight;
419 HINSTANCE ScintillaWin::hInstance = 0;
420 ATOM ScintillaWin::scintillaClassAtom = 0;
421 ATOM ScintillaWin::callClassAtom = 0;
423 ScintillaWin::ScintillaWin(HWND hwnd) {
425 lastKeyDownConsumed = false;
426 lastHighSurrogateChar = 0;
428 capturedMouse = false;
429 trackedMouseLeave = false;
430 SetCoalescableTimerFn = 0;
432 linesPerScroll = 0;
433 wheelDelta = 0; // Wheel delta from roll
435 hRgnUpdate = 0;
437 hasOKText = false;
439 // There does not seem to be a real standard for indicating that the clipboard
440 // contains a rectangular selection, so copy Developer Studio and Borland Delphi.
441 cfColumnSelect = static_cast<CLIPFORMAT>(
442 ::RegisterClipboardFormat(TEXT("MSDEVColumnSelect")));
443 cfBorlandIDEBlockType = static_cast<CLIPFORMAT>(
444 ::RegisterClipboardFormat(TEXT("Borland IDE Block Type")));
446 // Likewise for line-copy (copies a full line when no text is selected)
447 cfLineSelect = static_cast<CLIPFORMAT>(
448 ::RegisterClipboardFormat(TEXT("MSDEVLineSelect")));
449 cfVSLineTag = static_cast<CLIPFORMAT>(
450 ::RegisterClipboardFormat(TEXT("VisualStudioEditorOperationsLineCutCopyClipboardTag")));
451 hrOle = E_FAIL;
453 wMain = hwnd;
455 dob.sci = this;
456 ds.sci = this;
457 dt.sci = this;
459 sysCaretBitmap = 0;
460 sysCaretWidth = 0;
461 sysCaretHeight = 0;
463 #if defined(USE_D2D)
464 pRenderTarget = 0;
465 renderTargetValid = true;
466 #endif
468 caret.period = ::GetCaretBlinkTime();
469 if (caret.period < 0)
470 caret.period = 0;
472 Init();
475 ScintillaWin::~ScintillaWin() {}
477 void ScintillaWin::Init() {
478 // Initialize COM. If the app has already done this it will have
479 // no effect. If the app hasn't, we really shouldn't ask them to call
480 // it just so this internal feature works.
481 hrOle = ::OleInitialize(NULL);
483 // Find SetCoalescableTimer which is only available from Windows 8+
484 HMODULE user32 = ::GetModuleHandle(TEXT("user32.dll"));
485 if (user32) {
486 SetCoalescableTimerFn = (SetCoalescableTimerSig)::GetProcAddress(user32, "SetCoalescableTimer");
489 for (TickReason tr = tickCaret; tr <= tickDwell; tr = static_cast<TickReason>(tr + 1)) {
490 timers[tr] = 0;
492 vs.indicators[SC_INDICATOR_UNKNOWN] = Indicator(INDIC_HIDDEN, ColourDesired(0, 0, 0xff));
493 vs.indicators[SC_INDICATOR_INPUT] = Indicator(INDIC_DOTS, ColourDesired(0, 0, 0xff));
494 vs.indicators[SC_INDICATOR_CONVERTED] = Indicator(INDIC_COMPOSITIONTHICK, ColourDesired(0, 0, 0xff));
495 vs.indicators[SC_INDICATOR_TARGET] = Indicator(INDIC_STRAIGHTBOX, ColourDesired(0, 0, 0xff));
498 void ScintillaWin::Finalise() {
499 ScintillaBase::Finalise();
500 for (TickReason tr = tickCaret; tr <= tickDwell; tr = static_cast<TickReason>(tr + 1)) {
501 FineTickerCancel(tr);
503 SetIdle(false);
504 #if defined(USE_D2D)
505 DropRenderTarget();
506 #endif
507 ::RevokeDragDrop(MainHWND());
508 if (SUCCEEDED(hrOle)) {
509 ::OleUninitialize();
513 #if defined(USE_D2D)
515 void ScintillaWin::EnsureRenderTarget(HDC hdc) {
516 if (!renderTargetValid) {
517 DropRenderTarget();
518 renderTargetValid = true;
520 if (pD2DFactory && !pRenderTarget) {
521 RECT rc;
522 HWND hw = MainHWND();
523 GetClientRect(hw, &rc);
525 D2D1_SIZE_U size = D2D1::SizeU(rc.right - rc.left, rc.bottom - rc.top);
527 // Create a Direct2D render target.
528 #if 1
529 D2D1_RENDER_TARGET_PROPERTIES drtp;
530 drtp.type = D2D1_RENDER_TARGET_TYPE_DEFAULT;
531 drtp.pixelFormat.format = DXGI_FORMAT_UNKNOWN;
532 drtp.pixelFormat.alphaMode = D2D1_ALPHA_MODE_UNKNOWN;
533 drtp.dpiX = 96.0;
534 drtp.dpiY = 96.0;
535 drtp.usage = D2D1_RENDER_TARGET_USAGE_NONE;
536 drtp.minLevel = D2D1_FEATURE_LEVEL_DEFAULT;
538 if (technology == SC_TECHNOLOGY_DIRECTWRITEDC) {
539 // Explicit pixel format needed.
540 drtp.pixelFormat = D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM,
541 D2D1_ALPHA_MODE_IGNORE);
543 ID2D1DCRenderTarget *pDCRT = NULL;
544 HRESULT hr = pD2DFactory->CreateDCRenderTarget(&drtp, &pDCRT);
545 if (SUCCEEDED(hr)) {
546 pRenderTarget = pDCRT;
547 } else {
548 Platform::DebugPrintf("Failed CreateDCRenderTarget 0x%x\n", hr);
549 pRenderTarget = NULL;
552 } else {
553 D2D1_HWND_RENDER_TARGET_PROPERTIES dhrtp;
554 dhrtp.hwnd = hw;
555 dhrtp.pixelSize = size;
556 dhrtp.presentOptions = (technology == SC_TECHNOLOGY_DIRECTWRITERETAIN) ?
557 D2D1_PRESENT_OPTIONS_RETAIN_CONTENTS : D2D1_PRESENT_OPTIONS_NONE;
559 ID2D1HwndRenderTarget *pHwndRenderTarget = NULL;
560 HRESULT hr = pD2DFactory->CreateHwndRenderTarget(drtp, dhrtp, &pHwndRenderTarget);
561 if (SUCCEEDED(hr)) {
562 pRenderTarget = pHwndRenderTarget;
563 } else {
564 Platform::DebugPrintf("Failed CreateHwndRenderTarget 0x%x\n", hr);
565 pRenderTarget = NULL;
568 #else
569 pD2DFactory->CreateHwndRenderTarget(
570 D2D1::RenderTargetProperties(
571 D2D1_RENDER_TARGET_TYPE_DEFAULT ,
572 D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_PREMULTIPLIED),
573 96.0f, 96.0f, D2D1_RENDER_TARGET_USAGE_NONE, D2D1_FEATURE_LEVEL_DEFAULT),
574 D2D1::HwndRenderTargetProperties(hw, size),
575 &pRenderTarget);
576 #endif
577 // Pixmaps were created to be compatible with previous render target so
578 // need to be recreated.
579 DropGraphics(false);
582 if ((technology == SC_TECHNOLOGY_DIRECTWRITEDC) && pRenderTarget) {
583 RECT rcWindow;
584 GetClientRect(MainHWND(), &rcWindow);
585 HRESULT hr = static_cast<ID2D1DCRenderTarget*>(pRenderTarget)->BindDC(hdc, &rcWindow);
586 if (FAILED(hr)) {
587 Platform::DebugPrintf("BindDC failed 0x%x\n", hr);
588 DropRenderTarget();
593 void ScintillaWin::DropRenderTarget() {
594 if (pRenderTarget) {
595 pRenderTarget->Release();
596 pRenderTarget = 0;
600 #endif
602 HWND ScintillaWin::MainHWND() {
603 return static_cast<HWND>(wMain.GetID());
606 bool ScintillaWin::DragThreshold(Point ptStart, Point ptNow) {
607 const int xMove = static_cast<int>(std::abs(ptStart.x - ptNow.x));
608 const int yMove = static_cast<int>(std::abs(ptStart.y - ptNow.y));
609 return (xMove > ::GetSystemMetrics(SM_CXDRAG)) ||
610 (yMove > ::GetSystemMetrics(SM_CYDRAG));
613 void ScintillaWin::StartDrag() {
614 inDragDrop = ddDragging;
615 DWORD dwEffect = 0;
616 dropWentOutside = true;
617 IDataObject *pDataObject = reinterpret_cast<IDataObject *>(&dob);
618 IDropSource *pDropSource = reinterpret_cast<IDropSource *>(&ds);
619 //Platform::DebugPrintf("About to DoDragDrop %x %x\n", pDataObject, pDropSource);
620 const HRESULT hr = ::DoDragDrop(
621 pDataObject,
622 pDropSource,
623 DROPEFFECT_COPY | DROPEFFECT_MOVE, &dwEffect);
624 //Platform::DebugPrintf("DoDragDrop = %x\n", hr);
625 if (SUCCEEDED(hr)) {
626 if ((hr == DRAGDROP_S_DROP) && (dwEffect == DROPEFFECT_MOVE) && dropWentOutside) {
627 // Remove dragged out text
628 ClearSelection();
631 inDragDrop = ddNone;
632 SetDragPosition(SelectionPosition(Sci::invalidPosition));
635 int ScintillaWin::MouseModifiers(uptr_t wParam) {
636 return ModifierFlags((wParam & MK_SHIFT) != 0,
637 (wParam & MK_CONTROL) != 0,
638 KeyboardIsKeyDown(VK_MENU));
641 // Avoid warnings everywhere for old style casts by concentrating them here
642 static WORD LoWord(uptr_t l) {
643 return LOWORD(l);
646 static WORD HiWord(uptr_t l) {
647 return HIWORD(l);
650 static int InputCodePage() {
651 HKL inputLocale = ::GetKeyboardLayout(0);
652 LANGID inputLang = LOWORD(inputLocale);
653 char sCodePage[10];
654 const int res = ::GetLocaleInfoA(MAKELCID(inputLang, SORT_DEFAULT),
655 LOCALE_IDEFAULTANSICODEPAGE, sCodePage, sizeof(sCodePage));
656 if (!res)
657 return 0;
658 return atoi(sCodePage);
661 /** Map the key codes to their equivalent SCK_ form. */
662 static int KeyTranslate(int keyIn) {
663 //PLATFORM_ASSERT(!keyIn);
664 switch (keyIn) {
665 case VK_DOWN: return SCK_DOWN;
666 case VK_UP: return SCK_UP;
667 case VK_LEFT: return SCK_LEFT;
668 case VK_RIGHT: return SCK_RIGHT;
669 case VK_HOME: return SCK_HOME;
670 case VK_END: return SCK_END;
671 case VK_PRIOR: return SCK_PRIOR;
672 case VK_NEXT: return SCK_NEXT;
673 case VK_DELETE: return SCK_DELETE;
674 case VK_INSERT: return SCK_INSERT;
675 case VK_ESCAPE: return SCK_ESCAPE;
676 case VK_BACK: return SCK_BACK;
677 case VK_TAB: return SCK_TAB;
678 case VK_RETURN: return SCK_RETURN;
679 case VK_ADD: return SCK_ADD;
680 case VK_SUBTRACT: return SCK_SUBTRACT;
681 case VK_DIVIDE: return SCK_DIVIDE;
682 case VK_LWIN: return SCK_WIN;
683 case VK_RWIN: return SCK_RWIN;
684 case VK_APPS: return SCK_MENU;
685 case VK_OEM_2: return '/';
686 case VK_OEM_3: return '`';
687 case VK_OEM_4: return '[';
688 case VK_OEM_5: return '\\';
689 case VK_OEM_6: return ']';
690 default: return keyIn;
694 static bool BoundsContains(PRectangle rcBounds, HRGN hRgnBounds, PRectangle rcCheck) {
695 bool contains = true;
696 if (!rcCheck.Empty()) {
697 if (!rcBounds.Contains(rcCheck)) {
698 contains = false;
699 } else if (hRgnBounds) {
700 // In bounding rectangle so check more accurately using region
701 HRGN hRgnCheck = ::CreateRectRgn(static_cast<int>(rcCheck.left), static_cast<int>(rcCheck.top),
702 static_cast<int>(rcCheck.right), static_cast<int>(rcCheck.bottom));
703 if (hRgnCheck) {
704 HRGN hRgnDifference = ::CreateRectRgn(0, 0, 0, 0);
705 if (hRgnDifference) {
706 const int combination = ::CombineRgn(hRgnDifference, hRgnCheck, hRgnBounds, RGN_DIFF);
707 if (combination != NULLREGION) {
708 contains = false;
710 ::DeleteRgn(hRgnDifference);
712 ::DeleteRgn(hRgnCheck);
716 return contains;
719 static std::string StringEncode(const std::wstring &s, int codePage) {
720 const int cchMulti = s.length() ? ::WideCharToMultiByte(codePage, 0, s.c_str(), static_cast<int>(s.length()), NULL, 0, NULL, NULL) : 0;
721 std::string sMulti(cchMulti, 0);
722 if (cchMulti) {
723 ::WideCharToMultiByte(codePage, 0, s.c_str(), static_cast<int>(s.size()), &sMulti[0], cchMulti, NULL, NULL);
725 return sMulti;
728 static std::wstring StringDecode(const std::string &s, int codePage) {
729 const int cchWide = s.length() ? ::MultiByteToWideChar(codePage, 0, s.c_str(), static_cast<int>(s.length()), NULL, 0) : 0;
730 std::wstring sWide(cchWide, 0);
731 if (cchWide) {
732 ::MultiByteToWideChar(codePage, 0, s.c_str(), static_cast<int>(s.length()), &sWide[0], cchWide);
734 return sWide;
737 static std::wstring StringMapCase(const std::wstring &ws, DWORD mapFlags) {
738 const int charsConverted = ::LCMapStringW(LOCALE_SYSTEM_DEFAULT, mapFlags,
739 ws.c_str(), static_cast<int>(ws.length()), NULL, 0);
740 std::wstring wsConverted(charsConverted, 0);
741 if (charsConverted) {
742 ::LCMapStringW(LOCALE_SYSTEM_DEFAULT, mapFlags,
743 ws.c_str(), static_cast<int>(ws.length()), &wsConverted[0], charsConverted);
745 return wsConverted;
748 // Returns the target converted to UTF8.
749 // Return the length in bytes.
750 Sci::Position ScintillaWin::TargetAsUTF8(char *text) {
751 Sci::Position targetLength = targetEnd - targetStart;
752 if (IsUnicodeMode()) {
753 if (text) {
754 pdoc->GetCharRange(text, targetStart, targetLength);
756 } else {
757 // Need to convert
758 const std::string s = RangeText(targetStart, targetEnd);
759 const std::wstring characters = StringDecode(s, CodePageOfDocument());
760 const int utf8Len = ::WideCharToMultiByte(CP_UTF8, 0, characters.c_str(), static_cast<int>(characters.length()), NULL, 0, 0, 0);
761 if (text) {
762 ::WideCharToMultiByte(CP_UTF8, 0, characters.c_str(), static_cast<int>(characters.length()), text, utf8Len, 0, 0);
763 text[utf8Len] = '\0';
765 return utf8Len;
767 return targetLength;
770 // Translates a nul terminated UTF8 string into the document encoding.
771 // Return the length of the result in bytes.
772 Sci::Position ScintillaWin::EncodedFromUTF8(const char *utf8, char *encoded) const {
773 const Sci::Position inputLength = (lengthForEncode >= 0) ? lengthForEncode : static_cast<Sci::Position>(strlen(utf8));
774 if (IsUnicodeMode()) {
775 if (encoded) {
776 memcpy(encoded, utf8, inputLength);
778 return inputLength;
779 } else {
780 // Need to convert
781 const int charsLen = ::MultiByteToWideChar(CP_UTF8, 0, utf8, static_cast<int>(inputLength), NULL, 0);
782 std::wstring characters(charsLen, '\0');
783 ::MultiByteToWideChar(CP_UTF8, 0, utf8, static_cast<int>(inputLength), &characters[0], charsLen);
785 const int encodedLen = ::WideCharToMultiByte(CodePageOfDocument(),
786 0, &characters[0], charsLen, NULL, 0, 0, 0);
787 if (encoded) {
788 ::WideCharToMultiByte(CodePageOfDocument(), 0, &characters[0], charsLen, encoded, encodedLen, 0, 0);
789 encoded[encodedLen] = '\0';
791 return encodedLen;
795 // Add one character from a UTF-16 string, by converting to either UTF-8 or
796 // the current codepage. Code is similar to HandleCompositionWindowed().
797 void ScintillaWin::AddCharUTF16(wchar_t const *wcs, unsigned int wclen) {
798 if (IsUnicodeMode()) {
799 char utfval[maxLenInputIME * 3];
800 size_t len = UTF8Length(wcs, wclen);
801 UTF8FromUTF16(wcs, wclen, utfval, len);
802 utfval[len] = '\0';
803 AddCharUTF(utfval, static_cast<unsigned int>(len));
804 } else {
805 UINT cpDest = CodePageOfDocument();
806 char inBufferCP[maxLenInputIME * 2];
807 const int size = ::WideCharToMultiByte(cpDest,
808 0, wcs, wclen, inBufferCP, sizeof(inBufferCP) - 1, 0, 0);
809 for (int i=0; i<size; i++) {
810 AddChar(inBufferCP[i]);
815 sptr_t ScintillaWin::WndPaint(uptr_t wParam) {
816 //ElapsedTime et;
818 // Redirect assertions to debug output and save current state
819 const bool assertsPopup = Platform::ShowAssertionPopUps(false);
820 paintState = painting;
821 PAINTSTRUCT ps;
822 PAINTSTRUCT *pps;
824 const bool IsOcxCtrl = (wParam != 0); // if wParam != 0, it contains
825 // a PAINSTRUCT* from the OCX
826 // Removed since this interferes with reporting other assertions as it occurs repeatedly
827 //PLATFORM_ASSERT(hRgnUpdate == NULL);
828 hRgnUpdate = ::CreateRectRgn(0, 0, 0, 0);
829 if (IsOcxCtrl) {
830 pps = reinterpret_cast<PAINTSTRUCT*>(wParam);
831 } else {
832 ::GetUpdateRgn(MainHWND(), hRgnUpdate, FALSE);
833 pps = &ps;
834 ::BeginPaint(MainHWND(), pps);
836 rcPaint = PRectangle::FromInts(pps->rcPaint.left, pps->rcPaint.top, pps->rcPaint.right, pps->rcPaint.bottom);
837 const PRectangle rcClient = GetClientRectangle();
838 paintingAllText = BoundsContains(rcPaint, hRgnUpdate, rcClient);
839 if (technology == SC_TECHNOLOGY_DEFAULT) {
840 AutoSurface surfaceWindow(pps->hdc, this);
841 if (surfaceWindow) {
842 Paint(surfaceWindow, rcPaint);
843 surfaceWindow->Release();
845 } else {
846 #if defined(USE_D2D)
847 EnsureRenderTarget(pps->hdc);
848 AutoSurface surfaceWindow(pRenderTarget, this);
849 if (surfaceWindow) {
850 pRenderTarget->BeginDraw();
851 Paint(surfaceWindow, rcPaint);
852 surfaceWindow->Release();
853 const HRESULT hr = pRenderTarget->EndDraw();
854 if (hr == static_cast<HRESULT>(D2DERR_RECREATE_TARGET)) {
855 DropRenderTarget();
856 paintState = paintAbandoned;
859 #endif
861 if (hRgnUpdate) {
862 ::DeleteRgn(hRgnUpdate);
863 hRgnUpdate = 0;
866 if (!IsOcxCtrl)
867 ::EndPaint(MainHWND(), pps);
868 if (paintState == paintAbandoned) {
869 // Painting area was insufficient to cover new styling or brace highlight positions
870 if (IsOcxCtrl) {
871 FullPaintDC(pps->hdc);
872 } else {
873 FullPaint();
876 paintState = notPainting;
878 // Restore debug output state
879 Platform::ShowAssertionPopUps(assertsPopup);
881 //Platform::DebugPrintf("Paint took %g\n", et.Duration());
882 return 0l;
885 sptr_t ScintillaWin::HandleCompositionWindowed(uptr_t wParam, sptr_t lParam) {
886 if (lParam & GCS_RESULTSTR) {
887 IMContext imc(MainHWND());
888 if (imc.hIMC) {
889 AddWString(imc.GetCompositionString(GCS_RESULTSTR));
891 // Set new position after converted
892 Point pos = PointMainCaret();
893 COMPOSITIONFORM CompForm;
894 CompForm.dwStyle = CFS_POINT;
895 CompForm.ptCurrentPos.x = static_cast<int>(pos.x);
896 CompForm.ptCurrentPos.y = static_cast<int>(pos.y);
897 ::ImmSetCompositionWindow(imc.hIMC, &CompForm);
899 return 0;
901 return ::DefWindowProc(MainHWND(), WM_IME_COMPOSITION, wParam, lParam);
904 bool ScintillaWin::KoreanIME() {
905 const int codePage = InputCodePage();
906 return codePage == 949 || codePage == 1361;
909 void ScintillaWin::MoveImeCarets(Sci::Position offset) {
910 // Move carets relatively by bytes.
911 for (size_t r=0; r<sel.Count(); r++) {
912 Sci::Position positionInsert = sel.Range(r).Start().Position();
913 sel.Range(r).caret.SetPosition(positionInsert + offset);
914 sel.Range(r).anchor.SetPosition(positionInsert + offset);
918 void ScintillaWin::DrawImeIndicator(int indicator, int len) {
919 // Emulate the visual style of IME characters with indicators.
920 // Draw an indicator on the character before caret by the character bytes of len
921 // so it should be called after addCharUTF().
922 // It does not affect caret positions.
923 if (indicator < 8 || indicator > INDIC_MAX) {
924 return;
926 pdoc->DecorationSetCurrentIndicator(indicator);
927 for (size_t r=0; r<sel.Count(); r++) {
928 Sci::Position positionInsert = sel.Range(r).Start().Position();
929 pdoc->DecorationFillRange(positionInsert - len, 1, len);
933 void ScintillaWin::SetCandidateWindowPos() {
934 IMContext imc(MainHWND());
935 if (imc.hIMC) {
936 Point pos = PointMainCaret();
937 CANDIDATEFORM CandForm;
938 CandForm.dwIndex = 0;
939 CandForm.dwStyle = CFS_CANDIDATEPOS;
940 CandForm.ptCurrentPos.x = static_cast<int>(pos.x);
941 CandForm.ptCurrentPos.y = static_cast<int>(pos.y + vs.lineHeight);
942 ::ImmSetCandidateWindow(imc.hIMC, &CandForm);
946 void ScintillaWin::SelectionToHangul() {
947 // Convert every hanja to hangul within the main range.
948 const Sci::Position selStart = sel.RangeMain().Start().Position();
949 const Sci::Position documentStrLen = sel.RangeMain().Length();
950 const Sci::Position selEnd = selStart + documentStrLen;
951 const Sci::Position utf16Len = pdoc->CountUTF16(selStart, selEnd);
953 if (utf16Len > 0) {
954 std::string documentStr(documentStrLen, '\0');
955 pdoc->GetCharRange(&documentStr[0], selStart, documentStrLen);
957 std::wstring uniStr = StringDecode(documentStr, CodePageOfDocument());
958 const int converted = HanjaDict::GetHangulOfHanja(&uniStr[0]);
959 documentStr = StringEncode(uniStr, CodePageOfDocument());
961 if (converted > 0) {
962 pdoc->BeginUndoAction();
963 ClearSelection();
964 InsertPaste(&documentStr[0], static_cast<int>(documentStr.size()));
965 pdoc->EndUndoAction();
970 void ScintillaWin::EscapeHanja() {
971 // The candidate box pops up to user to select a hanja.
972 // It comes into WM_IME_COMPOSITION with GCS_RESULTSTR.
973 // The existing hangul or hanja is replaced with it.
974 if (sel.Count() > 1) {
975 return; // Do not allow multi carets.
977 Sci::Position currentPos = CurrentPosition();
978 int oneCharLen = pdoc->LenChar(currentPos);
980 if (oneCharLen < 2) {
981 return; // No need to handle SBCS.
984 // ImmEscapeW() may overwrite uniChar[] with a null terminated string.
985 // So enlarge it enough to Maximum 4 as in UTF-8.
986 unsigned int const safeLength = UTF8MaxBytes+1;
987 std::string oneChar(safeLength, '\0');
988 pdoc->GetCharRange(&oneChar[0], currentPos, oneCharLen);
990 std::wstring uniChar = StringDecode(oneChar, CodePageOfDocument());
992 IMContext imc(MainHWND());
993 if (imc.hIMC) {
994 // Set the candidate box position since IME may show it.
995 SetCandidateWindowPos();
996 // IME_ESC_HANJA_MODE appears to receive the first character only.
997 if (ImmEscapeW(GetKeyboardLayout(0), imc.hIMC, IME_ESC_HANJA_MODE, &uniChar[0])) {
998 SetSelection(currentPos, currentPos + oneCharLen);
1003 void ScintillaWin::ToggleHanja() {
1004 // If selection, convert every hanja to hangul within the main range.
1005 // If no selection, commit to IME.
1006 if (sel.Count() > 1) {
1007 return; // Do not allow multi carets.
1010 if (sel.Empty()) {
1011 EscapeHanja();
1012 } else {
1013 SelectionToHangul();
1017 namespace {
1019 std::vector<int> MapImeIndicators(std::vector<BYTE> inputStyle) {
1020 std::vector<int> imeIndicator(inputStyle.size(), SC_INDICATOR_UNKNOWN);
1021 for (size_t i = 0; i < inputStyle.size(); i++) {
1022 switch (static_cast<int>(inputStyle.at(i))) {
1023 case ATTR_INPUT:
1024 imeIndicator[i] = SC_INDICATOR_INPUT;
1025 break;
1026 case ATTR_TARGET_NOTCONVERTED:
1027 case ATTR_TARGET_CONVERTED:
1028 imeIndicator[i] = SC_INDICATOR_TARGET;
1029 break;
1030 case ATTR_CONVERTED:
1031 imeIndicator[i] = SC_INDICATOR_CONVERTED;
1032 break;
1033 default:
1034 imeIndicator[i] = SC_INDICATOR_UNKNOWN;
1035 break;
1038 return imeIndicator;
1043 void ScintillaWin::AddWString(std::wstring wcs) {
1044 if (wcs.empty())
1045 return;
1047 const int codePage = CodePageOfDocument();
1048 for (size_t i = 0; i < wcs.size(); ) {
1049 const size_t ucWidth = UTF16CharLength(wcs[i]);
1050 const std::wstring uniChar(wcs, i, ucWidth);
1051 std::string docChar = StringEncode(uniChar, codePage);
1053 AddCharUTF(docChar.c_str(), static_cast<unsigned int>(docChar.size()));
1054 i += ucWidth;
1058 sptr_t ScintillaWin::HandleCompositionInline(uptr_t, sptr_t lParam) {
1059 // Copy & paste by johnsonj with a lot of helps of Neil.
1060 // Great thanks for my foreruners, jiniya and BLUEnLIVE.
1062 IMContext imc(MainHWND());
1063 if (!imc.hIMC)
1064 return 0;
1065 if (pdoc->IsReadOnly() || SelectionContainsProtected()) {
1066 ::ImmNotifyIME(imc.hIMC, NI_COMPOSITIONSTR, CPS_CANCEL, 0);
1067 return 0;
1070 bool initialCompose = false;
1071 if (pdoc->TentativeActive()) {
1072 pdoc->TentativeUndo();
1073 } else {
1074 // No tentative undo means start of this composition so
1075 // fill in any virtual spaces.
1076 initialCompose = true;
1079 view.imeCaretBlockOverride = false;
1081 if (lParam & GCS_COMPSTR) {
1082 const std::wstring wcs = imc.GetCompositionString(GCS_COMPSTR);
1083 if ((wcs.size() == 0) || (wcs.size() >= maxLenInputIME)) {
1084 ShowCaretAtCurrentPosition();
1085 return 0;
1088 if (initialCompose)
1089 ClearBeforeTentativeStart();
1090 pdoc->TentativeStart(); // TentativeActive from now on.
1092 std::vector<int> imeIndicator = MapImeIndicators(imc.GetImeAttributes());
1094 const bool tmpRecordingMacro = recordingMacro;
1095 recordingMacro = false;
1096 const int codePage = CodePageOfDocument();
1097 for (size_t i = 0; i < wcs.size(); ) {
1098 const size_t ucWidth = UTF16CharLength(wcs[i]);
1099 const std::wstring uniChar(wcs, i, ucWidth);
1100 std::string docChar = StringEncode(uniChar, codePage);
1102 AddCharUTF(docChar.c_str(), static_cast<unsigned int>(docChar.size()));
1104 DrawImeIndicator(imeIndicator[i], static_cast<unsigned int>(docChar.size()));
1105 i += ucWidth;
1107 recordingMacro = tmpRecordingMacro;
1109 // Move IME caret from current last position to imeCaretPos.
1110 const int imeEndToImeCaretU16 = imc.GetImeCaretPos() - static_cast<unsigned int>(wcs.size());
1111 Sci::Position imeCaretPosDoc = pdoc->GetRelativePositionUTF16(CurrentPosition(), imeEndToImeCaretU16);
1113 MoveImeCarets(- CurrentPosition() + imeCaretPosDoc);
1115 if (KoreanIME()) {
1116 view.imeCaretBlockOverride = true;
1118 } else if (lParam & GCS_RESULTSTR) {
1119 AddWString(imc.GetCompositionString(GCS_RESULTSTR));
1121 EnsureCaretVisible();
1122 SetCandidateWindowPos();
1123 ShowCaretAtCurrentPosition();
1124 return 0;
1127 // Translate message IDs from WM_* and EM_* to SCI_* so can partly emulate Windows Edit control
1128 static unsigned int SciMessageFromEM(unsigned int iMessage) {
1129 switch (iMessage) {
1130 case EM_CANPASTE: return SCI_CANPASTE;
1131 case EM_CANUNDO: return SCI_CANUNDO;
1132 case EM_EMPTYUNDOBUFFER: return SCI_EMPTYUNDOBUFFER;
1133 case EM_FINDTEXTEX: return SCI_FINDTEXT;
1134 case EM_FORMATRANGE: return SCI_FORMATRANGE;
1135 case EM_GETFIRSTVISIBLELINE: return SCI_GETFIRSTVISIBLELINE;
1136 case EM_GETLINECOUNT: return SCI_GETLINECOUNT;
1137 case EM_GETSELTEXT: return SCI_GETSELTEXT;
1138 case EM_GETTEXTRANGE: return SCI_GETTEXTRANGE;
1139 case EM_HIDESELECTION: return SCI_HIDESELECTION;
1140 case EM_LINEINDEX: return SCI_POSITIONFROMLINE;
1141 case EM_LINESCROLL: return SCI_LINESCROLL;
1142 case EM_REPLACESEL: return SCI_REPLACESEL;
1143 case EM_SCROLLCARET: return SCI_SCROLLCARET;
1144 case EM_SETREADONLY: return SCI_SETREADONLY;
1145 case WM_CLEAR: return SCI_CLEAR;
1146 case WM_COPY: return SCI_COPY;
1147 case WM_CUT: return SCI_CUT;
1148 case WM_SETTEXT: return SCI_SETTEXT;
1149 case WM_PASTE: return SCI_PASTE;
1150 case WM_UNDO: return SCI_UNDO;
1152 return iMessage;
1155 UINT CodePageFromCharSet(DWORD characterSet, UINT documentCodePage) {
1156 if (documentCodePage == SC_CP_UTF8) {
1157 return SC_CP_UTF8;
1159 switch (characterSet) {
1160 case SC_CHARSET_ANSI: return 1252;
1161 case SC_CHARSET_DEFAULT: return documentCodePage ? documentCodePage : 1252;
1162 case SC_CHARSET_BALTIC: return 1257;
1163 case SC_CHARSET_CHINESEBIG5: return 950;
1164 case SC_CHARSET_EASTEUROPE: return 1250;
1165 case SC_CHARSET_GB2312: return 936;
1166 case SC_CHARSET_GREEK: return 1253;
1167 case SC_CHARSET_HANGUL: return 949;
1168 case SC_CHARSET_MAC: return 10000;
1169 case SC_CHARSET_OEM: return 437;
1170 case SC_CHARSET_RUSSIAN: return 1251;
1171 case SC_CHARSET_SHIFTJIS: return 932;
1172 case SC_CHARSET_TURKISH: return 1254;
1173 case SC_CHARSET_JOHAB: return 1361;
1174 case SC_CHARSET_HEBREW: return 1255;
1175 case SC_CHARSET_ARABIC: return 1256;
1176 case SC_CHARSET_VIETNAMESE: return 1258;
1177 case SC_CHARSET_THAI: return 874;
1178 case SC_CHARSET_8859_15: return 28605;
1179 // Not supported
1180 case SC_CHARSET_CYRILLIC: return documentCodePage;
1181 case SC_CHARSET_SYMBOL: return documentCodePage;
1183 return documentCodePage;
1186 UINT ScintillaWin::CodePageOfDocument() const {
1187 return CodePageFromCharSet(vs.styles[STYLE_DEFAULT].characterSet, pdoc->dbcsCodePage);
1190 sptr_t ScintillaWin::GetTextLength() {
1191 if (pdoc->Length() == 0)
1192 return 0;
1193 std::vector<char> docBytes(pdoc->Length(), '\0');
1194 pdoc->GetCharRange(&docBytes[0], 0, pdoc->Length());
1195 if (IsUnicodeMode()) {
1196 return UTF16Length(&docBytes[0], docBytes.size());
1197 } else {
1198 return ::MultiByteToWideChar(CodePageOfDocument(), 0, &docBytes[0],
1199 static_cast<int>(docBytes.size()), NULL, 0);
1203 sptr_t ScintillaWin::GetText(uptr_t wParam, sptr_t lParam) {
1204 wchar_t *ptr = reinterpret_cast<wchar_t *>(lParam);
1205 if (pdoc->Length() == 0) {
1206 *ptr = L'\0';
1207 return 0;
1209 std::vector<char> docBytes(pdoc->Length(), '\0');
1210 pdoc->GetCharRange(&docBytes[0], 0, pdoc->Length());
1211 if (IsUnicodeMode()) {
1212 size_t lengthUTF16 = UTF16Length(&docBytes[0], static_cast<unsigned int>(docBytes.size()));
1213 if (lParam == 0)
1214 return lengthUTF16;
1215 if (wParam == 0)
1216 return 0;
1217 size_t uLen = UTF16FromUTF8(&docBytes[0], docBytes.size(),
1218 ptr, wParam - 1);
1219 ptr[uLen] = L'\0';
1220 return uLen;
1221 } else {
1222 // Not Unicode mode
1223 // Convert to Unicode using the current Scintilla code page
1224 const UINT cpSrc = CodePageOfDocument();
1225 int lengthUTF16 = ::MultiByteToWideChar(cpSrc, 0, &docBytes[0],
1226 static_cast<int>(docBytes.size()), NULL, 0);
1227 if (lengthUTF16 >= static_cast<int>(wParam))
1228 lengthUTF16 = static_cast<int>(wParam)-1;
1229 ::MultiByteToWideChar(cpSrc, 0, &docBytes[0],
1230 static_cast<int>(docBytes.size()),
1231 ptr, lengthUTF16);
1232 ptr[lengthUTF16] = L'\0';
1233 return lengthUTF16;
1237 sptr_t ScintillaWin::WndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
1238 try {
1239 //Platform::DebugPrintf("S M:%x WP:%x L:%x\n", iMessage, wParam, lParam);
1240 iMessage = SciMessageFromEM(iMessage);
1241 switch (iMessage) {
1243 case WM_CREATE:
1244 ctrlID = ::GetDlgCtrlID(static_cast<HWND>(wMain.GetID()));
1245 // Get Intellimouse scroll line parameters
1246 GetIntelliMouseParameters();
1247 ::RegisterDragDrop(MainHWND(), reinterpret_cast<IDropTarget *>(&dt));
1248 break;
1250 case WM_COMMAND:
1251 Command(LoWord(wParam));
1252 break;
1254 case WM_PAINT:
1255 return WndPaint(wParam);
1257 case WM_PRINTCLIENT: {
1258 HDC hdc = reinterpret_cast<HDC>(wParam);
1259 if (!IsCompatibleDC(hdc)) {
1260 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1262 FullPaintDC(hdc);
1264 break;
1266 case WM_VSCROLL:
1267 ScrollMessage(wParam);
1268 break;
1270 case WM_HSCROLL:
1271 HorizontalScrollMessage(wParam);
1272 break;
1274 case WM_SIZE: {
1275 #if defined(USE_D2D)
1276 if (paintState == notPainting) {
1277 DropRenderTarget();
1278 } else {
1279 renderTargetValid = false;
1281 #endif
1282 //Platform::DebugPrintf("Scintilla WM_SIZE %d %d\n", LoWord(lParam), HiWord(lParam));
1283 ChangeSize();
1285 break;
1287 case WM_MOUSEWHEEL:
1288 if (!mouseWheelCaptures) {
1289 // if the mouse wheel is not captured, test if the mouse
1290 // pointer is over the editor window and if not, don't
1291 // handle the message but pass it on.
1292 RECT rc;
1293 GetWindowRect(MainHWND(), &rc);
1294 POINT pt;
1295 pt.x = GET_X_LPARAM(lParam);
1296 pt.y = GET_Y_LPARAM(lParam);
1297 if (!PtInRect(&rc, pt))
1298 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1300 // if autocomplete list active then send mousewheel message to it
1301 if (ac.Active()) {
1302 HWND hWnd = static_cast<HWND>(ac.lb->GetID());
1303 ::SendMessage(hWnd, iMessage, wParam, lParam);
1304 break;
1307 // Don't handle datazoom.
1308 // (A good idea for datazoom would be to "fold" or "unfold" details.
1309 // i.e. if datazoomed out only class structures are visible, when datazooming in the control
1310 // structures appear, then eventually the individual statements...)
1311 if (wParam & MK_SHIFT) {
1312 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1315 // Either SCROLL or ZOOM. We handle the wheel steppings calculation
1316 wheelDelta -= static_cast<short>(HiWord(wParam));
1317 if (abs(wheelDelta) >= WHEEL_DELTA && linesPerScroll > 0) {
1318 Sci::Line linesToScroll = linesPerScroll;
1319 if (linesPerScroll == WHEEL_PAGESCROLL)
1320 linesToScroll = LinesOnScreen() - 1;
1321 if (linesToScroll == 0) {
1322 linesToScroll = 1;
1324 linesToScroll *= (wheelDelta / WHEEL_DELTA);
1325 if (wheelDelta >= 0)
1326 wheelDelta = wheelDelta % WHEEL_DELTA;
1327 else
1328 wheelDelta = - (-wheelDelta % WHEEL_DELTA);
1330 if (wParam & MK_CONTROL) {
1331 // Zoom! We play with the font sizes in the styles.
1332 // Number of steps/line is ignored, we just care if sizing up or down
1333 if (linesToScroll < 0) {
1334 KeyCommand(SCI_ZOOMIN);
1335 } else {
1336 KeyCommand(SCI_ZOOMOUT);
1338 } else {
1339 // Scroll
1340 ScrollTo(topLine + linesToScroll);
1343 return 0;
1345 case WM_TIMER:
1346 if (wParam == idleTimerID && idler.state) {
1347 SendMessage(MainHWND(), SC_WIN_IDLE, 0, 1);
1348 } else {
1349 TickFor(static_cast<TickReason>(wParam - fineTimerStart));
1351 break;
1353 case SC_WIN_IDLE:
1354 // wParam=dwTickCountInitial, or 0 to initialize. lParam=bSkipUserInputTest
1355 if (idler.state) {
1356 if (lParam || (WAIT_TIMEOUT == MsgWaitForMultipleObjects(0, 0, 0, 0, QS_INPUT|QS_HOTKEY))) {
1357 if (Idle()) {
1358 // User input was given priority above, but all events do get a turn. Other
1359 // messages, notifications, etc. will get interleaved with the idle messages.
1361 // However, some things like WM_PAINT are a lower priority, and will not fire
1362 // when there's a message posted. So, several times a second, we stop and let
1363 // the low priority events have a turn (after which the timer will fire again).
1365 // Suppress a warning from Code Analysis that the GetTickCount function
1366 // wraps after 49 days. The WM_TIMER will kick off another SC_WIN_IDLE
1367 // after the wrap.
1368 #ifdef _MSC_VER
1369 #pragma warning(suppress: 28159)
1370 #endif
1371 const DWORD dwCurrent = GetTickCount();
1372 const DWORD dwStart = wParam ? static_cast<DWORD>(wParam) : dwCurrent;
1373 const DWORD maxWorkTime = 50;
1375 if (dwCurrent >= dwStart && dwCurrent > maxWorkTime && dwCurrent - maxWorkTime < dwStart)
1376 PostMessage(MainHWND(), SC_WIN_IDLE, dwStart, 0);
1377 } else {
1378 SetIdle(false);
1382 break;
1384 case WM_GETMINMAXINFO:
1385 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1387 case WM_LBUTTONDOWN: {
1388 // For IME, set the composition string as the result string.
1389 IMContext imc(MainHWND());
1390 ::ImmNotifyIME(imc.hIMC, NI_COMPOSITIONSTR, CPS_COMPLETE, 0);
1392 //Platform::DebugPrintf("Buttdown %d %x %x %x %x %x\n",iMessage, wParam, lParam,
1393 // KeyboardIsKeyDown(VK_SHIFT),
1394 // KeyboardIsKeyDown(VK_CONTROL),
1395 // KeyboardIsKeyDown(VK_MENU));
1396 ::SetFocus(MainHWND());
1397 ButtonDownWithModifiers(PointFromLParam(lParam), ::GetMessageTime(),
1398 MouseModifiers(wParam));
1400 break;
1402 case WM_MOUSEMOVE: {
1403 const Point pt = PointFromLParam(lParam);
1405 // Windows might send WM_MOUSEMOVE even though the mouse has not been moved:
1406 // http://blogs.msdn.com/b/oldnewthing/archive/2003/10/01/55108.aspx
1407 if (ptMouseLast.x != pt.x || ptMouseLast.y != pt.y) {
1408 SetTrackMouseLeaveEvent(true);
1409 ButtonMoveWithModifiers(pt, ::GetMessageTime(), MouseModifiers(wParam));
1412 break;
1414 case WM_MOUSELEAVE:
1415 SetTrackMouseLeaveEvent(false);
1416 MouseLeave();
1417 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1419 case WM_LBUTTONUP:
1420 ButtonUpWithModifiers(PointFromLParam(lParam),
1421 ::GetMessageTime(), MouseModifiers(wParam));
1422 break;
1424 case WM_RBUTTONDOWN: {
1425 ::SetFocus(MainHWND());
1426 Point pt = PointFromLParam(lParam);
1427 if (!PointInSelection(pt)) {
1428 CancelModes();
1429 SetEmptySelection(PositionFromLocation(PointFromLParam(lParam)));
1432 RightButtonDownWithModifiers(pt, ::GetMessageTime(), MouseModifiers(wParam));
1434 break;
1436 case WM_SETCURSOR:
1437 if (LoWord(lParam) == HTCLIENT) {
1438 if (inDragDrop == ddDragging) {
1439 DisplayCursor(Window::cursorUp);
1440 } else {
1441 // Display regular (drag) cursor over selection
1442 POINT pt;
1443 if (0 != ::GetCursorPos(&pt)) {
1444 ::ScreenToClient(MainHWND(), &pt);
1445 if (PointInSelMargin(PointFromPOINT(pt))) {
1446 DisplayCursor(GetMarginCursor(PointFromPOINT(pt)));
1447 } else if (PointInSelection(PointFromPOINT(pt)) && !SelectionEmpty()) {
1448 DisplayCursor(Window::cursorArrow);
1449 } else if (PointIsHotspot(PointFromPOINT(pt))) {
1450 DisplayCursor(Window::cursorHand);
1451 } else {
1452 DisplayCursor(Window::cursorText);
1456 return TRUE;
1457 } else {
1458 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1461 case WM_CHAR:
1462 if (((wParam >= 128) || !iscntrl(static_cast<int>(wParam))) || !lastKeyDownConsumed) {
1463 wchar_t wcs[3] = {static_cast<wchar_t>(wParam), 0};
1464 unsigned int wclen = 1;
1465 if (IS_HIGH_SURROGATE(wcs[0])) {
1466 // If this is a high surrogate character, we need a second one
1467 lastHighSurrogateChar = wcs[0];
1468 return 0;
1469 } else if (IS_LOW_SURROGATE(wcs[0])) {
1470 wcs[1] = wcs[0];
1471 wcs[0] = lastHighSurrogateChar;
1472 lastHighSurrogateChar = 0;
1473 wclen = 2;
1475 AddCharUTF16(wcs, wclen);
1477 return 0;
1479 case WM_UNICHAR:
1480 if (wParam == UNICODE_NOCHAR) {
1481 return TRUE;
1482 } else if (lastKeyDownConsumed) {
1483 return 1;
1484 } else {
1485 wchar_t wcs[3] = {0};
1486 unsigned int wclen = UTF16FromUTF32Character(static_cast<unsigned int>(wParam), wcs);
1487 AddCharUTF16(wcs, wclen);
1488 return FALSE;
1491 case WM_SYSKEYDOWN:
1492 case WM_KEYDOWN: {
1493 //Platform::DebugPrintf("S keydown %d %x %x %x %x\n",iMessage, wParam, lParam, ::IsKeyDown(VK_SHIFT), ::IsKeyDown(VK_CONTROL));
1494 lastKeyDownConsumed = false;
1495 const int ret = KeyDownWithModifiers(KeyTranslate(static_cast<int>(wParam)),
1496 ModifierFlags(KeyboardIsKeyDown(VK_SHIFT),
1497 KeyboardIsKeyDown(VK_CONTROL),
1498 KeyboardIsKeyDown(VK_MENU)),
1499 &lastKeyDownConsumed);
1500 if (!ret && !lastKeyDownConsumed) {
1501 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1503 break;
1506 case WM_IME_KEYDOWN: {
1507 if (wParam == VK_HANJA) {
1508 ToggleHanja();
1510 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1513 case WM_IME_REQUEST: {
1514 if (wParam == IMR_RECONVERTSTRING) {
1515 return ImeOnReconvert(lParam);
1517 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1520 case WM_KEYUP:
1521 //Platform::DebugPrintf("S keyup %d %x %x\n",iMessage, wParam, lParam);
1522 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1524 case WM_SETTINGCHANGE:
1525 //Platform::DebugPrintf("Setting Changed\n");
1526 InvalidateStyleData();
1527 // Get Intellimouse scroll line parameters
1528 GetIntelliMouseParameters();
1529 break;
1531 case WM_GETDLGCODE:
1532 return DLGC_HASSETSEL | DLGC_WANTALLKEYS;
1534 case WM_KILLFOCUS: {
1535 HWND wOther = reinterpret_cast<HWND>(wParam);
1536 HWND wThis = MainHWND();
1537 const HWND wCT = static_cast<HWND>(ct.wCallTip.GetID());
1538 if (!wParam ||
1539 !(::IsChild(wThis, wOther) || (wOther == wCT))) {
1540 SetFocusState(false);
1541 DestroySystemCaret();
1543 // Explicitly complete any IME composition
1544 IMContext imc(MainHWND());
1545 if (imc.hIMC) {
1546 ::ImmNotifyIME(imc.hIMC, NI_COMPOSITIONSTR, CPS_COMPLETE, 0);
1549 break;
1551 case WM_SETFOCUS:
1552 SetFocusState(true);
1553 DestroySystemCaret();
1554 CreateSystemCaret();
1555 break;
1557 case WM_SYSCOLORCHANGE:
1558 //Platform::DebugPrintf("Setting Changed\n");
1559 InvalidateStyleData();
1560 break;
1562 case WM_IME_STARTCOMPOSITION: // dbcs
1563 if (KoreanIME() || imeInteraction == imeInline) {
1564 return 0;
1565 } else {
1566 ImeStartComposition();
1567 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1570 case WM_IME_ENDCOMPOSITION: // dbcs
1571 ImeEndComposition();
1572 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1574 case WM_IME_COMPOSITION:
1575 if (KoreanIME() || imeInteraction == imeInline) {
1576 return HandleCompositionInline(wParam, lParam);
1577 } else {
1578 return HandleCompositionWindowed(wParam, lParam);
1581 case WM_CONTEXTMENU: {
1582 Point pt = PointFromLParam(lParam);
1583 POINT rpt = {static_cast<int>(pt.x), static_cast<int>(pt.y)};
1584 ::ScreenToClient(MainHWND(), &rpt);
1585 const Point ptClient = PointFromPOINT(rpt);
1586 if (ShouldDisplayPopup(ptClient)) {
1587 if ((pt.x == -1) && (pt.y == -1)) {
1588 // Caused by keyboard so display menu near caret
1589 pt = PointMainCaret();
1590 POINT spt = {static_cast<int>(pt.x), static_cast<int>(pt.y)};
1591 ::ClientToScreen(MainHWND(), &spt);
1592 pt = PointFromPOINT(spt);
1594 ContextMenu(pt);
1595 return 0;
1598 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1600 case WM_INPUTLANGCHANGE:
1601 //::SetThreadLocale(LOWORD(lParam));
1602 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1604 case WM_INPUTLANGCHANGEREQUEST:
1605 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1607 case WM_ERASEBKGND:
1608 return 1; // Avoid any background erasure as whole window painted.
1610 case WM_CAPTURECHANGED:
1611 capturedMouse = false;
1612 return 0;
1614 case WM_IME_SETCONTEXT:
1615 if (KoreanIME() || imeInteraction == imeInline) {
1616 if (wParam) {
1617 LPARAM NoImeWin = lParam;
1618 NoImeWin = NoImeWin & (~ISC_SHOWUICOMPOSITIONWINDOW);
1619 return ::DefWindowProc(MainHWND(), iMessage, wParam, NoImeWin);
1622 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1624 // These are not handled in Scintilla and its faster to dispatch them here.
1625 // Also moves time out to here so profile doesn't count lots of empty message calls.
1627 case WM_MOVE:
1628 case WM_MOUSEACTIVATE:
1629 case WM_NCHITTEST:
1630 case WM_NCCALCSIZE:
1631 case WM_NCPAINT:
1632 case WM_NCMOUSEMOVE:
1633 case WM_NCLBUTTONDOWN:
1634 case WM_IME_NOTIFY:
1635 case WM_SYSCOMMAND:
1636 case WM_WINDOWPOSCHANGING:
1637 case WM_WINDOWPOSCHANGED:
1638 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1640 case WM_GETTEXTLENGTH:
1641 return GetTextLength();
1643 case WM_GETTEXT:
1644 return GetText(wParam, lParam);
1646 case EM_LINEFROMCHAR:
1647 if (static_cast<int>(wParam) < 0) {
1648 wParam = SelectionStart().Position();
1650 return pdoc->LineFromPosition(static_cast<int>(wParam));
1652 case EM_EXLINEFROMCHAR:
1653 return pdoc->LineFromPosition(static_cast<int>(lParam));
1655 case EM_GETSEL:
1656 if (wParam) {
1657 *reinterpret_cast<int *>(wParam) = static_cast<int>(SelectionStart().Position());
1659 if (lParam) {
1660 *reinterpret_cast<int *>(lParam) = static_cast<int>(SelectionEnd().Position());
1662 return MAKELONG(SelectionStart().Position(), SelectionEnd().Position());
1664 case EM_EXGETSEL: {
1665 if (lParam == 0) {
1666 return 0;
1668 Sci_CharacterRange *pCR = reinterpret_cast<Sci_CharacterRange *>(lParam);
1669 pCR->cpMin = static_cast<Sci_PositionCR>(SelectionStart().Position());
1670 pCR->cpMax = static_cast<Sci_PositionCR>(SelectionEnd().Position());
1672 break;
1674 case EM_SETSEL: {
1675 Sci::Position nStart = static_cast<Sci::Position>(wParam);
1676 Sci::Position nEnd = static_cast<Sci::Position>(lParam);
1677 if (nStart == 0 && nEnd == -1) {
1678 nEnd = static_cast<Sci::Position>(pdoc->Length());
1680 if (nStart == -1) {
1681 nStart = nEnd; // Remove selection
1683 SetSelection(nEnd, nStart);
1684 EnsureCaretVisible();
1686 break;
1688 case EM_EXSETSEL: {
1689 if (lParam == 0) {
1690 return 0;
1692 Sci_CharacterRange *pCR = reinterpret_cast<Sci_CharacterRange *>(lParam);
1693 sel.selType = Selection::selStream;
1694 if (pCR->cpMin == 0 && pCR->cpMax == -1) {
1695 SetSelection(pCR->cpMin, static_cast<Sci::Position>(pdoc->Length()));
1696 } else {
1697 SetSelection(pCR->cpMin, pCR->cpMax);
1699 EnsureCaretVisible();
1700 return pdoc->LineFromPosition(SelectionStart().Position());
1703 case SCI_GETDIRECTFUNCTION:
1704 return reinterpret_cast<sptr_t>(DirectFunction);
1706 case SCI_GETDIRECTPOINTER:
1707 return reinterpret_cast<sptr_t>(this);
1709 case SCI_GRABFOCUS:
1710 ::SetFocus(MainHWND());
1711 break;
1713 #ifdef INCLUDE_DEPRECATED_FEATURES
1714 case SCI_SETKEYSUNICODE:
1715 break;
1717 case SCI_GETKEYSUNICODE:
1718 return true;
1719 #endif
1721 case SCI_SETTECHNOLOGY:
1722 if ((wParam == SC_TECHNOLOGY_DEFAULT) ||
1723 (wParam == SC_TECHNOLOGY_DIRECTWRITERETAIN) ||
1724 (wParam == SC_TECHNOLOGY_DIRECTWRITEDC) ||
1725 (wParam == SC_TECHNOLOGY_DIRECTWRITE)) {
1726 if (technology != static_cast<int>(wParam)) {
1727 if (static_cast<int>(wParam) > SC_TECHNOLOGY_DEFAULT) {
1728 #if defined(USE_D2D)
1729 if (!LoadD2D())
1730 // Failed to load Direct2D or DirectWrite so no effect
1731 return 0;
1732 #else
1733 return 0;
1734 #endif
1736 #if defined(USE_D2D)
1737 DropRenderTarget();
1738 #endif
1739 technology = static_cast<int>(wParam);
1740 // Invalidate all cached information including layout.
1741 DropGraphics(true);
1742 InvalidateStyleRedraw();
1745 break;
1747 case SCI_SETBIDIRECTIONAL:
1748 if (technology == SC_TECHNOLOGY_DEFAULT) {
1749 bidirectional = EditModel::Bidirectional::bidiDisabled;
1750 } else if ((wParam >= SC_BIDIRECTIONAL_DISABLED) && (wParam <= SC_BIDIRECTIONAL_R2L)) {
1751 bidirectional = static_cast<EditModel::Bidirectional>(wParam);
1753 // Invalidate all cached information including layout.
1754 DropGraphics(true);
1755 InvalidateStyleRedraw();
1756 break;
1758 #ifdef SCI_LEXER
1759 case SCI_LOADLEXERLIBRARY:
1760 LexerManager::GetInstance()->Load(reinterpret_cast<const char *>(lParam));
1761 break;
1762 #endif
1764 case SCI_TARGETASUTF8:
1765 return TargetAsUTF8(reinterpret_cast<char*>(lParam));
1767 case SCI_ENCODEDFROMUTF8:
1768 return EncodedFromUTF8(reinterpret_cast<const char*>(wParam),
1769 reinterpret_cast<char*>(lParam));
1771 default:
1772 return ScintillaBase::WndProc(iMessage, wParam, lParam);
1774 } catch (std::bad_alloc &) {
1775 errorStatus = SC_STATUS_BADALLOC;
1776 } catch (...) {
1777 errorStatus = SC_STATUS_FAILURE;
1779 return 0l;
1782 bool ScintillaWin::ValidCodePage(int codePage) const {
1783 return codePage == 0 || codePage == SC_CP_UTF8 ||
1784 codePage == 932 || codePage == 936 || codePage == 949 ||
1785 codePage == 950 || codePage == 1361;
1788 sptr_t ScintillaWin::DefWndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
1789 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1792 bool ScintillaWin::FineTickerRunning(TickReason reason) {
1793 return timers[reason] != 0;
1796 void ScintillaWin::FineTickerStart(TickReason reason, int millis, int tolerance) {
1797 FineTickerCancel(reason);
1798 if (SetCoalescableTimerFn && tolerance) {
1799 timers[reason] = SetCoalescableTimerFn(MainHWND(), fineTimerStart + reason, millis, NULL, tolerance);
1800 } else {
1801 timers[reason] = ::SetTimer(MainHWND(), fineTimerStart + reason, millis, NULL);
1805 void ScintillaWin::FineTickerCancel(TickReason reason) {
1806 if (timers[reason]) {
1807 ::KillTimer(MainHWND(), timers[reason]);
1808 timers[reason] = 0;
1813 bool ScintillaWin::SetIdle(bool on) {
1814 // On Win32 the Idler is implemented as a Timer on the Scintilla window. This
1815 // takes advantage of the fact that WM_TIMER messages are very low priority,
1816 // and are only posted when the message queue is empty, i.e. during idle time.
1817 if (idler.state != on) {
1818 if (on) {
1819 idler.idlerID = ::SetTimer(MainHWND(), idleTimerID, 10, NULL)
1820 ? reinterpret_cast<IdlerID>(idleTimerID) : 0;
1821 } else {
1822 ::KillTimer(MainHWND(), reinterpret_cast<uptr_t>(idler.idlerID));
1823 idler.idlerID = 0;
1825 idler.state = idler.idlerID != 0;
1827 return idler.state;
1830 void ScintillaWin::SetMouseCapture(bool on) {
1831 if (mouseDownCaptures) {
1832 if (on) {
1833 ::SetCapture(MainHWND());
1834 } else {
1835 ::ReleaseCapture();
1838 capturedMouse = on;
1841 bool ScintillaWin::HaveMouseCapture() {
1842 // Cannot just see if GetCapture is this window as the scroll bar also sets capture for the window
1843 return capturedMouse;
1844 //return capturedMouse && (::GetCapture() == MainHWND());
1847 void ScintillaWin::SetTrackMouseLeaveEvent(bool on) {
1848 if (on && !trackedMouseLeave) {
1849 TRACKMOUSEEVENT tme;
1850 tme.cbSize = sizeof(tme);
1851 tme.dwFlags = TME_LEAVE;
1852 tme.hwndTrack = MainHWND();
1853 tme.dwHoverTime = HOVER_DEFAULT; // Unused but triggers Dr. Memory if not initialized
1854 TrackMouseEvent(&tme);
1856 trackedMouseLeave = on;
1859 bool ScintillaWin::PaintContains(PRectangle rc) {
1860 if (paintState == painting) {
1861 return BoundsContains(rcPaint, hRgnUpdate, rc);
1863 return true;
1866 void ScintillaWin::ScrollText(Sci::Line /* linesToMove */) {
1867 //Platform::DebugPrintf("ScintillaWin::ScrollText %d\n", linesToMove);
1868 //::ScrollWindow(MainHWND(), 0,
1869 // vs.lineHeight * linesToMove, 0, 0);
1870 //::UpdateWindow(MainHWND());
1871 Redraw();
1872 UpdateSystemCaret();
1875 void ScintillaWin::NotifyCaretMove() {
1876 NotifyWinEvent(EVENT_OBJECT_LOCATIONCHANGE, MainHWND(), OBJID_CARET, CHILDID_SELF);
1879 void ScintillaWin::UpdateSystemCaret() {
1880 if (hasFocus) {
1881 if (HasCaretSizeChanged()) {
1882 DestroySystemCaret();
1883 CreateSystemCaret();
1885 Point pos = PointMainCaret();
1886 ::SetCaretPos(static_cast<int>(pos.x), static_cast<int>(pos.y));
1890 int ScintillaWin::SetScrollInfo(int nBar, LPCSCROLLINFO lpsi, BOOL bRedraw) {
1891 return ::SetScrollInfo(MainHWND(), nBar, lpsi, bRedraw);
1894 bool ScintillaWin::GetScrollInfo(int nBar, LPSCROLLINFO lpsi) {
1895 return ::GetScrollInfo(MainHWND(), nBar, lpsi) ? true : false;
1898 // Change the scroll position but avoid repaint if changing to same value
1899 void ScintillaWin::ChangeScrollPos(int barType, Sci::Position pos) {
1900 SCROLLINFO sci = {
1901 sizeof(sci), 0, 0, 0, 0, 0, 0
1903 sci.fMask = SIF_POS;
1904 GetScrollInfo(barType, &sci);
1905 if (sci.nPos != pos) {
1906 DwellEnd(true);
1907 sci.nPos = static_cast<int>(pos);
1908 SetScrollInfo(barType, &sci, TRUE);
1912 void ScintillaWin::SetVerticalScrollPos() {
1913 ChangeScrollPos(SB_VERT, topLine);
1916 void ScintillaWin::SetHorizontalScrollPos() {
1917 ChangeScrollPos(SB_HORZ, xOffset);
1920 bool ScintillaWin::ModifyScrollBars(Sci::Line nMax, Sci::Line nPage) {
1921 bool modified = false;
1922 SCROLLINFO sci = {
1923 sizeof(sci), 0, 0, 0, 0, 0, 0
1925 sci.fMask = SIF_PAGE | SIF_RANGE;
1926 GetScrollInfo(SB_VERT, &sci);
1927 Sci::Line vertEndPreferred = nMax;
1928 if (!verticalScrollBarVisible)
1929 nPage = vertEndPreferred + 1;
1930 if ((sci.nMin != 0) ||
1931 (sci.nMax != vertEndPreferred) ||
1932 (sci.nPage != static_cast<unsigned int>(nPage)) ||
1933 (sci.nPos != 0)) {
1934 sci.fMask = SIF_PAGE | SIF_RANGE;
1935 sci.nMin = 0;
1936 sci.nMax = static_cast<int>(vertEndPreferred);
1937 sci.nPage = static_cast<UINT>(nPage);
1938 sci.nPos = 0;
1939 sci.nTrackPos = 1;
1940 SetScrollInfo(SB_VERT, &sci, TRUE);
1941 modified = true;
1944 const PRectangle rcText = GetTextRectangle();
1945 int horizEndPreferred = scrollWidth;
1946 if (horizEndPreferred < 0)
1947 horizEndPreferred = 0;
1948 int pageWidth = static_cast<int>(rcText.Width());
1949 if (!horizontalScrollBarVisible || Wrapping())
1950 pageWidth = horizEndPreferred + 1;
1951 sci.fMask = SIF_PAGE | SIF_RANGE;
1952 GetScrollInfo(SB_HORZ, &sci);
1953 if ((sci.nMin != 0) ||
1954 (sci.nMax != horizEndPreferred) ||
1955 (sci.nPage != static_cast<unsigned int>(pageWidth)) ||
1956 (sci.nPos != 0)) {
1957 sci.fMask = SIF_PAGE | SIF_RANGE;
1958 sci.nMin = 0;
1959 sci.nMax = horizEndPreferred;
1960 sci.nPage = pageWidth;
1961 sci.nPos = 0;
1962 sci.nTrackPos = 1;
1963 SetScrollInfo(SB_HORZ, &sci, TRUE);
1964 modified = true;
1965 if (scrollWidth < pageWidth) {
1966 HorizontalScrollTo(0);
1969 return modified;
1972 void ScintillaWin::NotifyChange() {
1973 ::SendMessage(::GetParent(MainHWND()), WM_COMMAND,
1974 MAKELONG(GetCtrlID(), SCEN_CHANGE),
1975 reinterpret_cast<LPARAM>(MainHWND()));
1978 void ScintillaWin::NotifyFocus(bool focus) {
1979 ::SendMessage(::GetParent(MainHWND()), WM_COMMAND,
1980 MAKELONG(GetCtrlID(), focus ? SCEN_SETFOCUS : SCEN_KILLFOCUS),
1981 reinterpret_cast<LPARAM>(MainHWND()));
1982 Editor::NotifyFocus(focus);
1985 void ScintillaWin::SetCtrlID(int identifier) {
1986 ::SetWindowID(static_cast<HWND>(wMain.GetID()), identifier);
1989 int ScintillaWin::GetCtrlID() {
1990 return ::GetDlgCtrlID(static_cast<HWND>(wMain.GetID()));
1993 void ScintillaWin::NotifyParent(SCNotification scn) {
1994 scn.nmhdr.hwndFrom = MainHWND();
1995 scn.nmhdr.idFrom = GetCtrlID();
1996 ::SendMessage(::GetParent(MainHWND()), WM_NOTIFY,
1997 GetCtrlID(), reinterpret_cast<LPARAM>(&scn));
2000 void ScintillaWin::NotifyDoubleClick(Point pt, int modifiers) {
2001 //Platform::DebugPrintf("ScintillaWin Double click 0\n");
2002 ScintillaBase::NotifyDoubleClick(pt, modifiers);
2003 // Send myself a WM_LBUTTONDBLCLK, so the container can handle it too.
2004 ::SendMessage(MainHWND(),
2005 WM_LBUTTONDBLCLK,
2006 (modifiers & SCI_SHIFT) ? MK_SHIFT : 0,
2007 MAKELPARAM(pt.x, pt.y));
2010 class CaseFolderDBCS : public CaseFolderTable {
2011 // Allocate the expandable storage here so that it does not need to be reallocated
2012 // for each call to Fold.
2013 std::vector<wchar_t> utf16Mixed;
2014 std::vector<wchar_t> utf16Folded;
2015 UINT cp;
2016 public:
2017 explicit CaseFolderDBCS(UINT cp_) : cp(cp_) {
2018 StandardASCII();
2020 size_t Fold(char *folded, size_t sizeFolded, const char *mixed, size_t lenMixed) override {
2021 if ((lenMixed == 1) && (sizeFolded > 0)) {
2022 folded[0] = mapping[static_cast<unsigned char>(mixed[0])];
2023 return 1;
2024 } else {
2025 if (lenMixed > utf16Mixed.size()) {
2026 utf16Mixed.resize(lenMixed + 8);
2028 const size_t nUtf16Mixed = ::MultiByteToWideChar(cp, 0, mixed,
2029 static_cast<int>(lenMixed),
2030 &utf16Mixed[0],
2031 static_cast<int>(utf16Mixed.size()));
2033 if (nUtf16Mixed == 0) {
2034 // Failed to convert -> bad input
2035 folded[0] = '\0';
2036 return 1;
2039 unsigned int lenFlat = 0;
2040 for (size_t mixIndex=0; mixIndex < nUtf16Mixed; mixIndex++) {
2041 if ((lenFlat + 20) > utf16Folded.size())
2042 utf16Folded.resize(lenFlat + 60);
2043 const char *foldedUTF8 = CaseConvert(utf16Mixed[mixIndex], CaseConversionFold);
2044 if (foldedUTF8) {
2045 // Maximum length of a case conversion is 6 bytes, 3 characters
2046 wchar_t wFolded[20];
2047 const size_t charsConverted = UTF16FromUTF8(foldedUTF8,
2048 strlen(foldedUTF8),
2049 wFolded, ELEMENTS(wFolded));
2050 for (size_t j=0; j<charsConverted; j++)
2051 utf16Folded[lenFlat++] = wFolded[j];
2052 } else {
2053 utf16Folded[lenFlat++] = utf16Mixed[mixIndex];
2057 size_t lenOut = ::WideCharToMultiByte(cp, 0,
2058 &utf16Folded[0], lenFlat,
2059 NULL, 0, NULL, 0);
2061 if (lenOut < sizeFolded) {
2062 ::WideCharToMultiByte(cp, 0,
2063 &utf16Folded[0], lenFlat,
2064 folded, static_cast<int>(lenOut), NULL, 0);
2065 return lenOut;
2066 } else {
2067 return 0;
2073 CaseFolder *ScintillaWin::CaseFolderForEncoding() {
2074 UINT cpDest = CodePageOfDocument();
2075 if (cpDest == SC_CP_UTF8) {
2076 return new CaseFolderUnicode();
2077 } else {
2078 if (pdoc->dbcsCodePage == 0) {
2079 CaseFolderTable *pcf = new CaseFolderTable();
2080 pcf->StandardASCII();
2081 // Only for single byte encodings
2082 UINT cpDoc = CodePageOfDocument();
2083 for (int i=0x80; i<0x100; i++) {
2084 char sCharacter[2] = "A";
2085 sCharacter[0] = static_cast<char>(i);
2086 wchar_t wCharacter[20];
2087 const unsigned int lengthUTF16 = ::MultiByteToWideChar(cpDoc, 0, sCharacter, 1,
2088 wCharacter, ELEMENTS(wCharacter));
2089 if (lengthUTF16 == 1) {
2090 const char *caseFolded = CaseConvert(wCharacter[0], CaseConversionFold);
2091 if (caseFolded) {
2092 wchar_t wLower[20];
2093 const size_t charsConverted = UTF16FromUTF8(caseFolded,
2094 strlen(caseFolded),
2095 wLower, ELEMENTS(wLower));
2096 if (charsConverted == 1) {
2097 char sCharacterLowered[20];
2098 const unsigned int lengthConverted = ::WideCharToMultiByte(cpDoc, 0,
2099 wLower, static_cast<int>(charsConverted),
2100 sCharacterLowered, ELEMENTS(sCharacterLowered), NULL, 0);
2101 if ((lengthConverted == 1) && (sCharacter[0] != sCharacterLowered[0])) {
2102 pcf->SetTranslation(sCharacter[0], sCharacterLowered[0]);
2108 return pcf;
2109 } else {
2110 return new CaseFolderDBCS(cpDest);
2115 std::string ScintillaWin::CaseMapString(const std::string &s, int caseMapping) {
2116 if ((s.size() == 0) || (caseMapping == cmSame))
2117 return s;
2119 const UINT cpDoc = CodePageOfDocument();
2120 if (cpDoc == SC_CP_UTF8) {
2121 return CaseConvertString(s, (caseMapping == cmUpper) ? CaseConversionUpper : CaseConversionLower);
2124 // Change text to UTF-16
2125 const std::wstring wsText = StringDecode(s, cpDoc);
2127 const DWORD mapFlags = LCMAP_LINGUISTIC_CASING |
2128 ((caseMapping == cmUpper) ? LCMAP_UPPERCASE : LCMAP_LOWERCASE);
2130 // Change case
2131 const std::wstring wsConverted = StringMapCase(wsText, mapFlags);
2133 // Change back to document encoding
2134 std::string sConverted = StringEncode(wsConverted, cpDoc);
2136 return sConverted;
2139 void ScintillaWin::Copy() {
2140 //Platform::DebugPrintf("Copy\n");
2141 if (!sel.Empty()) {
2142 SelectionText selectedText;
2143 CopySelectionRange(&selectedText);
2144 CopyToClipboard(selectedText);
2148 void ScintillaWin::CopyAllowLine() {
2149 SelectionText selectedText;
2150 CopySelectionRange(&selectedText, true);
2151 CopyToClipboard(selectedText);
2154 bool ScintillaWin::CanPaste() {
2155 if (!Editor::CanPaste())
2156 return false;
2157 if (::IsClipboardFormatAvailable(CF_TEXT))
2158 return true;
2159 if (IsUnicodeMode())
2160 return ::IsClipboardFormatAvailable(CF_UNICODETEXT) != 0;
2161 return false;
2164 class GlobalMemory {
2165 HGLOBAL hand;
2166 public:
2167 void *ptr;
2168 GlobalMemory() : hand(0), ptr(0) {
2170 explicit GlobalMemory(HGLOBAL hand_) : hand(hand_), ptr(0) {
2171 if (hand) {
2172 ptr = ::GlobalLock(hand);
2175 ~GlobalMemory() {
2176 PLATFORM_ASSERT(!ptr);
2177 assert(!hand);
2179 void Allocate(size_t bytes) {
2180 assert(!hand);
2181 hand = ::GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT, bytes);
2182 if (hand) {
2183 ptr = ::GlobalLock(hand);
2186 HGLOBAL Unlock() {
2187 PLATFORM_ASSERT(ptr);
2188 HGLOBAL handCopy = hand;
2189 ::GlobalUnlock(hand);
2190 ptr = 0;
2191 hand = 0;
2192 return handCopy;
2194 void SetClip(UINT uFormat) {
2195 ::SetClipboardData(uFormat, Unlock());
2197 operator bool() const {
2198 return ptr != 0;
2200 SIZE_T Size() {
2201 return ::GlobalSize(hand);
2205 // OpenClipboard may fail if another application has opened the clipboard.
2206 // Try up to 8 times, with an initial delay of 1 ms and an exponential back off
2207 // for a maximum total delay of 127 ms (1+2+4+8+16+32+64).
2208 static bool OpenClipboardRetry(HWND hwnd) {
2209 for (int attempt=0; attempt<8; attempt++) {
2210 if (attempt > 0) {
2211 ::Sleep(1 << (attempt-1));
2213 if (::OpenClipboard(hwnd)) {
2214 return true;
2217 return false;
2220 void ScintillaWin::Paste() {
2221 if (!::OpenClipboardRetry(MainHWND())) {
2222 return;
2224 UndoGroup ug(pdoc);
2225 const bool isLine = SelectionEmpty() &&
2226 (::IsClipboardFormatAvailable(cfLineSelect) || ::IsClipboardFormatAvailable(cfVSLineTag));
2227 ClearSelection(multiPasteMode == SC_MULTIPASTE_EACH);
2228 bool isRectangular = (::IsClipboardFormatAvailable(cfColumnSelect) != 0);
2230 if (!isRectangular) {
2231 // Evaluate "Borland IDE Block Type" explicitly
2232 GlobalMemory memBorlandSelection(::GetClipboardData(cfBorlandIDEBlockType));
2233 if (memBorlandSelection) {
2234 isRectangular = (memBorlandSelection.Size() == 1) && (static_cast<BYTE *>(memBorlandSelection.ptr)[0] == 0x02);
2235 memBorlandSelection.Unlock();
2238 const PasteShape pasteShape = isRectangular ? pasteRectangular : (isLine ? pasteLine : pasteStream);
2240 // Always use CF_UNICODETEXT if available
2241 GlobalMemory memUSelection(::GetClipboardData(CF_UNICODETEXT));
2242 if (memUSelection) {
2243 wchar_t *uptr = static_cast<wchar_t *>(memUSelection.ptr);
2244 if (uptr) {
2245 size_t len;
2246 std::vector<char> putf;
2247 // Default Scintilla behaviour in Unicode mode
2248 if (IsUnicodeMode()) {
2249 const size_t bytes = memUSelection.Size();
2250 len = UTF8Length(uptr, bytes / 2);
2251 putf.resize(len + 1);
2252 UTF8FromUTF16(uptr, bytes / 2, &putf[0], len);
2253 } else {
2254 // CF_UNICODETEXT available, but not in Unicode mode
2255 // Convert from Unicode to current Scintilla code page
2256 UINT cpDest = CodePageOfDocument();
2257 len = ::WideCharToMultiByte(cpDest, 0, uptr, -1,
2258 NULL, 0, NULL, NULL) - 1; // subtract 0 terminator
2259 putf.resize(len + 1);
2260 ::WideCharToMultiByte(cpDest, 0, uptr, -1,
2261 &putf[0], static_cast<int>(len) + 1, NULL, NULL);
2264 InsertPasteShape(&putf[0], static_cast<int>(len), pasteShape);
2266 memUSelection.Unlock();
2267 } else {
2268 // CF_UNICODETEXT not available, paste ANSI text
2269 GlobalMemory memSelection(::GetClipboardData(CF_TEXT));
2270 if (memSelection) {
2271 char *ptr = static_cast<char *>(memSelection.ptr);
2272 if (ptr) {
2273 const size_t bytes = memSelection.Size();
2274 size_t len = bytes;
2275 for (size_t i = 0; i < bytes; i++) {
2276 if ((len == bytes) && (0 == ptr[i]))
2277 len = i;
2279 const int ilen = static_cast<int>(len);
2281 // In Unicode mode, convert clipboard text to UTF-8
2282 if (IsUnicodeMode()) {
2283 std::vector<wchar_t> uptr(len+1);
2285 const size_t ulen = ::MultiByteToWideChar(CP_ACP, 0,
2286 ptr, ilen, &uptr[0], ilen +1);
2288 const size_t mlen = UTF8Length(&uptr[0], ulen);
2289 std::vector<char> putf(mlen+1);
2290 UTF8FromUTF16(&uptr[0], ulen, &putf[0], mlen);
2292 InsertPasteShape(&putf[0], static_cast<int>(mlen), pasteShape);
2293 } else {
2294 InsertPasteShape(ptr, ilen, pasteShape);
2297 memSelection.Unlock();
2300 ::CloseClipboard();
2301 Redraw();
2304 void ScintillaWin::CreateCallTipWindow(PRectangle) {
2305 if (!ct.wCallTip.Created()) {
2306 ct.wCallTip = ::CreateWindow(callClassName, TEXT("ACallTip"),
2307 WS_POPUP, 100, 100, 150, 20,
2308 MainHWND(), 0,
2309 GetWindowInstance(MainHWND()),
2310 this);
2311 ct.wDraw = ct.wCallTip;
2315 void ScintillaWin::AddToPopUp(const char *label, int cmd, bool enabled) {
2316 HMENU hmenuPopup = static_cast<HMENU>(popup.GetID());
2317 if (!label[0])
2318 ::AppendMenuA(hmenuPopup, MF_SEPARATOR, 0, "");
2319 else if (enabled)
2320 ::AppendMenuA(hmenuPopup, MF_STRING, cmd, label);
2321 else
2322 ::AppendMenuA(hmenuPopup, MF_STRING | MF_DISABLED | MF_GRAYED, cmd, label);
2325 void ScintillaWin::ClaimSelection() {
2326 // Windows does not have a primary selection
2329 /// Implement IUnknown
2331 STDMETHODIMP_(ULONG)FormatEnumerator_AddRef(FormatEnumerator *fe);
2332 STDMETHODIMP FormatEnumerator_QueryInterface(FormatEnumerator *fe, REFIID riid, PVOID *ppv) {
2333 //Platform::DebugPrintf("EFE QI");
2334 *ppv = NULL;
2335 if (riid == IID_IUnknown)
2336 *ppv = reinterpret_cast<IEnumFORMATETC *>(fe);
2337 if (riid == IID_IEnumFORMATETC)
2338 *ppv = reinterpret_cast<IEnumFORMATETC *>(fe);
2339 if (!*ppv)
2340 return E_NOINTERFACE;
2341 FormatEnumerator_AddRef(fe);
2342 return S_OK;
2344 STDMETHODIMP_(ULONG)FormatEnumerator_AddRef(FormatEnumerator *fe) {
2345 return ++fe->ref;
2347 STDMETHODIMP_(ULONG)FormatEnumerator_Release(FormatEnumerator *fe) {
2348 fe->ref--;
2349 if (fe->ref > 0)
2350 return fe->ref;
2351 delete fe;
2352 return 0;
2354 /// Implement IEnumFORMATETC
2355 STDMETHODIMP FormatEnumerator_Next(FormatEnumerator *fe, ULONG celt, FORMATETC *rgelt, ULONG *pceltFetched) {
2356 if (rgelt == NULL) return E_POINTER;
2357 unsigned int putPos = 0;
2358 while ((fe->pos < fe->formats.size()) && (putPos < celt)) {
2359 rgelt->cfFormat = fe->formats[fe->pos];
2360 rgelt->ptd = 0;
2361 rgelt->dwAspect = DVASPECT_CONTENT;
2362 rgelt->lindex = -1;
2363 rgelt->tymed = TYMED_HGLOBAL;
2364 rgelt++;
2365 fe->pos++;
2366 putPos++;
2368 if (pceltFetched)
2369 *pceltFetched = putPos;
2370 return putPos ? S_OK : S_FALSE;
2372 STDMETHODIMP FormatEnumerator_Skip(FormatEnumerator *fe, ULONG celt) {
2373 fe->pos += celt;
2374 return S_OK;
2376 STDMETHODIMP FormatEnumerator_Reset(FormatEnumerator *fe) {
2377 fe->pos = 0;
2378 return S_OK;
2380 STDMETHODIMP FormatEnumerator_Clone(FormatEnumerator *fe, IEnumFORMATETC **ppenum) {
2381 FormatEnumerator *pfe;
2382 try {
2383 pfe = new FormatEnumerator(fe->pos, &fe->formats[0], fe->formats.size());
2384 } catch (...) {
2385 return E_OUTOFMEMORY;
2387 return FormatEnumerator_QueryInterface(pfe, IID_IEnumFORMATETC,
2388 reinterpret_cast<void **>(ppenum));
2391 static VFunction *vtFormatEnumerator[] = {
2392 (VFunction *)(FormatEnumerator_QueryInterface),
2393 (VFunction *)(FormatEnumerator_AddRef),
2394 (VFunction *)(FormatEnumerator_Release),
2395 (VFunction *)(FormatEnumerator_Next),
2396 (VFunction *)(FormatEnumerator_Skip),
2397 (VFunction *)(FormatEnumerator_Reset),
2398 (VFunction *)(FormatEnumerator_Clone)
2401 FormatEnumerator::FormatEnumerator(int pos_, CLIPFORMAT formats_[], size_t formatsLen_) {
2402 vtbl = vtFormatEnumerator;
2403 ref = 0; // First QI adds first reference...
2404 pos = pos_;
2405 formats.insert(formats.begin(), formats_, formats_+formatsLen_);
2408 /// Implement IUnknown
2409 STDMETHODIMP DropSource_QueryInterface(DropSource *ds, REFIID riid, PVOID *ppv) {
2410 return ds->sci->QueryInterface(riid, ppv);
2412 STDMETHODIMP_(ULONG)DropSource_AddRef(DropSource *ds) {
2413 return ds->sci->AddRef();
2415 STDMETHODIMP_(ULONG)DropSource_Release(DropSource *ds) {
2416 return ds->sci->Release();
2419 /// Implement IDropSource
2420 STDMETHODIMP DropSource_QueryContinueDrag(DropSource *, BOOL fEsc, DWORD grfKeyState) {
2421 if (fEsc)
2422 return DRAGDROP_S_CANCEL;
2423 if (!(grfKeyState & MK_LBUTTON))
2424 return DRAGDROP_S_DROP;
2425 return S_OK;
2428 STDMETHODIMP DropSource_GiveFeedback(DropSource *, DWORD) {
2429 return DRAGDROP_S_USEDEFAULTCURSORS;
2432 static VFunction *vtDropSource[] = {
2433 (VFunction *)(DropSource_QueryInterface),
2434 (VFunction *)(DropSource_AddRef),
2435 (VFunction *)(DropSource_Release),
2436 (VFunction *)(DropSource_QueryContinueDrag),
2437 (VFunction *)(DropSource_GiveFeedback)
2440 DropSource::DropSource() {
2441 vtbl = vtDropSource;
2442 sci = 0;
2445 /// Implement IUnkown
2446 STDMETHODIMP DataObject_QueryInterface(DataObject *pd, REFIID riid, PVOID *ppv) {
2447 //Platform::DebugPrintf("DO QI %x\n", pd);
2448 return pd->sci->QueryInterface(riid, ppv);
2450 STDMETHODIMP_(ULONG)DataObject_AddRef(DataObject *pd) {
2451 return pd->sci->AddRef();
2453 STDMETHODIMP_(ULONG)DataObject_Release(DataObject *pd) {
2454 return pd->sci->Release();
2456 /// Implement IDataObject
2457 STDMETHODIMP DataObject_GetData(DataObject *pd, FORMATETC *pFEIn, STGMEDIUM *pSTM) {
2458 return pd->sci->GetData(pFEIn, pSTM);
2461 STDMETHODIMP DataObject_GetDataHere(DataObject *, FORMATETC *, STGMEDIUM *) {
2462 //Platform::DebugPrintf("DOB GetDataHere\n");
2463 return E_NOTIMPL;
2466 STDMETHODIMP DataObject_QueryGetData(DataObject *pd, FORMATETC *pFE) {
2467 if (pd->sci->DragIsRectangularOK(pFE->cfFormat) &&
2468 pFE->ptd == 0 &&
2469 (pFE->dwAspect & DVASPECT_CONTENT) != 0 &&
2470 pFE->lindex == -1 &&
2471 (pFE->tymed & TYMED_HGLOBAL) != 0
2473 return S_OK;
2476 const bool formatOK = (pFE->cfFormat == CF_TEXT) ||
2477 ((pFE->cfFormat == CF_UNICODETEXT) && pd->sci->IsUnicodeMode());
2478 if (!formatOK ||
2479 pFE->ptd != 0 ||
2480 (pFE->dwAspect & DVASPECT_CONTENT) == 0 ||
2481 pFE->lindex != -1 ||
2482 (pFE->tymed & TYMED_HGLOBAL) == 0
2484 //Platform::DebugPrintf("DOB QueryGetData No %x\n",pFE->cfFormat);
2485 //return DATA_E_FORMATETC;
2486 return S_FALSE;
2488 //Platform::DebugPrintf("DOB QueryGetData OK %x\n",pFE->cfFormat);
2489 return S_OK;
2492 STDMETHODIMP DataObject_GetCanonicalFormatEtc(DataObject *pd, FORMATETC *, FORMATETC *pFEOut) {
2493 //Platform::DebugPrintf("DOB GetCanon\n");
2494 if (pd->sci->IsUnicodeMode())
2495 pFEOut->cfFormat = CF_UNICODETEXT;
2496 else
2497 pFEOut->cfFormat = CF_TEXT;
2498 pFEOut->ptd = 0;
2499 pFEOut->dwAspect = DVASPECT_CONTENT;
2500 pFEOut->lindex = -1;
2501 pFEOut->tymed = TYMED_HGLOBAL;
2502 return S_OK;
2505 STDMETHODIMP DataObject_SetData(DataObject *, FORMATETC *, STGMEDIUM *, BOOL) {
2506 //Platform::DebugPrintf("DOB SetData\n");
2507 return E_FAIL;
2510 STDMETHODIMP DataObject_EnumFormatEtc(DataObject *pd, DWORD dwDirection, IEnumFORMATETC **ppEnum) {
2511 try {
2512 //Platform::DebugPrintf("DOB EnumFormatEtc %d\n", dwDirection);
2513 if (dwDirection != DATADIR_GET) {
2514 *ppEnum = 0;
2515 return E_FAIL;
2517 FormatEnumerator *pfe;
2518 if (pd->sci->IsUnicodeMode()) {
2519 CLIPFORMAT formats[] = {CF_UNICODETEXT, CF_TEXT};
2520 pfe = new FormatEnumerator(0, formats, ELEMENTS(formats));
2521 } else {
2522 CLIPFORMAT formats[] = {CF_TEXT};
2523 pfe = new FormatEnumerator(0, formats, ELEMENTS(formats));
2525 return FormatEnumerator_QueryInterface(pfe, IID_IEnumFORMATETC,
2526 reinterpret_cast<void **>(ppEnum));
2527 } catch (std::bad_alloc &) {
2528 pd->sci->errorStatus = SC_STATUS_BADALLOC;
2529 return E_OUTOFMEMORY;
2530 } catch (...) {
2531 pd->sci->errorStatus = SC_STATUS_FAILURE;
2532 return E_FAIL;
2536 STDMETHODIMP DataObject_DAdvise(DataObject *, FORMATETC *, DWORD, IAdviseSink *, PDWORD) {
2537 //Platform::DebugPrintf("DOB DAdvise\n");
2538 return E_FAIL;
2541 STDMETHODIMP DataObject_DUnadvise(DataObject *, DWORD) {
2542 //Platform::DebugPrintf("DOB DUnadvise\n");
2543 return E_FAIL;
2546 STDMETHODIMP DataObject_EnumDAdvise(DataObject *, IEnumSTATDATA **) {
2547 //Platform::DebugPrintf("DOB EnumDAdvise\n");
2548 return E_FAIL;
2551 static VFunction *vtDataObject[] = {
2552 (VFunction *)(DataObject_QueryInterface),
2553 (VFunction *)(DataObject_AddRef),
2554 (VFunction *)(DataObject_Release),
2555 (VFunction *)(DataObject_GetData),
2556 (VFunction *)(DataObject_GetDataHere),
2557 (VFunction *)(DataObject_QueryGetData),
2558 (VFunction *)(DataObject_GetCanonicalFormatEtc),
2559 (VFunction *)(DataObject_SetData),
2560 (VFunction *)(DataObject_EnumFormatEtc),
2561 (VFunction *)(DataObject_DAdvise),
2562 (VFunction *)(DataObject_DUnadvise),
2563 (VFunction *)(DataObject_EnumDAdvise)
2566 DataObject::DataObject() {
2567 vtbl = vtDataObject;
2568 sci = 0;
2571 /// Implement IUnknown
2572 STDMETHODIMP DropTarget_QueryInterface(DropTarget *dt, REFIID riid, PVOID *ppv) {
2573 //Platform::DebugPrintf("DT QI %x\n", dt);
2574 return dt->sci->QueryInterface(riid, ppv);
2576 STDMETHODIMP_(ULONG)DropTarget_AddRef(DropTarget *dt) {
2577 return dt->sci->AddRef();
2579 STDMETHODIMP_(ULONG)DropTarget_Release(DropTarget *dt) {
2580 return dt->sci->Release();
2583 /// Implement IDropTarget by forwarding to Scintilla
2584 STDMETHODIMP DropTarget_DragEnter(DropTarget *dt, LPDATAOBJECT pIDataSource, DWORD grfKeyState,
2585 POINTL pt, PDWORD pdwEffect) {
2586 try {
2587 return dt->sci->DragEnter(pIDataSource, grfKeyState, pt, pdwEffect);
2588 } catch (...) {
2589 dt->sci->errorStatus = SC_STATUS_FAILURE;
2591 return E_FAIL;
2593 STDMETHODIMP DropTarget_DragOver(DropTarget *dt, DWORD grfKeyState, POINTL pt, PDWORD pdwEffect) {
2594 try {
2595 return dt->sci->DragOver(grfKeyState, pt, pdwEffect);
2596 } catch (...) {
2597 dt->sci->errorStatus = SC_STATUS_FAILURE;
2599 return E_FAIL;
2601 STDMETHODIMP DropTarget_DragLeave(DropTarget *dt) {
2602 try {
2603 return dt->sci->DragLeave();
2604 } catch (...) {
2605 dt->sci->errorStatus = SC_STATUS_FAILURE;
2607 return E_FAIL;
2609 STDMETHODIMP DropTarget_Drop(DropTarget *dt, LPDATAOBJECT pIDataSource, DWORD grfKeyState,
2610 POINTL pt, PDWORD pdwEffect) {
2611 try {
2612 return dt->sci->Drop(pIDataSource, grfKeyState, pt, pdwEffect);
2613 } catch (...) {
2614 dt->sci->errorStatus = SC_STATUS_FAILURE;
2616 return E_FAIL;
2619 static VFunction *vtDropTarget[] = {
2620 (VFunction *)(DropTarget_QueryInterface),
2621 (VFunction *)(DropTarget_AddRef),
2622 (VFunction *)(DropTarget_Release),
2623 (VFunction *)(DropTarget_DragEnter),
2624 (VFunction *)(DropTarget_DragOver),
2625 (VFunction *)(DropTarget_DragLeave),
2626 (VFunction *)(DropTarget_Drop)
2629 DropTarget::DropTarget() {
2630 vtbl = vtDropTarget;
2631 sci = 0;
2635 * DBCS: support Input Method Editor (IME).
2636 * Called when IME Window opened.
2638 void ScintillaWin::ImeStartComposition() {
2639 if (caret.active) {
2640 // Move IME Window to current caret position
2641 IMContext imc(MainHWND());
2642 Point pos = PointMainCaret();
2643 COMPOSITIONFORM CompForm;
2644 CompForm.dwStyle = CFS_POINT;
2645 CompForm.ptCurrentPos.x = static_cast<int>(pos.x);
2646 CompForm.ptCurrentPos.y = static_cast<int>(pos.y);
2648 ::ImmSetCompositionWindow(imc.hIMC, &CompForm);
2650 // Set font of IME window to same as surrounded text.
2651 if (stylesValid) {
2652 // Since the style creation code has been made platform independent,
2653 // The logfont for the IME is recreated here.
2654 const int styleHere = pdoc->StyleIndexAt(sel.MainCaret());
2655 LOGFONTW lf = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, L""};
2656 int sizeZoomed = vs.styles[styleHere].size + vs.zoomLevel * SC_FONT_SIZE_MULTIPLIER;
2657 if (sizeZoomed <= 2 * SC_FONT_SIZE_MULTIPLIER) // Hangs if sizeZoomed <= 1
2658 sizeZoomed = 2 * SC_FONT_SIZE_MULTIPLIER;
2659 AutoSurface surface(this);
2660 int deviceHeight = sizeZoomed;
2661 if (surface) {
2662 deviceHeight = (sizeZoomed * surface->LogPixelsY()) / 72;
2664 // The negative is to allow for leading
2665 lf.lfHeight = -(abs(deviceHeight / SC_FONT_SIZE_MULTIPLIER));
2666 lf.lfWeight = vs.styles[styleHere].weight;
2667 lf.lfItalic = static_cast<BYTE>(vs.styles[styleHere].italic ? 1 : 0);
2668 lf.lfCharSet = DEFAULT_CHARSET;
2669 lf.lfFaceName[0] = L'\0';
2670 if (vs.styles[styleHere].fontName) {
2671 const char* fontName = vs.styles[styleHere].fontName;
2672 UTF16FromUTF8(fontName, strlen(fontName)+1, lf.lfFaceName, LF_FACESIZE);
2675 ::ImmSetCompositionFontW(imc.hIMC, &lf);
2677 // Caret is displayed in IME window. So, caret in Scintilla is useless.
2678 DropCaret();
2682 /** Called when IME Window closed. */
2683 void ScintillaWin::ImeEndComposition() {
2684 ShowCaretAtCurrentPosition();
2687 LRESULT ScintillaWin::ImeOnReconvert(LPARAM lParam) {
2688 // Reconversion on windows limits within one line without eol.
2689 // Look around: baseStart <-- (|mainStart| -- mainEnd) --> baseEnd.
2690 const Sci::Position mainStart = sel.RangeMain().Start().Position();
2691 const Sci::Position mainEnd = sel.RangeMain().End().Position();
2692 const Sci::Line curLine = static_cast<Sci::Line>(pdoc->LineFromPosition(mainStart));
2693 if (curLine != pdoc->LineFromPosition(mainEnd))
2694 return 0;
2695 const Sci::Position baseStart = static_cast<Sci::Position>(pdoc->LineStart(curLine));
2696 const Sci::Position baseEnd = static_cast<Sci::Position>(pdoc->LineEnd(curLine));
2697 if ((baseStart == baseEnd) || (mainEnd > baseEnd))
2698 return 0;
2700 const int codePage = CodePageOfDocument();
2701 const std::wstring rcFeed = StringDecode(RangeText(baseStart, baseEnd), codePage);
2702 const int rcFeedLen = static_cast<int>(rcFeed.length()) * sizeof(wchar_t);
2703 const int rcSize = sizeof(RECONVERTSTRING) + rcFeedLen + sizeof(wchar_t);
2705 RECONVERTSTRING *rc = reinterpret_cast<RECONVERTSTRING *>(lParam);
2706 if (!rc)
2707 return rcSize; // Immediately be back with rcSize of memory block.
2709 wchar_t *rcFeedStart = reinterpret_cast<wchar_t*>(rc + 1);
2710 memcpy(rcFeedStart, &rcFeed[0], rcFeedLen);
2712 std::string rcCompString = RangeText(mainStart, mainEnd);
2713 std::wstring rcCompWstring = StringDecode(rcCompString, codePage);
2714 std::string rcCompStart = RangeText(baseStart, mainStart);
2715 std::wstring rcCompWstart = StringDecode(rcCompStart, codePage);
2717 // Map selection to dwCompStr.
2718 // No selection assumes current caret as rcCompString without length.
2719 rc->dwVersion = 0; // It should be absolutely 0.
2720 rc->dwStrLen = static_cast<DWORD>(rcFeed.length());
2721 rc->dwStrOffset = sizeof(RECONVERTSTRING);
2722 rc->dwCompStrLen = static_cast<DWORD>(rcCompWstring.length());
2723 rc->dwCompStrOffset = static_cast<DWORD>(rcCompWstart.length()) * sizeof(wchar_t);
2724 rc->dwTargetStrLen = rc->dwCompStrLen;
2725 rc->dwTargetStrOffset =rc->dwCompStrOffset;
2727 IMContext imc(MainHWND());
2728 if (!imc.hIMC)
2729 return 0;
2731 if (!::ImmSetCompositionStringW(imc.hIMC, SCS_QUERYRECONVERTSTRING, rc, rcSize, NULL, 0))
2732 return 0;
2734 // No selection asks IME to fill target fields with its own value.
2735 int tgWlen = rc->dwTargetStrLen;
2736 int tgWstart = rc->dwTargetStrOffset / sizeof(wchar_t);
2738 std::string tgCompStart = StringEncode(rcFeed.substr(0, tgWstart), codePage);
2739 std::string tgComp = StringEncode(rcFeed.substr(tgWstart, tgWlen), codePage);
2741 // No selection needs to adjust reconvert start position for IME set.
2742 int adjust = static_cast<int>(tgCompStart.length() - rcCompStart.length());
2743 int docCompLen = static_cast<int>(tgComp.length());
2745 // Make place for next composition string to sit in.
2746 for (size_t r=0; r<sel.Count(); r++) {
2747 Sci::Position rBase = sel.Range(r).Start().Position();
2748 Sci::Position docCompStart = rBase + adjust;
2750 if (inOverstrike) { // the docCompLen of bytes will be overstriked.
2751 sel.Range(r).caret.SetPosition(docCompStart);
2752 sel.Range(r).anchor.SetPosition(docCompStart);
2753 } else {
2754 // Ensure docCompStart+docCompLen be not beyond lineEnd.
2755 // since docCompLen by byte might break eol.
2756 Sci::Position lineEnd = static_cast<Sci::Position>(pdoc->LineEnd(pdoc->LineFromPosition(rBase)));
2757 Sci::Position overflow = (docCompStart + docCompLen) - lineEnd;
2758 if (overflow > 0) {
2759 pdoc->DeleteChars(docCompStart, docCompLen - overflow);
2760 } else {
2761 pdoc->DeleteChars(docCompStart, docCompLen);
2765 // Immediately Target Input or candidate box choice with GCS_COMPSTR.
2766 return rcSize;
2769 void ScintillaWin::GetIntelliMouseParameters() {
2770 // This retrieves the number of lines per scroll as configured inthe Mouse Properties sheet in Control Panel
2771 ::SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, &linesPerScroll, 0);
2774 void ScintillaWin::CopyToClipboard(const SelectionText &selectedText) {
2775 if (!::OpenClipboardRetry(MainHWND())) {
2776 return;
2778 ::EmptyClipboard();
2780 GlobalMemory uniText;
2782 // Default Scintilla behaviour in Unicode mode
2783 if (IsUnicodeMode()) {
2784 size_t uchars = UTF16Length(selectedText.Data(),
2785 selectedText.LengthWithTerminator());
2786 uniText.Allocate(2 * uchars);
2787 if (uniText) {
2788 UTF16FromUTF8(selectedText.Data(), selectedText.LengthWithTerminator(),
2789 static_cast<wchar_t *>(uniText.ptr), uchars);
2791 } else {
2792 // Not Unicode mode
2793 // Convert to Unicode using the current Scintilla code page
2794 UINT cpSrc = CodePageFromCharSet(
2795 selectedText.characterSet, selectedText.codePage);
2796 int uLen = ::MultiByteToWideChar(cpSrc, 0, selectedText.Data(),
2797 static_cast<int>(selectedText.LengthWithTerminator()), 0, 0);
2798 uniText.Allocate(2 * uLen);
2799 if (uniText) {
2800 ::MultiByteToWideChar(cpSrc, 0, selectedText.Data(),
2801 static_cast<int>(selectedText.LengthWithTerminator()),
2802 static_cast<wchar_t *>(uniText.ptr), uLen);
2806 if (uniText) {
2807 uniText.SetClip(CF_UNICODETEXT);
2808 } else {
2809 // There was a failure - try to copy at least ANSI text
2810 GlobalMemory ansiText;
2811 ansiText.Allocate(selectedText.LengthWithTerminator());
2812 if (ansiText) {
2813 memcpy(static_cast<char *>(ansiText.ptr), selectedText.Data(), selectedText.LengthWithTerminator());
2814 ansiText.SetClip(CF_TEXT);
2818 if (selectedText.rectangular) {
2819 ::SetClipboardData(cfColumnSelect, 0);
2821 GlobalMemory borlandSelection;
2822 borlandSelection.Allocate(1);
2823 if (borlandSelection) {
2824 static_cast<BYTE *>(borlandSelection.ptr)[0] = 0x02;
2825 borlandSelection.SetClip(cfBorlandIDEBlockType);
2829 if (selectedText.lineCopy) {
2830 ::SetClipboardData(cfLineSelect, 0);
2831 ::SetClipboardData(cfVSLineTag, 0);
2834 ::CloseClipboard();
2837 void ScintillaWin::ScrollMessage(WPARAM wParam) {
2838 //DWORD dwStart = timeGetTime();
2839 //Platform::DebugPrintf("Scroll %x %d\n", wParam, lParam);
2841 SCROLLINFO sci = {};
2842 sci.cbSize = sizeof(sci);
2843 sci.fMask = SIF_ALL;
2845 GetScrollInfo(SB_VERT, &sci);
2847 //Platform::DebugPrintf("ScrollInfo %d mask=%x min=%d max=%d page=%d pos=%d track=%d\n", b,sci.fMask,
2848 //sci.nMin, sci.nMax, sci.nPage, sci.nPos, sci.nTrackPos);
2850 Sci::Line topLineNew = topLine;
2851 switch (LoWord(wParam)) {
2852 case SB_LINEUP:
2853 topLineNew -= 1;
2854 break;
2855 case SB_LINEDOWN:
2856 topLineNew += 1;
2857 break;
2858 case SB_PAGEUP:
2859 topLineNew -= LinesToScroll(); break;
2860 case SB_PAGEDOWN: topLineNew += LinesToScroll(); break;
2861 case SB_TOP: topLineNew = 0; break;
2862 case SB_BOTTOM: topLineNew = MaxScrollPos(); break;
2863 case SB_THUMBPOSITION: topLineNew = sci.nTrackPos; break;
2864 case SB_THUMBTRACK: topLineNew = sci.nTrackPos; break;
2866 ScrollTo(topLineNew);
2869 void ScintillaWin::HorizontalScrollMessage(WPARAM wParam) {
2870 int xPos = xOffset;
2871 const PRectangle rcText = GetTextRectangle();
2872 const int pageWidth = static_cast<int>(rcText.Width() * 2 / 3);
2873 switch (LoWord(wParam)) {
2874 case SB_LINEUP:
2875 xPos -= 20;
2876 break;
2877 case SB_LINEDOWN: // May move past the logical end
2878 xPos += 20;
2879 break;
2880 case SB_PAGEUP:
2881 xPos -= pageWidth;
2882 break;
2883 case SB_PAGEDOWN:
2884 xPos += pageWidth;
2885 if (xPos > scrollWidth - rcText.Width()) { // Hit the end exactly
2886 xPos = scrollWidth - static_cast<int>(rcText.Width());
2888 break;
2889 case SB_TOP:
2890 xPos = 0;
2891 break;
2892 case SB_BOTTOM:
2893 xPos = scrollWidth;
2894 break;
2895 case SB_THUMBPOSITION:
2896 case SB_THUMBTRACK: {
2897 // 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 =]
2898 SCROLLINFO si;
2899 si.cbSize = sizeof(si);
2900 si.fMask = SIF_TRACKPOS;
2901 if (GetScrollInfo(SB_HORZ, &si)) {
2902 xPos = si.nTrackPos;
2905 break;
2907 HorizontalScrollTo(xPos);
2911 * Redraw all of text area.
2912 * This paint will not be abandoned.
2914 void ScintillaWin::FullPaint() {
2915 if ((technology == SC_TECHNOLOGY_DEFAULT) || (technology == SC_TECHNOLOGY_DIRECTWRITEDC)) {
2916 HDC hdc = ::GetDC(MainHWND());
2917 FullPaintDC(hdc);
2918 ::ReleaseDC(MainHWND(), hdc);
2919 } else {
2920 FullPaintDC(0);
2925 * Redraw all of text area on the specified DC.
2926 * This paint will not be abandoned.
2928 void ScintillaWin::FullPaintDC(HDC hdc) {
2929 paintState = painting;
2930 rcPaint = GetClientRectangle();
2931 paintingAllText = true;
2932 if (technology == SC_TECHNOLOGY_DEFAULT) {
2933 AutoSurface surfaceWindow(hdc, this);
2934 if (surfaceWindow) {
2935 Paint(surfaceWindow, rcPaint);
2936 surfaceWindow->Release();
2938 } else {
2939 #if defined(USE_D2D)
2940 EnsureRenderTarget(hdc);
2941 AutoSurface surfaceWindow(pRenderTarget, this);
2942 if (surfaceWindow) {
2943 pRenderTarget->BeginDraw();
2944 Paint(surfaceWindow, rcPaint);
2945 surfaceWindow->Release();
2946 const HRESULT hr = pRenderTarget->EndDraw();
2947 if (hr == static_cast<HRESULT>(D2DERR_RECREATE_TARGET)) {
2948 DropRenderTarget();
2951 #endif
2953 paintState = notPainting;
2956 static bool CompareDevCap(HDC hdc, HDC hOtherDC, int nIndex) {
2957 return ::GetDeviceCaps(hdc, nIndex) == ::GetDeviceCaps(hOtherDC, nIndex);
2960 bool ScintillaWin::IsCompatibleDC(HDC hOtherDC) {
2961 HDC hdc = ::GetDC(MainHWND());
2962 const bool isCompatible =
2963 CompareDevCap(hdc, hOtherDC, TECHNOLOGY) &&
2964 CompareDevCap(hdc, hOtherDC, LOGPIXELSY) &&
2965 CompareDevCap(hdc, hOtherDC, LOGPIXELSX) &&
2966 CompareDevCap(hdc, hOtherDC, BITSPIXEL) &&
2967 CompareDevCap(hdc, hOtherDC, PLANES);
2968 ::ReleaseDC(MainHWND(), hdc);
2969 return isCompatible;
2972 DWORD ScintillaWin::EffectFromState(DWORD grfKeyState) const {
2973 // These are the Wordpad semantics.
2974 DWORD dwEffect;
2975 if (inDragDrop == ddDragging) // Internal defaults to move
2976 dwEffect = DROPEFFECT_MOVE;
2977 else
2978 dwEffect = DROPEFFECT_COPY;
2979 if (grfKeyState & MK_ALT)
2980 dwEffect = DROPEFFECT_MOVE;
2981 if (grfKeyState & MK_CONTROL)
2982 dwEffect = DROPEFFECT_COPY;
2983 return dwEffect;
2986 /// Implement IUnknown
2987 STDMETHODIMP ScintillaWin::QueryInterface(REFIID riid, PVOID *ppv) {
2988 *ppv = NULL;
2989 if (riid == IID_IUnknown)
2990 *ppv = reinterpret_cast<IDropTarget *>(&dt);
2991 if (riid == IID_IDropSource)
2992 *ppv = reinterpret_cast<IDropSource *>(&ds);
2993 if (riid == IID_IDropTarget)
2994 *ppv = reinterpret_cast<IDropTarget *>(&dt);
2995 if (riid == IID_IDataObject)
2996 *ppv = reinterpret_cast<IDataObject *>(&dob);
2997 if (!*ppv)
2998 return E_NOINTERFACE;
2999 return S_OK;
3002 STDMETHODIMP_(ULONG) ScintillaWin::AddRef() {
3003 return 1;
3006 STDMETHODIMP_(ULONG) ScintillaWin::Release() {
3007 return 1;
3010 /// Implement IDropTarget
3011 STDMETHODIMP ScintillaWin::DragEnter(LPDATAOBJECT pIDataSource, DWORD grfKeyState,
3012 POINTL, PDWORD pdwEffect) {
3013 if (pIDataSource == NULL)
3014 return E_POINTER;
3015 FORMATETC fmtu = {CF_UNICODETEXT, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
3016 const HRESULT hrHasUText = pIDataSource->QueryGetData(&fmtu);
3017 hasOKText = (hrHasUText == S_OK);
3018 if (!hasOKText) {
3019 FORMATETC fmte = {CF_TEXT, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
3020 const HRESULT hrHasText = pIDataSource->QueryGetData(&fmte);
3021 hasOKText = (hrHasText == S_OK);
3023 if (!hasOKText) {
3024 *pdwEffect = DROPEFFECT_NONE;
3025 return S_OK;
3028 *pdwEffect = EffectFromState(grfKeyState);
3029 return S_OK;
3032 STDMETHODIMP ScintillaWin::DragOver(DWORD grfKeyState, POINTL pt, PDWORD pdwEffect) {
3033 try {
3034 if (!hasOKText || pdoc->IsReadOnly()) {
3035 *pdwEffect = DROPEFFECT_NONE;
3036 return S_OK;
3039 *pdwEffect = EffectFromState(grfKeyState);
3041 // Update the cursor.
3042 POINT rpt = {pt.x, pt.y};
3043 ::ScreenToClient(MainHWND(), &rpt);
3044 SetDragPosition(SPositionFromLocation(PointFromPOINT(rpt), false, false, UserVirtualSpace()));
3046 return S_OK;
3047 } catch (...) {
3048 errorStatus = SC_STATUS_FAILURE;
3050 return E_FAIL;
3053 STDMETHODIMP ScintillaWin::DragLeave() {
3054 try {
3055 SetDragPosition(SelectionPosition(Sci::invalidPosition));
3056 return S_OK;
3057 } catch (...) {
3058 errorStatus = SC_STATUS_FAILURE;
3060 return E_FAIL;
3063 STDMETHODIMP ScintillaWin::Drop(LPDATAOBJECT pIDataSource, DWORD grfKeyState,
3064 POINTL pt, PDWORD pdwEffect) {
3065 try {
3066 *pdwEffect = EffectFromState(grfKeyState);
3068 if (pIDataSource == NULL)
3069 return E_POINTER;
3071 SetDragPosition(SelectionPosition(Sci::invalidPosition));
3073 STGMEDIUM medium = {0, {0}, 0};
3075 std::vector<char> data; // Includes terminating NUL
3077 FORMATETC fmtu = {CF_UNICODETEXT, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
3078 HRESULT hr = pIDataSource->GetData(&fmtu, &medium);
3079 if (SUCCEEDED(hr) && medium.hGlobal) {
3080 GlobalMemory memUDrop(medium.hGlobal);
3081 wchar_t *udata = static_cast<wchar_t *>(memUDrop.ptr);
3082 if (udata) {
3083 if (IsUnicodeMode()) {
3084 const size_t tlen = memUDrop.Size();
3085 // Convert UTF-16 to UTF-8
3086 const size_t dataLen = UTF8Length(udata, tlen/2);
3087 data.resize(dataLen+1);
3088 UTF8FromUTF16(udata, tlen/2, &data[0], dataLen);
3089 } else {
3090 // Convert UTF-16 to ANSI
3092 // Default Scintilla behavior in Unicode mode
3093 // CF_UNICODETEXT available, but not in Unicode mode
3094 // Convert from Unicode to current Scintilla code page
3095 UINT cpDest = CodePageOfDocument();
3096 int tlen = ::WideCharToMultiByte(cpDest, 0, udata, -1,
3097 NULL, 0, NULL, NULL) - 1; // subtract 0 terminator
3098 data.resize(tlen + 1);
3099 ::WideCharToMultiByte(cpDest, 0, udata, -1,
3100 &data[0], tlen + 1, NULL, NULL);
3103 memUDrop.Unlock();
3104 } else {
3105 FORMATETC fmte = {CF_TEXT, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
3106 hr = pIDataSource->GetData(&fmte, &medium);
3107 if (SUCCEEDED(hr) && medium.hGlobal) {
3108 GlobalMemory memDrop(medium.hGlobal);
3109 const char *cdata = static_cast<char *>(memDrop.ptr);
3110 if (cdata)
3111 data.assign(cdata, cdata+strlen(cdata)+1);
3112 memDrop.Unlock();
3116 if (!SUCCEEDED(hr) || data.empty()) {
3117 //Platform::DebugPrintf("Bad data format: 0x%x\n", hres);
3118 return hr;
3121 FORMATETC fmtr = {cfColumnSelect, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
3122 HRESULT hrRectangular = pIDataSource->QueryGetData(&fmtr);
3124 POINT rpt = {pt.x, pt.y};
3125 ::ScreenToClient(MainHWND(), &rpt);
3126 SelectionPosition movePos = SPositionFromLocation(PointFromPOINT(rpt), false, false, UserVirtualSpace());
3128 DropAt(movePos, &data[0], data.size() - 1, *pdwEffect == DROPEFFECT_MOVE, hrRectangular == S_OK);
3130 // Free data
3131 if (medium.pUnkForRelease != NULL)
3132 medium.pUnkForRelease->Release();
3133 else
3134 ::GlobalFree(medium.hGlobal);
3136 return S_OK;
3137 } catch (...) {
3138 errorStatus = SC_STATUS_FAILURE;
3140 return E_FAIL;
3143 /// Implement important part of IDataObject
3144 STDMETHODIMP ScintillaWin::GetData(FORMATETC *pFEIn, STGMEDIUM *pSTM) {
3145 const bool formatOK = (pFEIn->cfFormat == CF_TEXT) ||
3146 ((pFEIn->cfFormat == CF_UNICODETEXT) && IsUnicodeMode());
3147 if (!formatOK ||
3148 pFEIn->ptd != 0 ||
3149 (pFEIn->dwAspect & DVASPECT_CONTENT) == 0 ||
3150 pFEIn->lindex != -1 ||
3151 (pFEIn->tymed & TYMED_HGLOBAL) == 0
3153 //Platform::DebugPrintf("DOB GetData No %d %x %x fmt=%x\n", lenDrag, pFEIn, pSTM, pFEIn->cfFormat);
3154 return DATA_E_FORMATETC;
3156 pSTM->tymed = TYMED_HGLOBAL;
3157 //Platform::DebugPrintf("DOB GetData OK %d %x %x\n", lenDrag, pFEIn, pSTM);
3159 GlobalMemory text;
3160 if (pFEIn->cfFormat == CF_UNICODETEXT) {
3161 size_t uchars = UTF16Length(drag.Data(), drag.LengthWithTerminator());
3162 text.Allocate(2 * uchars);
3163 if (text) {
3164 UTF16FromUTF8(drag.Data(), drag.LengthWithTerminator(),
3165 static_cast<wchar_t *>(text.ptr), uchars);
3167 } else {
3168 text.Allocate(drag.LengthWithTerminator());
3169 if (text) {
3170 memcpy(static_cast<char *>(text.ptr), drag.Data(), drag.LengthWithTerminator());
3173 pSTM->hGlobal = text ? text.Unlock() : 0;
3174 pSTM->pUnkForRelease = 0;
3175 return S_OK;
3178 bool ScintillaWin::Register(HINSTANCE hInstance_) {
3180 hInstance = hInstance_;
3181 bool result;
3183 // Register the Scintilla class
3184 // Register Scintilla as a wide character window
3185 WNDCLASSEXW wndclass;
3186 wndclass.cbSize = sizeof(wndclass);
3187 wndclass.style = CS_GLOBALCLASS | CS_HREDRAW | CS_VREDRAW;
3188 wndclass.lpfnWndProc = ScintillaWin::SWndProc;
3189 wndclass.cbClsExtra = 0;
3190 wndclass.cbWndExtra = sizeof(ScintillaWin *);
3191 wndclass.hInstance = hInstance;
3192 wndclass.hIcon = NULL;
3193 wndclass.hCursor = NULL;
3194 wndclass.hbrBackground = NULL;
3195 wndclass.lpszMenuName = NULL;
3196 wndclass.lpszClassName = L"Scintilla";
3197 wndclass.hIconSm = 0;
3198 scintillaClassAtom = ::RegisterClassExW(&wndclass);
3199 result = 0 != scintillaClassAtom;
3201 if (result) {
3202 // Register the CallTip class
3203 WNDCLASSEX wndclassc;
3204 wndclassc.cbSize = sizeof(wndclassc);
3205 wndclassc.style = CS_GLOBALCLASS | CS_HREDRAW | CS_VREDRAW;
3206 wndclassc.cbClsExtra = 0;
3207 wndclassc.cbWndExtra = sizeof(ScintillaWin *);
3208 wndclassc.hInstance = hInstance;
3209 wndclassc.hIcon = NULL;
3210 wndclassc.hbrBackground = NULL;
3211 wndclassc.lpszMenuName = NULL;
3212 wndclassc.lpfnWndProc = ScintillaWin::CTWndProc;
3213 wndclassc.hCursor = ::LoadCursor(NULL, IDC_ARROW);
3214 wndclassc.lpszClassName = callClassName;
3215 wndclassc.hIconSm = 0;
3217 callClassAtom = ::RegisterClassEx(&wndclassc);
3218 result = 0 != callClassAtom;
3221 return result;
3224 bool ScintillaWin::Unregister() {
3225 bool result = true;
3226 if (0 != scintillaClassAtom) {
3227 if (::UnregisterClass(MAKEINTATOM(scintillaClassAtom), hInstance) == 0) {
3228 result = false;
3230 scintillaClassAtom = 0;
3232 if (0 != callClassAtom) {
3233 if (::UnregisterClass(MAKEINTATOM(callClassAtom), hInstance) == 0) {
3234 result = false;
3236 callClassAtom = 0;
3238 return result;
3241 bool ScintillaWin::HasCaretSizeChanged() const {
3242 if (
3243 ( (0 != vs.caretWidth) && (sysCaretWidth != vs.caretWidth) )
3244 || ((0 != vs.lineHeight) && (sysCaretHeight != vs.lineHeight))
3246 return true;
3248 return false;
3251 BOOL ScintillaWin::CreateSystemCaret() {
3252 sysCaretWidth = vs.caretWidth;
3253 if (0 == sysCaretWidth) {
3254 sysCaretWidth = 1;
3256 sysCaretHeight = vs.lineHeight;
3257 int bitmapSize = (((sysCaretWidth + 15) & ~15) >> 3) *
3258 sysCaretHeight;
3259 std::vector<char> bits(bitmapSize);
3260 sysCaretBitmap = ::CreateBitmap(sysCaretWidth, sysCaretHeight, 1,
3261 1, reinterpret_cast<BYTE *>(&bits[0]));
3262 BOOL retval = ::CreateCaret(
3263 MainHWND(), sysCaretBitmap,
3264 sysCaretWidth, sysCaretHeight);
3265 if (technology == SC_TECHNOLOGY_DEFAULT) {
3266 // System caret interferes with Direct2D drawing so only show it for GDI.
3267 ::ShowCaret(MainHWND());
3269 return retval;
3272 BOOL ScintillaWin::DestroySystemCaret() {
3273 ::HideCaret(MainHWND());
3274 BOOL retval = ::DestroyCaret();
3275 if (sysCaretBitmap) {
3276 ::DeleteObject(sysCaretBitmap);
3277 sysCaretBitmap = 0;
3279 return retval;
3282 LRESULT PASCAL ScintillaWin::CTWndProc(
3283 HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam) {
3284 // Find C++ object associated with window.
3285 ScintillaWin *sciThis = static_cast<ScintillaWin *>(PointerFromWindow(hWnd));
3286 try {
3287 // ctp will be zero if WM_CREATE not seen yet
3288 if (sciThis == 0) {
3289 if (iMessage == WM_CREATE) {
3290 // Associate CallTip object with window
3291 CREATESTRUCT *pCreate = reinterpret_cast<CREATESTRUCT *>(lParam);
3292 SetWindowPointer(hWnd, pCreate->lpCreateParams);
3293 return 0;
3294 } else {
3295 return ::DefWindowProc(hWnd, iMessage, wParam, lParam);
3297 } else {
3298 if (iMessage == WM_NCDESTROY) {
3299 ::SetWindowLong(hWnd, 0, 0);
3300 return ::DefWindowProc(hWnd, iMessage, wParam, lParam);
3301 } else if (iMessage == WM_PAINT) {
3302 PAINTSTRUCT ps;
3303 ::BeginPaint(hWnd, &ps);
3304 std::unique_ptr<Surface> surfaceWindow(Surface::Allocate(sciThis->technology));
3305 #if defined(USE_D2D)
3306 ID2D1HwndRenderTarget *pCTRenderTarget = 0;
3307 #endif
3308 RECT rc;
3309 GetClientRect(hWnd, &rc);
3310 // Create a Direct2D render target.
3311 if (sciThis->technology == SC_TECHNOLOGY_DEFAULT) {
3312 surfaceWindow->Init(ps.hdc, hWnd);
3313 } else {
3314 #if defined(USE_D2D)
3315 D2D1_HWND_RENDER_TARGET_PROPERTIES dhrtp;
3316 dhrtp.hwnd = hWnd;
3317 dhrtp.pixelSize = D2D1::SizeU(rc.right - rc.left, rc.bottom - rc.top);
3318 dhrtp.presentOptions = (sciThis->technology == SC_TECHNOLOGY_DIRECTWRITERETAIN) ?
3319 D2D1_PRESENT_OPTIONS_RETAIN_CONTENTS : D2D1_PRESENT_OPTIONS_NONE;
3321 D2D1_RENDER_TARGET_PROPERTIES drtp;
3322 drtp.type = D2D1_RENDER_TARGET_TYPE_DEFAULT;
3323 drtp.pixelFormat.format = DXGI_FORMAT_UNKNOWN;
3324 drtp.pixelFormat.alphaMode = D2D1_ALPHA_MODE_UNKNOWN;
3325 drtp.dpiX = 96.0;
3326 drtp.dpiY = 96.0;
3327 drtp.usage = D2D1_RENDER_TARGET_USAGE_NONE;
3328 drtp.minLevel = D2D1_FEATURE_LEVEL_DEFAULT;
3330 if (!SUCCEEDED(pD2DFactory->CreateHwndRenderTarget(drtp, dhrtp, &pCTRenderTarget))) {
3331 surfaceWindow->Release();
3332 ::EndPaint(hWnd, &ps);
3333 return 0;
3335 surfaceWindow->Init(pCTRenderTarget, hWnd);
3336 pCTRenderTarget->BeginDraw();
3337 #endif
3339 surfaceWindow->SetUnicodeMode(SC_CP_UTF8 == sciThis->ct.codePage);
3340 surfaceWindow->SetDBCSMode(sciThis->ct.codePage);
3341 sciThis->ct.PaintCT(surfaceWindow.get());
3342 #if defined(USE_D2D)
3343 if (pCTRenderTarget)
3344 pCTRenderTarget->EndDraw();
3345 #endif
3346 surfaceWindow->Release();
3347 #if defined(USE_D2D)
3348 if (pCTRenderTarget)
3349 pCTRenderTarget->Release();
3350 #endif
3351 ::EndPaint(hWnd, &ps);
3352 return 0;
3353 } else if ((iMessage == WM_NCLBUTTONDOWN) || (iMessage == WM_NCLBUTTONDBLCLK)) {
3354 POINT pt;
3355 pt.x = static_cast<short>(LOWORD(lParam));
3356 pt.y = static_cast<short>(HIWORD(lParam));
3357 ScreenToClient(hWnd, &pt);
3358 sciThis->ct.MouseClick(PointFromPOINT(pt));
3359 sciThis->CallTipClick();
3360 return 0;
3361 } else if (iMessage == WM_LBUTTONDOWN) {
3362 // This does not fire due to the hit test code
3363 sciThis->ct.MouseClick(PointFromLParam(lParam));
3364 sciThis->CallTipClick();
3365 return 0;
3366 } else if (iMessage == WM_SETCURSOR) {
3367 ::SetCursor(::LoadCursor(NULL, IDC_ARROW));
3368 return 0;
3369 } else if (iMessage == WM_NCHITTEST) {
3370 return HTCAPTION;
3371 } else {
3372 return ::DefWindowProc(hWnd, iMessage, wParam, lParam);
3375 } catch (...) {
3376 sciThis->errorStatus = SC_STATUS_FAILURE;
3378 return ::DefWindowProc(hWnd, iMessage, wParam, lParam);
3381 sptr_t ScintillaWin::DirectFunction(
3382 sptr_t ptr, UINT iMessage, uptr_t wParam, sptr_t lParam) {
3383 PLATFORM_ASSERT(::GetCurrentThreadId() == ::GetWindowThreadProcessId(reinterpret_cast<ScintillaWin *>(ptr)->MainHWND(), NULL));
3384 return reinterpret_cast<ScintillaWin *>(ptr)->WndProc(iMessage, wParam, lParam);
3387 namespace Scintilla {
3389 sptr_t DirectFunction(
3390 ScintillaWin *sci, UINT iMessage, uptr_t wParam, sptr_t lParam) {
3391 return sci->WndProc(iMessage, wParam, lParam);
3396 LRESULT PASCAL ScintillaWin::SWndProc(
3397 HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam) {
3398 //Platform::DebugPrintf("S W:%x M:%x WP:%x L:%x\n", hWnd, iMessage, wParam, lParam);
3400 // Find C++ object associated with window.
3401 ScintillaWin *sci = static_cast<ScintillaWin *>(PointerFromWindow(hWnd));
3402 // sci will be zero if WM_CREATE not seen yet
3403 if (sci == 0) {
3404 try {
3405 if (iMessage == WM_CREATE) {
3406 // Create C++ object associated with window
3407 sci = new ScintillaWin(hWnd);
3408 SetWindowPointer(hWnd, sci);
3409 return sci->WndProc(iMessage, wParam, lParam);
3411 } catch (...) {
3413 return ::DefWindowProc(hWnd, iMessage, wParam, lParam);
3414 } else {
3415 if (iMessage == WM_NCDESTROY) {
3416 try {
3417 sci->Finalise();
3418 delete sci;
3419 } catch (...) {
3421 ::SetWindowLong(hWnd, 0, 0);
3422 return ::DefWindowProc(hWnd, iMessage, wParam, lParam);
3423 } else {
3424 return sci->WndProc(iMessage, wParam, lParam);
3429 // This function is externally visible so it can be called from container when building statically.
3430 // Must be called once only.
3431 int Scintilla_RegisterClasses(void *hInstance) {
3432 Platform_Initialise(hInstance);
3433 const bool result = ScintillaWin::Register(static_cast<HINSTANCE>(hInstance));
3434 #ifdef SCI_LEXER
3435 Scintilla_LinkLexers();
3436 #endif
3437 return result;
3440 namespace Scintilla {
3442 int ResourcesRelease(bool fromDllMain) {
3443 const bool result = ScintillaWin::Unregister();
3444 Platform_Finalise(fromDllMain);
3445 return result;
3450 // This function is externally visible so it can be called from container when building statically.
3451 int Scintilla_ReleaseResources() {
3452 return Scintilla::ResourcesRelease(false);