Update Scintilla to 3.7.2
[TortoiseGit.git] / ext / scintilla / win32 / ScintillaWin.cxx
blob4dea4b19e9bbae8acb497603c1c6df3bc7cccd4b
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 <stdlib.h>
9 #include <string.h>
10 #include <stdio.h>
11 #include <assert.h>
12 #include <ctype.h>
13 #include <limits.h>
15 #include <cmath>
16 #include <stdexcept>
17 #include <new>
18 #include <string>
19 #include <vector>
20 #include <map>
21 #include <algorithm>
23 #undef _WIN32_WINNT
24 #define _WIN32_WINNT 0x0500
25 #undef WINVER
26 #define WINVER 0x0500
27 #include <windows.h>
28 #include <commctrl.h>
29 #include <richedit.h>
30 #include <windowsx.h>
31 #include <zmouse.h>
32 #include <ole2.h>
34 #if defined(NTDDI_WIN7) && !defined(DISABLE_D2D)
35 #define USE_D2D 1
36 #endif
38 #if defined(USE_D2D)
39 #include <d2d1.h>
40 #include <dwrite.h>
41 #endif
43 #include "Platform.h"
45 #include "ILexer.h"
46 #include "Scintilla.h"
48 #ifdef SCI_LEXER
49 #include "SciLexer.h"
50 #endif
51 #include "StringCopy.h"
52 #ifdef SCI_LEXER
53 #include "LexerModule.h"
54 #endif
55 #include "Position.h"
56 #include "SplitVector.h"
57 #include "Partitioning.h"
58 #include "RunStyles.h"
59 #include "ContractionState.h"
60 #include "CellBuffer.h"
61 #include "CallTip.h"
62 #include "KeyMap.h"
63 #include "Indicator.h"
64 #include "XPM.h"
65 #include "LineMarker.h"
66 #include "Style.h"
67 #include "ViewStyle.h"
68 #include "CharClassify.h"
69 #include "Decoration.h"
70 #include "CaseFolder.h"
71 #include "Document.h"
72 #include "CaseConvert.h"
73 #include "UniConversion.h"
74 #include "Selection.h"
75 #include "PositionCache.h"
76 #include "EditModel.h"
77 #include "MarginView.h"
78 #include "EditView.h"
79 #include "Editor.h"
81 #include "AutoComplete.h"
82 #include "ScintillaBase.h"
84 #ifdef SCI_LEXER
85 #include "ExternalLexer.h"
86 #endif
88 #include "PlatWin.h"
89 #include "HanjaDic.h"
91 #ifndef SPI_GETWHEELSCROLLLINES
92 #define SPI_GETWHEELSCROLLLINES 104
93 #endif
95 #ifndef WM_UNICHAR
96 #define WM_UNICHAR 0x0109
97 #endif
99 #ifndef UNICODE_NOCHAR
100 #define UNICODE_NOCHAR 0xFFFF
101 #endif
103 #ifndef IS_HIGH_SURROGATE
104 #define IS_HIGH_SURROGATE(x) ((x) >= SURROGATE_LEAD_FIRST && (x) <= SURROGATE_LEAD_LAST)
105 #endif
107 #ifndef IS_LOW_SURROGATE
108 #define IS_LOW_SURROGATE(x) ((x) >= SURROGATE_TRAIL_FIRST && (x) <= SURROGATE_TRAIL_LAST)
109 #endif
111 #ifndef MK_ALT
112 #define MK_ALT 32
113 #endif
115 #define SC_WIN_IDLE 5001
117 #define SC_INDICATOR_INPUT INDIC_IME
118 #define SC_INDICATOR_TARGET INDIC_IME+1
119 #define SC_INDICATOR_CONVERTED INDIC_IME+2
120 #define SC_INDICATOR_UNKNOWN INDIC_IME_MAX
122 #ifndef SCS_CAP_SETRECONVERTSTRING
123 #define SCS_CAP_SETRECONVERTSTRING 0x00000004
124 #define SCS_QUERYRECONVERTSTRING 0x00020000
125 #define SCS_SETRECONVERTSTRING 0x00010000
126 #endif
128 typedef BOOL (WINAPI *TrackMouseEventSig)(LPTRACKMOUSEEVENT);
129 typedef UINT_PTR (WINAPI *SetCoalescableTimerSig)(HWND hwnd, UINT_PTR nIDEvent,
130 UINT uElapse, TIMERPROC lpTimerFunc, ULONG uToleranceDelay);
132 // GCC has trouble with the standard COM ABI so do it the old C way with explicit vtables.
134 const TCHAR callClassName[] = TEXT("CallTip");
136 #ifdef SCI_NAMESPACE
137 using namespace Scintilla;
138 #endif
140 static void *PointerFromWindow(HWND hWnd) {
141 return reinterpret_cast<void *>(::GetWindowLongPtr(hWnd, 0));
144 static void SetWindowPointer(HWND hWnd, void *ptr) {
145 ::SetWindowLongPtr(hWnd, 0, reinterpret_cast<LONG_PTR>(ptr));
148 static void SetWindowID(HWND hWnd, int identifier) {
149 ::SetWindowLongPtr(hWnd, GWLP_ID, identifier);
152 static Point PointFromPOINT(POINT pt) {
153 return Point::FromInts(pt.x, pt.y);
156 class ScintillaWin; // Forward declaration for COM interface subobjects
158 typedef void VFunction(void);
160 static HMODULE commctrl32 = 0;
164 class FormatEnumerator {
165 public:
166 VFunction **vtbl;
167 int ref;
168 unsigned int pos;
169 std::vector<CLIPFORMAT> formats;
170 FormatEnumerator(int pos_, CLIPFORMAT formats_[], size_t formatsLen_);
175 class DropSource {
176 public:
177 VFunction **vtbl;
178 ScintillaWin *sci;
179 DropSource();
184 class DataObject {
185 public:
186 VFunction **vtbl;
187 ScintillaWin *sci;
188 DataObject();
193 class DropTarget {
194 public:
195 VFunction **vtbl;
196 ScintillaWin *sci;
197 DropTarget();
200 namespace {
202 class IMContext {
203 HWND hwnd;
204 public:
205 HIMC hIMC;
206 IMContext(HWND hwnd_) :
207 hwnd(hwnd_), hIMC(::ImmGetContext(hwnd_)) {
209 ~IMContext() {
210 if (hIMC)
211 ::ImmReleaseContext(hwnd, hIMC);
214 unsigned int GetImeCaretPos() {
215 return ImmGetCompositionStringW(hIMC, GCS_CURSORPOS, NULL, 0);
218 std::vector<BYTE> GetImeAttributes() {
219 int attrLen = ::ImmGetCompositionStringW(hIMC, GCS_COMPATTR, NULL, 0);
220 std::vector<BYTE> attr(attrLen, 0);
221 ::ImmGetCompositionStringW(hIMC, GCS_COMPATTR, &attr[0], static_cast<DWORD>(attr.size()));
222 return attr;
225 std::wstring GetCompositionString(DWORD dwIndex) {
226 const LONG byteLen = ::ImmGetCompositionStringW(hIMC, dwIndex, NULL, 0);
227 std::wstring wcs(byteLen / 2, 0);
228 ::ImmGetCompositionStringW(hIMC, dwIndex, &wcs[0], byteLen);
229 return wcs;
237 class ScintillaWin :
238 public ScintillaBase {
240 bool lastKeyDownConsumed;
241 wchar_t lastHighSurrogateChar;
243 bool capturedMouse;
244 bool trackedMouseLeave;
245 TrackMouseEventSig TrackMouseEventFn;
246 SetCoalescableTimerSig SetCoalescableTimerFn;
248 unsigned int linesPerScroll; ///< Intellimouse support
249 int wheelDelta; ///< Wheel delta from roll
251 HRGN hRgnUpdate;
253 bool hasOKText;
255 CLIPFORMAT cfColumnSelect;
256 CLIPFORMAT cfBorlandIDEBlockType;
257 CLIPFORMAT cfLineSelect;
258 CLIPFORMAT cfVSLineTag;
260 HRESULT hrOle;
261 DropSource ds;
262 DataObject dob;
263 DropTarget dt;
265 static HINSTANCE hInstance;
266 static ATOM scintillaClassAtom;
267 static ATOM callClassAtom;
269 #if defined(USE_D2D)
270 ID2D1RenderTarget *pRenderTarget;
271 bool renderTargetValid;
272 #endif
274 explicit ScintillaWin(HWND hwnd);
275 ScintillaWin(const ScintillaWin &);
276 virtual ~ScintillaWin();
277 ScintillaWin &operator=(const ScintillaWin &);
279 virtual void Initialise();
280 virtual void Finalise();
281 #if defined(USE_D2D)
282 void EnsureRenderTarget(HDC hdc);
283 void DropRenderTarget();
284 #endif
285 HWND MainHWND();
287 static sptr_t DirectFunction(
288 sptr_t ptr, UINT iMessage, uptr_t wParam, sptr_t lParam);
289 static LRESULT PASCAL SWndProc(
290 HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam);
291 static LRESULT PASCAL CTWndProc(
292 HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam);
294 enum { invalidTimerID, standardTimerID, idleTimerID, fineTimerStart };
296 virtual bool DragThreshold(Point ptStart, Point ptNow);
297 virtual void StartDrag();
298 int TargetAsUTF8(char *text);
299 void AddCharUTF16(wchar_t const *wcs, unsigned int wclen);
300 int EncodedFromUTF8(char *utf8, char *encoded) const;
301 sptr_t WndPaint(uptr_t wParam);
303 sptr_t HandleCompositionWindowed(uptr_t wParam, sptr_t lParam);
304 sptr_t HandleCompositionInline(uptr_t wParam, sptr_t lParam);
305 static bool KoreanIME();
306 void MoveImeCarets(int offset);
307 void DrawImeIndicator(int indicator, int len);
308 void SetCandidateWindowPos();
309 void SelectionToHangul();
310 void EscapeHanja();
311 void ToggleHanja();
312 void AddWString(std::wstring wcs);
314 UINT CodePageOfDocument() const;
315 virtual bool ValidCodePage(int codePage) const;
316 virtual sptr_t DefWndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam);
317 virtual bool SetIdle(bool on);
318 UINT_PTR timers[tickDwell+1];
319 virtual bool FineTickerAvailable();
320 virtual bool FineTickerRunning(TickReason reason);
321 virtual void FineTickerStart(TickReason reason, int millis, int tolerance);
322 virtual void FineTickerCancel(TickReason reason);
323 virtual void SetMouseCapture(bool on);
324 virtual bool HaveMouseCapture();
325 virtual void SetTrackMouseLeaveEvent(bool on);
326 virtual bool PaintContains(PRectangle rc);
327 virtual void ScrollText(int linesToMove);
328 virtual void NotifyCaretMove();
329 virtual void UpdateSystemCaret();
330 virtual void SetVerticalScrollPos();
331 virtual void SetHorizontalScrollPos();
332 virtual bool ModifyScrollBars(int nMax, int nPage);
333 virtual void NotifyChange();
334 virtual void NotifyFocus(bool focus);
335 virtual void SetCtrlID(int identifier);
336 virtual int GetCtrlID();
337 virtual void NotifyParent(SCNotification scn);
338 virtual void NotifyDoubleClick(Point pt, int modifiers);
339 virtual CaseFolder *CaseFolderForEncoding();
340 virtual std::string CaseMapString(const std::string &s, int caseMapping);
341 virtual void Copy();
342 virtual void CopyAllowLine();
343 virtual bool CanPaste();
344 virtual void Paste();
345 virtual void CreateCallTipWindow(PRectangle rc);
346 virtual void AddToPopUp(const char *label, int cmd = 0, bool enabled = true);
347 virtual void ClaimSelection();
349 // DBCS
350 void ImeStartComposition();
351 void ImeEndComposition();
352 LRESULT ImeOnReconvert(LPARAM lParam);
354 void GetIntelliMouseParameters();
355 virtual void CopyToClipboard(const SelectionText &selectedText);
356 void ScrollMessage(WPARAM wParam);
357 void HorizontalScrollMessage(WPARAM wParam);
358 void FullPaint();
359 void FullPaintDC(HDC dc);
360 bool IsCompatibleDC(HDC dc);
361 DWORD EffectFromState(DWORD grfKeyState) const;
363 virtual int SetScrollInfo(int nBar, LPCSCROLLINFO lpsi, BOOL bRedraw);
364 virtual bool GetScrollInfo(int nBar, LPSCROLLINFO lpsi);
365 void ChangeScrollPos(int barType, int pos);
366 sptr_t GetTextLength();
367 sptr_t GetText(uptr_t wParam, sptr_t lParam);
369 public:
370 // Public for benefit of Scintilla_DirectFunction
371 virtual sptr_t WndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam);
373 /// Implement IUnknown
374 STDMETHODIMP QueryInterface(REFIID riid, PVOID *ppv);
375 STDMETHODIMP_(ULONG)AddRef();
376 STDMETHODIMP_(ULONG)Release();
378 /// Implement IDropTarget
379 STDMETHODIMP DragEnter(LPDATAOBJECT pIDataSource, DWORD grfKeyState,
380 POINTL pt, PDWORD pdwEffect);
381 STDMETHODIMP DragOver(DWORD grfKeyState, POINTL pt, PDWORD pdwEffect);
382 STDMETHODIMP DragLeave();
383 STDMETHODIMP Drop(LPDATAOBJECT pIDataSource, DWORD grfKeyState,
384 POINTL pt, PDWORD pdwEffect);
386 /// Implement important part of IDataObject
387 STDMETHODIMP GetData(FORMATETC *pFEIn, STGMEDIUM *pSTM);
389 static bool Register(HINSTANCE hInstance_);
390 static bool Unregister();
392 friend class DropSource;
393 friend class DataObject;
394 friend class DropTarget;
395 bool DragIsRectangularOK(CLIPFORMAT fmt) const {
396 return drag.rectangular && (fmt == cfColumnSelect);
399 private:
400 // For use in creating a system caret
401 bool HasCaretSizeChanged() const;
402 BOOL CreateSystemCaret();
403 BOOL DestroySystemCaret();
404 HBITMAP sysCaretBitmap;
405 int sysCaretWidth;
406 int sysCaretHeight;
409 HINSTANCE ScintillaWin::hInstance = 0;
410 ATOM ScintillaWin::scintillaClassAtom = 0;
411 ATOM ScintillaWin::callClassAtom = 0;
413 ScintillaWin::ScintillaWin(HWND hwnd) {
415 lastKeyDownConsumed = false;
416 lastHighSurrogateChar = 0;
418 capturedMouse = false;
419 trackedMouseLeave = false;
420 TrackMouseEventFn = 0;
421 SetCoalescableTimerFn = 0;
423 linesPerScroll = 0;
424 wheelDelta = 0; // Wheel delta from roll
426 hRgnUpdate = 0;
428 hasOKText = false;
430 // There does not seem to be a real standard for indicating that the clipboard
431 // contains a rectangular selection, so copy Developer Studio and Borland Delphi.
432 cfColumnSelect = static_cast<CLIPFORMAT>(
433 ::RegisterClipboardFormat(TEXT("MSDEVColumnSelect")));
434 cfBorlandIDEBlockType = static_cast<CLIPFORMAT>(
435 ::RegisterClipboardFormat(TEXT("Borland IDE Block Type")));
437 // Likewise for line-copy (copies a full line when no text is selected)
438 cfLineSelect = static_cast<CLIPFORMAT>(
439 ::RegisterClipboardFormat(TEXT("MSDEVLineSelect")));
440 cfVSLineTag = static_cast<CLIPFORMAT>(
441 ::RegisterClipboardFormat(TEXT("VisualStudioEditorOperationsLineCutCopyClipboardTag")));
442 hrOle = E_FAIL;
444 wMain = hwnd;
446 dob.sci = this;
447 ds.sci = this;
448 dt.sci = this;
450 sysCaretBitmap = 0;
451 sysCaretWidth = 0;
452 sysCaretHeight = 0;
454 #if defined(USE_D2D)
455 pRenderTarget = 0;
456 renderTargetValid = true;
457 #endif
459 caret.period = ::GetCaretBlinkTime();
460 if (caret.period < 0)
461 caret.period = 0;
463 Initialise();
466 ScintillaWin::~ScintillaWin() {}
468 void ScintillaWin::Initialise() {
469 // Initialize COM. If the app has already done this it will have
470 // no effect. If the app hasn't, we really shouldn't ask them to call
471 // it just so this internal feature works.
472 hrOle = ::OleInitialize(NULL);
474 // Find TrackMouseEvent which is available on Windows > 95
475 HMODULE user32 = ::GetModuleHandle(TEXT("user32.dll"));
476 if (user32) {
477 TrackMouseEventFn = (TrackMouseEventSig)::GetProcAddress(user32, "TrackMouseEvent");
478 SetCoalescableTimerFn = (SetCoalescableTimerSig)::GetProcAddress(user32, "SetCoalescableTimer");
480 if (TrackMouseEventFn == NULL) {
481 // Windows 95 has an emulation in comctl32.dll:_TrackMouseEvent
482 if (!commctrl32)
483 commctrl32 = ::LoadLibrary(TEXT("comctl32.dll"));
484 if (commctrl32 != NULL) {
485 TrackMouseEventFn = (TrackMouseEventSig)
486 ::GetProcAddress(commctrl32, "_TrackMouseEvent");
489 for (TickReason tr = tickCaret; tr <= tickDwell; tr = static_cast<TickReason>(tr + 1)) {
490 timers[tr] = 0;
492 vs.indicators[SC_INDICATOR_UNKNOWN] = Indicator(INDIC_HIDDEN, ColourDesired(0, 0, 0xff));
493 vs.indicators[SC_INDICATOR_INPUT] = Indicator(INDIC_DOTS, ColourDesired(0, 0, 0xff));
494 vs.indicators[SC_INDICATOR_CONVERTED] = Indicator(INDIC_COMPOSITIONTHICK, ColourDesired(0, 0, 0xff));
495 vs.indicators[SC_INDICATOR_TARGET] = Indicator(INDIC_STRAIGHTBOX, ColourDesired(0, 0, 0xff));
498 void ScintillaWin::Finalise() {
499 ScintillaBase::Finalise();
500 for (TickReason tr = tickCaret; tr <= tickDwell; tr = static_cast<TickReason>(tr + 1)) {
501 FineTickerCancel(tr);
503 SetIdle(false);
504 #if defined(USE_D2D)
505 DropRenderTarget();
506 #endif
507 ::RevokeDragDrop(MainHWND());
508 if (SUCCEEDED(hrOle)) {
509 ::OleUninitialize();
513 #if defined(USE_D2D)
515 void ScintillaWin::EnsureRenderTarget(HDC hdc) {
516 if (!renderTargetValid) {
517 DropRenderTarget();
518 renderTargetValid = true;
520 if (pD2DFactory && !pRenderTarget) {
521 RECT rc;
522 HWND hw = MainHWND();
523 GetClientRect(hw, &rc);
525 D2D1_SIZE_U size = D2D1::SizeU(rc.right - rc.left, rc.bottom - rc.top);
527 // Create a Direct2D render target.
528 #if 1
529 D2D1_RENDER_TARGET_PROPERTIES drtp;
530 drtp.type = D2D1_RENDER_TARGET_TYPE_DEFAULT;
531 drtp.pixelFormat.format = DXGI_FORMAT_UNKNOWN;
532 drtp.pixelFormat.alphaMode = D2D1_ALPHA_MODE_UNKNOWN;
533 drtp.dpiX = 96.0;
534 drtp.dpiY = 96.0;
535 drtp.usage = D2D1_RENDER_TARGET_USAGE_NONE;
536 drtp.minLevel = D2D1_FEATURE_LEVEL_DEFAULT;
538 if (technology == SC_TECHNOLOGY_DIRECTWRITEDC) {
539 // Explicit pixel format needed.
540 drtp.pixelFormat = D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM,
541 D2D1_ALPHA_MODE_IGNORE);
543 ID2D1DCRenderTarget *pDCRT = NULL;
544 HRESULT hr = pD2DFactory->CreateDCRenderTarget(&drtp, &pDCRT);
545 if (SUCCEEDED(hr)) {
546 pRenderTarget = pDCRT;
547 } else {
548 Platform::DebugPrintf("Failed CreateDCRenderTarget 0x%x\n", hr);
549 pRenderTarget = NULL;
552 } else {
553 D2D1_HWND_RENDER_TARGET_PROPERTIES dhrtp;
554 dhrtp.hwnd = hw;
555 dhrtp.pixelSize = size;
556 dhrtp.presentOptions = (technology == SC_TECHNOLOGY_DIRECTWRITERETAIN) ?
557 D2D1_PRESENT_OPTIONS_RETAIN_CONTENTS : D2D1_PRESENT_OPTIONS_NONE;
559 ID2D1HwndRenderTarget *pHwndRenderTarget = NULL;
560 HRESULT hr = pD2DFactory->CreateHwndRenderTarget(drtp, dhrtp, &pHwndRenderTarget);
561 if (SUCCEEDED(hr)) {
562 pRenderTarget = pHwndRenderTarget;
563 } else {
564 Platform::DebugPrintf("Failed CreateHwndRenderTarget 0x%x\n", hr);
565 pRenderTarget = NULL;
568 #else
569 pD2DFactory->CreateHwndRenderTarget(
570 D2D1::RenderTargetProperties(
571 D2D1_RENDER_TARGET_TYPE_DEFAULT ,
572 D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_PREMULTIPLIED),
573 96.0f, 96.0f, D2D1_RENDER_TARGET_USAGE_NONE, D2D1_FEATURE_LEVEL_DEFAULT),
574 D2D1::HwndRenderTargetProperties(hw, size),
575 &pRenderTarget);
576 #endif
577 // Pixmaps were created to be compatible with previous render target so
578 // need to be recreated.
579 DropGraphics(false);
582 if ((technology == SC_TECHNOLOGY_DIRECTWRITEDC) && pRenderTarget) {
583 RECT rcWindow;
584 GetClientRect(MainHWND(), &rcWindow);
585 HRESULT hr = static_cast<ID2D1DCRenderTarget*>(pRenderTarget)->BindDC(hdc, &rcWindow);
586 if (FAILED(hr)) {
587 Platform::DebugPrintf("BindDC failed 0x%x\n", hr);
588 DropRenderTarget();
593 void ScintillaWin::DropRenderTarget() {
594 if (pRenderTarget) {
595 pRenderTarget->Release();
596 pRenderTarget = 0;
600 #endif
602 HWND ScintillaWin::MainHWND() {
603 return static_cast<HWND>(wMain.GetID());
606 bool ScintillaWin::DragThreshold(Point ptStart, Point ptNow) {
607 int xMove = static_cast<int>(std::abs(ptStart.x - ptNow.x));
608 int yMove = static_cast<int>(std::abs(ptStart.y - ptNow.y));
609 return (xMove > ::GetSystemMetrics(SM_CXDRAG)) ||
610 (yMove > ::GetSystemMetrics(SM_CYDRAG));
613 void ScintillaWin::StartDrag() {
614 inDragDrop = ddDragging;
615 DWORD dwEffect = 0;
616 dropWentOutside = true;
617 IDataObject *pDataObject = reinterpret_cast<IDataObject *>(&dob);
618 IDropSource *pDropSource = reinterpret_cast<IDropSource *>(&ds);
619 //Platform::DebugPrintf("About to DoDragDrop %x %x\n", pDataObject, pDropSource);
620 HRESULT hr = ::DoDragDrop(
621 pDataObject,
622 pDropSource,
623 DROPEFFECT_COPY | DROPEFFECT_MOVE, &dwEffect);
624 //Platform::DebugPrintf("DoDragDrop = %x\n", hr);
625 if (SUCCEEDED(hr)) {
626 if ((hr == DRAGDROP_S_DROP) && (dwEffect == DROPEFFECT_MOVE) && dropWentOutside) {
627 // Remove dragged out text
628 ClearSelection();
631 inDragDrop = ddNone;
632 SetDragPosition(SelectionPosition(invalidPosition));
635 // Avoid warnings everywhere for old style casts by concentrating them here
636 static WORD LoWord(uptr_t l) {
637 return LOWORD(l);
640 static WORD HiWord(uptr_t l) {
641 return HIWORD(l);
644 static int InputCodePage() {
645 HKL inputLocale = ::GetKeyboardLayout(0);
646 LANGID inputLang = LOWORD(inputLocale);
647 char sCodePage[10];
648 int res = ::GetLocaleInfoA(MAKELCID(inputLang, SORT_DEFAULT),
649 LOCALE_IDEFAULTANSICODEPAGE, sCodePage, sizeof(sCodePage));
650 if (!res)
651 return 0;
652 return atoi(sCodePage);
655 /** Map the key codes to their equivalent SCK_ form. */
656 static int KeyTranslate(int keyIn) {
657 //PLATFORM_ASSERT(!keyIn);
658 switch (keyIn) {
659 case VK_DOWN: return SCK_DOWN;
660 case VK_UP: return SCK_UP;
661 case VK_LEFT: return SCK_LEFT;
662 case VK_RIGHT: return SCK_RIGHT;
663 case VK_HOME: return SCK_HOME;
664 case VK_END: return SCK_END;
665 case VK_PRIOR: return SCK_PRIOR;
666 case VK_NEXT: return SCK_NEXT;
667 case VK_DELETE: return SCK_DELETE;
668 case VK_INSERT: return SCK_INSERT;
669 case VK_ESCAPE: return SCK_ESCAPE;
670 case VK_BACK: return SCK_BACK;
671 case VK_TAB: return SCK_TAB;
672 case VK_RETURN: return SCK_RETURN;
673 case VK_ADD: return SCK_ADD;
674 case VK_SUBTRACT: return SCK_SUBTRACT;
675 case VK_DIVIDE: return SCK_DIVIDE;
676 case VK_LWIN: return SCK_WIN;
677 case VK_RWIN: return SCK_RWIN;
678 case VK_APPS: return SCK_MENU;
679 case VK_OEM_2: return '/';
680 case VK_OEM_3: return '`';
681 case VK_OEM_4: return '[';
682 case VK_OEM_5: return '\\';
683 case VK_OEM_6: return ']';
684 default: return keyIn;
688 static bool BoundsContains(PRectangle rcBounds, HRGN hRgnBounds, PRectangle rcCheck) {
689 bool contains = true;
690 if (!rcCheck.Empty()) {
691 if (!rcBounds.Contains(rcCheck)) {
692 contains = false;
693 } else if (hRgnBounds) {
694 // In bounding rectangle so check more accurately using region
695 HRGN hRgnCheck = ::CreateRectRgn(static_cast<int>(rcCheck.left), static_cast<int>(rcCheck.top),
696 static_cast<int>(rcCheck.right), static_cast<int>(rcCheck.bottom));
697 if (hRgnCheck) {
698 HRGN hRgnDifference = ::CreateRectRgn(0, 0, 0, 0);
699 if (hRgnDifference) {
700 int combination = ::CombineRgn(hRgnDifference, hRgnCheck, hRgnBounds, RGN_DIFF);
701 if (combination != NULLREGION) {
702 contains = false;
704 ::DeleteRgn(hRgnDifference);
706 ::DeleteRgn(hRgnCheck);
710 return contains;
713 static std::string StringEncode(const std::wstring &s, int codePage) {
714 const int cchMulti = s.length() ? ::WideCharToMultiByte(codePage, 0, s.c_str(), static_cast<int>(s.length()), NULL, 0, NULL, NULL) : 0;
715 std::string sMulti(cchMulti, 0);
716 if (cchMulti) {
717 ::WideCharToMultiByte(codePage, 0, s.c_str(), static_cast<int>(s.size()), &sMulti[0], cchMulti, NULL, NULL);
719 return sMulti;
722 static std::wstring StringDecode(const std::string &s, int codePage) {
723 const int cchWide = s.length() ? ::MultiByteToWideChar(codePage, 0, s.c_str(), static_cast<int>(s.length()), NULL, 0) : 0;
724 std::wstring sWide(cchWide, 0);
725 if (cchWide) {
726 ::MultiByteToWideChar(codePage, 0, s.c_str(), static_cast<int>(s.length()), &sWide[0], cchWide);
728 return sWide;
731 static std::wstring StringMapCase(const std::wstring &ws, DWORD mapFlags) {
732 const int charsConverted = ::LCMapStringW(LOCALE_SYSTEM_DEFAULT, mapFlags,
733 ws.c_str(), static_cast<int>(ws.length()), NULL, 0);
734 std::wstring wsConverted(charsConverted, 0);
735 if (charsConverted) {
736 ::LCMapStringW(LOCALE_SYSTEM_DEFAULT, mapFlags,
737 ws.c_str(), static_cast<int>(ws.length()), &wsConverted[0], charsConverted);
739 return wsConverted;
742 // Returns the target converted to UTF8.
743 // Return the length in bytes.
744 int ScintillaWin::TargetAsUTF8(char *text) {
745 int targetLength = targetEnd - targetStart;
746 if (IsUnicodeMode()) {
747 if (text) {
748 pdoc->GetCharRange(text, targetStart, targetLength);
750 } else {
751 // Need to convert
752 const std::string s = RangeText(targetStart, targetEnd);
753 const std::wstring characters = StringDecode(s, CodePageOfDocument());
754 const int utf8Len = ::WideCharToMultiByte(CP_UTF8, 0, characters.c_str(), static_cast<int>(characters.length()), NULL, 0, 0, 0);
755 if (text) {
756 ::WideCharToMultiByte(CP_UTF8, 0, characters.c_str(), static_cast<int>(characters.length()), text, utf8Len, 0, 0);
757 text[utf8Len] = '\0';
759 return utf8Len;
761 return targetLength;
764 // Translates a nul terminated UTF8 string into the document encoding.
765 // Return the length of the result in bytes.
766 int ScintillaWin::EncodedFromUTF8(char *utf8, char *encoded) const {
767 int inputLength = (lengthForEncode >= 0) ? lengthForEncode : static_cast<int>(strlen(utf8));
768 if (IsUnicodeMode()) {
769 if (encoded) {
770 memcpy(encoded, utf8, inputLength);
772 return inputLength;
773 } else {
774 // Need to convert
775 int charsLen = ::MultiByteToWideChar(CP_UTF8, 0, utf8, inputLength, NULL, 0);
776 std::wstring characters(charsLen, '\0');
777 ::MultiByteToWideChar(CP_UTF8, 0, utf8, inputLength, &characters[0], charsLen);
779 int encodedLen = ::WideCharToMultiByte(CodePageOfDocument(),
780 0, &characters[0], charsLen, NULL, 0, 0, 0);
781 if (encoded) {
782 ::WideCharToMultiByte(CodePageOfDocument(), 0, &characters[0], charsLen, encoded, encodedLen, 0, 0);
783 encoded[encodedLen] = '\0';
785 return encodedLen;
789 // Add one character from a UTF-16 string, by converting to either UTF-8 or
790 // the current codepage. Code is similar to HandleCompositionWindowed().
791 void ScintillaWin::AddCharUTF16(wchar_t const *wcs, unsigned int wclen) {
792 if (IsUnicodeMode()) {
793 char utfval[maxLenInputIME * 3];
794 unsigned int len = UTF8Length(wcs, wclen);
795 UTF8FromUTF16(wcs, wclen, utfval, len);
796 utfval[len] = '\0';
797 AddCharUTF(utfval, len);
798 } else {
799 UINT cpDest = CodePageOfDocument();
800 char inBufferCP[maxLenInputIME * 2];
801 int size = ::WideCharToMultiByte(cpDest,
802 0, wcs, wclen, inBufferCP, sizeof(inBufferCP) - 1, 0, 0);
803 for (int i=0; i<size; i++) {
804 AddChar(inBufferCP[i]);
809 sptr_t ScintillaWin::WndPaint(uptr_t wParam) {
810 //ElapsedTime et;
812 // Redirect assertions to debug output and save current state
813 bool assertsPopup = Platform::ShowAssertionPopUps(false);
814 paintState = painting;
815 PAINTSTRUCT ps;
816 PAINTSTRUCT *pps;
818 bool IsOcxCtrl = (wParam != 0); // if wParam != 0, it contains
819 // a PAINSTRUCT* from the OCX
820 // Removed since this interferes with reporting other assertions as it occurs repeatedly
821 //PLATFORM_ASSERT(hRgnUpdate == NULL);
822 hRgnUpdate = ::CreateRectRgn(0, 0, 0, 0);
823 if (IsOcxCtrl) {
824 pps = reinterpret_cast<PAINTSTRUCT*>(wParam);
825 } else {
826 ::GetUpdateRgn(MainHWND(), hRgnUpdate, FALSE);
827 pps = &ps;
828 ::BeginPaint(MainHWND(), pps);
830 rcPaint = PRectangle::FromInts(pps->rcPaint.left, pps->rcPaint.top, pps->rcPaint.right, pps->rcPaint.bottom);
831 PRectangle rcClient = GetClientRectangle();
832 paintingAllText = BoundsContains(rcPaint, hRgnUpdate, rcClient);
833 if (technology == SC_TECHNOLOGY_DEFAULT) {
834 AutoSurface surfaceWindow(pps->hdc, this);
835 if (surfaceWindow) {
836 Paint(surfaceWindow, rcPaint);
837 surfaceWindow->Release();
839 } else {
840 #if defined(USE_D2D)
841 EnsureRenderTarget(pps->hdc);
842 AutoSurface surfaceWindow(pRenderTarget, this);
843 if (surfaceWindow) {
844 pRenderTarget->BeginDraw();
845 Paint(surfaceWindow, rcPaint);
846 surfaceWindow->Release();
847 HRESULT hr = pRenderTarget->EndDraw();
848 if (hr == static_cast<HRESULT>(D2DERR_RECREATE_TARGET)) {
849 DropRenderTarget();
850 paintState = paintAbandoned;
853 #endif
855 if (hRgnUpdate) {
856 ::DeleteRgn(hRgnUpdate);
857 hRgnUpdate = 0;
860 if (!IsOcxCtrl)
861 ::EndPaint(MainHWND(), pps);
862 if (paintState == paintAbandoned) {
863 // Painting area was insufficient to cover new styling or brace highlight positions
864 if (IsOcxCtrl) {
865 FullPaintDC(pps->hdc);
866 } else {
867 FullPaint();
870 paintState = notPainting;
872 // Restore debug output state
873 Platform::ShowAssertionPopUps(assertsPopup);
875 //Platform::DebugPrintf("Paint took %g\n", et.Duration());
876 return 0l;
879 sptr_t ScintillaWin::HandleCompositionWindowed(uptr_t wParam, sptr_t lParam) {
880 if (lParam & GCS_RESULTSTR) {
881 IMContext imc(MainHWND());
882 if (imc.hIMC) {
883 AddWString(imc.GetCompositionString(GCS_RESULTSTR));
885 // Set new position after converted
886 Point pos = PointMainCaret();
887 COMPOSITIONFORM CompForm;
888 CompForm.dwStyle = CFS_POINT;
889 CompForm.ptCurrentPos.x = static_cast<int>(pos.x);
890 CompForm.ptCurrentPos.y = static_cast<int>(pos.y);
891 ::ImmSetCompositionWindow(imc.hIMC, &CompForm);
893 return 0;
895 return ::DefWindowProc(MainHWND(), WM_IME_COMPOSITION, wParam, lParam);
898 bool ScintillaWin::KoreanIME() {
899 const int codePage = InputCodePage();
900 return codePage == 949 || codePage == 1361;
903 void ScintillaWin::MoveImeCarets(int offset) {
904 // Move carets relatively by bytes.
905 for (size_t r=0; r<sel.Count(); r++) {
906 int positionInsert = sel.Range(r).Start().Position();
907 sel.Range(r).caret.SetPosition(positionInsert + offset);
908 sel.Range(r).anchor.SetPosition(positionInsert + offset);
912 void ScintillaWin::DrawImeIndicator(int indicator, int len) {
913 // Emulate the visual style of IME characters with indicators.
914 // Draw an indicator on the character before caret by the character bytes of len
915 // so it should be called after addCharUTF().
916 // It does not affect caret positions.
917 if (indicator < 8 || indicator > INDIC_MAX) {
918 return;
920 pdoc->decorations.SetCurrentIndicator(indicator);
921 for (size_t r=0; r<sel.Count(); r++) {
922 int positionInsert = sel.Range(r).Start().Position();
923 pdoc->DecorationFillRange(positionInsert - len, 1, len);
927 void ScintillaWin::SetCandidateWindowPos() {
928 IMContext imc(MainHWND());
929 if (imc.hIMC) {
930 Point pos = PointMainCaret();
931 CANDIDATEFORM CandForm;
932 CandForm.dwIndex = 0;
933 CandForm.dwStyle = CFS_CANDIDATEPOS;
934 CandForm.ptCurrentPos.x = static_cast<int>(pos.x);
935 CandForm.ptCurrentPos.y = static_cast<int>(pos.y + vs.lineHeight);
936 ::ImmSetCandidateWindow(imc.hIMC, &CandForm);
940 void ScintillaWin::SelectionToHangul() {
941 // Convert every hanja to hangul within the main range.
942 const int selStart = sel.RangeMain().Start().Position();
943 const int documentStrLen = sel.RangeMain().Length();
944 const int selEnd = selStart + documentStrLen;
945 const int utf16Len = pdoc->CountUTF16(selStart, selEnd);
947 if (utf16Len > 0) {
948 std::string documentStr(documentStrLen, '\0');
949 pdoc->GetCharRange(&documentStr[0], selStart, documentStrLen);
951 std::wstring uniStr = StringDecode(documentStr, CodePageOfDocument());
952 int converted = HanjaDict::GetHangulOfHanja(&uniStr[0]);
953 documentStr = StringEncode(uniStr, CodePageOfDocument());
955 if (converted > 0) {
956 pdoc->BeginUndoAction();
957 ClearSelection();
958 InsertPaste(&documentStr[0], static_cast<int>(documentStr.size()));
959 pdoc->EndUndoAction();
964 void ScintillaWin::EscapeHanja() {
965 // The candidate box pops up to user to select a hanja.
966 // It comes into WM_IME_COMPOSITION with GCS_RESULTSTR.
967 // The existing hangul or hanja is replaced with it.
968 if (sel.Count() > 1) {
969 return; // Do not allow multi carets.
971 int currentPos = CurrentPosition();
972 int oneCharLen = pdoc->LenChar(currentPos);
974 if (oneCharLen < 2) {
975 return; // No need to handle SBCS.
978 // ImmEscapeW() may overwrite uniChar[] with a null terminated string.
979 // So enlarge it enough to Maximum 4 as in UTF-8.
980 unsigned int const safeLength = UTF8MaxBytes+1;
981 std::string oneChar(safeLength, '\0');
982 pdoc->GetCharRange(&oneChar[0], currentPos, oneCharLen);
984 std::wstring uniChar = StringDecode(oneChar, CodePageOfDocument());
986 IMContext imc(MainHWND());
987 if (imc.hIMC) {
988 // Set the candidate box position since IME may show it.
989 SetCandidateWindowPos();
990 // IME_ESC_HANJA_MODE appears to receive the first character only.
991 if (ImmEscapeW(GetKeyboardLayout(0), imc.hIMC, IME_ESC_HANJA_MODE, &uniChar[0])) {
992 SetSelection(currentPos, currentPos + oneCharLen);
997 void ScintillaWin::ToggleHanja() {
998 // If selection, convert every hanja to hangul within the main range.
999 // If no selection, commit to IME.
1000 if (sel.Count() > 1) {
1001 return; // Do not allow multi carets.
1004 if (sel.Empty()) {
1005 EscapeHanja();
1006 } else {
1007 SelectionToHangul();
1011 namespace {
1013 std::vector<int> MapImeIndicators(std::vector<BYTE> inputStyle) {
1014 std::vector<int> imeIndicator(inputStyle.size(), SC_INDICATOR_UNKNOWN);
1015 for (size_t i = 0; i < inputStyle.size(); i++) {
1016 switch (static_cast<int>(inputStyle.at(i))) {
1017 case ATTR_INPUT:
1018 imeIndicator[i] = SC_INDICATOR_INPUT;
1019 break;
1020 case ATTR_TARGET_NOTCONVERTED:
1021 case ATTR_TARGET_CONVERTED:
1022 imeIndicator[i] = SC_INDICATOR_TARGET;
1023 break;
1024 case ATTR_CONVERTED:
1025 imeIndicator[i] = SC_INDICATOR_CONVERTED;
1026 break;
1027 default:
1028 imeIndicator[i] = SC_INDICATOR_UNKNOWN;
1029 break;
1032 return imeIndicator;
1037 void ScintillaWin::AddWString(std::wstring wcs) {
1038 if (wcs.empty())
1039 return;
1041 int codePage = CodePageOfDocument();
1042 for (size_t i = 0; i < wcs.size(); ) {
1043 const size_t ucWidth = UTF16CharLength(wcs[i]);
1044 const std::wstring uniChar(wcs, i, ucWidth);
1045 std::string docChar = StringEncode(uniChar, codePage);
1047 AddCharUTF(docChar.c_str(), static_cast<unsigned int>(docChar.size()));
1048 i += ucWidth;
1052 sptr_t ScintillaWin::HandleCompositionInline(uptr_t, sptr_t lParam) {
1053 // Copy & paste by johnsonj with a lot of helps of Neil.
1054 // Great thanks for my foreruners, jiniya and BLUEnLIVE.
1056 IMContext imc(MainHWND());
1057 if (!imc.hIMC)
1058 return 0;
1059 if (pdoc->IsReadOnly() || SelectionContainsProtected()) {
1060 ::ImmNotifyIME(imc.hIMC, NI_COMPOSITIONSTR, CPS_CANCEL, 0);
1061 return 0;
1064 if (pdoc->TentativeActive()) {
1065 pdoc->TentativeUndo();
1066 } else {
1067 // No tentative undo means start of this composition so
1068 // fill in any virtual spaces.
1069 ClearBeforeTentativeStart();
1072 view.imeCaretBlockOverride = false;
1074 if (lParam & GCS_COMPSTR) {
1075 const std::wstring wcs = imc.GetCompositionString(GCS_COMPSTR);
1076 if ((wcs.size() == 0) || (wcs.size() >= maxLenInputIME)) {
1077 ShowCaretAtCurrentPosition();
1078 return 0;
1081 pdoc->TentativeStart(); // TentativeActive from now on.
1083 std::vector<int> imeIndicator = MapImeIndicators(imc.GetImeAttributes());
1085 bool tmpRecordingMacro = recordingMacro;
1086 recordingMacro = false;
1087 int codePage = CodePageOfDocument();
1088 for (size_t i = 0; i < wcs.size(); ) {
1089 const size_t ucWidth = UTF16CharLength(wcs[i]);
1090 const std::wstring uniChar(wcs, i, ucWidth);
1091 std::string docChar = StringEncode(uniChar, codePage);
1093 AddCharUTF(docChar.c_str(), static_cast<unsigned int>(docChar.size()));
1095 DrawImeIndicator(imeIndicator[i], static_cast<unsigned int>(docChar.size()));
1096 i += ucWidth;
1098 recordingMacro = tmpRecordingMacro;
1100 // Move IME caret from current last position to imeCaretPos.
1101 int imeEndToImeCaretU16 = imc.GetImeCaretPos() - static_cast<unsigned int>(wcs.size());
1102 int imeCaretPosDoc = pdoc->GetRelativePositionUTF16(CurrentPosition(), imeEndToImeCaretU16);
1104 MoveImeCarets(- CurrentPosition() + imeCaretPosDoc);
1106 if (KoreanIME()) {
1107 view.imeCaretBlockOverride = true;
1109 } else if (lParam & GCS_RESULTSTR) {
1110 AddWString(imc.GetCompositionString(GCS_RESULTSTR));
1112 EnsureCaretVisible();
1113 SetCandidateWindowPos();
1114 ShowCaretAtCurrentPosition();
1115 return 0;
1118 // Translate message IDs from WM_* and EM_* to SCI_* so can partly emulate Windows Edit control
1119 static unsigned int SciMessageFromEM(unsigned int iMessage) {
1120 switch (iMessage) {
1121 case EM_CANPASTE: return SCI_CANPASTE;
1122 case EM_CANUNDO: return SCI_CANUNDO;
1123 case EM_EMPTYUNDOBUFFER: return SCI_EMPTYUNDOBUFFER;
1124 case EM_FINDTEXTEX: return SCI_FINDTEXT;
1125 case EM_FORMATRANGE: return SCI_FORMATRANGE;
1126 case EM_GETFIRSTVISIBLELINE: return SCI_GETFIRSTVISIBLELINE;
1127 case EM_GETLINECOUNT: return SCI_GETLINECOUNT;
1128 case EM_GETSELTEXT: return SCI_GETSELTEXT;
1129 case EM_GETTEXTRANGE: return SCI_GETTEXTRANGE;
1130 case EM_HIDESELECTION: return SCI_HIDESELECTION;
1131 case EM_LINEINDEX: return SCI_POSITIONFROMLINE;
1132 case EM_LINESCROLL: return SCI_LINESCROLL;
1133 case EM_REPLACESEL: return SCI_REPLACESEL;
1134 case EM_SCROLLCARET: return SCI_SCROLLCARET;
1135 case EM_SETREADONLY: return SCI_SETREADONLY;
1136 case WM_CLEAR: return SCI_CLEAR;
1137 case WM_COPY: return SCI_COPY;
1138 case WM_CUT: return SCI_CUT;
1139 case WM_SETTEXT: return SCI_SETTEXT;
1140 case WM_PASTE: return SCI_PASTE;
1141 case WM_UNDO: return SCI_UNDO;
1143 return iMessage;
1146 UINT CodePageFromCharSet(DWORD characterSet, UINT documentCodePage) {
1147 if (documentCodePage == SC_CP_UTF8) {
1148 return SC_CP_UTF8;
1150 switch (characterSet) {
1151 case SC_CHARSET_ANSI: return 1252;
1152 case SC_CHARSET_DEFAULT: return documentCodePage ? documentCodePage : 1252;
1153 case SC_CHARSET_BALTIC: return 1257;
1154 case SC_CHARSET_CHINESEBIG5: return 950;
1155 case SC_CHARSET_EASTEUROPE: return 1250;
1156 case SC_CHARSET_GB2312: return 936;
1157 case SC_CHARSET_GREEK: return 1253;
1158 case SC_CHARSET_HANGUL: return 949;
1159 case SC_CHARSET_MAC: return 10000;
1160 case SC_CHARSET_OEM: return 437;
1161 case SC_CHARSET_RUSSIAN: return 1251;
1162 case SC_CHARSET_SHIFTJIS: return 932;
1163 case SC_CHARSET_TURKISH: return 1254;
1164 case SC_CHARSET_JOHAB: return 1361;
1165 case SC_CHARSET_HEBREW: return 1255;
1166 case SC_CHARSET_ARABIC: return 1256;
1167 case SC_CHARSET_VIETNAMESE: return 1258;
1168 case SC_CHARSET_THAI: return 874;
1169 case SC_CHARSET_8859_15: return 28605;
1170 // Not supported
1171 case SC_CHARSET_CYRILLIC: return documentCodePage;
1172 case SC_CHARSET_SYMBOL: return documentCodePage;
1174 return documentCodePage;
1177 UINT ScintillaWin::CodePageOfDocument() const {
1178 return CodePageFromCharSet(vs.styles[STYLE_DEFAULT].characterSet, pdoc->dbcsCodePage);
1181 sptr_t ScintillaWin::GetTextLength() {
1182 if (pdoc->Length() == 0)
1183 return 0;
1184 std::vector<char> docBytes(pdoc->Length(), '\0');
1185 pdoc->GetCharRange(&docBytes[0], 0, pdoc->Length());
1186 if (IsUnicodeMode()) {
1187 return UTF16Length(&docBytes[0], static_cast<unsigned int>(docBytes.size()));
1188 } else {
1189 return ::MultiByteToWideChar(CodePageOfDocument(), 0, &docBytes[0],
1190 static_cast<int>(docBytes.size()), NULL, 0);
1194 sptr_t ScintillaWin::GetText(uptr_t wParam, sptr_t lParam) {
1195 wchar_t *ptr = reinterpret_cast<wchar_t *>(lParam);
1196 if (pdoc->Length() == 0) {
1197 *ptr = L'\0';
1198 return 0;
1200 std::vector<char> docBytes(pdoc->Length(), '\0');
1201 pdoc->GetCharRange(&docBytes[0], 0, pdoc->Length());
1202 if (IsUnicodeMode()) {
1203 size_t lengthUTF16 = UTF16Length(&docBytes[0], static_cast<unsigned int>(docBytes.size()));
1204 if (lParam == 0)
1205 return lengthUTF16;
1206 if (wParam == 0)
1207 return 0;
1208 size_t uLen = UTF16FromUTF8(&docBytes[0], docBytes.size(),
1209 ptr, static_cast<int>(wParam) - 1);
1210 ptr[uLen] = L'\0';
1211 return uLen;
1212 } else {
1213 // Not Unicode mode
1214 // Convert to Unicode using the current Scintilla code page
1215 const UINT cpSrc = CodePageOfDocument();
1216 int lengthUTF16 = ::MultiByteToWideChar(cpSrc, 0, &docBytes[0],
1217 static_cast<int>(docBytes.size()), NULL, 0);
1218 if (lengthUTF16 >= static_cast<int>(wParam))
1219 lengthUTF16 = static_cast<int>(wParam)-1;
1220 ::MultiByteToWideChar(cpSrc, 0, &docBytes[0],
1221 static_cast<int>(docBytes.size()),
1222 ptr, lengthUTF16);
1223 ptr[lengthUTF16] = L'\0';
1224 return lengthUTF16;
1228 sptr_t ScintillaWin::WndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
1229 try {
1230 //Platform::DebugPrintf("S M:%x WP:%x L:%x\n", iMessage, wParam, lParam);
1231 iMessage = SciMessageFromEM(iMessage);
1232 switch (iMessage) {
1234 case WM_CREATE:
1235 ctrlID = ::GetDlgCtrlID(static_cast<HWND>(wMain.GetID()));
1236 // Get Intellimouse scroll line parameters
1237 GetIntelliMouseParameters();
1238 ::RegisterDragDrop(MainHWND(), reinterpret_cast<IDropTarget *>(&dt));
1239 break;
1241 case WM_COMMAND:
1242 Command(LoWord(wParam));
1243 break;
1245 case WM_PAINT:
1246 return WndPaint(wParam);
1248 case WM_PRINTCLIENT: {
1249 HDC hdc = reinterpret_cast<HDC>(wParam);
1250 if (!IsCompatibleDC(hdc)) {
1251 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1253 FullPaintDC(hdc);
1255 break;
1257 case WM_VSCROLL:
1258 ScrollMessage(wParam);
1259 break;
1261 case WM_HSCROLL:
1262 HorizontalScrollMessage(wParam);
1263 break;
1265 case WM_SIZE: {
1266 #if defined(USE_D2D)
1267 if (paintState == notPainting) {
1268 DropRenderTarget();
1269 } else {
1270 renderTargetValid = false;
1272 #endif
1273 //Platform::DebugPrintf("Scintilla WM_SIZE %d %d\n", LoWord(lParam), HiWord(lParam));
1274 ChangeSize();
1276 break;
1278 case WM_MOUSEWHEEL:
1279 if (!mouseWheelCaptures) {
1280 // if the mouse wheel is not captured, test if the mouse
1281 // pointer is over the editor window and if not, don't
1282 // handle the message but pass it on.
1283 RECT rc;
1284 GetWindowRect(MainHWND(), &rc);
1285 POINT pt;
1286 pt.x = GET_X_LPARAM(lParam);
1287 pt.y = GET_Y_LPARAM(lParam);
1288 if (!PtInRect(&rc, pt))
1289 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1291 // if autocomplete list active then send mousewheel message to it
1292 if (ac.Active()) {
1293 HWND hWnd = static_cast<HWND>(ac.lb->GetID());
1294 ::SendMessage(hWnd, iMessage, wParam, lParam);
1295 break;
1298 // Don't handle datazoom.
1299 // (A good idea for datazoom would be to "fold" or "unfold" details.
1300 // i.e. if datazoomed out only class structures are visible, when datazooming in the control
1301 // structures appear, then eventually the individual statements...)
1302 if (wParam & MK_SHIFT) {
1303 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1306 // Either SCROLL or ZOOM. We handle the wheel steppings calculation
1307 wheelDelta -= static_cast<short>(HiWord(wParam));
1308 if (abs(wheelDelta) >= WHEEL_DELTA && linesPerScroll > 0) {
1309 int linesToScroll = linesPerScroll;
1310 if (linesPerScroll == WHEEL_PAGESCROLL)
1311 linesToScroll = LinesOnScreen() - 1;
1312 if (linesToScroll == 0) {
1313 linesToScroll = 1;
1315 linesToScroll *= (wheelDelta / WHEEL_DELTA);
1316 if (wheelDelta >= 0)
1317 wheelDelta = wheelDelta % WHEEL_DELTA;
1318 else
1319 wheelDelta = - (-wheelDelta % WHEEL_DELTA);
1321 if (wParam & MK_CONTROL) {
1322 // Zoom! We play with the font sizes in the styles.
1323 // Number of steps/line is ignored, we just care if sizing up or down
1324 if (linesToScroll < 0) {
1325 KeyCommand(SCI_ZOOMIN);
1326 } else {
1327 KeyCommand(SCI_ZOOMOUT);
1329 } else {
1330 // Scroll
1331 ScrollTo(topLine + linesToScroll);
1334 return 0;
1336 case WM_TIMER:
1337 if (wParam == idleTimerID && idler.state) {
1338 SendMessage(MainHWND(), SC_WIN_IDLE, 0, 1);
1339 } else {
1340 TickFor(static_cast<TickReason>(wParam - fineTimerStart));
1342 break;
1344 case SC_WIN_IDLE:
1345 // wParam=dwTickCountInitial, or 0 to initialize. lParam=bSkipUserInputTest
1346 if (idler.state) {
1347 if (lParam || (WAIT_TIMEOUT == MsgWaitForMultipleObjects(0, 0, 0, 0, QS_INPUT|QS_HOTKEY))) {
1348 if (Idle()) {
1349 // User input was given priority above, but all events do get a turn. Other
1350 // messages, notifications, etc. will get interleaved with the idle messages.
1352 // However, some things like WM_PAINT are a lower priority, and will not fire
1353 // when there's a message posted. So, several times a second, we stop and let
1354 // the low priority events have a turn (after which the timer will fire again).
1356 // Suppress a warning from Code Analysis that the GetTickCount function
1357 // wraps after 49 days. The WM_TIMER will kick off another SC_WIN_IDLE
1358 // after the wrap.
1359 #ifdef _MSC_VER
1360 #pragma warning(suppress: 28159)
1361 #endif
1362 DWORD dwCurrent = GetTickCount();
1363 DWORD dwStart = wParam ? static_cast<DWORD>(wParam) : dwCurrent;
1364 const DWORD maxWorkTime = 50;
1366 if (dwCurrent >= dwStart && dwCurrent > maxWorkTime && dwCurrent - maxWorkTime < dwStart)
1367 PostMessage(MainHWND(), SC_WIN_IDLE, dwStart, 0);
1368 } else {
1369 SetIdle(false);
1373 break;
1375 case WM_GETMINMAXINFO:
1376 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1378 case WM_LBUTTONDOWN: {
1379 // For IME, set the composition string as the result string.
1380 IMContext imc(MainHWND());
1381 ::ImmNotifyIME(imc.hIMC, NI_COMPOSITIONSTR, CPS_COMPLETE, 0);
1383 //Platform::DebugPrintf("Buttdown %d %x %x %x %x %x\n",iMessage, wParam, lParam,
1384 // Platform::IsKeyDown(VK_SHIFT),
1385 // Platform::IsKeyDown(VK_CONTROL),
1386 // Platform::IsKeyDown(VK_MENU));
1387 ::SetFocus(MainHWND());
1388 ButtonDown(Point::FromLong(static_cast<long>(lParam)), ::GetMessageTime(),
1389 (wParam & MK_SHIFT) != 0,
1390 (wParam & MK_CONTROL) != 0,
1391 Platform::IsKeyDown(VK_MENU));
1393 break;
1395 case WM_MOUSEMOVE: {
1396 const Point pt = Point::FromLong(static_cast<long>(lParam));
1398 // Windows might send WM_MOUSEMOVE even though the mouse has not been moved:
1399 // http://blogs.msdn.com/b/oldnewthing/archive/2003/10/01/55108.aspx
1400 if (ptMouseLast.x != pt.x || ptMouseLast.y != pt.y) {
1401 SetTrackMouseLeaveEvent(true);
1402 ButtonMoveWithModifiers(pt,
1403 ((wParam & MK_SHIFT) != 0 ? SCI_SHIFT : 0) |
1404 ((wParam & MK_CONTROL) != 0 ? SCI_CTRL : 0) |
1405 (Platform::IsKeyDown(VK_MENU) ? SCI_ALT : 0));
1408 break;
1410 case WM_MOUSELEAVE:
1411 SetTrackMouseLeaveEvent(false);
1412 MouseLeave();
1413 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1415 case WM_LBUTTONUP:
1416 ButtonUp(Point::FromLong(static_cast<long>(lParam)),
1417 ::GetMessageTime(),
1418 (wParam & MK_CONTROL) != 0);
1419 break;
1421 case WM_RBUTTONDOWN: {
1422 ::SetFocus(MainHWND());
1423 Point pt = Point::FromLong(static_cast<long>(lParam));
1424 if (!PointInSelection(pt)) {
1425 CancelModes();
1426 SetEmptySelection(PositionFromLocation(Point::FromLong(static_cast<long>(lParam))));
1429 RightButtonDownWithModifiers(pt, ::GetMessageTime(), ModifierFlags((wParam & MK_SHIFT) != 0,
1430 (wParam & MK_CONTROL) != 0,
1431 Platform::IsKeyDown(VK_MENU)));
1433 break;
1435 case WM_SETCURSOR:
1436 if (LoWord(lParam) == HTCLIENT) {
1437 if (inDragDrop == ddDragging) {
1438 DisplayCursor(Window::cursorUp);
1439 } else {
1440 // Display regular (drag) cursor over selection
1441 POINT pt;
1442 if (0 != ::GetCursorPos(&pt)) {
1443 ::ScreenToClient(MainHWND(), &pt);
1444 if (PointInSelMargin(PointFromPOINT(pt))) {
1445 DisplayCursor(GetMarginCursor(PointFromPOINT(pt)));
1446 } else if (PointInSelection(PointFromPOINT(pt)) && !SelectionEmpty()) {
1447 DisplayCursor(Window::cursorArrow);
1448 } else if (PointIsHotspot(PointFromPOINT(pt))) {
1449 DisplayCursor(Window::cursorHand);
1450 } else {
1451 DisplayCursor(Window::cursorText);
1455 return TRUE;
1456 } else {
1457 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1460 case WM_CHAR:
1461 if (((wParam >= 128) || !iscntrl(static_cast<int>(wParam))) || !lastKeyDownConsumed) {
1462 wchar_t wcs[3] = {static_cast<wchar_t>(wParam), 0};
1463 unsigned int wclen = 1;
1464 if (IS_HIGH_SURROGATE(wcs[0])) {
1465 // If this is a high surrogate character, we need a second one
1466 lastHighSurrogateChar = wcs[0];
1467 return 0;
1468 } else if (IS_LOW_SURROGATE(wcs[0])) {
1469 wcs[1] = wcs[0];
1470 wcs[0] = lastHighSurrogateChar;
1471 lastHighSurrogateChar = 0;
1472 wclen = 2;
1474 AddCharUTF16(wcs, wclen);
1476 return 0;
1478 case WM_UNICHAR:
1479 if (wParam == UNICODE_NOCHAR) {
1480 return TRUE;
1481 } else if (lastKeyDownConsumed) {
1482 return 1;
1483 } else {
1484 wchar_t wcs[3] = {0};
1485 unsigned int wclen = UTF16FromUTF32Character(static_cast<unsigned int>(wParam), wcs);
1486 AddCharUTF16(wcs, wclen);
1487 return FALSE;
1490 case WM_SYSKEYDOWN:
1491 case WM_KEYDOWN: {
1492 //Platform::DebugPrintf("S keydown %d %x %x %x %x\n",iMessage, wParam, lParam, ::IsKeyDown(VK_SHIFT), ::IsKeyDown(VK_CONTROL));
1493 lastKeyDownConsumed = false;
1494 int ret = KeyDown(KeyTranslate(static_cast<int>(wParam)),
1495 Platform::IsKeyDown(VK_SHIFT),
1496 Platform::IsKeyDown(VK_CONTROL),
1497 Platform::IsKeyDown(VK_MENU),
1498 &lastKeyDownConsumed);
1499 if (!ret && !lastKeyDownConsumed) {
1500 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1502 break;
1505 case WM_IME_KEYDOWN: {
1506 if (wParam == VK_HANJA) {
1507 ToggleHanja();
1509 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1512 case WM_IME_REQUEST: {
1513 if (wParam == IMR_RECONVERTSTRING) {
1514 return ImeOnReconvert(lParam);
1516 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1519 case WM_KEYUP:
1520 //Platform::DebugPrintf("S keyup %d %x %x\n",iMessage, wParam, lParam);
1521 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1523 case WM_SETTINGCHANGE:
1524 //Platform::DebugPrintf("Setting Changed\n");
1525 InvalidateStyleData();
1526 // Get Intellimouse scroll line parameters
1527 GetIntelliMouseParameters();
1528 break;
1530 case WM_GETDLGCODE:
1531 return DLGC_HASSETSEL | DLGC_WANTALLKEYS;
1533 case WM_KILLFOCUS: {
1534 HWND wOther = reinterpret_cast<HWND>(wParam);
1535 HWND wThis = MainHWND();
1536 HWND wCT = static_cast<HWND>(ct.wCallTip.GetID());
1537 if (!wParam ||
1538 !(::IsChild(wThis, wOther) || (wOther == wCT))) {
1539 SetFocusState(false);
1540 DestroySystemCaret();
1542 // Explicitly complete any IME composition
1543 IMContext imc(MainHWND());
1544 if (imc.hIMC) {
1545 ::ImmNotifyIME(imc.hIMC, NI_COMPOSITIONSTR, CPS_COMPLETE, 0);
1548 break;
1550 case WM_SETFOCUS:
1551 SetFocusState(true);
1552 DestroySystemCaret();
1553 CreateSystemCaret();
1554 break;
1556 case WM_SYSCOLORCHANGE:
1557 //Platform::DebugPrintf("Setting Changed\n");
1558 InvalidateStyleData();
1559 break;
1561 case WM_IME_STARTCOMPOSITION: // dbcs
1562 if (KoreanIME() || imeInteraction == imeInline) {
1563 return 0;
1564 } else {
1565 ImeStartComposition();
1566 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1569 case WM_IME_ENDCOMPOSITION: // dbcs
1570 ImeEndComposition();
1571 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1573 case WM_IME_COMPOSITION:
1574 if (KoreanIME() || imeInteraction == imeInline) {
1575 return HandleCompositionInline(wParam, lParam);
1576 } else {
1577 return HandleCompositionWindowed(wParam, lParam);
1580 case WM_CONTEXTMENU: {
1581 Point pt = Point::FromLong(static_cast<long>(lParam));
1582 POINT rpt = {static_cast<int>(pt.x), static_cast<int>(pt.y)};
1583 ::ScreenToClient(MainHWND(), &rpt);
1584 const Point ptClient = PointFromPOINT(rpt);
1585 if (ShouldDisplayPopup(ptClient)) {
1586 if ((pt.x == -1) && (pt.y == -1)) {
1587 // Caused by keyboard so display menu near caret
1588 pt = PointMainCaret();
1589 POINT spt = {static_cast<int>(pt.x), static_cast<int>(pt.y)};
1590 ::ClientToScreen(MainHWND(), &spt);
1591 pt = PointFromPOINT(spt);
1593 ContextMenu(pt);
1594 return 0;
1597 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1599 case WM_INPUTLANGCHANGE:
1600 //::SetThreadLocale(LOWORD(lParam));
1601 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1603 case WM_INPUTLANGCHANGEREQUEST:
1604 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1606 case WM_ERASEBKGND:
1607 return 1; // Avoid any background erasure as whole window painted.
1609 case WM_CAPTURECHANGED:
1610 capturedMouse = false;
1611 return 0;
1613 case WM_IME_SETCONTEXT:
1614 if (KoreanIME() || imeInteraction == imeInline) {
1615 if (wParam) {
1616 LPARAM NoImeWin = lParam;
1617 NoImeWin = NoImeWin & (~ISC_SHOWUICOMPOSITIONWINDOW);
1618 return ::DefWindowProc(MainHWND(), iMessage, wParam, NoImeWin);
1621 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1623 // These are not handled in Scintilla and its faster to dispatch them here.
1624 // Also moves time out to here so profile doesn't count lots of empty message calls.
1626 case WM_MOVE:
1627 case WM_MOUSEACTIVATE:
1628 case WM_NCHITTEST:
1629 case WM_NCCALCSIZE:
1630 case WM_NCPAINT:
1631 case WM_NCMOUSEMOVE:
1632 case WM_NCLBUTTONDOWN:
1633 case WM_IME_NOTIFY:
1634 case WM_SYSCOMMAND:
1635 case WM_WINDOWPOSCHANGING:
1636 case WM_WINDOWPOSCHANGED:
1637 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1639 case WM_GETTEXTLENGTH:
1640 return GetTextLength();
1642 case WM_GETTEXT:
1643 return GetText(wParam, lParam);
1645 case EM_LINEFROMCHAR:
1646 if (static_cast<int>(wParam) < 0) {
1647 wParam = SelectionStart().Position();
1649 return pdoc->LineFromPosition(static_cast<int>(wParam));
1651 case EM_EXLINEFROMCHAR:
1652 return pdoc->LineFromPosition(static_cast<int>(lParam));
1654 case EM_GETSEL:
1655 if (wParam) {
1656 *reinterpret_cast<int *>(wParam) = SelectionStart().Position();
1658 if (lParam) {
1659 *reinterpret_cast<int *>(lParam) = SelectionEnd().Position();
1661 return MAKELONG(SelectionStart().Position(), SelectionEnd().Position());
1663 case EM_EXGETSEL: {
1664 if (lParam == 0) {
1665 return 0;
1667 Sci_CharacterRange *pCR = reinterpret_cast<Sci_CharacterRange *>(lParam);
1668 pCR->cpMin = SelectionStart().Position();
1669 pCR->cpMax = SelectionEnd().Position();
1671 break;
1673 case EM_SETSEL: {
1674 int nStart = static_cast<int>(wParam);
1675 int nEnd = static_cast<int>(lParam);
1676 if (nStart == 0 && nEnd == -1) {
1677 nEnd = pdoc->Length();
1679 if (nStart == -1) {
1680 nStart = nEnd; // Remove selection
1682 SetSelection(nEnd, nStart);
1683 EnsureCaretVisible();
1685 break;
1687 case EM_EXSETSEL: {
1688 if (lParam == 0) {
1689 return 0;
1691 Sci_CharacterRange *pCR = reinterpret_cast<Sci_CharacterRange *>(lParam);
1692 sel.selType = Selection::selStream;
1693 if (pCR->cpMin == 0 && pCR->cpMax == -1) {
1694 SetSelection(pCR->cpMin, pdoc->Length());
1695 } else {
1696 SetSelection(pCR->cpMin, pCR->cpMax);
1698 EnsureCaretVisible();
1699 return pdoc->LineFromPosition(SelectionStart().Position());
1702 case SCI_GETDIRECTFUNCTION:
1703 return reinterpret_cast<sptr_t>(DirectFunction);
1705 case SCI_GETDIRECTPOINTER:
1706 return reinterpret_cast<sptr_t>(this);
1708 case SCI_GRABFOCUS:
1709 ::SetFocus(MainHWND());
1710 break;
1712 #ifdef INCLUDE_DEPRECATED_FEATURES
1713 case SCI_SETKEYSUNICODE:
1714 break;
1716 case SCI_GETKEYSUNICODE:
1717 return true;
1718 #endif
1720 case SCI_SETTECHNOLOGY:
1721 if ((wParam == SC_TECHNOLOGY_DEFAULT) ||
1722 (wParam == SC_TECHNOLOGY_DIRECTWRITERETAIN) ||
1723 (wParam == SC_TECHNOLOGY_DIRECTWRITEDC) ||
1724 (wParam == SC_TECHNOLOGY_DIRECTWRITE)) {
1725 if (technology != static_cast<int>(wParam)) {
1726 if (static_cast<int>(wParam) > SC_TECHNOLOGY_DEFAULT) {
1727 #if defined(USE_D2D)
1728 if (!LoadD2D())
1729 // Failed to load Direct2D or DirectWrite so no effect
1730 return 0;
1731 #else
1732 return 0;
1733 #endif
1735 #if defined(USE_D2D)
1736 DropRenderTarget();
1737 #endif
1738 technology = static_cast<int>(wParam);
1739 // Invalidate all cached information including layout.
1740 DropGraphics(true);
1741 InvalidateStyleRedraw();
1744 break;
1746 #ifdef SCI_LEXER
1747 case SCI_LOADLEXERLIBRARY:
1748 LexerManager::GetInstance()->Load(reinterpret_cast<const char *>(lParam));
1749 break;
1750 #endif
1752 case SCI_TARGETASUTF8:
1753 return TargetAsUTF8(reinterpret_cast<char*>(lParam));
1755 case SCI_ENCODEDFROMUTF8:
1756 return EncodedFromUTF8(reinterpret_cast<char*>(wParam),
1757 reinterpret_cast<char*>(lParam));
1759 default:
1760 return ScintillaBase::WndProc(iMessage, wParam, lParam);
1762 } catch (std::bad_alloc &) {
1763 errorStatus = SC_STATUS_BADALLOC;
1764 } catch (...) {
1765 errorStatus = SC_STATUS_FAILURE;
1767 return 0l;
1770 bool ScintillaWin::ValidCodePage(int codePage) const {
1771 return codePage == 0 || codePage == SC_CP_UTF8 ||
1772 codePage == 932 || codePage == 936 || codePage == 949 ||
1773 codePage == 950 || codePage == 1361;
1776 sptr_t ScintillaWin::DefWndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
1777 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1781 * Report that this Editor subclass has a working implementation of FineTickerStart.
1783 bool ScintillaWin::FineTickerAvailable() {
1784 return true;
1787 bool ScintillaWin::FineTickerRunning(TickReason reason) {
1788 return timers[reason] != 0;
1791 void ScintillaWin::FineTickerStart(TickReason reason, int millis, int tolerance) {
1792 FineTickerCancel(reason);
1793 if (SetCoalescableTimerFn && tolerance) {
1794 timers[reason] = SetCoalescableTimerFn(MainHWND(), fineTimerStart + reason, millis, NULL, tolerance);
1795 } else {
1796 timers[reason] = ::SetTimer(MainHWND(), fineTimerStart + reason, millis, NULL);
1800 void ScintillaWin::FineTickerCancel(TickReason reason) {
1801 if (timers[reason]) {
1802 ::KillTimer(MainHWND(), timers[reason]);
1803 timers[reason] = 0;
1808 bool ScintillaWin::SetIdle(bool on) {
1809 // On Win32 the Idler is implemented as a Timer on the Scintilla window. This
1810 // takes advantage of the fact that WM_TIMER messages are very low priority,
1811 // and are only posted when the message queue is empty, i.e. during idle time.
1812 if (idler.state != on) {
1813 if (on) {
1814 idler.idlerID = ::SetTimer(MainHWND(), idleTimerID, 10, NULL)
1815 ? reinterpret_cast<IdlerID>(idleTimerID) : 0;
1816 } else {
1817 ::KillTimer(MainHWND(), reinterpret_cast<uptr_t>(idler.idlerID));
1818 idler.idlerID = 0;
1820 idler.state = idler.idlerID != 0;
1822 return idler.state;
1825 void ScintillaWin::SetMouseCapture(bool on) {
1826 if (mouseDownCaptures) {
1827 if (on) {
1828 ::SetCapture(MainHWND());
1829 } else {
1830 ::ReleaseCapture();
1833 capturedMouse = on;
1836 bool ScintillaWin::HaveMouseCapture() {
1837 // Cannot just see if GetCapture is this window as the scroll bar also sets capture for the window
1838 return capturedMouse;
1839 //return capturedMouse && (::GetCapture() == MainHWND());
1842 void ScintillaWin::SetTrackMouseLeaveEvent(bool on) {
1843 if (on && TrackMouseEventFn && !trackedMouseLeave) {
1844 TRACKMOUSEEVENT tme;
1845 tme.cbSize = sizeof(tme);
1846 tme.dwFlags = TME_LEAVE;
1847 tme.hwndTrack = MainHWND();
1848 tme.dwHoverTime = HOVER_DEFAULT; // Unused but triggers Dr. Memory if not initialized
1849 TrackMouseEventFn(&tme);
1851 trackedMouseLeave = on;
1854 bool ScintillaWin::PaintContains(PRectangle rc) {
1855 if (paintState == painting) {
1856 return BoundsContains(rcPaint, hRgnUpdate, rc);
1858 return true;
1861 void ScintillaWin::ScrollText(int /* linesToMove */) {
1862 //Platform::DebugPrintf("ScintillaWin::ScrollText %d\n", linesToMove);
1863 //::ScrollWindow(MainHWND(), 0,
1864 // vs.lineHeight * linesToMove, 0, 0);
1865 //::UpdateWindow(MainHWND());
1866 Redraw();
1867 UpdateSystemCaret();
1870 void ScintillaWin::NotifyCaretMove() {
1871 NotifyWinEvent(EVENT_OBJECT_LOCATIONCHANGE, MainHWND(), OBJID_CARET, CHILDID_SELF);
1874 void ScintillaWin::UpdateSystemCaret() {
1875 if (hasFocus) {
1876 if (HasCaretSizeChanged()) {
1877 DestroySystemCaret();
1878 CreateSystemCaret();
1880 Point pos = PointMainCaret();
1881 ::SetCaretPos(static_cast<int>(pos.x), static_cast<int>(pos.y));
1885 int ScintillaWin::SetScrollInfo(int nBar, LPCSCROLLINFO lpsi, BOOL bRedraw) {
1886 return ::SetScrollInfo(MainHWND(), nBar, lpsi, bRedraw);
1889 bool ScintillaWin::GetScrollInfo(int nBar, LPSCROLLINFO lpsi) {
1890 return ::GetScrollInfo(MainHWND(), nBar, lpsi) ? true : false;
1893 // Change the scroll position but avoid repaint if changing to same value
1894 void ScintillaWin::ChangeScrollPos(int barType, int pos) {
1895 SCROLLINFO sci = {
1896 sizeof(sci), 0, 0, 0, 0, 0, 0
1898 sci.fMask = SIF_POS;
1899 GetScrollInfo(barType, &sci);
1900 if (sci.nPos != pos) {
1901 DwellEnd(true);
1902 sci.nPos = pos;
1903 SetScrollInfo(barType, &sci, TRUE);
1907 void ScintillaWin::SetVerticalScrollPos() {
1908 ChangeScrollPos(SB_VERT, topLine);
1911 void ScintillaWin::SetHorizontalScrollPos() {
1912 ChangeScrollPos(SB_HORZ, xOffset);
1915 bool ScintillaWin::ModifyScrollBars(int nMax, int nPage) {
1916 bool modified = false;
1917 SCROLLINFO sci = {
1918 sizeof(sci), 0, 0, 0, 0, 0, 0
1920 sci.fMask = SIF_PAGE | SIF_RANGE;
1921 GetScrollInfo(SB_VERT, &sci);
1922 int vertEndPreferred = nMax;
1923 if (!verticalScrollBarVisible)
1924 nPage = vertEndPreferred + 1;
1925 if ((sci.nMin != 0) ||
1926 (sci.nMax != vertEndPreferred) ||
1927 (sci.nPage != static_cast<unsigned int>(nPage)) ||
1928 (sci.nPos != 0)) {
1929 sci.fMask = SIF_PAGE | SIF_RANGE;
1930 sci.nMin = 0;
1931 sci.nMax = vertEndPreferred;
1932 sci.nPage = nPage;
1933 sci.nPos = 0;
1934 sci.nTrackPos = 1;
1935 SetScrollInfo(SB_VERT, &sci, TRUE);
1936 modified = true;
1939 PRectangle rcText = GetTextRectangle();
1940 int horizEndPreferred = scrollWidth;
1941 if (horizEndPreferred < 0)
1942 horizEndPreferred = 0;
1943 unsigned int pageWidth = static_cast<unsigned int>(rcText.Width());
1944 if (!horizontalScrollBarVisible || Wrapping())
1945 pageWidth = horizEndPreferred + 1;
1946 sci.fMask = SIF_PAGE | SIF_RANGE;
1947 GetScrollInfo(SB_HORZ, &sci);
1948 if ((sci.nMin != 0) ||
1949 (sci.nMax != horizEndPreferred) ||
1950 (sci.nPage != pageWidth) ||
1951 (sci.nPos != 0)) {
1952 sci.fMask = SIF_PAGE | SIF_RANGE;
1953 sci.nMin = 0;
1954 sci.nMax = horizEndPreferred;
1955 sci.nPage = pageWidth;
1956 sci.nPos = 0;
1957 sci.nTrackPos = 1;
1958 SetScrollInfo(SB_HORZ, &sci, TRUE);
1959 modified = true;
1960 if (scrollWidth < static_cast<int>(pageWidth)) {
1961 HorizontalScrollTo(0);
1964 return modified;
1967 void ScintillaWin::NotifyChange() {
1968 ::SendMessage(::GetParent(MainHWND()), WM_COMMAND,
1969 MAKELONG(GetCtrlID(), SCEN_CHANGE),
1970 reinterpret_cast<LPARAM>(MainHWND()));
1973 void ScintillaWin::NotifyFocus(bool focus) {
1974 ::SendMessage(::GetParent(MainHWND()), WM_COMMAND,
1975 MAKELONG(GetCtrlID(), focus ? SCEN_SETFOCUS : SCEN_KILLFOCUS),
1976 reinterpret_cast<LPARAM>(MainHWND()));
1977 Editor::NotifyFocus(focus);
1980 void ScintillaWin::SetCtrlID(int identifier) {
1981 ::SetWindowID(static_cast<HWND>(wMain.GetID()), identifier);
1984 int ScintillaWin::GetCtrlID() {
1985 return ::GetDlgCtrlID(static_cast<HWND>(wMain.GetID()));
1988 void ScintillaWin::NotifyParent(SCNotification scn) {
1989 scn.nmhdr.hwndFrom = MainHWND();
1990 scn.nmhdr.idFrom = GetCtrlID();
1991 ::SendMessage(::GetParent(MainHWND()), WM_NOTIFY,
1992 GetCtrlID(), reinterpret_cast<LPARAM>(&scn));
1995 void ScintillaWin::NotifyDoubleClick(Point pt, int modifiers) {
1996 //Platform::DebugPrintf("ScintillaWin Double click 0\n");
1997 ScintillaBase::NotifyDoubleClick(pt, modifiers);
1998 // Send myself a WM_LBUTTONDBLCLK, so the container can handle it too.
1999 ::SendMessage(MainHWND(),
2000 WM_LBUTTONDBLCLK,
2001 (modifiers & SCI_SHIFT) ? MK_SHIFT : 0,
2002 MAKELPARAM(pt.x, pt.y));
2005 class CaseFolderDBCS : public CaseFolderTable {
2006 // Allocate the expandable storage here so that it does not need to be reallocated
2007 // for each call to Fold.
2008 std::vector<wchar_t> utf16Mixed;
2009 std::vector<wchar_t> utf16Folded;
2010 UINT cp;
2011 public:
2012 explicit CaseFolderDBCS(UINT cp_) : cp(cp_) {
2013 StandardASCII();
2015 virtual size_t Fold(char *folded, size_t sizeFolded, const char *mixed, size_t lenMixed) {
2016 if ((lenMixed == 1) && (sizeFolded > 0)) {
2017 folded[0] = mapping[static_cast<unsigned char>(mixed[0])];
2018 return 1;
2019 } else {
2020 if (lenMixed > utf16Mixed.size()) {
2021 utf16Mixed.resize(lenMixed + 8);
2023 size_t nUtf16Mixed = ::MultiByteToWideChar(cp, 0, mixed,
2024 static_cast<int>(lenMixed),
2025 &utf16Mixed[0],
2026 static_cast<int>(utf16Mixed.size()));
2028 if (nUtf16Mixed == 0) {
2029 // Failed to convert -> bad input
2030 folded[0] = '\0';
2031 return 1;
2034 unsigned int lenFlat = 0;
2035 for (size_t mixIndex=0; mixIndex < nUtf16Mixed; mixIndex++) {
2036 if ((lenFlat + 20) > utf16Folded.size())
2037 utf16Folded.resize(lenFlat + 60);
2038 const char *foldedUTF8 = CaseConvert(utf16Mixed[mixIndex], CaseConversionFold);
2039 if (foldedUTF8) {
2040 // Maximum length of a case conversion is 6 bytes, 3 characters
2041 wchar_t wFolded[20];
2042 size_t charsConverted = UTF16FromUTF8(foldedUTF8,
2043 strlen(foldedUTF8),
2044 wFolded, ELEMENTS(wFolded));
2045 for (size_t j=0; j<charsConverted; j++)
2046 utf16Folded[lenFlat++] = wFolded[j];
2047 } else {
2048 utf16Folded[lenFlat++] = utf16Mixed[mixIndex];
2052 size_t lenOut = ::WideCharToMultiByte(cp, 0,
2053 &utf16Folded[0], lenFlat,
2054 NULL, 0, NULL, 0);
2056 if (lenOut < sizeFolded) {
2057 ::WideCharToMultiByte(cp, 0,
2058 &utf16Folded[0], lenFlat,
2059 folded, static_cast<int>(lenOut), NULL, 0);
2060 return lenOut;
2061 } else {
2062 return 0;
2068 CaseFolder *ScintillaWin::CaseFolderForEncoding() {
2069 UINT cpDest = CodePageOfDocument();
2070 if (cpDest == SC_CP_UTF8) {
2071 return new CaseFolderUnicode();
2072 } else {
2073 if (pdoc->dbcsCodePage == 0) {
2074 CaseFolderTable *pcf = new CaseFolderTable();
2075 pcf->StandardASCII();
2076 // Only for single byte encodings
2077 UINT cpDoc = CodePageOfDocument();
2078 for (int i=0x80; i<0x100; i++) {
2079 char sCharacter[2] = "A";
2080 sCharacter[0] = static_cast<char>(i);
2081 wchar_t wCharacter[20];
2082 unsigned int lengthUTF16 = ::MultiByteToWideChar(cpDoc, 0, sCharacter, 1,
2083 wCharacter, ELEMENTS(wCharacter));
2084 if (lengthUTF16 == 1) {
2085 const char *caseFolded = CaseConvert(wCharacter[0], CaseConversionFold);
2086 if (caseFolded) {
2087 wchar_t wLower[20];
2088 size_t charsConverted = UTF16FromUTF8(caseFolded,
2089 strlen(caseFolded),
2090 wLower, ELEMENTS(wLower));
2091 if (charsConverted == 1) {
2092 char sCharacterLowered[20];
2093 unsigned int lengthConverted = ::WideCharToMultiByte(cpDoc, 0,
2094 wLower, static_cast<int>(charsConverted),
2095 sCharacterLowered, ELEMENTS(sCharacterLowered), NULL, 0);
2096 if ((lengthConverted == 1) && (sCharacter[0] != sCharacterLowered[0])) {
2097 pcf->SetTranslation(sCharacter[0], sCharacterLowered[0]);
2103 return pcf;
2104 } else {
2105 return new CaseFolderDBCS(cpDest);
2110 std::string ScintillaWin::CaseMapString(const std::string &s, int caseMapping) {
2111 if ((s.size() == 0) || (caseMapping == cmSame))
2112 return s;
2114 const UINT cpDoc = CodePageOfDocument();
2115 if (cpDoc == SC_CP_UTF8) {
2116 return CaseConvertString(s, (caseMapping == cmUpper) ? CaseConversionUpper : CaseConversionLower);
2119 // Change text to UTF-16
2120 const std::wstring wsText = StringDecode(s, cpDoc);
2122 const DWORD mapFlags = LCMAP_LINGUISTIC_CASING |
2123 ((caseMapping == cmUpper) ? LCMAP_UPPERCASE : LCMAP_LOWERCASE);
2125 // Change case
2126 const std::wstring wsConverted = StringMapCase(wsText, mapFlags);
2128 // Change back to document encoding
2129 std::string sConverted = StringEncode(wsConverted, cpDoc);
2131 return sConverted;
2134 void ScintillaWin::Copy() {
2135 //Platform::DebugPrintf("Copy\n");
2136 if (!sel.Empty()) {
2137 SelectionText selectedText;
2138 CopySelectionRange(&selectedText);
2139 CopyToClipboard(selectedText);
2143 void ScintillaWin::CopyAllowLine() {
2144 SelectionText selectedText;
2145 CopySelectionRange(&selectedText, true);
2146 CopyToClipboard(selectedText);
2149 bool ScintillaWin::CanPaste() {
2150 if (!Editor::CanPaste())
2151 return false;
2152 if (::IsClipboardFormatAvailable(CF_TEXT))
2153 return true;
2154 if (IsUnicodeMode())
2155 return ::IsClipboardFormatAvailable(CF_UNICODETEXT) != 0;
2156 return false;
2159 class GlobalMemory {
2160 HGLOBAL hand;
2161 public:
2162 void *ptr;
2163 GlobalMemory() : hand(0), ptr(0) {
2165 explicit GlobalMemory(HGLOBAL hand_) : hand(hand_), ptr(0) {
2166 if (hand) {
2167 ptr = ::GlobalLock(hand);
2170 ~GlobalMemory() {
2171 PLATFORM_ASSERT(!ptr);
2172 assert(!hand);
2174 void Allocate(size_t bytes) {
2175 assert(!hand);
2176 hand = ::GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT, bytes);
2177 if (hand) {
2178 ptr = ::GlobalLock(hand);
2181 HGLOBAL Unlock() {
2182 PLATFORM_ASSERT(ptr);
2183 HGLOBAL handCopy = hand;
2184 ::GlobalUnlock(hand);
2185 ptr = 0;
2186 hand = 0;
2187 return handCopy;
2189 void SetClip(UINT uFormat) {
2190 ::SetClipboardData(uFormat, Unlock());
2192 operator bool() const {
2193 return ptr != 0;
2195 SIZE_T Size() {
2196 return ::GlobalSize(hand);
2200 // OpenClipboard may fail if another application has opened the clipboard.
2201 // Try up to 8 times, with an initial delay of 1 ms and an exponential back off
2202 // for a maximum total delay of 127 ms (1+2+4+8+16+32+64).
2203 static bool OpenClipboardRetry(HWND hwnd) {
2204 for (int attempt=0; attempt<8; attempt++) {
2205 if (attempt > 0) {
2206 ::Sleep(1 << (attempt-1));
2208 if (::OpenClipboard(hwnd)) {
2209 return true;
2212 return false;
2215 void ScintillaWin::Paste() {
2216 if (!::OpenClipboardRetry(MainHWND())) {
2217 return;
2219 UndoGroup ug(pdoc);
2220 const bool isLine = SelectionEmpty() &&
2221 (::IsClipboardFormatAvailable(cfLineSelect) || ::IsClipboardFormatAvailable(cfVSLineTag));
2222 ClearSelection(multiPasteMode == SC_MULTIPASTE_EACH);
2223 bool isRectangular = (::IsClipboardFormatAvailable(cfColumnSelect) != 0);
2225 if (!isRectangular) {
2226 // Evaluate "Borland IDE Block Type" explicitly
2227 GlobalMemory memBorlandSelection(::GetClipboardData(cfBorlandIDEBlockType));
2228 if (memBorlandSelection) {
2229 isRectangular = (memBorlandSelection.Size() == 1) && (static_cast<BYTE *>(memBorlandSelection.ptr)[0] == 0x02);
2230 memBorlandSelection.Unlock();
2233 const PasteShape pasteShape = isRectangular ? pasteRectangular : (isLine ? pasteLine : pasteStream);
2235 // Always use CF_UNICODETEXT if available
2236 GlobalMemory memUSelection(::GetClipboardData(CF_UNICODETEXT));
2237 if (memUSelection) {
2238 wchar_t *uptr = static_cast<wchar_t *>(memUSelection.ptr);
2239 if (uptr) {
2240 unsigned int len;
2241 std::vector<char> putf;
2242 // Default Scintilla behaviour in Unicode mode
2243 if (IsUnicodeMode()) {
2244 unsigned int bytes = static_cast<unsigned int>(memUSelection.Size());
2245 len = UTF8Length(uptr, bytes / 2);
2246 putf.resize(len + 1);
2247 UTF8FromUTF16(uptr, bytes / 2, &putf[0], len);
2248 } else {
2249 // CF_UNICODETEXT available, but not in Unicode mode
2250 // Convert from Unicode to current Scintilla code page
2251 UINT cpDest = CodePageOfDocument();
2252 len = ::WideCharToMultiByte(cpDest, 0, uptr, -1,
2253 NULL, 0, NULL, NULL) - 1; // subtract 0 terminator
2254 putf.resize(len + 1);
2255 ::WideCharToMultiByte(cpDest, 0, uptr, -1,
2256 &putf[0], len + 1, NULL, NULL);
2259 InsertPasteShape(&putf[0], len, pasteShape);
2261 memUSelection.Unlock();
2262 } else {
2263 // CF_UNICODETEXT not available, paste ANSI text
2264 GlobalMemory memSelection(::GetClipboardData(CF_TEXT));
2265 if (memSelection) {
2266 char *ptr = static_cast<char *>(memSelection.ptr);
2267 if (ptr) {
2268 unsigned int bytes = static_cast<unsigned int>(memSelection.Size());
2269 unsigned int len = bytes;
2270 for (unsigned int i = 0; i < bytes; i++) {
2271 if ((len == bytes) && (0 == ptr[i]))
2272 len = i;
2275 // In Unicode mode, convert clipboard text to UTF-8
2276 if (IsUnicodeMode()) {
2277 std::vector<wchar_t> uptr(len+1);
2279 unsigned int ulen = ::MultiByteToWideChar(CP_ACP, 0,
2280 ptr, len, &uptr[0], len+1);
2282 unsigned int mlen = UTF8Length(&uptr[0], ulen);
2283 std::vector<char> putf(mlen+1);
2284 UTF8FromUTF16(&uptr[0], ulen, &putf[0], mlen);
2286 InsertPasteShape(&putf[0], mlen, pasteShape);
2287 } else {
2288 InsertPasteShape(ptr, len, pasteShape);
2291 memSelection.Unlock();
2294 ::CloseClipboard();
2295 Redraw();
2298 void ScintillaWin::CreateCallTipWindow(PRectangle) {
2299 if (!ct.wCallTip.Created()) {
2300 ct.wCallTip = ::CreateWindow(callClassName, TEXT("ACallTip"),
2301 WS_POPUP, 100, 100, 150, 20,
2302 MainHWND(), 0,
2303 GetWindowInstance(MainHWND()),
2304 this);
2305 ct.wDraw = ct.wCallTip;
2309 void ScintillaWin::AddToPopUp(const char *label, int cmd, bool enabled) {
2310 HMENU hmenuPopup = static_cast<HMENU>(popup.GetID());
2311 if (!label[0])
2312 ::AppendMenuA(hmenuPopup, MF_SEPARATOR, 0, "");
2313 else if (enabled)
2314 ::AppendMenuA(hmenuPopup, MF_STRING, cmd, label);
2315 else
2316 ::AppendMenuA(hmenuPopup, MF_STRING | MF_DISABLED | MF_GRAYED, cmd, label);
2319 void ScintillaWin::ClaimSelection() {
2320 // Windows does not have a primary selection
2323 /// Implement IUnknown
2325 STDMETHODIMP_(ULONG)FormatEnumerator_AddRef(FormatEnumerator *fe);
2326 STDMETHODIMP FormatEnumerator_QueryInterface(FormatEnumerator *fe, REFIID riid, PVOID *ppv) {
2327 //Platform::DebugPrintf("EFE QI");
2328 *ppv = NULL;
2329 if (riid == IID_IUnknown)
2330 *ppv = reinterpret_cast<IEnumFORMATETC *>(fe);
2331 if (riid == IID_IEnumFORMATETC)
2332 *ppv = reinterpret_cast<IEnumFORMATETC *>(fe);
2333 if (!*ppv)
2334 return E_NOINTERFACE;
2335 FormatEnumerator_AddRef(fe);
2336 return S_OK;
2338 STDMETHODIMP_(ULONG)FormatEnumerator_AddRef(FormatEnumerator *fe) {
2339 return ++fe->ref;
2341 STDMETHODIMP_(ULONG)FormatEnumerator_Release(FormatEnumerator *fe) {
2342 fe->ref--;
2343 if (fe->ref > 0)
2344 return fe->ref;
2345 delete fe;
2346 return 0;
2348 /// Implement IEnumFORMATETC
2349 STDMETHODIMP FormatEnumerator_Next(FormatEnumerator *fe, ULONG celt, FORMATETC *rgelt, ULONG *pceltFetched) {
2350 if (rgelt == NULL) return E_POINTER;
2351 unsigned int putPos = 0;
2352 while ((fe->pos < fe->formats.size()) && (putPos < celt)) {
2353 rgelt->cfFormat = fe->formats[fe->pos];
2354 rgelt->ptd = 0;
2355 rgelt->dwAspect = DVASPECT_CONTENT;
2356 rgelt->lindex = -1;
2357 rgelt->tymed = TYMED_HGLOBAL;
2358 rgelt++;
2359 fe->pos++;
2360 putPos++;
2362 if (pceltFetched)
2363 *pceltFetched = putPos;
2364 return putPos ? S_OK : S_FALSE;
2366 STDMETHODIMP FormatEnumerator_Skip(FormatEnumerator *fe, ULONG celt) {
2367 fe->pos += celt;
2368 return S_OK;
2370 STDMETHODIMP FormatEnumerator_Reset(FormatEnumerator *fe) {
2371 fe->pos = 0;
2372 return S_OK;
2374 STDMETHODIMP FormatEnumerator_Clone(FormatEnumerator *fe, IEnumFORMATETC **ppenum) {
2375 FormatEnumerator *pfe;
2376 try {
2377 pfe = new FormatEnumerator(fe->pos, &fe->formats[0], fe->formats.size());
2378 } catch (...) {
2379 return E_OUTOFMEMORY;
2381 return FormatEnumerator_QueryInterface(pfe, IID_IEnumFORMATETC,
2382 reinterpret_cast<void **>(ppenum));
2385 static VFunction *vtFormatEnumerator[] = {
2386 (VFunction *)(FormatEnumerator_QueryInterface),
2387 (VFunction *)(FormatEnumerator_AddRef),
2388 (VFunction *)(FormatEnumerator_Release),
2389 (VFunction *)(FormatEnumerator_Next),
2390 (VFunction *)(FormatEnumerator_Skip),
2391 (VFunction *)(FormatEnumerator_Reset),
2392 (VFunction *)(FormatEnumerator_Clone)
2395 FormatEnumerator::FormatEnumerator(int pos_, CLIPFORMAT formats_[], size_t formatsLen_) {
2396 vtbl = vtFormatEnumerator;
2397 ref = 0; // First QI adds first reference...
2398 pos = pos_;
2399 formats.insert(formats.begin(), formats_, formats_+formatsLen_);
2402 /// Implement IUnknown
2403 STDMETHODIMP DropSource_QueryInterface(DropSource *ds, REFIID riid, PVOID *ppv) {
2404 return ds->sci->QueryInterface(riid, ppv);
2406 STDMETHODIMP_(ULONG)DropSource_AddRef(DropSource *ds) {
2407 return ds->sci->AddRef();
2409 STDMETHODIMP_(ULONG)DropSource_Release(DropSource *ds) {
2410 return ds->sci->Release();
2413 /// Implement IDropSource
2414 STDMETHODIMP DropSource_QueryContinueDrag(DropSource *, BOOL fEsc, DWORD grfKeyState) {
2415 if (fEsc)
2416 return DRAGDROP_S_CANCEL;
2417 if (!(grfKeyState & MK_LBUTTON))
2418 return DRAGDROP_S_DROP;
2419 return S_OK;
2422 STDMETHODIMP DropSource_GiveFeedback(DropSource *, DWORD) {
2423 return DRAGDROP_S_USEDEFAULTCURSORS;
2426 static VFunction *vtDropSource[] = {
2427 (VFunction *)(DropSource_QueryInterface),
2428 (VFunction *)(DropSource_AddRef),
2429 (VFunction *)(DropSource_Release),
2430 (VFunction *)(DropSource_QueryContinueDrag),
2431 (VFunction *)(DropSource_GiveFeedback)
2434 DropSource::DropSource() {
2435 vtbl = vtDropSource;
2436 sci = 0;
2439 /// Implement IUnkown
2440 STDMETHODIMP DataObject_QueryInterface(DataObject *pd, REFIID riid, PVOID *ppv) {
2441 //Platform::DebugPrintf("DO QI %x\n", pd);
2442 return pd->sci->QueryInterface(riid, ppv);
2444 STDMETHODIMP_(ULONG)DataObject_AddRef(DataObject *pd) {
2445 return pd->sci->AddRef();
2447 STDMETHODIMP_(ULONG)DataObject_Release(DataObject *pd) {
2448 return pd->sci->Release();
2450 /// Implement IDataObject
2451 STDMETHODIMP DataObject_GetData(DataObject *pd, FORMATETC *pFEIn, STGMEDIUM *pSTM) {
2452 return pd->sci->GetData(pFEIn, pSTM);
2455 STDMETHODIMP DataObject_GetDataHere(DataObject *, FORMATETC *, STGMEDIUM *) {
2456 //Platform::DebugPrintf("DOB GetDataHere\n");
2457 return E_NOTIMPL;
2460 STDMETHODIMP DataObject_QueryGetData(DataObject *pd, FORMATETC *pFE) {
2461 if (pd->sci->DragIsRectangularOK(pFE->cfFormat) &&
2462 pFE->ptd == 0 &&
2463 (pFE->dwAspect & DVASPECT_CONTENT) != 0 &&
2464 pFE->lindex == -1 &&
2465 (pFE->tymed & TYMED_HGLOBAL) != 0
2467 return S_OK;
2470 bool formatOK = (pFE->cfFormat == CF_TEXT) ||
2471 ((pFE->cfFormat == CF_UNICODETEXT) && pd->sci->IsUnicodeMode());
2472 if (!formatOK ||
2473 pFE->ptd != 0 ||
2474 (pFE->dwAspect & DVASPECT_CONTENT) == 0 ||
2475 pFE->lindex != -1 ||
2476 (pFE->tymed & TYMED_HGLOBAL) == 0
2478 //Platform::DebugPrintf("DOB QueryGetData No %x\n",pFE->cfFormat);
2479 //return DATA_E_FORMATETC;
2480 return S_FALSE;
2482 //Platform::DebugPrintf("DOB QueryGetData OK %x\n",pFE->cfFormat);
2483 return S_OK;
2486 STDMETHODIMP DataObject_GetCanonicalFormatEtc(DataObject *pd, FORMATETC *, FORMATETC *pFEOut) {
2487 //Platform::DebugPrintf("DOB GetCanon\n");
2488 if (pd->sci->IsUnicodeMode())
2489 pFEOut->cfFormat = CF_UNICODETEXT;
2490 else
2491 pFEOut->cfFormat = CF_TEXT;
2492 pFEOut->ptd = 0;
2493 pFEOut->dwAspect = DVASPECT_CONTENT;
2494 pFEOut->lindex = -1;
2495 pFEOut->tymed = TYMED_HGLOBAL;
2496 return S_OK;
2499 STDMETHODIMP DataObject_SetData(DataObject *, FORMATETC *, STGMEDIUM *, BOOL) {
2500 //Platform::DebugPrintf("DOB SetData\n");
2501 return E_FAIL;
2504 STDMETHODIMP DataObject_EnumFormatEtc(DataObject *pd, DWORD dwDirection, IEnumFORMATETC **ppEnum) {
2505 try {
2506 //Platform::DebugPrintf("DOB EnumFormatEtc %d\n", dwDirection);
2507 if (dwDirection != DATADIR_GET) {
2508 *ppEnum = 0;
2509 return E_FAIL;
2511 FormatEnumerator *pfe;
2512 if (pd->sci->IsUnicodeMode()) {
2513 CLIPFORMAT formats[] = {CF_UNICODETEXT, CF_TEXT};
2514 pfe = new FormatEnumerator(0, formats, ELEMENTS(formats));
2515 } else {
2516 CLIPFORMAT formats[] = {CF_TEXT};
2517 pfe = new FormatEnumerator(0, formats, ELEMENTS(formats));
2519 return FormatEnumerator_QueryInterface(pfe, IID_IEnumFORMATETC,
2520 reinterpret_cast<void **>(ppEnum));
2521 } catch (std::bad_alloc &) {
2522 pd->sci->errorStatus = SC_STATUS_BADALLOC;
2523 return E_OUTOFMEMORY;
2524 } catch (...) {
2525 pd->sci->errorStatus = SC_STATUS_FAILURE;
2526 return E_FAIL;
2530 STDMETHODIMP DataObject_DAdvise(DataObject *, FORMATETC *, DWORD, IAdviseSink *, PDWORD) {
2531 //Platform::DebugPrintf("DOB DAdvise\n");
2532 return E_FAIL;
2535 STDMETHODIMP DataObject_DUnadvise(DataObject *, DWORD) {
2536 //Platform::DebugPrintf("DOB DUnadvise\n");
2537 return E_FAIL;
2540 STDMETHODIMP DataObject_EnumDAdvise(DataObject *, IEnumSTATDATA **) {
2541 //Platform::DebugPrintf("DOB EnumDAdvise\n");
2542 return E_FAIL;
2545 static VFunction *vtDataObject[] = {
2546 (VFunction *)(DataObject_QueryInterface),
2547 (VFunction *)(DataObject_AddRef),
2548 (VFunction *)(DataObject_Release),
2549 (VFunction *)(DataObject_GetData),
2550 (VFunction *)(DataObject_GetDataHere),
2551 (VFunction *)(DataObject_QueryGetData),
2552 (VFunction *)(DataObject_GetCanonicalFormatEtc),
2553 (VFunction *)(DataObject_SetData),
2554 (VFunction *)(DataObject_EnumFormatEtc),
2555 (VFunction *)(DataObject_DAdvise),
2556 (VFunction *)(DataObject_DUnadvise),
2557 (VFunction *)(DataObject_EnumDAdvise)
2560 DataObject::DataObject() {
2561 vtbl = vtDataObject;
2562 sci = 0;
2565 /// Implement IUnknown
2566 STDMETHODIMP DropTarget_QueryInterface(DropTarget *dt, REFIID riid, PVOID *ppv) {
2567 //Platform::DebugPrintf("DT QI %x\n", dt);
2568 return dt->sci->QueryInterface(riid, ppv);
2570 STDMETHODIMP_(ULONG)DropTarget_AddRef(DropTarget *dt) {
2571 return dt->sci->AddRef();
2573 STDMETHODIMP_(ULONG)DropTarget_Release(DropTarget *dt) {
2574 return dt->sci->Release();
2577 /// Implement IDropTarget by forwarding to Scintilla
2578 STDMETHODIMP DropTarget_DragEnter(DropTarget *dt, LPDATAOBJECT pIDataSource, DWORD grfKeyState,
2579 POINTL pt, PDWORD pdwEffect) {
2580 try {
2581 return dt->sci->DragEnter(pIDataSource, grfKeyState, pt, pdwEffect);
2582 } catch (...) {
2583 dt->sci->errorStatus = SC_STATUS_FAILURE;
2585 return E_FAIL;
2587 STDMETHODIMP DropTarget_DragOver(DropTarget *dt, DWORD grfKeyState, POINTL pt, PDWORD pdwEffect) {
2588 try {
2589 return dt->sci->DragOver(grfKeyState, pt, pdwEffect);
2590 } catch (...) {
2591 dt->sci->errorStatus = SC_STATUS_FAILURE;
2593 return E_FAIL;
2595 STDMETHODIMP DropTarget_DragLeave(DropTarget *dt) {
2596 try {
2597 return dt->sci->DragLeave();
2598 } catch (...) {
2599 dt->sci->errorStatus = SC_STATUS_FAILURE;
2601 return E_FAIL;
2603 STDMETHODIMP DropTarget_Drop(DropTarget *dt, LPDATAOBJECT pIDataSource, DWORD grfKeyState,
2604 POINTL pt, PDWORD pdwEffect) {
2605 try {
2606 return dt->sci->Drop(pIDataSource, grfKeyState, pt, pdwEffect);
2607 } catch (...) {
2608 dt->sci->errorStatus = SC_STATUS_FAILURE;
2610 return E_FAIL;
2613 static VFunction *vtDropTarget[] = {
2614 (VFunction *)(DropTarget_QueryInterface),
2615 (VFunction *)(DropTarget_AddRef),
2616 (VFunction *)(DropTarget_Release),
2617 (VFunction *)(DropTarget_DragEnter),
2618 (VFunction *)(DropTarget_DragOver),
2619 (VFunction *)(DropTarget_DragLeave),
2620 (VFunction *)(DropTarget_Drop)
2623 DropTarget::DropTarget() {
2624 vtbl = vtDropTarget;
2625 sci = 0;
2629 * DBCS: support Input Method Editor (IME).
2630 * Called when IME Window opened.
2632 void ScintillaWin::ImeStartComposition() {
2633 if (caret.active) {
2634 // Move IME Window to current caret position
2635 IMContext imc(MainHWND());
2636 Point pos = PointMainCaret();
2637 COMPOSITIONFORM CompForm;
2638 CompForm.dwStyle = CFS_POINT;
2639 CompForm.ptCurrentPos.x = static_cast<int>(pos.x);
2640 CompForm.ptCurrentPos.y = static_cast<int>(pos.y);
2642 ::ImmSetCompositionWindow(imc.hIMC, &CompForm);
2644 // Set font of IME window to same as surrounded text.
2645 if (stylesValid) {
2646 // Since the style creation code has been made platform independent,
2647 // The logfont for the IME is recreated here.
2648 const int styleHere = pdoc->StyleIndexAt(sel.MainCaret());
2649 LOGFONTW lf = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, L""};
2650 int sizeZoomed = vs.styles[styleHere].size + vs.zoomLevel * SC_FONT_SIZE_MULTIPLIER;
2651 if (sizeZoomed <= 2 * SC_FONT_SIZE_MULTIPLIER) // Hangs if sizeZoomed <= 1
2652 sizeZoomed = 2 * SC_FONT_SIZE_MULTIPLIER;
2653 AutoSurface surface(this);
2654 int deviceHeight = sizeZoomed;
2655 if (surface) {
2656 deviceHeight = (sizeZoomed * surface->LogPixelsY()) / 72;
2658 // The negative is to allow for leading
2659 lf.lfHeight = -(abs(deviceHeight / SC_FONT_SIZE_MULTIPLIER));
2660 lf.lfWeight = vs.styles[styleHere].weight;
2661 lf.lfItalic = static_cast<BYTE>(vs.styles[styleHere].italic ? 1 : 0);
2662 lf.lfCharSet = DEFAULT_CHARSET;
2663 lf.lfFaceName[0] = L'\0';
2664 if (vs.styles[styleHere].fontName) {
2665 const char* fontName = vs.styles[styleHere].fontName;
2666 UTF16FromUTF8(fontName, strlen(fontName)+1, lf.lfFaceName, LF_FACESIZE);
2669 ::ImmSetCompositionFontW(imc.hIMC, &lf);
2671 // Caret is displayed in IME window. So, caret in Scintilla is useless.
2672 DropCaret();
2676 /** Called when IME Window closed. */
2677 void ScintillaWin::ImeEndComposition() {
2678 ShowCaretAtCurrentPosition();
2681 LRESULT ScintillaWin::ImeOnReconvert(LPARAM lParam) {
2682 // Reconversion on windows limits within one line without eol.
2683 // Look around: baseStart <-- (|mainStart| -- mainEnd) --> baseEnd.
2684 const int mainStart = sel.RangeMain().Start().Position();
2685 const int mainEnd = sel.RangeMain().End().Position();
2686 const int curLine = pdoc->LineFromPosition(mainStart);
2687 if (curLine != pdoc->LineFromPosition(mainEnd))
2688 return 0;
2689 const int baseStart = pdoc->LineStart(curLine);
2690 const int baseEnd = pdoc->LineEnd(curLine);
2691 if ((baseStart == baseEnd) || (mainEnd > baseEnd))
2692 return 0;
2694 const int codePage = CodePageOfDocument();
2695 const std::wstring rcFeed = StringDecode(RangeText(baseStart, baseEnd), codePage);
2696 const int rcFeedLen = static_cast<int>(rcFeed.length()) * sizeof(wchar_t);
2697 const int rcSize = sizeof(RECONVERTSTRING) + rcFeedLen + sizeof(wchar_t);
2699 RECONVERTSTRING *rc = (RECONVERTSTRING *)lParam;
2700 if (!rc)
2701 return rcSize; // Immediately be back with rcSize of memory block.
2703 wchar_t *rcFeedStart = (wchar_t*)(rc + 1);
2704 memcpy(rcFeedStart, &rcFeed[0], rcFeedLen);
2706 std::string rcCompString = RangeText(mainStart, mainEnd);
2707 std::wstring rcCompWstring = StringDecode(rcCompString, codePage);
2708 std::string rcCompStart = RangeText(baseStart, mainStart);
2709 std::wstring rcCompWstart = StringDecode(rcCompStart, codePage);
2711 // Map selection to dwCompStr.
2712 // No selection assumes current caret as rcCompString without length.
2713 rc->dwVersion = 0; // It should be absolutely 0.
2714 rc->dwStrLen = (DWORD)static_cast<int>(rcFeed.length());
2715 rc->dwStrOffset = sizeof(RECONVERTSTRING);
2716 rc->dwCompStrLen = (DWORD)static_cast<int>(rcCompWstring.length());
2717 rc->dwCompStrOffset = (DWORD)static_cast<int>(rcCompWstart.length()) * sizeof(wchar_t);
2718 rc->dwTargetStrLen = rc->dwCompStrLen;
2719 rc->dwTargetStrOffset =rc->dwCompStrOffset;
2721 IMContext imc(MainHWND());
2722 if (!imc.hIMC)
2723 return 0;
2725 if (!::ImmSetCompositionStringW(imc.hIMC, SCS_QUERYRECONVERTSTRING, rc, rcSize, NULL, 0))
2726 return 0;
2728 // No selection asks IME to fill target fields with its own value.
2729 int tgWlen = rc->dwTargetStrLen;
2730 int tgWstart = rc->dwTargetStrOffset / sizeof(wchar_t);
2732 std::string tgCompStart = StringEncode(rcFeed.substr(0, tgWstart), codePage);
2733 std::string tgComp = StringEncode(rcFeed.substr(tgWstart, tgWlen), codePage);
2735 // No selection needs to adjust reconvert start position for IME set.
2736 int adjust = static_cast<int>(tgCompStart.length() - rcCompStart.length());
2737 int docCompLen = static_cast<int>(tgComp.length());
2739 // Make place for next composition string to sit in.
2740 for (size_t r=0; r<sel.Count(); r++) {
2741 int rBase = sel.Range(r).Start().Position();
2742 int docCompStart = rBase + adjust;
2744 if (inOverstrike) { // the docCompLen of bytes will be overstriked.
2745 sel.Range(r).caret.SetPosition(docCompStart);
2746 sel.Range(r).anchor.SetPosition(docCompStart);
2747 } else {
2748 // Ensure docCompStart+docCompLen be not beyond lineEnd.
2749 // since docCompLen by byte might break eol.
2750 int lineEnd = pdoc->LineEnd(pdoc->LineFromPosition(rBase));
2751 int overflow = (docCompStart + docCompLen) - lineEnd;
2752 if (overflow > 0) {
2753 pdoc->DeleteChars(docCompStart, docCompLen - overflow);
2754 } else {
2755 pdoc->DeleteChars(docCompStart, docCompLen);
2759 // Immediately Target Input or candidate box choice with GCS_COMPSTR.
2760 return rcSize;
2763 void ScintillaWin::GetIntelliMouseParameters() {
2764 // This retrieves the number of lines per scroll as configured inthe Mouse Properties sheet in Control Panel
2765 ::SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, &linesPerScroll, 0);
2768 void ScintillaWin::CopyToClipboard(const SelectionText &selectedText) {
2769 if (!::OpenClipboardRetry(MainHWND())) {
2770 return;
2772 ::EmptyClipboard();
2774 GlobalMemory uniText;
2776 // Default Scintilla behaviour in Unicode mode
2777 if (IsUnicodeMode()) {
2778 size_t uchars = UTF16Length(selectedText.Data(),
2779 static_cast<int>(selectedText.LengthWithTerminator()));
2780 uniText.Allocate(2 * uchars);
2781 if (uniText) {
2782 UTF16FromUTF8(selectedText.Data(), selectedText.LengthWithTerminator(),
2783 static_cast<wchar_t *>(uniText.ptr), uchars);
2785 } else {
2786 // Not Unicode mode
2787 // Convert to Unicode using the current Scintilla code page
2788 UINT cpSrc = CodePageFromCharSet(
2789 selectedText.characterSet, selectedText.codePage);
2790 int uLen = ::MultiByteToWideChar(cpSrc, 0, selectedText.Data(),
2791 static_cast<int>(selectedText.LengthWithTerminator()), 0, 0);
2792 uniText.Allocate(2 * uLen);
2793 if (uniText) {
2794 ::MultiByteToWideChar(cpSrc, 0, selectedText.Data(),
2795 static_cast<int>(selectedText.LengthWithTerminator()),
2796 static_cast<wchar_t *>(uniText.ptr), uLen);
2800 if (uniText) {
2801 uniText.SetClip(CF_UNICODETEXT);
2802 } else {
2803 // There was a failure - try to copy at least ANSI text
2804 GlobalMemory ansiText;
2805 ansiText.Allocate(selectedText.LengthWithTerminator());
2806 if (ansiText) {
2807 memcpy(static_cast<char *>(ansiText.ptr), selectedText.Data(), selectedText.LengthWithTerminator());
2808 ansiText.SetClip(CF_TEXT);
2812 if (selectedText.rectangular) {
2813 ::SetClipboardData(cfColumnSelect, 0);
2815 GlobalMemory borlandSelection;
2816 borlandSelection.Allocate(1);
2817 if (borlandSelection) {
2818 static_cast<BYTE *>(borlandSelection.ptr)[0] = 0x02;
2819 borlandSelection.SetClip(cfBorlandIDEBlockType);
2823 if (selectedText.lineCopy) {
2824 ::SetClipboardData(cfLineSelect, 0);
2825 ::SetClipboardData(cfVSLineTag, 0);
2828 ::CloseClipboard();
2831 void ScintillaWin::ScrollMessage(WPARAM wParam) {
2832 //DWORD dwStart = timeGetTime();
2833 //Platform::DebugPrintf("Scroll %x %d\n", wParam, lParam);
2835 SCROLLINFO sci = {};
2836 sci.cbSize = sizeof(sci);
2837 sci.fMask = SIF_ALL;
2839 GetScrollInfo(SB_VERT, &sci);
2841 //Platform::DebugPrintf("ScrollInfo %d mask=%x min=%d max=%d page=%d pos=%d track=%d\n", b,sci.fMask,
2842 //sci.nMin, sci.nMax, sci.nPage, sci.nPos, sci.nTrackPos);
2844 int topLineNew = topLine;
2845 switch (LoWord(wParam)) {
2846 case SB_LINEUP:
2847 topLineNew -= 1;
2848 break;
2849 case SB_LINEDOWN:
2850 topLineNew += 1;
2851 break;
2852 case SB_PAGEUP:
2853 topLineNew -= LinesToScroll(); break;
2854 case SB_PAGEDOWN: topLineNew += LinesToScroll(); break;
2855 case SB_TOP: topLineNew = 0; break;
2856 case SB_BOTTOM: topLineNew = MaxScrollPos(); break;
2857 case SB_THUMBPOSITION: topLineNew = sci.nTrackPos; break;
2858 case SB_THUMBTRACK: topLineNew = sci.nTrackPos; break;
2860 ScrollTo(topLineNew);
2863 void ScintillaWin::HorizontalScrollMessage(WPARAM wParam) {
2864 int xPos = xOffset;
2865 PRectangle rcText = GetTextRectangle();
2866 int pageWidth = static_cast<int>(rcText.Width() * 2 / 3);
2867 switch (LoWord(wParam)) {
2868 case SB_LINEUP:
2869 xPos -= 20;
2870 break;
2871 case SB_LINEDOWN: // May move past the logical end
2872 xPos += 20;
2873 break;
2874 case SB_PAGEUP:
2875 xPos -= pageWidth;
2876 break;
2877 case SB_PAGEDOWN:
2878 xPos += pageWidth;
2879 if (xPos > scrollWidth - rcText.Width()) { // Hit the end exactly
2880 xPos = scrollWidth - static_cast<int>(rcText.Width());
2882 break;
2883 case SB_TOP:
2884 xPos = 0;
2885 break;
2886 case SB_BOTTOM:
2887 xPos = scrollWidth;
2888 break;
2889 case SB_THUMBPOSITION:
2890 case SB_THUMBTRACK: {
2891 // 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 =]
2892 SCROLLINFO si;
2893 si.cbSize = sizeof(si);
2894 si.fMask = SIF_TRACKPOS;
2895 if (GetScrollInfo(SB_HORZ, &si)) {
2896 xPos = si.nTrackPos;
2899 break;
2901 HorizontalScrollTo(xPos);
2905 * Redraw all of text area.
2906 * This paint will not be abandoned.
2908 void ScintillaWin::FullPaint() {
2909 if ((technology == SC_TECHNOLOGY_DEFAULT) || (technology == SC_TECHNOLOGY_DIRECTWRITEDC)) {
2910 HDC hdc = ::GetDC(MainHWND());
2911 FullPaintDC(hdc);
2912 ::ReleaseDC(MainHWND(), hdc);
2913 } else {
2914 FullPaintDC(0);
2919 * Redraw all of text area on the specified DC.
2920 * This paint will not be abandoned.
2922 void ScintillaWin::FullPaintDC(HDC hdc) {
2923 paintState = painting;
2924 rcPaint = GetClientRectangle();
2925 paintingAllText = true;
2926 if (technology == SC_TECHNOLOGY_DEFAULT) {
2927 AutoSurface surfaceWindow(hdc, this);
2928 if (surfaceWindow) {
2929 Paint(surfaceWindow, rcPaint);
2930 surfaceWindow->Release();
2932 } else {
2933 #if defined(USE_D2D)
2934 EnsureRenderTarget(hdc);
2935 AutoSurface surfaceWindow(pRenderTarget, this);
2936 if (surfaceWindow) {
2937 pRenderTarget->BeginDraw();
2938 Paint(surfaceWindow, rcPaint);
2939 surfaceWindow->Release();
2940 HRESULT hr = pRenderTarget->EndDraw();
2941 if (hr == static_cast<HRESULT>(D2DERR_RECREATE_TARGET)) {
2942 DropRenderTarget();
2945 #endif
2947 paintState = notPainting;
2950 static bool CompareDevCap(HDC hdc, HDC hOtherDC, int nIndex) {
2951 return ::GetDeviceCaps(hdc, nIndex) == ::GetDeviceCaps(hOtherDC, nIndex);
2954 bool ScintillaWin::IsCompatibleDC(HDC hOtherDC) {
2955 HDC hdc = ::GetDC(MainHWND());
2956 bool isCompatible =
2957 CompareDevCap(hdc, hOtherDC, TECHNOLOGY) &&
2958 CompareDevCap(hdc, hOtherDC, LOGPIXELSY) &&
2959 CompareDevCap(hdc, hOtherDC, LOGPIXELSX) &&
2960 CompareDevCap(hdc, hOtherDC, BITSPIXEL) &&
2961 CompareDevCap(hdc, hOtherDC, PLANES);
2962 ::ReleaseDC(MainHWND(), hdc);
2963 return isCompatible;
2966 DWORD ScintillaWin::EffectFromState(DWORD grfKeyState) const {
2967 // These are the Wordpad semantics.
2968 DWORD dwEffect;
2969 if (inDragDrop == ddDragging) // Internal defaults to move
2970 dwEffect = DROPEFFECT_MOVE;
2971 else
2972 dwEffect = DROPEFFECT_COPY;
2973 if (grfKeyState & MK_ALT)
2974 dwEffect = DROPEFFECT_MOVE;
2975 if (grfKeyState & MK_CONTROL)
2976 dwEffect = DROPEFFECT_COPY;
2977 return dwEffect;
2980 /// Implement IUnknown
2981 STDMETHODIMP ScintillaWin::QueryInterface(REFIID riid, PVOID *ppv) {
2982 *ppv = NULL;
2983 if (riid == IID_IUnknown)
2984 *ppv = reinterpret_cast<IDropTarget *>(&dt);
2985 if (riid == IID_IDropSource)
2986 *ppv = reinterpret_cast<IDropSource *>(&ds);
2987 if (riid == IID_IDropTarget)
2988 *ppv = reinterpret_cast<IDropTarget *>(&dt);
2989 if (riid == IID_IDataObject)
2990 *ppv = reinterpret_cast<IDataObject *>(&dob);
2991 if (!*ppv)
2992 return E_NOINTERFACE;
2993 return S_OK;
2996 STDMETHODIMP_(ULONG) ScintillaWin::AddRef() {
2997 return 1;
3000 STDMETHODIMP_(ULONG) ScintillaWin::Release() {
3001 return 1;
3004 /// Implement IDropTarget
3005 STDMETHODIMP ScintillaWin::DragEnter(LPDATAOBJECT pIDataSource, DWORD grfKeyState,
3006 POINTL, PDWORD pdwEffect) {
3007 if (pIDataSource == NULL)
3008 return E_POINTER;
3009 FORMATETC fmtu = {CF_UNICODETEXT, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
3010 HRESULT hrHasUText = pIDataSource->QueryGetData(&fmtu);
3011 hasOKText = (hrHasUText == S_OK);
3012 if (!hasOKText) {
3013 FORMATETC fmte = {CF_TEXT, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
3014 HRESULT hrHasText = pIDataSource->QueryGetData(&fmte);
3015 hasOKText = (hrHasText == S_OK);
3017 if (!hasOKText) {
3018 *pdwEffect = DROPEFFECT_NONE;
3019 return S_OK;
3022 *pdwEffect = EffectFromState(grfKeyState);
3023 return S_OK;
3026 STDMETHODIMP ScintillaWin::DragOver(DWORD grfKeyState, POINTL pt, PDWORD pdwEffect) {
3027 try {
3028 if (!hasOKText || pdoc->IsReadOnly()) {
3029 *pdwEffect = DROPEFFECT_NONE;
3030 return S_OK;
3033 *pdwEffect = EffectFromState(grfKeyState);
3035 // Update the cursor.
3036 POINT rpt = {pt.x, pt.y};
3037 ::ScreenToClient(MainHWND(), &rpt);
3038 SetDragPosition(SPositionFromLocation(PointFromPOINT(rpt), false, false, UserVirtualSpace()));
3040 return S_OK;
3041 } catch (...) {
3042 errorStatus = SC_STATUS_FAILURE;
3044 return E_FAIL;
3047 STDMETHODIMP ScintillaWin::DragLeave() {
3048 try {
3049 SetDragPosition(SelectionPosition(invalidPosition));
3050 return S_OK;
3051 } catch (...) {
3052 errorStatus = SC_STATUS_FAILURE;
3054 return E_FAIL;
3057 STDMETHODIMP ScintillaWin::Drop(LPDATAOBJECT pIDataSource, DWORD grfKeyState,
3058 POINTL pt, PDWORD pdwEffect) {
3059 try {
3060 *pdwEffect = EffectFromState(grfKeyState);
3062 if (pIDataSource == NULL)
3063 return E_POINTER;
3065 SetDragPosition(SelectionPosition(invalidPosition));
3067 STGMEDIUM medium = {0, {0}, 0};
3069 std::vector<char> data; // Includes terminating NUL
3071 FORMATETC fmtu = {CF_UNICODETEXT, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
3072 HRESULT hr = pIDataSource->GetData(&fmtu, &medium);
3073 if (SUCCEEDED(hr) && medium.hGlobal) {
3074 GlobalMemory memUDrop(medium.hGlobal);
3075 wchar_t *udata = static_cast<wchar_t *>(memUDrop.ptr);
3076 if (udata) {
3077 if (IsUnicodeMode()) {
3078 int tlen = static_cast<int>(memUDrop.Size());
3079 // Convert UTF-16 to UTF-8
3080 int dataLen = UTF8Length(udata, tlen/2);
3081 data.resize(dataLen+1);
3082 UTF8FromUTF16(udata, tlen/2, &data[0], dataLen);
3083 } else {
3084 // Convert UTF-16 to ANSI
3086 // Default Scintilla behavior in Unicode mode
3087 // CF_UNICODETEXT available, but not in Unicode mode
3088 // Convert from Unicode to current Scintilla code page
3089 UINT cpDest = CodePageOfDocument();
3090 int tlen = ::WideCharToMultiByte(cpDest, 0, udata, -1,
3091 NULL, 0, NULL, NULL) - 1; // subtract 0 terminator
3092 data.resize(tlen + 1);
3093 ::WideCharToMultiByte(cpDest, 0, udata, -1,
3094 &data[0], tlen + 1, NULL, NULL);
3097 memUDrop.Unlock();
3098 } else {
3099 FORMATETC fmte = {CF_TEXT, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
3100 hr = pIDataSource->GetData(&fmte, &medium);
3101 if (SUCCEEDED(hr) && medium.hGlobal) {
3102 GlobalMemory memDrop(medium.hGlobal);
3103 const char *cdata = static_cast<char *>(memDrop.ptr);
3104 if (cdata)
3105 data.assign(cdata, cdata+strlen(cdata)+1);
3106 memDrop.Unlock();
3110 if (!SUCCEEDED(hr) || data.empty()) {
3111 //Platform::DebugPrintf("Bad data format: 0x%x\n", hres);
3112 return hr;
3115 FORMATETC fmtr = {cfColumnSelect, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
3116 HRESULT hrRectangular = pIDataSource->QueryGetData(&fmtr);
3118 POINT rpt = {pt.x, pt.y};
3119 ::ScreenToClient(MainHWND(), &rpt);
3120 SelectionPosition movePos = SPositionFromLocation(PointFromPOINT(rpt), false, false, UserVirtualSpace());
3122 DropAt(movePos, &data[0], data.size() - 1, *pdwEffect == DROPEFFECT_MOVE, hrRectangular == S_OK);
3124 // Free data
3125 if (medium.pUnkForRelease != NULL)
3126 medium.pUnkForRelease->Release();
3127 else
3128 ::GlobalFree(medium.hGlobal);
3130 return S_OK;
3131 } catch (...) {
3132 errorStatus = SC_STATUS_FAILURE;
3134 return E_FAIL;
3137 /// Implement important part of IDataObject
3138 STDMETHODIMP ScintillaWin::GetData(FORMATETC *pFEIn, STGMEDIUM *pSTM) {
3139 bool formatOK = (pFEIn->cfFormat == CF_TEXT) ||
3140 ((pFEIn->cfFormat == CF_UNICODETEXT) && IsUnicodeMode());
3141 if (!formatOK ||
3142 pFEIn->ptd != 0 ||
3143 (pFEIn->dwAspect & DVASPECT_CONTENT) == 0 ||
3144 pFEIn->lindex != -1 ||
3145 (pFEIn->tymed & TYMED_HGLOBAL) == 0
3147 //Platform::DebugPrintf("DOB GetData No %d %x %x fmt=%x\n", lenDrag, pFEIn, pSTM, pFEIn->cfFormat);
3148 return DATA_E_FORMATETC;
3150 pSTM->tymed = TYMED_HGLOBAL;
3151 //Platform::DebugPrintf("DOB GetData OK %d %x %x\n", lenDrag, pFEIn, pSTM);
3153 GlobalMemory text;
3154 if (pFEIn->cfFormat == CF_UNICODETEXT) {
3155 size_t uchars = UTF16Length(drag.Data(), static_cast<int>(drag.LengthWithTerminator()));
3156 text.Allocate(2 * uchars);
3157 if (text) {
3158 UTF16FromUTF8(drag.Data(), drag.LengthWithTerminator(),
3159 static_cast<wchar_t *>(text.ptr), uchars);
3161 } else {
3162 text.Allocate(drag.LengthWithTerminator());
3163 if (text) {
3164 memcpy(static_cast<char *>(text.ptr), drag.Data(), drag.LengthWithTerminator());
3167 pSTM->hGlobal = text ? text.Unlock() : 0;
3168 pSTM->pUnkForRelease = 0;
3169 return S_OK;
3172 bool ScintillaWin::Register(HINSTANCE hInstance_) {
3174 hInstance = hInstance_;
3175 bool result;
3177 // Register the Scintilla class
3178 // Register Scintilla as a wide character window
3179 WNDCLASSEXW wndclass;
3180 wndclass.cbSize = sizeof(wndclass);
3181 wndclass.style = CS_GLOBALCLASS | CS_HREDRAW | CS_VREDRAW;
3182 wndclass.lpfnWndProc = ScintillaWin::SWndProc;
3183 wndclass.cbClsExtra = 0;
3184 wndclass.cbWndExtra = sizeof(ScintillaWin *);
3185 wndclass.hInstance = hInstance;
3186 wndclass.hIcon = NULL;
3187 wndclass.hCursor = NULL;
3188 wndclass.hbrBackground = NULL;
3189 wndclass.lpszMenuName = NULL;
3190 wndclass.lpszClassName = L"Scintilla";
3191 wndclass.hIconSm = 0;
3192 scintillaClassAtom = ::RegisterClassExW(&wndclass);
3193 result = 0 != scintillaClassAtom;
3195 if (result) {
3196 // Register the CallTip class
3197 WNDCLASSEX wndclassc;
3198 wndclassc.cbSize = sizeof(wndclassc);
3199 wndclassc.style = CS_GLOBALCLASS | CS_HREDRAW | CS_VREDRAW;
3200 wndclassc.cbClsExtra = 0;
3201 wndclassc.cbWndExtra = sizeof(ScintillaWin *);
3202 wndclassc.hInstance = hInstance;
3203 wndclassc.hIcon = NULL;
3204 wndclassc.hbrBackground = NULL;
3205 wndclassc.lpszMenuName = NULL;
3206 wndclassc.lpfnWndProc = ScintillaWin::CTWndProc;
3207 wndclassc.hCursor = ::LoadCursor(NULL, IDC_ARROW);
3208 wndclassc.lpszClassName = callClassName;
3209 wndclassc.hIconSm = 0;
3211 callClassAtom = ::RegisterClassEx(&wndclassc);
3212 result = 0 != callClassAtom;
3215 return result;
3218 bool ScintillaWin::Unregister() {
3219 bool result = true;
3220 if (0 != scintillaClassAtom) {
3221 if (::UnregisterClass(MAKEINTATOM(scintillaClassAtom), hInstance) == 0) {
3222 result = false;
3224 scintillaClassAtom = 0;
3226 if (0 != callClassAtom) {
3227 if (::UnregisterClass(MAKEINTATOM(callClassAtom), hInstance) == 0) {
3228 result = false;
3230 callClassAtom = 0;
3232 return result;
3235 bool ScintillaWin::HasCaretSizeChanged() const {
3236 if (
3237 ( (0 != vs.caretWidth) && (sysCaretWidth != vs.caretWidth) )
3238 || ((0 != vs.lineHeight) && (sysCaretHeight != vs.lineHeight))
3240 return true;
3242 return false;
3245 BOOL ScintillaWin::CreateSystemCaret() {
3246 sysCaretWidth = vs.caretWidth;
3247 if (0 == sysCaretWidth) {
3248 sysCaretWidth = 1;
3250 sysCaretHeight = vs.lineHeight;
3251 int bitmapSize = (((sysCaretWidth + 15) & ~15) >> 3) *
3252 sysCaretHeight;
3253 std::vector<char> bits(bitmapSize);
3254 sysCaretBitmap = ::CreateBitmap(sysCaretWidth, sysCaretHeight, 1,
3255 1, reinterpret_cast<BYTE *>(&bits[0]));
3256 BOOL retval = ::CreateCaret(
3257 MainHWND(), sysCaretBitmap,
3258 sysCaretWidth, sysCaretHeight);
3259 if (technology == SC_TECHNOLOGY_DEFAULT) {
3260 // System caret interferes with Direct2D drawing so only show it for GDI.
3261 ::ShowCaret(MainHWND());
3263 return retval;
3266 BOOL ScintillaWin::DestroySystemCaret() {
3267 ::HideCaret(MainHWND());
3268 BOOL retval = ::DestroyCaret();
3269 if (sysCaretBitmap) {
3270 ::DeleteObject(sysCaretBitmap);
3271 sysCaretBitmap = 0;
3273 return retval;
3276 LRESULT PASCAL ScintillaWin::CTWndProc(
3277 HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam) {
3278 // Find C++ object associated with window.
3279 ScintillaWin *sciThis = static_cast<ScintillaWin *>(PointerFromWindow(hWnd));
3280 try {
3281 // ctp will be zero if WM_CREATE not seen yet
3282 if (sciThis == 0) {
3283 if (iMessage == WM_CREATE) {
3284 // Associate CallTip object with window
3285 CREATESTRUCT *pCreate = reinterpret_cast<CREATESTRUCT *>(lParam);
3286 SetWindowPointer(hWnd, pCreate->lpCreateParams);
3287 return 0;
3288 } else {
3289 return ::DefWindowProc(hWnd, iMessage, wParam, lParam);
3291 } else {
3292 if (iMessage == WM_NCDESTROY) {
3293 ::SetWindowLong(hWnd, 0, 0);
3294 return ::DefWindowProc(hWnd, iMessage, wParam, lParam);
3295 } else if (iMessage == WM_PAINT) {
3296 PAINTSTRUCT ps;
3297 ::BeginPaint(hWnd, &ps);
3298 Surface *surfaceWindow = Surface::Allocate(sciThis->technology);
3299 if (surfaceWindow) {
3300 #if defined(USE_D2D)
3301 ID2D1HwndRenderTarget *pCTRenderTarget = 0;
3302 #endif
3303 RECT rc;
3304 GetClientRect(hWnd, &rc);
3305 // Create a Direct2D render target.
3306 if (sciThis->technology == SC_TECHNOLOGY_DEFAULT) {
3307 surfaceWindow->Init(ps.hdc, hWnd);
3308 } else {
3309 #if defined(USE_D2D)
3310 D2D1_HWND_RENDER_TARGET_PROPERTIES dhrtp;
3311 dhrtp.hwnd = hWnd;
3312 dhrtp.pixelSize = D2D1::SizeU(rc.right - rc.left, rc.bottom - rc.top);
3313 dhrtp.presentOptions = (sciThis->technology == SC_TECHNOLOGY_DIRECTWRITERETAIN) ?
3314 D2D1_PRESENT_OPTIONS_RETAIN_CONTENTS : D2D1_PRESENT_OPTIONS_NONE;
3316 D2D1_RENDER_TARGET_PROPERTIES drtp;
3317 drtp.type = D2D1_RENDER_TARGET_TYPE_DEFAULT;
3318 drtp.pixelFormat.format = DXGI_FORMAT_UNKNOWN;
3319 drtp.pixelFormat.alphaMode = D2D1_ALPHA_MODE_UNKNOWN;
3320 drtp.dpiX = 96.0;
3321 drtp.dpiY = 96.0;
3322 drtp.usage = D2D1_RENDER_TARGET_USAGE_NONE;
3323 drtp.minLevel = D2D1_FEATURE_LEVEL_DEFAULT;
3325 if (!SUCCEEDED(pD2DFactory->CreateHwndRenderTarget(drtp, dhrtp, &pCTRenderTarget))) {
3326 surfaceWindow->Release();
3327 delete surfaceWindow;
3328 ::EndPaint(hWnd, &ps);
3329 return 0;
3331 surfaceWindow->Init(pCTRenderTarget, hWnd);
3332 pCTRenderTarget->BeginDraw();
3333 #endif
3335 surfaceWindow->SetUnicodeMode(SC_CP_UTF8 == sciThis->ct.codePage);
3336 surfaceWindow->SetDBCSMode(sciThis->ct.codePage);
3337 sciThis->ct.PaintCT(surfaceWindow);
3338 #if defined(USE_D2D)
3339 if (pCTRenderTarget)
3340 pCTRenderTarget->EndDraw();
3341 #endif
3342 surfaceWindow->Release();
3343 delete surfaceWindow;
3344 #if defined(USE_D2D)
3345 if (pCTRenderTarget)
3346 pCTRenderTarget->Release();
3347 #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 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 bool result = ScintillaWin::Unregister();
3440 if (commctrl32) {
3441 FreeLibrary(commctrl32);
3442 commctrl32 = NULL;
3444 Platform_Finalise(fromDllMain);
3445 return result;
3448 // This function is externally visible so it can be called from container when building statically.
3449 int Scintilla_ReleaseResources() {
3450 return ResourcesRelease(false);
3453 #ifndef STATIC_BUILD
3454 extern "C" int APIENTRY DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpvReserved) {
3455 //Platform::DebugPrintf("Scintilla::DllMain %d %d\n", hInstance, dwReason);
3456 if (dwReason == DLL_PROCESS_ATTACH) {
3457 if (!Scintilla_RegisterClasses(hInstance))
3458 return FALSE;
3459 } else if (dwReason == DLL_PROCESS_DETACH) {
3460 if (lpvReserved == NULL) {
3461 ResourcesRelease(true);
3464 return TRUE;
3466 #endif