Applied backgroundcolors.patch
[TortoiseGit.git] / ext / scintilla / win32 / ScintillaWin.cxx
bloba0d92451974dfdb592b8a9ecc160164b09ee24f5
1 // Scintilla source code edit control
2 /** @file ScintillaWin.cxx
3 ** Windows specific subclass of ScintillaBase.
4 **/
5 // Copyright 1998-2003 by Neil Hodgson <neilh@scintilla.org>
6 // The License.txt file describes the conditions under which this software may be distributed.
8 #include <stdlib.h>
9 #include <string.h>
10 #include <stdio.h>
11 #include <ctype.h>
12 #include <assert.h>
13 #include <limits.h>
15 #include <new>
16 #include <string>
17 #include <vector>
18 #include <map>
19 #include <algorithm>
21 #undef _WIN32_WINNT
22 #define _WIN32_WINNT 0x0500
23 #undef WINVER
24 #define WINVER 0x0500
25 #include <windows.h>
26 #include <commctrl.h>
27 #include <richedit.h>
28 #include <windowsx.h>
30 #if defined(NTDDI_WIN7) && !defined(DISABLE_D2D)
31 #define USE_D2D 1
32 #endif
34 #if defined(USE_D2D)
35 #include <d2d1.h>
36 #include <dwrite.h>
37 #endif
39 #include "Platform.h"
41 #include "ILexer.h"
42 #include "Scintilla.h"
44 #ifdef SCI_LEXER
45 #include "SciLexer.h"
46 #include "LexerModule.h"
47 #endif
48 #include "StringCopy.h"
49 #include "SplitVector.h"
50 #include "Partitioning.h"
51 #include "RunStyles.h"
52 #include "ContractionState.h"
53 #include "CellBuffer.h"
54 #include "CallTip.h"
55 #include "KeyMap.h"
56 #include "Indicator.h"
57 #include "XPM.h"
58 #include "LineMarker.h"
59 #include "Style.h"
60 #include "AutoComplete.h"
61 #include "ViewStyle.h"
62 #include "CharClassify.h"
63 #include "Decoration.h"
64 #include "CaseFolder.h"
65 #include "Document.h"
66 #include "Selection.h"
67 #include "PositionCache.h"
68 #include "Editor.h"
69 #include "ScintillaBase.h"
70 #include "UniConversion.h"
71 #include "CaseConvert.h"
73 #include "PlatWin.h"
75 #ifdef SCI_LEXER
76 #include "ExternalLexer.h"
77 #endif
79 #ifndef SPI_GETWHEELSCROLLLINES
80 #define SPI_GETWHEELSCROLLLINES 104
81 #endif
83 #ifndef WM_UNICHAR
84 #define WM_UNICHAR 0x0109
85 #endif
87 #ifndef UNICODE_NOCHAR
88 #define UNICODE_NOCHAR 0xFFFF
89 #endif
91 #include <commctrl.h>
92 #include <zmouse.h>
93 #include <ole2.h>
95 #ifndef MK_ALT
96 #define MK_ALT 32
97 #endif
99 #define SC_WIN_IDLE 5001
101 typedef BOOL (WINAPI *TrackMouseEventSig)(LPTRACKMOUSEEVENT);
103 // GCC has trouble with the standard COM ABI so do it the old C way with explicit vtables.
105 const TCHAR scintillaClassName[] = TEXT("Scintilla");
106 const TCHAR callClassName[] = TEXT("CallTip");
108 #ifdef SCI_NAMESPACE
109 using namespace Scintilla;
110 #endif
112 static void *PointerFromWindow(HWND hWnd) {
113 return reinterpret_cast<void *>(::GetWindowLongPtr(hWnd, 0));
116 static void SetWindowPointer(HWND hWnd, void *ptr) {
117 ::SetWindowLongPtr(hWnd, 0, reinterpret_cast<LONG_PTR>(ptr));
120 static void SetWindowID(HWND hWnd, int identifier) {
121 ::SetWindowLongPtr(hWnd, GWLP_ID, identifier);
124 static Point PointFromPOINT(POINT pt) {
125 return Point::FromInts(pt.x, pt.y);
128 class ScintillaWin; // Forward declaration for COM interface subobjects
130 typedef void VFunction(void);
132 static HMODULE commctrl32 = 0;
136 class FormatEnumerator {
137 public:
138 VFunction **vtbl;
139 int ref;
140 unsigned int pos;
141 std::vector<CLIPFORMAT> formats;
142 FormatEnumerator(int pos_, CLIPFORMAT formats_[], size_t formatsLen_);
147 class DropSource {
148 public:
149 VFunction **vtbl;
150 ScintillaWin *sci;
151 DropSource();
156 class DataObject {
157 public:
158 VFunction **vtbl;
159 ScintillaWin *sci;
160 DataObject();
165 class DropTarget {
166 public:
167 VFunction **vtbl;
168 ScintillaWin *sci;
169 DropTarget();
174 class ScintillaWin :
175 public ScintillaBase {
177 bool lastKeyDownConsumed;
179 bool capturedMouse;
180 bool trackedMouseLeave;
181 TrackMouseEventSig TrackMouseEventFn;
183 unsigned int linesPerScroll; ///< Intellimouse support
184 int wheelDelta; ///< Wheel delta from roll
186 HRGN hRgnUpdate;
188 bool hasOKText;
190 CLIPFORMAT cfColumnSelect;
191 CLIPFORMAT cfBorlandIDEBlockType;
192 CLIPFORMAT cfLineSelect;
194 HRESULT hrOle;
195 DropSource ds;
196 DataObject dob;
197 DropTarget dt;
199 static HINSTANCE hInstance;
201 #if defined(USE_D2D)
202 ID2D1HwndRenderTarget *pRenderTarget;
203 bool renderTargetValid;
204 #endif
206 explicit ScintillaWin(HWND hwnd);
207 ScintillaWin(const ScintillaWin &);
208 virtual ~ScintillaWin();
209 ScintillaWin &operator=(const ScintillaWin &);
211 virtual void Initialise();
212 virtual void Finalise();
213 #if defined(USE_D2D)
214 void EnsureRenderTarget();
215 void DropRenderTarget();
216 #endif
217 HWND MainHWND();
219 static sptr_t DirectFunction(
220 sptr_t ptr, UINT iMessage, uptr_t wParam, sptr_t lParam);
221 static sptr_t PASCAL SWndProc(
222 HWND hWnd, UINT iMessage, WPARAM wParam, sptr_t lParam);
223 static sptr_t PASCAL CTWndProc(
224 HWND hWnd, UINT iMessage, WPARAM wParam, sptr_t lParam);
226 enum { invalidTimerID, standardTimerID, idleTimerID };
228 virtual bool DragThreshold(Point ptStart, Point ptNow);
229 virtual void StartDrag();
230 sptr_t WndPaint(uptr_t wParam);
231 sptr_t HandleComposition(uptr_t wParam, sptr_t lParam);
232 UINT CodePageOfDocument();
233 virtual bool ValidCodePage(int codePage) const;
234 virtual sptr_t DefWndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam);
235 virtual bool SetIdle(bool on);
236 virtual void SetTicking(bool on);
237 virtual void SetMouseCapture(bool on);
238 virtual bool HaveMouseCapture();
239 virtual void SetTrackMouseLeaveEvent(bool on);
240 virtual bool PaintContains(PRectangle rc);
241 virtual void ScrollText(int linesToMove);
242 virtual void UpdateSystemCaret();
243 virtual void SetVerticalScrollPos();
244 virtual void SetHorizontalScrollPos();
245 virtual bool ModifyScrollBars(int nMax, int nPage);
246 virtual void NotifyChange();
247 virtual void NotifyFocus(bool focus);
248 virtual void SetCtrlID(int identifier);
249 virtual int GetCtrlID();
250 virtual void NotifyParent(SCNotification scn);
251 virtual void NotifyParent(SCNotification * scn);
252 virtual void NotifyDoubleClick(Point pt, int modifiers);
253 virtual CaseFolder *CaseFolderForEncoding();
254 virtual std::string CaseMapString(const std::string &s, int caseMapping);
255 virtual void Copy();
256 virtual void CopyAllowLine();
257 virtual bool CanPaste();
258 virtual void Paste();
259 virtual void CreateCallTipWindow(PRectangle rc);
260 virtual void AddToPopUp(const char *label, int cmd = 0, bool enabled = true);
261 virtual void ClaimSelection();
263 // DBCS
264 void ImeStartComposition();
265 void ImeEndComposition();
267 void AddCharBytes(char b0, char b1);
269 void GetIntelliMouseParameters();
270 virtual void CopyToClipboard(const SelectionText &selectedText);
271 void ScrollMessage(WPARAM wParam);
272 void HorizontalScrollMessage(WPARAM wParam);
273 void FullPaint();
274 void FullPaintDC(HDC dc);
275 bool IsCompatibleDC(HDC dc);
276 DWORD EffectFromState(DWORD grfKeyState) const;
278 virtual int SetScrollInfo(int nBar, LPCSCROLLINFO lpsi, BOOL bRedraw);
279 virtual bool GetScrollInfo(int nBar, LPSCROLLINFO lpsi);
280 void ChangeScrollPos(int barType, int pos);
281 sptr_t GetTextLength();
282 sptr_t GetText(uptr_t wParam, sptr_t lParam);
284 public:
285 // Public for benefit of Scintilla_DirectFunction
286 virtual sptr_t WndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam);
288 /// Implement IUnknown
289 STDMETHODIMP QueryInterface(REFIID riid, PVOID *ppv);
290 STDMETHODIMP_(ULONG)AddRef();
291 STDMETHODIMP_(ULONG)Release();
293 /// Implement IDropTarget
294 STDMETHODIMP DragEnter(LPDATAOBJECT pIDataSource, DWORD grfKeyState,
295 POINTL pt, PDWORD pdwEffect);
296 STDMETHODIMP DragOver(DWORD grfKeyState, POINTL pt, PDWORD pdwEffect);
297 STDMETHODIMP DragLeave();
298 STDMETHODIMP Drop(LPDATAOBJECT pIDataSource, DWORD grfKeyState,
299 POINTL pt, PDWORD pdwEffect);
301 /// Implement important part of IDataObject
302 STDMETHODIMP GetData(FORMATETC *pFEIn, STGMEDIUM *pSTM);
304 static bool Register(HINSTANCE hInstance_);
305 static bool Unregister();
307 friend class DropSource;
308 friend class DataObject;
309 friend class DropTarget;
310 bool DragIsRectangularOK(CLIPFORMAT fmt) const {
311 return drag.rectangular && (fmt == cfColumnSelect);
314 private:
315 // For use in creating a system caret
316 bool HasCaretSizeChanged() const;
317 BOOL CreateSystemCaret();
318 BOOL DestroySystemCaret();
319 HBITMAP sysCaretBitmap;
320 int sysCaretWidth;
321 int sysCaretHeight;
322 bool keysAlwaysUnicode;
325 HINSTANCE ScintillaWin::hInstance = 0;
327 ScintillaWin::ScintillaWin(HWND hwnd) {
329 lastKeyDownConsumed = false;
331 capturedMouse = false;
332 trackedMouseLeave = false;
333 TrackMouseEventFn = 0;
335 linesPerScroll = 0;
336 wheelDelta = 0; // Wheel delta from roll
338 hRgnUpdate = 0;
340 hasOKText = false;
342 // There does not seem to be a real standard for indicating that the clipboard
343 // contains a rectangular selection, so copy Developer Studio and Borland Delphi.
344 cfColumnSelect = static_cast<CLIPFORMAT>(
345 ::RegisterClipboardFormat(TEXT("MSDEVColumnSelect")));
346 cfBorlandIDEBlockType = static_cast<CLIPFORMAT>(
347 ::RegisterClipboardFormat(TEXT("Borland IDE Block Type")));
349 // Likewise for line-copy (copies a full line when no text is selected)
350 cfLineSelect = static_cast<CLIPFORMAT>(
351 ::RegisterClipboardFormat(TEXT("MSDEVLineSelect")));
353 hrOle = E_FAIL;
355 wMain = hwnd;
357 dob.sci = this;
358 ds.sci = this;
359 dt.sci = this;
361 sysCaretBitmap = 0;
362 sysCaretWidth = 0;
363 sysCaretHeight = 0;
365 #if defined(USE_D2D)
366 pRenderTarget = 0;
367 renderTargetValid = true;
368 #endif
370 keysAlwaysUnicode = false;
372 caret.period = ::GetCaretBlinkTime();
373 if (caret.period < 0)
374 caret.period = 0;
376 Initialise();
379 ScintillaWin::~ScintillaWin() {}
381 void ScintillaWin::Initialise() {
382 // Initialize COM. If the app has already done this it will have
383 // no effect. If the app hasnt, we really shouldnt ask them to call
384 // it just so this internal feature works.
385 hrOle = ::OleInitialize(NULL);
387 // Find TrackMouseEvent which is available on Windows > 95
388 HMODULE user32 = ::GetModuleHandle(TEXT("user32.dll"));
389 if (user32)
390 TrackMouseEventFn = (TrackMouseEventSig)::GetProcAddress(user32, "TrackMouseEvent");
391 if (TrackMouseEventFn == NULL) {
392 // Windows 95 has an emulation in comctl32.dll:_TrackMouseEvent
393 if (!commctrl32)
394 commctrl32 = ::LoadLibrary(TEXT("comctl32.dll"));
395 if (commctrl32 != NULL) {
396 TrackMouseEventFn = (TrackMouseEventSig)
397 ::GetProcAddress(commctrl32, "_TrackMouseEvent");
402 void ScintillaWin::Finalise() {
403 ScintillaBase::Finalise();
404 SetTicking(false);
405 SetIdle(false);
406 #if defined(USE_D2D)
407 DropRenderTarget();
408 #endif
409 ::RevokeDragDrop(MainHWND());
410 if (SUCCEEDED(hrOle)) {
411 ::OleUninitialize();
415 #if defined(USE_D2D)
417 void ScintillaWin::EnsureRenderTarget() {
418 if (!renderTargetValid) {
419 DropRenderTarget();
420 renderTargetValid = true;
422 if (pD2DFactory && !pRenderTarget) {
423 RECT rc;
424 HWND hw = MainHWND();
425 GetClientRect(hw, &rc);
427 D2D1_SIZE_U size = D2D1::SizeU(rc.right - rc.left, rc.bottom - rc.top);
429 // Create a Direct2D render target.
430 #if 1
431 D2D1_HWND_RENDER_TARGET_PROPERTIES dhrtp;
432 dhrtp.hwnd = hw;
433 dhrtp.pixelSize = size;
434 dhrtp.presentOptions = D2D1_PRESENT_OPTIONS_NONE;
436 D2D1_RENDER_TARGET_PROPERTIES drtp;
437 drtp.type = D2D1_RENDER_TARGET_TYPE_DEFAULT;
438 drtp.pixelFormat.format = DXGI_FORMAT_UNKNOWN;
439 drtp.pixelFormat.alphaMode = D2D1_ALPHA_MODE_UNKNOWN;
440 drtp.dpiX = 96.0;
441 drtp.dpiY = 96.0;
442 drtp.usage = D2D1_RENDER_TARGET_USAGE_NONE;
443 drtp.minLevel = D2D1_FEATURE_LEVEL_DEFAULT;
445 pD2DFactory->CreateHwndRenderTarget(drtp, dhrtp, &pRenderTarget);
446 #else
447 pD2DFactory->CreateHwndRenderTarget(
448 D2D1::RenderTargetProperties(
449 D2D1_RENDER_TARGET_TYPE_DEFAULT ,
450 D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_PREMULTIPLIED),
451 96.0f, 96.0f, D2D1_RENDER_TARGET_USAGE_NONE, D2D1_FEATURE_LEVEL_DEFAULT),
452 D2D1::HwndRenderTargetProperties(hw, size),
453 &pRenderTarget);
454 #endif
455 // Pixmaps were created to be compatible with previous render target so
456 // need to be recreated.
457 DropGraphics(false);
461 void ScintillaWin::DropRenderTarget() {
462 if (pRenderTarget) {
463 pRenderTarget->Release();
464 pRenderTarget = 0;
468 #endif
470 HWND ScintillaWin::MainHWND() {
471 return reinterpret_cast<HWND>(wMain.GetID());
474 bool ScintillaWin::DragThreshold(Point ptStart, Point ptNow) {
475 int xMove = static_cast<int>(abs(ptStart.x - ptNow.x));
476 int yMove = static_cast<int>(abs(ptStart.y - ptNow.y));
477 return (xMove > ::GetSystemMetrics(SM_CXDRAG)) ||
478 (yMove > ::GetSystemMetrics(SM_CYDRAG));
481 void ScintillaWin::StartDrag() {
482 inDragDrop = ddDragging;
483 DWORD dwEffect = 0;
484 dropWentOutside = true;
485 IDataObject *pDataObject = reinterpret_cast<IDataObject *>(&dob);
486 IDropSource *pDropSource = reinterpret_cast<IDropSource *>(&ds);
487 //Platform::DebugPrintf("About to DoDragDrop %x %x\n", pDataObject, pDropSource);
488 HRESULT hr = ::DoDragDrop(
489 pDataObject,
490 pDropSource,
491 DROPEFFECT_COPY | DROPEFFECT_MOVE, &dwEffect);
492 //Platform::DebugPrintf("DoDragDrop = %x\n", hr);
493 if (SUCCEEDED(hr)) {
494 if ((hr == DRAGDROP_S_DROP) && (dwEffect == DROPEFFECT_MOVE) && dropWentOutside) {
495 // Remove dragged out text
496 ClearSelection();
499 inDragDrop = ddNone;
500 SetDragPosition(SelectionPosition(invalidPosition));
503 // Avoid warnings everywhere for old style casts by concentrating them here
504 static WORD LoWord(uptr_t l) {
505 return LOWORD(l);
508 static WORD HiWord(uptr_t l) {
509 return HIWORD(l);
512 static int InputCodePage() {
513 HKL inputLocale = ::GetKeyboardLayout(0);
514 LANGID inputLang = LOWORD(inputLocale);
515 char sCodePage[10];
516 int res = ::GetLocaleInfoA(MAKELCID(inputLang, SORT_DEFAULT),
517 LOCALE_IDEFAULTANSICODEPAGE, sCodePage, sizeof(sCodePage));
518 if (!res)
519 return 0;
520 return atoi(sCodePage);
523 /** Map the key codes to their equivalent SCK_ form. */
524 static int KeyTranslate(int keyIn) {
525 //PLATFORM_ASSERT(!keyIn);
526 switch (keyIn) {
527 case VK_DOWN: return SCK_DOWN;
528 case VK_UP: return SCK_UP;
529 case VK_LEFT: return SCK_LEFT;
530 case VK_RIGHT: return SCK_RIGHT;
531 case VK_HOME: return SCK_HOME;
532 case VK_END: return SCK_END;
533 case VK_PRIOR: return SCK_PRIOR;
534 case VK_NEXT: return SCK_NEXT;
535 case VK_DELETE: return SCK_DELETE;
536 case VK_INSERT: return SCK_INSERT;
537 case VK_ESCAPE: return SCK_ESCAPE;
538 case VK_BACK: return SCK_BACK;
539 case VK_TAB: return SCK_TAB;
540 case VK_RETURN: return SCK_RETURN;
541 case VK_ADD: return SCK_ADD;
542 case VK_SUBTRACT: return SCK_SUBTRACT;
543 case VK_DIVIDE: return SCK_DIVIDE;
544 case VK_LWIN: return SCK_WIN;
545 case VK_RWIN: return SCK_RWIN;
546 case VK_APPS: return SCK_MENU;
547 case VK_OEM_2: return '/';
548 case VK_OEM_3: return '`';
549 case VK_OEM_4: return '[';
550 case VK_OEM_5: return '\\';
551 case VK_OEM_6: return ']';
552 default: return keyIn;
556 LRESULT ScintillaWin::WndPaint(uptr_t wParam) {
557 //ElapsedTime et;
559 // Redirect assertions to debug output and save current state
560 bool assertsPopup = Platform::ShowAssertionPopUps(false);
561 paintState = painting;
562 PAINTSTRUCT ps;
563 PAINTSTRUCT *pps;
565 bool IsOcxCtrl = (wParam != 0); // if wParam != 0, it contains
566 // a PAINSTRUCT* from the OCX
567 // Removed since this interferes with reporting other assertions as it occurs repeatedly
568 //PLATFORM_ASSERT(hRgnUpdate == NULL);
569 hRgnUpdate = ::CreateRectRgn(0, 0, 0, 0);
570 if (IsOcxCtrl) {
571 pps = reinterpret_cast<PAINTSTRUCT*>(wParam);
572 } else {
573 ::GetUpdateRgn(MainHWND(), hRgnUpdate, FALSE);
574 pps = &ps;
575 ::BeginPaint(MainHWND(), pps);
577 rcPaint = PRectangle::FromInts(pps->rcPaint.left, pps->rcPaint.top, pps->rcPaint.right, pps->rcPaint.bottom);
578 PRectangle rcClient = GetClientRectangle();
579 paintingAllText = rcPaint.Contains(rcClient);
580 if (technology == SC_TECHNOLOGY_DEFAULT) {
581 AutoSurface surfaceWindow(pps->hdc, this);
582 if (surfaceWindow) {
583 Paint(surfaceWindow, rcPaint);
584 surfaceWindow->Release();
586 } else {
587 #if defined(USE_D2D)
588 EnsureRenderTarget();
589 AutoSurface surfaceWindow(pRenderTarget, this);
590 if (surfaceWindow) {
591 pRenderTarget->BeginDraw();
592 Paint(surfaceWindow, rcPaint);
593 surfaceWindow->Release();
594 HRESULT hr = pRenderTarget->EndDraw();
595 if (hr == D2DERR_RECREATE_TARGET) {
596 DropRenderTarget();
597 paintState = paintAbandoned;
600 #endif
602 if (hRgnUpdate) {
603 ::DeleteRgn(hRgnUpdate);
604 hRgnUpdate = 0;
607 if (!IsOcxCtrl)
608 ::EndPaint(MainHWND(), pps);
609 if (paintState == paintAbandoned) {
610 // Painting area was insufficient to cover new styling or brace highlight positions
611 if (IsOcxCtrl) {
612 FullPaintDC(pps->hdc);
613 } else {
614 FullPaint();
617 paintState = notPainting;
619 // Restore debug output state
620 Platform::ShowAssertionPopUps(assertsPopup);
622 //Platform::DebugPrintf("Paint took %g\n", et.Duration());
623 return 0l;
626 sptr_t ScintillaWin::HandleComposition(uptr_t wParam, sptr_t lParam) {
627 if (lParam & GCS_RESULTSTR) {
628 HIMC hIMC = ::ImmGetContext(MainHWND());
629 if (hIMC) {
630 const int maxLenInputIME = 200;
631 wchar_t wcs[maxLenInputIME];
632 LONG bytes = ::ImmGetCompositionStringW(hIMC,
633 GCS_RESULTSTR, wcs, (maxLenInputIME-1)*2);
634 int wides = bytes / 2;
635 if (IsUnicodeMode()) {
636 char utfval[maxLenInputIME * 3];
637 unsigned int len = UTF8Length(wcs, wides);
638 UTF8FromUTF16(wcs, wides, utfval, len);
639 utfval[len] = '\0';
640 AddCharUTF(utfval, len);
641 } else {
642 char dbcsval[maxLenInputIME * 2];
643 int size = ::WideCharToMultiByte(InputCodePage(),
644 0, wcs, wides, dbcsval, sizeof(dbcsval) - 1, 0, 0);
645 for (int i=0; i<size; i++) {
646 AddChar(dbcsval[i]);
649 // Set new position after converted
650 Point pos = PointMainCaret();
651 COMPOSITIONFORM CompForm;
652 CompForm.dwStyle = CFS_POINT;
653 CompForm.ptCurrentPos.x = static_cast<int>(pos.x);
654 CompForm.ptCurrentPos.y = static_cast<int>(pos.y);
655 ::ImmSetCompositionWindow(hIMC, &CompForm);
656 ::ImmReleaseContext(MainHWND(), hIMC);
658 return 0;
660 return ::DefWindowProc(MainHWND(), WM_IME_COMPOSITION, wParam, lParam);
663 // Translate message IDs from WM_* and EM_* to SCI_* so can partly emulate Windows Edit control
664 static unsigned int SciMessageFromEM(unsigned int iMessage) {
665 switch (iMessage) {
666 case EM_CANPASTE: return SCI_CANPASTE;
667 case EM_CANUNDO: return SCI_CANUNDO;
668 case EM_EMPTYUNDOBUFFER: return SCI_EMPTYUNDOBUFFER;
669 case EM_FINDTEXTEX: return SCI_FINDTEXT;
670 case EM_FORMATRANGE: return SCI_FORMATRANGE;
671 case EM_GETFIRSTVISIBLELINE: return SCI_GETFIRSTVISIBLELINE;
672 case EM_GETLINECOUNT: return SCI_GETLINECOUNT;
673 case EM_GETSELTEXT: return SCI_GETSELTEXT;
674 case EM_GETTEXTRANGE: return SCI_GETTEXTRANGE;
675 case EM_HIDESELECTION: return SCI_HIDESELECTION;
676 case EM_LINEINDEX: return SCI_POSITIONFROMLINE;
677 case EM_LINESCROLL: return SCI_LINESCROLL;
678 case EM_REPLACESEL: return SCI_REPLACESEL;
679 case EM_SCROLLCARET: return SCI_SCROLLCARET;
680 case EM_SETREADONLY: return SCI_SETREADONLY;
681 case WM_CLEAR: return SCI_CLEAR;
682 case WM_COPY: return SCI_COPY;
683 case WM_CUT: return SCI_CUT;
684 case WM_SETTEXT: return SCI_SETTEXT;
685 case WM_PASTE: return SCI_PASTE;
686 case WM_UNDO: return SCI_UNDO;
688 return iMessage;
691 UINT CodePageFromCharSet(DWORD characterSet, UINT documentCodePage) {
692 if (documentCodePage == SC_CP_UTF8) {
693 return SC_CP_UTF8;
695 switch (characterSet) {
696 case SC_CHARSET_ANSI: return 1252;
697 case SC_CHARSET_DEFAULT: return documentCodePage;
698 case SC_CHARSET_BALTIC: return 1257;
699 case SC_CHARSET_CHINESEBIG5: return 950;
700 case SC_CHARSET_EASTEUROPE: return 1250;
701 case SC_CHARSET_GB2312: return 936;
702 case SC_CHARSET_GREEK: return 1253;
703 case SC_CHARSET_HANGUL: return 949;
704 case SC_CHARSET_MAC: return 10000;
705 case SC_CHARSET_OEM: return 437;
706 case SC_CHARSET_RUSSIAN: return 1251;
707 case SC_CHARSET_SHIFTJIS: return 932;
708 case SC_CHARSET_TURKISH: return 1254;
709 case SC_CHARSET_JOHAB: return 1361;
710 case SC_CHARSET_HEBREW: return 1255;
711 case SC_CHARSET_ARABIC: return 1256;
712 case SC_CHARSET_VIETNAMESE: return 1258;
713 case SC_CHARSET_THAI: return 874;
714 case SC_CHARSET_8859_15: return 28605;
715 // Not supported
716 case SC_CHARSET_CYRILLIC: return documentCodePage;
717 case SC_CHARSET_SYMBOL: return documentCodePage;
719 return documentCodePage;
722 UINT ScintillaWin::CodePageOfDocument() {
723 return CodePageFromCharSet(vs.styles[STYLE_DEFAULT].characterSet, pdoc->dbcsCodePage);
726 sptr_t ScintillaWin::GetTextLength() {
727 if (::IsWindowUnicode(MainHWND())) {
728 if (pdoc->Length() == 0)
729 return 0;
730 std::vector<char> docBytes(pdoc->Length(), '\0');
731 pdoc->GetCharRange(&docBytes[0], 0, pdoc->Length());
732 if (IsUnicodeMode()) {
733 return UTF16Length(&docBytes[0], static_cast<unsigned int>(docBytes.size()));
734 } else {
735 return ::MultiByteToWideChar(CodePageOfDocument(), 0, &docBytes[0],
736 static_cast<int>(docBytes.size()), NULL, 0);
738 } else {
739 return pdoc->Length();
743 sptr_t ScintillaWin::GetText(uptr_t wParam, sptr_t lParam) {
744 if (::IsWindowUnicode(MainHWND())) {
745 wchar_t *ptr = reinterpret_cast<wchar_t *>(lParam);
746 if (pdoc->Length() == 0) {
747 *ptr = L'\0';
748 return 0;
750 std::vector<char> docBytes(pdoc->Length(), '\0');
751 pdoc->GetCharRange(&docBytes[0], 0, pdoc->Length());
752 if (IsUnicodeMode()) {
753 unsigned int lengthUTF16 = UTF16Length(&docBytes[0], static_cast<unsigned int>(docBytes.size()));
754 if (lParam == 0)
755 return lengthUTF16;
756 if (wParam == 0)
757 return 0;
758 unsigned int uLen = UTF16FromUTF8(&docBytes[0], static_cast<unsigned int>(docBytes.size()),
759 ptr, static_cast<int>(wParam) - 1);
760 ptr[uLen] = L'\0';
761 return uLen;
762 } else {
763 // Not Unicode mode
764 // Convert to Unicode using the current Scintilla code page
765 const UINT cpSrc = CodePageOfDocument();
766 int lengthUTF16 = ::MultiByteToWideChar(cpSrc, 0, &docBytes[0],
767 static_cast<int>(docBytes.size()), NULL, 0);
768 if (lengthUTF16 >= static_cast<int>(wParam))
769 lengthUTF16 = static_cast<int>(wParam)-1;
770 ::MultiByteToWideChar(cpSrc, 0, &docBytes[0],
771 static_cast<int>(docBytes.size()),
772 ptr, lengthUTF16);
773 ptr[lengthUTF16] = L'\0';
774 return lengthUTF16;
776 } else {
777 if (lParam == 0)
778 return pdoc->Length() + 1;
779 if (wParam == 0)
780 return 0;
781 char *ptr = reinterpret_cast<char *>(lParam);
782 unsigned int iChar = 0;
783 for (; iChar < wParam - 1; iChar++)
784 ptr[iChar] = pdoc->CharAt(iChar);
785 ptr[iChar] = '\0';
786 return iChar;
790 sptr_t ScintillaWin::WndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
791 try {
792 //Platform::DebugPrintf("S M:%x WP:%x L:%x\n", iMessage, wParam, lParam);
793 iMessage = SciMessageFromEM(iMessage);
794 switch (iMessage) {
796 case WM_CREATE:
797 ctrlID = ::GetDlgCtrlID(reinterpret_cast<HWND>(wMain.GetID()));
798 // Get Intellimouse scroll line parameters
799 GetIntelliMouseParameters();
800 ::RegisterDragDrop(MainHWND(), reinterpret_cast<IDropTarget *>(&dt));
801 break;
803 case WM_COMMAND:
804 Command(LoWord(wParam));
805 break;
807 case WM_PAINT:
808 return WndPaint(wParam);
810 case WM_PRINTCLIENT: {
811 HDC hdc = reinterpret_cast<HDC>(wParam);
812 if (!IsCompatibleDC(hdc)) {
813 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
815 FullPaintDC(hdc);
817 break;
819 case WM_VSCROLL:
820 ScrollMessage(wParam);
821 break;
823 case WM_HSCROLL:
824 HorizontalScrollMessage(wParam);
825 break;
827 case WM_SIZE: {
828 #if defined(USE_D2D)
829 if (paintState == notPainting) {
830 DropRenderTarget();
831 } else {
832 renderTargetValid = false;
834 #endif
835 //Platform::DebugPrintf("Scintilla WM_SIZE %d %d\n", LoWord(lParam), HiWord(lParam));
836 ChangeSize();
838 break;
840 case WM_MOUSEWHEEL:
841 // if autocomplete list active then send mousewheel message to it
842 if (ac.Active()) {
843 HWND hWnd = reinterpret_cast<HWND>(ac.lb->GetID());
844 ::SendMessage(hWnd, iMessage, wParam, lParam);
845 break;
848 // Don't handle datazoom.
849 // (A good idea for datazoom would be to "fold" or "unfold" details.
850 // i.e. if datazoomed out only class structures are visible, when datazooming in the control
851 // structures appear, then eventually the individual statements...)
852 if (wParam & MK_SHIFT) {
853 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
856 // Either SCROLL or ZOOM. We handle the wheel steppings calculation
857 wheelDelta -= static_cast<short>(HiWord(wParam));
858 if (abs(wheelDelta) >= WHEEL_DELTA && linesPerScroll > 0) {
859 int linesToScroll = linesPerScroll;
860 if (linesPerScroll == WHEEL_PAGESCROLL)
861 linesToScroll = LinesOnScreen() - 1;
862 if (linesToScroll == 0) {
863 linesToScroll = 1;
865 linesToScroll *= (wheelDelta / WHEEL_DELTA);
866 if (wheelDelta >= 0)
867 wheelDelta = wheelDelta % WHEEL_DELTA;
868 else
869 wheelDelta = - (-wheelDelta % WHEEL_DELTA);
871 if (wParam & MK_CONTROL) {
872 // Zoom! We play with the font sizes in the styles.
873 // Number of steps/line is ignored, we just care if sizing up or down
874 if (linesToScroll < 0) {
875 KeyCommand(SCI_ZOOMIN);
876 } else {
877 KeyCommand(SCI_ZOOMOUT);
879 } else {
880 // Scroll
881 ScrollTo(topLine + linesToScroll);
884 return 0;
886 case WM_TIMER:
887 if (wParam == standardTimerID && timer.ticking) {
888 Tick();
889 } else if (wParam == idleTimerID && idler.state) {
890 SendMessage(MainHWND(), SC_WIN_IDLE, 0, 1);
891 } else {
892 return 1;
894 break;
896 case SC_WIN_IDLE:
897 // wParam=dwTickCountInitial, or 0 to initialize. lParam=bSkipUserInputTest
898 if (idler.state) {
899 if (lParam || (WAIT_TIMEOUT == MsgWaitForMultipleObjects(0, 0, 0, 0, QS_INPUT|QS_HOTKEY))) {
900 if (Idle()) {
901 // User input was given priority above, but all events do get a turn. Other
902 // messages, notifications, etc. will get interleaved with the idle messages.
904 // However, some things like WM_PAINT are a lower priority, and will not fire
905 // when there's a message posted. So, several times a second, we stop and let
906 // the low priority events have a turn (after which the timer will fire again).
908 DWORD dwCurrent = GetTickCount();
909 DWORD dwStart = wParam ? static_cast<DWORD>(wParam) : dwCurrent;
910 const DWORD maxWorkTime = 50;
912 if (dwCurrent >= dwStart && dwCurrent > maxWorkTime && dwCurrent - maxWorkTime < dwStart)
913 PostMessage(MainHWND(), SC_WIN_IDLE, dwStart, 0);
914 } else {
915 SetIdle(false);
919 break;
921 case WM_GETMINMAXINFO:
922 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
924 case WM_LBUTTONDOWN: {
925 // For IME, set the composition string as the result string.
926 HIMC hIMC = ::ImmGetContext(MainHWND());
927 ::ImmNotifyIME(hIMC, NI_COMPOSITIONSTR, CPS_COMPLETE, 0);
928 ::ImmReleaseContext(MainHWND(), hIMC);
930 //Platform::DebugPrintf("Buttdown %d %x %x %x %x %x\n",iMessage, wParam, lParam,
931 // Platform::IsKeyDown(VK_SHIFT),
932 // Platform::IsKeyDown(VK_CONTROL),
933 // Platform::IsKeyDown(VK_MENU));
934 ::SetFocus(MainHWND());
935 ButtonDown(Point::FromLong(static_cast<long>(lParam)), ::GetMessageTime(),
936 (wParam & MK_SHIFT) != 0,
937 (wParam & MK_CONTROL) != 0,
938 Platform::IsKeyDown(VK_MENU));
940 break;
942 case WM_MOUSEMOVE:
943 SetTrackMouseLeaveEvent(true);
944 ButtonMoveWithModifiers(Point::FromLong(static_cast<long>(lParam)),
945 ((wParam & MK_SHIFT) != 0 ? SCI_SHIFT : 0) |
946 ((wParam & MK_CONTROL) != 0 ? SCI_CTRL : 0) |
947 (Platform::IsKeyDown(VK_MENU) ? SCI_ALT : 0));
948 break;
950 case WM_MOUSELEAVE:
951 SetTrackMouseLeaveEvent(false);
952 MouseLeave();
953 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
955 case WM_LBUTTONUP:
956 ButtonUp(Point::FromLong(static_cast<long>(lParam)),
957 ::GetMessageTime(),
958 (wParam & MK_CONTROL) != 0);
959 break;
961 case WM_RBUTTONDOWN:
962 ::SetFocus(MainHWND());
963 if (!PointInSelection(Point::FromLong(static_cast<long>(lParam)))) {
964 CancelModes();
965 SetEmptySelection(PositionFromLocation(Point::FromLong(static_cast<long>(lParam))));
967 break;
969 case WM_SETCURSOR:
970 if (LoWord(lParam) == HTCLIENT) {
971 if (inDragDrop == ddDragging) {
972 DisplayCursor(Window::cursorUp);
973 } else {
974 // Display regular (drag) cursor over selection
975 POINT pt;
976 if (0 != ::GetCursorPos(&pt)) {
977 ::ScreenToClient(MainHWND(), &pt);
978 if (PointInSelMargin(PointFromPOINT(pt))) {
979 DisplayCursor(GetMarginCursor(PointFromPOINT(pt)));
980 } else if (PointInSelection(PointFromPOINT(pt)) && !SelectionEmpty()) {
981 DisplayCursor(Window::cursorArrow);
982 } else if (PointIsHotspot(PointFromPOINT(pt))) {
983 DisplayCursor(Window::cursorHand);
984 } else {
985 DisplayCursor(Window::cursorText);
989 return TRUE;
990 } else {
991 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
994 case WM_CHAR:
995 if (((wParam >= 128) || !iscntrl(static_cast<int>(wParam))) || !lastKeyDownConsumed) {
996 if (::IsWindowUnicode(MainHWND()) || keysAlwaysUnicode) {
997 wchar_t wcs[2] = {static_cast<wchar_t>(wParam), 0};
998 if (IsUnicodeMode()) {
999 // For a wide character version of the window:
1000 char utfval[4];
1001 unsigned int len = UTF8Length(wcs, 1);
1002 UTF8FromUTF16(wcs, 1, utfval, len);
1003 AddCharUTF(utfval, len);
1004 } else {
1005 UINT cpDest = CodePageOfDocument();
1006 char inBufferCP[20];
1007 int size = ::WideCharToMultiByte(cpDest,
1008 0, wcs, 1, inBufferCP, sizeof(inBufferCP) - 1, 0, 0);
1009 inBufferCP[size] = '\0';
1010 AddCharUTF(inBufferCP, size);
1012 } else {
1013 if (IsUnicodeMode()) {
1014 AddCharBytes('\0', LOBYTE(wParam));
1015 } else {
1016 AddChar(LOBYTE(wParam));
1020 return 0;
1022 case WM_UNICHAR:
1023 if (wParam == UNICODE_NOCHAR) {
1024 return IsUnicodeMode() ? 1 : 0;
1025 } else if (lastKeyDownConsumed) {
1026 return 1;
1027 } else {
1028 if (IsUnicodeMode()) {
1029 char utfval[4];
1030 wchar_t wcs[2] = {static_cast<wchar_t>(wParam), 0};
1031 unsigned int len = UTF8Length(wcs, 1);
1032 UTF8FromUTF16(wcs, 1, utfval, len);
1033 AddCharUTF(utfval, len);
1034 return 1;
1035 } else {
1036 return 0;
1040 case WM_SYSKEYDOWN:
1041 case WM_KEYDOWN: {
1042 //Platform::DebugPrintf("S keydown %d %x %x %x %x\n",iMessage, wParam, lParam, ::IsKeyDown(VK_SHIFT), ::IsKeyDown(VK_CONTROL));
1043 lastKeyDownConsumed = false;
1044 int ret = KeyDown(KeyTranslate(static_cast<int>(wParam)),
1045 Platform::IsKeyDown(VK_SHIFT),
1046 Platform::IsKeyDown(VK_CONTROL),
1047 Platform::IsKeyDown(VK_MENU),
1048 &lastKeyDownConsumed);
1049 if (!ret && !lastKeyDownConsumed) {
1050 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1052 break;
1055 case WM_IME_KEYDOWN:
1056 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1058 case WM_KEYUP:
1059 //Platform::DebugPrintf("S keyup %d %x %x\n",iMessage, wParam, lParam);
1060 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1062 case WM_SETTINGCHANGE:
1063 //Platform::DebugPrintf("Setting Changed\n");
1064 InvalidateStyleData();
1065 // Get Intellimouse scroll line parameters
1066 GetIntelliMouseParameters();
1067 break;
1069 case WM_GETDLGCODE:
1070 return DLGC_HASSETSEL | DLGC_WANTALLKEYS;
1072 case WM_KILLFOCUS: {
1073 HWND wOther = reinterpret_cast<HWND>(wParam);
1074 HWND wThis = MainHWND();
1075 HWND wCT = reinterpret_cast<HWND>(ct.wCallTip.GetID());
1076 if (!wParam ||
1077 !(::IsChild(wThis, wOther) || (wOther == wCT))) {
1078 SetFocusState(false);
1079 DestroySystemCaret();
1082 break;
1084 case WM_SETFOCUS:
1085 SetFocusState(true);
1086 DestroySystemCaret();
1087 CreateSystemCaret();
1088 break;
1090 case WM_SYSCOLORCHANGE:
1091 //Platform::DebugPrintf("Setting Changed\n");
1092 InvalidateStyleData();
1093 break;
1095 case WM_IME_STARTCOMPOSITION: // dbcs
1096 ImeStartComposition();
1097 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1099 case WM_IME_ENDCOMPOSITION: // dbcs
1100 ImeEndComposition();
1101 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1103 case WM_IME_COMPOSITION:
1104 return HandleComposition(wParam, lParam);
1106 case WM_IME_CHAR: {
1107 AddCharBytes(HIBYTE(wParam), LOBYTE(wParam));
1108 return 0;
1111 case WM_CONTEXTMENU:
1112 if (displayPopupMenu) {
1113 Point pt = Point::FromLong(static_cast<long>(lParam));
1114 if ((pt.x == -1) && (pt.y == -1)) {
1115 // Caused by keyboard so display menu near caret
1116 pt = PointMainCaret();
1117 POINT spt = {static_cast<int>(pt.x), static_cast<int>(pt.y)};
1118 ::ClientToScreen(MainHWND(), &spt);
1119 pt = PointFromPOINT(spt);
1121 ContextMenu(pt);
1122 return 0;
1124 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1126 case WM_INPUTLANGCHANGE:
1127 //::SetThreadLocale(LOWORD(lParam));
1128 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1130 case WM_INPUTLANGCHANGEREQUEST:
1131 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1133 case WM_ERASEBKGND:
1134 return 1; // Avoid any background erasure as whole window painted.
1136 case WM_CAPTURECHANGED:
1137 capturedMouse = false;
1138 return 0;
1140 // These are not handled in Scintilla and its faster to dispatch them here.
1141 // Also moves time out to here so profile doesn't count lots of empty message calls.
1143 case WM_MOVE:
1144 case WM_MOUSEACTIVATE:
1145 case WM_NCHITTEST:
1146 case WM_NCCALCSIZE:
1147 case WM_NCPAINT:
1148 case WM_NCMOUSEMOVE:
1149 case WM_NCLBUTTONDOWN:
1150 case WM_IME_SETCONTEXT:
1151 case WM_IME_NOTIFY:
1152 case WM_SYSCOMMAND:
1153 case WM_WINDOWPOSCHANGING:
1154 case WM_WINDOWPOSCHANGED:
1155 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1157 case WM_GETTEXTLENGTH:
1158 return GetTextLength();
1160 case WM_GETTEXT:
1161 return GetText(wParam, lParam);
1163 case EM_LINEFROMCHAR:
1164 if (static_cast<int>(wParam) < 0) {
1165 wParam = SelectionStart().Position();
1167 return pdoc->LineFromPosition(static_cast<int>(wParam));
1169 case EM_EXLINEFROMCHAR:
1170 return pdoc->LineFromPosition(static_cast<int>(lParam));
1172 case EM_GETSEL:
1173 if (wParam) {
1174 *reinterpret_cast<int *>(wParam) = SelectionStart().Position();
1176 if (lParam) {
1177 *reinterpret_cast<int *>(lParam) = SelectionEnd().Position();
1179 return MAKELONG(SelectionStart().Position(), SelectionEnd().Position());
1181 case EM_EXGETSEL: {
1182 if (lParam == 0) {
1183 return 0;
1185 Sci_CharacterRange *pCR = reinterpret_cast<Sci_CharacterRange *>(lParam);
1186 pCR->cpMin = SelectionStart().Position();
1187 pCR->cpMax = SelectionEnd().Position();
1189 break;
1191 case EM_SETSEL: {
1192 int nStart = static_cast<int>(wParam);
1193 int nEnd = static_cast<int>(lParam);
1194 if (nStart == 0 && nEnd == -1) {
1195 nEnd = pdoc->Length();
1197 if (nStart == -1) {
1198 nStart = nEnd; // Remove selection
1200 if (nStart > nEnd) {
1201 SetSelection(nEnd, nStart);
1202 } else {
1203 SetSelection(nStart, nEnd);
1205 EnsureCaretVisible();
1207 break;
1209 case EM_EXSETSEL: {
1210 if (lParam == 0) {
1211 return 0;
1213 Sci_CharacterRange *pCR = reinterpret_cast<Sci_CharacterRange *>(lParam);
1214 sel.selType = Selection::selStream;
1215 if (pCR->cpMin == 0 && pCR->cpMax == -1) {
1216 SetSelection(pCR->cpMin, pdoc->Length());
1217 } else {
1218 SetSelection(pCR->cpMin, pCR->cpMax);
1220 EnsureCaretVisible();
1221 return pdoc->LineFromPosition(SelectionStart().Position());
1224 case SCI_GETDIRECTFUNCTION:
1225 return reinterpret_cast<sptr_t>(DirectFunction);
1227 case SCI_GETDIRECTPOINTER:
1228 return reinterpret_cast<sptr_t>(this);
1230 case SCI_GRABFOCUS:
1231 ::SetFocus(MainHWND());
1232 break;
1234 case SCI_SETKEYSUNICODE:
1235 keysAlwaysUnicode = wParam != 0;
1236 break;
1238 case SCI_GETKEYSUNICODE:
1239 return keysAlwaysUnicode;
1241 case SCI_SETTECHNOLOGY:
1242 if ((wParam == SC_TECHNOLOGY_DEFAULT) || (wParam == SC_TECHNOLOGY_DIRECTWRITE)) {
1243 if (technology != static_cast<int>(wParam)) {
1244 if (static_cast<int>(wParam) == SC_TECHNOLOGY_DIRECTWRITE) {
1245 #if defined(USE_D2D)
1246 if (!LoadD2D())
1247 // Failed to load Direct2D or DirectWrite so no effect
1248 return 0;
1249 #else
1250 return 0;
1251 #endif
1253 technology = static_cast<int>(wParam);
1254 // Invalidate all cached information including layout.
1255 DropGraphics(true);
1256 InvalidateStyleRedraw();
1259 break;
1261 #ifdef SCI_LEXER
1262 case SCI_LOADLEXERLIBRARY:
1263 LexerManager::GetInstance()->Load(reinterpret_cast<const char *>(lParam));
1264 break;
1265 #endif
1267 default:
1268 return ScintillaBase::WndProc(iMessage, wParam, lParam);
1270 } catch (std::bad_alloc &) {
1271 errorStatus = SC_STATUS_BADALLOC;
1272 } catch (...) {
1273 errorStatus = SC_STATUS_FAILURE;
1275 return 0l;
1278 bool ScintillaWin::ValidCodePage(int codePage) const {
1279 return codePage == 0 || codePage == SC_CP_UTF8 ||
1280 codePage == 932 || codePage == 936 || codePage == 949 ||
1281 codePage == 950 || codePage == 1361;
1284 sptr_t ScintillaWin::DefWndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
1285 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1288 void ScintillaWin::SetTicking(bool on) {
1289 if (timer.ticking != on) {
1290 timer.ticking = on;
1291 if (timer.ticking) {
1292 timer.tickerID = ::SetTimer(MainHWND(), standardTimerID, timer.tickSize, NULL)
1293 ? reinterpret_cast<TickerID>(standardTimerID) : 0;
1294 } else {
1295 ::KillTimer(MainHWND(), reinterpret_cast<uptr_t>(timer.tickerID));
1296 timer.tickerID = 0;
1299 timer.ticksToWait = caret.period;
1302 bool ScintillaWin::SetIdle(bool on) {
1303 // On Win32 the Idler is implemented as a Timer on the Scintilla window. This
1304 // takes advantage of the fact that WM_TIMER messages are very low priority,
1305 // and are only posted when the message queue is empty, i.e. during idle time.
1306 if (idler.state != on) {
1307 if (on) {
1308 idler.idlerID = ::SetTimer(MainHWND(), idleTimerID, 10, NULL)
1309 ? reinterpret_cast<IdlerID>(idleTimerID) : 0;
1310 } else {
1311 ::KillTimer(MainHWND(), reinterpret_cast<uptr_t>(idler.idlerID));
1312 idler.idlerID = 0;
1314 idler.state = idler.idlerID != 0;
1316 return idler.state;
1319 void ScintillaWin::SetMouseCapture(bool on) {
1320 if (mouseDownCaptures) {
1321 if (on) {
1322 ::SetCapture(MainHWND());
1323 } else {
1324 ::ReleaseCapture();
1327 capturedMouse = on;
1330 bool ScintillaWin::HaveMouseCapture() {
1331 // Cannot just see if GetCapture is this window as the scroll bar also sets capture for the window
1332 return capturedMouse;
1333 //return capturedMouse && (::GetCapture() == MainHWND());
1336 void ScintillaWin::SetTrackMouseLeaveEvent(bool on) {
1337 if (on && TrackMouseEventFn && !trackedMouseLeave) {
1338 TRACKMOUSEEVENT tme;
1339 tme.cbSize = sizeof(tme);
1340 tme.dwFlags = TME_LEAVE;
1341 tme.hwndTrack = MainHWND();
1342 TrackMouseEventFn(&tme);
1344 trackedMouseLeave = on;
1347 bool ScintillaWin::PaintContains(PRectangle rc) {
1348 bool contains = true;
1349 if ((paintState == painting) && (!rc.Empty())) {
1350 if (!rcPaint.Contains(rc)) {
1351 contains = false;
1352 } else {
1353 // In bounding rectangle so check more accurately using region
1354 HRGN hRgnRange = ::CreateRectRgn(static_cast<int>(rc.left), static_cast<int>(rc.top), static_cast<int>(rc.right), static_cast<int>(rc.bottom));
1355 if (hRgnRange) {
1356 HRGN hRgnDest = ::CreateRectRgn(0, 0, 0, 0);
1357 if (hRgnDest) {
1358 int combination = ::CombineRgn(hRgnDest, hRgnRange, hRgnUpdate, RGN_DIFF);
1359 if (combination != NULLREGION) {
1360 contains = false;
1362 ::DeleteRgn(hRgnDest);
1364 ::DeleteRgn(hRgnRange);
1368 return contains;
1371 void ScintillaWin::ScrollText(int /* linesToMove */) {
1372 //Platform::DebugPrintf("ScintillaWin::ScrollText %d\n", linesToMove);
1373 //::ScrollWindow(MainHWND(), 0,
1374 // vs.lineHeight * linesToMove, 0, 0);
1375 //::UpdateWindow(MainHWND());
1376 Redraw();
1377 UpdateSystemCaret();
1380 void ScintillaWin::UpdateSystemCaret() {
1381 if (hasFocus) {
1382 if (HasCaretSizeChanged()) {
1383 DestroySystemCaret();
1384 CreateSystemCaret();
1386 Point pos = PointMainCaret();
1387 ::SetCaretPos(static_cast<int>(pos.x), static_cast<int>(pos.y));
1391 int ScintillaWin::SetScrollInfo(int nBar, LPCSCROLLINFO lpsi, BOOL bRedraw) {
1392 return ::SetScrollInfo(MainHWND(), nBar, lpsi, bRedraw);
1395 bool ScintillaWin::GetScrollInfo(int nBar, LPSCROLLINFO lpsi) {
1396 return ::GetScrollInfo(MainHWND(), nBar, lpsi) ? true : false;
1399 // Change the scroll position but avoid repaint if changing to same value
1400 void ScintillaWin::ChangeScrollPos(int barType, int pos) {
1401 SCROLLINFO sci = {
1402 sizeof(sci), 0, 0, 0, 0, 0, 0
1404 sci.fMask = SIF_POS;
1405 GetScrollInfo(barType, &sci);
1406 if (sci.nPos != pos) {
1407 DwellEnd(true);
1408 sci.nPos = pos;
1409 SetScrollInfo(barType, &sci, TRUE);
1413 void ScintillaWin::SetVerticalScrollPos() {
1414 ChangeScrollPos(SB_VERT, topLine);
1417 void ScintillaWin::SetHorizontalScrollPos() {
1418 ChangeScrollPos(SB_HORZ, xOffset);
1421 bool ScintillaWin::ModifyScrollBars(int nMax, int nPage) {
1422 bool modified = false;
1423 SCROLLINFO sci = {
1424 sizeof(sci), 0, 0, 0, 0, 0, 0
1426 sci.fMask = SIF_PAGE | SIF_RANGE;
1427 GetScrollInfo(SB_VERT, &sci);
1428 int vertEndPreferred = nMax;
1429 if (!verticalScrollBarVisible)
1430 nPage = vertEndPreferred + 1;
1431 if ((sci.nMin != 0) ||
1432 (sci.nMax != vertEndPreferred) ||
1433 (sci.nPage != static_cast<unsigned int>(nPage)) ||
1434 (sci.nPos != 0)) {
1435 sci.fMask = SIF_PAGE | SIF_RANGE;
1436 sci.nMin = 0;
1437 sci.nMax = vertEndPreferred;
1438 sci.nPage = nPage;
1439 sci.nPos = 0;
1440 sci.nTrackPos = 1;
1441 SetScrollInfo(SB_VERT, &sci, TRUE);
1442 modified = true;
1445 PRectangle rcText = GetTextRectangle();
1446 int horizEndPreferred = scrollWidth;
1447 if (horizEndPreferred < 0)
1448 horizEndPreferred = 0;
1449 unsigned int pageWidth = static_cast<unsigned int>(rcText.Width());
1450 if (!horizontalScrollBarVisible || Wrapping())
1451 pageWidth = horizEndPreferred + 1;
1452 sci.fMask = SIF_PAGE | SIF_RANGE;
1453 GetScrollInfo(SB_HORZ, &sci);
1454 if ((sci.nMin != 0) ||
1455 (sci.nMax != horizEndPreferred) ||
1456 (sci.nPage != pageWidth) ||
1457 (sci.nPos != 0)) {
1458 sci.fMask = SIF_PAGE | SIF_RANGE;
1459 sci.nMin = 0;
1460 sci.nMax = horizEndPreferred;
1461 sci.nPage = pageWidth;
1462 sci.nPos = 0;
1463 sci.nTrackPos = 1;
1464 SetScrollInfo(SB_HORZ, &sci, TRUE);
1465 modified = true;
1466 if (scrollWidth < static_cast<int>(pageWidth)) {
1467 HorizontalScrollTo(0);
1470 return modified;
1473 void ScintillaWin::NotifyChange() {
1474 ::SendMessage(::GetParent(MainHWND()), WM_COMMAND,
1475 MAKELONG(GetCtrlID(), SCEN_CHANGE),
1476 reinterpret_cast<LPARAM>(MainHWND()));
1479 void ScintillaWin::NotifyFocus(bool focus) {
1480 ::SendMessage(::GetParent(MainHWND()), WM_COMMAND,
1481 MAKELONG(GetCtrlID(), focus ? SCEN_SETFOCUS : SCEN_KILLFOCUS),
1482 reinterpret_cast<LPARAM>(MainHWND()));
1483 Editor::NotifyFocus(focus);
1486 void ScintillaWin::SetCtrlID(int identifier) {
1487 ::SetWindowID(reinterpret_cast<HWND>(wMain.GetID()), identifier);
1490 int ScintillaWin::GetCtrlID() {
1491 return ::GetDlgCtrlID(reinterpret_cast<HWND>(wMain.GetID()));
1494 void ScintillaWin::NotifyParent(SCNotification scn) {
1495 scn.nmhdr.hwndFrom = MainHWND();
1496 scn.nmhdr.idFrom = GetCtrlID();
1497 ::SendMessage(::GetParent(MainHWND()), WM_NOTIFY,
1498 GetCtrlID(), reinterpret_cast<LPARAM>(&scn));
1501 void ScintillaWin::NotifyParent(SCNotification * scn) {
1502 scn->nmhdr.hwndFrom = MainHWND();
1503 scn->nmhdr.idFrom = GetCtrlID();
1504 ::SendMessage(::GetParent(MainHWND()), WM_NOTIFY,
1505 GetCtrlID(), reinterpret_cast<LPARAM>(scn));
1508 void ScintillaWin::NotifyDoubleClick(Point pt, int modifiers) {
1509 //Platform::DebugPrintf("ScintillaWin Double click 0\n");
1510 ScintillaBase::NotifyDoubleClick(pt, modifiers);
1511 // Send myself a WM_LBUTTONDBLCLK, so the container can handle it too.
1512 ::SendMessage(MainHWND(),
1513 WM_LBUTTONDBLCLK,
1514 (modifiers & SCI_SHIFT) ? MK_SHIFT : 0,
1515 MAKELPARAM(pt.x, pt.y));
1518 class CaseFolderDBCS : public CaseFolderTable {
1519 // Allocate the expandable storage here so that it does not need to be reallocated
1520 // for each call to Fold.
1521 std::vector<wchar_t> utf16Mixed;
1522 std::vector<wchar_t> utf16Folded;
1523 UINT cp;
1524 public:
1525 explicit CaseFolderDBCS(UINT cp_) : cp(cp_) {
1526 StandardASCII();
1528 virtual size_t Fold(char *folded, size_t sizeFolded, const char *mixed, size_t lenMixed) {
1529 if ((lenMixed == 1) && (sizeFolded > 0)) {
1530 folded[0] = mapping[static_cast<unsigned char>(mixed[0])];
1531 return 1;
1532 } else {
1533 if (lenMixed > utf16Mixed.size()) {
1534 utf16Mixed.resize(lenMixed + 8);
1536 size_t nUtf16Mixed = ::MultiByteToWideChar(cp, 0, mixed,
1537 static_cast<int>(lenMixed),
1538 &utf16Mixed[0],
1539 static_cast<int>(utf16Mixed.size()));
1541 if (nUtf16Mixed == 0) {
1542 // Failed to convert -> bad input
1543 folded[0] = '\0';
1544 return 1;
1547 unsigned int lenFlat = 0;
1548 for (size_t mixIndex=0; mixIndex < nUtf16Mixed; mixIndex++) {
1549 if ((lenFlat + 20) > utf16Folded.size())
1550 utf16Folded.resize(lenFlat + 60);
1551 const char *foldedUTF8 = CaseConvert(utf16Mixed[mixIndex], CaseConversionFold);
1552 if (foldedUTF8) {
1553 // Maximum length of a case conversion is 6 bytes, 3 characters
1554 wchar_t wFolded[20];
1555 unsigned int charsConverted = UTF16FromUTF8(foldedUTF8,
1556 static_cast<unsigned int>(strlen(foldedUTF8)),
1557 wFolded, ELEMENTS(wFolded));
1558 for (size_t j=0; j<charsConverted; j++)
1559 utf16Folded[lenFlat++] = wFolded[j];
1560 } else {
1561 utf16Folded[lenFlat++] = utf16Mixed[mixIndex];
1565 size_t lenOut = ::WideCharToMultiByte(cp, 0,
1566 &utf16Folded[0], lenFlat,
1567 NULL, 0, NULL, 0);
1569 if (lenOut < sizeFolded) {
1570 ::WideCharToMultiByte(cp, 0,
1571 &utf16Folded[0], lenFlat,
1572 folded, static_cast<int>(lenOut), NULL, 0);
1573 return lenOut;
1574 } else {
1575 return 0;
1581 CaseFolder *ScintillaWin::CaseFolderForEncoding() {
1582 UINT cpDest = CodePageOfDocument();
1583 if (cpDest == SC_CP_UTF8) {
1584 return new CaseFolderUnicode();
1585 } else {
1586 if (pdoc->dbcsCodePage == 0) {
1587 CaseFolderTable *pcf = new CaseFolderTable();
1588 pcf->StandardASCII();
1589 // Only for single byte encodings
1590 UINT cpDoc = CodePageOfDocument();
1591 for (int i=0x80; i<0x100; i++) {
1592 char sCharacter[2] = "A";
1593 sCharacter[0] = static_cast<char>(i);
1594 wchar_t wCharacter[20];
1595 unsigned int lengthUTF16 = ::MultiByteToWideChar(cpDoc, 0, sCharacter, 1,
1596 wCharacter, ELEMENTS(wCharacter));
1597 if (lengthUTF16 == 1) {
1598 const char *caseFolded = CaseConvert(wCharacter[0], CaseConversionFold);
1599 if (caseFolded) {
1600 wchar_t wLower[20];
1601 unsigned int charsConverted = UTF16FromUTF8(caseFolded,
1602 static_cast<unsigned int>(strlen(caseFolded)),
1603 wLower, ELEMENTS(wLower));
1604 if (charsConverted == 1) {
1605 char sCharacterLowered[20];
1606 unsigned int lengthConverted = ::WideCharToMultiByte(cpDoc, 0,
1607 wLower, charsConverted,
1608 sCharacterLowered, ELEMENTS(sCharacterLowered), NULL, 0);
1609 if ((lengthConverted == 1) && (sCharacter[0] != sCharacterLowered[0])) {
1610 pcf->SetTranslation(sCharacter[0], sCharacterLowered[0]);
1616 return pcf;
1617 } else {
1618 return new CaseFolderDBCS(cpDest);
1623 std::string ScintillaWin::CaseMapString(const std::string &s, int caseMapping) {
1624 if ((s.size() == 0) || (caseMapping == cmSame))
1625 return s;
1627 UINT cpDoc = CodePageOfDocument();
1628 if (cpDoc == SC_CP_UTF8) {
1629 std::string retMapped(s.length() * maxExpansionCaseConversion, 0);
1630 size_t lenMapped = CaseConvertString(&retMapped[0], retMapped.length(), s.c_str(), s.length(),
1631 (caseMapping == cmUpper) ? CaseConversionUpper : CaseConversionLower);
1632 retMapped.resize(lenMapped);
1633 return retMapped;
1636 unsigned int lengthUTF16 = ::MultiByteToWideChar(cpDoc, 0, s.c_str(),
1637 static_cast<int>(s.size()), NULL, 0);
1638 if (lengthUTF16 == 0) // Failed to convert
1639 return s;
1641 DWORD mapFlags = LCMAP_LINGUISTIC_CASING |
1642 ((caseMapping == cmUpper) ? LCMAP_UPPERCASE : LCMAP_LOWERCASE);
1644 // Change text to UTF-16
1645 std::vector<wchar_t> vwcText(lengthUTF16);
1646 ::MultiByteToWideChar(cpDoc, 0, s.c_str(), static_cast<int>(s.size()), &vwcText[0], lengthUTF16);
1648 // Change case
1649 int charsConverted = ::LCMapStringW(LOCALE_SYSTEM_DEFAULT, mapFlags,
1650 &vwcText[0], lengthUTF16, NULL, 0);
1651 std::vector<wchar_t> vwcConverted(charsConverted);
1652 ::LCMapStringW(LOCALE_SYSTEM_DEFAULT, mapFlags,
1653 &vwcText[0], lengthUTF16, &vwcConverted[0], charsConverted);
1655 // Change back to document encoding
1656 unsigned int lengthConverted = ::WideCharToMultiByte(cpDoc, 0,
1657 &vwcConverted[0], static_cast<int>(vwcConverted.size()),
1658 NULL, 0, NULL, 0);
1659 std::vector<char> vcConverted(lengthConverted);
1660 ::WideCharToMultiByte(cpDoc, 0,
1661 &vwcConverted[0], static_cast<int>(vwcConverted.size()),
1662 &vcConverted[0], static_cast<int>(vcConverted.size()), NULL, 0);
1664 return std::string(&vcConverted[0], vcConverted.size());
1667 void ScintillaWin::Copy() {
1668 //Platform::DebugPrintf("Copy\n");
1669 if (!sel.Empty()) {
1670 SelectionText selectedText;
1671 CopySelectionRange(&selectedText);
1672 CopyToClipboard(selectedText);
1676 void ScintillaWin::CopyAllowLine() {
1677 SelectionText selectedText;
1678 CopySelectionRange(&selectedText, true);
1679 CopyToClipboard(selectedText);
1682 bool ScintillaWin::CanPaste() {
1683 if (!Editor::CanPaste())
1684 return false;
1685 if (::IsClipboardFormatAvailable(CF_TEXT))
1686 return true;
1687 if (IsUnicodeMode())
1688 return ::IsClipboardFormatAvailable(CF_UNICODETEXT) != 0;
1689 return false;
1692 class GlobalMemory {
1693 HGLOBAL hand;
1694 public:
1695 void *ptr;
1696 GlobalMemory() : hand(0), ptr(0) {
1698 explicit GlobalMemory(HGLOBAL hand_) : hand(hand_), ptr(0) {
1699 if (hand) {
1700 ptr = ::GlobalLock(hand);
1703 ~GlobalMemory() {
1704 PLATFORM_ASSERT(!ptr);
1706 void Allocate(size_t bytes) {
1707 hand = ::GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT, bytes);
1708 if (hand) {
1709 ptr = ::GlobalLock(hand);
1712 HGLOBAL Unlock() {
1713 PLATFORM_ASSERT(ptr);
1714 HGLOBAL handCopy = hand;
1715 ::GlobalUnlock(hand);
1716 ptr = 0;
1717 hand = 0;
1718 return handCopy;
1720 void SetClip(UINT uFormat) {
1721 ::SetClipboardData(uFormat, Unlock());
1723 operator bool() const {
1724 return ptr != 0;
1726 SIZE_T Size() {
1727 return ::GlobalSize(hand);
1731 void ScintillaWin::Paste() {
1732 if (!::OpenClipboard(MainHWND()))
1733 return;
1734 UndoGroup ug(pdoc);
1735 const bool isLine = SelectionEmpty() && (::IsClipboardFormatAvailable(cfLineSelect) != 0);
1736 ClearSelection(multiPasteMode == SC_MULTIPASTE_EACH);
1737 bool isRectangular = (::IsClipboardFormatAvailable(cfColumnSelect) != 0);
1739 if (!isRectangular) {
1740 // Evaluate "Borland IDE Block Type" explicitly
1741 GlobalMemory memBorlandSelection(::GetClipboardData(cfBorlandIDEBlockType));
1742 if (memBorlandSelection) {
1743 isRectangular = (memBorlandSelection.Size() == 1) && (static_cast<BYTE *>(memBorlandSelection.ptr)[0] == 0x02);
1744 memBorlandSelection.Unlock();
1747 const PasteShape pasteShape = isRectangular ? pasteRectangular : (isLine ? pasteLine : pasteStream);
1749 // Always use CF_UNICODETEXT if available
1750 GlobalMemory memUSelection(::GetClipboardData(CF_UNICODETEXT));
1751 if (memUSelection) {
1752 wchar_t *uptr = static_cast<wchar_t *>(memUSelection.ptr);
1753 if (uptr) {
1754 unsigned int len;
1755 std::vector<char> putf;
1756 // Default Scintilla behaviour in Unicode mode
1757 if (IsUnicodeMode()) {
1758 unsigned int bytes = static_cast<unsigned int>(memUSelection.Size());
1759 len = UTF8Length(uptr, bytes / 2);
1760 putf.resize(len + 1);
1761 UTF8FromUTF16(uptr, bytes / 2, &putf[0], len);
1762 } else {
1763 // CF_UNICODETEXT available, but not in Unicode mode
1764 // Convert from Unicode to current Scintilla code page
1765 UINT cpDest = CodePageOfDocument();
1766 len = ::WideCharToMultiByte(cpDest, 0, uptr, -1,
1767 NULL, 0, NULL, NULL) - 1; // subtract 0 terminator
1768 putf.resize(len + 1);
1769 ::WideCharToMultiByte(cpDest, 0, uptr, -1,
1770 &putf[0], len + 1, NULL, NULL);
1773 InsertPasteShape(&putf[0], len, pasteShape);
1775 memUSelection.Unlock();
1776 } else {
1777 // CF_UNICODETEXT not available, paste ANSI text
1778 GlobalMemory memSelection(::GetClipboardData(CF_TEXT));
1779 if (memSelection) {
1780 char *ptr = static_cast<char *>(memSelection.ptr);
1781 if (ptr) {
1782 unsigned int bytes = static_cast<unsigned int>(memSelection.Size());
1783 unsigned int len = bytes;
1784 for (unsigned int i = 0; i < bytes; i++) {
1785 if ((len == bytes) && (0 == ptr[i]))
1786 len = i;
1789 // In Unicode mode, convert clipboard text to UTF-8
1790 if (IsUnicodeMode()) {
1791 std::vector<wchar_t> uptr(len+1);
1793 unsigned int ulen = ::MultiByteToWideChar(CP_ACP, 0,
1794 ptr, len, &uptr[0], len+1);
1796 unsigned int mlen = UTF8Length(&uptr[0], ulen);
1797 std::vector<char> putf(mlen+1);
1798 // CP_UTF8 not available on Windows 95, so use UTF8FromUTF16()
1799 UTF8FromUTF16(&uptr[0], ulen, &putf[0], mlen);
1801 InsertPasteShape(&putf[0], mlen, pasteShape);
1802 } else {
1803 InsertPasteShape(ptr, len, pasteShape);
1806 memSelection.Unlock();
1809 ::CloseClipboard();
1810 Redraw();
1813 void ScintillaWin::CreateCallTipWindow(PRectangle) {
1814 if (!ct.wCallTip.Created()) {
1815 ct.wCallTip = ::CreateWindow(callClassName, TEXT("ACallTip"),
1816 WS_POPUP, 100, 100, 150, 20,
1817 MainHWND(), 0,
1818 GetWindowInstance(MainHWND()),
1819 this);
1820 ct.wDraw = ct.wCallTip;
1824 void ScintillaWin::AddToPopUp(const char *label, int cmd, bool enabled) {
1825 HMENU hmenuPopup = reinterpret_cast<HMENU>(popup.GetID());
1826 if (!label[0])
1827 ::AppendMenuA(hmenuPopup, MF_SEPARATOR, 0, "");
1828 else if (enabled)
1829 ::AppendMenuA(hmenuPopup, MF_STRING, cmd, label);
1830 else
1831 ::AppendMenuA(hmenuPopup, MF_STRING | MF_DISABLED | MF_GRAYED, cmd, label);
1834 void ScintillaWin::ClaimSelection() {
1835 // Windows does not have a primary selection
1838 /// Implement IUnknown
1840 STDMETHODIMP_(ULONG)FormatEnumerator_AddRef(FormatEnumerator *fe);
1841 STDMETHODIMP FormatEnumerator_QueryInterface(FormatEnumerator *fe, REFIID riid, PVOID *ppv) {
1842 //Platform::DebugPrintf("EFE QI");
1843 *ppv = NULL;
1844 if (riid == IID_IUnknown)
1845 *ppv = reinterpret_cast<IEnumFORMATETC *>(fe);
1846 if (riid == IID_IEnumFORMATETC)
1847 *ppv = reinterpret_cast<IEnumFORMATETC *>(fe);
1848 if (!*ppv)
1849 return E_NOINTERFACE;
1850 FormatEnumerator_AddRef(fe);
1851 return S_OK;
1853 STDMETHODIMP_(ULONG)FormatEnumerator_AddRef(FormatEnumerator *fe) {
1854 return ++fe->ref;
1856 STDMETHODIMP_(ULONG)FormatEnumerator_Release(FormatEnumerator *fe) {
1857 fe->ref--;
1858 if (fe->ref > 0)
1859 return fe->ref;
1860 delete fe;
1861 return 0;
1863 /// Implement IEnumFORMATETC
1864 STDMETHODIMP FormatEnumerator_Next(FormatEnumerator *fe, ULONG celt, FORMATETC *rgelt, ULONG *pceltFetched) {
1865 if (rgelt == NULL) return E_POINTER;
1866 unsigned int putPos = 0;
1867 while ((fe->pos < fe->formats.size()) && (putPos < celt)) {
1868 rgelt->cfFormat = fe->formats[fe->pos];
1869 rgelt->ptd = 0;
1870 rgelt->dwAspect = DVASPECT_CONTENT;
1871 rgelt->lindex = -1;
1872 rgelt->tymed = TYMED_HGLOBAL;
1873 rgelt++;
1874 fe->pos++;
1875 putPos++;
1877 if (pceltFetched)
1878 *pceltFetched = putPos;
1879 return putPos ? S_OK : S_FALSE;
1881 STDMETHODIMP FormatEnumerator_Skip(FormatEnumerator *fe, ULONG celt) {
1882 fe->pos += celt;
1883 return S_OK;
1885 STDMETHODIMP FormatEnumerator_Reset(FormatEnumerator *fe) {
1886 fe->pos = 0;
1887 return S_OK;
1889 STDMETHODIMP FormatEnumerator_Clone(FormatEnumerator *fe, IEnumFORMATETC **ppenum) {
1890 FormatEnumerator *pfe;
1891 try {
1892 pfe = new FormatEnumerator(fe->pos, &fe->formats[0], fe->formats.size());
1893 } catch (...) {
1894 return E_OUTOFMEMORY;
1896 return FormatEnumerator_QueryInterface(pfe, IID_IEnumFORMATETC,
1897 reinterpret_cast<void **>(ppenum));
1900 static VFunction *vtFormatEnumerator[] = {
1901 (VFunction *)(FormatEnumerator_QueryInterface),
1902 (VFunction *)(FormatEnumerator_AddRef),
1903 (VFunction *)(FormatEnumerator_Release),
1904 (VFunction *)(FormatEnumerator_Next),
1905 (VFunction *)(FormatEnumerator_Skip),
1906 (VFunction *)(FormatEnumerator_Reset),
1907 (VFunction *)(FormatEnumerator_Clone)
1910 FormatEnumerator::FormatEnumerator(int pos_, CLIPFORMAT formats_[], size_t formatsLen_) {
1911 vtbl = vtFormatEnumerator;
1912 ref = 0; // First QI adds first reference...
1913 pos = pos_;
1914 formats.insert(formats.begin(), formats_, formats_+formatsLen_);
1917 /// Implement IUnknown
1918 STDMETHODIMP DropSource_QueryInterface(DropSource *ds, REFIID riid, PVOID *ppv) {
1919 return ds->sci->QueryInterface(riid, ppv);
1921 STDMETHODIMP_(ULONG)DropSource_AddRef(DropSource *ds) {
1922 return ds->sci->AddRef();
1924 STDMETHODIMP_(ULONG)DropSource_Release(DropSource *ds) {
1925 return ds->sci->Release();
1928 /// Implement IDropSource
1929 STDMETHODIMP DropSource_QueryContinueDrag(DropSource *, BOOL fEsc, DWORD grfKeyState) {
1930 if (fEsc)
1931 return DRAGDROP_S_CANCEL;
1932 if (!(grfKeyState & MK_LBUTTON))
1933 return DRAGDROP_S_DROP;
1934 return S_OK;
1937 STDMETHODIMP DropSource_GiveFeedback(DropSource *, DWORD) {
1938 return DRAGDROP_S_USEDEFAULTCURSORS;
1941 static VFunction *vtDropSource[] = {
1942 (VFunction *)(DropSource_QueryInterface),
1943 (VFunction *)(DropSource_AddRef),
1944 (VFunction *)(DropSource_Release),
1945 (VFunction *)(DropSource_QueryContinueDrag),
1946 (VFunction *)(DropSource_GiveFeedback)
1949 DropSource::DropSource() {
1950 vtbl = vtDropSource;
1951 sci = 0;
1954 /// Implement IUnkown
1955 STDMETHODIMP DataObject_QueryInterface(DataObject *pd, REFIID riid, PVOID *ppv) {
1956 //Platform::DebugPrintf("DO QI %x\n", pd);
1957 return pd->sci->QueryInterface(riid, ppv);
1959 STDMETHODIMP_(ULONG)DataObject_AddRef(DataObject *pd) {
1960 return pd->sci->AddRef();
1962 STDMETHODIMP_(ULONG)DataObject_Release(DataObject *pd) {
1963 return pd->sci->Release();
1965 /// Implement IDataObject
1966 STDMETHODIMP DataObject_GetData(DataObject *pd, FORMATETC *pFEIn, STGMEDIUM *pSTM) {
1967 return pd->sci->GetData(pFEIn, pSTM);
1970 STDMETHODIMP DataObject_GetDataHere(DataObject *, FORMATETC *, STGMEDIUM *) {
1971 //Platform::DebugPrintf("DOB GetDataHere\n");
1972 return E_NOTIMPL;
1975 STDMETHODIMP DataObject_QueryGetData(DataObject *pd, FORMATETC *pFE) {
1976 if (pd->sci->DragIsRectangularOK(pFE->cfFormat) &&
1977 pFE->ptd == 0 &&
1978 (pFE->dwAspect & DVASPECT_CONTENT) != 0 &&
1979 pFE->lindex == -1 &&
1980 (pFE->tymed & TYMED_HGLOBAL) != 0
1982 return S_OK;
1985 bool formatOK = (pFE->cfFormat == CF_TEXT) ||
1986 ((pFE->cfFormat == CF_UNICODETEXT) && pd->sci->IsUnicodeMode());
1987 if (!formatOK ||
1988 pFE->ptd != 0 ||
1989 (pFE->dwAspect & DVASPECT_CONTENT) == 0 ||
1990 pFE->lindex != -1 ||
1991 (pFE->tymed & TYMED_HGLOBAL) == 0
1993 //Platform::DebugPrintf("DOB QueryGetData No %x\n",pFE->cfFormat);
1994 //return DATA_E_FORMATETC;
1995 return S_FALSE;
1997 //Platform::DebugPrintf("DOB QueryGetData OK %x\n",pFE->cfFormat);
1998 return S_OK;
2001 STDMETHODIMP DataObject_GetCanonicalFormatEtc(DataObject *pd, FORMATETC *, FORMATETC *pFEOut) {
2002 //Platform::DebugPrintf("DOB GetCanon\n");
2003 if (pd->sci->IsUnicodeMode())
2004 pFEOut->cfFormat = CF_UNICODETEXT;
2005 else
2006 pFEOut->cfFormat = CF_TEXT;
2007 pFEOut->ptd = 0;
2008 pFEOut->dwAspect = DVASPECT_CONTENT;
2009 pFEOut->lindex = -1;
2010 pFEOut->tymed = TYMED_HGLOBAL;
2011 return S_OK;
2014 STDMETHODIMP DataObject_SetData(DataObject *, FORMATETC *, STGMEDIUM *, BOOL) {
2015 //Platform::DebugPrintf("DOB SetData\n");
2016 return E_FAIL;
2019 STDMETHODIMP DataObject_EnumFormatEtc(DataObject *pd, DWORD dwDirection, IEnumFORMATETC **ppEnum) {
2020 try {
2021 //Platform::DebugPrintf("DOB EnumFormatEtc %d\n", dwDirection);
2022 if (dwDirection != DATADIR_GET) {
2023 *ppEnum = 0;
2024 return E_FAIL;
2026 FormatEnumerator *pfe;
2027 if (pd->sci->IsUnicodeMode()) {
2028 CLIPFORMAT formats[] = {CF_UNICODETEXT, CF_TEXT};
2029 pfe = new FormatEnumerator(0, formats, ELEMENTS(formats));
2030 } else {
2031 CLIPFORMAT formats[] = {CF_TEXT};
2032 pfe = new FormatEnumerator(0, formats, ELEMENTS(formats));
2034 return FormatEnumerator_QueryInterface(pfe, IID_IEnumFORMATETC,
2035 reinterpret_cast<void **>(ppEnum));
2036 } catch (std::bad_alloc &) {
2037 pd->sci->errorStatus = SC_STATUS_BADALLOC;
2038 return E_OUTOFMEMORY;
2039 } catch (...) {
2040 pd->sci->errorStatus = SC_STATUS_FAILURE;
2041 return E_FAIL;
2045 STDMETHODIMP DataObject_DAdvise(DataObject *, FORMATETC *, DWORD, IAdviseSink *, PDWORD) {
2046 //Platform::DebugPrintf("DOB DAdvise\n");
2047 return E_FAIL;
2050 STDMETHODIMP DataObject_DUnadvise(DataObject *, DWORD) {
2051 //Platform::DebugPrintf("DOB DUnadvise\n");
2052 return E_FAIL;
2055 STDMETHODIMP DataObject_EnumDAdvise(DataObject *, IEnumSTATDATA **) {
2056 //Platform::DebugPrintf("DOB EnumDAdvise\n");
2057 return E_FAIL;
2060 static VFunction *vtDataObject[] = {
2061 (VFunction *)(DataObject_QueryInterface),
2062 (VFunction *)(DataObject_AddRef),
2063 (VFunction *)(DataObject_Release),
2064 (VFunction *)(DataObject_GetData),
2065 (VFunction *)(DataObject_GetDataHere),
2066 (VFunction *)(DataObject_QueryGetData),
2067 (VFunction *)(DataObject_GetCanonicalFormatEtc),
2068 (VFunction *)(DataObject_SetData),
2069 (VFunction *)(DataObject_EnumFormatEtc),
2070 (VFunction *)(DataObject_DAdvise),
2071 (VFunction *)(DataObject_DUnadvise),
2072 (VFunction *)(DataObject_EnumDAdvise)
2075 DataObject::DataObject() {
2076 vtbl = vtDataObject;
2077 sci = 0;
2080 /// Implement IUnknown
2081 STDMETHODIMP DropTarget_QueryInterface(DropTarget *dt, REFIID riid, PVOID *ppv) {
2082 //Platform::DebugPrintf("DT QI %x\n", dt);
2083 return dt->sci->QueryInterface(riid, ppv);
2085 STDMETHODIMP_(ULONG)DropTarget_AddRef(DropTarget *dt) {
2086 return dt->sci->AddRef();
2088 STDMETHODIMP_(ULONG)DropTarget_Release(DropTarget *dt) {
2089 return dt->sci->Release();
2092 /// Implement IDropTarget by forwarding to Scintilla
2093 STDMETHODIMP DropTarget_DragEnter(DropTarget *dt, LPDATAOBJECT pIDataSource, DWORD grfKeyState,
2094 POINTL pt, PDWORD pdwEffect) {
2095 try {
2096 return dt->sci->DragEnter(pIDataSource, grfKeyState, pt, pdwEffect);
2097 } catch (...) {
2098 dt->sci->errorStatus = SC_STATUS_FAILURE;
2100 return E_FAIL;
2102 STDMETHODIMP DropTarget_DragOver(DropTarget *dt, DWORD grfKeyState, POINTL pt, PDWORD pdwEffect) {
2103 try {
2104 return dt->sci->DragOver(grfKeyState, pt, pdwEffect);
2105 } catch (...) {
2106 dt->sci->errorStatus = SC_STATUS_FAILURE;
2108 return E_FAIL;
2110 STDMETHODIMP DropTarget_DragLeave(DropTarget *dt) {
2111 try {
2112 return dt->sci->DragLeave();
2113 } catch (...) {
2114 dt->sci->errorStatus = SC_STATUS_FAILURE;
2116 return E_FAIL;
2118 STDMETHODIMP DropTarget_Drop(DropTarget *dt, LPDATAOBJECT pIDataSource, DWORD grfKeyState,
2119 POINTL pt, PDWORD pdwEffect) {
2120 try {
2121 return dt->sci->Drop(pIDataSource, grfKeyState, pt, pdwEffect);
2122 } catch (...) {
2123 dt->sci->errorStatus = SC_STATUS_FAILURE;
2125 return E_FAIL;
2128 static VFunction *vtDropTarget[] = {
2129 (VFunction *)(DropTarget_QueryInterface),
2130 (VFunction *)(DropTarget_AddRef),
2131 (VFunction *)(DropTarget_Release),
2132 (VFunction *)(DropTarget_DragEnter),
2133 (VFunction *)(DropTarget_DragOver),
2134 (VFunction *)(DropTarget_DragLeave),
2135 (VFunction *)(DropTarget_Drop)
2138 DropTarget::DropTarget() {
2139 vtbl = vtDropTarget;
2140 sci = 0;
2144 * DBCS: support Input Method Editor (IME).
2145 * Called when IME Window opened.
2147 void ScintillaWin::ImeStartComposition() {
2148 if (caret.active) {
2149 // Move IME Window to current caret position
2150 HIMC hIMC = ::ImmGetContext(MainHWND());
2151 Point pos = PointMainCaret();
2152 COMPOSITIONFORM CompForm;
2153 CompForm.dwStyle = CFS_POINT;
2154 CompForm.ptCurrentPos.x = static_cast<int>(pos.x);
2155 CompForm.ptCurrentPos.y = static_cast<int>(pos.y);
2157 ::ImmSetCompositionWindow(hIMC, &CompForm);
2159 // Set font of IME window to same as surrounded text.
2160 if (stylesValid) {
2161 // Since the style creation code has been made platform independent,
2162 // The logfont for the IME is recreated here.
2163 int styleHere = (pdoc->StyleAt(sel.MainCaret())) & 31;
2164 LOGFONTA lf = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ""};
2165 int sizeZoomed = vs.styles[styleHere].size + vs.zoomLevel * SC_FONT_SIZE_MULTIPLIER;
2166 if (sizeZoomed <= 2 * SC_FONT_SIZE_MULTIPLIER) // Hangs if sizeZoomed <= 1
2167 sizeZoomed = 2 * SC_FONT_SIZE_MULTIPLIER;
2168 AutoSurface surface(this);
2169 int deviceHeight = sizeZoomed;
2170 if (surface) {
2171 deviceHeight = (sizeZoomed * surface->LogPixelsY()) / 72;
2173 // The negative is to allow for leading
2174 lf.lfHeight = -(abs(deviceHeight / SC_FONT_SIZE_MULTIPLIER));
2175 lf.lfWeight = vs.styles[styleHere].weight;
2176 lf.lfItalic = static_cast<BYTE>(vs.styles[styleHere].italic ? 1 : 0);
2177 lf.lfCharSet = DEFAULT_CHARSET;
2178 lf.lfFaceName[0] = '\0';
2179 if (vs.styles[styleHere].fontName)
2180 StringCopy(lf.lfFaceName, vs.styles[styleHere].fontName);
2182 ::ImmSetCompositionFontA(hIMC, &lf);
2184 ::ImmReleaseContext(MainHWND(), hIMC);
2185 // Caret is displayed in IME window. So, caret in Scintilla is useless.
2186 DropCaret();
2190 /** Called when IME Window closed. */
2191 void ScintillaWin::ImeEndComposition() {
2192 ShowCaretAtCurrentPosition();
2195 void ScintillaWin::AddCharBytes(char b0, char b1) {
2197 int inputCodePage = InputCodePage();
2198 if (inputCodePage && IsUnicodeMode()) {
2199 char utfval[4] = "\0\0\0";
2200 char ansiChars[3];
2201 wchar_t wcs[2];
2202 if (b0) { // Two bytes from IME
2203 ansiChars[0] = b0;
2204 ansiChars[1] = b1;
2205 ansiChars[2] = '\0';
2206 ::MultiByteToWideChar(inputCodePage, 0, ansiChars, 2, wcs, 1);
2207 } else {
2208 ansiChars[0] = b1;
2209 ansiChars[1] = '\0';
2210 ::MultiByteToWideChar(inputCodePage, 0, ansiChars, 1, wcs, 1);
2212 unsigned int len = UTF8Length(wcs, 1);
2213 UTF8FromUTF16(wcs, 1, utfval, len);
2214 utfval[len] = '\0';
2215 AddCharUTF(utfval, len ? len : 1);
2216 } else if (b0) {
2217 char dbcsChars[3];
2218 dbcsChars[0] = b0;
2219 dbcsChars[1] = b1;
2220 dbcsChars[2] = '\0';
2221 AddCharUTF(dbcsChars, 2, true);
2222 } else {
2223 AddChar(b1);
2227 void ScintillaWin::GetIntelliMouseParameters() {
2228 // This retrieves the number of lines per scroll as configured inthe Mouse Properties sheet in Control Panel
2229 ::SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, &linesPerScroll, 0);
2232 void ScintillaWin::CopyToClipboard(const SelectionText &selectedText) {
2233 if (!::OpenClipboard(MainHWND()))
2234 return;
2235 ::EmptyClipboard();
2237 GlobalMemory uniText;
2239 // Default Scintilla behaviour in Unicode mode
2240 if (IsUnicodeMode()) {
2241 int uchars = UTF16Length(selectedText.Data(),
2242 static_cast<int>(selectedText.LengthWithTerminator()));
2243 uniText.Allocate(2 * uchars);
2244 if (uniText) {
2245 UTF16FromUTF8(selectedText.Data(), static_cast<int>(selectedText.LengthWithTerminator()),
2246 static_cast<wchar_t *>(uniText.ptr), uchars);
2248 } else {
2249 // Not Unicode mode
2250 // Convert to Unicode using the current Scintilla code page
2251 UINT cpSrc = CodePageFromCharSet(
2252 selectedText.characterSet, selectedText.codePage);
2253 int uLen = ::MultiByteToWideChar(cpSrc, 0, selectedText.Data(),
2254 static_cast<int>(selectedText.LengthWithTerminator()), 0, 0);
2255 uniText.Allocate(2 * uLen);
2256 if (uniText) {
2257 ::MultiByteToWideChar(cpSrc, 0, selectedText.Data(),
2258 static_cast<int>(selectedText.LengthWithTerminator()),
2259 static_cast<wchar_t *>(uniText.ptr), uLen);
2263 if (uniText) {
2264 if (!IsNT()) {
2265 // Copy ANSI text to clipboard on Windows 9x
2266 // Convert from Unicode text, so other ANSI programs can
2267 // paste the text
2268 // Windows NT, 2k, XP automatically generates CF_TEXT
2269 GlobalMemory ansiText;
2270 ansiText.Allocate(selectedText.LengthWithTerminator());
2271 if (ansiText) {
2272 ::WideCharToMultiByte(CP_ACP, 0, static_cast<wchar_t *>(uniText.ptr), -1,
2273 static_cast<char *>(ansiText.ptr),
2274 static_cast<int>(selectedText.LengthWithTerminator()), NULL, NULL);
2275 ansiText.SetClip(CF_TEXT);
2278 uniText.SetClip(CF_UNICODETEXT);
2279 } else {
2280 // There was a failure - try to copy at least ANSI text
2281 GlobalMemory ansiText;
2282 ansiText.Allocate(selectedText.LengthWithTerminator());
2283 if (ansiText) {
2284 memcpy(static_cast<char *>(ansiText.ptr), selectedText.Data(), selectedText.LengthWithTerminator());
2285 ansiText.SetClip(CF_TEXT);
2289 if (selectedText.rectangular) {
2290 ::SetClipboardData(cfColumnSelect, 0);
2292 GlobalMemory borlandSelection;
2293 borlandSelection.Allocate(1);
2294 if (borlandSelection) {
2295 static_cast<BYTE *>(borlandSelection.ptr)[0] = 0x02;
2296 borlandSelection.SetClip(cfBorlandIDEBlockType);
2300 if (selectedText.lineCopy) {
2301 ::SetClipboardData(cfLineSelect, 0);
2304 ::CloseClipboard();
2307 void ScintillaWin::ScrollMessage(WPARAM wParam) {
2308 //DWORD dwStart = timeGetTime();
2309 //Platform::DebugPrintf("Scroll %x %d\n", wParam, lParam);
2311 SCROLLINFO sci = {};
2312 sci.cbSize = sizeof(sci);
2313 sci.fMask = SIF_ALL;
2315 GetScrollInfo(SB_VERT, &sci);
2317 //Platform::DebugPrintf("ScrollInfo %d mask=%x min=%d max=%d page=%d pos=%d track=%d\n", b,sci.fMask,
2318 //sci.nMin, sci.nMax, sci.nPage, sci.nPos, sci.nTrackPos);
2320 int topLineNew = topLine;
2321 switch (LoWord(wParam)) {
2322 case SB_LINEUP:
2323 topLineNew -= 1;
2324 break;
2325 case SB_LINEDOWN:
2326 topLineNew += 1;
2327 break;
2328 case SB_PAGEUP:
2329 topLineNew -= LinesToScroll(); break;
2330 case SB_PAGEDOWN: topLineNew += LinesToScroll(); break;
2331 case SB_TOP: topLineNew = 0; break;
2332 case SB_BOTTOM: topLineNew = MaxScrollPos(); break;
2333 case SB_THUMBPOSITION: topLineNew = sci.nTrackPos; break;
2334 case SB_THUMBTRACK: topLineNew = sci.nTrackPos; break;
2336 ScrollTo(topLineNew);
2339 void ScintillaWin::HorizontalScrollMessage(WPARAM wParam) {
2340 int xPos = xOffset;
2341 PRectangle rcText = GetTextRectangle();
2342 int pageWidth = static_cast<int>(rcText.Width() * 2 / 3);
2343 switch (LoWord(wParam)) {
2344 case SB_LINEUP:
2345 xPos -= 20;
2346 break;
2347 case SB_LINEDOWN: // May move past the logical end
2348 xPos += 20;
2349 break;
2350 case SB_PAGEUP:
2351 xPos -= pageWidth;
2352 break;
2353 case SB_PAGEDOWN:
2354 xPos += pageWidth;
2355 if (xPos > scrollWidth - rcText.Width()) { // Hit the end exactly
2356 xPos = scrollWidth - static_cast<int>(rcText.Width());
2358 break;
2359 case SB_TOP:
2360 xPos = 0;
2361 break;
2362 case SB_BOTTOM:
2363 xPos = scrollWidth;
2364 break;
2365 case SB_THUMBPOSITION:
2366 case SB_THUMBTRACK: {
2367 // 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 =]
2368 SCROLLINFO si;
2369 si.cbSize = sizeof(si);
2370 si.fMask = SIF_TRACKPOS;
2371 if (GetScrollInfo(SB_HORZ, &si)) {
2372 xPos = si.nTrackPos;
2375 break;
2377 HorizontalScrollTo(xPos);
2381 * Redraw all of text area.
2382 * This paint will not be abandoned.
2384 void ScintillaWin::FullPaint() {
2385 if (technology == SC_TECHNOLOGY_DEFAULT) {
2386 HDC hdc = ::GetDC(MainHWND());
2387 FullPaintDC(hdc);
2388 ::ReleaseDC(MainHWND(), hdc);
2389 } else {
2390 FullPaintDC(0);
2395 * Redraw all of text area on the specified DC.
2396 * This paint will not be abandoned.
2398 void ScintillaWin::FullPaintDC(HDC hdc) {
2399 paintState = painting;
2400 rcPaint = GetClientRectangle();
2401 paintingAllText = true;
2402 if (technology == SC_TECHNOLOGY_DEFAULT) {
2403 AutoSurface surfaceWindow(hdc, this);
2404 if (surfaceWindow) {
2405 Paint(surfaceWindow, rcPaint);
2406 surfaceWindow->Release();
2408 } else {
2409 #if defined(USE_D2D)
2410 EnsureRenderTarget();
2411 AutoSurface surfaceWindow(pRenderTarget, this);
2412 if (surfaceWindow) {
2413 pRenderTarget->BeginDraw();
2414 Paint(surfaceWindow, rcPaint);
2415 surfaceWindow->Release();
2416 HRESULT hr = pRenderTarget->EndDraw();
2417 if (hr == D2DERR_RECREATE_TARGET) {
2418 DropRenderTarget();
2421 #endif
2423 paintState = notPainting;
2426 static bool CompareDevCap(HDC hdc, HDC hOtherDC, int nIndex) {
2427 return ::GetDeviceCaps(hdc, nIndex) == ::GetDeviceCaps(hOtherDC, nIndex);
2430 bool ScintillaWin::IsCompatibleDC(HDC hOtherDC) {
2431 HDC hdc = ::GetDC(MainHWND());
2432 bool isCompatible =
2433 CompareDevCap(hdc, hOtherDC, TECHNOLOGY) &&
2434 CompareDevCap(hdc, hOtherDC, LOGPIXELSY) &&
2435 CompareDevCap(hdc, hOtherDC, LOGPIXELSX) &&
2436 CompareDevCap(hdc, hOtherDC, BITSPIXEL) &&
2437 CompareDevCap(hdc, hOtherDC, PLANES);
2438 ::ReleaseDC(MainHWND(), hdc);
2439 return isCompatible;
2442 DWORD ScintillaWin::EffectFromState(DWORD grfKeyState) const {
2443 // These are the Wordpad semantics.
2444 DWORD dwEffect;
2445 if (inDragDrop == ddDragging) // Internal defaults to move
2446 dwEffect = DROPEFFECT_MOVE;
2447 else
2448 dwEffect = DROPEFFECT_COPY;
2449 if (grfKeyState & MK_ALT)
2450 dwEffect = DROPEFFECT_MOVE;
2451 if (grfKeyState & MK_CONTROL)
2452 dwEffect = DROPEFFECT_COPY;
2453 return dwEffect;
2456 /// Implement IUnknown
2457 STDMETHODIMP ScintillaWin::QueryInterface(REFIID riid, PVOID *ppv) {
2458 *ppv = NULL;
2459 if (riid == IID_IUnknown)
2460 *ppv = reinterpret_cast<IDropTarget *>(&dt);
2461 if (riid == IID_IDropSource)
2462 *ppv = reinterpret_cast<IDropSource *>(&ds);
2463 if (riid == IID_IDropTarget)
2464 *ppv = reinterpret_cast<IDropTarget *>(&dt);
2465 if (riid == IID_IDataObject)
2466 *ppv = reinterpret_cast<IDataObject *>(&dob);
2467 if (!*ppv)
2468 return E_NOINTERFACE;
2469 return S_OK;
2472 STDMETHODIMP_(ULONG) ScintillaWin::AddRef() {
2473 return 1;
2476 STDMETHODIMP_(ULONG) ScintillaWin::Release() {
2477 return 1;
2480 /// Implement IDropTarget
2481 STDMETHODIMP ScintillaWin::DragEnter(LPDATAOBJECT pIDataSource, DWORD grfKeyState,
2482 POINTL, PDWORD pdwEffect) {
2483 if (pIDataSource == NULL)
2484 return E_POINTER;
2485 FORMATETC fmtu = {CF_UNICODETEXT, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
2486 HRESULT hrHasUText = pIDataSource->QueryGetData(&fmtu);
2487 hasOKText = (hrHasUText == S_OK);
2488 if (!hasOKText) {
2489 FORMATETC fmte = {CF_TEXT, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
2490 HRESULT hrHasText = pIDataSource->QueryGetData(&fmte);
2491 hasOKText = (hrHasText == S_OK);
2493 if (!hasOKText) {
2494 *pdwEffect = DROPEFFECT_NONE;
2495 return S_OK;
2498 *pdwEffect = EffectFromState(grfKeyState);
2499 return S_OK;
2502 STDMETHODIMP ScintillaWin::DragOver(DWORD grfKeyState, POINTL pt, PDWORD pdwEffect) {
2503 try {
2504 if (!hasOKText || pdoc->IsReadOnly()) {
2505 *pdwEffect = DROPEFFECT_NONE;
2506 return S_OK;
2509 *pdwEffect = EffectFromState(grfKeyState);
2511 // Update the cursor.
2512 POINT rpt = {pt.x, pt.y};
2513 ::ScreenToClient(MainHWND(), &rpt);
2514 SetDragPosition(SPositionFromLocation(PointFromPOINT(rpt), false, false, UserVirtualSpace()));
2516 return S_OK;
2517 } catch (...) {
2518 errorStatus = SC_STATUS_FAILURE;
2520 return E_FAIL;
2523 STDMETHODIMP ScintillaWin::DragLeave() {
2524 try {
2525 SetDragPosition(SelectionPosition(invalidPosition));
2526 return S_OK;
2527 } catch (...) {
2528 errorStatus = SC_STATUS_FAILURE;
2530 return E_FAIL;
2533 STDMETHODIMP ScintillaWin::Drop(LPDATAOBJECT pIDataSource, DWORD grfKeyState,
2534 POINTL pt, PDWORD pdwEffect) {
2535 try {
2536 *pdwEffect = EffectFromState(grfKeyState);
2538 if (pIDataSource == NULL)
2539 return E_POINTER;
2541 SetDragPosition(SelectionPosition(invalidPosition));
2543 STGMEDIUM medium = {0, {0}, 0};
2545 std::vector<char> data; // Includes terminating NUL
2547 FORMATETC fmtu = {CF_UNICODETEXT, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
2548 HRESULT hr = pIDataSource->GetData(&fmtu, &medium);
2549 if (SUCCEEDED(hr) && medium.hGlobal) {
2550 GlobalMemory memUDrop(medium.hGlobal);
2551 wchar_t *udata = static_cast<wchar_t *>(memUDrop.ptr);
2552 if (udata) {
2553 if (IsUnicodeMode()) {
2554 int tlen = static_cast<int>(memUDrop.Size());
2555 // Convert UTF-16 to UTF-8
2556 int dataLen = UTF8Length(udata, tlen/2);
2557 data.resize(dataLen+1);
2558 UTF8FromUTF16(udata, tlen/2, &data[0], dataLen);
2559 } else {
2560 // Convert UTF-16 to ANSI
2562 // Default Scintilla behavior in Unicode mode
2563 // CF_UNICODETEXT available, but not in Unicode mode
2564 // Convert from Unicode to current Scintilla code page
2565 UINT cpDest = CodePageOfDocument();
2566 int tlen = ::WideCharToMultiByte(cpDest, 0, udata, -1,
2567 NULL, 0, NULL, NULL) - 1; // subtract 0 terminator
2568 data.resize(tlen + 1);
2569 ::WideCharToMultiByte(cpDest, 0, udata, -1,
2570 &data[0], tlen + 1, NULL, NULL);
2573 memUDrop.Unlock();
2574 } else {
2575 FORMATETC fmte = {CF_TEXT, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
2576 hr = pIDataSource->GetData(&fmte, &medium);
2577 if (SUCCEEDED(hr) && medium.hGlobal) {
2578 GlobalMemory memDrop(medium.hGlobal);
2579 const char *cdata = static_cast<char *>(memDrop.ptr);
2580 if (cdata)
2581 data.assign(cdata, cdata+strlen(cdata)+1);
2582 memDrop.Unlock();
2586 if (!SUCCEEDED(hr) || data.empty()) {
2587 //Platform::DebugPrintf("Bad data format: 0x%x\n", hres);
2588 return hr;
2591 FORMATETC fmtr = {cfColumnSelect, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
2592 HRESULT hrRectangular = pIDataSource->QueryGetData(&fmtr);
2594 POINT rpt = {pt.x, pt.y};
2595 ::ScreenToClient(MainHWND(), &rpt);
2596 SelectionPosition movePos = SPositionFromLocation(PointFromPOINT(rpt), false, false, UserVirtualSpace());
2598 DropAt(movePos, &data[0], data.size() - 1, *pdwEffect == DROPEFFECT_MOVE, hrRectangular == S_OK);
2600 // Free data
2601 if (medium.pUnkForRelease != NULL)
2602 medium.pUnkForRelease->Release();
2603 else
2604 ::GlobalFree(medium.hGlobal);
2606 return S_OK;
2607 } catch (...) {
2608 errorStatus = SC_STATUS_FAILURE;
2610 return E_FAIL;
2613 /// Implement important part of IDataObject
2614 STDMETHODIMP ScintillaWin::GetData(FORMATETC *pFEIn, STGMEDIUM *pSTM) {
2615 bool formatOK = (pFEIn->cfFormat == CF_TEXT) ||
2616 ((pFEIn->cfFormat == CF_UNICODETEXT) && IsUnicodeMode());
2617 if (!formatOK ||
2618 pFEIn->ptd != 0 ||
2619 (pFEIn->dwAspect & DVASPECT_CONTENT) == 0 ||
2620 pFEIn->lindex != -1 ||
2621 (pFEIn->tymed & TYMED_HGLOBAL) == 0
2623 //Platform::DebugPrintf("DOB GetData No %d %x %x fmt=%x\n", lenDrag, pFEIn, pSTM, pFEIn->cfFormat);
2624 return DATA_E_FORMATETC;
2626 pSTM->tymed = TYMED_HGLOBAL;
2627 //Platform::DebugPrintf("DOB GetData OK %d %x %x\n", lenDrag, pFEIn, pSTM);
2629 GlobalMemory text;
2630 if (pFEIn->cfFormat == CF_UNICODETEXT) {
2631 int uchars = UTF16Length(drag.Data(), static_cast<int>(drag.LengthWithTerminator()));
2632 text.Allocate(2 * uchars);
2633 if (text) {
2634 UTF16FromUTF8(drag.Data(), static_cast<int>(drag.LengthWithTerminator()),
2635 static_cast<wchar_t *>(text.ptr), uchars);
2637 } else {
2638 text.Allocate(drag.LengthWithTerminator());
2639 if (text) {
2640 memcpy(static_cast<char *>(text.ptr), drag.Data(), drag.LengthWithTerminator());
2643 pSTM->hGlobal = text ? text.Unlock() : 0;
2644 pSTM->pUnkForRelease = 0;
2645 return S_OK;
2648 bool ScintillaWin::Register(HINSTANCE hInstance_) {
2650 hInstance = hInstance_;
2651 bool result;
2653 // Register the Scintilla class
2654 if (IsNT()) {
2656 // Register Scintilla as a wide character window
2657 WNDCLASSEXW wndclass;
2658 wndclass.cbSize = sizeof(wndclass);
2659 wndclass.style = CS_GLOBALCLASS | CS_HREDRAW | CS_VREDRAW;
2660 wndclass.lpfnWndProc = ScintillaWin::SWndProc;
2661 wndclass.cbClsExtra = 0;
2662 wndclass.cbWndExtra = sizeof(ScintillaWin *);
2663 wndclass.hInstance = hInstance;
2664 wndclass.hIcon = NULL;
2665 wndclass.hCursor = NULL;
2666 wndclass.hbrBackground = NULL;
2667 wndclass.lpszMenuName = NULL;
2668 wndclass.lpszClassName = L"Scintilla";
2669 wndclass.hIconSm = 0;
2670 result = ::RegisterClassExW(&wndclass) != 0;
2671 } else {
2673 // Register Scintilla as a normal character window
2674 WNDCLASSEX wndclass;
2675 wndclass.cbSize = sizeof(wndclass);
2676 wndclass.style = CS_GLOBALCLASS | CS_HREDRAW | CS_VREDRAW;
2677 wndclass.lpfnWndProc = ScintillaWin::SWndProc;
2678 wndclass.cbClsExtra = 0;
2679 wndclass.cbWndExtra = sizeof(ScintillaWin *);
2680 wndclass.hInstance = hInstance;
2681 wndclass.hIcon = NULL;
2682 wndclass.hCursor = NULL;
2683 wndclass.hbrBackground = NULL;
2684 wndclass.lpszMenuName = NULL;
2685 wndclass.lpszClassName = scintillaClassName;
2686 wndclass.hIconSm = 0;
2687 result = ::RegisterClassEx(&wndclass) != 0;
2690 if (result) {
2691 // Register the CallTip class
2692 WNDCLASSEX wndclassc;
2693 wndclassc.cbSize = sizeof(wndclassc);
2694 wndclassc.style = CS_GLOBALCLASS | CS_HREDRAW | CS_VREDRAW;
2695 wndclassc.cbClsExtra = 0;
2696 wndclassc.cbWndExtra = sizeof(ScintillaWin *);
2697 wndclassc.hInstance = hInstance;
2698 wndclassc.hIcon = NULL;
2699 wndclassc.hbrBackground = NULL;
2700 wndclassc.lpszMenuName = NULL;
2701 wndclassc.lpfnWndProc = ScintillaWin::CTWndProc;
2702 wndclassc.hCursor = ::LoadCursor(NULL, IDC_ARROW);
2703 wndclassc.lpszClassName = callClassName;
2704 wndclassc.hIconSm = 0;
2706 result = ::RegisterClassEx(&wndclassc) != 0;
2709 return result;
2712 bool ScintillaWin::Unregister() {
2713 bool result = ::UnregisterClass(scintillaClassName, hInstance) != 0;
2714 if (::UnregisterClass(callClassName, hInstance) == 0)
2715 result = false;
2716 return result;
2719 bool ScintillaWin::HasCaretSizeChanged() const {
2720 if (
2721 ( (0 != vs.caretWidth) && (sysCaretWidth != vs.caretWidth) )
2722 || ((0 != vs.lineHeight) && (sysCaretHeight != vs.lineHeight))
2724 return true;
2726 return false;
2729 BOOL ScintillaWin::CreateSystemCaret() {
2730 sysCaretWidth = vs.caretWidth;
2731 if (0 == sysCaretWidth) {
2732 sysCaretWidth = 1;
2734 sysCaretHeight = vs.lineHeight;
2735 int bitmapSize = (((sysCaretWidth + 15) & ~15) >> 3) *
2736 sysCaretHeight;
2737 std::vector<char> bits(bitmapSize);
2738 sysCaretBitmap = ::CreateBitmap(sysCaretWidth, sysCaretHeight, 1,
2739 1, reinterpret_cast<BYTE *>(&bits[0]));
2740 BOOL retval = ::CreateCaret(
2741 MainHWND(), sysCaretBitmap,
2742 sysCaretWidth, sysCaretHeight);
2743 ::ShowCaret(MainHWND());
2744 return retval;
2747 BOOL ScintillaWin::DestroySystemCaret() {
2748 ::HideCaret(MainHWND());
2749 BOOL retval = ::DestroyCaret();
2750 if (sysCaretBitmap) {
2751 ::DeleteObject(sysCaretBitmap);
2752 sysCaretBitmap = 0;
2754 return retval;
2757 sptr_t PASCAL ScintillaWin::CTWndProc(
2758 HWND hWnd, UINT iMessage, WPARAM wParam, sptr_t lParam) {
2759 // Find C++ object associated with window.
2760 ScintillaWin *sciThis = reinterpret_cast<ScintillaWin *>(PointerFromWindow(hWnd));
2761 try {
2762 // ctp will be zero if WM_CREATE not seen yet
2763 if (sciThis == 0) {
2764 if (iMessage == WM_CREATE) {
2765 // Associate CallTip object with window
2766 CREATESTRUCT *pCreate = reinterpret_cast<CREATESTRUCT *>(lParam);
2767 SetWindowPointer(hWnd, pCreate->lpCreateParams);
2768 return 0;
2769 } else {
2770 return ::DefWindowProc(hWnd, iMessage, wParam, lParam);
2772 } else {
2773 if (iMessage == WM_NCDESTROY) {
2774 ::SetWindowLong(hWnd, 0, 0);
2775 return ::DefWindowProc(hWnd, iMessage, wParam, lParam);
2776 } else if (iMessage == WM_PAINT) {
2777 PAINTSTRUCT ps;
2778 ::BeginPaint(hWnd, &ps);
2779 Surface *surfaceWindow = Surface::Allocate(sciThis->technology);
2780 if (surfaceWindow) {
2781 #if defined(USE_D2D)
2782 ID2D1HwndRenderTarget *pCTRenderTarget = 0;
2783 #endif
2784 RECT rc;
2785 GetClientRect(hWnd, &rc);
2786 // Create a Direct2D render target.
2787 if (sciThis->technology == SC_TECHNOLOGY_DEFAULT) {
2788 surfaceWindow->Init(ps.hdc, hWnd);
2789 } else {
2790 #if defined(USE_D2D)
2791 D2D1_HWND_RENDER_TARGET_PROPERTIES dhrtp;
2792 dhrtp.hwnd = hWnd;
2793 dhrtp.pixelSize = D2D1::SizeU(rc.right - rc.left, rc.bottom - rc.top);
2794 dhrtp.presentOptions = D2D1_PRESENT_OPTIONS_NONE;
2796 D2D1_RENDER_TARGET_PROPERTIES drtp;
2797 drtp.type = D2D1_RENDER_TARGET_TYPE_DEFAULT;
2798 drtp.pixelFormat.format = DXGI_FORMAT_UNKNOWN;
2799 drtp.pixelFormat.alphaMode = D2D1_ALPHA_MODE_UNKNOWN;
2800 drtp.dpiX = 96.0;
2801 drtp.dpiY = 96.0;
2802 drtp.usage = D2D1_RENDER_TARGET_USAGE_NONE;
2803 drtp.minLevel = D2D1_FEATURE_LEVEL_DEFAULT;
2805 if (!SUCCEEDED(pD2DFactory->CreateHwndRenderTarget(drtp, dhrtp, &pCTRenderTarget))) {
2806 surfaceWindow->Release();
2807 delete surfaceWindow;
2808 ::EndPaint(hWnd, &ps);
2809 return 0;
2811 surfaceWindow->Init(pCTRenderTarget, hWnd);
2812 pCTRenderTarget->BeginDraw();
2813 #endif
2815 surfaceWindow->SetUnicodeMode(SC_CP_UTF8 == sciThis->ct.codePage);
2816 surfaceWindow->SetDBCSMode(sciThis->ct.codePage);
2817 sciThis->ct.PaintCT(surfaceWindow);
2818 #if defined(USE_D2D)
2819 if (pCTRenderTarget)
2820 pCTRenderTarget->EndDraw();
2821 #endif
2822 surfaceWindow->Release();
2823 delete surfaceWindow;
2824 #if defined(USE_D2D)
2825 if (pCTRenderTarget)
2826 pCTRenderTarget->Release();
2827 #endif
2829 ::EndPaint(hWnd, &ps);
2830 return 0;
2831 } else if ((iMessage == WM_NCLBUTTONDOWN) || (iMessage == WM_NCLBUTTONDBLCLK)) {
2832 POINT pt;
2833 pt.x = static_cast<short>(LOWORD(lParam));
2834 pt.y = static_cast<short>(HIWORD(lParam));
2835 ScreenToClient(hWnd, &pt);
2836 sciThis->ct.MouseClick(PointFromPOINT(pt));
2837 sciThis->CallTipClick();
2838 return 0;
2839 } else if (iMessage == WM_LBUTTONDOWN) {
2840 // This does not fire due to the hit test code
2841 sciThis->ct.MouseClick(Point::FromLong(static_cast<long>(lParam)));
2842 sciThis->CallTipClick();
2843 return 0;
2844 } else if (iMessage == WM_SETCURSOR) {
2845 ::SetCursor(::LoadCursor(NULL, IDC_ARROW));
2846 return 0;
2847 } else if (iMessage == WM_NCHITTEST) {
2848 return HTCAPTION;
2849 } else {
2850 return ::DefWindowProc(hWnd, iMessage, wParam, lParam);
2853 } catch (...) {
2854 sciThis->errorStatus = SC_STATUS_FAILURE;
2856 return ::DefWindowProc(hWnd, iMessage, wParam, lParam);
2859 sptr_t ScintillaWin::DirectFunction(
2860 sptr_t ptr, UINT iMessage, uptr_t wParam, sptr_t lParam) {
2861 PLATFORM_ASSERT(::GetCurrentThreadId() == ::GetWindowThreadProcessId(reinterpret_cast<ScintillaWin *>(ptr)->MainHWND(), NULL));
2862 return reinterpret_cast<ScintillaWin *>(ptr)->WndProc(iMessage, wParam, lParam);
2865 extern "C"
2866 #ifndef STATIC_BUILD
2867 __declspec(dllexport)
2868 #endif
2869 sptr_t __stdcall Scintilla_DirectFunction(
2870 ScintillaWin *sci, UINT iMessage, uptr_t wParam, sptr_t lParam) {
2871 return sci->WndProc(iMessage, wParam, lParam);
2874 sptr_t PASCAL ScintillaWin::SWndProc(
2875 HWND hWnd, UINT iMessage, WPARAM wParam, sptr_t lParam) {
2876 //Platform::DebugPrintf("S W:%x M:%x WP:%x L:%x\n", hWnd, iMessage, wParam, lParam);
2878 // Find C++ object associated with window.
2879 ScintillaWin *sci = reinterpret_cast<ScintillaWin *>(PointerFromWindow(hWnd));
2880 // sci will be zero if WM_CREATE not seen yet
2881 if (sci == 0) {
2882 try {
2883 if (iMessage == WM_CREATE) {
2884 // Create C++ object associated with window
2885 sci = new ScintillaWin(hWnd);
2886 SetWindowPointer(hWnd, sci);
2887 return sci->WndProc(iMessage, wParam, lParam);
2889 } catch (...) {
2891 return ::DefWindowProc(hWnd, iMessage, wParam, lParam);
2892 } else {
2893 if (iMessage == WM_NCDESTROY) {
2894 try {
2895 sci->Finalise();
2896 delete sci;
2897 } catch (...) {
2899 ::SetWindowLong(hWnd, 0, 0);
2900 return ::DefWindowProc(hWnd, iMessage, wParam, lParam);
2901 } else {
2902 return sci->WndProc(iMessage, wParam, lParam);
2907 // This function is externally visible so it can be called from container when building statically.
2908 // Must be called once only.
2909 int Scintilla_RegisterClasses(void *hInstance) {
2910 Platform_Initialise(hInstance);
2911 bool result = ScintillaWin::Register(reinterpret_cast<HINSTANCE>(hInstance));
2912 #ifdef SCI_LEXER
2913 Scintilla_LinkLexers();
2914 #endif
2915 return result;
2918 static int ResourcesRelease(bool fromDllMain) {
2919 bool result = ScintillaWin::Unregister();
2920 if (commctrl32) {
2921 FreeLibrary(commctrl32);
2922 commctrl32 = NULL;
2924 Platform_Finalise(fromDllMain);
2925 return result;
2928 // This function is externally visible so it can be called from container when building statically.
2929 int Scintilla_ReleaseResources() {
2930 return ResourcesRelease(false);
2933 #ifndef STATIC_BUILD
2934 extern "C" int APIENTRY DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpvReserved) {
2935 //Platform::DebugPrintf("Scintilla::DllMain %d %d\n", hInstance, dwReason);
2936 if (dwReason == DLL_PROCESS_ATTACH) {
2937 if (!Scintilla_RegisterClasses(hInstance))
2938 return FALSE;
2939 } else if (dwReason == DLL_PROCESS_DETACH) {
2940 if (lpvReserved == NULL) {
2941 ResourcesRelease(true);
2944 return TRUE;
2946 #endif