Upgraded to scintilla 3.2.3
[TortoiseGit.git] / ext / scintilla / win32 / ScintillaWin.cxx
blobe43481099a3154dc95dbeaf636e26304a4fdbfb6
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 NotifyDoubleClick(Point pt, bool shift, bool ctrl, bool alt);
256 virtual CaseFolder *CaseFolderForEncoding();
257 virtual std::string CaseMapString(const std::string &s, int caseMapping);
258 virtual void Copy();
259 virtual void CopyAllowLine();
260 virtual bool CanPaste();
261 virtual void Paste();
262 virtual void CreateCallTipWindow(PRectangle rc);
263 virtual void AddToPopUp(const char *label, int cmd = 0, bool enabled = true);
264 virtual void ClaimSelection();
266 // DBCS
267 void ImeStartComposition();
268 void ImeEndComposition();
270 void AddCharBytes(char b0, char b1);
272 void GetIntelliMouseParameters();
273 virtual void CopyToClipboard(const SelectionText &selectedText);
274 void ScrollMessage(WPARAM wParam);
275 void HorizontalScrollMessage(WPARAM wParam);
276 void FullPaint();
277 void FullPaintDC(HDC dc);
278 bool IsCompatibleDC(HDC dc);
279 DWORD EffectFromState(DWORD grfKeyState);
281 virtual int SetScrollInfo(int nBar, LPCSCROLLINFO lpsi, BOOL bRedraw);
282 virtual bool GetScrollInfo(int nBar, LPSCROLLINFO lpsi);
283 void ChangeScrollPos(int barType, int pos);
285 void InsertPasteText(const char *text, int len, SelectionPosition selStart, bool isRectangular, bool isLine);
287 public:
288 // Public for benefit of Scintilla_DirectFunction
289 virtual sptr_t WndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam);
291 /// Implement IUnknown
292 STDMETHODIMP QueryInterface(REFIID riid, PVOID *ppv);
293 STDMETHODIMP_(ULONG)AddRef();
294 STDMETHODIMP_(ULONG)Release();
296 /// Implement IDropTarget
297 STDMETHODIMP DragEnter(LPDATAOBJECT pIDataSource, DWORD grfKeyState,
298 POINTL pt, PDWORD pdwEffect);
299 STDMETHODIMP DragOver(DWORD grfKeyState, POINTL pt, PDWORD pdwEffect);
300 STDMETHODIMP DragLeave();
301 STDMETHODIMP Drop(LPDATAOBJECT pIDataSource, DWORD grfKeyState,
302 POINTL pt, PDWORD pdwEffect);
304 /// Implement important part of IDataObject
305 STDMETHODIMP GetData(FORMATETC *pFEIn, STGMEDIUM *pSTM);
307 static bool Register(HINSTANCE hInstance_);
308 static bool Unregister();
310 friend class DropSource;
311 friend class DataObject;
312 friend class DropTarget;
313 bool DragIsRectangularOK(CLIPFORMAT fmt) {
314 return drag.rectangular && (fmt == cfColumnSelect);
317 private:
318 // For use in creating a system caret
319 bool HasCaretSizeChanged();
320 BOOL CreateSystemCaret();
321 BOOL DestroySystemCaret();
322 HBITMAP sysCaretBitmap;
323 int sysCaretWidth;
324 int sysCaretHeight;
325 bool keysAlwaysUnicode;
328 HINSTANCE ScintillaWin::hInstance = 0;
330 ScintillaWin::ScintillaWin(HWND hwnd) {
332 lastKeyDownConsumed = false;
334 capturedMouse = false;
335 trackedMouseLeave = false;
336 TrackMouseEventFn = 0;
338 linesPerScroll = 0;
339 wheelDelta = 0; // Wheel delta from roll
341 hRgnUpdate = 0;
343 hasOKText = false;
345 // There does not seem to be a real standard for indicating that the clipboard
346 // contains a rectangular selection, so copy Developer Studio.
347 cfColumnSelect = static_cast<CLIPFORMAT>(
348 ::RegisterClipboardFormat(TEXT("MSDEVColumnSelect")));
350 // Likewise for line-copy (copies a full line when no text is selected)
351 cfLineSelect = static_cast<CLIPFORMAT>(
352 ::RegisterClipboardFormat(TEXT("MSDEVLineSelect")));
354 hrOle = E_FAIL;
356 wMain = hwnd;
358 dob.sci = this;
359 ds.sci = this;
360 dt.sci = this;
362 sysCaretBitmap = 0;
363 sysCaretWidth = 0;
364 sysCaretHeight = 0;
366 #if defined(USE_D2D)
367 pRenderTarget = 0;
368 renderTargetValid = true;
369 #endif
371 keysAlwaysUnicode = false;
373 caret.period = ::GetCaretBlinkTime();
374 if (caret.period < 0)
375 caret.period = 0;
377 Initialise();
380 ScintillaWin::~ScintillaWin() {}
382 void ScintillaWin::Initialise() {
383 // Initialize COM. If the app has already done this it will have
384 // no effect. If the app hasnt, we really shouldnt ask them to call
385 // it just so this internal feature works.
386 hrOle = ::OleInitialize(NULL);
388 // Find TrackMouseEvent which is available on Windows > 95
389 HMODULE user32 = ::GetModuleHandle(TEXT("user32.dll"));
390 if (user32)
391 TrackMouseEventFn = (TrackMouseEventSig)::GetProcAddress(user32, "TrackMouseEvent");
392 if (TrackMouseEventFn == NULL) {
393 // Windows 95 has an emulation in comctl32.dll:_TrackMouseEvent
394 HMODULE commctrl32 = ::LoadLibrary(TEXT("comctl32.dll"));
395 if (commctrl32 != NULL) {
396 TrackMouseEventFn = (TrackMouseEventSig)
397 ::GetProcAddress(commctrl32, "_TrackMouseEvent");
402 void ScintillaWin::Finalise() {
403 ScintillaBase::Finalise();
404 SetTicking(false);
405 SetIdle(false);
406 DropRenderTarget();
407 ::RevokeDragDrop(MainHWND());
408 if (SUCCEEDED(hrOle)) {
409 ::OleUninitialize();
413 void ScintillaWin::EnsureRenderTarget() {
414 #if defined(USE_D2D)
415 if (!renderTargetValid) {
416 DropRenderTarget();
417 renderTargetValid = true;
419 if (pD2DFactory && !pRenderTarget) {
420 RECT rc;
421 HWND hw = MainHWND();
422 GetClientRect(hw, &rc);
424 D2D1_SIZE_U size = D2D1::SizeU(rc.right - rc.left, rc.bottom - rc.top);
426 // Create a Direct2D render target.
427 #if 1
428 pD2DFactory->CreateHwndRenderTarget(
429 D2D1::RenderTargetProperties(D2D1_RENDER_TARGET_TYPE_DEFAULT, D2D1::PixelFormat(), 96.0, 96.0),
430 D2D1::HwndRenderTargetProperties(hw, size),
431 &pRenderTarget);
432 #else
433 pD2DFactory->CreateHwndRenderTarget(
434 D2D1::RenderTargetProperties(
435 D2D1_RENDER_TARGET_TYPE_DEFAULT ,
436 D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_PREMULTIPLIED),
437 96.0f, 96.0f, D2D1_RENDER_TARGET_USAGE_NONE, D2D1_FEATURE_LEVEL_DEFAULT),
438 D2D1::HwndRenderTargetProperties(hw, size),
439 &pRenderTarget);
440 #endif
441 // Pixmaps were created to be compatible with previous render target so
442 // need to be recreated.
443 DropGraphics(false);
445 #endif
448 void ScintillaWin::DropRenderTarget() {
449 #if defined(USE_D2D)
450 if (pRenderTarget) {
451 pRenderTarget->Release();
452 pRenderTarget = 0;
454 #endif
457 HWND ScintillaWin::MainHWND() {
458 return reinterpret_cast<HWND>(wMain.GetID());
461 bool ScintillaWin::DragThreshold(Point ptStart, Point ptNow) {
462 int xMove = abs(ptStart.x - ptNow.x);
463 int yMove = abs(ptStart.y - ptNow.y);
464 return (xMove > ::GetSystemMetrics(SM_CXDRAG)) ||
465 (yMove > ::GetSystemMetrics(SM_CYDRAG));
468 void ScintillaWin::StartDrag() {
469 inDragDrop = ddDragging;
470 DWORD dwEffect = 0;
471 dropWentOutside = true;
472 IDataObject *pDataObject = reinterpret_cast<IDataObject *>(&dob);
473 IDropSource *pDropSource = reinterpret_cast<IDropSource *>(&ds);
474 //Platform::DebugPrintf("About to DoDragDrop %x %x\n", pDataObject, pDropSource);
475 HRESULT hr = ::DoDragDrop(
476 pDataObject,
477 pDropSource,
478 DROPEFFECT_COPY | DROPEFFECT_MOVE, &dwEffect);
479 //Platform::DebugPrintf("DoDragDrop = %x\n", hr);
480 if (SUCCEEDED(hr)) {
481 if ((hr == DRAGDROP_S_DROP) && (dwEffect == DROPEFFECT_MOVE) && dropWentOutside) {
482 // Remove dragged out text
483 ClearSelection();
486 inDragDrop = ddNone;
487 SetDragPosition(SelectionPosition(invalidPosition));
490 // Avoid warnings everywhere for old style casts by concentrating them here
491 static WORD LoWord(DWORD l) {
492 return LOWORD(l);
495 static WORD HiWord(DWORD l) {
496 return HIWORD(l);
499 static int InputCodePage() {
500 HKL inputLocale = ::GetKeyboardLayout(0);
501 LANGID inputLang = LOWORD(inputLocale);
502 char sCodePage[10];
503 int res = ::GetLocaleInfoA(MAKELCID(inputLang, SORT_DEFAULT),
504 LOCALE_IDEFAULTANSICODEPAGE, sCodePage, sizeof(sCodePage));
505 if (!res)
506 return 0;
507 return atoi(sCodePage);
510 #ifndef VK_OEM_2
511 static const int VK_OEM_2=0xbf;
512 static const int VK_OEM_3=0xc0;
513 static const int VK_OEM_4=0xdb;
514 static const int VK_OEM_5=0xdc;
515 static const int VK_OEM_6=0xdd;
516 #endif
518 /** Map the key codes to their equivalent SCK_ form. */
519 static int KeyTranslate(int keyIn) {
520 //PLATFORM_ASSERT(!keyIn);
521 switch (keyIn) {
522 case VK_DOWN: return SCK_DOWN;
523 case VK_UP: return SCK_UP;
524 case VK_LEFT: return SCK_LEFT;
525 case VK_RIGHT: return SCK_RIGHT;
526 case VK_HOME: return SCK_HOME;
527 case VK_END: return SCK_END;
528 case VK_PRIOR: return SCK_PRIOR;
529 case VK_NEXT: return SCK_NEXT;
530 case VK_DELETE: return SCK_DELETE;
531 case VK_INSERT: return SCK_INSERT;
532 case VK_ESCAPE: return SCK_ESCAPE;
533 case VK_BACK: return SCK_BACK;
534 case VK_TAB: return SCK_TAB;
535 case VK_RETURN: return SCK_RETURN;
536 case VK_ADD: return SCK_ADD;
537 case VK_SUBTRACT: return SCK_SUBTRACT;
538 case VK_DIVIDE: return SCK_DIVIDE;
539 case VK_LWIN: return SCK_WIN;
540 case VK_RWIN: return SCK_RWIN;
541 case VK_APPS: return SCK_MENU;
542 case VK_OEM_2: return '/';
543 case VK_OEM_3: return '`';
544 case VK_OEM_4: return '[';
545 case VK_OEM_5: return '\\';
546 case VK_OEM_6: return ']';
547 default: return keyIn;
551 LRESULT ScintillaWin::WndPaint(uptr_t wParam) {
552 //ElapsedTime et;
554 // Redirect assertions to debug output and save current state
555 bool assertsPopup = Platform::ShowAssertionPopUps(false);
556 paintState = painting;
557 PAINTSTRUCT ps;
558 PAINTSTRUCT *pps;
560 bool IsOcxCtrl = (wParam != 0); // if wParam != 0, it contains
561 // a PAINSTRUCT* from the OCX
562 // Removed since this interferes with reporting other assertions as it occurs repeatedly
563 //PLATFORM_ASSERT(hRgnUpdate == NULL);
564 hRgnUpdate = ::CreateRectRgn(0, 0, 0, 0);
565 if (IsOcxCtrl) {
566 pps = reinterpret_cast<PAINTSTRUCT*>(wParam);
567 } else {
568 ::GetUpdateRgn(MainHWND(), hRgnUpdate, FALSE);
569 pps = &ps;
570 ::BeginPaint(MainHWND(), pps);
572 if (technology == SC_TECHNOLOGY_DEFAULT) {
573 AutoSurface surfaceWindow(pps->hdc, this);
574 if (surfaceWindow) {
575 rcPaint = PRectangle(pps->rcPaint.left, pps->rcPaint.top, pps->rcPaint.right, pps->rcPaint.bottom);
576 PRectangle rcClient = GetClientRectangle();
577 paintingAllText = rcPaint.Contains(rcClient);
578 Paint(surfaceWindow, rcPaint);
579 surfaceWindow->Release();
581 } else {
582 #if defined(USE_D2D)
583 EnsureRenderTarget();
584 AutoSurface surfaceWindow(pRenderTarget, this);
585 if (surfaceWindow) {
586 pRenderTarget->BeginDraw();
587 rcPaint = PRectangle(pps->rcPaint.left, pps->rcPaint.top, pps->rcPaint.right, pps->rcPaint.bottom);
588 PRectangle rcClient = GetClientRectangle();
589 paintingAllText = rcPaint.Contains(rcClient);
590 Paint(surfaceWindow, rcPaint);
591 surfaceWindow->Release();
592 HRESULT hr = pRenderTarget->EndDraw();
593 if (hr == D2DERR_RECREATE_TARGET) {
594 DropRenderTarget();
597 #endif
599 if (hRgnUpdate) {
600 ::DeleteRgn(hRgnUpdate);
601 hRgnUpdate = 0;
604 if (!IsOcxCtrl)
605 ::EndPaint(MainHWND(), pps);
606 if (paintState == paintAbandoned) {
607 // Painting area was insufficient to cover new styling or brace highlight positions
608 FullPaint();
610 paintState = notPainting;
612 // Restore debug output state
613 Platform::ShowAssertionPopUps(assertsPopup);
615 //Platform::DebugPrintf("Paint took %g\n", et.Duration());
616 return 0l;
619 sptr_t ScintillaWin::HandleComposition(uptr_t wParam, sptr_t lParam) {
620 #ifdef __DMC__
621 // Digital Mars compiler does not include Imm library
622 return 0;
623 #else
624 if (lParam & GCS_RESULTSTR) {
625 HIMC hIMC = ::ImmGetContext(MainHWND());
626 if (hIMC) {
627 const int maxLenInputIME = 200;
628 wchar_t wcs[maxLenInputIME];
629 LONG bytes = ::ImmGetCompositionStringW(hIMC,
630 GCS_RESULTSTR, wcs, (maxLenInputIME-1)*2);
631 int wides = bytes / 2;
632 if (IsUnicodeMode()) {
633 char utfval[maxLenInputIME * 3];
634 unsigned int len = UTF8Length(wcs, wides);
635 UTF8FromUTF16(wcs, wides, utfval, len);
636 utfval[len] = '\0';
637 AddCharUTF(utfval, len);
638 } else {
639 char dbcsval[maxLenInputIME * 2];
640 int size = ::WideCharToMultiByte(InputCodePage(),
641 0, wcs, wides, dbcsval, sizeof(dbcsval) - 1, 0, 0);
642 for (int i=0; i<size; i++) {
643 AddChar(dbcsval[i]);
646 // Set new position after converted
647 Point pos = PointMainCaret();
648 COMPOSITIONFORM CompForm;
649 CompForm.dwStyle = CFS_POINT;
650 CompForm.ptCurrentPos.x = pos.x;
651 CompForm.ptCurrentPos.y = pos.y;
652 ::ImmSetCompositionWindow(hIMC, &CompForm);
653 ::ImmReleaseContext(MainHWND(), hIMC);
655 return 0;
657 return ::DefWindowProc(MainHWND(), WM_IME_COMPOSITION, wParam, lParam);
658 #endif
661 // Translate message IDs from WM_* and EM_* to SCI_* so can partly emulate Windows Edit control
662 static unsigned int SciMessageFromEM(unsigned int iMessage) {
663 switch (iMessage) {
664 case EM_CANPASTE: return SCI_CANPASTE;
665 case EM_CANUNDO: return SCI_CANUNDO;
666 case EM_EMPTYUNDOBUFFER: return SCI_EMPTYUNDOBUFFER;
667 case EM_FINDTEXTEX: return SCI_FINDTEXT;
668 case EM_FORMATRANGE: return SCI_FORMATRANGE;
669 case EM_GETFIRSTVISIBLELINE: return SCI_GETFIRSTVISIBLELINE;
670 case EM_GETLINECOUNT: return SCI_GETLINECOUNT;
671 case EM_GETSELTEXT: return SCI_GETSELTEXT;
672 case EM_GETTEXTRANGE: return SCI_GETTEXTRANGE;
673 case EM_HIDESELECTION: return SCI_HIDESELECTION;
674 case EM_LINEINDEX: return SCI_POSITIONFROMLINE;
675 case EM_LINESCROLL: return SCI_LINESCROLL;
676 case EM_REPLACESEL: return SCI_REPLACESEL;
677 case EM_SCROLLCARET: return SCI_SCROLLCARET;
678 case EM_SETREADONLY: return SCI_SETREADONLY;
679 case WM_CLEAR: return SCI_CLEAR;
680 case WM_COPY: return SCI_COPY;
681 case WM_CUT: return SCI_CUT;
682 case WM_GETTEXT: return SCI_GETTEXT;
683 case WM_SETTEXT: return SCI_SETTEXT;
684 case WM_GETTEXTLENGTH: return SCI_GETTEXTLENGTH;
685 case WM_PASTE: return SCI_PASTE;
686 case WM_UNDO: return SCI_UNDO;
688 return iMessage;
691 static UINT CodePageFromCharSet(DWORD characterSet, UINT documentCodePage) {
692 if (documentCodePage == SC_CP_UTF8) {
693 // The system calls here are a little slow so avoid if known case.
694 return SC_CP_UTF8;
696 CHARSETINFO ci = { 0, 0, { { 0, 0, 0, 0 }, { 0, 0 } } };
697 BOOL bci = ::TranslateCharsetInfo((DWORD*)characterSet,
698 &ci, TCI_SRCCHARSET);
700 UINT cp;
701 if (bci)
702 cp = ci.ciACP;
703 else
704 cp = documentCodePage;
706 CPINFO cpi;
707 if (!IsValidCodePage(cp) && !GetCPInfo(cp, &cpi))
708 cp = CP_ACP;
710 return cp;
713 UINT ScintillaWin::CodePageOfDocument() {
714 return CodePageFromCharSet(vs.styles[STYLE_DEFAULT].characterSet, pdoc->dbcsCodePage);
717 sptr_t ScintillaWin::WndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
718 try {
719 //Platform::DebugPrintf("S M:%x WP:%x L:%x\n", iMessage, wParam, lParam);
720 iMessage = SciMessageFromEM(iMessage);
721 switch (iMessage) {
723 case WM_CREATE:
724 ctrlID = ::GetDlgCtrlID(reinterpret_cast<HWND>(wMain.GetID()));
725 // Get Intellimouse scroll line parameters
726 GetIntelliMouseParameters();
727 ::RegisterDragDrop(MainHWND(), reinterpret_cast<IDropTarget *>(&dt));
728 break;
730 case WM_COMMAND:
731 Command(LoWord(wParam));
732 break;
734 case WM_PAINT:
735 return WndPaint(wParam);
737 case WM_PRINTCLIENT: {
738 HDC hdc = reinterpret_cast<HDC>(wParam);
739 if (!IsCompatibleDC(hdc)) {
740 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
742 FullPaintDC(hdc);
744 break;
746 case WM_VSCROLL:
747 ScrollMessage(wParam);
748 break;
750 case WM_HSCROLL:
751 HorizontalScrollMessage(wParam);
752 break;
754 case WM_SIZE: {
755 #if defined(USE_D2D)
756 if (paintState == notPainting) {
757 DropRenderTarget();
758 } else {
759 renderTargetValid = false;
761 #endif
762 //Platform::DebugPrintf("Scintilla WM_SIZE %d %d\n", LoWord(lParam), HiWord(lParam));
763 ChangeSize();
765 break;
767 case WM_MOUSEWHEEL:
768 // if autocomplete list active then send mousewheel message to it
769 if (ac.Active()) {
770 HWND hWnd = reinterpret_cast<HWND>(ac.lb->GetID());
771 ::SendMessage(hWnd, iMessage, wParam, lParam);
772 break;
775 // Don't handle datazoom.
776 // (A good idea for datazoom would be to "fold" or "unfold" details.
777 // i.e. if datazoomed out only class structures are visible, when datazooming in the control
778 // structures appear, then eventually the individual statements...)
779 if (wParam & MK_SHIFT) {
780 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
783 // Either SCROLL or ZOOM. We handle the wheel steppings calculation
784 wheelDelta -= static_cast<short>(HiWord(wParam));
785 if (abs(wheelDelta) >= WHEEL_DELTA && linesPerScroll > 0) {
786 int linesToScroll = linesPerScroll;
787 if (linesPerScroll == WHEEL_PAGESCROLL)
788 linesToScroll = LinesOnScreen() - 1;
789 if (linesToScroll == 0) {
790 linesToScroll = 1;
792 linesToScroll *= (wheelDelta / WHEEL_DELTA);
793 if (wheelDelta >= 0)
794 wheelDelta = wheelDelta % WHEEL_DELTA;
795 else
796 wheelDelta = - (-wheelDelta % WHEEL_DELTA);
798 if (wParam & MK_CONTROL) {
799 // Zoom! We play with the font sizes in the styles.
800 // Number of steps/line is ignored, we just care if sizing up or down
801 if (linesToScroll < 0) {
802 KeyCommand(SCI_ZOOMIN);
803 } else {
804 KeyCommand(SCI_ZOOMOUT);
806 } else {
807 // Scroll
808 ScrollTo(topLine + linesToScroll);
811 return 0;
813 case WM_TIMER:
814 if (wParam == standardTimerID && timer.ticking) {
815 Tick();
816 } else if (wParam == idleTimerID && idler.state) {
817 SendMessage(MainHWND(), SC_WIN_IDLE, 0, 1);
818 } else {
819 return 1;
821 break;
823 case SC_WIN_IDLE:
824 // wParam=dwTickCountInitial, or 0 to initialize. lParam=bSkipUserInputTest
825 if (idler.state) {
826 if (lParam || (WAIT_TIMEOUT == MsgWaitForMultipleObjects(0, 0, 0, 0, QS_INPUT|QS_HOTKEY))) {
827 if (Idle()) {
828 // User input was given priority above, but all events do get a turn. Other
829 // messages, notifications, etc. will get interleaved with the idle messages.
831 // However, some things like WM_PAINT are a lower priority, and will not fire
832 // when there's a message posted. So, several times a second, we stop and let
833 // the low priority events have a turn (after which the timer will fire again).
835 DWORD dwCurrent = GetTickCount();
836 DWORD dwStart = wParam ? wParam : dwCurrent;
837 const DWORD maxWorkTime = 50;
839 if (dwCurrent >= dwStart && dwCurrent > maxWorkTime && dwCurrent - maxWorkTime < dwStart)
840 PostMessage(MainHWND(), SC_WIN_IDLE, dwStart, 0);
841 } else {
842 SetIdle(false);
846 break;
848 case WM_GETMINMAXINFO:
849 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
851 case WM_LBUTTONDOWN: {
852 #ifndef __DMC__
853 // Digital Mars compiler does not include Imm library
854 // For IME, set the composition string as the result string.
855 HIMC hIMC = ::ImmGetContext(MainHWND());
856 ::ImmNotifyIME(hIMC, NI_COMPOSITIONSTR, CPS_COMPLETE, 0);
857 ::ImmReleaseContext(MainHWND(), hIMC);
858 #endif
860 //Platform::DebugPrintf("Buttdown %d %x %x %x %x %x\n",iMessage, wParam, lParam,
861 // Platform::IsKeyDown(VK_SHIFT),
862 // Platform::IsKeyDown(VK_CONTROL),
863 // Platform::IsKeyDown(VK_MENU));
864 ::SetFocus(MainHWND());
865 ButtonDown(Point::FromLong(lParam), ::GetMessageTime(),
866 (wParam & MK_SHIFT) != 0,
867 (wParam & MK_CONTROL) != 0,
868 Platform::IsKeyDown(VK_MENU));
870 break;
872 case WM_MOUSEMOVE:
873 SetTrackMouseLeaveEvent(true);
874 ButtonMove(Point::FromLong(lParam));
875 break;
877 case WM_MOUSELEAVE:
878 SetTrackMouseLeaveEvent(false);
879 MouseLeave();
880 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
882 case WM_LBUTTONUP:
883 ButtonUp(Point::FromLong(lParam),
884 ::GetMessageTime(),
885 (wParam & MK_CONTROL) != 0);
886 break;
888 case WM_RBUTTONDOWN:
889 ::SetFocus(MainHWND());
890 if (!PointInSelection(Point::FromLong(lParam))) {
891 CancelModes();
892 SetEmptySelection(PositionFromLocation(Point::FromLong(lParam)));
894 break;
896 case WM_SETCURSOR:
897 if (LoWord(lParam) == HTCLIENT) {
898 if (inDragDrop == ddDragging) {
899 DisplayCursor(Window::cursorUp);
900 } else {
901 // Display regular (drag) cursor over selection
902 POINT pt;
903 if (0 != ::GetCursorPos(&pt)) {
904 ::ScreenToClient(MainHWND(), &pt);
905 if (PointInSelMargin(Point(pt.x, pt.y))) {
906 DisplayCursor(GetMarginCursor(Point(pt.x, pt.y)));
907 } else if (PointInSelection(Point(pt.x, pt.y)) && !SelectionEmpty()) {
908 DisplayCursor(Window::cursorArrow);
909 } else if (PointIsHotspot(Point(pt.x, pt.y))) {
910 DisplayCursor(Window::cursorHand);
911 } else {
912 DisplayCursor(Window::cursorText);
916 return TRUE;
917 } else {
918 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
921 case WM_CHAR:
922 if (((wParam >= 128) || !iscntrl(wParam)) || !lastKeyDownConsumed) {
923 if (::IsWindowUnicode(MainHWND()) || keysAlwaysUnicode) {
924 wchar_t wcs[2] = {static_cast<wchar_t>(wParam), 0};
925 if (IsUnicodeMode()) {
926 // For a wide character version of the window:
927 char utfval[4];
928 unsigned int len = UTF8Length(wcs, 1);
929 UTF8FromUTF16(wcs, 1, utfval, len);
930 AddCharUTF(utfval, len);
931 } else {
932 UINT cpDest = CodePageOfDocument();
933 char inBufferCP[20];
934 int size = ::WideCharToMultiByte(cpDest,
935 0, wcs, 1, inBufferCP, sizeof(inBufferCP) - 1, 0, 0);
936 inBufferCP[size] = '\0';
937 AddCharUTF(inBufferCP, size);
939 } else {
940 if (IsUnicodeMode()) {
941 AddCharBytes('\0', LOBYTE(wParam));
942 } else {
943 AddChar(LOBYTE(wParam));
947 return 0;
949 case WM_UNICHAR:
950 if (wParam == UNICODE_NOCHAR) {
951 return IsUnicodeMode() ? 1 : 0;
952 } else if (lastKeyDownConsumed) {
953 return 1;
954 } else {
955 if (IsUnicodeMode()) {
956 char utfval[4];
957 wchar_t wcs[2] = {static_cast<wchar_t>(wParam), 0};
958 unsigned int len = UTF8Length(wcs, 1);
959 UTF8FromUTF16(wcs, 1, utfval, len);
960 AddCharUTF(utfval, len);
961 return 1;
962 } else {
963 return 0;
967 case WM_SYSKEYDOWN:
968 case WM_KEYDOWN: {
969 //Platform::DebugPrintf("S keydown %d %x %x %x %x\n",iMessage, wParam, lParam, ::IsKeyDown(VK_SHIFT), ::IsKeyDown(VK_CONTROL));
970 lastKeyDownConsumed = false;
971 int ret = KeyDown(KeyTranslate(wParam),
972 Platform::IsKeyDown(VK_SHIFT),
973 Platform::IsKeyDown(VK_CONTROL),
974 Platform::IsKeyDown(VK_MENU),
975 &lastKeyDownConsumed);
976 if (!ret && !lastKeyDownConsumed) {
977 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
979 break;
982 case WM_IME_KEYDOWN:
983 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
985 case WM_KEYUP:
986 //Platform::DebugPrintf("S keyup %d %x %x\n",iMessage, wParam, lParam);
987 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
989 case WM_SETTINGCHANGE:
990 //Platform::DebugPrintf("Setting Changed\n");
991 InvalidateStyleData();
992 // Get Intellimouse scroll line parameters
993 GetIntelliMouseParameters();
994 break;
996 case WM_GETDLGCODE:
997 return DLGC_HASSETSEL | DLGC_WANTALLKEYS;
999 case WM_KILLFOCUS: {
1000 HWND wOther = reinterpret_cast<HWND>(wParam);
1001 HWND wThis = MainHWND();
1002 HWND wCT = reinterpret_cast<HWND>(ct.wCallTip.GetID());
1003 if (!wParam ||
1004 !(::IsChild(wThis, wOther) || (wOther == wCT))) {
1005 SetFocusState(false);
1006 DestroySystemCaret();
1009 break;
1011 case WM_SETFOCUS:
1012 SetFocusState(true);
1013 DestroySystemCaret();
1014 CreateSystemCaret();
1015 break;
1017 case WM_SYSCOLORCHANGE:
1018 //Platform::DebugPrintf("Setting Changed\n");
1019 InvalidateStyleData();
1020 break;
1022 case WM_IME_STARTCOMPOSITION: // dbcs
1023 ImeStartComposition();
1024 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1026 case WM_IME_ENDCOMPOSITION: // dbcs
1027 ImeEndComposition();
1028 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1030 case WM_IME_COMPOSITION:
1031 return HandleComposition(wParam, lParam);
1033 case WM_IME_CHAR: {
1034 AddCharBytes(HIBYTE(wParam), LOBYTE(wParam));
1035 return 0;
1038 case WM_CONTEXTMENU:
1039 if (displayPopupMenu) {
1040 Point pt = Point::FromLong(lParam);
1041 if ((pt.x == -1) && (pt.y == -1)) {
1042 // Caused by keyboard so display menu near caret
1043 pt = PointMainCaret();
1044 POINT spt = {static_cast<int>(pt.x), static_cast<int>(pt.y)};
1045 ::ClientToScreen(MainHWND(), &spt);
1046 pt = Point(spt.x, spt.y);
1048 ContextMenu(pt);
1049 return 0;
1051 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1053 case WM_INPUTLANGCHANGE:
1054 //::SetThreadLocale(LOWORD(lParam));
1055 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1057 case WM_INPUTLANGCHANGEREQUEST:
1058 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1060 case WM_ERASEBKGND:
1061 return 1; // Avoid any background erasure as whole window painted.
1063 case WM_CAPTURECHANGED:
1064 capturedMouse = false;
1065 return 0;
1067 // These are not handled in Scintilla and its faster to dispatch them here.
1068 // Also moves time out to here so profile doesn't count lots of empty message calls.
1070 case WM_MOVE:
1071 case WM_MOUSEACTIVATE:
1072 case WM_NCHITTEST:
1073 case WM_NCCALCSIZE:
1074 case WM_NCPAINT:
1075 case WM_NCMOUSEMOVE:
1076 case WM_NCLBUTTONDOWN:
1077 case WM_IME_SETCONTEXT:
1078 case WM_IME_NOTIFY:
1079 case WM_SYSCOMMAND:
1080 case WM_WINDOWPOSCHANGING:
1081 case WM_WINDOWPOSCHANGED:
1082 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1084 case EM_LINEFROMCHAR:
1085 if (static_cast<int>(wParam) < 0) {
1086 wParam = SelectionStart().Position();
1088 return pdoc->LineFromPosition(wParam);
1090 case EM_EXLINEFROMCHAR:
1091 return pdoc->LineFromPosition(lParam);
1093 case EM_GETSEL:
1094 if (wParam) {
1095 *reinterpret_cast<int *>(wParam) = SelectionStart().Position();
1097 if (lParam) {
1098 *reinterpret_cast<int *>(lParam) = SelectionEnd().Position();
1100 return MAKELONG(SelectionStart().Position(), SelectionEnd().Position());
1102 case EM_EXGETSEL: {
1103 if (lParam == 0) {
1104 return 0;
1106 Sci_CharacterRange *pCR = reinterpret_cast<Sci_CharacterRange *>(lParam);
1107 pCR->cpMin = SelectionStart().Position();
1108 pCR->cpMax = SelectionEnd().Position();
1110 break;
1112 case EM_SETSEL: {
1113 int nStart = static_cast<int>(wParam);
1114 int nEnd = static_cast<int>(lParam);
1115 if (nStart == 0 && nEnd == -1) {
1116 nEnd = pdoc->Length();
1118 if (nStart == -1) {
1119 nStart = nEnd; // Remove selection
1121 if (nStart > nEnd) {
1122 SetSelection(nEnd, nStart);
1123 } else {
1124 SetSelection(nStart, nEnd);
1126 EnsureCaretVisible();
1128 break;
1130 case EM_EXSETSEL: {
1131 if (lParam == 0) {
1132 return 0;
1134 Sci_CharacterRange *pCR = reinterpret_cast<Sci_CharacterRange *>(lParam);
1135 sel.selType = Selection::selStream;
1136 if (pCR->cpMin == 0 && pCR->cpMax == -1) {
1137 SetSelection(pCR->cpMin, pdoc->Length());
1138 } else {
1139 SetSelection(pCR->cpMin, pCR->cpMax);
1141 EnsureCaretVisible();
1142 return pdoc->LineFromPosition(SelectionStart().Position());
1145 case SCI_GETDIRECTFUNCTION:
1146 return reinterpret_cast<sptr_t>(DirectFunction);
1148 case SCI_GETDIRECTPOINTER:
1149 return reinterpret_cast<sptr_t>(this);
1151 case SCI_GRABFOCUS:
1152 ::SetFocus(MainHWND());
1153 break;
1155 case SCI_SETKEYSUNICODE:
1156 keysAlwaysUnicode = wParam != 0;
1157 break;
1159 case SCI_GETKEYSUNICODE:
1160 return keysAlwaysUnicode;
1162 case SCI_SETTECHNOLOGY:
1163 if ((wParam == SC_TECHNOLOGY_DEFAULT) || (wParam == SC_TECHNOLOGY_DIRECTWRITE)) {
1164 if (technology != static_cast<int>(wParam)) {
1165 if (static_cast<int>(wParam) == SC_TECHNOLOGY_DIRECTWRITE) {
1166 #if defined(USE_D2D)
1167 if (!LoadD2D())
1168 // Failed to load Direct2D or DirectWrite so no effect
1169 return 0;
1170 #else
1171 return 0;
1172 #endif
1174 technology = wParam;
1175 // Invalidate all cached information including layout.
1176 DropGraphics(true);
1177 InvalidateStyleRedraw();
1180 break;
1182 #ifdef SCI_LEXER
1183 case SCI_LOADLEXERLIBRARY:
1184 LexerManager::GetInstance()->Load(reinterpret_cast<const char *>(lParam));
1185 break;
1186 #endif
1188 default:
1189 return ScintillaBase::WndProc(iMessage, wParam, lParam);
1191 } catch (std::bad_alloc &) {
1192 errorStatus = SC_STATUS_BADALLOC;
1193 } catch (...) {
1194 errorStatus = SC_STATUS_FAILURE;
1196 return 0l;
1199 bool ScintillaWin::ValidCodePage(int codePage) const {
1200 return codePage == 0 || codePage == SC_CP_UTF8 ||
1201 codePage == 932 || codePage == 936 || codePage == 949 ||
1202 codePage == 950 || codePage == 1361;
1205 sptr_t ScintillaWin::DefWndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
1206 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1209 void ScintillaWin::SetTicking(bool on) {
1210 if (timer.ticking != on) {
1211 timer.ticking = on;
1212 if (timer.ticking) {
1213 timer.tickerID = ::SetTimer(MainHWND(), standardTimerID, timer.tickSize, NULL)
1214 ? reinterpret_cast<TickerID>(standardTimerID) : 0;
1215 } else {
1216 ::KillTimer(MainHWND(), reinterpret_cast<uptr_t>(timer.tickerID));
1217 timer.tickerID = 0;
1220 timer.ticksToWait = caret.period;
1223 bool ScintillaWin::SetIdle(bool on) {
1224 // On Win32 the Idler is implemented as a Timer on the Scintilla window. This
1225 // takes advantage of the fact that WM_TIMER messages are very low priority,
1226 // and are only posted when the message queue is empty, i.e. during idle time.
1227 if (idler.state != on) {
1228 if (on) {
1229 idler.idlerID = ::SetTimer(MainHWND(), idleTimerID, 10, NULL)
1230 ? reinterpret_cast<IdlerID>(idleTimerID) : 0;
1231 } else {
1232 ::KillTimer(MainHWND(), reinterpret_cast<uptr_t>(idler.idlerID));
1233 idler.idlerID = 0;
1235 idler.state = idler.idlerID != 0;
1237 return idler.state;
1240 void ScintillaWin::SetMouseCapture(bool on) {
1241 if (mouseDownCaptures) {
1242 if (on) {
1243 ::SetCapture(MainHWND());
1244 } else {
1245 ::ReleaseCapture();
1248 capturedMouse = on;
1251 bool ScintillaWin::HaveMouseCapture() {
1252 // Cannot just see if GetCapture is this window as the scroll bar also sets capture for the window
1253 return capturedMouse;
1254 //return capturedMouse && (::GetCapture() == MainHWND());
1257 void ScintillaWin::SetTrackMouseLeaveEvent(bool on) {
1258 if (on && TrackMouseEventFn && !trackedMouseLeave) {
1259 TRACKMOUSEEVENT tme;
1260 tme.cbSize = sizeof(tme);
1261 tme.dwFlags = TME_LEAVE;
1262 tme.hwndTrack = MainHWND();
1263 TrackMouseEventFn(&tme);
1265 trackedMouseLeave = on;
1268 bool ScintillaWin::PaintContains(PRectangle rc) {
1269 bool contains = true;
1270 if ((paintState == painting) && (!rc.Empty())) {
1271 if (!rcPaint.Contains(rc)) {
1272 contains = false;
1273 } else {
1274 // In bounding rectangle so check more accurately using region
1275 HRGN hRgnRange = ::CreateRectRgn(rc.left, rc.top, rc.right, rc.bottom);
1276 if (hRgnRange) {
1277 HRGN hRgnDest = ::CreateRectRgn(0, 0, 0, 0);
1278 if (hRgnDest) {
1279 int combination = ::CombineRgn(hRgnDest, hRgnRange, hRgnUpdate, RGN_DIFF);
1280 if (combination != NULLREGION) {
1281 contains = false;
1283 ::DeleteRgn(hRgnDest);
1285 ::DeleteRgn(hRgnRange);
1289 return contains;
1292 void ScintillaWin::ScrollText(int /* linesToMove */) {
1293 //Platform::DebugPrintf("ScintillaWin::ScrollText %d\n", linesToMove);
1294 //::ScrollWindow(MainHWND(), 0,
1295 // vs.lineHeight * linesToMove, 0, 0);
1296 //::UpdateWindow(MainHWND());
1297 Redraw();
1300 void ScintillaWin::UpdateSystemCaret() {
1301 if (hasFocus) {
1302 if (HasCaretSizeChanged()) {
1303 DestroySystemCaret();
1304 CreateSystemCaret();
1306 Point pos = PointMainCaret();
1307 ::SetCaretPos(pos.x, pos.y);
1311 int ScintillaWin::SetScrollInfo(int nBar, LPCSCROLLINFO lpsi, BOOL bRedraw) {
1312 return ::SetScrollInfo(MainHWND(), nBar, lpsi, bRedraw);
1315 bool ScintillaWin::GetScrollInfo(int nBar, LPSCROLLINFO lpsi) {
1316 return ::GetScrollInfo(MainHWND(), nBar, lpsi) ? true : false;
1319 // Change the scroll position but avoid repaint if changing to same value
1320 void ScintillaWin::ChangeScrollPos(int barType, int pos) {
1321 SCROLLINFO sci = {
1322 sizeof(sci), 0, 0, 0, 0, 0, 0
1324 sci.fMask = SIF_POS;
1325 GetScrollInfo(barType, &sci);
1326 if (sci.nPos != pos) {
1327 DwellEnd(true);
1328 sci.nPos = pos;
1329 SetScrollInfo(barType, &sci, TRUE);
1333 void ScintillaWin::SetVerticalScrollPos() {
1334 ChangeScrollPos(SB_VERT, topLine);
1337 void ScintillaWin::SetHorizontalScrollPos() {
1338 ChangeScrollPos(SB_HORZ, xOffset);
1341 bool ScintillaWin::ModifyScrollBars(int nMax, int nPage) {
1342 bool modified = false;
1343 SCROLLINFO sci = {
1344 sizeof(sci), 0, 0, 0, 0, 0, 0
1346 sci.fMask = SIF_PAGE | SIF_RANGE;
1347 GetScrollInfo(SB_VERT, &sci);
1348 int vertEndPreferred = nMax;
1349 if (!verticalScrollBarVisible)
1350 nPage = vertEndPreferred + 1;
1351 if ((sci.nMin != 0) ||
1352 (sci.nMax != vertEndPreferred) ||
1353 (sci.nPage != static_cast<unsigned int>(nPage)) ||
1354 (sci.nPos != 0)) {
1355 sci.fMask = SIF_PAGE | SIF_RANGE;
1356 sci.nMin = 0;
1357 sci.nMax = vertEndPreferred;
1358 sci.nPage = nPage;
1359 sci.nPos = 0;
1360 sci.nTrackPos = 1;
1361 SetScrollInfo(SB_VERT, &sci, TRUE);
1362 modified = true;
1365 PRectangle rcText = GetTextRectangle();
1366 int horizEndPreferred = scrollWidth;
1367 if (horizEndPreferred < 0)
1368 horizEndPreferred = 0;
1369 unsigned int pageWidth = rcText.Width();
1370 if (!horizontalScrollBarVisible || (wrapState != eWrapNone))
1371 pageWidth = horizEndPreferred + 1;
1372 sci.fMask = SIF_PAGE | SIF_RANGE;
1373 GetScrollInfo(SB_HORZ, &sci);
1374 if ((sci.nMin != 0) ||
1375 (sci.nMax != horizEndPreferred) ||
1376 (sci.nPage != pageWidth) ||
1377 (sci.nPos != 0)) {
1378 sci.fMask = SIF_PAGE | SIF_RANGE;
1379 sci.nMin = 0;
1380 sci.nMax = horizEndPreferred;
1381 sci.nPage = pageWidth;
1382 sci.nPos = 0;
1383 sci.nTrackPos = 1;
1384 SetScrollInfo(SB_HORZ, &sci, TRUE);
1385 modified = true;
1386 if (scrollWidth < static_cast<int>(pageWidth)) {
1387 HorizontalScrollTo(0);
1390 return modified;
1393 void ScintillaWin::NotifyChange() {
1394 ::SendMessage(::GetParent(MainHWND()), WM_COMMAND,
1395 MAKELONG(GetCtrlID(), SCEN_CHANGE),
1396 reinterpret_cast<LPARAM>(MainHWND()));
1399 void ScintillaWin::NotifyFocus(bool focus) {
1400 ::SendMessage(::GetParent(MainHWND()), WM_COMMAND,
1401 MAKELONG(GetCtrlID(), focus ? SCEN_SETFOCUS : SCEN_KILLFOCUS),
1402 reinterpret_cast<LPARAM>(MainHWND()));
1405 void ScintillaWin::SetCtrlID(int identifier) {
1406 ::SetWindowID(reinterpret_cast<HWND>(wMain.GetID()), identifier);
1409 int ScintillaWin::GetCtrlID() {
1410 return ::GetDlgCtrlID(reinterpret_cast<HWND>(wMain.GetID()));
1413 void ScintillaWin::NotifyParent(SCNotification scn) {
1414 scn.nmhdr.hwndFrom = MainHWND();
1415 scn.nmhdr.idFrom = GetCtrlID();
1416 ::SendMessage(::GetParent(MainHWND()), WM_NOTIFY,
1417 GetCtrlID(), reinterpret_cast<LPARAM>(&scn));
1420 void ScintillaWin::NotifyDoubleClick(Point pt, bool shift, bool ctrl, bool alt) {
1421 //Platform::DebugPrintf("ScintillaWin Double click 0\n");
1422 ScintillaBase::NotifyDoubleClick(pt, shift, ctrl, alt);
1423 // Send myself a WM_LBUTTONDBLCLK, so the container can handle it too.
1424 ::SendMessage(MainHWND(),
1425 WM_LBUTTONDBLCLK,
1426 shift ? MK_SHIFT : 0,
1427 MAKELPARAM(pt.x, pt.y));
1430 class CaseFolderUTF8 : public CaseFolderTable {
1431 // Allocate the expandable storage here so that it does not need to be reallocated
1432 // for each call to Fold.
1433 std::vector<wchar_t> utf16Mixed;
1434 std::vector<wchar_t> utf16Folded;
1435 public:
1436 CaseFolderUTF8() {
1437 StandardASCII();
1439 virtual size_t Fold(char *folded, size_t sizeFolded, const char *mixed, size_t lenMixed) {
1440 if ((lenMixed == 1) && (sizeFolded > 0)) {
1441 folded[0] = mapping[static_cast<unsigned char>(mixed[0])];
1442 return 1;
1443 } else {
1444 if (lenMixed > utf16Mixed.size()) {
1445 utf16Mixed.resize(lenMixed + 8);
1447 size_t nUtf16Mixed = ::MultiByteToWideChar(65001, 0, mixed,
1448 static_cast<int>(lenMixed),
1449 &utf16Mixed[0],
1450 static_cast<int>(utf16Mixed.size()));
1452 if (nUtf16Mixed == 0) {
1453 // Failed to convert -> bad UTF-8
1454 folded[0] = '\0';
1455 return 1;
1458 if (nUtf16Mixed * 4 > utf16Folded.size()) { // Maximum folding expansion factor of 4
1459 utf16Folded.resize(nUtf16Mixed * 4 + 8);
1461 int lenFlat = ::LCMapStringW(LOCALE_SYSTEM_DEFAULT,
1462 LCMAP_LINGUISTIC_CASING | LCMAP_LOWERCASE,
1463 &utf16Mixed[0],
1464 static_cast<int>(nUtf16Mixed),
1465 &utf16Folded[0],
1466 static_cast<int>(utf16Folded.size()));
1468 size_t lenOut = UTF8Length(&utf16Folded[0], lenFlat);
1469 if (lenOut < sizeFolded) {
1470 UTF8FromUTF16(&utf16Folded[0], lenFlat, folded, static_cast<int>(lenOut));
1471 return lenOut;
1472 } else {
1473 return 0;
1479 class CaseFolderDBCS : public CaseFolderTable {
1480 // Allocate the expandable storage here so that it does not need to be reallocated
1481 // for each call to Fold.
1482 std::vector<wchar_t> utf16Mixed;
1483 std::vector<wchar_t> utf16Folded;
1484 UINT cp;
1485 public:
1486 CaseFolderDBCS(UINT cp_) : cp(cp_) {
1487 StandardASCII();
1489 virtual size_t Fold(char *folded, size_t sizeFolded, const char *mixed, size_t lenMixed) {
1490 if ((lenMixed == 1) && (sizeFolded > 0)) {
1491 folded[0] = mapping[static_cast<unsigned char>(mixed[0])];
1492 return 1;
1493 } else {
1494 if (lenMixed > utf16Mixed.size()) {
1495 utf16Mixed.resize(lenMixed + 8);
1497 size_t nUtf16Mixed = ::MultiByteToWideChar(cp, 0, mixed,
1498 static_cast<int>(lenMixed),
1499 &utf16Mixed[0],
1500 static_cast<int>(utf16Mixed.size()));
1502 if (nUtf16Mixed == 0) {
1503 // Failed to convert -> bad input
1504 folded[0] = '\0';
1505 return 1;
1508 if (nUtf16Mixed * 4 > utf16Folded.size()) { // Maximum folding expansion factor of 4
1509 utf16Folded.resize(nUtf16Mixed * 4 + 8);
1511 int lenFlat = ::LCMapStringW(LOCALE_SYSTEM_DEFAULT,
1512 LCMAP_LINGUISTIC_CASING | LCMAP_LOWERCASE,
1513 &utf16Mixed[0],
1514 static_cast<int>(nUtf16Mixed),
1515 &utf16Folded[0],
1516 static_cast<int>(utf16Folded.size()));
1518 size_t lenOut = ::WideCharToMultiByte(cp, 0,
1519 &utf16Folded[0], lenFlat,
1520 NULL, 0, NULL, 0);
1522 if (lenOut < sizeFolded) {
1523 ::WideCharToMultiByte(cp, 0,
1524 &utf16Folded[0], lenFlat,
1525 folded, static_cast<int>(lenOut), NULL, 0);
1526 return lenOut;
1527 } else {
1528 return 0;
1534 CaseFolder *ScintillaWin::CaseFolderForEncoding() {
1535 UINT cpDest = CodePageOfDocument();
1536 if (cpDest == SC_CP_UTF8) {
1537 return new CaseFolderUTF8();
1538 } else {
1539 if (pdoc->dbcsCodePage == 0) {
1540 CaseFolderTable *pcf = new CaseFolderTable();
1541 pcf->StandardASCII();
1542 // Only for single byte encodings
1543 UINT cpDoc = CodePageOfDocument();
1544 for (int i=0x80; i<0x100; i++) {
1545 char sCharacter[2] = "A";
1546 sCharacter[0] = static_cast<char>(i);
1547 wchar_t wCharacter[20];
1548 unsigned int lengthUTF16 = ::MultiByteToWideChar(cpDoc, 0, sCharacter, 1,
1549 wCharacter, sizeof(wCharacter)/sizeof(wCharacter[0]));
1550 if (lengthUTF16 == 1) {
1551 wchar_t wLower[20];
1552 int charsConverted = ::LCMapStringW(LOCALE_SYSTEM_DEFAULT,
1553 LCMAP_LINGUISTIC_CASING | LCMAP_LOWERCASE,
1554 wCharacter, lengthUTF16, wLower, sizeof(wLower)/sizeof(wLower[0]));
1555 char sCharacterLowered[20];
1556 unsigned int lengthConverted = ::WideCharToMultiByte(cpDoc, 0,
1557 wLower, charsConverted,
1558 sCharacterLowered, sizeof(sCharacterLowered), NULL, 0);
1559 if ((lengthConverted == 1) && (sCharacter[0] != sCharacterLowered[0])) {
1560 pcf->SetTranslation(sCharacter[0], sCharacterLowered[0]);
1564 return pcf;
1565 } else {
1566 return new CaseFolderDBCS(cpDest);
1571 std::string ScintillaWin::CaseMapString(const std::string &s, int caseMapping) {
1572 if (s.size() == 0)
1573 return std::string();
1575 if (caseMapping == cmSame)
1576 return s;
1578 UINT cpDoc = CodePageOfDocument();
1580 unsigned int lengthUTF16 = ::MultiByteToWideChar(cpDoc, 0, s.c_str(),
1581 static_cast<int>(s.size()), NULL, 0);
1582 if (lengthUTF16 == 0) // Failed to convert
1583 return s;
1585 DWORD mapFlags = LCMAP_LINGUISTIC_CASING |
1586 ((caseMapping == cmUpper) ? LCMAP_UPPERCASE : LCMAP_LOWERCASE);
1588 // Many conversions performed by search function are short so optimize this case.
1589 enum { shortSize=20 };
1591 if (s.size() > shortSize) {
1592 // Use dynamic allocations for long strings
1594 // Change text to UTF-16
1595 std::vector<wchar_t> vwcText(lengthUTF16);
1596 ::MultiByteToWideChar(cpDoc, 0, s.c_str(), static_cast<int>(s.size()), &vwcText[0], lengthUTF16);
1598 // Change case
1599 int charsConverted = ::LCMapStringW(LOCALE_SYSTEM_DEFAULT, mapFlags,
1600 &vwcText[0], lengthUTF16, NULL, 0);
1601 std::vector<wchar_t> vwcConverted(charsConverted);
1602 ::LCMapStringW(LOCALE_SYSTEM_DEFAULT, mapFlags,
1603 &vwcText[0], lengthUTF16, &vwcConverted[0], charsConverted);
1605 // Change back to document encoding
1606 unsigned int lengthConverted = ::WideCharToMultiByte(cpDoc, 0,
1607 &vwcConverted[0], static_cast<int>(vwcConverted.size()),
1608 NULL, 0, NULL, 0);
1609 std::vector<char> vcConverted(lengthConverted);
1610 ::WideCharToMultiByte(cpDoc, 0,
1611 &vwcConverted[0], static_cast<int>(vwcConverted.size()),
1612 &vcConverted[0], static_cast<int>(vcConverted.size()), NULL, 0);
1614 return std::string(&vcConverted[0], vcConverted.size());
1616 } else {
1617 // Use static allocations for short strings as much faster
1618 // A factor of 15 for single character strings
1620 // Change text to UTF-16
1621 wchar_t vwcText[shortSize];
1622 ::MultiByteToWideChar(cpDoc, 0, s.c_str(), static_cast<int>(s.size()),
1623 vwcText, lengthUTF16);
1625 // Change case
1626 int charsConverted = ::LCMapStringW(LOCALE_SYSTEM_DEFAULT, mapFlags,
1627 vwcText, lengthUTF16, NULL, 0);
1628 // Full mapping may produce up to 3 characters per input character
1629 wchar_t vwcConverted[shortSize*3];
1630 ::LCMapStringW(LOCALE_SYSTEM_DEFAULT, mapFlags, vwcText, lengthUTF16,
1631 vwcConverted, charsConverted);
1633 // Change back to document encoding
1634 unsigned int lengthConverted = ::WideCharToMultiByte(cpDoc, 0,
1635 vwcConverted, charsConverted,
1636 NULL, 0, NULL, 0);
1637 // Each UTF-16 code unit may need up to 3 bytes in UTF-8
1638 char vcConverted[shortSize * 3 * 3];
1639 ::WideCharToMultiByte(cpDoc, 0,
1640 vwcConverted, charsConverted,
1641 vcConverted, lengthConverted, NULL, 0);
1643 return std::string(vcConverted, lengthConverted);
1647 void ScintillaWin::Copy() {
1648 //Platform::DebugPrintf("Copy\n");
1649 if (!sel.Empty()) {
1650 SelectionText selectedText;
1651 CopySelectionRange(&selectedText);
1652 CopyToClipboard(selectedText);
1656 void ScintillaWin::CopyAllowLine() {
1657 SelectionText selectedText;
1658 CopySelectionRange(&selectedText, true);
1659 CopyToClipboard(selectedText);
1662 bool ScintillaWin::CanPaste() {
1663 if (!Editor::CanPaste())
1664 return false;
1665 if (::IsClipboardFormatAvailable(CF_TEXT))
1666 return true;
1667 if (IsUnicodeMode())
1668 return ::IsClipboardFormatAvailable(CF_UNICODETEXT) != 0;
1669 return false;
1672 class GlobalMemory {
1673 HGLOBAL hand;
1674 public:
1675 void *ptr;
1676 GlobalMemory() : hand(0), ptr(0) {
1678 GlobalMemory(HGLOBAL hand_) : hand(hand_), ptr(0) {
1679 if (hand) {
1680 ptr = ::GlobalLock(hand);
1683 ~GlobalMemory() {
1684 PLATFORM_ASSERT(!ptr);
1686 void Allocate(size_t bytes) {
1687 hand = ::GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT, bytes);
1688 if (hand) {
1689 ptr = ::GlobalLock(hand);
1692 HGLOBAL Unlock() {
1693 PLATFORM_ASSERT(ptr);
1694 HGLOBAL handCopy = hand;
1695 ::GlobalUnlock(hand);
1696 ptr = 0;
1697 hand = 0;
1698 return handCopy;
1700 void SetClip(UINT uFormat) {
1701 ::SetClipboardData(uFormat, Unlock());
1703 operator bool() const {
1704 return ptr != 0;
1706 SIZE_T Size() {
1707 return ::GlobalSize(hand);
1711 void ScintillaWin::InsertPasteText(const char *text, int len, SelectionPosition selStart, bool isRectangular, bool isLine) {
1712 if (isRectangular) {
1713 PasteRectangular(selStart, text, len);
1714 } else {
1715 char *convertedText = 0;
1716 if (convertPastes) {
1717 // Convert line endings of the paste into our local line-endings mode
1718 convertedText = Document::TransformLineEnds(&len, text, len, pdoc->eolMode);
1719 text = convertedText;
1721 if (isLine) {
1722 int insertPos = pdoc->LineStart(pdoc->LineFromPosition(sel.MainCaret()));
1723 pdoc->InsertString(insertPos, text, len);
1724 // add the newline if necessary
1725 if ((len > 0) && (text[len-1] != '\n' && text[len-1] != '\r')) {
1726 const char *endline = StringFromEOLMode(pdoc->eolMode);
1727 pdoc->InsertString(insertPos + len, endline, static_cast<int>(strlen(endline)));
1728 len += static_cast<int>(strlen(endline));
1730 if (sel.MainCaret() == insertPos) {
1731 SetEmptySelection(sel.MainCaret() + len);
1733 } else {
1734 InsertPaste(selStart, text, len);
1736 delete []convertedText;
1740 void ScintillaWin::Paste() {
1741 if (!::OpenClipboard(MainHWND()))
1742 return;
1743 UndoGroup ug(pdoc);
1744 bool isLine = SelectionEmpty() && (::IsClipboardFormatAvailable(cfLineSelect) != 0);
1745 ClearSelection(multiPasteMode == SC_MULTIPASTE_EACH);
1746 SelectionPosition selStart = sel.IsRectangular() ?
1747 sel.Rectangular().Start() :
1748 sel.Range(sel.Main()).Start();
1749 bool isRectangular = ::IsClipboardFormatAvailable(cfColumnSelect) != 0;
1751 // Always use CF_UNICODETEXT if available
1752 GlobalMemory memUSelection(::GetClipboardData(CF_UNICODETEXT));
1753 if (memUSelection) {
1754 wchar_t *uptr = static_cast<wchar_t *>(memUSelection.ptr);
1755 if (uptr) {
1756 unsigned int len;
1757 char *putf;
1758 // Default Scintilla behaviour in Unicode mode
1759 if (IsUnicodeMode()) {
1760 unsigned int bytes = memUSelection.Size();
1761 len = UTF8Length(uptr, bytes / 2);
1762 putf = new char[len + 1];
1763 UTF8FromUTF16(uptr, bytes / 2, putf, len);
1764 } else {
1765 // CF_UNICODETEXT available, but not in Unicode mode
1766 // Convert from Unicode to current Scintilla code page
1767 UINT cpDest = CodePageOfDocument();
1768 len = ::WideCharToMultiByte(cpDest, 0, uptr, -1,
1769 NULL, 0, NULL, NULL) - 1; // subtract 0 terminator
1770 putf = new char[len + 1];
1771 ::WideCharToMultiByte(cpDest, 0, uptr, -1,
1772 putf, len + 1, NULL, NULL);
1775 InsertPasteText(putf, len, selStart, isRectangular, isLine);
1776 delete []putf;
1778 memUSelection.Unlock();
1779 } else {
1780 // CF_UNICODETEXT not available, paste ANSI text
1781 GlobalMemory memSelection(::GetClipboardData(CF_TEXT));
1782 if (memSelection) {
1783 char *ptr = static_cast<char *>(memSelection.ptr);
1784 if (ptr) {
1785 unsigned int bytes = memSelection.Size();
1786 unsigned int len = bytes;
1787 for (unsigned int i = 0; i < bytes; i++) {
1788 if ((len == bytes) && (0 == ptr[i]))
1789 len = i;
1792 // In Unicode mode, convert clipboard text to UTF-8
1793 if (IsUnicodeMode()) {
1794 wchar_t *uptr = new wchar_t[len+1];
1796 unsigned int ulen = ::MultiByteToWideChar(CP_ACP, 0,
1797 ptr, len, uptr, len+1);
1799 unsigned int mlen = UTF8Length(uptr, ulen);
1800 char *putf = new char[mlen + 1];
1801 if (putf) {
1802 // CP_UTF8 not available on Windows 95, so use UTF8FromUTF16()
1803 UTF8FromUTF16(uptr, ulen, putf, mlen);
1806 delete []uptr;
1808 if (putf) {
1809 InsertPasteText(putf, mlen, selStart, isRectangular, isLine);
1810 delete []putf;
1812 } else {
1813 InsertPasteText(ptr, len, selStart, isRectangular, isLine);
1816 memSelection.Unlock();
1819 ::CloseClipboard();
1820 Redraw();
1823 void ScintillaWin::CreateCallTipWindow(PRectangle) {
1824 if (!ct.wCallTip.Created()) {
1825 ct.wCallTip = ::CreateWindow(callClassName, TEXT("ACallTip"),
1826 WS_POPUP, 100, 100, 150, 20,
1827 MainHWND(), 0,
1828 GetWindowInstance(MainHWND()),
1829 this);
1830 ct.wDraw = ct.wCallTip;
1834 void ScintillaWin::AddToPopUp(const char *label, int cmd, bool enabled) {
1835 HMENU hmenuPopup = reinterpret_cast<HMENU>(popup.GetID());
1836 if (!label[0])
1837 ::AppendMenuA(hmenuPopup, MF_SEPARATOR, 0, "");
1838 else if (enabled)
1839 ::AppendMenuA(hmenuPopup, MF_STRING, cmd, label);
1840 else
1841 ::AppendMenuA(hmenuPopup, MF_STRING | MF_DISABLED | MF_GRAYED, cmd, label);
1844 void ScintillaWin::ClaimSelection() {
1845 // Windows does not have a primary selection
1848 /// Implement IUnknown
1850 STDMETHODIMP_(ULONG)FormatEnumerator_AddRef(FormatEnumerator *fe);
1851 STDMETHODIMP FormatEnumerator_QueryInterface(FormatEnumerator *fe, REFIID riid, PVOID *ppv) {
1852 //Platform::DebugPrintf("EFE QI");
1853 *ppv = NULL;
1854 if (riid == IID_IUnknown)
1855 *ppv = reinterpret_cast<IEnumFORMATETC *>(fe);
1856 if (riid == IID_IEnumFORMATETC)
1857 *ppv = reinterpret_cast<IEnumFORMATETC *>(fe);
1858 if (!*ppv)
1859 return E_NOINTERFACE;
1860 FormatEnumerator_AddRef(fe);
1861 return S_OK;
1863 STDMETHODIMP_(ULONG)FormatEnumerator_AddRef(FormatEnumerator *fe) {
1864 return ++fe->ref;
1866 STDMETHODIMP_(ULONG)FormatEnumerator_Release(FormatEnumerator *fe) {
1867 fe->ref--;
1868 if (fe->ref > 0)
1869 return fe->ref;
1870 delete fe;
1871 return 0;
1873 /// Implement IEnumFORMATETC
1874 STDMETHODIMP FormatEnumerator_Next(FormatEnumerator *fe, ULONG celt, FORMATETC *rgelt, ULONG *pceltFetched) {
1875 //Platform::DebugPrintf("EFE Next %d %d", fe->pos, celt);
1876 if (rgelt == NULL) return E_POINTER;
1877 // We only support one format, so this is simple.
1878 unsigned int putPos = 0;
1879 while ((fe->pos < fe->formatsLen) && (putPos < celt)) {
1880 rgelt->cfFormat = fe->formats[fe->pos];
1881 rgelt->ptd = 0;
1882 rgelt->dwAspect = DVASPECT_CONTENT;
1883 rgelt->lindex = -1;
1884 rgelt->tymed = TYMED_HGLOBAL;
1885 fe->pos++;
1886 putPos++;
1888 if (pceltFetched)
1889 *pceltFetched = putPos;
1890 return putPos ? S_OK : S_FALSE;
1892 STDMETHODIMP FormatEnumerator_Skip(FormatEnumerator *fe, ULONG celt) {
1893 fe->pos += celt;
1894 return S_OK;
1896 STDMETHODIMP FormatEnumerator_Reset(FormatEnumerator *fe) {
1897 fe->pos = 0;
1898 return S_OK;
1900 STDMETHODIMP FormatEnumerator_Clone(FormatEnumerator *fe, IEnumFORMATETC **ppenum) {
1901 FormatEnumerator *pfe;
1902 try {
1903 pfe = new FormatEnumerator(fe->pos, fe->formats, fe->formatsLen);
1904 } catch (...) {
1905 return E_OUTOFMEMORY;
1907 return FormatEnumerator_QueryInterface(pfe, IID_IEnumFORMATETC,
1908 reinterpret_cast<void **>(ppenum));
1911 static VFunction *vtFormatEnumerator[] = {
1912 (VFunction *)(FormatEnumerator_QueryInterface),
1913 (VFunction *)(FormatEnumerator_AddRef),
1914 (VFunction *)(FormatEnumerator_Release),
1915 (VFunction *)(FormatEnumerator_Next),
1916 (VFunction *)(FormatEnumerator_Skip),
1917 (VFunction *)(FormatEnumerator_Reset),
1918 (VFunction *)(FormatEnumerator_Clone)
1921 FormatEnumerator::FormatEnumerator(int pos_, CLIPFORMAT formats_[], int formatsLen_) {
1922 vtbl = vtFormatEnumerator;
1923 ref = 0; // First QI adds first reference...
1924 pos = pos_;
1925 formatsLen = formatsLen_;
1926 for (int i=0; i<formatsLen; i++)
1927 formats[i] = formats_[i];
1930 /// Implement IUnknown
1931 STDMETHODIMP DropSource_QueryInterface(DropSource *ds, REFIID riid, PVOID *ppv) {
1932 return ds->sci->QueryInterface(riid, ppv);
1934 STDMETHODIMP_(ULONG)DropSource_AddRef(DropSource *ds) {
1935 return ds->sci->AddRef();
1937 STDMETHODIMP_(ULONG)DropSource_Release(DropSource *ds) {
1938 return ds->sci->Release();
1941 /// Implement IDropSource
1942 STDMETHODIMP DropSource_QueryContinueDrag(DropSource *, BOOL fEsc, DWORD grfKeyState) {
1943 if (fEsc)
1944 return DRAGDROP_S_CANCEL;
1945 if (!(grfKeyState & MK_LBUTTON))
1946 return DRAGDROP_S_DROP;
1947 return S_OK;
1950 STDMETHODIMP DropSource_GiveFeedback(DropSource *, DWORD) {
1951 return DRAGDROP_S_USEDEFAULTCURSORS;
1954 static VFunction *vtDropSource[] = {
1955 (VFunction *)(DropSource_QueryInterface),
1956 (VFunction *)(DropSource_AddRef),
1957 (VFunction *)(DropSource_Release),
1958 (VFunction *)(DropSource_QueryContinueDrag),
1959 (VFunction *)(DropSource_GiveFeedback)
1962 DropSource::DropSource() {
1963 vtbl = vtDropSource;
1964 sci = 0;
1967 /// Implement IUnkown
1968 STDMETHODIMP DataObject_QueryInterface(DataObject *pd, REFIID riid, PVOID *ppv) {
1969 //Platform::DebugPrintf("DO QI %x\n", pd);
1970 return pd->sci->QueryInterface(riid, ppv);
1972 STDMETHODIMP_(ULONG)DataObject_AddRef(DataObject *pd) {
1973 return pd->sci->AddRef();
1975 STDMETHODIMP_(ULONG)DataObject_Release(DataObject *pd) {
1976 return pd->sci->Release();
1978 /// Implement IDataObject
1979 STDMETHODIMP DataObject_GetData(DataObject *pd, FORMATETC *pFEIn, STGMEDIUM *pSTM) {
1980 return pd->sci->GetData(pFEIn, pSTM);
1983 STDMETHODIMP DataObject_GetDataHere(DataObject *, FORMATETC *, STGMEDIUM *) {
1984 //Platform::DebugPrintf("DOB GetDataHere\n");
1985 return E_NOTIMPL;
1988 STDMETHODIMP DataObject_QueryGetData(DataObject *pd, FORMATETC *pFE) {
1989 if (pd->sci->DragIsRectangularOK(pFE->cfFormat) &&
1990 pFE->ptd == 0 &&
1991 (pFE->dwAspect & DVASPECT_CONTENT) != 0 &&
1992 pFE->lindex == -1 &&
1993 (pFE->tymed & TYMED_HGLOBAL) != 0
1995 return S_OK;
1998 bool formatOK = (pFE->cfFormat == CF_TEXT) ||
1999 ((pFE->cfFormat == CF_UNICODETEXT) && pd->sci->IsUnicodeMode());
2000 if (!formatOK ||
2001 pFE->ptd != 0 ||
2002 (pFE->dwAspect & DVASPECT_CONTENT) == 0 ||
2003 pFE->lindex != -1 ||
2004 (pFE->tymed & TYMED_HGLOBAL) == 0
2006 //Platform::DebugPrintf("DOB QueryGetData No %x\n",pFE->cfFormat);
2007 //return DATA_E_FORMATETC;
2008 return S_FALSE;
2010 //Platform::DebugPrintf("DOB QueryGetData OK %x\n",pFE->cfFormat);
2011 return S_OK;
2014 STDMETHODIMP DataObject_GetCanonicalFormatEtc(DataObject *pd, FORMATETC *, FORMATETC *pFEOut) {
2015 //Platform::DebugPrintf("DOB GetCanon\n");
2016 if (pd->sci->IsUnicodeMode())
2017 pFEOut->cfFormat = CF_UNICODETEXT;
2018 else
2019 pFEOut->cfFormat = CF_TEXT;
2020 pFEOut->ptd = 0;
2021 pFEOut->dwAspect = DVASPECT_CONTENT;
2022 pFEOut->lindex = -1;
2023 pFEOut->tymed = TYMED_HGLOBAL;
2024 return S_OK;
2027 STDMETHODIMP DataObject_SetData(DataObject *, FORMATETC *, STGMEDIUM *, BOOL) {
2028 //Platform::DebugPrintf("DOB SetData\n");
2029 return E_FAIL;
2032 STDMETHODIMP DataObject_EnumFormatEtc(DataObject *pd, DWORD dwDirection, IEnumFORMATETC **ppEnum) {
2033 try {
2034 //Platform::DebugPrintf("DOB EnumFormatEtc %d\n", dwDirection);
2035 if (dwDirection != DATADIR_GET) {
2036 *ppEnum = 0;
2037 return E_FAIL;
2039 FormatEnumerator *pfe;
2040 if (pd->sci->IsUnicodeMode()) {
2041 CLIPFORMAT formats[] = {CF_UNICODETEXT, CF_TEXT};
2042 pfe = new FormatEnumerator(0, formats, 2);
2043 } else {
2044 CLIPFORMAT formats[] = {CF_TEXT};
2045 pfe = new FormatEnumerator(0, formats, 1);
2047 return FormatEnumerator_QueryInterface(pfe, IID_IEnumFORMATETC,
2048 reinterpret_cast<void **>(ppEnum));
2049 } catch (std::bad_alloc &) {
2050 pd->sci->errorStatus = SC_STATUS_BADALLOC;
2051 return E_OUTOFMEMORY;
2052 } catch (...) {
2053 pd->sci->errorStatus = SC_STATUS_FAILURE;
2054 return E_FAIL;
2058 STDMETHODIMP DataObject_DAdvise(DataObject *, FORMATETC *, DWORD, IAdviseSink *, PDWORD) {
2059 //Platform::DebugPrintf("DOB DAdvise\n");
2060 return E_FAIL;
2063 STDMETHODIMP DataObject_DUnadvise(DataObject *, DWORD) {
2064 //Platform::DebugPrintf("DOB DUnadvise\n");
2065 return E_FAIL;
2068 STDMETHODIMP DataObject_EnumDAdvise(DataObject *, IEnumSTATDATA **) {
2069 //Platform::DebugPrintf("DOB EnumDAdvise\n");
2070 return E_FAIL;
2073 static VFunction *vtDataObject[] = {
2074 (VFunction *)(DataObject_QueryInterface),
2075 (VFunction *)(DataObject_AddRef),
2076 (VFunction *)(DataObject_Release),
2077 (VFunction *)(DataObject_GetData),
2078 (VFunction *)(DataObject_GetDataHere),
2079 (VFunction *)(DataObject_QueryGetData),
2080 (VFunction *)(DataObject_GetCanonicalFormatEtc),
2081 (VFunction *)(DataObject_SetData),
2082 (VFunction *)(DataObject_EnumFormatEtc),
2083 (VFunction *)(DataObject_DAdvise),
2084 (VFunction *)(DataObject_DUnadvise),
2085 (VFunction *)(DataObject_EnumDAdvise)
2088 DataObject::DataObject() {
2089 vtbl = vtDataObject;
2090 sci = 0;
2093 /// Implement IUnknown
2094 STDMETHODIMP DropTarget_QueryInterface(DropTarget *dt, REFIID riid, PVOID *ppv) {
2095 //Platform::DebugPrintf("DT QI %x\n", dt);
2096 return dt->sci->QueryInterface(riid, ppv);
2098 STDMETHODIMP_(ULONG)DropTarget_AddRef(DropTarget *dt) {
2099 return dt->sci->AddRef();
2101 STDMETHODIMP_(ULONG)DropTarget_Release(DropTarget *dt) {
2102 return dt->sci->Release();
2105 /// Implement IDropTarget by forwarding to Scintilla
2106 STDMETHODIMP DropTarget_DragEnter(DropTarget *dt, LPDATAOBJECT pIDataSource, DWORD grfKeyState,
2107 POINTL pt, PDWORD pdwEffect) {
2108 try {
2109 return dt->sci->DragEnter(pIDataSource, grfKeyState, pt, pdwEffect);
2110 } catch (...) {
2111 dt->sci->errorStatus = SC_STATUS_FAILURE;
2113 return E_FAIL;
2115 STDMETHODIMP DropTarget_DragOver(DropTarget *dt, DWORD grfKeyState, POINTL pt, PDWORD pdwEffect) {
2116 try {
2117 return dt->sci->DragOver(grfKeyState, pt, pdwEffect);
2118 } catch (...) {
2119 dt->sci->errorStatus = SC_STATUS_FAILURE;
2121 return E_FAIL;
2123 STDMETHODIMP DropTarget_DragLeave(DropTarget *dt) {
2124 try {
2125 return dt->sci->DragLeave();
2126 } catch (...) {
2127 dt->sci->errorStatus = SC_STATUS_FAILURE;
2129 return E_FAIL;
2131 STDMETHODIMP DropTarget_Drop(DropTarget *dt, LPDATAOBJECT pIDataSource, DWORD grfKeyState,
2132 POINTL pt, PDWORD pdwEffect) {
2133 try {
2134 return dt->sci->Drop(pIDataSource, grfKeyState, pt, pdwEffect);
2135 } catch (...) {
2136 dt->sci->errorStatus = SC_STATUS_FAILURE;
2138 return E_FAIL;
2141 static VFunction *vtDropTarget[] = {
2142 (VFunction *)(DropTarget_QueryInterface),
2143 (VFunction *)(DropTarget_AddRef),
2144 (VFunction *)(DropTarget_Release),
2145 (VFunction *)(DropTarget_DragEnter),
2146 (VFunction *)(DropTarget_DragOver),
2147 (VFunction *)(DropTarget_DragLeave),
2148 (VFunction *)(DropTarget_Drop)
2151 DropTarget::DropTarget() {
2152 vtbl = vtDropTarget;
2153 sci = 0;
2157 * DBCS: support Input Method Editor (IME).
2158 * Called when IME Window opened.
2160 void ScintillaWin::ImeStartComposition() {
2161 #ifndef __DMC__
2162 // Digital Mars compiler does not include Imm library
2163 if (caret.active) {
2164 // Move IME Window to current caret position
2165 HIMC hIMC = ::ImmGetContext(MainHWND());
2166 Point pos = PointMainCaret();
2167 COMPOSITIONFORM CompForm;
2168 CompForm.dwStyle = CFS_POINT;
2169 CompForm.ptCurrentPos.x = pos.x;
2170 CompForm.ptCurrentPos.y = pos.y;
2172 ::ImmSetCompositionWindow(hIMC, &CompForm);
2174 // Set font of IME window to same as surrounded text.
2175 if (stylesValid) {
2176 // Since the style creation code has been made platform independent,
2177 // The logfont for the IME is recreated here.
2178 int styleHere = (pdoc->StyleAt(sel.MainCaret())) & 31;
2179 LOGFONTA lf = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ""};
2180 int sizeZoomed = vs.styles[styleHere].size + vs.zoomLevel * SC_FONT_SIZE_MULTIPLIER;
2181 if (sizeZoomed <= 2 * SC_FONT_SIZE_MULTIPLIER) // Hangs if sizeZoomed <= 1
2182 sizeZoomed = 2 * SC_FONT_SIZE_MULTIPLIER;
2183 AutoSurface surface(this);
2184 int deviceHeight = sizeZoomed;
2185 if (surface) {
2186 deviceHeight = (sizeZoomed * surface->LogPixelsY()) / 72;
2188 // The negative is to allow for leading
2189 lf.lfHeight = -(abs(deviceHeight / SC_FONT_SIZE_MULTIPLIER));
2190 lf.lfWeight = vs.styles[styleHere].weight;
2191 lf.lfItalic = static_cast<BYTE>(vs.styles[styleHere].italic ? 1 : 0);
2192 lf.lfCharSet = DEFAULT_CHARSET;
2193 lf.lfFaceName[0] = '\0';
2194 if (vs.styles[styleHere].fontName)
2195 strcpy(lf.lfFaceName, vs.styles[styleHere].fontName);
2197 ::ImmSetCompositionFontA(hIMC, &lf);
2199 ::ImmReleaseContext(MainHWND(), hIMC);
2200 // Caret is displayed in IME window. So, caret in Scintilla is useless.
2201 DropCaret();
2203 #endif
2206 /** Called when IME Window closed. */
2207 void ScintillaWin::ImeEndComposition() {
2208 ShowCaretAtCurrentPosition();
2211 void ScintillaWin::AddCharBytes(char b0, char b1) {
2213 int inputCodePage = InputCodePage();
2214 if (inputCodePage && IsUnicodeMode()) {
2215 char utfval[4] = "\0\0\0";
2216 char ansiChars[3];
2217 wchar_t wcs[2];
2218 if (b0) { // Two bytes from IME
2219 ansiChars[0] = b0;
2220 ansiChars[1] = b1;
2221 ansiChars[2] = '\0';
2222 ::MultiByteToWideChar(inputCodePage, 0, ansiChars, 2, wcs, 1);
2223 } else {
2224 ansiChars[0] = b1;
2225 ansiChars[1] = '\0';
2226 ::MultiByteToWideChar(inputCodePage, 0, ansiChars, 1, wcs, 1);
2228 unsigned int len = UTF8Length(wcs, 1);
2229 UTF8FromUTF16(wcs, 1, utfval, len);
2230 utfval[len] = '\0';
2231 AddCharUTF(utfval, len ? len : 1);
2232 } else if (b0) {
2233 char dbcsChars[3];
2234 dbcsChars[0] = b0;
2235 dbcsChars[1] = b1;
2236 dbcsChars[2] = '\0';
2237 AddCharUTF(dbcsChars, 2, true);
2238 } else {
2239 AddChar(b1);
2243 void ScintillaWin::GetIntelliMouseParameters() {
2244 // This retrieves the number of lines per scroll as configured inthe Mouse Properties sheet in Control Panel
2245 ::SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, &linesPerScroll, 0);
2248 void ScintillaWin::CopyToClipboard(const SelectionText &selectedText) {
2249 if (!::OpenClipboard(MainHWND()))
2250 return;
2251 ::EmptyClipboard();
2253 GlobalMemory uniText;
2255 // Default Scintilla behaviour in Unicode mode
2256 if (IsUnicodeMode()) {
2257 int uchars = UTF16Length(selectedText.s, selectedText.len);
2258 uniText.Allocate(2 * uchars);
2259 if (uniText) {
2260 UTF16FromUTF8(selectedText.s, selectedText.len, static_cast<wchar_t *>(uniText.ptr), uchars);
2262 } else {
2263 // Not Unicode mode
2264 // Convert to Unicode using the current Scintilla code page
2265 UINT cpSrc = CodePageFromCharSet(
2266 selectedText.characterSet, selectedText.codePage);
2267 int uLen = ::MultiByteToWideChar(cpSrc, 0, selectedText.s, selectedText.len, 0, 0);
2268 uniText.Allocate(2 * uLen);
2269 if (uniText) {
2270 ::MultiByteToWideChar(cpSrc, 0, selectedText.s, selectedText.len,
2271 static_cast<wchar_t *>(uniText.ptr), uLen);
2275 if (uniText) {
2276 if (!IsNT()) {
2277 // Copy ANSI text to clipboard on Windows 9x
2278 // Convert from Unicode text, so other ANSI programs can
2279 // paste the text
2280 // Windows NT, 2k, XP automatically generates CF_TEXT
2281 GlobalMemory ansiText;
2282 ansiText.Allocate(selectedText.len);
2283 if (ansiText) {
2284 ::WideCharToMultiByte(CP_ACP, 0, static_cast<wchar_t *>(uniText.ptr), -1,
2285 static_cast<char *>(ansiText.ptr), selectedText.len, NULL, NULL);
2286 ansiText.SetClip(CF_TEXT);
2289 uniText.SetClip(CF_UNICODETEXT);
2290 } else {
2291 // There was a failure - try to copy at least ANSI text
2292 GlobalMemory ansiText;
2293 ansiText.Allocate(selectedText.len);
2294 if (ansiText) {
2295 memcpy(static_cast<char *>(ansiText.ptr), selectedText.s, selectedText.len);
2296 ansiText.SetClip(CF_TEXT);
2300 if (selectedText.rectangular) {
2301 ::SetClipboardData(cfColumnSelect, 0);
2304 if (selectedText.lineCopy) {
2305 ::SetClipboardData(cfLineSelect, 0);
2308 ::CloseClipboard();
2311 void ScintillaWin::ScrollMessage(WPARAM wParam) {
2312 //DWORD dwStart = timeGetTime();
2313 //Platform::DebugPrintf("Scroll %x %d\n", wParam, lParam);
2315 SCROLLINFO sci;
2316 memset(&sci, 0, sizeof(sci));
2317 sci.cbSize = sizeof(sci);
2318 sci.fMask = SIF_ALL;
2320 GetScrollInfo(SB_VERT, &sci);
2322 //Platform::DebugPrintf("ScrollInfo %d mask=%x min=%d max=%d page=%d pos=%d track=%d\n", b,sci.fMask,
2323 //sci.nMin, sci.nMax, sci.nPage, sci.nPos, sci.nTrackPos);
2325 int topLineNew = topLine;
2326 switch (LoWord(wParam)) {
2327 case SB_LINEUP:
2328 topLineNew -= 1;
2329 break;
2330 case SB_LINEDOWN:
2331 topLineNew += 1;
2332 break;
2333 case SB_PAGEUP:
2334 topLineNew -= LinesToScroll(); break;
2335 case SB_PAGEDOWN: topLineNew += LinesToScroll(); break;
2336 case SB_TOP: topLineNew = 0; break;
2337 case SB_BOTTOM: topLineNew = MaxScrollPos(); break;
2338 case SB_THUMBPOSITION: topLineNew = sci.nTrackPos; break;
2339 case SB_THUMBTRACK: topLineNew = sci.nTrackPos; break;
2341 ScrollTo(topLineNew);
2344 void ScintillaWin::HorizontalScrollMessage(WPARAM wParam) {
2345 int xPos = xOffset;
2346 PRectangle rcText = GetTextRectangle();
2347 int pageWidth = rcText.Width() * 2 / 3;
2348 switch (LoWord(wParam)) {
2349 case SB_LINEUP:
2350 xPos -= 20;
2351 break;
2352 case SB_LINEDOWN: // May move past the logical end
2353 xPos += 20;
2354 break;
2355 case SB_PAGEUP:
2356 xPos -= pageWidth;
2357 break;
2358 case SB_PAGEDOWN:
2359 xPos += pageWidth;
2360 if (xPos > scrollWidth - rcText.Width()) { // Hit the end exactly
2361 xPos = scrollWidth - rcText.Width();
2363 break;
2364 case SB_TOP:
2365 xPos = 0;
2366 break;
2367 case SB_BOTTOM:
2368 xPos = scrollWidth;
2369 break;
2370 case SB_THUMBPOSITION:
2371 case SB_THUMBTRACK: {
2372 // 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 =]
2373 SCROLLINFO si;
2374 si.cbSize = sizeof(si);
2375 si.fMask = SIF_TRACKPOS;
2376 if (GetScrollInfo(SB_HORZ, &si)) {
2377 xPos = si.nTrackPos;
2380 break;
2382 HorizontalScrollTo(xPos);
2386 * Redraw all of text area.
2387 * This paint will not be abandoned.
2389 void ScintillaWin::FullPaint() {
2390 if (technology == SC_TECHNOLOGY_DEFAULT) {
2391 HDC hdc = ::GetDC(MainHWND());
2392 FullPaintDC(hdc);
2393 ::ReleaseDC(MainHWND(), hdc);
2394 } else {
2395 FullPaintDC(0);
2400 * Redraw all of text area on the specified DC.
2401 * This paint will not be abandoned.
2403 void ScintillaWin::FullPaintDC(HDC hdc) {
2404 paintState = painting;
2405 rcPaint = GetClientRectangle();
2406 paintingAllText = true;
2407 if (technology == SC_TECHNOLOGY_DEFAULT) {
2408 AutoSurface surfaceWindow(hdc, this);
2409 if (surfaceWindow) {
2410 Paint(surfaceWindow, rcPaint);
2411 surfaceWindow->Release();
2413 } else {
2414 #if defined(USE_D2D)
2415 EnsureRenderTarget();
2416 AutoSurface surfaceWindow(pRenderTarget, this);
2417 if (surfaceWindow) {
2418 pRenderTarget->BeginDraw();
2419 Paint(surfaceWindow, rcPaint);
2420 surfaceWindow->Release();
2421 HRESULT hr = pRenderTarget->EndDraw();
2422 if (hr == D2DERR_RECREATE_TARGET) {
2423 DropRenderTarget();
2426 #endif
2428 paintState = notPainting;
2431 static bool CompareDevCap(HDC hdc, HDC hOtherDC, int nIndex) {
2432 return ::GetDeviceCaps(hdc, nIndex) == ::GetDeviceCaps(hOtherDC, nIndex);
2435 bool ScintillaWin::IsCompatibleDC(HDC hOtherDC) {
2436 HDC hdc = ::GetDC(MainHWND());
2437 bool isCompatible =
2438 CompareDevCap(hdc, hOtherDC, TECHNOLOGY) &&
2439 CompareDevCap(hdc, hOtherDC, LOGPIXELSY) &&
2440 CompareDevCap(hdc, hOtherDC, LOGPIXELSX) &&
2441 CompareDevCap(hdc, hOtherDC, BITSPIXEL) &&
2442 CompareDevCap(hdc, hOtherDC, PLANES);
2443 ::ReleaseDC(MainHWND(), hdc);
2444 return isCompatible;
2447 DWORD ScintillaWin::EffectFromState(DWORD grfKeyState) {
2448 // These are the Wordpad semantics.
2449 DWORD dwEffect;
2450 if (inDragDrop == ddDragging) // Internal defaults to move
2451 dwEffect = DROPEFFECT_MOVE;
2452 else
2453 dwEffect = DROPEFFECT_COPY;
2454 if (grfKeyState & MK_ALT)
2455 dwEffect = DROPEFFECT_MOVE;
2456 if (grfKeyState & MK_CONTROL)
2457 dwEffect = DROPEFFECT_COPY;
2458 return dwEffect;
2461 /// Implement IUnknown
2462 STDMETHODIMP ScintillaWin::QueryInterface(REFIID riid, PVOID *ppv) {
2463 *ppv = NULL;
2464 if (riid == IID_IUnknown)
2465 *ppv = reinterpret_cast<IDropTarget *>(&dt);
2466 if (riid == IID_IDropSource)
2467 *ppv = reinterpret_cast<IDropSource *>(&ds);
2468 if (riid == IID_IDropTarget)
2469 *ppv = reinterpret_cast<IDropTarget *>(&dt);
2470 if (riid == IID_IDataObject)
2471 *ppv = reinterpret_cast<IDataObject *>(&dob);
2472 if (!*ppv)
2473 return E_NOINTERFACE;
2474 return S_OK;
2477 STDMETHODIMP_(ULONG) ScintillaWin::AddRef() {
2478 return 1;
2481 STDMETHODIMP_(ULONG) ScintillaWin::Release() {
2482 return 1;
2485 /// Implement IDropTarget
2486 STDMETHODIMP ScintillaWin::DragEnter(LPDATAOBJECT pIDataSource, DWORD grfKeyState,
2487 POINTL, PDWORD pdwEffect) {
2488 if (pIDataSource == NULL)
2489 return E_POINTER;
2490 FORMATETC fmtu = {CF_UNICODETEXT, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
2491 HRESULT hrHasUText = pIDataSource->QueryGetData(&fmtu);
2492 hasOKText = (hrHasUText == S_OK);
2493 if (!hasOKText) {
2494 FORMATETC fmte = {CF_TEXT, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
2495 HRESULT hrHasText = pIDataSource->QueryGetData(&fmte);
2496 hasOKText = (hrHasText == S_OK);
2498 if (!hasOKText) {
2499 *pdwEffect = DROPEFFECT_NONE;
2500 return S_OK;
2503 *pdwEffect = EffectFromState(grfKeyState);
2504 return S_OK;
2507 STDMETHODIMP ScintillaWin::DragOver(DWORD grfKeyState, POINTL pt, PDWORD pdwEffect) {
2508 try {
2509 if (!hasOKText || pdoc->IsReadOnly()) {
2510 *pdwEffect = DROPEFFECT_NONE;
2511 return S_OK;
2514 *pdwEffect = EffectFromState(grfKeyState);
2516 // Update the cursor.
2517 POINT rpt = {pt.x, pt.y};
2518 ::ScreenToClient(MainHWND(), &rpt);
2519 SetDragPosition(SPositionFromLocation(Point(rpt.x, rpt.y), false, false, UserVirtualSpace()));
2521 return S_OK;
2522 } catch (...) {
2523 errorStatus = SC_STATUS_FAILURE;
2525 return E_FAIL;
2528 STDMETHODIMP ScintillaWin::DragLeave() {
2529 try {
2530 SetDragPosition(SelectionPosition(invalidPosition));
2531 return S_OK;
2532 } catch (...) {
2533 errorStatus = SC_STATUS_FAILURE;
2535 return E_FAIL;
2538 STDMETHODIMP ScintillaWin::Drop(LPDATAOBJECT pIDataSource, DWORD grfKeyState,
2539 POINTL pt, PDWORD pdwEffect) {
2540 try {
2541 *pdwEffect = EffectFromState(grfKeyState);
2543 if (pIDataSource == NULL)
2544 return E_POINTER;
2546 SetDragPosition(SelectionPosition(invalidPosition));
2548 STGMEDIUM medium = {0, {0}, 0};
2550 char *data = 0;
2551 bool dataAllocated = false;
2553 FORMATETC fmtu = {CF_UNICODETEXT, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
2554 HRESULT hr = pIDataSource->GetData(&fmtu, &medium);
2555 if (SUCCEEDED(hr) && medium.hGlobal) {
2556 wchar_t *udata = static_cast<wchar_t *>(::GlobalLock(medium.hGlobal));
2557 if (udata) {
2558 if (IsUnicodeMode()) {
2559 int tlen = ::GlobalSize(medium.hGlobal);
2560 // Convert UTF-16 to UTF-8
2561 int dataLen = UTF8Length(udata, tlen/2);
2562 data = new char[dataLen+1];
2563 UTF8FromUTF16(udata, tlen/2, data, dataLen);
2564 dataAllocated = true;
2565 } else {
2566 // Convert UTF-16 to ANSI
2568 // Default Scintilla behavior in Unicode mode
2569 // CF_UNICODETEXT available, but not in Unicode mode
2570 // Convert from Unicode to current Scintilla code page
2571 UINT cpDest = CodePageOfDocument();
2572 int tlen = ::WideCharToMultiByte(cpDest, 0, udata, -1,
2573 NULL, 0, NULL, NULL) - 1; // subtract 0 terminator
2574 data = new char[tlen + 1];
2575 memset(data, 0, (tlen+1));
2576 ::WideCharToMultiByte(cpDest, 0, udata, -1,
2577 data, tlen + 1, NULL, NULL);
2578 dataAllocated = true;
2583 if (!data) {
2584 FORMATETC fmte = {CF_TEXT, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
2585 hr = pIDataSource->GetData(&fmte, &medium);
2586 if (SUCCEEDED(hr) && medium.hGlobal) {
2587 data = static_cast<char *>(::GlobalLock(medium.hGlobal));
2591 if (data && convertPastes) {
2592 // Convert line endings of the drop into our local line-endings mode
2593 int len = static_cast<int>(strlen(data));
2594 char *convertedText = Document::TransformLineEnds(&len, data, len, pdoc->eolMode);
2595 if (dataAllocated)
2596 delete []data;
2597 data = convertedText;
2598 dataAllocated = true;
2601 if (!data) {
2602 //Platform::DebugPrintf("Bad data format: 0x%x\n", hres);
2603 return hr;
2606 FORMATETC fmtr = {cfColumnSelect, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
2607 HRESULT hrRectangular = pIDataSource->QueryGetData(&fmtr);
2609 POINT rpt = {pt.x, pt.y};
2610 ::ScreenToClient(MainHWND(), &rpt);
2611 SelectionPosition movePos = SPositionFromLocation(Point(rpt.x, rpt.y), false, false, UserVirtualSpace());
2613 DropAt(movePos, data, *pdwEffect == DROPEFFECT_MOVE, hrRectangular == S_OK);
2615 ::GlobalUnlock(medium.hGlobal);
2617 // Free data
2618 if (medium.pUnkForRelease != NULL)
2619 medium.pUnkForRelease->Release();
2620 else
2621 ::GlobalFree(medium.hGlobal);
2623 if (dataAllocated)
2624 delete []data;
2626 return S_OK;
2627 } catch (...) {
2628 errorStatus = SC_STATUS_FAILURE;
2630 return E_FAIL;
2633 /// Implement important part of IDataObject
2634 STDMETHODIMP ScintillaWin::GetData(FORMATETC *pFEIn, STGMEDIUM *pSTM) {
2635 bool formatOK = (pFEIn->cfFormat == CF_TEXT) ||
2636 ((pFEIn->cfFormat == CF_UNICODETEXT) && IsUnicodeMode());
2637 if (!formatOK ||
2638 pFEIn->ptd != 0 ||
2639 (pFEIn->dwAspect & DVASPECT_CONTENT) == 0 ||
2640 pFEIn->lindex != -1 ||
2641 (pFEIn->tymed & TYMED_HGLOBAL) == 0
2643 //Platform::DebugPrintf("DOB GetData No %d %x %x fmt=%x\n", lenDrag, pFEIn, pSTM, pFEIn->cfFormat);
2644 return DATA_E_FORMATETC;
2646 pSTM->tymed = TYMED_HGLOBAL;
2647 //Platform::DebugPrintf("DOB GetData OK %d %x %x\n", lenDrag, pFEIn, pSTM);
2649 GlobalMemory text;
2650 if (pFEIn->cfFormat == CF_UNICODETEXT) {
2651 int uchars = UTF16Length(drag.s, drag.len);
2652 text.Allocate(2 * uchars);
2653 if (text) {
2654 UTF16FromUTF8(drag.s, drag.len, static_cast<wchar_t *>(text.ptr), uchars);
2656 } else {
2657 text.Allocate(drag.len);
2658 if (text) {
2659 memcpy(static_cast<char *>(text.ptr), drag.s, drag.len);
2662 pSTM->hGlobal = text ? text.Unlock() : 0;
2663 pSTM->pUnkForRelease = 0;
2664 return S_OK;
2667 bool ScintillaWin::Register(HINSTANCE hInstance_) {
2669 hInstance = hInstance_;
2670 bool result;
2672 // Register the Scintilla class
2673 if (IsNT()) {
2675 // Register Scintilla as a wide character window
2676 WNDCLASSEXW wndclass;
2677 wndclass.cbSize = sizeof(wndclass);
2678 wndclass.style = CS_GLOBALCLASS | CS_HREDRAW | CS_VREDRAW;
2679 wndclass.lpfnWndProc = ScintillaWin::SWndProc;
2680 wndclass.cbClsExtra = 0;
2681 wndclass.cbWndExtra = sizeof(ScintillaWin *);
2682 wndclass.hInstance = hInstance;
2683 wndclass.hIcon = NULL;
2684 wndclass.hCursor = NULL;
2685 wndclass.hbrBackground = NULL;
2686 wndclass.lpszMenuName = NULL;
2687 wndclass.lpszClassName = L"Scintilla";
2688 wndclass.hIconSm = 0;
2689 result = ::RegisterClassExW(&wndclass) != 0;
2690 } else {
2692 // Register Scintilla as a normal character window
2693 WNDCLASSEX wndclass;
2694 wndclass.cbSize = sizeof(wndclass);
2695 wndclass.style = CS_GLOBALCLASS | CS_HREDRAW | CS_VREDRAW;
2696 wndclass.lpfnWndProc = ScintillaWin::SWndProc;
2697 wndclass.cbClsExtra = 0;
2698 wndclass.cbWndExtra = sizeof(ScintillaWin *);
2699 wndclass.hInstance = hInstance;
2700 wndclass.hIcon = NULL;
2701 wndclass.hCursor = NULL;
2702 wndclass.hbrBackground = NULL;
2703 wndclass.lpszMenuName = NULL;
2704 wndclass.lpszClassName = scintillaClassName;
2705 wndclass.hIconSm = 0;
2706 result = ::RegisterClassEx(&wndclass) != 0;
2709 if (result) {
2710 // Register the CallTip class
2711 WNDCLASSEX wndclassc;
2712 wndclassc.cbSize = sizeof(wndclassc);
2713 wndclassc.style = CS_GLOBALCLASS | CS_HREDRAW | CS_VREDRAW;
2714 wndclassc.cbClsExtra = 0;
2715 wndclassc.cbWndExtra = sizeof(ScintillaWin *);
2716 wndclassc.hInstance = hInstance;
2717 wndclassc.hIcon = NULL;
2718 wndclassc.hbrBackground = NULL;
2719 wndclassc.lpszMenuName = NULL;
2720 wndclassc.lpfnWndProc = ScintillaWin::CTWndProc;
2721 wndclassc.hCursor = ::LoadCursor(NULL, IDC_ARROW);
2722 wndclassc.lpszClassName = callClassName;
2723 wndclassc.hIconSm = 0;
2725 result = ::RegisterClassEx(&wndclassc) != 0;
2728 return result;
2731 bool ScintillaWin::Unregister() {
2732 bool result = ::UnregisterClass(scintillaClassName, hInstance) != 0;
2733 if (::UnregisterClass(callClassName, hInstance) == 0)
2734 result = false;
2735 return result;
2738 bool ScintillaWin::HasCaretSizeChanged() {
2739 if (
2740 ( (0 != vs.caretWidth) && (sysCaretWidth != vs.caretWidth) )
2741 || ((0 != vs.lineHeight) && (sysCaretHeight != vs.lineHeight))
2743 return true;
2745 return false;
2748 BOOL ScintillaWin::CreateSystemCaret() {
2749 sysCaretWidth = vs.caretWidth;
2750 if (0 == sysCaretWidth) {
2751 sysCaretWidth = 1;
2753 sysCaretHeight = vs.lineHeight;
2754 int bitmapSize = (((sysCaretWidth + 15) & ~15) >> 3) *
2755 sysCaretHeight;
2756 char *bits = new char[bitmapSize];
2757 memset(bits, 0, bitmapSize);
2758 sysCaretBitmap = ::CreateBitmap(sysCaretWidth, sysCaretHeight, 1,
2759 1, reinterpret_cast<BYTE *>(bits));
2760 delete []bits;
2761 BOOL retval = ::CreateCaret(
2762 MainHWND(), sysCaretBitmap,
2763 sysCaretWidth, sysCaretHeight);
2764 ::ShowCaret(MainHWND());
2765 return retval;
2768 BOOL ScintillaWin::DestroySystemCaret() {
2769 ::HideCaret(MainHWND());
2770 BOOL retval = ::DestroyCaret();
2771 if (sysCaretBitmap) {
2772 ::DeleteObject(sysCaretBitmap);
2773 sysCaretBitmap = 0;
2775 return retval;
2778 sptr_t PASCAL ScintillaWin::CTWndProc(
2779 HWND hWnd, UINT iMessage, WPARAM wParam, sptr_t lParam) {
2780 // Find C++ object associated with window.
2781 ScintillaWin *sciThis = reinterpret_cast<ScintillaWin *>(PointerFromWindow(hWnd));
2782 try {
2783 // ctp will be zero if WM_CREATE not seen yet
2784 if (sciThis == 0) {
2785 if (iMessage == WM_CREATE) {
2786 // Associate CallTip object with window
2787 CREATESTRUCT *pCreate = reinterpret_cast<CREATESTRUCT *>(lParam);
2788 SetWindowPointer(hWnd, pCreate->lpCreateParams);
2789 return 0;
2790 } else {
2791 return ::DefWindowProc(hWnd, iMessage, wParam, lParam);
2793 } else {
2794 if (iMessage == WM_NCDESTROY) {
2795 ::SetWindowLong(hWnd, 0, 0);
2796 return ::DefWindowProc(hWnd, iMessage, wParam, lParam);
2797 } else if (iMessage == WM_PAINT) {
2798 PAINTSTRUCT ps;
2799 ::BeginPaint(hWnd, &ps);
2800 Surface *surfaceWindow = Surface::Allocate(sciThis->technology);
2801 if (surfaceWindow) {
2802 #if defined(USE_D2D)
2803 ID2D1HwndRenderTarget *pCTRenderTarget = 0;
2804 #endif
2805 RECT rc;
2806 GetClientRect(hWnd, &rc);
2807 // Create a Direct2D render target.
2808 if (sciThis->technology == SC_TECHNOLOGY_DEFAULT) {
2809 surfaceWindow->Init(ps.hdc, hWnd);
2810 } else {
2811 #if defined(USE_D2D)
2812 pD2DFactory->CreateHwndRenderTarget(
2813 D2D1::RenderTargetProperties(D2D1_RENDER_TARGET_TYPE_DEFAULT, D2D1::PixelFormat(), 96.0, 96.0),
2814 D2D1::HwndRenderTargetProperties(hWnd, D2D1::SizeU(rc.right - rc.left, rc.bottom - rc.top)),
2815 &pCTRenderTarget);
2816 surfaceWindow->Init(pCTRenderTarget, hWnd);
2817 pCTRenderTarget->BeginDraw();
2818 #endif
2820 surfaceWindow->SetUnicodeMode(SC_CP_UTF8 == sciThis->ct.codePage);
2821 surfaceWindow->SetDBCSMode(sciThis->ct.codePage);
2822 sciThis->ct.PaintCT(surfaceWindow);
2823 #if defined(USE_D2D)
2824 if (pCTRenderTarget)
2825 pCTRenderTarget->EndDraw();
2826 #endif
2827 surfaceWindow->Release();
2828 delete surfaceWindow;
2829 #if defined(USE_D2D)
2830 if (pCTRenderTarget)
2831 pCTRenderTarget->Release();
2832 #endif
2834 ::EndPaint(hWnd, &ps);
2835 return 0;
2836 } else if ((iMessage == WM_NCLBUTTONDOWN) || (iMessage == WM_NCLBUTTONDBLCLK)) {
2837 POINT pt;
2838 pt.x = static_cast<short>(LOWORD(lParam));
2839 pt.y = static_cast<short>(HIWORD(lParam));
2840 ScreenToClient(hWnd, &pt);
2841 sciThis->ct.MouseClick(Point(pt.x, pt.y));
2842 sciThis->CallTipClick();
2843 return 0;
2844 } else if (iMessage == WM_LBUTTONDOWN) {
2845 // This does not fire due to the hit test code
2846 sciThis->ct.MouseClick(Point::FromLong(lParam));
2847 sciThis->CallTipClick();
2848 return 0;
2849 } else if (iMessage == WM_SETCURSOR) {
2850 ::SetCursor(::LoadCursor(NULL, IDC_ARROW));
2851 return 0;
2852 } else if (iMessage == WM_NCHITTEST) {
2853 return HTCAPTION;
2854 } else {
2855 return ::DefWindowProc(hWnd, iMessage, wParam, lParam);
2858 } catch (...) {
2859 sciThis->errorStatus = SC_STATUS_FAILURE;
2861 return ::DefWindowProc(hWnd, iMessage, wParam, lParam);
2864 sptr_t ScintillaWin::DirectFunction(
2865 ScintillaWin *sci, UINT iMessage, uptr_t wParam, sptr_t lParam) {
2866 PLATFORM_ASSERT(::GetCurrentThreadId() == ::GetWindowThreadProcessId(sci->MainHWND(), NULL));
2867 return sci->WndProc(iMessage, wParam, lParam);
2870 extern "C"
2871 #ifndef STATIC_BUILD
2872 __declspec(dllexport)
2873 #endif
2874 sptr_t __stdcall Scintilla_DirectFunction(
2875 ScintillaWin *sci, UINT iMessage, uptr_t wParam, sptr_t lParam) {
2876 return sci->WndProc(iMessage, wParam, lParam);
2879 sptr_t PASCAL ScintillaWin::SWndProc(
2880 HWND hWnd, UINT iMessage, WPARAM wParam, sptr_t lParam) {
2881 //Platform::DebugPrintf("S W:%x M:%x WP:%x L:%x\n", hWnd, iMessage, wParam, lParam);
2883 // Find C++ object associated with window.
2884 ScintillaWin *sci = reinterpret_cast<ScintillaWin *>(PointerFromWindow(hWnd));
2885 // sci will be zero if WM_CREATE not seen yet
2886 if (sci == 0) {
2887 try {
2888 if (iMessage == WM_CREATE) {
2889 // Create C++ object associated with window
2890 sci = new ScintillaWin(hWnd);
2891 SetWindowPointer(hWnd, sci);
2892 return sci->WndProc(iMessage, wParam, lParam);
2894 } catch (...) {
2896 return ::DefWindowProc(hWnd, iMessage, wParam, lParam);
2897 } else {
2898 if (iMessage == WM_NCDESTROY) {
2899 try {
2900 sci->Finalise();
2901 delete sci;
2902 } catch (...) {
2904 ::SetWindowLong(hWnd, 0, 0);
2905 return ::DefWindowProc(hWnd, iMessage, wParam, lParam);
2906 } else {
2907 return sci->WndProc(iMessage, wParam, lParam);
2912 // This function is externally visible so it can be called from container when building statically.
2913 // Must be called once only.
2914 int Scintilla_RegisterClasses(void *hInstance) {
2915 Platform_Initialise(hInstance);
2916 bool result = ScintillaWin::Register(reinterpret_cast<HINSTANCE>(hInstance));
2917 #ifdef SCI_LEXER
2918 Scintilla_LinkLexers();
2919 #endif
2920 return result;
2923 // This function is externally visible so it can be called from container when building statically.
2924 int Scintilla_ReleaseResources() {
2925 bool result = ScintillaWin::Unregister();
2926 Platform_Finalise();
2927 return result;
2930 #ifndef STATIC_BUILD
2931 extern "C" int APIENTRY DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID) {
2932 //Platform::DebugPrintf("Scintilla::DllMain %d %d\n", hInstance, dwReason);
2933 if (dwReason == DLL_PROCESS_ATTACH) {
2934 if (!Scintilla_RegisterClasses(hInstance))
2935 return FALSE;
2936 } else if (dwReason == DLL_PROCESS_DETACH) {
2937 Scintilla_ReleaseResources();
2939 return TRUE;
2941 #endif