Applied backgroundcolors.patch
[TortoiseGit.git] / ext / scintilla / win32 / ScintillaWin.cxx
blob63c70a7f5f7953be922d8a83defecfebeb23655a
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(NTDDI_WIN7) && !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 "ILexer.h"
48 #include "Scintilla.h"
50 #ifdef SCI_LEXER
51 #include "SciLexer.h"
52 #endif
53 #include "StringCopy.h"
54 #ifdef SCI_LEXER
55 #include "LexerModule.h"
56 #endif
57 #include "Position.h"
58 #include "UniqueString.h"
59 #include "SplitVector.h"
60 #include "Partitioning.h"
61 #include "RunStyles.h"
62 #include "ContractionState.h"
63 #include "CellBuffer.h"
64 #include "CallTip.h"
65 #include "KeyMap.h"
66 #include "Indicator.h"
67 #include "XPM.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"
94 #ifndef SPI_GETWHEELSCROLLLINES
95 #define SPI_GETWHEELSCROLLLINES 104
96 #endif
98 #ifndef WM_UNICHAR
99 #define WM_UNICHAR 0x0109
100 #endif
102 #ifndef UNICODE_NOCHAR
103 #define UNICODE_NOCHAR 0xFFFF
104 #endif
106 #ifndef IS_HIGH_SURROGATE
107 #define IS_HIGH_SURROGATE(x) ((x) >= SURROGATE_LEAD_FIRST && (x) <= SURROGATE_LEAD_LAST)
108 #endif
110 #ifndef IS_LOW_SURROGATE
111 #define IS_LOW_SURROGATE(x) ((x) >= SURROGATE_TRAIL_FIRST && (x) <= SURROGATE_TRAIL_LAST)
112 #endif
114 #ifndef MK_ALT
115 #define MK_ALT 32
116 #endif
118 #define SC_WIN_IDLE 5001
120 #define SC_INDICATOR_INPUT INDIC_IME
121 #define SC_INDICATOR_TARGET INDIC_IME+1
122 #define SC_INDICATOR_CONVERTED INDIC_IME+2
123 #define SC_INDICATOR_UNKNOWN INDIC_IME_MAX
125 #ifndef SCS_CAP_SETRECONVERTSTRING
126 #define SCS_CAP_SETRECONVERTSTRING 0x00000004
127 #define SCS_QUERYRECONVERTSTRING 0x00020000
128 #define SCS_SETRECONVERTSTRING 0x00010000
129 #endif
131 typedef BOOL (WINAPI *TrackMouseEventSig)(LPTRACKMOUSEEVENT);
132 typedef UINT_PTR (WINAPI *SetCoalescableTimerSig)(HWND hwnd, UINT_PTR nIDEvent,
133 UINT uElapse, TIMERPROC lpTimerFunc, ULONG uToleranceDelay);
135 // GCC has trouble with the standard COM ABI so do it the old C way with explicit vtables.
137 const TCHAR callClassName[] = TEXT("CallTip");
139 #ifdef SCI_NAMESPACE
140 using namespace Scintilla;
141 #endif
143 static void *PointerFromWindow(HWND hWnd) {
144 return reinterpret_cast<void *>(::GetWindowLongPtr(hWnd, 0));
147 static void SetWindowPointer(HWND hWnd, void *ptr) {
148 ::SetWindowLongPtr(hWnd, 0, reinterpret_cast<LONG_PTR>(ptr));
151 static void SetWindowID(HWND hWnd, int identifier) {
152 ::SetWindowLongPtr(hWnd, GWLP_ID, identifier);
155 static Point PointFromPOINT(POINT pt) {
156 return Point::FromInts(pt.x, pt.y);
159 class ScintillaWin; // Forward declaration for COM interface subobjects
161 typedef void VFunction(void);
166 class FormatEnumerator {
167 public:
168 VFunction **vtbl;
169 int ref;
170 unsigned int pos;
171 std::vector<CLIPFORMAT> formats;
172 FormatEnumerator(int pos_, CLIPFORMAT formats_[], size_t formatsLen_);
177 class DropSource {
178 public:
179 VFunction **vtbl;
180 ScintillaWin *sci;
181 DropSource();
186 class DataObject {
187 public:
188 VFunction **vtbl;
189 ScintillaWin *sci;
190 DataObject();
195 class DropTarget {
196 public:
197 VFunction **vtbl;
198 ScintillaWin *sci;
199 DropTarget();
202 namespace {
204 class IMContext {
205 HWND hwnd;
206 public:
207 HIMC hIMC;
208 IMContext(HWND hwnd_) :
209 hwnd(hwnd_), hIMC(::ImmGetContext(hwnd_)) {
211 ~IMContext() {
212 if (hIMC)
213 ::ImmReleaseContext(hwnd, hIMC);
216 unsigned int GetImeCaretPos() {
217 return ImmGetCompositionStringW(hIMC, GCS_CURSORPOS, NULL, 0);
220 std::vector<BYTE> GetImeAttributes() {
221 int attrLen = ::ImmGetCompositionStringW(hIMC, GCS_COMPATTR, NULL, 0);
222 std::vector<BYTE> attr(attrLen, 0);
223 ::ImmGetCompositionStringW(hIMC, GCS_COMPATTR, &attr[0], static_cast<DWORD>(attr.size()));
224 return attr;
227 std::wstring GetCompositionString(DWORD dwIndex) {
228 const LONG byteLen = ::ImmGetCompositionStringW(hIMC, dwIndex, NULL, 0);
229 std::wstring wcs(byteLen / 2, 0);
230 ::ImmGetCompositionStringW(hIMC, dwIndex, &wcs[0], byteLen);
231 return wcs;
239 class ScintillaWin :
240 public ScintillaBase {
242 bool lastKeyDownConsumed;
243 wchar_t lastHighSurrogateChar;
245 bool capturedMouse;
246 bool trackedMouseLeave;
247 SetCoalescableTimerSig SetCoalescableTimerFn;
249 unsigned int linesPerScroll; ///< Intellimouse support
250 int wheelDelta; ///< Wheel delta from roll
252 HRGN hRgnUpdate;
254 bool hasOKText;
256 CLIPFORMAT cfColumnSelect;
257 CLIPFORMAT cfBorlandIDEBlockType;
258 CLIPFORMAT cfLineSelect;
259 CLIPFORMAT cfVSLineTag;
261 HRESULT hrOle;
262 DropSource ds;
263 DataObject dob;
264 DropTarget dt;
266 static HINSTANCE hInstance;
267 static ATOM scintillaClassAtom;
268 static ATOM callClassAtom;
270 #if defined(USE_D2D)
271 ID2D1RenderTarget *pRenderTarget;
272 bool renderTargetValid;
273 #endif
275 explicit ScintillaWin(HWND hwnd);
276 // Deleted so ScintillaWin objects can not be copied.
277 ScintillaWin(const ScintillaWin &) = delete;
278 ScintillaWin &operator=(const ScintillaWin &) = delete;
279 ~ScintillaWin() override;
281 void Init();
282 void Finalise() override;
283 #if defined(USE_D2D)
284 void EnsureRenderTarget(HDC hdc);
285 void DropRenderTarget();
286 #endif
287 HWND MainHWND();
289 static sptr_t DirectFunction(
290 sptr_t ptr, UINT iMessage, uptr_t wParam, sptr_t lParam);
291 static LRESULT PASCAL SWndProc(
292 HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam);
293 static LRESULT PASCAL CTWndProc(
294 HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam);
296 enum { invalidTimerID, standardTimerID, idleTimerID, fineTimerStart };
298 bool DragThreshold(Point ptStart, Point ptNow) override;
299 void StartDrag() override;
300 Sci::Position TargetAsUTF8(char *text);
301 void AddCharUTF16(wchar_t const *wcs, unsigned int wclen);
302 Sci::Position EncodedFromUTF8(char *utf8, char *encoded) const;
303 sptr_t WndPaint(uptr_t wParam);
305 sptr_t HandleCompositionWindowed(uptr_t wParam, sptr_t lParam);
306 sptr_t HandleCompositionInline(uptr_t wParam, sptr_t lParam);
307 static bool KoreanIME();
308 void MoveImeCarets(Sci::Position offset);
309 void DrawImeIndicator(int indicator, int len);
310 void SetCandidateWindowPos();
311 void SelectionToHangul();
312 void EscapeHanja();
313 void ToggleHanja();
314 void AddWString(std::wstring wcs);
316 UINT CodePageOfDocument() const;
317 bool ValidCodePage(int codePage) const override;
318 sptr_t DefWndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam) override;
319 bool SetIdle(bool on) override;
320 UINT_PTR timers[tickDwell+1];
321 bool FineTickerAvailable() override;
322 bool FineTickerRunning(TickReason reason) override;
323 void FineTickerStart(TickReason reason, int millis, int tolerance) override;
324 void FineTickerCancel(TickReason reason) override;
325 void SetMouseCapture(bool on) override;
326 bool HaveMouseCapture() override;
327 void SetTrackMouseLeaveEvent(bool on);
328 bool PaintContains(PRectangle rc) override;
329 void ScrollText(Sci::Line linesToMove) override;
330 void NotifyCaretMove() override;
331 void UpdateSystemCaret() override;
332 void SetVerticalScrollPos() override;
333 void SetHorizontalScrollPos() override;
334 bool ModifyScrollBars(Sci::Line nMax, Sci::Line nPage) override;
335 void NotifyChange() override;
336 void NotifyFocus(bool focus) override;
337 void SetCtrlID(int identifier) override;
338 int GetCtrlID() override;
339 void NotifyParent(SCNotification scn) override;
340 void NotifyParent(SCNotification* scn) override;
341 void NotifyDoubleClick(Point pt, int modifiers) override;
342 CaseFolder *CaseFolderForEncoding() override;
343 std::string CaseMapString(const std::string &s, int caseMapping) override;
344 void Copy() override;
345 void CopyAllowLine() override;
346 bool CanPaste() override;
347 void Paste() override;
348 void CreateCallTipWindow(PRectangle rc) override;
349 void AddToPopUp(const char *label, int cmd = 0, bool enabled = true) override;
350 void ClaimSelection() override;
352 // DBCS
353 void ImeStartComposition();
354 void ImeEndComposition();
355 LRESULT ImeOnReconvert(LPARAM lParam);
357 void GetIntelliMouseParameters();
358 void CopyToClipboard(const SelectionText &selectedText) override;
359 void ScrollMessage(WPARAM wParam);
360 void HorizontalScrollMessage(WPARAM wParam);
361 void FullPaint();
362 void FullPaintDC(HDC hdc);
363 bool IsCompatibleDC(HDC hOtherDC);
364 DWORD EffectFromState(DWORD grfKeyState) const;
366 int SetScrollInfo(int nBar, LPCSCROLLINFO lpsi, BOOL bRedraw);
367 bool GetScrollInfo(int nBar, LPSCROLLINFO lpsi);
368 void ChangeScrollPos(int barType, Sci::Position pos);
369 sptr_t GetTextLength();
370 sptr_t GetText(uptr_t wParam, sptr_t lParam);
372 public:
373 // Public for benefit of Scintilla_DirectFunction
374 sptr_t WndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam) override;
376 /// Implement IUnknown
377 STDMETHODIMP QueryInterface(REFIID riid, PVOID *ppv);
378 STDMETHODIMP_(ULONG)AddRef();
379 STDMETHODIMP_(ULONG)Release();
381 /// Implement IDropTarget
382 STDMETHODIMP DragEnter(LPDATAOBJECT pIDataSource, DWORD grfKeyState,
383 POINTL pt, PDWORD pdwEffect);
384 STDMETHODIMP DragOver(DWORD grfKeyState, POINTL pt, PDWORD pdwEffect);
385 STDMETHODIMP DragLeave();
386 STDMETHODIMP Drop(LPDATAOBJECT pIDataSource, DWORD grfKeyState,
387 POINTL pt, PDWORD pdwEffect);
389 /// Implement important part of IDataObject
390 STDMETHODIMP GetData(FORMATETC *pFEIn, STGMEDIUM *pSTM);
392 static bool Register(HINSTANCE hInstance_);
393 static bool Unregister();
395 friend class DropSource;
396 friend class DataObject;
397 friend class DropTarget;
398 bool DragIsRectangularOK(CLIPFORMAT fmt) const {
399 return drag.rectangular && (fmt == cfColumnSelect);
402 private:
403 // For use in creating a system caret
404 bool HasCaretSizeChanged() const;
405 BOOL CreateSystemCaret();
406 BOOL DestroySystemCaret();
407 HBITMAP sysCaretBitmap;
408 int sysCaretWidth;
409 int sysCaretHeight;
412 HINSTANCE ScintillaWin::hInstance = 0;
413 ATOM ScintillaWin::scintillaClassAtom = 0;
414 ATOM ScintillaWin::callClassAtom = 0;
416 ScintillaWin::ScintillaWin(HWND hwnd) {
418 lastKeyDownConsumed = false;
419 lastHighSurrogateChar = 0;
421 capturedMouse = false;
422 trackedMouseLeave = false;
423 SetCoalescableTimerFn = 0;
425 linesPerScroll = 0;
426 wheelDelta = 0; // Wheel delta from roll
428 hRgnUpdate = 0;
430 hasOKText = false;
432 // There does not seem to be a real standard for indicating that the clipboard
433 // contains a rectangular selection, so copy Developer Studio and Borland Delphi.
434 cfColumnSelect = static_cast<CLIPFORMAT>(
435 ::RegisterClipboardFormat(TEXT("MSDEVColumnSelect")));
436 cfBorlandIDEBlockType = static_cast<CLIPFORMAT>(
437 ::RegisterClipboardFormat(TEXT("Borland IDE Block Type")));
439 // Likewise for line-copy (copies a full line when no text is selected)
440 cfLineSelect = static_cast<CLIPFORMAT>(
441 ::RegisterClipboardFormat(TEXT("MSDEVLineSelect")));
442 cfVSLineTag = static_cast<CLIPFORMAT>(
443 ::RegisterClipboardFormat(TEXT("VisualStudioEditorOperationsLineCutCopyClipboardTag")));
444 hrOle = E_FAIL;
446 wMain = hwnd;
448 dob.sci = this;
449 ds.sci = this;
450 dt.sci = this;
452 sysCaretBitmap = 0;
453 sysCaretWidth = 0;
454 sysCaretHeight = 0;
456 #if defined(USE_D2D)
457 pRenderTarget = 0;
458 renderTargetValid = true;
459 #endif
461 caret.period = ::GetCaretBlinkTime();
462 if (caret.period < 0)
463 caret.period = 0;
465 Init();
468 ScintillaWin::~ScintillaWin() {}
470 void ScintillaWin::Init() {
471 // Initialize COM. If the app has already done this it will have
472 // no effect. If the app hasn't, we really shouldn't ask them to call
473 // it just so this internal feature works.
474 hrOle = ::OleInitialize(NULL);
476 // Find SetCoalescableTimer which is only available from Windows 8+
477 HMODULE user32 = ::GetModuleHandle(TEXT("user32.dll"));
478 if (user32) {
479 SetCoalescableTimerFn = (SetCoalescableTimerSig)::GetProcAddress(user32, "SetCoalescableTimer");
482 for (TickReason tr = tickCaret; tr <= tickDwell; tr = static_cast<TickReason>(tr + 1)) {
483 timers[tr] = 0;
485 vs.indicators[SC_INDICATOR_UNKNOWN] = Indicator(INDIC_HIDDEN, ColourDesired(0, 0, 0xff));
486 vs.indicators[SC_INDICATOR_INPUT] = Indicator(INDIC_DOTS, ColourDesired(0, 0, 0xff));
487 vs.indicators[SC_INDICATOR_CONVERTED] = Indicator(INDIC_COMPOSITIONTHICK, ColourDesired(0, 0, 0xff));
488 vs.indicators[SC_INDICATOR_TARGET] = Indicator(INDIC_STRAIGHTBOX, ColourDesired(0, 0, 0xff));
491 void ScintillaWin::Finalise() {
492 ScintillaBase::Finalise();
493 for (TickReason tr = tickCaret; tr <= tickDwell; tr = static_cast<TickReason>(tr + 1)) {
494 FineTickerCancel(tr);
496 SetIdle(false);
497 #if defined(USE_D2D)
498 DropRenderTarget();
499 #endif
500 ::RevokeDragDrop(MainHWND());
501 if (SUCCEEDED(hrOle)) {
502 ::OleUninitialize();
506 #if defined(USE_D2D)
508 void ScintillaWin::EnsureRenderTarget(HDC hdc) {
509 if (!renderTargetValid) {
510 DropRenderTarget();
511 renderTargetValid = true;
513 if (pD2DFactory && !pRenderTarget) {
514 RECT rc;
515 HWND hw = MainHWND();
516 GetClientRect(hw, &rc);
518 D2D1_SIZE_U size = D2D1::SizeU(rc.right - rc.left, rc.bottom - rc.top);
520 // Create a Direct2D render target.
521 #if 1
522 D2D1_RENDER_TARGET_PROPERTIES drtp;
523 drtp.type = D2D1_RENDER_TARGET_TYPE_DEFAULT;
524 drtp.pixelFormat.format = DXGI_FORMAT_UNKNOWN;
525 drtp.pixelFormat.alphaMode = D2D1_ALPHA_MODE_UNKNOWN;
526 drtp.dpiX = 96.0;
527 drtp.dpiY = 96.0;
528 drtp.usage = D2D1_RENDER_TARGET_USAGE_NONE;
529 drtp.minLevel = D2D1_FEATURE_LEVEL_DEFAULT;
531 if (technology == SC_TECHNOLOGY_DIRECTWRITEDC) {
532 // Explicit pixel format needed.
533 drtp.pixelFormat = D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM,
534 D2D1_ALPHA_MODE_IGNORE);
536 ID2D1DCRenderTarget *pDCRT = NULL;
537 HRESULT hr = pD2DFactory->CreateDCRenderTarget(&drtp, &pDCRT);
538 if (SUCCEEDED(hr)) {
539 pRenderTarget = pDCRT;
540 } else {
541 Platform::DebugPrintf("Failed CreateDCRenderTarget 0x%x\n", hr);
542 pRenderTarget = NULL;
545 } else {
546 D2D1_HWND_RENDER_TARGET_PROPERTIES dhrtp;
547 dhrtp.hwnd = hw;
548 dhrtp.pixelSize = size;
549 dhrtp.presentOptions = (technology == SC_TECHNOLOGY_DIRECTWRITERETAIN) ?
550 D2D1_PRESENT_OPTIONS_RETAIN_CONTENTS : D2D1_PRESENT_OPTIONS_NONE;
552 ID2D1HwndRenderTarget *pHwndRenderTarget = NULL;
553 HRESULT hr = pD2DFactory->CreateHwndRenderTarget(drtp, dhrtp, &pHwndRenderTarget);
554 if (SUCCEEDED(hr)) {
555 pRenderTarget = pHwndRenderTarget;
556 } else {
557 Platform::DebugPrintf("Failed CreateHwndRenderTarget 0x%x\n", hr);
558 pRenderTarget = NULL;
561 #else
562 pD2DFactory->CreateHwndRenderTarget(
563 D2D1::RenderTargetProperties(
564 D2D1_RENDER_TARGET_TYPE_DEFAULT ,
565 D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_PREMULTIPLIED),
566 96.0f, 96.0f, D2D1_RENDER_TARGET_USAGE_NONE, D2D1_FEATURE_LEVEL_DEFAULT),
567 D2D1::HwndRenderTargetProperties(hw, size),
568 &pRenderTarget);
569 #endif
570 // Pixmaps were created to be compatible with previous render target so
571 // need to be recreated.
572 DropGraphics(false);
575 if ((technology == SC_TECHNOLOGY_DIRECTWRITEDC) && pRenderTarget) {
576 RECT rcWindow;
577 GetClientRect(MainHWND(), &rcWindow);
578 HRESULT hr = static_cast<ID2D1DCRenderTarget*>(pRenderTarget)->BindDC(hdc, &rcWindow);
579 if (FAILED(hr)) {
580 Platform::DebugPrintf("BindDC failed 0x%x\n", hr);
581 DropRenderTarget();
586 void ScintillaWin::DropRenderTarget() {
587 if (pRenderTarget) {
588 pRenderTarget->Release();
589 pRenderTarget = 0;
593 #endif
595 HWND ScintillaWin::MainHWND() {
596 return static_cast<HWND>(wMain.GetID());
599 bool ScintillaWin::DragThreshold(Point ptStart, Point ptNow) {
600 const int xMove = static_cast<int>(std::abs(ptStart.x - ptNow.x));
601 const int yMove = static_cast<int>(std::abs(ptStart.y - ptNow.y));
602 return (xMove > ::GetSystemMetrics(SM_CXDRAG)) ||
603 (yMove > ::GetSystemMetrics(SM_CYDRAG));
606 void ScintillaWin::StartDrag() {
607 inDragDrop = ddDragging;
608 DWORD dwEffect = 0;
609 dropWentOutside = true;
610 IDataObject *pDataObject = reinterpret_cast<IDataObject *>(&dob);
611 IDropSource *pDropSource = reinterpret_cast<IDropSource *>(&ds);
612 //Platform::DebugPrintf("About to DoDragDrop %x %x\n", pDataObject, pDropSource);
613 const HRESULT hr = ::DoDragDrop(
614 pDataObject,
615 pDropSource,
616 DROPEFFECT_COPY | DROPEFFECT_MOVE, &dwEffect);
617 //Platform::DebugPrintf("DoDragDrop = %x\n", hr);
618 if (SUCCEEDED(hr)) {
619 if ((hr == DRAGDROP_S_DROP) && (dwEffect == DROPEFFECT_MOVE) && dropWentOutside) {
620 // Remove dragged out text
621 ClearSelection();
624 inDragDrop = ddNone;
625 SetDragPosition(SelectionPosition(Sci::invalidPosition));
628 // Avoid warnings everywhere for old style casts by concentrating them here
629 static WORD LoWord(uptr_t l) {
630 return LOWORD(l);
633 static WORD HiWord(uptr_t l) {
634 return HIWORD(l);
637 static int InputCodePage() {
638 HKL inputLocale = ::GetKeyboardLayout(0);
639 LANGID inputLang = LOWORD(inputLocale);
640 char sCodePage[10];
641 const int res = ::GetLocaleInfoA(MAKELCID(inputLang, SORT_DEFAULT),
642 LOCALE_IDEFAULTANSICODEPAGE, sCodePage, sizeof(sCodePage));
643 if (!res)
644 return 0;
645 return atoi(sCodePage);
648 /** Map the key codes to their equivalent SCK_ form. */
649 static int KeyTranslate(int keyIn) {
650 //PLATFORM_ASSERT(!keyIn);
651 switch (keyIn) {
652 case VK_DOWN: return SCK_DOWN;
653 case VK_UP: return SCK_UP;
654 case VK_LEFT: return SCK_LEFT;
655 case VK_RIGHT: return SCK_RIGHT;
656 case VK_HOME: return SCK_HOME;
657 case VK_END: return SCK_END;
658 case VK_PRIOR: return SCK_PRIOR;
659 case VK_NEXT: return SCK_NEXT;
660 case VK_DELETE: return SCK_DELETE;
661 case VK_INSERT: return SCK_INSERT;
662 case VK_ESCAPE: return SCK_ESCAPE;
663 case VK_BACK: return SCK_BACK;
664 case VK_TAB: return SCK_TAB;
665 case VK_RETURN: return SCK_RETURN;
666 case VK_ADD: return SCK_ADD;
667 case VK_SUBTRACT: return SCK_SUBTRACT;
668 case VK_DIVIDE: return SCK_DIVIDE;
669 case VK_LWIN: return SCK_WIN;
670 case VK_RWIN: return SCK_RWIN;
671 case VK_APPS: return SCK_MENU;
672 case VK_OEM_2: return '/';
673 case VK_OEM_3: return '`';
674 case VK_OEM_4: return '[';
675 case VK_OEM_5: return '\\';
676 case VK_OEM_6: return ']';
677 default: return keyIn;
681 static bool BoundsContains(PRectangle rcBounds, HRGN hRgnBounds, PRectangle rcCheck) {
682 bool contains = true;
683 if (!rcCheck.Empty()) {
684 if (!rcBounds.Contains(rcCheck)) {
685 contains = false;
686 } else if (hRgnBounds) {
687 // In bounding rectangle so check more accurately using region
688 HRGN hRgnCheck = ::CreateRectRgn(static_cast<int>(rcCheck.left), static_cast<int>(rcCheck.top),
689 static_cast<int>(rcCheck.right), static_cast<int>(rcCheck.bottom));
690 if (hRgnCheck) {
691 HRGN hRgnDifference = ::CreateRectRgn(0, 0, 0, 0);
692 if (hRgnDifference) {
693 const int combination = ::CombineRgn(hRgnDifference, hRgnCheck, hRgnBounds, RGN_DIFF);
694 if (combination != NULLREGION) {
695 contains = false;
697 ::DeleteRgn(hRgnDifference);
699 ::DeleteRgn(hRgnCheck);
703 return contains;
706 static std::string StringEncode(const std::wstring &s, int codePage) {
707 const int cchMulti = s.length() ? ::WideCharToMultiByte(codePage, 0, s.c_str(), static_cast<int>(s.length()), NULL, 0, NULL, NULL) : 0;
708 std::string sMulti(cchMulti, 0);
709 if (cchMulti) {
710 ::WideCharToMultiByte(codePage, 0, s.c_str(), static_cast<int>(s.size()), &sMulti[0], cchMulti, NULL, NULL);
712 return sMulti;
715 static std::wstring StringDecode(const std::string &s, int codePage) {
716 const int cchWide = s.length() ? ::MultiByteToWideChar(codePage, 0, s.c_str(), static_cast<int>(s.length()), NULL, 0) : 0;
717 std::wstring sWide(cchWide, 0);
718 if (cchWide) {
719 ::MultiByteToWideChar(codePage, 0, s.c_str(), static_cast<int>(s.length()), &sWide[0], cchWide);
721 return sWide;
724 static std::wstring StringMapCase(const std::wstring &ws, DWORD mapFlags) {
725 const int charsConverted = ::LCMapStringW(LOCALE_SYSTEM_DEFAULT, mapFlags,
726 ws.c_str(), static_cast<int>(ws.length()), NULL, 0);
727 std::wstring wsConverted(charsConverted, 0);
728 if (charsConverted) {
729 ::LCMapStringW(LOCALE_SYSTEM_DEFAULT, mapFlags,
730 ws.c_str(), static_cast<int>(ws.length()), &wsConverted[0], charsConverted);
732 return wsConverted;
735 // Returns the target converted to UTF8.
736 // Return the length in bytes.
737 Sci::Position ScintillaWin::TargetAsUTF8(char *text) {
738 Sci::Position targetLength = targetEnd - targetStart;
739 if (IsUnicodeMode()) {
740 if (text) {
741 pdoc->GetCharRange(text, targetStart, targetLength);
743 } else {
744 // Need to convert
745 const std::string s = RangeText(targetStart, targetEnd);
746 const std::wstring characters = StringDecode(s, CodePageOfDocument());
747 const int utf8Len = ::WideCharToMultiByte(CP_UTF8, 0, characters.c_str(), static_cast<int>(characters.length()), NULL, 0, 0, 0);
748 if (text) {
749 ::WideCharToMultiByte(CP_UTF8, 0, characters.c_str(), static_cast<int>(characters.length()), text, utf8Len, 0, 0);
750 text[utf8Len] = '\0';
752 return utf8Len;
754 return targetLength;
757 // Translates a nul terminated UTF8 string into the document encoding.
758 // Return the length of the result in bytes.
759 Sci::Position ScintillaWin::EncodedFromUTF8(char *utf8, char *encoded) const {
760 Sci::Position inputLength = (lengthForEncode >= 0) ? lengthForEncode : static_cast<Sci::Position>(strlen(utf8));
761 if (IsUnicodeMode()) {
762 if (encoded) {
763 memcpy(encoded, utf8, inputLength);
765 return inputLength;
766 } else {
767 // Need to convert
768 int charsLen = ::MultiByteToWideChar(CP_UTF8, 0, utf8, inputLength, NULL, 0);
769 std::wstring characters(charsLen, '\0');
770 ::MultiByteToWideChar(CP_UTF8, 0, utf8, inputLength, &characters[0], charsLen);
772 int encodedLen = ::WideCharToMultiByte(CodePageOfDocument(),
773 0, &characters[0], charsLen, NULL, 0, 0, 0);
774 if (encoded) {
775 ::WideCharToMultiByte(CodePageOfDocument(), 0, &characters[0], charsLen, encoded, encodedLen, 0, 0);
776 encoded[encodedLen] = '\0';
778 return encodedLen;
782 // Add one character from a UTF-16 string, by converting to either UTF-8 or
783 // the current codepage. Code is similar to HandleCompositionWindowed().
784 void ScintillaWin::AddCharUTF16(wchar_t const *wcs, unsigned int wclen) {
785 if (IsUnicodeMode()) {
786 char utfval[maxLenInputIME * 3];
787 size_t len = UTF8Length(wcs, wclen);
788 UTF8FromUTF16(wcs, wclen, utfval, len);
789 utfval[len] = '\0';
790 AddCharUTF(utfval, static_cast<unsigned int>(len));
791 } else {
792 UINT cpDest = CodePageOfDocument();
793 char inBufferCP[maxLenInputIME * 2];
794 const int size = ::WideCharToMultiByte(cpDest,
795 0, wcs, wclen, inBufferCP, sizeof(inBufferCP) - 1, 0, 0);
796 for (int i=0; i<size; i++) {
797 AddChar(inBufferCP[i]);
802 sptr_t ScintillaWin::WndPaint(uptr_t wParam) {
803 //ElapsedTime et;
805 // Redirect assertions to debug output and save current state
806 const bool assertsPopup = Platform::ShowAssertionPopUps(false);
807 paintState = painting;
808 PAINTSTRUCT ps;
809 PAINTSTRUCT *pps;
811 const bool IsOcxCtrl = (wParam != 0); // if wParam != 0, it contains
812 // a PAINSTRUCT* from the OCX
813 // Removed since this interferes with reporting other assertions as it occurs repeatedly
814 //PLATFORM_ASSERT(hRgnUpdate == NULL);
815 hRgnUpdate = ::CreateRectRgn(0, 0, 0, 0);
816 if (IsOcxCtrl) {
817 pps = reinterpret_cast<PAINTSTRUCT*>(wParam);
818 } else {
819 ::GetUpdateRgn(MainHWND(), hRgnUpdate, FALSE);
820 pps = &ps;
821 ::BeginPaint(MainHWND(), pps);
823 rcPaint = PRectangle::FromInts(pps->rcPaint.left, pps->rcPaint.top, pps->rcPaint.right, pps->rcPaint.bottom);
824 const PRectangle rcClient = GetClientRectangle();
825 paintingAllText = BoundsContains(rcPaint, hRgnUpdate, rcClient);
826 if (technology == SC_TECHNOLOGY_DEFAULT) {
827 AutoSurface surfaceWindow(pps->hdc, this);
828 if (surfaceWindow) {
829 Paint(surfaceWindow, rcPaint);
830 surfaceWindow->Release();
832 } else {
833 #if defined(USE_D2D)
834 EnsureRenderTarget(pps->hdc);
835 AutoSurface surfaceWindow(pRenderTarget, this);
836 if (surfaceWindow) {
837 pRenderTarget->BeginDraw();
838 Paint(surfaceWindow, rcPaint);
839 surfaceWindow->Release();
840 const HRESULT hr = pRenderTarget->EndDraw();
841 if (hr == static_cast<HRESULT>(D2DERR_RECREATE_TARGET)) {
842 DropRenderTarget();
843 paintState = paintAbandoned;
846 #endif
848 if (hRgnUpdate) {
849 ::DeleteRgn(hRgnUpdate);
850 hRgnUpdate = 0;
853 if (!IsOcxCtrl)
854 ::EndPaint(MainHWND(), pps);
855 if (paintState == paintAbandoned) {
856 // Painting area was insufficient to cover new styling or brace highlight positions
857 if (IsOcxCtrl) {
858 FullPaintDC(pps->hdc);
859 } else {
860 FullPaint();
863 paintState = notPainting;
865 // Restore debug output state
866 Platform::ShowAssertionPopUps(assertsPopup);
868 //Platform::DebugPrintf("Paint took %g\n", et.Duration());
869 return 0l;
872 sptr_t ScintillaWin::HandleCompositionWindowed(uptr_t wParam, sptr_t lParam) {
873 if (lParam & GCS_RESULTSTR) {
874 IMContext imc(MainHWND());
875 if (imc.hIMC) {
876 AddWString(imc.GetCompositionString(GCS_RESULTSTR));
878 // Set new position after converted
879 Point pos = PointMainCaret();
880 COMPOSITIONFORM CompForm;
881 CompForm.dwStyle = CFS_POINT;
882 CompForm.ptCurrentPos.x = static_cast<int>(pos.x);
883 CompForm.ptCurrentPos.y = static_cast<int>(pos.y);
884 ::ImmSetCompositionWindow(imc.hIMC, &CompForm);
886 return 0;
888 return ::DefWindowProc(MainHWND(), WM_IME_COMPOSITION, wParam, lParam);
891 bool ScintillaWin::KoreanIME() {
892 const int codePage = InputCodePage();
893 return codePage == 949 || codePage == 1361;
896 void ScintillaWin::MoveImeCarets(Sci::Position offset) {
897 // Move carets relatively by bytes.
898 for (size_t r=0; r<sel.Count(); r++) {
899 Sci::Position positionInsert = sel.Range(r).Start().Position();
900 sel.Range(r).caret.SetPosition(positionInsert + offset);
901 sel.Range(r).anchor.SetPosition(positionInsert + offset);
905 void ScintillaWin::DrawImeIndicator(int indicator, int len) {
906 // Emulate the visual style of IME characters with indicators.
907 // Draw an indicator on the character before caret by the character bytes of len
908 // so it should be called after addCharUTF().
909 // It does not affect caret positions.
910 if (indicator < 8 || indicator > INDIC_MAX) {
911 return;
913 pdoc->DecorationSetCurrentIndicator(indicator);
914 for (size_t r=0; r<sel.Count(); r++) {
915 Sci::Position positionInsert = sel.Range(r).Start().Position();
916 pdoc->DecorationFillRange(positionInsert - len, 1, len);
920 void ScintillaWin::SetCandidateWindowPos() {
921 IMContext imc(MainHWND());
922 if (imc.hIMC) {
923 Point pos = PointMainCaret();
924 CANDIDATEFORM CandForm;
925 CandForm.dwIndex = 0;
926 CandForm.dwStyle = CFS_CANDIDATEPOS;
927 CandForm.ptCurrentPos.x = static_cast<int>(pos.x);
928 CandForm.ptCurrentPos.y = static_cast<int>(pos.y + vs.lineHeight);
929 ::ImmSetCandidateWindow(imc.hIMC, &CandForm);
933 void ScintillaWin::SelectionToHangul() {
934 // Convert every hanja to hangul within the main range.
935 const Sci::Position selStart = sel.RangeMain().Start().Position();
936 const Sci::Position documentStrLen = sel.RangeMain().Length();
937 const Sci::Position selEnd = selStart + documentStrLen;
938 const Sci::Position utf16Len = pdoc->CountUTF16(selStart, selEnd);
940 if (utf16Len > 0) {
941 std::string documentStr(documentStrLen, '\0');
942 pdoc->GetCharRange(&documentStr[0], selStart, documentStrLen);
944 std::wstring uniStr = StringDecode(documentStr, CodePageOfDocument());
945 const int converted = HanjaDict::GetHangulOfHanja(&uniStr[0]);
946 documentStr = StringEncode(uniStr, CodePageOfDocument());
948 if (converted > 0) {
949 pdoc->BeginUndoAction();
950 ClearSelection();
951 InsertPaste(&documentStr[0], static_cast<int>(documentStr.size()));
952 pdoc->EndUndoAction();
957 void ScintillaWin::EscapeHanja() {
958 // The candidate box pops up to user to select a hanja.
959 // It comes into WM_IME_COMPOSITION with GCS_RESULTSTR.
960 // The existing hangul or hanja is replaced with it.
961 if (sel.Count() > 1) {
962 return; // Do not allow multi carets.
964 Sci::Position currentPos = CurrentPosition();
965 int oneCharLen = pdoc->LenChar(currentPos);
967 if (oneCharLen < 2) {
968 return; // No need to handle SBCS.
971 // ImmEscapeW() may overwrite uniChar[] with a null terminated string.
972 // So enlarge it enough to Maximum 4 as in UTF-8.
973 unsigned int const safeLength = UTF8MaxBytes+1;
974 std::string oneChar(safeLength, '\0');
975 pdoc->GetCharRange(&oneChar[0], currentPos, oneCharLen);
977 std::wstring uniChar = StringDecode(oneChar, CodePageOfDocument());
979 IMContext imc(MainHWND());
980 if (imc.hIMC) {
981 // Set the candidate box position since IME may show it.
982 SetCandidateWindowPos();
983 // IME_ESC_HANJA_MODE appears to receive the first character only.
984 if (ImmEscapeW(GetKeyboardLayout(0), imc.hIMC, IME_ESC_HANJA_MODE, &uniChar[0])) {
985 SetSelection(currentPos, currentPos + oneCharLen);
990 void ScintillaWin::ToggleHanja() {
991 // If selection, convert every hanja to hangul within the main range.
992 // If no selection, commit to IME.
993 if (sel.Count() > 1) {
994 return; // Do not allow multi carets.
997 if (sel.Empty()) {
998 EscapeHanja();
999 } else {
1000 SelectionToHangul();
1004 namespace {
1006 std::vector<int> MapImeIndicators(std::vector<BYTE> inputStyle) {
1007 std::vector<int> imeIndicator(inputStyle.size(), SC_INDICATOR_UNKNOWN);
1008 for (size_t i = 0; i < inputStyle.size(); i++) {
1009 switch (static_cast<int>(inputStyle.at(i))) {
1010 case ATTR_INPUT:
1011 imeIndicator[i] = SC_INDICATOR_INPUT;
1012 break;
1013 case ATTR_TARGET_NOTCONVERTED:
1014 case ATTR_TARGET_CONVERTED:
1015 imeIndicator[i] = SC_INDICATOR_TARGET;
1016 break;
1017 case ATTR_CONVERTED:
1018 imeIndicator[i] = SC_INDICATOR_CONVERTED;
1019 break;
1020 default:
1021 imeIndicator[i] = SC_INDICATOR_UNKNOWN;
1022 break;
1025 return imeIndicator;
1030 void ScintillaWin::AddWString(std::wstring wcs) {
1031 if (wcs.empty())
1032 return;
1034 const int codePage = CodePageOfDocument();
1035 for (size_t i = 0; i < wcs.size(); ) {
1036 const size_t ucWidth = UTF16CharLength(wcs[i]);
1037 const std::wstring uniChar(wcs, i, ucWidth);
1038 std::string docChar = StringEncode(uniChar, codePage);
1040 AddCharUTF(docChar.c_str(), static_cast<unsigned int>(docChar.size()));
1041 i += ucWidth;
1045 sptr_t ScintillaWin::HandleCompositionInline(uptr_t, sptr_t lParam) {
1046 // Copy & paste by johnsonj with a lot of helps of Neil.
1047 // Great thanks for my foreruners, jiniya and BLUEnLIVE.
1049 IMContext imc(MainHWND());
1050 if (!imc.hIMC)
1051 return 0;
1052 if (pdoc->IsReadOnly() || SelectionContainsProtected()) {
1053 ::ImmNotifyIME(imc.hIMC, NI_COMPOSITIONSTR, CPS_CANCEL, 0);
1054 return 0;
1057 bool initialCompose = false;
1058 if (pdoc->TentativeActive()) {
1059 pdoc->TentativeUndo();
1060 } else {
1061 // No tentative undo means start of this composition so
1062 // fill in any virtual spaces.
1063 initialCompose = true;
1066 view.imeCaretBlockOverride = false;
1068 if (lParam & GCS_COMPSTR) {
1069 const std::wstring wcs = imc.GetCompositionString(GCS_COMPSTR);
1070 if ((wcs.size() == 0) || (wcs.size() >= maxLenInputIME)) {
1071 ShowCaretAtCurrentPosition();
1072 return 0;
1075 if (initialCompose)
1076 ClearBeforeTentativeStart();
1077 pdoc->TentativeStart(); // TentativeActive from now on.
1079 std::vector<int> imeIndicator = MapImeIndicators(imc.GetImeAttributes());
1081 const bool tmpRecordingMacro = recordingMacro;
1082 recordingMacro = false;
1083 const int codePage = CodePageOfDocument();
1084 for (size_t i = 0; i < wcs.size(); ) {
1085 const size_t ucWidth = UTF16CharLength(wcs[i]);
1086 const std::wstring uniChar(wcs, i, ucWidth);
1087 std::string docChar = StringEncode(uniChar, codePage);
1089 AddCharUTF(docChar.c_str(), static_cast<unsigned int>(docChar.size()));
1091 DrawImeIndicator(imeIndicator[i], static_cast<unsigned int>(docChar.size()));
1092 i += ucWidth;
1094 recordingMacro = tmpRecordingMacro;
1096 // Move IME caret from current last position to imeCaretPos.
1097 const int imeEndToImeCaretU16 = imc.GetImeCaretPos() - static_cast<unsigned int>(wcs.size());
1098 Sci::Position imeCaretPosDoc = pdoc->GetRelativePositionUTF16(CurrentPosition(), imeEndToImeCaretU16);
1100 MoveImeCarets(- CurrentPosition() + imeCaretPosDoc);
1102 if (KoreanIME()) {
1103 view.imeCaretBlockOverride = true;
1105 } else if (lParam & GCS_RESULTSTR) {
1106 AddWString(imc.GetCompositionString(GCS_RESULTSTR));
1108 EnsureCaretVisible();
1109 SetCandidateWindowPos();
1110 ShowCaretAtCurrentPosition();
1111 return 0;
1114 // Translate message IDs from WM_* and EM_* to SCI_* so can partly emulate Windows Edit control
1115 static unsigned int SciMessageFromEM(unsigned int iMessage) {
1116 switch (iMessage) {
1117 case EM_CANPASTE: return SCI_CANPASTE;
1118 case EM_CANUNDO: return SCI_CANUNDO;
1119 case EM_EMPTYUNDOBUFFER: return SCI_EMPTYUNDOBUFFER;
1120 case EM_FINDTEXTEX: return SCI_FINDTEXT;
1121 case EM_FORMATRANGE: return SCI_FORMATRANGE;
1122 case EM_GETFIRSTVISIBLELINE: return SCI_GETFIRSTVISIBLELINE;
1123 case EM_GETLINECOUNT: return SCI_GETLINECOUNT;
1124 case EM_GETSELTEXT: return SCI_GETSELTEXT;
1125 case EM_GETTEXTRANGE: return SCI_GETTEXTRANGE;
1126 case EM_HIDESELECTION: return SCI_HIDESELECTION;
1127 case EM_LINEINDEX: return SCI_POSITIONFROMLINE;
1128 case EM_LINESCROLL: return SCI_LINESCROLL;
1129 case EM_REPLACESEL: return SCI_REPLACESEL;
1130 case EM_SCROLLCARET: return SCI_SCROLLCARET;
1131 case EM_SETREADONLY: return SCI_SETREADONLY;
1132 case WM_CLEAR: return SCI_CLEAR;
1133 case WM_COPY: return SCI_COPY;
1134 case WM_CUT: return SCI_CUT;
1135 case WM_SETTEXT: return SCI_SETTEXT;
1136 case WM_PASTE: return SCI_PASTE;
1137 case WM_UNDO: return SCI_UNDO;
1139 return iMessage;
1142 UINT CodePageFromCharSet(DWORD characterSet, UINT documentCodePage) {
1143 if (documentCodePage == SC_CP_UTF8) {
1144 return SC_CP_UTF8;
1146 switch (characterSet) {
1147 case SC_CHARSET_ANSI: return 1252;
1148 case SC_CHARSET_DEFAULT: return documentCodePage ? documentCodePage : 1252;
1149 case SC_CHARSET_BALTIC: return 1257;
1150 case SC_CHARSET_CHINESEBIG5: return 950;
1151 case SC_CHARSET_EASTEUROPE: return 1250;
1152 case SC_CHARSET_GB2312: return 936;
1153 case SC_CHARSET_GREEK: return 1253;
1154 case SC_CHARSET_HANGUL: return 949;
1155 case SC_CHARSET_MAC: return 10000;
1156 case SC_CHARSET_OEM: return 437;
1157 case SC_CHARSET_RUSSIAN: return 1251;
1158 case SC_CHARSET_SHIFTJIS: return 932;
1159 case SC_CHARSET_TURKISH: return 1254;
1160 case SC_CHARSET_JOHAB: return 1361;
1161 case SC_CHARSET_HEBREW: return 1255;
1162 case SC_CHARSET_ARABIC: return 1256;
1163 case SC_CHARSET_VIETNAMESE: return 1258;
1164 case SC_CHARSET_THAI: return 874;
1165 case SC_CHARSET_8859_15: return 28605;
1166 // Not supported
1167 case SC_CHARSET_CYRILLIC: return documentCodePage;
1168 case SC_CHARSET_SYMBOL: return documentCodePage;
1170 return documentCodePage;
1173 UINT ScintillaWin::CodePageOfDocument() const {
1174 return CodePageFromCharSet(vs.styles[STYLE_DEFAULT].characterSet, pdoc->dbcsCodePage);
1177 sptr_t ScintillaWin::GetTextLength() {
1178 if (pdoc->Length() == 0)
1179 return 0;
1180 std::vector<char> docBytes(pdoc->Length(), '\0');
1181 pdoc->GetCharRange(&docBytes[0], 0, pdoc->Length());
1182 if (IsUnicodeMode()) {
1183 return UTF16Length(&docBytes[0], docBytes.size());
1184 } else {
1185 return ::MultiByteToWideChar(CodePageOfDocument(), 0, &docBytes[0],
1186 static_cast<int>(docBytes.size()), NULL, 0);
1190 sptr_t ScintillaWin::GetText(uptr_t wParam, sptr_t lParam) {
1191 wchar_t *ptr = reinterpret_cast<wchar_t *>(lParam);
1192 if (pdoc->Length() == 0) {
1193 *ptr = L'\0';
1194 return 0;
1196 std::vector<char> docBytes(pdoc->Length(), '\0');
1197 pdoc->GetCharRange(&docBytes[0], 0, pdoc->Length());
1198 if (IsUnicodeMode()) {
1199 size_t lengthUTF16 = UTF16Length(&docBytes[0], static_cast<unsigned int>(docBytes.size()));
1200 if (lParam == 0)
1201 return lengthUTF16;
1202 if (wParam == 0)
1203 return 0;
1204 size_t uLen = UTF16FromUTF8(&docBytes[0], docBytes.size(),
1205 ptr, wParam - 1);
1206 ptr[uLen] = L'\0';
1207 return uLen;
1208 } else {
1209 // Not Unicode mode
1210 // Convert to Unicode using the current Scintilla code page
1211 const UINT cpSrc = CodePageOfDocument();
1212 int lengthUTF16 = ::MultiByteToWideChar(cpSrc, 0, &docBytes[0],
1213 static_cast<int>(docBytes.size()), NULL, 0);
1214 if (lengthUTF16 >= static_cast<int>(wParam))
1215 lengthUTF16 = static_cast<int>(wParam)-1;
1216 ::MultiByteToWideChar(cpSrc, 0, &docBytes[0],
1217 static_cast<int>(docBytes.size()),
1218 ptr, lengthUTF16);
1219 ptr[lengthUTF16] = L'\0';
1220 return lengthUTF16;
1224 sptr_t ScintillaWin::WndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
1225 try {
1226 //Platform::DebugPrintf("S M:%x WP:%x L:%x\n", iMessage, wParam, lParam);
1227 iMessage = SciMessageFromEM(iMessage);
1228 switch (iMessage) {
1230 case WM_CREATE:
1231 ctrlID = ::GetDlgCtrlID(static_cast<HWND>(wMain.GetID()));
1232 // Get Intellimouse scroll line parameters
1233 GetIntelliMouseParameters();
1234 ::RegisterDragDrop(MainHWND(), reinterpret_cast<IDropTarget *>(&dt));
1235 break;
1237 case WM_COMMAND:
1238 Command(LoWord(wParam));
1239 break;
1241 case WM_PAINT:
1242 return WndPaint(wParam);
1244 case WM_PRINTCLIENT: {
1245 HDC hdc = reinterpret_cast<HDC>(wParam);
1246 if (!IsCompatibleDC(hdc)) {
1247 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1249 FullPaintDC(hdc);
1251 break;
1253 case WM_VSCROLL:
1254 ScrollMessage(wParam);
1255 break;
1257 case WM_HSCROLL:
1258 HorizontalScrollMessage(wParam);
1259 break;
1261 case WM_SIZE: {
1262 #if defined(USE_D2D)
1263 if (paintState == notPainting) {
1264 DropRenderTarget();
1265 } else {
1266 renderTargetValid = false;
1268 #endif
1269 //Platform::DebugPrintf("Scintilla WM_SIZE %d %d\n", LoWord(lParam), HiWord(lParam));
1270 ChangeSize();
1272 break;
1274 case WM_MOUSEWHEEL:
1275 if (!mouseWheelCaptures) {
1276 // if the mouse wheel is not captured, test if the mouse
1277 // pointer is over the editor window and if not, don't
1278 // handle the message but pass it on.
1279 RECT rc;
1280 GetWindowRect(MainHWND(), &rc);
1281 POINT pt;
1282 pt.x = GET_X_LPARAM(lParam);
1283 pt.y = GET_Y_LPARAM(lParam);
1284 if (!PtInRect(&rc, pt))
1285 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1287 // if autocomplete list active then send mousewheel message to it
1288 if (ac.Active()) {
1289 HWND hWnd = static_cast<HWND>(ac.lb->GetID());
1290 ::SendMessage(hWnd, iMessage, wParam, lParam);
1291 break;
1294 // Don't handle datazoom.
1295 // (A good idea for datazoom would be to "fold" or "unfold" details.
1296 // i.e. if datazoomed out only class structures are visible, when datazooming in the control
1297 // structures appear, then eventually the individual statements...)
1298 if (wParam & MK_SHIFT) {
1299 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1302 // Either SCROLL or ZOOM. We handle the wheel steppings calculation
1303 wheelDelta -= static_cast<short>(HiWord(wParam));
1304 if (abs(wheelDelta) >= WHEEL_DELTA && linesPerScroll > 0) {
1305 Sci::Line linesToScroll = linesPerScroll;
1306 if (linesPerScroll == WHEEL_PAGESCROLL)
1307 linesToScroll = LinesOnScreen() - 1;
1308 if (linesToScroll == 0) {
1309 linesToScroll = 1;
1311 linesToScroll *= (wheelDelta / WHEEL_DELTA);
1312 if (wheelDelta >= 0)
1313 wheelDelta = wheelDelta % WHEEL_DELTA;
1314 else
1315 wheelDelta = - (-wheelDelta % WHEEL_DELTA);
1317 if (wParam & MK_CONTROL) {
1318 // Zoom! We play with the font sizes in the styles.
1319 // Number of steps/line is ignored, we just care if sizing up or down
1320 if (linesToScroll < 0) {
1321 KeyCommand(SCI_ZOOMIN);
1322 } else {
1323 KeyCommand(SCI_ZOOMOUT);
1325 } else {
1326 // Scroll
1327 ScrollTo(topLine + linesToScroll);
1330 return 0;
1332 case WM_TIMER:
1333 if (wParam == idleTimerID && idler.state) {
1334 SendMessage(MainHWND(), SC_WIN_IDLE, 0, 1);
1335 } else {
1336 TickFor(static_cast<TickReason>(wParam - fineTimerStart));
1338 break;
1340 case SC_WIN_IDLE:
1341 // wParam=dwTickCountInitial, or 0 to initialize. lParam=bSkipUserInputTest
1342 if (idler.state) {
1343 if (lParam || (WAIT_TIMEOUT == MsgWaitForMultipleObjects(0, 0, 0, 0, QS_INPUT|QS_HOTKEY))) {
1344 if (Idle()) {
1345 // User input was given priority above, but all events do get a turn. Other
1346 // messages, notifications, etc. will get interleaved with the idle messages.
1348 // However, some things like WM_PAINT are a lower priority, and will not fire
1349 // when there's a message posted. So, several times a second, we stop and let
1350 // the low priority events have a turn (after which the timer will fire again).
1352 // Suppress a warning from Code Analysis that the GetTickCount function
1353 // wraps after 49 days. The WM_TIMER will kick off another SC_WIN_IDLE
1354 // after the wrap.
1355 #ifdef _MSC_VER
1356 #pragma warning(suppress: 28159)
1357 #endif
1358 const DWORD dwCurrent = GetTickCount();
1359 const DWORD dwStart = wParam ? static_cast<DWORD>(wParam) : dwCurrent;
1360 const DWORD maxWorkTime = 50;
1362 if (dwCurrent >= dwStart && dwCurrent > maxWorkTime && dwCurrent - maxWorkTime < dwStart)
1363 PostMessage(MainHWND(), SC_WIN_IDLE, dwStart, 0);
1364 } else {
1365 SetIdle(false);
1369 break;
1371 case WM_GETMINMAXINFO:
1372 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1374 case WM_LBUTTONDOWN: {
1375 // For IME, set the composition string as the result string.
1376 IMContext imc(MainHWND());
1377 ::ImmNotifyIME(imc.hIMC, NI_COMPOSITIONSTR, CPS_COMPLETE, 0);
1379 //Platform::DebugPrintf("Buttdown %d %x %x %x %x %x\n",iMessage, wParam, lParam,
1380 // Platform::IsKeyDown(VK_SHIFT),
1381 // Platform::IsKeyDown(VK_CONTROL),
1382 // Platform::IsKeyDown(VK_MENU));
1383 ::SetFocus(MainHWND());
1384 ButtonDown(Point::FromLong(static_cast<long>(lParam)), ::GetMessageTime(),
1385 (wParam & MK_SHIFT) != 0,
1386 (wParam & MK_CONTROL) != 0,
1387 Platform::IsKeyDown(VK_MENU));
1389 break;
1391 case WM_MOUSEMOVE: {
1392 const Point pt = Point::FromLong(static_cast<long>(lParam));
1394 // Windows might send WM_MOUSEMOVE even though the mouse has not been moved:
1395 // http://blogs.msdn.com/b/oldnewthing/archive/2003/10/01/55108.aspx
1396 if (ptMouseLast.x != pt.x || ptMouseLast.y != pt.y) {
1397 SetTrackMouseLeaveEvent(true);
1398 ButtonMoveWithModifiers(pt,
1399 ((wParam & MK_SHIFT) != 0 ? SCI_SHIFT : 0) |
1400 ((wParam & MK_CONTROL) != 0 ? SCI_CTRL : 0) |
1401 (Platform::IsKeyDown(VK_MENU) ? SCI_ALT : 0));
1404 break;
1406 case WM_MOUSELEAVE:
1407 SetTrackMouseLeaveEvent(false);
1408 MouseLeave();
1409 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1411 case WM_LBUTTONUP:
1412 ButtonUp(Point::FromLong(static_cast<long>(lParam)),
1413 ::GetMessageTime(),
1414 (wParam & MK_CONTROL) != 0);
1415 break;
1417 case WM_RBUTTONDOWN: {
1418 ::SetFocus(MainHWND());
1419 Point pt = Point::FromLong(static_cast<long>(lParam));
1420 if (!PointInSelection(pt)) {
1421 CancelModes();
1422 SetEmptySelection(PositionFromLocation(Point::FromLong(static_cast<long>(lParam))));
1425 RightButtonDownWithModifiers(pt, ::GetMessageTime(), ModifierFlags((wParam & MK_SHIFT) != 0,
1426 (wParam & MK_CONTROL) != 0,
1427 Platform::IsKeyDown(VK_MENU)));
1429 break;
1431 case WM_SETCURSOR:
1432 if (LoWord(lParam) == HTCLIENT) {
1433 if (inDragDrop == ddDragging) {
1434 DisplayCursor(Window::cursorUp);
1435 } else {
1436 // Display regular (drag) cursor over selection
1437 POINT pt;
1438 if (0 != ::GetCursorPos(&pt)) {
1439 ::ScreenToClient(MainHWND(), &pt);
1440 if (PointInSelMargin(PointFromPOINT(pt))) {
1441 DisplayCursor(GetMarginCursor(PointFromPOINT(pt)));
1442 } else if (PointInSelection(PointFromPOINT(pt)) && !SelectionEmpty()) {
1443 DisplayCursor(Window::cursorArrow);
1444 } else if (PointIsHotspot(PointFromPOINT(pt))) {
1445 DisplayCursor(Window::cursorHand);
1446 } else {
1447 DisplayCursor(Window::cursorText);
1451 return TRUE;
1452 } else {
1453 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1456 case WM_CHAR:
1457 if (((wParam >= 128) || !iscntrl(static_cast<int>(wParam))) || !lastKeyDownConsumed) {
1458 wchar_t wcs[3] = {static_cast<wchar_t>(wParam), 0};
1459 unsigned int wclen = 1;
1460 if (IS_HIGH_SURROGATE(wcs[0])) {
1461 // If this is a high surrogate character, we need a second one
1462 lastHighSurrogateChar = wcs[0];
1463 return 0;
1464 } else if (IS_LOW_SURROGATE(wcs[0])) {
1465 wcs[1] = wcs[0];
1466 wcs[0] = lastHighSurrogateChar;
1467 lastHighSurrogateChar = 0;
1468 wclen = 2;
1470 AddCharUTF16(wcs, wclen);
1472 return 0;
1474 case WM_UNICHAR:
1475 if (wParam == UNICODE_NOCHAR) {
1476 return TRUE;
1477 } else if (lastKeyDownConsumed) {
1478 return 1;
1479 } else {
1480 wchar_t wcs[3] = {0};
1481 unsigned int wclen = UTF16FromUTF32Character(static_cast<unsigned int>(wParam), wcs);
1482 AddCharUTF16(wcs, wclen);
1483 return FALSE;
1486 case WM_SYSKEYDOWN:
1487 case WM_KEYDOWN: {
1488 //Platform::DebugPrintf("S keydown %d %x %x %x %x\n",iMessage, wParam, lParam, ::IsKeyDown(VK_SHIFT), ::IsKeyDown(VK_CONTROL));
1489 lastKeyDownConsumed = false;
1490 const int ret = KeyDown(KeyTranslate(static_cast<int>(wParam)),
1491 Platform::IsKeyDown(VK_SHIFT),
1492 Platform::IsKeyDown(VK_CONTROL),
1493 Platform::IsKeyDown(VK_MENU),
1494 &lastKeyDownConsumed);
1495 if (!ret && !lastKeyDownConsumed) {
1496 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1498 break;
1501 case WM_IME_KEYDOWN: {
1502 if (wParam == VK_HANJA) {
1503 ToggleHanja();
1505 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1508 case WM_IME_REQUEST: {
1509 if (wParam == IMR_RECONVERTSTRING) {
1510 return ImeOnReconvert(lParam);
1512 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1515 case WM_KEYUP:
1516 //Platform::DebugPrintf("S keyup %d %x %x\n",iMessage, wParam, lParam);
1517 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1519 case WM_SETTINGCHANGE:
1520 //Platform::DebugPrintf("Setting Changed\n");
1521 InvalidateStyleData();
1522 // Get Intellimouse scroll line parameters
1523 GetIntelliMouseParameters();
1524 break;
1526 case WM_GETDLGCODE:
1527 return DLGC_HASSETSEL | DLGC_WANTALLKEYS;
1529 case WM_KILLFOCUS: {
1530 HWND wOther = reinterpret_cast<HWND>(wParam);
1531 HWND wThis = MainHWND();
1532 const HWND wCT = static_cast<HWND>(ct.wCallTip.GetID());
1533 if (!wParam ||
1534 !(::IsChild(wThis, wOther) || (wOther == wCT))) {
1535 SetFocusState(false);
1536 DestroySystemCaret();
1538 // Explicitly complete any IME composition
1539 IMContext imc(MainHWND());
1540 if (imc.hIMC) {
1541 ::ImmNotifyIME(imc.hIMC, NI_COMPOSITIONSTR, CPS_COMPLETE, 0);
1544 break;
1546 case WM_SETFOCUS:
1547 SetFocusState(true);
1548 DestroySystemCaret();
1549 CreateSystemCaret();
1550 break;
1552 case WM_SYSCOLORCHANGE:
1553 //Platform::DebugPrintf("Setting Changed\n");
1554 InvalidateStyleData();
1555 break;
1557 case WM_IME_STARTCOMPOSITION: // dbcs
1558 if (KoreanIME() || imeInteraction == imeInline) {
1559 return 0;
1560 } else {
1561 ImeStartComposition();
1562 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1565 case WM_IME_ENDCOMPOSITION: // dbcs
1566 ImeEndComposition();
1567 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1569 case WM_IME_COMPOSITION:
1570 if (KoreanIME() || imeInteraction == imeInline) {
1571 return HandleCompositionInline(wParam, lParam);
1572 } else {
1573 return HandleCompositionWindowed(wParam, lParam);
1576 case WM_CONTEXTMENU: {
1577 Point pt = Point::FromLong(static_cast<long>(lParam));
1578 POINT rpt = {static_cast<int>(pt.x), static_cast<int>(pt.y)};
1579 ::ScreenToClient(MainHWND(), &rpt);
1580 const Point ptClient = PointFromPOINT(rpt);
1581 if (ShouldDisplayPopup(ptClient)) {
1582 if ((pt.x == -1) && (pt.y == -1)) {
1583 // Caused by keyboard so display menu near caret
1584 pt = PointMainCaret();
1585 POINT spt = {static_cast<int>(pt.x), static_cast<int>(pt.y)};
1586 ::ClientToScreen(MainHWND(), &spt);
1587 pt = PointFromPOINT(spt);
1589 ContextMenu(pt);
1590 return 0;
1593 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1595 case WM_INPUTLANGCHANGE:
1596 //::SetThreadLocale(LOWORD(lParam));
1597 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1599 case WM_INPUTLANGCHANGEREQUEST:
1600 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1602 case WM_ERASEBKGND:
1603 return 1; // Avoid any background erasure as whole window painted.
1605 case WM_CAPTURECHANGED:
1606 capturedMouse = false;
1607 return 0;
1609 case WM_IME_SETCONTEXT:
1610 if (KoreanIME() || imeInteraction == imeInline) {
1611 if (wParam) {
1612 LPARAM NoImeWin = lParam;
1613 NoImeWin = NoImeWin & (~ISC_SHOWUICOMPOSITIONWINDOW);
1614 return ::DefWindowProc(MainHWND(), iMessage, wParam, NoImeWin);
1617 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1619 // These are not handled in Scintilla and its faster to dispatch them here.
1620 // Also moves time out to here so profile doesn't count lots of empty message calls.
1622 case WM_MOVE:
1623 case WM_MOUSEACTIVATE:
1624 case WM_NCHITTEST:
1625 case WM_NCCALCSIZE:
1626 case WM_NCPAINT:
1627 case WM_NCMOUSEMOVE:
1628 case WM_NCLBUTTONDOWN:
1629 case WM_IME_NOTIFY:
1630 case WM_SYSCOMMAND:
1631 case WM_WINDOWPOSCHANGING:
1632 case WM_WINDOWPOSCHANGED:
1633 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1635 case WM_GETTEXTLENGTH:
1636 return GetTextLength();
1638 case WM_GETTEXT:
1639 return GetText(wParam, lParam);
1641 case EM_LINEFROMCHAR:
1642 if (static_cast<int>(wParam) < 0) {
1643 wParam = SelectionStart().Position();
1645 return pdoc->LineFromPosition(static_cast<int>(wParam));
1647 case EM_EXLINEFROMCHAR:
1648 return pdoc->LineFromPosition(static_cast<int>(lParam));
1650 case EM_GETSEL:
1651 if (wParam) {
1652 *reinterpret_cast<int *>(wParam) = SelectionStart().Position();
1654 if (lParam) {
1655 *reinterpret_cast<int *>(lParam) = SelectionEnd().Position();
1657 return MAKELONG(SelectionStart().Position(), SelectionEnd().Position());
1659 case EM_EXGETSEL: {
1660 if (lParam == 0) {
1661 return 0;
1663 Sci_CharacterRange *pCR = reinterpret_cast<Sci_CharacterRange *>(lParam);
1664 pCR->cpMin = SelectionStart().Position();
1665 pCR->cpMax = SelectionEnd().Position();
1667 break;
1669 case EM_SETSEL: {
1670 Sci::Position nStart = static_cast<Sci::Position>(wParam);
1671 Sci::Position nEnd = static_cast<Sci::Position>(lParam);
1672 if (nStart == 0 && nEnd == -1) {
1673 nEnd = pdoc->Length();
1675 if (nStart == -1) {
1676 nStart = nEnd; // Remove selection
1678 SetSelection(nEnd, nStart);
1679 EnsureCaretVisible();
1681 break;
1683 case EM_EXSETSEL: {
1684 if (lParam == 0) {
1685 return 0;
1687 Sci_CharacterRange *pCR = reinterpret_cast<Sci_CharacterRange *>(lParam);
1688 sel.selType = Selection::selStream;
1689 if (pCR->cpMin == 0 && pCR->cpMax == -1) {
1690 SetSelection(pCR->cpMin, pdoc->Length());
1691 } else {
1692 SetSelection(pCR->cpMin, pCR->cpMax);
1694 EnsureCaretVisible();
1695 return pdoc->LineFromPosition(SelectionStart().Position());
1698 case SCI_GETDIRECTFUNCTION:
1699 return reinterpret_cast<sptr_t>(DirectFunction);
1701 case SCI_GETDIRECTPOINTER:
1702 return reinterpret_cast<sptr_t>(this);
1704 case SCI_GRABFOCUS:
1705 ::SetFocus(MainHWND());
1706 break;
1708 #ifdef INCLUDE_DEPRECATED_FEATURES
1709 case SCI_SETKEYSUNICODE:
1710 break;
1712 case SCI_GETKEYSUNICODE:
1713 return true;
1714 #endif
1716 case SCI_SETTECHNOLOGY:
1717 if ((wParam == SC_TECHNOLOGY_DEFAULT) ||
1718 (wParam == SC_TECHNOLOGY_DIRECTWRITERETAIN) ||
1719 (wParam == SC_TECHNOLOGY_DIRECTWRITEDC) ||
1720 (wParam == SC_TECHNOLOGY_DIRECTWRITE)) {
1721 if (technology != static_cast<int>(wParam)) {
1722 if (static_cast<int>(wParam) > SC_TECHNOLOGY_DEFAULT) {
1723 #if defined(USE_D2D)
1724 if (!LoadD2D())
1725 // Failed to load Direct2D or DirectWrite so no effect
1726 return 0;
1727 #else
1728 return 0;
1729 #endif
1731 #if defined(USE_D2D)
1732 DropRenderTarget();
1733 #endif
1734 technology = static_cast<int>(wParam);
1735 // Invalidate all cached information including layout.
1736 DropGraphics(true);
1737 InvalidateStyleRedraw();
1740 break;
1742 #ifdef SCI_LEXER
1743 case SCI_LOADLEXERLIBRARY:
1744 LexerManager::GetInstance()->Load(reinterpret_cast<const char *>(lParam));
1745 break;
1746 #endif
1748 case SCI_TARGETASUTF8:
1749 return TargetAsUTF8(reinterpret_cast<char*>(lParam));
1751 case SCI_ENCODEDFROMUTF8:
1752 return EncodedFromUTF8(reinterpret_cast<char*>(wParam),
1753 reinterpret_cast<char*>(lParam));
1755 default:
1756 return ScintillaBase::WndProc(iMessage, wParam, lParam);
1758 } catch (std::bad_alloc &) {
1759 errorStatus = SC_STATUS_BADALLOC;
1760 } catch (...) {
1761 errorStatus = SC_STATUS_FAILURE;
1763 return 0l;
1766 bool ScintillaWin::ValidCodePage(int codePage) const {
1767 return codePage == 0 || codePage == SC_CP_UTF8 ||
1768 codePage == 932 || codePage == 936 || codePage == 949 ||
1769 codePage == 950 || codePage == 1361;
1772 sptr_t ScintillaWin::DefWndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
1773 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1777 * Report that this Editor subclass has a working implementation of FineTickerStart.
1779 bool ScintillaWin::FineTickerAvailable() {
1780 return true;
1783 bool ScintillaWin::FineTickerRunning(TickReason reason) {
1784 return timers[reason] != 0;
1787 void ScintillaWin::FineTickerStart(TickReason reason, int millis, int tolerance) {
1788 FineTickerCancel(reason);
1789 if (SetCoalescableTimerFn && tolerance) {
1790 timers[reason] = SetCoalescableTimerFn(MainHWND(), fineTimerStart + reason, millis, NULL, tolerance);
1791 } else {
1792 timers[reason] = ::SetTimer(MainHWND(), fineTimerStart + reason, millis, NULL);
1796 void ScintillaWin::FineTickerCancel(TickReason reason) {
1797 if (timers[reason]) {
1798 ::KillTimer(MainHWND(), timers[reason]);
1799 timers[reason] = 0;
1804 bool ScintillaWin::SetIdle(bool on) {
1805 // On Win32 the Idler is implemented as a Timer on the Scintilla window. This
1806 // takes advantage of the fact that WM_TIMER messages are very low priority,
1807 // and are only posted when the message queue is empty, i.e. during idle time.
1808 if (idler.state != on) {
1809 if (on) {
1810 idler.idlerID = ::SetTimer(MainHWND(), idleTimerID, 10, NULL)
1811 ? reinterpret_cast<IdlerID>(idleTimerID) : 0;
1812 } else {
1813 ::KillTimer(MainHWND(), reinterpret_cast<uptr_t>(idler.idlerID));
1814 idler.idlerID = 0;
1816 idler.state = idler.idlerID != 0;
1818 return idler.state;
1821 void ScintillaWin::SetMouseCapture(bool on) {
1822 if (mouseDownCaptures) {
1823 if (on) {
1824 ::SetCapture(MainHWND());
1825 } else {
1826 ::ReleaseCapture();
1829 capturedMouse = on;
1832 bool ScintillaWin::HaveMouseCapture() {
1833 // Cannot just see if GetCapture is this window as the scroll bar also sets capture for the window
1834 return capturedMouse;
1835 //return capturedMouse && (::GetCapture() == MainHWND());
1838 void ScintillaWin::SetTrackMouseLeaveEvent(bool on) {
1839 if (on && !trackedMouseLeave) {
1840 TRACKMOUSEEVENT tme;
1841 tme.cbSize = sizeof(tme);
1842 tme.dwFlags = TME_LEAVE;
1843 tme.hwndTrack = MainHWND();
1844 tme.dwHoverTime = HOVER_DEFAULT; // Unused but triggers Dr. Memory if not initialized
1845 TrackMouseEvent(&tme);
1847 trackedMouseLeave = on;
1850 bool ScintillaWin::PaintContains(PRectangle rc) {
1851 if (paintState == painting) {
1852 return BoundsContains(rcPaint, hRgnUpdate, rc);
1854 return true;
1857 void ScintillaWin::ScrollText(Sci::Line /* linesToMove */) {
1858 //Platform::DebugPrintf("ScintillaWin::ScrollText %d\n", linesToMove);
1859 //::ScrollWindow(MainHWND(), 0,
1860 // vs.lineHeight * linesToMove, 0, 0);
1861 //::UpdateWindow(MainHWND());
1862 Redraw();
1863 UpdateSystemCaret();
1866 void ScintillaWin::NotifyCaretMove() {
1867 NotifyWinEvent(EVENT_OBJECT_LOCATIONCHANGE, MainHWND(), OBJID_CARET, CHILDID_SELF);
1870 void ScintillaWin::UpdateSystemCaret() {
1871 if (hasFocus) {
1872 if (HasCaretSizeChanged()) {
1873 DestroySystemCaret();
1874 CreateSystemCaret();
1876 Point pos = PointMainCaret();
1877 ::SetCaretPos(static_cast<int>(pos.x), static_cast<int>(pos.y));
1881 int ScintillaWin::SetScrollInfo(int nBar, LPCSCROLLINFO lpsi, BOOL bRedraw) {
1882 return ::SetScrollInfo(MainHWND(), nBar, lpsi, bRedraw);
1885 bool ScintillaWin::GetScrollInfo(int nBar, LPSCROLLINFO lpsi) {
1886 return ::GetScrollInfo(MainHWND(), nBar, lpsi) ? true : false;
1889 // Change the scroll position but avoid repaint if changing to same value
1890 void ScintillaWin::ChangeScrollPos(int barType, Sci::Position pos) {
1891 SCROLLINFO sci = {
1892 sizeof(sci), 0, 0, 0, 0, 0, 0
1894 sci.fMask = SIF_POS;
1895 GetScrollInfo(barType, &sci);
1896 if (sci.nPos != pos) {
1897 DwellEnd(true);
1898 sci.nPos = pos;
1899 SetScrollInfo(barType, &sci, TRUE);
1903 void ScintillaWin::SetVerticalScrollPos() {
1904 ChangeScrollPos(SB_VERT, topLine);
1907 void ScintillaWin::SetHorizontalScrollPos() {
1908 ChangeScrollPos(SB_HORZ, xOffset);
1911 bool ScintillaWin::ModifyScrollBars(Sci::Line nMax, Sci::Line nPage) {
1912 bool modified = false;
1913 SCROLLINFO sci = {
1914 sizeof(sci), 0, 0, 0, 0, 0, 0
1916 sci.fMask = SIF_PAGE | SIF_RANGE;
1917 GetScrollInfo(SB_VERT, &sci);
1918 Sci::Line vertEndPreferred = nMax;
1919 if (!verticalScrollBarVisible)
1920 nPage = vertEndPreferred + 1;
1921 if ((sci.nMin != 0) ||
1922 (sci.nMax != vertEndPreferred) ||
1923 (sci.nPage != static_cast<unsigned int>(nPage)) ||
1924 (sci.nPos != 0)) {
1925 sci.fMask = SIF_PAGE | SIF_RANGE;
1926 sci.nMin = 0;
1927 sci.nMax = vertEndPreferred;
1928 sci.nPage = nPage;
1929 sci.nPos = 0;
1930 sci.nTrackPos = 1;
1931 SetScrollInfo(SB_VERT, &sci, TRUE);
1932 modified = true;
1935 const PRectangle rcText = GetTextRectangle();
1936 int horizEndPreferred = scrollWidth;
1937 if (horizEndPreferred < 0)
1938 horizEndPreferred = 0;
1939 int pageWidth = static_cast<int>(rcText.Width());
1940 if (!horizontalScrollBarVisible || Wrapping())
1941 pageWidth = horizEndPreferred + 1;
1942 sci.fMask = SIF_PAGE | SIF_RANGE;
1943 GetScrollInfo(SB_HORZ, &sci);
1944 if ((sci.nMin != 0) ||
1945 (sci.nMax != horizEndPreferred) ||
1946 (sci.nPage != static_cast<unsigned int>(pageWidth)) ||
1947 (sci.nPos != 0)) {
1948 sci.fMask = SIF_PAGE | SIF_RANGE;
1949 sci.nMin = 0;
1950 sci.nMax = horizEndPreferred;
1951 sci.nPage = pageWidth;
1952 sci.nPos = 0;
1953 sci.nTrackPos = 1;
1954 SetScrollInfo(SB_HORZ, &sci, TRUE);
1955 modified = true;
1956 if (scrollWidth < pageWidth) {
1957 HorizontalScrollTo(0);
1960 return modified;
1963 void ScintillaWin::NotifyChange() {
1964 ::SendMessage(::GetParent(MainHWND()), WM_COMMAND,
1965 MAKELONG(GetCtrlID(), SCEN_CHANGE),
1966 reinterpret_cast<LPARAM>(MainHWND()));
1969 void ScintillaWin::NotifyFocus(bool focus) {
1970 ::SendMessage(::GetParent(MainHWND()), WM_COMMAND,
1971 MAKELONG(GetCtrlID(), focus ? SCEN_SETFOCUS : SCEN_KILLFOCUS),
1972 reinterpret_cast<LPARAM>(MainHWND()));
1973 Editor::NotifyFocus(focus);
1976 void ScintillaWin::SetCtrlID(int identifier) {
1977 ::SetWindowID(static_cast<HWND>(wMain.GetID()), identifier);
1980 int ScintillaWin::GetCtrlID() {
1981 return ::GetDlgCtrlID(static_cast<HWND>(wMain.GetID()));
1984 void ScintillaWin::NotifyParent(SCNotification scn) {
1985 scn.nmhdr.hwndFrom = MainHWND();
1986 scn.nmhdr.idFrom = GetCtrlID();
1987 ::SendMessage(::GetParent(MainHWND()), WM_NOTIFY,
1988 GetCtrlID(), reinterpret_cast<LPARAM>(&scn));
1991 void ScintillaWin::NotifyParent(SCNotification* scn) {
1992 scn->nmhdr.hwndFrom = MainHWND();
1993 scn->nmhdr.idFrom = GetCtrlID();
1994 ::SendMessage(::GetParent(MainHWND()), WM_NOTIFY,
1995 GetCtrlID(), reinterpret_cast<LPARAM>(scn));
1998 void ScintillaWin::NotifyDoubleClick(Point pt, int modifiers) {
1999 //Platform::DebugPrintf("ScintillaWin Double click 0\n");
2000 ScintillaBase::NotifyDoubleClick(pt, modifiers);
2001 // Send myself a WM_LBUTTONDBLCLK, so the container can handle it too.
2002 ::SendMessage(MainHWND(),
2003 WM_LBUTTONDBLCLK,
2004 (modifiers & SCI_SHIFT) ? MK_SHIFT : 0,
2005 MAKELPARAM(pt.x, pt.y));
2008 class CaseFolderDBCS : public CaseFolderTable {
2009 // Allocate the expandable storage here so that it does not need to be reallocated
2010 // for each call to Fold.
2011 std::vector<wchar_t> utf16Mixed;
2012 std::vector<wchar_t> utf16Folded;
2013 UINT cp;
2014 public:
2015 explicit CaseFolderDBCS(UINT cp_) : cp(cp_) {
2016 StandardASCII();
2018 size_t Fold(char *folded, size_t sizeFolded, const char *mixed, size_t lenMixed) override {
2019 if ((lenMixed == 1) && (sizeFolded > 0)) {
2020 folded[0] = mapping[static_cast<unsigned char>(mixed[0])];
2021 return 1;
2022 } else {
2023 if (lenMixed > utf16Mixed.size()) {
2024 utf16Mixed.resize(lenMixed + 8);
2026 const size_t nUtf16Mixed = ::MultiByteToWideChar(cp, 0, mixed,
2027 static_cast<int>(lenMixed),
2028 &utf16Mixed[0],
2029 static_cast<int>(utf16Mixed.size()));
2031 if (nUtf16Mixed == 0) {
2032 // Failed to convert -> bad input
2033 folded[0] = '\0';
2034 return 1;
2037 unsigned int lenFlat = 0;
2038 for (size_t mixIndex=0; mixIndex < nUtf16Mixed; mixIndex++) {
2039 if ((lenFlat + 20) > utf16Folded.size())
2040 utf16Folded.resize(lenFlat + 60);
2041 const char *foldedUTF8 = CaseConvert(utf16Mixed[mixIndex], CaseConversionFold);
2042 if (foldedUTF8) {
2043 // Maximum length of a case conversion is 6 bytes, 3 characters
2044 wchar_t wFolded[20];
2045 const size_t charsConverted = UTF16FromUTF8(foldedUTF8,
2046 strlen(foldedUTF8),
2047 wFolded, ELEMENTS(wFolded));
2048 for (size_t j=0; j<charsConverted; j++)
2049 utf16Folded[lenFlat++] = wFolded[j];
2050 } else {
2051 utf16Folded[lenFlat++] = utf16Mixed[mixIndex];
2055 size_t lenOut = ::WideCharToMultiByte(cp, 0,
2056 &utf16Folded[0], lenFlat,
2057 NULL, 0, NULL, 0);
2059 if (lenOut < sizeFolded) {
2060 ::WideCharToMultiByte(cp, 0,
2061 &utf16Folded[0], lenFlat,
2062 folded, static_cast<int>(lenOut), NULL, 0);
2063 return lenOut;
2064 } else {
2065 return 0;
2071 CaseFolder *ScintillaWin::CaseFolderForEncoding() {
2072 UINT cpDest = CodePageOfDocument();
2073 if (cpDest == SC_CP_UTF8) {
2074 return new CaseFolderUnicode();
2075 } else {
2076 if (pdoc->dbcsCodePage == 0) {
2077 CaseFolderTable *pcf = new CaseFolderTable();
2078 pcf->StandardASCII();
2079 // Only for single byte encodings
2080 UINT cpDoc = CodePageOfDocument();
2081 for (int i=0x80; i<0x100; i++) {
2082 char sCharacter[2] = "A";
2083 sCharacter[0] = static_cast<char>(i);
2084 wchar_t wCharacter[20];
2085 const unsigned int lengthUTF16 = ::MultiByteToWideChar(cpDoc, 0, sCharacter, 1,
2086 wCharacter, ELEMENTS(wCharacter));
2087 if (lengthUTF16 == 1) {
2088 const char *caseFolded = CaseConvert(wCharacter[0], CaseConversionFold);
2089 if (caseFolded) {
2090 wchar_t wLower[20];
2091 const size_t charsConverted = UTF16FromUTF8(caseFolded,
2092 strlen(caseFolded),
2093 wLower, ELEMENTS(wLower));
2094 if (charsConverted == 1) {
2095 char sCharacterLowered[20];
2096 const unsigned int lengthConverted = ::WideCharToMultiByte(cpDoc, 0,
2097 wLower, static_cast<int>(charsConverted),
2098 sCharacterLowered, ELEMENTS(sCharacterLowered), NULL, 0);
2099 if ((lengthConverted == 1) && (sCharacter[0] != sCharacterLowered[0])) {
2100 pcf->SetTranslation(sCharacter[0], sCharacterLowered[0]);
2106 return pcf;
2107 } else {
2108 return new CaseFolderDBCS(cpDest);
2113 std::string ScintillaWin::CaseMapString(const std::string &s, int caseMapping) {
2114 if ((s.size() == 0) || (caseMapping == cmSame))
2115 return s;
2117 const UINT cpDoc = CodePageOfDocument();
2118 if (cpDoc == SC_CP_UTF8) {
2119 return CaseConvertString(s, (caseMapping == cmUpper) ? CaseConversionUpper : CaseConversionLower);
2122 // Change text to UTF-16
2123 const std::wstring wsText = StringDecode(s, cpDoc);
2125 const DWORD mapFlags = LCMAP_LINGUISTIC_CASING |
2126 ((caseMapping == cmUpper) ? LCMAP_UPPERCASE : LCMAP_LOWERCASE);
2128 // Change case
2129 const std::wstring wsConverted = StringMapCase(wsText, mapFlags);
2131 // Change back to document encoding
2132 std::string sConverted = StringEncode(wsConverted, cpDoc);
2134 return sConverted;
2137 void ScintillaWin::Copy() {
2138 //Platform::DebugPrintf("Copy\n");
2139 if (!sel.Empty()) {
2140 SelectionText selectedText;
2141 CopySelectionRange(&selectedText);
2142 CopyToClipboard(selectedText);
2146 void ScintillaWin::CopyAllowLine() {
2147 SelectionText selectedText;
2148 CopySelectionRange(&selectedText, true);
2149 CopyToClipboard(selectedText);
2152 bool ScintillaWin::CanPaste() {
2153 if (!Editor::CanPaste())
2154 return false;
2155 if (::IsClipboardFormatAvailable(CF_TEXT))
2156 return true;
2157 if (IsUnicodeMode())
2158 return ::IsClipboardFormatAvailable(CF_UNICODETEXT) != 0;
2159 return false;
2162 class GlobalMemory {
2163 HGLOBAL hand;
2164 public:
2165 void *ptr;
2166 GlobalMemory() : hand(0), ptr(0) {
2168 explicit GlobalMemory(HGLOBAL hand_) : hand(hand_), ptr(0) {
2169 if (hand) {
2170 ptr = ::GlobalLock(hand);
2173 ~GlobalMemory() {
2174 PLATFORM_ASSERT(!ptr);
2175 assert(!hand);
2177 void Allocate(size_t bytes) {
2178 assert(!hand);
2179 hand = ::GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT, bytes);
2180 if (hand) {
2181 ptr = ::GlobalLock(hand);
2184 HGLOBAL Unlock() {
2185 PLATFORM_ASSERT(ptr);
2186 HGLOBAL handCopy = hand;
2187 ::GlobalUnlock(hand);
2188 ptr = 0;
2189 hand = 0;
2190 return handCopy;
2192 void SetClip(UINT uFormat) {
2193 ::SetClipboardData(uFormat, Unlock());
2195 operator bool() const {
2196 return ptr != 0;
2198 SIZE_T Size() {
2199 return ::GlobalSize(hand);
2203 // OpenClipboard may fail if another application has opened the clipboard.
2204 // Try up to 8 times, with an initial delay of 1 ms and an exponential back off
2205 // for a maximum total delay of 127 ms (1+2+4+8+16+32+64).
2206 static bool OpenClipboardRetry(HWND hwnd) {
2207 for (int attempt=0; attempt<8; attempt++) {
2208 if (attempt > 0) {
2209 ::Sleep(1 << (attempt-1));
2211 if (::OpenClipboard(hwnd)) {
2212 return true;
2215 return false;
2218 void ScintillaWin::Paste() {
2219 if (!::OpenClipboardRetry(MainHWND())) {
2220 return;
2222 UndoGroup ug(pdoc);
2223 const bool isLine = SelectionEmpty() &&
2224 (::IsClipboardFormatAvailable(cfLineSelect) || ::IsClipboardFormatAvailable(cfVSLineTag));
2225 ClearSelection(multiPasteMode == SC_MULTIPASTE_EACH);
2226 bool isRectangular = (::IsClipboardFormatAvailable(cfColumnSelect) != 0);
2228 if (!isRectangular) {
2229 // Evaluate "Borland IDE Block Type" explicitly
2230 GlobalMemory memBorlandSelection(::GetClipboardData(cfBorlandIDEBlockType));
2231 if (memBorlandSelection) {
2232 isRectangular = (memBorlandSelection.Size() == 1) && (static_cast<BYTE *>(memBorlandSelection.ptr)[0] == 0x02);
2233 memBorlandSelection.Unlock();
2236 const PasteShape pasteShape = isRectangular ? pasteRectangular : (isLine ? pasteLine : pasteStream);
2238 // Always use CF_UNICODETEXT if available
2239 GlobalMemory memUSelection(::GetClipboardData(CF_UNICODETEXT));
2240 if (memUSelection) {
2241 wchar_t *uptr = static_cast<wchar_t *>(memUSelection.ptr);
2242 if (uptr) {
2243 size_t len;
2244 std::vector<char> putf;
2245 // Default Scintilla behaviour in Unicode mode
2246 if (IsUnicodeMode()) {
2247 const size_t bytes = memUSelection.Size();
2248 len = UTF8Length(uptr, bytes / 2);
2249 putf.resize(len + 1);
2250 UTF8FromUTF16(uptr, bytes / 2, &putf[0], len);
2251 } else {
2252 // CF_UNICODETEXT available, but not in Unicode mode
2253 // Convert from Unicode to current Scintilla code page
2254 UINT cpDest = CodePageOfDocument();
2255 len = ::WideCharToMultiByte(cpDest, 0, uptr, -1,
2256 NULL, 0, NULL, NULL) - 1; // subtract 0 terminator
2257 putf.resize(len + 1);
2258 ::WideCharToMultiByte(cpDest, 0, uptr, -1,
2259 &putf[0], static_cast<int>(len) + 1, NULL, NULL);
2262 InsertPasteShape(&putf[0], static_cast<int>(len), pasteShape);
2264 memUSelection.Unlock();
2265 } else {
2266 // CF_UNICODETEXT not available, paste ANSI text
2267 GlobalMemory memSelection(::GetClipboardData(CF_TEXT));
2268 if (memSelection) {
2269 char *ptr = static_cast<char *>(memSelection.ptr);
2270 if (ptr) {
2271 const size_t bytes = memSelection.Size();
2272 size_t len = bytes;
2273 for (size_t i = 0; i < bytes; i++) {
2274 if ((len == bytes) && (0 == ptr[i]))
2275 len = i;
2277 const int ilen = static_cast<int>(len);
2279 // In Unicode mode, convert clipboard text to UTF-8
2280 if (IsUnicodeMode()) {
2281 std::vector<wchar_t> uptr(len+1);
2283 const size_t ulen = ::MultiByteToWideChar(CP_ACP, 0,
2284 ptr, ilen, &uptr[0], ilen +1);
2286 const size_t mlen = UTF8Length(&uptr[0], ulen);
2287 std::vector<char> putf(mlen+1);
2288 UTF8FromUTF16(&uptr[0], ulen, &putf[0], mlen);
2290 InsertPasteShape(&putf[0], static_cast<int>(mlen), pasteShape);
2291 } else {
2292 InsertPasteShape(ptr, ilen, pasteShape);
2295 memSelection.Unlock();
2298 ::CloseClipboard();
2299 Redraw();
2302 void ScintillaWin::CreateCallTipWindow(PRectangle) {
2303 if (!ct.wCallTip.Created()) {
2304 ct.wCallTip = ::CreateWindow(callClassName, TEXT("ACallTip"),
2305 WS_POPUP, 100, 100, 150, 20,
2306 MainHWND(), 0,
2307 GetWindowInstance(MainHWND()),
2308 this);
2309 ct.wDraw = ct.wCallTip;
2313 void ScintillaWin::AddToPopUp(const char *label, int cmd, bool enabled) {
2314 HMENU hmenuPopup = static_cast<HMENU>(popup.GetID());
2315 if (!label[0])
2316 ::AppendMenuA(hmenuPopup, MF_SEPARATOR, 0, "");
2317 else if (enabled)
2318 ::AppendMenuA(hmenuPopup, MF_STRING, cmd, label);
2319 else
2320 ::AppendMenuA(hmenuPopup, MF_STRING | MF_DISABLED | MF_GRAYED, cmd, label);
2323 void ScintillaWin::ClaimSelection() {
2324 // Windows does not have a primary selection
2327 /// Implement IUnknown
2329 STDMETHODIMP_(ULONG)FormatEnumerator_AddRef(FormatEnumerator *fe);
2330 STDMETHODIMP FormatEnumerator_QueryInterface(FormatEnumerator *fe, REFIID riid, PVOID *ppv) {
2331 //Platform::DebugPrintf("EFE QI");
2332 *ppv = NULL;
2333 if (riid == IID_IUnknown)
2334 *ppv = reinterpret_cast<IEnumFORMATETC *>(fe);
2335 if (riid == IID_IEnumFORMATETC)
2336 *ppv = reinterpret_cast<IEnumFORMATETC *>(fe);
2337 if (!*ppv)
2338 return E_NOINTERFACE;
2339 FormatEnumerator_AddRef(fe);
2340 return S_OK;
2342 STDMETHODIMP_(ULONG)FormatEnumerator_AddRef(FormatEnumerator *fe) {
2343 return ++fe->ref;
2345 STDMETHODIMP_(ULONG)FormatEnumerator_Release(FormatEnumerator *fe) {
2346 fe->ref--;
2347 if (fe->ref > 0)
2348 return fe->ref;
2349 delete fe;
2350 return 0;
2352 /// Implement IEnumFORMATETC
2353 STDMETHODIMP FormatEnumerator_Next(FormatEnumerator *fe, ULONG celt, FORMATETC *rgelt, ULONG *pceltFetched) {
2354 if (rgelt == NULL) return E_POINTER;
2355 unsigned int putPos = 0;
2356 while ((fe->pos < fe->formats.size()) && (putPos < celt)) {
2357 rgelt->cfFormat = fe->formats[fe->pos];
2358 rgelt->ptd = 0;
2359 rgelt->dwAspect = DVASPECT_CONTENT;
2360 rgelt->lindex = -1;
2361 rgelt->tymed = TYMED_HGLOBAL;
2362 rgelt++;
2363 fe->pos++;
2364 putPos++;
2366 if (pceltFetched)
2367 *pceltFetched = putPos;
2368 return putPos ? S_OK : S_FALSE;
2370 STDMETHODIMP FormatEnumerator_Skip(FormatEnumerator *fe, ULONG celt) {
2371 fe->pos += celt;
2372 return S_OK;
2374 STDMETHODIMP FormatEnumerator_Reset(FormatEnumerator *fe) {
2375 fe->pos = 0;
2376 return S_OK;
2378 STDMETHODIMP FormatEnumerator_Clone(FormatEnumerator *fe, IEnumFORMATETC **ppenum) {
2379 FormatEnumerator *pfe;
2380 try {
2381 pfe = new FormatEnumerator(fe->pos, &fe->formats[0], fe->formats.size());
2382 } catch (...) {
2383 return E_OUTOFMEMORY;
2385 return FormatEnumerator_QueryInterface(pfe, IID_IEnumFORMATETC,
2386 reinterpret_cast<void **>(ppenum));
2389 static VFunction *vtFormatEnumerator[] = {
2390 (VFunction *)(FormatEnumerator_QueryInterface),
2391 (VFunction *)(FormatEnumerator_AddRef),
2392 (VFunction *)(FormatEnumerator_Release),
2393 (VFunction *)(FormatEnumerator_Next),
2394 (VFunction *)(FormatEnumerator_Skip),
2395 (VFunction *)(FormatEnumerator_Reset),
2396 (VFunction *)(FormatEnumerator_Clone)
2399 FormatEnumerator::FormatEnumerator(int pos_, CLIPFORMAT formats_[], size_t formatsLen_) {
2400 vtbl = vtFormatEnumerator;
2401 ref = 0; // First QI adds first reference...
2402 pos = pos_;
2403 formats.insert(formats.begin(), formats_, formats_+formatsLen_);
2406 /// Implement IUnknown
2407 STDMETHODIMP DropSource_QueryInterface(DropSource *ds, REFIID riid, PVOID *ppv) {
2408 return ds->sci->QueryInterface(riid, ppv);
2410 STDMETHODIMP_(ULONG)DropSource_AddRef(DropSource *ds) {
2411 return ds->sci->AddRef();
2413 STDMETHODIMP_(ULONG)DropSource_Release(DropSource *ds) {
2414 return ds->sci->Release();
2417 /// Implement IDropSource
2418 STDMETHODIMP DropSource_QueryContinueDrag(DropSource *, BOOL fEsc, DWORD grfKeyState) {
2419 if (fEsc)
2420 return DRAGDROP_S_CANCEL;
2421 if (!(grfKeyState & MK_LBUTTON))
2422 return DRAGDROP_S_DROP;
2423 return S_OK;
2426 STDMETHODIMP DropSource_GiveFeedback(DropSource *, DWORD) {
2427 return DRAGDROP_S_USEDEFAULTCURSORS;
2430 static VFunction *vtDropSource[] = {
2431 (VFunction *)(DropSource_QueryInterface),
2432 (VFunction *)(DropSource_AddRef),
2433 (VFunction *)(DropSource_Release),
2434 (VFunction *)(DropSource_QueryContinueDrag),
2435 (VFunction *)(DropSource_GiveFeedback)
2438 DropSource::DropSource() {
2439 vtbl = vtDropSource;
2440 sci = 0;
2443 /// Implement IUnkown
2444 STDMETHODIMP DataObject_QueryInterface(DataObject *pd, REFIID riid, PVOID *ppv) {
2445 //Platform::DebugPrintf("DO QI %x\n", pd);
2446 return pd->sci->QueryInterface(riid, ppv);
2448 STDMETHODIMP_(ULONG)DataObject_AddRef(DataObject *pd) {
2449 return pd->sci->AddRef();
2451 STDMETHODIMP_(ULONG)DataObject_Release(DataObject *pd) {
2452 return pd->sci->Release();
2454 /// Implement IDataObject
2455 STDMETHODIMP DataObject_GetData(DataObject *pd, FORMATETC *pFEIn, STGMEDIUM *pSTM) {
2456 return pd->sci->GetData(pFEIn, pSTM);
2459 STDMETHODIMP DataObject_GetDataHere(DataObject *, FORMATETC *, STGMEDIUM *) {
2460 //Platform::DebugPrintf("DOB GetDataHere\n");
2461 return E_NOTIMPL;
2464 STDMETHODIMP DataObject_QueryGetData(DataObject *pd, FORMATETC *pFE) {
2465 if (pd->sci->DragIsRectangularOK(pFE->cfFormat) &&
2466 pFE->ptd == 0 &&
2467 (pFE->dwAspect & DVASPECT_CONTENT) != 0 &&
2468 pFE->lindex == -1 &&
2469 (pFE->tymed & TYMED_HGLOBAL) != 0
2471 return S_OK;
2474 const bool formatOK = (pFE->cfFormat == CF_TEXT) ||
2475 ((pFE->cfFormat == CF_UNICODETEXT) && pd->sci->IsUnicodeMode());
2476 if (!formatOK ||
2477 pFE->ptd != 0 ||
2478 (pFE->dwAspect & DVASPECT_CONTENT) == 0 ||
2479 pFE->lindex != -1 ||
2480 (pFE->tymed & TYMED_HGLOBAL) == 0
2482 //Platform::DebugPrintf("DOB QueryGetData No %x\n",pFE->cfFormat);
2483 //return DATA_E_FORMATETC;
2484 return S_FALSE;
2486 //Platform::DebugPrintf("DOB QueryGetData OK %x\n",pFE->cfFormat);
2487 return S_OK;
2490 STDMETHODIMP DataObject_GetCanonicalFormatEtc(DataObject *pd, FORMATETC *, FORMATETC *pFEOut) {
2491 //Platform::DebugPrintf("DOB GetCanon\n");
2492 if (pd->sci->IsUnicodeMode())
2493 pFEOut->cfFormat = CF_UNICODETEXT;
2494 else
2495 pFEOut->cfFormat = CF_TEXT;
2496 pFEOut->ptd = 0;
2497 pFEOut->dwAspect = DVASPECT_CONTENT;
2498 pFEOut->lindex = -1;
2499 pFEOut->tymed = TYMED_HGLOBAL;
2500 return S_OK;
2503 STDMETHODIMP DataObject_SetData(DataObject *, FORMATETC *, STGMEDIUM *, BOOL) {
2504 //Platform::DebugPrintf("DOB SetData\n");
2505 return E_FAIL;
2508 STDMETHODIMP DataObject_EnumFormatEtc(DataObject *pd, DWORD dwDirection, IEnumFORMATETC **ppEnum) {
2509 try {
2510 //Platform::DebugPrintf("DOB EnumFormatEtc %d\n", dwDirection);
2511 if (dwDirection != DATADIR_GET) {
2512 *ppEnum = 0;
2513 return E_FAIL;
2515 FormatEnumerator *pfe;
2516 if (pd->sci->IsUnicodeMode()) {
2517 CLIPFORMAT formats[] = {CF_UNICODETEXT, CF_TEXT};
2518 pfe = new FormatEnumerator(0, formats, ELEMENTS(formats));
2519 } else {
2520 CLIPFORMAT formats[] = {CF_TEXT};
2521 pfe = new FormatEnumerator(0, formats, ELEMENTS(formats));
2523 return FormatEnumerator_QueryInterface(pfe, IID_IEnumFORMATETC,
2524 reinterpret_cast<void **>(ppEnum));
2525 } catch (std::bad_alloc &) {
2526 pd->sci->errorStatus = SC_STATUS_BADALLOC;
2527 return E_OUTOFMEMORY;
2528 } catch (...) {
2529 pd->sci->errorStatus = SC_STATUS_FAILURE;
2530 return E_FAIL;
2534 STDMETHODIMP DataObject_DAdvise(DataObject *, FORMATETC *, DWORD, IAdviseSink *, PDWORD) {
2535 //Platform::DebugPrintf("DOB DAdvise\n");
2536 return E_FAIL;
2539 STDMETHODIMP DataObject_DUnadvise(DataObject *, DWORD) {
2540 //Platform::DebugPrintf("DOB DUnadvise\n");
2541 return E_FAIL;
2544 STDMETHODIMP DataObject_EnumDAdvise(DataObject *, IEnumSTATDATA **) {
2545 //Platform::DebugPrintf("DOB EnumDAdvise\n");
2546 return E_FAIL;
2549 static VFunction *vtDataObject[] = {
2550 (VFunction *)(DataObject_QueryInterface),
2551 (VFunction *)(DataObject_AddRef),
2552 (VFunction *)(DataObject_Release),
2553 (VFunction *)(DataObject_GetData),
2554 (VFunction *)(DataObject_GetDataHere),
2555 (VFunction *)(DataObject_QueryGetData),
2556 (VFunction *)(DataObject_GetCanonicalFormatEtc),
2557 (VFunction *)(DataObject_SetData),
2558 (VFunction *)(DataObject_EnumFormatEtc),
2559 (VFunction *)(DataObject_DAdvise),
2560 (VFunction *)(DataObject_DUnadvise),
2561 (VFunction *)(DataObject_EnumDAdvise)
2564 DataObject::DataObject() {
2565 vtbl = vtDataObject;
2566 sci = 0;
2569 /// Implement IUnknown
2570 STDMETHODIMP DropTarget_QueryInterface(DropTarget *dt, REFIID riid, PVOID *ppv) {
2571 //Platform::DebugPrintf("DT QI %x\n", dt);
2572 return dt->sci->QueryInterface(riid, ppv);
2574 STDMETHODIMP_(ULONG)DropTarget_AddRef(DropTarget *dt) {
2575 return dt->sci->AddRef();
2577 STDMETHODIMP_(ULONG)DropTarget_Release(DropTarget *dt) {
2578 return dt->sci->Release();
2581 /// Implement IDropTarget by forwarding to Scintilla
2582 STDMETHODIMP DropTarget_DragEnter(DropTarget *dt, LPDATAOBJECT pIDataSource, DWORD grfKeyState,
2583 POINTL pt, PDWORD pdwEffect) {
2584 try {
2585 return dt->sci->DragEnter(pIDataSource, grfKeyState, pt, pdwEffect);
2586 } catch (...) {
2587 dt->sci->errorStatus = SC_STATUS_FAILURE;
2589 return E_FAIL;
2591 STDMETHODIMP DropTarget_DragOver(DropTarget *dt, DWORD grfKeyState, POINTL pt, PDWORD pdwEffect) {
2592 try {
2593 return dt->sci->DragOver(grfKeyState, pt, pdwEffect);
2594 } catch (...) {
2595 dt->sci->errorStatus = SC_STATUS_FAILURE;
2597 return E_FAIL;
2599 STDMETHODIMP DropTarget_DragLeave(DropTarget *dt) {
2600 try {
2601 return dt->sci->DragLeave();
2602 } catch (...) {
2603 dt->sci->errorStatus = SC_STATUS_FAILURE;
2605 return E_FAIL;
2607 STDMETHODIMP DropTarget_Drop(DropTarget *dt, LPDATAOBJECT pIDataSource, DWORD grfKeyState,
2608 POINTL pt, PDWORD pdwEffect) {
2609 try {
2610 return dt->sci->Drop(pIDataSource, grfKeyState, pt, pdwEffect);
2611 } catch (...) {
2612 dt->sci->errorStatus = SC_STATUS_FAILURE;
2614 return E_FAIL;
2617 static VFunction *vtDropTarget[] = {
2618 (VFunction *)(DropTarget_QueryInterface),
2619 (VFunction *)(DropTarget_AddRef),
2620 (VFunction *)(DropTarget_Release),
2621 (VFunction *)(DropTarget_DragEnter),
2622 (VFunction *)(DropTarget_DragOver),
2623 (VFunction *)(DropTarget_DragLeave),
2624 (VFunction *)(DropTarget_Drop)
2627 DropTarget::DropTarget() {
2628 vtbl = vtDropTarget;
2629 sci = 0;
2633 * DBCS: support Input Method Editor (IME).
2634 * Called when IME Window opened.
2636 void ScintillaWin::ImeStartComposition() {
2637 if (caret.active) {
2638 // Move IME Window to current caret position
2639 IMContext imc(MainHWND());
2640 Point pos = PointMainCaret();
2641 COMPOSITIONFORM CompForm;
2642 CompForm.dwStyle = CFS_POINT;
2643 CompForm.ptCurrentPos.x = static_cast<int>(pos.x);
2644 CompForm.ptCurrentPos.y = static_cast<int>(pos.y);
2646 ::ImmSetCompositionWindow(imc.hIMC, &CompForm);
2648 // Set font of IME window to same as surrounded text.
2649 if (stylesValid) {
2650 // Since the style creation code has been made platform independent,
2651 // The logfont for the IME is recreated here.
2652 const int styleHere = pdoc->StyleIndexAt(sel.MainCaret());
2653 LOGFONTW lf = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, L""};
2654 int sizeZoomed = vs.styles[styleHere].size + vs.zoomLevel * SC_FONT_SIZE_MULTIPLIER;
2655 if (sizeZoomed <= 2 * SC_FONT_SIZE_MULTIPLIER) // Hangs if sizeZoomed <= 1
2656 sizeZoomed = 2 * SC_FONT_SIZE_MULTIPLIER;
2657 AutoSurface surface(this);
2658 int deviceHeight = sizeZoomed;
2659 if (surface) {
2660 deviceHeight = (sizeZoomed * surface->LogPixelsY()) / 72;
2662 // The negative is to allow for leading
2663 lf.lfHeight = -(abs(deviceHeight / SC_FONT_SIZE_MULTIPLIER));
2664 lf.lfWeight = vs.styles[styleHere].weight;
2665 lf.lfItalic = static_cast<BYTE>(vs.styles[styleHere].italic ? 1 : 0);
2666 lf.lfCharSet = DEFAULT_CHARSET;
2667 lf.lfFaceName[0] = L'\0';
2668 if (vs.styles[styleHere].fontName) {
2669 const char* fontName = vs.styles[styleHere].fontName;
2670 UTF16FromUTF8(fontName, strlen(fontName)+1, lf.lfFaceName, LF_FACESIZE);
2673 ::ImmSetCompositionFontW(imc.hIMC, &lf);
2675 // Caret is displayed in IME window. So, caret in Scintilla is useless.
2676 DropCaret();
2680 /** Called when IME Window closed. */
2681 void ScintillaWin::ImeEndComposition() {
2682 ShowCaretAtCurrentPosition();
2685 LRESULT ScintillaWin::ImeOnReconvert(LPARAM lParam) {
2686 // Reconversion on windows limits within one line without eol.
2687 // Look around: baseStart <-- (|mainStart| -- mainEnd) --> baseEnd.
2688 const Sci::Position mainStart = sel.RangeMain().Start().Position();
2689 const Sci::Position mainEnd = sel.RangeMain().End().Position();
2690 const Sci::Line curLine = pdoc->LineFromPosition(mainStart);
2691 if (curLine != pdoc->LineFromPosition(mainEnd))
2692 return 0;
2693 const Sci::Position baseStart = pdoc->LineStart(curLine);
2694 const Sci::Position baseEnd = pdoc->LineEnd(curLine);
2695 if ((baseStart == baseEnd) || (mainEnd > baseEnd))
2696 return 0;
2698 const int codePage = CodePageOfDocument();
2699 const std::wstring rcFeed = StringDecode(RangeText(baseStart, baseEnd), codePage);
2700 const int rcFeedLen = static_cast<int>(rcFeed.length()) * sizeof(wchar_t);
2701 const int rcSize = sizeof(RECONVERTSTRING) + rcFeedLen + sizeof(wchar_t);
2703 RECONVERTSTRING *rc = reinterpret_cast<RECONVERTSTRING *>(lParam);
2704 if (!rc)
2705 return rcSize; // Immediately be back with rcSize of memory block.
2707 wchar_t *rcFeedStart = reinterpret_cast<wchar_t*>(rc + 1);
2708 memcpy(rcFeedStart, &rcFeed[0], rcFeedLen);
2710 std::string rcCompString = RangeText(mainStart, mainEnd);
2711 std::wstring rcCompWstring = StringDecode(rcCompString, codePage);
2712 std::string rcCompStart = RangeText(baseStart, mainStart);
2713 std::wstring rcCompWstart = StringDecode(rcCompStart, codePage);
2715 // Map selection to dwCompStr.
2716 // No selection assumes current caret as rcCompString without length.
2717 rc->dwVersion = 0; // It should be absolutely 0.
2718 rc->dwStrLen = static_cast<DWORD>(rcFeed.length());
2719 rc->dwStrOffset = sizeof(RECONVERTSTRING);
2720 rc->dwCompStrLen = static_cast<DWORD>(rcCompWstring.length());
2721 rc->dwCompStrOffset = static_cast<DWORD>(rcCompWstart.length()) * sizeof(wchar_t);
2722 rc->dwTargetStrLen = rc->dwCompStrLen;
2723 rc->dwTargetStrOffset =rc->dwCompStrOffset;
2725 IMContext imc(MainHWND());
2726 if (!imc.hIMC)
2727 return 0;
2729 if (!::ImmSetCompositionStringW(imc.hIMC, SCS_QUERYRECONVERTSTRING, rc, rcSize, NULL, 0))
2730 return 0;
2732 // No selection asks IME to fill target fields with its own value.
2733 int tgWlen = rc->dwTargetStrLen;
2734 int tgWstart = rc->dwTargetStrOffset / sizeof(wchar_t);
2736 std::string tgCompStart = StringEncode(rcFeed.substr(0, tgWstart), codePage);
2737 std::string tgComp = StringEncode(rcFeed.substr(tgWstart, tgWlen), codePage);
2739 // No selection needs to adjust reconvert start position for IME set.
2740 int adjust = static_cast<int>(tgCompStart.length() - rcCompStart.length());
2741 int docCompLen = static_cast<int>(tgComp.length());
2743 // Make place for next composition string to sit in.
2744 for (size_t r=0; r<sel.Count(); r++) {
2745 Sci::Position rBase = sel.Range(r).Start().Position();
2746 Sci::Position docCompStart = rBase + adjust;
2748 if (inOverstrike) { // the docCompLen of bytes will be overstriked.
2749 sel.Range(r).caret.SetPosition(docCompStart);
2750 sel.Range(r).anchor.SetPosition(docCompStart);
2751 } else {
2752 // Ensure docCompStart+docCompLen be not beyond lineEnd.
2753 // since docCompLen by byte might break eol.
2754 Sci::Position lineEnd = pdoc->LineEnd(pdoc->LineFromPosition(rBase));
2755 Sci::Position overflow = (docCompStart + docCompLen) - lineEnd;
2756 if (overflow > 0) {
2757 pdoc->DeleteChars(docCompStart, docCompLen - overflow);
2758 } else {
2759 pdoc->DeleteChars(docCompStart, docCompLen);
2763 // Immediately Target Input or candidate box choice with GCS_COMPSTR.
2764 return rcSize;
2767 void ScintillaWin::GetIntelliMouseParameters() {
2768 // This retrieves the number of lines per scroll as configured inthe Mouse Properties sheet in Control Panel
2769 ::SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, &linesPerScroll, 0);
2772 void ScintillaWin::CopyToClipboard(const SelectionText &selectedText) {
2773 if (!::OpenClipboardRetry(MainHWND())) {
2774 return;
2776 ::EmptyClipboard();
2778 GlobalMemory uniText;
2780 // Default Scintilla behaviour in Unicode mode
2781 if (IsUnicodeMode()) {
2782 size_t uchars = UTF16Length(selectedText.Data(),
2783 selectedText.LengthWithTerminator());
2784 uniText.Allocate(2 * uchars);
2785 if (uniText) {
2786 UTF16FromUTF8(selectedText.Data(), selectedText.LengthWithTerminator(),
2787 static_cast<wchar_t *>(uniText.ptr), uchars);
2789 } else {
2790 // Not Unicode mode
2791 // Convert to Unicode using the current Scintilla code page
2792 UINT cpSrc = CodePageFromCharSet(
2793 selectedText.characterSet, selectedText.codePage);
2794 int uLen = ::MultiByteToWideChar(cpSrc, 0, selectedText.Data(),
2795 static_cast<int>(selectedText.LengthWithTerminator()), 0, 0);
2796 uniText.Allocate(2 * uLen);
2797 if (uniText) {
2798 ::MultiByteToWideChar(cpSrc, 0, selectedText.Data(),
2799 static_cast<int>(selectedText.LengthWithTerminator()),
2800 static_cast<wchar_t *>(uniText.ptr), uLen);
2804 if (uniText) {
2805 uniText.SetClip(CF_UNICODETEXT);
2806 } else {
2807 // There was a failure - try to copy at least ANSI text
2808 GlobalMemory ansiText;
2809 ansiText.Allocate(selectedText.LengthWithTerminator());
2810 if (ansiText) {
2811 memcpy(static_cast<char *>(ansiText.ptr), selectedText.Data(), selectedText.LengthWithTerminator());
2812 ansiText.SetClip(CF_TEXT);
2816 if (selectedText.rectangular) {
2817 ::SetClipboardData(cfColumnSelect, 0);
2819 GlobalMemory borlandSelection;
2820 borlandSelection.Allocate(1);
2821 if (borlandSelection) {
2822 static_cast<BYTE *>(borlandSelection.ptr)[0] = 0x02;
2823 borlandSelection.SetClip(cfBorlandIDEBlockType);
2827 if (selectedText.lineCopy) {
2828 ::SetClipboardData(cfLineSelect, 0);
2829 ::SetClipboardData(cfVSLineTag, 0);
2832 ::CloseClipboard();
2835 void ScintillaWin::ScrollMessage(WPARAM wParam) {
2836 //DWORD dwStart = timeGetTime();
2837 //Platform::DebugPrintf("Scroll %x %d\n", wParam, lParam);
2839 SCROLLINFO sci = {};
2840 sci.cbSize = sizeof(sci);
2841 sci.fMask = SIF_ALL;
2843 GetScrollInfo(SB_VERT, &sci);
2845 //Platform::DebugPrintf("ScrollInfo %d mask=%x min=%d max=%d page=%d pos=%d track=%d\n", b,sci.fMask,
2846 //sci.nMin, sci.nMax, sci.nPage, sci.nPos, sci.nTrackPos);
2848 Sci::Line topLineNew = topLine;
2849 switch (LoWord(wParam)) {
2850 case SB_LINEUP:
2851 topLineNew -= 1;
2852 break;
2853 case SB_LINEDOWN:
2854 topLineNew += 1;
2855 break;
2856 case SB_PAGEUP:
2857 topLineNew -= LinesToScroll(); break;
2858 case SB_PAGEDOWN: topLineNew += LinesToScroll(); break;
2859 case SB_TOP: topLineNew = 0; break;
2860 case SB_BOTTOM: topLineNew = MaxScrollPos(); break;
2861 case SB_THUMBPOSITION: topLineNew = sci.nTrackPos; break;
2862 case SB_THUMBTRACK: topLineNew = sci.nTrackPos; break;
2864 ScrollTo(topLineNew);
2867 void ScintillaWin::HorizontalScrollMessage(WPARAM wParam) {
2868 int xPos = xOffset;
2869 const PRectangle rcText = GetTextRectangle();
2870 const int pageWidth = static_cast<int>(rcText.Width() * 2 / 3);
2871 switch (LoWord(wParam)) {
2872 case SB_LINEUP:
2873 xPos -= 20;
2874 break;
2875 case SB_LINEDOWN: // May move past the logical end
2876 xPos += 20;
2877 break;
2878 case SB_PAGEUP:
2879 xPos -= pageWidth;
2880 break;
2881 case SB_PAGEDOWN:
2882 xPos += pageWidth;
2883 if (xPos > scrollWidth - rcText.Width()) { // Hit the end exactly
2884 xPos = scrollWidth - static_cast<int>(rcText.Width());
2886 break;
2887 case SB_TOP:
2888 xPos = 0;
2889 break;
2890 case SB_BOTTOM:
2891 xPos = scrollWidth;
2892 break;
2893 case SB_THUMBPOSITION:
2894 case SB_THUMBTRACK: {
2895 // 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 =]
2896 SCROLLINFO si;
2897 si.cbSize = sizeof(si);
2898 si.fMask = SIF_TRACKPOS;
2899 if (GetScrollInfo(SB_HORZ, &si)) {
2900 xPos = si.nTrackPos;
2903 break;
2905 HorizontalScrollTo(xPos);
2909 * Redraw all of text area.
2910 * This paint will not be abandoned.
2912 void ScintillaWin::FullPaint() {
2913 if ((technology == SC_TECHNOLOGY_DEFAULT) || (technology == SC_TECHNOLOGY_DIRECTWRITEDC)) {
2914 HDC hdc = ::GetDC(MainHWND());
2915 FullPaintDC(hdc);
2916 ::ReleaseDC(MainHWND(), hdc);
2917 } else {
2918 FullPaintDC(0);
2923 * Redraw all of text area on the specified DC.
2924 * This paint will not be abandoned.
2926 void ScintillaWin::FullPaintDC(HDC hdc) {
2927 paintState = painting;
2928 rcPaint = GetClientRectangle();
2929 paintingAllText = true;
2930 if (technology == SC_TECHNOLOGY_DEFAULT) {
2931 AutoSurface surfaceWindow(hdc, this);
2932 if (surfaceWindow) {
2933 Paint(surfaceWindow, rcPaint);
2934 surfaceWindow->Release();
2936 } else {
2937 #if defined(USE_D2D)
2938 EnsureRenderTarget(hdc);
2939 AutoSurface surfaceWindow(pRenderTarget, this);
2940 if (surfaceWindow) {
2941 pRenderTarget->BeginDraw();
2942 Paint(surfaceWindow, rcPaint);
2943 surfaceWindow->Release();
2944 const HRESULT hr = pRenderTarget->EndDraw();
2945 if (hr == static_cast<HRESULT>(D2DERR_RECREATE_TARGET)) {
2946 DropRenderTarget();
2949 #endif
2951 paintState = notPainting;
2954 static bool CompareDevCap(HDC hdc, HDC hOtherDC, int nIndex) {
2955 return ::GetDeviceCaps(hdc, nIndex) == ::GetDeviceCaps(hOtherDC, nIndex);
2958 bool ScintillaWin::IsCompatibleDC(HDC hOtherDC) {
2959 HDC hdc = ::GetDC(MainHWND());
2960 const bool isCompatible =
2961 CompareDevCap(hdc, hOtherDC, TECHNOLOGY) &&
2962 CompareDevCap(hdc, hOtherDC, LOGPIXELSY) &&
2963 CompareDevCap(hdc, hOtherDC, LOGPIXELSX) &&
2964 CompareDevCap(hdc, hOtherDC, BITSPIXEL) &&
2965 CompareDevCap(hdc, hOtherDC, PLANES);
2966 ::ReleaseDC(MainHWND(), hdc);
2967 return isCompatible;
2970 DWORD ScintillaWin::EffectFromState(DWORD grfKeyState) const {
2971 // These are the Wordpad semantics.
2972 DWORD dwEffect;
2973 if (inDragDrop == ddDragging) // Internal defaults to move
2974 dwEffect = DROPEFFECT_MOVE;
2975 else
2976 dwEffect = DROPEFFECT_COPY;
2977 if (grfKeyState & MK_ALT)
2978 dwEffect = DROPEFFECT_MOVE;
2979 if (grfKeyState & MK_CONTROL)
2980 dwEffect = DROPEFFECT_COPY;
2981 return dwEffect;
2984 /// Implement IUnknown
2985 STDMETHODIMP ScintillaWin::QueryInterface(REFIID riid, PVOID *ppv) {
2986 *ppv = NULL;
2987 if (riid == IID_IUnknown)
2988 *ppv = reinterpret_cast<IDropTarget *>(&dt);
2989 if (riid == IID_IDropSource)
2990 *ppv = reinterpret_cast<IDropSource *>(&ds);
2991 if (riid == IID_IDropTarget)
2992 *ppv = reinterpret_cast<IDropTarget *>(&dt);
2993 if (riid == IID_IDataObject)
2994 *ppv = reinterpret_cast<IDataObject *>(&dob);
2995 if (!*ppv)
2996 return E_NOINTERFACE;
2997 return S_OK;
3000 STDMETHODIMP_(ULONG) ScintillaWin::AddRef() {
3001 return 1;
3004 STDMETHODIMP_(ULONG) ScintillaWin::Release() {
3005 return 1;
3008 /// Implement IDropTarget
3009 STDMETHODIMP ScintillaWin::DragEnter(LPDATAOBJECT pIDataSource, DWORD grfKeyState,
3010 POINTL, PDWORD pdwEffect) {
3011 if (pIDataSource == NULL)
3012 return E_POINTER;
3013 FORMATETC fmtu = {CF_UNICODETEXT, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
3014 const HRESULT hrHasUText = pIDataSource->QueryGetData(&fmtu);
3015 hasOKText = (hrHasUText == S_OK);
3016 if (!hasOKText) {
3017 FORMATETC fmte = {CF_TEXT, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
3018 const HRESULT hrHasText = pIDataSource->QueryGetData(&fmte);
3019 hasOKText = (hrHasText == S_OK);
3021 if (!hasOKText) {
3022 *pdwEffect = DROPEFFECT_NONE;
3023 return S_OK;
3026 *pdwEffect = EffectFromState(grfKeyState);
3027 return S_OK;
3030 STDMETHODIMP ScintillaWin::DragOver(DWORD grfKeyState, POINTL pt, PDWORD pdwEffect) {
3031 try {
3032 if (!hasOKText || pdoc->IsReadOnly()) {
3033 *pdwEffect = DROPEFFECT_NONE;
3034 return S_OK;
3037 *pdwEffect = EffectFromState(grfKeyState);
3039 // Update the cursor.
3040 POINT rpt = {pt.x, pt.y};
3041 ::ScreenToClient(MainHWND(), &rpt);
3042 SetDragPosition(SPositionFromLocation(PointFromPOINT(rpt), false, false, UserVirtualSpace()));
3044 return S_OK;
3045 } catch (...) {
3046 errorStatus = SC_STATUS_FAILURE;
3048 return E_FAIL;
3051 STDMETHODIMP ScintillaWin::DragLeave() {
3052 try {
3053 SetDragPosition(SelectionPosition(Sci::invalidPosition));
3054 return S_OK;
3055 } catch (...) {
3056 errorStatus = SC_STATUS_FAILURE;
3058 return E_FAIL;
3061 STDMETHODIMP ScintillaWin::Drop(LPDATAOBJECT pIDataSource, DWORD grfKeyState,
3062 POINTL pt, PDWORD pdwEffect) {
3063 try {
3064 *pdwEffect = EffectFromState(grfKeyState);
3066 if (pIDataSource == NULL)
3067 return E_POINTER;
3069 SetDragPosition(SelectionPosition(Sci::invalidPosition));
3071 STGMEDIUM medium = {0, {0}, 0};
3073 std::vector<char> data; // Includes terminating NUL
3075 FORMATETC fmtu = {CF_UNICODETEXT, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
3076 HRESULT hr = pIDataSource->GetData(&fmtu, &medium);
3077 if (SUCCEEDED(hr) && medium.hGlobal) {
3078 GlobalMemory memUDrop(medium.hGlobal);
3079 wchar_t *udata = static_cast<wchar_t *>(memUDrop.ptr);
3080 if (udata) {
3081 if (IsUnicodeMode()) {
3082 const size_t tlen = memUDrop.Size();
3083 // Convert UTF-16 to UTF-8
3084 const size_t dataLen = UTF8Length(udata, tlen/2);
3085 data.resize(dataLen+1);
3086 UTF8FromUTF16(udata, tlen/2, &data[0], dataLen);
3087 } else {
3088 // Convert UTF-16 to ANSI
3090 // Default Scintilla behavior in Unicode mode
3091 // CF_UNICODETEXT available, but not in Unicode mode
3092 // Convert from Unicode to current Scintilla code page
3093 UINT cpDest = CodePageOfDocument();
3094 int tlen = ::WideCharToMultiByte(cpDest, 0, udata, -1,
3095 NULL, 0, NULL, NULL) - 1; // subtract 0 terminator
3096 data.resize(tlen + 1);
3097 ::WideCharToMultiByte(cpDest, 0, udata, -1,
3098 &data[0], tlen + 1, NULL, NULL);
3101 memUDrop.Unlock();
3102 } else {
3103 FORMATETC fmte = {CF_TEXT, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
3104 hr = pIDataSource->GetData(&fmte, &medium);
3105 if (SUCCEEDED(hr) && medium.hGlobal) {
3106 GlobalMemory memDrop(medium.hGlobal);
3107 const char *cdata = static_cast<char *>(memDrop.ptr);
3108 if (cdata)
3109 data.assign(cdata, cdata+strlen(cdata)+1);
3110 memDrop.Unlock();
3114 if (!SUCCEEDED(hr) || data.empty()) {
3115 //Platform::DebugPrintf("Bad data format: 0x%x\n", hres);
3116 return hr;
3119 FORMATETC fmtr = {cfColumnSelect, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
3120 HRESULT hrRectangular = pIDataSource->QueryGetData(&fmtr);
3122 POINT rpt = {pt.x, pt.y};
3123 ::ScreenToClient(MainHWND(), &rpt);
3124 SelectionPosition movePos = SPositionFromLocation(PointFromPOINT(rpt), false, false, UserVirtualSpace());
3126 DropAt(movePos, &data[0], data.size() - 1, *pdwEffect == DROPEFFECT_MOVE, hrRectangular == S_OK);
3128 // Free data
3129 if (medium.pUnkForRelease != NULL)
3130 medium.pUnkForRelease->Release();
3131 else
3132 ::GlobalFree(medium.hGlobal);
3134 return S_OK;
3135 } catch (...) {
3136 errorStatus = SC_STATUS_FAILURE;
3138 return E_FAIL;
3141 /// Implement important part of IDataObject
3142 STDMETHODIMP ScintillaWin::GetData(FORMATETC *pFEIn, STGMEDIUM *pSTM) {
3143 const bool formatOK = (pFEIn->cfFormat == CF_TEXT) ||
3144 ((pFEIn->cfFormat == CF_UNICODETEXT) && IsUnicodeMode());
3145 if (!formatOK ||
3146 pFEIn->ptd != 0 ||
3147 (pFEIn->dwAspect & DVASPECT_CONTENT) == 0 ||
3148 pFEIn->lindex != -1 ||
3149 (pFEIn->tymed & TYMED_HGLOBAL) == 0
3151 //Platform::DebugPrintf("DOB GetData No %d %x %x fmt=%x\n", lenDrag, pFEIn, pSTM, pFEIn->cfFormat);
3152 return DATA_E_FORMATETC;
3154 pSTM->tymed = TYMED_HGLOBAL;
3155 //Platform::DebugPrintf("DOB GetData OK %d %x %x\n", lenDrag, pFEIn, pSTM);
3157 GlobalMemory text;
3158 if (pFEIn->cfFormat == CF_UNICODETEXT) {
3159 size_t uchars = UTF16Length(drag.Data(), drag.LengthWithTerminator());
3160 text.Allocate(2 * uchars);
3161 if (text) {
3162 UTF16FromUTF8(drag.Data(), drag.LengthWithTerminator(),
3163 static_cast<wchar_t *>(text.ptr), uchars);
3165 } else {
3166 text.Allocate(drag.LengthWithTerminator());
3167 if (text) {
3168 memcpy(static_cast<char *>(text.ptr), drag.Data(), drag.LengthWithTerminator());
3171 pSTM->hGlobal = text ? text.Unlock() : 0;
3172 pSTM->pUnkForRelease = 0;
3173 return S_OK;
3176 bool ScintillaWin::Register(HINSTANCE hInstance_) {
3178 hInstance = hInstance_;
3179 bool result;
3181 // Register the Scintilla class
3182 // Register Scintilla as a wide character window
3183 WNDCLASSEXW wndclass;
3184 wndclass.cbSize = sizeof(wndclass);
3185 wndclass.style = CS_GLOBALCLASS | CS_HREDRAW | CS_VREDRAW;
3186 wndclass.lpfnWndProc = ScintillaWin::SWndProc;
3187 wndclass.cbClsExtra = 0;
3188 wndclass.cbWndExtra = sizeof(ScintillaWin *);
3189 wndclass.hInstance = hInstance;
3190 wndclass.hIcon = NULL;
3191 wndclass.hCursor = NULL;
3192 wndclass.hbrBackground = NULL;
3193 wndclass.lpszMenuName = NULL;
3194 wndclass.lpszClassName = L"Scintilla";
3195 wndclass.hIconSm = 0;
3196 scintillaClassAtom = ::RegisterClassExW(&wndclass);
3197 result = 0 != scintillaClassAtom;
3199 if (result) {
3200 // Register the CallTip class
3201 WNDCLASSEX wndclassc;
3202 wndclassc.cbSize = sizeof(wndclassc);
3203 wndclassc.style = CS_GLOBALCLASS | CS_HREDRAW | CS_VREDRAW;
3204 wndclassc.cbClsExtra = 0;
3205 wndclassc.cbWndExtra = sizeof(ScintillaWin *);
3206 wndclassc.hInstance = hInstance;
3207 wndclassc.hIcon = NULL;
3208 wndclassc.hbrBackground = NULL;
3209 wndclassc.lpszMenuName = NULL;
3210 wndclassc.lpfnWndProc = ScintillaWin::CTWndProc;
3211 wndclassc.hCursor = ::LoadCursor(NULL, IDC_ARROW);
3212 wndclassc.lpszClassName = callClassName;
3213 wndclassc.hIconSm = 0;
3215 callClassAtom = ::RegisterClassEx(&wndclassc);
3216 result = 0 != callClassAtom;
3219 return result;
3222 bool ScintillaWin::Unregister() {
3223 bool result = true;
3224 if (0 != scintillaClassAtom) {
3225 if (::UnregisterClass(MAKEINTATOM(scintillaClassAtom), hInstance) == 0) {
3226 result = false;
3228 scintillaClassAtom = 0;
3230 if (0 != callClassAtom) {
3231 if (::UnregisterClass(MAKEINTATOM(callClassAtom), hInstance) == 0) {
3232 result = false;
3234 callClassAtom = 0;
3236 return result;
3239 bool ScintillaWin::HasCaretSizeChanged() const {
3240 if (
3241 ( (0 != vs.caretWidth) && (sysCaretWidth != vs.caretWidth) )
3242 || ((0 != vs.lineHeight) && (sysCaretHeight != vs.lineHeight))
3244 return true;
3246 return false;
3249 BOOL ScintillaWin::CreateSystemCaret() {
3250 sysCaretWidth = vs.caretWidth;
3251 if (0 == sysCaretWidth) {
3252 sysCaretWidth = 1;
3254 sysCaretHeight = vs.lineHeight;
3255 int bitmapSize = (((sysCaretWidth + 15) & ~15) >> 3) *
3256 sysCaretHeight;
3257 std::vector<char> bits(bitmapSize);
3258 sysCaretBitmap = ::CreateBitmap(sysCaretWidth, sysCaretHeight, 1,
3259 1, reinterpret_cast<BYTE *>(&bits[0]));
3260 BOOL retval = ::CreateCaret(
3261 MainHWND(), sysCaretBitmap,
3262 sysCaretWidth, sysCaretHeight);
3263 if (technology == SC_TECHNOLOGY_DEFAULT) {
3264 // System caret interferes with Direct2D drawing so only show it for GDI.
3265 ::ShowCaret(MainHWND());
3267 return retval;
3270 BOOL ScintillaWin::DestroySystemCaret() {
3271 ::HideCaret(MainHWND());
3272 BOOL retval = ::DestroyCaret();
3273 if (sysCaretBitmap) {
3274 ::DeleteObject(sysCaretBitmap);
3275 sysCaretBitmap = 0;
3277 return retval;
3280 LRESULT PASCAL ScintillaWin::CTWndProc(
3281 HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam) {
3282 // Find C++ object associated with window.
3283 ScintillaWin *sciThis = static_cast<ScintillaWin *>(PointerFromWindow(hWnd));
3284 try {
3285 // ctp will be zero if WM_CREATE not seen yet
3286 if (sciThis == 0) {
3287 if (iMessage == WM_CREATE) {
3288 // Associate CallTip object with window
3289 CREATESTRUCT *pCreate = reinterpret_cast<CREATESTRUCT *>(lParam);
3290 SetWindowPointer(hWnd, pCreate->lpCreateParams);
3291 return 0;
3292 } else {
3293 return ::DefWindowProc(hWnd, iMessage, wParam, lParam);
3295 } else {
3296 if (iMessage == WM_NCDESTROY) {
3297 ::SetWindowLong(hWnd, 0, 0);
3298 return ::DefWindowProc(hWnd, iMessage, wParam, lParam);
3299 } else if (iMessage == WM_PAINT) {
3300 PAINTSTRUCT ps;
3301 ::BeginPaint(hWnd, &ps);
3302 std::unique_ptr<Surface> surfaceWindow(Surface::Allocate(sciThis->technology));
3303 #if defined(USE_D2D)
3304 ID2D1HwndRenderTarget *pCTRenderTarget = 0;
3305 #endif
3306 RECT rc;
3307 GetClientRect(hWnd, &rc);
3308 // Create a Direct2D render target.
3309 if (sciThis->technology == SC_TECHNOLOGY_DEFAULT) {
3310 surfaceWindow->Init(ps.hdc, hWnd);
3311 } else {
3312 #if defined(USE_D2D)
3313 D2D1_HWND_RENDER_TARGET_PROPERTIES dhrtp;
3314 dhrtp.hwnd = hWnd;
3315 dhrtp.pixelSize = D2D1::SizeU(rc.right - rc.left, rc.bottom - rc.top);
3316 dhrtp.presentOptions = (sciThis->technology == SC_TECHNOLOGY_DIRECTWRITERETAIN) ?
3317 D2D1_PRESENT_OPTIONS_RETAIN_CONTENTS : D2D1_PRESENT_OPTIONS_NONE;
3319 D2D1_RENDER_TARGET_PROPERTIES drtp;
3320 drtp.type = D2D1_RENDER_TARGET_TYPE_DEFAULT;
3321 drtp.pixelFormat.format = DXGI_FORMAT_UNKNOWN;
3322 drtp.pixelFormat.alphaMode = D2D1_ALPHA_MODE_UNKNOWN;
3323 drtp.dpiX = 96.0;
3324 drtp.dpiY = 96.0;
3325 drtp.usage = D2D1_RENDER_TARGET_USAGE_NONE;
3326 drtp.minLevel = D2D1_FEATURE_LEVEL_DEFAULT;
3328 if (!SUCCEEDED(pD2DFactory->CreateHwndRenderTarget(drtp, dhrtp, &pCTRenderTarget))) {
3329 surfaceWindow->Release();
3330 ::EndPaint(hWnd, &ps);
3331 return 0;
3333 surfaceWindow->Init(pCTRenderTarget, hWnd);
3334 pCTRenderTarget->BeginDraw();
3335 #endif
3337 surfaceWindow->SetUnicodeMode(SC_CP_UTF8 == sciThis->ct.codePage);
3338 surfaceWindow->SetDBCSMode(sciThis->ct.codePage);
3339 sciThis->ct.PaintCT(surfaceWindow.get());
3340 #if defined(USE_D2D)
3341 if (pCTRenderTarget)
3342 pCTRenderTarget->EndDraw();
3343 #endif
3344 surfaceWindow->Release();
3345 #if defined(USE_D2D)
3346 if (pCTRenderTarget)
3347 pCTRenderTarget->Release();
3348 #endif
3349 ::EndPaint(hWnd, &ps);
3350 return 0;
3351 } else if ((iMessage == WM_NCLBUTTONDOWN) || (iMessage == WM_NCLBUTTONDBLCLK)) {
3352 POINT pt;
3353 pt.x = static_cast<short>(LOWORD(lParam));
3354 pt.y = static_cast<short>(HIWORD(lParam));
3355 ScreenToClient(hWnd, &pt);
3356 sciThis->ct.MouseClick(PointFromPOINT(pt));
3357 sciThis->CallTipClick();
3358 return 0;
3359 } else if (iMessage == WM_LBUTTONDOWN) {
3360 // This does not fire due to the hit test code
3361 sciThis->ct.MouseClick(Point::FromLong(static_cast<long>(lParam)));
3362 sciThis->CallTipClick();
3363 return 0;
3364 } else if (iMessage == WM_SETCURSOR) {
3365 ::SetCursor(::LoadCursor(NULL, IDC_ARROW));
3366 return 0;
3367 } else if (iMessage == WM_NCHITTEST) {
3368 return HTCAPTION;
3369 } else {
3370 return ::DefWindowProc(hWnd, iMessage, wParam, lParam);
3373 } catch (...) {
3374 sciThis->errorStatus = SC_STATUS_FAILURE;
3376 return ::DefWindowProc(hWnd, iMessage, wParam, lParam);
3379 sptr_t ScintillaWin::DirectFunction(
3380 sptr_t ptr, UINT iMessage, uptr_t wParam, sptr_t lParam) {
3381 PLATFORM_ASSERT(::GetCurrentThreadId() == ::GetWindowThreadProcessId(reinterpret_cast<ScintillaWin *>(ptr)->MainHWND(), NULL));
3382 return reinterpret_cast<ScintillaWin *>(ptr)->WndProc(iMessage, wParam, lParam);
3385 extern "C"
3386 #ifndef STATIC_BUILD
3387 __declspec(dllexport)
3388 #endif
3389 sptr_t __stdcall Scintilla_DirectFunction(
3390 ScintillaWin *sci, UINT iMessage, uptr_t wParam, sptr_t lParam) {
3391 return sci->WndProc(iMessage, wParam, lParam);
3394 LRESULT PASCAL ScintillaWin::SWndProc(
3395 HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam) {
3396 //Platform::DebugPrintf("S W:%x M:%x WP:%x L:%x\n", hWnd, iMessage, wParam, lParam);
3398 // Find C++ object associated with window.
3399 ScintillaWin *sci = static_cast<ScintillaWin *>(PointerFromWindow(hWnd));
3400 // sci will be zero if WM_CREATE not seen yet
3401 if (sci == 0) {
3402 try {
3403 if (iMessage == WM_CREATE) {
3404 // Create C++ object associated with window
3405 sci = new ScintillaWin(hWnd);
3406 SetWindowPointer(hWnd, sci);
3407 return sci->WndProc(iMessage, wParam, lParam);
3409 } catch (...) {
3411 return ::DefWindowProc(hWnd, iMessage, wParam, lParam);
3412 } else {
3413 if (iMessage == WM_NCDESTROY) {
3414 try {
3415 sci->Finalise();
3416 delete sci;
3417 } catch (...) {
3419 ::SetWindowLong(hWnd, 0, 0);
3420 return ::DefWindowProc(hWnd, iMessage, wParam, lParam);
3421 } else {
3422 return sci->WndProc(iMessage, wParam, lParam);
3427 // This function is externally visible so it can be called from container when building statically.
3428 // Must be called once only.
3429 int Scintilla_RegisterClasses(void *hInstance) {
3430 Platform_Initialise(hInstance);
3431 const bool result = ScintillaWin::Register(static_cast<HINSTANCE>(hInstance));
3432 #ifdef SCI_LEXER
3433 Scintilla_LinkLexers();
3434 #endif
3435 return result;
3438 static int ResourcesRelease(bool fromDllMain) {
3439 const bool result = ScintillaWin::Unregister();
3440 Platform_Finalise(fromDllMain);
3441 return result;
3444 // This function is externally visible so it can be called from container when building statically.
3445 int Scintilla_ReleaseResources() {
3446 return ResourcesRelease(false);
3449 #ifndef STATIC_BUILD
3450 extern "C" int APIENTRY DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpvReserved) {
3451 //Platform::DebugPrintf("Scintilla::DllMain %d %d\n", hInstance, dwReason);
3452 if (dwReason == DLL_PROCESS_ATTACH) {
3453 if (!Scintilla_RegisterClasses(hInstance))
3454 return FALSE;
3455 } else if (dwReason == DLL_PROCESS_DETACH) {
3456 if (lpvReserved == NULL) {
3457 ResourcesRelease(true);
3460 return TRUE;
3462 #endif