applied backgroundcolors.patch
[TortoiseGit.git] / ext / scintilla / win32 / ScintillaWin.cxx
blobdf56a97fae7ebda156450555af37d8e86f9b6297
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 <new>
9 #include <stdlib.h>
10 #include <string.h>
11 #include <stdio.h>
12 #include <ctype.h>
13 #include <assert.h>
14 #include <limits.h>
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 "SplitVector.h"
49 #include "Partitioning.h"
50 #include "RunStyles.h"
51 #include "ContractionState.h"
52 #include "CellBuffer.h"
53 #include "CallTip.h"
54 #include "KeyMap.h"
55 #include "Indicator.h"
56 #include "XPM.h"
57 #include "LineMarker.h"
58 #include "Style.h"
59 #include "AutoComplete.h"
60 #include "ViewStyle.h"
61 #include "CharClassify.h"
62 #include "Decoration.h"
63 #include "CaseFolder.h"
64 #include "Document.h"
65 #include "Selection.h"
66 #include "PositionCache.h"
67 #include "Editor.h"
68 #include "ScintillaBase.h"
69 #include "UniConversion.h"
70 #include "CaseConvert.h"
72 #include "PlatWin.h"
74 #ifdef SCI_LEXER
75 #include "ExternalLexer.h"
76 #endif
78 #ifndef SPI_GETWHEELSCROLLLINES
79 #define SPI_GETWHEELSCROLLLINES 104
80 #endif
82 #ifndef WM_UNICHAR
83 #define WM_UNICHAR 0x0109
84 #endif
86 #ifndef UNICODE_NOCHAR
87 #define UNICODE_NOCHAR 0xFFFF
88 #endif
90 #ifndef WM_IME_STARTCOMPOSITION
91 #include <imm.h>
92 #endif
94 #include <commctrl.h>
95 #include <zmouse.h>
96 #include <ole2.h>
98 #ifndef MK_ALT
99 #define MK_ALT 32
100 #endif
102 #define SC_WIN_IDLE 5001
104 typedef BOOL (WINAPI *TrackMouseEventSig)(LPTRACKMOUSEEVENT);
106 // GCC has trouble with the standard COM ABI so do it the old C way with explicit vtables.
108 const TCHAR scintillaClassName[] = TEXT("Scintilla");
109 const TCHAR callClassName[] = TEXT("CallTip");
111 #ifdef SCI_NAMESPACE
112 using namespace Scintilla;
113 #endif
115 // Take care of 32/64 bit pointers
116 #ifdef GetWindowLongPtr
117 static void *PointerFromWindow(HWND hWnd) {
118 return reinterpret_cast<void *>(::GetWindowLongPtr(hWnd, 0));
120 static void SetWindowPointer(HWND hWnd, void *ptr) {
121 ::SetWindowLongPtr(hWnd, 0, reinterpret_cast<LONG_PTR>(ptr));
123 static void SetWindowID(HWND hWnd, int identifier) {
124 ::SetWindowLongPtr(hWnd, GWLP_ID, identifier);
126 #else
127 static void *PointerFromWindow(HWND hWnd) {
128 return reinterpret_cast<void *>(::GetWindowLong(hWnd, 0));
130 static void SetWindowPointer(HWND hWnd, void *ptr) {
131 ::SetWindowLong(hWnd, 0, reinterpret_cast<LONG>(ptr));
133 static void SetWindowID(HWND hWnd, int identifier) {
134 ::SetWindowLong(hWnd, GWL_ID, identifier);
136 #endif
138 class ScintillaWin; // Forward declaration for COM interface subobjects
140 typedef void VFunction(void);
144 class FormatEnumerator {
145 public:
146 VFunction **vtbl;
147 int ref;
148 int pos;
149 CLIPFORMAT formats[2];
150 int formatsLen;
151 FormatEnumerator(int pos_, CLIPFORMAT formats_[], int formatsLen_);
156 class DropSource {
157 public:
158 VFunction **vtbl;
159 ScintillaWin *sci;
160 DropSource();
165 class DataObject {
166 public:
167 VFunction **vtbl;
168 ScintillaWin *sci;
169 DataObject();
174 class DropTarget {
175 public:
176 VFunction **vtbl;
177 ScintillaWin *sci;
178 DropTarget();
183 class ScintillaWin :
184 public ScintillaBase {
186 bool lastKeyDownConsumed;
188 bool capturedMouse;
189 bool trackedMouseLeave;
190 TrackMouseEventSig TrackMouseEventFn;
192 unsigned int linesPerScroll; ///< Intellimouse support
193 int wheelDelta; ///< Wheel delta from roll
195 HRGN hRgnUpdate;
197 bool hasOKText;
199 CLIPFORMAT cfColumnSelect;
200 CLIPFORMAT cfLineSelect;
202 HRESULT hrOle;
203 DropSource ds;
204 DataObject dob;
205 DropTarget dt;
207 static HINSTANCE hInstance;
209 #if defined(USE_D2D)
210 ID2D1HwndRenderTarget *pRenderTarget;
211 bool renderTargetValid;
212 #endif
214 ScintillaWin(HWND hwnd);
215 ScintillaWin(const ScintillaWin &);
216 virtual ~ScintillaWin();
217 ScintillaWin &operator=(const ScintillaWin &);
219 virtual void Initialise();
220 virtual void Finalise();
221 #if defined(USE_D2D)
222 void EnsureRenderTarget();
223 void DropRenderTarget();
224 #endif
225 HWND MainHWND();
227 static sptr_t DirectFunction(
228 ScintillaWin *sci, UINT iMessage, uptr_t wParam, sptr_t lParam);
229 static sptr_t PASCAL SWndProc(
230 HWND hWnd, UINT iMessage, WPARAM wParam, sptr_t lParam);
231 static sptr_t PASCAL CTWndProc(
232 HWND hWnd, UINT iMessage, WPARAM wParam, sptr_t lParam);
234 enum { invalidTimerID, standardTimerID, idleTimerID };
236 virtual bool DragThreshold(Point ptStart, Point ptNow);
237 virtual void StartDrag();
238 sptr_t WndPaint(uptr_t wParam);
239 sptr_t HandleComposition(uptr_t wParam, sptr_t lParam);
240 UINT CodePageOfDocument();
241 virtual bool ValidCodePage(int codePage) const;
242 virtual sptr_t DefWndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam);
243 virtual bool SetIdle(bool on);
244 virtual void SetTicking(bool on);
245 virtual void SetMouseCapture(bool on);
246 virtual bool HaveMouseCapture();
247 virtual void SetTrackMouseLeaveEvent(bool on);
248 virtual bool PaintContains(PRectangle rc);
249 virtual void ScrollText(int linesToMove);
250 virtual void UpdateSystemCaret();
251 virtual void SetVerticalScrollPos();
252 virtual void SetHorizontalScrollPos();
253 virtual bool ModifyScrollBars(int nMax, int nPage);
254 virtual void NotifyChange();
255 virtual void NotifyFocus(bool focus);
256 virtual void SetCtrlID(int identifier);
257 virtual int GetCtrlID();
258 virtual void NotifyParent(SCNotification scn);
259 virtual void NotifyParent(SCNotification * scn);
260 virtual void NotifyDoubleClick(Point pt, bool shift, bool ctrl, bool alt);
261 virtual CaseFolder *CaseFolderForEncoding();
262 virtual std::string CaseMapString(const std::string &s, int caseMapping);
263 virtual void Copy();
264 virtual void CopyAllowLine();
265 virtual bool CanPaste();
266 virtual void Paste();
267 virtual void CreateCallTipWindow(PRectangle rc);
268 virtual void AddToPopUp(const char *label, int cmd = 0, bool enabled = true);
269 virtual void ClaimSelection();
271 // DBCS
272 void ImeStartComposition();
273 void ImeEndComposition();
275 void AddCharBytes(char b0, char b1);
277 void GetIntelliMouseParameters();
278 virtual void CopyToClipboard(const SelectionText &selectedText);
279 void ScrollMessage(WPARAM wParam);
280 void HorizontalScrollMessage(WPARAM wParam);
281 void FullPaint();
282 void FullPaintDC(HDC dc);
283 bool IsCompatibleDC(HDC dc);
284 DWORD EffectFromState(DWORD grfKeyState) const;
286 virtual int SetScrollInfo(int nBar, LPCSCROLLINFO lpsi, BOOL bRedraw);
287 virtual bool GetScrollInfo(int nBar, LPSCROLLINFO lpsi);
288 void ChangeScrollPos(int barType, int pos);
290 void InsertPasteText(const char *text, int len, SelectionPosition selStart, bool isRectangular, bool isLine);
292 public:
293 // Public for benefit of Scintilla_DirectFunction
294 virtual sptr_t WndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam);
296 /// Implement IUnknown
297 STDMETHODIMP QueryInterface(REFIID riid, PVOID *ppv);
298 STDMETHODIMP_(ULONG)AddRef();
299 STDMETHODIMP_(ULONG)Release();
301 /// Implement IDropTarget
302 STDMETHODIMP DragEnter(LPDATAOBJECT pIDataSource, DWORD grfKeyState,
303 POINTL pt, PDWORD pdwEffect);
304 STDMETHODIMP DragOver(DWORD grfKeyState, POINTL pt, PDWORD pdwEffect);
305 STDMETHODIMP DragLeave();
306 STDMETHODIMP Drop(LPDATAOBJECT pIDataSource, DWORD grfKeyState,
307 POINTL pt, PDWORD pdwEffect);
309 /// Implement important part of IDataObject
310 STDMETHODIMP GetData(FORMATETC *pFEIn, STGMEDIUM *pSTM);
312 static bool Register(HINSTANCE hInstance_);
313 static bool Unregister();
315 friend class DropSource;
316 friend class DataObject;
317 friend class DropTarget;
318 bool DragIsRectangularOK(CLIPFORMAT fmt) const {
319 return drag.rectangular && (fmt == cfColumnSelect);
322 private:
323 // For use in creating a system caret
324 bool HasCaretSizeChanged() const;
325 BOOL CreateSystemCaret();
326 BOOL DestroySystemCaret();
327 HBITMAP sysCaretBitmap;
328 int sysCaretWidth;
329 int sysCaretHeight;
330 bool keysAlwaysUnicode;
333 HINSTANCE ScintillaWin::hInstance = 0;
335 ScintillaWin::ScintillaWin(HWND hwnd) {
337 lastKeyDownConsumed = false;
339 capturedMouse = false;
340 trackedMouseLeave = false;
341 TrackMouseEventFn = 0;
343 linesPerScroll = 0;
344 wheelDelta = 0; // Wheel delta from roll
346 hRgnUpdate = 0;
348 hasOKText = false;
350 // There does not seem to be a real standard for indicating that the clipboard
351 // contains a rectangular selection, so copy Developer Studio.
352 cfColumnSelect = static_cast<CLIPFORMAT>(
353 ::RegisterClipboardFormat(TEXT("MSDEVColumnSelect")));
355 // Likewise for line-copy (copies a full line when no text is selected)
356 cfLineSelect = static_cast<CLIPFORMAT>(
357 ::RegisterClipboardFormat(TEXT("MSDEVLineSelect")));
359 hrOle = E_FAIL;
361 wMain = hwnd;
363 dob.sci = this;
364 ds.sci = this;
365 dt.sci = this;
367 sysCaretBitmap = 0;
368 sysCaretWidth = 0;
369 sysCaretHeight = 0;
371 #if defined(USE_D2D)
372 pRenderTarget = 0;
373 renderTargetValid = true;
374 #endif
376 keysAlwaysUnicode = false;
378 caret.period = ::GetCaretBlinkTime();
379 if (caret.period < 0)
380 caret.period = 0;
382 Initialise();
385 ScintillaWin::~ScintillaWin() {}
387 void ScintillaWin::Initialise() {
388 // Initialize COM. If the app has already done this it will have
389 // no effect. If the app hasnt, we really shouldnt ask them to call
390 // it just so this internal feature works.
391 hrOle = ::OleInitialize(NULL);
393 // Find TrackMouseEvent which is available on Windows > 95
394 HMODULE user32 = ::GetModuleHandle(TEXT("user32.dll"));
395 if (user32)
396 TrackMouseEventFn = (TrackMouseEventSig)::GetProcAddress(user32, "TrackMouseEvent");
397 if (TrackMouseEventFn == NULL) {
398 // Windows 95 has an emulation in comctl32.dll:_TrackMouseEvent
399 HMODULE commctrl32 = ::LoadLibrary(TEXT("comctl32.dll"));
400 if (commctrl32 != NULL) {
401 TrackMouseEventFn = (TrackMouseEventSig)
402 ::GetProcAddress(commctrl32, "_TrackMouseEvent");
407 void ScintillaWin::Finalise() {
408 ScintillaBase::Finalise();
409 SetTicking(false);
410 SetIdle(false);
411 #if defined(USE_D2D)
412 DropRenderTarget();
413 #endif
414 ::RevokeDragDrop(MainHWND());
415 if (SUCCEEDED(hrOle)) {
416 ::OleUninitialize();
420 #if defined(USE_D2D)
422 void ScintillaWin::EnsureRenderTarget() {
423 if (!renderTargetValid) {
424 DropRenderTarget();
425 renderTargetValid = true;
427 if (pD2DFactory && !pRenderTarget) {
428 RECT rc;
429 HWND hw = MainHWND();
430 GetClientRect(hw, &rc);
432 D2D1_SIZE_U size = D2D1::SizeU(rc.right - rc.left, rc.bottom - rc.top);
434 // Create a Direct2D render target.
435 #if 1
436 D2D1_HWND_RENDER_TARGET_PROPERTIES dhrtp;
437 dhrtp.hwnd = hw;
438 dhrtp.pixelSize = size;
439 dhrtp.presentOptions = D2D1_PRESENT_OPTIONS_NONE;
441 D2D1_RENDER_TARGET_PROPERTIES drtp;
442 drtp.type = D2D1_RENDER_TARGET_TYPE_DEFAULT;
443 drtp.pixelFormat.format = DXGI_FORMAT_UNKNOWN;
444 drtp.pixelFormat.alphaMode = D2D1_ALPHA_MODE_UNKNOWN;
445 drtp.dpiX = 96.0;
446 drtp.dpiY = 96.0;
447 drtp.usage = D2D1_RENDER_TARGET_USAGE_NONE;
448 drtp.minLevel = D2D1_FEATURE_LEVEL_DEFAULT;
450 pD2DFactory->CreateHwndRenderTarget(drtp, dhrtp, &pRenderTarget);
451 #else
452 pD2DFactory->CreateHwndRenderTarget(
453 D2D1::RenderTargetProperties(
454 D2D1_RENDER_TARGET_TYPE_DEFAULT ,
455 D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_PREMULTIPLIED),
456 96.0f, 96.0f, D2D1_RENDER_TARGET_USAGE_NONE, D2D1_FEATURE_LEVEL_DEFAULT),
457 D2D1::HwndRenderTargetProperties(hw, size),
458 &pRenderTarget);
459 #endif
460 // Pixmaps were created to be compatible with previous render target so
461 // need to be recreated.
462 DropGraphics(false);
466 void ScintillaWin::DropRenderTarget() {
467 if (pRenderTarget) {
468 pRenderTarget->Release();
469 pRenderTarget = 0;
473 #endif
475 HWND ScintillaWin::MainHWND() {
476 return reinterpret_cast<HWND>(wMain.GetID());
479 bool ScintillaWin::DragThreshold(Point ptStart, Point ptNow) {
480 int xMove = abs(ptStart.x - ptNow.x);
481 int yMove = abs(ptStart.y - ptNow.y);
482 return (xMove > ::GetSystemMetrics(SM_CXDRAG)) ||
483 (yMove > ::GetSystemMetrics(SM_CYDRAG));
486 void ScintillaWin::StartDrag() {
487 inDragDrop = ddDragging;
488 DWORD dwEffect = 0;
489 dropWentOutside = true;
490 IDataObject *pDataObject = reinterpret_cast<IDataObject *>(&dob);
491 IDropSource *pDropSource = reinterpret_cast<IDropSource *>(&ds);
492 //Platform::DebugPrintf("About to DoDragDrop %x %x\n", pDataObject, pDropSource);
493 HRESULT hr = ::DoDragDrop(
494 pDataObject,
495 pDropSource,
496 DROPEFFECT_COPY | DROPEFFECT_MOVE, &dwEffect);
497 //Platform::DebugPrintf("DoDragDrop = %x\n", hr);
498 if (SUCCEEDED(hr)) {
499 if ((hr == DRAGDROP_S_DROP) && (dwEffect == DROPEFFECT_MOVE) && dropWentOutside) {
500 // Remove dragged out text
501 ClearSelection();
504 inDragDrop = ddNone;
505 SetDragPosition(SelectionPosition(invalidPosition));
508 // Avoid warnings everywhere for old style casts by concentrating them here
509 static WORD LoWord(DWORD l) {
510 return LOWORD(l);
513 static WORD HiWord(DWORD l) {
514 return HIWORD(l);
517 static int InputCodePage() {
518 HKL inputLocale = ::GetKeyboardLayout(0);
519 LANGID inputLang = LOWORD(inputLocale);
520 char sCodePage[10];
521 int res = ::GetLocaleInfoA(MAKELCID(inputLang, SORT_DEFAULT),
522 LOCALE_IDEFAULTANSICODEPAGE, sCodePage, sizeof(sCodePage));
523 if (!res)
524 return 0;
525 return atoi(sCodePage);
528 #ifndef VK_OEM_2
529 static const int VK_OEM_2=0xbf;
530 static const int VK_OEM_3=0xc0;
531 static const int VK_OEM_4=0xdb;
532 static const int VK_OEM_5=0xdc;
533 static const int VK_OEM_6=0xdd;
534 #endif
536 /** Map the key codes to their equivalent SCK_ form. */
537 static int KeyTranslate(int keyIn) {
538 //PLATFORM_ASSERT(!keyIn);
539 switch (keyIn) {
540 case VK_DOWN: return SCK_DOWN;
541 case VK_UP: return SCK_UP;
542 case VK_LEFT: return SCK_LEFT;
543 case VK_RIGHT: return SCK_RIGHT;
544 case VK_HOME: return SCK_HOME;
545 case VK_END: return SCK_END;
546 case VK_PRIOR: return SCK_PRIOR;
547 case VK_NEXT: return SCK_NEXT;
548 case VK_DELETE: return SCK_DELETE;
549 case VK_INSERT: return SCK_INSERT;
550 case VK_ESCAPE: return SCK_ESCAPE;
551 case VK_BACK: return SCK_BACK;
552 case VK_TAB: return SCK_TAB;
553 case VK_RETURN: return SCK_RETURN;
554 case VK_ADD: return SCK_ADD;
555 case VK_SUBTRACT: return SCK_SUBTRACT;
556 case VK_DIVIDE: return SCK_DIVIDE;
557 case VK_LWIN: return SCK_WIN;
558 case VK_RWIN: return SCK_RWIN;
559 case VK_APPS: return SCK_MENU;
560 case VK_OEM_2: return '/';
561 case VK_OEM_3: return '`';
562 case VK_OEM_4: return '[';
563 case VK_OEM_5: return '\\';
564 case VK_OEM_6: return ']';
565 default: return keyIn;
569 LRESULT ScintillaWin::WndPaint(uptr_t wParam) {
570 //ElapsedTime et;
572 // Redirect assertions to debug output and save current state
573 bool assertsPopup = Platform::ShowAssertionPopUps(false);
574 paintState = painting;
575 PAINTSTRUCT ps;
576 PAINTSTRUCT *pps;
578 bool IsOcxCtrl = (wParam != 0); // if wParam != 0, it contains
579 // a PAINSTRUCT* from the OCX
580 // Removed since this interferes with reporting other assertions as it occurs repeatedly
581 //PLATFORM_ASSERT(hRgnUpdate == NULL);
582 hRgnUpdate = ::CreateRectRgn(0, 0, 0, 0);
583 if (IsOcxCtrl) {
584 pps = reinterpret_cast<PAINTSTRUCT*>(wParam);
585 } else {
586 ::GetUpdateRgn(MainHWND(), hRgnUpdate, FALSE);
587 pps = &ps;
588 ::BeginPaint(MainHWND(), pps);
590 rcPaint = PRectangle(pps->rcPaint.left, pps->rcPaint.top, pps->rcPaint.right, pps->rcPaint.bottom);
591 PRectangle rcClient = GetClientRectangle();
592 paintingAllText = rcPaint.Contains(rcClient);
593 if (technology == SC_TECHNOLOGY_DEFAULT) {
594 AutoSurface surfaceWindow(pps->hdc, this);
595 if (surfaceWindow) {
596 Paint(surfaceWindow, rcPaint);
597 surfaceWindow->Release();
599 } else {
600 #if defined(USE_D2D)
601 for (int attempt=0;attempt<2;attempt++) {
602 EnsureRenderTarget();
603 AutoSurface surfaceWindow(pRenderTarget, this);
604 if (surfaceWindow) {
605 pRenderTarget->BeginDraw();
606 Paint(surfaceWindow, rcPaint);
607 surfaceWindow->Release();
608 HRESULT hr = pRenderTarget->EndDraw();
609 if (hr == D2DERR_RECREATE_TARGET) {
610 DropRenderTarget();
611 } else {
612 break;
616 #endif
618 if (hRgnUpdate) {
619 ::DeleteRgn(hRgnUpdate);
620 hRgnUpdate = 0;
623 if (!IsOcxCtrl)
624 ::EndPaint(MainHWND(), pps);
625 if (paintState == paintAbandoned) {
626 // Painting area was insufficient to cover new styling or brace highlight positions
627 FullPaint();
629 paintState = notPainting;
631 // Restore debug output state
632 Platform::ShowAssertionPopUps(assertsPopup);
634 //Platform::DebugPrintf("Paint took %g\n", et.Duration());
635 return 0l;
638 sptr_t ScintillaWin::HandleComposition(uptr_t wParam, sptr_t lParam) {
639 if (lParam & GCS_RESULTSTR) {
640 HIMC hIMC = ::ImmGetContext(MainHWND());
641 if (hIMC) {
642 const int maxLenInputIME = 200;
643 wchar_t wcs[maxLenInputIME];
644 LONG bytes = ::ImmGetCompositionStringW(hIMC,
645 GCS_RESULTSTR, wcs, (maxLenInputIME-1)*2);
646 int wides = bytes / 2;
647 if (IsUnicodeMode()) {
648 char utfval[maxLenInputIME * 3];
649 unsigned int len = UTF8Length(wcs, wides);
650 UTF8FromUTF16(wcs, wides, utfval, len);
651 utfval[len] = '\0';
652 AddCharUTF(utfval, len);
653 } else {
654 char dbcsval[maxLenInputIME * 2];
655 int size = ::WideCharToMultiByte(InputCodePage(),
656 0, wcs, wides, dbcsval, sizeof(dbcsval) - 1, 0, 0);
657 for (int i=0; i<size; i++) {
658 AddChar(dbcsval[i]);
661 // Set new position after converted
662 Point pos = PointMainCaret();
663 COMPOSITIONFORM CompForm;
664 CompForm.dwStyle = CFS_POINT;
665 CompForm.ptCurrentPos.x = pos.x;
666 CompForm.ptCurrentPos.y = pos.y;
667 ::ImmSetCompositionWindow(hIMC, &CompForm);
668 ::ImmReleaseContext(MainHWND(), hIMC);
670 return 0;
672 return ::DefWindowProc(MainHWND(), WM_IME_COMPOSITION, wParam, lParam);
675 // Translate message IDs from WM_* and EM_* to SCI_* so can partly emulate Windows Edit control
676 static unsigned int SciMessageFromEM(unsigned int iMessage) {
677 switch (iMessage) {
678 case EM_CANPASTE: return SCI_CANPASTE;
679 case EM_CANUNDO: return SCI_CANUNDO;
680 case EM_EMPTYUNDOBUFFER: return SCI_EMPTYUNDOBUFFER;
681 case EM_FINDTEXTEX: return SCI_FINDTEXT;
682 case EM_FORMATRANGE: return SCI_FORMATRANGE;
683 case EM_GETFIRSTVISIBLELINE: return SCI_GETFIRSTVISIBLELINE;
684 case EM_GETLINECOUNT: return SCI_GETLINECOUNT;
685 case EM_GETSELTEXT: return SCI_GETSELTEXT;
686 case EM_GETTEXTRANGE: return SCI_GETTEXTRANGE;
687 case EM_HIDESELECTION: return SCI_HIDESELECTION;
688 case EM_LINEINDEX: return SCI_POSITIONFROMLINE;
689 case EM_LINESCROLL: return SCI_LINESCROLL;
690 case EM_REPLACESEL: return SCI_REPLACESEL;
691 case EM_SCROLLCARET: return SCI_SCROLLCARET;
692 case EM_SETREADONLY: return SCI_SETREADONLY;
693 case WM_CLEAR: return SCI_CLEAR;
694 case WM_COPY: return SCI_COPY;
695 case WM_CUT: return SCI_CUT;
696 case WM_GETTEXT: return SCI_GETTEXT;
697 case WM_SETTEXT: return SCI_SETTEXT;
698 case WM_GETTEXTLENGTH: return SCI_GETTEXTLENGTH;
699 case WM_PASTE: return SCI_PASTE;
700 case WM_UNDO: return SCI_UNDO;
702 return iMessage;
705 UINT CodePageFromCharSet(DWORD characterSet, UINT documentCodePage) {
706 if (documentCodePage == SC_CP_UTF8) {
707 return SC_CP_UTF8;
709 switch (characterSet) {
710 case SC_CHARSET_ANSI: return 1252;
711 case SC_CHARSET_DEFAULT: return documentCodePage;
712 case SC_CHARSET_BALTIC: return 1257;
713 case SC_CHARSET_CHINESEBIG5: return 950;
714 case SC_CHARSET_EASTEUROPE: return 1250;
715 case SC_CHARSET_GB2312: return 936;
716 case SC_CHARSET_GREEK: return 1253;
717 case SC_CHARSET_HANGUL: return 949;
718 case SC_CHARSET_MAC: return 10000;
719 case SC_CHARSET_OEM: return 437;
720 case SC_CHARSET_RUSSIAN: return 1251;
721 case SC_CHARSET_SHIFTJIS: return 932;
722 case SC_CHARSET_TURKISH: return 1254;
723 case SC_CHARSET_JOHAB: return 1361;
724 case SC_CHARSET_HEBREW: return 1255;
725 case SC_CHARSET_ARABIC: return 1256;
726 case SC_CHARSET_VIETNAMESE: return 1258;
727 case SC_CHARSET_THAI: return 874;
728 case SC_CHARSET_8859_15: return 28605;
729 // Not supported
730 case SC_CHARSET_CYRILLIC: return documentCodePage;
731 case SC_CHARSET_SYMBOL: return documentCodePage;
733 return documentCodePage;
736 UINT ScintillaWin::CodePageOfDocument() {
737 return CodePageFromCharSet(vs.styles[STYLE_DEFAULT].characterSet, pdoc->dbcsCodePage);
740 sptr_t ScintillaWin::WndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
741 try {
742 //Platform::DebugPrintf("S M:%x WP:%x L:%x\n", iMessage, wParam, lParam);
743 iMessage = SciMessageFromEM(iMessage);
744 switch (iMessage) {
746 case WM_CREATE:
747 ctrlID = ::GetDlgCtrlID(reinterpret_cast<HWND>(wMain.GetID()));
748 // Get Intellimouse scroll line parameters
749 GetIntelliMouseParameters();
750 ::RegisterDragDrop(MainHWND(), reinterpret_cast<IDropTarget *>(&dt));
751 break;
753 case WM_COMMAND:
754 Command(LoWord(wParam));
755 break;
757 case WM_PAINT:
758 return WndPaint(wParam);
760 case WM_PRINTCLIENT: {
761 HDC hdc = reinterpret_cast<HDC>(wParam);
762 if (!IsCompatibleDC(hdc)) {
763 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
765 FullPaintDC(hdc);
767 break;
769 case WM_VSCROLL:
770 ScrollMessage(wParam);
771 break;
773 case WM_HSCROLL:
774 HorizontalScrollMessage(wParam);
775 break;
777 case WM_SIZE: {
778 #if defined(USE_D2D)
779 if (paintState == notPainting) {
780 DropRenderTarget();
781 } else {
782 renderTargetValid = false;
784 #endif
785 //Platform::DebugPrintf("Scintilla WM_SIZE %d %d\n", LoWord(lParam), HiWord(lParam));
786 ChangeSize();
788 break;
790 case WM_MOUSEWHEEL:
791 // if autocomplete list active then send mousewheel message to it
792 if (ac.Active()) {
793 HWND hWnd = reinterpret_cast<HWND>(ac.lb->GetID());
794 ::SendMessage(hWnd, iMessage, wParam, lParam);
795 break;
798 // Don't handle datazoom.
799 // (A good idea for datazoom would be to "fold" or "unfold" details.
800 // i.e. if datazoomed out only class structures are visible, when datazooming in the control
801 // structures appear, then eventually the individual statements...)
802 if (wParam & MK_SHIFT) {
803 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
806 // Either SCROLL or ZOOM. We handle the wheel steppings calculation
807 wheelDelta -= static_cast<short>(HiWord(wParam));
808 if (abs(wheelDelta) >= WHEEL_DELTA && linesPerScroll > 0) {
809 int linesToScroll = linesPerScroll;
810 if (linesPerScroll == WHEEL_PAGESCROLL)
811 linesToScroll = LinesOnScreen() - 1;
812 if (linesToScroll == 0) {
813 linesToScroll = 1;
815 linesToScroll *= (wheelDelta / WHEEL_DELTA);
816 if (wheelDelta >= 0)
817 wheelDelta = wheelDelta % WHEEL_DELTA;
818 else
819 wheelDelta = - (-wheelDelta % WHEEL_DELTA);
821 if (wParam & MK_CONTROL) {
822 // Zoom! We play with the font sizes in the styles.
823 // Number of steps/line is ignored, we just care if sizing up or down
824 if (linesToScroll < 0) {
825 KeyCommand(SCI_ZOOMIN);
826 } else {
827 KeyCommand(SCI_ZOOMOUT);
829 } else {
830 // Scroll
831 ScrollTo(topLine + linesToScroll);
834 return 0;
836 case WM_TIMER:
837 if (wParam == standardTimerID && timer.ticking) {
838 Tick();
839 } else if (wParam == idleTimerID && idler.state) {
840 SendMessage(MainHWND(), SC_WIN_IDLE, 0, 1);
841 } else {
842 return 1;
844 break;
846 case SC_WIN_IDLE:
847 // wParam=dwTickCountInitial, or 0 to initialize. lParam=bSkipUserInputTest
848 if (idler.state) {
849 if (lParam || (WAIT_TIMEOUT == MsgWaitForMultipleObjects(0, 0, 0, 0, QS_INPUT|QS_HOTKEY))) {
850 if (Idle()) {
851 // User input was given priority above, but all events do get a turn. Other
852 // messages, notifications, etc. will get interleaved with the idle messages.
854 // However, some things like WM_PAINT are a lower priority, and will not fire
855 // when there's a message posted. So, several times a second, we stop and let
856 // the low priority events have a turn (after which the timer will fire again).
858 DWORD dwCurrent = GetTickCount();
859 DWORD dwStart = wParam ? wParam : dwCurrent;
860 const DWORD maxWorkTime = 50;
862 if (dwCurrent >= dwStart && dwCurrent > maxWorkTime && dwCurrent - maxWorkTime < dwStart)
863 PostMessage(MainHWND(), SC_WIN_IDLE, dwStart, 0);
864 } else {
865 SetIdle(false);
869 break;
871 case WM_GETMINMAXINFO:
872 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
874 case WM_LBUTTONDOWN: {
875 // For IME, set the composition string as the result string.
876 HIMC hIMC = ::ImmGetContext(MainHWND());
877 ::ImmNotifyIME(hIMC, NI_COMPOSITIONSTR, CPS_COMPLETE, 0);
878 ::ImmReleaseContext(MainHWND(), hIMC);
880 //Platform::DebugPrintf("Buttdown %d %x %x %x %x %x\n",iMessage, wParam, lParam,
881 // Platform::IsKeyDown(VK_SHIFT),
882 // Platform::IsKeyDown(VK_CONTROL),
883 // Platform::IsKeyDown(VK_MENU));
884 ::SetFocus(MainHWND());
885 ButtonDown(Point::FromLong(lParam), ::GetMessageTime(),
886 (wParam & MK_SHIFT) != 0,
887 (wParam & MK_CONTROL) != 0,
888 Platform::IsKeyDown(VK_MENU));
890 break;
892 case WM_MOUSEMOVE:
893 SetTrackMouseLeaveEvent(true);
894 ButtonMove(Point::FromLong(lParam));
895 break;
897 case WM_MOUSELEAVE:
898 SetTrackMouseLeaveEvent(false);
899 MouseLeave();
900 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
902 case WM_LBUTTONUP:
903 ButtonUp(Point::FromLong(lParam),
904 ::GetMessageTime(),
905 (wParam & MK_CONTROL) != 0);
906 break;
908 case WM_RBUTTONDOWN:
909 ::SetFocus(MainHWND());
910 if (!PointInSelection(Point::FromLong(lParam))) {
911 CancelModes();
912 SetEmptySelection(PositionFromLocation(Point::FromLong(lParam)));
914 break;
916 case WM_SETCURSOR:
917 if (LoWord(lParam) == HTCLIENT) {
918 if (inDragDrop == ddDragging) {
919 DisplayCursor(Window::cursorUp);
920 } else {
921 // Display regular (drag) cursor over selection
922 POINT pt;
923 if (0 != ::GetCursorPos(&pt)) {
924 ::ScreenToClient(MainHWND(), &pt);
925 if (PointInSelMargin(Point(pt.x, pt.y))) {
926 DisplayCursor(GetMarginCursor(Point(pt.x, pt.y)));
927 } else if (PointInSelection(Point(pt.x, pt.y)) && !SelectionEmpty()) {
928 DisplayCursor(Window::cursorArrow);
929 } else if (PointIsHotspot(Point(pt.x, pt.y))) {
930 DisplayCursor(Window::cursorHand);
931 } else {
932 DisplayCursor(Window::cursorText);
936 return TRUE;
937 } else {
938 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
941 case WM_CHAR:
942 if (((wParam >= 128) || !iscntrl(wParam)) || !lastKeyDownConsumed) {
943 if (::IsWindowUnicode(MainHWND()) || keysAlwaysUnicode) {
944 wchar_t wcs[2] = {static_cast<wchar_t>(wParam), 0};
945 if (IsUnicodeMode()) {
946 // For a wide character version of the window:
947 char utfval[4];
948 unsigned int len = UTF8Length(wcs, 1);
949 UTF8FromUTF16(wcs, 1, utfval, len);
950 AddCharUTF(utfval, len);
951 } else {
952 UINT cpDest = CodePageOfDocument();
953 char inBufferCP[20];
954 int size = ::WideCharToMultiByte(cpDest,
955 0, wcs, 1, inBufferCP, sizeof(inBufferCP) - 1, 0, 0);
956 inBufferCP[size] = '\0';
957 AddCharUTF(inBufferCP, size);
959 } else {
960 if (IsUnicodeMode()) {
961 AddCharBytes('\0', LOBYTE(wParam));
962 } else {
963 AddChar(LOBYTE(wParam));
967 return 0;
969 case WM_UNICHAR:
970 if (wParam == UNICODE_NOCHAR) {
971 return IsUnicodeMode() ? 1 : 0;
972 } else if (lastKeyDownConsumed) {
973 return 1;
974 } else {
975 if (IsUnicodeMode()) {
976 char utfval[4];
977 wchar_t wcs[2] = {static_cast<wchar_t>(wParam), 0};
978 unsigned int len = UTF8Length(wcs, 1);
979 UTF8FromUTF16(wcs, 1, utfval, len);
980 AddCharUTF(utfval, len);
981 return 1;
982 } else {
983 return 0;
987 case WM_SYSKEYDOWN:
988 case WM_KEYDOWN: {
989 //Platform::DebugPrintf("S keydown %d %x %x %x %x\n",iMessage, wParam, lParam, ::IsKeyDown(VK_SHIFT), ::IsKeyDown(VK_CONTROL));
990 lastKeyDownConsumed = false;
991 int ret = KeyDown(KeyTranslate(wParam),
992 Platform::IsKeyDown(VK_SHIFT),
993 Platform::IsKeyDown(VK_CONTROL),
994 Platform::IsKeyDown(VK_MENU),
995 &lastKeyDownConsumed);
996 if (!ret && !lastKeyDownConsumed) {
997 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
999 break;
1002 case WM_IME_KEYDOWN:
1003 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1005 case WM_KEYUP:
1006 //Platform::DebugPrintf("S keyup %d %x %x\n",iMessage, wParam, lParam);
1007 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1009 case WM_SETTINGCHANGE:
1010 //Platform::DebugPrintf("Setting Changed\n");
1011 InvalidateStyleData();
1012 // Get Intellimouse scroll line parameters
1013 GetIntelliMouseParameters();
1014 break;
1016 case WM_GETDLGCODE:
1017 return DLGC_HASSETSEL | DLGC_WANTALLKEYS;
1019 case WM_KILLFOCUS: {
1020 HWND wOther = reinterpret_cast<HWND>(wParam);
1021 HWND wThis = MainHWND();
1022 HWND wCT = reinterpret_cast<HWND>(ct.wCallTip.GetID());
1023 if (!wParam ||
1024 !(::IsChild(wThis, wOther) || (wOther == wCT))) {
1025 SetFocusState(false);
1026 DestroySystemCaret();
1029 break;
1031 case WM_SETFOCUS:
1032 SetFocusState(true);
1033 DestroySystemCaret();
1034 CreateSystemCaret();
1035 break;
1037 case WM_SYSCOLORCHANGE:
1038 //Platform::DebugPrintf("Setting Changed\n");
1039 InvalidateStyleData();
1040 break;
1042 case WM_IME_STARTCOMPOSITION: // dbcs
1043 ImeStartComposition();
1044 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1046 case WM_IME_ENDCOMPOSITION: // dbcs
1047 ImeEndComposition();
1048 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1050 case WM_IME_COMPOSITION:
1051 return HandleComposition(wParam, lParam);
1053 case WM_IME_CHAR: {
1054 AddCharBytes(HIBYTE(wParam), LOBYTE(wParam));
1055 return 0;
1058 case WM_CONTEXTMENU:
1059 if (displayPopupMenu) {
1060 Point pt = Point::FromLong(lParam);
1061 if ((pt.x == -1) && (pt.y == -1)) {
1062 // Caused by keyboard so display menu near caret
1063 pt = PointMainCaret();
1064 POINT spt = {static_cast<int>(pt.x), static_cast<int>(pt.y)};
1065 ::ClientToScreen(MainHWND(), &spt);
1066 pt = Point(spt.x, spt.y);
1068 ContextMenu(pt);
1069 return 0;
1071 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1073 case WM_INPUTLANGCHANGE:
1074 //::SetThreadLocale(LOWORD(lParam));
1075 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1077 case WM_INPUTLANGCHANGEREQUEST:
1078 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1080 case WM_ERASEBKGND:
1081 return 1; // Avoid any background erasure as whole window painted.
1083 case WM_CAPTURECHANGED:
1084 capturedMouse = false;
1085 return 0;
1087 // These are not handled in Scintilla and its faster to dispatch them here.
1088 // Also moves time out to here so profile doesn't count lots of empty message calls.
1090 case WM_MOVE:
1091 case WM_MOUSEACTIVATE:
1092 case WM_NCHITTEST:
1093 case WM_NCCALCSIZE:
1094 case WM_NCPAINT:
1095 case WM_NCMOUSEMOVE:
1096 case WM_NCLBUTTONDOWN:
1097 case WM_IME_SETCONTEXT:
1098 case WM_IME_NOTIFY:
1099 case WM_SYSCOMMAND:
1100 case WM_WINDOWPOSCHANGING:
1101 case WM_WINDOWPOSCHANGED:
1102 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1104 case EM_LINEFROMCHAR:
1105 if (static_cast<int>(wParam) < 0) {
1106 wParam = SelectionStart().Position();
1108 return pdoc->LineFromPosition(wParam);
1110 case EM_EXLINEFROMCHAR:
1111 return pdoc->LineFromPosition(lParam);
1113 case EM_GETSEL:
1114 if (wParam) {
1115 *reinterpret_cast<int *>(wParam) = SelectionStart().Position();
1117 if (lParam) {
1118 *reinterpret_cast<int *>(lParam) = SelectionEnd().Position();
1120 return MAKELONG(SelectionStart().Position(), SelectionEnd().Position());
1122 case EM_EXGETSEL: {
1123 if (lParam == 0) {
1124 return 0;
1126 Sci_CharacterRange *pCR = reinterpret_cast<Sci_CharacterRange *>(lParam);
1127 pCR->cpMin = SelectionStart().Position();
1128 pCR->cpMax = SelectionEnd().Position();
1130 break;
1132 case EM_SETSEL: {
1133 int nStart = static_cast<int>(wParam);
1134 int nEnd = static_cast<int>(lParam);
1135 if (nStart == 0 && nEnd == -1) {
1136 nEnd = pdoc->Length();
1138 if (nStart == -1) {
1139 nStart = nEnd; // Remove selection
1141 if (nStart > nEnd) {
1142 SetSelection(nEnd, nStart);
1143 } else {
1144 SetSelection(nStart, nEnd);
1146 EnsureCaretVisible();
1148 break;
1150 case EM_EXSETSEL: {
1151 if (lParam == 0) {
1152 return 0;
1154 Sci_CharacterRange *pCR = reinterpret_cast<Sci_CharacterRange *>(lParam);
1155 sel.selType = Selection::selStream;
1156 if (pCR->cpMin == 0 && pCR->cpMax == -1) {
1157 SetSelection(pCR->cpMin, pdoc->Length());
1158 } else {
1159 SetSelection(pCR->cpMin, pCR->cpMax);
1161 EnsureCaretVisible();
1162 return pdoc->LineFromPosition(SelectionStart().Position());
1165 case SCI_GETDIRECTFUNCTION:
1166 return reinterpret_cast<sptr_t>(DirectFunction);
1168 case SCI_GETDIRECTPOINTER:
1169 return reinterpret_cast<sptr_t>(this);
1171 case SCI_GRABFOCUS:
1172 ::SetFocus(MainHWND());
1173 break;
1175 case SCI_SETKEYSUNICODE:
1176 keysAlwaysUnicode = wParam != 0;
1177 break;
1179 case SCI_GETKEYSUNICODE:
1180 return keysAlwaysUnicode;
1182 case SCI_SETTECHNOLOGY:
1183 if ((wParam == SC_TECHNOLOGY_DEFAULT) || (wParam == SC_TECHNOLOGY_DIRECTWRITE)) {
1184 if (technology != static_cast<int>(wParam)) {
1185 if (static_cast<int>(wParam) == SC_TECHNOLOGY_DIRECTWRITE) {
1186 #if defined(USE_D2D)
1187 if (!LoadD2D())
1188 // Failed to load Direct2D or DirectWrite so no effect
1189 return 0;
1190 #else
1191 return 0;
1192 #endif
1194 technology = wParam;
1195 // Invalidate all cached information including layout.
1196 DropGraphics(true);
1197 InvalidateStyleRedraw();
1200 break;
1202 #ifdef SCI_LEXER
1203 case SCI_LOADLEXERLIBRARY:
1204 LexerManager::GetInstance()->Load(reinterpret_cast<const char *>(lParam));
1205 break;
1206 #endif
1208 default:
1209 return ScintillaBase::WndProc(iMessage, wParam, lParam);
1211 } catch (std::bad_alloc &) {
1212 errorStatus = SC_STATUS_BADALLOC;
1213 } catch (...) {
1214 errorStatus = SC_STATUS_FAILURE;
1216 return 0l;
1219 bool ScintillaWin::ValidCodePage(int codePage) const {
1220 return codePage == 0 || codePage == SC_CP_UTF8 ||
1221 codePage == 932 || codePage == 936 || codePage == 949 ||
1222 codePage == 950 || codePage == 1361;
1225 sptr_t ScintillaWin::DefWndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
1226 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1229 void ScintillaWin::SetTicking(bool on) {
1230 if (timer.ticking != on) {
1231 timer.ticking = on;
1232 if (timer.ticking) {
1233 timer.tickerID = ::SetTimer(MainHWND(), standardTimerID, timer.tickSize, NULL)
1234 ? reinterpret_cast<TickerID>(standardTimerID) : 0;
1235 } else {
1236 ::KillTimer(MainHWND(), reinterpret_cast<uptr_t>(timer.tickerID));
1237 timer.tickerID = 0;
1240 timer.ticksToWait = caret.period;
1243 bool ScintillaWin::SetIdle(bool on) {
1244 // On Win32 the Idler is implemented as a Timer on the Scintilla window. This
1245 // takes advantage of the fact that WM_TIMER messages are very low priority,
1246 // and are only posted when the message queue is empty, i.e. during idle time.
1247 if (idler.state != on) {
1248 if (on) {
1249 idler.idlerID = ::SetTimer(MainHWND(), idleTimerID, 10, NULL)
1250 ? reinterpret_cast<IdlerID>(idleTimerID) : 0;
1251 } else {
1252 ::KillTimer(MainHWND(), reinterpret_cast<uptr_t>(idler.idlerID));
1253 idler.idlerID = 0;
1255 idler.state = idler.idlerID != 0;
1257 return idler.state;
1260 void ScintillaWin::SetMouseCapture(bool on) {
1261 if (mouseDownCaptures) {
1262 if (on) {
1263 ::SetCapture(MainHWND());
1264 } else {
1265 ::ReleaseCapture();
1268 capturedMouse = on;
1271 bool ScintillaWin::HaveMouseCapture() {
1272 // Cannot just see if GetCapture is this window as the scroll bar also sets capture for the window
1273 return capturedMouse;
1274 //return capturedMouse && (::GetCapture() == MainHWND());
1277 void ScintillaWin::SetTrackMouseLeaveEvent(bool on) {
1278 if (on && TrackMouseEventFn && !trackedMouseLeave) {
1279 TRACKMOUSEEVENT tme;
1280 tme.cbSize = sizeof(tme);
1281 tme.dwFlags = TME_LEAVE;
1282 tme.hwndTrack = MainHWND();
1283 TrackMouseEventFn(&tme);
1285 trackedMouseLeave = on;
1288 bool ScintillaWin::PaintContains(PRectangle rc) {
1289 bool contains = true;
1290 if ((paintState == painting) && (!rc.Empty())) {
1291 if (!rcPaint.Contains(rc)) {
1292 contains = false;
1293 } else {
1294 // In bounding rectangle so check more accurately using region
1295 HRGN hRgnRange = ::CreateRectRgn(rc.left, rc.top, rc.right, rc.bottom);
1296 if (hRgnRange) {
1297 HRGN hRgnDest = ::CreateRectRgn(0, 0, 0, 0);
1298 if (hRgnDest) {
1299 int combination = ::CombineRgn(hRgnDest, hRgnRange, hRgnUpdate, RGN_DIFF);
1300 if (combination != NULLREGION) {
1301 contains = false;
1303 ::DeleteRgn(hRgnDest);
1305 ::DeleteRgn(hRgnRange);
1309 return contains;
1312 void ScintillaWin::ScrollText(int /* linesToMove */) {
1313 //Platform::DebugPrintf("ScintillaWin::ScrollText %d\n", linesToMove);
1314 //::ScrollWindow(MainHWND(), 0,
1315 // vs.lineHeight * linesToMove, 0, 0);
1316 //::UpdateWindow(MainHWND());
1317 Redraw();
1320 void ScintillaWin::UpdateSystemCaret() {
1321 if (hasFocus) {
1322 if (HasCaretSizeChanged()) {
1323 DestroySystemCaret();
1324 CreateSystemCaret();
1326 Point pos = PointMainCaret();
1327 ::SetCaretPos(pos.x, pos.y);
1331 int ScintillaWin::SetScrollInfo(int nBar, LPCSCROLLINFO lpsi, BOOL bRedraw) {
1332 return ::SetScrollInfo(MainHWND(), nBar, lpsi, bRedraw);
1335 bool ScintillaWin::GetScrollInfo(int nBar, LPSCROLLINFO lpsi) {
1336 return ::GetScrollInfo(MainHWND(), nBar, lpsi) ? true : false;
1339 // Change the scroll position but avoid repaint if changing to same value
1340 void ScintillaWin::ChangeScrollPos(int barType, int pos) {
1341 SCROLLINFO sci = {
1342 sizeof(sci), 0, 0, 0, 0, 0, 0
1344 sci.fMask = SIF_POS;
1345 GetScrollInfo(barType, &sci);
1346 if (sci.nPos != pos) {
1347 DwellEnd(true);
1348 sci.nPos = pos;
1349 SetScrollInfo(barType, &sci, TRUE);
1353 void ScintillaWin::SetVerticalScrollPos() {
1354 ChangeScrollPos(SB_VERT, topLine);
1357 void ScintillaWin::SetHorizontalScrollPos() {
1358 ChangeScrollPos(SB_HORZ, xOffset);
1361 bool ScintillaWin::ModifyScrollBars(int nMax, int nPage) {
1362 bool modified = false;
1363 SCROLLINFO sci = {
1364 sizeof(sci), 0, 0, 0, 0, 0, 0
1366 sci.fMask = SIF_PAGE | SIF_RANGE;
1367 GetScrollInfo(SB_VERT, &sci);
1368 int vertEndPreferred = nMax;
1369 if (!verticalScrollBarVisible)
1370 nPage = vertEndPreferred + 1;
1371 if ((sci.nMin != 0) ||
1372 (sci.nMax != vertEndPreferred) ||
1373 (sci.nPage != static_cast<unsigned int>(nPage)) ||
1374 (sci.nPos != 0)) {
1375 sci.fMask = SIF_PAGE | SIF_RANGE;
1376 sci.nMin = 0;
1377 sci.nMax = vertEndPreferred;
1378 sci.nPage = nPage;
1379 sci.nPos = 0;
1380 sci.nTrackPos = 1;
1381 SetScrollInfo(SB_VERT, &sci, TRUE);
1382 modified = true;
1385 PRectangle rcText = GetTextRectangle();
1386 int horizEndPreferred = scrollWidth;
1387 if (horizEndPreferred < 0)
1388 horizEndPreferred = 0;
1389 unsigned int pageWidth = rcText.Width();
1390 if (!horizontalScrollBarVisible || (wrapState != eWrapNone))
1391 pageWidth = horizEndPreferred + 1;
1392 sci.fMask = SIF_PAGE | SIF_RANGE;
1393 GetScrollInfo(SB_HORZ, &sci);
1394 if ((sci.nMin != 0) ||
1395 (sci.nMax != horizEndPreferred) ||
1396 (sci.nPage != pageWidth) ||
1397 (sci.nPos != 0)) {
1398 sci.fMask = SIF_PAGE | SIF_RANGE;
1399 sci.nMin = 0;
1400 sci.nMax = horizEndPreferred;
1401 sci.nPage = pageWidth;
1402 sci.nPos = 0;
1403 sci.nTrackPos = 1;
1404 SetScrollInfo(SB_HORZ, &sci, TRUE);
1405 modified = true;
1406 if (scrollWidth < static_cast<int>(pageWidth)) {
1407 HorizontalScrollTo(0);
1410 return modified;
1413 void ScintillaWin::NotifyChange() {
1414 ::SendMessage(::GetParent(MainHWND()), WM_COMMAND,
1415 MAKELONG(GetCtrlID(), SCEN_CHANGE),
1416 reinterpret_cast<LPARAM>(MainHWND()));
1419 void ScintillaWin::NotifyFocus(bool focus) {
1420 ::SendMessage(::GetParent(MainHWND()), WM_COMMAND,
1421 MAKELONG(GetCtrlID(), focus ? SCEN_SETFOCUS : SCEN_KILLFOCUS),
1422 reinterpret_cast<LPARAM>(MainHWND()));
1425 void ScintillaWin::SetCtrlID(int identifier) {
1426 ::SetWindowID(reinterpret_cast<HWND>(wMain.GetID()), identifier);
1429 int ScintillaWin::GetCtrlID() {
1430 return ::GetDlgCtrlID(reinterpret_cast<HWND>(wMain.GetID()));
1433 void ScintillaWin::NotifyParent(SCNotification scn) {
1434 scn.nmhdr.hwndFrom = MainHWND();
1435 scn.nmhdr.idFrom = GetCtrlID();
1436 ::SendMessage(::GetParent(MainHWND()), WM_NOTIFY,
1437 GetCtrlID(), reinterpret_cast<LPARAM>(&scn));
1440 void ScintillaWin::NotifyParent(SCNotification * scn) {
1441 scn->nmhdr.hwndFrom = MainHWND();
1442 scn->nmhdr.idFrom = GetCtrlID();
1443 ::SendMessage(::GetParent(MainHWND()), WM_NOTIFY,
1444 GetCtrlID(), reinterpret_cast<LPARAM>(scn));
1447 void ScintillaWin::NotifyDoubleClick(Point pt, bool shift, bool ctrl, bool alt) {
1448 //Platform::DebugPrintf("ScintillaWin Double click 0\n");
1449 ScintillaBase::NotifyDoubleClick(pt, shift, ctrl, alt);
1450 // Send myself a WM_LBUTTONDBLCLK, so the container can handle it too.
1451 ::SendMessage(MainHWND(),
1452 WM_LBUTTONDBLCLK,
1453 shift ? MK_SHIFT : 0,
1454 MAKELPARAM(pt.x, pt.y));
1457 class CaseFolderDBCS : public CaseFolderTable {
1458 // Allocate the expandable storage here so that it does not need to be reallocated
1459 // for each call to Fold.
1460 std::vector<wchar_t> utf16Mixed;
1461 std::vector<wchar_t> utf16Folded;
1462 UINT cp;
1463 public:
1464 CaseFolderDBCS(UINT cp_) : cp(cp_) {
1465 StandardASCII();
1467 virtual size_t Fold(char *folded, size_t sizeFolded, const char *mixed, size_t lenMixed) {
1468 if ((lenMixed == 1) && (sizeFolded > 0)) {
1469 folded[0] = mapping[static_cast<unsigned char>(mixed[0])];
1470 return 1;
1471 } else {
1472 if (lenMixed > utf16Mixed.size()) {
1473 utf16Mixed.resize(lenMixed + 8);
1475 size_t nUtf16Mixed = ::MultiByteToWideChar(cp, 0, mixed,
1476 static_cast<int>(lenMixed),
1477 &utf16Mixed[0],
1478 static_cast<int>(utf16Mixed.size()));
1480 if (nUtf16Mixed == 0) {
1481 // Failed to convert -> bad input
1482 folded[0] = '\0';
1483 return 1;
1486 unsigned int lenFlat = 0;
1487 for (size_t mixIndex=0; mixIndex < nUtf16Mixed; mixIndex++) {
1488 if ((lenFlat + 20) > utf16Folded.size())
1489 utf16Folded.resize(lenFlat + 60);
1490 const char *foldedUTF8 = CaseConvert(utf16Mixed[mixIndex], CaseConversionFold);
1491 if (foldedUTF8) {
1492 // Maximum length of a case conversion is 6 bytes, 3 characters
1493 wchar_t wFolded[20];
1494 unsigned int charsConverted = UTF16FromUTF8(foldedUTF8,
1495 static_cast<unsigned int>(strlen(foldedUTF8)),
1496 wFolded, sizeof(wFolded)/sizeof(wFolded[0]));
1497 for (size_t j=0;j<charsConverted;j++)
1498 utf16Folded[lenFlat++] = wFolded[j];
1499 } else {
1500 utf16Folded[lenFlat++] = utf16Mixed[mixIndex];
1504 size_t lenOut = ::WideCharToMultiByte(cp, 0,
1505 &utf16Folded[0], lenFlat,
1506 NULL, 0, NULL, 0);
1508 if (lenOut < sizeFolded) {
1509 ::WideCharToMultiByte(cp, 0,
1510 &utf16Folded[0], lenFlat,
1511 folded, static_cast<int>(lenOut), NULL, 0);
1512 return lenOut;
1513 } else {
1514 return 0;
1520 CaseFolder *ScintillaWin::CaseFolderForEncoding() {
1521 UINT cpDest = CodePageOfDocument();
1522 if (cpDest == SC_CP_UTF8) {
1523 return new CaseFolderUnicode();
1524 } else {
1525 if (pdoc->dbcsCodePage == 0) {
1526 CaseFolderTable *pcf = new CaseFolderTable();
1527 pcf->StandardASCII();
1528 // Only for single byte encodings
1529 UINT cpDoc = CodePageOfDocument();
1530 for (int i=0x80; i<0x100; i++) {
1531 char sCharacter[2] = "A";
1532 sCharacter[0] = static_cast<char>(i);
1533 wchar_t wCharacter[20];
1534 unsigned int lengthUTF16 = ::MultiByteToWideChar(cpDoc, 0, sCharacter, 1,
1535 wCharacter, sizeof(wCharacter)/sizeof(wCharacter[0]));
1536 if (lengthUTF16 == 1) {
1537 const char *caseFolded = CaseConvert(wCharacter[0], CaseConversionFold);
1538 if (caseFolded) {
1539 wchar_t wLower[20];
1540 unsigned int charsConverted = UTF16FromUTF8(caseFolded,
1541 static_cast<unsigned int>(strlen(caseFolded)),
1542 wLower, sizeof(wLower)/sizeof(wLower[0]));
1543 if (charsConverted == 1) {
1544 char sCharacterLowered[20];
1545 unsigned int lengthConverted = ::WideCharToMultiByte(cpDoc, 0,
1546 wLower, charsConverted,
1547 sCharacterLowered, sizeof(sCharacterLowered), NULL, 0);
1548 if ((lengthConverted == 1) && (sCharacter[0] != sCharacterLowered[0])) {
1549 pcf->SetTranslation(sCharacter[0], sCharacterLowered[0]);
1555 return pcf;
1556 } else {
1557 return new CaseFolderDBCS(cpDest);
1562 std::string ScintillaWin::CaseMapString(const std::string &s, int caseMapping) {
1563 if ((s.size() == 0) || (caseMapping == cmSame))
1564 return s;
1566 UINT cpDoc = CodePageOfDocument();
1567 if (cpDoc == SC_CP_UTF8) {
1568 std::string retMapped(s.length() * maxExpansionCaseConversion, 0);
1569 size_t lenMapped = CaseConvertString(&retMapped[0], retMapped.length(), s.c_str(), s.length(),
1570 (caseMapping == cmUpper) ? CaseConversionUpper : CaseConversionLower);
1571 retMapped.resize(lenMapped);
1572 return retMapped;
1575 unsigned int lengthUTF16 = ::MultiByteToWideChar(cpDoc, 0, s.c_str(),
1576 static_cast<int>(s.size()), NULL, 0);
1577 if (lengthUTF16 == 0) // Failed to convert
1578 return s;
1580 DWORD mapFlags = LCMAP_LINGUISTIC_CASING |
1581 ((caseMapping == cmUpper) ? LCMAP_UPPERCASE : LCMAP_LOWERCASE);
1583 // Change text to UTF-16
1584 std::vector<wchar_t> vwcText(lengthUTF16);
1585 ::MultiByteToWideChar(cpDoc, 0, s.c_str(), static_cast<int>(s.size()), &vwcText[0], lengthUTF16);
1587 // Change case
1588 int charsConverted = ::LCMapStringW(LOCALE_SYSTEM_DEFAULT, mapFlags,
1589 &vwcText[0], lengthUTF16, NULL, 0);
1590 std::vector<wchar_t> vwcConverted(charsConverted);
1591 ::LCMapStringW(LOCALE_SYSTEM_DEFAULT, mapFlags,
1592 &vwcText[0], lengthUTF16, &vwcConverted[0], charsConverted);
1594 // Change back to document encoding
1595 unsigned int lengthConverted = ::WideCharToMultiByte(cpDoc, 0,
1596 &vwcConverted[0], static_cast<int>(vwcConverted.size()),
1597 NULL, 0, NULL, 0);
1598 std::vector<char> vcConverted(lengthConverted);
1599 ::WideCharToMultiByte(cpDoc, 0,
1600 &vwcConverted[0], static_cast<int>(vwcConverted.size()),
1601 &vcConverted[0], static_cast<int>(vcConverted.size()), NULL, 0);
1603 return std::string(&vcConverted[0], vcConverted.size());
1606 void ScintillaWin::Copy() {
1607 //Platform::DebugPrintf("Copy\n");
1608 if (!sel.Empty()) {
1609 SelectionText selectedText;
1610 CopySelectionRange(&selectedText);
1611 CopyToClipboard(selectedText);
1615 void ScintillaWin::CopyAllowLine() {
1616 SelectionText selectedText;
1617 CopySelectionRange(&selectedText, true);
1618 CopyToClipboard(selectedText);
1621 bool ScintillaWin::CanPaste() {
1622 if (!Editor::CanPaste())
1623 return false;
1624 if (::IsClipboardFormatAvailable(CF_TEXT))
1625 return true;
1626 if (IsUnicodeMode())
1627 return ::IsClipboardFormatAvailable(CF_UNICODETEXT) != 0;
1628 return false;
1631 class GlobalMemory {
1632 HGLOBAL hand;
1633 public:
1634 void *ptr;
1635 GlobalMemory() : hand(0), ptr(0) {
1637 GlobalMemory(HGLOBAL hand_) : hand(hand_), ptr(0) {
1638 if (hand) {
1639 ptr = ::GlobalLock(hand);
1642 ~GlobalMemory() {
1643 PLATFORM_ASSERT(!ptr);
1645 void Allocate(size_t bytes) {
1646 hand = ::GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT, bytes);
1647 if (hand) {
1648 ptr = ::GlobalLock(hand);
1651 HGLOBAL Unlock() {
1652 PLATFORM_ASSERT(ptr);
1653 HGLOBAL handCopy = hand;
1654 ::GlobalUnlock(hand);
1655 ptr = 0;
1656 hand = 0;
1657 return handCopy;
1659 void SetClip(UINT uFormat) {
1660 ::SetClipboardData(uFormat, Unlock());
1662 operator bool() const {
1663 return ptr != 0;
1665 SIZE_T Size() {
1666 return ::GlobalSize(hand);
1670 void ScintillaWin::InsertPasteText(const char *text, int len, SelectionPosition selStart, bool isRectangular, bool isLine) {
1671 if (isRectangular) {
1672 PasteRectangular(selStart, text, len);
1673 } else {
1674 std::string convertedText;
1675 if (convertPastes) {
1676 // Convert line endings of the paste into our local line-endings mode
1677 convertedText = Document::TransformLineEnds(text, len, pdoc->eolMode);
1678 len = static_cast<int>(convertedText.length());
1679 text = convertedText.c_str();
1681 if (isLine) {
1682 int insertPos = pdoc->LineStart(pdoc->LineFromPosition(sel.MainCaret()));
1683 pdoc->InsertString(insertPos, text, len);
1684 // add the newline if necessary
1685 if ((len > 0) && (text[len-1] != '\n' && text[len-1] != '\r')) {
1686 const char *endline = StringFromEOLMode(pdoc->eolMode);
1687 pdoc->InsertString(insertPos + len, endline, static_cast<int>(strlen(endline)));
1688 len += static_cast<int>(strlen(endline));
1690 if (sel.MainCaret() == insertPos) {
1691 SetEmptySelection(sel.MainCaret() + len);
1693 } else {
1694 InsertPaste(selStart, text, len);
1699 void ScintillaWin::Paste() {
1700 if (!::OpenClipboard(MainHWND()))
1701 return;
1702 UndoGroup ug(pdoc);
1703 bool isLine = SelectionEmpty() && (::IsClipboardFormatAvailable(cfLineSelect) != 0);
1704 ClearSelection(multiPasteMode == SC_MULTIPASTE_EACH);
1705 SelectionPosition selStart = sel.IsRectangular() ?
1706 sel.Rectangular().Start() :
1707 sel.Range(sel.Main()).Start();
1708 bool isRectangular = ::IsClipboardFormatAvailable(cfColumnSelect) != 0;
1710 // Always use CF_UNICODETEXT if available
1711 GlobalMemory memUSelection(::GetClipboardData(CF_UNICODETEXT));
1712 if (memUSelection) {
1713 wchar_t *uptr = static_cast<wchar_t *>(memUSelection.ptr);
1714 if (uptr) {
1715 unsigned int len;
1716 std::vector<char> putf;
1717 // Default Scintilla behaviour in Unicode mode
1718 if (IsUnicodeMode()) {
1719 unsigned int bytes = memUSelection.Size();
1720 len = UTF8Length(uptr, bytes / 2);
1721 putf.resize(len + 1);
1722 UTF8FromUTF16(uptr, bytes / 2, &putf[0], len);
1723 } else {
1724 // CF_UNICODETEXT available, but not in Unicode mode
1725 // Convert from Unicode to current Scintilla code page
1726 UINT cpDest = CodePageOfDocument();
1727 len = ::WideCharToMultiByte(cpDest, 0, uptr, -1,
1728 NULL, 0, NULL, NULL) - 1; // subtract 0 terminator
1729 putf.resize(len + 1);
1730 ::WideCharToMultiByte(cpDest, 0, uptr, -1,
1731 &putf[0], len + 1, NULL, NULL);
1734 InsertPasteText(&putf[0], len, selStart, isRectangular, isLine);
1736 memUSelection.Unlock();
1737 } else {
1738 // CF_UNICODETEXT not available, paste ANSI text
1739 GlobalMemory memSelection(::GetClipboardData(CF_TEXT));
1740 if (memSelection) {
1741 char *ptr = static_cast<char *>(memSelection.ptr);
1742 if (ptr) {
1743 unsigned int bytes = memSelection.Size();
1744 unsigned int len = bytes;
1745 for (unsigned int i = 0; i < bytes; i++) {
1746 if ((len == bytes) && (0 == ptr[i]))
1747 len = i;
1750 // In Unicode mode, convert clipboard text to UTF-8
1751 if (IsUnicodeMode()) {
1752 std::vector<wchar_t> uptr(len+1);
1754 unsigned int ulen = ::MultiByteToWideChar(CP_ACP, 0,
1755 ptr, len, &uptr[0], len+1);
1757 unsigned int mlen = UTF8Length(&uptr[0], ulen);
1758 std::vector<char> putf(mlen+1);
1759 // CP_UTF8 not available on Windows 95, so use UTF8FromUTF16()
1760 UTF8FromUTF16(&uptr[0], ulen, &putf[0], mlen);
1762 InsertPasteText(&putf[0], mlen, selStart, isRectangular, isLine);
1763 } else {
1764 InsertPasteText(ptr, len, selStart, isRectangular, isLine);
1767 memSelection.Unlock();
1770 ::CloseClipboard();
1771 Redraw();
1774 void ScintillaWin::CreateCallTipWindow(PRectangle) {
1775 if (!ct.wCallTip.Created()) {
1776 ct.wCallTip = ::CreateWindow(callClassName, TEXT("ACallTip"),
1777 WS_POPUP, 100, 100, 150, 20,
1778 MainHWND(), 0,
1779 GetWindowInstance(MainHWND()),
1780 this);
1781 ct.wDraw = ct.wCallTip;
1785 void ScintillaWin::AddToPopUp(const char *label, int cmd, bool enabled) {
1786 HMENU hmenuPopup = reinterpret_cast<HMENU>(popup.GetID());
1787 if (!label[0])
1788 ::AppendMenuA(hmenuPopup, MF_SEPARATOR, 0, "");
1789 else if (enabled)
1790 ::AppendMenuA(hmenuPopup, MF_STRING, cmd, label);
1791 else
1792 ::AppendMenuA(hmenuPopup, MF_STRING | MF_DISABLED | MF_GRAYED, cmd, label);
1795 void ScintillaWin::ClaimSelection() {
1796 // Windows does not have a primary selection
1799 /// Implement IUnknown
1801 STDMETHODIMP_(ULONG)FormatEnumerator_AddRef(FormatEnumerator *fe);
1802 STDMETHODIMP FormatEnumerator_QueryInterface(FormatEnumerator *fe, REFIID riid, PVOID *ppv) {
1803 //Platform::DebugPrintf("EFE QI");
1804 *ppv = NULL;
1805 if (riid == IID_IUnknown)
1806 *ppv = reinterpret_cast<IEnumFORMATETC *>(fe);
1807 if (riid == IID_IEnumFORMATETC)
1808 *ppv = reinterpret_cast<IEnumFORMATETC *>(fe);
1809 if (!*ppv)
1810 return E_NOINTERFACE;
1811 FormatEnumerator_AddRef(fe);
1812 return S_OK;
1814 STDMETHODIMP_(ULONG)FormatEnumerator_AddRef(FormatEnumerator *fe) {
1815 return ++fe->ref;
1817 STDMETHODIMP_(ULONG)FormatEnumerator_Release(FormatEnumerator *fe) {
1818 fe->ref--;
1819 if (fe->ref > 0)
1820 return fe->ref;
1821 delete fe;
1822 return 0;
1824 /// Implement IEnumFORMATETC
1825 STDMETHODIMP FormatEnumerator_Next(FormatEnumerator *fe, ULONG celt, FORMATETC *rgelt, ULONG *pceltFetched) {
1826 //Platform::DebugPrintf("EFE Next %d %d", fe->pos, celt);
1827 if (rgelt == NULL) return E_POINTER;
1828 // We only support one format, so this is simple.
1829 unsigned int putPos = 0;
1830 while ((fe->pos < fe->formatsLen) && (putPos < celt)) {
1831 rgelt->cfFormat = fe->formats[fe->pos];
1832 rgelt->ptd = 0;
1833 rgelt->dwAspect = DVASPECT_CONTENT;
1834 rgelt->lindex = -1;
1835 rgelt->tymed = TYMED_HGLOBAL;
1836 fe->pos++;
1837 putPos++;
1839 if (pceltFetched)
1840 *pceltFetched = putPos;
1841 return putPos ? S_OK : S_FALSE;
1843 STDMETHODIMP FormatEnumerator_Skip(FormatEnumerator *fe, ULONG celt) {
1844 fe->pos += celt;
1845 return S_OK;
1847 STDMETHODIMP FormatEnumerator_Reset(FormatEnumerator *fe) {
1848 fe->pos = 0;
1849 return S_OK;
1851 STDMETHODIMP FormatEnumerator_Clone(FormatEnumerator *fe, IEnumFORMATETC **ppenum) {
1852 FormatEnumerator *pfe;
1853 try {
1854 pfe = new FormatEnumerator(fe->pos, fe->formats, fe->formatsLen);
1855 } catch (...) {
1856 return E_OUTOFMEMORY;
1858 return FormatEnumerator_QueryInterface(pfe, IID_IEnumFORMATETC,
1859 reinterpret_cast<void **>(ppenum));
1862 static VFunction *vtFormatEnumerator[] = {
1863 (VFunction *)(FormatEnumerator_QueryInterface),
1864 (VFunction *)(FormatEnumerator_AddRef),
1865 (VFunction *)(FormatEnumerator_Release),
1866 (VFunction *)(FormatEnumerator_Next),
1867 (VFunction *)(FormatEnumerator_Skip),
1868 (VFunction *)(FormatEnumerator_Reset),
1869 (VFunction *)(FormatEnumerator_Clone)
1872 FormatEnumerator::FormatEnumerator(int pos_, CLIPFORMAT formats_[], int formatsLen_) {
1873 vtbl = vtFormatEnumerator;
1874 ref = 0; // First QI adds first reference...
1875 pos = pos_;
1876 formatsLen = formatsLen_;
1877 for (int i=0; i<formatsLen; i++)
1878 formats[i] = formats_[i];
1881 /// Implement IUnknown
1882 STDMETHODIMP DropSource_QueryInterface(DropSource *ds, REFIID riid, PVOID *ppv) {
1883 return ds->sci->QueryInterface(riid, ppv);
1885 STDMETHODIMP_(ULONG)DropSource_AddRef(DropSource *ds) {
1886 return ds->sci->AddRef();
1888 STDMETHODIMP_(ULONG)DropSource_Release(DropSource *ds) {
1889 return ds->sci->Release();
1892 /// Implement IDropSource
1893 STDMETHODIMP DropSource_QueryContinueDrag(DropSource *, BOOL fEsc, DWORD grfKeyState) {
1894 if (fEsc)
1895 return DRAGDROP_S_CANCEL;
1896 if (!(grfKeyState & MK_LBUTTON))
1897 return DRAGDROP_S_DROP;
1898 return S_OK;
1901 STDMETHODIMP DropSource_GiveFeedback(DropSource *, DWORD) {
1902 return DRAGDROP_S_USEDEFAULTCURSORS;
1905 static VFunction *vtDropSource[] = {
1906 (VFunction *)(DropSource_QueryInterface),
1907 (VFunction *)(DropSource_AddRef),
1908 (VFunction *)(DropSource_Release),
1909 (VFunction *)(DropSource_QueryContinueDrag),
1910 (VFunction *)(DropSource_GiveFeedback)
1913 DropSource::DropSource() {
1914 vtbl = vtDropSource;
1915 sci = 0;
1918 /// Implement IUnkown
1919 STDMETHODIMP DataObject_QueryInterface(DataObject *pd, REFIID riid, PVOID *ppv) {
1920 //Platform::DebugPrintf("DO QI %x\n", pd);
1921 return pd->sci->QueryInterface(riid, ppv);
1923 STDMETHODIMP_(ULONG)DataObject_AddRef(DataObject *pd) {
1924 return pd->sci->AddRef();
1926 STDMETHODIMP_(ULONG)DataObject_Release(DataObject *pd) {
1927 return pd->sci->Release();
1929 /// Implement IDataObject
1930 STDMETHODIMP DataObject_GetData(DataObject *pd, FORMATETC *pFEIn, STGMEDIUM *pSTM) {
1931 return pd->sci->GetData(pFEIn, pSTM);
1934 STDMETHODIMP DataObject_GetDataHere(DataObject *, FORMATETC *, STGMEDIUM *) {
1935 //Platform::DebugPrintf("DOB GetDataHere\n");
1936 return E_NOTIMPL;
1939 STDMETHODIMP DataObject_QueryGetData(DataObject *pd, FORMATETC *pFE) {
1940 if (pd->sci->DragIsRectangularOK(pFE->cfFormat) &&
1941 pFE->ptd == 0 &&
1942 (pFE->dwAspect & DVASPECT_CONTENT) != 0 &&
1943 pFE->lindex == -1 &&
1944 (pFE->tymed & TYMED_HGLOBAL) != 0
1946 return S_OK;
1949 bool formatOK = (pFE->cfFormat == CF_TEXT) ||
1950 ((pFE->cfFormat == CF_UNICODETEXT) && pd->sci->IsUnicodeMode());
1951 if (!formatOK ||
1952 pFE->ptd != 0 ||
1953 (pFE->dwAspect & DVASPECT_CONTENT) == 0 ||
1954 pFE->lindex != -1 ||
1955 (pFE->tymed & TYMED_HGLOBAL) == 0
1957 //Platform::DebugPrintf("DOB QueryGetData No %x\n",pFE->cfFormat);
1958 //return DATA_E_FORMATETC;
1959 return S_FALSE;
1961 //Platform::DebugPrintf("DOB QueryGetData OK %x\n",pFE->cfFormat);
1962 return S_OK;
1965 STDMETHODIMP DataObject_GetCanonicalFormatEtc(DataObject *pd, FORMATETC *, FORMATETC *pFEOut) {
1966 //Platform::DebugPrintf("DOB GetCanon\n");
1967 if (pd->sci->IsUnicodeMode())
1968 pFEOut->cfFormat = CF_UNICODETEXT;
1969 else
1970 pFEOut->cfFormat = CF_TEXT;
1971 pFEOut->ptd = 0;
1972 pFEOut->dwAspect = DVASPECT_CONTENT;
1973 pFEOut->lindex = -1;
1974 pFEOut->tymed = TYMED_HGLOBAL;
1975 return S_OK;
1978 STDMETHODIMP DataObject_SetData(DataObject *, FORMATETC *, STGMEDIUM *, BOOL) {
1979 //Platform::DebugPrintf("DOB SetData\n");
1980 return E_FAIL;
1983 STDMETHODIMP DataObject_EnumFormatEtc(DataObject *pd, DWORD dwDirection, IEnumFORMATETC **ppEnum) {
1984 try {
1985 //Platform::DebugPrintf("DOB EnumFormatEtc %d\n", dwDirection);
1986 if (dwDirection != DATADIR_GET) {
1987 *ppEnum = 0;
1988 return E_FAIL;
1990 FormatEnumerator *pfe;
1991 if (pd->sci->IsUnicodeMode()) {
1992 CLIPFORMAT formats[] = {CF_UNICODETEXT, CF_TEXT};
1993 pfe = new FormatEnumerator(0, formats, 2);
1994 } else {
1995 CLIPFORMAT formats[] = {CF_TEXT};
1996 pfe = new FormatEnumerator(0, formats, 1);
1998 return FormatEnumerator_QueryInterface(pfe, IID_IEnumFORMATETC,
1999 reinterpret_cast<void **>(ppEnum));
2000 } catch (std::bad_alloc &) {
2001 pd->sci->errorStatus = SC_STATUS_BADALLOC;
2002 return E_OUTOFMEMORY;
2003 } catch (...) {
2004 pd->sci->errorStatus = SC_STATUS_FAILURE;
2005 return E_FAIL;
2009 STDMETHODIMP DataObject_DAdvise(DataObject *, FORMATETC *, DWORD, IAdviseSink *, PDWORD) {
2010 //Platform::DebugPrintf("DOB DAdvise\n");
2011 return E_FAIL;
2014 STDMETHODIMP DataObject_DUnadvise(DataObject *, DWORD) {
2015 //Platform::DebugPrintf("DOB DUnadvise\n");
2016 return E_FAIL;
2019 STDMETHODIMP DataObject_EnumDAdvise(DataObject *, IEnumSTATDATA **) {
2020 //Platform::DebugPrintf("DOB EnumDAdvise\n");
2021 return E_FAIL;
2024 static VFunction *vtDataObject[] = {
2025 (VFunction *)(DataObject_QueryInterface),
2026 (VFunction *)(DataObject_AddRef),
2027 (VFunction *)(DataObject_Release),
2028 (VFunction *)(DataObject_GetData),
2029 (VFunction *)(DataObject_GetDataHere),
2030 (VFunction *)(DataObject_QueryGetData),
2031 (VFunction *)(DataObject_GetCanonicalFormatEtc),
2032 (VFunction *)(DataObject_SetData),
2033 (VFunction *)(DataObject_EnumFormatEtc),
2034 (VFunction *)(DataObject_DAdvise),
2035 (VFunction *)(DataObject_DUnadvise),
2036 (VFunction *)(DataObject_EnumDAdvise)
2039 DataObject::DataObject() {
2040 vtbl = vtDataObject;
2041 sci = 0;
2044 /// Implement IUnknown
2045 STDMETHODIMP DropTarget_QueryInterface(DropTarget *dt, REFIID riid, PVOID *ppv) {
2046 //Platform::DebugPrintf("DT QI %x\n", dt);
2047 return dt->sci->QueryInterface(riid, ppv);
2049 STDMETHODIMP_(ULONG)DropTarget_AddRef(DropTarget *dt) {
2050 return dt->sci->AddRef();
2052 STDMETHODIMP_(ULONG)DropTarget_Release(DropTarget *dt) {
2053 return dt->sci->Release();
2056 /// Implement IDropTarget by forwarding to Scintilla
2057 STDMETHODIMP DropTarget_DragEnter(DropTarget *dt, LPDATAOBJECT pIDataSource, DWORD grfKeyState,
2058 POINTL pt, PDWORD pdwEffect) {
2059 try {
2060 return dt->sci->DragEnter(pIDataSource, grfKeyState, pt, pdwEffect);
2061 } catch (...) {
2062 dt->sci->errorStatus = SC_STATUS_FAILURE;
2064 return E_FAIL;
2066 STDMETHODIMP DropTarget_DragOver(DropTarget *dt, DWORD grfKeyState, POINTL pt, PDWORD pdwEffect) {
2067 try {
2068 return dt->sci->DragOver(grfKeyState, pt, pdwEffect);
2069 } catch (...) {
2070 dt->sci->errorStatus = SC_STATUS_FAILURE;
2072 return E_FAIL;
2074 STDMETHODIMP DropTarget_DragLeave(DropTarget *dt) {
2075 try {
2076 return dt->sci->DragLeave();
2077 } catch (...) {
2078 dt->sci->errorStatus = SC_STATUS_FAILURE;
2080 return E_FAIL;
2082 STDMETHODIMP DropTarget_Drop(DropTarget *dt, LPDATAOBJECT pIDataSource, DWORD grfKeyState,
2083 POINTL pt, PDWORD pdwEffect) {
2084 try {
2085 return dt->sci->Drop(pIDataSource, grfKeyState, pt, pdwEffect);
2086 } catch (...) {
2087 dt->sci->errorStatus = SC_STATUS_FAILURE;
2089 return E_FAIL;
2092 static VFunction *vtDropTarget[] = {
2093 (VFunction *)(DropTarget_QueryInterface),
2094 (VFunction *)(DropTarget_AddRef),
2095 (VFunction *)(DropTarget_Release),
2096 (VFunction *)(DropTarget_DragEnter),
2097 (VFunction *)(DropTarget_DragOver),
2098 (VFunction *)(DropTarget_DragLeave),
2099 (VFunction *)(DropTarget_Drop)
2102 DropTarget::DropTarget() {
2103 vtbl = vtDropTarget;
2104 sci = 0;
2108 * DBCS: support Input Method Editor (IME).
2109 * Called when IME Window opened.
2111 void ScintillaWin::ImeStartComposition() {
2112 if (caret.active) {
2113 // Move IME Window to current caret position
2114 HIMC hIMC = ::ImmGetContext(MainHWND());
2115 Point pos = PointMainCaret();
2116 COMPOSITIONFORM CompForm;
2117 CompForm.dwStyle = CFS_POINT;
2118 CompForm.ptCurrentPos.x = pos.x;
2119 CompForm.ptCurrentPos.y = pos.y;
2121 ::ImmSetCompositionWindow(hIMC, &CompForm);
2123 // Set font of IME window to same as surrounded text.
2124 if (stylesValid) {
2125 // Since the style creation code has been made platform independent,
2126 // The logfont for the IME is recreated here.
2127 int styleHere = (pdoc->StyleAt(sel.MainCaret())) & 31;
2128 LOGFONTA lf = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ""};
2129 int sizeZoomed = vs.styles[styleHere].size + vs.zoomLevel * SC_FONT_SIZE_MULTIPLIER;
2130 if (sizeZoomed <= 2 * SC_FONT_SIZE_MULTIPLIER) // Hangs if sizeZoomed <= 1
2131 sizeZoomed = 2 * SC_FONT_SIZE_MULTIPLIER;
2132 AutoSurface surface(this);
2133 int deviceHeight = sizeZoomed;
2134 if (surface) {
2135 deviceHeight = (sizeZoomed * surface->LogPixelsY()) / 72;
2137 // The negative is to allow for leading
2138 lf.lfHeight = -(abs(deviceHeight / SC_FONT_SIZE_MULTIPLIER));
2139 lf.lfWeight = vs.styles[styleHere].weight;
2140 lf.lfItalic = static_cast<BYTE>(vs.styles[styleHere].italic ? 1 : 0);
2141 lf.lfCharSet = DEFAULT_CHARSET;
2142 lf.lfFaceName[0] = '\0';
2143 if (vs.styles[styleHere].fontName)
2144 strcpy(lf.lfFaceName, vs.styles[styleHere].fontName);
2146 ::ImmSetCompositionFontA(hIMC, &lf);
2148 ::ImmReleaseContext(MainHWND(), hIMC);
2149 // Caret is displayed in IME window. So, caret in Scintilla is useless.
2150 DropCaret();
2154 /** Called when IME Window closed. */
2155 void ScintillaWin::ImeEndComposition() {
2156 ShowCaretAtCurrentPosition();
2159 void ScintillaWin::AddCharBytes(char b0, char b1) {
2161 int inputCodePage = InputCodePage();
2162 if (inputCodePage && IsUnicodeMode()) {
2163 char utfval[4] = "\0\0\0";
2164 char ansiChars[3];
2165 wchar_t wcs[2];
2166 if (b0) { // Two bytes from IME
2167 ansiChars[0] = b0;
2168 ansiChars[1] = b1;
2169 ansiChars[2] = '\0';
2170 ::MultiByteToWideChar(inputCodePage, 0, ansiChars, 2, wcs, 1);
2171 } else {
2172 ansiChars[0] = b1;
2173 ansiChars[1] = '\0';
2174 ::MultiByteToWideChar(inputCodePage, 0, ansiChars, 1, wcs, 1);
2176 unsigned int len = UTF8Length(wcs, 1);
2177 UTF8FromUTF16(wcs, 1, utfval, len);
2178 utfval[len] = '\0';
2179 AddCharUTF(utfval, len ? len : 1);
2180 } else if (b0) {
2181 char dbcsChars[3];
2182 dbcsChars[0] = b0;
2183 dbcsChars[1] = b1;
2184 dbcsChars[2] = '\0';
2185 AddCharUTF(dbcsChars, 2, true);
2186 } else {
2187 AddChar(b1);
2191 void ScintillaWin::GetIntelliMouseParameters() {
2192 // This retrieves the number of lines per scroll as configured inthe Mouse Properties sheet in Control Panel
2193 ::SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, &linesPerScroll, 0);
2196 void ScintillaWin::CopyToClipboard(const SelectionText &selectedText) {
2197 if (!::OpenClipboard(MainHWND()))
2198 return;
2199 ::EmptyClipboard();
2201 GlobalMemory uniText;
2203 // Default Scintilla behaviour in Unicode mode
2204 if (IsUnicodeMode()) {
2205 int uchars = UTF16Length(selectedText.Data(),
2206 static_cast<int>(selectedText.LengthWithTerminator()));
2207 uniText.Allocate(2 * uchars);
2208 if (uniText) {
2209 UTF16FromUTF8(selectedText.Data(), static_cast<int>(selectedText.LengthWithTerminator()),
2210 static_cast<wchar_t *>(uniText.ptr), uchars);
2212 } else {
2213 // Not Unicode mode
2214 // Convert to Unicode using the current Scintilla code page
2215 UINT cpSrc = CodePageFromCharSet(
2216 selectedText.characterSet, selectedText.codePage);
2217 int uLen = ::MultiByteToWideChar(cpSrc, 0, selectedText.Data(),
2218 static_cast<int>(selectedText.LengthWithTerminator()), 0, 0);
2219 uniText.Allocate(2 * uLen);
2220 if (uniText) {
2221 ::MultiByteToWideChar(cpSrc, 0, selectedText.Data(),
2222 static_cast<int>(selectedText.LengthWithTerminator()),
2223 static_cast<wchar_t *>(uniText.ptr), uLen);
2227 if (uniText) {
2228 if (!IsNT()) {
2229 // Copy ANSI text to clipboard on Windows 9x
2230 // Convert from Unicode text, so other ANSI programs can
2231 // paste the text
2232 // Windows NT, 2k, XP automatically generates CF_TEXT
2233 GlobalMemory ansiText;
2234 ansiText.Allocate(selectedText.LengthWithTerminator());
2235 if (ansiText) {
2236 ::WideCharToMultiByte(CP_ACP, 0, static_cast<wchar_t *>(uniText.ptr), -1,
2237 static_cast<char *>(ansiText.ptr),
2238 static_cast<int>(selectedText.LengthWithTerminator()), NULL, NULL);
2239 ansiText.SetClip(CF_TEXT);
2242 uniText.SetClip(CF_UNICODETEXT);
2243 } else {
2244 // There was a failure - try to copy at least ANSI text
2245 GlobalMemory ansiText;
2246 ansiText.Allocate(selectedText.LengthWithTerminator());
2247 if (ansiText) {
2248 memcpy(static_cast<char *>(ansiText.ptr), selectedText.Data(), selectedText.LengthWithTerminator());
2249 ansiText.SetClip(CF_TEXT);
2253 if (selectedText.rectangular) {
2254 ::SetClipboardData(cfColumnSelect, 0);
2257 if (selectedText.lineCopy) {
2258 ::SetClipboardData(cfLineSelect, 0);
2261 ::CloseClipboard();
2264 void ScintillaWin::ScrollMessage(WPARAM wParam) {
2265 //DWORD dwStart = timeGetTime();
2266 //Platform::DebugPrintf("Scroll %x %d\n", wParam, lParam);
2268 SCROLLINFO sci;
2269 memset(&sci, 0, sizeof(sci));
2270 sci.cbSize = sizeof(sci);
2271 sci.fMask = SIF_ALL;
2273 GetScrollInfo(SB_VERT, &sci);
2275 //Platform::DebugPrintf("ScrollInfo %d mask=%x min=%d max=%d page=%d pos=%d track=%d\n", b,sci.fMask,
2276 //sci.nMin, sci.nMax, sci.nPage, sci.nPos, sci.nTrackPos);
2278 int topLineNew = topLine;
2279 switch (LoWord(wParam)) {
2280 case SB_LINEUP:
2281 topLineNew -= 1;
2282 break;
2283 case SB_LINEDOWN:
2284 topLineNew += 1;
2285 break;
2286 case SB_PAGEUP:
2287 topLineNew -= LinesToScroll(); break;
2288 case SB_PAGEDOWN: topLineNew += LinesToScroll(); break;
2289 case SB_TOP: topLineNew = 0; break;
2290 case SB_BOTTOM: topLineNew = MaxScrollPos(); break;
2291 case SB_THUMBPOSITION: topLineNew = sci.nTrackPos; break;
2292 case SB_THUMBTRACK: topLineNew = sci.nTrackPos; break;
2294 ScrollTo(topLineNew);
2297 void ScintillaWin::HorizontalScrollMessage(WPARAM wParam) {
2298 int xPos = xOffset;
2299 PRectangle rcText = GetTextRectangle();
2300 int pageWidth = rcText.Width() * 2 / 3;
2301 switch (LoWord(wParam)) {
2302 case SB_LINEUP:
2303 xPos -= 20;
2304 break;
2305 case SB_LINEDOWN: // May move past the logical end
2306 xPos += 20;
2307 break;
2308 case SB_PAGEUP:
2309 xPos -= pageWidth;
2310 break;
2311 case SB_PAGEDOWN:
2312 xPos += pageWidth;
2313 if (xPos > scrollWidth - rcText.Width()) { // Hit the end exactly
2314 xPos = scrollWidth - rcText.Width();
2316 break;
2317 case SB_TOP:
2318 xPos = 0;
2319 break;
2320 case SB_BOTTOM:
2321 xPos = scrollWidth;
2322 break;
2323 case SB_THUMBPOSITION:
2324 case SB_THUMBTRACK: {
2325 // 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 =]
2326 SCROLLINFO si;
2327 si.cbSize = sizeof(si);
2328 si.fMask = SIF_TRACKPOS;
2329 if (GetScrollInfo(SB_HORZ, &si)) {
2330 xPos = si.nTrackPos;
2333 break;
2335 HorizontalScrollTo(xPos);
2339 * Redraw all of text area.
2340 * This paint will not be abandoned.
2342 void ScintillaWin::FullPaint() {
2343 if (technology == SC_TECHNOLOGY_DEFAULT) {
2344 HDC hdc = ::GetDC(MainHWND());
2345 FullPaintDC(hdc);
2346 ::ReleaseDC(MainHWND(), hdc);
2347 } else {
2348 FullPaintDC(0);
2353 * Redraw all of text area on the specified DC.
2354 * This paint will not be abandoned.
2356 void ScintillaWin::FullPaintDC(HDC hdc) {
2357 paintState = painting;
2358 rcPaint = GetClientRectangle();
2359 paintingAllText = true;
2360 if (technology == SC_TECHNOLOGY_DEFAULT) {
2361 AutoSurface surfaceWindow(hdc, this);
2362 if (surfaceWindow) {
2363 Paint(surfaceWindow, rcPaint);
2364 surfaceWindow->Release();
2366 } else {
2367 #if defined(USE_D2D)
2368 EnsureRenderTarget();
2369 AutoSurface surfaceWindow(pRenderTarget, this);
2370 if (surfaceWindow) {
2371 pRenderTarget->BeginDraw();
2372 Paint(surfaceWindow, rcPaint);
2373 surfaceWindow->Release();
2374 HRESULT hr = pRenderTarget->EndDraw();
2375 if (hr == D2DERR_RECREATE_TARGET) {
2376 DropRenderTarget();
2379 #endif
2381 paintState = notPainting;
2384 static bool CompareDevCap(HDC hdc, HDC hOtherDC, int nIndex) {
2385 return ::GetDeviceCaps(hdc, nIndex) == ::GetDeviceCaps(hOtherDC, nIndex);
2388 bool ScintillaWin::IsCompatibleDC(HDC hOtherDC) {
2389 HDC hdc = ::GetDC(MainHWND());
2390 bool isCompatible =
2391 CompareDevCap(hdc, hOtherDC, TECHNOLOGY) &&
2392 CompareDevCap(hdc, hOtherDC, LOGPIXELSY) &&
2393 CompareDevCap(hdc, hOtherDC, LOGPIXELSX) &&
2394 CompareDevCap(hdc, hOtherDC, BITSPIXEL) &&
2395 CompareDevCap(hdc, hOtherDC, PLANES);
2396 ::ReleaseDC(MainHWND(), hdc);
2397 return isCompatible;
2400 DWORD ScintillaWin::EffectFromState(DWORD grfKeyState) const {
2401 // These are the Wordpad semantics.
2402 DWORD dwEffect;
2403 if (inDragDrop == ddDragging) // Internal defaults to move
2404 dwEffect = DROPEFFECT_MOVE;
2405 else
2406 dwEffect = DROPEFFECT_COPY;
2407 if (grfKeyState & MK_ALT)
2408 dwEffect = DROPEFFECT_MOVE;
2409 if (grfKeyState & MK_CONTROL)
2410 dwEffect = DROPEFFECT_COPY;
2411 return dwEffect;
2414 /// Implement IUnknown
2415 STDMETHODIMP ScintillaWin::QueryInterface(REFIID riid, PVOID *ppv) {
2416 *ppv = NULL;
2417 if (riid == IID_IUnknown)
2418 *ppv = reinterpret_cast<IDropTarget *>(&dt);
2419 if (riid == IID_IDropSource)
2420 *ppv = reinterpret_cast<IDropSource *>(&ds);
2421 if (riid == IID_IDropTarget)
2422 *ppv = reinterpret_cast<IDropTarget *>(&dt);
2423 if (riid == IID_IDataObject)
2424 *ppv = reinterpret_cast<IDataObject *>(&dob);
2425 if (!*ppv)
2426 return E_NOINTERFACE;
2427 return S_OK;
2430 STDMETHODIMP_(ULONG) ScintillaWin::AddRef() {
2431 return 1;
2434 STDMETHODIMP_(ULONG) ScintillaWin::Release() {
2435 return 1;
2438 /// Implement IDropTarget
2439 STDMETHODIMP ScintillaWin::DragEnter(LPDATAOBJECT pIDataSource, DWORD grfKeyState,
2440 POINTL, PDWORD pdwEffect) {
2441 if (pIDataSource == NULL)
2442 return E_POINTER;
2443 FORMATETC fmtu = {CF_UNICODETEXT, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
2444 HRESULT hrHasUText = pIDataSource->QueryGetData(&fmtu);
2445 hasOKText = (hrHasUText == S_OK);
2446 if (!hasOKText) {
2447 FORMATETC fmte = {CF_TEXT, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
2448 HRESULT hrHasText = pIDataSource->QueryGetData(&fmte);
2449 hasOKText = (hrHasText == S_OK);
2451 if (!hasOKText) {
2452 *pdwEffect = DROPEFFECT_NONE;
2453 return S_OK;
2456 *pdwEffect = EffectFromState(grfKeyState);
2457 return S_OK;
2460 STDMETHODIMP ScintillaWin::DragOver(DWORD grfKeyState, POINTL pt, PDWORD pdwEffect) {
2461 try {
2462 if (!hasOKText || pdoc->IsReadOnly()) {
2463 *pdwEffect = DROPEFFECT_NONE;
2464 return S_OK;
2467 *pdwEffect = EffectFromState(grfKeyState);
2469 // Update the cursor.
2470 POINT rpt = {pt.x, pt.y};
2471 ::ScreenToClient(MainHWND(), &rpt);
2472 SetDragPosition(SPositionFromLocation(Point(rpt.x, rpt.y), false, false, UserVirtualSpace()));
2474 return S_OK;
2475 } catch (...) {
2476 errorStatus = SC_STATUS_FAILURE;
2478 return E_FAIL;
2481 STDMETHODIMP ScintillaWin::DragLeave() {
2482 try {
2483 SetDragPosition(SelectionPosition(invalidPosition));
2484 return S_OK;
2485 } catch (...) {
2486 errorStatus = SC_STATUS_FAILURE;
2488 return E_FAIL;
2491 STDMETHODIMP ScintillaWin::Drop(LPDATAOBJECT pIDataSource, DWORD grfKeyState,
2492 POINTL pt, PDWORD pdwEffect) {
2493 try {
2494 *pdwEffect = EffectFromState(grfKeyState);
2496 if (pIDataSource == NULL)
2497 return E_POINTER;
2499 SetDragPosition(SelectionPosition(invalidPosition));
2501 STGMEDIUM medium = {0, {0}, 0};
2503 std::vector<char> data; // Includes terminating NUL
2505 FORMATETC fmtu = {CF_UNICODETEXT, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
2506 HRESULT hr = pIDataSource->GetData(&fmtu, &medium);
2507 if (SUCCEEDED(hr) && medium.hGlobal) {
2508 GlobalMemory memUDrop(medium.hGlobal);
2509 wchar_t *udata = static_cast<wchar_t *>(memUDrop.ptr);
2510 if (udata) {
2511 if (IsUnicodeMode()) {
2512 int tlen = memUDrop.Size();
2513 // Convert UTF-16 to UTF-8
2514 int dataLen = UTF8Length(udata, tlen/2);
2515 data.resize(dataLen+1);
2516 UTF8FromUTF16(udata, tlen/2, &data[0], dataLen);
2517 } else {
2518 // Convert UTF-16 to ANSI
2520 // Default Scintilla behavior in Unicode mode
2521 // CF_UNICODETEXT available, but not in Unicode mode
2522 // Convert from Unicode to current Scintilla code page
2523 UINT cpDest = CodePageOfDocument();
2524 int tlen = ::WideCharToMultiByte(cpDest, 0, udata, -1,
2525 NULL, 0, NULL, NULL) - 1; // subtract 0 terminator
2526 data.resize(tlen + 1);
2527 ::WideCharToMultiByte(cpDest, 0, udata, -1,
2528 &data[0], tlen + 1, NULL, NULL);
2531 memUDrop.Unlock();
2532 } else {
2533 FORMATETC fmte = {CF_TEXT, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
2534 hr = pIDataSource->GetData(&fmte, &medium);
2535 if (SUCCEEDED(hr) && medium.hGlobal) {
2536 GlobalMemory memDrop(medium.hGlobal);
2537 const char *cdata = static_cast<char *>(memDrop.ptr);
2538 if (cdata)
2539 data.assign(cdata, cdata+strlen(cdata)+1);
2540 memDrop.Unlock();
2544 if (!data.empty() && convertPastes) {
2545 // Convert line endings of the drop into our local line-endings mode
2546 std::string convertedText = Document::TransformLineEnds(&data[0], data.size() - 1, pdoc->eolMode);
2547 data.assign(convertedText.c_str(), convertedText.c_str()+convertedText.length()+1);
2550 if (!SUCCEEDED(hr) || data.empty()) {
2551 //Platform::DebugPrintf("Bad data format: 0x%x\n", hres);
2552 return hr;
2555 FORMATETC fmtr = {cfColumnSelect, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
2556 HRESULT hrRectangular = pIDataSource->QueryGetData(&fmtr);
2558 POINT rpt = {pt.x, pt.y};
2559 ::ScreenToClient(MainHWND(), &rpt);
2560 SelectionPosition movePos = SPositionFromLocation(Point(rpt.x, rpt.y), false, false, UserVirtualSpace());
2562 DropAt(movePos, &data[0], data.size() - 1, *pdwEffect == DROPEFFECT_MOVE, hrRectangular == S_OK);
2564 // Free data
2565 if (medium.pUnkForRelease != NULL)
2566 medium.pUnkForRelease->Release();
2567 else
2568 ::GlobalFree(medium.hGlobal);
2570 return S_OK;
2571 } catch (...) {
2572 errorStatus = SC_STATUS_FAILURE;
2574 return E_FAIL;
2577 /// Implement important part of IDataObject
2578 STDMETHODIMP ScintillaWin::GetData(FORMATETC *pFEIn, STGMEDIUM *pSTM) {
2579 bool formatOK = (pFEIn->cfFormat == CF_TEXT) ||
2580 ((pFEIn->cfFormat == CF_UNICODETEXT) && IsUnicodeMode());
2581 if (!formatOK ||
2582 pFEIn->ptd != 0 ||
2583 (pFEIn->dwAspect & DVASPECT_CONTENT) == 0 ||
2584 pFEIn->lindex != -1 ||
2585 (pFEIn->tymed & TYMED_HGLOBAL) == 0
2587 //Platform::DebugPrintf("DOB GetData No %d %x %x fmt=%x\n", lenDrag, pFEIn, pSTM, pFEIn->cfFormat);
2588 return DATA_E_FORMATETC;
2590 pSTM->tymed = TYMED_HGLOBAL;
2591 //Platform::DebugPrintf("DOB GetData OK %d %x %x\n", lenDrag, pFEIn, pSTM);
2593 GlobalMemory text;
2594 if (pFEIn->cfFormat == CF_UNICODETEXT) {
2595 int uchars = UTF16Length(drag.Data(), static_cast<int>(drag.LengthWithTerminator()));
2596 text.Allocate(2 * uchars);
2597 if (text) {
2598 UTF16FromUTF8(drag.Data(), static_cast<int>(drag.LengthWithTerminator()),
2599 static_cast<wchar_t *>(text.ptr), uchars);
2601 } else {
2602 text.Allocate(drag.LengthWithTerminator());
2603 if (text) {
2604 memcpy(static_cast<char *>(text.ptr), drag.Data(), drag.LengthWithTerminator());
2607 pSTM->hGlobal = text ? text.Unlock() : 0;
2608 pSTM->pUnkForRelease = 0;
2609 return S_OK;
2612 bool ScintillaWin::Register(HINSTANCE hInstance_) {
2614 hInstance = hInstance_;
2615 bool result;
2617 // Register the Scintilla class
2618 if (IsNT()) {
2620 // Register Scintilla as a wide character window
2621 WNDCLASSEXW wndclass;
2622 wndclass.cbSize = sizeof(wndclass);
2623 wndclass.style = CS_GLOBALCLASS | CS_HREDRAW | CS_VREDRAW;
2624 wndclass.lpfnWndProc = ScintillaWin::SWndProc;
2625 wndclass.cbClsExtra = 0;
2626 wndclass.cbWndExtra = sizeof(ScintillaWin *);
2627 wndclass.hInstance = hInstance;
2628 wndclass.hIcon = NULL;
2629 wndclass.hCursor = NULL;
2630 wndclass.hbrBackground = NULL;
2631 wndclass.lpszMenuName = NULL;
2632 wndclass.lpszClassName = L"Scintilla";
2633 wndclass.hIconSm = 0;
2634 result = ::RegisterClassExW(&wndclass) != 0;
2635 } else {
2637 // Register Scintilla as a normal character window
2638 WNDCLASSEX wndclass;
2639 wndclass.cbSize = sizeof(wndclass);
2640 wndclass.style = CS_GLOBALCLASS | CS_HREDRAW | CS_VREDRAW;
2641 wndclass.lpfnWndProc = ScintillaWin::SWndProc;
2642 wndclass.cbClsExtra = 0;
2643 wndclass.cbWndExtra = sizeof(ScintillaWin *);
2644 wndclass.hInstance = hInstance;
2645 wndclass.hIcon = NULL;
2646 wndclass.hCursor = NULL;
2647 wndclass.hbrBackground = NULL;
2648 wndclass.lpszMenuName = NULL;
2649 wndclass.lpszClassName = scintillaClassName;
2650 wndclass.hIconSm = 0;
2651 result = ::RegisterClassEx(&wndclass) != 0;
2654 if (result) {
2655 // Register the CallTip class
2656 WNDCLASSEX wndclassc;
2657 wndclassc.cbSize = sizeof(wndclassc);
2658 wndclassc.style = CS_GLOBALCLASS | CS_HREDRAW | CS_VREDRAW;
2659 wndclassc.cbClsExtra = 0;
2660 wndclassc.cbWndExtra = sizeof(ScintillaWin *);
2661 wndclassc.hInstance = hInstance;
2662 wndclassc.hIcon = NULL;
2663 wndclassc.hbrBackground = NULL;
2664 wndclassc.lpszMenuName = NULL;
2665 wndclassc.lpfnWndProc = ScintillaWin::CTWndProc;
2666 wndclassc.hCursor = ::LoadCursor(NULL, IDC_ARROW);
2667 wndclassc.lpszClassName = callClassName;
2668 wndclassc.hIconSm = 0;
2670 result = ::RegisterClassEx(&wndclassc) != 0;
2673 return result;
2676 bool ScintillaWin::Unregister() {
2677 bool result = ::UnregisterClass(scintillaClassName, hInstance) != 0;
2678 if (::UnregisterClass(callClassName, hInstance) == 0)
2679 result = false;
2680 return result;
2683 bool ScintillaWin::HasCaretSizeChanged() const {
2684 if (
2685 ( (0 != vs.caretWidth) && (sysCaretWidth != vs.caretWidth) )
2686 || ((0 != vs.lineHeight) && (sysCaretHeight != vs.lineHeight))
2688 return true;
2690 return false;
2693 BOOL ScintillaWin::CreateSystemCaret() {
2694 sysCaretWidth = vs.caretWidth;
2695 if (0 == sysCaretWidth) {
2696 sysCaretWidth = 1;
2698 sysCaretHeight = vs.lineHeight;
2699 int bitmapSize = (((sysCaretWidth + 15) & ~15) >> 3) *
2700 sysCaretHeight;
2701 std::vector<char> bits(bitmapSize);
2702 sysCaretBitmap = ::CreateBitmap(sysCaretWidth, sysCaretHeight, 1,
2703 1, reinterpret_cast<BYTE *>(&bits[0]));
2704 BOOL retval = ::CreateCaret(
2705 MainHWND(), sysCaretBitmap,
2706 sysCaretWidth, sysCaretHeight);
2707 ::ShowCaret(MainHWND());
2708 return retval;
2711 BOOL ScintillaWin::DestroySystemCaret() {
2712 ::HideCaret(MainHWND());
2713 BOOL retval = ::DestroyCaret();
2714 if (sysCaretBitmap) {
2715 ::DeleteObject(sysCaretBitmap);
2716 sysCaretBitmap = 0;
2718 return retval;
2721 sptr_t PASCAL ScintillaWin::CTWndProc(
2722 HWND hWnd, UINT iMessage, WPARAM wParam, sptr_t lParam) {
2723 // Find C++ object associated with window.
2724 ScintillaWin *sciThis = reinterpret_cast<ScintillaWin *>(PointerFromWindow(hWnd));
2725 try {
2726 // ctp will be zero if WM_CREATE not seen yet
2727 if (sciThis == 0) {
2728 if (iMessage == WM_CREATE) {
2729 // Associate CallTip object with window
2730 CREATESTRUCT *pCreate = reinterpret_cast<CREATESTRUCT *>(lParam);
2731 SetWindowPointer(hWnd, pCreate->lpCreateParams);
2732 return 0;
2733 } else {
2734 return ::DefWindowProc(hWnd, iMessage, wParam, lParam);
2736 } else {
2737 if (iMessage == WM_NCDESTROY) {
2738 ::SetWindowLong(hWnd, 0, 0);
2739 return ::DefWindowProc(hWnd, iMessage, wParam, lParam);
2740 } else if (iMessage == WM_PAINT) {
2741 PAINTSTRUCT ps;
2742 ::BeginPaint(hWnd, &ps);
2743 Surface *surfaceWindow = Surface::Allocate(sciThis->technology);
2744 if (surfaceWindow) {
2745 #if defined(USE_D2D)
2746 ID2D1HwndRenderTarget *pCTRenderTarget = 0;
2747 #endif
2748 RECT rc;
2749 GetClientRect(hWnd, &rc);
2750 // Create a Direct2D render target.
2751 if (sciThis->technology == SC_TECHNOLOGY_DEFAULT) {
2752 surfaceWindow->Init(ps.hdc, hWnd);
2753 } else {
2754 #if defined(USE_D2D)
2755 D2D1_HWND_RENDER_TARGET_PROPERTIES dhrtp;
2756 dhrtp.hwnd = hWnd;
2757 dhrtp.pixelSize = D2D1::SizeU(rc.right - rc.left, rc.bottom - rc.top);
2758 dhrtp.presentOptions = D2D1_PRESENT_OPTIONS_NONE;
2760 D2D1_RENDER_TARGET_PROPERTIES drtp;
2761 drtp.type = D2D1_RENDER_TARGET_TYPE_DEFAULT;
2762 drtp.pixelFormat.format = DXGI_FORMAT_UNKNOWN;
2763 drtp.pixelFormat.alphaMode = D2D1_ALPHA_MODE_UNKNOWN;
2764 drtp.dpiX = 96.0;
2765 drtp.dpiY = 96.0;
2766 drtp.usage = D2D1_RENDER_TARGET_USAGE_NONE;
2767 drtp.minLevel = D2D1_FEATURE_LEVEL_DEFAULT;
2769 if (!SUCCEEDED(pD2DFactory->CreateHwndRenderTarget(drtp, dhrtp, &pCTRenderTarget))) {
2770 surfaceWindow->Release();
2771 delete surfaceWindow;
2772 ::EndPaint(hWnd, &ps);
2773 return 0;
2775 surfaceWindow->Init(pCTRenderTarget, hWnd);
2776 pCTRenderTarget->BeginDraw();
2777 #endif
2779 surfaceWindow->SetUnicodeMode(SC_CP_UTF8 == sciThis->ct.codePage);
2780 surfaceWindow->SetDBCSMode(sciThis->ct.codePage);
2781 sciThis->ct.PaintCT(surfaceWindow);
2782 #if defined(USE_D2D)
2783 if (pCTRenderTarget)
2784 pCTRenderTarget->EndDraw();
2785 #endif
2786 surfaceWindow->Release();
2787 delete surfaceWindow;
2788 #if defined(USE_D2D)
2789 if (pCTRenderTarget)
2790 pCTRenderTarget->Release();
2791 #endif
2793 ::EndPaint(hWnd, &ps);
2794 return 0;
2795 } else if ((iMessage == WM_NCLBUTTONDOWN) || (iMessage == WM_NCLBUTTONDBLCLK)) {
2796 POINT pt;
2797 pt.x = static_cast<short>(LOWORD(lParam));
2798 pt.y = static_cast<short>(HIWORD(lParam));
2799 ScreenToClient(hWnd, &pt);
2800 sciThis->ct.MouseClick(Point(pt.x, pt.y));
2801 sciThis->CallTipClick();
2802 return 0;
2803 } else if (iMessage == WM_LBUTTONDOWN) {
2804 // This does not fire due to the hit test code
2805 sciThis->ct.MouseClick(Point::FromLong(lParam));
2806 sciThis->CallTipClick();
2807 return 0;
2808 } else if (iMessage == WM_SETCURSOR) {
2809 ::SetCursor(::LoadCursor(NULL, IDC_ARROW));
2810 return 0;
2811 } else if (iMessage == WM_NCHITTEST) {
2812 return HTCAPTION;
2813 } else {
2814 return ::DefWindowProc(hWnd, iMessage, wParam, lParam);
2817 } catch (...) {
2818 sciThis->errorStatus = SC_STATUS_FAILURE;
2820 return ::DefWindowProc(hWnd, iMessage, wParam, lParam);
2823 sptr_t ScintillaWin::DirectFunction(
2824 ScintillaWin *sci, UINT iMessage, uptr_t wParam, sptr_t lParam) {
2825 PLATFORM_ASSERT(::GetCurrentThreadId() == ::GetWindowThreadProcessId(sci->MainHWND(), NULL));
2826 return sci->WndProc(iMessage, wParam, lParam);
2829 extern "C"
2830 #ifndef STATIC_BUILD
2831 __declspec(dllexport)
2832 #endif
2833 sptr_t __stdcall Scintilla_DirectFunction(
2834 ScintillaWin *sci, UINT iMessage, uptr_t wParam, sptr_t lParam) {
2835 return sci->WndProc(iMessage, wParam, lParam);
2838 sptr_t PASCAL ScintillaWin::SWndProc(
2839 HWND hWnd, UINT iMessage, WPARAM wParam, sptr_t lParam) {
2840 //Platform::DebugPrintf("S W:%x M:%x WP:%x L:%x\n", hWnd, iMessage, wParam, lParam);
2842 // Find C++ object associated with window.
2843 ScintillaWin *sci = reinterpret_cast<ScintillaWin *>(PointerFromWindow(hWnd));
2844 // sci will be zero if WM_CREATE not seen yet
2845 if (sci == 0) {
2846 try {
2847 if (iMessage == WM_CREATE) {
2848 // Create C++ object associated with window
2849 sci = new ScintillaWin(hWnd);
2850 SetWindowPointer(hWnd, sci);
2851 return sci->WndProc(iMessage, wParam, lParam);
2853 } catch (...) {
2855 return ::DefWindowProc(hWnd, iMessage, wParam, lParam);
2856 } else {
2857 if (iMessage == WM_NCDESTROY) {
2858 try {
2859 sci->Finalise();
2860 delete sci;
2861 } catch (...) {
2863 ::SetWindowLong(hWnd, 0, 0);
2864 return ::DefWindowProc(hWnd, iMessage, wParam, lParam);
2865 } else {
2866 return sci->WndProc(iMessage, wParam, lParam);
2871 // This function is externally visible so it can be called from container when building statically.
2872 // Must be called once only.
2873 int Scintilla_RegisterClasses(void *hInstance) {
2874 Platform_Initialise(hInstance);
2875 bool result = ScintillaWin::Register(reinterpret_cast<HINSTANCE>(hInstance));
2876 #ifdef SCI_LEXER
2877 Scintilla_LinkLexers();
2878 #endif
2879 return result;
2882 // This function is externally visible so it can be called from container when building statically.
2883 int Scintilla_ReleaseResources() {
2884 bool result = ScintillaWin::Unregister();
2885 Platform_Finalise();
2886 return result;
2889 #ifndef STATIC_BUILD
2890 extern "C" int APIENTRY DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID) {
2891 //Platform::DebugPrintf("Scintilla::DllMain %d %d\n", hInstance, dwReason);
2892 if (dwReason == DLL_PROCESS_ATTACH) {
2893 if (!Scintilla_RegisterClasses(hInstance))
2894 return FALSE;
2895 } else if (dwReason == DLL_PROCESS_DETACH) {
2896 Scintilla_ReleaseResources();
2898 return TRUE;
2900 #endif