applied backgroundcolors.patch
[TortoiseGit.git] / ext / scintilla / win32 / ScintillaWin.cxx
blobe3e72484a181374b5dd7036d51e5c4d765782623
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 #include <windows.h>
23 #include <commctrl.h>
24 #include <richedit.h>
25 #include <windowsx.h>
27 #if defined(_MSC_VER) && (_MSC_VER > 1200)
28 #define USE_D2D 1
29 #endif
31 #if defined(USE_D2D)
32 #include <d2d1.h>
33 #include <dwrite.h>
34 #endif
36 #include "Platform.h"
38 #include "ILexer.h"
39 #include "Scintilla.h"
41 #ifdef SCI_LEXER
42 #include "SciLexer.h"
43 #include "LexerModule.h"
44 #endif
45 #include "SplitVector.h"
46 #include "Partitioning.h"
47 #include "RunStyles.h"
48 #include "ContractionState.h"
49 #include "CellBuffer.h"
50 #include "CallTip.h"
51 #include "KeyMap.h"
52 #include "Indicator.h"
53 #include "XPM.h"
54 #include "LineMarker.h"
55 #include "Style.h"
56 #include "AutoComplete.h"
57 #include "ViewStyle.h"
58 #include "CharClassify.h"
59 #include "Decoration.h"
60 #include "Document.h"
61 #include "Selection.h"
62 #include "PositionCache.h"
63 #include "Editor.h"
64 #include "ScintillaBase.h"
65 #include "UniConversion.h"
66 #include "PlatWin.h"
68 #ifdef SCI_LEXER
69 #include "ExternalLexer.h"
70 #endif
72 #ifndef SPI_GETWHEELSCROLLLINES
73 #define SPI_GETWHEELSCROLLLINES 104
74 #endif
76 #ifndef WM_UNICHAR
77 #define WM_UNICHAR 0x0109
78 #endif
80 #ifndef UNICODE_NOCHAR
81 #define UNICODE_NOCHAR 0xFFFF
82 #endif
84 #ifndef WM_IME_STARTCOMPOSITION
85 #include <imm.h>
86 #endif
88 #include <commctrl.h>
89 #ifndef __DMC__
90 #include <zmouse.h>
91 #endif
92 #include <ole2.h>
94 #ifndef MK_ALT
95 #define MK_ALT 32
96 #endif
98 #define SC_WIN_IDLE 5001
100 typedef BOOL (WINAPI *TrackMouseEventSig)(LPTRACKMOUSEEVENT);
102 // GCC has trouble with the standard COM ABI so do it the old C way with explicit vtables.
104 const TCHAR scintillaClassName[] = TEXT("Scintilla");
105 const TCHAR callClassName[] = TEXT("CallTip");
107 #ifdef SCI_NAMESPACE
108 using namespace Scintilla;
109 #endif
111 // Take care of 32/64 bit pointers
112 #ifdef GetWindowLongPtr
113 static void *PointerFromWindow(HWND hWnd) {
114 return reinterpret_cast<void *>(::GetWindowLongPtr(hWnd, 0));
116 static void SetWindowPointer(HWND hWnd, void *ptr) {
117 ::SetWindowLongPtr(hWnd, 0, reinterpret_cast<LONG_PTR>(ptr));
119 static void SetWindowID(HWND hWnd, int identifier) {
120 ::SetWindowLongPtr(hWnd, GWLP_ID, identifier);
122 #else
123 static void *PointerFromWindow(HWND hWnd) {
124 return reinterpret_cast<void *>(::GetWindowLong(hWnd, 0));
126 static void SetWindowPointer(HWND hWnd, void *ptr) {
127 ::SetWindowLong(hWnd, 0, reinterpret_cast<LONG>(ptr));
129 static void SetWindowID(HWND hWnd, int identifier) {
130 ::SetWindowLong(hWnd, GWL_ID, identifier);
132 #endif
134 class ScintillaWin; // Forward declaration for COM interface subobjects
136 typedef void VFunction(void);
140 class FormatEnumerator {
141 public:
142 VFunction **vtbl;
143 int ref;
144 int pos;
145 CLIPFORMAT formats[2];
146 int formatsLen;
147 FormatEnumerator(int pos_, CLIPFORMAT formats_[], int formatsLen_);
152 class DropSource {
153 public:
154 VFunction **vtbl;
155 ScintillaWin *sci;
156 DropSource();
161 class DataObject {
162 public:
163 VFunction **vtbl;
164 ScintillaWin *sci;
165 DataObject();
170 class DropTarget {
171 public:
172 VFunction **vtbl;
173 ScintillaWin *sci;
174 DropTarget();
179 class ScintillaWin :
180 public ScintillaBase {
182 bool lastKeyDownConsumed;
184 bool capturedMouse;
185 bool trackedMouseLeave;
186 TrackMouseEventSig TrackMouseEventFn;
188 unsigned int linesPerScroll; ///< Intellimouse support
189 int wheelDelta; ///< Wheel delta from roll
191 HRGN hRgnUpdate;
193 bool hasOKText;
195 CLIPFORMAT cfColumnSelect;
196 CLIPFORMAT cfLineSelect;
198 HRESULT hrOle;
199 DropSource ds;
200 DataObject dob;
201 DropTarget dt;
203 static HINSTANCE hInstance;
205 #if defined(USE_D2D)
206 ID2D1HwndRenderTarget *pRenderTarget;
207 bool renderTargetValid;
208 #endif
210 ScintillaWin(HWND hwnd);
211 ScintillaWin(const ScintillaWin &);
212 virtual ~ScintillaWin();
213 ScintillaWin &operator=(const ScintillaWin &);
215 virtual void Initialise();
216 virtual void Finalise();
217 void EnsureRenderTarget();
218 void DropRenderTarget();
219 HWND MainHWND();
221 static sptr_t DirectFunction(
222 ScintillaWin *sci, UINT iMessage, uptr_t wParam, sptr_t lParam);
223 static sptr_t PASCAL SWndProc(
224 HWND hWnd, UINT iMessage, WPARAM wParam, sptr_t lParam);
225 static sptr_t PASCAL CTWndProc(
226 HWND hWnd, UINT iMessage, WPARAM wParam, sptr_t lParam);
228 enum { invalidTimerID, standardTimerID, idleTimerID };
230 virtual bool DragThreshold(Point ptStart, Point ptNow);
231 virtual void StartDrag();
232 sptr_t WndPaint(uptr_t wParam);
233 sptr_t HandleComposition(uptr_t wParam, sptr_t lParam);
234 UINT CodePageOfDocument();
235 virtual bool ValidCodePage(int codePage) const;
236 virtual sptr_t DefWndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam);
237 virtual bool SetIdle(bool on);
238 virtual void SetTicking(bool on);
239 virtual void SetMouseCapture(bool on);
240 virtual bool HaveMouseCapture();
241 virtual void SetTrackMouseLeaveEvent(bool on);
242 virtual bool PaintContains(PRectangle rc);
243 virtual void ScrollText(int linesToMove);
244 virtual void UpdateSystemCaret();
245 virtual void SetVerticalScrollPos();
246 virtual void SetHorizontalScrollPos();
247 virtual bool ModifyScrollBars(int nMax, int nPage);
248 virtual void NotifyChange();
249 virtual void NotifyFocus(bool focus);
250 virtual void SetCtrlID(int identifier);
251 virtual int GetCtrlID();
252 virtual void NotifyParent(SCNotification scn);
253 virtual void NotifyParent(SCNotification * scn);
254 virtual void NotifyDoubleClick(Point pt, bool shift, bool ctrl, bool alt);
255 virtual CaseFolder *CaseFolderForEncoding();
256 virtual std::string CaseMapString(const std::string &s, int caseMapping);
257 virtual void Copy();
258 virtual void CopyAllowLine();
259 virtual bool CanPaste();
260 virtual void Paste();
261 virtual void CreateCallTipWindow(PRectangle rc);
262 virtual void AddToPopUp(const char *label, int cmd = 0, bool enabled = true);
263 virtual void ClaimSelection();
265 // DBCS
266 void ImeStartComposition();
267 void ImeEndComposition();
269 void AddCharBytes(char b0, char b1);
271 void GetIntelliMouseParameters();
272 virtual void CopyToClipboard(const SelectionText &selectedText);
273 void ScrollMessage(WPARAM wParam);
274 void HorizontalScrollMessage(WPARAM wParam);
275 void FullPaint();
276 void FullPaintDC(HDC dc);
277 bool IsCompatibleDC(HDC dc);
278 DWORD EffectFromState(DWORD grfKeyState);
280 virtual int SetScrollInfo(int nBar, LPCSCROLLINFO lpsi, BOOL bRedraw);
281 virtual bool GetScrollInfo(int nBar, LPSCROLLINFO lpsi);
282 void ChangeScrollPos(int barType, int pos);
284 void InsertPasteText(const char *text, int len, SelectionPosition selStart, bool isRectangular, bool isLine);
286 public:
287 // Public for benefit of Scintilla_DirectFunction
288 virtual sptr_t WndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam);
290 /// Implement IUnknown
291 STDMETHODIMP QueryInterface(REFIID riid, PVOID *ppv);
292 STDMETHODIMP_(ULONG)AddRef();
293 STDMETHODIMP_(ULONG)Release();
295 /// Implement IDropTarget
296 STDMETHODIMP DragEnter(LPDATAOBJECT pIDataSource, DWORD grfKeyState,
297 POINTL pt, PDWORD pdwEffect);
298 STDMETHODIMP DragOver(DWORD grfKeyState, POINTL pt, PDWORD pdwEffect);
299 STDMETHODIMP DragLeave();
300 STDMETHODIMP Drop(LPDATAOBJECT pIDataSource, DWORD grfKeyState,
301 POINTL pt, PDWORD pdwEffect);
303 /// Implement important part of IDataObject
304 STDMETHODIMP GetData(FORMATETC *pFEIn, STGMEDIUM *pSTM);
306 static bool Register(HINSTANCE hInstance_);
307 static bool Unregister();
309 friend class DropSource;
310 friend class DataObject;
311 friend class DropTarget;
312 bool DragIsRectangularOK(CLIPFORMAT fmt) {
313 return drag.rectangular && (fmt == cfColumnSelect);
316 private:
317 // For use in creating a system caret
318 bool HasCaretSizeChanged();
319 BOOL CreateSystemCaret();
320 BOOL DestroySystemCaret();
321 HBITMAP sysCaretBitmap;
322 int sysCaretWidth;
323 int sysCaretHeight;
324 bool keysAlwaysUnicode;
327 HINSTANCE ScintillaWin::hInstance = 0;
329 ScintillaWin::ScintillaWin(HWND hwnd) {
331 lastKeyDownConsumed = false;
333 capturedMouse = false;
334 trackedMouseLeave = false;
335 TrackMouseEventFn = 0;
337 linesPerScroll = 0;
338 wheelDelta = 0; // Wheel delta from roll
340 hRgnUpdate = 0;
342 hasOKText = false;
344 // There does not seem to be a real standard for indicating that the clipboard
345 // contains a rectangular selection, so copy Developer Studio.
346 cfColumnSelect = static_cast<CLIPFORMAT>(
347 ::RegisterClipboardFormat(TEXT("MSDEVColumnSelect")));
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 TrackMouseEventFn = (TrackMouseEventSig)::GetProcAddress(user32, "TrackMouseEvent");
390 if (TrackMouseEventFn == NULL) {
391 // Windows 95 has an emulation in comctl32.dll:_TrackMouseEvent
392 HMODULE commctrl32 = ::LoadLibrary(TEXT("comctl32.dll"));
393 if (commctrl32 != NULL) {
394 TrackMouseEventFn = (TrackMouseEventSig)
395 ::GetProcAddress(commctrl32, "_TrackMouseEvent");
400 void ScintillaWin::Finalise() {
401 ScintillaBase::Finalise();
402 SetTicking(false);
403 SetIdle(false);
404 DropRenderTarget();
405 ::RevokeDragDrop(MainHWND());
406 if (SUCCEEDED(hrOle)) {
407 ::OleUninitialize();
411 void ScintillaWin::EnsureRenderTarget() {
412 #if defined(USE_D2D)
413 if (!renderTargetValid) {
414 DropRenderTarget();
415 renderTargetValid = true;
417 if (pD2DFactory && !pRenderTarget) {
418 RECT rc;
419 HWND hw = MainHWND();
420 GetClientRect(hw, &rc);
422 D2D1_SIZE_U size = D2D1::SizeU(rc.right - rc.left, rc.bottom - rc.top);
424 // Create a Direct2D render target.
425 #if 1
426 pD2DFactory->CreateHwndRenderTarget(
427 D2D1::RenderTargetProperties(),
428 D2D1::HwndRenderTargetProperties(hw, size),
429 &pRenderTarget);
430 #else
431 pD2DFactory->CreateHwndRenderTarget(
432 D2D1::RenderTargetProperties(
433 D2D1_RENDER_TARGET_TYPE_DEFAULT ,
434 D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_PREMULTIPLIED),
435 96.0f, 96.0f, D2D1_RENDER_TARGET_USAGE_NONE, D2D1_FEATURE_LEVEL_DEFAULT),
436 D2D1::HwndRenderTargetProperties(hw, size),
437 &pRenderTarget);
438 #endif
439 // Pixmaps were created to be compatible with previous render target so
440 // need to be recreated.
441 DropGraphics(false);
443 #endif
446 void ScintillaWin::DropRenderTarget() {
447 #if defined(USE_D2D)
448 if (pRenderTarget) {
449 pRenderTarget->Release();
450 pRenderTarget = 0;
452 #endif
455 HWND ScintillaWin::MainHWND() {
456 return reinterpret_cast<HWND>(wMain.GetID());
459 bool ScintillaWin::DragThreshold(Point ptStart, Point ptNow) {
460 int xMove = abs(ptStart.x - ptNow.x);
461 int yMove = abs(ptStart.y - ptNow.y);
462 return (xMove > ::GetSystemMetrics(SM_CXDRAG)) ||
463 (yMove > ::GetSystemMetrics(SM_CYDRAG));
466 void ScintillaWin::StartDrag() {
467 inDragDrop = ddDragging;
468 DWORD dwEffect = 0;
469 dropWentOutside = true;
470 IDataObject *pDataObject = reinterpret_cast<IDataObject *>(&dob);
471 IDropSource *pDropSource = reinterpret_cast<IDropSource *>(&ds);
472 //Platform::DebugPrintf("About to DoDragDrop %x %x\n", pDataObject, pDropSource);
473 HRESULT hr = ::DoDragDrop(
474 pDataObject,
475 pDropSource,
476 DROPEFFECT_COPY | DROPEFFECT_MOVE, &dwEffect);
477 //Platform::DebugPrintf("DoDragDrop = %x\n", hr);
478 if (SUCCEEDED(hr)) {
479 if ((hr == DRAGDROP_S_DROP) && (dwEffect == DROPEFFECT_MOVE) && dropWentOutside) {
480 // Remove dragged out text
481 ClearSelection();
484 inDragDrop = ddNone;
485 SetDragPosition(SelectionPosition(invalidPosition));
488 // Avoid warnings everywhere for old style casts by concentrating them here
489 static WORD LoWord(DWORD l) {
490 return LOWORD(l);
493 static WORD HiWord(DWORD l) {
494 return HIWORD(l);
497 static int InputCodePage() {
498 HKL inputLocale = ::GetKeyboardLayout(0);
499 LANGID inputLang = LOWORD(inputLocale);
500 char sCodePage[10];
501 int res = ::GetLocaleInfoA(MAKELCID(inputLang, SORT_DEFAULT),
502 LOCALE_IDEFAULTANSICODEPAGE, sCodePage, sizeof(sCodePage));
503 if (!res)
504 return 0;
505 return atoi(sCodePage);
508 #ifndef VK_OEM_2
509 static const int VK_OEM_2=0xbf;
510 static const int VK_OEM_3=0xc0;
511 static const int VK_OEM_4=0xdb;
512 static const int VK_OEM_5=0xdc;
513 static const int VK_OEM_6=0xdd;
514 #endif
516 /** Map the key codes to their equivalent SCK_ form. */
517 static int KeyTranslate(int keyIn) {
518 //PLATFORM_ASSERT(!keyIn);
519 switch (keyIn) {
520 case VK_DOWN: return SCK_DOWN;
521 case VK_UP: return SCK_UP;
522 case VK_LEFT: return SCK_LEFT;
523 case VK_RIGHT: return SCK_RIGHT;
524 case VK_HOME: return SCK_HOME;
525 case VK_END: return SCK_END;
526 case VK_PRIOR: return SCK_PRIOR;
527 case VK_NEXT: return SCK_NEXT;
528 case VK_DELETE: return SCK_DELETE;
529 case VK_INSERT: return SCK_INSERT;
530 case VK_ESCAPE: return SCK_ESCAPE;
531 case VK_BACK: return SCK_BACK;
532 case VK_TAB: return SCK_TAB;
533 case VK_RETURN: return SCK_RETURN;
534 case VK_ADD: return SCK_ADD;
535 case VK_SUBTRACT: return SCK_SUBTRACT;
536 case VK_DIVIDE: return SCK_DIVIDE;
537 case VK_LWIN: return SCK_WIN;
538 case VK_RWIN: return SCK_RWIN;
539 case VK_APPS: return SCK_MENU;
540 case VK_OEM_2: return '/';
541 case VK_OEM_3: return '`';
542 case VK_OEM_4: return '[';
543 case VK_OEM_5: return '\\';
544 case VK_OEM_6: return ']';
545 default: return keyIn;
549 LRESULT ScintillaWin::WndPaint(uptr_t wParam) {
550 //ElapsedTime et;
552 // Redirect assertions to debug output and save current state
553 bool assertsPopup = Platform::ShowAssertionPopUps(false);
554 paintState = painting;
555 PAINTSTRUCT ps;
556 PAINTSTRUCT *pps;
558 bool IsOcxCtrl = (wParam != 0); // if wParam != 0, it contains
559 // a PAINSTRUCT* from the OCX
560 // Removed since this interferes with reporting other assertions as it occurs repeatedly
561 //PLATFORM_ASSERT(hRgnUpdate == NULL);
562 hRgnUpdate = ::CreateRectRgn(0, 0, 0, 0);
563 if (IsOcxCtrl) {
564 pps = reinterpret_cast<PAINTSTRUCT*>(wParam);
565 } else {
566 ::GetUpdateRgn(MainHWND(), hRgnUpdate, FALSE);
567 pps = &ps;
568 ::BeginPaint(MainHWND(), pps);
570 if (technology == SC_TECHNOLOGY_DEFAULT) {
571 AutoSurface surfaceWindow(pps->hdc, this);
572 if (surfaceWindow) {
573 rcPaint = PRectangle(pps->rcPaint.left, pps->rcPaint.top, pps->rcPaint.right, pps->rcPaint.bottom);
574 PRectangle rcClient = GetClientRectangle();
575 paintingAllText = rcPaint.Contains(rcClient);
576 Paint(surfaceWindow, rcPaint);
577 surfaceWindow->Release();
579 } else {
580 #if defined(USE_D2D)
581 EnsureRenderTarget();
582 AutoSurface surfaceWindow(pRenderTarget, this);
583 if (surfaceWindow) {
584 pRenderTarget->BeginDraw();
585 rcPaint = PRectangle(pps->rcPaint.left, pps->rcPaint.top, pps->rcPaint.right, pps->rcPaint.bottom);
586 PRectangle rcClient = GetClientRectangle();
587 paintingAllText = rcPaint.Contains(rcClient);
588 if (paintingAllText) {
589 //Platform::DebugPrintf("Performing full text paint\n");
590 } else {
591 //Platform::DebugPrintf("Performing partial paint %d .. %d\n", rcPaint.top, rcPaint.bottom);
593 Paint(surfaceWindow, rcPaint);
594 surfaceWindow->Release();
595 HRESULT hr = pRenderTarget->EndDraw();
596 if (hr == D2DERR_RECREATE_TARGET) {
597 DropRenderTarget();
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 FullPaint();
613 paintState = notPainting;
615 // Restore debug output state
616 Platform::ShowAssertionPopUps(assertsPopup);
618 //Platform::DebugPrintf("Paint took %g\n", et.Duration());
619 return 0l;
622 sptr_t ScintillaWin::HandleComposition(uptr_t wParam, sptr_t lParam) {
623 #ifdef __DMC__
624 // Digital Mars compiler does not include Imm library
625 return 0;
626 #else
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 = pos.x;
654 CompForm.ptCurrentPos.y = pos.y;
655 ::ImmSetCompositionWindow(hIMC, &CompForm);
656 ::ImmReleaseContext(MainHWND(), hIMC);
658 return 0;
660 return ::DefWindowProc(MainHWND(), WM_IME_COMPOSITION, wParam, lParam);
661 #endif
664 // Translate message IDs from WM_* and EM_* to SCI_* so can partly emulate Windows Edit control
665 static unsigned int SciMessageFromEM(unsigned int iMessage) {
666 switch (iMessage) {
667 case EM_CANPASTE: return SCI_CANPASTE;
668 case EM_CANUNDO: return SCI_CANUNDO;
669 case EM_EMPTYUNDOBUFFER: return SCI_EMPTYUNDOBUFFER;
670 case EM_FINDTEXTEX: return SCI_FINDTEXT;
671 case EM_FORMATRANGE: return SCI_FORMATRANGE;
672 case EM_GETFIRSTVISIBLELINE: return SCI_GETFIRSTVISIBLELINE;
673 case EM_GETLINECOUNT: return SCI_GETLINECOUNT;
674 case EM_GETSELTEXT: return SCI_GETSELTEXT;
675 case EM_GETTEXTRANGE: return SCI_GETTEXTRANGE;
676 case EM_HIDESELECTION: return SCI_HIDESELECTION;
677 case EM_LINEINDEX: return SCI_POSITIONFROMLINE;
678 case EM_LINESCROLL: return SCI_LINESCROLL;
679 case EM_REPLACESEL: return SCI_REPLACESEL;
680 case EM_SCROLLCARET: return SCI_SCROLLCARET;
681 case EM_SETREADONLY: return SCI_SETREADONLY;
682 case WM_CLEAR: return SCI_CLEAR;
683 case WM_COPY: return SCI_COPY;
684 case WM_CUT: return SCI_CUT;
685 case WM_GETTEXT: return SCI_GETTEXT;
686 case WM_SETTEXT: return SCI_SETTEXT;
687 case WM_GETTEXTLENGTH: return SCI_GETTEXTLENGTH;
688 case WM_PASTE: return SCI_PASTE;
689 case WM_UNDO: return SCI_UNDO;
691 return iMessage;
694 static UINT CodePageFromCharSet(DWORD characterSet, UINT documentCodePage) {
695 if (documentCodePage == SC_CP_UTF8) {
696 // The system calls here are a little slow so avoid if known case.
697 return SC_CP_UTF8;
699 CHARSETINFO ci = { 0, 0, { { 0, 0, 0, 0 }, { 0, 0 } } };
700 BOOL bci = ::TranslateCharsetInfo((DWORD*)characterSet,
701 &ci, TCI_SRCCHARSET);
703 UINT cp;
704 if (bci)
705 cp = ci.ciACP;
706 else
707 cp = documentCodePage;
709 CPINFO cpi;
710 if (!IsValidCodePage(cp) && !GetCPInfo(cp, &cpi))
711 cp = CP_ACP;
713 return cp;
716 UINT ScintillaWin::CodePageOfDocument() {
717 return CodePageFromCharSet(vs.styles[STYLE_DEFAULT].characterSet, pdoc->dbcsCodePage);
720 sptr_t ScintillaWin::WndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
721 try {
722 //Platform::DebugPrintf("S M:%x WP:%x L:%x\n", iMessage, wParam, lParam);
723 iMessage = SciMessageFromEM(iMessage);
724 switch (iMessage) {
726 case WM_CREATE:
727 ctrlID = ::GetDlgCtrlID(reinterpret_cast<HWND>(wMain.GetID()));
728 // Get Intellimouse scroll line parameters
729 GetIntelliMouseParameters();
730 ::RegisterDragDrop(MainHWND(), reinterpret_cast<IDropTarget *>(&dt));
731 break;
733 case WM_COMMAND:
734 Command(LoWord(wParam));
735 break;
737 case WM_PAINT:
738 return WndPaint(wParam);
740 case WM_PRINTCLIENT: {
741 HDC hdc = reinterpret_cast<HDC>(wParam);
742 if (!IsCompatibleDC(hdc)) {
743 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
745 FullPaintDC(hdc);
747 break;
749 case WM_VSCROLL:
750 ScrollMessage(wParam);
751 break;
753 case WM_HSCROLL:
754 HorizontalScrollMessage(wParam);
755 break;
757 case WM_SIZE: {
758 #if defined(USE_D2D)
759 if (paintState == notPainting) {
760 DropRenderTarget();
761 } else {
762 renderTargetValid = false;
764 #endif
765 //Platform::DebugPrintf("Scintilla WM_SIZE %d %d\n", LoWord(lParam), HiWord(lParam));
766 ChangeSize();
768 break;
770 case WM_MOUSEWHEEL:
771 // if autocomplete list active then send mousewheel message to it
772 if (ac.Active()) {
773 HWND hWnd = reinterpret_cast<HWND>(ac.lb->GetID());
774 ::SendMessage(hWnd, iMessage, wParam, lParam);
775 break;
778 // Don't handle datazoom.
779 // (A good idea for datazoom would be to "fold" or "unfold" details.
780 // i.e. if datazoomed out only class structures are visible, when datazooming in the control
781 // structures appear, then eventually the individual statements...)
782 if (wParam & MK_SHIFT) {
783 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
786 // Either SCROLL or ZOOM. We handle the wheel steppings calculation
787 wheelDelta -= static_cast<short>(HiWord(wParam));
788 if (abs(wheelDelta) >= WHEEL_DELTA && linesPerScroll > 0) {
789 int linesToScroll = linesPerScroll;
790 if (linesPerScroll == WHEEL_PAGESCROLL)
791 linesToScroll = LinesOnScreen() - 1;
792 if (linesToScroll == 0) {
793 linesToScroll = 1;
795 linesToScroll *= (wheelDelta / WHEEL_DELTA);
796 if (wheelDelta >= 0)
797 wheelDelta = wheelDelta % WHEEL_DELTA;
798 else
799 wheelDelta = - (-wheelDelta % WHEEL_DELTA);
801 if (wParam & MK_CONTROL) {
802 // Zoom! We play with the font sizes in the styles.
803 // Number of steps/line is ignored, we just care if sizing up or down
804 if (linesToScroll < 0) {
805 KeyCommand(SCI_ZOOMIN);
806 } else {
807 KeyCommand(SCI_ZOOMOUT);
809 } else {
810 // Scroll
811 ScrollTo(topLine + linesToScroll);
814 return 0;
816 case WM_TIMER:
817 if (wParam == standardTimerID && timer.ticking) {
818 Tick();
819 } else if (wParam == idleTimerID && idler.state) {
820 SendMessage(MainHWND(), SC_WIN_IDLE, 0, 1);
821 } else {
822 return 1;
824 break;
826 case SC_WIN_IDLE:
827 // wParam=dwTickCountInitial, or 0 to initialize. lParam=bSkipUserInputTest
828 if (idler.state) {
829 if (lParam || (WAIT_TIMEOUT == MsgWaitForMultipleObjects(0, 0, 0, 0, QS_INPUT|QS_HOTKEY))) {
830 if (Idle()) {
831 // User input was given priority above, but all events do get a turn. Other
832 // messages, notifications, etc. will get interleaved with the idle messages.
834 // However, some things like WM_PAINT are a lower priority, and will not fire
835 // when there's a message posted. So, several times a second, we stop and let
836 // the low priority events have a turn (after which the timer will fire again).
838 DWORD dwCurrent = GetTickCount();
839 DWORD dwStart = wParam ? wParam : dwCurrent;
841 if (dwCurrent >= dwStart && dwCurrent > 200 && dwCurrent - 200 < dwStart)
842 PostMessage(MainHWND(), SC_WIN_IDLE, dwStart, 0);
843 } else {
844 SetIdle(false);
848 break;
850 case WM_GETMINMAXINFO:
851 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
853 case WM_LBUTTONDOWN: {
854 #ifndef __DMC__
855 // Digital Mars compiler does not include Imm library
856 // For IME, set the composition string as the result string.
857 HIMC hIMC = ::ImmGetContext(MainHWND());
858 ::ImmNotifyIME(hIMC, NI_COMPOSITIONSTR, CPS_COMPLETE, 0);
859 ::ImmReleaseContext(MainHWND(), hIMC);
860 #endif
862 //Platform::DebugPrintf("Buttdown %d %x %x %x %x %x\n",iMessage, wParam, lParam,
863 // Platform::IsKeyDown(VK_SHIFT),
864 // Platform::IsKeyDown(VK_CONTROL),
865 // Platform::IsKeyDown(VK_MENU));
866 ::SetFocus(MainHWND());
867 ButtonDown(Point::FromLong(lParam), ::GetMessageTime(),
868 (wParam & MK_SHIFT) != 0,
869 (wParam & MK_CONTROL) != 0,
870 Platform::IsKeyDown(VK_MENU));
872 break;
874 case WM_MOUSEMOVE:
875 SetTrackMouseLeaveEvent(true);
876 ButtonMove(Point::FromLong(lParam));
877 break;
879 case WM_MOUSELEAVE:
880 SetTrackMouseLeaveEvent(false);
881 MouseLeave();
882 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
884 case WM_LBUTTONUP:
885 ButtonUp(Point::FromLong(lParam),
886 ::GetMessageTime(),
887 (wParam & MK_CONTROL) != 0);
888 break;
890 case WM_RBUTTONDOWN:
891 ::SetFocus(MainHWND());
892 if (!PointInSelection(Point::FromLong(lParam))) {
893 CancelModes();
894 SetEmptySelection(PositionFromLocation(Point::FromLong(lParam)));
896 break;
898 case WM_SETCURSOR:
899 if (LoWord(lParam) == HTCLIENT) {
900 if (inDragDrop == ddDragging) {
901 DisplayCursor(Window::cursorUp);
902 } else {
903 // Display regular (drag) cursor over selection
904 POINT pt;
905 if (0 != ::GetCursorPos(&pt)) {
906 ::ScreenToClient(MainHWND(), &pt);
907 if (PointInSelMargin(Point(pt.x, pt.y))) {
908 DisplayCursor(GetMarginCursor(Point(pt.x, pt.y)));
909 } else if (PointInSelection(Point(pt.x, pt.y)) && !SelectionEmpty()) {
910 DisplayCursor(Window::cursorArrow);
911 } else if (PointIsHotspot(Point(pt.x, pt.y))) {
912 DisplayCursor(Window::cursorHand);
913 } else {
914 DisplayCursor(Window::cursorText);
918 return TRUE;
919 } else {
920 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
923 case WM_CHAR:
924 if (((wParam >= 128) || !iscntrl(wParam)) || !lastKeyDownConsumed) {
925 if (::IsWindowUnicode(MainHWND()) || keysAlwaysUnicode) {
926 wchar_t wcs[2] = {wParam, 0};
927 if (IsUnicodeMode()) {
928 // For a wide character version of the window:
929 char utfval[4];
930 unsigned int len = UTF8Length(wcs, 1);
931 UTF8FromUTF16(wcs, 1, utfval, len);
932 AddCharUTF(utfval, len);
933 } else {
934 UINT cpDest = CodePageOfDocument();
935 char inBufferCP[20];
936 int size = ::WideCharToMultiByte(cpDest,
937 0, wcs, 1, inBufferCP, sizeof(inBufferCP) - 1, 0, 0);
938 inBufferCP[size] = '\0';
939 AddCharUTF(inBufferCP, size);
941 } else {
942 if (IsUnicodeMode()) {
943 AddCharBytes('\0', LOBYTE(wParam));
944 } else {
945 AddChar(LOBYTE(wParam));
949 return 0;
951 case WM_UNICHAR:
952 if (wParam == UNICODE_NOCHAR) {
953 return IsUnicodeMode() ? 1 : 0;
954 } else if (lastKeyDownConsumed) {
955 return 1;
956 } else {
957 if (IsUnicodeMode()) {
958 char utfval[4];
959 wchar_t wcs[2] = {static_cast<wchar_t>(wParam), 0};
960 unsigned int len = UTF8Length(wcs, 1);
961 UTF8FromUTF16(wcs, 1, utfval, len);
962 AddCharUTF(utfval, len);
963 return 1;
964 } else {
965 return 0;
969 case WM_SYSKEYDOWN:
970 case WM_KEYDOWN: {
971 //Platform::DebugPrintf("S keydown %d %x %x %x %x\n",iMessage, wParam, lParam, ::IsKeyDown(VK_SHIFT), ::IsKeyDown(VK_CONTROL));
972 lastKeyDownConsumed = false;
973 int ret = KeyDown(KeyTranslate(wParam),
974 Platform::IsKeyDown(VK_SHIFT),
975 Platform::IsKeyDown(VK_CONTROL),
976 Platform::IsKeyDown(VK_MENU),
977 &lastKeyDownConsumed);
978 if (!ret && !lastKeyDownConsumed) {
979 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
981 break;
984 case WM_IME_KEYDOWN:
985 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
987 case WM_KEYUP:
988 //Platform::DebugPrintf("S keyup %d %x %x\n",iMessage, wParam, lParam);
989 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
991 case WM_SETTINGCHANGE:
992 //Platform::DebugPrintf("Setting Changed\n");
993 InvalidateStyleData();
994 // Get Intellimouse scroll line parameters
995 GetIntelliMouseParameters();
996 break;
998 case WM_GETDLGCODE:
999 return DLGC_HASSETSEL | DLGC_WANTALLKEYS;
1001 case WM_KILLFOCUS: {
1002 HWND wOther = reinterpret_cast<HWND>(wParam);
1003 HWND wThis = MainHWND();
1004 HWND wCT = reinterpret_cast<HWND>(ct.wCallTip.GetID());
1005 if (!wParam ||
1006 !(::IsChild(wThis, wOther) || (wOther == wCT))) {
1007 SetFocusState(false);
1008 DestroySystemCaret();
1011 break;
1013 case WM_SETFOCUS:
1014 SetFocusState(true);
1015 DestroySystemCaret();
1016 CreateSystemCaret();
1017 break;
1019 case WM_SYSCOLORCHANGE:
1020 //Platform::DebugPrintf("Setting Changed\n");
1021 InvalidateStyleData();
1022 break;
1024 case WM_IME_STARTCOMPOSITION: // dbcs
1025 ImeStartComposition();
1026 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1028 case WM_IME_ENDCOMPOSITION: // dbcs
1029 ImeEndComposition();
1030 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1032 case WM_IME_COMPOSITION:
1033 return HandleComposition(wParam, lParam);
1035 case WM_IME_CHAR: {
1036 AddCharBytes(HIBYTE(wParam), LOBYTE(wParam));
1037 return 0;
1040 case WM_CONTEXTMENU:
1041 if (displayPopupMenu) {
1042 Point pt = Point::FromLong(lParam);
1043 if ((pt.x == -1) && (pt.y == -1)) {
1044 // Caused by keyboard so display menu near caret
1045 pt = PointMainCaret();
1046 POINT spt = {pt.x, pt.y};
1047 ::ClientToScreen(MainHWND(), &spt);
1048 pt = Point(spt.x, spt.y);
1050 ContextMenu(pt);
1051 return 0;
1053 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1055 case WM_INPUTLANGCHANGE:
1056 //::SetThreadLocale(LOWORD(lParam));
1057 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1059 case WM_INPUTLANGCHANGEREQUEST:
1060 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1062 case WM_ERASEBKGND:
1063 return 1; // Avoid any background erasure as whole window painted.
1065 case WM_CAPTURECHANGED:
1066 capturedMouse = false;
1067 return 0;
1069 // These are not handled in Scintilla and its faster to dispatch them here.
1070 // Also moves time out to here so profile doesn't count lots of empty message calls.
1072 case WM_MOVE:
1073 case WM_MOUSEACTIVATE:
1074 case WM_NCHITTEST:
1075 case WM_NCCALCSIZE:
1076 case WM_NCPAINT:
1077 case WM_NCMOUSEMOVE:
1078 case WM_NCLBUTTONDOWN:
1079 case WM_IME_SETCONTEXT:
1080 case WM_IME_NOTIFY:
1081 case WM_SYSCOMMAND:
1082 case WM_WINDOWPOSCHANGING:
1083 case WM_WINDOWPOSCHANGED:
1084 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1086 case EM_LINEFROMCHAR:
1087 if (static_cast<int>(wParam) < 0) {
1088 wParam = SelectionStart().Position();
1090 return pdoc->LineFromPosition(wParam);
1092 case EM_EXLINEFROMCHAR:
1093 return pdoc->LineFromPosition(lParam);
1095 case EM_GETSEL:
1096 if (wParam) {
1097 *reinterpret_cast<int *>(wParam) = SelectionStart().Position();
1099 if (lParam) {
1100 *reinterpret_cast<int *>(lParam) = SelectionEnd().Position();
1102 return MAKELONG(SelectionStart().Position(), SelectionEnd().Position());
1104 case EM_EXGETSEL: {
1105 if (lParam == 0) {
1106 return 0;
1108 Sci_CharacterRange *pCR = reinterpret_cast<Sci_CharacterRange *>(lParam);
1109 pCR->cpMin = SelectionStart().Position();
1110 pCR->cpMax = SelectionEnd().Position();
1112 break;
1114 case EM_SETSEL: {
1115 int nStart = static_cast<int>(wParam);
1116 int nEnd = static_cast<int>(lParam);
1117 if (nStart == 0 && nEnd == -1) {
1118 nEnd = pdoc->Length();
1120 if (nStart == -1) {
1121 nStart = nEnd; // Remove selection
1123 if (nStart > nEnd) {
1124 SetSelection(nEnd, nStart);
1125 } else {
1126 SetSelection(nStart, nEnd);
1128 EnsureCaretVisible();
1130 break;
1132 case EM_EXSETSEL: {
1133 if (lParam == 0) {
1134 return 0;
1136 Sci_CharacterRange *pCR = reinterpret_cast<Sci_CharacterRange *>(lParam);
1137 sel.selType = Selection::selStream;
1138 if (pCR->cpMin == 0 && pCR->cpMax == -1) {
1139 SetSelection(pCR->cpMin, pdoc->Length());
1140 } else {
1141 SetSelection(pCR->cpMin, pCR->cpMax);
1143 EnsureCaretVisible();
1144 return pdoc->LineFromPosition(SelectionStart().Position());
1147 case SCI_GETDIRECTFUNCTION:
1148 return reinterpret_cast<sptr_t>(DirectFunction);
1150 case SCI_GETDIRECTPOINTER:
1151 return reinterpret_cast<sptr_t>(this);
1153 case SCI_GRABFOCUS:
1154 ::SetFocus(MainHWND());
1155 break;
1157 case SCI_SETKEYSUNICODE:
1158 keysAlwaysUnicode = wParam != 0;
1159 break;
1161 case SCI_GETKEYSUNICODE:
1162 return keysAlwaysUnicode;
1164 case SCI_SETTECHNOLOGY:
1165 if ((wParam == SC_TECHNOLOGY_DEFAULT) || (wParam == SC_TECHNOLOGY_DIRECTWRITE)) {
1166 if (technology != static_cast<int>(wParam)) {
1167 if (static_cast<int>(wParam) == SC_TECHNOLOGY_DIRECTWRITE) {
1168 #if defined(USE_D2D)
1169 if (!LoadD2D())
1170 // Failed to load Direct2D or DirectWrite so no effect
1171 return 0;
1172 #else
1173 return 0;
1174 #endif
1176 technology = wParam;
1177 // Invalidate all cached information including layout.
1178 DropGraphics(true);
1179 InvalidateStyleRedraw();
1182 break;
1184 #ifdef SCI_LEXER
1185 case SCI_LOADLEXERLIBRARY:
1186 LexerManager::GetInstance()->Load(reinterpret_cast<const char *>(lParam));
1187 break;
1188 #endif
1190 default:
1191 return ScintillaBase::WndProc(iMessage, wParam, lParam);
1193 } catch (std::bad_alloc &) {
1194 errorStatus = SC_STATUS_BADALLOC;
1195 } catch (...) {
1196 errorStatus = SC_STATUS_FAILURE;
1198 return 0l;
1201 bool ScintillaWin::ValidCodePage(int codePage) const {
1202 return codePage == 0 || codePage == SC_CP_UTF8 ||
1203 codePage == 932 || codePage == 936 || codePage == 949 ||
1204 codePage == 950 || codePage == 1361;
1207 sptr_t ScintillaWin::DefWndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
1208 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1211 void ScintillaWin::SetTicking(bool on) {
1212 if (timer.ticking != on) {
1213 timer.ticking = on;
1214 if (timer.ticking) {
1215 timer.tickerID = ::SetTimer(MainHWND(), standardTimerID, timer.tickSize, NULL)
1216 ? reinterpret_cast<TickerID>(standardTimerID) : 0;
1217 } else {
1218 ::KillTimer(MainHWND(), reinterpret_cast<uptr_t>(timer.tickerID));
1219 timer.tickerID = 0;
1222 timer.ticksToWait = caret.period;
1225 bool ScintillaWin::SetIdle(bool on) {
1226 // On Win32 the Idler is implemented as a Timer on the Scintilla window. This
1227 // takes advantage of the fact that WM_TIMER messages are very low priority,
1228 // and are only posted when the message queue is empty, i.e. during idle time.
1229 if (idler.state != on) {
1230 if (on) {
1231 idler.idlerID = ::SetTimer(MainHWND(), idleTimerID, 10, NULL)
1232 ? reinterpret_cast<IdlerID>(idleTimerID) : 0;
1233 } else {
1234 ::KillTimer(MainHWND(), reinterpret_cast<uptr_t>(idler.idlerID));
1235 idler.idlerID = 0;
1237 idler.state = idler.idlerID != 0;
1239 return idler.state;
1242 void ScintillaWin::SetMouseCapture(bool on) {
1243 if (mouseDownCaptures) {
1244 if (on) {
1245 ::SetCapture(MainHWND());
1246 } else {
1247 ::ReleaseCapture();
1250 capturedMouse = on;
1253 bool ScintillaWin::HaveMouseCapture() {
1254 // Cannot just see if GetCapture is this window as the scroll bar also sets capture for the window
1255 return capturedMouse;
1256 //return capturedMouse && (::GetCapture() == MainHWND());
1259 void ScintillaWin::SetTrackMouseLeaveEvent(bool on) {
1260 if (on && TrackMouseEventFn && !trackedMouseLeave) {
1261 TRACKMOUSEEVENT tme;
1262 tme.cbSize = sizeof(tme);
1263 tme.dwFlags = TME_LEAVE;
1264 tme.hwndTrack = MainHWND();
1265 TrackMouseEventFn(&tme);
1267 trackedMouseLeave = on;
1270 bool ScintillaWin::PaintContains(PRectangle rc) {
1271 bool contains = true;
1272 if ((paintState == painting) && (!rc.Empty())) {
1273 if (!rcPaint.Contains(rc)) {
1274 contains = false;
1275 } else {
1276 // In bounding rectangle so check more accurately using region
1277 HRGN hRgnRange = ::CreateRectRgn(rc.left, rc.top, rc.right, rc.bottom);
1278 if (hRgnRange) {
1279 HRGN hRgnDest = ::CreateRectRgn(0, 0, 0, 0);
1280 if (hRgnDest) {
1281 int combination = ::CombineRgn(hRgnDest, hRgnRange, hRgnUpdate, RGN_DIFF);
1282 if (combination != NULLREGION) {
1283 contains = false;
1285 ::DeleteRgn(hRgnDest);
1287 ::DeleteRgn(hRgnRange);
1291 return contains;
1294 void ScintillaWin::ScrollText(int /* linesToMove */) {
1295 //Platform::DebugPrintf("ScintillaWin::ScrollText %d\n", linesToMove);
1296 //::ScrollWindow(MainHWND(), 0,
1297 // vs.lineHeight * linesToMove, 0, 0);
1298 //::UpdateWindow(MainHWND());
1299 Redraw();
1302 void ScintillaWin::UpdateSystemCaret() {
1303 if (hasFocus) {
1304 if (HasCaretSizeChanged()) {
1305 DestroySystemCaret();
1306 CreateSystemCaret();
1308 Point pos = PointMainCaret();
1309 ::SetCaretPos(pos.x, pos.y);
1313 int ScintillaWin::SetScrollInfo(int nBar, LPCSCROLLINFO lpsi, BOOL bRedraw) {
1314 return ::SetScrollInfo(MainHWND(), nBar, lpsi, bRedraw);
1317 bool ScintillaWin::GetScrollInfo(int nBar, LPSCROLLINFO lpsi) {
1318 return ::GetScrollInfo(MainHWND(), nBar, lpsi) ? true : false;
1321 // Change the scroll position but avoid repaint if changing to same value
1322 void ScintillaWin::ChangeScrollPos(int barType, int pos) {
1323 SCROLLINFO sci = {
1324 sizeof(sci), 0, 0, 0, 0, 0, 0
1326 sci.fMask = SIF_POS;
1327 GetScrollInfo(barType, &sci);
1328 if (sci.nPos != pos) {
1329 DwellEnd(true);
1330 sci.nPos = pos;
1331 SetScrollInfo(barType, &sci, TRUE);
1335 void ScintillaWin::SetVerticalScrollPos() {
1336 ChangeScrollPos(SB_VERT, topLine);
1339 void ScintillaWin::SetHorizontalScrollPos() {
1340 ChangeScrollPos(SB_HORZ, xOffset);
1343 bool ScintillaWin::ModifyScrollBars(int nMax, int nPage) {
1344 bool modified = false;
1345 SCROLLINFO sci = {
1346 sizeof(sci), 0, 0, 0, 0, 0, 0
1348 sci.fMask = SIF_PAGE | SIF_RANGE;
1349 GetScrollInfo(SB_VERT, &sci);
1350 int vertEndPreferred = nMax;
1351 if (!verticalScrollBarVisible)
1352 vertEndPreferred = 0;
1353 if ((sci.nMin != 0) ||
1354 (sci.nMax != vertEndPreferred) ||
1355 (sci.nPage != static_cast<unsigned int>(nPage)) ||
1356 (sci.nPos != 0)) {
1357 //Platform::DebugPrintf("Scroll info changed %d %d %d %d %d\n",
1358 // sci.nMin, sci.nMax, sci.nPage, sci.nPos, sci.nTrackPos);
1359 sci.fMask = SIF_PAGE | SIF_RANGE;
1360 sci.nMin = 0;
1361 sci.nMax = vertEndPreferred;
1362 sci.nPage = nPage;
1363 sci.nPos = 0;
1364 sci.nTrackPos = 1;
1365 SetScrollInfo(SB_VERT, &sci, TRUE);
1366 modified = true;
1369 PRectangle rcText = GetTextRectangle();
1370 int horizEndPreferred = scrollWidth;
1371 if (horizEndPreferred < 0)
1372 horizEndPreferred = 0;
1373 if (!horizontalScrollBarVisible || (wrapState != eWrapNone))
1374 horizEndPreferred = 0;
1375 unsigned int pageWidth = rcText.Width();
1376 sci.fMask = SIF_PAGE | SIF_RANGE;
1377 GetScrollInfo(SB_HORZ, &sci);
1378 if ((sci.nMin != 0) ||
1379 (sci.nMax != horizEndPreferred) ||
1380 (sci.nPage != pageWidth) ||
1381 (sci.nPos != 0)) {
1382 sci.fMask = SIF_PAGE | SIF_RANGE;
1383 sci.nMin = 0;
1384 sci.nMax = horizEndPreferred;
1385 sci.nPage = pageWidth;
1386 sci.nPos = 0;
1387 sci.nTrackPos = 1;
1388 SetScrollInfo(SB_HORZ, &sci, TRUE);
1389 modified = true;
1390 if (scrollWidth < static_cast<int>(pageWidth)) {
1391 HorizontalScrollTo(0);
1394 return modified;
1397 void ScintillaWin::NotifyChange() {
1398 ::SendMessage(::GetParent(MainHWND()), WM_COMMAND,
1399 MAKELONG(GetCtrlID(), SCEN_CHANGE),
1400 reinterpret_cast<LPARAM>(MainHWND()));
1403 void ScintillaWin::NotifyFocus(bool focus) {
1404 ::SendMessage(::GetParent(MainHWND()), WM_COMMAND,
1405 MAKELONG(GetCtrlID(), focus ? SCEN_SETFOCUS : SCEN_KILLFOCUS),
1406 reinterpret_cast<LPARAM>(MainHWND()));
1409 void ScintillaWin::SetCtrlID(int identifier) {
1410 ::SetWindowID(reinterpret_cast<HWND>(wMain.GetID()), identifier);
1413 int ScintillaWin::GetCtrlID() {
1414 return ::GetDlgCtrlID(reinterpret_cast<HWND>(wMain.GetID()));
1417 void ScintillaWin::NotifyParent(SCNotification scn) {
1418 scn.nmhdr.hwndFrom = MainHWND();
1419 scn.nmhdr.idFrom = GetCtrlID();
1420 ::SendMessage(::GetParent(MainHWND()), WM_NOTIFY,
1421 GetCtrlID(), reinterpret_cast<LPARAM>(&scn));
1424 void ScintillaWin::NotifyParent(SCNotification * scn) {
1425 scn->nmhdr.hwndFrom = MainHWND();
1426 scn->nmhdr.idFrom = GetCtrlID();
1427 ::SendMessage(::GetParent(MainHWND()), WM_NOTIFY,
1428 GetCtrlID(), reinterpret_cast<LPARAM>(scn));
1431 void ScintillaWin::NotifyDoubleClick(Point pt, bool shift, bool ctrl, bool alt) {
1432 //Platform::DebugPrintf("ScintillaWin Double click 0\n");
1433 ScintillaBase::NotifyDoubleClick(pt, shift, ctrl, alt);
1434 // Send myself a WM_LBUTTONDBLCLK, so the container can handle it too.
1435 ::SendMessage(MainHWND(),
1436 WM_LBUTTONDBLCLK,
1437 shift ? MK_SHIFT : 0,
1438 MAKELPARAM(pt.x, pt.y));
1441 class CaseFolderUTF8 : public CaseFolderTable {
1442 // Allocate the expandable storage here so that it does not need to be reallocated
1443 // for each call to Fold.
1444 std::vector<wchar_t> utf16Mixed;
1445 std::vector<wchar_t> utf16Folded;
1446 public:
1447 CaseFolderUTF8() {
1448 StandardASCII();
1450 virtual size_t Fold(char *folded, size_t sizeFolded, const char *mixed, size_t lenMixed) {
1451 if ((lenMixed == 1) && (sizeFolded > 0)) {
1452 folded[0] = mapping[static_cast<unsigned char>(mixed[0])];
1453 return 1;
1454 } else {
1455 if (lenMixed > utf16Mixed.size()) {
1456 utf16Mixed.resize(lenMixed + 8);
1458 size_t nUtf16Mixed = ::MultiByteToWideChar(65001, 0, mixed,
1459 static_cast<int>(lenMixed),
1460 &utf16Mixed[0],
1461 static_cast<int>(utf16Mixed.size()));
1463 if (nUtf16Mixed == 0) {
1464 // Failed to convert -> bad UTF-8
1465 folded[0] = '\0';
1466 return 1;
1469 if (nUtf16Mixed * 4 > utf16Folded.size()) { // Maximum folding expansion factor of 4
1470 utf16Folded.resize(nUtf16Mixed * 4 + 8);
1472 int lenFlat = ::LCMapStringW(LOCALE_SYSTEM_DEFAULT,
1473 LCMAP_LINGUISTIC_CASING | LCMAP_LOWERCASE,
1474 &utf16Mixed[0],
1475 static_cast<int>(nUtf16Mixed),
1476 &utf16Folded[0],
1477 static_cast<int>(utf16Folded.size()));
1479 size_t lenOut = UTF8Length(&utf16Folded[0], lenFlat);
1480 if (lenOut < sizeFolded) {
1481 UTF8FromUTF16(&utf16Folded[0], lenFlat, folded, static_cast<int>(lenOut));
1482 return lenOut;
1483 } else {
1484 return 0;
1490 class CaseFolderDBCS : public CaseFolderTable {
1491 // Allocate the expandable storage here so that it does not need to be reallocated
1492 // for each call to Fold.
1493 std::vector<wchar_t> utf16Mixed;
1494 std::vector<wchar_t> utf16Folded;
1495 UINT cp;
1496 public:
1497 CaseFolderDBCS(UINT cp_) : cp(cp_) {
1498 StandardASCII();
1500 virtual size_t Fold(char *folded, size_t sizeFolded, const char *mixed, size_t lenMixed) {
1501 if ((lenMixed == 1) && (sizeFolded > 0)) {
1502 folded[0] = mapping[static_cast<unsigned char>(mixed[0])];
1503 return 1;
1504 } else {
1505 if (lenMixed > utf16Mixed.size()) {
1506 utf16Mixed.resize(lenMixed + 8);
1508 size_t nUtf16Mixed = ::MultiByteToWideChar(cp, 0, mixed,
1509 static_cast<int>(lenMixed),
1510 &utf16Mixed[0],
1511 static_cast<int>(utf16Mixed.size()));
1513 if (nUtf16Mixed == 0) {
1514 // Failed to convert -> bad input
1515 folded[0] = '\0';
1516 return 1;
1519 if (nUtf16Mixed * 4 > utf16Folded.size()) { // Maximum folding expansion factor of 4
1520 utf16Folded.resize(nUtf16Mixed * 4 + 8);
1522 int lenFlat = ::LCMapStringW(LOCALE_SYSTEM_DEFAULT,
1523 LCMAP_LINGUISTIC_CASING | LCMAP_LOWERCASE,
1524 &utf16Mixed[0],
1525 static_cast<int>(nUtf16Mixed),
1526 &utf16Folded[0],
1527 static_cast<int>(utf16Folded.size()));
1529 size_t lenOut = ::WideCharToMultiByte(cp, 0,
1530 &utf16Folded[0], lenFlat,
1531 NULL, 0, NULL, 0);
1533 if (lenOut < sizeFolded) {
1534 ::WideCharToMultiByte(cp, 0,
1535 &utf16Folded[0], lenFlat,
1536 folded, static_cast<int>(lenOut), NULL, 0);
1537 return lenOut;
1538 } else {
1539 return 0;
1545 CaseFolder *ScintillaWin::CaseFolderForEncoding() {
1546 UINT cpDest = CodePageOfDocument();
1547 if (cpDest == SC_CP_UTF8) {
1548 return new CaseFolderUTF8();
1549 } else {
1550 if (pdoc->dbcsCodePage == 0) {
1551 CaseFolderTable *pcf = new CaseFolderTable();
1552 pcf->StandardASCII();
1553 // Only for single byte encodings
1554 UINT cpDoc = CodePageOfDocument();
1555 for (int i=0x80; i<0x100; i++) {
1556 char sCharacter[2] = "A";
1557 sCharacter[0] = static_cast<char>(i);
1558 wchar_t wCharacter[20];
1559 unsigned int lengthUTF16 = ::MultiByteToWideChar(cpDoc, 0, sCharacter, 1,
1560 wCharacter, sizeof(wCharacter)/sizeof(wCharacter[0]));
1561 if (lengthUTF16 == 1) {
1562 wchar_t wLower[20];
1563 int charsConverted = ::LCMapStringW(LOCALE_SYSTEM_DEFAULT,
1564 LCMAP_LINGUISTIC_CASING | LCMAP_LOWERCASE,
1565 wCharacter, lengthUTF16, wLower, sizeof(wLower)/sizeof(wLower[0]));
1566 char sCharacterLowered[20];
1567 unsigned int lengthConverted = ::WideCharToMultiByte(cpDoc, 0,
1568 wLower, charsConverted,
1569 sCharacterLowered, sizeof(sCharacterLowered), NULL, 0);
1570 if ((lengthConverted == 1) && (sCharacter[0] != sCharacterLowered[0])) {
1571 pcf->SetTranslation(sCharacter[0], sCharacterLowered[0]);
1575 return pcf;
1576 } else {
1577 return new CaseFolderDBCS(cpDest);
1582 std::string ScintillaWin::CaseMapString(const std::string &s, int caseMapping) {
1583 if (s.size() == 0)
1584 return std::string();
1586 if (caseMapping == cmSame)
1587 return s;
1589 UINT cpDoc = CodePageOfDocument();
1591 unsigned int lengthUTF16 = ::MultiByteToWideChar(cpDoc, 0, s.c_str(),
1592 static_cast<int>(s.size()), NULL, 0);
1593 if (lengthUTF16 == 0) // Failed to convert
1594 return s;
1596 DWORD mapFlags = LCMAP_LINGUISTIC_CASING |
1597 ((caseMapping == cmUpper) ? LCMAP_UPPERCASE : LCMAP_LOWERCASE);
1599 // Many conversions performed by search function are short so optimize this case.
1600 enum { shortSize=20 };
1602 if (s.size() > shortSize) {
1603 // Use dynamic allocations for long strings
1605 // Change text to UTF-16
1606 std::vector<wchar_t> vwcText(lengthUTF16);
1607 ::MultiByteToWideChar(cpDoc, 0, s.c_str(), static_cast<int>(s.size()), &vwcText[0], lengthUTF16);
1609 // Change case
1610 int charsConverted = ::LCMapStringW(LOCALE_SYSTEM_DEFAULT, mapFlags,
1611 &vwcText[0], lengthUTF16, NULL, 0);
1612 std::vector<wchar_t> vwcConverted(charsConverted);
1613 ::LCMapStringW(LOCALE_SYSTEM_DEFAULT, mapFlags,
1614 &vwcText[0], lengthUTF16, &vwcConverted[0], charsConverted);
1616 // Change back to document encoding
1617 unsigned int lengthConverted = ::WideCharToMultiByte(cpDoc, 0,
1618 &vwcConverted[0], static_cast<int>(vwcConverted.size()),
1619 NULL, 0, NULL, 0);
1620 std::vector<char> vcConverted(lengthConverted);
1621 ::WideCharToMultiByte(cpDoc, 0,
1622 &vwcConverted[0], static_cast<int>(vwcConverted.size()),
1623 &vcConverted[0], static_cast<int>(vcConverted.size()), NULL, 0);
1625 return std::string(&vcConverted[0], vcConverted.size());
1627 } else {
1628 // Use static allocations for short strings as much faster
1629 // A factor of 15 for single character strings
1631 // Change text to UTF-16
1632 wchar_t vwcText[shortSize];
1633 ::MultiByteToWideChar(cpDoc, 0, s.c_str(), static_cast<int>(s.size()),
1634 vwcText, lengthUTF16);
1636 // Change case
1637 int charsConverted = ::LCMapStringW(LOCALE_SYSTEM_DEFAULT, mapFlags,
1638 vwcText, lengthUTF16, NULL, 0);
1639 // Full mapping may produce up to 3 characters per input character
1640 wchar_t vwcConverted[shortSize*3];
1641 ::LCMapStringW(LOCALE_SYSTEM_DEFAULT, mapFlags, vwcText, lengthUTF16,
1642 vwcConverted, charsConverted);
1644 // Change back to document encoding
1645 unsigned int lengthConverted = ::WideCharToMultiByte(cpDoc, 0,
1646 vwcConverted, charsConverted,
1647 NULL, 0, NULL, 0);
1648 // Each UTF-16 code unit may need up to 3 bytes in UTF-8
1649 char vcConverted[shortSize * 3 * 3];
1650 ::WideCharToMultiByte(cpDoc, 0,
1651 vwcConverted, charsConverted,
1652 vcConverted, lengthConverted, NULL, 0);
1654 return std::string(vcConverted, lengthConverted);
1658 void ScintillaWin::Copy() {
1659 //Platform::DebugPrintf("Copy\n");
1660 if (!sel.Empty()) {
1661 SelectionText selectedText;
1662 CopySelectionRange(&selectedText);
1663 CopyToClipboard(selectedText);
1667 void ScintillaWin::CopyAllowLine() {
1668 SelectionText selectedText;
1669 CopySelectionRange(&selectedText, true);
1670 CopyToClipboard(selectedText);
1673 bool ScintillaWin::CanPaste() {
1674 if (!Editor::CanPaste())
1675 return false;
1676 if (::IsClipboardFormatAvailable(CF_TEXT))
1677 return true;
1678 if (IsUnicodeMode())
1679 return ::IsClipboardFormatAvailable(CF_UNICODETEXT) != 0;
1680 return false;
1683 class GlobalMemory {
1684 HGLOBAL hand;
1685 public:
1686 void *ptr;
1687 GlobalMemory() : hand(0), ptr(0) {
1689 GlobalMemory(HGLOBAL hand_) : hand(hand_), ptr(0) {
1690 if (hand) {
1691 ptr = ::GlobalLock(hand);
1694 ~GlobalMemory() {
1695 PLATFORM_ASSERT(!ptr);
1697 void Allocate(size_t bytes) {
1698 hand = ::GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT, bytes);
1699 if (hand) {
1700 ptr = ::GlobalLock(hand);
1703 HGLOBAL Unlock() {
1704 PLATFORM_ASSERT(ptr);
1705 HGLOBAL handCopy = hand;
1706 ::GlobalUnlock(hand);
1707 ptr = 0;
1708 hand = 0;
1709 return handCopy;
1711 void SetClip(UINT uFormat) {
1712 ::SetClipboardData(uFormat, Unlock());
1714 operator bool() const {
1715 return ptr != 0;
1717 SIZE_T Size() {
1718 return ::GlobalSize(hand);
1722 void ScintillaWin::InsertPasteText(const char *text, int len, SelectionPosition selStart, bool isRectangular, bool isLine) {
1723 if (isRectangular) {
1724 PasteRectangular(selStart, text, len);
1725 } else {
1726 char *convertedText = 0;
1727 if (convertPastes) {
1728 // Convert line endings of the paste into our local line-endings mode
1729 convertedText = Document::TransformLineEnds(&len, text, len, pdoc->eolMode);
1730 text = convertedText;
1732 if (isLine) {
1733 int insertPos = pdoc->LineStart(pdoc->LineFromPosition(sel.MainCaret()));
1734 pdoc->InsertString(insertPos, text, len);
1735 // add the newline if necessary
1736 if ((len > 0) && (text[len-1] != '\n' && text[len-1] != '\r')) {
1737 const char *endline = StringFromEOLMode(pdoc->eolMode);
1738 pdoc->InsertString(insertPos + len, endline, static_cast<int>(strlen(endline)));
1739 len += static_cast<int>(strlen(endline));
1741 if (sel.MainCaret() == insertPos) {
1742 SetEmptySelection(sel.MainCaret() + len);
1744 } else {
1745 InsertPaste(selStart, text, len);
1747 delete []convertedText;
1751 void ScintillaWin::Paste() {
1752 if (!::OpenClipboard(MainHWND()))
1753 return;
1754 UndoGroup ug(pdoc);
1755 bool isLine = SelectionEmpty() && (::IsClipboardFormatAvailable(cfLineSelect) != 0);
1756 ClearSelection(multiPasteMode == SC_MULTIPASTE_EACH);
1757 SelectionPosition selStart = sel.IsRectangular() ?
1758 sel.Rectangular().Start() :
1759 sel.Range(sel.Main()).Start();
1760 bool isRectangular = ::IsClipboardFormatAvailable(cfColumnSelect) != 0;
1762 // Always use CF_UNICODETEXT if available
1763 GlobalMemory memUSelection(::GetClipboardData(CF_UNICODETEXT));
1764 if (memUSelection) {
1765 wchar_t *uptr = static_cast<wchar_t *>(memUSelection.ptr);
1766 if (uptr) {
1767 unsigned int len;
1768 char *putf;
1769 // Default Scintilla behaviour in Unicode mode
1770 if (IsUnicodeMode()) {
1771 unsigned int bytes = memUSelection.Size();
1772 len = UTF8Length(uptr, bytes / 2);
1773 putf = new char[len + 1];
1774 UTF8FromUTF16(uptr, bytes / 2, putf, len);
1775 } else {
1776 // CF_UNICODETEXT available, but not in Unicode mode
1777 // Convert from Unicode to current Scintilla code page
1778 UINT cpDest = CodePageOfDocument();
1779 len = ::WideCharToMultiByte(cpDest, 0, uptr, -1,
1780 NULL, 0, NULL, NULL) - 1; // subtract 0 terminator
1781 putf = new char[len + 1];
1782 ::WideCharToMultiByte(cpDest, 0, uptr, -1,
1783 putf, len + 1, NULL, NULL);
1786 InsertPasteText(putf, len, selStart, isRectangular, isLine);
1787 delete []putf;
1789 memUSelection.Unlock();
1790 } else {
1791 // CF_UNICODETEXT not available, paste ANSI text
1792 GlobalMemory memSelection(::GetClipboardData(CF_TEXT));
1793 if (memSelection) {
1794 char *ptr = static_cast<char *>(memSelection.ptr);
1795 if (ptr) {
1796 unsigned int bytes = memSelection.Size();
1797 unsigned int len = bytes;
1798 for (unsigned int i = 0; i < bytes; i++) {
1799 if ((len == bytes) && (0 == ptr[i]))
1800 len = i;
1803 // In Unicode mode, convert clipboard text to UTF-8
1804 if (IsUnicodeMode()) {
1805 wchar_t *uptr = new wchar_t[len+1];
1807 unsigned int ulen = ::MultiByteToWideChar(CP_ACP, 0,
1808 ptr, len, uptr, len+1);
1810 unsigned int mlen = UTF8Length(uptr, ulen);
1811 char *putf = new char[mlen + 1];
1812 if (putf) {
1813 // CP_UTF8 not available on Windows 95, so use UTF8FromUTF16()
1814 UTF8FromUTF16(uptr, ulen, putf, mlen);
1817 delete []uptr;
1819 if (putf) {
1820 InsertPasteText(putf, mlen, selStart, isRectangular, isLine);
1821 delete []putf;
1823 } else {
1824 InsertPasteText(ptr, len, selStart, isRectangular, isLine);
1827 memSelection.Unlock();
1830 ::CloseClipboard();
1831 Redraw();
1834 void ScintillaWin::CreateCallTipWindow(PRectangle) {
1835 if (!ct.wCallTip.Created()) {
1836 ct.wCallTip = ::CreateWindow(callClassName, TEXT("ACallTip"),
1837 WS_POPUP, 100, 100, 150, 20,
1838 MainHWND(), 0,
1839 GetWindowInstance(MainHWND()),
1840 this);
1841 ct.wDraw = ct.wCallTip;
1845 void ScintillaWin::AddToPopUp(const char *label, int cmd, bool enabled) {
1846 HMENU hmenuPopup = reinterpret_cast<HMENU>(popup.GetID());
1847 if (!label[0])
1848 ::AppendMenuA(hmenuPopup, MF_SEPARATOR, 0, "");
1849 else if (enabled)
1850 ::AppendMenuA(hmenuPopup, MF_STRING, cmd, label);
1851 else
1852 ::AppendMenuA(hmenuPopup, MF_STRING | MF_DISABLED | MF_GRAYED, cmd, label);
1855 void ScintillaWin::ClaimSelection() {
1856 // Windows does not have a primary selection
1859 /// Implement IUnknown
1861 STDMETHODIMP_(ULONG)FormatEnumerator_AddRef(FormatEnumerator *fe);
1862 STDMETHODIMP FormatEnumerator_QueryInterface(FormatEnumerator *fe, REFIID riid, PVOID *ppv) {
1863 //Platform::DebugPrintf("EFE QI");
1864 *ppv = NULL;
1865 if (riid == IID_IUnknown)
1866 *ppv = reinterpret_cast<IEnumFORMATETC *>(fe);
1867 if (riid == IID_IEnumFORMATETC)
1868 *ppv = reinterpret_cast<IEnumFORMATETC *>(fe);
1869 if (!*ppv)
1870 return E_NOINTERFACE;
1871 FormatEnumerator_AddRef(fe);
1872 return S_OK;
1874 STDMETHODIMP_(ULONG)FormatEnumerator_AddRef(FormatEnumerator *fe) {
1875 return ++fe->ref;
1877 STDMETHODIMP_(ULONG)FormatEnumerator_Release(FormatEnumerator *fe) {
1878 fe->ref--;
1879 if (fe->ref > 0)
1880 return fe->ref;
1881 delete fe;
1882 return 0;
1884 /// Implement IEnumFORMATETC
1885 STDMETHODIMP FormatEnumerator_Next(FormatEnumerator *fe, ULONG celt, FORMATETC *rgelt, ULONG *pceltFetched) {
1886 //Platform::DebugPrintf("EFE Next %d %d", fe->pos, celt);
1887 if (rgelt == NULL) return E_POINTER;
1888 // We only support one format, so this is simple.
1889 unsigned int putPos = 0;
1890 while ((fe->pos < fe->formatsLen) && (putPos < celt)) {
1891 rgelt->cfFormat = fe->formats[fe->pos];
1892 rgelt->ptd = 0;
1893 rgelt->dwAspect = DVASPECT_CONTENT;
1894 rgelt->lindex = -1;
1895 rgelt->tymed = TYMED_HGLOBAL;
1896 fe->pos++;
1897 putPos++;
1899 if (pceltFetched)
1900 *pceltFetched = putPos;
1901 return putPos ? S_OK : S_FALSE;
1903 STDMETHODIMP FormatEnumerator_Skip(FormatEnumerator *fe, ULONG celt) {
1904 fe->pos += celt;
1905 return S_OK;
1907 STDMETHODIMP FormatEnumerator_Reset(FormatEnumerator *fe) {
1908 fe->pos = 0;
1909 return S_OK;
1911 STDMETHODIMP FormatEnumerator_Clone(FormatEnumerator *fe, IEnumFORMATETC **ppenum) {
1912 FormatEnumerator *pfe;
1913 try {
1914 pfe = new FormatEnumerator(fe->pos, fe->formats, fe->formatsLen);
1915 } catch (...) {
1916 return E_OUTOFMEMORY;
1918 return FormatEnumerator_QueryInterface(pfe, IID_IEnumFORMATETC,
1919 reinterpret_cast<void **>(ppenum));
1922 static VFunction *vtFormatEnumerator[] = {
1923 (VFunction *)(FormatEnumerator_QueryInterface),
1924 (VFunction *)(FormatEnumerator_AddRef),
1925 (VFunction *)(FormatEnumerator_Release),
1926 (VFunction *)(FormatEnumerator_Next),
1927 (VFunction *)(FormatEnumerator_Skip),
1928 (VFunction *)(FormatEnumerator_Reset),
1929 (VFunction *)(FormatEnumerator_Clone)
1932 FormatEnumerator::FormatEnumerator(int pos_, CLIPFORMAT formats_[], int formatsLen_) {
1933 vtbl = vtFormatEnumerator;
1934 ref = 0; // First QI adds first reference...
1935 pos = pos_;
1936 formatsLen = formatsLen_;
1937 for (int i=0; i<formatsLen; i++)
1938 formats[i] = formats_[i];
1941 /// Implement IUnknown
1942 STDMETHODIMP DropSource_QueryInterface(DropSource *ds, REFIID riid, PVOID *ppv) {
1943 return ds->sci->QueryInterface(riid, ppv);
1945 STDMETHODIMP_(ULONG)DropSource_AddRef(DropSource *ds) {
1946 return ds->sci->AddRef();
1948 STDMETHODIMP_(ULONG)DropSource_Release(DropSource *ds) {
1949 return ds->sci->Release();
1952 /// Implement IDropSource
1953 STDMETHODIMP DropSource_QueryContinueDrag(DropSource *, BOOL fEsc, DWORD grfKeyState) {
1954 if (fEsc)
1955 return DRAGDROP_S_CANCEL;
1956 if (!(grfKeyState & MK_LBUTTON))
1957 return DRAGDROP_S_DROP;
1958 return S_OK;
1961 STDMETHODIMP DropSource_GiveFeedback(DropSource *, DWORD) {
1962 return DRAGDROP_S_USEDEFAULTCURSORS;
1965 static VFunction *vtDropSource[] = {
1966 (VFunction *)(DropSource_QueryInterface),
1967 (VFunction *)(DropSource_AddRef),
1968 (VFunction *)(DropSource_Release),
1969 (VFunction *)(DropSource_QueryContinueDrag),
1970 (VFunction *)(DropSource_GiveFeedback)
1973 DropSource::DropSource() {
1974 vtbl = vtDropSource;
1975 sci = 0;
1978 /// Implement IUnkown
1979 STDMETHODIMP DataObject_QueryInterface(DataObject *pd, REFIID riid, PVOID *ppv) {
1980 //Platform::DebugPrintf("DO QI %x\n", pd);
1981 return pd->sci->QueryInterface(riid, ppv);
1983 STDMETHODIMP_(ULONG)DataObject_AddRef(DataObject *pd) {
1984 return pd->sci->AddRef();
1986 STDMETHODIMP_(ULONG)DataObject_Release(DataObject *pd) {
1987 return pd->sci->Release();
1989 /// Implement IDataObject
1990 STDMETHODIMP DataObject_GetData(DataObject *pd, FORMATETC *pFEIn, STGMEDIUM *pSTM) {
1991 return pd->sci->GetData(pFEIn, pSTM);
1994 STDMETHODIMP DataObject_GetDataHere(DataObject *, FORMATETC *, STGMEDIUM *) {
1995 //Platform::DebugPrintf("DOB GetDataHere\n");
1996 return E_NOTIMPL;
1999 STDMETHODIMP DataObject_QueryGetData(DataObject *pd, FORMATETC *pFE) {
2000 if (pd->sci->DragIsRectangularOK(pFE->cfFormat) &&
2001 pFE->ptd == 0 &&
2002 (pFE->dwAspect & DVASPECT_CONTENT) != 0 &&
2003 pFE->lindex == -1 &&
2004 (pFE->tymed & TYMED_HGLOBAL) != 0
2006 return S_OK;
2009 bool formatOK = (pFE->cfFormat == CF_TEXT) ||
2010 ((pFE->cfFormat == CF_UNICODETEXT) && pd->sci->IsUnicodeMode());
2011 if (!formatOK ||
2012 pFE->ptd != 0 ||
2013 (pFE->dwAspect & DVASPECT_CONTENT) == 0 ||
2014 pFE->lindex != -1 ||
2015 (pFE->tymed & TYMED_HGLOBAL) == 0
2017 //Platform::DebugPrintf("DOB QueryGetData No %x\n",pFE->cfFormat);
2018 //return DATA_E_FORMATETC;
2019 return S_FALSE;
2021 //Platform::DebugPrintf("DOB QueryGetData OK %x\n",pFE->cfFormat);
2022 return S_OK;
2025 STDMETHODIMP DataObject_GetCanonicalFormatEtc(DataObject *pd, FORMATETC *, FORMATETC *pFEOut) {
2026 //Platform::DebugPrintf("DOB GetCanon\n");
2027 if (pd->sci->IsUnicodeMode())
2028 pFEOut->cfFormat = CF_UNICODETEXT;
2029 else
2030 pFEOut->cfFormat = CF_TEXT;
2031 pFEOut->ptd = 0;
2032 pFEOut->dwAspect = DVASPECT_CONTENT;
2033 pFEOut->lindex = -1;
2034 pFEOut->tymed = TYMED_HGLOBAL;
2035 return S_OK;
2038 STDMETHODIMP DataObject_SetData(DataObject *, FORMATETC *, STGMEDIUM *, BOOL) {
2039 //Platform::DebugPrintf("DOB SetData\n");
2040 return E_FAIL;
2043 STDMETHODIMP DataObject_EnumFormatEtc(DataObject *pd, DWORD dwDirection, IEnumFORMATETC **ppEnum) {
2044 try {
2045 //Platform::DebugPrintf("DOB EnumFormatEtc %d\n", dwDirection);
2046 if (dwDirection != DATADIR_GET) {
2047 *ppEnum = 0;
2048 return E_FAIL;
2050 FormatEnumerator *pfe;
2051 if (pd->sci->IsUnicodeMode()) {
2052 CLIPFORMAT formats[] = {CF_UNICODETEXT, CF_TEXT};
2053 pfe = new FormatEnumerator(0, formats, 2);
2054 } else {
2055 CLIPFORMAT formats[] = {CF_TEXT};
2056 pfe = new FormatEnumerator(0, formats, 1);
2058 return FormatEnumerator_QueryInterface(pfe, IID_IEnumFORMATETC,
2059 reinterpret_cast<void **>(ppEnum));
2060 } catch (std::bad_alloc &) {
2061 pd->sci->errorStatus = SC_STATUS_BADALLOC;
2062 return E_OUTOFMEMORY;
2063 } catch (...) {
2064 pd->sci->errorStatus = SC_STATUS_FAILURE;
2065 return E_FAIL;
2069 STDMETHODIMP DataObject_DAdvise(DataObject *, FORMATETC *, DWORD, IAdviseSink *, PDWORD) {
2070 //Platform::DebugPrintf("DOB DAdvise\n");
2071 return E_FAIL;
2074 STDMETHODIMP DataObject_DUnadvise(DataObject *, DWORD) {
2075 //Platform::DebugPrintf("DOB DUnadvise\n");
2076 return E_FAIL;
2079 STDMETHODIMP DataObject_EnumDAdvise(DataObject *, IEnumSTATDATA **) {
2080 //Platform::DebugPrintf("DOB EnumDAdvise\n");
2081 return E_FAIL;
2084 static VFunction *vtDataObject[] = {
2085 (VFunction *)(DataObject_QueryInterface),
2086 (VFunction *)(DataObject_AddRef),
2087 (VFunction *)(DataObject_Release),
2088 (VFunction *)(DataObject_GetData),
2089 (VFunction *)(DataObject_GetDataHere),
2090 (VFunction *)(DataObject_QueryGetData),
2091 (VFunction *)(DataObject_GetCanonicalFormatEtc),
2092 (VFunction *)(DataObject_SetData),
2093 (VFunction *)(DataObject_EnumFormatEtc),
2094 (VFunction *)(DataObject_DAdvise),
2095 (VFunction *)(DataObject_DUnadvise),
2096 (VFunction *)(DataObject_EnumDAdvise)
2099 DataObject::DataObject() {
2100 vtbl = vtDataObject;
2101 sci = 0;
2104 /// Implement IUnknown
2105 STDMETHODIMP DropTarget_QueryInterface(DropTarget *dt, REFIID riid, PVOID *ppv) {
2106 //Platform::DebugPrintf("DT QI %x\n", dt);
2107 return dt->sci->QueryInterface(riid, ppv);
2109 STDMETHODIMP_(ULONG)DropTarget_AddRef(DropTarget *dt) {
2110 return dt->sci->AddRef();
2112 STDMETHODIMP_(ULONG)DropTarget_Release(DropTarget *dt) {
2113 return dt->sci->Release();
2116 /// Implement IDropTarget by forwarding to Scintilla
2117 STDMETHODIMP DropTarget_DragEnter(DropTarget *dt, LPDATAOBJECT pIDataSource, DWORD grfKeyState,
2118 POINTL pt, PDWORD pdwEffect) {
2119 try {
2120 return dt->sci->DragEnter(pIDataSource, grfKeyState, pt, pdwEffect);
2121 } catch (...) {
2122 dt->sci->errorStatus = SC_STATUS_FAILURE;
2124 return E_FAIL;
2126 STDMETHODIMP DropTarget_DragOver(DropTarget *dt, DWORD grfKeyState, POINTL pt, PDWORD pdwEffect) {
2127 try {
2128 return dt->sci->DragOver(grfKeyState, pt, pdwEffect);
2129 } catch (...) {
2130 dt->sci->errorStatus = SC_STATUS_FAILURE;
2132 return E_FAIL;
2134 STDMETHODIMP DropTarget_DragLeave(DropTarget *dt) {
2135 try {
2136 return dt->sci->DragLeave();
2137 } catch (...) {
2138 dt->sci->errorStatus = SC_STATUS_FAILURE;
2140 return E_FAIL;
2142 STDMETHODIMP DropTarget_Drop(DropTarget *dt, LPDATAOBJECT pIDataSource, DWORD grfKeyState,
2143 POINTL pt, PDWORD pdwEffect) {
2144 try {
2145 return dt->sci->Drop(pIDataSource, grfKeyState, pt, pdwEffect);
2146 } catch (...) {
2147 dt->sci->errorStatus = SC_STATUS_FAILURE;
2149 return E_FAIL;
2152 static VFunction *vtDropTarget[] = {
2153 (VFunction *)(DropTarget_QueryInterface),
2154 (VFunction *)(DropTarget_AddRef),
2155 (VFunction *)(DropTarget_Release),
2156 (VFunction *)(DropTarget_DragEnter),
2157 (VFunction *)(DropTarget_DragOver),
2158 (VFunction *)(DropTarget_DragLeave),
2159 (VFunction *)(DropTarget_Drop)
2162 DropTarget::DropTarget() {
2163 vtbl = vtDropTarget;
2164 sci = 0;
2168 * DBCS: support Input Method Editor (IME).
2169 * Called when IME Window opened.
2171 void ScintillaWin::ImeStartComposition() {
2172 #ifndef __DMC__
2173 // Digital Mars compiler does not include Imm library
2174 if (caret.active) {
2175 // Move IME Window to current caret position
2176 HIMC hIMC = ::ImmGetContext(MainHWND());
2177 Point pos = PointMainCaret();
2178 COMPOSITIONFORM CompForm;
2179 CompForm.dwStyle = CFS_POINT;
2180 CompForm.ptCurrentPos.x = pos.x;
2181 CompForm.ptCurrentPos.y = pos.y;
2183 ::ImmSetCompositionWindow(hIMC, &CompForm);
2185 // Set font of IME window to same as surrounded text.
2186 if (stylesValid) {
2187 // Since the style creation code has been made platform independent,
2188 // The logfont for the IME is recreated here.
2189 int styleHere = (pdoc->StyleAt(sel.MainCaret())) & 31;
2190 LOGFONTA lf = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ""};
2191 int sizeZoomed = vs.styles[styleHere].size + vs.zoomLevel * SC_FONT_SIZE_MULTIPLIER;
2192 if (sizeZoomed <= 2 * SC_FONT_SIZE_MULTIPLIER) // Hangs if sizeZoomed <= 1
2193 sizeZoomed = 2 * SC_FONT_SIZE_MULTIPLIER;
2194 AutoSurface surface(this);
2195 int deviceHeight = sizeZoomed;
2196 if (surface) {
2197 deviceHeight = (sizeZoomed * surface->LogPixelsY()) / 72;
2199 // The negative is to allow for leading
2200 lf.lfHeight = -(abs(deviceHeight / SC_FONT_SIZE_MULTIPLIER));
2201 lf.lfWeight = vs.styles[styleHere].weight;
2202 lf.lfItalic = static_cast<BYTE>(vs.styles[styleHere].italic ? 1 : 0);
2203 lf.lfCharSet = DEFAULT_CHARSET;
2204 lf.lfFaceName[0] = '\0';
2205 if (vs.styles[styleHere].fontName)
2206 strcpy(lf.lfFaceName, vs.styles[styleHere].fontName);
2208 ::ImmSetCompositionFontA(hIMC, &lf);
2210 ::ImmReleaseContext(MainHWND(), hIMC);
2211 // Caret is displayed in IME window. So, caret in Scintilla is useless.
2212 DropCaret();
2214 #endif
2217 /** Called when IME Window closed. */
2218 void ScintillaWin::ImeEndComposition() {
2219 ShowCaretAtCurrentPosition();
2222 void ScintillaWin::AddCharBytes(char b0, char b1) {
2224 int inputCodePage = InputCodePage();
2225 if (inputCodePage && IsUnicodeMode()) {
2226 char utfval[4] = "\0\0\0";
2227 char ansiChars[3];
2228 wchar_t wcs[2];
2229 if (b0) { // Two bytes from IME
2230 ansiChars[0] = b0;
2231 ansiChars[1] = b1;
2232 ansiChars[2] = '\0';
2233 ::MultiByteToWideChar(inputCodePage, 0, ansiChars, 2, wcs, 1);
2234 } else {
2235 ansiChars[0] = b1;
2236 ansiChars[1] = '\0';
2237 ::MultiByteToWideChar(inputCodePage, 0, ansiChars, 1, wcs, 1);
2239 unsigned int len = UTF8Length(wcs, 1);
2240 UTF8FromUTF16(wcs, 1, utfval, len);
2241 utfval[len] = '\0';
2242 AddCharUTF(utfval, len ? len : 1);
2243 } else if (b0) {
2244 char dbcsChars[3];
2245 dbcsChars[0] = b0;
2246 dbcsChars[1] = b1;
2247 dbcsChars[2] = '\0';
2248 AddCharUTF(dbcsChars, 2, true);
2249 } else {
2250 AddChar(b1);
2254 void ScintillaWin::GetIntelliMouseParameters() {
2255 // This retrieves the number of lines per scroll as configured inthe Mouse Properties sheet in Control Panel
2256 ::SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, &linesPerScroll, 0);
2259 void ScintillaWin::CopyToClipboard(const SelectionText &selectedText) {
2260 if (!::OpenClipboard(MainHWND()))
2261 return;
2262 ::EmptyClipboard();
2264 GlobalMemory uniText;
2266 // Default Scintilla behaviour in Unicode mode
2267 if (IsUnicodeMode()) {
2268 int uchars = UTF16Length(selectedText.s, selectedText.len);
2269 uniText.Allocate(2 * uchars);
2270 if (uniText) {
2271 UTF16FromUTF8(selectedText.s, selectedText.len, static_cast<wchar_t *>(uniText.ptr), uchars);
2273 } else {
2274 // Not Unicode mode
2275 // Convert to Unicode using the current Scintilla code page
2276 UINT cpSrc = CodePageFromCharSet(
2277 selectedText.characterSet, selectedText.codePage);
2278 int uLen = ::MultiByteToWideChar(cpSrc, 0, selectedText.s, selectedText.len, 0, 0);
2279 uniText.Allocate(2 * uLen);
2280 if (uniText) {
2281 ::MultiByteToWideChar(cpSrc, 0, selectedText.s, selectedText.len,
2282 static_cast<wchar_t *>(uniText.ptr), uLen);
2286 if (uniText) {
2287 if (!IsNT()) {
2288 // Copy ANSI text to clipboard on Windows 9x
2289 // Convert from Unicode text, so other ANSI programs can
2290 // paste the text
2291 // Windows NT, 2k, XP automatically generates CF_TEXT
2292 GlobalMemory ansiText;
2293 ansiText.Allocate(selectedText.len);
2294 if (ansiText) {
2295 ::WideCharToMultiByte(CP_ACP, 0, static_cast<wchar_t *>(uniText.ptr), -1,
2296 static_cast<char *>(ansiText.ptr), selectedText.len, NULL, NULL);
2297 ansiText.SetClip(CF_TEXT);
2300 uniText.SetClip(CF_UNICODETEXT);
2301 } else {
2302 // There was a failure - try to copy at least ANSI text
2303 GlobalMemory ansiText;
2304 ansiText.Allocate(selectedText.len);
2305 if (ansiText) {
2306 memcpy(static_cast<char *>(ansiText.ptr), selectedText.s, selectedText.len);
2307 ansiText.SetClip(CF_TEXT);
2311 if (selectedText.rectangular) {
2312 ::SetClipboardData(cfColumnSelect, 0);
2315 if (selectedText.lineCopy) {
2316 ::SetClipboardData(cfLineSelect, 0);
2319 ::CloseClipboard();
2322 void ScintillaWin::ScrollMessage(WPARAM wParam) {
2323 //DWORD dwStart = timeGetTime();
2324 //Platform::DebugPrintf("Scroll %x %d\n", wParam, lParam);
2326 SCROLLINFO sci;
2327 memset(&sci, 0, sizeof(sci));
2328 sci.cbSize = sizeof(sci);
2329 sci.fMask = SIF_ALL;
2331 GetScrollInfo(SB_VERT, &sci);
2333 //Platform::DebugPrintf("ScrollInfo %d mask=%x min=%d max=%d page=%d pos=%d track=%d\n", b,sci.fMask,
2334 //sci.nMin, sci.nMax, sci.nPage, sci.nPos, sci.nTrackPos);
2336 int topLineNew = topLine;
2337 switch (LoWord(wParam)) {
2338 case SB_LINEUP:
2339 topLineNew -= 1;
2340 break;
2341 case SB_LINEDOWN:
2342 topLineNew += 1;
2343 break;
2344 case SB_PAGEUP:
2345 topLineNew -= LinesToScroll(); break;
2346 case SB_PAGEDOWN: topLineNew += LinesToScroll(); break;
2347 case SB_TOP: topLineNew = 0; break;
2348 case SB_BOTTOM: topLineNew = MaxScrollPos(); break;
2349 case SB_THUMBPOSITION: topLineNew = sci.nTrackPos; break;
2350 case SB_THUMBTRACK: topLineNew = sci.nTrackPos; break;
2352 ScrollTo(topLineNew);
2355 void ScintillaWin::HorizontalScrollMessage(WPARAM wParam) {
2356 int xPos = xOffset;
2357 PRectangle rcText = GetTextRectangle();
2358 int pageWidth = rcText.Width() * 2 / 3;
2359 switch (LoWord(wParam)) {
2360 case SB_LINEUP:
2361 xPos -= 20;
2362 break;
2363 case SB_LINEDOWN: // May move past the logical end
2364 xPos += 20;
2365 break;
2366 case SB_PAGEUP:
2367 xPos -= pageWidth;
2368 break;
2369 case SB_PAGEDOWN:
2370 xPos += pageWidth;
2371 if (xPos > scrollWidth - rcText.Width()) { // Hit the end exactly
2372 xPos = scrollWidth - rcText.Width();
2374 break;
2375 case SB_TOP:
2376 xPos = 0;
2377 break;
2378 case SB_BOTTOM:
2379 xPos = scrollWidth;
2380 break;
2381 case SB_THUMBPOSITION:
2382 case SB_THUMBTRACK: {
2383 // 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 =]
2384 SCROLLINFO si;
2385 si.cbSize = sizeof(si);
2386 si.fMask = SIF_TRACKPOS;
2387 if (GetScrollInfo(SB_HORZ, &si)) {
2388 xPos = si.nTrackPos;
2391 break;
2393 HorizontalScrollTo(xPos);
2397 * Redraw all of text area.
2398 * This paint will not be abandoned.
2400 void ScintillaWin::FullPaint() {
2401 if (technology == SC_TECHNOLOGY_DEFAULT) {
2402 HDC hdc = ::GetDC(MainHWND());
2403 FullPaintDC(hdc);
2404 ::ReleaseDC(MainHWND(), hdc);
2405 } else {
2406 FullPaintDC(0);
2411 * Redraw all of text area on the specified DC.
2412 * This paint will not be abandoned.
2414 void ScintillaWin::FullPaintDC(HDC hdc) {
2415 paintState = painting;
2416 rcPaint = GetClientRectangle();
2417 paintingAllText = true;
2418 if (technology == SC_TECHNOLOGY_DEFAULT) {
2419 AutoSurface surfaceWindow(hdc, this);
2420 if (surfaceWindow) {
2421 Paint(surfaceWindow, rcPaint);
2422 surfaceWindow->Release();
2424 } else {
2425 #if defined(USE_D2D)
2426 EnsureRenderTarget();
2427 AutoSurface surfaceWindow(pRenderTarget, this);
2428 if (surfaceWindow) {
2429 pRenderTarget->BeginDraw();
2430 Paint(surfaceWindow, rcPaint);
2431 surfaceWindow->Release();
2432 HRESULT hr = pRenderTarget->EndDraw();
2433 if (hr == D2DERR_RECREATE_TARGET) {
2434 DropRenderTarget();
2437 #endif
2439 paintState = notPainting;
2442 static bool CompareDevCap(HDC hdc, HDC hOtherDC, int nIndex) {
2443 return ::GetDeviceCaps(hdc, nIndex) == ::GetDeviceCaps(hOtherDC, nIndex);
2446 bool ScintillaWin::IsCompatibleDC(HDC hOtherDC) {
2447 HDC hdc = ::GetDC(MainHWND());
2448 bool isCompatible =
2449 CompareDevCap(hdc, hOtherDC, TECHNOLOGY) &&
2450 CompareDevCap(hdc, hOtherDC, LOGPIXELSY) &&
2451 CompareDevCap(hdc, hOtherDC, LOGPIXELSX) &&
2452 CompareDevCap(hdc, hOtherDC, BITSPIXEL) &&
2453 CompareDevCap(hdc, hOtherDC, PLANES);
2454 ::ReleaseDC(MainHWND(), hdc);
2455 return isCompatible;
2458 DWORD ScintillaWin::EffectFromState(DWORD grfKeyState) {
2459 // These are the Wordpad semantics.
2460 DWORD dwEffect;
2461 if (inDragDrop == ddDragging) // Internal defaults to move
2462 dwEffect = DROPEFFECT_MOVE;
2463 else
2464 dwEffect = DROPEFFECT_COPY;
2465 if (grfKeyState & MK_ALT)
2466 dwEffect = DROPEFFECT_MOVE;
2467 if (grfKeyState & MK_CONTROL)
2468 dwEffect = DROPEFFECT_COPY;
2469 return dwEffect;
2472 /// Implement IUnknown
2473 STDMETHODIMP ScintillaWin::QueryInterface(REFIID riid, PVOID *ppv) {
2474 *ppv = NULL;
2475 if (riid == IID_IUnknown)
2476 *ppv = reinterpret_cast<IDropTarget *>(&dt);
2477 if (riid == IID_IDropSource)
2478 *ppv = reinterpret_cast<IDropSource *>(&ds);
2479 if (riid == IID_IDropTarget)
2480 *ppv = reinterpret_cast<IDropTarget *>(&dt);
2481 if (riid == IID_IDataObject)
2482 *ppv = reinterpret_cast<IDataObject *>(&dob);
2483 if (!*ppv)
2484 return E_NOINTERFACE;
2485 return S_OK;
2488 STDMETHODIMP_(ULONG) ScintillaWin::AddRef() {
2489 return 1;
2492 STDMETHODIMP_(ULONG) ScintillaWin::Release() {
2493 return 1;
2496 /// Implement IDropTarget
2497 STDMETHODIMP ScintillaWin::DragEnter(LPDATAOBJECT pIDataSource, DWORD grfKeyState,
2498 POINTL, PDWORD pdwEffect) {
2499 if (pIDataSource == NULL)
2500 return E_POINTER;
2501 FORMATETC fmtu = {CF_UNICODETEXT, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
2502 HRESULT hrHasUText = pIDataSource->QueryGetData(&fmtu);
2503 hasOKText = (hrHasUText == S_OK);
2504 if (!hasOKText) {
2505 FORMATETC fmte = {CF_TEXT, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
2506 HRESULT hrHasText = pIDataSource->QueryGetData(&fmte);
2507 hasOKText = (hrHasText == S_OK);
2509 if (!hasOKText) {
2510 *pdwEffect = DROPEFFECT_NONE;
2511 return S_OK;
2514 *pdwEffect = EffectFromState(grfKeyState);
2515 return S_OK;
2518 STDMETHODIMP ScintillaWin::DragOver(DWORD grfKeyState, POINTL pt, PDWORD pdwEffect) {
2519 try {
2520 if (!hasOKText || pdoc->IsReadOnly()) {
2521 *pdwEffect = DROPEFFECT_NONE;
2522 return S_OK;
2525 *pdwEffect = EffectFromState(grfKeyState);
2527 // Update the cursor.
2528 POINT rpt = {pt.x, pt.y};
2529 ::ScreenToClient(MainHWND(), &rpt);
2530 SetDragPosition(SPositionFromLocation(Point(rpt.x, rpt.y), false, false, UserVirtualSpace()));
2532 return S_OK;
2533 } catch (...) {
2534 errorStatus = SC_STATUS_FAILURE;
2536 return E_FAIL;
2539 STDMETHODIMP ScintillaWin::DragLeave() {
2540 try {
2541 SetDragPosition(SelectionPosition(invalidPosition));
2542 return S_OK;
2543 } catch (...) {
2544 errorStatus = SC_STATUS_FAILURE;
2546 return E_FAIL;
2549 STDMETHODIMP ScintillaWin::Drop(LPDATAOBJECT pIDataSource, DWORD grfKeyState,
2550 POINTL pt, PDWORD pdwEffect) {
2551 try {
2552 *pdwEffect = EffectFromState(grfKeyState);
2554 if (pIDataSource == NULL)
2555 return E_POINTER;
2557 SetDragPosition(SelectionPosition(invalidPosition));
2559 STGMEDIUM medium = {0, {0}, 0};
2561 char *data = 0;
2562 bool dataAllocated = false;
2564 FORMATETC fmtu = {CF_UNICODETEXT, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
2565 HRESULT hr = pIDataSource->GetData(&fmtu, &medium);
2566 if (SUCCEEDED(hr) && medium.hGlobal) {
2567 wchar_t *udata = static_cast<wchar_t *>(::GlobalLock(medium.hGlobal));
2568 if (IsUnicodeMode()) {
2569 int tlen = ::GlobalSize(medium.hGlobal);
2570 // Convert UTF-16 to UTF-8
2571 int dataLen = UTF8Length(udata, tlen/2);
2572 data = new char[dataLen+1];
2573 UTF8FromUTF16(udata, tlen/2, data, dataLen);
2574 dataAllocated = true;
2575 } else {
2576 // Convert UTF-16 to ANSI
2578 // Default Scintilla behavior in Unicode mode
2579 // CF_UNICODETEXT available, but not in Unicode mode
2580 // Convert from Unicode to current Scintilla code page
2581 UINT cpDest = CodePageOfDocument();
2582 int tlen = ::WideCharToMultiByte(cpDest, 0, udata, -1,
2583 NULL, 0, NULL, NULL) - 1; // subtract 0 terminator
2584 data = new char[tlen + 1];
2585 memset(data, 0, (tlen+1));
2586 ::WideCharToMultiByte(cpDest, 0, udata, -1,
2587 data, tlen + 1, NULL, NULL);
2588 dataAllocated = true;
2592 if (!data) {
2593 FORMATETC fmte = {CF_TEXT, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
2594 hr = pIDataSource->GetData(&fmte, &medium);
2595 if (SUCCEEDED(hr) && medium.hGlobal) {
2596 data = static_cast<char *>(::GlobalLock(medium.hGlobal));
2600 if (data && convertPastes) {
2601 // Convert line endings of the drop into our local line-endings mode
2602 int len = static_cast<int>(strlen(data));
2603 char *convertedText = Document::TransformLineEnds(&len, data, len, pdoc->eolMode);
2604 if (dataAllocated)
2605 delete []data;
2606 data = convertedText;
2607 dataAllocated = true;
2610 if (!data) {
2611 //Platform::DebugPrintf("Bad data format: 0x%x\n", hres);
2612 return hr;
2615 FORMATETC fmtr = {cfColumnSelect, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
2616 HRESULT hrRectangular = pIDataSource->QueryGetData(&fmtr);
2618 POINT rpt = {pt.x, pt.y};
2619 ::ScreenToClient(MainHWND(), &rpt);
2620 SelectionPosition movePos = SPositionFromLocation(Point(rpt.x, rpt.y), false, false, UserVirtualSpace());
2622 DropAt(movePos, data, *pdwEffect == DROPEFFECT_MOVE, hrRectangular == S_OK);
2624 ::GlobalUnlock(medium.hGlobal);
2626 // Free data
2627 if (medium.pUnkForRelease != NULL)
2628 medium.pUnkForRelease->Release();
2629 else
2630 ::GlobalFree(medium.hGlobal);
2632 if (dataAllocated)
2633 delete []data;
2635 return S_OK;
2636 } catch (...) {
2637 errorStatus = SC_STATUS_FAILURE;
2639 return E_FAIL;
2642 /// Implement important part of IDataObject
2643 STDMETHODIMP ScintillaWin::GetData(FORMATETC *pFEIn, STGMEDIUM *pSTM) {
2644 bool formatOK = (pFEIn->cfFormat == CF_TEXT) ||
2645 ((pFEIn->cfFormat == CF_UNICODETEXT) && IsUnicodeMode());
2646 if (!formatOK ||
2647 pFEIn->ptd != 0 ||
2648 (pFEIn->dwAspect & DVASPECT_CONTENT) == 0 ||
2649 pFEIn->lindex != -1 ||
2650 (pFEIn->tymed & TYMED_HGLOBAL) == 0
2652 //Platform::DebugPrintf("DOB GetData No %d %x %x fmt=%x\n", lenDrag, pFEIn, pSTM, pFEIn->cfFormat);
2653 return DATA_E_FORMATETC;
2655 pSTM->tymed = TYMED_HGLOBAL;
2656 //Platform::DebugPrintf("DOB GetData OK %d %x %x\n", lenDrag, pFEIn, pSTM);
2658 GlobalMemory text;
2659 if (pFEIn->cfFormat == CF_UNICODETEXT) {
2660 int uchars = UTF16Length(drag.s, drag.len);
2661 text.Allocate(2 * uchars);
2662 if (text) {
2663 UTF16FromUTF8(drag.s, drag.len, static_cast<wchar_t *>(text.ptr), uchars);
2665 } else {
2666 text.Allocate(drag.len);
2667 if (text) {
2668 memcpy(static_cast<char *>(text.ptr), drag.s, drag.len);
2671 pSTM->hGlobal = text ? text.Unlock() : 0;
2672 pSTM->pUnkForRelease = 0;
2673 return S_OK;
2676 bool ScintillaWin::Register(HINSTANCE hInstance_) {
2678 hInstance = hInstance_;
2679 bool result;
2681 // Register the Scintilla class
2682 if (IsNT()) {
2684 // Register Scintilla as a wide character window
2685 WNDCLASSEXW wndclass;
2686 wndclass.cbSize = sizeof(wndclass);
2687 wndclass.style = CS_GLOBALCLASS | CS_HREDRAW | CS_VREDRAW;
2688 wndclass.lpfnWndProc = ScintillaWin::SWndProc;
2689 wndclass.cbClsExtra = 0;
2690 wndclass.cbWndExtra = sizeof(ScintillaWin *);
2691 wndclass.hInstance = hInstance;
2692 wndclass.hIcon = NULL;
2693 wndclass.hCursor = NULL;
2694 wndclass.hbrBackground = NULL;
2695 wndclass.lpszMenuName = NULL;
2696 wndclass.lpszClassName = L"Scintilla";
2697 wndclass.hIconSm = 0;
2698 result = ::RegisterClassExW(&wndclass) != 0;
2699 } else {
2701 // Register Scintilla as a normal character window
2702 WNDCLASSEX wndclass;
2703 wndclass.cbSize = sizeof(wndclass);
2704 wndclass.style = CS_GLOBALCLASS | CS_HREDRAW | CS_VREDRAW;
2705 wndclass.lpfnWndProc = ScintillaWin::SWndProc;
2706 wndclass.cbClsExtra = 0;
2707 wndclass.cbWndExtra = sizeof(ScintillaWin *);
2708 wndclass.hInstance = hInstance;
2709 wndclass.hIcon = NULL;
2710 wndclass.hCursor = NULL;
2711 wndclass.hbrBackground = NULL;
2712 wndclass.lpszMenuName = NULL;
2713 wndclass.lpszClassName = scintillaClassName;
2714 wndclass.hIconSm = 0;
2715 result = ::RegisterClassEx(&wndclass) != 0;
2718 if (result) {
2719 // Register the CallTip class
2720 WNDCLASSEX wndclassc;
2721 wndclassc.cbSize = sizeof(wndclassc);
2722 wndclassc.style = CS_GLOBALCLASS | CS_HREDRAW | CS_VREDRAW;
2723 wndclassc.cbClsExtra = 0;
2724 wndclassc.cbWndExtra = sizeof(ScintillaWin *);
2725 wndclassc.hInstance = hInstance;
2726 wndclassc.hIcon = NULL;
2727 wndclassc.hbrBackground = NULL;
2728 wndclassc.lpszMenuName = NULL;
2729 wndclassc.lpfnWndProc = ScintillaWin::CTWndProc;
2730 wndclassc.hCursor = ::LoadCursor(NULL, IDC_ARROW);
2731 wndclassc.lpszClassName = callClassName;
2732 wndclassc.hIconSm = 0;
2734 result = ::RegisterClassEx(&wndclassc) != 0;
2737 return result;
2740 bool ScintillaWin::Unregister() {
2741 bool result = ::UnregisterClass(scintillaClassName, hInstance) != 0;
2742 if (::UnregisterClass(callClassName, hInstance) == 0)
2743 result = false;
2744 return result;
2747 bool ScintillaWin::HasCaretSizeChanged() {
2748 if (
2749 ( (0 != vs.caretWidth) && (sysCaretWidth != vs.caretWidth) )
2750 || ((0 != vs.lineHeight) && (sysCaretHeight != vs.lineHeight))
2752 return true;
2754 return false;
2757 BOOL ScintillaWin::CreateSystemCaret() {
2758 sysCaretWidth = vs.caretWidth;
2759 if (0 == sysCaretWidth) {
2760 sysCaretWidth = 1;
2762 sysCaretHeight = vs.lineHeight;
2763 int bitmapSize = (((sysCaretWidth + 15) & ~15) >> 3) *
2764 sysCaretHeight;
2765 char *bits = new char[bitmapSize];
2766 memset(bits, 0, bitmapSize);
2767 sysCaretBitmap = ::CreateBitmap(sysCaretWidth, sysCaretHeight, 1,
2768 1, reinterpret_cast<BYTE *>(bits));
2769 delete []bits;
2770 BOOL retval = ::CreateCaret(
2771 MainHWND(), sysCaretBitmap,
2772 sysCaretWidth, sysCaretHeight);
2773 ::ShowCaret(MainHWND());
2774 return retval;
2777 BOOL ScintillaWin::DestroySystemCaret() {
2778 ::HideCaret(MainHWND());
2779 BOOL retval = ::DestroyCaret();
2780 if (sysCaretBitmap) {
2781 ::DeleteObject(sysCaretBitmap);
2782 sysCaretBitmap = 0;
2784 return retval;
2787 sptr_t PASCAL ScintillaWin::CTWndProc(
2788 HWND hWnd, UINT iMessage, WPARAM wParam, sptr_t lParam) {
2789 // Find C++ object associated with window.
2790 ScintillaWin *sciThis = reinterpret_cast<ScintillaWin *>(PointerFromWindow(hWnd));
2791 try {
2792 // ctp will be zero if WM_CREATE not seen yet
2793 if (sciThis == 0) {
2794 if (iMessage == WM_CREATE) {
2795 // Associate CallTip object with window
2796 CREATESTRUCT *pCreate = reinterpret_cast<CREATESTRUCT *>(lParam);
2797 SetWindowPointer(hWnd, pCreate->lpCreateParams);
2798 return 0;
2799 } else {
2800 return ::DefWindowProc(hWnd, iMessage, wParam, lParam);
2802 } else {
2803 if (iMessage == WM_NCDESTROY) {
2804 ::SetWindowLong(hWnd, 0, 0);
2805 return ::DefWindowProc(hWnd, iMessage, wParam, lParam);
2806 } else if (iMessage == WM_PAINT) {
2807 PAINTSTRUCT ps;
2808 ::BeginPaint(hWnd, &ps);
2809 Surface *surfaceWindow = Surface::Allocate(sciThis->technology);
2810 if (surfaceWindow) {
2811 #if defined(USE_D2D)
2812 ID2D1HwndRenderTarget *pCTRenderTarget = 0;
2813 #endif
2814 RECT rc;
2815 GetClientRect(hWnd, &rc);
2816 // Create a Direct2D render target.
2817 if (sciThis->technology == SC_TECHNOLOGY_DEFAULT) {
2818 surfaceWindow->Init(ps.hdc, hWnd);
2819 } else {
2820 #if defined(USE_D2D)
2821 pD2DFactory->CreateHwndRenderTarget(
2822 D2D1::RenderTargetProperties(),
2823 D2D1::HwndRenderTargetProperties(hWnd, D2D1::SizeU(rc.right - rc.left, rc.bottom - rc.top)),
2824 &pCTRenderTarget);
2825 surfaceWindow->Init(pCTRenderTarget, hWnd);
2826 pCTRenderTarget->BeginDraw();
2827 #endif
2829 surfaceWindow->SetUnicodeMode(SC_CP_UTF8 == sciThis->ct.codePage);
2830 surfaceWindow->SetDBCSMode(sciThis->ct.codePage);
2831 sciThis->ct.PaintCT(surfaceWindow);
2832 #if defined(USE_D2D)
2833 if (pCTRenderTarget)
2834 pCTRenderTarget->EndDraw();
2835 #endif
2836 surfaceWindow->Release();
2837 delete surfaceWindow;
2838 #if defined(USE_D2D)
2839 if (pCTRenderTarget)
2840 pCTRenderTarget->Release();
2841 #endif
2843 ::EndPaint(hWnd, &ps);
2844 return 0;
2845 } else if ((iMessage == WM_NCLBUTTONDOWN) || (iMessage == WM_NCLBUTTONDBLCLK)) {
2846 POINT pt;
2847 pt.x = static_cast<short>(LOWORD(lParam));
2848 pt.y = static_cast<short>(HIWORD(lParam));
2849 ScreenToClient(hWnd, &pt);
2850 sciThis->ct.MouseClick(Point(pt.x, pt.y));
2851 sciThis->CallTipClick();
2852 return 0;
2853 } else if (iMessage == WM_LBUTTONDOWN) {
2854 // This does not fire due to the hit test code
2855 sciThis->ct.MouseClick(Point::FromLong(lParam));
2856 sciThis->CallTipClick();
2857 return 0;
2858 } else if (iMessage == WM_SETCURSOR) {
2859 ::SetCursor(::LoadCursor(NULL, IDC_ARROW));
2860 return 0;
2861 } else if (iMessage == WM_NCHITTEST) {
2862 return HTCAPTION;
2863 } else {
2864 return ::DefWindowProc(hWnd, iMessage, wParam, lParam);
2867 } catch (...) {
2868 sciThis->errorStatus = SC_STATUS_FAILURE;
2870 return ::DefWindowProc(hWnd, iMessage, wParam, lParam);
2873 sptr_t ScintillaWin::DirectFunction(
2874 ScintillaWin *sci, UINT iMessage, uptr_t wParam, sptr_t lParam) {
2875 PLATFORM_ASSERT(::GetCurrentThreadId() == ::GetWindowThreadProcessId(sci->MainHWND(), NULL));
2876 return sci->WndProc(iMessage, wParam, lParam);
2879 extern "C"
2880 #ifndef STATIC_BUILD
2881 __declspec(dllexport)
2882 #endif
2883 sptr_t __stdcall Scintilla_DirectFunction(
2884 ScintillaWin *sci, UINT iMessage, uptr_t wParam, sptr_t lParam) {
2885 return sci->WndProc(iMessage, wParam, lParam);
2888 sptr_t PASCAL ScintillaWin::SWndProc(
2889 HWND hWnd, UINT iMessage, WPARAM wParam, sptr_t lParam) {
2890 //Platform::DebugPrintf("S W:%x M:%x WP:%x L:%x\n", hWnd, iMessage, wParam, lParam);
2892 // Find C++ object associated with window.
2893 ScintillaWin *sci = reinterpret_cast<ScintillaWin *>(PointerFromWindow(hWnd));
2894 // sci will be zero if WM_CREATE not seen yet
2895 if (sci == 0) {
2896 try {
2897 if (iMessage == WM_CREATE) {
2898 // Create C++ object associated with window
2899 sci = new ScintillaWin(hWnd);
2900 SetWindowPointer(hWnd, sci);
2901 return sci->WndProc(iMessage, wParam, lParam);
2903 } catch (...) {
2905 return ::DefWindowProc(hWnd, iMessage, wParam, lParam);
2906 } else {
2907 if (iMessage == WM_NCDESTROY) {
2908 try {
2909 sci->Finalise();
2910 delete sci;
2911 } catch (...) {
2913 ::SetWindowLong(hWnd, 0, 0);
2914 return ::DefWindowProc(hWnd, iMessage, wParam, lParam);
2915 } else {
2916 return sci->WndProc(iMessage, wParam, lParam);
2921 // This function is externally visible so it can be called from container when building statically.
2922 // Must be called once only.
2923 int Scintilla_RegisterClasses(void *hInstance) {
2924 Platform_Initialise(hInstance);
2925 bool result = ScintillaWin::Register(reinterpret_cast<HINSTANCE>(hInstance));
2926 #ifdef SCI_LEXER
2927 Scintilla_LinkLexers();
2928 #endif
2929 return result;
2932 // This function is externally visible so it can be called from container when building statically.
2933 int Scintilla_ReleaseResources() {
2934 bool result = ScintillaWin::Unregister();
2935 Platform_Finalise();
2936 return result;
2939 #ifndef STATIC_BUILD
2940 extern "C" int APIENTRY DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID) {
2941 //Platform::DebugPrintf("Scintilla::DllMain %d %d\n", hInstance, dwReason);
2942 if (dwReason == DLL_PROCESS_ATTACH) {
2943 if (!Scintilla_RegisterClasses(hInstance))
2944 return FALSE;
2945 } else if (dwReason == DLL_PROCESS_DETACH) {
2946 Scintilla_ReleaseResources();
2948 return TRUE;
2950 #endif