applied backgroundcolors.patch
[TortoiseGit.git] / ext / scintilla / win32 / ScintillaWin.cxx
blobf0068d46db358d14824abe46a5420f884dcd575a
1 // Scintilla source code edit control
2 /** @file ScintillaWin.cxx
3 ** Windows specific subclass of ScintillaBase.
4 **/
5 // Copyright 1998-2003 by Neil Hodgson <neilh@scintilla.org>
6 // The License.txt file describes the conditions under which this software may be distributed.
8 #include <new>
9 #include <stdlib.h>
10 #include <string.h>
11 #include <stdio.h>
12 #include <ctype.h>
13 #include <assert.h>
14 #include <limits.h>
16 #include <string>
17 #include <vector>
18 #include <map>
20 #undef _WIN32_WINNT
21 #define _WIN32_WINNT 0x0500
22 #undef WINVER
23 #define WINVER 0x0500
24 #include <windows.h>
25 #include <commctrl.h>
26 #include <richedit.h>
27 #include <windowsx.h>
29 #if defined(NTDDI_WIN7) && !defined(DISABLE_D2D)
30 #define USE_D2D 1
31 #endif
33 #if defined(USE_D2D)
34 #include <d2d1.h>
35 #include <dwrite.h>
36 #endif
38 #include "Platform.h"
40 #include "ILexer.h"
41 #include "Scintilla.h"
43 #ifdef SCI_LEXER
44 #include "SciLexer.h"
45 #include "LexerModule.h"
46 #endif
47 #include "SplitVector.h"
48 #include "Partitioning.h"
49 #include "RunStyles.h"
50 #include "ContractionState.h"
51 #include "CellBuffer.h"
52 #include "CallTip.h"
53 #include "KeyMap.h"
54 #include "Indicator.h"
55 #include "XPM.h"
56 #include "LineMarker.h"
57 #include "Style.h"
58 #include "AutoComplete.h"
59 #include "ViewStyle.h"
60 #include "CharClassify.h"
61 #include "Decoration.h"
62 #include "Document.h"
63 #include "Selection.h"
64 #include "PositionCache.h"
65 #include "Editor.h"
66 #include "ScintillaBase.h"
67 #include "UniConversion.h"
68 #include "PlatWin.h"
70 #ifdef SCI_LEXER
71 #include "ExternalLexer.h"
72 #endif
74 #ifndef SPI_GETWHEELSCROLLLINES
75 #define SPI_GETWHEELSCROLLLINES 104
76 #endif
78 #ifndef WM_UNICHAR
79 #define WM_UNICHAR 0x0109
80 #endif
82 #ifndef UNICODE_NOCHAR
83 #define UNICODE_NOCHAR 0xFFFF
84 #endif
86 #ifndef WM_IME_STARTCOMPOSITION
87 #include <imm.h>
88 #endif
90 #include <commctrl.h>
91 #ifndef __DMC__
92 #include <zmouse.h>
93 #endif
94 #include <ole2.h>
96 #ifndef MK_ALT
97 #define MK_ALT 32
98 #endif
100 #define SC_WIN_IDLE 5001
102 typedef BOOL (WINAPI *TrackMouseEventSig)(LPTRACKMOUSEEVENT);
104 // GCC has trouble with the standard COM ABI so do it the old C way with explicit vtables.
106 const TCHAR scintillaClassName[] = TEXT("Scintilla");
107 const TCHAR callClassName[] = TEXT("CallTip");
109 #ifdef SCI_NAMESPACE
110 using namespace Scintilla;
111 #endif
113 // Take care of 32/64 bit pointers
114 #ifdef GetWindowLongPtr
115 static void *PointerFromWindow(HWND hWnd) {
116 return reinterpret_cast<void *>(::GetWindowLongPtr(hWnd, 0));
118 static void SetWindowPointer(HWND hWnd, void *ptr) {
119 ::SetWindowLongPtr(hWnd, 0, reinterpret_cast<LONG_PTR>(ptr));
121 static void SetWindowID(HWND hWnd, int identifier) {
122 ::SetWindowLongPtr(hWnd, GWLP_ID, identifier);
124 #else
125 static void *PointerFromWindow(HWND hWnd) {
126 return reinterpret_cast<void *>(::GetWindowLong(hWnd, 0));
128 static void SetWindowPointer(HWND hWnd, void *ptr) {
129 ::SetWindowLong(hWnd, 0, reinterpret_cast<LONG>(ptr));
131 static void SetWindowID(HWND hWnd, int identifier) {
132 ::SetWindowLong(hWnd, GWL_ID, identifier);
134 #endif
136 class ScintillaWin; // Forward declaration for COM interface subobjects
138 typedef void VFunction(void);
142 class FormatEnumerator {
143 public:
144 VFunction **vtbl;
145 int ref;
146 int pos;
147 CLIPFORMAT formats[2];
148 int formatsLen;
149 FormatEnumerator(int pos_, CLIPFORMAT formats_[], int formatsLen_);
154 class DropSource {
155 public:
156 VFunction **vtbl;
157 ScintillaWin *sci;
158 DropSource();
163 class DataObject {
164 public:
165 VFunction **vtbl;
166 ScintillaWin *sci;
167 DataObject();
172 class DropTarget {
173 public:
174 VFunction **vtbl;
175 ScintillaWin *sci;
176 DropTarget();
181 class ScintillaWin :
182 public ScintillaBase {
184 bool lastKeyDownConsumed;
186 bool capturedMouse;
187 bool trackedMouseLeave;
188 TrackMouseEventSig TrackMouseEventFn;
190 unsigned int linesPerScroll; ///< Intellimouse support
191 int wheelDelta; ///< Wheel delta from roll
193 HRGN hRgnUpdate;
195 bool hasOKText;
197 CLIPFORMAT cfColumnSelect;
198 CLIPFORMAT cfLineSelect;
200 HRESULT hrOle;
201 DropSource ds;
202 DataObject dob;
203 DropTarget dt;
205 static HINSTANCE hInstance;
207 #if defined(USE_D2D)
208 ID2D1HwndRenderTarget *pRenderTarget;
209 bool renderTargetValid;
210 #endif
212 ScintillaWin(HWND hwnd);
213 ScintillaWin(const ScintillaWin &);
214 virtual ~ScintillaWin();
215 ScintillaWin &operator=(const ScintillaWin &);
217 virtual void Initialise();
218 virtual void Finalise();
219 void EnsureRenderTarget();
220 void DropRenderTarget();
221 HWND MainHWND();
223 static sptr_t DirectFunction(
224 ScintillaWin *sci, UINT iMessage, uptr_t wParam, sptr_t lParam);
225 static sptr_t PASCAL SWndProc(
226 HWND hWnd, UINT iMessage, WPARAM wParam, sptr_t lParam);
227 static sptr_t PASCAL CTWndProc(
228 HWND hWnd, UINT iMessage, WPARAM wParam, sptr_t lParam);
230 enum { invalidTimerID, standardTimerID, idleTimerID };
232 virtual bool DragThreshold(Point ptStart, Point ptNow);
233 virtual void StartDrag();
234 sptr_t WndPaint(uptr_t wParam);
235 sptr_t HandleComposition(uptr_t wParam, sptr_t lParam);
236 UINT CodePageOfDocument();
237 virtual bool ValidCodePage(int codePage) const;
238 virtual sptr_t DefWndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam);
239 virtual bool SetIdle(bool on);
240 virtual void SetTicking(bool on);
241 virtual void SetMouseCapture(bool on);
242 virtual bool HaveMouseCapture();
243 virtual void SetTrackMouseLeaveEvent(bool on);
244 virtual bool PaintContains(PRectangle rc);
245 virtual void ScrollText(int linesToMove);
246 virtual void UpdateSystemCaret();
247 virtual void SetVerticalScrollPos();
248 virtual void SetHorizontalScrollPos();
249 virtual bool ModifyScrollBars(int nMax, int nPage);
250 virtual void NotifyChange();
251 virtual void NotifyFocus(bool focus);
252 virtual void SetCtrlID(int identifier);
253 virtual int GetCtrlID();
254 virtual void NotifyParent(SCNotification scn);
255 virtual void NotifyParent(SCNotification * scn);
256 virtual void NotifyDoubleClick(Point pt, bool shift, bool ctrl, bool alt);
257 virtual CaseFolder *CaseFolderForEncoding();
258 virtual std::string CaseMapString(const std::string &s, int caseMapping);
259 virtual void Copy();
260 virtual void CopyAllowLine();
261 virtual bool CanPaste();
262 virtual void Paste();
263 virtual void CreateCallTipWindow(PRectangle rc);
264 virtual void AddToPopUp(const char *label, int cmd = 0, bool enabled = true);
265 virtual void ClaimSelection();
267 // DBCS
268 void ImeStartComposition();
269 void ImeEndComposition();
271 void AddCharBytes(char b0, char b1);
273 void GetIntelliMouseParameters();
274 virtual void CopyToClipboard(const SelectionText &selectedText);
275 void ScrollMessage(WPARAM wParam);
276 void HorizontalScrollMessage(WPARAM wParam);
277 void FullPaint();
278 void FullPaintDC(HDC dc);
279 bool IsCompatibleDC(HDC dc);
280 DWORD EffectFromState(DWORD grfKeyState);
282 virtual int SetScrollInfo(int nBar, LPCSCROLLINFO lpsi, BOOL bRedraw);
283 virtual bool GetScrollInfo(int nBar, LPSCROLLINFO lpsi);
284 void ChangeScrollPos(int barType, int pos);
286 void InsertPasteText(const char *text, int len, SelectionPosition selStart, bool isRectangular, bool isLine);
288 public:
289 // Public for benefit of Scintilla_DirectFunction
290 virtual sptr_t WndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam);
292 /// Implement IUnknown
293 STDMETHODIMP QueryInterface(REFIID riid, PVOID *ppv);
294 STDMETHODIMP_(ULONG)AddRef();
295 STDMETHODIMP_(ULONG)Release();
297 /// Implement IDropTarget
298 STDMETHODIMP DragEnter(LPDATAOBJECT pIDataSource, DWORD grfKeyState,
299 POINTL pt, PDWORD pdwEffect);
300 STDMETHODIMP DragOver(DWORD grfKeyState, POINTL pt, PDWORD pdwEffect);
301 STDMETHODIMP DragLeave();
302 STDMETHODIMP Drop(LPDATAOBJECT pIDataSource, DWORD grfKeyState,
303 POINTL pt, PDWORD pdwEffect);
305 /// Implement important part of IDataObject
306 STDMETHODIMP GetData(FORMATETC *pFEIn, STGMEDIUM *pSTM);
308 static bool Register(HINSTANCE hInstance_);
309 static bool Unregister();
311 friend class DropSource;
312 friend class DataObject;
313 friend class DropTarget;
314 bool DragIsRectangularOK(CLIPFORMAT fmt) {
315 return drag.rectangular && (fmt == cfColumnSelect);
318 private:
319 // For use in creating a system caret
320 bool HasCaretSizeChanged();
321 BOOL CreateSystemCaret();
322 BOOL DestroySystemCaret();
323 HBITMAP sysCaretBitmap;
324 int sysCaretWidth;
325 int sysCaretHeight;
326 bool keysAlwaysUnicode;
329 HINSTANCE ScintillaWin::hInstance = 0;
331 ScintillaWin::ScintillaWin(HWND hwnd) {
333 lastKeyDownConsumed = false;
335 capturedMouse = false;
336 trackedMouseLeave = false;
337 TrackMouseEventFn = 0;
339 linesPerScroll = 0;
340 wheelDelta = 0; // Wheel delta from roll
342 hRgnUpdate = 0;
344 hasOKText = false;
346 // There does not seem to be a real standard for indicating that the clipboard
347 // contains a rectangular selection, so copy Developer Studio.
348 cfColumnSelect = static_cast<CLIPFORMAT>(
349 ::RegisterClipboardFormat(TEXT("MSDEVColumnSelect")));
351 // Likewise for line-copy (copies a full line when no text is selected)
352 cfLineSelect = static_cast<CLIPFORMAT>(
353 ::RegisterClipboardFormat(TEXT("MSDEVLineSelect")));
355 hrOle = E_FAIL;
357 wMain = hwnd;
359 dob.sci = this;
360 ds.sci = this;
361 dt.sci = this;
363 sysCaretBitmap = 0;
364 sysCaretWidth = 0;
365 sysCaretHeight = 0;
367 #if defined(USE_D2D)
368 pRenderTarget = 0;
369 renderTargetValid = true;
370 #endif
372 keysAlwaysUnicode = false;
374 caret.period = ::GetCaretBlinkTime();
375 if (caret.period < 0)
376 caret.period = 0;
378 Initialise();
381 ScintillaWin::~ScintillaWin() {}
383 void ScintillaWin::Initialise() {
384 // Initialize COM. If the app has already done this it will have
385 // no effect. If the app hasnt, we really shouldnt ask them to call
386 // it just so this internal feature works.
387 hrOle = ::OleInitialize(NULL);
389 // Find TrackMouseEvent which is available on Windows > 95
390 HMODULE user32 = ::GetModuleHandle(TEXT("user32.dll"));
391 if (user32)
392 TrackMouseEventFn = (TrackMouseEventSig)::GetProcAddress(user32, "TrackMouseEvent");
393 if (TrackMouseEventFn == NULL) {
394 // Windows 95 has an emulation in comctl32.dll:_TrackMouseEvent
395 HMODULE commctrl32 = ::LoadLibrary(TEXT("comctl32.dll"));
396 if (commctrl32 != NULL) {
397 TrackMouseEventFn = (TrackMouseEventSig)
398 ::GetProcAddress(commctrl32, "_TrackMouseEvent");
403 void ScintillaWin::Finalise() {
404 ScintillaBase::Finalise();
405 SetTicking(false);
406 SetIdle(false);
407 DropRenderTarget();
408 ::RevokeDragDrop(MainHWND());
409 if (SUCCEEDED(hrOle)) {
410 ::OleUninitialize();
414 void ScintillaWin::EnsureRenderTarget() {
415 #if defined(USE_D2D)
416 if (!renderTargetValid) {
417 DropRenderTarget();
418 renderTargetValid = true;
420 if (pD2DFactory && !pRenderTarget) {
421 RECT rc;
422 HWND hw = MainHWND();
423 GetClientRect(hw, &rc);
425 D2D1_SIZE_U size = D2D1::SizeU(rc.right - rc.left, rc.bottom - rc.top);
427 // Create a Direct2D render target.
428 #if 1
429 pD2DFactory->CreateHwndRenderTarget(
430 D2D1::RenderTargetProperties(D2D1_RENDER_TARGET_TYPE_DEFAULT, D2D1::PixelFormat(), 96.0, 96.0),
431 D2D1::HwndRenderTargetProperties(hw, size),
432 &pRenderTarget);
433 #else
434 pD2DFactory->CreateHwndRenderTarget(
435 D2D1::RenderTargetProperties(
436 D2D1_RENDER_TARGET_TYPE_DEFAULT ,
437 D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_PREMULTIPLIED),
438 96.0f, 96.0f, D2D1_RENDER_TARGET_USAGE_NONE, D2D1_FEATURE_LEVEL_DEFAULT),
439 D2D1::HwndRenderTargetProperties(hw, size),
440 &pRenderTarget);
441 #endif
442 // Pixmaps were created to be compatible with previous render target so
443 // need to be recreated.
444 DropGraphics(false);
446 #endif
449 void ScintillaWin::DropRenderTarget() {
450 #if defined(USE_D2D)
451 if (pRenderTarget) {
452 pRenderTarget->Release();
453 pRenderTarget = 0;
455 #endif
458 HWND ScintillaWin::MainHWND() {
459 return reinterpret_cast<HWND>(wMain.GetID());
462 bool ScintillaWin::DragThreshold(Point ptStart, Point ptNow) {
463 int xMove = abs(ptStart.x - ptNow.x);
464 int yMove = abs(ptStart.y - ptNow.y);
465 return (xMove > ::GetSystemMetrics(SM_CXDRAG)) ||
466 (yMove > ::GetSystemMetrics(SM_CYDRAG));
469 void ScintillaWin::StartDrag() {
470 inDragDrop = ddDragging;
471 DWORD dwEffect = 0;
472 dropWentOutside = true;
473 IDataObject *pDataObject = reinterpret_cast<IDataObject *>(&dob);
474 IDropSource *pDropSource = reinterpret_cast<IDropSource *>(&ds);
475 //Platform::DebugPrintf("About to DoDragDrop %x %x\n", pDataObject, pDropSource);
476 HRESULT hr = ::DoDragDrop(
477 pDataObject,
478 pDropSource,
479 DROPEFFECT_COPY | DROPEFFECT_MOVE, &dwEffect);
480 //Platform::DebugPrintf("DoDragDrop = %x\n", hr);
481 if (SUCCEEDED(hr)) {
482 if ((hr == DRAGDROP_S_DROP) && (dwEffect == DROPEFFECT_MOVE) && dropWentOutside) {
483 // Remove dragged out text
484 ClearSelection();
487 inDragDrop = ddNone;
488 SetDragPosition(SelectionPosition(invalidPosition));
491 // Avoid warnings everywhere for old style casts by concentrating them here
492 static WORD LoWord(DWORD l) {
493 return LOWORD(l);
496 static WORD HiWord(DWORD l) {
497 return HIWORD(l);
500 static int InputCodePage() {
501 HKL inputLocale = ::GetKeyboardLayout(0);
502 LANGID inputLang = LOWORD(inputLocale);
503 char sCodePage[10];
504 int res = ::GetLocaleInfoA(MAKELCID(inputLang, SORT_DEFAULT),
505 LOCALE_IDEFAULTANSICODEPAGE, sCodePage, sizeof(sCodePage));
506 if (!res)
507 return 0;
508 return atoi(sCodePage);
511 #ifndef VK_OEM_2
512 static const int VK_OEM_2=0xbf;
513 static const int VK_OEM_3=0xc0;
514 static const int VK_OEM_4=0xdb;
515 static const int VK_OEM_5=0xdc;
516 static const int VK_OEM_6=0xdd;
517 #endif
519 /** Map the key codes to their equivalent SCK_ form. */
520 static int KeyTranslate(int keyIn) {
521 //PLATFORM_ASSERT(!keyIn);
522 switch (keyIn) {
523 case VK_DOWN: return SCK_DOWN;
524 case VK_UP: return SCK_UP;
525 case VK_LEFT: return SCK_LEFT;
526 case VK_RIGHT: return SCK_RIGHT;
527 case VK_HOME: return SCK_HOME;
528 case VK_END: return SCK_END;
529 case VK_PRIOR: return SCK_PRIOR;
530 case VK_NEXT: return SCK_NEXT;
531 case VK_DELETE: return SCK_DELETE;
532 case VK_INSERT: return SCK_INSERT;
533 case VK_ESCAPE: return SCK_ESCAPE;
534 case VK_BACK: return SCK_BACK;
535 case VK_TAB: return SCK_TAB;
536 case VK_RETURN: return SCK_RETURN;
537 case VK_ADD: return SCK_ADD;
538 case VK_SUBTRACT: return SCK_SUBTRACT;
539 case VK_DIVIDE: return SCK_DIVIDE;
540 case VK_LWIN: return SCK_WIN;
541 case VK_RWIN: return SCK_RWIN;
542 case VK_APPS: return SCK_MENU;
543 case VK_OEM_2: return '/';
544 case VK_OEM_3: return '`';
545 case VK_OEM_4: return '[';
546 case VK_OEM_5: return '\\';
547 case VK_OEM_6: return ']';
548 default: return keyIn;
552 LRESULT ScintillaWin::WndPaint(uptr_t wParam) {
553 //ElapsedTime et;
555 // Redirect assertions to debug output and save current state
556 bool assertsPopup = Platform::ShowAssertionPopUps(false);
557 paintState = painting;
558 PAINTSTRUCT ps;
559 PAINTSTRUCT *pps;
561 bool IsOcxCtrl = (wParam != 0); // if wParam != 0, it contains
562 // a PAINSTRUCT* from the OCX
563 // Removed since this interferes with reporting other assertions as it occurs repeatedly
564 //PLATFORM_ASSERT(hRgnUpdate == NULL);
565 hRgnUpdate = ::CreateRectRgn(0, 0, 0, 0);
566 if (IsOcxCtrl) {
567 pps = reinterpret_cast<PAINTSTRUCT*>(wParam);
568 } else {
569 ::GetUpdateRgn(MainHWND(), hRgnUpdate, FALSE);
570 pps = &ps;
571 ::BeginPaint(MainHWND(), pps);
573 if (technology == SC_TECHNOLOGY_DEFAULT) {
574 AutoSurface surfaceWindow(pps->hdc, this);
575 if (surfaceWindow) {
576 rcPaint = PRectangle(pps->rcPaint.left, pps->rcPaint.top, pps->rcPaint.right, pps->rcPaint.bottom);
577 PRectangle rcClient = GetClientRectangle();
578 paintingAllText = rcPaint.Contains(rcClient);
579 Paint(surfaceWindow, rcPaint);
580 surfaceWindow->Release();
582 } else {
583 #if defined(USE_D2D)
584 EnsureRenderTarget();
585 AutoSurface surfaceWindow(pRenderTarget, this);
586 if (surfaceWindow) {
587 pRenderTarget->BeginDraw();
588 rcPaint = PRectangle(pps->rcPaint.left, pps->rcPaint.top, pps->rcPaint.right, pps->rcPaint.bottom);
589 PRectangle rcClient = GetClientRectangle();
590 paintingAllText = rcPaint.Contains(rcClient);
591 Paint(surfaceWindow, rcPaint);
592 surfaceWindow->Release();
593 HRESULT hr = pRenderTarget->EndDraw();
594 if (hr == D2DERR_RECREATE_TARGET) {
595 DropRenderTarget();
598 #endif
600 if (hRgnUpdate) {
601 ::DeleteRgn(hRgnUpdate);
602 hRgnUpdate = 0;
605 if (!IsOcxCtrl)
606 ::EndPaint(MainHWND(), pps);
607 if (paintState == paintAbandoned) {
608 // Painting area was insufficient to cover new styling or brace highlight positions
609 FullPaint();
611 paintState = notPainting;
613 // Restore debug output state
614 Platform::ShowAssertionPopUps(assertsPopup);
616 //Platform::DebugPrintf("Paint took %g\n", et.Duration());
617 return 0l;
620 sptr_t ScintillaWin::HandleComposition(uptr_t wParam, sptr_t lParam) {
621 #ifdef __DMC__
622 // Digital Mars compiler does not include Imm library
623 return 0;
624 #else
625 if (lParam & GCS_RESULTSTR) {
626 HIMC hIMC = ::ImmGetContext(MainHWND());
627 if (hIMC) {
628 const int maxLenInputIME = 200;
629 wchar_t wcs[maxLenInputIME];
630 LONG bytes = ::ImmGetCompositionStringW(hIMC,
631 GCS_RESULTSTR, wcs, (maxLenInputIME-1)*2);
632 int wides = bytes / 2;
633 if (IsUnicodeMode()) {
634 char utfval[maxLenInputIME * 3];
635 unsigned int len = UTF8Length(wcs, wides);
636 UTF8FromUTF16(wcs, wides, utfval, len);
637 utfval[len] = '\0';
638 AddCharUTF(utfval, len);
639 } else {
640 char dbcsval[maxLenInputIME * 2];
641 int size = ::WideCharToMultiByte(InputCodePage(),
642 0, wcs, wides, dbcsval, sizeof(dbcsval) - 1, 0, 0);
643 for (int i=0; i<size; i++) {
644 AddChar(dbcsval[i]);
647 // Set new position after converted
648 Point pos = PointMainCaret();
649 COMPOSITIONFORM CompForm;
650 CompForm.dwStyle = CFS_POINT;
651 CompForm.ptCurrentPos.x = pos.x;
652 CompForm.ptCurrentPos.y = pos.y;
653 ::ImmSetCompositionWindow(hIMC, &CompForm);
654 ::ImmReleaseContext(MainHWND(), hIMC);
656 return 0;
658 return ::DefWindowProc(MainHWND(), WM_IME_COMPOSITION, wParam, lParam);
659 #endif
662 // Translate message IDs from WM_* and EM_* to SCI_* so can partly emulate Windows Edit control
663 static unsigned int SciMessageFromEM(unsigned int iMessage) {
664 switch (iMessage) {
665 case EM_CANPASTE: return SCI_CANPASTE;
666 case EM_CANUNDO: return SCI_CANUNDO;
667 case EM_EMPTYUNDOBUFFER: return SCI_EMPTYUNDOBUFFER;
668 case EM_FINDTEXTEX: return SCI_FINDTEXT;
669 case EM_FORMATRANGE: return SCI_FORMATRANGE;
670 case EM_GETFIRSTVISIBLELINE: return SCI_GETFIRSTVISIBLELINE;
671 case EM_GETLINECOUNT: return SCI_GETLINECOUNT;
672 case EM_GETSELTEXT: return SCI_GETSELTEXT;
673 case EM_GETTEXTRANGE: return SCI_GETTEXTRANGE;
674 case EM_HIDESELECTION: return SCI_HIDESELECTION;
675 case EM_LINEINDEX: return SCI_POSITIONFROMLINE;
676 case EM_LINESCROLL: return SCI_LINESCROLL;
677 case EM_REPLACESEL: return SCI_REPLACESEL;
678 case EM_SCROLLCARET: return SCI_SCROLLCARET;
679 case EM_SETREADONLY: return SCI_SETREADONLY;
680 case WM_CLEAR: return SCI_CLEAR;
681 case WM_COPY: return SCI_COPY;
682 case WM_CUT: return SCI_CUT;
683 case WM_GETTEXT: return SCI_GETTEXT;
684 case WM_SETTEXT: return SCI_SETTEXT;
685 case WM_GETTEXTLENGTH: return SCI_GETTEXTLENGTH;
686 case WM_PASTE: return SCI_PASTE;
687 case WM_UNDO: return SCI_UNDO;
689 return iMessage;
692 static UINT CodePageFromCharSet(DWORD characterSet, UINT documentCodePage) {
693 if (documentCodePage == SC_CP_UTF8) {
694 // The system calls here are a little slow so avoid if known case.
695 return SC_CP_UTF8;
697 CHARSETINFO ci = { 0, 0, { { 0, 0, 0, 0 }, { 0, 0 } } };
698 BOOL bci = ::TranslateCharsetInfo((DWORD*)characterSet,
699 &ci, TCI_SRCCHARSET);
701 UINT cp;
702 if (bci)
703 cp = ci.ciACP;
704 else
705 cp = documentCodePage;
707 CPINFO cpi;
708 if (!IsValidCodePage(cp) && !GetCPInfo(cp, &cpi))
709 cp = CP_ACP;
711 return cp;
714 UINT ScintillaWin::CodePageOfDocument() {
715 return CodePageFromCharSet(vs.styles[STYLE_DEFAULT].characterSet, pdoc->dbcsCodePage);
718 sptr_t ScintillaWin::WndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
719 try {
720 //Platform::DebugPrintf("S M:%x WP:%x L:%x\n", iMessage, wParam, lParam);
721 iMessage = SciMessageFromEM(iMessage);
722 switch (iMessage) {
724 case WM_CREATE:
725 ctrlID = ::GetDlgCtrlID(reinterpret_cast<HWND>(wMain.GetID()));
726 // Get Intellimouse scroll line parameters
727 GetIntelliMouseParameters();
728 ::RegisterDragDrop(MainHWND(), reinterpret_cast<IDropTarget *>(&dt));
729 break;
731 case WM_COMMAND:
732 Command(LoWord(wParam));
733 break;
735 case WM_PAINT:
736 return WndPaint(wParam);
738 case WM_PRINTCLIENT: {
739 HDC hdc = reinterpret_cast<HDC>(wParam);
740 if (!IsCompatibleDC(hdc)) {
741 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
743 FullPaintDC(hdc);
745 break;
747 case WM_VSCROLL:
748 ScrollMessage(wParam);
749 break;
751 case WM_HSCROLL:
752 HorizontalScrollMessage(wParam);
753 break;
755 case WM_SIZE: {
756 #if defined(USE_D2D)
757 if (paintState == notPainting) {
758 DropRenderTarget();
759 } else {
760 renderTargetValid = false;
762 #endif
763 //Platform::DebugPrintf("Scintilla WM_SIZE %d %d\n", LoWord(lParam), HiWord(lParam));
764 ChangeSize();
766 break;
768 case WM_MOUSEWHEEL:
769 // if autocomplete list active then send mousewheel message to it
770 if (ac.Active()) {
771 HWND hWnd = reinterpret_cast<HWND>(ac.lb->GetID());
772 ::SendMessage(hWnd, iMessage, wParam, lParam);
773 break;
776 // Don't handle datazoom.
777 // (A good idea for datazoom would be to "fold" or "unfold" details.
778 // i.e. if datazoomed out only class structures are visible, when datazooming in the control
779 // structures appear, then eventually the individual statements...)
780 if (wParam & MK_SHIFT) {
781 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
784 // Either SCROLL or ZOOM. We handle the wheel steppings calculation
785 wheelDelta -= static_cast<short>(HiWord(wParam));
786 if (abs(wheelDelta) >= WHEEL_DELTA && linesPerScroll > 0) {
787 int linesToScroll = linesPerScroll;
788 if (linesPerScroll == WHEEL_PAGESCROLL)
789 linesToScroll = LinesOnScreen() - 1;
790 if (linesToScroll == 0) {
791 linesToScroll = 1;
793 linesToScroll *= (wheelDelta / WHEEL_DELTA);
794 if (wheelDelta >= 0)
795 wheelDelta = wheelDelta % WHEEL_DELTA;
796 else
797 wheelDelta = - (-wheelDelta % WHEEL_DELTA);
799 if (wParam & MK_CONTROL) {
800 // Zoom! We play with the font sizes in the styles.
801 // Number of steps/line is ignored, we just care if sizing up or down
802 if (linesToScroll < 0) {
803 KeyCommand(SCI_ZOOMIN);
804 } else {
805 KeyCommand(SCI_ZOOMOUT);
807 } else {
808 // Scroll
809 ScrollTo(topLine + linesToScroll);
812 return 0;
814 case WM_TIMER:
815 if (wParam == standardTimerID && timer.ticking) {
816 Tick();
817 } else if (wParam == idleTimerID && idler.state) {
818 SendMessage(MainHWND(), SC_WIN_IDLE, 0, 1);
819 } else {
820 return 1;
822 break;
824 case SC_WIN_IDLE:
825 // wParam=dwTickCountInitial, or 0 to initialize. lParam=bSkipUserInputTest
826 if (idler.state) {
827 if (lParam || (WAIT_TIMEOUT == MsgWaitForMultipleObjects(0, 0, 0, 0, QS_INPUT|QS_HOTKEY))) {
828 if (Idle()) {
829 // User input was given priority above, but all events do get a turn. Other
830 // messages, notifications, etc. will get interleaved with the idle messages.
832 // However, some things like WM_PAINT are a lower priority, and will not fire
833 // when there's a message posted. So, several times a second, we stop and let
834 // the low priority events have a turn (after which the timer will fire again).
836 DWORD dwCurrent = GetTickCount();
837 DWORD dwStart = wParam ? wParam : dwCurrent;
838 const DWORD maxWorkTime = 50;
840 if (dwCurrent >= dwStart && dwCurrent > maxWorkTime && dwCurrent - maxWorkTime < dwStart)
841 PostMessage(MainHWND(), SC_WIN_IDLE, dwStart, 0);
842 } else {
843 SetIdle(false);
847 break;
849 case WM_GETMINMAXINFO:
850 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
852 case WM_LBUTTONDOWN: {
853 #ifndef __DMC__
854 // Digital Mars compiler does not include Imm library
855 // For IME, set the composition string as the result string.
856 HIMC hIMC = ::ImmGetContext(MainHWND());
857 ::ImmNotifyIME(hIMC, NI_COMPOSITIONSTR, CPS_COMPLETE, 0);
858 ::ImmReleaseContext(MainHWND(), hIMC);
859 #endif
861 //Platform::DebugPrintf("Buttdown %d %x %x %x %x %x\n",iMessage, wParam, lParam,
862 // Platform::IsKeyDown(VK_SHIFT),
863 // Platform::IsKeyDown(VK_CONTROL),
864 // Platform::IsKeyDown(VK_MENU));
865 ::SetFocus(MainHWND());
866 ButtonDown(Point::FromLong(lParam), ::GetMessageTime(),
867 (wParam & MK_SHIFT) != 0,
868 (wParam & MK_CONTROL) != 0,
869 Platform::IsKeyDown(VK_MENU));
871 break;
873 case WM_MOUSEMOVE:
874 SetTrackMouseLeaveEvent(true);
875 ButtonMove(Point::FromLong(lParam));
876 break;
878 case WM_MOUSELEAVE:
879 SetTrackMouseLeaveEvent(false);
880 MouseLeave();
881 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
883 case WM_LBUTTONUP:
884 ButtonUp(Point::FromLong(lParam),
885 ::GetMessageTime(),
886 (wParam & MK_CONTROL) != 0);
887 break;
889 case WM_RBUTTONDOWN:
890 ::SetFocus(MainHWND());
891 if (!PointInSelection(Point::FromLong(lParam))) {
892 CancelModes();
893 SetEmptySelection(PositionFromLocation(Point::FromLong(lParam)));
895 break;
897 case WM_SETCURSOR:
898 if (LoWord(lParam) == HTCLIENT) {
899 if (inDragDrop == ddDragging) {
900 DisplayCursor(Window::cursorUp);
901 } else {
902 // Display regular (drag) cursor over selection
903 POINT pt;
904 if (0 != ::GetCursorPos(&pt)) {
905 ::ScreenToClient(MainHWND(), &pt);
906 if (PointInSelMargin(Point(pt.x, pt.y))) {
907 DisplayCursor(GetMarginCursor(Point(pt.x, pt.y)));
908 } else if (PointInSelection(Point(pt.x, pt.y)) && !SelectionEmpty()) {
909 DisplayCursor(Window::cursorArrow);
910 } else if (PointIsHotspot(Point(pt.x, pt.y))) {
911 DisplayCursor(Window::cursorHand);
912 } else {
913 DisplayCursor(Window::cursorText);
917 return TRUE;
918 } else {
919 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
922 case WM_CHAR:
923 if (((wParam >= 128) || !iscntrl(wParam)) || !lastKeyDownConsumed) {
924 if (::IsWindowUnicode(MainHWND()) || keysAlwaysUnicode) {
925 wchar_t wcs[2] = {static_cast<wchar_t>(wParam), 0};
926 if (IsUnicodeMode()) {
927 // For a wide character version of the window:
928 char utfval[4];
929 unsigned int len = UTF8Length(wcs, 1);
930 UTF8FromUTF16(wcs, 1, utfval, len);
931 AddCharUTF(utfval, len);
932 } else {
933 UINT cpDest = CodePageOfDocument();
934 char inBufferCP[20];
935 int size = ::WideCharToMultiByte(cpDest,
936 0, wcs, 1, inBufferCP, sizeof(inBufferCP) - 1, 0, 0);
937 inBufferCP[size] = '\0';
938 AddCharUTF(inBufferCP, size);
940 } else {
941 if (IsUnicodeMode()) {
942 AddCharBytes('\0', LOBYTE(wParam));
943 } else {
944 AddChar(LOBYTE(wParam));
948 return 0;
950 case WM_UNICHAR:
951 if (wParam == UNICODE_NOCHAR) {
952 return IsUnicodeMode() ? 1 : 0;
953 } else if (lastKeyDownConsumed) {
954 return 1;
955 } else {
956 if (IsUnicodeMode()) {
957 char utfval[4];
958 wchar_t wcs[2] = {static_cast<wchar_t>(wParam), 0};
959 unsigned int len = UTF8Length(wcs, 1);
960 UTF8FromUTF16(wcs, 1, utfval, len);
961 AddCharUTF(utfval, len);
962 return 1;
963 } else {
964 return 0;
968 case WM_SYSKEYDOWN:
969 case WM_KEYDOWN: {
970 //Platform::DebugPrintf("S keydown %d %x %x %x %x\n",iMessage, wParam, lParam, ::IsKeyDown(VK_SHIFT), ::IsKeyDown(VK_CONTROL));
971 lastKeyDownConsumed = false;
972 int ret = KeyDown(KeyTranslate(wParam),
973 Platform::IsKeyDown(VK_SHIFT),
974 Platform::IsKeyDown(VK_CONTROL),
975 Platform::IsKeyDown(VK_MENU),
976 &lastKeyDownConsumed);
977 if (!ret && !lastKeyDownConsumed) {
978 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
980 break;
983 case WM_IME_KEYDOWN:
984 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
986 case WM_KEYUP:
987 //Platform::DebugPrintf("S keyup %d %x %x\n",iMessage, wParam, lParam);
988 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
990 case WM_SETTINGCHANGE:
991 //Platform::DebugPrintf("Setting Changed\n");
992 InvalidateStyleData();
993 // Get Intellimouse scroll line parameters
994 GetIntelliMouseParameters();
995 break;
997 case WM_GETDLGCODE:
998 return DLGC_HASSETSEL | DLGC_WANTALLKEYS;
1000 case WM_KILLFOCUS: {
1001 HWND wOther = reinterpret_cast<HWND>(wParam);
1002 HWND wThis = MainHWND();
1003 HWND wCT = reinterpret_cast<HWND>(ct.wCallTip.GetID());
1004 if (!wParam ||
1005 !(::IsChild(wThis, wOther) || (wOther == wCT))) {
1006 SetFocusState(false);
1007 DestroySystemCaret();
1010 break;
1012 case WM_SETFOCUS:
1013 SetFocusState(true);
1014 DestroySystemCaret();
1015 CreateSystemCaret();
1016 break;
1018 case WM_SYSCOLORCHANGE:
1019 //Platform::DebugPrintf("Setting Changed\n");
1020 InvalidateStyleData();
1021 break;
1023 case WM_IME_STARTCOMPOSITION: // dbcs
1024 ImeStartComposition();
1025 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1027 case WM_IME_ENDCOMPOSITION: // dbcs
1028 ImeEndComposition();
1029 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1031 case WM_IME_COMPOSITION:
1032 return HandleComposition(wParam, lParam);
1034 case WM_IME_CHAR: {
1035 AddCharBytes(HIBYTE(wParam), LOBYTE(wParam));
1036 return 0;
1039 case WM_CONTEXTMENU:
1040 if (displayPopupMenu) {
1041 Point pt = Point::FromLong(lParam);
1042 if ((pt.x == -1) && (pt.y == -1)) {
1043 // Caused by keyboard so display menu near caret
1044 pt = PointMainCaret();
1045 POINT spt = {static_cast<int>(pt.x), static_cast<int>(pt.y)};
1046 ::ClientToScreen(MainHWND(), &spt);
1047 pt = Point(spt.x, spt.y);
1049 ContextMenu(pt);
1050 return 0;
1052 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1054 case WM_INPUTLANGCHANGE:
1055 //::SetThreadLocale(LOWORD(lParam));
1056 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1058 case WM_INPUTLANGCHANGEREQUEST:
1059 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1061 case WM_ERASEBKGND:
1062 return 1; // Avoid any background erasure as whole window painted.
1064 case WM_CAPTURECHANGED:
1065 capturedMouse = false;
1066 return 0;
1068 // These are not handled in Scintilla and its faster to dispatch them here.
1069 // Also moves time out to here so profile doesn't count lots of empty message calls.
1071 case WM_MOVE:
1072 case WM_MOUSEACTIVATE:
1073 case WM_NCHITTEST:
1074 case WM_NCCALCSIZE:
1075 case WM_NCPAINT:
1076 case WM_NCMOUSEMOVE:
1077 case WM_NCLBUTTONDOWN:
1078 case WM_IME_SETCONTEXT:
1079 case WM_IME_NOTIFY:
1080 case WM_SYSCOMMAND:
1081 case WM_WINDOWPOSCHANGING:
1082 case WM_WINDOWPOSCHANGED:
1083 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1085 case EM_LINEFROMCHAR:
1086 if (static_cast<int>(wParam) < 0) {
1087 wParam = SelectionStart().Position();
1089 return pdoc->LineFromPosition(wParam);
1091 case EM_EXLINEFROMCHAR:
1092 return pdoc->LineFromPosition(lParam);
1094 case EM_GETSEL:
1095 if (wParam) {
1096 *reinterpret_cast<int *>(wParam) = SelectionStart().Position();
1098 if (lParam) {
1099 *reinterpret_cast<int *>(lParam) = SelectionEnd().Position();
1101 return MAKELONG(SelectionStart().Position(), SelectionEnd().Position());
1103 case EM_EXGETSEL: {
1104 if (lParam == 0) {
1105 return 0;
1107 Sci_CharacterRange *pCR = reinterpret_cast<Sci_CharacterRange *>(lParam);
1108 pCR->cpMin = SelectionStart().Position();
1109 pCR->cpMax = SelectionEnd().Position();
1111 break;
1113 case EM_SETSEL: {
1114 int nStart = static_cast<int>(wParam);
1115 int nEnd = static_cast<int>(lParam);
1116 if (nStart == 0 && nEnd == -1) {
1117 nEnd = pdoc->Length();
1119 if (nStart == -1) {
1120 nStart = nEnd; // Remove selection
1122 if (nStart > nEnd) {
1123 SetSelection(nEnd, nStart);
1124 } else {
1125 SetSelection(nStart, nEnd);
1127 EnsureCaretVisible();
1129 break;
1131 case EM_EXSETSEL: {
1132 if (lParam == 0) {
1133 return 0;
1135 Sci_CharacterRange *pCR = reinterpret_cast<Sci_CharacterRange *>(lParam);
1136 sel.selType = Selection::selStream;
1137 if (pCR->cpMin == 0 && pCR->cpMax == -1) {
1138 SetSelection(pCR->cpMin, pdoc->Length());
1139 } else {
1140 SetSelection(pCR->cpMin, pCR->cpMax);
1142 EnsureCaretVisible();
1143 return pdoc->LineFromPosition(SelectionStart().Position());
1146 case SCI_GETDIRECTFUNCTION:
1147 return reinterpret_cast<sptr_t>(DirectFunction);
1149 case SCI_GETDIRECTPOINTER:
1150 return reinterpret_cast<sptr_t>(this);
1152 case SCI_GRABFOCUS:
1153 ::SetFocus(MainHWND());
1154 break;
1156 case SCI_SETKEYSUNICODE:
1157 keysAlwaysUnicode = wParam != 0;
1158 break;
1160 case SCI_GETKEYSUNICODE:
1161 return keysAlwaysUnicode;
1163 case SCI_SETTECHNOLOGY:
1164 if ((wParam == SC_TECHNOLOGY_DEFAULT) || (wParam == SC_TECHNOLOGY_DIRECTWRITE)) {
1165 if (technology != static_cast<int>(wParam)) {
1166 if (static_cast<int>(wParam) == SC_TECHNOLOGY_DIRECTWRITE) {
1167 #if defined(USE_D2D)
1168 if (!LoadD2D())
1169 // Failed to load Direct2D or DirectWrite so no effect
1170 return 0;
1171 #else
1172 return 0;
1173 #endif
1175 technology = wParam;
1176 // Invalidate all cached information including layout.
1177 DropGraphics(true);
1178 InvalidateStyleRedraw();
1181 break;
1183 #ifdef SCI_LEXER
1184 case SCI_LOADLEXERLIBRARY:
1185 LexerManager::GetInstance()->Load(reinterpret_cast<const char *>(lParam));
1186 break;
1187 #endif
1189 default:
1190 return ScintillaBase::WndProc(iMessage, wParam, lParam);
1192 } catch (std::bad_alloc &) {
1193 errorStatus = SC_STATUS_BADALLOC;
1194 } catch (...) {
1195 errorStatus = SC_STATUS_FAILURE;
1197 return 0l;
1200 bool ScintillaWin::ValidCodePage(int codePage) const {
1201 return codePage == 0 || codePage == SC_CP_UTF8 ||
1202 codePage == 932 || codePage == 936 || codePage == 949 ||
1203 codePage == 950 || codePage == 1361;
1206 sptr_t ScintillaWin::DefWndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
1207 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1210 void ScintillaWin::SetTicking(bool on) {
1211 if (timer.ticking != on) {
1212 timer.ticking = on;
1213 if (timer.ticking) {
1214 timer.tickerID = ::SetTimer(MainHWND(), standardTimerID, timer.tickSize, NULL)
1215 ? reinterpret_cast<TickerID>(standardTimerID) : 0;
1216 } else {
1217 ::KillTimer(MainHWND(), reinterpret_cast<uptr_t>(timer.tickerID));
1218 timer.tickerID = 0;
1221 timer.ticksToWait = caret.period;
1224 bool ScintillaWin::SetIdle(bool on) {
1225 // On Win32 the Idler is implemented as a Timer on the Scintilla window. This
1226 // takes advantage of the fact that WM_TIMER messages are very low priority,
1227 // and are only posted when the message queue is empty, i.e. during idle time.
1228 if (idler.state != on) {
1229 if (on) {
1230 idler.idlerID = ::SetTimer(MainHWND(), idleTimerID, 10, NULL)
1231 ? reinterpret_cast<IdlerID>(idleTimerID) : 0;
1232 } else {
1233 ::KillTimer(MainHWND(), reinterpret_cast<uptr_t>(idler.idlerID));
1234 idler.idlerID = 0;
1236 idler.state = idler.idlerID != 0;
1238 return idler.state;
1241 void ScintillaWin::SetMouseCapture(bool on) {
1242 if (mouseDownCaptures) {
1243 if (on) {
1244 ::SetCapture(MainHWND());
1245 } else {
1246 ::ReleaseCapture();
1249 capturedMouse = on;
1252 bool ScintillaWin::HaveMouseCapture() {
1253 // Cannot just see if GetCapture is this window as the scroll bar also sets capture for the window
1254 return capturedMouse;
1255 //return capturedMouse && (::GetCapture() == MainHWND());
1258 void ScintillaWin::SetTrackMouseLeaveEvent(bool on) {
1259 if (on && TrackMouseEventFn && !trackedMouseLeave) {
1260 TRACKMOUSEEVENT tme;
1261 tme.cbSize = sizeof(tme);
1262 tme.dwFlags = TME_LEAVE;
1263 tme.hwndTrack = MainHWND();
1264 TrackMouseEventFn(&tme);
1266 trackedMouseLeave = on;
1269 bool ScintillaWin::PaintContains(PRectangle rc) {
1270 bool contains = true;
1271 if ((paintState == painting) && (!rc.Empty())) {
1272 if (!rcPaint.Contains(rc)) {
1273 contains = false;
1274 } else {
1275 // In bounding rectangle so check more accurately using region
1276 HRGN hRgnRange = ::CreateRectRgn(rc.left, rc.top, rc.right, rc.bottom);
1277 if (hRgnRange) {
1278 HRGN hRgnDest = ::CreateRectRgn(0, 0, 0, 0);
1279 if (hRgnDest) {
1280 int combination = ::CombineRgn(hRgnDest, hRgnRange, hRgnUpdate, RGN_DIFF);
1281 if (combination != NULLREGION) {
1282 contains = false;
1284 ::DeleteRgn(hRgnDest);
1286 ::DeleteRgn(hRgnRange);
1290 return contains;
1293 void ScintillaWin::ScrollText(int /* linesToMove */) {
1294 //Platform::DebugPrintf("ScintillaWin::ScrollText %d\n", linesToMove);
1295 //::ScrollWindow(MainHWND(), 0,
1296 // vs.lineHeight * linesToMove, 0, 0);
1297 //::UpdateWindow(MainHWND());
1298 Redraw();
1301 void ScintillaWin::UpdateSystemCaret() {
1302 if (hasFocus) {
1303 if (HasCaretSizeChanged()) {
1304 DestroySystemCaret();
1305 CreateSystemCaret();
1307 Point pos = PointMainCaret();
1308 ::SetCaretPos(pos.x, pos.y);
1312 int ScintillaWin::SetScrollInfo(int nBar, LPCSCROLLINFO lpsi, BOOL bRedraw) {
1313 return ::SetScrollInfo(MainHWND(), nBar, lpsi, bRedraw);
1316 bool ScintillaWin::GetScrollInfo(int nBar, LPSCROLLINFO lpsi) {
1317 return ::GetScrollInfo(MainHWND(), nBar, lpsi) ? true : false;
1320 // Change the scroll position but avoid repaint if changing to same value
1321 void ScintillaWin::ChangeScrollPos(int barType, int pos) {
1322 SCROLLINFO sci = {
1323 sizeof(sci), 0, 0, 0, 0, 0, 0
1325 sci.fMask = SIF_POS;
1326 GetScrollInfo(barType, &sci);
1327 if (sci.nPos != pos) {
1328 DwellEnd(true);
1329 sci.nPos = pos;
1330 SetScrollInfo(barType, &sci, TRUE);
1334 void ScintillaWin::SetVerticalScrollPos() {
1335 ChangeScrollPos(SB_VERT, topLine);
1338 void ScintillaWin::SetHorizontalScrollPos() {
1339 ChangeScrollPos(SB_HORZ, xOffset);
1342 bool ScintillaWin::ModifyScrollBars(int nMax, int nPage) {
1343 bool modified = false;
1344 SCROLLINFO sci = {
1345 sizeof(sci), 0, 0, 0, 0, 0, 0
1347 sci.fMask = SIF_PAGE | SIF_RANGE;
1348 GetScrollInfo(SB_VERT, &sci);
1349 int vertEndPreferred = nMax;
1350 if (!verticalScrollBarVisible)
1351 nPage = vertEndPreferred + 1;
1352 if ((sci.nMin != 0) ||
1353 (sci.nMax != vertEndPreferred) ||
1354 (sci.nPage != static_cast<unsigned int>(nPage)) ||
1355 (sci.nPos != 0)) {
1356 sci.fMask = SIF_PAGE | SIF_RANGE;
1357 sci.nMin = 0;
1358 sci.nMax = vertEndPreferred;
1359 sci.nPage = nPage;
1360 sci.nPos = 0;
1361 sci.nTrackPos = 1;
1362 SetScrollInfo(SB_VERT, &sci, TRUE);
1363 modified = true;
1366 PRectangle rcText = GetTextRectangle();
1367 int horizEndPreferred = scrollWidth;
1368 if (horizEndPreferred < 0)
1369 horizEndPreferred = 0;
1370 unsigned int pageWidth = rcText.Width();
1371 if (!horizontalScrollBarVisible || (wrapState != eWrapNone))
1372 pageWidth = horizEndPreferred + 1;
1373 sci.fMask = SIF_PAGE | SIF_RANGE;
1374 GetScrollInfo(SB_HORZ, &sci);
1375 if ((sci.nMin != 0) ||
1376 (sci.nMax != horizEndPreferred) ||
1377 (sci.nPage != pageWidth) ||
1378 (sci.nPos != 0)) {
1379 sci.fMask = SIF_PAGE | SIF_RANGE;
1380 sci.nMin = 0;
1381 sci.nMax = horizEndPreferred;
1382 sci.nPage = pageWidth;
1383 sci.nPos = 0;
1384 sci.nTrackPos = 1;
1385 SetScrollInfo(SB_HORZ, &sci, TRUE);
1386 modified = true;
1387 if (scrollWidth < static_cast<int>(pageWidth)) {
1388 HorizontalScrollTo(0);
1391 return modified;
1394 void ScintillaWin::NotifyChange() {
1395 ::SendMessage(::GetParent(MainHWND()), WM_COMMAND,
1396 MAKELONG(GetCtrlID(), SCEN_CHANGE),
1397 reinterpret_cast<LPARAM>(MainHWND()));
1400 void ScintillaWin::NotifyFocus(bool focus) {
1401 ::SendMessage(::GetParent(MainHWND()), WM_COMMAND,
1402 MAKELONG(GetCtrlID(), focus ? SCEN_SETFOCUS : SCEN_KILLFOCUS),
1403 reinterpret_cast<LPARAM>(MainHWND()));
1406 void ScintillaWin::SetCtrlID(int identifier) {
1407 ::SetWindowID(reinterpret_cast<HWND>(wMain.GetID()), identifier);
1410 int ScintillaWin::GetCtrlID() {
1411 return ::GetDlgCtrlID(reinterpret_cast<HWND>(wMain.GetID()));
1414 void ScintillaWin::NotifyParent(SCNotification scn) {
1415 scn.nmhdr.hwndFrom = MainHWND();
1416 scn.nmhdr.idFrom = GetCtrlID();
1417 ::SendMessage(::GetParent(MainHWND()), WM_NOTIFY,
1418 GetCtrlID(), reinterpret_cast<LPARAM>(&scn));
1421 void ScintillaWin::NotifyParent(SCNotification * scn) {
1422 scn->nmhdr.hwndFrom = MainHWND();
1423 scn->nmhdr.idFrom = GetCtrlID();
1424 ::SendMessage(::GetParent(MainHWND()), WM_NOTIFY,
1425 GetCtrlID(), reinterpret_cast<LPARAM>(scn));
1428 void ScintillaWin::NotifyDoubleClick(Point pt, bool shift, bool ctrl, bool alt) {
1429 //Platform::DebugPrintf("ScintillaWin Double click 0\n");
1430 ScintillaBase::NotifyDoubleClick(pt, shift, ctrl, alt);
1431 // Send myself a WM_LBUTTONDBLCLK, so the container can handle it too.
1432 ::SendMessage(MainHWND(),
1433 WM_LBUTTONDBLCLK,
1434 shift ? MK_SHIFT : 0,
1435 MAKELPARAM(pt.x, pt.y));
1438 class CaseFolderUTF8 : public CaseFolderTable {
1439 // Allocate the expandable storage here so that it does not need to be reallocated
1440 // for each call to Fold.
1441 std::vector<wchar_t> utf16Mixed;
1442 std::vector<wchar_t> utf16Folded;
1443 public:
1444 CaseFolderUTF8() {
1445 StandardASCII();
1447 virtual size_t Fold(char *folded, size_t sizeFolded, const char *mixed, size_t lenMixed) {
1448 if ((lenMixed == 1) && (sizeFolded > 0)) {
1449 folded[0] = mapping[static_cast<unsigned char>(mixed[0])];
1450 return 1;
1451 } else {
1452 if (lenMixed > utf16Mixed.size()) {
1453 utf16Mixed.resize(lenMixed + 8);
1455 size_t nUtf16Mixed = ::MultiByteToWideChar(65001, 0, mixed,
1456 static_cast<int>(lenMixed),
1457 &utf16Mixed[0],
1458 static_cast<int>(utf16Mixed.size()));
1460 if (nUtf16Mixed == 0) {
1461 // Failed to convert -> bad UTF-8
1462 folded[0] = '\0';
1463 return 1;
1466 if (nUtf16Mixed * 4 > utf16Folded.size()) { // Maximum folding expansion factor of 4
1467 utf16Folded.resize(nUtf16Mixed * 4 + 8);
1469 int lenFlat = ::LCMapStringW(LOCALE_SYSTEM_DEFAULT,
1470 LCMAP_LINGUISTIC_CASING | LCMAP_LOWERCASE,
1471 &utf16Mixed[0],
1472 static_cast<int>(nUtf16Mixed),
1473 &utf16Folded[0],
1474 static_cast<int>(utf16Folded.size()));
1476 size_t lenOut = UTF8Length(&utf16Folded[0], lenFlat);
1477 if (lenOut < sizeFolded) {
1478 UTF8FromUTF16(&utf16Folded[0], lenFlat, folded, static_cast<int>(lenOut));
1479 return lenOut;
1480 } else {
1481 return 0;
1487 class CaseFolderDBCS : public CaseFolderTable {
1488 // Allocate the expandable storage here so that it does not need to be reallocated
1489 // for each call to Fold.
1490 std::vector<wchar_t> utf16Mixed;
1491 std::vector<wchar_t> utf16Folded;
1492 UINT cp;
1493 public:
1494 CaseFolderDBCS(UINT cp_) : cp(cp_) {
1495 StandardASCII();
1497 virtual size_t Fold(char *folded, size_t sizeFolded, const char *mixed, size_t lenMixed) {
1498 if ((lenMixed == 1) && (sizeFolded > 0)) {
1499 folded[0] = mapping[static_cast<unsigned char>(mixed[0])];
1500 return 1;
1501 } else {
1502 if (lenMixed > utf16Mixed.size()) {
1503 utf16Mixed.resize(lenMixed + 8);
1505 size_t nUtf16Mixed = ::MultiByteToWideChar(cp, 0, mixed,
1506 static_cast<int>(lenMixed),
1507 &utf16Mixed[0],
1508 static_cast<int>(utf16Mixed.size()));
1510 if (nUtf16Mixed == 0) {
1511 // Failed to convert -> bad input
1512 folded[0] = '\0';
1513 return 1;
1516 if (nUtf16Mixed * 4 > utf16Folded.size()) { // Maximum folding expansion factor of 4
1517 utf16Folded.resize(nUtf16Mixed * 4 + 8);
1519 int lenFlat = ::LCMapStringW(LOCALE_SYSTEM_DEFAULT,
1520 LCMAP_LINGUISTIC_CASING | LCMAP_LOWERCASE,
1521 &utf16Mixed[0],
1522 static_cast<int>(nUtf16Mixed),
1523 &utf16Folded[0],
1524 static_cast<int>(utf16Folded.size()));
1526 size_t lenOut = ::WideCharToMultiByte(cp, 0,
1527 &utf16Folded[0], lenFlat,
1528 NULL, 0, NULL, 0);
1530 if (lenOut < sizeFolded) {
1531 ::WideCharToMultiByte(cp, 0,
1532 &utf16Folded[0], lenFlat,
1533 folded, static_cast<int>(lenOut), NULL, 0);
1534 return lenOut;
1535 } else {
1536 return 0;
1542 CaseFolder *ScintillaWin::CaseFolderForEncoding() {
1543 UINT cpDest = CodePageOfDocument();
1544 if (cpDest == SC_CP_UTF8) {
1545 return new CaseFolderUTF8();
1546 } else {
1547 if (pdoc->dbcsCodePage == 0) {
1548 CaseFolderTable *pcf = new CaseFolderTable();
1549 pcf->StandardASCII();
1550 // Only for single byte encodings
1551 UINT cpDoc = CodePageOfDocument();
1552 for (int i=0x80; i<0x100; i++) {
1553 char sCharacter[2] = "A";
1554 sCharacter[0] = static_cast<char>(i);
1555 wchar_t wCharacter[20];
1556 unsigned int lengthUTF16 = ::MultiByteToWideChar(cpDoc, 0, sCharacter, 1,
1557 wCharacter, sizeof(wCharacter)/sizeof(wCharacter[0]));
1558 if (lengthUTF16 == 1) {
1559 wchar_t wLower[20];
1560 int charsConverted = ::LCMapStringW(LOCALE_SYSTEM_DEFAULT,
1561 LCMAP_LINGUISTIC_CASING | LCMAP_LOWERCASE,
1562 wCharacter, lengthUTF16, wLower, sizeof(wLower)/sizeof(wLower[0]));
1563 char sCharacterLowered[20];
1564 unsigned int lengthConverted = ::WideCharToMultiByte(cpDoc, 0,
1565 wLower, charsConverted,
1566 sCharacterLowered, sizeof(sCharacterLowered), NULL, 0);
1567 if ((lengthConverted == 1) && (sCharacter[0] != sCharacterLowered[0])) {
1568 pcf->SetTranslation(sCharacter[0], sCharacterLowered[0]);
1572 return pcf;
1573 } else {
1574 return new CaseFolderDBCS(cpDest);
1579 std::string ScintillaWin::CaseMapString(const std::string &s, int caseMapping) {
1580 if (s.size() == 0)
1581 return std::string();
1583 if (caseMapping == cmSame)
1584 return s;
1586 UINT cpDoc = CodePageOfDocument();
1588 unsigned int lengthUTF16 = ::MultiByteToWideChar(cpDoc, 0, s.c_str(),
1589 static_cast<int>(s.size()), NULL, 0);
1590 if (lengthUTF16 == 0) // Failed to convert
1591 return s;
1593 DWORD mapFlags = LCMAP_LINGUISTIC_CASING |
1594 ((caseMapping == cmUpper) ? LCMAP_UPPERCASE : LCMAP_LOWERCASE);
1596 // Many conversions performed by search function are short so optimize this case.
1597 enum { shortSize=20 };
1599 if (s.size() > shortSize) {
1600 // Use dynamic allocations for long strings
1602 // Change text to UTF-16
1603 std::vector<wchar_t> vwcText(lengthUTF16);
1604 ::MultiByteToWideChar(cpDoc, 0, s.c_str(), static_cast<int>(s.size()), &vwcText[0], lengthUTF16);
1606 // Change case
1607 int charsConverted = ::LCMapStringW(LOCALE_SYSTEM_DEFAULT, mapFlags,
1608 &vwcText[0], lengthUTF16, NULL, 0);
1609 std::vector<wchar_t> vwcConverted(charsConverted);
1610 ::LCMapStringW(LOCALE_SYSTEM_DEFAULT, mapFlags,
1611 &vwcText[0], lengthUTF16, &vwcConverted[0], charsConverted);
1613 // Change back to document encoding
1614 unsigned int lengthConverted = ::WideCharToMultiByte(cpDoc, 0,
1615 &vwcConverted[0], static_cast<int>(vwcConverted.size()),
1616 NULL, 0, NULL, 0);
1617 std::vector<char> vcConverted(lengthConverted);
1618 ::WideCharToMultiByte(cpDoc, 0,
1619 &vwcConverted[0], static_cast<int>(vwcConverted.size()),
1620 &vcConverted[0], static_cast<int>(vcConverted.size()), NULL, 0);
1622 return std::string(&vcConverted[0], vcConverted.size());
1624 } else {
1625 // Use static allocations for short strings as much faster
1626 // A factor of 15 for single character strings
1628 // Change text to UTF-16
1629 wchar_t vwcText[shortSize];
1630 ::MultiByteToWideChar(cpDoc, 0, s.c_str(), static_cast<int>(s.size()),
1631 vwcText, lengthUTF16);
1633 // Change case
1634 int charsConverted = ::LCMapStringW(LOCALE_SYSTEM_DEFAULT, mapFlags,
1635 vwcText, lengthUTF16, NULL, 0);
1636 // Full mapping may produce up to 3 characters per input character
1637 wchar_t vwcConverted[shortSize*3];
1638 ::LCMapStringW(LOCALE_SYSTEM_DEFAULT, mapFlags, vwcText, lengthUTF16,
1639 vwcConverted, charsConverted);
1641 // Change back to document encoding
1642 unsigned int lengthConverted = ::WideCharToMultiByte(cpDoc, 0,
1643 vwcConverted, charsConverted,
1644 NULL, 0, NULL, 0);
1645 // Each UTF-16 code unit may need up to 3 bytes in UTF-8
1646 char vcConverted[shortSize * 3 * 3];
1647 ::WideCharToMultiByte(cpDoc, 0,
1648 vwcConverted, charsConverted,
1649 vcConverted, lengthConverted, NULL, 0);
1651 return std::string(vcConverted, lengthConverted);
1655 void ScintillaWin::Copy() {
1656 //Platform::DebugPrintf("Copy\n");
1657 if (!sel.Empty()) {
1658 SelectionText selectedText;
1659 CopySelectionRange(&selectedText);
1660 CopyToClipboard(selectedText);
1664 void ScintillaWin::CopyAllowLine() {
1665 SelectionText selectedText;
1666 CopySelectionRange(&selectedText, true);
1667 CopyToClipboard(selectedText);
1670 bool ScintillaWin::CanPaste() {
1671 if (!Editor::CanPaste())
1672 return false;
1673 if (::IsClipboardFormatAvailable(CF_TEXT))
1674 return true;
1675 if (IsUnicodeMode())
1676 return ::IsClipboardFormatAvailable(CF_UNICODETEXT) != 0;
1677 return false;
1680 class GlobalMemory {
1681 HGLOBAL hand;
1682 public:
1683 void *ptr;
1684 GlobalMemory() : hand(0), ptr(0) {
1686 GlobalMemory(HGLOBAL hand_) : hand(hand_), ptr(0) {
1687 if (hand) {
1688 ptr = ::GlobalLock(hand);
1691 ~GlobalMemory() {
1692 PLATFORM_ASSERT(!ptr);
1694 void Allocate(size_t bytes) {
1695 hand = ::GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT, bytes);
1696 if (hand) {
1697 ptr = ::GlobalLock(hand);
1700 HGLOBAL Unlock() {
1701 PLATFORM_ASSERT(ptr);
1702 HGLOBAL handCopy = hand;
1703 ::GlobalUnlock(hand);
1704 ptr = 0;
1705 hand = 0;
1706 return handCopy;
1708 void SetClip(UINT uFormat) {
1709 ::SetClipboardData(uFormat, Unlock());
1711 operator bool() const {
1712 return ptr != 0;
1714 SIZE_T Size() {
1715 return ::GlobalSize(hand);
1719 void ScintillaWin::InsertPasteText(const char *text, int len, SelectionPosition selStart, bool isRectangular, bool isLine) {
1720 if (isRectangular) {
1721 PasteRectangular(selStart, text, len);
1722 } else {
1723 char *convertedText = 0;
1724 if (convertPastes) {
1725 // Convert line endings of the paste into our local line-endings mode
1726 convertedText = Document::TransformLineEnds(&len, text, len, pdoc->eolMode);
1727 text = convertedText;
1729 if (isLine) {
1730 int insertPos = pdoc->LineStart(pdoc->LineFromPosition(sel.MainCaret()));
1731 pdoc->InsertString(insertPos, text, len);
1732 // add the newline if necessary
1733 if ((len > 0) && (text[len-1] != '\n' && text[len-1] != '\r')) {
1734 const char *endline = StringFromEOLMode(pdoc->eolMode);
1735 pdoc->InsertString(insertPos + len, endline, static_cast<int>(strlen(endline)));
1736 len += static_cast<int>(strlen(endline));
1738 if (sel.MainCaret() == insertPos) {
1739 SetEmptySelection(sel.MainCaret() + len);
1741 } else {
1742 InsertPaste(selStart, text, len);
1744 delete []convertedText;
1748 void ScintillaWin::Paste() {
1749 if (!::OpenClipboard(MainHWND()))
1750 return;
1751 UndoGroup ug(pdoc);
1752 bool isLine = SelectionEmpty() && (::IsClipboardFormatAvailable(cfLineSelect) != 0);
1753 ClearSelection(multiPasteMode == SC_MULTIPASTE_EACH);
1754 SelectionPosition selStart = sel.IsRectangular() ?
1755 sel.Rectangular().Start() :
1756 sel.Range(sel.Main()).Start();
1757 bool isRectangular = ::IsClipboardFormatAvailable(cfColumnSelect) != 0;
1759 // Always use CF_UNICODETEXT if available
1760 GlobalMemory memUSelection(::GetClipboardData(CF_UNICODETEXT));
1761 if (memUSelection) {
1762 wchar_t *uptr = static_cast<wchar_t *>(memUSelection.ptr);
1763 if (uptr) {
1764 unsigned int len;
1765 char *putf;
1766 // Default Scintilla behaviour in Unicode mode
1767 if (IsUnicodeMode()) {
1768 unsigned int bytes = memUSelection.Size();
1769 len = UTF8Length(uptr, bytes / 2);
1770 putf = new char[len + 1];
1771 UTF8FromUTF16(uptr, bytes / 2, putf, len);
1772 } else {
1773 // CF_UNICODETEXT available, but not in Unicode mode
1774 // Convert from Unicode to current Scintilla code page
1775 UINT cpDest = CodePageOfDocument();
1776 len = ::WideCharToMultiByte(cpDest, 0, uptr, -1,
1777 NULL, 0, NULL, NULL) - 1; // subtract 0 terminator
1778 putf = new char[len + 1];
1779 ::WideCharToMultiByte(cpDest, 0, uptr, -1,
1780 putf, len + 1, NULL, NULL);
1783 InsertPasteText(putf, len, selStart, isRectangular, isLine);
1784 delete []putf;
1786 memUSelection.Unlock();
1787 } else {
1788 // CF_UNICODETEXT not available, paste ANSI text
1789 GlobalMemory memSelection(::GetClipboardData(CF_TEXT));
1790 if (memSelection) {
1791 char *ptr = static_cast<char *>(memSelection.ptr);
1792 if (ptr) {
1793 unsigned int bytes = memSelection.Size();
1794 unsigned int len = bytes;
1795 for (unsigned int i = 0; i < bytes; i++) {
1796 if ((len == bytes) && (0 == ptr[i]))
1797 len = i;
1800 // In Unicode mode, convert clipboard text to UTF-8
1801 if (IsUnicodeMode()) {
1802 wchar_t *uptr = new wchar_t[len+1];
1804 unsigned int ulen = ::MultiByteToWideChar(CP_ACP, 0,
1805 ptr, len, uptr, len+1);
1807 unsigned int mlen = UTF8Length(uptr, ulen);
1808 char *putf = new char[mlen + 1];
1809 if (putf) {
1810 // CP_UTF8 not available on Windows 95, so use UTF8FromUTF16()
1811 UTF8FromUTF16(uptr, ulen, putf, mlen);
1814 delete []uptr;
1816 if (putf) {
1817 InsertPasteText(putf, mlen, selStart, isRectangular, isLine);
1818 delete []putf;
1820 } else {
1821 InsertPasteText(ptr, len, selStart, isRectangular, isLine);
1824 memSelection.Unlock();
1827 ::CloseClipboard();
1828 Redraw();
1831 void ScintillaWin::CreateCallTipWindow(PRectangle) {
1832 if (!ct.wCallTip.Created()) {
1833 ct.wCallTip = ::CreateWindow(callClassName, TEXT("ACallTip"),
1834 WS_POPUP, 100, 100, 150, 20,
1835 MainHWND(), 0,
1836 GetWindowInstance(MainHWND()),
1837 this);
1838 ct.wDraw = ct.wCallTip;
1842 void ScintillaWin::AddToPopUp(const char *label, int cmd, bool enabled) {
1843 HMENU hmenuPopup = reinterpret_cast<HMENU>(popup.GetID());
1844 if (!label[0])
1845 ::AppendMenuA(hmenuPopup, MF_SEPARATOR, 0, "");
1846 else if (enabled)
1847 ::AppendMenuA(hmenuPopup, MF_STRING, cmd, label);
1848 else
1849 ::AppendMenuA(hmenuPopup, MF_STRING | MF_DISABLED | MF_GRAYED, cmd, label);
1852 void ScintillaWin::ClaimSelection() {
1853 // Windows does not have a primary selection
1856 /// Implement IUnknown
1858 STDMETHODIMP_(ULONG)FormatEnumerator_AddRef(FormatEnumerator *fe);
1859 STDMETHODIMP FormatEnumerator_QueryInterface(FormatEnumerator *fe, REFIID riid, PVOID *ppv) {
1860 //Platform::DebugPrintf("EFE QI");
1861 *ppv = NULL;
1862 if (riid == IID_IUnknown)
1863 *ppv = reinterpret_cast<IEnumFORMATETC *>(fe);
1864 if (riid == IID_IEnumFORMATETC)
1865 *ppv = reinterpret_cast<IEnumFORMATETC *>(fe);
1866 if (!*ppv)
1867 return E_NOINTERFACE;
1868 FormatEnumerator_AddRef(fe);
1869 return S_OK;
1871 STDMETHODIMP_(ULONG)FormatEnumerator_AddRef(FormatEnumerator *fe) {
1872 return ++fe->ref;
1874 STDMETHODIMP_(ULONG)FormatEnumerator_Release(FormatEnumerator *fe) {
1875 fe->ref--;
1876 if (fe->ref > 0)
1877 return fe->ref;
1878 delete fe;
1879 return 0;
1881 /// Implement IEnumFORMATETC
1882 STDMETHODIMP FormatEnumerator_Next(FormatEnumerator *fe, ULONG celt, FORMATETC *rgelt, ULONG *pceltFetched) {
1883 //Platform::DebugPrintf("EFE Next %d %d", fe->pos, celt);
1884 if (rgelt == NULL) return E_POINTER;
1885 // We only support one format, so this is simple.
1886 unsigned int putPos = 0;
1887 while ((fe->pos < fe->formatsLen) && (putPos < celt)) {
1888 rgelt->cfFormat = fe->formats[fe->pos];
1889 rgelt->ptd = 0;
1890 rgelt->dwAspect = DVASPECT_CONTENT;
1891 rgelt->lindex = -1;
1892 rgelt->tymed = TYMED_HGLOBAL;
1893 fe->pos++;
1894 putPos++;
1896 if (pceltFetched)
1897 *pceltFetched = putPos;
1898 return putPos ? S_OK : S_FALSE;
1900 STDMETHODIMP FormatEnumerator_Skip(FormatEnumerator *fe, ULONG celt) {
1901 fe->pos += celt;
1902 return S_OK;
1904 STDMETHODIMP FormatEnumerator_Reset(FormatEnumerator *fe) {
1905 fe->pos = 0;
1906 return S_OK;
1908 STDMETHODIMP FormatEnumerator_Clone(FormatEnumerator *fe, IEnumFORMATETC **ppenum) {
1909 FormatEnumerator *pfe;
1910 try {
1911 pfe = new FormatEnumerator(fe->pos, fe->formats, fe->formatsLen);
1912 } catch (...) {
1913 return E_OUTOFMEMORY;
1915 return FormatEnumerator_QueryInterface(pfe, IID_IEnumFORMATETC,
1916 reinterpret_cast<void **>(ppenum));
1919 static VFunction *vtFormatEnumerator[] = {
1920 (VFunction *)(FormatEnumerator_QueryInterface),
1921 (VFunction *)(FormatEnumerator_AddRef),
1922 (VFunction *)(FormatEnumerator_Release),
1923 (VFunction *)(FormatEnumerator_Next),
1924 (VFunction *)(FormatEnumerator_Skip),
1925 (VFunction *)(FormatEnumerator_Reset),
1926 (VFunction *)(FormatEnumerator_Clone)
1929 FormatEnumerator::FormatEnumerator(int pos_, CLIPFORMAT formats_[], int formatsLen_) {
1930 vtbl = vtFormatEnumerator;
1931 ref = 0; // First QI adds first reference...
1932 pos = pos_;
1933 formatsLen = formatsLen_;
1934 for (int i=0; i<formatsLen; i++)
1935 formats[i] = formats_[i];
1938 /// Implement IUnknown
1939 STDMETHODIMP DropSource_QueryInterface(DropSource *ds, REFIID riid, PVOID *ppv) {
1940 return ds->sci->QueryInterface(riid, ppv);
1942 STDMETHODIMP_(ULONG)DropSource_AddRef(DropSource *ds) {
1943 return ds->sci->AddRef();
1945 STDMETHODIMP_(ULONG)DropSource_Release(DropSource *ds) {
1946 return ds->sci->Release();
1949 /// Implement IDropSource
1950 STDMETHODIMP DropSource_QueryContinueDrag(DropSource *, BOOL fEsc, DWORD grfKeyState) {
1951 if (fEsc)
1952 return DRAGDROP_S_CANCEL;
1953 if (!(grfKeyState & MK_LBUTTON))
1954 return DRAGDROP_S_DROP;
1955 return S_OK;
1958 STDMETHODIMP DropSource_GiveFeedback(DropSource *, DWORD) {
1959 return DRAGDROP_S_USEDEFAULTCURSORS;
1962 static VFunction *vtDropSource[] = {
1963 (VFunction *)(DropSource_QueryInterface),
1964 (VFunction *)(DropSource_AddRef),
1965 (VFunction *)(DropSource_Release),
1966 (VFunction *)(DropSource_QueryContinueDrag),
1967 (VFunction *)(DropSource_GiveFeedback)
1970 DropSource::DropSource() {
1971 vtbl = vtDropSource;
1972 sci = 0;
1975 /// Implement IUnkown
1976 STDMETHODIMP DataObject_QueryInterface(DataObject *pd, REFIID riid, PVOID *ppv) {
1977 //Platform::DebugPrintf("DO QI %x\n", pd);
1978 return pd->sci->QueryInterface(riid, ppv);
1980 STDMETHODIMP_(ULONG)DataObject_AddRef(DataObject *pd) {
1981 return pd->sci->AddRef();
1983 STDMETHODIMP_(ULONG)DataObject_Release(DataObject *pd) {
1984 return pd->sci->Release();
1986 /// Implement IDataObject
1987 STDMETHODIMP DataObject_GetData(DataObject *pd, FORMATETC *pFEIn, STGMEDIUM *pSTM) {
1988 return pd->sci->GetData(pFEIn, pSTM);
1991 STDMETHODIMP DataObject_GetDataHere(DataObject *, FORMATETC *, STGMEDIUM *) {
1992 //Platform::DebugPrintf("DOB GetDataHere\n");
1993 return E_NOTIMPL;
1996 STDMETHODIMP DataObject_QueryGetData(DataObject *pd, FORMATETC *pFE) {
1997 if (pd->sci->DragIsRectangularOK(pFE->cfFormat) &&
1998 pFE->ptd == 0 &&
1999 (pFE->dwAspect & DVASPECT_CONTENT) != 0 &&
2000 pFE->lindex == -1 &&
2001 (pFE->tymed & TYMED_HGLOBAL) != 0
2003 return S_OK;
2006 bool formatOK = (pFE->cfFormat == CF_TEXT) ||
2007 ((pFE->cfFormat == CF_UNICODETEXT) && pd->sci->IsUnicodeMode());
2008 if (!formatOK ||
2009 pFE->ptd != 0 ||
2010 (pFE->dwAspect & DVASPECT_CONTENT) == 0 ||
2011 pFE->lindex != -1 ||
2012 (pFE->tymed & TYMED_HGLOBAL) == 0
2014 //Platform::DebugPrintf("DOB QueryGetData No %x\n",pFE->cfFormat);
2015 //return DATA_E_FORMATETC;
2016 return S_FALSE;
2018 //Platform::DebugPrintf("DOB QueryGetData OK %x\n",pFE->cfFormat);
2019 return S_OK;
2022 STDMETHODIMP DataObject_GetCanonicalFormatEtc(DataObject *pd, FORMATETC *, FORMATETC *pFEOut) {
2023 //Platform::DebugPrintf("DOB GetCanon\n");
2024 if (pd->sci->IsUnicodeMode())
2025 pFEOut->cfFormat = CF_UNICODETEXT;
2026 else
2027 pFEOut->cfFormat = CF_TEXT;
2028 pFEOut->ptd = 0;
2029 pFEOut->dwAspect = DVASPECT_CONTENT;
2030 pFEOut->lindex = -1;
2031 pFEOut->tymed = TYMED_HGLOBAL;
2032 return S_OK;
2035 STDMETHODIMP DataObject_SetData(DataObject *, FORMATETC *, STGMEDIUM *, BOOL) {
2036 //Platform::DebugPrintf("DOB SetData\n");
2037 return E_FAIL;
2040 STDMETHODIMP DataObject_EnumFormatEtc(DataObject *pd, DWORD dwDirection, IEnumFORMATETC **ppEnum) {
2041 try {
2042 //Platform::DebugPrintf("DOB EnumFormatEtc %d\n", dwDirection);
2043 if (dwDirection != DATADIR_GET) {
2044 *ppEnum = 0;
2045 return E_FAIL;
2047 FormatEnumerator *pfe;
2048 if (pd->sci->IsUnicodeMode()) {
2049 CLIPFORMAT formats[] = {CF_UNICODETEXT, CF_TEXT};
2050 pfe = new FormatEnumerator(0, formats, 2);
2051 } else {
2052 CLIPFORMAT formats[] = {CF_TEXT};
2053 pfe = new FormatEnumerator(0, formats, 1);
2055 return FormatEnumerator_QueryInterface(pfe, IID_IEnumFORMATETC,
2056 reinterpret_cast<void **>(ppEnum));
2057 } catch (std::bad_alloc &) {
2058 pd->sci->errorStatus = SC_STATUS_BADALLOC;
2059 return E_OUTOFMEMORY;
2060 } catch (...) {
2061 pd->sci->errorStatus = SC_STATUS_FAILURE;
2062 return E_FAIL;
2066 STDMETHODIMP DataObject_DAdvise(DataObject *, FORMATETC *, DWORD, IAdviseSink *, PDWORD) {
2067 //Platform::DebugPrintf("DOB DAdvise\n");
2068 return E_FAIL;
2071 STDMETHODIMP DataObject_DUnadvise(DataObject *, DWORD) {
2072 //Platform::DebugPrintf("DOB DUnadvise\n");
2073 return E_FAIL;
2076 STDMETHODIMP DataObject_EnumDAdvise(DataObject *, IEnumSTATDATA **) {
2077 //Platform::DebugPrintf("DOB EnumDAdvise\n");
2078 return E_FAIL;
2081 static VFunction *vtDataObject[] = {
2082 (VFunction *)(DataObject_QueryInterface),
2083 (VFunction *)(DataObject_AddRef),
2084 (VFunction *)(DataObject_Release),
2085 (VFunction *)(DataObject_GetData),
2086 (VFunction *)(DataObject_GetDataHere),
2087 (VFunction *)(DataObject_QueryGetData),
2088 (VFunction *)(DataObject_GetCanonicalFormatEtc),
2089 (VFunction *)(DataObject_SetData),
2090 (VFunction *)(DataObject_EnumFormatEtc),
2091 (VFunction *)(DataObject_DAdvise),
2092 (VFunction *)(DataObject_DUnadvise),
2093 (VFunction *)(DataObject_EnumDAdvise)
2096 DataObject::DataObject() {
2097 vtbl = vtDataObject;
2098 sci = 0;
2101 /// Implement IUnknown
2102 STDMETHODIMP DropTarget_QueryInterface(DropTarget *dt, REFIID riid, PVOID *ppv) {
2103 //Platform::DebugPrintf("DT QI %x\n", dt);
2104 return dt->sci->QueryInterface(riid, ppv);
2106 STDMETHODIMP_(ULONG)DropTarget_AddRef(DropTarget *dt) {
2107 return dt->sci->AddRef();
2109 STDMETHODIMP_(ULONG)DropTarget_Release(DropTarget *dt) {
2110 return dt->sci->Release();
2113 /// Implement IDropTarget by forwarding to Scintilla
2114 STDMETHODIMP DropTarget_DragEnter(DropTarget *dt, LPDATAOBJECT pIDataSource, DWORD grfKeyState,
2115 POINTL pt, PDWORD pdwEffect) {
2116 try {
2117 return dt->sci->DragEnter(pIDataSource, grfKeyState, pt, pdwEffect);
2118 } catch (...) {
2119 dt->sci->errorStatus = SC_STATUS_FAILURE;
2121 return E_FAIL;
2123 STDMETHODIMP DropTarget_DragOver(DropTarget *dt, DWORD grfKeyState, POINTL pt, PDWORD pdwEffect) {
2124 try {
2125 return dt->sci->DragOver(grfKeyState, pt, pdwEffect);
2126 } catch (...) {
2127 dt->sci->errorStatus = SC_STATUS_FAILURE;
2129 return E_FAIL;
2131 STDMETHODIMP DropTarget_DragLeave(DropTarget *dt) {
2132 try {
2133 return dt->sci->DragLeave();
2134 } catch (...) {
2135 dt->sci->errorStatus = SC_STATUS_FAILURE;
2137 return E_FAIL;
2139 STDMETHODIMP DropTarget_Drop(DropTarget *dt, LPDATAOBJECT pIDataSource, DWORD grfKeyState,
2140 POINTL pt, PDWORD pdwEffect) {
2141 try {
2142 return dt->sci->Drop(pIDataSource, grfKeyState, pt, pdwEffect);
2143 } catch (...) {
2144 dt->sci->errorStatus = SC_STATUS_FAILURE;
2146 return E_FAIL;
2149 static VFunction *vtDropTarget[] = {
2150 (VFunction *)(DropTarget_QueryInterface),
2151 (VFunction *)(DropTarget_AddRef),
2152 (VFunction *)(DropTarget_Release),
2153 (VFunction *)(DropTarget_DragEnter),
2154 (VFunction *)(DropTarget_DragOver),
2155 (VFunction *)(DropTarget_DragLeave),
2156 (VFunction *)(DropTarget_Drop)
2159 DropTarget::DropTarget() {
2160 vtbl = vtDropTarget;
2161 sci = 0;
2165 * DBCS: support Input Method Editor (IME).
2166 * Called when IME Window opened.
2168 void ScintillaWin::ImeStartComposition() {
2169 #ifndef __DMC__
2170 // Digital Mars compiler does not include Imm library
2171 if (caret.active) {
2172 // Move IME Window to current caret position
2173 HIMC hIMC = ::ImmGetContext(MainHWND());
2174 Point pos = PointMainCaret();
2175 COMPOSITIONFORM CompForm;
2176 CompForm.dwStyle = CFS_POINT;
2177 CompForm.ptCurrentPos.x = pos.x;
2178 CompForm.ptCurrentPos.y = pos.y;
2180 ::ImmSetCompositionWindow(hIMC, &CompForm);
2182 // Set font of IME window to same as surrounded text.
2183 if (stylesValid) {
2184 // Since the style creation code has been made platform independent,
2185 // The logfont for the IME is recreated here.
2186 int styleHere = (pdoc->StyleAt(sel.MainCaret())) & 31;
2187 LOGFONTA lf = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ""};
2188 int sizeZoomed = vs.styles[styleHere].size + vs.zoomLevel * SC_FONT_SIZE_MULTIPLIER;
2189 if (sizeZoomed <= 2 * SC_FONT_SIZE_MULTIPLIER) // Hangs if sizeZoomed <= 1
2190 sizeZoomed = 2 * SC_FONT_SIZE_MULTIPLIER;
2191 AutoSurface surface(this);
2192 int deviceHeight = sizeZoomed;
2193 if (surface) {
2194 deviceHeight = (sizeZoomed * surface->LogPixelsY()) / 72;
2196 // The negative is to allow for leading
2197 lf.lfHeight = -(abs(deviceHeight / SC_FONT_SIZE_MULTIPLIER));
2198 lf.lfWeight = vs.styles[styleHere].weight;
2199 lf.lfItalic = static_cast<BYTE>(vs.styles[styleHere].italic ? 1 : 0);
2200 lf.lfCharSet = DEFAULT_CHARSET;
2201 lf.lfFaceName[0] = '\0';
2202 if (vs.styles[styleHere].fontName)
2203 strcpy(lf.lfFaceName, vs.styles[styleHere].fontName);
2205 ::ImmSetCompositionFontA(hIMC, &lf);
2207 ::ImmReleaseContext(MainHWND(), hIMC);
2208 // Caret is displayed in IME window. So, caret in Scintilla is useless.
2209 DropCaret();
2211 #endif
2214 /** Called when IME Window closed. */
2215 void ScintillaWin::ImeEndComposition() {
2216 ShowCaretAtCurrentPosition();
2219 void ScintillaWin::AddCharBytes(char b0, char b1) {
2221 int inputCodePage = InputCodePage();
2222 if (inputCodePage && IsUnicodeMode()) {
2223 char utfval[4] = "\0\0\0";
2224 char ansiChars[3];
2225 wchar_t wcs[2];
2226 if (b0) { // Two bytes from IME
2227 ansiChars[0] = b0;
2228 ansiChars[1] = b1;
2229 ansiChars[2] = '\0';
2230 ::MultiByteToWideChar(inputCodePage, 0, ansiChars, 2, wcs, 1);
2231 } else {
2232 ansiChars[0] = b1;
2233 ansiChars[1] = '\0';
2234 ::MultiByteToWideChar(inputCodePage, 0, ansiChars, 1, wcs, 1);
2236 unsigned int len = UTF8Length(wcs, 1);
2237 UTF8FromUTF16(wcs, 1, utfval, len);
2238 utfval[len] = '\0';
2239 AddCharUTF(utfval, len ? len : 1);
2240 } else if (b0) {
2241 char dbcsChars[3];
2242 dbcsChars[0] = b0;
2243 dbcsChars[1] = b1;
2244 dbcsChars[2] = '\0';
2245 AddCharUTF(dbcsChars, 2, true);
2246 } else {
2247 AddChar(b1);
2251 void ScintillaWin::GetIntelliMouseParameters() {
2252 // This retrieves the number of lines per scroll as configured inthe Mouse Properties sheet in Control Panel
2253 ::SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, &linesPerScroll, 0);
2256 void ScintillaWin::CopyToClipboard(const SelectionText &selectedText) {
2257 if (!::OpenClipboard(MainHWND()))
2258 return;
2259 ::EmptyClipboard();
2261 GlobalMemory uniText;
2263 // Default Scintilla behaviour in Unicode mode
2264 if (IsUnicodeMode()) {
2265 int uchars = UTF16Length(selectedText.s, selectedText.len);
2266 uniText.Allocate(2 * uchars);
2267 if (uniText) {
2268 UTF16FromUTF8(selectedText.s, selectedText.len, static_cast<wchar_t *>(uniText.ptr), uchars);
2270 } else {
2271 // Not Unicode mode
2272 // Convert to Unicode using the current Scintilla code page
2273 UINT cpSrc = CodePageFromCharSet(
2274 selectedText.characterSet, selectedText.codePage);
2275 int uLen = ::MultiByteToWideChar(cpSrc, 0, selectedText.s, selectedText.len, 0, 0);
2276 uniText.Allocate(2 * uLen);
2277 if (uniText) {
2278 ::MultiByteToWideChar(cpSrc, 0, selectedText.s, selectedText.len,
2279 static_cast<wchar_t *>(uniText.ptr), uLen);
2283 if (uniText) {
2284 if (!IsNT()) {
2285 // Copy ANSI text to clipboard on Windows 9x
2286 // Convert from Unicode text, so other ANSI programs can
2287 // paste the text
2288 // Windows NT, 2k, XP automatically generates CF_TEXT
2289 GlobalMemory ansiText;
2290 ansiText.Allocate(selectedText.len);
2291 if (ansiText) {
2292 ::WideCharToMultiByte(CP_ACP, 0, static_cast<wchar_t *>(uniText.ptr), -1,
2293 static_cast<char *>(ansiText.ptr), selectedText.len, NULL, NULL);
2294 ansiText.SetClip(CF_TEXT);
2297 uniText.SetClip(CF_UNICODETEXT);
2298 } else {
2299 // There was a failure - try to copy at least ANSI text
2300 GlobalMemory ansiText;
2301 ansiText.Allocate(selectedText.len);
2302 if (ansiText) {
2303 memcpy(static_cast<char *>(ansiText.ptr), selectedText.s, selectedText.len);
2304 ansiText.SetClip(CF_TEXT);
2308 if (selectedText.rectangular) {
2309 ::SetClipboardData(cfColumnSelect, 0);
2312 if (selectedText.lineCopy) {
2313 ::SetClipboardData(cfLineSelect, 0);
2316 ::CloseClipboard();
2319 void ScintillaWin::ScrollMessage(WPARAM wParam) {
2320 //DWORD dwStart = timeGetTime();
2321 //Platform::DebugPrintf("Scroll %x %d\n", wParam, lParam);
2323 SCROLLINFO sci;
2324 memset(&sci, 0, sizeof(sci));
2325 sci.cbSize = sizeof(sci);
2326 sci.fMask = SIF_ALL;
2328 GetScrollInfo(SB_VERT, &sci);
2330 //Platform::DebugPrintf("ScrollInfo %d mask=%x min=%d max=%d page=%d pos=%d track=%d\n", b,sci.fMask,
2331 //sci.nMin, sci.nMax, sci.nPage, sci.nPos, sci.nTrackPos);
2333 int topLineNew = topLine;
2334 switch (LoWord(wParam)) {
2335 case SB_LINEUP:
2336 topLineNew -= 1;
2337 break;
2338 case SB_LINEDOWN:
2339 topLineNew += 1;
2340 break;
2341 case SB_PAGEUP:
2342 topLineNew -= LinesToScroll(); break;
2343 case SB_PAGEDOWN: topLineNew += LinesToScroll(); break;
2344 case SB_TOP: topLineNew = 0; break;
2345 case SB_BOTTOM: topLineNew = MaxScrollPos(); break;
2346 case SB_THUMBPOSITION: topLineNew = sci.nTrackPos; break;
2347 case SB_THUMBTRACK: topLineNew = sci.nTrackPos; break;
2349 ScrollTo(topLineNew);
2352 void ScintillaWin::HorizontalScrollMessage(WPARAM wParam) {
2353 int xPos = xOffset;
2354 PRectangle rcText = GetTextRectangle();
2355 int pageWidth = rcText.Width() * 2 / 3;
2356 switch (LoWord(wParam)) {
2357 case SB_LINEUP:
2358 xPos -= 20;
2359 break;
2360 case SB_LINEDOWN: // May move past the logical end
2361 xPos += 20;
2362 break;
2363 case SB_PAGEUP:
2364 xPos -= pageWidth;
2365 break;
2366 case SB_PAGEDOWN:
2367 xPos += pageWidth;
2368 if (xPos > scrollWidth - rcText.Width()) { // Hit the end exactly
2369 xPos = scrollWidth - rcText.Width();
2371 break;
2372 case SB_TOP:
2373 xPos = 0;
2374 break;
2375 case SB_BOTTOM:
2376 xPos = scrollWidth;
2377 break;
2378 case SB_THUMBPOSITION:
2379 case SB_THUMBTRACK: {
2380 // 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 =]
2381 SCROLLINFO si;
2382 si.cbSize = sizeof(si);
2383 si.fMask = SIF_TRACKPOS;
2384 if (GetScrollInfo(SB_HORZ, &si)) {
2385 xPos = si.nTrackPos;
2388 break;
2390 HorizontalScrollTo(xPos);
2394 * Redraw all of text area.
2395 * This paint will not be abandoned.
2397 void ScintillaWin::FullPaint() {
2398 if (technology == SC_TECHNOLOGY_DEFAULT) {
2399 HDC hdc = ::GetDC(MainHWND());
2400 FullPaintDC(hdc);
2401 ::ReleaseDC(MainHWND(), hdc);
2402 } else {
2403 FullPaintDC(0);
2408 * Redraw all of text area on the specified DC.
2409 * This paint will not be abandoned.
2411 void ScintillaWin::FullPaintDC(HDC hdc) {
2412 paintState = painting;
2413 rcPaint = GetClientRectangle();
2414 paintingAllText = true;
2415 if (technology == SC_TECHNOLOGY_DEFAULT) {
2416 AutoSurface surfaceWindow(hdc, this);
2417 if (surfaceWindow) {
2418 Paint(surfaceWindow, rcPaint);
2419 surfaceWindow->Release();
2421 } else {
2422 #if defined(USE_D2D)
2423 EnsureRenderTarget();
2424 AutoSurface surfaceWindow(pRenderTarget, this);
2425 if (surfaceWindow) {
2426 pRenderTarget->BeginDraw();
2427 Paint(surfaceWindow, rcPaint);
2428 surfaceWindow->Release();
2429 HRESULT hr = pRenderTarget->EndDraw();
2430 if (hr == D2DERR_RECREATE_TARGET) {
2431 DropRenderTarget();
2434 #endif
2436 paintState = notPainting;
2439 static bool CompareDevCap(HDC hdc, HDC hOtherDC, int nIndex) {
2440 return ::GetDeviceCaps(hdc, nIndex) == ::GetDeviceCaps(hOtherDC, nIndex);
2443 bool ScintillaWin::IsCompatibleDC(HDC hOtherDC) {
2444 HDC hdc = ::GetDC(MainHWND());
2445 bool isCompatible =
2446 CompareDevCap(hdc, hOtherDC, TECHNOLOGY) &&
2447 CompareDevCap(hdc, hOtherDC, LOGPIXELSY) &&
2448 CompareDevCap(hdc, hOtherDC, LOGPIXELSX) &&
2449 CompareDevCap(hdc, hOtherDC, BITSPIXEL) &&
2450 CompareDevCap(hdc, hOtherDC, PLANES);
2451 ::ReleaseDC(MainHWND(), hdc);
2452 return isCompatible;
2455 DWORD ScintillaWin::EffectFromState(DWORD grfKeyState) {
2456 // These are the Wordpad semantics.
2457 DWORD dwEffect;
2458 if (inDragDrop == ddDragging) // Internal defaults to move
2459 dwEffect = DROPEFFECT_MOVE;
2460 else
2461 dwEffect = DROPEFFECT_COPY;
2462 if (grfKeyState & MK_ALT)
2463 dwEffect = DROPEFFECT_MOVE;
2464 if (grfKeyState & MK_CONTROL)
2465 dwEffect = DROPEFFECT_COPY;
2466 return dwEffect;
2469 /// Implement IUnknown
2470 STDMETHODIMP ScintillaWin::QueryInterface(REFIID riid, PVOID *ppv) {
2471 *ppv = NULL;
2472 if (riid == IID_IUnknown)
2473 *ppv = reinterpret_cast<IDropTarget *>(&dt);
2474 if (riid == IID_IDropSource)
2475 *ppv = reinterpret_cast<IDropSource *>(&ds);
2476 if (riid == IID_IDropTarget)
2477 *ppv = reinterpret_cast<IDropTarget *>(&dt);
2478 if (riid == IID_IDataObject)
2479 *ppv = reinterpret_cast<IDataObject *>(&dob);
2480 if (!*ppv)
2481 return E_NOINTERFACE;
2482 return S_OK;
2485 STDMETHODIMP_(ULONG) ScintillaWin::AddRef() {
2486 return 1;
2489 STDMETHODIMP_(ULONG) ScintillaWin::Release() {
2490 return 1;
2493 /// Implement IDropTarget
2494 STDMETHODIMP ScintillaWin::DragEnter(LPDATAOBJECT pIDataSource, DWORD grfKeyState,
2495 POINTL, PDWORD pdwEffect) {
2496 if (pIDataSource == NULL)
2497 return E_POINTER;
2498 FORMATETC fmtu = {CF_UNICODETEXT, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
2499 HRESULT hrHasUText = pIDataSource->QueryGetData(&fmtu);
2500 hasOKText = (hrHasUText == S_OK);
2501 if (!hasOKText) {
2502 FORMATETC fmte = {CF_TEXT, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
2503 HRESULT hrHasText = pIDataSource->QueryGetData(&fmte);
2504 hasOKText = (hrHasText == S_OK);
2506 if (!hasOKText) {
2507 *pdwEffect = DROPEFFECT_NONE;
2508 return S_OK;
2511 *pdwEffect = EffectFromState(grfKeyState);
2512 return S_OK;
2515 STDMETHODIMP ScintillaWin::DragOver(DWORD grfKeyState, POINTL pt, PDWORD pdwEffect) {
2516 try {
2517 if (!hasOKText || pdoc->IsReadOnly()) {
2518 *pdwEffect = DROPEFFECT_NONE;
2519 return S_OK;
2522 *pdwEffect = EffectFromState(grfKeyState);
2524 // Update the cursor.
2525 POINT rpt = {pt.x, pt.y};
2526 ::ScreenToClient(MainHWND(), &rpt);
2527 SetDragPosition(SPositionFromLocation(Point(rpt.x, rpt.y), false, false, UserVirtualSpace()));
2529 return S_OK;
2530 } catch (...) {
2531 errorStatus = SC_STATUS_FAILURE;
2533 return E_FAIL;
2536 STDMETHODIMP ScintillaWin::DragLeave() {
2537 try {
2538 SetDragPosition(SelectionPosition(invalidPosition));
2539 return S_OK;
2540 } catch (...) {
2541 errorStatus = SC_STATUS_FAILURE;
2543 return E_FAIL;
2546 STDMETHODIMP ScintillaWin::Drop(LPDATAOBJECT pIDataSource, DWORD grfKeyState,
2547 POINTL pt, PDWORD pdwEffect) {
2548 try {
2549 *pdwEffect = EffectFromState(grfKeyState);
2551 if (pIDataSource == NULL)
2552 return E_POINTER;
2554 SetDragPosition(SelectionPosition(invalidPosition));
2556 STGMEDIUM medium = {0, {0}, 0};
2558 char *data = 0;
2559 bool dataAllocated = false;
2561 FORMATETC fmtu = {CF_UNICODETEXT, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
2562 HRESULT hr = pIDataSource->GetData(&fmtu, &medium);
2563 if (SUCCEEDED(hr) && medium.hGlobal) {
2564 wchar_t *udata = static_cast<wchar_t *>(::GlobalLock(medium.hGlobal));
2565 if (udata) {
2566 if (IsUnicodeMode()) {
2567 int tlen = ::GlobalSize(medium.hGlobal);
2568 // Convert UTF-16 to UTF-8
2569 int dataLen = UTF8Length(udata, tlen/2);
2570 data = new char[dataLen+1];
2571 UTF8FromUTF16(udata, tlen/2, data, dataLen);
2572 dataAllocated = true;
2573 } else {
2574 // Convert UTF-16 to ANSI
2576 // Default Scintilla behavior in Unicode mode
2577 // CF_UNICODETEXT available, but not in Unicode mode
2578 // Convert from Unicode to current Scintilla code page
2579 UINT cpDest = CodePageOfDocument();
2580 int tlen = ::WideCharToMultiByte(cpDest, 0, udata, -1,
2581 NULL, 0, NULL, NULL) - 1; // subtract 0 terminator
2582 data = new char[tlen + 1];
2583 memset(data, 0, (tlen+1));
2584 ::WideCharToMultiByte(cpDest, 0, udata, -1,
2585 data, tlen + 1, NULL, NULL);
2586 dataAllocated = true;
2591 if (!data) {
2592 FORMATETC fmte = {CF_TEXT, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
2593 hr = pIDataSource->GetData(&fmte, &medium);
2594 if (SUCCEEDED(hr) && medium.hGlobal) {
2595 data = static_cast<char *>(::GlobalLock(medium.hGlobal));
2599 if (data && convertPastes) {
2600 // Convert line endings of the drop into our local line-endings mode
2601 int len = static_cast<int>(strlen(data));
2602 char *convertedText = Document::TransformLineEnds(&len, data, len, pdoc->eolMode);
2603 if (dataAllocated)
2604 delete []data;
2605 data = convertedText;
2606 dataAllocated = true;
2609 if (!data) {
2610 //Platform::DebugPrintf("Bad data format: 0x%x\n", hres);
2611 return hr;
2614 FORMATETC fmtr = {cfColumnSelect, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
2615 HRESULT hrRectangular = pIDataSource->QueryGetData(&fmtr);
2617 POINT rpt = {pt.x, pt.y};
2618 ::ScreenToClient(MainHWND(), &rpt);
2619 SelectionPosition movePos = SPositionFromLocation(Point(rpt.x, rpt.y), false, false, UserVirtualSpace());
2621 DropAt(movePos, data, *pdwEffect == DROPEFFECT_MOVE, hrRectangular == S_OK);
2623 ::GlobalUnlock(medium.hGlobal);
2625 // Free data
2626 if (medium.pUnkForRelease != NULL)
2627 medium.pUnkForRelease->Release();
2628 else
2629 ::GlobalFree(medium.hGlobal);
2631 if (dataAllocated)
2632 delete []data;
2634 return S_OK;
2635 } catch (...) {
2636 errorStatus = SC_STATUS_FAILURE;
2638 return E_FAIL;
2641 /// Implement important part of IDataObject
2642 STDMETHODIMP ScintillaWin::GetData(FORMATETC *pFEIn, STGMEDIUM *pSTM) {
2643 bool formatOK = (pFEIn->cfFormat == CF_TEXT) ||
2644 ((pFEIn->cfFormat == CF_UNICODETEXT) && IsUnicodeMode());
2645 if (!formatOK ||
2646 pFEIn->ptd != 0 ||
2647 (pFEIn->dwAspect & DVASPECT_CONTENT) == 0 ||
2648 pFEIn->lindex != -1 ||
2649 (pFEIn->tymed & TYMED_HGLOBAL) == 0
2651 //Platform::DebugPrintf("DOB GetData No %d %x %x fmt=%x\n", lenDrag, pFEIn, pSTM, pFEIn->cfFormat);
2652 return DATA_E_FORMATETC;
2654 pSTM->tymed = TYMED_HGLOBAL;
2655 //Platform::DebugPrintf("DOB GetData OK %d %x %x\n", lenDrag, pFEIn, pSTM);
2657 GlobalMemory text;
2658 if (pFEIn->cfFormat == CF_UNICODETEXT) {
2659 int uchars = UTF16Length(drag.s, drag.len);
2660 text.Allocate(2 * uchars);
2661 if (text) {
2662 UTF16FromUTF8(drag.s, drag.len, static_cast<wchar_t *>(text.ptr), uchars);
2664 } else {
2665 text.Allocate(drag.len);
2666 if (text) {
2667 memcpy(static_cast<char *>(text.ptr), drag.s, drag.len);
2670 pSTM->hGlobal = text ? text.Unlock() : 0;
2671 pSTM->pUnkForRelease = 0;
2672 return S_OK;
2675 bool ScintillaWin::Register(HINSTANCE hInstance_) {
2677 hInstance = hInstance_;
2678 bool result;
2680 // Register the Scintilla class
2681 if (IsNT()) {
2683 // Register Scintilla as a wide character window
2684 WNDCLASSEXW wndclass;
2685 wndclass.cbSize = sizeof(wndclass);
2686 wndclass.style = CS_GLOBALCLASS | CS_HREDRAW | CS_VREDRAW;
2687 wndclass.lpfnWndProc = ScintillaWin::SWndProc;
2688 wndclass.cbClsExtra = 0;
2689 wndclass.cbWndExtra = sizeof(ScintillaWin *);
2690 wndclass.hInstance = hInstance;
2691 wndclass.hIcon = NULL;
2692 wndclass.hCursor = NULL;
2693 wndclass.hbrBackground = NULL;
2694 wndclass.lpszMenuName = NULL;
2695 wndclass.lpszClassName = L"Scintilla";
2696 wndclass.hIconSm = 0;
2697 result = ::RegisterClassExW(&wndclass) != 0;
2698 } else {
2700 // Register Scintilla as a normal character window
2701 WNDCLASSEX wndclass;
2702 wndclass.cbSize = sizeof(wndclass);
2703 wndclass.style = CS_GLOBALCLASS | CS_HREDRAW | CS_VREDRAW;
2704 wndclass.lpfnWndProc = ScintillaWin::SWndProc;
2705 wndclass.cbClsExtra = 0;
2706 wndclass.cbWndExtra = sizeof(ScintillaWin *);
2707 wndclass.hInstance = hInstance;
2708 wndclass.hIcon = NULL;
2709 wndclass.hCursor = NULL;
2710 wndclass.hbrBackground = NULL;
2711 wndclass.lpszMenuName = NULL;
2712 wndclass.lpszClassName = scintillaClassName;
2713 wndclass.hIconSm = 0;
2714 result = ::RegisterClassEx(&wndclass) != 0;
2717 if (result) {
2718 // Register the CallTip class
2719 WNDCLASSEX wndclassc;
2720 wndclassc.cbSize = sizeof(wndclassc);
2721 wndclassc.style = CS_GLOBALCLASS | CS_HREDRAW | CS_VREDRAW;
2722 wndclassc.cbClsExtra = 0;
2723 wndclassc.cbWndExtra = sizeof(ScintillaWin *);
2724 wndclassc.hInstance = hInstance;
2725 wndclassc.hIcon = NULL;
2726 wndclassc.hbrBackground = NULL;
2727 wndclassc.lpszMenuName = NULL;
2728 wndclassc.lpfnWndProc = ScintillaWin::CTWndProc;
2729 wndclassc.hCursor = ::LoadCursor(NULL, IDC_ARROW);
2730 wndclassc.lpszClassName = callClassName;
2731 wndclassc.hIconSm = 0;
2733 result = ::RegisterClassEx(&wndclassc) != 0;
2736 return result;
2739 bool ScintillaWin::Unregister() {
2740 bool result = ::UnregisterClass(scintillaClassName, hInstance) != 0;
2741 if (::UnregisterClass(callClassName, hInstance) == 0)
2742 result = false;
2743 return result;
2746 bool ScintillaWin::HasCaretSizeChanged() {
2747 if (
2748 ( (0 != vs.caretWidth) && (sysCaretWidth != vs.caretWidth) )
2749 || ((0 != vs.lineHeight) && (sysCaretHeight != vs.lineHeight))
2751 return true;
2753 return false;
2756 BOOL ScintillaWin::CreateSystemCaret() {
2757 sysCaretWidth = vs.caretWidth;
2758 if (0 == sysCaretWidth) {
2759 sysCaretWidth = 1;
2761 sysCaretHeight = vs.lineHeight;
2762 int bitmapSize = (((sysCaretWidth + 15) & ~15) >> 3) *
2763 sysCaretHeight;
2764 char *bits = new char[bitmapSize];
2765 memset(bits, 0, bitmapSize);
2766 sysCaretBitmap = ::CreateBitmap(sysCaretWidth, sysCaretHeight, 1,
2767 1, reinterpret_cast<BYTE *>(bits));
2768 delete []bits;
2769 BOOL retval = ::CreateCaret(
2770 MainHWND(), sysCaretBitmap,
2771 sysCaretWidth, sysCaretHeight);
2772 ::ShowCaret(MainHWND());
2773 return retval;
2776 BOOL ScintillaWin::DestroySystemCaret() {
2777 ::HideCaret(MainHWND());
2778 BOOL retval = ::DestroyCaret();
2779 if (sysCaretBitmap) {
2780 ::DeleteObject(sysCaretBitmap);
2781 sysCaretBitmap = 0;
2783 return retval;
2786 sptr_t PASCAL ScintillaWin::CTWndProc(
2787 HWND hWnd, UINT iMessage, WPARAM wParam, sptr_t lParam) {
2788 // Find C++ object associated with window.
2789 ScintillaWin *sciThis = reinterpret_cast<ScintillaWin *>(PointerFromWindow(hWnd));
2790 try {
2791 // ctp will be zero if WM_CREATE not seen yet
2792 if (sciThis == 0) {
2793 if (iMessage == WM_CREATE) {
2794 // Associate CallTip object with window
2795 CREATESTRUCT *pCreate = reinterpret_cast<CREATESTRUCT *>(lParam);
2796 SetWindowPointer(hWnd, pCreate->lpCreateParams);
2797 return 0;
2798 } else {
2799 return ::DefWindowProc(hWnd, iMessage, wParam, lParam);
2801 } else {
2802 if (iMessage == WM_NCDESTROY) {
2803 ::SetWindowLong(hWnd, 0, 0);
2804 return ::DefWindowProc(hWnd, iMessage, wParam, lParam);
2805 } else if (iMessage == WM_PAINT) {
2806 PAINTSTRUCT ps;
2807 ::BeginPaint(hWnd, &ps);
2808 Surface *surfaceWindow = Surface::Allocate(sciThis->technology);
2809 if (surfaceWindow) {
2810 #if defined(USE_D2D)
2811 ID2D1HwndRenderTarget *pCTRenderTarget = 0;
2812 #endif
2813 RECT rc;
2814 GetClientRect(hWnd, &rc);
2815 // Create a Direct2D render target.
2816 if (sciThis->technology == SC_TECHNOLOGY_DEFAULT) {
2817 surfaceWindow->Init(ps.hdc, hWnd);
2818 } else {
2819 #if defined(USE_D2D)
2820 pD2DFactory->CreateHwndRenderTarget(
2821 D2D1::RenderTargetProperties(D2D1_RENDER_TARGET_TYPE_DEFAULT, D2D1::PixelFormat(), 96.0, 96.0),
2822 D2D1::HwndRenderTargetProperties(hWnd, D2D1::SizeU(rc.right - rc.left, rc.bottom - rc.top)),
2823 &pCTRenderTarget);
2824 surfaceWindow->Init(pCTRenderTarget, hWnd);
2825 pCTRenderTarget->BeginDraw();
2826 #endif
2828 surfaceWindow->SetUnicodeMode(SC_CP_UTF8 == sciThis->ct.codePage);
2829 surfaceWindow->SetDBCSMode(sciThis->ct.codePage);
2830 sciThis->ct.PaintCT(surfaceWindow);
2831 #if defined(USE_D2D)
2832 if (pCTRenderTarget)
2833 pCTRenderTarget->EndDraw();
2834 #endif
2835 surfaceWindow->Release();
2836 delete surfaceWindow;
2837 #if defined(USE_D2D)
2838 if (pCTRenderTarget)
2839 pCTRenderTarget->Release();
2840 #endif
2842 ::EndPaint(hWnd, &ps);
2843 return 0;
2844 } else if ((iMessage == WM_NCLBUTTONDOWN) || (iMessage == WM_NCLBUTTONDBLCLK)) {
2845 POINT pt;
2846 pt.x = static_cast<short>(LOWORD(lParam));
2847 pt.y = static_cast<short>(HIWORD(lParam));
2848 ScreenToClient(hWnd, &pt);
2849 sciThis->ct.MouseClick(Point(pt.x, pt.y));
2850 sciThis->CallTipClick();
2851 return 0;
2852 } else if (iMessage == WM_LBUTTONDOWN) {
2853 // This does not fire due to the hit test code
2854 sciThis->ct.MouseClick(Point::FromLong(lParam));
2855 sciThis->CallTipClick();
2856 return 0;
2857 } else if (iMessage == WM_SETCURSOR) {
2858 ::SetCursor(::LoadCursor(NULL, IDC_ARROW));
2859 return 0;
2860 } else if (iMessage == WM_NCHITTEST) {
2861 return HTCAPTION;
2862 } else {
2863 return ::DefWindowProc(hWnd, iMessage, wParam, lParam);
2866 } catch (...) {
2867 sciThis->errorStatus = SC_STATUS_FAILURE;
2869 return ::DefWindowProc(hWnd, iMessage, wParam, lParam);
2872 sptr_t ScintillaWin::DirectFunction(
2873 ScintillaWin *sci, UINT iMessage, uptr_t wParam, sptr_t lParam) {
2874 PLATFORM_ASSERT(::GetCurrentThreadId() == ::GetWindowThreadProcessId(sci->MainHWND(), NULL));
2875 return sci->WndProc(iMessage, wParam, lParam);
2878 extern "C"
2879 #ifndef STATIC_BUILD
2880 __declspec(dllexport)
2881 #endif
2882 sptr_t __stdcall Scintilla_DirectFunction(
2883 ScintillaWin *sci, UINT iMessage, uptr_t wParam, sptr_t lParam) {
2884 return sci->WndProc(iMessage, wParam, lParam);
2887 sptr_t PASCAL ScintillaWin::SWndProc(
2888 HWND hWnd, UINT iMessage, WPARAM wParam, sptr_t lParam) {
2889 //Platform::DebugPrintf("S W:%x M:%x WP:%x L:%x\n", hWnd, iMessage, wParam, lParam);
2891 // Find C++ object associated with window.
2892 ScintillaWin *sci = reinterpret_cast<ScintillaWin *>(PointerFromWindow(hWnd));
2893 // sci will be zero if WM_CREATE not seen yet
2894 if (sci == 0) {
2895 try {
2896 if (iMessage == WM_CREATE) {
2897 // Create C++ object associated with window
2898 sci = new ScintillaWin(hWnd);
2899 SetWindowPointer(hWnd, sci);
2900 return sci->WndProc(iMessage, wParam, lParam);
2902 } catch (...) {
2904 return ::DefWindowProc(hWnd, iMessage, wParam, lParam);
2905 } else {
2906 if (iMessage == WM_NCDESTROY) {
2907 try {
2908 sci->Finalise();
2909 delete sci;
2910 } catch (...) {
2912 ::SetWindowLong(hWnd, 0, 0);
2913 return ::DefWindowProc(hWnd, iMessage, wParam, lParam);
2914 } else {
2915 return sci->WndProc(iMessage, wParam, lParam);
2920 // This function is externally visible so it can be called from container when building statically.
2921 // Must be called once only.
2922 int Scintilla_RegisterClasses(void *hInstance) {
2923 Platform_Initialise(hInstance);
2924 bool result = ScintillaWin::Register(reinterpret_cast<HINSTANCE>(hInstance));
2925 #ifdef SCI_LEXER
2926 Scintilla_LinkLexers();
2927 #endif
2928 return result;
2931 // This function is externally visible so it can be called from container when building statically.
2932 int Scintilla_ReleaseResources() {
2933 bool result = ScintillaWin::Unregister();
2934 Platform_Finalise();
2935 return result;
2938 #ifndef STATIC_BUILD
2939 extern "C" int APIENTRY DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID) {
2940 //Platform::DebugPrintf("Scintilla::DllMain %d %d\n", hInstance, dwReason);
2941 if (dwReason == DLL_PROCESS_ATTACH) {
2942 if (!Scintilla_RegisterClasses(hInstance))
2943 return FALSE;
2944 } else if (dwReason == DLL_PROCESS_DETACH) {
2945 Scintilla_ReleaseResources();
2947 return TRUE;
2949 #endif