Update Scintilla to v3.4.1
[TortoiseGit.git] / ext / scintilla / win32 / ScintillaWin.cxx
blob7bc542f0d8b404b3c3f9edb9aa0cef3172e5c918
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 <ctype.h>
12 #include <assert.h>
13 #include <limits.h>
15 #include <new>
16 #include <string>
17 #include <vector>
18 #include <map>
19 #include <algorithm>
21 #undef _WIN32_WINNT
22 #define _WIN32_WINNT 0x0500
23 #undef WINVER
24 #define WINVER 0x0500
25 #include <windows.h>
26 #include <commctrl.h>
27 #include <richedit.h>
28 #include <windowsx.h>
30 #if defined(NTDDI_WIN7) && !defined(DISABLE_D2D)
31 #define USE_D2D 1
32 #endif
34 #if defined(USE_D2D)
35 #include <d2d1.h>
36 #include <dwrite.h>
37 #endif
39 #include "Platform.h"
41 #include "ILexer.h"
42 #include "Scintilla.h"
44 #ifdef SCI_LEXER
45 #include "SciLexer.h"
46 #include "LexerModule.h"
47 #endif
48 #include "StringCopy.h"
49 #include "SplitVector.h"
50 #include "Partitioning.h"
51 #include "RunStyles.h"
52 #include "ContractionState.h"
53 #include "CellBuffer.h"
54 #include "CallTip.h"
55 #include "KeyMap.h"
56 #include "Indicator.h"
57 #include "XPM.h"
58 #include "LineMarker.h"
59 #include "Style.h"
60 #include "AutoComplete.h"
61 #include "ViewStyle.h"
62 #include "CharClassify.h"
63 #include "Decoration.h"
64 #include "CaseFolder.h"
65 #include "Document.h"
66 #include "Selection.h"
67 #include "PositionCache.h"
68 #include "Editor.h"
69 #include "ScintillaBase.h"
70 #include "UniConversion.h"
71 #include "CaseConvert.h"
73 #include "PlatWin.h"
75 #ifdef SCI_LEXER
76 #include "ExternalLexer.h"
77 #endif
79 #ifndef SPI_GETWHEELSCROLLLINES
80 #define SPI_GETWHEELSCROLLLINES 104
81 #endif
83 #ifndef WM_UNICHAR
84 #define WM_UNICHAR 0x0109
85 #endif
87 #ifndef UNICODE_NOCHAR
88 #define UNICODE_NOCHAR 0xFFFF
89 #endif
91 #include <commctrl.h>
92 #include <zmouse.h>
93 #include <ole2.h>
95 #ifndef MK_ALT
96 #define MK_ALT 32
97 #endif
99 #define SC_WIN_IDLE 5001
101 typedef BOOL (WINAPI *TrackMouseEventSig)(LPTRACKMOUSEEVENT);
103 // GCC has trouble with the standard COM ABI so do it the old C way with explicit vtables.
105 const TCHAR scintillaClassName[] = TEXT("Scintilla");
106 const TCHAR callClassName[] = TEXT("CallTip");
108 #ifdef SCI_NAMESPACE
109 using namespace Scintilla;
110 #endif
112 static void *PointerFromWindow(HWND hWnd) {
113 return reinterpret_cast<void *>(::GetWindowLongPtr(hWnd, 0));
116 static void SetWindowPointer(HWND hWnd, void *ptr) {
117 ::SetWindowLongPtr(hWnd, 0, reinterpret_cast<LONG_PTR>(ptr));
120 static void SetWindowID(HWND hWnd, int identifier) {
121 ::SetWindowLongPtr(hWnd, GWLP_ID, identifier);
124 class ScintillaWin; // Forward declaration for COM interface subobjects
126 typedef void VFunction(void);
130 class FormatEnumerator {
131 public:
132 VFunction **vtbl;
133 int ref;
134 unsigned int pos;
135 std::vector<CLIPFORMAT> formats;
136 FormatEnumerator(int pos_, CLIPFORMAT formats_[], size_t formatsLen_);
141 class DropSource {
142 public:
143 VFunction **vtbl;
144 ScintillaWin *sci;
145 DropSource();
150 class DataObject {
151 public:
152 VFunction **vtbl;
153 ScintillaWin *sci;
154 DataObject();
159 class DropTarget {
160 public:
161 VFunction **vtbl;
162 ScintillaWin *sci;
163 DropTarget();
168 class ScintillaWin :
169 public ScintillaBase {
171 bool lastKeyDownConsumed;
173 bool capturedMouse;
174 bool trackedMouseLeave;
175 TrackMouseEventSig TrackMouseEventFn;
177 unsigned int linesPerScroll; ///< Intellimouse support
178 int wheelDelta; ///< Wheel delta from roll
180 HRGN hRgnUpdate;
182 bool hasOKText;
184 CLIPFORMAT cfColumnSelect;
185 CLIPFORMAT cfBorlandIDEBlockType;
186 CLIPFORMAT cfLineSelect;
188 HRESULT hrOle;
189 DropSource ds;
190 DataObject dob;
191 DropTarget dt;
193 static HINSTANCE hInstance;
195 #if defined(USE_D2D)
196 ID2D1HwndRenderTarget *pRenderTarget;
197 bool renderTargetValid;
198 #endif
200 explicit ScintillaWin(HWND hwnd);
201 ScintillaWin(const ScintillaWin &);
202 virtual ~ScintillaWin();
203 ScintillaWin &operator=(const ScintillaWin &);
205 virtual void Initialise();
206 virtual void Finalise();
207 #if defined(USE_D2D)
208 void EnsureRenderTarget();
209 void DropRenderTarget();
210 #endif
211 HWND MainHWND();
213 static sptr_t DirectFunction(
214 ScintillaWin *sci, UINT iMessage, uptr_t wParam, sptr_t lParam);
215 static sptr_t PASCAL SWndProc(
216 HWND hWnd, UINT iMessage, WPARAM wParam, sptr_t lParam);
217 static sptr_t PASCAL CTWndProc(
218 HWND hWnd, UINT iMessage, WPARAM wParam, sptr_t lParam);
220 enum { invalidTimerID, standardTimerID, idleTimerID };
222 virtual bool DragThreshold(Point ptStart, Point ptNow);
223 virtual void StartDrag();
224 sptr_t WndPaint(uptr_t wParam);
225 sptr_t HandleComposition(uptr_t wParam, sptr_t lParam);
226 UINT CodePageOfDocument();
227 virtual bool ValidCodePage(int codePage) const;
228 virtual sptr_t DefWndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam);
229 virtual bool SetIdle(bool on);
230 virtual void SetTicking(bool on);
231 virtual void SetMouseCapture(bool on);
232 virtual bool HaveMouseCapture();
233 virtual void SetTrackMouseLeaveEvent(bool on);
234 virtual bool PaintContains(PRectangle rc);
235 virtual void ScrollText(int linesToMove);
236 virtual void UpdateSystemCaret();
237 virtual void SetVerticalScrollPos();
238 virtual void SetHorizontalScrollPos();
239 virtual bool ModifyScrollBars(int nMax, int nPage);
240 virtual void NotifyChange();
241 virtual void NotifyFocus(bool focus);
242 virtual void SetCtrlID(int identifier);
243 virtual int GetCtrlID();
244 virtual void NotifyParent(SCNotification scn);
245 virtual void NotifyDoubleClick(Point pt, int modifiers);
246 virtual CaseFolder *CaseFolderForEncoding();
247 virtual std::string CaseMapString(const std::string &s, int caseMapping);
248 virtual void Copy();
249 virtual void CopyAllowLine();
250 virtual bool CanPaste();
251 virtual void Paste();
252 virtual void CreateCallTipWindow(PRectangle rc);
253 virtual void AddToPopUp(const char *label, int cmd = 0, bool enabled = true);
254 virtual void ClaimSelection();
256 // DBCS
257 void ImeStartComposition();
258 void ImeEndComposition();
260 void AddCharBytes(char b0, char b1);
262 void GetIntelliMouseParameters();
263 virtual void CopyToClipboard(const SelectionText &selectedText);
264 void ScrollMessage(WPARAM wParam);
265 void HorizontalScrollMessage(WPARAM wParam);
266 void FullPaint();
267 void FullPaintDC(HDC dc);
268 bool IsCompatibleDC(HDC dc);
269 DWORD EffectFromState(DWORD grfKeyState) const;
271 virtual int SetScrollInfo(int nBar, LPCSCROLLINFO lpsi, BOOL bRedraw);
272 virtual bool GetScrollInfo(int nBar, LPSCROLLINFO lpsi);
273 void ChangeScrollPos(int barType, int pos);
275 void InsertPasteText(const char *text, int len, SelectionPosition selStart, bool isRectangular, bool isLine);
277 public:
278 // Public for benefit of Scintilla_DirectFunction
279 virtual sptr_t WndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam);
281 /// Implement IUnknown
282 STDMETHODIMP QueryInterface(REFIID riid, PVOID *ppv);
283 STDMETHODIMP_(ULONG)AddRef();
284 STDMETHODIMP_(ULONG)Release();
286 /// Implement IDropTarget
287 STDMETHODIMP DragEnter(LPDATAOBJECT pIDataSource, DWORD grfKeyState,
288 POINTL pt, PDWORD pdwEffect);
289 STDMETHODIMP DragOver(DWORD grfKeyState, POINTL pt, PDWORD pdwEffect);
290 STDMETHODIMP DragLeave();
291 STDMETHODIMP Drop(LPDATAOBJECT pIDataSource, DWORD grfKeyState,
292 POINTL pt, PDWORD pdwEffect);
294 /// Implement important part of IDataObject
295 STDMETHODIMP GetData(FORMATETC *pFEIn, STGMEDIUM *pSTM);
297 static bool Register(HINSTANCE hInstance_);
298 static bool Unregister();
300 friend class DropSource;
301 friend class DataObject;
302 friend class DropTarget;
303 bool DragIsRectangularOK(CLIPFORMAT fmt) const {
304 return drag.rectangular && (fmt == cfColumnSelect);
307 private:
308 // For use in creating a system caret
309 bool HasCaretSizeChanged() const;
310 BOOL CreateSystemCaret();
311 BOOL DestroySystemCaret();
312 HBITMAP sysCaretBitmap;
313 int sysCaretWidth;
314 int sysCaretHeight;
315 bool keysAlwaysUnicode;
318 HINSTANCE ScintillaWin::hInstance = 0;
320 ScintillaWin::ScintillaWin(HWND hwnd) {
322 lastKeyDownConsumed = false;
324 capturedMouse = false;
325 trackedMouseLeave = false;
326 TrackMouseEventFn = 0;
328 linesPerScroll = 0;
329 wheelDelta = 0; // Wheel delta from roll
331 hRgnUpdate = 0;
333 hasOKText = false;
335 // There does not seem to be a real standard for indicating that the clipboard
336 // contains a rectangular selection, so copy Developer Studio and Borland Delphi.
337 cfColumnSelect = static_cast<CLIPFORMAT>(
338 ::RegisterClipboardFormat(TEXT("MSDEVColumnSelect")));
339 cfBorlandIDEBlockType = static_cast<CLIPFORMAT>(
340 ::RegisterClipboardFormat(TEXT("Borland IDE Block Type")));
342 // Likewise for line-copy (copies a full line when no text is selected)
343 cfLineSelect = static_cast<CLIPFORMAT>(
344 ::RegisterClipboardFormat(TEXT("MSDEVLineSelect")));
346 hrOle = E_FAIL;
348 wMain = hwnd;
350 dob.sci = this;
351 ds.sci = this;
352 dt.sci = this;
354 sysCaretBitmap = 0;
355 sysCaretWidth = 0;
356 sysCaretHeight = 0;
358 #if defined(USE_D2D)
359 pRenderTarget = 0;
360 renderTargetValid = true;
361 #endif
363 keysAlwaysUnicode = false;
365 caret.period = ::GetCaretBlinkTime();
366 if (caret.period < 0)
367 caret.period = 0;
369 Initialise();
372 ScintillaWin::~ScintillaWin() {}
374 void ScintillaWin::Initialise() {
375 // Initialize COM. If the app has already done this it will have
376 // no effect. If the app hasnt, we really shouldnt ask them to call
377 // it just so this internal feature works.
378 hrOle = ::OleInitialize(NULL);
380 // Find TrackMouseEvent which is available on Windows > 95
381 HMODULE user32 = ::GetModuleHandle(TEXT("user32.dll"));
382 if (user32)
383 TrackMouseEventFn = (TrackMouseEventSig)::GetProcAddress(user32, "TrackMouseEvent");
384 if (TrackMouseEventFn == NULL) {
385 // Windows 95 has an emulation in comctl32.dll:_TrackMouseEvent
386 HMODULE commctrl32 = ::LoadLibrary(TEXT("comctl32.dll"));
387 if (commctrl32 != NULL) {
388 TrackMouseEventFn = (TrackMouseEventSig)
389 ::GetProcAddress(commctrl32, "_TrackMouseEvent");
394 void ScintillaWin::Finalise() {
395 ScintillaBase::Finalise();
396 SetTicking(false);
397 SetIdle(false);
398 #if defined(USE_D2D)
399 DropRenderTarget();
400 #endif
401 ::RevokeDragDrop(MainHWND());
402 if (SUCCEEDED(hrOle)) {
403 ::OleUninitialize();
407 #if defined(USE_D2D)
409 void ScintillaWin::EnsureRenderTarget() {
410 if (!renderTargetValid) {
411 DropRenderTarget();
412 renderTargetValid = true;
414 if (pD2DFactory && !pRenderTarget) {
415 RECT rc;
416 HWND hw = MainHWND();
417 GetClientRect(hw, &rc);
419 D2D1_SIZE_U size = D2D1::SizeU(rc.right - rc.left, rc.bottom - rc.top);
421 // Create a Direct2D render target.
422 #if 1
423 D2D1_HWND_RENDER_TARGET_PROPERTIES dhrtp;
424 dhrtp.hwnd = hw;
425 dhrtp.pixelSize = size;
426 dhrtp.presentOptions = D2D1_PRESENT_OPTIONS_NONE;
428 D2D1_RENDER_TARGET_PROPERTIES drtp;
429 drtp.type = D2D1_RENDER_TARGET_TYPE_DEFAULT;
430 drtp.pixelFormat.format = DXGI_FORMAT_UNKNOWN;
431 drtp.pixelFormat.alphaMode = D2D1_ALPHA_MODE_UNKNOWN;
432 drtp.dpiX = 96.0;
433 drtp.dpiY = 96.0;
434 drtp.usage = D2D1_RENDER_TARGET_USAGE_NONE;
435 drtp.minLevel = D2D1_FEATURE_LEVEL_DEFAULT;
437 pD2DFactory->CreateHwndRenderTarget(drtp, dhrtp, &pRenderTarget);
438 #else
439 pD2DFactory->CreateHwndRenderTarget(
440 D2D1::RenderTargetProperties(
441 D2D1_RENDER_TARGET_TYPE_DEFAULT ,
442 D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_PREMULTIPLIED),
443 96.0f, 96.0f, D2D1_RENDER_TARGET_USAGE_NONE, D2D1_FEATURE_LEVEL_DEFAULT),
444 D2D1::HwndRenderTargetProperties(hw, size),
445 &pRenderTarget);
446 #endif
447 // Pixmaps were created to be compatible with previous render target so
448 // need to be recreated.
449 DropGraphics(false);
453 void ScintillaWin::DropRenderTarget() {
454 if (pRenderTarget) {
455 pRenderTarget->Release();
456 pRenderTarget = 0;
460 #endif
462 HWND ScintillaWin::MainHWND() {
463 return reinterpret_cast<HWND>(wMain.GetID());
466 bool ScintillaWin::DragThreshold(Point ptStart, Point ptNow) {
467 int xMove = abs(ptStart.x - ptNow.x);
468 int yMove = abs(ptStart.y - ptNow.y);
469 return (xMove > ::GetSystemMetrics(SM_CXDRAG)) ||
470 (yMove > ::GetSystemMetrics(SM_CYDRAG));
473 void ScintillaWin::StartDrag() {
474 inDragDrop = ddDragging;
475 DWORD dwEffect = 0;
476 dropWentOutside = true;
477 IDataObject *pDataObject = reinterpret_cast<IDataObject *>(&dob);
478 IDropSource *pDropSource = reinterpret_cast<IDropSource *>(&ds);
479 //Platform::DebugPrintf("About to DoDragDrop %x %x\n", pDataObject, pDropSource);
480 HRESULT hr = ::DoDragDrop(
481 pDataObject,
482 pDropSource,
483 DROPEFFECT_COPY | DROPEFFECT_MOVE, &dwEffect);
484 //Platform::DebugPrintf("DoDragDrop = %x\n", hr);
485 if (SUCCEEDED(hr)) {
486 if ((hr == DRAGDROP_S_DROP) && (dwEffect == DROPEFFECT_MOVE) && dropWentOutside) {
487 // Remove dragged out text
488 ClearSelection();
491 inDragDrop = ddNone;
492 SetDragPosition(SelectionPosition(invalidPosition));
495 // Avoid warnings everywhere for old style casts by concentrating them here
496 static WORD LoWord(DWORD l) {
497 return LOWORD(l);
500 static WORD HiWord(DWORD l) {
501 return HIWORD(l);
504 static int InputCodePage() {
505 HKL inputLocale = ::GetKeyboardLayout(0);
506 LANGID inputLang = LOWORD(inputLocale);
507 char sCodePage[10];
508 int res = ::GetLocaleInfoA(MAKELCID(inputLang, SORT_DEFAULT),
509 LOCALE_IDEFAULTANSICODEPAGE, sCodePage, sizeof(sCodePage));
510 if (!res)
511 return 0;
512 return atoi(sCodePage);
515 /** Map the key codes to their equivalent SCK_ form. */
516 static int KeyTranslate(int keyIn) {
517 //PLATFORM_ASSERT(!keyIn);
518 switch (keyIn) {
519 case VK_DOWN: return SCK_DOWN;
520 case VK_UP: return SCK_UP;
521 case VK_LEFT: return SCK_LEFT;
522 case VK_RIGHT: return SCK_RIGHT;
523 case VK_HOME: return SCK_HOME;
524 case VK_END: return SCK_END;
525 case VK_PRIOR: return SCK_PRIOR;
526 case VK_NEXT: return SCK_NEXT;
527 case VK_DELETE: return SCK_DELETE;
528 case VK_INSERT: return SCK_INSERT;
529 case VK_ESCAPE: return SCK_ESCAPE;
530 case VK_BACK: return SCK_BACK;
531 case VK_TAB: return SCK_TAB;
532 case VK_RETURN: return SCK_RETURN;
533 case VK_ADD: return SCK_ADD;
534 case VK_SUBTRACT: return SCK_SUBTRACT;
535 case VK_DIVIDE: return SCK_DIVIDE;
536 case VK_LWIN: return SCK_WIN;
537 case VK_RWIN: return SCK_RWIN;
538 case VK_APPS: return SCK_MENU;
539 case VK_OEM_2: return '/';
540 case VK_OEM_3: return '`';
541 case VK_OEM_4: return '[';
542 case VK_OEM_5: return '\\';
543 case VK_OEM_6: return ']';
544 default: return keyIn;
548 LRESULT ScintillaWin::WndPaint(uptr_t wParam) {
549 //ElapsedTime et;
551 // Redirect assertions to debug output and save current state
552 bool assertsPopup = Platform::ShowAssertionPopUps(false);
553 paintState = painting;
554 PAINTSTRUCT ps;
555 PAINTSTRUCT *pps;
557 bool IsOcxCtrl = (wParam != 0); // if wParam != 0, it contains
558 // a PAINSTRUCT* from the OCX
559 // Removed since this interferes with reporting other assertions as it occurs repeatedly
560 //PLATFORM_ASSERT(hRgnUpdate == NULL);
561 hRgnUpdate = ::CreateRectRgn(0, 0, 0, 0);
562 if (IsOcxCtrl) {
563 pps = reinterpret_cast<PAINTSTRUCT*>(wParam);
564 } else {
565 ::GetUpdateRgn(MainHWND(), hRgnUpdate, FALSE);
566 pps = &ps;
567 ::BeginPaint(MainHWND(), pps);
569 rcPaint = PRectangle(pps->rcPaint.left, pps->rcPaint.top, pps->rcPaint.right, pps->rcPaint.bottom);
570 PRectangle rcClient = GetClientRectangle();
571 paintingAllText = rcPaint.Contains(rcClient);
572 if (technology == SC_TECHNOLOGY_DEFAULT) {
573 AutoSurface surfaceWindow(pps->hdc, this);
574 if (surfaceWindow) {
575 Paint(surfaceWindow, rcPaint);
576 surfaceWindow->Release();
578 } else {
579 #if defined(USE_D2D)
580 EnsureRenderTarget();
581 AutoSurface surfaceWindow(pRenderTarget, this);
582 if (surfaceWindow) {
583 pRenderTarget->BeginDraw();
584 Paint(surfaceWindow, rcPaint);
585 surfaceWindow->Release();
586 HRESULT hr = pRenderTarget->EndDraw();
587 if (hr == D2DERR_RECREATE_TARGET) {
588 DropRenderTarget();
589 paintState = paintAbandoned;
592 #endif
594 if (hRgnUpdate) {
595 ::DeleteRgn(hRgnUpdate);
596 hRgnUpdate = 0;
599 if (!IsOcxCtrl)
600 ::EndPaint(MainHWND(), pps);
601 if (paintState == paintAbandoned) {
602 // Painting area was insufficient to cover new styling or brace highlight positions
603 if (IsOcxCtrl) {
604 FullPaintDC(pps->hdc);
605 } else {
606 FullPaint();
609 paintState = notPainting;
611 // Restore debug output state
612 Platform::ShowAssertionPopUps(assertsPopup);
614 //Platform::DebugPrintf("Paint took %g\n", et.Duration());
615 return 0l;
618 sptr_t ScintillaWin::HandleComposition(uptr_t wParam, sptr_t lParam) {
619 if (lParam & GCS_RESULTSTR) {
620 HIMC hIMC = ::ImmGetContext(MainHWND());
621 if (hIMC) {
622 const int maxLenInputIME = 200;
623 wchar_t wcs[maxLenInputIME];
624 LONG bytes = ::ImmGetCompositionStringW(hIMC,
625 GCS_RESULTSTR, wcs, (maxLenInputIME-1)*2);
626 int wides = bytes / 2;
627 if (IsUnicodeMode()) {
628 char utfval[maxLenInputIME * 3];
629 unsigned int len = UTF8Length(wcs, wides);
630 UTF8FromUTF16(wcs, wides, utfval, len);
631 utfval[len] = '\0';
632 AddCharUTF(utfval, len);
633 } else {
634 char dbcsval[maxLenInputIME * 2];
635 int size = ::WideCharToMultiByte(InputCodePage(),
636 0, wcs, wides, dbcsval, sizeof(dbcsval) - 1, 0, 0);
637 for (int i=0; i<size; i++) {
638 AddChar(dbcsval[i]);
641 // Set new position after converted
642 Point pos = PointMainCaret();
643 COMPOSITIONFORM CompForm;
644 CompForm.dwStyle = CFS_POINT;
645 CompForm.ptCurrentPos.x = pos.x;
646 CompForm.ptCurrentPos.y = pos.y;
647 ::ImmSetCompositionWindow(hIMC, &CompForm);
648 ::ImmReleaseContext(MainHWND(), hIMC);
650 return 0;
652 return ::DefWindowProc(MainHWND(), WM_IME_COMPOSITION, wParam, lParam);
655 // Translate message IDs from WM_* and EM_* to SCI_* so can partly emulate Windows Edit control
656 static unsigned int SciMessageFromEM(unsigned int iMessage) {
657 switch (iMessage) {
658 case EM_CANPASTE: return SCI_CANPASTE;
659 case EM_CANUNDO: return SCI_CANUNDO;
660 case EM_EMPTYUNDOBUFFER: return SCI_EMPTYUNDOBUFFER;
661 case EM_FINDTEXTEX: return SCI_FINDTEXT;
662 case EM_FORMATRANGE: return SCI_FORMATRANGE;
663 case EM_GETFIRSTVISIBLELINE: return SCI_GETFIRSTVISIBLELINE;
664 case EM_GETLINECOUNT: return SCI_GETLINECOUNT;
665 case EM_GETSELTEXT: return SCI_GETSELTEXT;
666 case EM_GETTEXTRANGE: return SCI_GETTEXTRANGE;
667 case EM_HIDESELECTION: return SCI_HIDESELECTION;
668 case EM_LINEINDEX: return SCI_POSITIONFROMLINE;
669 case EM_LINESCROLL: return SCI_LINESCROLL;
670 case EM_REPLACESEL: return SCI_REPLACESEL;
671 case EM_SCROLLCARET: return SCI_SCROLLCARET;
672 case EM_SETREADONLY: return SCI_SETREADONLY;
673 case WM_CLEAR: return SCI_CLEAR;
674 case WM_COPY: return SCI_COPY;
675 case WM_CUT: return SCI_CUT;
676 case WM_GETTEXT: return SCI_GETTEXT;
677 case WM_SETTEXT: return SCI_SETTEXT;
678 case WM_GETTEXTLENGTH: return SCI_GETTEXTLENGTH;
679 case WM_PASTE: return SCI_PASTE;
680 case WM_UNDO: return SCI_UNDO;
682 return iMessage;
685 UINT CodePageFromCharSet(DWORD characterSet, UINT documentCodePage) {
686 if (documentCodePage == SC_CP_UTF8) {
687 return SC_CP_UTF8;
689 switch (characterSet) {
690 case SC_CHARSET_ANSI: return 1252;
691 case SC_CHARSET_DEFAULT: return documentCodePage;
692 case SC_CHARSET_BALTIC: return 1257;
693 case SC_CHARSET_CHINESEBIG5: return 950;
694 case SC_CHARSET_EASTEUROPE: return 1250;
695 case SC_CHARSET_GB2312: return 936;
696 case SC_CHARSET_GREEK: return 1253;
697 case SC_CHARSET_HANGUL: return 949;
698 case SC_CHARSET_MAC: return 10000;
699 case SC_CHARSET_OEM: return 437;
700 case SC_CHARSET_RUSSIAN: return 1251;
701 case SC_CHARSET_SHIFTJIS: return 932;
702 case SC_CHARSET_TURKISH: return 1254;
703 case SC_CHARSET_JOHAB: return 1361;
704 case SC_CHARSET_HEBREW: return 1255;
705 case SC_CHARSET_ARABIC: return 1256;
706 case SC_CHARSET_VIETNAMESE: return 1258;
707 case SC_CHARSET_THAI: return 874;
708 case SC_CHARSET_8859_15: return 28605;
709 // Not supported
710 case SC_CHARSET_CYRILLIC: return documentCodePage;
711 case SC_CHARSET_SYMBOL: return documentCodePage;
713 return documentCodePage;
716 UINT ScintillaWin::CodePageOfDocument() {
717 return CodePageFromCharSet(vs.styles[STYLE_DEFAULT].characterSet, pdoc->dbcsCodePage);
720 sptr_t ScintillaWin::WndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
721 try {
722 //Platform::DebugPrintf("S M:%x WP:%x L:%x\n", iMessage, wParam, lParam);
723 iMessage = SciMessageFromEM(iMessage);
724 switch (iMessage) {
726 case WM_CREATE:
727 ctrlID = ::GetDlgCtrlID(reinterpret_cast<HWND>(wMain.GetID()));
728 // Get Intellimouse scroll line parameters
729 GetIntelliMouseParameters();
730 ::RegisterDragDrop(MainHWND(), reinterpret_cast<IDropTarget *>(&dt));
731 break;
733 case WM_COMMAND:
734 Command(LoWord(wParam));
735 break;
737 case WM_PAINT:
738 return WndPaint(wParam);
740 case WM_PRINTCLIENT: {
741 HDC hdc = reinterpret_cast<HDC>(wParam);
742 if (!IsCompatibleDC(hdc)) {
743 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
745 FullPaintDC(hdc);
747 break;
749 case WM_VSCROLL:
750 ScrollMessage(wParam);
751 break;
753 case WM_HSCROLL:
754 HorizontalScrollMessage(wParam);
755 break;
757 case WM_SIZE: {
758 #if defined(USE_D2D)
759 if (paintState == notPainting) {
760 DropRenderTarget();
761 } else {
762 renderTargetValid = false;
764 #endif
765 //Platform::DebugPrintf("Scintilla WM_SIZE %d %d\n", LoWord(lParam), HiWord(lParam));
766 ChangeSize();
768 break;
770 case WM_MOUSEWHEEL:
771 // if autocomplete list active then send mousewheel message to it
772 if (ac.Active()) {
773 HWND hWnd = reinterpret_cast<HWND>(ac.lb->GetID());
774 ::SendMessage(hWnd, iMessage, wParam, lParam);
775 break;
778 // Don't handle datazoom.
779 // (A good idea for datazoom would be to "fold" or "unfold" details.
780 // i.e. if datazoomed out only class structures are visible, when datazooming in the control
781 // structures appear, then eventually the individual statements...)
782 if (wParam & MK_SHIFT) {
783 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
786 // Either SCROLL or ZOOM. We handle the wheel steppings calculation
787 wheelDelta -= static_cast<short>(HiWord(wParam));
788 if (abs(wheelDelta) >= WHEEL_DELTA && linesPerScroll > 0) {
789 int linesToScroll = linesPerScroll;
790 if (linesPerScroll == WHEEL_PAGESCROLL)
791 linesToScroll = LinesOnScreen() - 1;
792 if (linesToScroll == 0) {
793 linesToScroll = 1;
795 linesToScroll *= (wheelDelta / WHEEL_DELTA);
796 if (wheelDelta >= 0)
797 wheelDelta = wheelDelta % WHEEL_DELTA;
798 else
799 wheelDelta = - (-wheelDelta % WHEEL_DELTA);
801 if (wParam & MK_CONTROL) {
802 // Zoom! We play with the font sizes in the styles.
803 // Number of steps/line is ignored, we just care if sizing up or down
804 if (linesToScroll < 0) {
805 KeyCommand(SCI_ZOOMIN);
806 } else {
807 KeyCommand(SCI_ZOOMOUT);
809 } else {
810 // Scroll
811 ScrollTo(topLine + linesToScroll);
814 return 0;
816 case WM_TIMER:
817 if (wParam == standardTimerID && timer.ticking) {
818 Tick();
819 } else if (wParam == idleTimerID && idler.state) {
820 SendMessage(MainHWND(), SC_WIN_IDLE, 0, 1);
821 } else {
822 return 1;
824 break;
826 case SC_WIN_IDLE:
827 // wParam=dwTickCountInitial, or 0 to initialize. lParam=bSkipUserInputTest
828 if (idler.state) {
829 if (lParam || (WAIT_TIMEOUT == MsgWaitForMultipleObjects(0, 0, 0, 0, QS_INPUT|QS_HOTKEY))) {
830 if (Idle()) {
831 // User input was given priority above, but all events do get a turn. Other
832 // messages, notifications, etc. will get interleaved with the idle messages.
834 // However, some things like WM_PAINT are a lower priority, and will not fire
835 // when there's a message posted. So, several times a second, we stop and let
836 // the low priority events have a turn (after which the timer will fire again).
838 DWORD dwCurrent = GetTickCount();
839 DWORD dwStart = wParam ? wParam : dwCurrent;
840 const DWORD maxWorkTime = 50;
842 if (dwCurrent >= dwStart && dwCurrent > maxWorkTime && dwCurrent - maxWorkTime < dwStart)
843 PostMessage(MainHWND(), SC_WIN_IDLE, dwStart, 0);
844 } else {
845 SetIdle(false);
849 break;
851 case WM_GETMINMAXINFO:
852 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
854 case WM_LBUTTONDOWN: {
855 // For IME, set the composition string as the result string.
856 HIMC hIMC = ::ImmGetContext(MainHWND());
857 ::ImmNotifyIME(hIMC, NI_COMPOSITIONSTR, CPS_COMPLETE, 0);
858 ::ImmReleaseContext(MainHWND(), hIMC);
860 //Platform::DebugPrintf("Buttdown %d %x %x %x %x %x\n",iMessage, wParam, lParam,
861 // Platform::IsKeyDown(VK_SHIFT),
862 // Platform::IsKeyDown(VK_CONTROL),
863 // Platform::IsKeyDown(VK_MENU));
864 ::SetFocus(MainHWND());
865 ButtonDown(Point::FromLong(lParam), ::GetMessageTime(),
866 (wParam & MK_SHIFT) != 0,
867 (wParam & MK_CONTROL) != 0,
868 Platform::IsKeyDown(VK_MENU));
870 break;
872 case WM_MOUSEMOVE:
873 SetTrackMouseLeaveEvent(true);
874 ButtonMoveWithModifiers(Point::FromLong(lParam),
875 ((wParam & MK_SHIFT) != 0 ? SCI_SHIFT : 0) |
876 ((wParam & MK_CONTROL) != 0 ? SCI_CTRL : 0) |
877 (Platform::IsKeyDown(VK_MENU) ? SCI_ALT : 0));
878 break;
880 case WM_MOUSELEAVE:
881 SetTrackMouseLeaveEvent(false);
882 MouseLeave();
883 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
885 case WM_LBUTTONUP:
886 ButtonUp(Point::FromLong(lParam),
887 ::GetMessageTime(),
888 (wParam & MK_CONTROL) != 0);
889 break;
891 case WM_RBUTTONDOWN:
892 ::SetFocus(MainHWND());
893 if (!PointInSelection(Point::FromLong(lParam))) {
894 CancelModes();
895 SetEmptySelection(PositionFromLocation(Point::FromLong(lParam)));
897 break;
899 case WM_SETCURSOR:
900 if (LoWord(lParam) == HTCLIENT) {
901 if (inDragDrop == ddDragging) {
902 DisplayCursor(Window::cursorUp);
903 } else {
904 // Display regular (drag) cursor over selection
905 POINT pt;
906 if (0 != ::GetCursorPos(&pt)) {
907 ::ScreenToClient(MainHWND(), &pt);
908 if (PointInSelMargin(Point(pt.x, pt.y))) {
909 DisplayCursor(GetMarginCursor(Point(pt.x, pt.y)));
910 } else if (PointInSelection(Point(pt.x, pt.y)) && !SelectionEmpty()) {
911 DisplayCursor(Window::cursorArrow);
912 } else if (PointIsHotspot(Point(pt.x, pt.y))) {
913 DisplayCursor(Window::cursorHand);
914 } else {
915 DisplayCursor(Window::cursorText);
919 return TRUE;
920 } else {
921 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
924 case WM_CHAR:
925 if (((wParam >= 128) || !iscntrl(wParam)) || !lastKeyDownConsumed) {
926 if (::IsWindowUnicode(MainHWND()) || keysAlwaysUnicode) {
927 wchar_t wcs[2] = {static_cast<wchar_t>(wParam), 0};
928 if (IsUnicodeMode()) {
929 // For a wide character version of the window:
930 char utfval[4];
931 unsigned int len = UTF8Length(wcs, 1);
932 UTF8FromUTF16(wcs, 1, utfval, len);
933 AddCharUTF(utfval, len);
934 } else {
935 UINT cpDest = CodePageOfDocument();
936 char inBufferCP[20];
937 int size = ::WideCharToMultiByte(cpDest,
938 0, wcs, 1, inBufferCP, sizeof(inBufferCP) - 1, 0, 0);
939 inBufferCP[size] = '\0';
940 AddCharUTF(inBufferCP, size);
942 } else {
943 if (IsUnicodeMode()) {
944 AddCharBytes('\0', LOBYTE(wParam));
945 } else {
946 AddChar(LOBYTE(wParam));
950 return 0;
952 case WM_UNICHAR:
953 if (wParam == UNICODE_NOCHAR) {
954 return IsUnicodeMode() ? 1 : 0;
955 } else if (lastKeyDownConsumed) {
956 return 1;
957 } else {
958 if (IsUnicodeMode()) {
959 char utfval[4];
960 wchar_t wcs[2] = {static_cast<wchar_t>(wParam), 0};
961 unsigned int len = UTF8Length(wcs, 1);
962 UTF8FromUTF16(wcs, 1, utfval, len);
963 AddCharUTF(utfval, len);
964 return 1;
965 } else {
966 return 0;
970 case WM_SYSKEYDOWN:
971 case WM_KEYDOWN: {
972 //Platform::DebugPrintf("S keydown %d %x %x %x %x\n",iMessage, wParam, lParam, ::IsKeyDown(VK_SHIFT), ::IsKeyDown(VK_CONTROL));
973 lastKeyDownConsumed = false;
974 int ret = KeyDown(KeyTranslate(wParam),
975 Platform::IsKeyDown(VK_SHIFT),
976 Platform::IsKeyDown(VK_CONTROL),
977 Platform::IsKeyDown(VK_MENU),
978 &lastKeyDownConsumed);
979 if (!ret && !lastKeyDownConsumed) {
980 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
982 break;
985 case WM_IME_KEYDOWN:
986 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
988 case WM_KEYUP:
989 //Platform::DebugPrintf("S keyup %d %x %x\n",iMessage, wParam, lParam);
990 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
992 case WM_SETTINGCHANGE:
993 //Platform::DebugPrintf("Setting Changed\n");
994 InvalidateStyleData();
995 // Get Intellimouse scroll line parameters
996 GetIntelliMouseParameters();
997 break;
999 case WM_GETDLGCODE:
1000 return DLGC_HASSETSEL | DLGC_WANTALLKEYS;
1002 case WM_KILLFOCUS: {
1003 HWND wOther = reinterpret_cast<HWND>(wParam);
1004 HWND wThis = MainHWND();
1005 HWND wCT = reinterpret_cast<HWND>(ct.wCallTip.GetID());
1006 if (!wParam ||
1007 !(::IsChild(wThis, wOther) || (wOther == wCT))) {
1008 SetFocusState(false);
1009 DestroySystemCaret();
1012 break;
1014 case WM_SETFOCUS:
1015 SetFocusState(true);
1016 DestroySystemCaret();
1017 CreateSystemCaret();
1018 break;
1020 case WM_SYSCOLORCHANGE:
1021 //Platform::DebugPrintf("Setting Changed\n");
1022 InvalidateStyleData();
1023 break;
1025 case WM_IME_STARTCOMPOSITION: // dbcs
1026 ImeStartComposition();
1027 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1029 case WM_IME_ENDCOMPOSITION: // dbcs
1030 ImeEndComposition();
1031 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1033 case WM_IME_COMPOSITION:
1034 return HandleComposition(wParam, lParam);
1036 case WM_IME_CHAR: {
1037 AddCharBytes(HIBYTE(wParam), LOBYTE(wParam));
1038 return 0;
1041 case WM_CONTEXTMENU:
1042 if (displayPopupMenu) {
1043 Point pt = Point::FromLong(lParam);
1044 if ((pt.x == -1) && (pt.y == -1)) {
1045 // Caused by keyboard so display menu near caret
1046 pt = PointMainCaret();
1047 POINT spt = {static_cast<int>(pt.x), static_cast<int>(pt.y)};
1048 ::ClientToScreen(MainHWND(), &spt);
1049 pt = Point(spt.x, spt.y);
1051 ContextMenu(pt);
1052 return 0;
1054 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1056 case WM_INPUTLANGCHANGE:
1057 //::SetThreadLocale(LOWORD(lParam));
1058 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1060 case WM_INPUTLANGCHANGEREQUEST:
1061 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1063 case WM_ERASEBKGND:
1064 return 1; // Avoid any background erasure as whole window painted.
1066 case WM_CAPTURECHANGED:
1067 capturedMouse = false;
1068 return 0;
1070 // These are not handled in Scintilla and its faster to dispatch them here.
1071 // Also moves time out to here so profile doesn't count lots of empty message calls.
1073 case WM_MOVE:
1074 case WM_MOUSEACTIVATE:
1075 case WM_NCHITTEST:
1076 case WM_NCCALCSIZE:
1077 case WM_NCPAINT:
1078 case WM_NCMOUSEMOVE:
1079 case WM_NCLBUTTONDOWN:
1080 case WM_IME_SETCONTEXT:
1081 case WM_IME_NOTIFY:
1082 case WM_SYSCOMMAND:
1083 case WM_WINDOWPOSCHANGING:
1084 case WM_WINDOWPOSCHANGED:
1085 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1087 case EM_LINEFROMCHAR:
1088 if (static_cast<int>(wParam) < 0) {
1089 wParam = SelectionStart().Position();
1091 return pdoc->LineFromPosition(wParam);
1093 case EM_EXLINEFROMCHAR:
1094 return pdoc->LineFromPosition(lParam);
1096 case EM_GETSEL:
1097 if (wParam) {
1098 *reinterpret_cast<int *>(wParam) = SelectionStart().Position();
1100 if (lParam) {
1101 *reinterpret_cast<int *>(lParam) = SelectionEnd().Position();
1103 return MAKELONG(SelectionStart().Position(), SelectionEnd().Position());
1105 case EM_EXGETSEL: {
1106 if (lParam == 0) {
1107 return 0;
1109 Sci_CharacterRange *pCR = reinterpret_cast<Sci_CharacterRange *>(lParam);
1110 pCR->cpMin = SelectionStart().Position();
1111 pCR->cpMax = SelectionEnd().Position();
1113 break;
1115 case EM_SETSEL: {
1116 int nStart = static_cast<int>(wParam);
1117 int nEnd = static_cast<int>(lParam);
1118 if (nStart == 0 && nEnd == -1) {
1119 nEnd = pdoc->Length();
1121 if (nStart == -1) {
1122 nStart = nEnd; // Remove selection
1124 if (nStart > nEnd) {
1125 SetSelection(nEnd, nStart);
1126 } else {
1127 SetSelection(nStart, nEnd);
1129 EnsureCaretVisible();
1131 break;
1133 case EM_EXSETSEL: {
1134 if (lParam == 0) {
1135 return 0;
1137 Sci_CharacterRange *pCR = reinterpret_cast<Sci_CharacterRange *>(lParam);
1138 sel.selType = Selection::selStream;
1139 if (pCR->cpMin == 0 && pCR->cpMax == -1) {
1140 SetSelection(pCR->cpMin, pdoc->Length());
1141 } else {
1142 SetSelection(pCR->cpMin, pCR->cpMax);
1144 EnsureCaretVisible();
1145 return pdoc->LineFromPosition(SelectionStart().Position());
1148 case SCI_GETDIRECTFUNCTION:
1149 return reinterpret_cast<sptr_t>(DirectFunction);
1151 case SCI_GETDIRECTPOINTER:
1152 return reinterpret_cast<sptr_t>(this);
1154 case SCI_GRABFOCUS:
1155 ::SetFocus(MainHWND());
1156 break;
1158 case SCI_SETKEYSUNICODE:
1159 keysAlwaysUnicode = wParam != 0;
1160 break;
1162 case SCI_GETKEYSUNICODE:
1163 return keysAlwaysUnicode;
1165 case SCI_SETTECHNOLOGY:
1166 if ((wParam == SC_TECHNOLOGY_DEFAULT) || (wParam == SC_TECHNOLOGY_DIRECTWRITE)) {
1167 if (technology != static_cast<int>(wParam)) {
1168 if (static_cast<int>(wParam) == SC_TECHNOLOGY_DIRECTWRITE) {
1169 #if defined(USE_D2D)
1170 if (!LoadD2D())
1171 // Failed to load Direct2D or DirectWrite so no effect
1172 return 0;
1173 #else
1174 return 0;
1175 #endif
1177 technology = wParam;
1178 // Invalidate all cached information including layout.
1179 DropGraphics(true);
1180 InvalidateStyleRedraw();
1183 break;
1185 #ifdef SCI_LEXER
1186 case SCI_LOADLEXERLIBRARY:
1187 LexerManager::GetInstance()->Load(reinterpret_cast<const char *>(lParam));
1188 break;
1189 #endif
1191 default:
1192 return ScintillaBase::WndProc(iMessage, wParam, lParam);
1194 } catch (std::bad_alloc &) {
1195 errorStatus = SC_STATUS_BADALLOC;
1196 } catch (...) {
1197 errorStatus = SC_STATUS_FAILURE;
1199 return 0l;
1202 bool ScintillaWin::ValidCodePage(int codePage) const {
1203 return codePage == 0 || codePage == SC_CP_UTF8 ||
1204 codePage == 932 || codePage == 936 || codePage == 949 ||
1205 codePage == 950 || codePage == 1361;
1208 sptr_t ScintillaWin::DefWndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
1209 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1212 void ScintillaWin::SetTicking(bool on) {
1213 if (timer.ticking != on) {
1214 timer.ticking = on;
1215 if (timer.ticking) {
1216 timer.tickerID = ::SetTimer(MainHWND(), standardTimerID, timer.tickSize, NULL)
1217 ? reinterpret_cast<TickerID>(standardTimerID) : 0;
1218 } else {
1219 ::KillTimer(MainHWND(), reinterpret_cast<uptr_t>(timer.tickerID));
1220 timer.tickerID = 0;
1223 timer.ticksToWait = caret.period;
1226 bool ScintillaWin::SetIdle(bool on) {
1227 // On Win32 the Idler is implemented as a Timer on the Scintilla window. This
1228 // takes advantage of the fact that WM_TIMER messages are very low priority,
1229 // and are only posted when the message queue is empty, i.e. during idle time.
1230 if (idler.state != on) {
1231 if (on) {
1232 idler.idlerID = ::SetTimer(MainHWND(), idleTimerID, 10, NULL)
1233 ? reinterpret_cast<IdlerID>(idleTimerID) : 0;
1234 } else {
1235 ::KillTimer(MainHWND(), reinterpret_cast<uptr_t>(idler.idlerID));
1236 idler.idlerID = 0;
1238 idler.state = idler.idlerID != 0;
1240 return idler.state;
1243 void ScintillaWin::SetMouseCapture(bool on) {
1244 if (mouseDownCaptures) {
1245 if (on) {
1246 ::SetCapture(MainHWND());
1247 } else {
1248 ::ReleaseCapture();
1251 capturedMouse = on;
1254 bool ScintillaWin::HaveMouseCapture() {
1255 // Cannot just see if GetCapture is this window as the scroll bar also sets capture for the window
1256 return capturedMouse;
1257 //return capturedMouse && (::GetCapture() == MainHWND());
1260 void ScintillaWin::SetTrackMouseLeaveEvent(bool on) {
1261 if (on && TrackMouseEventFn && !trackedMouseLeave) {
1262 TRACKMOUSEEVENT tme;
1263 tme.cbSize = sizeof(tme);
1264 tme.dwFlags = TME_LEAVE;
1265 tme.hwndTrack = MainHWND();
1266 TrackMouseEventFn(&tme);
1268 trackedMouseLeave = on;
1271 bool ScintillaWin::PaintContains(PRectangle rc) {
1272 bool contains = true;
1273 if ((paintState == painting) && (!rc.Empty())) {
1274 if (!rcPaint.Contains(rc)) {
1275 contains = false;
1276 } else {
1277 // In bounding rectangle so check more accurately using region
1278 HRGN hRgnRange = ::CreateRectRgn(rc.left, rc.top, rc.right, rc.bottom);
1279 if (hRgnRange) {
1280 HRGN hRgnDest = ::CreateRectRgn(0, 0, 0, 0);
1281 if (hRgnDest) {
1282 int combination = ::CombineRgn(hRgnDest, hRgnRange, hRgnUpdate, RGN_DIFF);
1283 if (combination != NULLREGION) {
1284 contains = false;
1286 ::DeleteRgn(hRgnDest);
1288 ::DeleteRgn(hRgnRange);
1292 return contains;
1295 void ScintillaWin::ScrollText(int /* linesToMove */) {
1296 //Platform::DebugPrintf("ScintillaWin::ScrollText %d\n", linesToMove);
1297 //::ScrollWindow(MainHWND(), 0,
1298 // vs.lineHeight * linesToMove, 0, 0);
1299 //::UpdateWindow(MainHWND());
1300 Redraw();
1301 UpdateSystemCaret();
1304 void ScintillaWin::UpdateSystemCaret() {
1305 if (hasFocus) {
1306 if (HasCaretSizeChanged()) {
1307 DestroySystemCaret();
1308 CreateSystemCaret();
1310 Point pos = PointMainCaret();
1311 ::SetCaretPos(pos.x, pos.y);
1315 int ScintillaWin::SetScrollInfo(int nBar, LPCSCROLLINFO lpsi, BOOL bRedraw) {
1316 return ::SetScrollInfo(MainHWND(), nBar, lpsi, bRedraw);
1319 bool ScintillaWin::GetScrollInfo(int nBar, LPSCROLLINFO lpsi) {
1320 return ::GetScrollInfo(MainHWND(), nBar, lpsi) ? true : false;
1323 // Change the scroll position but avoid repaint if changing to same value
1324 void ScintillaWin::ChangeScrollPos(int barType, int pos) {
1325 SCROLLINFO sci = {
1326 sizeof(sci), 0, 0, 0, 0, 0, 0
1328 sci.fMask = SIF_POS;
1329 GetScrollInfo(barType, &sci);
1330 if (sci.nPos != pos) {
1331 DwellEnd(true);
1332 sci.nPos = pos;
1333 SetScrollInfo(barType, &sci, TRUE);
1337 void ScintillaWin::SetVerticalScrollPos() {
1338 ChangeScrollPos(SB_VERT, topLine);
1341 void ScintillaWin::SetHorizontalScrollPos() {
1342 ChangeScrollPos(SB_HORZ, xOffset);
1345 bool ScintillaWin::ModifyScrollBars(int nMax, int nPage) {
1346 bool modified = false;
1347 SCROLLINFO sci = {
1348 sizeof(sci), 0, 0, 0, 0, 0, 0
1350 sci.fMask = SIF_PAGE | SIF_RANGE;
1351 GetScrollInfo(SB_VERT, &sci);
1352 int vertEndPreferred = nMax;
1353 if (!verticalScrollBarVisible)
1354 nPage = vertEndPreferred + 1;
1355 if ((sci.nMin != 0) ||
1356 (sci.nMax != vertEndPreferred) ||
1357 (sci.nPage != static_cast<unsigned int>(nPage)) ||
1358 (sci.nPos != 0)) {
1359 sci.fMask = SIF_PAGE | SIF_RANGE;
1360 sci.nMin = 0;
1361 sci.nMax = vertEndPreferred;
1362 sci.nPage = nPage;
1363 sci.nPos = 0;
1364 sci.nTrackPos = 1;
1365 SetScrollInfo(SB_VERT, &sci, TRUE);
1366 modified = true;
1369 PRectangle rcText = GetTextRectangle();
1370 int horizEndPreferred = scrollWidth;
1371 if (horizEndPreferred < 0)
1372 horizEndPreferred = 0;
1373 unsigned int pageWidth = rcText.Width();
1374 if (!horizontalScrollBarVisible || Wrapping())
1375 pageWidth = horizEndPreferred + 1;
1376 sci.fMask = SIF_PAGE | SIF_RANGE;
1377 GetScrollInfo(SB_HORZ, &sci);
1378 if ((sci.nMin != 0) ||
1379 (sci.nMax != horizEndPreferred) ||
1380 (sci.nPage != pageWidth) ||
1381 (sci.nPos != 0)) {
1382 sci.fMask = SIF_PAGE | SIF_RANGE;
1383 sci.nMin = 0;
1384 sci.nMax = horizEndPreferred;
1385 sci.nPage = pageWidth;
1386 sci.nPos = 0;
1387 sci.nTrackPos = 1;
1388 SetScrollInfo(SB_HORZ, &sci, TRUE);
1389 modified = true;
1390 if (scrollWidth < static_cast<int>(pageWidth)) {
1391 HorizontalScrollTo(0);
1394 return modified;
1397 void ScintillaWin::NotifyChange() {
1398 ::SendMessage(::GetParent(MainHWND()), WM_COMMAND,
1399 MAKELONG(GetCtrlID(), SCEN_CHANGE),
1400 reinterpret_cast<LPARAM>(MainHWND()));
1403 void ScintillaWin::NotifyFocus(bool focus) {
1404 ::SendMessage(::GetParent(MainHWND()), WM_COMMAND,
1405 MAKELONG(GetCtrlID(), focus ? SCEN_SETFOCUS : SCEN_KILLFOCUS),
1406 reinterpret_cast<LPARAM>(MainHWND()));
1407 Editor::NotifyFocus(focus);
1410 void ScintillaWin::SetCtrlID(int identifier) {
1411 ::SetWindowID(reinterpret_cast<HWND>(wMain.GetID()), identifier);
1414 int ScintillaWin::GetCtrlID() {
1415 return ::GetDlgCtrlID(reinterpret_cast<HWND>(wMain.GetID()));
1418 void ScintillaWin::NotifyParent(SCNotification scn) {
1419 scn.nmhdr.hwndFrom = MainHWND();
1420 scn.nmhdr.idFrom = GetCtrlID();
1421 ::SendMessage(::GetParent(MainHWND()), WM_NOTIFY,
1422 GetCtrlID(), reinterpret_cast<LPARAM>(&scn));
1425 void ScintillaWin::NotifyDoubleClick(Point pt, int modifiers) {
1426 //Platform::DebugPrintf("ScintillaWin Double click 0\n");
1427 ScintillaBase::NotifyDoubleClick(pt, modifiers);
1428 // Send myself a WM_LBUTTONDBLCLK, so the container can handle it too.
1429 ::SendMessage(MainHWND(),
1430 WM_LBUTTONDBLCLK,
1431 (modifiers & SCI_SHIFT) ? MK_SHIFT : 0,
1432 MAKELPARAM(pt.x, pt.y));
1435 class CaseFolderDBCS : public CaseFolderTable {
1436 // Allocate the expandable storage here so that it does not need to be reallocated
1437 // for each call to Fold.
1438 std::vector<wchar_t> utf16Mixed;
1439 std::vector<wchar_t> utf16Folded;
1440 UINT cp;
1441 public:
1442 explicit CaseFolderDBCS(UINT cp_) : cp(cp_) {
1443 StandardASCII();
1445 virtual size_t Fold(char *folded, size_t sizeFolded, const char *mixed, size_t lenMixed) {
1446 if ((lenMixed == 1) && (sizeFolded > 0)) {
1447 folded[0] = mapping[static_cast<unsigned char>(mixed[0])];
1448 return 1;
1449 } else {
1450 if (lenMixed > utf16Mixed.size()) {
1451 utf16Mixed.resize(lenMixed + 8);
1453 size_t nUtf16Mixed = ::MultiByteToWideChar(cp, 0, mixed,
1454 static_cast<int>(lenMixed),
1455 &utf16Mixed[0],
1456 static_cast<int>(utf16Mixed.size()));
1458 if (nUtf16Mixed == 0) {
1459 // Failed to convert -> bad input
1460 folded[0] = '\0';
1461 return 1;
1464 unsigned int lenFlat = 0;
1465 for (size_t mixIndex=0; mixIndex < nUtf16Mixed; mixIndex++) {
1466 if ((lenFlat + 20) > utf16Folded.size())
1467 utf16Folded.resize(lenFlat + 60);
1468 const char *foldedUTF8 = CaseConvert(utf16Mixed[mixIndex], CaseConversionFold);
1469 if (foldedUTF8) {
1470 // Maximum length of a case conversion is 6 bytes, 3 characters
1471 wchar_t wFolded[20];
1472 unsigned int charsConverted = UTF16FromUTF8(foldedUTF8,
1473 static_cast<unsigned int>(strlen(foldedUTF8)),
1474 wFolded, ELEMENTS(wFolded));
1475 for (size_t j=0; j<charsConverted; j++)
1476 utf16Folded[lenFlat++] = wFolded[j];
1477 } else {
1478 utf16Folded[lenFlat++] = utf16Mixed[mixIndex];
1482 size_t lenOut = ::WideCharToMultiByte(cp, 0,
1483 &utf16Folded[0], lenFlat,
1484 NULL, 0, NULL, 0);
1486 if (lenOut < sizeFolded) {
1487 ::WideCharToMultiByte(cp, 0,
1488 &utf16Folded[0], lenFlat,
1489 folded, static_cast<int>(lenOut), NULL, 0);
1490 return lenOut;
1491 } else {
1492 return 0;
1498 CaseFolder *ScintillaWin::CaseFolderForEncoding() {
1499 UINT cpDest = CodePageOfDocument();
1500 if (cpDest == SC_CP_UTF8) {
1501 return new CaseFolderUnicode();
1502 } else {
1503 if (pdoc->dbcsCodePage == 0) {
1504 CaseFolderTable *pcf = new CaseFolderTable();
1505 pcf->StandardASCII();
1506 // Only for single byte encodings
1507 UINT cpDoc = CodePageOfDocument();
1508 for (int i=0x80; i<0x100; i++) {
1509 char sCharacter[2] = "A";
1510 sCharacter[0] = static_cast<char>(i);
1511 wchar_t wCharacter[20];
1512 unsigned int lengthUTF16 = ::MultiByteToWideChar(cpDoc, 0, sCharacter, 1,
1513 wCharacter, ELEMENTS(wCharacter));
1514 if (lengthUTF16 == 1) {
1515 const char *caseFolded = CaseConvert(wCharacter[0], CaseConversionFold);
1516 if (caseFolded) {
1517 wchar_t wLower[20];
1518 unsigned int charsConverted = UTF16FromUTF8(caseFolded,
1519 static_cast<unsigned int>(strlen(caseFolded)),
1520 wLower, ELEMENTS(wLower));
1521 if (charsConverted == 1) {
1522 char sCharacterLowered[20];
1523 unsigned int lengthConverted = ::WideCharToMultiByte(cpDoc, 0,
1524 wLower, charsConverted,
1525 sCharacterLowered, ELEMENTS(sCharacterLowered), NULL, 0);
1526 if ((lengthConverted == 1) && (sCharacter[0] != sCharacterLowered[0])) {
1527 pcf->SetTranslation(sCharacter[0], sCharacterLowered[0]);
1533 return pcf;
1534 } else {
1535 return new CaseFolderDBCS(cpDest);
1540 std::string ScintillaWin::CaseMapString(const std::string &s, int caseMapping) {
1541 if ((s.size() == 0) || (caseMapping == cmSame))
1542 return s;
1544 UINT cpDoc = CodePageOfDocument();
1545 if (cpDoc == SC_CP_UTF8) {
1546 std::string retMapped(s.length() * maxExpansionCaseConversion, 0);
1547 size_t lenMapped = CaseConvertString(&retMapped[0], retMapped.length(), s.c_str(), s.length(),
1548 (caseMapping == cmUpper) ? CaseConversionUpper : CaseConversionLower);
1549 retMapped.resize(lenMapped);
1550 return retMapped;
1553 unsigned int lengthUTF16 = ::MultiByteToWideChar(cpDoc, 0, s.c_str(),
1554 static_cast<int>(s.size()), NULL, 0);
1555 if (lengthUTF16 == 0) // Failed to convert
1556 return s;
1558 DWORD mapFlags = LCMAP_LINGUISTIC_CASING |
1559 ((caseMapping == cmUpper) ? LCMAP_UPPERCASE : LCMAP_LOWERCASE);
1561 // Change text to UTF-16
1562 std::vector<wchar_t> vwcText(lengthUTF16);
1563 ::MultiByteToWideChar(cpDoc, 0, s.c_str(), static_cast<int>(s.size()), &vwcText[0], lengthUTF16);
1565 // Change case
1566 int charsConverted = ::LCMapStringW(LOCALE_SYSTEM_DEFAULT, mapFlags,
1567 &vwcText[0], lengthUTF16, NULL, 0);
1568 std::vector<wchar_t> vwcConverted(charsConverted);
1569 ::LCMapStringW(LOCALE_SYSTEM_DEFAULT, mapFlags,
1570 &vwcText[0], lengthUTF16, &vwcConverted[0], charsConverted);
1572 // Change back to document encoding
1573 unsigned int lengthConverted = ::WideCharToMultiByte(cpDoc, 0,
1574 &vwcConverted[0], static_cast<int>(vwcConverted.size()),
1575 NULL, 0, NULL, 0);
1576 std::vector<char> vcConverted(lengthConverted);
1577 ::WideCharToMultiByte(cpDoc, 0,
1578 &vwcConverted[0], static_cast<int>(vwcConverted.size()),
1579 &vcConverted[0], static_cast<int>(vcConverted.size()), NULL, 0);
1581 return std::string(&vcConverted[0], vcConverted.size());
1584 void ScintillaWin::Copy() {
1585 //Platform::DebugPrintf("Copy\n");
1586 if (!sel.Empty()) {
1587 SelectionText selectedText;
1588 CopySelectionRange(&selectedText);
1589 CopyToClipboard(selectedText);
1593 void ScintillaWin::CopyAllowLine() {
1594 SelectionText selectedText;
1595 CopySelectionRange(&selectedText, true);
1596 CopyToClipboard(selectedText);
1599 bool ScintillaWin::CanPaste() {
1600 if (!Editor::CanPaste())
1601 return false;
1602 if (::IsClipboardFormatAvailable(CF_TEXT))
1603 return true;
1604 if (IsUnicodeMode())
1605 return ::IsClipboardFormatAvailable(CF_UNICODETEXT) != 0;
1606 return false;
1609 class GlobalMemory {
1610 HGLOBAL hand;
1611 public:
1612 void *ptr;
1613 GlobalMemory() : hand(0), ptr(0) {
1615 explicit GlobalMemory(HGLOBAL hand_) : hand(hand_), ptr(0) {
1616 if (hand) {
1617 ptr = ::GlobalLock(hand);
1620 ~GlobalMemory() {
1621 PLATFORM_ASSERT(!ptr);
1623 void Allocate(size_t bytes) {
1624 hand = ::GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT, bytes);
1625 if (hand) {
1626 ptr = ::GlobalLock(hand);
1629 HGLOBAL Unlock() {
1630 PLATFORM_ASSERT(ptr);
1631 HGLOBAL handCopy = hand;
1632 ::GlobalUnlock(hand);
1633 ptr = 0;
1634 hand = 0;
1635 return handCopy;
1637 void SetClip(UINT uFormat) {
1638 ::SetClipboardData(uFormat, Unlock());
1640 operator bool() const {
1641 return ptr != 0;
1643 SIZE_T Size() {
1644 return ::GlobalSize(hand);
1648 void ScintillaWin::InsertPasteText(const char *text, int len, SelectionPosition selStart, bool isRectangular, bool isLine) {
1649 if (isRectangular) {
1650 PasteRectangular(selStart, text, len);
1651 } else {
1652 std::string convertedText;
1653 if (convertPastes) {
1654 // Convert line endings of the paste into our local line-endings mode
1655 convertedText = Document::TransformLineEnds(text, len, pdoc->eolMode);
1656 len = static_cast<int>(convertedText.length());
1657 text = convertedText.c_str();
1659 if (isLine) {
1660 int insertPos = pdoc->LineStart(pdoc->LineFromPosition(sel.MainCaret()));
1661 pdoc->InsertString(insertPos, text, len);
1662 // add the newline if necessary
1663 if ((len > 0) && (text[len-1] != '\n' && text[len-1] != '\r')) {
1664 const char *endline = StringFromEOLMode(pdoc->eolMode);
1665 pdoc->InsertString(insertPos + len, endline, static_cast<int>(strlen(endline)));
1666 len += static_cast<int>(strlen(endline));
1668 if (sel.MainCaret() == insertPos) {
1669 SetEmptySelection(sel.MainCaret() + len);
1671 } else {
1672 InsertPaste(selStart, text, len);
1677 void ScintillaWin::Paste() {
1678 if (!::OpenClipboard(MainHWND()))
1679 return;
1680 UndoGroup ug(pdoc);
1681 bool isLine = SelectionEmpty() && (::IsClipboardFormatAvailable(cfLineSelect) != 0);
1682 ClearSelection(multiPasteMode == SC_MULTIPASTE_EACH);
1683 SelectionPosition selStart = sel.IsRectangular() ?
1684 sel.Rectangular().Start() :
1685 sel.Range(sel.Main()).Start();
1686 bool isRectangular = (::IsClipboardFormatAvailable(cfColumnSelect) != 0);
1688 if (!isRectangular) {
1689 // Evaluate "Borland IDE Block Type" explicitly
1690 GlobalMemory memBorlandSelection(::GetClipboardData(cfBorlandIDEBlockType));
1691 if (memBorlandSelection) {
1692 isRectangular = (memBorlandSelection.Size() == 1) && (static_cast<BYTE *>(memBorlandSelection.ptr)[0] == 0x02);
1693 memBorlandSelection.Unlock();
1697 // Always use CF_UNICODETEXT if available
1698 GlobalMemory memUSelection(::GetClipboardData(CF_UNICODETEXT));
1699 if (memUSelection) {
1700 wchar_t *uptr = static_cast<wchar_t *>(memUSelection.ptr);
1701 if (uptr) {
1702 unsigned int len;
1703 std::vector<char> putf;
1704 // Default Scintilla behaviour in Unicode mode
1705 if (IsUnicodeMode()) {
1706 unsigned int bytes = memUSelection.Size();
1707 len = UTF8Length(uptr, bytes / 2);
1708 putf.resize(len + 1);
1709 UTF8FromUTF16(uptr, bytes / 2, &putf[0], len);
1710 } else {
1711 // CF_UNICODETEXT available, but not in Unicode mode
1712 // Convert from Unicode to current Scintilla code page
1713 UINT cpDest = CodePageOfDocument();
1714 len = ::WideCharToMultiByte(cpDest, 0, uptr, -1,
1715 NULL, 0, NULL, NULL) - 1; // subtract 0 terminator
1716 putf.resize(len + 1);
1717 ::WideCharToMultiByte(cpDest, 0, uptr, -1,
1718 &putf[0], len + 1, NULL, NULL);
1721 InsertPasteText(&putf[0], len, selStart, isRectangular, isLine);
1723 memUSelection.Unlock();
1724 } else {
1725 // CF_UNICODETEXT not available, paste ANSI text
1726 GlobalMemory memSelection(::GetClipboardData(CF_TEXT));
1727 if (memSelection) {
1728 char *ptr = static_cast<char *>(memSelection.ptr);
1729 if (ptr) {
1730 unsigned int bytes = memSelection.Size();
1731 unsigned int len = bytes;
1732 for (unsigned int i = 0; i < bytes; i++) {
1733 if ((len == bytes) && (0 == ptr[i]))
1734 len = i;
1737 // In Unicode mode, convert clipboard text to UTF-8
1738 if (IsUnicodeMode()) {
1739 std::vector<wchar_t> uptr(len+1);
1741 unsigned int ulen = ::MultiByteToWideChar(CP_ACP, 0,
1742 ptr, len, &uptr[0], len+1);
1744 unsigned int mlen = UTF8Length(&uptr[0], ulen);
1745 std::vector<char> putf(mlen+1);
1746 // CP_UTF8 not available on Windows 95, so use UTF8FromUTF16()
1747 UTF8FromUTF16(&uptr[0], ulen, &putf[0], mlen);
1749 InsertPasteText(&putf[0], mlen, selStart, isRectangular, isLine);
1750 } else {
1751 InsertPasteText(ptr, len, selStart, isRectangular, isLine);
1754 memSelection.Unlock();
1757 ::CloseClipboard();
1758 Redraw();
1761 void ScintillaWin::CreateCallTipWindow(PRectangle) {
1762 if (!ct.wCallTip.Created()) {
1763 ct.wCallTip = ::CreateWindow(callClassName, TEXT("ACallTip"),
1764 WS_POPUP, 100, 100, 150, 20,
1765 MainHWND(), 0,
1766 GetWindowInstance(MainHWND()),
1767 this);
1768 ct.wDraw = ct.wCallTip;
1772 void ScintillaWin::AddToPopUp(const char *label, int cmd, bool enabled) {
1773 HMENU hmenuPopup = reinterpret_cast<HMENU>(popup.GetID());
1774 if (!label[0])
1775 ::AppendMenuA(hmenuPopup, MF_SEPARATOR, 0, "");
1776 else if (enabled)
1777 ::AppendMenuA(hmenuPopup, MF_STRING, cmd, label);
1778 else
1779 ::AppendMenuA(hmenuPopup, MF_STRING | MF_DISABLED | MF_GRAYED, cmd, label);
1782 void ScintillaWin::ClaimSelection() {
1783 // Windows does not have a primary selection
1786 /// Implement IUnknown
1788 STDMETHODIMP_(ULONG)FormatEnumerator_AddRef(FormatEnumerator *fe);
1789 STDMETHODIMP FormatEnumerator_QueryInterface(FormatEnumerator *fe, REFIID riid, PVOID *ppv) {
1790 //Platform::DebugPrintf("EFE QI");
1791 *ppv = NULL;
1792 if (riid == IID_IUnknown)
1793 *ppv = reinterpret_cast<IEnumFORMATETC *>(fe);
1794 if (riid == IID_IEnumFORMATETC)
1795 *ppv = reinterpret_cast<IEnumFORMATETC *>(fe);
1796 if (!*ppv)
1797 return E_NOINTERFACE;
1798 FormatEnumerator_AddRef(fe);
1799 return S_OK;
1801 STDMETHODIMP_(ULONG)FormatEnumerator_AddRef(FormatEnumerator *fe) {
1802 return ++fe->ref;
1804 STDMETHODIMP_(ULONG)FormatEnumerator_Release(FormatEnumerator *fe) {
1805 fe->ref--;
1806 if (fe->ref > 0)
1807 return fe->ref;
1808 delete fe;
1809 return 0;
1811 /// Implement IEnumFORMATETC
1812 STDMETHODIMP FormatEnumerator_Next(FormatEnumerator *fe, ULONG celt, FORMATETC *rgelt, ULONG *pceltFetched) {
1813 if (rgelt == NULL) return E_POINTER;
1814 unsigned int putPos = 0;
1815 while ((fe->pos < fe->formats.size()) && (putPos < celt)) {
1816 rgelt->cfFormat = fe->formats[fe->pos];
1817 rgelt->ptd = 0;
1818 rgelt->dwAspect = DVASPECT_CONTENT;
1819 rgelt->lindex = -1;
1820 rgelt->tymed = TYMED_HGLOBAL;
1821 rgelt++;
1822 fe->pos++;
1823 putPos++;
1825 if (pceltFetched)
1826 *pceltFetched = putPos;
1827 return putPos ? S_OK : S_FALSE;
1829 STDMETHODIMP FormatEnumerator_Skip(FormatEnumerator *fe, ULONG celt) {
1830 fe->pos += celt;
1831 return S_OK;
1833 STDMETHODIMP FormatEnumerator_Reset(FormatEnumerator *fe) {
1834 fe->pos = 0;
1835 return S_OK;
1837 STDMETHODIMP FormatEnumerator_Clone(FormatEnumerator *fe, IEnumFORMATETC **ppenum) {
1838 FormatEnumerator *pfe;
1839 try {
1840 pfe = new FormatEnumerator(fe->pos, &fe->formats[0], fe->formats.size());
1841 } catch (...) {
1842 return E_OUTOFMEMORY;
1844 return FormatEnumerator_QueryInterface(pfe, IID_IEnumFORMATETC,
1845 reinterpret_cast<void **>(ppenum));
1848 static VFunction *vtFormatEnumerator[] = {
1849 (VFunction *)(FormatEnumerator_QueryInterface),
1850 (VFunction *)(FormatEnumerator_AddRef),
1851 (VFunction *)(FormatEnumerator_Release),
1852 (VFunction *)(FormatEnumerator_Next),
1853 (VFunction *)(FormatEnumerator_Skip),
1854 (VFunction *)(FormatEnumerator_Reset),
1855 (VFunction *)(FormatEnumerator_Clone)
1858 FormatEnumerator::FormatEnumerator(int pos_, CLIPFORMAT formats_[], size_t formatsLen_) {
1859 vtbl = vtFormatEnumerator;
1860 ref = 0; // First QI adds first reference...
1861 pos = pos_;
1862 formats.insert(formats.begin(), formats_, formats_+formatsLen_);
1865 /// Implement IUnknown
1866 STDMETHODIMP DropSource_QueryInterface(DropSource *ds, REFIID riid, PVOID *ppv) {
1867 return ds->sci->QueryInterface(riid, ppv);
1869 STDMETHODIMP_(ULONG)DropSource_AddRef(DropSource *ds) {
1870 return ds->sci->AddRef();
1872 STDMETHODIMP_(ULONG)DropSource_Release(DropSource *ds) {
1873 return ds->sci->Release();
1876 /// Implement IDropSource
1877 STDMETHODIMP DropSource_QueryContinueDrag(DropSource *, BOOL fEsc, DWORD grfKeyState) {
1878 if (fEsc)
1879 return DRAGDROP_S_CANCEL;
1880 if (!(grfKeyState & MK_LBUTTON))
1881 return DRAGDROP_S_DROP;
1882 return S_OK;
1885 STDMETHODIMP DropSource_GiveFeedback(DropSource *, DWORD) {
1886 return DRAGDROP_S_USEDEFAULTCURSORS;
1889 static VFunction *vtDropSource[] = {
1890 (VFunction *)(DropSource_QueryInterface),
1891 (VFunction *)(DropSource_AddRef),
1892 (VFunction *)(DropSource_Release),
1893 (VFunction *)(DropSource_QueryContinueDrag),
1894 (VFunction *)(DropSource_GiveFeedback)
1897 DropSource::DropSource() {
1898 vtbl = vtDropSource;
1899 sci = 0;
1902 /// Implement IUnkown
1903 STDMETHODIMP DataObject_QueryInterface(DataObject *pd, REFIID riid, PVOID *ppv) {
1904 //Platform::DebugPrintf("DO QI %x\n", pd);
1905 return pd->sci->QueryInterface(riid, ppv);
1907 STDMETHODIMP_(ULONG)DataObject_AddRef(DataObject *pd) {
1908 return pd->sci->AddRef();
1910 STDMETHODIMP_(ULONG)DataObject_Release(DataObject *pd) {
1911 return pd->sci->Release();
1913 /// Implement IDataObject
1914 STDMETHODIMP DataObject_GetData(DataObject *pd, FORMATETC *pFEIn, STGMEDIUM *pSTM) {
1915 return pd->sci->GetData(pFEIn, pSTM);
1918 STDMETHODIMP DataObject_GetDataHere(DataObject *, FORMATETC *, STGMEDIUM *) {
1919 //Platform::DebugPrintf("DOB GetDataHere\n");
1920 return E_NOTIMPL;
1923 STDMETHODIMP DataObject_QueryGetData(DataObject *pd, FORMATETC *pFE) {
1924 if (pd->sci->DragIsRectangularOK(pFE->cfFormat) &&
1925 pFE->ptd == 0 &&
1926 (pFE->dwAspect & DVASPECT_CONTENT) != 0 &&
1927 pFE->lindex == -1 &&
1928 (pFE->tymed & TYMED_HGLOBAL) != 0
1930 return S_OK;
1933 bool formatOK = (pFE->cfFormat == CF_TEXT) ||
1934 ((pFE->cfFormat == CF_UNICODETEXT) && pd->sci->IsUnicodeMode());
1935 if (!formatOK ||
1936 pFE->ptd != 0 ||
1937 (pFE->dwAspect & DVASPECT_CONTENT) == 0 ||
1938 pFE->lindex != -1 ||
1939 (pFE->tymed & TYMED_HGLOBAL) == 0
1941 //Platform::DebugPrintf("DOB QueryGetData No %x\n",pFE->cfFormat);
1942 //return DATA_E_FORMATETC;
1943 return S_FALSE;
1945 //Platform::DebugPrintf("DOB QueryGetData OK %x\n",pFE->cfFormat);
1946 return S_OK;
1949 STDMETHODIMP DataObject_GetCanonicalFormatEtc(DataObject *pd, FORMATETC *, FORMATETC *pFEOut) {
1950 //Platform::DebugPrintf("DOB GetCanon\n");
1951 if (pd->sci->IsUnicodeMode())
1952 pFEOut->cfFormat = CF_UNICODETEXT;
1953 else
1954 pFEOut->cfFormat = CF_TEXT;
1955 pFEOut->ptd = 0;
1956 pFEOut->dwAspect = DVASPECT_CONTENT;
1957 pFEOut->lindex = -1;
1958 pFEOut->tymed = TYMED_HGLOBAL;
1959 return S_OK;
1962 STDMETHODIMP DataObject_SetData(DataObject *, FORMATETC *, STGMEDIUM *, BOOL) {
1963 //Platform::DebugPrintf("DOB SetData\n");
1964 return E_FAIL;
1967 STDMETHODIMP DataObject_EnumFormatEtc(DataObject *pd, DWORD dwDirection, IEnumFORMATETC **ppEnum) {
1968 try {
1969 //Platform::DebugPrintf("DOB EnumFormatEtc %d\n", dwDirection);
1970 if (dwDirection != DATADIR_GET) {
1971 *ppEnum = 0;
1972 return E_FAIL;
1974 FormatEnumerator *pfe;
1975 if (pd->sci->IsUnicodeMode()) {
1976 CLIPFORMAT formats[] = {CF_UNICODETEXT, CF_TEXT};
1977 pfe = new FormatEnumerator(0, formats, ELEMENTS(formats));
1978 } else {
1979 CLIPFORMAT formats[] = {CF_TEXT};
1980 pfe = new FormatEnumerator(0, formats, ELEMENTS(formats));
1982 return FormatEnumerator_QueryInterface(pfe, IID_IEnumFORMATETC,
1983 reinterpret_cast<void **>(ppEnum));
1984 } catch (std::bad_alloc &) {
1985 pd->sci->errorStatus = SC_STATUS_BADALLOC;
1986 return E_OUTOFMEMORY;
1987 } catch (...) {
1988 pd->sci->errorStatus = SC_STATUS_FAILURE;
1989 return E_FAIL;
1993 STDMETHODIMP DataObject_DAdvise(DataObject *, FORMATETC *, DWORD, IAdviseSink *, PDWORD) {
1994 //Platform::DebugPrintf("DOB DAdvise\n");
1995 return E_FAIL;
1998 STDMETHODIMP DataObject_DUnadvise(DataObject *, DWORD) {
1999 //Platform::DebugPrintf("DOB DUnadvise\n");
2000 return E_FAIL;
2003 STDMETHODIMP DataObject_EnumDAdvise(DataObject *, IEnumSTATDATA **) {
2004 //Platform::DebugPrintf("DOB EnumDAdvise\n");
2005 return E_FAIL;
2008 static VFunction *vtDataObject[] = {
2009 (VFunction *)(DataObject_QueryInterface),
2010 (VFunction *)(DataObject_AddRef),
2011 (VFunction *)(DataObject_Release),
2012 (VFunction *)(DataObject_GetData),
2013 (VFunction *)(DataObject_GetDataHere),
2014 (VFunction *)(DataObject_QueryGetData),
2015 (VFunction *)(DataObject_GetCanonicalFormatEtc),
2016 (VFunction *)(DataObject_SetData),
2017 (VFunction *)(DataObject_EnumFormatEtc),
2018 (VFunction *)(DataObject_DAdvise),
2019 (VFunction *)(DataObject_DUnadvise),
2020 (VFunction *)(DataObject_EnumDAdvise)
2023 DataObject::DataObject() {
2024 vtbl = vtDataObject;
2025 sci = 0;
2028 /// Implement IUnknown
2029 STDMETHODIMP DropTarget_QueryInterface(DropTarget *dt, REFIID riid, PVOID *ppv) {
2030 //Platform::DebugPrintf("DT QI %x\n", dt);
2031 return dt->sci->QueryInterface(riid, ppv);
2033 STDMETHODIMP_(ULONG)DropTarget_AddRef(DropTarget *dt) {
2034 return dt->sci->AddRef();
2036 STDMETHODIMP_(ULONG)DropTarget_Release(DropTarget *dt) {
2037 return dt->sci->Release();
2040 /// Implement IDropTarget by forwarding to Scintilla
2041 STDMETHODIMP DropTarget_DragEnter(DropTarget *dt, LPDATAOBJECT pIDataSource, DWORD grfKeyState,
2042 POINTL pt, PDWORD pdwEffect) {
2043 try {
2044 return dt->sci->DragEnter(pIDataSource, grfKeyState, pt, pdwEffect);
2045 } catch (...) {
2046 dt->sci->errorStatus = SC_STATUS_FAILURE;
2048 return E_FAIL;
2050 STDMETHODIMP DropTarget_DragOver(DropTarget *dt, DWORD grfKeyState, POINTL pt, PDWORD pdwEffect) {
2051 try {
2052 return dt->sci->DragOver(grfKeyState, pt, pdwEffect);
2053 } catch (...) {
2054 dt->sci->errorStatus = SC_STATUS_FAILURE;
2056 return E_FAIL;
2058 STDMETHODIMP DropTarget_DragLeave(DropTarget *dt) {
2059 try {
2060 return dt->sci->DragLeave();
2061 } catch (...) {
2062 dt->sci->errorStatus = SC_STATUS_FAILURE;
2064 return E_FAIL;
2066 STDMETHODIMP DropTarget_Drop(DropTarget *dt, LPDATAOBJECT pIDataSource, DWORD grfKeyState,
2067 POINTL pt, PDWORD pdwEffect) {
2068 try {
2069 return dt->sci->Drop(pIDataSource, grfKeyState, pt, pdwEffect);
2070 } catch (...) {
2071 dt->sci->errorStatus = SC_STATUS_FAILURE;
2073 return E_FAIL;
2076 static VFunction *vtDropTarget[] = {
2077 (VFunction *)(DropTarget_QueryInterface),
2078 (VFunction *)(DropTarget_AddRef),
2079 (VFunction *)(DropTarget_Release),
2080 (VFunction *)(DropTarget_DragEnter),
2081 (VFunction *)(DropTarget_DragOver),
2082 (VFunction *)(DropTarget_DragLeave),
2083 (VFunction *)(DropTarget_Drop)
2086 DropTarget::DropTarget() {
2087 vtbl = vtDropTarget;
2088 sci = 0;
2092 * DBCS: support Input Method Editor (IME).
2093 * Called when IME Window opened.
2095 void ScintillaWin::ImeStartComposition() {
2096 if (caret.active) {
2097 // Move IME Window to current caret position
2098 HIMC hIMC = ::ImmGetContext(MainHWND());
2099 Point pos = PointMainCaret();
2100 COMPOSITIONFORM CompForm;
2101 CompForm.dwStyle = CFS_POINT;
2102 CompForm.ptCurrentPos.x = pos.x;
2103 CompForm.ptCurrentPos.y = pos.y;
2105 ::ImmSetCompositionWindow(hIMC, &CompForm);
2107 // Set font of IME window to same as surrounded text.
2108 if (stylesValid) {
2109 // Since the style creation code has been made platform independent,
2110 // The logfont for the IME is recreated here.
2111 int styleHere = (pdoc->StyleAt(sel.MainCaret())) & 31;
2112 LOGFONTA lf = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ""};
2113 int sizeZoomed = vs.styles[styleHere].size + vs.zoomLevel * SC_FONT_SIZE_MULTIPLIER;
2114 if (sizeZoomed <= 2 * SC_FONT_SIZE_MULTIPLIER) // Hangs if sizeZoomed <= 1
2115 sizeZoomed = 2 * SC_FONT_SIZE_MULTIPLIER;
2116 AutoSurface surface(this);
2117 int deviceHeight = sizeZoomed;
2118 if (surface) {
2119 deviceHeight = (sizeZoomed * surface->LogPixelsY()) / 72;
2121 // The negative is to allow for leading
2122 lf.lfHeight = -(abs(deviceHeight / SC_FONT_SIZE_MULTIPLIER));
2123 lf.lfWeight = vs.styles[styleHere].weight;
2124 lf.lfItalic = static_cast<BYTE>(vs.styles[styleHere].italic ? 1 : 0);
2125 lf.lfCharSet = DEFAULT_CHARSET;
2126 lf.lfFaceName[0] = '\0';
2127 if (vs.styles[styleHere].fontName)
2128 StringCopy(lf.lfFaceName, vs.styles[styleHere].fontName);
2130 ::ImmSetCompositionFontA(hIMC, &lf);
2132 ::ImmReleaseContext(MainHWND(), hIMC);
2133 // Caret is displayed in IME window. So, caret in Scintilla is useless.
2134 DropCaret();
2138 /** Called when IME Window closed. */
2139 void ScintillaWin::ImeEndComposition() {
2140 ShowCaretAtCurrentPosition();
2143 void ScintillaWin::AddCharBytes(char b0, char b1) {
2145 int inputCodePage = InputCodePage();
2146 if (inputCodePage && IsUnicodeMode()) {
2147 char utfval[4] = "\0\0\0";
2148 char ansiChars[3];
2149 wchar_t wcs[2];
2150 if (b0) { // Two bytes from IME
2151 ansiChars[0] = b0;
2152 ansiChars[1] = b1;
2153 ansiChars[2] = '\0';
2154 ::MultiByteToWideChar(inputCodePage, 0, ansiChars, 2, wcs, 1);
2155 } else {
2156 ansiChars[0] = b1;
2157 ansiChars[1] = '\0';
2158 ::MultiByteToWideChar(inputCodePage, 0, ansiChars, 1, wcs, 1);
2160 unsigned int len = UTF8Length(wcs, 1);
2161 UTF8FromUTF16(wcs, 1, utfval, len);
2162 utfval[len] = '\0';
2163 AddCharUTF(utfval, len ? len : 1);
2164 } else if (b0) {
2165 char dbcsChars[3];
2166 dbcsChars[0] = b0;
2167 dbcsChars[1] = b1;
2168 dbcsChars[2] = '\0';
2169 AddCharUTF(dbcsChars, 2, true);
2170 } else {
2171 AddChar(b1);
2175 void ScintillaWin::GetIntelliMouseParameters() {
2176 // This retrieves the number of lines per scroll as configured inthe Mouse Properties sheet in Control Panel
2177 ::SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, &linesPerScroll, 0);
2180 void ScintillaWin::CopyToClipboard(const SelectionText &selectedText) {
2181 if (!::OpenClipboard(MainHWND()))
2182 return;
2183 ::EmptyClipboard();
2185 GlobalMemory uniText;
2187 // Default Scintilla behaviour in Unicode mode
2188 if (IsUnicodeMode()) {
2189 int uchars = UTF16Length(selectedText.Data(),
2190 static_cast<int>(selectedText.LengthWithTerminator()));
2191 uniText.Allocate(2 * uchars);
2192 if (uniText) {
2193 UTF16FromUTF8(selectedText.Data(), static_cast<int>(selectedText.LengthWithTerminator()),
2194 static_cast<wchar_t *>(uniText.ptr), uchars);
2196 } else {
2197 // Not Unicode mode
2198 // Convert to Unicode using the current Scintilla code page
2199 UINT cpSrc = CodePageFromCharSet(
2200 selectedText.characterSet, selectedText.codePage);
2201 int uLen = ::MultiByteToWideChar(cpSrc, 0, selectedText.Data(),
2202 static_cast<int>(selectedText.LengthWithTerminator()), 0, 0);
2203 uniText.Allocate(2 * uLen);
2204 if (uniText) {
2205 ::MultiByteToWideChar(cpSrc, 0, selectedText.Data(),
2206 static_cast<int>(selectedText.LengthWithTerminator()),
2207 static_cast<wchar_t *>(uniText.ptr), uLen);
2211 if (uniText) {
2212 if (!IsNT()) {
2213 // Copy ANSI text to clipboard on Windows 9x
2214 // Convert from Unicode text, so other ANSI programs can
2215 // paste the text
2216 // Windows NT, 2k, XP automatically generates CF_TEXT
2217 GlobalMemory ansiText;
2218 ansiText.Allocate(selectedText.LengthWithTerminator());
2219 if (ansiText) {
2220 ::WideCharToMultiByte(CP_ACP, 0, static_cast<wchar_t *>(uniText.ptr), -1,
2221 static_cast<char *>(ansiText.ptr),
2222 static_cast<int>(selectedText.LengthWithTerminator()), NULL, NULL);
2223 ansiText.SetClip(CF_TEXT);
2226 uniText.SetClip(CF_UNICODETEXT);
2227 } else {
2228 // There was a failure - try to copy at least ANSI text
2229 GlobalMemory ansiText;
2230 ansiText.Allocate(selectedText.LengthWithTerminator());
2231 if (ansiText) {
2232 memcpy(static_cast<char *>(ansiText.ptr), selectedText.Data(), selectedText.LengthWithTerminator());
2233 ansiText.SetClip(CF_TEXT);
2237 if (selectedText.rectangular) {
2238 ::SetClipboardData(cfColumnSelect, 0);
2240 GlobalMemory borlandSelection;
2241 borlandSelection.Allocate(1);
2242 if (borlandSelection) {
2243 static_cast<BYTE *>(borlandSelection.ptr)[0] = 0x02;
2244 borlandSelection.SetClip(cfBorlandIDEBlockType);
2248 if (selectedText.lineCopy) {
2249 ::SetClipboardData(cfLineSelect, 0);
2252 ::CloseClipboard();
2255 void ScintillaWin::ScrollMessage(WPARAM wParam) {
2256 //DWORD dwStart = timeGetTime();
2257 //Platform::DebugPrintf("Scroll %x %d\n", wParam, lParam);
2259 SCROLLINFO sci = {};
2260 sci.cbSize = sizeof(sci);
2261 sci.fMask = SIF_ALL;
2263 GetScrollInfo(SB_VERT, &sci);
2265 //Platform::DebugPrintf("ScrollInfo %d mask=%x min=%d max=%d page=%d pos=%d track=%d\n", b,sci.fMask,
2266 //sci.nMin, sci.nMax, sci.nPage, sci.nPos, sci.nTrackPos);
2268 int topLineNew = topLine;
2269 switch (LoWord(wParam)) {
2270 case SB_LINEUP:
2271 topLineNew -= 1;
2272 break;
2273 case SB_LINEDOWN:
2274 topLineNew += 1;
2275 break;
2276 case SB_PAGEUP:
2277 topLineNew -= LinesToScroll(); break;
2278 case SB_PAGEDOWN: topLineNew += LinesToScroll(); break;
2279 case SB_TOP: topLineNew = 0; break;
2280 case SB_BOTTOM: topLineNew = MaxScrollPos(); break;
2281 case SB_THUMBPOSITION: topLineNew = sci.nTrackPos; break;
2282 case SB_THUMBTRACK: topLineNew = sci.nTrackPos; break;
2284 ScrollTo(topLineNew);
2287 void ScintillaWin::HorizontalScrollMessage(WPARAM wParam) {
2288 int xPos = xOffset;
2289 PRectangle rcText = GetTextRectangle();
2290 int pageWidth = rcText.Width() * 2 / 3;
2291 switch (LoWord(wParam)) {
2292 case SB_LINEUP:
2293 xPos -= 20;
2294 break;
2295 case SB_LINEDOWN: // May move past the logical end
2296 xPos += 20;
2297 break;
2298 case SB_PAGEUP:
2299 xPos -= pageWidth;
2300 break;
2301 case SB_PAGEDOWN:
2302 xPos += pageWidth;
2303 if (xPos > scrollWidth - rcText.Width()) { // Hit the end exactly
2304 xPos = scrollWidth - rcText.Width();
2306 break;
2307 case SB_TOP:
2308 xPos = 0;
2309 break;
2310 case SB_BOTTOM:
2311 xPos = scrollWidth;
2312 break;
2313 case SB_THUMBPOSITION:
2314 case SB_THUMBTRACK: {
2315 // 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 =]
2316 SCROLLINFO si;
2317 si.cbSize = sizeof(si);
2318 si.fMask = SIF_TRACKPOS;
2319 if (GetScrollInfo(SB_HORZ, &si)) {
2320 xPos = si.nTrackPos;
2323 break;
2325 HorizontalScrollTo(xPos);
2329 * Redraw all of text area.
2330 * This paint will not be abandoned.
2332 void ScintillaWin::FullPaint() {
2333 if (technology == SC_TECHNOLOGY_DEFAULT) {
2334 HDC hdc = ::GetDC(MainHWND());
2335 FullPaintDC(hdc);
2336 ::ReleaseDC(MainHWND(), hdc);
2337 } else {
2338 FullPaintDC(0);
2343 * Redraw all of text area on the specified DC.
2344 * This paint will not be abandoned.
2346 void ScintillaWin::FullPaintDC(HDC hdc) {
2347 paintState = painting;
2348 rcPaint = GetClientRectangle();
2349 paintingAllText = true;
2350 if (technology == SC_TECHNOLOGY_DEFAULT) {
2351 AutoSurface surfaceWindow(hdc, this);
2352 if (surfaceWindow) {
2353 Paint(surfaceWindow, rcPaint);
2354 surfaceWindow->Release();
2356 } else {
2357 #if defined(USE_D2D)
2358 EnsureRenderTarget();
2359 AutoSurface surfaceWindow(pRenderTarget, this);
2360 if (surfaceWindow) {
2361 pRenderTarget->BeginDraw();
2362 Paint(surfaceWindow, rcPaint);
2363 surfaceWindow->Release();
2364 HRESULT hr = pRenderTarget->EndDraw();
2365 if (hr == D2DERR_RECREATE_TARGET) {
2366 DropRenderTarget();
2369 #endif
2371 paintState = notPainting;
2374 static bool CompareDevCap(HDC hdc, HDC hOtherDC, int nIndex) {
2375 return ::GetDeviceCaps(hdc, nIndex) == ::GetDeviceCaps(hOtherDC, nIndex);
2378 bool ScintillaWin::IsCompatibleDC(HDC hOtherDC) {
2379 HDC hdc = ::GetDC(MainHWND());
2380 bool isCompatible =
2381 CompareDevCap(hdc, hOtherDC, TECHNOLOGY) &&
2382 CompareDevCap(hdc, hOtherDC, LOGPIXELSY) &&
2383 CompareDevCap(hdc, hOtherDC, LOGPIXELSX) &&
2384 CompareDevCap(hdc, hOtherDC, BITSPIXEL) &&
2385 CompareDevCap(hdc, hOtherDC, PLANES);
2386 ::ReleaseDC(MainHWND(), hdc);
2387 return isCompatible;
2390 DWORD ScintillaWin::EffectFromState(DWORD grfKeyState) const {
2391 // These are the Wordpad semantics.
2392 DWORD dwEffect;
2393 if (inDragDrop == ddDragging) // Internal defaults to move
2394 dwEffect = DROPEFFECT_MOVE;
2395 else
2396 dwEffect = DROPEFFECT_COPY;
2397 if (grfKeyState & MK_ALT)
2398 dwEffect = DROPEFFECT_MOVE;
2399 if (grfKeyState & MK_CONTROL)
2400 dwEffect = DROPEFFECT_COPY;
2401 return dwEffect;
2404 /// Implement IUnknown
2405 STDMETHODIMP ScintillaWin::QueryInterface(REFIID riid, PVOID *ppv) {
2406 *ppv = NULL;
2407 if (riid == IID_IUnknown)
2408 *ppv = reinterpret_cast<IDropTarget *>(&dt);
2409 if (riid == IID_IDropSource)
2410 *ppv = reinterpret_cast<IDropSource *>(&ds);
2411 if (riid == IID_IDropTarget)
2412 *ppv = reinterpret_cast<IDropTarget *>(&dt);
2413 if (riid == IID_IDataObject)
2414 *ppv = reinterpret_cast<IDataObject *>(&dob);
2415 if (!*ppv)
2416 return E_NOINTERFACE;
2417 return S_OK;
2420 STDMETHODIMP_(ULONG) ScintillaWin::AddRef() {
2421 return 1;
2424 STDMETHODIMP_(ULONG) ScintillaWin::Release() {
2425 return 1;
2428 /// Implement IDropTarget
2429 STDMETHODIMP ScintillaWin::DragEnter(LPDATAOBJECT pIDataSource, DWORD grfKeyState,
2430 POINTL, PDWORD pdwEffect) {
2431 if (pIDataSource == NULL)
2432 return E_POINTER;
2433 FORMATETC fmtu = {CF_UNICODETEXT, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
2434 HRESULT hrHasUText = pIDataSource->QueryGetData(&fmtu);
2435 hasOKText = (hrHasUText == S_OK);
2436 if (!hasOKText) {
2437 FORMATETC fmte = {CF_TEXT, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
2438 HRESULT hrHasText = pIDataSource->QueryGetData(&fmte);
2439 hasOKText = (hrHasText == S_OK);
2441 if (!hasOKText) {
2442 *pdwEffect = DROPEFFECT_NONE;
2443 return S_OK;
2446 *pdwEffect = EffectFromState(grfKeyState);
2447 return S_OK;
2450 STDMETHODIMP ScintillaWin::DragOver(DWORD grfKeyState, POINTL pt, PDWORD pdwEffect) {
2451 try {
2452 if (!hasOKText || pdoc->IsReadOnly()) {
2453 *pdwEffect = DROPEFFECT_NONE;
2454 return S_OK;
2457 *pdwEffect = EffectFromState(grfKeyState);
2459 // Update the cursor.
2460 POINT rpt = {pt.x, pt.y};
2461 ::ScreenToClient(MainHWND(), &rpt);
2462 SetDragPosition(SPositionFromLocation(Point(rpt.x, rpt.y), false, false, UserVirtualSpace()));
2464 return S_OK;
2465 } catch (...) {
2466 errorStatus = SC_STATUS_FAILURE;
2468 return E_FAIL;
2471 STDMETHODIMP ScintillaWin::DragLeave() {
2472 try {
2473 SetDragPosition(SelectionPosition(invalidPosition));
2474 return S_OK;
2475 } catch (...) {
2476 errorStatus = SC_STATUS_FAILURE;
2478 return E_FAIL;
2481 STDMETHODIMP ScintillaWin::Drop(LPDATAOBJECT pIDataSource, DWORD grfKeyState,
2482 POINTL pt, PDWORD pdwEffect) {
2483 try {
2484 *pdwEffect = EffectFromState(grfKeyState);
2486 if (pIDataSource == NULL)
2487 return E_POINTER;
2489 SetDragPosition(SelectionPosition(invalidPosition));
2491 STGMEDIUM medium = {0, {0}, 0};
2493 std::vector<char> data; // Includes terminating NUL
2495 FORMATETC fmtu = {CF_UNICODETEXT, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
2496 HRESULT hr = pIDataSource->GetData(&fmtu, &medium);
2497 if (SUCCEEDED(hr) && medium.hGlobal) {
2498 GlobalMemory memUDrop(medium.hGlobal);
2499 wchar_t *udata = static_cast<wchar_t *>(memUDrop.ptr);
2500 if (udata) {
2501 if (IsUnicodeMode()) {
2502 int tlen = memUDrop.Size();
2503 // Convert UTF-16 to UTF-8
2504 int dataLen = UTF8Length(udata, tlen/2);
2505 data.resize(dataLen+1);
2506 UTF8FromUTF16(udata, tlen/2, &data[0], dataLen);
2507 } else {
2508 // Convert UTF-16 to ANSI
2510 // Default Scintilla behavior in Unicode mode
2511 // CF_UNICODETEXT available, but not in Unicode mode
2512 // Convert from Unicode to current Scintilla code page
2513 UINT cpDest = CodePageOfDocument();
2514 int tlen = ::WideCharToMultiByte(cpDest, 0, udata, -1,
2515 NULL, 0, NULL, NULL) - 1; // subtract 0 terminator
2516 data.resize(tlen + 1);
2517 ::WideCharToMultiByte(cpDest, 0, udata, -1,
2518 &data[0], tlen + 1, NULL, NULL);
2521 memUDrop.Unlock();
2522 } else {
2523 FORMATETC fmte = {CF_TEXT, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
2524 hr = pIDataSource->GetData(&fmte, &medium);
2525 if (SUCCEEDED(hr) && medium.hGlobal) {
2526 GlobalMemory memDrop(medium.hGlobal);
2527 const char *cdata = static_cast<char *>(memDrop.ptr);
2528 if (cdata)
2529 data.assign(cdata, cdata+strlen(cdata)+1);
2530 memDrop.Unlock();
2534 if (!data.empty() && convertPastes) {
2535 // Convert line endings of the drop into our local line-endings mode
2536 std::string convertedText = Document::TransformLineEnds(&data[0], data.size() - 1, pdoc->eolMode);
2537 data.assign(convertedText.c_str(), convertedText.c_str()+convertedText.length()+1);
2540 if (!SUCCEEDED(hr) || data.empty()) {
2541 //Platform::DebugPrintf("Bad data format: 0x%x\n", hres);
2542 return hr;
2545 FORMATETC fmtr = {cfColumnSelect, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
2546 HRESULT hrRectangular = pIDataSource->QueryGetData(&fmtr);
2548 POINT rpt = {pt.x, pt.y};
2549 ::ScreenToClient(MainHWND(), &rpt);
2550 SelectionPosition movePos = SPositionFromLocation(Point(rpt.x, rpt.y), false, false, UserVirtualSpace());
2552 DropAt(movePos, &data[0], data.size() - 1, *pdwEffect == DROPEFFECT_MOVE, hrRectangular == S_OK);
2554 // Free data
2555 if (medium.pUnkForRelease != NULL)
2556 medium.pUnkForRelease->Release();
2557 else
2558 ::GlobalFree(medium.hGlobal);
2560 return S_OK;
2561 } catch (...) {
2562 errorStatus = SC_STATUS_FAILURE;
2564 return E_FAIL;
2567 /// Implement important part of IDataObject
2568 STDMETHODIMP ScintillaWin::GetData(FORMATETC *pFEIn, STGMEDIUM *pSTM) {
2569 bool formatOK = (pFEIn->cfFormat == CF_TEXT) ||
2570 ((pFEIn->cfFormat == CF_UNICODETEXT) && IsUnicodeMode());
2571 if (!formatOK ||
2572 pFEIn->ptd != 0 ||
2573 (pFEIn->dwAspect & DVASPECT_CONTENT) == 0 ||
2574 pFEIn->lindex != -1 ||
2575 (pFEIn->tymed & TYMED_HGLOBAL) == 0
2577 //Platform::DebugPrintf("DOB GetData No %d %x %x fmt=%x\n", lenDrag, pFEIn, pSTM, pFEIn->cfFormat);
2578 return DATA_E_FORMATETC;
2580 pSTM->tymed = TYMED_HGLOBAL;
2581 //Platform::DebugPrintf("DOB GetData OK %d %x %x\n", lenDrag, pFEIn, pSTM);
2583 GlobalMemory text;
2584 if (pFEIn->cfFormat == CF_UNICODETEXT) {
2585 int uchars = UTF16Length(drag.Data(), static_cast<int>(drag.LengthWithTerminator()));
2586 text.Allocate(2 * uchars);
2587 if (text) {
2588 UTF16FromUTF8(drag.Data(), static_cast<int>(drag.LengthWithTerminator()),
2589 static_cast<wchar_t *>(text.ptr), uchars);
2591 } else {
2592 text.Allocate(drag.LengthWithTerminator());
2593 if (text) {
2594 memcpy(static_cast<char *>(text.ptr), drag.Data(), drag.LengthWithTerminator());
2597 pSTM->hGlobal = text ? text.Unlock() : 0;
2598 pSTM->pUnkForRelease = 0;
2599 return S_OK;
2602 bool ScintillaWin::Register(HINSTANCE hInstance_) {
2604 hInstance = hInstance_;
2605 bool result;
2607 // Register the Scintilla class
2608 if (IsNT()) {
2610 // Register Scintilla as a wide character window
2611 WNDCLASSEXW wndclass;
2612 wndclass.cbSize = sizeof(wndclass);
2613 wndclass.style = CS_GLOBALCLASS | CS_HREDRAW | CS_VREDRAW;
2614 wndclass.lpfnWndProc = ScintillaWin::SWndProc;
2615 wndclass.cbClsExtra = 0;
2616 wndclass.cbWndExtra = sizeof(ScintillaWin *);
2617 wndclass.hInstance = hInstance;
2618 wndclass.hIcon = NULL;
2619 wndclass.hCursor = NULL;
2620 wndclass.hbrBackground = NULL;
2621 wndclass.lpszMenuName = NULL;
2622 wndclass.lpszClassName = L"Scintilla";
2623 wndclass.hIconSm = 0;
2624 result = ::RegisterClassExW(&wndclass) != 0;
2625 } else {
2627 // Register Scintilla as a normal character window
2628 WNDCLASSEX wndclass;
2629 wndclass.cbSize = sizeof(wndclass);
2630 wndclass.style = CS_GLOBALCLASS | CS_HREDRAW | CS_VREDRAW;
2631 wndclass.lpfnWndProc = ScintillaWin::SWndProc;
2632 wndclass.cbClsExtra = 0;
2633 wndclass.cbWndExtra = sizeof(ScintillaWin *);
2634 wndclass.hInstance = hInstance;
2635 wndclass.hIcon = NULL;
2636 wndclass.hCursor = NULL;
2637 wndclass.hbrBackground = NULL;
2638 wndclass.lpszMenuName = NULL;
2639 wndclass.lpszClassName = scintillaClassName;
2640 wndclass.hIconSm = 0;
2641 result = ::RegisterClassEx(&wndclass) != 0;
2644 if (result) {
2645 // Register the CallTip class
2646 WNDCLASSEX wndclassc;
2647 wndclassc.cbSize = sizeof(wndclassc);
2648 wndclassc.style = CS_GLOBALCLASS | CS_HREDRAW | CS_VREDRAW;
2649 wndclassc.cbClsExtra = 0;
2650 wndclassc.cbWndExtra = sizeof(ScintillaWin *);
2651 wndclassc.hInstance = hInstance;
2652 wndclassc.hIcon = NULL;
2653 wndclassc.hbrBackground = NULL;
2654 wndclassc.lpszMenuName = NULL;
2655 wndclassc.lpfnWndProc = ScintillaWin::CTWndProc;
2656 wndclassc.hCursor = ::LoadCursor(NULL, IDC_ARROW);
2657 wndclassc.lpszClassName = callClassName;
2658 wndclassc.hIconSm = 0;
2660 result = ::RegisterClassEx(&wndclassc) != 0;
2663 return result;
2666 bool ScintillaWin::Unregister() {
2667 bool result = ::UnregisterClass(scintillaClassName, hInstance) != 0;
2668 if (::UnregisterClass(callClassName, hInstance) == 0)
2669 result = false;
2670 return result;
2673 bool ScintillaWin::HasCaretSizeChanged() const {
2674 if (
2675 ( (0 != vs.caretWidth) && (sysCaretWidth != vs.caretWidth) )
2676 || ((0 != vs.lineHeight) && (sysCaretHeight != vs.lineHeight))
2678 return true;
2680 return false;
2683 BOOL ScintillaWin::CreateSystemCaret() {
2684 sysCaretWidth = vs.caretWidth;
2685 if (0 == sysCaretWidth) {
2686 sysCaretWidth = 1;
2688 sysCaretHeight = vs.lineHeight;
2689 int bitmapSize = (((sysCaretWidth + 15) & ~15) >> 3) *
2690 sysCaretHeight;
2691 std::vector<char> bits(bitmapSize);
2692 sysCaretBitmap = ::CreateBitmap(sysCaretWidth, sysCaretHeight, 1,
2693 1, reinterpret_cast<BYTE *>(&bits[0]));
2694 BOOL retval = ::CreateCaret(
2695 MainHWND(), sysCaretBitmap,
2696 sysCaretWidth, sysCaretHeight);
2697 ::ShowCaret(MainHWND());
2698 return retval;
2701 BOOL ScintillaWin::DestroySystemCaret() {
2702 ::HideCaret(MainHWND());
2703 BOOL retval = ::DestroyCaret();
2704 if (sysCaretBitmap) {
2705 ::DeleteObject(sysCaretBitmap);
2706 sysCaretBitmap = 0;
2708 return retval;
2711 sptr_t PASCAL ScintillaWin::CTWndProc(
2712 HWND hWnd, UINT iMessage, WPARAM wParam, sptr_t lParam) {
2713 // Find C++ object associated with window.
2714 ScintillaWin *sciThis = reinterpret_cast<ScintillaWin *>(PointerFromWindow(hWnd));
2715 try {
2716 // ctp will be zero if WM_CREATE not seen yet
2717 if (sciThis == 0) {
2718 if (iMessage == WM_CREATE) {
2719 // Associate CallTip object with window
2720 CREATESTRUCT *pCreate = reinterpret_cast<CREATESTRUCT *>(lParam);
2721 SetWindowPointer(hWnd, pCreate->lpCreateParams);
2722 return 0;
2723 } else {
2724 return ::DefWindowProc(hWnd, iMessage, wParam, lParam);
2726 } else {
2727 if (iMessage == WM_NCDESTROY) {
2728 ::SetWindowLong(hWnd, 0, 0);
2729 return ::DefWindowProc(hWnd, iMessage, wParam, lParam);
2730 } else if (iMessage == WM_PAINT) {
2731 PAINTSTRUCT ps;
2732 ::BeginPaint(hWnd, &ps);
2733 Surface *surfaceWindow = Surface::Allocate(sciThis->technology);
2734 if (surfaceWindow) {
2735 #if defined(USE_D2D)
2736 ID2D1HwndRenderTarget *pCTRenderTarget = 0;
2737 #endif
2738 RECT rc;
2739 GetClientRect(hWnd, &rc);
2740 // Create a Direct2D render target.
2741 if (sciThis->technology == SC_TECHNOLOGY_DEFAULT) {
2742 surfaceWindow->Init(ps.hdc, hWnd);
2743 } else {
2744 #if defined(USE_D2D)
2745 D2D1_HWND_RENDER_TARGET_PROPERTIES dhrtp;
2746 dhrtp.hwnd = hWnd;
2747 dhrtp.pixelSize = D2D1::SizeU(rc.right - rc.left, rc.bottom - rc.top);
2748 dhrtp.presentOptions = D2D1_PRESENT_OPTIONS_NONE;
2750 D2D1_RENDER_TARGET_PROPERTIES drtp;
2751 drtp.type = D2D1_RENDER_TARGET_TYPE_DEFAULT;
2752 drtp.pixelFormat.format = DXGI_FORMAT_UNKNOWN;
2753 drtp.pixelFormat.alphaMode = D2D1_ALPHA_MODE_UNKNOWN;
2754 drtp.dpiX = 96.0;
2755 drtp.dpiY = 96.0;
2756 drtp.usage = D2D1_RENDER_TARGET_USAGE_NONE;
2757 drtp.minLevel = D2D1_FEATURE_LEVEL_DEFAULT;
2759 if (!SUCCEEDED(pD2DFactory->CreateHwndRenderTarget(drtp, dhrtp, &pCTRenderTarget))) {
2760 surfaceWindow->Release();
2761 delete surfaceWindow;
2762 ::EndPaint(hWnd, &ps);
2763 return 0;
2765 surfaceWindow->Init(pCTRenderTarget, hWnd);
2766 pCTRenderTarget->BeginDraw();
2767 #endif
2769 surfaceWindow->SetUnicodeMode(SC_CP_UTF8 == sciThis->ct.codePage);
2770 surfaceWindow->SetDBCSMode(sciThis->ct.codePage);
2771 sciThis->ct.PaintCT(surfaceWindow);
2772 #if defined(USE_D2D)
2773 if (pCTRenderTarget)
2774 pCTRenderTarget->EndDraw();
2775 #endif
2776 surfaceWindow->Release();
2777 delete surfaceWindow;
2778 #if defined(USE_D2D)
2779 if (pCTRenderTarget)
2780 pCTRenderTarget->Release();
2781 #endif
2783 ::EndPaint(hWnd, &ps);
2784 return 0;
2785 } else if ((iMessage == WM_NCLBUTTONDOWN) || (iMessage == WM_NCLBUTTONDBLCLK)) {
2786 POINT pt;
2787 pt.x = static_cast<short>(LOWORD(lParam));
2788 pt.y = static_cast<short>(HIWORD(lParam));
2789 ScreenToClient(hWnd, &pt);
2790 sciThis->ct.MouseClick(Point(pt.x, pt.y));
2791 sciThis->CallTipClick();
2792 return 0;
2793 } else if (iMessage == WM_LBUTTONDOWN) {
2794 // This does not fire due to the hit test code
2795 sciThis->ct.MouseClick(Point::FromLong(lParam));
2796 sciThis->CallTipClick();
2797 return 0;
2798 } else if (iMessage == WM_SETCURSOR) {
2799 ::SetCursor(::LoadCursor(NULL, IDC_ARROW));
2800 return 0;
2801 } else if (iMessage == WM_NCHITTEST) {
2802 return HTCAPTION;
2803 } else {
2804 return ::DefWindowProc(hWnd, iMessage, wParam, lParam);
2807 } catch (...) {
2808 sciThis->errorStatus = SC_STATUS_FAILURE;
2810 return ::DefWindowProc(hWnd, iMessage, wParam, lParam);
2813 sptr_t ScintillaWin::DirectFunction(
2814 ScintillaWin *sci, UINT iMessage, uptr_t wParam, sptr_t lParam) {
2815 PLATFORM_ASSERT(::GetCurrentThreadId() == ::GetWindowThreadProcessId(sci->MainHWND(), NULL));
2816 return sci->WndProc(iMessage, wParam, lParam);
2819 extern "C"
2820 #ifndef STATIC_BUILD
2821 __declspec(dllexport)
2822 #endif
2823 sptr_t __stdcall Scintilla_DirectFunction(
2824 ScintillaWin *sci, UINT iMessage, uptr_t wParam, sptr_t lParam) {
2825 return sci->WndProc(iMessage, wParam, lParam);
2828 sptr_t PASCAL ScintillaWin::SWndProc(
2829 HWND hWnd, UINT iMessage, WPARAM wParam, sptr_t lParam) {
2830 //Platform::DebugPrintf("S W:%x M:%x WP:%x L:%x\n", hWnd, iMessage, wParam, lParam);
2832 // Find C++ object associated with window.
2833 ScintillaWin *sci = reinterpret_cast<ScintillaWin *>(PointerFromWindow(hWnd));
2834 // sci will be zero if WM_CREATE not seen yet
2835 if (sci == 0) {
2836 try {
2837 if (iMessage == WM_CREATE) {
2838 // Create C++ object associated with window
2839 sci = new ScintillaWin(hWnd);
2840 SetWindowPointer(hWnd, sci);
2841 return sci->WndProc(iMessage, wParam, lParam);
2843 } catch (...) {
2845 return ::DefWindowProc(hWnd, iMessage, wParam, lParam);
2846 } else {
2847 if (iMessage == WM_NCDESTROY) {
2848 try {
2849 sci->Finalise();
2850 delete sci;
2851 } catch (...) {
2853 ::SetWindowLong(hWnd, 0, 0);
2854 return ::DefWindowProc(hWnd, iMessage, wParam, lParam);
2855 } else {
2856 return sci->WndProc(iMessage, wParam, lParam);
2861 // This function is externally visible so it can be called from container when building statically.
2862 // Must be called once only.
2863 int Scintilla_RegisterClasses(void *hInstance) {
2864 Platform_Initialise(hInstance);
2865 bool result = ScintillaWin::Register(reinterpret_cast<HINSTANCE>(hInstance));
2866 #ifdef SCI_LEXER
2867 Scintilla_LinkLexers();
2868 #endif
2869 return result;
2872 // This function is externally visible so it can be called from container when building statically.
2873 int Scintilla_ReleaseResources() {
2874 bool result = ScintillaWin::Unregister();
2875 Platform_Finalise();
2876 return result;
2879 #ifndef STATIC_BUILD
2880 extern "C" int APIENTRY DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID) {
2881 //Platform::DebugPrintf("Scintilla::DllMain %d %d\n", hInstance, dwReason);
2882 if (dwReason == DLL_PROCESS_ATTACH) {
2883 if (!Scintilla_RegisterClasses(hInstance))
2884 return FALSE;
2885 } else if (dwReason == DLL_PROCESS_DETACH) {
2886 Scintilla_ReleaseResources();
2888 return TRUE;
2890 #endif