Synced with Transifex
[TortoiseGit.git] / ext / scintilla / win32 / ScintillaWin.cxx
blob3550ad0339bf88825c866d70be73a4e136e1fe69
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 "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 cfBorlandIDEBlockType;
201 CLIPFORMAT cfLineSelect;
203 HRESULT hrOle;
204 DropSource ds;
205 DataObject dob;
206 DropTarget dt;
208 static HINSTANCE hInstance;
210 #if defined(USE_D2D)
211 ID2D1HwndRenderTarget *pRenderTarget;
212 bool renderTargetValid;
213 #endif
215 ScintillaWin(HWND hwnd);
216 ScintillaWin(const ScintillaWin &);
217 virtual ~ScintillaWin();
218 ScintillaWin &operator=(const ScintillaWin &);
220 virtual void Initialise();
221 virtual void Finalise();
222 #if defined(USE_D2D)
223 void EnsureRenderTarget();
224 void DropRenderTarget();
225 #endif
226 HWND MainHWND();
228 static sptr_t DirectFunction(
229 ScintillaWin *sci, UINT iMessage, uptr_t wParam, sptr_t lParam);
230 static sptr_t PASCAL SWndProc(
231 HWND hWnd, UINT iMessage, WPARAM wParam, sptr_t lParam);
232 static sptr_t PASCAL CTWndProc(
233 HWND hWnd, UINT iMessage, WPARAM wParam, sptr_t lParam);
235 enum { invalidTimerID, standardTimerID, idleTimerID };
237 virtual bool DragThreshold(Point ptStart, Point ptNow);
238 virtual void StartDrag();
239 sptr_t WndPaint(uptr_t wParam);
240 sptr_t HandleComposition(uptr_t wParam, sptr_t lParam);
241 UINT CodePageOfDocument();
242 virtual bool ValidCodePage(int codePage) const;
243 virtual sptr_t DefWndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam);
244 virtual bool SetIdle(bool on);
245 virtual void SetTicking(bool on);
246 virtual void SetMouseCapture(bool on);
247 virtual bool HaveMouseCapture();
248 virtual void SetTrackMouseLeaveEvent(bool on);
249 virtual bool PaintContains(PRectangle rc);
250 virtual void ScrollText(int linesToMove);
251 virtual void UpdateSystemCaret();
252 virtual void SetVerticalScrollPos();
253 virtual void SetHorizontalScrollPos();
254 virtual bool ModifyScrollBars(int nMax, int nPage);
255 virtual void NotifyChange();
256 virtual void NotifyFocus(bool focus);
257 virtual void SetCtrlID(int identifier);
258 virtual int GetCtrlID();
259 virtual void NotifyParent(SCNotification scn);
260 virtual void NotifyParent(SCNotification * scn);
261 virtual void NotifyDoubleClick(Point pt, bool shift, bool ctrl, bool alt);
262 virtual CaseFolder *CaseFolderForEncoding();
263 virtual std::string CaseMapString(const std::string &s, int caseMapping);
264 virtual void Copy();
265 virtual void CopyAllowLine();
266 virtual bool CanPaste();
267 virtual void Paste();
268 virtual void CreateCallTipWindow(PRectangle rc);
269 virtual void AddToPopUp(const char *label, int cmd = 0, bool enabled = true);
270 virtual void ClaimSelection();
272 // DBCS
273 void ImeStartComposition();
274 void ImeEndComposition();
276 void AddCharBytes(char b0, char b1);
278 void GetIntelliMouseParameters();
279 virtual void CopyToClipboard(const SelectionText &selectedText);
280 void ScrollMessage(WPARAM wParam);
281 void HorizontalScrollMessage(WPARAM wParam);
282 void FullPaint();
283 void FullPaintDC(HDC dc);
284 bool IsCompatibleDC(HDC dc);
285 DWORD EffectFromState(DWORD grfKeyState) const;
287 virtual int SetScrollInfo(int nBar, LPCSCROLLINFO lpsi, BOOL bRedraw);
288 virtual bool GetScrollInfo(int nBar, LPSCROLLINFO lpsi);
289 void ChangeScrollPos(int barType, int pos);
291 void InsertPasteText(const char *text, int len, SelectionPosition selStart, bool isRectangular, bool isLine);
293 public:
294 // Public for benefit of Scintilla_DirectFunction
295 virtual sptr_t WndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam);
297 /// Implement IUnknown
298 STDMETHODIMP QueryInterface(REFIID riid, PVOID *ppv);
299 STDMETHODIMP_(ULONG)AddRef();
300 STDMETHODIMP_(ULONG)Release();
302 /// Implement IDropTarget
303 STDMETHODIMP DragEnter(LPDATAOBJECT pIDataSource, DWORD grfKeyState,
304 POINTL pt, PDWORD pdwEffect);
305 STDMETHODIMP DragOver(DWORD grfKeyState, POINTL pt, PDWORD pdwEffect);
306 STDMETHODIMP DragLeave();
307 STDMETHODIMP Drop(LPDATAOBJECT pIDataSource, DWORD grfKeyState,
308 POINTL pt, PDWORD pdwEffect);
310 /// Implement important part of IDataObject
311 STDMETHODIMP GetData(FORMATETC *pFEIn, STGMEDIUM *pSTM);
313 static bool Register(HINSTANCE hInstance_);
314 static bool Unregister();
316 friend class DropSource;
317 friend class DataObject;
318 friend class DropTarget;
319 bool DragIsRectangularOK(CLIPFORMAT fmt) const {
320 return drag.rectangular && (fmt == cfColumnSelect);
323 private:
324 // For use in creating a system caret
325 bool HasCaretSizeChanged() const;
326 BOOL CreateSystemCaret();
327 BOOL DestroySystemCaret();
328 HBITMAP sysCaretBitmap;
329 int sysCaretWidth;
330 int sysCaretHeight;
331 bool keysAlwaysUnicode;
334 HINSTANCE ScintillaWin::hInstance = 0;
336 ScintillaWin::ScintillaWin(HWND hwnd) {
338 lastKeyDownConsumed = false;
340 capturedMouse = false;
341 trackedMouseLeave = false;
342 TrackMouseEventFn = 0;
344 linesPerScroll = 0;
345 wheelDelta = 0; // Wheel delta from roll
347 hRgnUpdate = 0;
349 hasOKText = false;
351 // There does not seem to be a real standard for indicating that the clipboard
352 // contains a rectangular selection, so copy Developer Studio and Borland Delphi.
353 cfColumnSelect = static_cast<CLIPFORMAT>(
354 ::RegisterClipboardFormat(TEXT("MSDEVColumnSelect")));
355 cfBorlandIDEBlockType = static_cast<CLIPFORMAT>(
356 ::RegisterClipboardFormat(TEXT("Borland IDE Block Type")));
358 // Likewise for line-copy (copies a full line when no text is selected)
359 cfLineSelect = static_cast<CLIPFORMAT>(
360 ::RegisterClipboardFormat(TEXT("MSDEVLineSelect")));
362 hrOle = E_FAIL;
364 wMain = hwnd;
366 dob.sci = this;
367 ds.sci = this;
368 dt.sci = this;
370 sysCaretBitmap = 0;
371 sysCaretWidth = 0;
372 sysCaretHeight = 0;
374 #if defined(USE_D2D)
375 pRenderTarget = 0;
376 renderTargetValid = true;
377 #endif
379 keysAlwaysUnicode = false;
381 caret.period = ::GetCaretBlinkTime();
382 if (caret.period < 0)
383 caret.period = 0;
385 Initialise();
388 ScintillaWin::~ScintillaWin() {}
390 void ScintillaWin::Initialise() {
391 // Initialize COM. If the app has already done this it will have
392 // no effect. If the app hasnt, we really shouldnt ask them to call
393 // it just so this internal feature works.
394 hrOle = ::OleInitialize(NULL);
396 // Find TrackMouseEvent which is available on Windows > 95
397 HMODULE user32 = ::GetModuleHandle(TEXT("user32.dll"));
398 if (user32)
399 TrackMouseEventFn = (TrackMouseEventSig)::GetProcAddress(user32, "TrackMouseEvent");
400 if (TrackMouseEventFn == NULL) {
401 // Windows 95 has an emulation in comctl32.dll:_TrackMouseEvent
402 HMODULE commctrl32 = ::LoadLibrary(TEXT("comctl32.dll"));
403 if (commctrl32 != NULL) {
404 TrackMouseEventFn = (TrackMouseEventSig)
405 ::GetProcAddress(commctrl32, "_TrackMouseEvent");
410 void ScintillaWin::Finalise() {
411 ScintillaBase::Finalise();
412 SetTicking(false);
413 SetIdle(false);
414 #if defined(USE_D2D)
415 DropRenderTarget();
416 #endif
417 ::RevokeDragDrop(MainHWND());
418 if (SUCCEEDED(hrOle)) {
419 ::OleUninitialize();
423 #if defined(USE_D2D)
425 void ScintillaWin::EnsureRenderTarget() {
426 if (!renderTargetValid) {
427 DropRenderTarget();
428 renderTargetValid = true;
430 if (pD2DFactory && !pRenderTarget) {
431 RECT rc;
432 HWND hw = MainHWND();
433 GetClientRect(hw, &rc);
435 D2D1_SIZE_U size = D2D1::SizeU(rc.right - rc.left, rc.bottom - rc.top);
437 // Create a Direct2D render target.
438 #if 1
439 D2D1_HWND_RENDER_TARGET_PROPERTIES dhrtp;
440 dhrtp.hwnd = hw;
441 dhrtp.pixelSize = size;
442 dhrtp.presentOptions = D2D1_PRESENT_OPTIONS_NONE;
444 D2D1_RENDER_TARGET_PROPERTIES drtp;
445 drtp.type = D2D1_RENDER_TARGET_TYPE_DEFAULT;
446 drtp.pixelFormat.format = DXGI_FORMAT_UNKNOWN;
447 drtp.pixelFormat.alphaMode = D2D1_ALPHA_MODE_UNKNOWN;
448 drtp.dpiX = 96.0;
449 drtp.dpiY = 96.0;
450 drtp.usage = D2D1_RENDER_TARGET_USAGE_NONE;
451 drtp.minLevel = D2D1_FEATURE_LEVEL_DEFAULT;
453 pD2DFactory->CreateHwndRenderTarget(drtp, dhrtp, &pRenderTarget);
454 #else
455 pD2DFactory->CreateHwndRenderTarget(
456 D2D1::RenderTargetProperties(
457 D2D1_RENDER_TARGET_TYPE_DEFAULT ,
458 D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_PREMULTIPLIED),
459 96.0f, 96.0f, D2D1_RENDER_TARGET_USAGE_NONE, D2D1_FEATURE_LEVEL_DEFAULT),
460 D2D1::HwndRenderTargetProperties(hw, size),
461 &pRenderTarget);
462 #endif
463 // Pixmaps were created to be compatible with previous render target so
464 // need to be recreated.
465 DropGraphics(false);
469 void ScintillaWin::DropRenderTarget() {
470 if (pRenderTarget) {
471 pRenderTarget->Release();
472 pRenderTarget = 0;
476 #endif
478 HWND ScintillaWin::MainHWND() {
479 return reinterpret_cast<HWND>(wMain.GetID());
482 bool ScintillaWin::DragThreshold(Point ptStart, Point ptNow) {
483 int xMove = abs(ptStart.x - ptNow.x);
484 int yMove = abs(ptStart.y - ptNow.y);
485 return (xMove > ::GetSystemMetrics(SM_CXDRAG)) ||
486 (yMove > ::GetSystemMetrics(SM_CYDRAG));
489 void ScintillaWin::StartDrag() {
490 inDragDrop = ddDragging;
491 DWORD dwEffect = 0;
492 dropWentOutside = true;
493 IDataObject *pDataObject = reinterpret_cast<IDataObject *>(&dob);
494 IDropSource *pDropSource = reinterpret_cast<IDropSource *>(&ds);
495 //Platform::DebugPrintf("About to DoDragDrop %x %x\n", pDataObject, pDropSource);
496 HRESULT hr = ::DoDragDrop(
497 pDataObject,
498 pDropSource,
499 DROPEFFECT_COPY | DROPEFFECT_MOVE, &dwEffect);
500 //Platform::DebugPrintf("DoDragDrop = %x\n", hr);
501 if (SUCCEEDED(hr)) {
502 if ((hr == DRAGDROP_S_DROP) && (dwEffect == DROPEFFECT_MOVE) && dropWentOutside) {
503 // Remove dragged out text
504 ClearSelection();
507 inDragDrop = ddNone;
508 SetDragPosition(SelectionPosition(invalidPosition));
511 // Avoid warnings everywhere for old style casts by concentrating them here
512 static WORD LoWord(DWORD l) {
513 return LOWORD(l);
516 static WORD HiWord(DWORD l) {
517 return HIWORD(l);
520 static int InputCodePage() {
521 HKL inputLocale = ::GetKeyboardLayout(0);
522 LANGID inputLang = LOWORD(inputLocale);
523 char sCodePage[10];
524 int res = ::GetLocaleInfoA(MAKELCID(inputLang, SORT_DEFAULT),
525 LOCALE_IDEFAULTANSICODEPAGE, sCodePage, sizeof(sCodePage));
526 if (!res)
527 return 0;
528 return atoi(sCodePage);
531 #ifndef VK_OEM_2
532 static const int VK_OEM_2=0xbf;
533 static const int VK_OEM_3=0xc0;
534 static const int VK_OEM_4=0xdb;
535 static const int VK_OEM_5=0xdc;
536 static const int VK_OEM_6=0xdd;
537 #endif
539 /** Map the key codes to their equivalent SCK_ form. */
540 static int KeyTranslate(int keyIn) {
541 //PLATFORM_ASSERT(!keyIn);
542 switch (keyIn) {
543 case VK_DOWN: return SCK_DOWN;
544 case VK_UP: return SCK_UP;
545 case VK_LEFT: return SCK_LEFT;
546 case VK_RIGHT: return SCK_RIGHT;
547 case VK_HOME: return SCK_HOME;
548 case VK_END: return SCK_END;
549 case VK_PRIOR: return SCK_PRIOR;
550 case VK_NEXT: return SCK_NEXT;
551 case VK_DELETE: return SCK_DELETE;
552 case VK_INSERT: return SCK_INSERT;
553 case VK_ESCAPE: return SCK_ESCAPE;
554 case VK_BACK: return SCK_BACK;
555 case VK_TAB: return SCK_TAB;
556 case VK_RETURN: return SCK_RETURN;
557 case VK_ADD: return SCK_ADD;
558 case VK_SUBTRACT: return SCK_SUBTRACT;
559 case VK_DIVIDE: return SCK_DIVIDE;
560 case VK_LWIN: return SCK_WIN;
561 case VK_RWIN: return SCK_RWIN;
562 case VK_APPS: return SCK_MENU;
563 case VK_OEM_2: return '/';
564 case VK_OEM_3: return '`';
565 case VK_OEM_4: return '[';
566 case VK_OEM_5: return '\\';
567 case VK_OEM_6: return ']';
568 default: return keyIn;
572 LRESULT ScintillaWin::WndPaint(uptr_t wParam) {
573 //ElapsedTime et;
575 // Redirect assertions to debug output and save current state
576 bool assertsPopup = Platform::ShowAssertionPopUps(false);
577 paintState = painting;
578 PAINTSTRUCT ps;
579 PAINTSTRUCT *pps;
581 bool IsOcxCtrl = (wParam != 0); // if wParam != 0, it contains
582 // a PAINSTRUCT* from the OCX
583 // Removed since this interferes with reporting other assertions as it occurs repeatedly
584 //PLATFORM_ASSERT(hRgnUpdate == NULL);
585 hRgnUpdate = ::CreateRectRgn(0, 0, 0, 0);
586 if (IsOcxCtrl) {
587 pps = reinterpret_cast<PAINTSTRUCT*>(wParam);
588 } else {
589 ::GetUpdateRgn(MainHWND(), hRgnUpdate, FALSE);
590 pps = &ps;
591 ::BeginPaint(MainHWND(), pps);
593 rcPaint = PRectangle(pps->rcPaint.left, pps->rcPaint.top, pps->rcPaint.right, pps->rcPaint.bottom);
594 PRectangle rcClient = GetClientRectangle();
595 paintingAllText = rcPaint.Contains(rcClient);
596 if (technology == SC_TECHNOLOGY_DEFAULT) {
597 AutoSurface surfaceWindow(pps->hdc, this);
598 if (surfaceWindow) {
599 Paint(surfaceWindow, rcPaint);
600 surfaceWindow->Release();
602 } else {
603 #if defined(USE_D2D)
604 EnsureRenderTarget();
605 AutoSurface surfaceWindow(pRenderTarget, this);
606 if (surfaceWindow) {
607 pRenderTarget->BeginDraw();
608 Paint(surfaceWindow, rcPaint);
609 surfaceWindow->Release();
610 HRESULT hr = pRenderTarget->EndDraw();
611 if (hr == D2DERR_RECREATE_TARGET) {
612 DropRenderTarget();
613 paintState = paintAbandoned;
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 if (IsOcxCtrl) {
628 FullPaintDC(pps->hdc);
629 } else {
630 FullPaint();
633 paintState = notPainting;
635 // Restore debug output state
636 Platform::ShowAssertionPopUps(assertsPopup);
638 //Platform::DebugPrintf("Paint took %g\n", et.Duration());
639 return 0l;
642 sptr_t ScintillaWin::HandleComposition(uptr_t wParam, sptr_t lParam) {
643 if (lParam & GCS_RESULTSTR) {
644 HIMC hIMC = ::ImmGetContext(MainHWND());
645 if (hIMC) {
646 const int maxLenInputIME = 200;
647 wchar_t wcs[maxLenInputIME];
648 LONG bytes = ::ImmGetCompositionStringW(hIMC,
649 GCS_RESULTSTR, wcs, (maxLenInputIME-1)*2);
650 int wides = bytes / 2;
651 if (IsUnicodeMode()) {
652 char utfval[maxLenInputIME * 3];
653 unsigned int len = UTF8Length(wcs, wides);
654 UTF8FromUTF16(wcs, wides, utfval, len);
655 utfval[len] = '\0';
656 AddCharUTF(utfval, len);
657 } else {
658 char dbcsval[maxLenInputIME * 2];
659 int size = ::WideCharToMultiByte(InputCodePage(),
660 0, wcs, wides, dbcsval, sizeof(dbcsval) - 1, 0, 0);
661 for (int i=0; i<size; i++) {
662 AddChar(dbcsval[i]);
665 // Set new position after converted
666 Point pos = PointMainCaret();
667 COMPOSITIONFORM CompForm;
668 CompForm.dwStyle = CFS_POINT;
669 CompForm.ptCurrentPos.x = pos.x;
670 CompForm.ptCurrentPos.y = pos.y;
671 ::ImmSetCompositionWindow(hIMC, &CompForm);
672 ::ImmReleaseContext(MainHWND(), hIMC);
674 return 0;
676 return ::DefWindowProc(MainHWND(), WM_IME_COMPOSITION, wParam, lParam);
679 // Translate message IDs from WM_* and EM_* to SCI_* so can partly emulate Windows Edit control
680 static unsigned int SciMessageFromEM(unsigned int iMessage) {
681 switch (iMessage) {
682 case EM_CANPASTE: return SCI_CANPASTE;
683 case EM_CANUNDO: return SCI_CANUNDO;
684 case EM_EMPTYUNDOBUFFER: return SCI_EMPTYUNDOBUFFER;
685 case EM_FINDTEXTEX: return SCI_FINDTEXT;
686 case EM_FORMATRANGE: return SCI_FORMATRANGE;
687 case EM_GETFIRSTVISIBLELINE: return SCI_GETFIRSTVISIBLELINE;
688 case EM_GETLINECOUNT: return SCI_GETLINECOUNT;
689 case EM_GETSELTEXT: return SCI_GETSELTEXT;
690 case EM_GETTEXTRANGE: return SCI_GETTEXTRANGE;
691 case EM_HIDESELECTION: return SCI_HIDESELECTION;
692 case EM_LINEINDEX: return SCI_POSITIONFROMLINE;
693 case EM_LINESCROLL: return SCI_LINESCROLL;
694 case EM_REPLACESEL: return SCI_REPLACESEL;
695 case EM_SCROLLCARET: return SCI_SCROLLCARET;
696 case EM_SETREADONLY: return SCI_SETREADONLY;
697 case WM_CLEAR: return SCI_CLEAR;
698 case WM_COPY: return SCI_COPY;
699 case WM_CUT: return SCI_CUT;
700 case WM_GETTEXT: return SCI_GETTEXT;
701 case WM_SETTEXT: return SCI_SETTEXT;
702 case WM_GETTEXTLENGTH: return SCI_GETTEXTLENGTH;
703 case WM_PASTE: return SCI_PASTE;
704 case WM_UNDO: return SCI_UNDO;
706 return iMessage;
709 UINT CodePageFromCharSet(DWORD characterSet, UINT documentCodePage) {
710 if (documentCodePage == SC_CP_UTF8) {
711 return SC_CP_UTF8;
713 switch (characterSet) {
714 case SC_CHARSET_ANSI: return 1252;
715 case SC_CHARSET_DEFAULT: return documentCodePage;
716 case SC_CHARSET_BALTIC: return 1257;
717 case SC_CHARSET_CHINESEBIG5: return 950;
718 case SC_CHARSET_EASTEUROPE: return 1250;
719 case SC_CHARSET_GB2312: return 936;
720 case SC_CHARSET_GREEK: return 1253;
721 case SC_CHARSET_HANGUL: return 949;
722 case SC_CHARSET_MAC: return 10000;
723 case SC_CHARSET_OEM: return 437;
724 case SC_CHARSET_RUSSIAN: return 1251;
725 case SC_CHARSET_SHIFTJIS: return 932;
726 case SC_CHARSET_TURKISH: return 1254;
727 case SC_CHARSET_JOHAB: return 1361;
728 case SC_CHARSET_HEBREW: return 1255;
729 case SC_CHARSET_ARABIC: return 1256;
730 case SC_CHARSET_VIETNAMESE: return 1258;
731 case SC_CHARSET_THAI: return 874;
732 case SC_CHARSET_8859_15: return 28605;
733 // Not supported
734 case SC_CHARSET_CYRILLIC: return documentCodePage;
735 case SC_CHARSET_SYMBOL: return documentCodePage;
737 return documentCodePage;
740 UINT ScintillaWin::CodePageOfDocument() {
741 return CodePageFromCharSet(vs.styles[STYLE_DEFAULT].characterSet, pdoc->dbcsCodePage);
744 sptr_t ScintillaWin::WndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
745 try {
746 //Platform::DebugPrintf("S M:%x WP:%x L:%x\n", iMessage, wParam, lParam);
747 iMessage = SciMessageFromEM(iMessage);
748 switch (iMessage) {
750 case WM_CREATE:
751 ctrlID = ::GetDlgCtrlID(reinterpret_cast<HWND>(wMain.GetID()));
752 // Get Intellimouse scroll line parameters
753 GetIntelliMouseParameters();
754 ::RegisterDragDrop(MainHWND(), reinterpret_cast<IDropTarget *>(&dt));
755 break;
757 case WM_COMMAND:
758 Command(LoWord(wParam));
759 break;
761 case WM_PAINT:
762 return WndPaint(wParam);
764 case WM_PRINTCLIENT: {
765 HDC hdc = reinterpret_cast<HDC>(wParam);
766 if (!IsCompatibleDC(hdc)) {
767 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
769 FullPaintDC(hdc);
771 break;
773 case WM_VSCROLL:
774 ScrollMessage(wParam);
775 break;
777 case WM_HSCROLL:
778 HorizontalScrollMessage(wParam);
779 break;
781 case WM_SIZE: {
782 #if defined(USE_D2D)
783 if (paintState == notPainting) {
784 DropRenderTarget();
785 } else {
786 renderTargetValid = false;
788 #endif
789 //Platform::DebugPrintf("Scintilla WM_SIZE %d %d\n", LoWord(lParam), HiWord(lParam));
790 ChangeSize();
792 break;
794 case WM_MOUSEWHEEL:
795 // if autocomplete list active then send mousewheel message to it
796 if (ac.Active()) {
797 HWND hWnd = reinterpret_cast<HWND>(ac.lb->GetID());
798 ::SendMessage(hWnd, iMessage, wParam, lParam);
799 break;
802 // Don't handle datazoom.
803 // (A good idea for datazoom would be to "fold" or "unfold" details.
804 // i.e. if datazoomed out only class structures are visible, when datazooming in the control
805 // structures appear, then eventually the individual statements...)
806 if (wParam & MK_SHIFT) {
807 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
810 // Either SCROLL or ZOOM. We handle the wheel steppings calculation
811 wheelDelta -= static_cast<short>(HiWord(wParam));
812 if (abs(wheelDelta) >= WHEEL_DELTA && linesPerScroll > 0) {
813 int linesToScroll = linesPerScroll;
814 if (linesPerScroll == WHEEL_PAGESCROLL)
815 linesToScroll = LinesOnScreen() - 1;
816 if (linesToScroll == 0) {
817 linesToScroll = 1;
819 linesToScroll *= (wheelDelta / WHEEL_DELTA);
820 if (wheelDelta >= 0)
821 wheelDelta = wheelDelta % WHEEL_DELTA;
822 else
823 wheelDelta = - (-wheelDelta % WHEEL_DELTA);
825 if (wParam & MK_CONTROL) {
826 // Zoom! We play with the font sizes in the styles.
827 // Number of steps/line is ignored, we just care if sizing up or down
828 if (linesToScroll < 0) {
829 KeyCommand(SCI_ZOOMIN);
830 } else {
831 KeyCommand(SCI_ZOOMOUT);
833 } else {
834 // Scroll
835 ScrollTo(topLine + linesToScroll);
838 return 0;
840 case WM_TIMER:
841 if (wParam == standardTimerID && timer.ticking) {
842 Tick();
843 } else if (wParam == idleTimerID && idler.state) {
844 SendMessage(MainHWND(), SC_WIN_IDLE, 0, 1);
845 } else {
846 return 1;
848 break;
850 case SC_WIN_IDLE:
851 // wParam=dwTickCountInitial, or 0 to initialize. lParam=bSkipUserInputTest
852 if (idler.state) {
853 if (lParam || (WAIT_TIMEOUT == MsgWaitForMultipleObjects(0, 0, 0, 0, QS_INPUT|QS_HOTKEY))) {
854 if (Idle()) {
855 // User input was given priority above, but all events do get a turn. Other
856 // messages, notifications, etc. will get interleaved with the idle messages.
858 // However, some things like WM_PAINT are a lower priority, and will not fire
859 // when there's a message posted. So, several times a second, we stop and let
860 // the low priority events have a turn (after which the timer will fire again).
862 DWORD dwCurrent = GetTickCount();
863 DWORD dwStart = wParam ? wParam : dwCurrent;
864 const DWORD maxWorkTime = 50;
866 if (dwCurrent >= dwStart && dwCurrent > maxWorkTime && dwCurrent - maxWorkTime < dwStart)
867 PostMessage(MainHWND(), SC_WIN_IDLE, dwStart, 0);
868 } else {
869 SetIdle(false);
873 break;
875 case WM_GETMINMAXINFO:
876 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
878 case WM_LBUTTONDOWN: {
879 // For IME, set the composition string as the result string.
880 HIMC hIMC = ::ImmGetContext(MainHWND());
881 ::ImmNotifyIME(hIMC, NI_COMPOSITIONSTR, CPS_COMPLETE, 0);
882 ::ImmReleaseContext(MainHWND(), hIMC);
884 //Platform::DebugPrintf("Buttdown %d %x %x %x %x %x\n",iMessage, wParam, lParam,
885 // Platform::IsKeyDown(VK_SHIFT),
886 // Platform::IsKeyDown(VK_CONTROL),
887 // Platform::IsKeyDown(VK_MENU));
888 ::SetFocus(MainHWND());
889 ButtonDown(Point::FromLong(lParam), ::GetMessageTime(),
890 (wParam & MK_SHIFT) != 0,
891 (wParam & MK_CONTROL) != 0,
892 Platform::IsKeyDown(VK_MENU));
894 break;
896 case WM_MOUSEMOVE:
897 SetTrackMouseLeaveEvent(true);
898 ButtonMoveWithModifiers(Point::FromLong(lParam),
899 ((wParam & MK_SHIFT) != 0 ? SCI_SHIFT : 0) |
900 ((wParam & MK_CONTROL) != 0 ? SCI_CTRL : 0) |
901 (Platform::IsKeyDown(VK_MENU) ? SCI_ALT : 0));
902 break;
904 case WM_MOUSELEAVE:
905 SetTrackMouseLeaveEvent(false);
906 MouseLeave();
907 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
909 case WM_LBUTTONUP:
910 ButtonUp(Point::FromLong(lParam),
911 ::GetMessageTime(),
912 (wParam & MK_CONTROL) != 0);
913 break;
915 case WM_RBUTTONDOWN:
916 ::SetFocus(MainHWND());
917 if (!PointInSelection(Point::FromLong(lParam))) {
918 CancelModes();
919 SetEmptySelection(PositionFromLocation(Point::FromLong(lParam)));
921 break;
923 case WM_SETCURSOR:
924 if (LoWord(lParam) == HTCLIENT) {
925 if (inDragDrop == ddDragging) {
926 DisplayCursor(Window::cursorUp);
927 } else {
928 // Display regular (drag) cursor over selection
929 POINT pt;
930 if (0 != ::GetCursorPos(&pt)) {
931 ::ScreenToClient(MainHWND(), &pt);
932 if (PointInSelMargin(Point(pt.x, pt.y))) {
933 DisplayCursor(GetMarginCursor(Point(pt.x, pt.y)));
934 } else if (PointInSelection(Point(pt.x, pt.y)) && !SelectionEmpty()) {
935 DisplayCursor(Window::cursorArrow);
936 } else if (PointIsHotspot(Point(pt.x, pt.y))) {
937 DisplayCursor(Window::cursorHand);
938 } else {
939 DisplayCursor(Window::cursorText);
943 return TRUE;
944 } else {
945 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
948 case WM_CHAR:
949 if (((wParam >= 128) || !iscntrl(wParam)) || !lastKeyDownConsumed) {
950 if (::IsWindowUnicode(MainHWND()) || keysAlwaysUnicode) {
951 wchar_t wcs[2] = {static_cast<wchar_t>(wParam), 0};
952 if (IsUnicodeMode()) {
953 // For a wide character version of the window:
954 char utfval[4];
955 unsigned int len = UTF8Length(wcs, 1);
956 UTF8FromUTF16(wcs, 1, utfval, len);
957 AddCharUTF(utfval, len);
958 } else {
959 UINT cpDest = CodePageOfDocument();
960 char inBufferCP[20];
961 int size = ::WideCharToMultiByte(cpDest,
962 0, wcs, 1, inBufferCP, sizeof(inBufferCP) - 1, 0, 0);
963 inBufferCP[size] = '\0';
964 AddCharUTF(inBufferCP, size);
966 } else {
967 if (IsUnicodeMode()) {
968 AddCharBytes('\0', LOBYTE(wParam));
969 } else {
970 AddChar(LOBYTE(wParam));
974 return 0;
976 case WM_UNICHAR:
977 if (wParam == UNICODE_NOCHAR) {
978 return IsUnicodeMode() ? 1 : 0;
979 } else if (lastKeyDownConsumed) {
980 return 1;
981 } else {
982 if (IsUnicodeMode()) {
983 char utfval[4];
984 wchar_t wcs[2] = {static_cast<wchar_t>(wParam), 0};
985 unsigned int len = UTF8Length(wcs, 1);
986 UTF8FromUTF16(wcs, 1, utfval, len);
987 AddCharUTF(utfval, len);
988 return 1;
989 } else {
990 return 0;
994 case WM_SYSKEYDOWN:
995 case WM_KEYDOWN: {
996 //Platform::DebugPrintf("S keydown %d %x %x %x %x\n",iMessage, wParam, lParam, ::IsKeyDown(VK_SHIFT), ::IsKeyDown(VK_CONTROL));
997 lastKeyDownConsumed = false;
998 int ret = KeyDown(KeyTranslate(wParam),
999 Platform::IsKeyDown(VK_SHIFT),
1000 Platform::IsKeyDown(VK_CONTROL),
1001 Platform::IsKeyDown(VK_MENU),
1002 &lastKeyDownConsumed);
1003 if (!ret && !lastKeyDownConsumed) {
1004 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1006 break;
1009 case WM_IME_KEYDOWN:
1010 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1012 case WM_KEYUP:
1013 //Platform::DebugPrintf("S keyup %d %x %x\n",iMessage, wParam, lParam);
1014 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1016 case WM_SETTINGCHANGE:
1017 //Platform::DebugPrintf("Setting Changed\n");
1018 InvalidateStyleData();
1019 // Get Intellimouse scroll line parameters
1020 GetIntelliMouseParameters();
1021 break;
1023 case WM_GETDLGCODE:
1024 return DLGC_HASSETSEL | DLGC_WANTALLKEYS;
1026 case WM_KILLFOCUS: {
1027 HWND wOther = reinterpret_cast<HWND>(wParam);
1028 HWND wThis = MainHWND();
1029 HWND wCT = reinterpret_cast<HWND>(ct.wCallTip.GetID());
1030 if (!wParam ||
1031 !(::IsChild(wThis, wOther) || (wOther == wCT))) {
1032 SetFocusState(false);
1033 DestroySystemCaret();
1036 break;
1038 case WM_SETFOCUS:
1039 SetFocusState(true);
1040 DestroySystemCaret();
1041 CreateSystemCaret();
1042 break;
1044 case WM_SYSCOLORCHANGE:
1045 //Platform::DebugPrintf("Setting Changed\n");
1046 InvalidateStyleData();
1047 break;
1049 case WM_IME_STARTCOMPOSITION: // dbcs
1050 ImeStartComposition();
1051 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1053 case WM_IME_ENDCOMPOSITION: // dbcs
1054 ImeEndComposition();
1055 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1057 case WM_IME_COMPOSITION:
1058 return HandleComposition(wParam, lParam);
1060 case WM_IME_CHAR: {
1061 AddCharBytes(HIBYTE(wParam), LOBYTE(wParam));
1062 return 0;
1065 case WM_CONTEXTMENU:
1066 if (displayPopupMenu) {
1067 Point pt = Point::FromLong(lParam);
1068 if ((pt.x == -1) && (pt.y == -1)) {
1069 // Caused by keyboard so display menu near caret
1070 pt = PointMainCaret();
1071 POINT spt = {static_cast<int>(pt.x), static_cast<int>(pt.y)};
1072 ::ClientToScreen(MainHWND(), &spt);
1073 pt = Point(spt.x, spt.y);
1075 ContextMenu(pt);
1076 return 0;
1078 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1080 case WM_INPUTLANGCHANGE:
1081 //::SetThreadLocale(LOWORD(lParam));
1082 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1084 case WM_INPUTLANGCHANGEREQUEST:
1085 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1087 case WM_ERASEBKGND:
1088 return 1; // Avoid any background erasure as whole window painted.
1090 case WM_CAPTURECHANGED:
1091 capturedMouse = false;
1092 return 0;
1094 // These are not handled in Scintilla and its faster to dispatch them here.
1095 // Also moves time out to here so profile doesn't count lots of empty message calls.
1097 case WM_MOVE:
1098 case WM_MOUSEACTIVATE:
1099 case WM_NCHITTEST:
1100 case WM_NCCALCSIZE:
1101 case WM_NCPAINT:
1102 case WM_NCMOUSEMOVE:
1103 case WM_NCLBUTTONDOWN:
1104 case WM_IME_SETCONTEXT:
1105 case WM_IME_NOTIFY:
1106 case WM_SYSCOMMAND:
1107 case WM_WINDOWPOSCHANGING:
1108 case WM_WINDOWPOSCHANGED:
1109 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1111 case EM_LINEFROMCHAR:
1112 if (static_cast<int>(wParam) < 0) {
1113 wParam = SelectionStart().Position();
1115 return pdoc->LineFromPosition(wParam);
1117 case EM_EXLINEFROMCHAR:
1118 return pdoc->LineFromPosition(lParam);
1120 case EM_GETSEL:
1121 if (wParam) {
1122 *reinterpret_cast<int *>(wParam) = SelectionStart().Position();
1124 if (lParam) {
1125 *reinterpret_cast<int *>(lParam) = SelectionEnd().Position();
1127 return MAKELONG(SelectionStart().Position(), SelectionEnd().Position());
1129 case EM_EXGETSEL: {
1130 if (lParam == 0) {
1131 return 0;
1133 Sci_CharacterRange *pCR = reinterpret_cast<Sci_CharacterRange *>(lParam);
1134 pCR->cpMin = SelectionStart().Position();
1135 pCR->cpMax = SelectionEnd().Position();
1137 break;
1139 case EM_SETSEL: {
1140 int nStart = static_cast<int>(wParam);
1141 int nEnd = static_cast<int>(lParam);
1142 if (nStart == 0 && nEnd == -1) {
1143 nEnd = pdoc->Length();
1145 if (nStart == -1) {
1146 nStart = nEnd; // Remove selection
1148 if (nStart > nEnd) {
1149 SetSelection(nEnd, nStart);
1150 } else {
1151 SetSelection(nStart, nEnd);
1153 EnsureCaretVisible();
1155 break;
1157 case EM_EXSETSEL: {
1158 if (lParam == 0) {
1159 return 0;
1161 Sci_CharacterRange *pCR = reinterpret_cast<Sci_CharacterRange *>(lParam);
1162 sel.selType = Selection::selStream;
1163 if (pCR->cpMin == 0 && pCR->cpMax == -1) {
1164 SetSelection(pCR->cpMin, pdoc->Length());
1165 } else {
1166 SetSelection(pCR->cpMin, pCR->cpMax);
1168 EnsureCaretVisible();
1169 return pdoc->LineFromPosition(SelectionStart().Position());
1172 case SCI_GETDIRECTFUNCTION:
1173 return reinterpret_cast<sptr_t>(DirectFunction);
1175 case SCI_GETDIRECTPOINTER:
1176 return reinterpret_cast<sptr_t>(this);
1178 case SCI_GRABFOCUS:
1179 ::SetFocus(MainHWND());
1180 break;
1182 case SCI_SETKEYSUNICODE:
1183 keysAlwaysUnicode = wParam != 0;
1184 break;
1186 case SCI_GETKEYSUNICODE:
1187 return keysAlwaysUnicode;
1189 case SCI_SETTECHNOLOGY:
1190 if ((wParam == SC_TECHNOLOGY_DEFAULT) || (wParam == SC_TECHNOLOGY_DIRECTWRITE)) {
1191 if (technology != static_cast<int>(wParam)) {
1192 if (static_cast<int>(wParam) == SC_TECHNOLOGY_DIRECTWRITE) {
1193 #if defined(USE_D2D)
1194 if (!LoadD2D())
1195 // Failed to load Direct2D or DirectWrite so no effect
1196 return 0;
1197 #else
1198 return 0;
1199 #endif
1201 technology = wParam;
1202 // Invalidate all cached information including layout.
1203 DropGraphics(true);
1204 InvalidateStyleRedraw();
1207 break;
1209 #ifdef SCI_LEXER
1210 case SCI_LOADLEXERLIBRARY:
1211 LexerManager::GetInstance()->Load(reinterpret_cast<const char *>(lParam));
1212 break;
1213 #endif
1215 default:
1216 return ScintillaBase::WndProc(iMessage, wParam, lParam);
1218 } catch (std::bad_alloc &) {
1219 errorStatus = SC_STATUS_BADALLOC;
1220 } catch (...) {
1221 errorStatus = SC_STATUS_FAILURE;
1223 return 0l;
1226 bool ScintillaWin::ValidCodePage(int codePage) const {
1227 return codePage == 0 || codePage == SC_CP_UTF8 ||
1228 codePage == 932 || codePage == 936 || codePage == 949 ||
1229 codePage == 950 || codePage == 1361;
1232 sptr_t ScintillaWin::DefWndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
1233 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1236 void ScintillaWin::SetTicking(bool on) {
1237 if (timer.ticking != on) {
1238 timer.ticking = on;
1239 if (timer.ticking) {
1240 timer.tickerID = ::SetTimer(MainHWND(), standardTimerID, timer.tickSize, NULL)
1241 ? reinterpret_cast<TickerID>(standardTimerID) : 0;
1242 } else {
1243 ::KillTimer(MainHWND(), reinterpret_cast<uptr_t>(timer.tickerID));
1244 timer.tickerID = 0;
1247 timer.ticksToWait = caret.period;
1250 bool ScintillaWin::SetIdle(bool on) {
1251 // On Win32 the Idler is implemented as a Timer on the Scintilla window. This
1252 // takes advantage of the fact that WM_TIMER messages are very low priority,
1253 // and are only posted when the message queue is empty, i.e. during idle time.
1254 if (idler.state != on) {
1255 if (on) {
1256 idler.idlerID = ::SetTimer(MainHWND(), idleTimerID, 10, NULL)
1257 ? reinterpret_cast<IdlerID>(idleTimerID) : 0;
1258 } else {
1259 ::KillTimer(MainHWND(), reinterpret_cast<uptr_t>(idler.idlerID));
1260 idler.idlerID = 0;
1262 idler.state = idler.idlerID != 0;
1264 return idler.state;
1267 void ScintillaWin::SetMouseCapture(bool on) {
1268 if (mouseDownCaptures) {
1269 if (on) {
1270 ::SetCapture(MainHWND());
1271 } else {
1272 ::ReleaseCapture();
1275 capturedMouse = on;
1278 bool ScintillaWin::HaveMouseCapture() {
1279 // Cannot just see if GetCapture is this window as the scroll bar also sets capture for the window
1280 return capturedMouse;
1281 //return capturedMouse && (::GetCapture() == MainHWND());
1284 void ScintillaWin::SetTrackMouseLeaveEvent(bool on) {
1285 if (on && TrackMouseEventFn && !trackedMouseLeave) {
1286 TRACKMOUSEEVENT tme;
1287 tme.cbSize = sizeof(tme);
1288 tme.dwFlags = TME_LEAVE;
1289 tme.hwndTrack = MainHWND();
1290 TrackMouseEventFn(&tme);
1292 trackedMouseLeave = on;
1295 bool ScintillaWin::PaintContains(PRectangle rc) {
1296 bool contains = true;
1297 if ((paintState == painting) && (!rc.Empty())) {
1298 if (!rcPaint.Contains(rc)) {
1299 contains = false;
1300 } else {
1301 // In bounding rectangle so check more accurately using region
1302 HRGN hRgnRange = ::CreateRectRgn(rc.left, rc.top, rc.right, rc.bottom);
1303 if (hRgnRange) {
1304 HRGN hRgnDest = ::CreateRectRgn(0, 0, 0, 0);
1305 if (hRgnDest) {
1306 int combination = ::CombineRgn(hRgnDest, hRgnRange, hRgnUpdate, RGN_DIFF);
1307 if (combination != NULLREGION) {
1308 contains = false;
1310 ::DeleteRgn(hRgnDest);
1312 ::DeleteRgn(hRgnRange);
1316 return contains;
1319 void ScintillaWin::ScrollText(int /* linesToMove */) {
1320 //Platform::DebugPrintf("ScintillaWin::ScrollText %d\n", linesToMove);
1321 //::ScrollWindow(MainHWND(), 0,
1322 // vs.lineHeight * linesToMove, 0, 0);
1323 //::UpdateWindow(MainHWND());
1324 Redraw();
1327 void ScintillaWin::UpdateSystemCaret() {
1328 if (hasFocus) {
1329 if (HasCaretSizeChanged()) {
1330 DestroySystemCaret();
1331 CreateSystemCaret();
1333 Point pos = PointMainCaret();
1334 ::SetCaretPos(pos.x, pos.y);
1338 int ScintillaWin::SetScrollInfo(int nBar, LPCSCROLLINFO lpsi, BOOL bRedraw) {
1339 return ::SetScrollInfo(MainHWND(), nBar, lpsi, bRedraw);
1342 bool ScintillaWin::GetScrollInfo(int nBar, LPSCROLLINFO lpsi) {
1343 return ::GetScrollInfo(MainHWND(), nBar, lpsi) ? true : false;
1346 // Change the scroll position but avoid repaint if changing to same value
1347 void ScintillaWin::ChangeScrollPos(int barType, int pos) {
1348 SCROLLINFO sci = {
1349 sizeof(sci), 0, 0, 0, 0, 0, 0
1351 sci.fMask = SIF_POS;
1352 GetScrollInfo(barType, &sci);
1353 if (sci.nPos != pos) {
1354 DwellEnd(true);
1355 sci.nPos = pos;
1356 SetScrollInfo(barType, &sci, TRUE);
1360 void ScintillaWin::SetVerticalScrollPos() {
1361 ChangeScrollPos(SB_VERT, topLine);
1364 void ScintillaWin::SetHorizontalScrollPos() {
1365 ChangeScrollPos(SB_HORZ, xOffset);
1368 bool ScintillaWin::ModifyScrollBars(int nMax, int nPage) {
1369 bool modified = false;
1370 SCROLLINFO sci = {
1371 sizeof(sci), 0, 0, 0, 0, 0, 0
1373 sci.fMask = SIF_PAGE | SIF_RANGE;
1374 GetScrollInfo(SB_VERT, &sci);
1375 int vertEndPreferred = nMax;
1376 if (!verticalScrollBarVisible)
1377 nPage = vertEndPreferred + 1;
1378 if ((sci.nMin != 0) ||
1379 (sci.nMax != vertEndPreferred) ||
1380 (sci.nPage != static_cast<unsigned int>(nPage)) ||
1381 (sci.nPos != 0)) {
1382 sci.fMask = SIF_PAGE | SIF_RANGE;
1383 sci.nMin = 0;
1384 sci.nMax = vertEndPreferred;
1385 sci.nPage = nPage;
1386 sci.nPos = 0;
1387 sci.nTrackPos = 1;
1388 SetScrollInfo(SB_VERT, &sci, TRUE);
1389 modified = true;
1392 PRectangle rcText = GetTextRectangle();
1393 int horizEndPreferred = scrollWidth;
1394 if (horizEndPreferred < 0)
1395 horizEndPreferred = 0;
1396 unsigned int pageWidth = rcText.Width();
1397 if (!horizontalScrollBarVisible || Wrapping())
1398 pageWidth = horizEndPreferred + 1;
1399 sci.fMask = SIF_PAGE | SIF_RANGE;
1400 GetScrollInfo(SB_HORZ, &sci);
1401 if ((sci.nMin != 0) ||
1402 (sci.nMax != horizEndPreferred) ||
1403 (sci.nPage != pageWidth) ||
1404 (sci.nPos != 0)) {
1405 sci.fMask = SIF_PAGE | SIF_RANGE;
1406 sci.nMin = 0;
1407 sci.nMax = horizEndPreferred;
1408 sci.nPage = pageWidth;
1409 sci.nPos = 0;
1410 sci.nTrackPos = 1;
1411 SetScrollInfo(SB_HORZ, &sci, TRUE);
1412 modified = true;
1413 if (scrollWidth < static_cast<int>(pageWidth)) {
1414 HorizontalScrollTo(0);
1417 return modified;
1420 void ScintillaWin::NotifyChange() {
1421 ::SendMessage(::GetParent(MainHWND()), WM_COMMAND,
1422 MAKELONG(GetCtrlID(), SCEN_CHANGE),
1423 reinterpret_cast<LPARAM>(MainHWND()));
1426 void ScintillaWin::NotifyFocus(bool focus) {
1427 ::SendMessage(::GetParent(MainHWND()), WM_COMMAND,
1428 MAKELONG(GetCtrlID(), focus ? SCEN_SETFOCUS : SCEN_KILLFOCUS),
1429 reinterpret_cast<LPARAM>(MainHWND()));
1430 Editor::NotifyFocus(focus);
1433 void ScintillaWin::SetCtrlID(int identifier) {
1434 ::SetWindowID(reinterpret_cast<HWND>(wMain.GetID()), identifier);
1437 int ScintillaWin::GetCtrlID() {
1438 return ::GetDlgCtrlID(reinterpret_cast<HWND>(wMain.GetID()));
1441 void ScintillaWin::NotifyParent(SCNotification scn) {
1442 scn.nmhdr.hwndFrom = MainHWND();
1443 scn.nmhdr.idFrom = GetCtrlID();
1444 ::SendMessage(::GetParent(MainHWND()), WM_NOTIFY,
1445 GetCtrlID(), reinterpret_cast<LPARAM>(&scn));
1448 void ScintillaWin::NotifyParent(SCNotification * scn) {
1449 scn->nmhdr.hwndFrom = MainHWND();
1450 scn->nmhdr.idFrom = GetCtrlID();
1451 ::SendMessage(::GetParent(MainHWND()), WM_NOTIFY,
1452 GetCtrlID(), reinterpret_cast<LPARAM>(scn));
1455 void ScintillaWin::NotifyDoubleClick(Point pt, bool shift, bool ctrl, bool alt) {
1456 //Platform::DebugPrintf("ScintillaWin Double click 0\n");
1457 ScintillaBase::NotifyDoubleClick(pt, shift, ctrl, alt);
1458 // Send myself a WM_LBUTTONDBLCLK, so the container can handle it too.
1459 ::SendMessage(MainHWND(),
1460 WM_LBUTTONDBLCLK,
1461 shift ? MK_SHIFT : 0,
1462 MAKELPARAM(pt.x, pt.y));
1465 class CaseFolderDBCS : public CaseFolderTable {
1466 // Allocate the expandable storage here so that it does not need to be reallocated
1467 // for each call to Fold.
1468 std::vector<wchar_t> utf16Mixed;
1469 std::vector<wchar_t> utf16Folded;
1470 UINT cp;
1471 public:
1472 CaseFolderDBCS(UINT cp_) : cp(cp_) {
1473 StandardASCII();
1475 virtual size_t Fold(char *folded, size_t sizeFolded, const char *mixed, size_t lenMixed) {
1476 if ((lenMixed == 1) && (sizeFolded > 0)) {
1477 folded[0] = mapping[static_cast<unsigned char>(mixed[0])];
1478 return 1;
1479 } else {
1480 if (lenMixed > utf16Mixed.size()) {
1481 utf16Mixed.resize(lenMixed + 8);
1483 size_t nUtf16Mixed = ::MultiByteToWideChar(cp, 0, mixed,
1484 static_cast<int>(lenMixed),
1485 &utf16Mixed[0],
1486 static_cast<int>(utf16Mixed.size()));
1488 if (nUtf16Mixed == 0) {
1489 // Failed to convert -> bad input
1490 folded[0] = '\0';
1491 return 1;
1494 unsigned int lenFlat = 0;
1495 for (size_t mixIndex=0; mixIndex < nUtf16Mixed; mixIndex++) {
1496 if ((lenFlat + 20) > utf16Folded.size())
1497 utf16Folded.resize(lenFlat + 60);
1498 const char *foldedUTF8 = CaseConvert(utf16Mixed[mixIndex], CaseConversionFold);
1499 if (foldedUTF8) {
1500 // Maximum length of a case conversion is 6 bytes, 3 characters
1501 wchar_t wFolded[20];
1502 unsigned int charsConverted = UTF16FromUTF8(foldedUTF8,
1503 static_cast<unsigned int>(strlen(foldedUTF8)),
1504 wFolded, sizeof(wFolded)/sizeof(wFolded[0]));
1505 for (size_t j=0;j<charsConverted;j++)
1506 utf16Folded[lenFlat++] = wFolded[j];
1507 } else {
1508 utf16Folded[lenFlat++] = utf16Mixed[mixIndex];
1512 size_t lenOut = ::WideCharToMultiByte(cp, 0,
1513 &utf16Folded[0], lenFlat,
1514 NULL, 0, NULL, 0);
1516 if (lenOut < sizeFolded) {
1517 ::WideCharToMultiByte(cp, 0,
1518 &utf16Folded[0], lenFlat,
1519 folded, static_cast<int>(lenOut), NULL, 0);
1520 return lenOut;
1521 } else {
1522 return 0;
1528 CaseFolder *ScintillaWin::CaseFolderForEncoding() {
1529 UINT cpDest = CodePageOfDocument();
1530 if (cpDest == SC_CP_UTF8) {
1531 return new CaseFolderUnicode();
1532 } else {
1533 if (pdoc->dbcsCodePage == 0) {
1534 CaseFolderTable *pcf = new CaseFolderTable();
1535 pcf->StandardASCII();
1536 // Only for single byte encodings
1537 UINT cpDoc = CodePageOfDocument();
1538 for (int i=0x80; i<0x100; i++) {
1539 char sCharacter[2] = "A";
1540 sCharacter[0] = static_cast<char>(i);
1541 wchar_t wCharacter[20];
1542 unsigned int lengthUTF16 = ::MultiByteToWideChar(cpDoc, 0, sCharacter, 1,
1543 wCharacter, sizeof(wCharacter)/sizeof(wCharacter[0]));
1544 if (lengthUTF16 == 1) {
1545 const char *caseFolded = CaseConvert(wCharacter[0], CaseConversionFold);
1546 if (caseFolded) {
1547 wchar_t wLower[20];
1548 unsigned int charsConverted = UTF16FromUTF8(caseFolded,
1549 static_cast<unsigned int>(strlen(caseFolded)),
1550 wLower, sizeof(wLower)/sizeof(wLower[0]));
1551 if (charsConverted == 1) {
1552 char sCharacterLowered[20];
1553 unsigned int lengthConverted = ::WideCharToMultiByte(cpDoc, 0,
1554 wLower, charsConverted,
1555 sCharacterLowered, sizeof(sCharacterLowered), NULL, 0);
1556 if ((lengthConverted == 1) && (sCharacter[0] != sCharacterLowered[0])) {
1557 pcf->SetTranslation(sCharacter[0], sCharacterLowered[0]);
1563 return pcf;
1564 } else {
1565 return new CaseFolderDBCS(cpDest);
1570 std::string ScintillaWin::CaseMapString(const std::string &s, int caseMapping) {
1571 if ((s.size() == 0) || (caseMapping == cmSame))
1572 return s;
1574 UINT cpDoc = CodePageOfDocument();
1575 if (cpDoc == SC_CP_UTF8) {
1576 std::string retMapped(s.length() * maxExpansionCaseConversion, 0);
1577 size_t lenMapped = CaseConvertString(&retMapped[0], retMapped.length(), s.c_str(), s.length(),
1578 (caseMapping == cmUpper) ? CaseConversionUpper : CaseConversionLower);
1579 retMapped.resize(lenMapped);
1580 return retMapped;
1583 unsigned int lengthUTF16 = ::MultiByteToWideChar(cpDoc, 0, s.c_str(),
1584 static_cast<int>(s.size()), NULL, 0);
1585 if (lengthUTF16 == 0) // Failed to convert
1586 return s;
1588 DWORD mapFlags = LCMAP_LINGUISTIC_CASING |
1589 ((caseMapping == cmUpper) ? LCMAP_UPPERCASE : LCMAP_LOWERCASE);
1591 // Change text to UTF-16
1592 std::vector<wchar_t> vwcText(lengthUTF16);
1593 ::MultiByteToWideChar(cpDoc, 0, s.c_str(), static_cast<int>(s.size()), &vwcText[0], lengthUTF16);
1595 // Change case
1596 int charsConverted = ::LCMapStringW(LOCALE_SYSTEM_DEFAULT, mapFlags,
1597 &vwcText[0], lengthUTF16, NULL, 0);
1598 std::vector<wchar_t> vwcConverted(charsConverted);
1599 ::LCMapStringW(LOCALE_SYSTEM_DEFAULT, mapFlags,
1600 &vwcText[0], lengthUTF16, &vwcConverted[0], charsConverted);
1602 // Change back to document encoding
1603 unsigned int lengthConverted = ::WideCharToMultiByte(cpDoc, 0,
1604 &vwcConverted[0], static_cast<int>(vwcConverted.size()),
1605 NULL, 0, NULL, 0);
1606 std::vector<char> vcConverted(lengthConverted);
1607 ::WideCharToMultiByte(cpDoc, 0,
1608 &vwcConverted[0], static_cast<int>(vwcConverted.size()),
1609 &vcConverted[0], static_cast<int>(vcConverted.size()), NULL, 0);
1611 return std::string(&vcConverted[0], vcConverted.size());
1614 void ScintillaWin::Copy() {
1615 //Platform::DebugPrintf("Copy\n");
1616 if (!sel.Empty()) {
1617 SelectionText selectedText;
1618 CopySelectionRange(&selectedText);
1619 CopyToClipboard(selectedText);
1623 void ScintillaWin::CopyAllowLine() {
1624 SelectionText selectedText;
1625 CopySelectionRange(&selectedText, true);
1626 CopyToClipboard(selectedText);
1629 bool ScintillaWin::CanPaste() {
1630 if (!Editor::CanPaste())
1631 return false;
1632 if (::IsClipboardFormatAvailable(CF_TEXT))
1633 return true;
1634 if (IsUnicodeMode())
1635 return ::IsClipboardFormatAvailable(CF_UNICODETEXT) != 0;
1636 return false;
1639 class GlobalMemory {
1640 HGLOBAL hand;
1641 public:
1642 void *ptr;
1643 GlobalMemory() : hand(0), ptr(0) {
1645 GlobalMemory(HGLOBAL hand_) : hand(hand_), ptr(0) {
1646 if (hand) {
1647 ptr = ::GlobalLock(hand);
1650 ~GlobalMemory() {
1651 PLATFORM_ASSERT(!ptr);
1653 void Allocate(size_t bytes) {
1654 hand = ::GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT, bytes);
1655 if (hand) {
1656 ptr = ::GlobalLock(hand);
1659 HGLOBAL Unlock() {
1660 PLATFORM_ASSERT(ptr);
1661 HGLOBAL handCopy = hand;
1662 ::GlobalUnlock(hand);
1663 ptr = 0;
1664 hand = 0;
1665 return handCopy;
1667 void SetClip(UINT uFormat) {
1668 ::SetClipboardData(uFormat, Unlock());
1670 operator bool() const {
1671 return ptr != 0;
1673 SIZE_T Size() {
1674 return ::GlobalSize(hand);
1678 void ScintillaWin::InsertPasteText(const char *text, int len, SelectionPosition selStart, bool isRectangular, bool isLine) {
1679 if (isRectangular) {
1680 PasteRectangular(selStart, text, len);
1681 } else {
1682 std::string convertedText;
1683 if (convertPastes) {
1684 // Convert line endings of the paste into our local line-endings mode
1685 convertedText = Document::TransformLineEnds(text, len, pdoc->eolMode);
1686 len = static_cast<int>(convertedText.length());
1687 text = convertedText.c_str();
1689 if (isLine) {
1690 int insertPos = pdoc->LineStart(pdoc->LineFromPosition(sel.MainCaret()));
1691 pdoc->InsertString(insertPos, text, len);
1692 // add the newline if necessary
1693 if ((len > 0) && (text[len-1] != '\n' && text[len-1] != '\r')) {
1694 const char *endline = StringFromEOLMode(pdoc->eolMode);
1695 pdoc->InsertString(insertPos + len, endline, static_cast<int>(strlen(endline)));
1696 len += static_cast<int>(strlen(endline));
1698 if (sel.MainCaret() == insertPos) {
1699 SetEmptySelection(sel.MainCaret() + len);
1701 } else {
1702 InsertPaste(selStart, text, len);
1707 void ScintillaWin::Paste() {
1708 if (!::OpenClipboard(MainHWND()))
1709 return;
1710 UndoGroup ug(pdoc);
1711 bool isLine = SelectionEmpty() && (::IsClipboardFormatAvailable(cfLineSelect) != 0);
1712 ClearSelection(multiPasteMode == SC_MULTIPASTE_EACH);
1713 SelectionPosition selStart = sel.IsRectangular() ?
1714 sel.Rectangular().Start() :
1715 sel.Range(sel.Main()).Start();
1716 bool isRectangular = (::IsClipboardFormatAvailable(cfColumnSelect) != 0);
1718 if (!isRectangular) {
1719 // Evaluate "Borland IDE Block Type" explicitly
1720 GlobalMemory memBorlandSelection(::GetClipboardData(cfBorlandIDEBlockType));
1721 if (memBorlandSelection) {
1722 isRectangular = (memBorlandSelection.Size() == 1) && (static_cast<BYTE *>(memBorlandSelection.ptr)[0] == 0x02);
1723 memBorlandSelection.Unlock();
1727 // Always use CF_UNICODETEXT if available
1728 GlobalMemory memUSelection(::GetClipboardData(CF_UNICODETEXT));
1729 if (memUSelection) {
1730 wchar_t *uptr = static_cast<wchar_t *>(memUSelection.ptr);
1731 if (uptr) {
1732 unsigned int len;
1733 std::vector<char> putf;
1734 // Default Scintilla behaviour in Unicode mode
1735 if (IsUnicodeMode()) {
1736 unsigned int bytes = memUSelection.Size();
1737 len = UTF8Length(uptr, bytes / 2);
1738 putf.resize(len + 1);
1739 UTF8FromUTF16(uptr, bytes / 2, &putf[0], len);
1740 } else {
1741 // CF_UNICODETEXT available, but not in Unicode mode
1742 // Convert from Unicode to current Scintilla code page
1743 UINT cpDest = CodePageOfDocument();
1744 len = ::WideCharToMultiByte(cpDest, 0, uptr, -1,
1745 NULL, 0, NULL, NULL) - 1; // subtract 0 terminator
1746 putf.resize(len + 1);
1747 ::WideCharToMultiByte(cpDest, 0, uptr, -1,
1748 &putf[0], len + 1, NULL, NULL);
1751 InsertPasteText(&putf[0], len, selStart, isRectangular, isLine);
1753 memUSelection.Unlock();
1754 } else {
1755 // CF_UNICODETEXT not available, paste ANSI text
1756 GlobalMemory memSelection(::GetClipboardData(CF_TEXT));
1757 if (memSelection) {
1758 char *ptr = static_cast<char *>(memSelection.ptr);
1759 if (ptr) {
1760 unsigned int bytes = memSelection.Size();
1761 unsigned int len = bytes;
1762 for (unsigned int i = 0; i < bytes; i++) {
1763 if ((len == bytes) && (0 == ptr[i]))
1764 len = i;
1767 // In Unicode mode, convert clipboard text to UTF-8
1768 if (IsUnicodeMode()) {
1769 std::vector<wchar_t> uptr(len+1);
1771 unsigned int ulen = ::MultiByteToWideChar(CP_ACP, 0,
1772 ptr, len, &uptr[0], len+1);
1774 unsigned int mlen = UTF8Length(&uptr[0], ulen);
1775 std::vector<char> putf(mlen+1);
1776 // CP_UTF8 not available on Windows 95, so use UTF8FromUTF16()
1777 UTF8FromUTF16(&uptr[0], ulen, &putf[0], mlen);
1779 InsertPasteText(&putf[0], mlen, selStart, isRectangular, isLine);
1780 } else {
1781 InsertPasteText(ptr, len, selStart, isRectangular, isLine);
1784 memSelection.Unlock();
1787 ::CloseClipboard();
1788 Redraw();
1791 void ScintillaWin::CreateCallTipWindow(PRectangle) {
1792 if (!ct.wCallTip.Created()) {
1793 ct.wCallTip = ::CreateWindow(callClassName, TEXT("ACallTip"),
1794 WS_POPUP, 100, 100, 150, 20,
1795 MainHWND(), 0,
1796 GetWindowInstance(MainHWND()),
1797 this);
1798 ct.wDraw = ct.wCallTip;
1802 void ScintillaWin::AddToPopUp(const char *label, int cmd, bool enabled) {
1803 HMENU hmenuPopup = reinterpret_cast<HMENU>(popup.GetID());
1804 if (!label[0])
1805 ::AppendMenuA(hmenuPopup, MF_SEPARATOR, 0, "");
1806 else if (enabled)
1807 ::AppendMenuA(hmenuPopup, MF_STRING, cmd, label);
1808 else
1809 ::AppendMenuA(hmenuPopup, MF_STRING | MF_DISABLED | MF_GRAYED, cmd, label);
1812 void ScintillaWin::ClaimSelection() {
1813 // Windows does not have a primary selection
1816 /// Implement IUnknown
1818 STDMETHODIMP_(ULONG)FormatEnumerator_AddRef(FormatEnumerator *fe);
1819 STDMETHODIMP FormatEnumerator_QueryInterface(FormatEnumerator *fe, REFIID riid, PVOID *ppv) {
1820 //Platform::DebugPrintf("EFE QI");
1821 *ppv = NULL;
1822 if (riid == IID_IUnknown)
1823 *ppv = reinterpret_cast<IEnumFORMATETC *>(fe);
1824 if (riid == IID_IEnumFORMATETC)
1825 *ppv = reinterpret_cast<IEnumFORMATETC *>(fe);
1826 if (!*ppv)
1827 return E_NOINTERFACE;
1828 FormatEnumerator_AddRef(fe);
1829 return S_OK;
1831 STDMETHODIMP_(ULONG)FormatEnumerator_AddRef(FormatEnumerator *fe) {
1832 return ++fe->ref;
1834 STDMETHODIMP_(ULONG)FormatEnumerator_Release(FormatEnumerator *fe) {
1835 fe->ref--;
1836 if (fe->ref > 0)
1837 return fe->ref;
1838 delete fe;
1839 return 0;
1841 /// Implement IEnumFORMATETC
1842 STDMETHODIMP FormatEnumerator_Next(FormatEnumerator *fe, ULONG celt, FORMATETC *rgelt, ULONG *pceltFetched) {
1843 //Platform::DebugPrintf("EFE Next %d %d", fe->pos, celt);
1844 if (rgelt == NULL) return E_POINTER;
1845 // We only support one format, so this is simple.
1846 unsigned int putPos = 0;
1847 while ((fe->pos < fe->formatsLen) && (putPos < celt)) {
1848 rgelt->cfFormat = fe->formats[fe->pos];
1849 rgelt->ptd = 0;
1850 rgelt->dwAspect = DVASPECT_CONTENT;
1851 rgelt->lindex = -1;
1852 rgelt->tymed = TYMED_HGLOBAL;
1853 fe->pos++;
1854 putPos++;
1856 if (pceltFetched)
1857 *pceltFetched = putPos;
1858 return putPos ? S_OK : S_FALSE;
1860 STDMETHODIMP FormatEnumerator_Skip(FormatEnumerator *fe, ULONG celt) {
1861 fe->pos += celt;
1862 return S_OK;
1864 STDMETHODIMP FormatEnumerator_Reset(FormatEnumerator *fe) {
1865 fe->pos = 0;
1866 return S_OK;
1868 STDMETHODIMP FormatEnumerator_Clone(FormatEnumerator *fe, IEnumFORMATETC **ppenum) {
1869 FormatEnumerator *pfe;
1870 try {
1871 pfe = new FormatEnumerator(fe->pos, fe->formats, fe->formatsLen);
1872 } catch (...) {
1873 return E_OUTOFMEMORY;
1875 return FormatEnumerator_QueryInterface(pfe, IID_IEnumFORMATETC,
1876 reinterpret_cast<void **>(ppenum));
1879 static VFunction *vtFormatEnumerator[] = {
1880 (VFunction *)(FormatEnumerator_QueryInterface),
1881 (VFunction *)(FormatEnumerator_AddRef),
1882 (VFunction *)(FormatEnumerator_Release),
1883 (VFunction *)(FormatEnumerator_Next),
1884 (VFunction *)(FormatEnumerator_Skip),
1885 (VFunction *)(FormatEnumerator_Reset),
1886 (VFunction *)(FormatEnumerator_Clone)
1889 FormatEnumerator::FormatEnumerator(int pos_, CLIPFORMAT formats_[], int formatsLen_) {
1890 vtbl = vtFormatEnumerator;
1891 ref = 0; // First QI adds first reference...
1892 pos = pos_;
1893 formatsLen = formatsLen_;
1894 for (int i=0; i<formatsLen; i++)
1895 formats[i] = formats_[i];
1898 /// Implement IUnknown
1899 STDMETHODIMP DropSource_QueryInterface(DropSource *ds, REFIID riid, PVOID *ppv) {
1900 return ds->sci->QueryInterface(riid, ppv);
1902 STDMETHODIMP_(ULONG)DropSource_AddRef(DropSource *ds) {
1903 return ds->sci->AddRef();
1905 STDMETHODIMP_(ULONG)DropSource_Release(DropSource *ds) {
1906 return ds->sci->Release();
1909 /// Implement IDropSource
1910 STDMETHODIMP DropSource_QueryContinueDrag(DropSource *, BOOL fEsc, DWORD grfKeyState) {
1911 if (fEsc)
1912 return DRAGDROP_S_CANCEL;
1913 if (!(grfKeyState & MK_LBUTTON))
1914 return DRAGDROP_S_DROP;
1915 return S_OK;
1918 STDMETHODIMP DropSource_GiveFeedback(DropSource *, DWORD) {
1919 return DRAGDROP_S_USEDEFAULTCURSORS;
1922 static VFunction *vtDropSource[] = {
1923 (VFunction *)(DropSource_QueryInterface),
1924 (VFunction *)(DropSource_AddRef),
1925 (VFunction *)(DropSource_Release),
1926 (VFunction *)(DropSource_QueryContinueDrag),
1927 (VFunction *)(DropSource_GiveFeedback)
1930 DropSource::DropSource() {
1931 vtbl = vtDropSource;
1932 sci = 0;
1935 /// Implement IUnkown
1936 STDMETHODIMP DataObject_QueryInterface(DataObject *pd, REFIID riid, PVOID *ppv) {
1937 //Platform::DebugPrintf("DO QI %x\n", pd);
1938 return pd->sci->QueryInterface(riid, ppv);
1940 STDMETHODIMP_(ULONG)DataObject_AddRef(DataObject *pd) {
1941 return pd->sci->AddRef();
1943 STDMETHODIMP_(ULONG)DataObject_Release(DataObject *pd) {
1944 return pd->sci->Release();
1946 /// Implement IDataObject
1947 STDMETHODIMP DataObject_GetData(DataObject *pd, FORMATETC *pFEIn, STGMEDIUM *pSTM) {
1948 return pd->sci->GetData(pFEIn, pSTM);
1951 STDMETHODIMP DataObject_GetDataHere(DataObject *, FORMATETC *, STGMEDIUM *) {
1952 //Platform::DebugPrintf("DOB GetDataHere\n");
1953 return E_NOTIMPL;
1956 STDMETHODIMP DataObject_QueryGetData(DataObject *pd, FORMATETC *pFE) {
1957 if (pd->sci->DragIsRectangularOK(pFE->cfFormat) &&
1958 pFE->ptd == 0 &&
1959 (pFE->dwAspect & DVASPECT_CONTENT) != 0 &&
1960 pFE->lindex == -1 &&
1961 (pFE->tymed & TYMED_HGLOBAL) != 0
1963 return S_OK;
1966 bool formatOK = (pFE->cfFormat == CF_TEXT) ||
1967 ((pFE->cfFormat == CF_UNICODETEXT) && pd->sci->IsUnicodeMode());
1968 if (!formatOK ||
1969 pFE->ptd != 0 ||
1970 (pFE->dwAspect & DVASPECT_CONTENT) == 0 ||
1971 pFE->lindex != -1 ||
1972 (pFE->tymed & TYMED_HGLOBAL) == 0
1974 //Platform::DebugPrintf("DOB QueryGetData No %x\n",pFE->cfFormat);
1975 //return DATA_E_FORMATETC;
1976 return S_FALSE;
1978 //Platform::DebugPrintf("DOB QueryGetData OK %x\n",pFE->cfFormat);
1979 return S_OK;
1982 STDMETHODIMP DataObject_GetCanonicalFormatEtc(DataObject *pd, FORMATETC *, FORMATETC *pFEOut) {
1983 //Platform::DebugPrintf("DOB GetCanon\n");
1984 if (pd->sci->IsUnicodeMode())
1985 pFEOut->cfFormat = CF_UNICODETEXT;
1986 else
1987 pFEOut->cfFormat = CF_TEXT;
1988 pFEOut->ptd = 0;
1989 pFEOut->dwAspect = DVASPECT_CONTENT;
1990 pFEOut->lindex = -1;
1991 pFEOut->tymed = TYMED_HGLOBAL;
1992 return S_OK;
1995 STDMETHODIMP DataObject_SetData(DataObject *, FORMATETC *, STGMEDIUM *, BOOL) {
1996 //Platform::DebugPrintf("DOB SetData\n");
1997 return E_FAIL;
2000 STDMETHODIMP DataObject_EnumFormatEtc(DataObject *pd, DWORD dwDirection, IEnumFORMATETC **ppEnum) {
2001 try {
2002 //Platform::DebugPrintf("DOB EnumFormatEtc %d\n", dwDirection);
2003 if (dwDirection != DATADIR_GET) {
2004 *ppEnum = 0;
2005 return E_FAIL;
2007 FormatEnumerator *pfe;
2008 if (pd->sci->IsUnicodeMode()) {
2009 CLIPFORMAT formats[] = {CF_UNICODETEXT, CF_TEXT};
2010 pfe = new FormatEnumerator(0, formats, 2);
2011 } else {
2012 CLIPFORMAT formats[] = {CF_TEXT};
2013 pfe = new FormatEnumerator(0, formats, 1);
2015 return FormatEnumerator_QueryInterface(pfe, IID_IEnumFORMATETC,
2016 reinterpret_cast<void **>(ppEnum));
2017 } catch (std::bad_alloc &) {
2018 pd->sci->errorStatus = SC_STATUS_BADALLOC;
2019 return E_OUTOFMEMORY;
2020 } catch (...) {
2021 pd->sci->errorStatus = SC_STATUS_FAILURE;
2022 return E_FAIL;
2026 STDMETHODIMP DataObject_DAdvise(DataObject *, FORMATETC *, DWORD, IAdviseSink *, PDWORD) {
2027 //Platform::DebugPrintf("DOB DAdvise\n");
2028 return E_FAIL;
2031 STDMETHODIMP DataObject_DUnadvise(DataObject *, DWORD) {
2032 //Platform::DebugPrintf("DOB DUnadvise\n");
2033 return E_FAIL;
2036 STDMETHODIMP DataObject_EnumDAdvise(DataObject *, IEnumSTATDATA **) {
2037 //Platform::DebugPrintf("DOB EnumDAdvise\n");
2038 return E_FAIL;
2041 static VFunction *vtDataObject[] = {
2042 (VFunction *)(DataObject_QueryInterface),
2043 (VFunction *)(DataObject_AddRef),
2044 (VFunction *)(DataObject_Release),
2045 (VFunction *)(DataObject_GetData),
2046 (VFunction *)(DataObject_GetDataHere),
2047 (VFunction *)(DataObject_QueryGetData),
2048 (VFunction *)(DataObject_GetCanonicalFormatEtc),
2049 (VFunction *)(DataObject_SetData),
2050 (VFunction *)(DataObject_EnumFormatEtc),
2051 (VFunction *)(DataObject_DAdvise),
2052 (VFunction *)(DataObject_DUnadvise),
2053 (VFunction *)(DataObject_EnumDAdvise)
2056 DataObject::DataObject() {
2057 vtbl = vtDataObject;
2058 sci = 0;
2061 /// Implement IUnknown
2062 STDMETHODIMP DropTarget_QueryInterface(DropTarget *dt, REFIID riid, PVOID *ppv) {
2063 //Platform::DebugPrintf("DT QI %x\n", dt);
2064 return dt->sci->QueryInterface(riid, ppv);
2066 STDMETHODIMP_(ULONG)DropTarget_AddRef(DropTarget *dt) {
2067 return dt->sci->AddRef();
2069 STDMETHODIMP_(ULONG)DropTarget_Release(DropTarget *dt) {
2070 return dt->sci->Release();
2073 /// Implement IDropTarget by forwarding to Scintilla
2074 STDMETHODIMP DropTarget_DragEnter(DropTarget *dt, LPDATAOBJECT pIDataSource, DWORD grfKeyState,
2075 POINTL pt, PDWORD pdwEffect) {
2076 try {
2077 return dt->sci->DragEnter(pIDataSource, grfKeyState, pt, pdwEffect);
2078 } catch (...) {
2079 dt->sci->errorStatus = SC_STATUS_FAILURE;
2081 return E_FAIL;
2083 STDMETHODIMP DropTarget_DragOver(DropTarget *dt, DWORD grfKeyState, POINTL pt, PDWORD pdwEffect) {
2084 try {
2085 return dt->sci->DragOver(grfKeyState, pt, pdwEffect);
2086 } catch (...) {
2087 dt->sci->errorStatus = SC_STATUS_FAILURE;
2089 return E_FAIL;
2091 STDMETHODIMP DropTarget_DragLeave(DropTarget *dt) {
2092 try {
2093 return dt->sci->DragLeave();
2094 } catch (...) {
2095 dt->sci->errorStatus = SC_STATUS_FAILURE;
2097 return E_FAIL;
2099 STDMETHODIMP DropTarget_Drop(DropTarget *dt, LPDATAOBJECT pIDataSource, DWORD grfKeyState,
2100 POINTL pt, PDWORD pdwEffect) {
2101 try {
2102 return dt->sci->Drop(pIDataSource, grfKeyState, pt, pdwEffect);
2103 } catch (...) {
2104 dt->sci->errorStatus = SC_STATUS_FAILURE;
2106 return E_FAIL;
2109 static VFunction *vtDropTarget[] = {
2110 (VFunction *)(DropTarget_QueryInterface),
2111 (VFunction *)(DropTarget_AddRef),
2112 (VFunction *)(DropTarget_Release),
2113 (VFunction *)(DropTarget_DragEnter),
2114 (VFunction *)(DropTarget_DragOver),
2115 (VFunction *)(DropTarget_DragLeave),
2116 (VFunction *)(DropTarget_Drop)
2119 DropTarget::DropTarget() {
2120 vtbl = vtDropTarget;
2121 sci = 0;
2125 * DBCS: support Input Method Editor (IME).
2126 * Called when IME Window opened.
2128 void ScintillaWin::ImeStartComposition() {
2129 if (caret.active) {
2130 // Move IME Window to current caret position
2131 HIMC hIMC = ::ImmGetContext(MainHWND());
2132 Point pos = PointMainCaret();
2133 COMPOSITIONFORM CompForm;
2134 CompForm.dwStyle = CFS_POINT;
2135 CompForm.ptCurrentPos.x = pos.x;
2136 CompForm.ptCurrentPos.y = pos.y;
2138 ::ImmSetCompositionWindow(hIMC, &CompForm);
2140 // Set font of IME window to same as surrounded text.
2141 if (stylesValid) {
2142 // Since the style creation code has been made platform independent,
2143 // The logfont for the IME is recreated here.
2144 int styleHere = (pdoc->StyleAt(sel.MainCaret())) & 31;
2145 LOGFONTA lf = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ""};
2146 int sizeZoomed = vs.styles[styleHere].size + vs.zoomLevel * SC_FONT_SIZE_MULTIPLIER;
2147 if (sizeZoomed <= 2 * SC_FONT_SIZE_MULTIPLIER) // Hangs if sizeZoomed <= 1
2148 sizeZoomed = 2 * SC_FONT_SIZE_MULTIPLIER;
2149 AutoSurface surface(this);
2150 int deviceHeight = sizeZoomed;
2151 if (surface) {
2152 deviceHeight = (sizeZoomed * surface->LogPixelsY()) / 72;
2154 // The negative is to allow for leading
2155 lf.lfHeight = -(abs(deviceHeight / SC_FONT_SIZE_MULTIPLIER));
2156 lf.lfWeight = vs.styles[styleHere].weight;
2157 lf.lfItalic = static_cast<BYTE>(vs.styles[styleHere].italic ? 1 : 0);
2158 lf.lfCharSet = DEFAULT_CHARSET;
2159 lf.lfFaceName[0] = '\0';
2160 if (vs.styles[styleHere].fontName && (strlen(vs.styles[styleHere].fontName) < sizeof(lf.lfFaceName)))
2161 strcpy(lf.lfFaceName, vs.styles[styleHere].fontName);
2163 ::ImmSetCompositionFontA(hIMC, &lf);
2165 ::ImmReleaseContext(MainHWND(), hIMC);
2166 // Caret is displayed in IME window. So, caret in Scintilla is useless.
2167 DropCaret();
2171 /** Called when IME Window closed. */
2172 void ScintillaWin::ImeEndComposition() {
2173 ShowCaretAtCurrentPosition();
2176 void ScintillaWin::AddCharBytes(char b0, char b1) {
2178 int inputCodePage = InputCodePage();
2179 if (inputCodePage && IsUnicodeMode()) {
2180 char utfval[4] = "\0\0\0";
2181 char ansiChars[3];
2182 wchar_t wcs[2];
2183 if (b0) { // Two bytes from IME
2184 ansiChars[0] = b0;
2185 ansiChars[1] = b1;
2186 ansiChars[2] = '\0';
2187 ::MultiByteToWideChar(inputCodePage, 0, ansiChars, 2, wcs, 1);
2188 } else {
2189 ansiChars[0] = b1;
2190 ansiChars[1] = '\0';
2191 ::MultiByteToWideChar(inputCodePage, 0, ansiChars, 1, wcs, 1);
2193 unsigned int len = UTF8Length(wcs, 1);
2194 UTF8FromUTF16(wcs, 1, utfval, len);
2195 utfval[len] = '\0';
2196 AddCharUTF(utfval, len ? len : 1);
2197 } else if (b0) {
2198 char dbcsChars[3];
2199 dbcsChars[0] = b0;
2200 dbcsChars[1] = b1;
2201 dbcsChars[2] = '\0';
2202 AddCharUTF(dbcsChars, 2, true);
2203 } else {
2204 AddChar(b1);
2208 void ScintillaWin::GetIntelliMouseParameters() {
2209 // This retrieves the number of lines per scroll as configured inthe Mouse Properties sheet in Control Panel
2210 ::SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, &linesPerScroll, 0);
2213 void ScintillaWin::CopyToClipboard(const SelectionText &selectedText) {
2214 if (!::OpenClipboard(MainHWND()))
2215 return;
2216 ::EmptyClipboard();
2218 GlobalMemory uniText;
2220 // Default Scintilla behaviour in Unicode mode
2221 if (IsUnicodeMode()) {
2222 int uchars = UTF16Length(selectedText.Data(),
2223 static_cast<int>(selectedText.LengthWithTerminator()));
2224 uniText.Allocate(2 * uchars);
2225 if (uniText) {
2226 UTF16FromUTF8(selectedText.Data(), static_cast<int>(selectedText.LengthWithTerminator()),
2227 static_cast<wchar_t *>(uniText.ptr), uchars);
2229 } else {
2230 // Not Unicode mode
2231 // Convert to Unicode using the current Scintilla code page
2232 UINT cpSrc = CodePageFromCharSet(
2233 selectedText.characterSet, selectedText.codePage);
2234 int uLen = ::MultiByteToWideChar(cpSrc, 0, selectedText.Data(),
2235 static_cast<int>(selectedText.LengthWithTerminator()), 0, 0);
2236 uniText.Allocate(2 * uLen);
2237 if (uniText) {
2238 ::MultiByteToWideChar(cpSrc, 0, selectedText.Data(),
2239 static_cast<int>(selectedText.LengthWithTerminator()),
2240 static_cast<wchar_t *>(uniText.ptr), uLen);
2244 if (uniText) {
2245 if (!IsNT()) {
2246 // Copy ANSI text to clipboard on Windows 9x
2247 // Convert from Unicode text, so other ANSI programs can
2248 // paste the text
2249 // Windows NT, 2k, XP automatically generates CF_TEXT
2250 GlobalMemory ansiText;
2251 ansiText.Allocate(selectedText.LengthWithTerminator());
2252 if (ansiText) {
2253 ::WideCharToMultiByte(CP_ACP, 0, static_cast<wchar_t *>(uniText.ptr), -1,
2254 static_cast<char *>(ansiText.ptr),
2255 static_cast<int>(selectedText.LengthWithTerminator()), NULL, NULL);
2256 ansiText.SetClip(CF_TEXT);
2259 uniText.SetClip(CF_UNICODETEXT);
2260 } else {
2261 // There was a failure - try to copy at least ANSI text
2262 GlobalMemory ansiText;
2263 ansiText.Allocate(selectedText.LengthWithTerminator());
2264 if (ansiText) {
2265 memcpy(static_cast<char *>(ansiText.ptr), selectedText.Data(), selectedText.LengthWithTerminator());
2266 ansiText.SetClip(CF_TEXT);
2270 if (selectedText.rectangular) {
2271 ::SetClipboardData(cfColumnSelect, 0);
2273 GlobalMemory borlandSelection;
2274 borlandSelection.Allocate(1);
2275 if (borlandSelection) {
2276 static_cast<BYTE *>(borlandSelection.ptr)[0] = 0x02;
2277 borlandSelection.SetClip(cfBorlandIDEBlockType);
2281 if (selectedText.lineCopy) {
2282 ::SetClipboardData(cfLineSelect, 0);
2285 ::CloseClipboard();
2288 void ScintillaWin::ScrollMessage(WPARAM wParam) {
2289 //DWORD dwStart = timeGetTime();
2290 //Platform::DebugPrintf("Scroll %x %d\n", wParam, lParam);
2292 SCROLLINFO sci = {};
2293 sci.cbSize = sizeof(sci);
2294 sci.fMask = SIF_ALL;
2296 GetScrollInfo(SB_VERT, &sci);
2298 //Platform::DebugPrintf("ScrollInfo %d mask=%x min=%d max=%d page=%d pos=%d track=%d\n", b,sci.fMask,
2299 //sci.nMin, sci.nMax, sci.nPage, sci.nPos, sci.nTrackPos);
2301 int topLineNew = topLine;
2302 switch (LoWord(wParam)) {
2303 case SB_LINEUP:
2304 topLineNew -= 1;
2305 break;
2306 case SB_LINEDOWN:
2307 topLineNew += 1;
2308 break;
2309 case SB_PAGEUP:
2310 topLineNew -= LinesToScroll(); break;
2311 case SB_PAGEDOWN: topLineNew += LinesToScroll(); break;
2312 case SB_TOP: topLineNew = 0; break;
2313 case SB_BOTTOM: topLineNew = MaxScrollPos(); break;
2314 case SB_THUMBPOSITION: topLineNew = sci.nTrackPos; break;
2315 case SB_THUMBTRACK: topLineNew = sci.nTrackPos; break;
2317 ScrollTo(topLineNew);
2320 void ScintillaWin::HorizontalScrollMessage(WPARAM wParam) {
2321 int xPos = xOffset;
2322 PRectangle rcText = GetTextRectangle();
2323 int pageWidth = rcText.Width() * 2 / 3;
2324 switch (LoWord(wParam)) {
2325 case SB_LINEUP:
2326 xPos -= 20;
2327 break;
2328 case SB_LINEDOWN: // May move past the logical end
2329 xPos += 20;
2330 break;
2331 case SB_PAGEUP:
2332 xPos -= pageWidth;
2333 break;
2334 case SB_PAGEDOWN:
2335 xPos += pageWidth;
2336 if (xPos > scrollWidth - rcText.Width()) { // Hit the end exactly
2337 xPos = scrollWidth - rcText.Width();
2339 break;
2340 case SB_TOP:
2341 xPos = 0;
2342 break;
2343 case SB_BOTTOM:
2344 xPos = scrollWidth;
2345 break;
2346 case SB_THUMBPOSITION:
2347 case SB_THUMBTRACK: {
2348 // 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 =]
2349 SCROLLINFO si;
2350 si.cbSize = sizeof(si);
2351 si.fMask = SIF_TRACKPOS;
2352 if (GetScrollInfo(SB_HORZ, &si)) {
2353 xPos = si.nTrackPos;
2356 break;
2358 HorizontalScrollTo(xPos);
2362 * Redraw all of text area.
2363 * This paint will not be abandoned.
2365 void ScintillaWin::FullPaint() {
2366 if (technology == SC_TECHNOLOGY_DEFAULT) {
2367 HDC hdc = ::GetDC(MainHWND());
2368 FullPaintDC(hdc);
2369 ::ReleaseDC(MainHWND(), hdc);
2370 } else {
2371 FullPaintDC(0);
2376 * Redraw all of text area on the specified DC.
2377 * This paint will not be abandoned.
2379 void ScintillaWin::FullPaintDC(HDC hdc) {
2380 paintState = painting;
2381 rcPaint = GetClientRectangle();
2382 paintingAllText = true;
2383 if (technology == SC_TECHNOLOGY_DEFAULT) {
2384 AutoSurface surfaceWindow(hdc, this);
2385 if (surfaceWindow) {
2386 Paint(surfaceWindow, rcPaint);
2387 surfaceWindow->Release();
2389 } else {
2390 #if defined(USE_D2D)
2391 EnsureRenderTarget();
2392 AutoSurface surfaceWindow(pRenderTarget, this);
2393 if (surfaceWindow) {
2394 pRenderTarget->BeginDraw();
2395 Paint(surfaceWindow, rcPaint);
2396 surfaceWindow->Release();
2397 HRESULT hr = pRenderTarget->EndDraw();
2398 if (hr == D2DERR_RECREATE_TARGET) {
2399 DropRenderTarget();
2402 #endif
2404 paintState = notPainting;
2407 static bool CompareDevCap(HDC hdc, HDC hOtherDC, int nIndex) {
2408 return ::GetDeviceCaps(hdc, nIndex) == ::GetDeviceCaps(hOtherDC, nIndex);
2411 bool ScintillaWin::IsCompatibleDC(HDC hOtherDC) {
2412 HDC hdc = ::GetDC(MainHWND());
2413 bool isCompatible =
2414 CompareDevCap(hdc, hOtherDC, TECHNOLOGY) &&
2415 CompareDevCap(hdc, hOtherDC, LOGPIXELSY) &&
2416 CompareDevCap(hdc, hOtherDC, LOGPIXELSX) &&
2417 CompareDevCap(hdc, hOtherDC, BITSPIXEL) &&
2418 CompareDevCap(hdc, hOtherDC, PLANES);
2419 ::ReleaseDC(MainHWND(), hdc);
2420 return isCompatible;
2423 DWORD ScintillaWin::EffectFromState(DWORD grfKeyState) const {
2424 // These are the Wordpad semantics.
2425 DWORD dwEffect;
2426 if (inDragDrop == ddDragging) // Internal defaults to move
2427 dwEffect = DROPEFFECT_MOVE;
2428 else
2429 dwEffect = DROPEFFECT_COPY;
2430 if (grfKeyState & MK_ALT)
2431 dwEffect = DROPEFFECT_MOVE;
2432 if (grfKeyState & MK_CONTROL)
2433 dwEffect = DROPEFFECT_COPY;
2434 return dwEffect;
2437 /// Implement IUnknown
2438 STDMETHODIMP ScintillaWin::QueryInterface(REFIID riid, PVOID *ppv) {
2439 *ppv = NULL;
2440 if (riid == IID_IUnknown)
2441 *ppv = reinterpret_cast<IDropTarget *>(&dt);
2442 if (riid == IID_IDropSource)
2443 *ppv = reinterpret_cast<IDropSource *>(&ds);
2444 if (riid == IID_IDropTarget)
2445 *ppv = reinterpret_cast<IDropTarget *>(&dt);
2446 if (riid == IID_IDataObject)
2447 *ppv = reinterpret_cast<IDataObject *>(&dob);
2448 if (!*ppv)
2449 return E_NOINTERFACE;
2450 return S_OK;
2453 STDMETHODIMP_(ULONG) ScintillaWin::AddRef() {
2454 return 1;
2457 STDMETHODIMP_(ULONG) ScintillaWin::Release() {
2458 return 1;
2461 /// Implement IDropTarget
2462 STDMETHODIMP ScintillaWin::DragEnter(LPDATAOBJECT pIDataSource, DWORD grfKeyState,
2463 POINTL, PDWORD pdwEffect) {
2464 if (pIDataSource == NULL)
2465 return E_POINTER;
2466 FORMATETC fmtu = {CF_UNICODETEXT, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
2467 HRESULT hrHasUText = pIDataSource->QueryGetData(&fmtu);
2468 hasOKText = (hrHasUText == S_OK);
2469 if (!hasOKText) {
2470 FORMATETC fmte = {CF_TEXT, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
2471 HRESULT hrHasText = pIDataSource->QueryGetData(&fmte);
2472 hasOKText = (hrHasText == S_OK);
2474 if (!hasOKText) {
2475 *pdwEffect = DROPEFFECT_NONE;
2476 return S_OK;
2479 *pdwEffect = EffectFromState(grfKeyState);
2480 return S_OK;
2483 STDMETHODIMP ScintillaWin::DragOver(DWORD grfKeyState, POINTL pt, PDWORD pdwEffect) {
2484 try {
2485 if (!hasOKText || pdoc->IsReadOnly()) {
2486 *pdwEffect = DROPEFFECT_NONE;
2487 return S_OK;
2490 *pdwEffect = EffectFromState(grfKeyState);
2492 // Update the cursor.
2493 POINT rpt = {pt.x, pt.y};
2494 ::ScreenToClient(MainHWND(), &rpt);
2495 SetDragPosition(SPositionFromLocation(Point(rpt.x, rpt.y), false, false, UserVirtualSpace()));
2497 return S_OK;
2498 } catch (...) {
2499 errorStatus = SC_STATUS_FAILURE;
2501 return E_FAIL;
2504 STDMETHODIMP ScintillaWin::DragLeave() {
2505 try {
2506 SetDragPosition(SelectionPosition(invalidPosition));
2507 return S_OK;
2508 } catch (...) {
2509 errorStatus = SC_STATUS_FAILURE;
2511 return E_FAIL;
2514 STDMETHODIMP ScintillaWin::Drop(LPDATAOBJECT pIDataSource, DWORD grfKeyState,
2515 POINTL pt, PDWORD pdwEffect) {
2516 try {
2517 *pdwEffect = EffectFromState(grfKeyState);
2519 if (pIDataSource == NULL)
2520 return E_POINTER;
2522 SetDragPosition(SelectionPosition(invalidPosition));
2524 STGMEDIUM medium = {0, {0}, 0};
2526 std::vector<char> data; // Includes terminating NUL
2528 FORMATETC fmtu = {CF_UNICODETEXT, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
2529 HRESULT hr = pIDataSource->GetData(&fmtu, &medium);
2530 if (SUCCEEDED(hr) && medium.hGlobal) {
2531 GlobalMemory memUDrop(medium.hGlobal);
2532 wchar_t *udata = static_cast<wchar_t *>(memUDrop.ptr);
2533 if (udata) {
2534 if (IsUnicodeMode()) {
2535 int tlen = memUDrop.Size();
2536 // Convert UTF-16 to UTF-8
2537 int dataLen = UTF8Length(udata, tlen/2);
2538 data.resize(dataLen+1);
2539 UTF8FromUTF16(udata, tlen/2, &data[0], dataLen);
2540 } else {
2541 // Convert UTF-16 to ANSI
2543 // Default Scintilla behavior in Unicode mode
2544 // CF_UNICODETEXT available, but not in Unicode mode
2545 // Convert from Unicode to current Scintilla code page
2546 UINT cpDest = CodePageOfDocument();
2547 int tlen = ::WideCharToMultiByte(cpDest, 0, udata, -1,
2548 NULL, 0, NULL, NULL) - 1; // subtract 0 terminator
2549 data.resize(tlen + 1);
2550 ::WideCharToMultiByte(cpDest, 0, udata, -1,
2551 &data[0], tlen + 1, NULL, NULL);
2554 memUDrop.Unlock();
2555 } else {
2556 FORMATETC fmte = {CF_TEXT, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
2557 hr = pIDataSource->GetData(&fmte, &medium);
2558 if (SUCCEEDED(hr) && medium.hGlobal) {
2559 GlobalMemory memDrop(medium.hGlobal);
2560 const char *cdata = static_cast<char *>(memDrop.ptr);
2561 if (cdata)
2562 data.assign(cdata, cdata+strlen(cdata)+1);
2563 memDrop.Unlock();
2567 if (!data.empty() && convertPastes) {
2568 // Convert line endings of the drop into our local line-endings mode
2569 std::string convertedText = Document::TransformLineEnds(&data[0], data.size() - 1, pdoc->eolMode);
2570 data.assign(convertedText.c_str(), convertedText.c_str()+convertedText.length()+1);
2573 if (!SUCCEEDED(hr) || data.empty()) {
2574 //Platform::DebugPrintf("Bad data format: 0x%x\n", hres);
2575 return hr;
2578 FORMATETC fmtr = {cfColumnSelect, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
2579 HRESULT hrRectangular = pIDataSource->QueryGetData(&fmtr);
2581 POINT rpt = {pt.x, pt.y};
2582 ::ScreenToClient(MainHWND(), &rpt);
2583 SelectionPosition movePos = SPositionFromLocation(Point(rpt.x, rpt.y), false, false, UserVirtualSpace());
2585 DropAt(movePos, &data[0], data.size() - 1, *pdwEffect == DROPEFFECT_MOVE, hrRectangular == S_OK);
2587 // Free data
2588 if (medium.pUnkForRelease != NULL)
2589 medium.pUnkForRelease->Release();
2590 else
2591 ::GlobalFree(medium.hGlobal);
2593 return S_OK;
2594 } catch (...) {
2595 errorStatus = SC_STATUS_FAILURE;
2597 return E_FAIL;
2600 /// Implement important part of IDataObject
2601 STDMETHODIMP ScintillaWin::GetData(FORMATETC *pFEIn, STGMEDIUM *pSTM) {
2602 bool formatOK = (pFEIn->cfFormat == CF_TEXT) ||
2603 ((pFEIn->cfFormat == CF_UNICODETEXT) && IsUnicodeMode());
2604 if (!formatOK ||
2605 pFEIn->ptd != 0 ||
2606 (pFEIn->dwAspect & DVASPECT_CONTENT) == 0 ||
2607 pFEIn->lindex != -1 ||
2608 (pFEIn->tymed & TYMED_HGLOBAL) == 0
2610 //Platform::DebugPrintf("DOB GetData No %d %x %x fmt=%x\n", lenDrag, pFEIn, pSTM, pFEIn->cfFormat);
2611 return DATA_E_FORMATETC;
2613 pSTM->tymed = TYMED_HGLOBAL;
2614 //Platform::DebugPrintf("DOB GetData OK %d %x %x\n", lenDrag, pFEIn, pSTM);
2616 GlobalMemory text;
2617 if (pFEIn->cfFormat == CF_UNICODETEXT) {
2618 int uchars = UTF16Length(drag.Data(), static_cast<int>(drag.LengthWithTerminator()));
2619 text.Allocate(2 * uchars);
2620 if (text) {
2621 UTF16FromUTF8(drag.Data(), static_cast<int>(drag.LengthWithTerminator()),
2622 static_cast<wchar_t *>(text.ptr), uchars);
2624 } else {
2625 text.Allocate(drag.LengthWithTerminator());
2626 if (text) {
2627 memcpy(static_cast<char *>(text.ptr), drag.Data(), drag.LengthWithTerminator());
2630 pSTM->hGlobal = text ? text.Unlock() : 0;
2631 pSTM->pUnkForRelease = 0;
2632 return S_OK;
2635 bool ScintillaWin::Register(HINSTANCE hInstance_) {
2637 hInstance = hInstance_;
2638 bool result;
2640 // Register the Scintilla class
2641 if (IsNT()) {
2643 // Register Scintilla as a wide character window
2644 WNDCLASSEXW wndclass;
2645 wndclass.cbSize = sizeof(wndclass);
2646 wndclass.style = CS_GLOBALCLASS | CS_HREDRAW | CS_VREDRAW;
2647 wndclass.lpfnWndProc = ScintillaWin::SWndProc;
2648 wndclass.cbClsExtra = 0;
2649 wndclass.cbWndExtra = sizeof(ScintillaWin *);
2650 wndclass.hInstance = hInstance;
2651 wndclass.hIcon = NULL;
2652 wndclass.hCursor = NULL;
2653 wndclass.hbrBackground = NULL;
2654 wndclass.lpszMenuName = NULL;
2655 wndclass.lpszClassName = L"Scintilla";
2656 wndclass.hIconSm = 0;
2657 result = ::RegisterClassExW(&wndclass) != 0;
2658 } else {
2660 // Register Scintilla as a normal character window
2661 WNDCLASSEX wndclass;
2662 wndclass.cbSize = sizeof(wndclass);
2663 wndclass.style = CS_GLOBALCLASS | CS_HREDRAW | CS_VREDRAW;
2664 wndclass.lpfnWndProc = ScintillaWin::SWndProc;
2665 wndclass.cbClsExtra = 0;
2666 wndclass.cbWndExtra = sizeof(ScintillaWin *);
2667 wndclass.hInstance = hInstance;
2668 wndclass.hIcon = NULL;
2669 wndclass.hCursor = NULL;
2670 wndclass.hbrBackground = NULL;
2671 wndclass.lpszMenuName = NULL;
2672 wndclass.lpszClassName = scintillaClassName;
2673 wndclass.hIconSm = 0;
2674 result = ::RegisterClassEx(&wndclass) != 0;
2677 if (result) {
2678 // Register the CallTip class
2679 WNDCLASSEX wndclassc;
2680 wndclassc.cbSize = sizeof(wndclassc);
2681 wndclassc.style = CS_GLOBALCLASS | CS_HREDRAW | CS_VREDRAW;
2682 wndclassc.cbClsExtra = 0;
2683 wndclassc.cbWndExtra = sizeof(ScintillaWin *);
2684 wndclassc.hInstance = hInstance;
2685 wndclassc.hIcon = NULL;
2686 wndclassc.hbrBackground = NULL;
2687 wndclassc.lpszMenuName = NULL;
2688 wndclassc.lpfnWndProc = ScintillaWin::CTWndProc;
2689 wndclassc.hCursor = ::LoadCursor(NULL, IDC_ARROW);
2690 wndclassc.lpszClassName = callClassName;
2691 wndclassc.hIconSm = 0;
2693 result = ::RegisterClassEx(&wndclassc) != 0;
2696 return result;
2699 bool ScintillaWin::Unregister() {
2700 bool result = ::UnregisterClass(scintillaClassName, hInstance) != 0;
2701 if (::UnregisterClass(callClassName, hInstance) == 0)
2702 result = false;
2703 return result;
2706 bool ScintillaWin::HasCaretSizeChanged() const {
2707 if (
2708 ( (0 != vs.caretWidth) && (sysCaretWidth != vs.caretWidth) )
2709 || ((0 != vs.lineHeight) && (sysCaretHeight != vs.lineHeight))
2711 return true;
2713 return false;
2716 BOOL ScintillaWin::CreateSystemCaret() {
2717 sysCaretWidth = vs.caretWidth;
2718 if (0 == sysCaretWidth) {
2719 sysCaretWidth = 1;
2721 sysCaretHeight = vs.lineHeight;
2722 int bitmapSize = (((sysCaretWidth + 15) & ~15) >> 3) *
2723 sysCaretHeight;
2724 std::vector<char> bits(bitmapSize);
2725 sysCaretBitmap = ::CreateBitmap(sysCaretWidth, sysCaretHeight, 1,
2726 1, reinterpret_cast<BYTE *>(&bits[0]));
2727 BOOL retval = ::CreateCaret(
2728 MainHWND(), sysCaretBitmap,
2729 sysCaretWidth, sysCaretHeight);
2730 ::ShowCaret(MainHWND());
2731 return retval;
2734 BOOL ScintillaWin::DestroySystemCaret() {
2735 ::HideCaret(MainHWND());
2736 BOOL retval = ::DestroyCaret();
2737 if (sysCaretBitmap) {
2738 ::DeleteObject(sysCaretBitmap);
2739 sysCaretBitmap = 0;
2741 return retval;
2744 sptr_t PASCAL ScintillaWin::CTWndProc(
2745 HWND hWnd, UINT iMessage, WPARAM wParam, sptr_t lParam) {
2746 // Find C++ object associated with window.
2747 ScintillaWin *sciThis = reinterpret_cast<ScintillaWin *>(PointerFromWindow(hWnd));
2748 try {
2749 // ctp will be zero if WM_CREATE not seen yet
2750 if (sciThis == 0) {
2751 if (iMessage == WM_CREATE) {
2752 // Associate CallTip object with window
2753 CREATESTRUCT *pCreate = reinterpret_cast<CREATESTRUCT *>(lParam);
2754 SetWindowPointer(hWnd, pCreate->lpCreateParams);
2755 return 0;
2756 } else {
2757 return ::DefWindowProc(hWnd, iMessage, wParam, lParam);
2759 } else {
2760 if (iMessage == WM_NCDESTROY) {
2761 ::SetWindowLong(hWnd, 0, 0);
2762 return ::DefWindowProc(hWnd, iMessage, wParam, lParam);
2763 } else if (iMessage == WM_PAINT) {
2764 PAINTSTRUCT ps;
2765 ::BeginPaint(hWnd, &ps);
2766 Surface *surfaceWindow = Surface::Allocate(sciThis->technology);
2767 if (surfaceWindow) {
2768 #if defined(USE_D2D)
2769 ID2D1HwndRenderTarget *pCTRenderTarget = 0;
2770 #endif
2771 RECT rc;
2772 GetClientRect(hWnd, &rc);
2773 // Create a Direct2D render target.
2774 if (sciThis->technology == SC_TECHNOLOGY_DEFAULT) {
2775 surfaceWindow->Init(ps.hdc, hWnd);
2776 } else {
2777 #if defined(USE_D2D)
2778 D2D1_HWND_RENDER_TARGET_PROPERTIES dhrtp;
2779 dhrtp.hwnd = hWnd;
2780 dhrtp.pixelSize = D2D1::SizeU(rc.right - rc.left, rc.bottom - rc.top);
2781 dhrtp.presentOptions = D2D1_PRESENT_OPTIONS_NONE;
2783 D2D1_RENDER_TARGET_PROPERTIES drtp;
2784 drtp.type = D2D1_RENDER_TARGET_TYPE_DEFAULT;
2785 drtp.pixelFormat.format = DXGI_FORMAT_UNKNOWN;
2786 drtp.pixelFormat.alphaMode = D2D1_ALPHA_MODE_UNKNOWN;
2787 drtp.dpiX = 96.0;
2788 drtp.dpiY = 96.0;
2789 drtp.usage = D2D1_RENDER_TARGET_USAGE_NONE;
2790 drtp.minLevel = D2D1_FEATURE_LEVEL_DEFAULT;
2792 if (!SUCCEEDED(pD2DFactory->CreateHwndRenderTarget(drtp, dhrtp, &pCTRenderTarget))) {
2793 surfaceWindow->Release();
2794 delete surfaceWindow;
2795 ::EndPaint(hWnd, &ps);
2796 return 0;
2798 surfaceWindow->Init(pCTRenderTarget, hWnd);
2799 pCTRenderTarget->BeginDraw();
2800 #endif
2802 surfaceWindow->SetUnicodeMode(SC_CP_UTF8 == sciThis->ct.codePage);
2803 surfaceWindow->SetDBCSMode(sciThis->ct.codePage);
2804 sciThis->ct.PaintCT(surfaceWindow);
2805 #if defined(USE_D2D)
2806 if (pCTRenderTarget)
2807 pCTRenderTarget->EndDraw();
2808 #endif
2809 surfaceWindow->Release();
2810 delete surfaceWindow;
2811 #if defined(USE_D2D)
2812 if (pCTRenderTarget)
2813 pCTRenderTarget->Release();
2814 #endif
2816 ::EndPaint(hWnd, &ps);
2817 return 0;
2818 } else if ((iMessage == WM_NCLBUTTONDOWN) || (iMessage == WM_NCLBUTTONDBLCLK)) {
2819 POINT pt;
2820 pt.x = static_cast<short>(LOWORD(lParam));
2821 pt.y = static_cast<short>(HIWORD(lParam));
2822 ScreenToClient(hWnd, &pt);
2823 sciThis->ct.MouseClick(Point(pt.x, pt.y));
2824 sciThis->CallTipClick();
2825 return 0;
2826 } else if (iMessage == WM_LBUTTONDOWN) {
2827 // This does not fire due to the hit test code
2828 sciThis->ct.MouseClick(Point::FromLong(lParam));
2829 sciThis->CallTipClick();
2830 return 0;
2831 } else if (iMessage == WM_SETCURSOR) {
2832 ::SetCursor(::LoadCursor(NULL, IDC_ARROW));
2833 return 0;
2834 } else if (iMessage == WM_NCHITTEST) {
2835 return HTCAPTION;
2836 } else {
2837 return ::DefWindowProc(hWnd, iMessage, wParam, lParam);
2840 } catch (...) {
2841 sciThis->errorStatus = SC_STATUS_FAILURE;
2843 return ::DefWindowProc(hWnd, iMessage, wParam, lParam);
2846 sptr_t ScintillaWin::DirectFunction(
2847 ScintillaWin *sci, UINT iMessage, uptr_t wParam, sptr_t lParam) {
2848 PLATFORM_ASSERT(::GetCurrentThreadId() == ::GetWindowThreadProcessId(sci->MainHWND(), NULL));
2849 return sci->WndProc(iMessage, wParam, lParam);
2852 extern "C"
2853 #ifndef STATIC_BUILD
2854 __declspec(dllexport)
2855 #endif
2856 sptr_t __stdcall Scintilla_DirectFunction(
2857 ScintillaWin *sci, UINT iMessage, uptr_t wParam, sptr_t lParam) {
2858 return sci->WndProc(iMessage, wParam, lParam);
2861 sptr_t PASCAL ScintillaWin::SWndProc(
2862 HWND hWnd, UINT iMessage, WPARAM wParam, sptr_t lParam) {
2863 //Platform::DebugPrintf("S W:%x M:%x WP:%x L:%x\n", hWnd, iMessage, wParam, lParam);
2865 // Find C++ object associated with window.
2866 ScintillaWin *sci = reinterpret_cast<ScintillaWin *>(PointerFromWindow(hWnd));
2867 // sci will be zero if WM_CREATE not seen yet
2868 if (sci == 0) {
2869 try {
2870 if (iMessage == WM_CREATE) {
2871 // Create C++ object associated with window
2872 sci = new ScintillaWin(hWnd);
2873 SetWindowPointer(hWnd, sci);
2874 return sci->WndProc(iMessage, wParam, lParam);
2876 } catch (...) {
2878 return ::DefWindowProc(hWnd, iMessage, wParam, lParam);
2879 } else {
2880 if (iMessage == WM_NCDESTROY) {
2881 try {
2882 sci->Finalise();
2883 delete sci;
2884 } catch (...) {
2886 ::SetWindowLong(hWnd, 0, 0);
2887 return ::DefWindowProc(hWnd, iMessage, wParam, lParam);
2888 } else {
2889 return sci->WndProc(iMessage, wParam, lParam);
2894 // This function is externally visible so it can be called from container when building statically.
2895 // Must be called once only.
2896 int Scintilla_RegisterClasses(void *hInstance) {
2897 Platform_Initialise(hInstance);
2898 bool result = ScintillaWin::Register(reinterpret_cast<HINSTANCE>(hInstance));
2899 #ifdef SCI_LEXER
2900 Scintilla_LinkLexers();
2901 #endif
2902 return result;
2905 // This function is externally visible so it can be called from container when building statically.
2906 int Scintilla_ReleaseResources() {
2907 bool result = ScintillaWin::Unregister();
2908 Platform_Finalise();
2909 return result;
2912 #ifndef STATIC_BUILD
2913 extern "C" int APIENTRY DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID) {
2914 //Platform::DebugPrintf("Scintilla::DllMain %d %d\n", hInstance, dwReason);
2915 if (dwReason == DLL_PROCESS_ATTACH) {
2916 if (!Scintilla_RegisterClasses(hInstance))
2917 return FALSE;
2918 } else if (dwReason == DLL_PROCESS_DETACH) {
2919 Scintilla_ReleaseResources();
2921 return TRUE;
2923 #endif