Applied unicodefont.patch
[TortoiseGit.git] / ext / scintilla / win32 / ScintillaWin.cxx
blobdb39d58a2362ed613b8034d0a274a5872877742c
1 // Scintilla source code edit control
2 /** @file ScintillaWin.cxx
3 ** Windows specific subclass of ScintillaBase.
4 **/
5 // Copyright 1998-2003 by Neil Hodgson <neilh@scintilla.org>
6 // The License.txt file describes the conditions under which this software may be distributed.
8 #include <stdlib.h>
9 #include <string.h>
10 #include <stdio.h>
11 #include <assert.h>
12 #include <ctype.h>
13 #include <limits.h>
15 #include <new>
16 #include <string>
17 #include <vector>
18 #include <map>
19 #include <algorithm>
21 #undef _WIN32_WINNT
22 #define _WIN32_WINNT 0x0500
23 #undef WINVER
24 #define WINVER 0x0500
25 #include <windows.h>
26 #include <commctrl.h>
27 #include <richedit.h>
28 #include <windowsx.h>
29 #include <zmouse.h>
30 #include <ole2.h>
32 #if defined(NTDDI_WIN7) && !defined(DISABLE_D2D)
33 #define USE_D2D 1
34 #endif
36 #if defined(USE_D2D)
37 #include <d2d1.h>
38 #include <dwrite.h>
39 #endif
41 #include "Platform.h"
43 #include "ILexer.h"
44 #include "Scintilla.h"
46 #ifdef SCI_LEXER
47 #include "SciLexer.h"
48 #endif
49 #include "StringCopy.h"
50 #ifdef SCI_LEXER
51 #include "LexerModule.h"
52 #endif
53 #include "SplitVector.h"
54 #include "Partitioning.h"
55 #include "RunStyles.h"
56 #include "ContractionState.h"
57 #include "CellBuffer.h"
58 #include "CallTip.h"
59 #include "KeyMap.h"
60 #include "Indicator.h"
61 #include "XPM.h"
62 #include "LineMarker.h"
63 #include "Style.h"
64 #include "ViewStyle.h"
65 #include "CharClassify.h"
66 #include "Decoration.h"
67 #include "CaseFolder.h"
68 #include "Document.h"
69 #include "CaseConvert.h"
70 #include "UniConversion.h"
71 #include "Selection.h"
72 #include "PositionCache.h"
73 #include "Editor.h"
75 #include "AutoComplete.h"
76 #include "ScintillaBase.h"
78 #ifdef SCI_LEXER
79 #include "ExternalLexer.h"
80 #endif
82 #include "PlatWin.h"
84 #ifndef SPI_GETWHEELSCROLLLINES
85 #define SPI_GETWHEELSCROLLLINES 104
86 #endif
88 #ifndef WM_UNICHAR
89 #define WM_UNICHAR 0x0109
90 #endif
92 #ifndef UNICODE_NOCHAR
93 #define UNICODE_NOCHAR 0xFFFF
94 #endif
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 static void *PointerFromWindow(HWND hWnd) {
114 return reinterpret_cast<void *>(::GetWindowLongPtr(hWnd, 0));
117 static void SetWindowPointer(HWND hWnd, void *ptr) {
118 ::SetWindowLongPtr(hWnd, 0, reinterpret_cast<LONG_PTR>(ptr));
121 static void SetWindowID(HWND hWnd, int identifier) {
122 ::SetWindowLongPtr(hWnd, GWLP_ID, identifier);
125 static Point PointFromPOINT(POINT pt) {
126 return Point::FromInts(pt.x, pt.y);
129 class ScintillaWin; // Forward declaration for COM interface subobjects
131 typedef void VFunction(void);
133 static HMODULE commctrl32 = 0;
137 class FormatEnumerator {
138 public:
139 VFunction **vtbl;
140 int ref;
141 unsigned int pos;
142 std::vector<CLIPFORMAT> formats;
143 FormatEnumerator(int pos_, CLIPFORMAT formats_[], size_t formatsLen_);
148 class DropSource {
149 public:
150 VFunction **vtbl;
151 ScintillaWin *sci;
152 DropSource();
157 class DataObject {
158 public:
159 VFunction **vtbl;
160 ScintillaWin *sci;
161 DataObject();
166 class DropTarget {
167 public:
168 VFunction **vtbl;
169 ScintillaWin *sci;
170 DropTarget();
175 class ScintillaWin :
176 public ScintillaBase {
178 bool lastKeyDownConsumed;
180 bool capturedMouse;
181 bool trackedMouseLeave;
182 TrackMouseEventSig TrackMouseEventFn;
184 unsigned int linesPerScroll; ///< Intellimouse support
185 int wheelDelta; ///< Wheel delta from roll
187 HRGN hRgnUpdate;
189 bool hasOKText;
191 CLIPFORMAT cfColumnSelect;
192 CLIPFORMAT cfBorlandIDEBlockType;
193 CLIPFORMAT cfLineSelect;
195 HRESULT hrOle;
196 DropSource ds;
197 DataObject dob;
198 DropTarget dt;
200 static HINSTANCE hInstance;
202 #if defined(USE_D2D)
203 ID2D1HwndRenderTarget *pRenderTarget;
204 bool renderTargetValid;
205 #endif
207 explicit ScintillaWin(HWND hwnd);
208 ScintillaWin(const ScintillaWin &);
209 virtual ~ScintillaWin();
210 ScintillaWin &operator=(const ScintillaWin &);
212 virtual void Initialise();
213 virtual void Finalise();
214 #if defined(USE_D2D)
215 void EnsureRenderTarget();
216 void DropRenderTarget();
217 #endif
218 HWND MainHWND();
220 static sptr_t DirectFunction(
221 sptr_t ptr, UINT iMessage, uptr_t wParam, sptr_t lParam);
222 static sptr_t PASCAL SWndProc(
223 HWND hWnd, UINT iMessage, WPARAM wParam, sptr_t lParam);
224 static sptr_t PASCAL CTWndProc(
225 HWND hWnd, UINT iMessage, WPARAM wParam, sptr_t lParam);
227 enum { invalidTimerID, standardTimerID, idleTimerID };
229 virtual bool DragThreshold(Point ptStart, Point ptNow);
230 virtual void StartDrag();
231 sptr_t WndPaint(uptr_t wParam);
232 sptr_t HandleComposition(uptr_t wParam, sptr_t lParam);
233 UINT CodePageOfDocument();
234 virtual bool ValidCodePage(int codePage) const;
235 virtual sptr_t DefWndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam);
236 virtual bool SetIdle(bool on);
237 virtual void SetTicking(bool on);
238 virtual void SetMouseCapture(bool on);
239 virtual bool HaveMouseCapture();
240 virtual void SetTrackMouseLeaveEvent(bool on);
241 virtual bool PaintContains(PRectangle rc);
242 virtual void ScrollText(int linesToMove);
243 virtual void UpdateSystemCaret();
244 virtual void SetVerticalScrollPos();
245 virtual void SetHorizontalScrollPos();
246 virtual bool ModifyScrollBars(int nMax, int nPage);
247 virtual void NotifyChange();
248 virtual void NotifyFocus(bool focus);
249 virtual void SetCtrlID(int identifier);
250 virtual int GetCtrlID();
251 virtual void NotifyParent(SCNotification scn);
252 virtual void NotifyParent(SCNotification * scn);
253 virtual void NotifyDoubleClick(Point pt, int modifiers);
254 virtual CaseFolder *CaseFolderForEncoding();
255 virtual std::string CaseMapString(const std::string &s, int caseMapping);
256 virtual void Copy();
257 virtual void CopyAllowLine();
258 virtual bool CanPaste();
259 virtual void Paste();
260 virtual void CreateCallTipWindow(PRectangle rc);
261 virtual void AddToPopUp(const char *label, int cmd = 0, bool enabled = true);
262 virtual void ClaimSelection();
264 // DBCS
265 void ImeStartComposition();
266 void ImeEndComposition();
268 void AddCharBytes(char b0, char b1);
270 void GetIntelliMouseParameters();
271 virtual void CopyToClipboard(const SelectionText &selectedText);
272 void ScrollMessage(WPARAM wParam);
273 void HorizontalScrollMessage(WPARAM wParam);
274 void FullPaint();
275 void FullPaintDC(HDC dc);
276 bool IsCompatibleDC(HDC dc);
277 DWORD EffectFromState(DWORD grfKeyState) const;
279 virtual int SetScrollInfo(int nBar, LPCSCROLLINFO lpsi, BOOL bRedraw);
280 virtual bool GetScrollInfo(int nBar, LPSCROLLINFO lpsi);
281 void ChangeScrollPos(int barType, int pos);
282 sptr_t GetTextLength();
283 sptr_t GetText(uptr_t wParam, sptr_t lParam);
285 public:
286 // Public for benefit of Scintilla_DirectFunction
287 virtual sptr_t WndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam);
289 /// Implement IUnknown
290 STDMETHODIMP QueryInterface(REFIID riid, PVOID *ppv);
291 STDMETHODIMP_(ULONG)AddRef();
292 STDMETHODIMP_(ULONG)Release();
294 /// Implement IDropTarget
295 STDMETHODIMP DragEnter(LPDATAOBJECT pIDataSource, DWORD grfKeyState,
296 POINTL pt, PDWORD pdwEffect);
297 STDMETHODIMP DragOver(DWORD grfKeyState, POINTL pt, PDWORD pdwEffect);
298 STDMETHODIMP DragLeave();
299 STDMETHODIMP Drop(LPDATAOBJECT pIDataSource, DWORD grfKeyState,
300 POINTL pt, PDWORD pdwEffect);
302 /// Implement important part of IDataObject
303 STDMETHODIMP GetData(FORMATETC *pFEIn, STGMEDIUM *pSTM);
305 static bool Register(HINSTANCE hInstance_);
306 static bool Unregister();
308 friend class DropSource;
309 friend class DataObject;
310 friend class DropTarget;
311 bool DragIsRectangularOK(CLIPFORMAT fmt) const {
312 return drag.rectangular && (fmt == cfColumnSelect);
315 private:
316 // For use in creating a system caret
317 bool HasCaretSizeChanged() const;
318 BOOL CreateSystemCaret();
319 BOOL DestroySystemCaret();
320 HBITMAP sysCaretBitmap;
321 int sysCaretWidth;
322 int sysCaretHeight;
323 bool keysAlwaysUnicode;
326 HINSTANCE ScintillaWin::hInstance = 0;
328 ScintillaWin::ScintillaWin(HWND hwnd) {
330 lastKeyDownConsumed = false;
332 capturedMouse = false;
333 trackedMouseLeave = false;
334 TrackMouseEventFn = 0;
336 linesPerScroll = 0;
337 wheelDelta = 0; // Wheel delta from roll
339 hRgnUpdate = 0;
341 hasOKText = false;
343 // There does not seem to be a real standard for indicating that the clipboard
344 // contains a rectangular selection, so copy Developer Studio and Borland Delphi.
345 cfColumnSelect = static_cast<CLIPFORMAT>(
346 ::RegisterClipboardFormat(TEXT("MSDEVColumnSelect")));
347 cfBorlandIDEBlockType = static_cast<CLIPFORMAT>(
348 ::RegisterClipboardFormat(TEXT("Borland IDE Block Type")));
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 if (!commctrl32)
395 commctrl32 = ::LoadLibrary(TEXT("comctl32.dll"));
396 if (commctrl32 != NULL) {
397 TrackMouseEventFn = (TrackMouseEventSig)
398 ::GetProcAddress(commctrl32, "_TrackMouseEvent");
403 void ScintillaWin::Finalise() {
404 ScintillaBase::Finalise();
405 SetTicking(false);
406 SetIdle(false);
407 #if defined(USE_D2D)
408 DropRenderTarget();
409 #endif
410 ::RevokeDragDrop(MainHWND());
411 if (SUCCEEDED(hrOle)) {
412 ::OleUninitialize();
416 #if defined(USE_D2D)
418 void ScintillaWin::EnsureRenderTarget() {
419 if (!renderTargetValid) {
420 DropRenderTarget();
421 renderTargetValid = true;
423 if (pD2DFactory && !pRenderTarget) {
424 RECT rc;
425 HWND hw = MainHWND();
426 GetClientRect(hw, &rc);
428 D2D1_SIZE_U size = D2D1::SizeU(rc.right - rc.left, rc.bottom - rc.top);
430 // Create a Direct2D render target.
431 #if 1
432 D2D1_HWND_RENDER_TARGET_PROPERTIES dhrtp;
433 dhrtp.hwnd = hw;
434 dhrtp.pixelSize = size;
435 dhrtp.presentOptions = D2D1_PRESENT_OPTIONS_NONE;
437 D2D1_RENDER_TARGET_PROPERTIES drtp;
438 drtp.type = D2D1_RENDER_TARGET_TYPE_DEFAULT;
439 drtp.pixelFormat.format = DXGI_FORMAT_UNKNOWN;
440 drtp.pixelFormat.alphaMode = D2D1_ALPHA_MODE_UNKNOWN;
441 drtp.dpiX = 96.0;
442 drtp.dpiY = 96.0;
443 drtp.usage = D2D1_RENDER_TARGET_USAGE_NONE;
444 drtp.minLevel = D2D1_FEATURE_LEVEL_DEFAULT;
446 pD2DFactory->CreateHwndRenderTarget(drtp, dhrtp, &pRenderTarget);
447 #else
448 pD2DFactory->CreateHwndRenderTarget(
449 D2D1::RenderTargetProperties(
450 D2D1_RENDER_TARGET_TYPE_DEFAULT ,
451 D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_PREMULTIPLIED),
452 96.0f, 96.0f, D2D1_RENDER_TARGET_USAGE_NONE, D2D1_FEATURE_LEVEL_DEFAULT),
453 D2D1::HwndRenderTargetProperties(hw, size),
454 &pRenderTarget);
455 #endif
456 // Pixmaps were created to be compatible with previous render target so
457 // need to be recreated.
458 DropGraphics(false);
462 void ScintillaWin::DropRenderTarget() {
463 if (pRenderTarget) {
464 pRenderTarget->Release();
465 pRenderTarget = 0;
469 #endif
471 HWND ScintillaWin::MainHWND() {
472 return reinterpret_cast<HWND>(wMain.GetID());
475 bool ScintillaWin::DragThreshold(Point ptStart, Point ptNow) {
476 int xMove = static_cast<int>(abs(ptStart.x - ptNow.x));
477 int yMove = static_cast<int>(abs(ptStart.y - ptNow.y));
478 return (xMove > ::GetSystemMetrics(SM_CXDRAG)) ||
479 (yMove > ::GetSystemMetrics(SM_CYDRAG));
482 void ScintillaWin::StartDrag() {
483 inDragDrop = ddDragging;
484 DWORD dwEffect = 0;
485 dropWentOutside = true;
486 IDataObject *pDataObject = reinterpret_cast<IDataObject *>(&dob);
487 IDropSource *pDropSource = reinterpret_cast<IDropSource *>(&ds);
488 //Platform::DebugPrintf("About to DoDragDrop %x %x\n", pDataObject, pDropSource);
489 HRESULT hr = ::DoDragDrop(
490 pDataObject,
491 pDropSource,
492 DROPEFFECT_COPY | DROPEFFECT_MOVE, &dwEffect);
493 //Platform::DebugPrintf("DoDragDrop = %x\n", hr);
494 if (SUCCEEDED(hr)) {
495 if ((hr == DRAGDROP_S_DROP) && (dwEffect == DROPEFFECT_MOVE) && dropWentOutside) {
496 // Remove dragged out text
497 ClearSelection();
500 inDragDrop = ddNone;
501 SetDragPosition(SelectionPosition(invalidPosition));
504 // Avoid warnings everywhere for old style casts by concentrating them here
505 static WORD LoWord(uptr_t l) {
506 return LOWORD(l);
509 static WORD HiWord(uptr_t l) {
510 return HIWORD(l);
513 static int InputCodePage() {
514 HKL inputLocale = ::GetKeyboardLayout(0);
515 LANGID inputLang = LOWORD(inputLocale);
516 char sCodePage[10];
517 int res = ::GetLocaleInfoA(MAKELCID(inputLang, SORT_DEFAULT),
518 LOCALE_IDEFAULTANSICODEPAGE, sCodePage, sizeof(sCodePage));
519 if (!res)
520 return 0;
521 return atoi(sCodePage);
524 /** Map the key codes to their equivalent SCK_ form. */
525 static int KeyTranslate(int keyIn) {
526 //PLATFORM_ASSERT(!keyIn);
527 switch (keyIn) {
528 case VK_DOWN: return SCK_DOWN;
529 case VK_UP: return SCK_UP;
530 case VK_LEFT: return SCK_LEFT;
531 case VK_RIGHT: return SCK_RIGHT;
532 case VK_HOME: return SCK_HOME;
533 case VK_END: return SCK_END;
534 case VK_PRIOR: return SCK_PRIOR;
535 case VK_NEXT: return SCK_NEXT;
536 case VK_DELETE: return SCK_DELETE;
537 case VK_INSERT: return SCK_INSERT;
538 case VK_ESCAPE: return SCK_ESCAPE;
539 case VK_BACK: return SCK_BACK;
540 case VK_TAB: return SCK_TAB;
541 case VK_RETURN: return SCK_RETURN;
542 case VK_ADD: return SCK_ADD;
543 case VK_SUBTRACT: return SCK_SUBTRACT;
544 case VK_DIVIDE: return SCK_DIVIDE;
545 case VK_LWIN: return SCK_WIN;
546 case VK_RWIN: return SCK_RWIN;
547 case VK_APPS: return SCK_MENU;
548 case VK_OEM_2: return '/';
549 case VK_OEM_3: return '`';
550 case VK_OEM_4: return '[';
551 case VK_OEM_5: return '\\';
552 case VK_OEM_6: return ']';
553 default: return keyIn;
557 static bool BoundsContains(PRectangle rcBounds, HRGN hRgnBounds, PRectangle rcCheck) {
558 bool contains = true;
559 if (!rcCheck.Empty()) {
560 if (!rcBounds.Contains(rcCheck)) {
561 contains = false;
562 } else if (hRgnBounds) {
563 // In bounding rectangle so check more accurately using region
564 HRGN hRgnCheck = ::CreateRectRgn(static_cast<int>(rcCheck.left), static_cast<int>(rcCheck.top),
565 static_cast<int>(rcCheck.right), static_cast<int>(rcCheck.bottom));
566 if (hRgnCheck) {
567 HRGN hRgnDifference = ::CreateRectRgn(0, 0, 0, 0);
568 if (hRgnDifference) {
569 int combination = ::CombineRgn(hRgnDifference, hRgnCheck, hRgnBounds, RGN_DIFF);
570 if (combination != NULLREGION) {
571 contains = false;
573 ::DeleteRgn(hRgnDifference);
575 ::DeleteRgn(hRgnCheck);
579 return contains;
582 LRESULT ScintillaWin::WndPaint(uptr_t wParam) {
583 //ElapsedTime et;
585 // Redirect assertions to debug output and save current state
586 bool assertsPopup = Platform::ShowAssertionPopUps(false);
587 paintState = painting;
588 PAINTSTRUCT ps;
589 PAINTSTRUCT *pps;
591 bool IsOcxCtrl = (wParam != 0); // if wParam != 0, it contains
592 // a PAINSTRUCT* from the OCX
593 // Removed since this interferes with reporting other assertions as it occurs repeatedly
594 //PLATFORM_ASSERT(hRgnUpdate == NULL);
595 hRgnUpdate = ::CreateRectRgn(0, 0, 0, 0);
596 if (IsOcxCtrl) {
597 pps = reinterpret_cast<PAINTSTRUCT*>(wParam);
598 } else {
599 ::GetUpdateRgn(MainHWND(), hRgnUpdate, FALSE);
600 pps = &ps;
601 ::BeginPaint(MainHWND(), pps);
603 rcPaint = PRectangle::FromInts(pps->rcPaint.left, pps->rcPaint.top, pps->rcPaint.right, pps->rcPaint.bottom);
604 PRectangle rcClient = GetClientRectangle();
605 paintingAllText = BoundsContains(rcPaint, hRgnUpdate, rcClient);
606 if (technology == SC_TECHNOLOGY_DEFAULT) {
607 AutoSurface surfaceWindow(pps->hdc, this);
608 if (surfaceWindow) {
609 Paint(surfaceWindow, rcPaint);
610 surfaceWindow->Release();
612 } else {
613 #if defined(USE_D2D)
614 EnsureRenderTarget();
615 AutoSurface surfaceWindow(pRenderTarget, this);
616 if (surfaceWindow) {
617 pRenderTarget->BeginDraw();
618 Paint(surfaceWindow, rcPaint);
619 surfaceWindow->Release();
620 HRESULT hr = pRenderTarget->EndDraw();
621 if (hr == D2DERR_RECREATE_TARGET) {
622 DropRenderTarget();
623 paintState = paintAbandoned;
626 #endif
628 if (hRgnUpdate) {
629 ::DeleteRgn(hRgnUpdate);
630 hRgnUpdate = 0;
633 if (!IsOcxCtrl)
634 ::EndPaint(MainHWND(), pps);
635 if (paintState == paintAbandoned) {
636 // Painting area was insufficient to cover new styling or brace highlight positions
637 if (IsOcxCtrl) {
638 FullPaintDC(pps->hdc);
639 } else {
640 FullPaint();
643 paintState = notPainting;
645 // Restore debug output state
646 Platform::ShowAssertionPopUps(assertsPopup);
648 //Platform::DebugPrintf("Paint took %g\n", et.Duration());
649 return 0l;
652 sptr_t ScintillaWin::HandleComposition(uptr_t wParam, sptr_t lParam) {
653 if (lParam & GCS_RESULTSTR) {
654 HIMC hIMC = ::ImmGetContext(MainHWND());
655 if (hIMC) {
656 const int maxLenInputIME = 200;
657 wchar_t wcs[maxLenInputIME];
658 LONG bytes = ::ImmGetCompositionStringW(hIMC,
659 GCS_RESULTSTR, wcs, (maxLenInputIME-1)*2);
660 int wides = bytes / 2;
661 if (IsUnicodeMode()) {
662 char utfval[maxLenInputIME * 3];
663 unsigned int len = UTF8Length(wcs, wides);
664 UTF8FromUTF16(wcs, wides, utfval, len);
665 utfval[len] = '\0';
666 AddCharUTF(utfval, len);
667 } else {
668 char dbcsval[maxLenInputIME * 2];
669 int size = ::WideCharToMultiByte(InputCodePage(),
670 0, wcs, wides, dbcsval, sizeof(dbcsval) - 1, 0, 0);
671 for (int i=0; i<size; i++) {
672 AddChar(dbcsval[i]);
675 // Set new position after converted
676 Point pos = PointMainCaret();
677 COMPOSITIONFORM CompForm;
678 CompForm.dwStyle = CFS_POINT;
679 CompForm.ptCurrentPos.x = static_cast<int>(pos.x);
680 CompForm.ptCurrentPos.y = static_cast<int>(pos.y);
681 ::ImmSetCompositionWindow(hIMC, &CompForm);
682 ::ImmReleaseContext(MainHWND(), hIMC);
684 return 0;
686 return ::DefWindowProc(MainHWND(), WM_IME_COMPOSITION, wParam, lParam);
689 // Translate message IDs from WM_* and EM_* to SCI_* so can partly emulate Windows Edit control
690 static unsigned int SciMessageFromEM(unsigned int iMessage) {
691 switch (iMessage) {
692 case EM_CANPASTE: return SCI_CANPASTE;
693 case EM_CANUNDO: return SCI_CANUNDO;
694 case EM_EMPTYUNDOBUFFER: return SCI_EMPTYUNDOBUFFER;
695 case EM_FINDTEXTEX: return SCI_FINDTEXT;
696 case EM_FORMATRANGE: return SCI_FORMATRANGE;
697 case EM_GETFIRSTVISIBLELINE: return SCI_GETFIRSTVISIBLELINE;
698 case EM_GETLINECOUNT: return SCI_GETLINECOUNT;
699 case EM_GETSELTEXT: return SCI_GETSELTEXT;
700 case EM_GETTEXTRANGE: return SCI_GETTEXTRANGE;
701 case EM_HIDESELECTION: return SCI_HIDESELECTION;
702 case EM_LINEINDEX: return SCI_POSITIONFROMLINE;
703 case EM_LINESCROLL: return SCI_LINESCROLL;
704 case EM_REPLACESEL: return SCI_REPLACESEL;
705 case EM_SCROLLCARET: return SCI_SCROLLCARET;
706 case EM_SETREADONLY: return SCI_SETREADONLY;
707 case WM_CLEAR: return SCI_CLEAR;
708 case WM_COPY: return SCI_COPY;
709 case WM_CUT: return SCI_CUT;
710 case WM_SETTEXT: return SCI_SETTEXT;
711 case WM_PASTE: return SCI_PASTE;
712 case WM_UNDO: return SCI_UNDO;
714 return iMessage;
717 UINT CodePageFromCharSet(DWORD characterSet, UINT documentCodePage) {
718 if (documentCodePage == SC_CP_UTF8) {
719 return SC_CP_UTF8;
721 switch (characterSet) {
722 case SC_CHARSET_ANSI: return 1252;
723 case SC_CHARSET_DEFAULT: return documentCodePage;
724 case SC_CHARSET_BALTIC: return 1257;
725 case SC_CHARSET_CHINESEBIG5: return 950;
726 case SC_CHARSET_EASTEUROPE: return 1250;
727 case SC_CHARSET_GB2312: return 936;
728 case SC_CHARSET_GREEK: return 1253;
729 case SC_CHARSET_HANGUL: return 949;
730 case SC_CHARSET_MAC: return 10000;
731 case SC_CHARSET_OEM: return 437;
732 case SC_CHARSET_RUSSIAN: return 1251;
733 case SC_CHARSET_SHIFTJIS: return 932;
734 case SC_CHARSET_TURKISH: return 1254;
735 case SC_CHARSET_JOHAB: return 1361;
736 case SC_CHARSET_HEBREW: return 1255;
737 case SC_CHARSET_ARABIC: return 1256;
738 case SC_CHARSET_VIETNAMESE: return 1258;
739 case SC_CHARSET_THAI: return 874;
740 case SC_CHARSET_8859_15: return 28605;
741 // Not supported
742 case SC_CHARSET_CYRILLIC: return documentCodePage;
743 case SC_CHARSET_SYMBOL: return documentCodePage;
745 return documentCodePage;
748 UINT ScintillaWin::CodePageOfDocument() {
749 return CodePageFromCharSet(vs.styles[STYLE_DEFAULT].characterSet, pdoc->dbcsCodePage);
752 sptr_t ScintillaWin::GetTextLength() {
753 if (::IsWindowUnicode(MainHWND())) {
754 if (pdoc->Length() == 0)
755 return 0;
756 std::vector<char> docBytes(pdoc->Length(), '\0');
757 pdoc->GetCharRange(&docBytes[0], 0, pdoc->Length());
758 if (IsUnicodeMode()) {
759 return UTF16Length(&docBytes[0], static_cast<unsigned int>(docBytes.size()));
760 } else {
761 return ::MultiByteToWideChar(CodePageOfDocument(), 0, &docBytes[0],
762 static_cast<int>(docBytes.size()), NULL, 0);
764 } else {
765 return pdoc->Length();
769 sptr_t ScintillaWin::GetText(uptr_t wParam, sptr_t lParam) {
770 if (::IsWindowUnicode(MainHWND())) {
771 wchar_t *ptr = reinterpret_cast<wchar_t *>(lParam);
772 if (pdoc->Length() == 0) {
773 *ptr = L'\0';
774 return 0;
776 std::vector<char> docBytes(pdoc->Length(), '\0');
777 pdoc->GetCharRange(&docBytes[0], 0, pdoc->Length());
778 if (IsUnicodeMode()) {
779 unsigned int lengthUTF16 = UTF16Length(&docBytes[0], static_cast<unsigned int>(docBytes.size()));
780 if (lParam == 0)
781 return lengthUTF16;
782 if (wParam == 0)
783 return 0;
784 unsigned int uLen = UTF16FromUTF8(&docBytes[0], static_cast<unsigned int>(docBytes.size()),
785 ptr, static_cast<int>(wParam) - 1);
786 ptr[uLen] = L'\0';
787 return uLen;
788 } else {
789 // Not Unicode mode
790 // Convert to Unicode using the current Scintilla code page
791 const UINT cpSrc = CodePageOfDocument();
792 int lengthUTF16 = ::MultiByteToWideChar(cpSrc, 0, &docBytes[0],
793 static_cast<int>(docBytes.size()), NULL, 0);
794 if (lengthUTF16 >= static_cast<int>(wParam))
795 lengthUTF16 = static_cast<int>(wParam)-1;
796 ::MultiByteToWideChar(cpSrc, 0, &docBytes[0],
797 static_cast<int>(docBytes.size()),
798 ptr, lengthUTF16);
799 ptr[lengthUTF16] = L'\0';
800 return lengthUTF16;
802 } else {
803 if (lParam == 0)
804 return pdoc->Length() + 1;
805 if (wParam == 0)
806 return 0;
807 char *ptr = reinterpret_cast<char *>(lParam);
808 unsigned int iChar = 0;
809 for (; iChar < wParam - 1; iChar++)
810 ptr[iChar] = pdoc->CharAt(iChar);
811 ptr[iChar] = '\0';
812 return iChar;
816 sptr_t ScintillaWin::WndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
817 try {
818 //Platform::DebugPrintf("S M:%x WP:%x L:%x\n", iMessage, wParam, lParam);
819 iMessage = SciMessageFromEM(iMessage);
820 switch (iMessage) {
822 case WM_CREATE:
823 ctrlID = ::GetDlgCtrlID(reinterpret_cast<HWND>(wMain.GetID()));
824 // Get Intellimouse scroll line parameters
825 GetIntelliMouseParameters();
826 ::RegisterDragDrop(MainHWND(), reinterpret_cast<IDropTarget *>(&dt));
827 break;
829 case WM_COMMAND:
830 Command(LoWord(wParam));
831 break;
833 case WM_PAINT:
834 return WndPaint(wParam);
836 case WM_PRINTCLIENT: {
837 HDC hdc = reinterpret_cast<HDC>(wParam);
838 if (!IsCompatibleDC(hdc)) {
839 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
841 FullPaintDC(hdc);
843 break;
845 case WM_VSCROLL:
846 ScrollMessage(wParam);
847 break;
849 case WM_HSCROLL:
850 HorizontalScrollMessage(wParam);
851 break;
853 case WM_SIZE: {
854 #if defined(USE_D2D)
855 if (paintState == notPainting) {
856 DropRenderTarget();
857 } else {
858 renderTargetValid = false;
860 #endif
861 //Platform::DebugPrintf("Scintilla WM_SIZE %d %d\n", LoWord(lParam), HiWord(lParam));
862 ChangeSize();
864 break;
866 case WM_MOUSEWHEEL:
867 // if autocomplete list active then send mousewheel message to it
868 if (ac.Active()) {
869 HWND hWnd = reinterpret_cast<HWND>(ac.lb->GetID());
870 ::SendMessage(hWnd, iMessage, wParam, lParam);
871 break;
874 // Don't handle datazoom.
875 // (A good idea for datazoom would be to "fold" or "unfold" details.
876 // i.e. if datazoomed out only class structures are visible, when datazooming in the control
877 // structures appear, then eventually the individual statements...)
878 if (wParam & MK_SHIFT) {
879 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
882 // Either SCROLL or ZOOM. We handle the wheel steppings calculation
883 wheelDelta -= static_cast<short>(HiWord(wParam));
884 if (abs(wheelDelta) >= WHEEL_DELTA && linesPerScroll > 0) {
885 int linesToScroll = linesPerScroll;
886 if (linesPerScroll == WHEEL_PAGESCROLL)
887 linesToScroll = LinesOnScreen() - 1;
888 if (linesToScroll == 0) {
889 linesToScroll = 1;
891 linesToScroll *= (wheelDelta / WHEEL_DELTA);
892 if (wheelDelta >= 0)
893 wheelDelta = wheelDelta % WHEEL_DELTA;
894 else
895 wheelDelta = - (-wheelDelta % WHEEL_DELTA);
897 if (wParam & MK_CONTROL) {
898 // Zoom! We play with the font sizes in the styles.
899 // Number of steps/line is ignored, we just care if sizing up or down
900 if (linesToScroll < 0) {
901 KeyCommand(SCI_ZOOMIN);
902 } else {
903 KeyCommand(SCI_ZOOMOUT);
905 } else {
906 // Scroll
907 ScrollTo(topLine + linesToScroll);
910 return 0;
912 case WM_TIMER:
913 if (wParam == standardTimerID && timer.ticking) {
914 Tick();
915 } else if (wParam == idleTimerID && idler.state) {
916 SendMessage(MainHWND(), SC_WIN_IDLE, 0, 1);
917 } else {
918 return 1;
920 break;
922 case SC_WIN_IDLE:
923 // wParam=dwTickCountInitial, or 0 to initialize. lParam=bSkipUserInputTest
924 if (idler.state) {
925 if (lParam || (WAIT_TIMEOUT == MsgWaitForMultipleObjects(0, 0, 0, 0, QS_INPUT|QS_HOTKEY))) {
926 if (Idle()) {
927 // User input was given priority above, but all events do get a turn. Other
928 // messages, notifications, etc. will get interleaved with the idle messages.
930 // However, some things like WM_PAINT are a lower priority, and will not fire
931 // when there's a message posted. So, several times a second, we stop and let
932 // the low priority events have a turn (after which the timer will fire again).
934 DWORD dwCurrent = GetTickCount();
935 DWORD dwStart = wParam ? static_cast<DWORD>(wParam) : dwCurrent;
936 const DWORD maxWorkTime = 50;
938 if (dwCurrent >= dwStart && dwCurrent > maxWorkTime && dwCurrent - maxWorkTime < dwStart)
939 PostMessage(MainHWND(), SC_WIN_IDLE, dwStart, 0);
940 } else {
941 SetIdle(false);
945 break;
947 case WM_GETMINMAXINFO:
948 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
950 case WM_LBUTTONDOWN: {
951 // For IME, set the composition string as the result string.
952 HIMC hIMC = ::ImmGetContext(MainHWND());
953 ::ImmNotifyIME(hIMC, NI_COMPOSITIONSTR, CPS_COMPLETE, 0);
954 ::ImmReleaseContext(MainHWND(), hIMC);
956 //Platform::DebugPrintf("Buttdown %d %x %x %x %x %x\n",iMessage, wParam, lParam,
957 // Platform::IsKeyDown(VK_SHIFT),
958 // Platform::IsKeyDown(VK_CONTROL),
959 // Platform::IsKeyDown(VK_MENU));
960 ::SetFocus(MainHWND());
961 ButtonDown(Point::FromLong(static_cast<long>(lParam)), ::GetMessageTime(),
962 (wParam & MK_SHIFT) != 0,
963 (wParam & MK_CONTROL) != 0,
964 Platform::IsKeyDown(VK_MENU));
966 break;
968 case WM_MOUSEMOVE:
969 SetTrackMouseLeaveEvent(true);
970 ButtonMoveWithModifiers(Point::FromLong(static_cast<long>(lParam)),
971 ((wParam & MK_SHIFT) != 0 ? SCI_SHIFT : 0) |
972 ((wParam & MK_CONTROL) != 0 ? SCI_CTRL : 0) |
973 (Platform::IsKeyDown(VK_MENU) ? SCI_ALT : 0));
974 break;
976 case WM_MOUSELEAVE:
977 SetTrackMouseLeaveEvent(false);
978 MouseLeave();
979 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
981 case WM_LBUTTONUP:
982 ButtonUp(Point::FromLong(static_cast<long>(lParam)),
983 ::GetMessageTime(),
984 (wParam & MK_CONTROL) != 0);
985 break;
987 case WM_RBUTTONDOWN:
988 ::SetFocus(MainHWND());
989 if (!PointInSelection(Point::FromLong(static_cast<long>(lParam)))) {
990 CancelModes();
991 SetEmptySelection(PositionFromLocation(Point::FromLong(static_cast<long>(lParam))));
993 break;
995 case WM_SETCURSOR:
996 if (LoWord(lParam) == HTCLIENT) {
997 if (inDragDrop == ddDragging) {
998 DisplayCursor(Window::cursorUp);
999 } else {
1000 // Display regular (drag) cursor over selection
1001 POINT pt;
1002 if (0 != ::GetCursorPos(&pt)) {
1003 ::ScreenToClient(MainHWND(), &pt);
1004 if (PointInSelMargin(PointFromPOINT(pt))) {
1005 DisplayCursor(GetMarginCursor(PointFromPOINT(pt)));
1006 } else if (PointInSelection(PointFromPOINT(pt)) && !SelectionEmpty()) {
1007 DisplayCursor(Window::cursorArrow);
1008 } else if (PointIsHotspot(PointFromPOINT(pt))) {
1009 DisplayCursor(Window::cursorHand);
1010 } else {
1011 DisplayCursor(Window::cursorText);
1015 return TRUE;
1016 } else {
1017 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1020 case WM_CHAR:
1021 if (((wParam >= 128) || !iscntrl(static_cast<int>(wParam))) || !lastKeyDownConsumed) {
1022 if (::IsWindowUnicode(MainHWND()) || keysAlwaysUnicode) {
1023 wchar_t wcs[2] = {static_cast<wchar_t>(wParam), 0};
1024 if (IsUnicodeMode()) {
1025 // For a wide character version of the window:
1026 char utfval[4];
1027 unsigned int len = UTF8Length(wcs, 1);
1028 UTF8FromUTF16(wcs, 1, utfval, len);
1029 AddCharUTF(utfval, len);
1030 } else {
1031 UINT cpDest = CodePageOfDocument();
1032 char inBufferCP[20];
1033 int size = ::WideCharToMultiByte(cpDest,
1034 0, wcs, 1, inBufferCP, sizeof(inBufferCP) - 1, 0, 0);
1035 inBufferCP[size] = '\0';
1036 AddCharUTF(inBufferCP, size);
1038 } else {
1039 if (IsUnicodeMode()) {
1040 AddCharBytes('\0', LOBYTE(wParam));
1041 } else {
1042 AddChar(LOBYTE(wParam));
1046 return 0;
1048 case WM_UNICHAR:
1049 if (wParam == UNICODE_NOCHAR) {
1050 return IsUnicodeMode() ? 1 : 0;
1051 } else if (lastKeyDownConsumed) {
1052 return 1;
1053 } else {
1054 if (IsUnicodeMode()) {
1055 char utfval[4];
1056 wchar_t wcs[2] = {static_cast<wchar_t>(wParam), 0};
1057 unsigned int len = UTF8Length(wcs, 1);
1058 UTF8FromUTF16(wcs, 1, utfval, len);
1059 AddCharUTF(utfval, len);
1060 return 1;
1061 } else {
1062 return 0;
1066 case WM_SYSKEYDOWN:
1067 case WM_KEYDOWN: {
1068 //Platform::DebugPrintf("S keydown %d %x %x %x %x\n",iMessage, wParam, lParam, ::IsKeyDown(VK_SHIFT), ::IsKeyDown(VK_CONTROL));
1069 lastKeyDownConsumed = false;
1070 int ret = KeyDown(KeyTranslate(static_cast<int>(wParam)),
1071 Platform::IsKeyDown(VK_SHIFT),
1072 Platform::IsKeyDown(VK_CONTROL),
1073 Platform::IsKeyDown(VK_MENU),
1074 &lastKeyDownConsumed);
1075 if (!ret && !lastKeyDownConsumed) {
1076 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1078 break;
1081 case WM_IME_KEYDOWN:
1082 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1084 case WM_KEYUP:
1085 //Platform::DebugPrintf("S keyup %d %x %x\n",iMessage, wParam, lParam);
1086 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1088 case WM_SETTINGCHANGE:
1089 //Platform::DebugPrintf("Setting Changed\n");
1090 InvalidateStyleData();
1091 // Get Intellimouse scroll line parameters
1092 GetIntelliMouseParameters();
1093 break;
1095 case WM_GETDLGCODE:
1096 return DLGC_HASSETSEL | DLGC_WANTALLKEYS;
1098 case WM_KILLFOCUS: {
1099 HWND wOther = reinterpret_cast<HWND>(wParam);
1100 HWND wThis = MainHWND();
1101 HWND wCT = reinterpret_cast<HWND>(ct.wCallTip.GetID());
1102 if (!wParam ||
1103 !(::IsChild(wThis, wOther) || (wOther == wCT))) {
1104 SetFocusState(false);
1105 DestroySystemCaret();
1108 break;
1110 case WM_SETFOCUS:
1111 SetFocusState(true);
1112 DestroySystemCaret();
1113 CreateSystemCaret();
1114 break;
1116 case WM_SYSCOLORCHANGE:
1117 //Platform::DebugPrintf("Setting Changed\n");
1118 InvalidateStyleData();
1119 break;
1121 case WM_IME_STARTCOMPOSITION: // dbcs
1122 ImeStartComposition();
1123 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1125 case WM_IME_ENDCOMPOSITION: // dbcs
1126 ImeEndComposition();
1127 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1129 case WM_IME_COMPOSITION:
1130 return HandleComposition(wParam, lParam);
1132 case WM_IME_CHAR: {
1133 AddCharBytes(HIBYTE(wParam), LOBYTE(wParam));
1134 return 0;
1137 case WM_CONTEXTMENU:
1138 if (displayPopupMenu) {
1139 Point pt = Point::FromLong(static_cast<long>(lParam));
1140 if ((pt.x == -1) && (pt.y == -1)) {
1141 // Caused by keyboard so display menu near caret
1142 pt = PointMainCaret();
1143 POINT spt = {static_cast<int>(pt.x), static_cast<int>(pt.y)};
1144 ::ClientToScreen(MainHWND(), &spt);
1145 pt = PointFromPOINT(spt);
1147 ContextMenu(pt);
1148 return 0;
1150 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1152 case WM_INPUTLANGCHANGE:
1153 //::SetThreadLocale(LOWORD(lParam));
1154 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1156 case WM_INPUTLANGCHANGEREQUEST:
1157 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1159 case WM_ERASEBKGND:
1160 return 1; // Avoid any background erasure as whole window painted.
1162 case WM_CAPTURECHANGED:
1163 capturedMouse = false;
1164 return 0;
1166 // These are not handled in Scintilla and its faster to dispatch them here.
1167 // Also moves time out to here so profile doesn't count lots of empty message calls.
1169 case WM_MOVE:
1170 case WM_MOUSEACTIVATE:
1171 case WM_NCHITTEST:
1172 case WM_NCCALCSIZE:
1173 case WM_NCPAINT:
1174 case WM_NCMOUSEMOVE:
1175 case WM_NCLBUTTONDOWN:
1176 case WM_IME_SETCONTEXT:
1177 case WM_IME_NOTIFY:
1178 case WM_SYSCOMMAND:
1179 case WM_WINDOWPOSCHANGING:
1180 case WM_WINDOWPOSCHANGED:
1181 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1183 case WM_GETTEXTLENGTH:
1184 return GetTextLength();
1186 case WM_GETTEXT:
1187 return GetText(wParam, lParam);
1189 case EM_LINEFROMCHAR:
1190 if (static_cast<int>(wParam) < 0) {
1191 wParam = SelectionStart().Position();
1193 return pdoc->LineFromPosition(static_cast<int>(wParam));
1195 case EM_EXLINEFROMCHAR:
1196 return pdoc->LineFromPosition(static_cast<int>(lParam));
1198 case EM_GETSEL:
1199 if (wParam) {
1200 *reinterpret_cast<int *>(wParam) = SelectionStart().Position();
1202 if (lParam) {
1203 *reinterpret_cast<int *>(lParam) = SelectionEnd().Position();
1205 return MAKELONG(SelectionStart().Position(), SelectionEnd().Position());
1207 case EM_EXGETSEL: {
1208 if (lParam == 0) {
1209 return 0;
1211 Sci_CharacterRange *pCR = reinterpret_cast<Sci_CharacterRange *>(lParam);
1212 pCR->cpMin = SelectionStart().Position();
1213 pCR->cpMax = SelectionEnd().Position();
1215 break;
1217 case EM_SETSEL: {
1218 int nStart = static_cast<int>(wParam);
1219 int nEnd = static_cast<int>(lParam);
1220 if (nStart == 0 && nEnd == -1) {
1221 nEnd = pdoc->Length();
1223 if (nStart == -1) {
1224 nStart = nEnd; // Remove selection
1226 if (nStart > nEnd) {
1227 SetSelection(nEnd, nStart);
1228 } else {
1229 SetSelection(nStart, nEnd);
1231 EnsureCaretVisible();
1233 break;
1235 case EM_EXSETSEL: {
1236 if (lParam == 0) {
1237 return 0;
1239 Sci_CharacterRange *pCR = reinterpret_cast<Sci_CharacterRange *>(lParam);
1240 sel.selType = Selection::selStream;
1241 if (pCR->cpMin == 0 && pCR->cpMax == -1) {
1242 SetSelection(pCR->cpMin, pdoc->Length());
1243 } else {
1244 SetSelection(pCR->cpMin, pCR->cpMax);
1246 EnsureCaretVisible();
1247 return pdoc->LineFromPosition(SelectionStart().Position());
1250 case SCI_GETDIRECTFUNCTION:
1251 return reinterpret_cast<sptr_t>(DirectFunction);
1253 case SCI_GETDIRECTPOINTER:
1254 return reinterpret_cast<sptr_t>(this);
1256 case SCI_GRABFOCUS:
1257 ::SetFocus(MainHWND());
1258 break;
1260 case SCI_SETKEYSUNICODE:
1261 keysAlwaysUnicode = wParam != 0;
1262 break;
1264 case SCI_GETKEYSUNICODE:
1265 return keysAlwaysUnicode;
1267 case SCI_SETTECHNOLOGY:
1268 if ((wParam == SC_TECHNOLOGY_DEFAULT) || (wParam == SC_TECHNOLOGY_DIRECTWRITE)) {
1269 if (technology != static_cast<int>(wParam)) {
1270 if (static_cast<int>(wParam) == SC_TECHNOLOGY_DIRECTWRITE) {
1271 #if defined(USE_D2D)
1272 if (!LoadD2D())
1273 // Failed to load Direct2D or DirectWrite so no effect
1274 return 0;
1275 #else
1276 return 0;
1277 #endif
1279 technology = static_cast<int>(wParam);
1280 // Invalidate all cached information including layout.
1281 DropGraphics(true);
1282 InvalidateStyleRedraw();
1285 break;
1287 #ifdef SCI_LEXER
1288 case SCI_LOADLEXERLIBRARY:
1289 LexerManager::GetInstance()->Load(reinterpret_cast<const char *>(lParam));
1290 break;
1291 #endif
1293 default:
1294 return ScintillaBase::WndProc(iMessage, wParam, lParam);
1296 } catch (std::bad_alloc &) {
1297 errorStatus = SC_STATUS_BADALLOC;
1298 } catch (...) {
1299 errorStatus = SC_STATUS_FAILURE;
1301 return 0l;
1304 bool ScintillaWin::ValidCodePage(int codePage) const {
1305 return codePage == 0 || codePage == SC_CP_UTF8 ||
1306 codePage == 932 || codePage == 936 || codePage == 949 ||
1307 codePage == 950 || codePage == 1361;
1310 sptr_t ScintillaWin::DefWndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
1311 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1314 void ScintillaWin::SetTicking(bool on) {
1315 if (timer.ticking != on) {
1316 timer.ticking = on;
1317 if (timer.ticking) {
1318 timer.tickerID = ::SetTimer(MainHWND(), standardTimerID, timer.tickSize, NULL)
1319 ? reinterpret_cast<TickerID>(standardTimerID) : 0;
1320 } else {
1321 ::KillTimer(MainHWND(), reinterpret_cast<uptr_t>(timer.tickerID));
1322 timer.tickerID = 0;
1325 timer.ticksToWait = caret.period;
1328 bool ScintillaWin::SetIdle(bool on) {
1329 // On Win32 the Idler is implemented as a Timer on the Scintilla window. This
1330 // takes advantage of the fact that WM_TIMER messages are very low priority,
1331 // and are only posted when the message queue is empty, i.e. during idle time.
1332 if (idler.state != on) {
1333 if (on) {
1334 idler.idlerID = ::SetTimer(MainHWND(), idleTimerID, 10, NULL)
1335 ? reinterpret_cast<IdlerID>(idleTimerID) : 0;
1336 } else {
1337 ::KillTimer(MainHWND(), reinterpret_cast<uptr_t>(idler.idlerID));
1338 idler.idlerID = 0;
1340 idler.state = idler.idlerID != 0;
1342 return idler.state;
1345 void ScintillaWin::SetMouseCapture(bool on) {
1346 if (mouseDownCaptures) {
1347 if (on) {
1348 ::SetCapture(MainHWND());
1349 } else {
1350 ::ReleaseCapture();
1353 capturedMouse = on;
1356 bool ScintillaWin::HaveMouseCapture() {
1357 // Cannot just see if GetCapture is this window as the scroll bar also sets capture for the window
1358 return capturedMouse;
1359 //return capturedMouse && (::GetCapture() == MainHWND());
1362 void ScintillaWin::SetTrackMouseLeaveEvent(bool on) {
1363 if (on && TrackMouseEventFn && !trackedMouseLeave) {
1364 TRACKMOUSEEVENT tme;
1365 tme.cbSize = sizeof(tme);
1366 tme.dwFlags = TME_LEAVE;
1367 tme.hwndTrack = MainHWND();
1368 tme.dwHoverTime = HOVER_DEFAULT; // Unused but triggers Dr. Memory if not initialized
1369 TrackMouseEventFn(&tme);
1371 trackedMouseLeave = on;
1374 bool ScintillaWin::PaintContains(PRectangle rc) {
1375 if (paintState == painting) {
1376 return BoundsContains(rcPaint, hRgnUpdate, rc);
1378 return true;
1381 void ScintillaWin::ScrollText(int /* linesToMove */) {
1382 //Platform::DebugPrintf("ScintillaWin::ScrollText %d\n", linesToMove);
1383 //::ScrollWindow(MainHWND(), 0,
1384 // vs.lineHeight * linesToMove, 0, 0);
1385 //::UpdateWindow(MainHWND());
1386 Redraw();
1387 UpdateSystemCaret();
1390 void ScintillaWin::UpdateSystemCaret() {
1391 if (hasFocus) {
1392 if (HasCaretSizeChanged()) {
1393 DestroySystemCaret();
1394 CreateSystemCaret();
1396 Point pos = PointMainCaret();
1397 ::SetCaretPos(static_cast<int>(pos.x), static_cast<int>(pos.y));
1401 int ScintillaWin::SetScrollInfo(int nBar, LPCSCROLLINFO lpsi, BOOL bRedraw) {
1402 return ::SetScrollInfo(MainHWND(), nBar, lpsi, bRedraw);
1405 bool ScintillaWin::GetScrollInfo(int nBar, LPSCROLLINFO lpsi) {
1406 return ::GetScrollInfo(MainHWND(), nBar, lpsi) ? true : false;
1409 // Change the scroll position but avoid repaint if changing to same value
1410 void ScintillaWin::ChangeScrollPos(int barType, int pos) {
1411 SCROLLINFO sci = {
1412 sizeof(sci), 0, 0, 0, 0, 0, 0
1414 sci.fMask = SIF_POS;
1415 GetScrollInfo(barType, &sci);
1416 if (sci.nPos != pos) {
1417 DwellEnd(true);
1418 sci.nPos = pos;
1419 SetScrollInfo(barType, &sci, TRUE);
1423 void ScintillaWin::SetVerticalScrollPos() {
1424 ChangeScrollPos(SB_VERT, topLine);
1427 void ScintillaWin::SetHorizontalScrollPos() {
1428 ChangeScrollPos(SB_HORZ, xOffset);
1431 bool ScintillaWin::ModifyScrollBars(int nMax, int nPage) {
1432 bool modified = false;
1433 SCROLLINFO sci = {
1434 sizeof(sci), 0, 0, 0, 0, 0, 0
1436 sci.fMask = SIF_PAGE | SIF_RANGE;
1437 GetScrollInfo(SB_VERT, &sci);
1438 int vertEndPreferred = nMax;
1439 if (!verticalScrollBarVisible)
1440 nPage = vertEndPreferred + 1;
1441 if ((sci.nMin != 0) ||
1442 (sci.nMax != vertEndPreferred) ||
1443 (sci.nPage != static_cast<unsigned int>(nPage)) ||
1444 (sci.nPos != 0)) {
1445 sci.fMask = SIF_PAGE | SIF_RANGE;
1446 sci.nMin = 0;
1447 sci.nMax = vertEndPreferred;
1448 sci.nPage = nPage;
1449 sci.nPos = 0;
1450 sci.nTrackPos = 1;
1451 SetScrollInfo(SB_VERT, &sci, TRUE);
1452 modified = true;
1455 PRectangle rcText = GetTextRectangle();
1456 int horizEndPreferred = scrollWidth;
1457 if (horizEndPreferred < 0)
1458 horizEndPreferred = 0;
1459 unsigned int pageWidth = static_cast<unsigned int>(rcText.Width());
1460 if (!horizontalScrollBarVisible || Wrapping())
1461 pageWidth = horizEndPreferred + 1;
1462 sci.fMask = SIF_PAGE | SIF_RANGE;
1463 GetScrollInfo(SB_HORZ, &sci);
1464 if ((sci.nMin != 0) ||
1465 (sci.nMax != horizEndPreferred) ||
1466 (sci.nPage != pageWidth) ||
1467 (sci.nPos != 0)) {
1468 sci.fMask = SIF_PAGE | SIF_RANGE;
1469 sci.nMin = 0;
1470 sci.nMax = horizEndPreferred;
1471 sci.nPage = pageWidth;
1472 sci.nPos = 0;
1473 sci.nTrackPos = 1;
1474 SetScrollInfo(SB_HORZ, &sci, TRUE);
1475 modified = true;
1476 if (scrollWidth < static_cast<int>(pageWidth)) {
1477 HorizontalScrollTo(0);
1480 return modified;
1483 void ScintillaWin::NotifyChange() {
1484 ::SendMessage(::GetParent(MainHWND()), WM_COMMAND,
1485 MAKELONG(GetCtrlID(), SCEN_CHANGE),
1486 reinterpret_cast<LPARAM>(MainHWND()));
1489 void ScintillaWin::NotifyFocus(bool focus) {
1490 ::SendMessage(::GetParent(MainHWND()), WM_COMMAND,
1491 MAKELONG(GetCtrlID(), focus ? SCEN_SETFOCUS : SCEN_KILLFOCUS),
1492 reinterpret_cast<LPARAM>(MainHWND()));
1493 Editor::NotifyFocus(focus);
1496 void ScintillaWin::SetCtrlID(int identifier) {
1497 ::SetWindowID(reinterpret_cast<HWND>(wMain.GetID()), identifier);
1500 int ScintillaWin::GetCtrlID() {
1501 return ::GetDlgCtrlID(reinterpret_cast<HWND>(wMain.GetID()));
1504 void ScintillaWin::NotifyParent(SCNotification scn) {
1505 scn.nmhdr.hwndFrom = MainHWND();
1506 scn.nmhdr.idFrom = GetCtrlID();
1507 ::SendMessage(::GetParent(MainHWND()), WM_NOTIFY,
1508 GetCtrlID(), reinterpret_cast<LPARAM>(&scn));
1511 void ScintillaWin::NotifyParent(SCNotification * scn) {
1512 scn->nmhdr.hwndFrom = MainHWND();
1513 scn->nmhdr.idFrom = GetCtrlID();
1514 ::SendMessage(::GetParent(MainHWND()), WM_NOTIFY,
1515 GetCtrlID(), reinterpret_cast<LPARAM>(scn));
1518 void ScintillaWin::NotifyDoubleClick(Point pt, int modifiers) {
1519 //Platform::DebugPrintf("ScintillaWin Double click 0\n");
1520 ScintillaBase::NotifyDoubleClick(pt, modifiers);
1521 // Send myself a WM_LBUTTONDBLCLK, so the container can handle it too.
1522 ::SendMessage(MainHWND(),
1523 WM_LBUTTONDBLCLK,
1524 (modifiers & SCI_SHIFT) ? MK_SHIFT : 0,
1525 MAKELPARAM(pt.x, pt.y));
1528 class CaseFolderDBCS : public CaseFolderTable {
1529 // Allocate the expandable storage here so that it does not need to be reallocated
1530 // for each call to Fold.
1531 std::vector<wchar_t> utf16Mixed;
1532 std::vector<wchar_t> utf16Folded;
1533 UINT cp;
1534 public:
1535 explicit CaseFolderDBCS(UINT cp_) : cp(cp_) {
1536 StandardASCII();
1538 virtual size_t Fold(char *folded, size_t sizeFolded, const char *mixed, size_t lenMixed) {
1539 if ((lenMixed == 1) && (sizeFolded > 0)) {
1540 folded[0] = mapping[static_cast<unsigned char>(mixed[0])];
1541 return 1;
1542 } else {
1543 if (lenMixed > utf16Mixed.size()) {
1544 utf16Mixed.resize(lenMixed + 8);
1546 size_t nUtf16Mixed = ::MultiByteToWideChar(cp, 0, mixed,
1547 static_cast<int>(lenMixed),
1548 &utf16Mixed[0],
1549 static_cast<int>(utf16Mixed.size()));
1551 if (nUtf16Mixed == 0) {
1552 // Failed to convert -> bad input
1553 folded[0] = '\0';
1554 return 1;
1557 unsigned int lenFlat = 0;
1558 for (size_t mixIndex=0; mixIndex < nUtf16Mixed; mixIndex++) {
1559 if ((lenFlat + 20) > utf16Folded.size())
1560 utf16Folded.resize(lenFlat + 60);
1561 const char *foldedUTF8 = CaseConvert(utf16Mixed[mixIndex], CaseConversionFold);
1562 if (foldedUTF8) {
1563 // Maximum length of a case conversion is 6 bytes, 3 characters
1564 wchar_t wFolded[20];
1565 unsigned int charsConverted = UTF16FromUTF8(foldedUTF8,
1566 static_cast<unsigned int>(strlen(foldedUTF8)),
1567 wFolded, ELEMENTS(wFolded));
1568 for (size_t j=0; j<charsConverted; j++)
1569 utf16Folded[lenFlat++] = wFolded[j];
1570 } else {
1571 utf16Folded[lenFlat++] = utf16Mixed[mixIndex];
1575 size_t lenOut = ::WideCharToMultiByte(cp, 0,
1576 &utf16Folded[0], lenFlat,
1577 NULL, 0, NULL, 0);
1579 if (lenOut < sizeFolded) {
1580 ::WideCharToMultiByte(cp, 0,
1581 &utf16Folded[0], lenFlat,
1582 folded, static_cast<int>(lenOut), NULL, 0);
1583 return lenOut;
1584 } else {
1585 return 0;
1591 CaseFolder *ScintillaWin::CaseFolderForEncoding() {
1592 UINT cpDest = CodePageOfDocument();
1593 if (cpDest == SC_CP_UTF8) {
1594 return new CaseFolderUnicode();
1595 } else {
1596 if (pdoc->dbcsCodePage == 0) {
1597 CaseFolderTable *pcf = new CaseFolderTable();
1598 pcf->StandardASCII();
1599 // Only for single byte encodings
1600 UINT cpDoc = CodePageOfDocument();
1601 for (int i=0x80; i<0x100; i++) {
1602 char sCharacter[2] = "A";
1603 sCharacter[0] = static_cast<char>(i);
1604 wchar_t wCharacter[20];
1605 unsigned int lengthUTF16 = ::MultiByteToWideChar(cpDoc, 0, sCharacter, 1,
1606 wCharacter, ELEMENTS(wCharacter));
1607 if (lengthUTF16 == 1) {
1608 const char *caseFolded = CaseConvert(wCharacter[0], CaseConversionFold);
1609 if (caseFolded) {
1610 wchar_t wLower[20];
1611 unsigned int charsConverted = UTF16FromUTF8(caseFolded,
1612 static_cast<unsigned int>(strlen(caseFolded)),
1613 wLower, ELEMENTS(wLower));
1614 if (charsConverted == 1) {
1615 char sCharacterLowered[20];
1616 unsigned int lengthConverted = ::WideCharToMultiByte(cpDoc, 0,
1617 wLower, charsConverted,
1618 sCharacterLowered, ELEMENTS(sCharacterLowered), NULL, 0);
1619 if ((lengthConverted == 1) && (sCharacter[0] != sCharacterLowered[0])) {
1620 pcf->SetTranslation(sCharacter[0], sCharacterLowered[0]);
1626 return pcf;
1627 } else {
1628 return new CaseFolderDBCS(cpDest);
1633 std::string ScintillaWin::CaseMapString(const std::string &s, int caseMapping) {
1634 if ((s.size() == 0) || (caseMapping == cmSame))
1635 return s;
1637 UINT cpDoc = CodePageOfDocument();
1638 if (cpDoc == SC_CP_UTF8) {
1639 std::string retMapped(s.length() * maxExpansionCaseConversion, 0);
1640 size_t lenMapped = CaseConvertString(&retMapped[0], retMapped.length(), s.c_str(), s.length(),
1641 (caseMapping == cmUpper) ? CaseConversionUpper : CaseConversionLower);
1642 retMapped.resize(lenMapped);
1643 return retMapped;
1646 unsigned int lengthUTF16 = ::MultiByteToWideChar(cpDoc, 0, s.c_str(),
1647 static_cast<int>(s.size()), NULL, 0);
1648 if (lengthUTF16 == 0) // Failed to convert
1649 return s;
1651 DWORD mapFlags = LCMAP_LINGUISTIC_CASING |
1652 ((caseMapping == cmUpper) ? LCMAP_UPPERCASE : LCMAP_LOWERCASE);
1654 // Change text to UTF-16
1655 std::vector<wchar_t> vwcText(lengthUTF16);
1656 ::MultiByteToWideChar(cpDoc, 0, s.c_str(), static_cast<int>(s.size()), &vwcText[0], lengthUTF16);
1658 // Change case
1659 int charsConverted = ::LCMapStringW(LOCALE_SYSTEM_DEFAULT, mapFlags,
1660 &vwcText[0], lengthUTF16, NULL, 0);
1661 std::vector<wchar_t> vwcConverted(charsConverted);
1662 ::LCMapStringW(LOCALE_SYSTEM_DEFAULT, mapFlags,
1663 &vwcText[0], lengthUTF16, &vwcConverted[0], charsConverted);
1665 // Change back to document encoding
1666 unsigned int lengthConverted = ::WideCharToMultiByte(cpDoc, 0,
1667 &vwcConverted[0], static_cast<int>(vwcConverted.size()),
1668 NULL, 0, NULL, 0);
1669 std::vector<char> vcConverted(lengthConverted);
1670 ::WideCharToMultiByte(cpDoc, 0,
1671 &vwcConverted[0], static_cast<int>(vwcConverted.size()),
1672 &vcConverted[0], static_cast<int>(vcConverted.size()), NULL, 0);
1674 return std::string(&vcConverted[0], vcConverted.size());
1677 void ScintillaWin::Copy() {
1678 //Platform::DebugPrintf("Copy\n");
1679 if (!sel.Empty()) {
1680 SelectionText selectedText;
1681 CopySelectionRange(&selectedText);
1682 CopyToClipboard(selectedText);
1686 void ScintillaWin::CopyAllowLine() {
1687 SelectionText selectedText;
1688 CopySelectionRange(&selectedText, true);
1689 CopyToClipboard(selectedText);
1692 bool ScintillaWin::CanPaste() {
1693 if (!Editor::CanPaste())
1694 return false;
1695 if (::IsClipboardFormatAvailable(CF_TEXT))
1696 return true;
1697 if (IsUnicodeMode())
1698 return ::IsClipboardFormatAvailable(CF_UNICODETEXT) != 0;
1699 return false;
1702 class GlobalMemory {
1703 HGLOBAL hand;
1704 public:
1705 void *ptr;
1706 GlobalMemory() : hand(0), ptr(0) {
1708 explicit GlobalMemory(HGLOBAL hand_) : hand(hand_), ptr(0) {
1709 if (hand) {
1710 ptr = ::GlobalLock(hand);
1713 ~GlobalMemory() {
1714 PLATFORM_ASSERT(!ptr);
1716 void Allocate(size_t bytes) {
1717 hand = ::GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT, bytes);
1718 if (hand) {
1719 ptr = ::GlobalLock(hand);
1722 HGLOBAL Unlock() {
1723 PLATFORM_ASSERT(ptr);
1724 HGLOBAL handCopy = hand;
1725 ::GlobalUnlock(hand);
1726 ptr = 0;
1727 hand = 0;
1728 return handCopy;
1730 void SetClip(UINT uFormat) {
1731 ::SetClipboardData(uFormat, Unlock());
1733 operator bool() const {
1734 return ptr != 0;
1736 SIZE_T Size() {
1737 return ::GlobalSize(hand);
1741 void ScintillaWin::Paste() {
1742 if (!::OpenClipboard(MainHWND()))
1743 return;
1744 UndoGroup ug(pdoc);
1745 const bool isLine = SelectionEmpty() && (::IsClipboardFormatAvailable(cfLineSelect) != 0);
1746 ClearSelection(multiPasteMode == SC_MULTIPASTE_EACH);
1747 bool isRectangular = (::IsClipboardFormatAvailable(cfColumnSelect) != 0);
1749 if (!isRectangular) {
1750 // Evaluate "Borland IDE Block Type" explicitly
1751 GlobalMemory memBorlandSelection(::GetClipboardData(cfBorlandIDEBlockType));
1752 if (memBorlandSelection) {
1753 isRectangular = (memBorlandSelection.Size() == 1) && (static_cast<BYTE *>(memBorlandSelection.ptr)[0] == 0x02);
1754 memBorlandSelection.Unlock();
1757 const PasteShape pasteShape = isRectangular ? pasteRectangular : (isLine ? pasteLine : pasteStream);
1759 // Always use CF_UNICODETEXT if available
1760 GlobalMemory memUSelection(::GetClipboardData(CF_UNICODETEXT));
1761 if (memUSelection) {
1762 wchar_t *uptr = static_cast<wchar_t *>(memUSelection.ptr);
1763 if (uptr) {
1764 unsigned int len;
1765 std::vector<char> putf;
1766 // Default Scintilla behaviour in Unicode mode
1767 if (IsUnicodeMode()) {
1768 unsigned int bytes = static_cast<unsigned int>(memUSelection.Size());
1769 len = UTF8Length(uptr, bytes / 2);
1770 putf.resize(len + 1);
1771 UTF8FromUTF16(uptr, bytes / 2, &putf[0], len);
1772 } else {
1773 // CF_UNICODETEXT available, but not in Unicode mode
1774 // Convert from Unicode to current Scintilla code page
1775 UINT cpDest = CodePageOfDocument();
1776 len = ::WideCharToMultiByte(cpDest, 0, uptr, -1,
1777 NULL, 0, NULL, NULL) - 1; // subtract 0 terminator
1778 putf.resize(len + 1);
1779 ::WideCharToMultiByte(cpDest, 0, uptr, -1,
1780 &putf[0], len + 1, NULL, NULL);
1783 InsertPasteShape(&putf[0], len, pasteShape);
1785 memUSelection.Unlock();
1786 } else {
1787 // CF_UNICODETEXT not available, paste ANSI text
1788 GlobalMemory memSelection(::GetClipboardData(CF_TEXT));
1789 if (memSelection) {
1790 char *ptr = static_cast<char *>(memSelection.ptr);
1791 if (ptr) {
1792 unsigned int bytes = static_cast<unsigned int>(memSelection.Size());
1793 unsigned int len = bytes;
1794 for (unsigned int i = 0; i < bytes; i++) {
1795 if ((len == bytes) && (0 == ptr[i]))
1796 len = i;
1799 // In Unicode mode, convert clipboard text to UTF-8
1800 if (IsUnicodeMode()) {
1801 std::vector<wchar_t> uptr(len+1);
1803 unsigned int ulen = ::MultiByteToWideChar(CP_ACP, 0,
1804 ptr, len, &uptr[0], len+1);
1806 unsigned int mlen = UTF8Length(&uptr[0], ulen);
1807 std::vector<char> putf(mlen+1);
1808 // CP_UTF8 not available on Windows 95, so use UTF8FromUTF16()
1809 UTF8FromUTF16(&uptr[0], ulen, &putf[0], mlen);
1811 InsertPasteShape(&putf[0], mlen, pasteShape);
1812 } else {
1813 InsertPasteShape(ptr, len, pasteShape);
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 if (rgelt == NULL) return E_POINTER;
1876 unsigned int putPos = 0;
1877 while ((fe->pos < fe->formats.size()) && (putPos < celt)) {
1878 rgelt->cfFormat = fe->formats[fe->pos];
1879 rgelt->ptd = 0;
1880 rgelt->dwAspect = DVASPECT_CONTENT;
1881 rgelt->lindex = -1;
1882 rgelt->tymed = TYMED_HGLOBAL;
1883 rgelt++;
1884 fe->pos++;
1885 putPos++;
1887 if (pceltFetched)
1888 *pceltFetched = putPos;
1889 return putPos ? S_OK : S_FALSE;
1891 STDMETHODIMP FormatEnumerator_Skip(FormatEnumerator *fe, ULONG celt) {
1892 fe->pos += celt;
1893 return S_OK;
1895 STDMETHODIMP FormatEnumerator_Reset(FormatEnumerator *fe) {
1896 fe->pos = 0;
1897 return S_OK;
1899 STDMETHODIMP FormatEnumerator_Clone(FormatEnumerator *fe, IEnumFORMATETC **ppenum) {
1900 FormatEnumerator *pfe;
1901 try {
1902 pfe = new FormatEnumerator(fe->pos, &fe->formats[0], fe->formats.size());
1903 } catch (...) {
1904 return E_OUTOFMEMORY;
1906 return FormatEnumerator_QueryInterface(pfe, IID_IEnumFORMATETC,
1907 reinterpret_cast<void **>(ppenum));
1910 static VFunction *vtFormatEnumerator[] = {
1911 (VFunction *)(FormatEnumerator_QueryInterface),
1912 (VFunction *)(FormatEnumerator_AddRef),
1913 (VFunction *)(FormatEnumerator_Release),
1914 (VFunction *)(FormatEnumerator_Next),
1915 (VFunction *)(FormatEnumerator_Skip),
1916 (VFunction *)(FormatEnumerator_Reset),
1917 (VFunction *)(FormatEnumerator_Clone)
1920 FormatEnumerator::FormatEnumerator(int pos_, CLIPFORMAT formats_[], size_t formatsLen_) {
1921 vtbl = vtFormatEnumerator;
1922 ref = 0; // First QI adds first reference...
1923 pos = pos_;
1924 formats.insert(formats.begin(), formats_, formats_+formatsLen_);
1927 /// Implement IUnknown
1928 STDMETHODIMP DropSource_QueryInterface(DropSource *ds, REFIID riid, PVOID *ppv) {
1929 return ds->sci->QueryInterface(riid, ppv);
1931 STDMETHODIMP_(ULONG)DropSource_AddRef(DropSource *ds) {
1932 return ds->sci->AddRef();
1934 STDMETHODIMP_(ULONG)DropSource_Release(DropSource *ds) {
1935 return ds->sci->Release();
1938 /// Implement IDropSource
1939 STDMETHODIMP DropSource_QueryContinueDrag(DropSource *, BOOL fEsc, DWORD grfKeyState) {
1940 if (fEsc)
1941 return DRAGDROP_S_CANCEL;
1942 if (!(grfKeyState & MK_LBUTTON))
1943 return DRAGDROP_S_DROP;
1944 return S_OK;
1947 STDMETHODIMP DropSource_GiveFeedback(DropSource *, DWORD) {
1948 return DRAGDROP_S_USEDEFAULTCURSORS;
1951 static VFunction *vtDropSource[] = {
1952 (VFunction *)(DropSource_QueryInterface),
1953 (VFunction *)(DropSource_AddRef),
1954 (VFunction *)(DropSource_Release),
1955 (VFunction *)(DropSource_QueryContinueDrag),
1956 (VFunction *)(DropSource_GiveFeedback)
1959 DropSource::DropSource() {
1960 vtbl = vtDropSource;
1961 sci = 0;
1964 /// Implement IUnkown
1965 STDMETHODIMP DataObject_QueryInterface(DataObject *pd, REFIID riid, PVOID *ppv) {
1966 //Platform::DebugPrintf("DO QI %x\n", pd);
1967 return pd->sci->QueryInterface(riid, ppv);
1969 STDMETHODIMP_(ULONG)DataObject_AddRef(DataObject *pd) {
1970 return pd->sci->AddRef();
1972 STDMETHODIMP_(ULONG)DataObject_Release(DataObject *pd) {
1973 return pd->sci->Release();
1975 /// Implement IDataObject
1976 STDMETHODIMP DataObject_GetData(DataObject *pd, FORMATETC *pFEIn, STGMEDIUM *pSTM) {
1977 return pd->sci->GetData(pFEIn, pSTM);
1980 STDMETHODIMP DataObject_GetDataHere(DataObject *, FORMATETC *, STGMEDIUM *) {
1981 //Platform::DebugPrintf("DOB GetDataHere\n");
1982 return E_NOTIMPL;
1985 STDMETHODIMP DataObject_QueryGetData(DataObject *pd, FORMATETC *pFE) {
1986 if (pd->sci->DragIsRectangularOK(pFE->cfFormat) &&
1987 pFE->ptd == 0 &&
1988 (pFE->dwAspect & DVASPECT_CONTENT) != 0 &&
1989 pFE->lindex == -1 &&
1990 (pFE->tymed & TYMED_HGLOBAL) != 0
1992 return S_OK;
1995 bool formatOK = (pFE->cfFormat == CF_TEXT) ||
1996 ((pFE->cfFormat == CF_UNICODETEXT) && pd->sci->IsUnicodeMode());
1997 if (!formatOK ||
1998 pFE->ptd != 0 ||
1999 (pFE->dwAspect & DVASPECT_CONTENT) == 0 ||
2000 pFE->lindex != -1 ||
2001 (pFE->tymed & TYMED_HGLOBAL) == 0
2003 //Platform::DebugPrintf("DOB QueryGetData No %x\n",pFE->cfFormat);
2004 //return DATA_E_FORMATETC;
2005 return S_FALSE;
2007 //Platform::DebugPrintf("DOB QueryGetData OK %x\n",pFE->cfFormat);
2008 return S_OK;
2011 STDMETHODIMP DataObject_GetCanonicalFormatEtc(DataObject *pd, FORMATETC *, FORMATETC *pFEOut) {
2012 //Platform::DebugPrintf("DOB GetCanon\n");
2013 if (pd->sci->IsUnicodeMode())
2014 pFEOut->cfFormat = CF_UNICODETEXT;
2015 else
2016 pFEOut->cfFormat = CF_TEXT;
2017 pFEOut->ptd = 0;
2018 pFEOut->dwAspect = DVASPECT_CONTENT;
2019 pFEOut->lindex = -1;
2020 pFEOut->tymed = TYMED_HGLOBAL;
2021 return S_OK;
2024 STDMETHODIMP DataObject_SetData(DataObject *, FORMATETC *, STGMEDIUM *, BOOL) {
2025 //Platform::DebugPrintf("DOB SetData\n");
2026 return E_FAIL;
2029 STDMETHODIMP DataObject_EnumFormatEtc(DataObject *pd, DWORD dwDirection, IEnumFORMATETC **ppEnum) {
2030 try {
2031 //Platform::DebugPrintf("DOB EnumFormatEtc %d\n", dwDirection);
2032 if (dwDirection != DATADIR_GET) {
2033 *ppEnum = 0;
2034 return E_FAIL;
2036 FormatEnumerator *pfe;
2037 if (pd->sci->IsUnicodeMode()) {
2038 CLIPFORMAT formats[] = {CF_UNICODETEXT, CF_TEXT};
2039 pfe = new FormatEnumerator(0, formats, ELEMENTS(formats));
2040 } else {
2041 CLIPFORMAT formats[] = {CF_TEXT};
2042 pfe = new FormatEnumerator(0, formats, ELEMENTS(formats));
2044 return FormatEnumerator_QueryInterface(pfe, IID_IEnumFORMATETC,
2045 reinterpret_cast<void **>(ppEnum));
2046 } catch (std::bad_alloc &) {
2047 pd->sci->errorStatus = SC_STATUS_BADALLOC;
2048 return E_OUTOFMEMORY;
2049 } catch (...) {
2050 pd->sci->errorStatus = SC_STATUS_FAILURE;
2051 return E_FAIL;
2055 STDMETHODIMP DataObject_DAdvise(DataObject *, FORMATETC *, DWORD, IAdviseSink *, PDWORD) {
2056 //Platform::DebugPrintf("DOB DAdvise\n");
2057 return E_FAIL;
2060 STDMETHODIMP DataObject_DUnadvise(DataObject *, DWORD) {
2061 //Platform::DebugPrintf("DOB DUnadvise\n");
2062 return E_FAIL;
2065 STDMETHODIMP DataObject_EnumDAdvise(DataObject *, IEnumSTATDATA **) {
2066 //Platform::DebugPrintf("DOB EnumDAdvise\n");
2067 return E_FAIL;
2070 static VFunction *vtDataObject[] = {
2071 (VFunction *)(DataObject_QueryInterface),
2072 (VFunction *)(DataObject_AddRef),
2073 (VFunction *)(DataObject_Release),
2074 (VFunction *)(DataObject_GetData),
2075 (VFunction *)(DataObject_GetDataHere),
2076 (VFunction *)(DataObject_QueryGetData),
2077 (VFunction *)(DataObject_GetCanonicalFormatEtc),
2078 (VFunction *)(DataObject_SetData),
2079 (VFunction *)(DataObject_EnumFormatEtc),
2080 (VFunction *)(DataObject_DAdvise),
2081 (VFunction *)(DataObject_DUnadvise),
2082 (VFunction *)(DataObject_EnumDAdvise)
2085 DataObject::DataObject() {
2086 vtbl = vtDataObject;
2087 sci = 0;
2090 /// Implement IUnknown
2091 STDMETHODIMP DropTarget_QueryInterface(DropTarget *dt, REFIID riid, PVOID *ppv) {
2092 //Platform::DebugPrintf("DT QI %x\n", dt);
2093 return dt->sci->QueryInterface(riid, ppv);
2095 STDMETHODIMP_(ULONG)DropTarget_AddRef(DropTarget *dt) {
2096 return dt->sci->AddRef();
2098 STDMETHODIMP_(ULONG)DropTarget_Release(DropTarget *dt) {
2099 return dt->sci->Release();
2102 /// Implement IDropTarget by forwarding to Scintilla
2103 STDMETHODIMP DropTarget_DragEnter(DropTarget *dt, LPDATAOBJECT pIDataSource, DWORD grfKeyState,
2104 POINTL pt, PDWORD pdwEffect) {
2105 try {
2106 return dt->sci->DragEnter(pIDataSource, grfKeyState, pt, pdwEffect);
2107 } catch (...) {
2108 dt->sci->errorStatus = SC_STATUS_FAILURE;
2110 return E_FAIL;
2112 STDMETHODIMP DropTarget_DragOver(DropTarget *dt, DWORD grfKeyState, POINTL pt, PDWORD pdwEffect) {
2113 try {
2114 return dt->sci->DragOver(grfKeyState, pt, pdwEffect);
2115 } catch (...) {
2116 dt->sci->errorStatus = SC_STATUS_FAILURE;
2118 return E_FAIL;
2120 STDMETHODIMP DropTarget_DragLeave(DropTarget *dt) {
2121 try {
2122 return dt->sci->DragLeave();
2123 } catch (...) {
2124 dt->sci->errorStatus = SC_STATUS_FAILURE;
2126 return E_FAIL;
2128 STDMETHODIMP DropTarget_Drop(DropTarget *dt, LPDATAOBJECT pIDataSource, DWORD grfKeyState,
2129 POINTL pt, PDWORD pdwEffect) {
2130 try {
2131 return dt->sci->Drop(pIDataSource, grfKeyState, pt, pdwEffect);
2132 } catch (...) {
2133 dt->sci->errorStatus = SC_STATUS_FAILURE;
2135 return E_FAIL;
2138 static VFunction *vtDropTarget[] = {
2139 (VFunction *)(DropTarget_QueryInterface),
2140 (VFunction *)(DropTarget_AddRef),
2141 (VFunction *)(DropTarget_Release),
2142 (VFunction *)(DropTarget_DragEnter),
2143 (VFunction *)(DropTarget_DragOver),
2144 (VFunction *)(DropTarget_DragLeave),
2145 (VFunction *)(DropTarget_Drop)
2148 DropTarget::DropTarget() {
2149 vtbl = vtDropTarget;
2150 sci = 0;
2154 * DBCS: support Input Method Editor (IME).
2155 * Called when IME Window opened.
2157 void ScintillaWin::ImeStartComposition() {
2158 if (caret.active) {
2159 // Move IME Window to current caret position
2160 HIMC hIMC = ::ImmGetContext(MainHWND());
2161 Point pos = PointMainCaret();
2162 COMPOSITIONFORM CompForm;
2163 CompForm.dwStyle = CFS_POINT;
2164 CompForm.ptCurrentPos.x = static_cast<int>(pos.x);
2165 CompForm.ptCurrentPos.y = static_cast<int>(pos.y);
2167 ::ImmSetCompositionWindow(hIMC, &CompForm);
2169 // Set font of IME window to same as surrounded text.
2170 if (stylesValid) {
2171 // Since the style creation code has been made platform independent,
2172 // The logfont for the IME is recreated here.
2173 int styleHere = (pdoc->StyleAt(sel.MainCaret())) & 31;
2174 LOGFONTW lf = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, L""};
2175 int sizeZoomed = vs.styles[styleHere].size + vs.zoomLevel * SC_FONT_SIZE_MULTIPLIER;
2176 if (sizeZoomed <= 2 * SC_FONT_SIZE_MULTIPLIER) // Hangs if sizeZoomed <= 1
2177 sizeZoomed = 2 * SC_FONT_SIZE_MULTIPLIER;
2178 AutoSurface surface(this);
2179 int deviceHeight = sizeZoomed;
2180 if (surface) {
2181 deviceHeight = (sizeZoomed * surface->LogPixelsY()) / 72;
2183 // The negative is to allow for leading
2184 lf.lfHeight = -(abs(deviceHeight / SC_FONT_SIZE_MULTIPLIER));
2185 lf.lfWeight = vs.styles[styleHere].weight;
2186 lf.lfItalic = static_cast<BYTE>(vs.styles[styleHere].italic ? 1 : 0);
2187 lf.lfCharSet = DEFAULT_CHARSET;
2188 lf.lfFaceName[0] = '\0';
2189 if (vs.styles[styleHere].fontName) {
2190 const char* fontName = vs.styles[styleHere].fontName;
2191 UTF16FromUTF8(fontName, strlen(fontName)+1, lf.lfFaceName, LF_FACESIZE);
2193 ::ImmSetCompositionFontW(hIMC, &lf);
2195 ::ImmReleaseContext(MainHWND(), hIMC);
2196 // Caret is displayed in IME window. So, caret in Scintilla is useless.
2197 DropCaret();
2201 /** Called when IME Window closed. */
2202 void ScintillaWin::ImeEndComposition() {
2203 ShowCaretAtCurrentPosition();
2206 void ScintillaWin::AddCharBytes(char b0, char b1) {
2208 int inputCodePage = InputCodePage();
2209 if (inputCodePage && IsUnicodeMode()) {
2210 char utfval[4] = "\0\0\0";
2211 char ansiChars[3];
2212 wchar_t wcs[2];
2213 if (b0) { // Two bytes from IME
2214 ansiChars[0] = b0;
2215 ansiChars[1] = b1;
2216 ansiChars[2] = '\0';
2217 ::MultiByteToWideChar(inputCodePage, 0, ansiChars, 2, wcs, 1);
2218 } else {
2219 ansiChars[0] = b1;
2220 ansiChars[1] = '\0';
2221 ::MultiByteToWideChar(inputCodePage, 0, ansiChars, 1, wcs, 1);
2223 unsigned int len = UTF8Length(wcs, 1);
2224 UTF8FromUTF16(wcs, 1, utfval, len);
2225 utfval[len] = '\0';
2226 AddCharUTF(utfval, len ? len : 1);
2227 } else if (b0) {
2228 char dbcsChars[3];
2229 dbcsChars[0] = b0;
2230 dbcsChars[1] = b1;
2231 dbcsChars[2] = '\0';
2232 AddCharUTF(dbcsChars, 2, true);
2233 } else {
2234 AddChar(b1);
2238 void ScintillaWin::GetIntelliMouseParameters() {
2239 // This retrieves the number of lines per scroll as configured inthe Mouse Properties sheet in Control Panel
2240 ::SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, &linesPerScroll, 0);
2243 void ScintillaWin::CopyToClipboard(const SelectionText &selectedText) {
2244 if (!::OpenClipboard(MainHWND()))
2245 return;
2246 ::EmptyClipboard();
2248 GlobalMemory uniText;
2250 // Default Scintilla behaviour in Unicode mode
2251 if (IsUnicodeMode()) {
2252 int uchars = UTF16Length(selectedText.Data(),
2253 static_cast<int>(selectedText.LengthWithTerminator()));
2254 uniText.Allocate(2 * uchars);
2255 if (uniText) {
2256 UTF16FromUTF8(selectedText.Data(), static_cast<int>(selectedText.LengthWithTerminator()),
2257 static_cast<wchar_t *>(uniText.ptr), uchars);
2259 } else {
2260 // Not Unicode mode
2261 // Convert to Unicode using the current Scintilla code page
2262 UINT cpSrc = CodePageFromCharSet(
2263 selectedText.characterSet, selectedText.codePage);
2264 int uLen = ::MultiByteToWideChar(cpSrc, 0, selectedText.Data(),
2265 static_cast<int>(selectedText.LengthWithTerminator()), 0, 0);
2266 uniText.Allocate(2 * uLen);
2267 if (uniText) {
2268 ::MultiByteToWideChar(cpSrc, 0, selectedText.Data(),
2269 static_cast<int>(selectedText.LengthWithTerminator()),
2270 static_cast<wchar_t *>(uniText.ptr), uLen);
2274 if (uniText) {
2275 if (!IsNT()) {
2276 // Copy ANSI text to clipboard on Windows 9x
2277 // Convert from Unicode text, so other ANSI programs can
2278 // paste the text
2279 // Windows NT, 2k, XP automatically generates CF_TEXT
2280 GlobalMemory ansiText;
2281 ansiText.Allocate(selectedText.LengthWithTerminator());
2282 if (ansiText) {
2283 ::WideCharToMultiByte(CP_ACP, 0, static_cast<wchar_t *>(uniText.ptr), -1,
2284 static_cast<char *>(ansiText.ptr),
2285 static_cast<int>(selectedText.LengthWithTerminator()), 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.LengthWithTerminator());
2294 if (ansiText) {
2295 memcpy(static_cast<char *>(ansiText.ptr), selectedText.Data(), selectedText.LengthWithTerminator());
2296 ansiText.SetClip(CF_TEXT);
2300 if (selectedText.rectangular) {
2301 ::SetClipboardData(cfColumnSelect, 0);
2303 GlobalMemory borlandSelection;
2304 borlandSelection.Allocate(1);
2305 if (borlandSelection) {
2306 static_cast<BYTE *>(borlandSelection.ptr)[0] = 0x02;
2307 borlandSelection.SetClip(cfBorlandIDEBlockType);
2311 if (selectedText.lineCopy) {
2312 ::SetClipboardData(cfLineSelect, 0);
2315 ::CloseClipboard();
2318 void ScintillaWin::ScrollMessage(WPARAM wParam) {
2319 //DWORD dwStart = timeGetTime();
2320 //Platform::DebugPrintf("Scroll %x %d\n", wParam, lParam);
2322 SCROLLINFO sci = {};
2323 sci.cbSize = sizeof(sci);
2324 sci.fMask = SIF_ALL;
2326 GetScrollInfo(SB_VERT, &sci);
2328 //Platform::DebugPrintf("ScrollInfo %d mask=%x min=%d max=%d page=%d pos=%d track=%d\n", b,sci.fMask,
2329 //sci.nMin, sci.nMax, sci.nPage, sci.nPos, sci.nTrackPos);
2331 int topLineNew = topLine;
2332 switch (LoWord(wParam)) {
2333 case SB_LINEUP:
2334 topLineNew -= 1;
2335 break;
2336 case SB_LINEDOWN:
2337 topLineNew += 1;
2338 break;
2339 case SB_PAGEUP:
2340 topLineNew -= LinesToScroll(); break;
2341 case SB_PAGEDOWN: topLineNew += LinesToScroll(); break;
2342 case SB_TOP: topLineNew = 0; break;
2343 case SB_BOTTOM: topLineNew = MaxScrollPos(); break;
2344 case SB_THUMBPOSITION: topLineNew = sci.nTrackPos; break;
2345 case SB_THUMBTRACK: topLineNew = sci.nTrackPos; break;
2347 ScrollTo(topLineNew);
2350 void ScintillaWin::HorizontalScrollMessage(WPARAM wParam) {
2351 int xPos = xOffset;
2352 PRectangle rcText = GetTextRectangle();
2353 int pageWidth = static_cast<int>(rcText.Width() * 2 / 3);
2354 switch (LoWord(wParam)) {
2355 case SB_LINEUP:
2356 xPos -= 20;
2357 break;
2358 case SB_LINEDOWN: // May move past the logical end
2359 xPos += 20;
2360 break;
2361 case SB_PAGEUP:
2362 xPos -= pageWidth;
2363 break;
2364 case SB_PAGEDOWN:
2365 xPos += pageWidth;
2366 if (xPos > scrollWidth - rcText.Width()) { // Hit the end exactly
2367 xPos = scrollWidth - static_cast<int>(rcText.Width());
2369 break;
2370 case SB_TOP:
2371 xPos = 0;
2372 break;
2373 case SB_BOTTOM:
2374 xPos = scrollWidth;
2375 break;
2376 case SB_THUMBPOSITION:
2377 case SB_THUMBTRACK: {
2378 // 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 =]
2379 SCROLLINFO si;
2380 si.cbSize = sizeof(si);
2381 si.fMask = SIF_TRACKPOS;
2382 if (GetScrollInfo(SB_HORZ, &si)) {
2383 xPos = si.nTrackPos;
2386 break;
2388 HorizontalScrollTo(xPos);
2392 * Redraw all of text area.
2393 * This paint will not be abandoned.
2395 void ScintillaWin::FullPaint() {
2396 if (technology == SC_TECHNOLOGY_DEFAULT) {
2397 HDC hdc = ::GetDC(MainHWND());
2398 FullPaintDC(hdc);
2399 ::ReleaseDC(MainHWND(), hdc);
2400 } else {
2401 FullPaintDC(0);
2406 * Redraw all of text area on the specified DC.
2407 * This paint will not be abandoned.
2409 void ScintillaWin::FullPaintDC(HDC hdc) {
2410 paintState = painting;
2411 rcPaint = GetClientRectangle();
2412 paintingAllText = true;
2413 if (technology == SC_TECHNOLOGY_DEFAULT) {
2414 AutoSurface surfaceWindow(hdc, this);
2415 if (surfaceWindow) {
2416 Paint(surfaceWindow, rcPaint);
2417 surfaceWindow->Release();
2419 } else {
2420 #if defined(USE_D2D)
2421 EnsureRenderTarget();
2422 AutoSurface surfaceWindow(pRenderTarget, this);
2423 if (surfaceWindow) {
2424 pRenderTarget->BeginDraw();
2425 Paint(surfaceWindow, rcPaint);
2426 surfaceWindow->Release();
2427 HRESULT hr = pRenderTarget->EndDraw();
2428 if (hr == D2DERR_RECREATE_TARGET) {
2429 DropRenderTarget();
2432 #endif
2434 paintState = notPainting;
2437 static bool CompareDevCap(HDC hdc, HDC hOtherDC, int nIndex) {
2438 return ::GetDeviceCaps(hdc, nIndex) == ::GetDeviceCaps(hOtherDC, nIndex);
2441 bool ScintillaWin::IsCompatibleDC(HDC hOtherDC) {
2442 HDC hdc = ::GetDC(MainHWND());
2443 bool isCompatible =
2444 CompareDevCap(hdc, hOtherDC, TECHNOLOGY) &&
2445 CompareDevCap(hdc, hOtherDC, LOGPIXELSY) &&
2446 CompareDevCap(hdc, hOtherDC, LOGPIXELSX) &&
2447 CompareDevCap(hdc, hOtherDC, BITSPIXEL) &&
2448 CompareDevCap(hdc, hOtherDC, PLANES);
2449 ::ReleaseDC(MainHWND(), hdc);
2450 return isCompatible;
2453 DWORD ScintillaWin::EffectFromState(DWORD grfKeyState) const {
2454 // These are the Wordpad semantics.
2455 DWORD dwEffect;
2456 if (inDragDrop == ddDragging) // Internal defaults to move
2457 dwEffect = DROPEFFECT_MOVE;
2458 else
2459 dwEffect = DROPEFFECT_COPY;
2460 if (grfKeyState & MK_ALT)
2461 dwEffect = DROPEFFECT_MOVE;
2462 if (grfKeyState & MK_CONTROL)
2463 dwEffect = DROPEFFECT_COPY;
2464 return dwEffect;
2467 /// Implement IUnknown
2468 STDMETHODIMP ScintillaWin::QueryInterface(REFIID riid, PVOID *ppv) {
2469 *ppv = NULL;
2470 if (riid == IID_IUnknown)
2471 *ppv = reinterpret_cast<IDropTarget *>(&dt);
2472 if (riid == IID_IDropSource)
2473 *ppv = reinterpret_cast<IDropSource *>(&ds);
2474 if (riid == IID_IDropTarget)
2475 *ppv = reinterpret_cast<IDropTarget *>(&dt);
2476 if (riid == IID_IDataObject)
2477 *ppv = reinterpret_cast<IDataObject *>(&dob);
2478 if (!*ppv)
2479 return E_NOINTERFACE;
2480 return S_OK;
2483 STDMETHODIMP_(ULONG) ScintillaWin::AddRef() {
2484 return 1;
2487 STDMETHODIMP_(ULONG) ScintillaWin::Release() {
2488 return 1;
2491 /// Implement IDropTarget
2492 STDMETHODIMP ScintillaWin::DragEnter(LPDATAOBJECT pIDataSource, DWORD grfKeyState,
2493 POINTL, PDWORD pdwEffect) {
2494 if (pIDataSource == NULL)
2495 return E_POINTER;
2496 FORMATETC fmtu = {CF_UNICODETEXT, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
2497 HRESULT hrHasUText = pIDataSource->QueryGetData(&fmtu);
2498 hasOKText = (hrHasUText == S_OK);
2499 if (!hasOKText) {
2500 FORMATETC fmte = {CF_TEXT, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
2501 HRESULT hrHasText = pIDataSource->QueryGetData(&fmte);
2502 hasOKText = (hrHasText == S_OK);
2504 if (!hasOKText) {
2505 *pdwEffect = DROPEFFECT_NONE;
2506 return S_OK;
2509 *pdwEffect = EffectFromState(grfKeyState);
2510 return S_OK;
2513 STDMETHODIMP ScintillaWin::DragOver(DWORD grfKeyState, POINTL pt, PDWORD pdwEffect) {
2514 try {
2515 if (!hasOKText || pdoc->IsReadOnly()) {
2516 *pdwEffect = DROPEFFECT_NONE;
2517 return S_OK;
2520 *pdwEffect = EffectFromState(grfKeyState);
2522 // Update the cursor.
2523 POINT rpt = {pt.x, pt.y};
2524 ::ScreenToClient(MainHWND(), &rpt);
2525 SetDragPosition(SPositionFromLocation(PointFromPOINT(rpt), false, false, UserVirtualSpace()));
2527 return S_OK;
2528 } catch (...) {
2529 errorStatus = SC_STATUS_FAILURE;
2531 return E_FAIL;
2534 STDMETHODIMP ScintillaWin::DragLeave() {
2535 try {
2536 SetDragPosition(SelectionPosition(invalidPosition));
2537 return S_OK;
2538 } catch (...) {
2539 errorStatus = SC_STATUS_FAILURE;
2541 return E_FAIL;
2544 STDMETHODIMP ScintillaWin::Drop(LPDATAOBJECT pIDataSource, DWORD grfKeyState,
2545 POINTL pt, PDWORD pdwEffect) {
2546 try {
2547 *pdwEffect = EffectFromState(grfKeyState);
2549 if (pIDataSource == NULL)
2550 return E_POINTER;
2552 SetDragPosition(SelectionPosition(invalidPosition));
2554 STGMEDIUM medium = {0, {0}, 0};
2556 std::vector<char> data; // Includes terminating NUL
2558 FORMATETC fmtu = {CF_UNICODETEXT, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
2559 HRESULT hr = pIDataSource->GetData(&fmtu, &medium);
2560 if (SUCCEEDED(hr) && medium.hGlobal) {
2561 GlobalMemory memUDrop(medium.hGlobal);
2562 wchar_t *udata = static_cast<wchar_t *>(memUDrop.ptr);
2563 if (udata) {
2564 if (IsUnicodeMode()) {
2565 int tlen = static_cast<int>(memUDrop.Size());
2566 // Convert UTF-16 to UTF-8
2567 int dataLen = UTF8Length(udata, tlen/2);
2568 data.resize(dataLen+1);
2569 UTF8FromUTF16(udata, tlen/2, &data[0], dataLen);
2570 } else {
2571 // Convert UTF-16 to ANSI
2573 // Default Scintilla behavior in Unicode mode
2574 // CF_UNICODETEXT available, but not in Unicode mode
2575 // Convert from Unicode to current Scintilla code page
2576 UINT cpDest = CodePageOfDocument();
2577 int tlen = ::WideCharToMultiByte(cpDest, 0, udata, -1,
2578 NULL, 0, NULL, NULL) - 1; // subtract 0 terminator
2579 data.resize(tlen + 1);
2580 ::WideCharToMultiByte(cpDest, 0, udata, -1,
2581 &data[0], tlen + 1, NULL, NULL);
2584 memUDrop.Unlock();
2585 } else {
2586 FORMATETC fmte = {CF_TEXT, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
2587 hr = pIDataSource->GetData(&fmte, &medium);
2588 if (SUCCEEDED(hr) && medium.hGlobal) {
2589 GlobalMemory memDrop(medium.hGlobal);
2590 const char *cdata = static_cast<char *>(memDrop.ptr);
2591 if (cdata)
2592 data.assign(cdata, cdata+strlen(cdata)+1);
2593 memDrop.Unlock();
2597 if (!SUCCEEDED(hr) || data.empty()) {
2598 //Platform::DebugPrintf("Bad data format: 0x%x\n", hres);
2599 return hr;
2602 FORMATETC fmtr = {cfColumnSelect, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
2603 HRESULT hrRectangular = pIDataSource->QueryGetData(&fmtr);
2605 POINT rpt = {pt.x, pt.y};
2606 ::ScreenToClient(MainHWND(), &rpt);
2607 SelectionPosition movePos = SPositionFromLocation(PointFromPOINT(rpt), false, false, UserVirtualSpace());
2609 DropAt(movePos, &data[0], data.size() - 1, *pdwEffect == DROPEFFECT_MOVE, hrRectangular == S_OK);
2611 // Free data
2612 if (medium.pUnkForRelease != NULL)
2613 medium.pUnkForRelease->Release();
2614 else
2615 ::GlobalFree(medium.hGlobal);
2617 return S_OK;
2618 } catch (...) {
2619 errorStatus = SC_STATUS_FAILURE;
2621 return E_FAIL;
2624 /// Implement important part of IDataObject
2625 STDMETHODIMP ScintillaWin::GetData(FORMATETC *pFEIn, STGMEDIUM *pSTM) {
2626 bool formatOK = (pFEIn->cfFormat == CF_TEXT) ||
2627 ((pFEIn->cfFormat == CF_UNICODETEXT) && IsUnicodeMode());
2628 if (!formatOK ||
2629 pFEIn->ptd != 0 ||
2630 (pFEIn->dwAspect & DVASPECT_CONTENT) == 0 ||
2631 pFEIn->lindex != -1 ||
2632 (pFEIn->tymed & TYMED_HGLOBAL) == 0
2634 //Platform::DebugPrintf("DOB GetData No %d %x %x fmt=%x\n", lenDrag, pFEIn, pSTM, pFEIn->cfFormat);
2635 return DATA_E_FORMATETC;
2637 pSTM->tymed = TYMED_HGLOBAL;
2638 //Platform::DebugPrintf("DOB GetData OK %d %x %x\n", lenDrag, pFEIn, pSTM);
2640 GlobalMemory text;
2641 if (pFEIn->cfFormat == CF_UNICODETEXT) {
2642 int uchars = UTF16Length(drag.Data(), static_cast<int>(drag.LengthWithTerminator()));
2643 text.Allocate(2 * uchars);
2644 if (text) {
2645 UTF16FromUTF8(drag.Data(), static_cast<int>(drag.LengthWithTerminator()),
2646 static_cast<wchar_t *>(text.ptr), uchars);
2648 } else {
2649 text.Allocate(drag.LengthWithTerminator());
2650 if (text) {
2651 memcpy(static_cast<char *>(text.ptr), drag.Data(), drag.LengthWithTerminator());
2654 pSTM->hGlobal = text ? text.Unlock() : 0;
2655 pSTM->pUnkForRelease = 0;
2656 return S_OK;
2659 bool ScintillaWin::Register(HINSTANCE hInstance_) {
2661 hInstance = hInstance_;
2662 bool result;
2664 // Register the Scintilla class
2665 if (IsNT()) {
2667 // Register Scintilla as a wide character window
2668 WNDCLASSEXW wndclass;
2669 wndclass.cbSize = sizeof(wndclass);
2670 wndclass.style = CS_GLOBALCLASS | CS_HREDRAW | CS_VREDRAW;
2671 wndclass.lpfnWndProc = ScintillaWin::SWndProc;
2672 wndclass.cbClsExtra = 0;
2673 wndclass.cbWndExtra = sizeof(ScintillaWin *);
2674 wndclass.hInstance = hInstance;
2675 wndclass.hIcon = NULL;
2676 wndclass.hCursor = NULL;
2677 wndclass.hbrBackground = NULL;
2678 wndclass.lpszMenuName = NULL;
2679 wndclass.lpszClassName = L"Scintilla";
2680 wndclass.hIconSm = 0;
2681 result = ::RegisterClassExW(&wndclass) != 0;
2682 } else {
2684 // Register Scintilla as a normal character window
2685 WNDCLASSEX 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 = scintillaClassName;
2697 wndclass.hIconSm = 0;
2698 result = ::RegisterClassEx(&wndclass) != 0;
2701 if (result) {
2702 // Register the CallTip class
2703 WNDCLASSEX wndclassc;
2704 wndclassc.cbSize = sizeof(wndclassc);
2705 wndclassc.style = CS_GLOBALCLASS | CS_HREDRAW | CS_VREDRAW;
2706 wndclassc.cbClsExtra = 0;
2707 wndclassc.cbWndExtra = sizeof(ScintillaWin *);
2708 wndclassc.hInstance = hInstance;
2709 wndclassc.hIcon = NULL;
2710 wndclassc.hbrBackground = NULL;
2711 wndclassc.lpszMenuName = NULL;
2712 wndclassc.lpfnWndProc = ScintillaWin::CTWndProc;
2713 wndclassc.hCursor = ::LoadCursor(NULL, IDC_ARROW);
2714 wndclassc.lpszClassName = callClassName;
2715 wndclassc.hIconSm = 0;
2717 result = ::RegisterClassEx(&wndclassc) != 0;
2720 return result;
2723 bool ScintillaWin::Unregister() {
2724 bool result = ::UnregisterClass(scintillaClassName, hInstance) != 0;
2725 if (::UnregisterClass(callClassName, hInstance) == 0)
2726 result = false;
2727 return result;
2730 bool ScintillaWin::HasCaretSizeChanged() const {
2731 if (
2732 ( (0 != vs.caretWidth) && (sysCaretWidth != vs.caretWidth) )
2733 || ((0 != vs.lineHeight) && (sysCaretHeight != vs.lineHeight))
2735 return true;
2737 return false;
2740 BOOL ScintillaWin::CreateSystemCaret() {
2741 sysCaretWidth = vs.caretWidth;
2742 if (0 == sysCaretWidth) {
2743 sysCaretWidth = 1;
2745 sysCaretHeight = vs.lineHeight;
2746 int bitmapSize = (((sysCaretWidth + 15) & ~15) >> 3) *
2747 sysCaretHeight;
2748 std::vector<char> bits(bitmapSize);
2749 sysCaretBitmap = ::CreateBitmap(sysCaretWidth, sysCaretHeight, 1,
2750 1, reinterpret_cast<BYTE *>(&bits[0]));
2751 BOOL retval = ::CreateCaret(
2752 MainHWND(), sysCaretBitmap,
2753 sysCaretWidth, sysCaretHeight);
2754 ::ShowCaret(MainHWND());
2755 return retval;
2758 BOOL ScintillaWin::DestroySystemCaret() {
2759 ::HideCaret(MainHWND());
2760 BOOL retval = ::DestroyCaret();
2761 if (sysCaretBitmap) {
2762 ::DeleteObject(sysCaretBitmap);
2763 sysCaretBitmap = 0;
2765 return retval;
2768 sptr_t PASCAL ScintillaWin::CTWndProc(
2769 HWND hWnd, UINT iMessage, WPARAM wParam, sptr_t lParam) {
2770 // Find C++ object associated with window.
2771 ScintillaWin *sciThis = reinterpret_cast<ScintillaWin *>(PointerFromWindow(hWnd));
2772 try {
2773 // ctp will be zero if WM_CREATE not seen yet
2774 if (sciThis == 0) {
2775 if (iMessage == WM_CREATE) {
2776 // Associate CallTip object with window
2777 CREATESTRUCT *pCreate = reinterpret_cast<CREATESTRUCT *>(lParam);
2778 SetWindowPointer(hWnd, pCreate->lpCreateParams);
2779 return 0;
2780 } else {
2781 return ::DefWindowProc(hWnd, iMessage, wParam, lParam);
2783 } else {
2784 if (iMessage == WM_NCDESTROY) {
2785 ::SetWindowLong(hWnd, 0, 0);
2786 return ::DefWindowProc(hWnd, iMessage, wParam, lParam);
2787 } else if (iMessage == WM_PAINT) {
2788 PAINTSTRUCT ps;
2789 ::BeginPaint(hWnd, &ps);
2790 Surface *surfaceWindow = Surface::Allocate(sciThis->technology);
2791 if (surfaceWindow) {
2792 #if defined(USE_D2D)
2793 ID2D1HwndRenderTarget *pCTRenderTarget = 0;
2794 #endif
2795 RECT rc;
2796 GetClientRect(hWnd, &rc);
2797 // Create a Direct2D render target.
2798 if (sciThis->technology == SC_TECHNOLOGY_DEFAULT) {
2799 surfaceWindow->Init(ps.hdc, hWnd);
2800 } else {
2801 #if defined(USE_D2D)
2802 D2D1_HWND_RENDER_TARGET_PROPERTIES dhrtp;
2803 dhrtp.hwnd = hWnd;
2804 dhrtp.pixelSize = D2D1::SizeU(rc.right - rc.left, rc.bottom - rc.top);
2805 dhrtp.presentOptions = D2D1_PRESENT_OPTIONS_NONE;
2807 D2D1_RENDER_TARGET_PROPERTIES drtp;
2808 drtp.type = D2D1_RENDER_TARGET_TYPE_DEFAULT;
2809 drtp.pixelFormat.format = DXGI_FORMAT_UNKNOWN;
2810 drtp.pixelFormat.alphaMode = D2D1_ALPHA_MODE_UNKNOWN;
2811 drtp.dpiX = 96.0;
2812 drtp.dpiY = 96.0;
2813 drtp.usage = D2D1_RENDER_TARGET_USAGE_NONE;
2814 drtp.minLevel = D2D1_FEATURE_LEVEL_DEFAULT;
2816 if (!SUCCEEDED(pD2DFactory->CreateHwndRenderTarget(drtp, dhrtp, &pCTRenderTarget))) {
2817 surfaceWindow->Release();
2818 delete surfaceWindow;
2819 ::EndPaint(hWnd, &ps);
2820 return 0;
2822 surfaceWindow->Init(pCTRenderTarget, hWnd);
2823 pCTRenderTarget->BeginDraw();
2824 #endif
2826 surfaceWindow->SetUnicodeMode(SC_CP_UTF8 == sciThis->ct.codePage);
2827 surfaceWindow->SetDBCSMode(sciThis->ct.codePage);
2828 sciThis->ct.PaintCT(surfaceWindow);
2829 #if defined(USE_D2D)
2830 if (pCTRenderTarget)
2831 pCTRenderTarget->EndDraw();
2832 #endif
2833 surfaceWindow->Release();
2834 delete surfaceWindow;
2835 #if defined(USE_D2D)
2836 if (pCTRenderTarget)
2837 pCTRenderTarget->Release();
2838 #endif
2840 ::EndPaint(hWnd, &ps);
2841 return 0;
2842 } else if ((iMessage == WM_NCLBUTTONDOWN) || (iMessage == WM_NCLBUTTONDBLCLK)) {
2843 POINT pt;
2844 pt.x = static_cast<short>(LOWORD(lParam));
2845 pt.y = static_cast<short>(HIWORD(lParam));
2846 ScreenToClient(hWnd, &pt);
2847 sciThis->ct.MouseClick(PointFromPOINT(pt));
2848 sciThis->CallTipClick();
2849 return 0;
2850 } else if (iMessage == WM_LBUTTONDOWN) {
2851 // This does not fire due to the hit test code
2852 sciThis->ct.MouseClick(Point::FromLong(static_cast<long>(lParam)));
2853 sciThis->CallTipClick();
2854 return 0;
2855 } else if (iMessage == WM_SETCURSOR) {
2856 ::SetCursor(::LoadCursor(NULL, IDC_ARROW));
2857 return 0;
2858 } else if (iMessage == WM_NCHITTEST) {
2859 return HTCAPTION;
2860 } else {
2861 return ::DefWindowProc(hWnd, iMessage, wParam, lParam);
2864 } catch (...) {
2865 sciThis->errorStatus = SC_STATUS_FAILURE;
2867 return ::DefWindowProc(hWnd, iMessage, wParam, lParam);
2870 sptr_t ScintillaWin::DirectFunction(
2871 sptr_t ptr, UINT iMessage, uptr_t wParam, sptr_t lParam) {
2872 PLATFORM_ASSERT(::GetCurrentThreadId() == ::GetWindowThreadProcessId(reinterpret_cast<ScintillaWin *>(ptr)->MainHWND(), NULL));
2873 return reinterpret_cast<ScintillaWin *>(ptr)->WndProc(iMessage, wParam, lParam);
2876 extern "C"
2877 #ifndef STATIC_BUILD
2878 __declspec(dllexport)
2879 #endif
2880 sptr_t __stdcall Scintilla_DirectFunction(
2881 ScintillaWin *sci, UINT iMessage, uptr_t wParam, sptr_t lParam) {
2882 return sci->WndProc(iMessage, wParam, lParam);
2885 sptr_t PASCAL ScintillaWin::SWndProc(
2886 HWND hWnd, UINT iMessage, WPARAM wParam, sptr_t lParam) {
2887 //Platform::DebugPrintf("S W:%x M:%x WP:%x L:%x\n", hWnd, iMessage, wParam, lParam);
2889 // Find C++ object associated with window.
2890 ScintillaWin *sci = reinterpret_cast<ScintillaWin *>(PointerFromWindow(hWnd));
2891 // sci will be zero if WM_CREATE not seen yet
2892 if (sci == 0) {
2893 try {
2894 if (iMessage == WM_CREATE) {
2895 // Create C++ object associated with window
2896 sci = new ScintillaWin(hWnd);
2897 SetWindowPointer(hWnd, sci);
2898 return sci->WndProc(iMessage, wParam, lParam);
2900 } catch (...) {
2902 return ::DefWindowProc(hWnd, iMessage, wParam, lParam);
2903 } else {
2904 if (iMessage == WM_NCDESTROY) {
2905 try {
2906 sci->Finalise();
2907 delete sci;
2908 } catch (...) {
2910 ::SetWindowLong(hWnd, 0, 0);
2911 return ::DefWindowProc(hWnd, iMessage, wParam, lParam);
2912 } else {
2913 return sci->WndProc(iMessage, wParam, lParam);
2918 // This function is externally visible so it can be called from container when building statically.
2919 // Must be called once only.
2920 int Scintilla_RegisterClasses(void *hInstance) {
2921 Platform_Initialise(hInstance);
2922 bool result = ScintillaWin::Register(reinterpret_cast<HINSTANCE>(hInstance));
2923 #ifdef SCI_LEXER
2924 Scintilla_LinkLexers();
2925 #endif
2926 return result;
2929 static int ResourcesRelease(bool fromDllMain) {
2930 bool result = ScintillaWin::Unregister();
2931 if (commctrl32) {
2932 FreeLibrary(commctrl32);
2933 commctrl32 = NULL;
2935 Platform_Finalise(fromDllMain);
2936 return result;
2939 // This function is externally visible so it can be called from container when building statically.
2940 int Scintilla_ReleaseResources() {
2941 return ResourcesRelease(false);
2944 #ifndef STATIC_BUILD
2945 extern "C" int APIENTRY DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpvReserved) {
2946 //Platform::DebugPrintf("Scintilla::DllMain %d %d\n", hInstance, dwReason);
2947 if (dwReason == DLL_PROCESS_ATTACH) {
2948 if (!Scintilla_RegisterClasses(hInstance))
2949 return FALSE;
2950 } else if (dwReason == DLL_PROCESS_DETACH) {
2951 if (lpvReserved == NULL) {
2952 ResourcesRelease(true);
2955 return TRUE;
2957 #endif