Update Scintilla to version 3.5.3
[TortoiseGit.git] / ext / scintilla / win32 / ScintillaWin.cxx
blob211b8d46800db0516ea06d7d626a1510ce43ce47
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 <stdexcept>
16 #include <new>
17 #include <string>
18 #include <vector>
19 #include <map>
20 #include <algorithm>
22 #undef _WIN32_WINNT
23 #define _WIN32_WINNT 0x0500
24 #undef WINVER
25 #define WINVER 0x0500
26 #include <windows.h>
27 #include <commctrl.h>
28 #include <richedit.h>
29 #include <windowsx.h>
30 #include <zmouse.h>
31 #include <ole2.h>
33 #if defined(NTDDI_WIN7) && !defined(DISABLE_D2D)
34 #define USE_D2D 1
35 #endif
37 #if defined(USE_D2D)
38 #include <d2d1.h>
39 #include <dwrite.h>
40 #endif
42 #include "Platform.h"
44 #include "ILexer.h"
45 #include "Scintilla.h"
47 #ifdef SCI_LEXER
48 #include "SciLexer.h"
49 #endif
50 #include "StringCopy.h"
51 #ifdef SCI_LEXER
52 #include "LexerModule.h"
53 #endif
54 #include "SplitVector.h"
55 #include "Partitioning.h"
56 #include "RunStyles.h"
57 #include "ContractionState.h"
58 #include "CellBuffer.h"
59 #include "CallTip.h"
60 #include "KeyMap.h"
61 #include "Indicator.h"
62 #include "XPM.h"
63 #include "LineMarker.h"
64 #include "Style.h"
65 #include "ViewStyle.h"
66 #include "CharClassify.h"
67 #include "Decoration.h"
68 #include "CaseFolder.h"
69 #include "Document.h"
70 #include "CaseConvert.h"
71 #include "UniConversion.h"
72 #include "Selection.h"
73 #include "PositionCache.h"
74 #include "EditModel.h"
75 #include "MarginView.h"
76 #include "EditView.h"
77 #include "Editor.h"
79 #include "AutoComplete.h"
80 #include "ScintillaBase.h"
82 #ifdef SCI_LEXER
83 #include "ExternalLexer.h"
84 #endif
86 #include "PlatWin.h"
88 #ifndef SPI_GETWHEELSCROLLLINES
89 #define SPI_GETWHEELSCROLLLINES 104
90 #endif
92 #ifndef WM_UNICHAR
93 #define WM_UNICHAR 0x0109
94 #endif
96 #ifndef UNICODE_NOCHAR
97 #define UNICODE_NOCHAR 0xFFFF
98 #endif
100 #ifndef MK_ALT
101 #define MK_ALT 32
102 #endif
104 #define SC_WIN_IDLE 5001
106 #define SC_INDICATOR_INPUT INDIC_IME
107 #define SC_INDICATOR_TARGET INDIC_IME+1
108 #define SC_INDICATOR_CONVERTED INDIC_IME+2
109 #define SC_INDICATOR_UNKNOWN INDIC_IME_MAX
111 typedef BOOL (WINAPI *TrackMouseEventSig)(LPTRACKMOUSEEVENT);
112 typedef UINT_PTR (WINAPI *SetCoalescableTimerSig)(HWND hwnd, UINT_PTR nIDEvent,
113 UINT uElapse, TIMERPROC lpTimerFunc, ULONG uToleranceDelay);
115 // GCC has trouble with the standard COM ABI so do it the old C way with explicit vtables.
117 const TCHAR scintillaClassName[] = TEXT("Scintilla");
118 const TCHAR callClassName[] = TEXT("CallTip");
120 #ifdef SCI_NAMESPACE
121 using namespace Scintilla;
122 #endif
124 static void *PointerFromWindow(HWND hWnd) {
125 return reinterpret_cast<void *>(::GetWindowLongPtr(hWnd, 0));
128 static void SetWindowPointer(HWND hWnd, void *ptr) {
129 ::SetWindowLongPtr(hWnd, 0, reinterpret_cast<LONG_PTR>(ptr));
132 static void SetWindowID(HWND hWnd, int identifier) {
133 ::SetWindowLongPtr(hWnd, GWLP_ID, identifier);
136 static Point PointFromPOINT(POINT pt) {
137 return Point::FromInts(pt.x, pt.y);
140 class ScintillaWin; // Forward declaration for COM interface subobjects
142 typedef void VFunction(void);
144 static HMODULE commctrl32 = 0;
148 class FormatEnumerator {
149 public:
150 VFunction **vtbl;
151 int ref;
152 unsigned int pos;
153 std::vector<CLIPFORMAT> formats;
154 FormatEnumerator(int pos_, CLIPFORMAT formats_[], size_t formatsLen_);
159 class DropSource {
160 public:
161 VFunction **vtbl;
162 ScintillaWin *sci;
163 DropSource();
168 class DataObject {
169 public:
170 VFunction **vtbl;
171 ScintillaWin *sci;
172 DataObject();
177 class DropTarget {
178 public:
179 VFunction **vtbl;
180 ScintillaWin *sci;
181 DropTarget();
186 class ScintillaWin :
187 public ScintillaBase {
189 bool lastKeyDownConsumed;
191 bool capturedMouse;
192 bool trackedMouseLeave;
193 TrackMouseEventSig TrackMouseEventFn;
194 SetCoalescableTimerSig SetCoalescableTimerFn;
196 unsigned int linesPerScroll; ///< Intellimouse support
197 int wheelDelta; ///< Wheel delta from roll
199 HRGN hRgnUpdate;
201 bool hasOKText;
203 CLIPFORMAT cfColumnSelect;
204 CLIPFORMAT cfBorlandIDEBlockType;
205 CLIPFORMAT cfLineSelect;
206 CLIPFORMAT cfVSLineTag;
208 HRESULT hrOle;
209 DropSource ds;
210 DataObject dob;
211 DropTarget dt;
213 static HINSTANCE hInstance;
214 static ATOM scintillaClassAtom;
215 static ATOM callClassAtom;
217 #if defined(USE_D2D)
218 ID2D1RenderTarget *pRenderTarget;
219 bool renderTargetValid;
220 #endif
222 explicit ScintillaWin(HWND hwnd);
223 ScintillaWin(const ScintillaWin &);
224 virtual ~ScintillaWin();
225 ScintillaWin &operator=(const ScintillaWin &);
227 virtual void Initialise();
228 virtual void Finalise();
229 #if defined(USE_D2D)
230 void EnsureRenderTarget(HDC hdc);
231 void DropRenderTarget();
232 #endif
233 HWND MainHWND();
235 static sptr_t DirectFunction(
236 sptr_t ptr, UINT iMessage, uptr_t wParam, sptr_t lParam);
237 static sptr_t PASCAL SWndProc(
238 HWND hWnd, UINT iMessage, WPARAM wParam, sptr_t lParam);
239 static sptr_t PASCAL CTWndProc(
240 HWND hWnd, UINT iMessage, WPARAM wParam, sptr_t lParam);
242 enum { invalidTimerID, standardTimerID, idleTimerID, fineTimerStart };
244 virtual bool DragThreshold(Point ptStart, Point ptNow);
245 virtual void StartDrag();
246 sptr_t WndPaint(uptr_t wParam);
248 sptr_t HandleCompositionWindowed(uptr_t wParam, sptr_t lParam);
249 sptr_t HandleCompositionInline(uptr_t wParam, sptr_t lParam);
250 static bool KoreanIME();
251 void MoveImeCarets(int offset);
252 void DrawImeIndicator(int indicator, int len);
253 void SetCandidateWindowPos();
255 UINT CodePageOfDocument();
256 virtual bool ValidCodePage(int codePage) const;
257 virtual sptr_t DefWndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam);
258 virtual bool SetIdle(bool on);
259 UINT_PTR timers[tickDwell+1];
260 virtual bool FineTickerAvailable();
261 virtual bool FineTickerRunning(TickReason reason);
262 virtual void FineTickerStart(TickReason reason, int millis, int tolerance);
263 virtual void FineTickerCancel(TickReason reason);
264 virtual void SetMouseCapture(bool on);
265 virtual bool HaveMouseCapture();
266 virtual void SetTrackMouseLeaveEvent(bool on);
267 virtual bool PaintContains(PRectangle rc);
268 virtual void ScrollText(int linesToMove);
269 virtual void UpdateSystemCaret();
270 virtual void SetVerticalScrollPos();
271 virtual void SetHorizontalScrollPos();
272 virtual bool ModifyScrollBars(int nMax, int nPage);
273 virtual void NotifyChange();
274 virtual void NotifyFocus(bool focus);
275 virtual void SetCtrlID(int identifier);
276 virtual int GetCtrlID();
277 virtual void NotifyParent(SCNotification scn);
278 virtual void NotifyDoubleClick(Point pt, int modifiers);
279 virtual CaseFolder *CaseFolderForEncoding();
280 virtual std::string CaseMapString(const std::string &s, int caseMapping);
281 virtual void Copy();
282 virtual void CopyAllowLine();
283 virtual bool CanPaste();
284 virtual void Paste();
285 virtual void CreateCallTipWindow(PRectangle rc);
286 virtual void AddToPopUp(const char *label, int cmd = 0, bool enabled = true);
287 virtual void ClaimSelection();
289 // DBCS
290 void ImeStartComposition();
291 void ImeEndComposition();
293 void AddCharBytes(char b0, char b1);
295 void GetIntelliMouseParameters();
296 virtual void CopyToClipboard(const SelectionText &selectedText);
297 void ScrollMessage(WPARAM wParam);
298 void HorizontalScrollMessage(WPARAM wParam);
299 void FullPaint();
300 void FullPaintDC(HDC dc);
301 bool IsCompatibleDC(HDC dc);
302 DWORD EffectFromState(DWORD grfKeyState) const;
304 virtual int SetScrollInfo(int nBar, LPCSCROLLINFO lpsi, BOOL bRedraw);
305 virtual bool GetScrollInfo(int nBar, LPSCROLLINFO lpsi);
306 void ChangeScrollPos(int barType, int pos);
307 sptr_t GetTextLength();
308 sptr_t GetText(uptr_t wParam, sptr_t lParam);
310 public:
311 // Public for benefit of Scintilla_DirectFunction
312 virtual sptr_t WndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam);
314 /// Implement IUnknown
315 STDMETHODIMP QueryInterface(REFIID riid, PVOID *ppv);
316 STDMETHODIMP_(ULONG)AddRef();
317 STDMETHODIMP_(ULONG)Release();
319 /// Implement IDropTarget
320 STDMETHODIMP DragEnter(LPDATAOBJECT pIDataSource, DWORD grfKeyState,
321 POINTL pt, PDWORD pdwEffect);
322 STDMETHODIMP DragOver(DWORD grfKeyState, POINTL pt, PDWORD pdwEffect);
323 STDMETHODIMP DragLeave();
324 STDMETHODIMP Drop(LPDATAOBJECT pIDataSource, DWORD grfKeyState,
325 POINTL pt, PDWORD pdwEffect);
327 /// Implement important part of IDataObject
328 STDMETHODIMP GetData(FORMATETC *pFEIn, STGMEDIUM *pSTM);
330 static bool Register(HINSTANCE hInstance_);
331 static bool Unregister();
333 friend class DropSource;
334 friend class DataObject;
335 friend class DropTarget;
336 bool DragIsRectangularOK(CLIPFORMAT fmt) const {
337 return drag.rectangular && (fmt == cfColumnSelect);
340 private:
341 // For use in creating a system caret
342 bool HasCaretSizeChanged() const;
343 BOOL CreateSystemCaret();
344 BOOL DestroySystemCaret();
345 HBITMAP sysCaretBitmap;
346 int sysCaretWidth;
347 int sysCaretHeight;
348 bool keysAlwaysUnicode;
351 HINSTANCE ScintillaWin::hInstance = 0;
352 ATOM ScintillaWin::scintillaClassAtom = 0;
353 ATOM ScintillaWin::callClassAtom = 0;
355 ScintillaWin::ScintillaWin(HWND hwnd) {
357 lastKeyDownConsumed = false;
359 capturedMouse = false;
360 trackedMouseLeave = false;
361 TrackMouseEventFn = 0;
362 SetCoalescableTimerFn = 0;
364 linesPerScroll = 0;
365 wheelDelta = 0; // Wheel delta from roll
367 hRgnUpdate = 0;
369 hasOKText = false;
371 // There does not seem to be a real standard for indicating that the clipboard
372 // contains a rectangular selection, so copy Developer Studio and Borland Delphi.
373 cfColumnSelect = static_cast<CLIPFORMAT>(
374 ::RegisterClipboardFormat(TEXT("MSDEVColumnSelect")));
375 cfBorlandIDEBlockType = static_cast<CLIPFORMAT>(
376 ::RegisterClipboardFormat(TEXT("Borland IDE Block Type")));
378 // Likewise for line-copy (copies a full line when no text is selected)
379 cfLineSelect = static_cast<CLIPFORMAT>(
380 ::RegisterClipboardFormat(TEXT("MSDEVLineSelect")));
381 cfVSLineTag = static_cast<CLIPFORMAT>(
382 ::RegisterClipboardFormat(TEXT("VisualStudioEditorOperationsLineCutCopyClipboardTag")));
383 hrOle = E_FAIL;
385 wMain = hwnd;
387 dob.sci = this;
388 ds.sci = this;
389 dt.sci = this;
391 sysCaretBitmap = 0;
392 sysCaretWidth = 0;
393 sysCaretHeight = 0;
395 #if defined(USE_D2D)
396 pRenderTarget = 0;
397 renderTargetValid = true;
398 #endif
400 keysAlwaysUnicode = false;
402 caret.period = ::GetCaretBlinkTime();
403 if (caret.period < 0)
404 caret.period = 0;
406 Initialise();
409 ScintillaWin::~ScintillaWin() {}
411 void ScintillaWin::Initialise() {
412 // Initialize COM. If the app has already done this it will have
413 // no effect. If the app hasnt, we really shouldnt ask them to call
414 // it just so this internal feature works.
415 hrOle = ::OleInitialize(NULL);
417 // Find TrackMouseEvent which is available on Windows > 95
418 HMODULE user32 = ::GetModuleHandle(TEXT("user32.dll"));
419 if (user32) {
420 TrackMouseEventFn = (TrackMouseEventSig)::GetProcAddress(user32, "TrackMouseEvent");
421 SetCoalescableTimerFn = (SetCoalescableTimerSig)::GetProcAddress(user32, "SetCoalescableTimer");
423 if (TrackMouseEventFn == NULL) {
424 // Windows 95 has an emulation in comctl32.dll:_TrackMouseEvent
425 if (!commctrl32)
426 commctrl32 = ::LoadLibrary(TEXT("comctl32.dll"));
427 if (commctrl32 != NULL) {
428 TrackMouseEventFn = (TrackMouseEventSig)
429 ::GetProcAddress(commctrl32, "_TrackMouseEvent");
432 for (TickReason tr = tickCaret; tr <= tickDwell; tr = static_cast<TickReason>(tr + 1)) {
433 timers[tr] = 0;
435 vs.indicators[SC_INDICATOR_UNKNOWN] = Indicator(INDIC_HIDDEN, ColourDesired(0, 0, 0xff));
436 vs.indicators[SC_INDICATOR_INPUT] = Indicator(INDIC_DOTS, ColourDesired(0, 0, 0xff));
437 vs.indicators[SC_INDICATOR_CONVERTED] = Indicator(INDIC_COMPOSITIONTHICK, ColourDesired(0, 0, 0xff));
438 vs.indicators[SC_INDICATOR_TARGET] = Indicator(INDIC_STRAIGHTBOX, ColourDesired(0, 0, 0xff));
441 void ScintillaWin::Finalise() {
442 ScintillaBase::Finalise();
443 for (TickReason tr = tickCaret; tr <= tickDwell; tr = static_cast<TickReason>(tr + 1)) {
444 FineTickerCancel(tr);
446 SetIdle(false);
447 #if defined(USE_D2D)
448 DropRenderTarget();
449 #endif
450 ::RevokeDragDrop(MainHWND());
451 if (SUCCEEDED(hrOle)) {
452 ::OleUninitialize();
456 #if defined(USE_D2D)
458 void ScintillaWin::EnsureRenderTarget(HDC hdc) {
459 if (!renderTargetValid) {
460 DropRenderTarget();
461 renderTargetValid = true;
463 if (pD2DFactory && !pRenderTarget) {
464 RECT rc;
465 HWND hw = MainHWND();
466 GetClientRect(hw, &rc);
468 D2D1_SIZE_U size = D2D1::SizeU(rc.right - rc.left, rc.bottom - rc.top);
470 // Create a Direct2D render target.
471 #if 1
472 D2D1_RENDER_TARGET_PROPERTIES drtp;
473 drtp.type = D2D1_RENDER_TARGET_TYPE_DEFAULT;
474 drtp.pixelFormat.format = DXGI_FORMAT_UNKNOWN;
475 drtp.pixelFormat.alphaMode = D2D1_ALPHA_MODE_UNKNOWN;
476 drtp.dpiX = 96.0;
477 drtp.dpiY = 96.0;
478 drtp.usage = D2D1_RENDER_TARGET_USAGE_NONE;
479 drtp.minLevel = D2D1_FEATURE_LEVEL_DEFAULT;
481 if (technology == SC_TECHNOLOGY_DIRECTWRITEDC) {
482 // Explicit pixel format needed.
483 drtp.pixelFormat = D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM,
484 D2D1_ALPHA_MODE_IGNORE);
486 ID2D1DCRenderTarget *pDCRT = NULL;
487 HRESULT hr = pD2DFactory->CreateDCRenderTarget(&drtp, &pDCRT);
488 if (SUCCEEDED(hr)) {
489 pRenderTarget = pDCRT;
490 } else {
491 Platform::DebugPrintf("Failed CreateDCRenderTarget 0x%x\n", hr);
492 pRenderTarget = NULL;
495 } else {
496 D2D1_HWND_RENDER_TARGET_PROPERTIES dhrtp;
497 dhrtp.hwnd = hw;
498 dhrtp.pixelSize = size;
499 dhrtp.presentOptions = (technology == SC_TECHNOLOGY_DIRECTWRITERETAIN) ?
500 D2D1_PRESENT_OPTIONS_RETAIN_CONTENTS : D2D1_PRESENT_OPTIONS_NONE;
502 ID2D1HwndRenderTarget *pHwndRenderTarget = NULL;
503 HRESULT hr = pD2DFactory->CreateHwndRenderTarget(drtp, dhrtp, &pHwndRenderTarget);
504 if (SUCCEEDED(hr)) {
505 pRenderTarget = pHwndRenderTarget;
506 } else {
507 Platform::DebugPrintf("Failed CreateHwndRenderTarget 0x%x\n", hr);
508 pRenderTarget = NULL;
511 #else
512 pD2DFactory->CreateHwndRenderTarget(
513 D2D1::RenderTargetProperties(
514 D2D1_RENDER_TARGET_TYPE_DEFAULT ,
515 D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_PREMULTIPLIED),
516 96.0f, 96.0f, D2D1_RENDER_TARGET_USAGE_NONE, D2D1_FEATURE_LEVEL_DEFAULT),
517 D2D1::HwndRenderTargetProperties(hw, size),
518 &pRenderTarget);
519 #endif
520 // Pixmaps were created to be compatible with previous render target so
521 // need to be recreated.
522 DropGraphics(false);
525 if ((technology == SC_TECHNOLOGY_DIRECTWRITEDC) && pRenderTarget) {
526 RECT rcWindow;
527 GetClientRect(MainHWND(), &rcWindow);
528 HRESULT hr = static_cast<ID2D1DCRenderTarget*>(pRenderTarget)->BindDC(hdc, &rcWindow);
529 if (FAILED(hr)) {
530 Platform::DebugPrintf("BindDC failed 0x%x\n", hr);
531 DropRenderTarget();
536 void ScintillaWin::DropRenderTarget() {
537 if (pRenderTarget) {
538 pRenderTarget->Release();
539 pRenderTarget = 0;
543 #endif
545 HWND ScintillaWin::MainHWND() {
546 return reinterpret_cast<HWND>(wMain.GetID());
549 bool ScintillaWin::DragThreshold(Point ptStart, Point ptNow) {
550 int xMove = static_cast<int>(abs(ptStart.x - ptNow.x));
551 int yMove = static_cast<int>(abs(ptStart.y - ptNow.y));
552 return (xMove > ::GetSystemMetrics(SM_CXDRAG)) ||
553 (yMove > ::GetSystemMetrics(SM_CYDRAG));
556 void ScintillaWin::StartDrag() {
557 inDragDrop = ddDragging;
558 DWORD dwEffect = 0;
559 dropWentOutside = true;
560 IDataObject *pDataObject = reinterpret_cast<IDataObject *>(&dob);
561 IDropSource *pDropSource = reinterpret_cast<IDropSource *>(&ds);
562 //Platform::DebugPrintf("About to DoDragDrop %x %x\n", pDataObject, pDropSource);
563 HRESULT hr = ::DoDragDrop(
564 pDataObject,
565 pDropSource,
566 DROPEFFECT_COPY | DROPEFFECT_MOVE, &dwEffect);
567 //Platform::DebugPrintf("DoDragDrop = %x\n", hr);
568 if (SUCCEEDED(hr)) {
569 if ((hr == DRAGDROP_S_DROP) && (dwEffect == DROPEFFECT_MOVE) && dropWentOutside) {
570 // Remove dragged out text
571 ClearSelection();
574 inDragDrop = ddNone;
575 SetDragPosition(SelectionPosition(invalidPosition));
578 // Avoid warnings everywhere for old style casts by concentrating them here
579 static WORD LoWord(uptr_t l) {
580 return LOWORD(l);
583 static WORD HiWord(uptr_t l) {
584 return HIWORD(l);
587 static int InputCodePage() {
588 HKL inputLocale = ::GetKeyboardLayout(0);
589 LANGID inputLang = LOWORD(inputLocale);
590 char sCodePage[10];
591 int res = ::GetLocaleInfoA(MAKELCID(inputLang, SORT_DEFAULT),
592 LOCALE_IDEFAULTANSICODEPAGE, sCodePage, sizeof(sCodePage));
593 if (!res)
594 return 0;
595 return atoi(sCodePage);
598 /** Map the key codes to their equivalent SCK_ form. */
599 static int KeyTranslate(int keyIn) {
600 //PLATFORM_ASSERT(!keyIn);
601 switch (keyIn) {
602 case VK_DOWN: return SCK_DOWN;
603 case VK_UP: return SCK_UP;
604 case VK_LEFT: return SCK_LEFT;
605 case VK_RIGHT: return SCK_RIGHT;
606 case VK_HOME: return SCK_HOME;
607 case VK_END: return SCK_END;
608 case VK_PRIOR: return SCK_PRIOR;
609 case VK_NEXT: return SCK_NEXT;
610 case VK_DELETE: return SCK_DELETE;
611 case VK_INSERT: return SCK_INSERT;
612 case VK_ESCAPE: return SCK_ESCAPE;
613 case VK_BACK: return SCK_BACK;
614 case VK_TAB: return SCK_TAB;
615 case VK_RETURN: return SCK_RETURN;
616 case VK_ADD: return SCK_ADD;
617 case VK_SUBTRACT: return SCK_SUBTRACT;
618 case VK_DIVIDE: return SCK_DIVIDE;
619 case VK_LWIN: return SCK_WIN;
620 case VK_RWIN: return SCK_RWIN;
621 case VK_APPS: return SCK_MENU;
622 case VK_OEM_2: return '/';
623 case VK_OEM_3: return '`';
624 case VK_OEM_4: return '[';
625 case VK_OEM_5: return '\\';
626 case VK_OEM_6: return ']';
627 default: return keyIn;
631 static bool BoundsContains(PRectangle rcBounds, HRGN hRgnBounds, PRectangle rcCheck) {
632 bool contains = true;
633 if (!rcCheck.Empty()) {
634 if (!rcBounds.Contains(rcCheck)) {
635 contains = false;
636 } else if (hRgnBounds) {
637 // In bounding rectangle so check more accurately using region
638 HRGN hRgnCheck = ::CreateRectRgn(static_cast<int>(rcCheck.left), static_cast<int>(rcCheck.top),
639 static_cast<int>(rcCheck.right), static_cast<int>(rcCheck.bottom));
640 if (hRgnCheck) {
641 HRGN hRgnDifference = ::CreateRectRgn(0, 0, 0, 0);
642 if (hRgnDifference) {
643 int combination = ::CombineRgn(hRgnDifference, hRgnCheck, hRgnBounds, RGN_DIFF);
644 if (combination != NULLREGION) {
645 contains = false;
647 ::DeleteRgn(hRgnDifference);
649 ::DeleteRgn(hRgnCheck);
653 return contains;
656 LRESULT ScintillaWin::WndPaint(uptr_t wParam) {
657 //ElapsedTime et;
659 // Redirect assertions to debug output and save current state
660 bool assertsPopup = Platform::ShowAssertionPopUps(false);
661 paintState = painting;
662 PAINTSTRUCT ps;
663 PAINTSTRUCT *pps;
665 bool IsOcxCtrl = (wParam != 0); // if wParam != 0, it contains
666 // a PAINSTRUCT* from the OCX
667 // Removed since this interferes with reporting other assertions as it occurs repeatedly
668 //PLATFORM_ASSERT(hRgnUpdate == NULL);
669 hRgnUpdate = ::CreateRectRgn(0, 0, 0, 0);
670 if (IsOcxCtrl) {
671 pps = reinterpret_cast<PAINTSTRUCT*>(wParam);
672 } else {
673 ::GetUpdateRgn(MainHWND(), hRgnUpdate, FALSE);
674 pps = &ps;
675 ::BeginPaint(MainHWND(), pps);
677 rcPaint = PRectangle::FromInts(pps->rcPaint.left, pps->rcPaint.top, pps->rcPaint.right, pps->rcPaint.bottom);
678 PRectangle rcClient = GetClientRectangle();
679 paintingAllText = BoundsContains(rcPaint, hRgnUpdate, rcClient);
680 if (technology == SC_TECHNOLOGY_DEFAULT) {
681 AutoSurface surfaceWindow(pps->hdc, this);
682 if (surfaceWindow) {
683 Paint(surfaceWindow, rcPaint);
684 surfaceWindow->Release();
686 } else {
687 #if defined(USE_D2D)
688 EnsureRenderTarget(pps->hdc);
689 AutoSurface surfaceWindow(pRenderTarget, this);
690 if (surfaceWindow) {
691 pRenderTarget->BeginDraw();
692 Paint(surfaceWindow, rcPaint);
693 surfaceWindow->Release();
694 HRESULT hr = pRenderTarget->EndDraw();
695 if (hr == D2DERR_RECREATE_TARGET) {
696 DropRenderTarget();
697 paintState = paintAbandoned;
700 #endif
702 if (hRgnUpdate) {
703 ::DeleteRgn(hRgnUpdate);
704 hRgnUpdate = 0;
707 if (!IsOcxCtrl)
708 ::EndPaint(MainHWND(), pps);
709 if (paintState == paintAbandoned) {
710 // Painting area was insufficient to cover new styling or brace highlight positions
711 if (IsOcxCtrl) {
712 FullPaintDC(pps->hdc);
713 } else {
714 FullPaint();
717 paintState = notPainting;
719 // Restore debug output state
720 Platform::ShowAssertionPopUps(assertsPopup);
722 //Platform::DebugPrintf("Paint took %g\n", et.Duration());
723 return 0l;
726 sptr_t ScintillaWin::HandleCompositionWindowed(uptr_t wParam, sptr_t lParam) {
727 if (lParam & GCS_RESULTSTR) {
728 HIMC hIMC = ::ImmGetContext(MainHWND());
729 if (hIMC) {
730 wchar_t wcs[maxLenInputIME];
731 LONG bytes = ::ImmGetCompositionStringW(hIMC,
732 GCS_RESULTSTR, wcs, (maxLenInputIME-1)*2);
733 int wides = bytes / 2;
734 if (IsUnicodeMode()) {
735 char utfval[maxLenInputIME * 3];
736 unsigned int len = UTF8Length(wcs, wides);
737 UTF8FromUTF16(wcs, wides, utfval, len);
738 utfval[len] = '\0';
739 AddCharUTF(utfval, len);
740 } else {
741 char dbcsval[maxLenInputIME * 2];
742 int size = ::WideCharToMultiByte(InputCodePage(),
743 0, wcs, wides, dbcsval, sizeof(dbcsval) - 1, 0, 0);
744 for (int i=0; i<size; i++) {
745 AddChar(dbcsval[i]);
748 // Set new position after converted
749 Point pos = PointMainCaret();
750 COMPOSITIONFORM CompForm;
751 CompForm.dwStyle = CFS_POINT;
752 CompForm.ptCurrentPos.x = static_cast<int>(pos.x);
753 CompForm.ptCurrentPos.y = static_cast<int>(pos.y);
754 ::ImmSetCompositionWindow(hIMC, &CompForm);
755 ::ImmReleaseContext(MainHWND(), hIMC);
757 return 0;
759 return ::DefWindowProc(MainHWND(), WM_IME_COMPOSITION, wParam, lParam);
762 bool ScintillaWin::KoreanIME() {
763 const int codePage = InputCodePage();
764 return codePage == 949 || codePage == 1361;
767 void ScintillaWin::MoveImeCarets(int offset) {
768 // Move carets relatively by bytes.
769 for (size_t r=0; r<sel.Count(); r++) {
770 int positionInsert = sel.Range(r).Start().Position();
771 sel.Range(r).caret.SetPosition(positionInsert + offset);
772 sel.Range(r).anchor.SetPosition(positionInsert + offset);
776 void ScintillaWin::DrawImeIndicator(int indicator, int len) {
777 // Emulate the visual style of IME characters with indicators.
778 // Draw an indicator on the character before caret by the character bytes of len
779 // so it should be called after addCharUTF().
780 // It does not affect caret positions.
781 if (indicator < 8 || indicator > INDIC_MAX) {
782 return;
784 pdoc->decorations.SetCurrentIndicator(indicator);
785 for (size_t r=0; r<sel.Count(); r++) {
786 int positionInsert = sel.Range(r).Start().Position();
787 pdoc->DecorationFillRange(positionInsert - len, 1, len);
791 void ScintillaWin::SetCandidateWindowPos() {
792 HIMC hIMC = ::ImmGetContext(MainHWND());
793 if (hIMC) {
794 Point pos = PointMainCaret();
795 CANDIDATEFORM CandForm;
796 CandForm.dwIndex = 0;
797 CandForm.dwStyle = CFS_CANDIDATEPOS;
798 CandForm.ptCurrentPos.x = static_cast<int>(pos.x);
799 CandForm.ptCurrentPos.y = static_cast<int>(pos.y + vs.lineHeight);
800 ::ImmSetCandidateWindow(hIMC, &CandForm);
801 ::ImmReleaseContext(MainHWND(), hIMC);
805 sptr_t ScintillaWin::HandleCompositionInline(uptr_t, sptr_t lParam) {
806 // Copy & paste by johnsonj with a lot of helps of Neil.
807 // Great thanks for my foreruners, jiniya and BLUEnLIVE.
809 HIMC hIMC = ::ImmGetContext(MainHWND());
810 if (!hIMC) {
811 return 0;
814 if (pdoc->TentativeActive()) {
815 pdoc->TentativeUndo();
816 } else {
817 // No tentative undo means start of this composition so
818 // fill in any virtual spaces.
819 FillVirtualSpace();
822 view.imeCaretBlockOverride = false;
824 if (lParam & GCS_COMPSTR) {
825 wchar_t wcs[maxLenInputIME] = { 0 };
826 long bytes = ::ImmGetCompositionStringW
827 (hIMC, GCS_COMPSTR, wcs, maxLenInputIME);
828 unsigned int wcsLen = bytes / 2;
830 if ((wcsLen == 0) || (wcsLen >= maxLenInputIME)) {
831 ShowCaretAtCurrentPosition();
832 ::ImmReleaseContext(MainHWND(), hIMC);
833 return 0;
836 pdoc->TentativeStart(); // TentativeActive from now on.
838 // Get attribute information from composition string.
839 BYTE compAttr[maxLenInputIME] = { 0 };
840 unsigned int imeCursorPos = 0;
842 if (lParam & GCS_COMPATTR) {
843 ImmGetCompositionStringW(hIMC, GCS_COMPATTR, compAttr, sizeof(compAttr));
845 if (lParam & GCS_CURSORPOS) {
846 imeCursorPos = ImmGetCompositionStringW(hIMC, GCS_CURSORPOS, NULL, 0);
849 // Display character by character.
850 int numBytes = 0;
851 int imeCharPos[maxLenInputIME + 1] = { 0 };
853 bool tmpRecordingMacro = recordingMacro;
854 recordingMacro = false;
855 for (unsigned int i = 0; i < wcsLen; i++) {
856 wchar_t uniChar[1] = { 0 };
857 char oneChar[UTF8MaxBytes + 1] = "\0\0\0\0"; // Maximum 4 bytes in utf8
858 unsigned int oneCharLen = 0;
860 uniChar[0] = wcs[i];
862 if (IsUnicodeMode()) {
863 oneCharLen = UTF8Length(uniChar, 1);
864 UTF8FromUTF16(uniChar, 1, oneChar, oneCharLen);
865 oneChar[oneCharLen] = '\0';
866 } else {
867 oneCharLen = ::WideCharToMultiByte(InputCodePage(), 0,
868 uniChar, 1, oneChar, sizeof(oneChar)-1, 0, 0);
869 oneChar[oneCharLen] = '\0';
872 // Display a character.
873 AddCharUTF(oneChar, oneCharLen);
875 // Record compstr character positions for moving IME carets.
876 numBytes += oneCharLen;
877 imeCharPos[i + 1] = numBytes;
879 // Draw an indicator on the character.
880 int indicator = SC_INDICATOR_UNKNOWN;
881 switch ((int)compAttr[i]) {
882 case ATTR_INPUT:
883 indicator = SC_INDICATOR_INPUT;
884 break;
885 case ATTR_TARGET_NOTCONVERTED:
886 case ATTR_TARGET_CONVERTED:
887 indicator = SC_INDICATOR_TARGET;
888 break;
889 case ATTR_CONVERTED:
890 indicator = SC_INDICATOR_CONVERTED;
891 break;
893 DrawImeIndicator(indicator, oneCharLen);
895 recordingMacro = tmpRecordingMacro;
897 // Move IME caret position.
898 MoveImeCarets(-imeCharPos[wcsLen] + imeCharPos[imeCursorPos]);
899 if (KoreanIME()) {
900 view.imeCaretBlockOverride = true;
902 } else if (lParam & GCS_RESULTSTR) {
903 wchar_t wcs[maxLenInputIME] = { 0 };
904 long bytes = ::ImmGetCompositionStringW
905 (hIMC, GCS_RESULTSTR, wcs, maxLenInputIME);
906 unsigned int wcsLen = bytes / 2;
908 for (unsigned int i = 0; i < wcsLen; i++) {
909 wchar_t uniChar[1] = { 0 };
910 char oneChar[UTF8MaxBytes+1] = "\0\0\0\0"; // Maximum 4 bytes in UTF-8.
911 unsigned int oneCharLen = 0;
913 uniChar[0] = wcs[i];
915 if (IsUnicodeMode()) {
916 oneCharLen = UTF8Length(uniChar, 1);
917 UTF8FromUTF16(uniChar, 1, oneChar, oneCharLen);
918 oneChar[oneCharLen] = '\0';
919 } else {
920 oneCharLen = ::WideCharToMultiByte(InputCodePage(), 0,
921 uniChar, 1, oneChar, sizeof(oneChar)-1, 0, 0);
922 oneChar[oneCharLen] = '\0';
924 AddCharUTF(oneChar, oneCharLen);
927 SetCandidateWindowPos();
928 ShowCaretAtCurrentPosition();
929 ::ImmReleaseContext(MainHWND(), hIMC);
930 return 0;
933 // Translate message IDs from WM_* and EM_* to SCI_* so can partly emulate Windows Edit control
934 static unsigned int SciMessageFromEM(unsigned int iMessage) {
935 switch (iMessage) {
936 case EM_CANPASTE: return SCI_CANPASTE;
937 case EM_CANUNDO: return SCI_CANUNDO;
938 case EM_EMPTYUNDOBUFFER: return SCI_EMPTYUNDOBUFFER;
939 case EM_FINDTEXTEX: return SCI_FINDTEXT;
940 case EM_FORMATRANGE: return SCI_FORMATRANGE;
941 case EM_GETFIRSTVISIBLELINE: return SCI_GETFIRSTVISIBLELINE;
942 case EM_GETLINECOUNT: return SCI_GETLINECOUNT;
943 case EM_GETSELTEXT: return SCI_GETSELTEXT;
944 case EM_GETTEXTRANGE: return SCI_GETTEXTRANGE;
945 case EM_HIDESELECTION: return SCI_HIDESELECTION;
946 case EM_LINEINDEX: return SCI_POSITIONFROMLINE;
947 case EM_LINESCROLL: return SCI_LINESCROLL;
948 case EM_REPLACESEL: return SCI_REPLACESEL;
949 case EM_SCROLLCARET: return SCI_SCROLLCARET;
950 case EM_SETREADONLY: return SCI_SETREADONLY;
951 case WM_CLEAR: return SCI_CLEAR;
952 case WM_COPY: return SCI_COPY;
953 case WM_CUT: return SCI_CUT;
954 case WM_SETTEXT: return SCI_SETTEXT;
955 case WM_PASTE: return SCI_PASTE;
956 case WM_UNDO: return SCI_UNDO;
958 return iMessage;
961 UINT CodePageFromCharSet(DWORD characterSet, UINT documentCodePage) {
962 if (documentCodePage == SC_CP_UTF8) {
963 return SC_CP_UTF8;
965 switch (characterSet) {
966 case SC_CHARSET_ANSI: return 1252;
967 case SC_CHARSET_DEFAULT: return documentCodePage;
968 case SC_CHARSET_BALTIC: return 1257;
969 case SC_CHARSET_CHINESEBIG5: return 950;
970 case SC_CHARSET_EASTEUROPE: return 1250;
971 case SC_CHARSET_GB2312: return 936;
972 case SC_CHARSET_GREEK: return 1253;
973 case SC_CHARSET_HANGUL: return 949;
974 case SC_CHARSET_MAC: return 10000;
975 case SC_CHARSET_OEM: return 437;
976 case SC_CHARSET_RUSSIAN: return 1251;
977 case SC_CHARSET_SHIFTJIS: return 932;
978 case SC_CHARSET_TURKISH: return 1254;
979 case SC_CHARSET_JOHAB: return 1361;
980 case SC_CHARSET_HEBREW: return 1255;
981 case SC_CHARSET_ARABIC: return 1256;
982 case SC_CHARSET_VIETNAMESE: return 1258;
983 case SC_CHARSET_THAI: return 874;
984 case SC_CHARSET_8859_15: return 28605;
985 // Not supported
986 case SC_CHARSET_CYRILLIC: return documentCodePage;
987 case SC_CHARSET_SYMBOL: return documentCodePage;
989 return documentCodePage;
992 UINT ScintillaWin::CodePageOfDocument() {
993 return CodePageFromCharSet(vs.styles[STYLE_DEFAULT].characterSet, pdoc->dbcsCodePage);
996 sptr_t ScintillaWin::GetTextLength() {
997 if (::IsWindowUnicode(MainHWND())) {
998 if (pdoc->Length() == 0)
999 return 0;
1000 std::vector<char> docBytes(pdoc->Length(), '\0');
1001 pdoc->GetCharRange(&docBytes[0], 0, pdoc->Length());
1002 if (IsUnicodeMode()) {
1003 return UTF16Length(&docBytes[0], static_cast<unsigned int>(docBytes.size()));
1004 } else {
1005 return ::MultiByteToWideChar(CodePageOfDocument(), 0, &docBytes[0],
1006 static_cast<int>(docBytes.size()), NULL, 0);
1008 } else {
1009 return pdoc->Length();
1013 sptr_t ScintillaWin::GetText(uptr_t wParam, sptr_t lParam) {
1014 if (::IsWindowUnicode(MainHWND())) {
1015 wchar_t *ptr = reinterpret_cast<wchar_t *>(lParam);
1016 if (pdoc->Length() == 0) {
1017 *ptr = L'\0';
1018 return 0;
1020 std::vector<char> docBytes(pdoc->Length(), '\0');
1021 pdoc->GetCharRange(&docBytes[0], 0, pdoc->Length());
1022 if (IsUnicodeMode()) {
1023 size_t lengthUTF16 = UTF16Length(&docBytes[0], static_cast<unsigned int>(docBytes.size()));
1024 if (lParam == 0)
1025 return lengthUTF16;
1026 if (wParam == 0)
1027 return 0;
1028 size_t uLen = UTF16FromUTF8(&docBytes[0], docBytes.size(),
1029 ptr, static_cast<int>(wParam) - 1);
1030 ptr[uLen] = L'\0';
1031 return uLen;
1032 } else {
1033 // Not Unicode mode
1034 // Convert to Unicode using the current Scintilla code page
1035 const UINT cpSrc = CodePageOfDocument();
1036 int lengthUTF16 = ::MultiByteToWideChar(cpSrc, 0, &docBytes[0],
1037 static_cast<int>(docBytes.size()), NULL, 0);
1038 if (lengthUTF16 >= static_cast<int>(wParam))
1039 lengthUTF16 = static_cast<int>(wParam)-1;
1040 ::MultiByteToWideChar(cpSrc, 0, &docBytes[0],
1041 static_cast<int>(docBytes.size()),
1042 ptr, lengthUTF16);
1043 ptr[lengthUTF16] = L'\0';
1044 return lengthUTF16;
1046 } else {
1047 if (lParam == 0)
1048 return pdoc->Length() + 1;
1049 if (wParam == 0)
1050 return 0;
1051 char *ptr = reinterpret_cast<char *>(lParam);
1052 unsigned int iChar = 0;
1053 for (; iChar < wParam - 1; iChar++)
1054 ptr[iChar] = pdoc->CharAt(iChar);
1055 ptr[iChar] = '\0';
1056 return iChar;
1060 sptr_t ScintillaWin::WndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
1061 try {
1062 //Platform::DebugPrintf("S M:%x WP:%x L:%x\n", iMessage, wParam, lParam);
1063 iMessage = SciMessageFromEM(iMessage);
1064 switch (iMessage) {
1066 case WM_CREATE:
1067 ctrlID = ::GetDlgCtrlID(reinterpret_cast<HWND>(wMain.GetID()));
1068 // Get Intellimouse scroll line parameters
1069 GetIntelliMouseParameters();
1070 ::RegisterDragDrop(MainHWND(), reinterpret_cast<IDropTarget *>(&dt));
1071 break;
1073 case WM_COMMAND:
1074 Command(LoWord(wParam));
1075 break;
1077 case WM_PAINT:
1078 return WndPaint(wParam);
1080 case WM_PRINTCLIENT: {
1081 HDC hdc = reinterpret_cast<HDC>(wParam);
1082 if (!IsCompatibleDC(hdc)) {
1083 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1085 FullPaintDC(hdc);
1087 break;
1089 case WM_VSCROLL:
1090 ScrollMessage(wParam);
1091 break;
1093 case WM_HSCROLL:
1094 HorizontalScrollMessage(wParam);
1095 break;
1097 case WM_SIZE: {
1098 #if defined(USE_D2D)
1099 if (paintState == notPainting) {
1100 DropRenderTarget();
1101 } else {
1102 renderTargetValid = false;
1104 #endif
1105 //Platform::DebugPrintf("Scintilla WM_SIZE %d %d\n", LoWord(lParam), HiWord(lParam));
1106 ChangeSize();
1108 break;
1110 case WM_MOUSEWHEEL:
1111 // if autocomplete list active then send mousewheel message to it
1112 if (ac.Active()) {
1113 HWND hWnd = reinterpret_cast<HWND>(ac.lb->GetID());
1114 ::SendMessage(hWnd, iMessage, wParam, lParam);
1115 break;
1118 // Don't handle datazoom.
1119 // (A good idea for datazoom would be to "fold" or "unfold" details.
1120 // i.e. if datazoomed out only class structures are visible, when datazooming in the control
1121 // structures appear, then eventually the individual statements...)
1122 if (wParam & MK_SHIFT) {
1123 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1126 // Either SCROLL or ZOOM. We handle the wheel steppings calculation
1127 wheelDelta -= static_cast<short>(HiWord(wParam));
1128 if (abs(wheelDelta) >= WHEEL_DELTA && linesPerScroll > 0) {
1129 int linesToScroll = linesPerScroll;
1130 if (linesPerScroll == WHEEL_PAGESCROLL)
1131 linesToScroll = LinesOnScreen() - 1;
1132 if (linesToScroll == 0) {
1133 linesToScroll = 1;
1135 linesToScroll *= (wheelDelta / WHEEL_DELTA);
1136 if (wheelDelta >= 0)
1137 wheelDelta = wheelDelta % WHEEL_DELTA;
1138 else
1139 wheelDelta = - (-wheelDelta % WHEEL_DELTA);
1141 if (wParam & MK_CONTROL) {
1142 // Zoom! We play with the font sizes in the styles.
1143 // Number of steps/line is ignored, we just care if sizing up or down
1144 if (linesToScroll < 0) {
1145 KeyCommand(SCI_ZOOMIN);
1146 } else {
1147 KeyCommand(SCI_ZOOMOUT);
1149 } else {
1150 // Scroll
1151 ScrollTo(topLine + linesToScroll);
1154 return 0;
1156 case WM_TIMER:
1157 if (wParam == idleTimerID && idler.state) {
1158 SendMessage(MainHWND(), SC_WIN_IDLE, 0, 1);
1159 } else {
1160 TickFor(static_cast<TickReason>(wParam - fineTimerStart));
1162 break;
1164 case SC_WIN_IDLE:
1165 // wParam=dwTickCountInitial, or 0 to initialize. lParam=bSkipUserInputTest
1166 if (idler.state) {
1167 if (lParam || (WAIT_TIMEOUT == MsgWaitForMultipleObjects(0, 0, 0, 0, QS_INPUT|QS_HOTKEY))) {
1168 if (Idle()) {
1169 // User input was given priority above, but all events do get a turn. Other
1170 // messages, notifications, etc. will get interleaved with the idle messages.
1172 // However, some things like WM_PAINT are a lower priority, and will not fire
1173 // when there's a message posted. So, several times a second, we stop and let
1174 // the low priority events have a turn (after which the timer will fire again).
1176 DWORD dwCurrent = GetTickCount();
1177 DWORD dwStart = wParam ? static_cast<DWORD>(wParam) : dwCurrent;
1178 const DWORD maxWorkTime = 50;
1180 if (dwCurrent >= dwStart && dwCurrent > maxWorkTime && dwCurrent - maxWorkTime < dwStart)
1181 PostMessage(MainHWND(), SC_WIN_IDLE, dwStart, 0);
1182 } else {
1183 SetIdle(false);
1187 break;
1189 case WM_GETMINMAXINFO:
1190 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1192 case WM_LBUTTONDOWN: {
1193 // For IME, set the composition string as the result string.
1194 HIMC hIMC = ::ImmGetContext(MainHWND());
1195 ::ImmNotifyIME(hIMC, NI_COMPOSITIONSTR, CPS_COMPLETE, 0);
1196 ::ImmReleaseContext(MainHWND(), hIMC);
1198 //Platform::DebugPrintf("Buttdown %d %x %x %x %x %x\n",iMessage, wParam, lParam,
1199 // Platform::IsKeyDown(VK_SHIFT),
1200 // Platform::IsKeyDown(VK_CONTROL),
1201 // Platform::IsKeyDown(VK_MENU));
1202 ::SetFocus(MainHWND());
1203 ButtonDown(Point::FromLong(static_cast<long>(lParam)), ::GetMessageTime(),
1204 (wParam & MK_SHIFT) != 0,
1205 (wParam & MK_CONTROL) != 0,
1206 Platform::IsKeyDown(VK_MENU));
1208 break;
1210 case WM_MOUSEMOVE: {
1211 const Point pt = Point::FromLong(static_cast<long>(lParam));
1213 // Windows might send WM_MOUSEMOVE even though the mouse has not been moved:
1214 // http://blogs.msdn.com/b/oldnewthing/archive/2003/10/01/55108.aspx
1215 if (ptMouseLast.x != pt.x || ptMouseLast.y != pt.y) {
1216 SetTrackMouseLeaveEvent(true);
1217 ButtonMoveWithModifiers(pt,
1218 ((wParam & MK_SHIFT) != 0 ? SCI_SHIFT : 0) |
1219 ((wParam & MK_CONTROL) != 0 ? SCI_CTRL : 0) |
1220 (Platform::IsKeyDown(VK_MENU) ? SCI_ALT : 0));
1223 break;
1225 case WM_MOUSELEAVE:
1226 SetTrackMouseLeaveEvent(false);
1227 MouseLeave();
1228 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1230 case WM_LBUTTONUP:
1231 ButtonUp(Point::FromLong(static_cast<long>(lParam)),
1232 ::GetMessageTime(),
1233 (wParam & MK_CONTROL) != 0);
1234 break;
1236 case WM_RBUTTONDOWN:
1237 ::SetFocus(MainHWND());
1238 if (!PointInSelection(Point::FromLong(static_cast<long>(lParam)))) {
1239 CancelModes();
1240 SetEmptySelection(PositionFromLocation(Point::FromLong(static_cast<long>(lParam))));
1242 break;
1244 case WM_SETCURSOR:
1245 if (LoWord(lParam) == HTCLIENT) {
1246 if (inDragDrop == ddDragging) {
1247 DisplayCursor(Window::cursorUp);
1248 } else {
1249 // Display regular (drag) cursor over selection
1250 POINT pt;
1251 if (0 != ::GetCursorPos(&pt)) {
1252 ::ScreenToClient(MainHWND(), &pt);
1253 if (PointInSelMargin(PointFromPOINT(pt))) {
1254 DisplayCursor(GetMarginCursor(PointFromPOINT(pt)));
1255 } else if (PointInSelection(PointFromPOINT(pt)) && !SelectionEmpty()) {
1256 DisplayCursor(Window::cursorArrow);
1257 } else if (PointIsHotspot(PointFromPOINT(pt))) {
1258 DisplayCursor(Window::cursorHand);
1259 } else {
1260 DisplayCursor(Window::cursorText);
1264 return TRUE;
1265 } else {
1266 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1269 case WM_CHAR:
1270 if (((wParam >= 128) || !iscntrl(static_cast<int>(wParam))) || !lastKeyDownConsumed) {
1271 if (::IsWindowUnicode(MainHWND()) || keysAlwaysUnicode) {
1272 wchar_t wcs[2] = {static_cast<wchar_t>(wParam), 0};
1273 if (IsUnicodeMode()) {
1274 // For a wide character version of the window:
1275 char utfval[4];
1276 unsigned int len = UTF8Length(wcs, 1);
1277 UTF8FromUTF16(wcs, 1, utfval, len);
1278 AddCharUTF(utfval, len);
1279 } else {
1280 UINT cpDest = CodePageOfDocument();
1281 char inBufferCP[20];
1282 int size = ::WideCharToMultiByte(cpDest,
1283 0, wcs, 1, inBufferCP, sizeof(inBufferCP) - 1, 0, 0);
1284 inBufferCP[size] = '\0';
1285 AddCharUTF(inBufferCP, size);
1287 } else {
1288 if (IsUnicodeMode()) {
1289 AddCharBytes('\0', LOBYTE(wParam));
1290 } else {
1291 AddChar(LOBYTE(wParam));
1295 return 0;
1297 case WM_UNICHAR:
1298 if (wParam == UNICODE_NOCHAR) {
1299 return IsUnicodeMode() ? 1 : 0;
1300 } else if (lastKeyDownConsumed) {
1301 return 1;
1302 } else {
1303 if (IsUnicodeMode()) {
1304 char utfval[4];
1305 wchar_t wcs[2] = {static_cast<wchar_t>(wParam), 0};
1306 unsigned int len = UTF8Length(wcs, 1);
1307 UTF8FromUTF16(wcs, 1, utfval, len);
1308 AddCharUTF(utfval, len);
1309 return 1;
1310 } else {
1311 return 0;
1315 case WM_SYSKEYDOWN:
1316 case WM_KEYDOWN: {
1317 //Platform::DebugPrintf("S keydown %d %x %x %x %x\n",iMessage, wParam, lParam, ::IsKeyDown(VK_SHIFT), ::IsKeyDown(VK_CONTROL));
1318 lastKeyDownConsumed = false;
1319 int ret = KeyDown(KeyTranslate(static_cast<int>(wParam)),
1320 Platform::IsKeyDown(VK_SHIFT),
1321 Platform::IsKeyDown(VK_CONTROL),
1322 Platform::IsKeyDown(VK_MENU),
1323 &lastKeyDownConsumed);
1324 if (!ret && !lastKeyDownConsumed) {
1325 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1327 break;
1330 case WM_IME_KEYDOWN:
1331 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1333 case WM_KEYUP:
1334 //Platform::DebugPrintf("S keyup %d %x %x\n",iMessage, wParam, lParam);
1335 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1337 case WM_SETTINGCHANGE:
1338 //Platform::DebugPrintf("Setting Changed\n");
1339 InvalidateStyleData();
1340 // Get Intellimouse scroll line parameters
1341 GetIntelliMouseParameters();
1342 break;
1344 case WM_GETDLGCODE:
1345 return DLGC_HASSETSEL | DLGC_WANTALLKEYS;
1347 case WM_KILLFOCUS: {
1348 HWND wOther = reinterpret_cast<HWND>(wParam);
1349 HWND wThis = MainHWND();
1350 HWND wCT = reinterpret_cast<HWND>(ct.wCallTip.GetID());
1351 if (!wParam ||
1352 !(::IsChild(wThis, wOther) || (wOther == wCT))) {
1353 SetFocusState(false);
1354 DestroySystemCaret();
1356 // Explicitly complete any IME composition
1357 HIMC hIMC = ImmGetContext(MainHWND());
1358 if (hIMC) {
1359 ::ImmNotifyIME(hIMC, NI_COMPOSITIONSTR, CPS_COMPLETE, 0);
1360 ::ImmReleaseContext(MainHWND(), hIMC);
1363 break;
1365 case WM_SETFOCUS:
1366 SetFocusState(true);
1367 DestroySystemCaret();
1368 CreateSystemCaret();
1369 break;
1371 case WM_SYSCOLORCHANGE:
1372 //Platform::DebugPrintf("Setting Changed\n");
1373 InvalidateStyleData();
1374 break;
1376 case WM_IME_STARTCOMPOSITION: // dbcs
1377 if (KoreanIME() || imeInteraction == imeInline) {
1378 return 0;
1379 } else {
1380 ImeStartComposition();
1381 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1384 case WM_IME_ENDCOMPOSITION: // dbcs
1385 ImeEndComposition();
1386 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1388 case WM_IME_COMPOSITION:
1389 if (KoreanIME() || imeInteraction == imeInline) {
1390 return HandleCompositionInline(wParam, lParam);
1391 } else {
1392 return HandleCompositionWindowed(wParam, lParam);
1395 case WM_IME_CHAR: {
1396 AddCharBytes(HIBYTE(wParam), LOBYTE(wParam));
1397 return 0;
1400 case WM_CONTEXTMENU:
1401 if (displayPopupMenu) {
1402 Point pt = Point::FromLong(static_cast<long>(lParam));
1403 if ((pt.x == -1) && (pt.y == -1)) {
1404 // Caused by keyboard so display menu near caret
1405 pt = PointMainCaret();
1406 POINT spt = {static_cast<int>(pt.x), static_cast<int>(pt.y)};
1407 ::ClientToScreen(MainHWND(), &spt);
1408 pt = PointFromPOINT(spt);
1410 ContextMenu(pt);
1411 return 0;
1413 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1415 case WM_INPUTLANGCHANGE:
1416 //::SetThreadLocale(LOWORD(lParam));
1417 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1419 case WM_INPUTLANGCHANGEREQUEST:
1420 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1422 case WM_ERASEBKGND:
1423 return 1; // Avoid any background erasure as whole window painted.
1425 case WM_CAPTURECHANGED:
1426 capturedMouse = false;
1427 return 0;
1429 case WM_IME_SETCONTEXT:
1430 if (KoreanIME() || imeInteraction == imeInline) {
1431 if (wParam) {
1432 LPARAM NoImeWin = lParam;
1433 NoImeWin = NoImeWin & (~ISC_SHOWUICOMPOSITIONWINDOW);
1434 return ::DefWindowProc(MainHWND(), iMessage, wParam, NoImeWin);
1437 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1439 // These are not handled in Scintilla and its faster to dispatch them here.
1440 // Also moves time out to here so profile doesn't count lots of empty message calls.
1442 case WM_MOVE:
1443 case WM_MOUSEACTIVATE:
1444 case WM_NCHITTEST:
1445 case WM_NCCALCSIZE:
1446 case WM_NCPAINT:
1447 case WM_NCMOUSEMOVE:
1448 case WM_NCLBUTTONDOWN:
1449 case WM_IME_NOTIFY:
1450 case WM_SYSCOMMAND:
1451 case WM_WINDOWPOSCHANGING:
1452 case WM_WINDOWPOSCHANGED:
1453 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1455 case WM_GETTEXTLENGTH:
1456 return GetTextLength();
1458 case WM_GETTEXT:
1459 return GetText(wParam, lParam);
1461 case EM_LINEFROMCHAR:
1462 if (static_cast<int>(wParam) < 0) {
1463 wParam = SelectionStart().Position();
1465 return pdoc->LineFromPosition(static_cast<int>(wParam));
1467 case EM_EXLINEFROMCHAR:
1468 return pdoc->LineFromPosition(static_cast<int>(lParam));
1470 case EM_GETSEL:
1471 if (wParam) {
1472 *reinterpret_cast<int *>(wParam) = SelectionStart().Position();
1474 if (lParam) {
1475 *reinterpret_cast<int *>(lParam) = SelectionEnd().Position();
1477 return MAKELONG(SelectionStart().Position(), SelectionEnd().Position());
1479 case EM_EXGETSEL: {
1480 if (lParam == 0) {
1481 return 0;
1483 Sci_CharacterRange *pCR = reinterpret_cast<Sci_CharacterRange *>(lParam);
1484 pCR->cpMin = SelectionStart().Position();
1485 pCR->cpMax = SelectionEnd().Position();
1487 break;
1489 case EM_SETSEL: {
1490 int nStart = static_cast<int>(wParam);
1491 int nEnd = static_cast<int>(lParam);
1492 if (nStart == 0 && nEnd == -1) {
1493 nEnd = pdoc->Length();
1495 if (nStart == -1) {
1496 nStart = nEnd; // Remove selection
1498 if (nStart > nEnd) {
1499 SetSelection(nEnd, nStart);
1500 } else {
1501 SetSelection(nStart, nEnd);
1503 EnsureCaretVisible();
1505 break;
1507 case EM_EXSETSEL: {
1508 if (lParam == 0) {
1509 return 0;
1511 Sci_CharacterRange *pCR = reinterpret_cast<Sci_CharacterRange *>(lParam);
1512 sel.selType = Selection::selStream;
1513 if (pCR->cpMin == 0 && pCR->cpMax == -1) {
1514 SetSelection(pCR->cpMin, pdoc->Length());
1515 } else {
1516 SetSelection(pCR->cpMin, pCR->cpMax);
1518 EnsureCaretVisible();
1519 return pdoc->LineFromPosition(SelectionStart().Position());
1522 case SCI_GETDIRECTFUNCTION:
1523 return reinterpret_cast<sptr_t>(DirectFunction);
1525 case SCI_GETDIRECTPOINTER:
1526 return reinterpret_cast<sptr_t>(this);
1528 case SCI_GRABFOCUS:
1529 ::SetFocus(MainHWND());
1530 break;
1532 case SCI_SETKEYSUNICODE:
1533 keysAlwaysUnicode = wParam != 0;
1534 break;
1536 case SCI_GETKEYSUNICODE:
1537 return keysAlwaysUnicode;
1539 case SCI_SETTECHNOLOGY:
1540 if ((wParam == SC_TECHNOLOGY_DEFAULT) ||
1541 (wParam == SC_TECHNOLOGY_DIRECTWRITERETAIN) ||
1542 (wParam == SC_TECHNOLOGY_DIRECTWRITEDC) ||
1543 (wParam == SC_TECHNOLOGY_DIRECTWRITE)) {
1544 if (technology != static_cast<int>(wParam)) {
1545 if (static_cast<int>(wParam) > SC_TECHNOLOGY_DEFAULT) {
1546 #if defined(USE_D2D)
1547 if (!LoadD2D())
1548 // Failed to load Direct2D or DirectWrite so no effect
1549 return 0;
1550 #else
1551 return 0;
1552 #endif
1554 #if defined(USE_D2D)
1555 DropRenderTarget();
1556 #endif
1557 technology = static_cast<int>(wParam);
1558 // Invalidate all cached information including layout.
1559 DropGraphics(true);
1560 InvalidateStyleRedraw();
1563 break;
1565 #ifdef SCI_LEXER
1566 case SCI_LOADLEXERLIBRARY:
1567 LexerManager::GetInstance()->Load(reinterpret_cast<const char *>(lParam));
1568 break;
1569 #endif
1571 default:
1572 return ScintillaBase::WndProc(iMessage, wParam, lParam);
1574 } catch (std::bad_alloc &) {
1575 errorStatus = SC_STATUS_BADALLOC;
1576 } catch (...) {
1577 errorStatus = SC_STATUS_FAILURE;
1579 return 0l;
1582 bool ScintillaWin::ValidCodePage(int codePage) const {
1583 return codePage == 0 || codePage == SC_CP_UTF8 ||
1584 codePage == 932 || codePage == 936 || codePage == 949 ||
1585 codePage == 950 || codePage == 1361;
1588 sptr_t ScintillaWin::DefWndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
1589 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1593 * Report that this Editor subclass has a working implementation of FineTickerStart.
1595 bool ScintillaWin::FineTickerAvailable() {
1596 return true;
1599 bool ScintillaWin::FineTickerRunning(TickReason reason) {
1600 return timers[reason] != 0;
1603 void ScintillaWin::FineTickerStart(TickReason reason, int millis, int tolerance) {
1604 FineTickerCancel(reason);
1605 if (SetCoalescableTimerFn && tolerance) {
1606 timers[reason] = SetCoalescableTimerFn(MainHWND(), fineTimerStart + reason, millis, NULL, tolerance);
1607 } else {
1608 timers[reason] = ::SetTimer(MainHWND(), fineTimerStart + reason, millis, NULL);
1612 void ScintillaWin::FineTickerCancel(TickReason reason) {
1613 if (timers[reason]) {
1614 ::KillTimer(MainHWND(), timers[reason]);
1615 timers[reason] = 0;
1620 bool ScintillaWin::SetIdle(bool on) {
1621 // On Win32 the Idler is implemented as a Timer on the Scintilla window. This
1622 // takes advantage of the fact that WM_TIMER messages are very low priority,
1623 // and are only posted when the message queue is empty, i.e. during idle time.
1624 if (idler.state != on) {
1625 if (on) {
1626 idler.idlerID = ::SetTimer(MainHWND(), idleTimerID, 10, NULL)
1627 ? reinterpret_cast<IdlerID>(idleTimerID) : 0;
1628 } else {
1629 ::KillTimer(MainHWND(), reinterpret_cast<uptr_t>(idler.idlerID));
1630 idler.idlerID = 0;
1632 idler.state = idler.idlerID != 0;
1634 return idler.state;
1637 void ScintillaWin::SetMouseCapture(bool on) {
1638 if (mouseDownCaptures) {
1639 if (on) {
1640 ::SetCapture(MainHWND());
1641 } else {
1642 ::ReleaseCapture();
1645 capturedMouse = on;
1648 bool ScintillaWin::HaveMouseCapture() {
1649 // Cannot just see if GetCapture is this window as the scroll bar also sets capture for the window
1650 return capturedMouse;
1651 //return capturedMouse && (::GetCapture() == MainHWND());
1654 void ScintillaWin::SetTrackMouseLeaveEvent(bool on) {
1655 if (on && TrackMouseEventFn && !trackedMouseLeave) {
1656 TRACKMOUSEEVENT tme;
1657 tme.cbSize = sizeof(tme);
1658 tme.dwFlags = TME_LEAVE;
1659 tme.hwndTrack = MainHWND();
1660 tme.dwHoverTime = HOVER_DEFAULT; // Unused but triggers Dr. Memory if not initialized
1661 TrackMouseEventFn(&tme);
1663 trackedMouseLeave = on;
1666 bool ScintillaWin::PaintContains(PRectangle rc) {
1667 if (paintState == painting) {
1668 return BoundsContains(rcPaint, hRgnUpdate, rc);
1670 return true;
1673 void ScintillaWin::ScrollText(int /* linesToMove */) {
1674 //Platform::DebugPrintf("ScintillaWin::ScrollText %d\n", linesToMove);
1675 //::ScrollWindow(MainHWND(), 0,
1676 // vs.lineHeight * linesToMove, 0, 0);
1677 //::UpdateWindow(MainHWND());
1678 Redraw();
1679 UpdateSystemCaret();
1682 void ScintillaWin::UpdateSystemCaret() {
1683 if (hasFocus) {
1684 if (HasCaretSizeChanged()) {
1685 DestroySystemCaret();
1686 CreateSystemCaret();
1688 Point pos = PointMainCaret();
1689 ::SetCaretPos(static_cast<int>(pos.x), static_cast<int>(pos.y));
1693 int ScintillaWin::SetScrollInfo(int nBar, LPCSCROLLINFO lpsi, BOOL bRedraw) {
1694 return ::SetScrollInfo(MainHWND(), nBar, lpsi, bRedraw);
1697 bool ScintillaWin::GetScrollInfo(int nBar, LPSCROLLINFO lpsi) {
1698 return ::GetScrollInfo(MainHWND(), nBar, lpsi) ? true : false;
1701 // Change the scroll position but avoid repaint if changing to same value
1702 void ScintillaWin::ChangeScrollPos(int barType, int pos) {
1703 SCROLLINFO sci = {
1704 sizeof(sci), 0, 0, 0, 0, 0, 0
1706 sci.fMask = SIF_POS;
1707 GetScrollInfo(barType, &sci);
1708 if (sci.nPos != pos) {
1709 DwellEnd(true);
1710 sci.nPos = pos;
1711 SetScrollInfo(barType, &sci, TRUE);
1715 void ScintillaWin::SetVerticalScrollPos() {
1716 ChangeScrollPos(SB_VERT, topLine);
1719 void ScintillaWin::SetHorizontalScrollPos() {
1720 ChangeScrollPos(SB_HORZ, xOffset);
1723 bool ScintillaWin::ModifyScrollBars(int nMax, int nPage) {
1724 bool modified = false;
1725 SCROLLINFO sci = {
1726 sizeof(sci), 0, 0, 0, 0, 0, 0
1728 sci.fMask = SIF_PAGE | SIF_RANGE;
1729 GetScrollInfo(SB_VERT, &sci);
1730 int vertEndPreferred = nMax;
1731 if (!verticalScrollBarVisible)
1732 nPage = vertEndPreferred + 1;
1733 if ((sci.nMin != 0) ||
1734 (sci.nMax != vertEndPreferred) ||
1735 (sci.nPage != static_cast<unsigned int>(nPage)) ||
1736 (sci.nPos != 0)) {
1737 sci.fMask = SIF_PAGE | SIF_RANGE;
1738 sci.nMin = 0;
1739 sci.nMax = vertEndPreferred;
1740 sci.nPage = nPage;
1741 sci.nPos = 0;
1742 sci.nTrackPos = 1;
1743 SetScrollInfo(SB_VERT, &sci, TRUE);
1744 modified = true;
1747 PRectangle rcText = GetTextRectangle();
1748 int horizEndPreferred = scrollWidth;
1749 if (horizEndPreferred < 0)
1750 horizEndPreferred = 0;
1751 unsigned int pageWidth = static_cast<unsigned int>(rcText.Width());
1752 if (!horizontalScrollBarVisible || Wrapping())
1753 pageWidth = horizEndPreferred + 1;
1754 sci.fMask = SIF_PAGE | SIF_RANGE;
1755 GetScrollInfo(SB_HORZ, &sci);
1756 if ((sci.nMin != 0) ||
1757 (sci.nMax != horizEndPreferred) ||
1758 (sci.nPage != pageWidth) ||
1759 (sci.nPos != 0)) {
1760 sci.fMask = SIF_PAGE | SIF_RANGE;
1761 sci.nMin = 0;
1762 sci.nMax = horizEndPreferred;
1763 sci.nPage = pageWidth;
1764 sci.nPos = 0;
1765 sci.nTrackPos = 1;
1766 SetScrollInfo(SB_HORZ, &sci, TRUE);
1767 modified = true;
1768 if (scrollWidth < static_cast<int>(pageWidth)) {
1769 HorizontalScrollTo(0);
1772 return modified;
1775 void ScintillaWin::NotifyChange() {
1776 ::SendMessage(::GetParent(MainHWND()), WM_COMMAND,
1777 MAKELONG(GetCtrlID(), SCEN_CHANGE),
1778 reinterpret_cast<LPARAM>(MainHWND()));
1781 void ScintillaWin::NotifyFocus(bool focus) {
1782 ::SendMessage(::GetParent(MainHWND()), WM_COMMAND,
1783 MAKELONG(GetCtrlID(), focus ? SCEN_SETFOCUS : SCEN_KILLFOCUS),
1784 reinterpret_cast<LPARAM>(MainHWND()));
1785 Editor::NotifyFocus(focus);
1788 void ScintillaWin::SetCtrlID(int identifier) {
1789 ::SetWindowID(reinterpret_cast<HWND>(wMain.GetID()), identifier);
1792 int ScintillaWin::GetCtrlID() {
1793 return ::GetDlgCtrlID(reinterpret_cast<HWND>(wMain.GetID()));
1796 void ScintillaWin::NotifyParent(SCNotification scn) {
1797 scn.nmhdr.hwndFrom = MainHWND();
1798 scn.nmhdr.idFrom = GetCtrlID();
1799 ::SendMessage(::GetParent(MainHWND()), WM_NOTIFY,
1800 GetCtrlID(), reinterpret_cast<LPARAM>(&scn));
1803 void ScintillaWin::NotifyDoubleClick(Point pt, int modifiers) {
1804 //Platform::DebugPrintf("ScintillaWin Double click 0\n");
1805 ScintillaBase::NotifyDoubleClick(pt, modifiers);
1806 // Send myself a WM_LBUTTONDBLCLK, so the container can handle it too.
1807 ::SendMessage(MainHWND(),
1808 WM_LBUTTONDBLCLK,
1809 (modifiers & SCI_SHIFT) ? MK_SHIFT : 0,
1810 MAKELPARAM(pt.x, pt.y));
1813 class CaseFolderDBCS : public CaseFolderTable {
1814 // Allocate the expandable storage here so that it does not need to be reallocated
1815 // for each call to Fold.
1816 std::vector<wchar_t> utf16Mixed;
1817 std::vector<wchar_t> utf16Folded;
1818 UINT cp;
1819 public:
1820 explicit CaseFolderDBCS(UINT cp_) : cp(cp_) {
1821 StandardASCII();
1823 virtual size_t Fold(char *folded, size_t sizeFolded, const char *mixed, size_t lenMixed) {
1824 if ((lenMixed == 1) && (sizeFolded > 0)) {
1825 folded[0] = mapping[static_cast<unsigned char>(mixed[0])];
1826 return 1;
1827 } else {
1828 if (lenMixed > utf16Mixed.size()) {
1829 utf16Mixed.resize(lenMixed + 8);
1831 size_t nUtf16Mixed = ::MultiByteToWideChar(cp, 0, mixed,
1832 static_cast<int>(lenMixed),
1833 &utf16Mixed[0],
1834 static_cast<int>(utf16Mixed.size()));
1836 if (nUtf16Mixed == 0) {
1837 // Failed to convert -> bad input
1838 folded[0] = '\0';
1839 return 1;
1842 unsigned int lenFlat = 0;
1843 for (size_t mixIndex=0; mixIndex < nUtf16Mixed; mixIndex++) {
1844 if ((lenFlat + 20) > utf16Folded.size())
1845 utf16Folded.resize(lenFlat + 60);
1846 const char *foldedUTF8 = CaseConvert(utf16Mixed[mixIndex], CaseConversionFold);
1847 if (foldedUTF8) {
1848 // Maximum length of a case conversion is 6 bytes, 3 characters
1849 wchar_t wFolded[20];
1850 size_t charsConverted = UTF16FromUTF8(foldedUTF8,
1851 strlen(foldedUTF8),
1852 wFolded, ELEMENTS(wFolded));
1853 for (size_t j=0; j<charsConverted; j++)
1854 utf16Folded[lenFlat++] = wFolded[j];
1855 } else {
1856 utf16Folded[lenFlat++] = utf16Mixed[mixIndex];
1860 size_t lenOut = ::WideCharToMultiByte(cp, 0,
1861 &utf16Folded[0], lenFlat,
1862 NULL, 0, NULL, 0);
1864 if (lenOut < sizeFolded) {
1865 ::WideCharToMultiByte(cp, 0,
1866 &utf16Folded[0], lenFlat,
1867 folded, static_cast<int>(lenOut), NULL, 0);
1868 return lenOut;
1869 } else {
1870 return 0;
1876 CaseFolder *ScintillaWin::CaseFolderForEncoding() {
1877 UINT cpDest = CodePageOfDocument();
1878 if (cpDest == SC_CP_UTF8) {
1879 return new CaseFolderUnicode();
1880 } else {
1881 if (pdoc->dbcsCodePage == 0) {
1882 CaseFolderTable *pcf = new CaseFolderTable();
1883 pcf->StandardASCII();
1884 // Only for single byte encodings
1885 UINT cpDoc = CodePageOfDocument();
1886 for (int i=0x80; i<0x100; i++) {
1887 char sCharacter[2] = "A";
1888 sCharacter[0] = static_cast<char>(i);
1889 wchar_t wCharacter[20];
1890 unsigned int lengthUTF16 = ::MultiByteToWideChar(cpDoc, 0, sCharacter, 1,
1891 wCharacter, ELEMENTS(wCharacter));
1892 if (lengthUTF16 == 1) {
1893 const char *caseFolded = CaseConvert(wCharacter[0], CaseConversionFold);
1894 if (caseFolded) {
1895 wchar_t wLower[20];
1896 size_t charsConverted = UTF16FromUTF8(caseFolded,
1897 strlen(caseFolded),
1898 wLower, ELEMENTS(wLower));
1899 if (charsConverted == 1) {
1900 char sCharacterLowered[20];
1901 unsigned int lengthConverted = ::WideCharToMultiByte(cpDoc, 0,
1902 wLower, static_cast<int>(charsConverted),
1903 sCharacterLowered, ELEMENTS(sCharacterLowered), NULL, 0);
1904 if ((lengthConverted == 1) && (sCharacter[0] != sCharacterLowered[0])) {
1905 pcf->SetTranslation(sCharacter[0], sCharacterLowered[0]);
1911 return pcf;
1912 } else {
1913 return new CaseFolderDBCS(cpDest);
1918 std::string ScintillaWin::CaseMapString(const std::string &s, int caseMapping) {
1919 if ((s.size() == 0) || (caseMapping == cmSame))
1920 return s;
1922 UINT cpDoc = CodePageOfDocument();
1923 if (cpDoc == SC_CP_UTF8) {
1924 std::string retMapped(s.length() * maxExpansionCaseConversion, 0);
1925 size_t lenMapped = CaseConvertString(&retMapped[0], retMapped.length(), s.c_str(), s.length(),
1926 (caseMapping == cmUpper) ? CaseConversionUpper : CaseConversionLower);
1927 retMapped.resize(lenMapped);
1928 return retMapped;
1931 unsigned int lengthUTF16 = ::MultiByteToWideChar(cpDoc, 0, s.c_str(),
1932 static_cast<int>(s.size()), NULL, 0);
1933 if (lengthUTF16 == 0) // Failed to convert
1934 return s;
1936 DWORD mapFlags = LCMAP_LINGUISTIC_CASING |
1937 ((caseMapping == cmUpper) ? LCMAP_UPPERCASE : LCMAP_LOWERCASE);
1939 // Change text to UTF-16
1940 std::vector<wchar_t> vwcText(lengthUTF16);
1941 ::MultiByteToWideChar(cpDoc, 0, s.c_str(), static_cast<int>(s.size()), &vwcText[0], lengthUTF16);
1943 // Change case
1944 int charsConverted = ::LCMapStringW(LOCALE_SYSTEM_DEFAULT, mapFlags,
1945 &vwcText[0], lengthUTF16, NULL, 0);
1946 std::vector<wchar_t> vwcConverted(charsConverted);
1947 ::LCMapStringW(LOCALE_SYSTEM_DEFAULT, mapFlags,
1948 &vwcText[0], lengthUTF16, &vwcConverted[0], charsConverted);
1950 // Change back to document encoding
1951 unsigned int lengthConverted = ::WideCharToMultiByte(cpDoc, 0,
1952 &vwcConverted[0], static_cast<int>(vwcConverted.size()),
1953 NULL, 0, NULL, 0);
1954 std::vector<char> vcConverted(lengthConverted);
1955 ::WideCharToMultiByte(cpDoc, 0,
1956 &vwcConverted[0], static_cast<int>(vwcConverted.size()),
1957 &vcConverted[0], static_cast<int>(vcConverted.size()), NULL, 0);
1959 return std::string(&vcConverted[0], vcConverted.size());
1962 void ScintillaWin::Copy() {
1963 //Platform::DebugPrintf("Copy\n");
1964 if (!sel.Empty()) {
1965 SelectionText selectedText;
1966 CopySelectionRange(&selectedText);
1967 CopyToClipboard(selectedText);
1971 void ScintillaWin::CopyAllowLine() {
1972 SelectionText selectedText;
1973 CopySelectionRange(&selectedText, true);
1974 CopyToClipboard(selectedText);
1977 bool ScintillaWin::CanPaste() {
1978 if (!Editor::CanPaste())
1979 return false;
1980 if (::IsClipboardFormatAvailable(CF_TEXT))
1981 return true;
1982 if (IsUnicodeMode())
1983 return ::IsClipboardFormatAvailable(CF_UNICODETEXT) != 0;
1984 return false;
1987 class GlobalMemory {
1988 HGLOBAL hand;
1989 public:
1990 void *ptr;
1991 GlobalMemory() : hand(0), ptr(0) {
1993 explicit GlobalMemory(HGLOBAL hand_) : hand(hand_), ptr(0) {
1994 if (hand) {
1995 ptr = ::GlobalLock(hand);
1998 ~GlobalMemory() {
1999 PLATFORM_ASSERT(!ptr);
2000 assert(!hand);
2002 void Allocate(size_t bytes) {
2003 assert(!hand);
2004 hand = ::GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT, bytes);
2005 if (hand) {
2006 ptr = ::GlobalLock(hand);
2009 HGLOBAL Unlock() {
2010 PLATFORM_ASSERT(ptr);
2011 HGLOBAL handCopy = hand;
2012 ::GlobalUnlock(hand);
2013 ptr = 0;
2014 hand = 0;
2015 return handCopy;
2017 void SetClip(UINT uFormat) {
2018 ::SetClipboardData(uFormat, Unlock());
2020 operator bool() const {
2021 return ptr != 0;
2023 SIZE_T Size() {
2024 return ::GlobalSize(hand);
2028 void ScintillaWin::Paste() {
2029 if (!::OpenClipboard(MainHWND()))
2030 return;
2031 UndoGroup ug(pdoc);
2032 const bool isLine = SelectionEmpty() &&
2033 (::IsClipboardFormatAvailable(cfLineSelect) || ::IsClipboardFormatAvailable(cfVSLineTag));
2034 ClearSelection(multiPasteMode == SC_MULTIPASTE_EACH);
2035 bool isRectangular = (::IsClipboardFormatAvailable(cfColumnSelect) != 0);
2037 if (!isRectangular) {
2038 // Evaluate "Borland IDE Block Type" explicitly
2039 GlobalMemory memBorlandSelection(::GetClipboardData(cfBorlandIDEBlockType));
2040 if (memBorlandSelection) {
2041 isRectangular = (memBorlandSelection.Size() == 1) && (static_cast<BYTE *>(memBorlandSelection.ptr)[0] == 0x02);
2042 memBorlandSelection.Unlock();
2045 const PasteShape pasteShape = isRectangular ? pasteRectangular : (isLine ? pasteLine : pasteStream);
2047 // Always use CF_UNICODETEXT if available
2048 GlobalMemory memUSelection(::GetClipboardData(CF_UNICODETEXT));
2049 if (memUSelection) {
2050 wchar_t *uptr = static_cast<wchar_t *>(memUSelection.ptr);
2051 if (uptr) {
2052 unsigned int len;
2053 std::vector<char> putf;
2054 // Default Scintilla behaviour in Unicode mode
2055 if (IsUnicodeMode()) {
2056 unsigned int bytes = static_cast<unsigned int>(memUSelection.Size());
2057 len = UTF8Length(uptr, bytes / 2);
2058 putf.resize(len + 1);
2059 UTF8FromUTF16(uptr, bytes / 2, &putf[0], len);
2060 } else {
2061 // CF_UNICODETEXT available, but not in Unicode mode
2062 // Convert from Unicode to current Scintilla code page
2063 UINT cpDest = CodePageOfDocument();
2064 len = ::WideCharToMultiByte(cpDest, 0, uptr, -1,
2065 NULL, 0, NULL, NULL) - 1; // subtract 0 terminator
2066 putf.resize(len + 1);
2067 ::WideCharToMultiByte(cpDest, 0, uptr, -1,
2068 &putf[0], len + 1, NULL, NULL);
2071 InsertPasteShape(&putf[0], len, pasteShape);
2073 memUSelection.Unlock();
2074 } else {
2075 // CF_UNICODETEXT not available, paste ANSI text
2076 GlobalMemory memSelection(::GetClipboardData(CF_TEXT));
2077 if (memSelection) {
2078 char *ptr = static_cast<char *>(memSelection.ptr);
2079 if (ptr) {
2080 unsigned int bytes = static_cast<unsigned int>(memSelection.Size());
2081 unsigned int len = bytes;
2082 for (unsigned int i = 0; i < bytes; i++) {
2083 if ((len == bytes) && (0 == ptr[i]))
2084 len = i;
2087 // In Unicode mode, convert clipboard text to UTF-8
2088 if (IsUnicodeMode()) {
2089 std::vector<wchar_t> uptr(len+1);
2091 unsigned int ulen = ::MultiByteToWideChar(CP_ACP, 0,
2092 ptr, len, &uptr[0], len+1);
2094 unsigned int mlen = UTF8Length(&uptr[0], ulen);
2095 std::vector<char> putf(mlen+1);
2096 // CP_UTF8 not available on Windows 95, so use UTF8FromUTF16()
2097 UTF8FromUTF16(&uptr[0], ulen, &putf[0], mlen);
2099 InsertPasteShape(&putf[0], mlen, pasteShape);
2100 } else {
2101 InsertPasteShape(ptr, len, pasteShape);
2104 memSelection.Unlock();
2107 ::CloseClipboard();
2108 Redraw();
2111 void ScintillaWin::CreateCallTipWindow(PRectangle) {
2112 if (!ct.wCallTip.Created()) {
2113 ct.wCallTip = ::CreateWindow(callClassName, TEXT("ACallTip"),
2114 WS_POPUP, 100, 100, 150, 20,
2115 MainHWND(), 0,
2116 GetWindowInstance(MainHWND()),
2117 this);
2118 ct.wDraw = ct.wCallTip;
2122 void ScintillaWin::AddToPopUp(const char *label, int cmd, bool enabled) {
2123 HMENU hmenuPopup = reinterpret_cast<HMENU>(popup.GetID());
2124 if (!label[0])
2125 ::AppendMenuA(hmenuPopup, MF_SEPARATOR, 0, "");
2126 else if (enabled)
2127 ::AppendMenuA(hmenuPopup, MF_STRING, cmd, label);
2128 else
2129 ::AppendMenuA(hmenuPopup, MF_STRING | MF_DISABLED | MF_GRAYED, cmd, label);
2132 void ScintillaWin::ClaimSelection() {
2133 // Windows does not have a primary selection
2136 /// Implement IUnknown
2138 STDMETHODIMP_(ULONG)FormatEnumerator_AddRef(FormatEnumerator *fe);
2139 STDMETHODIMP FormatEnumerator_QueryInterface(FormatEnumerator *fe, REFIID riid, PVOID *ppv) {
2140 //Platform::DebugPrintf("EFE QI");
2141 *ppv = NULL;
2142 if (riid == IID_IUnknown)
2143 *ppv = reinterpret_cast<IEnumFORMATETC *>(fe);
2144 if (riid == IID_IEnumFORMATETC)
2145 *ppv = reinterpret_cast<IEnumFORMATETC *>(fe);
2146 if (!*ppv)
2147 return E_NOINTERFACE;
2148 FormatEnumerator_AddRef(fe);
2149 return S_OK;
2151 STDMETHODIMP_(ULONG)FormatEnumerator_AddRef(FormatEnumerator *fe) {
2152 return ++fe->ref;
2154 STDMETHODIMP_(ULONG)FormatEnumerator_Release(FormatEnumerator *fe) {
2155 fe->ref--;
2156 if (fe->ref > 0)
2157 return fe->ref;
2158 delete fe;
2159 return 0;
2161 /// Implement IEnumFORMATETC
2162 STDMETHODIMP FormatEnumerator_Next(FormatEnumerator *fe, ULONG celt, FORMATETC *rgelt, ULONG *pceltFetched) {
2163 if (rgelt == NULL) return E_POINTER;
2164 unsigned int putPos = 0;
2165 while ((fe->pos < fe->formats.size()) && (putPos < celt)) {
2166 rgelt->cfFormat = fe->formats[fe->pos];
2167 rgelt->ptd = 0;
2168 rgelt->dwAspect = DVASPECT_CONTENT;
2169 rgelt->lindex = -1;
2170 rgelt->tymed = TYMED_HGLOBAL;
2171 rgelt++;
2172 fe->pos++;
2173 putPos++;
2175 if (pceltFetched)
2176 *pceltFetched = putPos;
2177 return putPos ? S_OK : S_FALSE;
2179 STDMETHODIMP FormatEnumerator_Skip(FormatEnumerator *fe, ULONG celt) {
2180 fe->pos += celt;
2181 return S_OK;
2183 STDMETHODIMP FormatEnumerator_Reset(FormatEnumerator *fe) {
2184 fe->pos = 0;
2185 return S_OK;
2187 STDMETHODIMP FormatEnumerator_Clone(FormatEnumerator *fe, IEnumFORMATETC **ppenum) {
2188 FormatEnumerator *pfe;
2189 try {
2190 pfe = new FormatEnumerator(fe->pos, &fe->formats[0], fe->formats.size());
2191 } catch (...) {
2192 return E_OUTOFMEMORY;
2194 return FormatEnumerator_QueryInterface(pfe, IID_IEnumFORMATETC,
2195 reinterpret_cast<void **>(ppenum));
2198 static VFunction *vtFormatEnumerator[] = {
2199 (VFunction *)(FormatEnumerator_QueryInterface),
2200 (VFunction *)(FormatEnumerator_AddRef),
2201 (VFunction *)(FormatEnumerator_Release),
2202 (VFunction *)(FormatEnumerator_Next),
2203 (VFunction *)(FormatEnumerator_Skip),
2204 (VFunction *)(FormatEnumerator_Reset),
2205 (VFunction *)(FormatEnumerator_Clone)
2208 FormatEnumerator::FormatEnumerator(int pos_, CLIPFORMAT formats_[], size_t formatsLen_) {
2209 vtbl = vtFormatEnumerator;
2210 ref = 0; // First QI adds first reference...
2211 pos = pos_;
2212 formats.insert(formats.begin(), formats_, formats_+formatsLen_);
2215 /// Implement IUnknown
2216 STDMETHODIMP DropSource_QueryInterface(DropSource *ds, REFIID riid, PVOID *ppv) {
2217 return ds->sci->QueryInterface(riid, ppv);
2219 STDMETHODIMP_(ULONG)DropSource_AddRef(DropSource *ds) {
2220 return ds->sci->AddRef();
2222 STDMETHODIMP_(ULONG)DropSource_Release(DropSource *ds) {
2223 return ds->sci->Release();
2226 /// Implement IDropSource
2227 STDMETHODIMP DropSource_QueryContinueDrag(DropSource *, BOOL fEsc, DWORD grfKeyState) {
2228 if (fEsc)
2229 return DRAGDROP_S_CANCEL;
2230 if (!(grfKeyState & MK_LBUTTON))
2231 return DRAGDROP_S_DROP;
2232 return S_OK;
2235 STDMETHODIMP DropSource_GiveFeedback(DropSource *, DWORD) {
2236 return DRAGDROP_S_USEDEFAULTCURSORS;
2239 static VFunction *vtDropSource[] = {
2240 (VFunction *)(DropSource_QueryInterface),
2241 (VFunction *)(DropSource_AddRef),
2242 (VFunction *)(DropSource_Release),
2243 (VFunction *)(DropSource_QueryContinueDrag),
2244 (VFunction *)(DropSource_GiveFeedback)
2247 DropSource::DropSource() {
2248 vtbl = vtDropSource;
2249 sci = 0;
2252 /// Implement IUnkown
2253 STDMETHODIMP DataObject_QueryInterface(DataObject *pd, REFIID riid, PVOID *ppv) {
2254 //Platform::DebugPrintf("DO QI %x\n", pd);
2255 return pd->sci->QueryInterface(riid, ppv);
2257 STDMETHODIMP_(ULONG)DataObject_AddRef(DataObject *pd) {
2258 return pd->sci->AddRef();
2260 STDMETHODIMP_(ULONG)DataObject_Release(DataObject *pd) {
2261 return pd->sci->Release();
2263 /// Implement IDataObject
2264 STDMETHODIMP DataObject_GetData(DataObject *pd, FORMATETC *pFEIn, STGMEDIUM *pSTM) {
2265 return pd->sci->GetData(pFEIn, pSTM);
2268 STDMETHODIMP DataObject_GetDataHere(DataObject *, FORMATETC *, STGMEDIUM *) {
2269 //Platform::DebugPrintf("DOB GetDataHere\n");
2270 return E_NOTIMPL;
2273 STDMETHODIMP DataObject_QueryGetData(DataObject *pd, FORMATETC *pFE) {
2274 if (pd->sci->DragIsRectangularOK(pFE->cfFormat) &&
2275 pFE->ptd == 0 &&
2276 (pFE->dwAspect & DVASPECT_CONTENT) != 0 &&
2277 pFE->lindex == -1 &&
2278 (pFE->tymed & TYMED_HGLOBAL) != 0
2280 return S_OK;
2283 bool formatOK = (pFE->cfFormat == CF_TEXT) ||
2284 ((pFE->cfFormat == CF_UNICODETEXT) && pd->sci->IsUnicodeMode());
2285 if (!formatOK ||
2286 pFE->ptd != 0 ||
2287 (pFE->dwAspect & DVASPECT_CONTENT) == 0 ||
2288 pFE->lindex != -1 ||
2289 (pFE->tymed & TYMED_HGLOBAL) == 0
2291 //Platform::DebugPrintf("DOB QueryGetData No %x\n",pFE->cfFormat);
2292 //return DATA_E_FORMATETC;
2293 return S_FALSE;
2295 //Platform::DebugPrintf("DOB QueryGetData OK %x\n",pFE->cfFormat);
2296 return S_OK;
2299 STDMETHODIMP DataObject_GetCanonicalFormatEtc(DataObject *pd, FORMATETC *, FORMATETC *pFEOut) {
2300 //Platform::DebugPrintf("DOB GetCanon\n");
2301 if (pd->sci->IsUnicodeMode())
2302 pFEOut->cfFormat = CF_UNICODETEXT;
2303 else
2304 pFEOut->cfFormat = CF_TEXT;
2305 pFEOut->ptd = 0;
2306 pFEOut->dwAspect = DVASPECT_CONTENT;
2307 pFEOut->lindex = -1;
2308 pFEOut->tymed = TYMED_HGLOBAL;
2309 return S_OK;
2312 STDMETHODIMP DataObject_SetData(DataObject *, FORMATETC *, STGMEDIUM *, BOOL) {
2313 //Platform::DebugPrintf("DOB SetData\n");
2314 return E_FAIL;
2317 STDMETHODIMP DataObject_EnumFormatEtc(DataObject *pd, DWORD dwDirection, IEnumFORMATETC **ppEnum) {
2318 try {
2319 //Platform::DebugPrintf("DOB EnumFormatEtc %d\n", dwDirection);
2320 if (dwDirection != DATADIR_GET) {
2321 *ppEnum = 0;
2322 return E_FAIL;
2324 FormatEnumerator *pfe;
2325 if (pd->sci->IsUnicodeMode()) {
2326 CLIPFORMAT formats[] = {CF_UNICODETEXT, CF_TEXT};
2327 pfe = new FormatEnumerator(0, formats, ELEMENTS(formats));
2328 } else {
2329 CLIPFORMAT formats[] = {CF_TEXT};
2330 pfe = new FormatEnumerator(0, formats, ELEMENTS(formats));
2332 return FormatEnumerator_QueryInterface(pfe, IID_IEnumFORMATETC,
2333 reinterpret_cast<void **>(ppEnum));
2334 } catch (std::bad_alloc &) {
2335 pd->sci->errorStatus = SC_STATUS_BADALLOC;
2336 return E_OUTOFMEMORY;
2337 } catch (...) {
2338 pd->sci->errorStatus = SC_STATUS_FAILURE;
2339 return E_FAIL;
2343 STDMETHODIMP DataObject_DAdvise(DataObject *, FORMATETC *, DWORD, IAdviseSink *, PDWORD) {
2344 //Platform::DebugPrintf("DOB DAdvise\n");
2345 return E_FAIL;
2348 STDMETHODIMP DataObject_DUnadvise(DataObject *, DWORD) {
2349 //Platform::DebugPrintf("DOB DUnadvise\n");
2350 return E_FAIL;
2353 STDMETHODIMP DataObject_EnumDAdvise(DataObject *, IEnumSTATDATA **) {
2354 //Platform::DebugPrintf("DOB EnumDAdvise\n");
2355 return E_FAIL;
2358 static VFunction *vtDataObject[] = {
2359 (VFunction *)(DataObject_QueryInterface),
2360 (VFunction *)(DataObject_AddRef),
2361 (VFunction *)(DataObject_Release),
2362 (VFunction *)(DataObject_GetData),
2363 (VFunction *)(DataObject_GetDataHere),
2364 (VFunction *)(DataObject_QueryGetData),
2365 (VFunction *)(DataObject_GetCanonicalFormatEtc),
2366 (VFunction *)(DataObject_SetData),
2367 (VFunction *)(DataObject_EnumFormatEtc),
2368 (VFunction *)(DataObject_DAdvise),
2369 (VFunction *)(DataObject_DUnadvise),
2370 (VFunction *)(DataObject_EnumDAdvise)
2373 DataObject::DataObject() {
2374 vtbl = vtDataObject;
2375 sci = 0;
2378 /// Implement IUnknown
2379 STDMETHODIMP DropTarget_QueryInterface(DropTarget *dt, REFIID riid, PVOID *ppv) {
2380 //Platform::DebugPrintf("DT QI %x\n", dt);
2381 return dt->sci->QueryInterface(riid, ppv);
2383 STDMETHODIMP_(ULONG)DropTarget_AddRef(DropTarget *dt) {
2384 return dt->sci->AddRef();
2386 STDMETHODIMP_(ULONG)DropTarget_Release(DropTarget *dt) {
2387 return dt->sci->Release();
2390 /// Implement IDropTarget by forwarding to Scintilla
2391 STDMETHODIMP DropTarget_DragEnter(DropTarget *dt, LPDATAOBJECT pIDataSource, DWORD grfKeyState,
2392 POINTL pt, PDWORD pdwEffect) {
2393 try {
2394 return dt->sci->DragEnter(pIDataSource, grfKeyState, pt, pdwEffect);
2395 } catch (...) {
2396 dt->sci->errorStatus = SC_STATUS_FAILURE;
2398 return E_FAIL;
2400 STDMETHODIMP DropTarget_DragOver(DropTarget *dt, DWORD grfKeyState, POINTL pt, PDWORD pdwEffect) {
2401 try {
2402 return dt->sci->DragOver(grfKeyState, pt, pdwEffect);
2403 } catch (...) {
2404 dt->sci->errorStatus = SC_STATUS_FAILURE;
2406 return E_FAIL;
2408 STDMETHODIMP DropTarget_DragLeave(DropTarget *dt) {
2409 try {
2410 return dt->sci->DragLeave();
2411 } catch (...) {
2412 dt->sci->errorStatus = SC_STATUS_FAILURE;
2414 return E_FAIL;
2416 STDMETHODIMP DropTarget_Drop(DropTarget *dt, LPDATAOBJECT pIDataSource, DWORD grfKeyState,
2417 POINTL pt, PDWORD pdwEffect) {
2418 try {
2419 return dt->sci->Drop(pIDataSource, grfKeyState, pt, pdwEffect);
2420 } catch (...) {
2421 dt->sci->errorStatus = SC_STATUS_FAILURE;
2423 return E_FAIL;
2426 static VFunction *vtDropTarget[] = {
2427 (VFunction *)(DropTarget_QueryInterface),
2428 (VFunction *)(DropTarget_AddRef),
2429 (VFunction *)(DropTarget_Release),
2430 (VFunction *)(DropTarget_DragEnter),
2431 (VFunction *)(DropTarget_DragOver),
2432 (VFunction *)(DropTarget_DragLeave),
2433 (VFunction *)(DropTarget_Drop)
2436 DropTarget::DropTarget() {
2437 vtbl = vtDropTarget;
2438 sci = 0;
2442 * DBCS: support Input Method Editor (IME).
2443 * Called when IME Window opened.
2445 void ScintillaWin::ImeStartComposition() {
2446 if (caret.active) {
2447 // Move IME Window to current caret position
2448 HIMC hIMC = ::ImmGetContext(MainHWND());
2449 Point pos = PointMainCaret();
2450 COMPOSITIONFORM CompForm;
2451 CompForm.dwStyle = CFS_POINT;
2452 CompForm.ptCurrentPos.x = static_cast<int>(pos.x);
2453 CompForm.ptCurrentPos.y = static_cast<int>(pos.y);
2455 ::ImmSetCompositionWindow(hIMC, &CompForm);
2457 // Set font of IME window to same as surrounded text.
2458 if (stylesValid) {
2459 // Since the style creation code has been made platform independent,
2460 // The logfont for the IME is recreated here.
2461 int styleHere = (pdoc->StyleAt(sel.MainCaret())) & 31;
2462 LOGFONTW lf = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, L""};
2463 int sizeZoomed = vs.styles[styleHere].size + vs.zoomLevel * SC_FONT_SIZE_MULTIPLIER;
2464 if (sizeZoomed <= 2 * SC_FONT_SIZE_MULTIPLIER) // Hangs if sizeZoomed <= 1
2465 sizeZoomed = 2 * SC_FONT_SIZE_MULTIPLIER;
2466 AutoSurface surface(this);
2467 int deviceHeight = sizeZoomed;
2468 if (surface) {
2469 deviceHeight = (sizeZoomed * surface->LogPixelsY()) / 72;
2471 // The negative is to allow for leading
2472 lf.lfHeight = -(abs(deviceHeight / SC_FONT_SIZE_MULTIPLIER));
2473 lf.lfWeight = vs.styles[styleHere].weight;
2474 lf.lfItalic = static_cast<BYTE>(vs.styles[styleHere].italic ? 1 : 0);
2475 lf.lfCharSet = DEFAULT_CHARSET;
2476 lf.lfFaceName[0] = L'\0';
2477 if (vs.styles[styleHere].fontName) {
2478 const char* fontName = vs.styles[styleHere].fontName;
2479 UTF16FromUTF8(fontName, strlen(fontName)+1, lf.lfFaceName, LF_FACESIZE);
2482 ::ImmSetCompositionFontW(hIMC, &lf);
2484 ::ImmReleaseContext(MainHWND(), hIMC);
2485 // Caret is displayed in IME window. So, caret in Scintilla is useless.
2486 DropCaret();
2490 /** Called when IME Window closed. */
2491 void ScintillaWin::ImeEndComposition() {
2492 ShowCaretAtCurrentPosition();
2495 void ScintillaWin::AddCharBytes(char b0, char b1) {
2497 int inputCodePage = InputCodePage();
2498 if (inputCodePage && IsUnicodeMode()) {
2499 char utfval[4] = "\0\0\0";
2500 char ansiChars[3];
2501 wchar_t wcs[2];
2502 if (b0) { // Two bytes from IME
2503 ansiChars[0] = b0;
2504 ansiChars[1] = b1;
2505 ansiChars[2] = '\0';
2506 ::MultiByteToWideChar(inputCodePage, 0, ansiChars, 2, wcs, 1);
2507 } else {
2508 ansiChars[0] = b1;
2509 ansiChars[1] = '\0';
2510 ::MultiByteToWideChar(inputCodePage, 0, ansiChars, 1, wcs, 1);
2512 unsigned int len = UTF8Length(wcs, 1);
2513 UTF8FromUTF16(wcs, 1, utfval, len);
2514 utfval[len] = '\0';
2515 AddCharUTF(utfval, len ? len : 1);
2516 } else if (b0) {
2517 char dbcsChars[3];
2518 dbcsChars[0] = b0;
2519 dbcsChars[1] = b1;
2520 dbcsChars[2] = '\0';
2521 AddCharUTF(dbcsChars, 2, true);
2522 } else {
2523 AddChar(b1);
2527 void ScintillaWin::GetIntelliMouseParameters() {
2528 // This retrieves the number of lines per scroll as configured inthe Mouse Properties sheet in Control Panel
2529 ::SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, &linesPerScroll, 0);
2532 void ScintillaWin::CopyToClipboard(const SelectionText &selectedText) {
2533 if (!::OpenClipboard(MainHWND()))
2534 return;
2535 ::EmptyClipboard();
2537 GlobalMemory uniText;
2539 // Default Scintilla behaviour in Unicode mode
2540 if (IsUnicodeMode()) {
2541 size_t uchars = UTF16Length(selectedText.Data(),
2542 static_cast<int>(selectedText.LengthWithTerminator()));
2543 uniText.Allocate(2 * uchars);
2544 if (uniText) {
2545 UTF16FromUTF8(selectedText.Data(), selectedText.LengthWithTerminator(),
2546 static_cast<wchar_t *>(uniText.ptr), uchars);
2548 } else {
2549 // Not Unicode mode
2550 // Convert to Unicode using the current Scintilla code page
2551 UINT cpSrc = CodePageFromCharSet(
2552 selectedText.characterSet, selectedText.codePage);
2553 int uLen = ::MultiByteToWideChar(cpSrc, 0, selectedText.Data(),
2554 static_cast<int>(selectedText.LengthWithTerminator()), 0, 0);
2555 uniText.Allocate(2 * uLen);
2556 if (uniText) {
2557 ::MultiByteToWideChar(cpSrc, 0, selectedText.Data(),
2558 static_cast<int>(selectedText.LengthWithTerminator()),
2559 static_cast<wchar_t *>(uniText.ptr), uLen);
2563 if (uniText) {
2564 uniText.SetClip(CF_UNICODETEXT);
2565 } else {
2566 // There was a failure - try to copy at least ANSI text
2567 GlobalMemory ansiText;
2568 ansiText.Allocate(selectedText.LengthWithTerminator());
2569 if (ansiText) {
2570 memcpy(static_cast<char *>(ansiText.ptr), selectedText.Data(), selectedText.LengthWithTerminator());
2571 ansiText.SetClip(CF_TEXT);
2575 if (selectedText.rectangular) {
2576 ::SetClipboardData(cfColumnSelect, 0);
2578 GlobalMemory borlandSelection;
2579 borlandSelection.Allocate(1);
2580 if (borlandSelection) {
2581 static_cast<BYTE *>(borlandSelection.ptr)[0] = 0x02;
2582 borlandSelection.SetClip(cfBorlandIDEBlockType);
2586 if (selectedText.lineCopy) {
2587 ::SetClipboardData(cfLineSelect, 0);
2588 ::SetClipboardData(cfVSLineTag, 0);
2591 ::CloseClipboard();
2594 void ScintillaWin::ScrollMessage(WPARAM wParam) {
2595 //DWORD dwStart = timeGetTime();
2596 //Platform::DebugPrintf("Scroll %x %d\n", wParam, lParam);
2598 SCROLLINFO sci = {};
2599 sci.cbSize = sizeof(sci);
2600 sci.fMask = SIF_ALL;
2602 GetScrollInfo(SB_VERT, &sci);
2604 //Platform::DebugPrintf("ScrollInfo %d mask=%x min=%d max=%d page=%d pos=%d track=%d\n", b,sci.fMask,
2605 //sci.nMin, sci.nMax, sci.nPage, sci.nPos, sci.nTrackPos);
2607 int topLineNew = topLine;
2608 switch (LoWord(wParam)) {
2609 case SB_LINEUP:
2610 topLineNew -= 1;
2611 break;
2612 case SB_LINEDOWN:
2613 topLineNew += 1;
2614 break;
2615 case SB_PAGEUP:
2616 topLineNew -= LinesToScroll(); break;
2617 case SB_PAGEDOWN: topLineNew += LinesToScroll(); break;
2618 case SB_TOP: topLineNew = 0; break;
2619 case SB_BOTTOM: topLineNew = MaxScrollPos(); break;
2620 case SB_THUMBPOSITION: topLineNew = sci.nTrackPos; break;
2621 case SB_THUMBTRACK: topLineNew = sci.nTrackPos; break;
2623 ScrollTo(topLineNew);
2626 void ScintillaWin::HorizontalScrollMessage(WPARAM wParam) {
2627 int xPos = xOffset;
2628 PRectangle rcText = GetTextRectangle();
2629 int pageWidth = static_cast<int>(rcText.Width() * 2 / 3);
2630 switch (LoWord(wParam)) {
2631 case SB_LINEUP:
2632 xPos -= 20;
2633 break;
2634 case SB_LINEDOWN: // May move past the logical end
2635 xPos += 20;
2636 break;
2637 case SB_PAGEUP:
2638 xPos -= pageWidth;
2639 break;
2640 case SB_PAGEDOWN:
2641 xPos += pageWidth;
2642 if (xPos > scrollWidth - rcText.Width()) { // Hit the end exactly
2643 xPos = scrollWidth - static_cast<int>(rcText.Width());
2645 break;
2646 case SB_TOP:
2647 xPos = 0;
2648 break;
2649 case SB_BOTTOM:
2650 xPos = scrollWidth;
2651 break;
2652 case SB_THUMBPOSITION:
2653 case SB_THUMBTRACK: {
2654 // 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 =]
2655 SCROLLINFO si;
2656 si.cbSize = sizeof(si);
2657 si.fMask = SIF_TRACKPOS;
2658 if (GetScrollInfo(SB_HORZ, &si)) {
2659 xPos = si.nTrackPos;
2662 break;
2664 HorizontalScrollTo(xPos);
2668 * Redraw all of text area.
2669 * This paint will not be abandoned.
2671 void ScintillaWin::FullPaint() {
2672 if ((technology == SC_TECHNOLOGY_DEFAULT) || (technology == SC_TECHNOLOGY_DIRECTWRITEDC)) {
2673 HDC hdc = ::GetDC(MainHWND());
2674 FullPaintDC(hdc);
2675 ::ReleaseDC(MainHWND(), hdc);
2676 } else {
2677 FullPaintDC(0);
2682 * Redraw all of text area on the specified DC.
2683 * This paint will not be abandoned.
2685 void ScintillaWin::FullPaintDC(HDC hdc) {
2686 paintState = painting;
2687 rcPaint = GetClientRectangle();
2688 paintingAllText = true;
2689 if (technology == SC_TECHNOLOGY_DEFAULT) {
2690 AutoSurface surfaceWindow(hdc, this);
2691 if (surfaceWindow) {
2692 Paint(surfaceWindow, rcPaint);
2693 surfaceWindow->Release();
2695 } else {
2696 #if defined(USE_D2D)
2697 EnsureRenderTarget(hdc);
2698 AutoSurface surfaceWindow(pRenderTarget, this);
2699 if (surfaceWindow) {
2700 pRenderTarget->BeginDraw();
2701 Paint(surfaceWindow, rcPaint);
2702 surfaceWindow->Release();
2703 HRESULT hr = pRenderTarget->EndDraw();
2704 if (hr == D2DERR_RECREATE_TARGET) {
2705 DropRenderTarget();
2708 #endif
2710 paintState = notPainting;
2713 static bool CompareDevCap(HDC hdc, HDC hOtherDC, int nIndex) {
2714 return ::GetDeviceCaps(hdc, nIndex) == ::GetDeviceCaps(hOtherDC, nIndex);
2717 bool ScintillaWin::IsCompatibleDC(HDC hOtherDC) {
2718 HDC hdc = ::GetDC(MainHWND());
2719 bool isCompatible =
2720 CompareDevCap(hdc, hOtherDC, TECHNOLOGY) &&
2721 CompareDevCap(hdc, hOtherDC, LOGPIXELSY) &&
2722 CompareDevCap(hdc, hOtherDC, LOGPIXELSX) &&
2723 CompareDevCap(hdc, hOtherDC, BITSPIXEL) &&
2724 CompareDevCap(hdc, hOtherDC, PLANES);
2725 ::ReleaseDC(MainHWND(), hdc);
2726 return isCompatible;
2729 DWORD ScintillaWin::EffectFromState(DWORD grfKeyState) const {
2730 // These are the Wordpad semantics.
2731 DWORD dwEffect;
2732 if (inDragDrop == ddDragging) // Internal defaults to move
2733 dwEffect = DROPEFFECT_MOVE;
2734 else
2735 dwEffect = DROPEFFECT_COPY;
2736 if (grfKeyState & MK_ALT)
2737 dwEffect = DROPEFFECT_MOVE;
2738 if (grfKeyState & MK_CONTROL)
2739 dwEffect = DROPEFFECT_COPY;
2740 return dwEffect;
2743 /// Implement IUnknown
2744 STDMETHODIMP ScintillaWin::QueryInterface(REFIID riid, PVOID *ppv) {
2745 *ppv = NULL;
2746 if (riid == IID_IUnknown)
2747 *ppv = reinterpret_cast<IDropTarget *>(&dt);
2748 if (riid == IID_IDropSource)
2749 *ppv = reinterpret_cast<IDropSource *>(&ds);
2750 if (riid == IID_IDropTarget)
2751 *ppv = reinterpret_cast<IDropTarget *>(&dt);
2752 if (riid == IID_IDataObject)
2753 *ppv = reinterpret_cast<IDataObject *>(&dob);
2754 if (!*ppv)
2755 return E_NOINTERFACE;
2756 return S_OK;
2759 STDMETHODIMP_(ULONG) ScintillaWin::AddRef() {
2760 return 1;
2763 STDMETHODIMP_(ULONG) ScintillaWin::Release() {
2764 return 1;
2767 /// Implement IDropTarget
2768 STDMETHODIMP ScintillaWin::DragEnter(LPDATAOBJECT pIDataSource, DWORD grfKeyState,
2769 POINTL, PDWORD pdwEffect) {
2770 if (pIDataSource == NULL)
2771 return E_POINTER;
2772 FORMATETC fmtu = {CF_UNICODETEXT, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
2773 HRESULT hrHasUText = pIDataSource->QueryGetData(&fmtu);
2774 hasOKText = (hrHasUText == S_OK);
2775 if (!hasOKText) {
2776 FORMATETC fmte = {CF_TEXT, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
2777 HRESULT hrHasText = pIDataSource->QueryGetData(&fmte);
2778 hasOKText = (hrHasText == S_OK);
2780 if (!hasOKText) {
2781 *pdwEffect = DROPEFFECT_NONE;
2782 return S_OK;
2785 *pdwEffect = EffectFromState(grfKeyState);
2786 return S_OK;
2789 STDMETHODIMP ScintillaWin::DragOver(DWORD grfKeyState, POINTL pt, PDWORD pdwEffect) {
2790 try {
2791 if (!hasOKText || pdoc->IsReadOnly()) {
2792 *pdwEffect = DROPEFFECT_NONE;
2793 return S_OK;
2796 *pdwEffect = EffectFromState(grfKeyState);
2798 // Update the cursor.
2799 POINT rpt = {pt.x, pt.y};
2800 ::ScreenToClient(MainHWND(), &rpt);
2801 SetDragPosition(SPositionFromLocation(PointFromPOINT(rpt), false, false, UserVirtualSpace()));
2803 return S_OK;
2804 } catch (...) {
2805 errorStatus = SC_STATUS_FAILURE;
2807 return E_FAIL;
2810 STDMETHODIMP ScintillaWin::DragLeave() {
2811 try {
2812 SetDragPosition(SelectionPosition(invalidPosition));
2813 return S_OK;
2814 } catch (...) {
2815 errorStatus = SC_STATUS_FAILURE;
2817 return E_FAIL;
2820 STDMETHODIMP ScintillaWin::Drop(LPDATAOBJECT pIDataSource, DWORD grfKeyState,
2821 POINTL pt, PDWORD pdwEffect) {
2822 try {
2823 *pdwEffect = EffectFromState(grfKeyState);
2825 if (pIDataSource == NULL)
2826 return E_POINTER;
2828 SetDragPosition(SelectionPosition(invalidPosition));
2830 STGMEDIUM medium = {0, {0}, 0};
2832 std::vector<char> data; // Includes terminating NUL
2834 FORMATETC fmtu = {CF_UNICODETEXT, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
2835 HRESULT hr = pIDataSource->GetData(&fmtu, &medium);
2836 if (SUCCEEDED(hr) && medium.hGlobal) {
2837 GlobalMemory memUDrop(medium.hGlobal);
2838 wchar_t *udata = static_cast<wchar_t *>(memUDrop.ptr);
2839 if (udata) {
2840 if (IsUnicodeMode()) {
2841 int tlen = static_cast<int>(memUDrop.Size());
2842 // Convert UTF-16 to UTF-8
2843 int dataLen = UTF8Length(udata, tlen/2);
2844 data.resize(dataLen+1);
2845 UTF8FromUTF16(udata, tlen/2, &data[0], dataLen);
2846 } else {
2847 // Convert UTF-16 to ANSI
2849 // Default Scintilla behavior in Unicode mode
2850 // CF_UNICODETEXT available, but not in Unicode mode
2851 // Convert from Unicode to current Scintilla code page
2852 UINT cpDest = CodePageOfDocument();
2853 int tlen = ::WideCharToMultiByte(cpDest, 0, udata, -1,
2854 NULL, 0, NULL, NULL) - 1; // subtract 0 terminator
2855 data.resize(tlen + 1);
2856 ::WideCharToMultiByte(cpDest, 0, udata, -1,
2857 &data[0], tlen + 1, NULL, NULL);
2860 memUDrop.Unlock();
2861 } else {
2862 FORMATETC fmte = {CF_TEXT, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
2863 hr = pIDataSource->GetData(&fmte, &medium);
2864 if (SUCCEEDED(hr) && medium.hGlobal) {
2865 GlobalMemory memDrop(medium.hGlobal);
2866 const char *cdata = static_cast<char *>(memDrop.ptr);
2867 if (cdata)
2868 data.assign(cdata, cdata+strlen(cdata)+1);
2869 memDrop.Unlock();
2873 if (!SUCCEEDED(hr) || data.empty()) {
2874 //Platform::DebugPrintf("Bad data format: 0x%x\n", hres);
2875 return hr;
2878 FORMATETC fmtr = {cfColumnSelect, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
2879 HRESULT hrRectangular = pIDataSource->QueryGetData(&fmtr);
2881 POINT rpt = {pt.x, pt.y};
2882 ::ScreenToClient(MainHWND(), &rpt);
2883 SelectionPosition movePos = SPositionFromLocation(PointFromPOINT(rpt), false, false, UserVirtualSpace());
2885 DropAt(movePos, &data[0], data.size() - 1, *pdwEffect == DROPEFFECT_MOVE, hrRectangular == S_OK);
2887 // Free data
2888 if (medium.pUnkForRelease != NULL)
2889 medium.pUnkForRelease->Release();
2890 else
2891 ::GlobalFree(medium.hGlobal);
2893 return S_OK;
2894 } catch (...) {
2895 errorStatus = SC_STATUS_FAILURE;
2897 return E_FAIL;
2900 /// Implement important part of IDataObject
2901 STDMETHODIMP ScintillaWin::GetData(FORMATETC *pFEIn, STGMEDIUM *pSTM) {
2902 bool formatOK = (pFEIn->cfFormat == CF_TEXT) ||
2903 ((pFEIn->cfFormat == CF_UNICODETEXT) && IsUnicodeMode());
2904 if (!formatOK ||
2905 pFEIn->ptd != 0 ||
2906 (pFEIn->dwAspect & DVASPECT_CONTENT) == 0 ||
2907 pFEIn->lindex != -1 ||
2908 (pFEIn->tymed & TYMED_HGLOBAL) == 0
2910 //Platform::DebugPrintf("DOB GetData No %d %x %x fmt=%x\n", lenDrag, pFEIn, pSTM, pFEIn->cfFormat);
2911 return DATA_E_FORMATETC;
2913 pSTM->tymed = TYMED_HGLOBAL;
2914 //Platform::DebugPrintf("DOB GetData OK %d %x %x\n", lenDrag, pFEIn, pSTM);
2916 GlobalMemory text;
2917 if (pFEIn->cfFormat == CF_UNICODETEXT) {
2918 size_t uchars = UTF16Length(drag.Data(), static_cast<int>(drag.LengthWithTerminator()));
2919 text.Allocate(2 * uchars);
2920 if (text) {
2921 UTF16FromUTF8(drag.Data(), drag.LengthWithTerminator(),
2922 static_cast<wchar_t *>(text.ptr), uchars);
2924 } else {
2925 text.Allocate(drag.LengthWithTerminator());
2926 if (text) {
2927 memcpy(static_cast<char *>(text.ptr), drag.Data(), drag.LengthWithTerminator());
2930 pSTM->hGlobal = text ? text.Unlock() : 0;
2931 pSTM->pUnkForRelease = 0;
2932 return S_OK;
2935 bool ScintillaWin::Register(HINSTANCE hInstance_) {
2937 hInstance = hInstance_;
2938 bool result;
2940 // Register the Scintilla class
2941 // Register Scintilla as a wide character window
2942 WNDCLASSEXW wndclass;
2943 wndclass.cbSize = sizeof(wndclass);
2944 wndclass.style = CS_GLOBALCLASS | CS_HREDRAW | CS_VREDRAW;
2945 wndclass.lpfnWndProc = ScintillaWin::SWndProc;
2946 wndclass.cbClsExtra = 0;
2947 wndclass.cbWndExtra = sizeof(ScintillaWin *);
2948 wndclass.hInstance = hInstance;
2949 wndclass.hIcon = NULL;
2950 wndclass.hCursor = NULL;
2951 wndclass.hbrBackground = NULL;
2952 wndclass.lpszMenuName = NULL;
2953 wndclass.lpszClassName = L"Scintilla";
2954 wndclass.hIconSm = 0;
2955 scintillaClassAtom = ::RegisterClassExW(&wndclass);
2956 result = 0 != scintillaClassAtom;
2958 if (result) {
2959 // Register the CallTip class
2960 WNDCLASSEX wndclassc;
2961 wndclassc.cbSize = sizeof(wndclassc);
2962 wndclassc.style = CS_GLOBALCLASS | CS_HREDRAW | CS_VREDRAW;
2963 wndclassc.cbClsExtra = 0;
2964 wndclassc.cbWndExtra = sizeof(ScintillaWin *);
2965 wndclassc.hInstance = hInstance;
2966 wndclassc.hIcon = NULL;
2967 wndclassc.hbrBackground = NULL;
2968 wndclassc.lpszMenuName = NULL;
2969 wndclassc.lpfnWndProc = ScintillaWin::CTWndProc;
2970 wndclassc.hCursor = ::LoadCursor(NULL, IDC_ARROW);
2971 wndclassc.lpszClassName = callClassName;
2972 wndclassc.hIconSm = 0;
2974 callClassAtom = ::RegisterClassEx(&wndclassc);
2975 result = 0 != callClassAtom;
2978 return result;
2981 bool ScintillaWin::Unregister() {
2982 bool result = true;
2983 if (0 != scintillaClassAtom) {
2984 if (::UnregisterClass(MAKEINTATOM(scintillaClassAtom), hInstance) == 0) {
2985 result = false;
2987 scintillaClassAtom = 0;
2989 if (0 != callClassAtom) {
2990 if (::UnregisterClass(MAKEINTATOM(callClassAtom), hInstance) == 0) {
2991 result = false;
2993 callClassAtom = 0;
2995 return result;
2998 bool ScintillaWin::HasCaretSizeChanged() const {
2999 if (
3000 ( (0 != vs.caretWidth) && (sysCaretWidth != vs.caretWidth) )
3001 || ((0 != vs.lineHeight) && (sysCaretHeight != vs.lineHeight))
3003 return true;
3005 return false;
3008 BOOL ScintillaWin::CreateSystemCaret() {
3009 sysCaretWidth = vs.caretWidth;
3010 if (0 == sysCaretWidth) {
3011 sysCaretWidth = 1;
3013 sysCaretHeight = vs.lineHeight;
3014 int bitmapSize = (((sysCaretWidth + 15) & ~15) >> 3) *
3015 sysCaretHeight;
3016 std::vector<char> bits(bitmapSize);
3017 sysCaretBitmap = ::CreateBitmap(sysCaretWidth, sysCaretHeight, 1,
3018 1, reinterpret_cast<BYTE *>(&bits[0]));
3019 BOOL retval = ::CreateCaret(
3020 MainHWND(), sysCaretBitmap,
3021 sysCaretWidth, sysCaretHeight);
3022 if (technology == SC_TECHNOLOGY_DEFAULT) {
3023 // System caret interferes with Direct2D drawing so only show it for GDI.
3024 ::ShowCaret(MainHWND());
3026 return retval;
3029 BOOL ScintillaWin::DestroySystemCaret() {
3030 ::HideCaret(MainHWND());
3031 BOOL retval = ::DestroyCaret();
3032 if (sysCaretBitmap) {
3033 ::DeleteObject(sysCaretBitmap);
3034 sysCaretBitmap = 0;
3036 return retval;
3039 sptr_t PASCAL ScintillaWin::CTWndProc(
3040 HWND hWnd, UINT iMessage, WPARAM wParam, sptr_t lParam) {
3041 // Find C++ object associated with window.
3042 ScintillaWin *sciThis = reinterpret_cast<ScintillaWin *>(PointerFromWindow(hWnd));
3043 try {
3044 // ctp will be zero if WM_CREATE not seen yet
3045 if (sciThis == 0) {
3046 if (iMessage == WM_CREATE) {
3047 // Associate CallTip object with window
3048 CREATESTRUCT *pCreate = reinterpret_cast<CREATESTRUCT *>(lParam);
3049 SetWindowPointer(hWnd, pCreate->lpCreateParams);
3050 return 0;
3051 } else {
3052 return ::DefWindowProc(hWnd, iMessage, wParam, lParam);
3054 } else {
3055 if (iMessage == WM_NCDESTROY) {
3056 ::SetWindowLong(hWnd, 0, 0);
3057 return ::DefWindowProc(hWnd, iMessage, wParam, lParam);
3058 } else if (iMessage == WM_PAINT) {
3059 PAINTSTRUCT ps;
3060 ::BeginPaint(hWnd, &ps);
3061 Surface *surfaceWindow = Surface::Allocate(sciThis->technology);
3062 if (surfaceWindow) {
3063 #if defined(USE_D2D)
3064 ID2D1HwndRenderTarget *pCTRenderTarget = 0;
3065 #endif
3066 RECT rc;
3067 GetClientRect(hWnd, &rc);
3068 // Create a Direct2D render target.
3069 if (sciThis->technology == SC_TECHNOLOGY_DEFAULT) {
3070 surfaceWindow->Init(ps.hdc, hWnd);
3071 } else {
3072 #if defined(USE_D2D)
3073 D2D1_HWND_RENDER_TARGET_PROPERTIES dhrtp;
3074 dhrtp.hwnd = hWnd;
3075 dhrtp.pixelSize = D2D1::SizeU(rc.right - rc.left, rc.bottom - rc.top);
3076 dhrtp.presentOptions = (sciThis->technology == SC_TECHNOLOGY_DIRECTWRITERETAIN) ?
3077 D2D1_PRESENT_OPTIONS_RETAIN_CONTENTS : D2D1_PRESENT_OPTIONS_NONE;
3079 D2D1_RENDER_TARGET_PROPERTIES drtp;
3080 drtp.type = D2D1_RENDER_TARGET_TYPE_DEFAULT;
3081 drtp.pixelFormat.format = DXGI_FORMAT_UNKNOWN;
3082 drtp.pixelFormat.alphaMode = D2D1_ALPHA_MODE_UNKNOWN;
3083 drtp.dpiX = 96.0;
3084 drtp.dpiY = 96.0;
3085 drtp.usage = D2D1_RENDER_TARGET_USAGE_NONE;
3086 drtp.minLevel = D2D1_FEATURE_LEVEL_DEFAULT;
3088 if (!SUCCEEDED(pD2DFactory->CreateHwndRenderTarget(drtp, dhrtp, &pCTRenderTarget))) {
3089 surfaceWindow->Release();
3090 delete surfaceWindow;
3091 ::EndPaint(hWnd, &ps);
3092 return 0;
3094 surfaceWindow->Init(pCTRenderTarget, hWnd);
3095 pCTRenderTarget->BeginDraw();
3096 #endif
3098 surfaceWindow->SetUnicodeMode(SC_CP_UTF8 == sciThis->ct.codePage);
3099 surfaceWindow->SetDBCSMode(sciThis->ct.codePage);
3100 sciThis->ct.PaintCT(surfaceWindow);
3101 #if defined(USE_D2D)
3102 if (pCTRenderTarget)
3103 pCTRenderTarget->EndDraw();
3104 #endif
3105 surfaceWindow->Release();
3106 delete surfaceWindow;
3107 #if defined(USE_D2D)
3108 if (pCTRenderTarget)
3109 pCTRenderTarget->Release();
3110 #endif
3112 ::EndPaint(hWnd, &ps);
3113 return 0;
3114 } else if ((iMessage == WM_NCLBUTTONDOWN) || (iMessage == WM_NCLBUTTONDBLCLK)) {
3115 POINT pt;
3116 pt.x = static_cast<short>(LOWORD(lParam));
3117 pt.y = static_cast<short>(HIWORD(lParam));
3118 ScreenToClient(hWnd, &pt);
3119 sciThis->ct.MouseClick(PointFromPOINT(pt));
3120 sciThis->CallTipClick();
3121 return 0;
3122 } else if (iMessage == WM_LBUTTONDOWN) {
3123 // This does not fire due to the hit test code
3124 sciThis->ct.MouseClick(Point::FromLong(static_cast<long>(lParam)));
3125 sciThis->CallTipClick();
3126 return 0;
3127 } else if (iMessage == WM_SETCURSOR) {
3128 ::SetCursor(::LoadCursor(NULL, IDC_ARROW));
3129 return 0;
3130 } else if (iMessage == WM_NCHITTEST) {
3131 return HTCAPTION;
3132 } else {
3133 return ::DefWindowProc(hWnd, iMessage, wParam, lParam);
3136 } catch (...) {
3137 sciThis->errorStatus = SC_STATUS_FAILURE;
3139 return ::DefWindowProc(hWnd, iMessage, wParam, lParam);
3142 sptr_t ScintillaWin::DirectFunction(
3143 sptr_t ptr, UINT iMessage, uptr_t wParam, sptr_t lParam) {
3144 PLATFORM_ASSERT(::GetCurrentThreadId() == ::GetWindowThreadProcessId(reinterpret_cast<ScintillaWin *>(ptr)->MainHWND(), NULL));
3145 return reinterpret_cast<ScintillaWin *>(ptr)->WndProc(iMessage, wParam, lParam);
3148 extern "C"
3149 #ifndef STATIC_BUILD
3150 __declspec(dllexport)
3151 #endif
3152 sptr_t __stdcall Scintilla_DirectFunction(
3153 ScintillaWin *sci, UINT iMessage, uptr_t wParam, sptr_t lParam) {
3154 return sci->WndProc(iMessage, wParam, lParam);
3157 sptr_t PASCAL ScintillaWin::SWndProc(
3158 HWND hWnd, UINT iMessage, WPARAM wParam, sptr_t lParam) {
3159 //Platform::DebugPrintf("S W:%x M:%x WP:%x L:%x\n", hWnd, iMessage, wParam, lParam);
3161 // Find C++ object associated with window.
3162 ScintillaWin *sci = reinterpret_cast<ScintillaWin *>(PointerFromWindow(hWnd));
3163 // sci will be zero if WM_CREATE not seen yet
3164 if (sci == 0) {
3165 try {
3166 if (iMessage == WM_CREATE) {
3167 // Create C++ object associated with window
3168 sci = new ScintillaWin(hWnd);
3169 SetWindowPointer(hWnd, sci);
3170 return sci->WndProc(iMessage, wParam, lParam);
3172 } catch (...) {
3174 return ::DefWindowProc(hWnd, iMessage, wParam, lParam);
3175 } else {
3176 if (iMessage == WM_NCDESTROY) {
3177 try {
3178 sci->Finalise();
3179 delete sci;
3180 } catch (...) {
3182 ::SetWindowLong(hWnd, 0, 0);
3183 return ::DefWindowProc(hWnd, iMessage, wParam, lParam);
3184 } else {
3185 return sci->WndProc(iMessage, wParam, lParam);
3190 // This function is externally visible so it can be called from container when building statically.
3191 // Must be called once only.
3192 int Scintilla_RegisterClasses(void *hInstance) {
3193 Platform_Initialise(hInstance);
3194 bool result = ScintillaWin::Register(reinterpret_cast<HINSTANCE>(hInstance));
3195 #ifdef SCI_LEXER
3196 Scintilla_LinkLexers();
3197 #endif
3198 return result;
3201 static int ResourcesRelease(bool fromDllMain) {
3202 bool result = ScintillaWin::Unregister();
3203 if (commctrl32) {
3204 FreeLibrary(commctrl32);
3205 commctrl32 = NULL;
3207 Platform_Finalise(fromDllMain);
3208 return result;
3211 // This function is externally visible so it can be called from container when building statically.
3212 int Scintilla_ReleaseResources() {
3213 return ResourcesRelease(false);
3216 #ifndef STATIC_BUILD
3217 extern "C" int APIENTRY DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpvReserved) {
3218 //Platform::DebugPrintf("Scintilla::DllMain %d %d\n", hInstance, dwReason);
3219 if (dwReason == DLL_PROCESS_ATTACH) {
3220 if (!Scintilla_RegisterClasses(hInstance))
3221 return FALSE;
3222 } else if (dwReason == DLL_PROCESS_DETACH) {
3223 if (lpvReserved == NULL) {
3224 ResourcesRelease(true);
3227 return TRUE;
3229 #endif