Update Scintilla to version 3.5.4
[TortoiseGit.git] / ext / scintilla / win32 / ScintillaWin.cxx
blob69e6407a165604f93d7bfa58700e3159ee75e16f
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"
87 #include "HanjaDic.h"
89 #ifndef SPI_GETWHEELSCROLLLINES
90 #define SPI_GETWHEELSCROLLLINES 104
91 #endif
93 #ifndef WM_UNICHAR
94 #define WM_UNICHAR 0x0109
95 #endif
97 #ifndef UNICODE_NOCHAR
98 #define UNICODE_NOCHAR 0xFFFF
99 #endif
101 #ifndef MK_ALT
102 #define MK_ALT 32
103 #endif
105 #define SC_WIN_IDLE 5001
107 #define SC_INDICATOR_INPUT INDIC_IME
108 #define SC_INDICATOR_TARGET INDIC_IME+1
109 #define SC_INDICATOR_CONVERTED INDIC_IME+2
110 #define SC_INDICATOR_UNKNOWN INDIC_IME_MAX
112 typedef BOOL (WINAPI *TrackMouseEventSig)(LPTRACKMOUSEEVENT);
113 typedef UINT_PTR (WINAPI *SetCoalescableTimerSig)(HWND hwnd, UINT_PTR nIDEvent,
114 UINT uElapse, TIMERPROC lpTimerFunc, ULONG uToleranceDelay);
116 // GCC has trouble with the standard COM ABI so do it the old C way with explicit vtables.
118 const TCHAR scintillaClassName[] = TEXT("Scintilla");
119 const TCHAR callClassName[] = TEXT("CallTip");
121 #ifdef SCI_NAMESPACE
122 using namespace Scintilla;
123 #endif
125 static void *PointerFromWindow(HWND hWnd) {
126 return reinterpret_cast<void *>(::GetWindowLongPtr(hWnd, 0));
129 static void SetWindowPointer(HWND hWnd, void *ptr) {
130 ::SetWindowLongPtr(hWnd, 0, reinterpret_cast<LONG_PTR>(ptr));
133 static void SetWindowID(HWND hWnd, int identifier) {
134 ::SetWindowLongPtr(hWnd, GWLP_ID, identifier);
137 static Point PointFromPOINT(POINT pt) {
138 return Point::FromInts(pt.x, pt.y);
141 class ScintillaWin; // Forward declaration for COM interface subobjects
143 typedef void VFunction(void);
145 static HMODULE commctrl32 = 0;
149 class FormatEnumerator {
150 public:
151 VFunction **vtbl;
152 int ref;
153 unsigned int pos;
154 std::vector<CLIPFORMAT> formats;
155 FormatEnumerator(int pos_, CLIPFORMAT formats_[], size_t formatsLen_);
160 class DropSource {
161 public:
162 VFunction **vtbl;
163 ScintillaWin *sci;
164 DropSource();
169 class DataObject {
170 public:
171 VFunction **vtbl;
172 ScintillaWin *sci;
173 DataObject();
178 class DropTarget {
179 public:
180 VFunction **vtbl;
181 ScintillaWin *sci;
182 DropTarget();
187 class ScintillaWin :
188 public ScintillaBase {
190 bool lastKeyDownConsumed;
192 bool capturedMouse;
193 bool trackedMouseLeave;
194 TrackMouseEventSig TrackMouseEventFn;
195 SetCoalescableTimerSig SetCoalescableTimerFn;
197 unsigned int linesPerScroll; ///< Intellimouse support
198 int wheelDelta; ///< Wheel delta from roll
200 HRGN hRgnUpdate;
202 bool hasOKText;
204 CLIPFORMAT cfColumnSelect;
205 CLIPFORMAT cfBorlandIDEBlockType;
206 CLIPFORMAT cfLineSelect;
207 CLIPFORMAT cfVSLineTag;
209 HRESULT hrOle;
210 DropSource ds;
211 DataObject dob;
212 DropTarget dt;
214 static HINSTANCE hInstance;
215 static ATOM scintillaClassAtom;
216 static ATOM callClassAtom;
218 #if defined(USE_D2D)
219 ID2D1RenderTarget *pRenderTarget;
220 bool renderTargetValid;
221 #endif
223 explicit ScintillaWin(HWND hwnd);
224 ScintillaWin(const ScintillaWin &);
225 virtual ~ScintillaWin();
226 ScintillaWin &operator=(const ScintillaWin &);
228 virtual void Initialise();
229 virtual void Finalise();
230 #if defined(USE_D2D)
231 void EnsureRenderTarget(HDC hdc);
232 void DropRenderTarget();
233 #endif
234 HWND MainHWND();
236 static sptr_t DirectFunction(
237 sptr_t ptr, UINT iMessage, uptr_t wParam, sptr_t lParam);
238 static sptr_t PASCAL SWndProc(
239 HWND hWnd, UINT iMessage, WPARAM wParam, sptr_t lParam);
240 static sptr_t PASCAL CTWndProc(
241 HWND hWnd, UINT iMessage, WPARAM wParam, sptr_t lParam);
243 enum { invalidTimerID, standardTimerID, idleTimerID, fineTimerStart };
245 virtual bool DragThreshold(Point ptStart, Point ptNow);
246 virtual void StartDrag();
247 sptr_t WndPaint(uptr_t wParam);
249 sptr_t HandleCompositionWindowed(uptr_t wParam, sptr_t lParam);
250 sptr_t HandleCompositionInline(uptr_t wParam, sptr_t lParam);
251 static bool KoreanIME();
252 void MoveImeCarets(int offset);
253 void DrawImeIndicator(int indicator, int len);
254 void SetCandidateWindowPos();
255 void BytesToUniChar(const char *bytes, const int bytesLen, wchar_t *character, int &charsLen);
256 void UniCharToBytes(const wchar_t *character, const int charsLen, char *bytes, int &bytesLen);
257 void RangeToHangul(int uniStrLen);
258 void EscapeHanja();
259 void ToggleHanja();
261 UINT CodePageOfDocument();
262 virtual bool ValidCodePage(int codePage) const;
263 virtual sptr_t DefWndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam);
264 virtual bool SetIdle(bool on);
265 UINT_PTR timers[tickDwell+1];
266 virtual bool FineTickerAvailable();
267 virtual bool FineTickerRunning(TickReason reason);
268 virtual void FineTickerStart(TickReason reason, int millis, int tolerance);
269 virtual void FineTickerCancel(TickReason reason);
270 virtual void SetMouseCapture(bool on);
271 virtual bool HaveMouseCapture();
272 virtual void SetTrackMouseLeaveEvent(bool on);
273 virtual bool PaintContains(PRectangle rc);
274 virtual void ScrollText(int linesToMove);
275 virtual void UpdateSystemCaret();
276 virtual void SetVerticalScrollPos();
277 virtual void SetHorizontalScrollPos();
278 virtual bool ModifyScrollBars(int nMax, int nPage);
279 virtual void NotifyChange();
280 virtual void NotifyFocus(bool focus);
281 virtual void SetCtrlID(int identifier);
282 virtual int GetCtrlID();
283 virtual void NotifyParent(SCNotification scn);
284 virtual void NotifyDoubleClick(Point pt, int modifiers);
285 virtual CaseFolder *CaseFolderForEncoding();
286 virtual std::string CaseMapString(const std::string &s, int caseMapping);
287 virtual void Copy();
288 virtual void CopyAllowLine();
289 virtual bool CanPaste();
290 virtual void Paste();
291 virtual void CreateCallTipWindow(PRectangle rc);
292 virtual void AddToPopUp(const char *label, int cmd = 0, bool enabled = true);
293 virtual void ClaimSelection();
295 // DBCS
296 void ImeStartComposition();
297 void ImeEndComposition();
299 void AddCharBytes(char b0, char b1);
301 void GetIntelliMouseParameters();
302 virtual void CopyToClipboard(const SelectionText &selectedText);
303 void ScrollMessage(WPARAM wParam);
304 void HorizontalScrollMessage(WPARAM wParam);
305 void FullPaint();
306 void FullPaintDC(HDC dc);
307 bool IsCompatibleDC(HDC dc);
308 DWORD EffectFromState(DWORD grfKeyState) const;
310 virtual int SetScrollInfo(int nBar, LPCSCROLLINFO lpsi, BOOL bRedraw);
311 virtual bool GetScrollInfo(int nBar, LPSCROLLINFO lpsi);
312 void ChangeScrollPos(int barType, int pos);
313 sptr_t GetTextLength();
314 sptr_t GetText(uptr_t wParam, sptr_t lParam);
316 public:
317 // Public for benefit of Scintilla_DirectFunction
318 virtual sptr_t WndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam);
320 /// Implement IUnknown
321 STDMETHODIMP QueryInterface(REFIID riid, PVOID *ppv);
322 STDMETHODIMP_(ULONG)AddRef();
323 STDMETHODIMP_(ULONG)Release();
325 /// Implement IDropTarget
326 STDMETHODIMP DragEnter(LPDATAOBJECT pIDataSource, DWORD grfKeyState,
327 POINTL pt, PDWORD pdwEffect);
328 STDMETHODIMP DragOver(DWORD grfKeyState, POINTL pt, PDWORD pdwEffect);
329 STDMETHODIMP DragLeave();
330 STDMETHODIMP Drop(LPDATAOBJECT pIDataSource, DWORD grfKeyState,
331 POINTL pt, PDWORD pdwEffect);
333 /// Implement important part of IDataObject
334 STDMETHODIMP GetData(FORMATETC *pFEIn, STGMEDIUM *pSTM);
336 static bool Register(HINSTANCE hInstance_);
337 static bool Unregister();
339 friend class DropSource;
340 friend class DataObject;
341 friend class DropTarget;
342 bool DragIsRectangularOK(CLIPFORMAT fmt) const {
343 return drag.rectangular && (fmt == cfColumnSelect);
346 private:
347 // For use in creating a system caret
348 bool HasCaretSizeChanged() const;
349 BOOL CreateSystemCaret();
350 BOOL DestroySystemCaret();
351 HBITMAP sysCaretBitmap;
352 int sysCaretWidth;
353 int sysCaretHeight;
354 bool keysAlwaysUnicode;
357 HINSTANCE ScintillaWin::hInstance = 0;
358 ATOM ScintillaWin::scintillaClassAtom = 0;
359 ATOM ScintillaWin::callClassAtom = 0;
361 ScintillaWin::ScintillaWin(HWND hwnd) {
363 lastKeyDownConsumed = false;
365 capturedMouse = false;
366 trackedMouseLeave = false;
367 TrackMouseEventFn = 0;
368 SetCoalescableTimerFn = 0;
370 linesPerScroll = 0;
371 wheelDelta = 0; // Wheel delta from roll
373 hRgnUpdate = 0;
375 hasOKText = false;
377 // There does not seem to be a real standard for indicating that the clipboard
378 // contains a rectangular selection, so copy Developer Studio and Borland Delphi.
379 cfColumnSelect = static_cast<CLIPFORMAT>(
380 ::RegisterClipboardFormat(TEXT("MSDEVColumnSelect")));
381 cfBorlandIDEBlockType = static_cast<CLIPFORMAT>(
382 ::RegisterClipboardFormat(TEXT("Borland IDE Block Type")));
384 // Likewise for line-copy (copies a full line when no text is selected)
385 cfLineSelect = static_cast<CLIPFORMAT>(
386 ::RegisterClipboardFormat(TEXT("MSDEVLineSelect")));
387 cfVSLineTag = static_cast<CLIPFORMAT>(
388 ::RegisterClipboardFormat(TEXT("VisualStudioEditorOperationsLineCutCopyClipboardTag")));
389 hrOle = E_FAIL;
391 wMain = hwnd;
393 dob.sci = this;
394 ds.sci = this;
395 dt.sci = this;
397 sysCaretBitmap = 0;
398 sysCaretWidth = 0;
399 sysCaretHeight = 0;
401 #if defined(USE_D2D)
402 pRenderTarget = 0;
403 renderTargetValid = true;
404 #endif
406 keysAlwaysUnicode = false;
408 caret.period = ::GetCaretBlinkTime();
409 if (caret.period < 0)
410 caret.period = 0;
412 Initialise();
415 ScintillaWin::~ScintillaWin() {}
417 void ScintillaWin::Initialise() {
418 // Initialize COM. If the app has already done this it will have
419 // no effect. If the app hasnt, we really shouldnt ask them to call
420 // it just so this internal feature works.
421 hrOle = ::OleInitialize(NULL);
423 // Find TrackMouseEvent which is available on Windows > 95
424 HMODULE user32 = ::GetModuleHandle(TEXT("user32.dll"));
425 if (user32) {
426 TrackMouseEventFn = (TrackMouseEventSig)::GetProcAddress(user32, "TrackMouseEvent");
427 SetCoalescableTimerFn = (SetCoalescableTimerSig)::GetProcAddress(user32, "SetCoalescableTimer");
429 if (TrackMouseEventFn == NULL) {
430 // Windows 95 has an emulation in comctl32.dll:_TrackMouseEvent
431 if (!commctrl32)
432 commctrl32 = ::LoadLibrary(TEXT("comctl32.dll"));
433 if (commctrl32 != NULL) {
434 TrackMouseEventFn = (TrackMouseEventSig)
435 ::GetProcAddress(commctrl32, "_TrackMouseEvent");
438 for (TickReason tr = tickCaret; tr <= tickDwell; tr = static_cast<TickReason>(tr + 1)) {
439 timers[tr] = 0;
441 vs.indicators[SC_INDICATOR_UNKNOWN] = Indicator(INDIC_HIDDEN, ColourDesired(0, 0, 0xff));
442 vs.indicators[SC_INDICATOR_INPUT] = Indicator(INDIC_DOTS, ColourDesired(0, 0, 0xff));
443 vs.indicators[SC_INDICATOR_CONVERTED] = Indicator(INDIC_COMPOSITIONTHICK, ColourDesired(0, 0, 0xff));
444 vs.indicators[SC_INDICATOR_TARGET] = Indicator(INDIC_STRAIGHTBOX, ColourDesired(0, 0, 0xff));
447 void ScintillaWin::Finalise() {
448 ScintillaBase::Finalise();
449 for (TickReason tr = tickCaret; tr <= tickDwell; tr = static_cast<TickReason>(tr + 1)) {
450 FineTickerCancel(tr);
452 SetIdle(false);
453 #if defined(USE_D2D)
454 DropRenderTarget();
455 #endif
456 ::RevokeDragDrop(MainHWND());
457 if (SUCCEEDED(hrOle)) {
458 ::OleUninitialize();
462 #if defined(USE_D2D)
464 void ScintillaWin::EnsureRenderTarget(HDC hdc) {
465 if (!renderTargetValid) {
466 DropRenderTarget();
467 renderTargetValid = true;
469 if (pD2DFactory && !pRenderTarget) {
470 RECT rc;
471 HWND hw = MainHWND();
472 GetClientRect(hw, &rc);
474 D2D1_SIZE_U size = D2D1::SizeU(rc.right - rc.left, rc.bottom - rc.top);
476 // Create a Direct2D render target.
477 #if 1
478 D2D1_RENDER_TARGET_PROPERTIES drtp;
479 drtp.type = D2D1_RENDER_TARGET_TYPE_DEFAULT;
480 drtp.pixelFormat.format = DXGI_FORMAT_UNKNOWN;
481 drtp.pixelFormat.alphaMode = D2D1_ALPHA_MODE_UNKNOWN;
482 drtp.dpiX = 96.0;
483 drtp.dpiY = 96.0;
484 drtp.usage = D2D1_RENDER_TARGET_USAGE_NONE;
485 drtp.minLevel = D2D1_FEATURE_LEVEL_DEFAULT;
487 if (technology == SC_TECHNOLOGY_DIRECTWRITEDC) {
488 // Explicit pixel format needed.
489 drtp.pixelFormat = D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM,
490 D2D1_ALPHA_MODE_IGNORE);
492 ID2D1DCRenderTarget *pDCRT = NULL;
493 HRESULT hr = pD2DFactory->CreateDCRenderTarget(&drtp, &pDCRT);
494 if (SUCCEEDED(hr)) {
495 pRenderTarget = pDCRT;
496 } else {
497 Platform::DebugPrintf("Failed CreateDCRenderTarget 0x%x\n", hr);
498 pRenderTarget = NULL;
501 } else {
502 D2D1_HWND_RENDER_TARGET_PROPERTIES dhrtp;
503 dhrtp.hwnd = hw;
504 dhrtp.pixelSize = size;
505 dhrtp.presentOptions = (technology == SC_TECHNOLOGY_DIRECTWRITERETAIN) ?
506 D2D1_PRESENT_OPTIONS_RETAIN_CONTENTS : D2D1_PRESENT_OPTIONS_NONE;
508 ID2D1HwndRenderTarget *pHwndRenderTarget = NULL;
509 HRESULT hr = pD2DFactory->CreateHwndRenderTarget(drtp, dhrtp, &pHwndRenderTarget);
510 if (SUCCEEDED(hr)) {
511 pRenderTarget = pHwndRenderTarget;
512 } else {
513 Platform::DebugPrintf("Failed CreateHwndRenderTarget 0x%x\n", hr);
514 pRenderTarget = NULL;
517 #else
518 pD2DFactory->CreateHwndRenderTarget(
519 D2D1::RenderTargetProperties(
520 D2D1_RENDER_TARGET_TYPE_DEFAULT ,
521 D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_PREMULTIPLIED),
522 96.0f, 96.0f, D2D1_RENDER_TARGET_USAGE_NONE, D2D1_FEATURE_LEVEL_DEFAULT),
523 D2D1::HwndRenderTargetProperties(hw, size),
524 &pRenderTarget);
525 #endif
526 // Pixmaps were created to be compatible with previous render target so
527 // need to be recreated.
528 DropGraphics(false);
531 if ((technology == SC_TECHNOLOGY_DIRECTWRITEDC) && pRenderTarget) {
532 RECT rcWindow;
533 GetClientRect(MainHWND(), &rcWindow);
534 HRESULT hr = static_cast<ID2D1DCRenderTarget*>(pRenderTarget)->BindDC(hdc, &rcWindow);
535 if (FAILED(hr)) {
536 Platform::DebugPrintf("BindDC failed 0x%x\n", hr);
537 DropRenderTarget();
542 void ScintillaWin::DropRenderTarget() {
543 if (pRenderTarget) {
544 pRenderTarget->Release();
545 pRenderTarget = 0;
549 #endif
551 HWND ScintillaWin::MainHWND() {
552 return reinterpret_cast<HWND>(wMain.GetID());
555 bool ScintillaWin::DragThreshold(Point ptStart, Point ptNow) {
556 int xMove = static_cast<int>(abs(ptStart.x - ptNow.x));
557 int yMove = static_cast<int>(abs(ptStart.y - ptNow.y));
558 return (xMove > ::GetSystemMetrics(SM_CXDRAG)) ||
559 (yMove > ::GetSystemMetrics(SM_CYDRAG));
562 void ScintillaWin::StartDrag() {
563 inDragDrop = ddDragging;
564 DWORD dwEffect = 0;
565 dropWentOutside = true;
566 IDataObject *pDataObject = reinterpret_cast<IDataObject *>(&dob);
567 IDropSource *pDropSource = reinterpret_cast<IDropSource *>(&ds);
568 //Platform::DebugPrintf("About to DoDragDrop %x %x\n", pDataObject, pDropSource);
569 HRESULT hr = ::DoDragDrop(
570 pDataObject,
571 pDropSource,
572 DROPEFFECT_COPY | DROPEFFECT_MOVE, &dwEffect);
573 //Platform::DebugPrintf("DoDragDrop = %x\n", hr);
574 if (SUCCEEDED(hr)) {
575 if ((hr == DRAGDROP_S_DROP) && (dwEffect == DROPEFFECT_MOVE) && dropWentOutside) {
576 // Remove dragged out text
577 ClearSelection();
580 inDragDrop = ddNone;
581 SetDragPosition(SelectionPosition(invalidPosition));
584 // Avoid warnings everywhere for old style casts by concentrating them here
585 static WORD LoWord(uptr_t l) {
586 return LOWORD(l);
589 static WORD HiWord(uptr_t l) {
590 return HIWORD(l);
593 static int InputCodePage() {
594 HKL inputLocale = ::GetKeyboardLayout(0);
595 LANGID inputLang = LOWORD(inputLocale);
596 char sCodePage[10];
597 int res = ::GetLocaleInfoA(MAKELCID(inputLang, SORT_DEFAULT),
598 LOCALE_IDEFAULTANSICODEPAGE, sCodePage, sizeof(sCodePage));
599 if (!res)
600 return 0;
601 return atoi(sCodePage);
604 /** Map the key codes to their equivalent SCK_ form. */
605 static int KeyTranslate(int keyIn) {
606 //PLATFORM_ASSERT(!keyIn);
607 switch (keyIn) {
608 case VK_DOWN: return SCK_DOWN;
609 case VK_UP: return SCK_UP;
610 case VK_LEFT: return SCK_LEFT;
611 case VK_RIGHT: return SCK_RIGHT;
612 case VK_HOME: return SCK_HOME;
613 case VK_END: return SCK_END;
614 case VK_PRIOR: return SCK_PRIOR;
615 case VK_NEXT: return SCK_NEXT;
616 case VK_DELETE: return SCK_DELETE;
617 case VK_INSERT: return SCK_INSERT;
618 case VK_ESCAPE: return SCK_ESCAPE;
619 case VK_BACK: return SCK_BACK;
620 case VK_TAB: return SCK_TAB;
621 case VK_RETURN: return SCK_RETURN;
622 case VK_ADD: return SCK_ADD;
623 case VK_SUBTRACT: return SCK_SUBTRACT;
624 case VK_DIVIDE: return SCK_DIVIDE;
625 case VK_LWIN: return SCK_WIN;
626 case VK_RWIN: return SCK_RWIN;
627 case VK_APPS: return SCK_MENU;
628 case VK_OEM_2: return '/';
629 case VK_OEM_3: return '`';
630 case VK_OEM_4: return '[';
631 case VK_OEM_5: return '\\';
632 case VK_OEM_6: return ']';
633 default: return keyIn;
637 static bool BoundsContains(PRectangle rcBounds, HRGN hRgnBounds, PRectangle rcCheck) {
638 bool contains = true;
639 if (!rcCheck.Empty()) {
640 if (!rcBounds.Contains(rcCheck)) {
641 contains = false;
642 } else if (hRgnBounds) {
643 // In bounding rectangle so check more accurately using region
644 HRGN hRgnCheck = ::CreateRectRgn(static_cast<int>(rcCheck.left), static_cast<int>(rcCheck.top),
645 static_cast<int>(rcCheck.right), static_cast<int>(rcCheck.bottom));
646 if (hRgnCheck) {
647 HRGN hRgnDifference = ::CreateRectRgn(0, 0, 0, 0);
648 if (hRgnDifference) {
649 int combination = ::CombineRgn(hRgnDifference, hRgnCheck, hRgnBounds, RGN_DIFF);
650 if (combination != NULLREGION) {
651 contains = false;
653 ::DeleteRgn(hRgnDifference);
655 ::DeleteRgn(hRgnCheck);
659 return contains;
662 LRESULT ScintillaWin::WndPaint(uptr_t wParam) {
663 //ElapsedTime et;
665 // Redirect assertions to debug output and save current state
666 bool assertsPopup = Platform::ShowAssertionPopUps(false);
667 paintState = painting;
668 PAINTSTRUCT ps;
669 PAINTSTRUCT *pps;
671 bool IsOcxCtrl = (wParam != 0); // if wParam != 0, it contains
672 // a PAINSTRUCT* from the OCX
673 // Removed since this interferes with reporting other assertions as it occurs repeatedly
674 //PLATFORM_ASSERT(hRgnUpdate == NULL);
675 hRgnUpdate = ::CreateRectRgn(0, 0, 0, 0);
676 if (IsOcxCtrl) {
677 pps = reinterpret_cast<PAINTSTRUCT*>(wParam);
678 } else {
679 ::GetUpdateRgn(MainHWND(), hRgnUpdate, FALSE);
680 pps = &ps;
681 ::BeginPaint(MainHWND(), pps);
683 rcPaint = PRectangle::FromInts(pps->rcPaint.left, pps->rcPaint.top, pps->rcPaint.right, pps->rcPaint.bottom);
684 PRectangle rcClient = GetClientRectangle();
685 paintingAllText = BoundsContains(rcPaint, hRgnUpdate, rcClient);
686 if (technology == SC_TECHNOLOGY_DEFAULT) {
687 AutoSurface surfaceWindow(pps->hdc, this);
688 if (surfaceWindow) {
689 Paint(surfaceWindow, rcPaint);
690 surfaceWindow->Release();
692 } else {
693 #if defined(USE_D2D)
694 EnsureRenderTarget(pps->hdc);
695 AutoSurface surfaceWindow(pRenderTarget, this);
696 if (surfaceWindow) {
697 pRenderTarget->BeginDraw();
698 Paint(surfaceWindow, rcPaint);
699 surfaceWindow->Release();
700 HRESULT hr = pRenderTarget->EndDraw();
701 if (hr == D2DERR_RECREATE_TARGET) {
702 DropRenderTarget();
703 paintState = paintAbandoned;
706 #endif
708 if (hRgnUpdate) {
709 ::DeleteRgn(hRgnUpdate);
710 hRgnUpdate = 0;
713 if (!IsOcxCtrl)
714 ::EndPaint(MainHWND(), pps);
715 if (paintState == paintAbandoned) {
716 // Painting area was insufficient to cover new styling or brace highlight positions
717 if (IsOcxCtrl) {
718 FullPaintDC(pps->hdc);
719 } else {
720 FullPaint();
723 paintState = notPainting;
725 // Restore debug output state
726 Platform::ShowAssertionPopUps(assertsPopup);
728 //Platform::DebugPrintf("Paint took %g\n", et.Duration());
729 return 0l;
732 sptr_t ScintillaWin::HandleCompositionWindowed(uptr_t wParam, sptr_t lParam) {
733 if (lParam & GCS_RESULTSTR) {
734 HIMC hIMC = ::ImmGetContext(MainHWND());
735 if (hIMC) {
736 wchar_t wcs[maxLenInputIME];
737 LONG bytes = ::ImmGetCompositionStringW(hIMC,
738 GCS_RESULTSTR, wcs, (maxLenInputIME-1)*2);
739 int wides = bytes / 2;
740 if (IsUnicodeMode()) {
741 char utfval[maxLenInputIME * 3];
742 unsigned int len = UTF8Length(wcs, wides);
743 UTF8FromUTF16(wcs, wides, utfval, len);
744 utfval[len] = '\0';
745 AddCharUTF(utfval, len);
746 } else {
747 char dbcsval[maxLenInputIME * 2];
748 int size = ::WideCharToMultiByte(InputCodePage(),
749 0, wcs, wides, dbcsval, sizeof(dbcsval) - 1, 0, 0);
750 for (int i=0; i<size; i++) {
751 AddChar(dbcsval[i]);
754 // Set new position after converted
755 Point pos = PointMainCaret();
756 COMPOSITIONFORM CompForm;
757 CompForm.dwStyle = CFS_POINT;
758 CompForm.ptCurrentPos.x = static_cast<int>(pos.x);
759 CompForm.ptCurrentPos.y = static_cast<int>(pos.y);
760 ::ImmSetCompositionWindow(hIMC, &CompForm);
761 ::ImmReleaseContext(MainHWND(), hIMC);
763 return 0;
765 return ::DefWindowProc(MainHWND(), WM_IME_COMPOSITION, wParam, lParam);
768 bool ScintillaWin::KoreanIME() {
769 const int codePage = InputCodePage();
770 return codePage == 949 || codePage == 1361;
773 void ScintillaWin::MoveImeCarets(int offset) {
774 // Move carets relatively by bytes.
775 for (size_t r=0; r<sel.Count(); r++) {
776 int positionInsert = sel.Range(r).Start().Position();
777 sel.Range(r).caret.SetPosition(positionInsert + offset);
778 sel.Range(r).anchor.SetPosition(positionInsert + offset);
782 void ScintillaWin::DrawImeIndicator(int indicator, int len) {
783 // Emulate the visual style of IME characters with indicators.
784 // Draw an indicator on the character before caret by the character bytes of len
785 // so it should be called after addCharUTF().
786 // It does not affect caret positions.
787 if (indicator < 8 || indicator > INDIC_MAX) {
788 return;
790 pdoc->decorations.SetCurrentIndicator(indicator);
791 for (size_t r=0; r<sel.Count(); r++) {
792 int positionInsert = sel.Range(r).Start().Position();
793 pdoc->DecorationFillRange(positionInsert - len, 1, len);
797 void ScintillaWin::SetCandidateWindowPos() {
798 HIMC hIMC = ::ImmGetContext(MainHWND());
799 if (hIMC) {
800 Point pos = PointMainCaret();
801 CANDIDATEFORM CandForm;
802 CandForm.dwIndex = 0;
803 CandForm.dwStyle = CFS_CANDIDATEPOS;
804 CandForm.ptCurrentPos.x = static_cast<int>(pos.x);
805 CandForm.ptCurrentPos.y = static_cast<int>(pos.y + vs.lineHeight);
806 ::ImmSetCandidateWindow(hIMC, &CandForm);
807 ::ImmReleaseContext(MainHWND(), hIMC);
811 void ScintillaWin::BytesToUniChar(const char *bytes, const int bytesLen, wchar_t *characters, int &charsLen) {
812 // Return results over characters and charsLen.
813 if (IsUnicodeMode()) {
814 charsLen = ::MultiByteToWideChar(SC_CP_UTF8, 0, bytes, bytesLen, NULL, 0);
815 ::MultiByteToWideChar(SC_CP_UTF8, 0, bytes, bytesLen, characters, charsLen);
816 } else {
817 charsLen = ::MultiByteToWideChar(CodePageOfDocument(), 0, bytes, bytesLen, NULL, 0);
818 ::MultiByteToWideChar(CodePageOfDocument(), 0, bytes, bytesLen, characters, charsLen);
822 void ScintillaWin::UniCharToBytes(const wchar_t *characters, const int charsLen, char *bytes, int &bytesLen) {
823 // Return results over bytes and bytesLen.
824 if (IsUnicodeMode()) {
825 bytesLen = UTF8Length(characters, charsLen);
826 UTF8FromUTF16(characters, charsLen, bytes, bytesLen);
827 bytes[bytesLen] = '\0';
828 } else {
829 bytesLen = ::WideCharToMultiByte(CodePageOfDocument(), 0,
830 characters, charsLen, bytes, bytesLen, 0, 0);
831 bytes[bytesLen] = '\0';
835 void ScintillaWin::RangeToHangul(int uniStrLen) {
836 // Convert every hanja to hangul from current position to the length uniStrLen.
837 // Even if not coverted, the caret should advance by 1 character.
838 pdoc->BeginUndoAction();
839 for (int i=0; i<uniStrLen; i++) {
840 unsigned int const safeLength = UTF8MaxBytes+1;
842 int currentPos = CurrentPosition();
843 int oneCharLen = pdoc->LenChar(currentPos);
845 if (oneCharLen > 1) {
846 wchar_t uniChar[safeLength] = { 0 };
847 int uniCharLen = 1;
848 char oneChar[safeLength] = "\0\0\0\0";
849 pdoc->GetCharRange(oneChar, currentPos, oneCharLen);
851 BytesToUniChar(oneChar, oneCharLen, uniChar, uniCharLen);
853 int hangul = HanjaDict::GetHangulOfHanja(uniChar[0]);
854 if (hangul > 0) {
855 uniChar[0] = static_cast<wchar_t>(hangul);
857 UniCharToBytes(uniChar, uniCharLen, oneChar, oneCharLen);
859 pdoc->DelChar(currentPos);
860 InsertPaste(oneChar, oneCharLen);
861 } else {
862 MoveImeCarets(oneCharLen);
864 } else {
865 MoveImeCarets(oneCharLen);
868 pdoc->EndUndoAction();
871 void ScintillaWin::EscapeHanja() {
872 // The candidate box pops up to user to select a hanja.
873 // It comes into WM_IME_COMPOSITION with GCS_RESULTSTR.
874 // The existing hangul or hanja is replaced with it.
875 if (sel.Count() > 1) {
876 return; // Do not allow multi carets.
878 int currentPos = CurrentPosition();
879 int oneCharLen = pdoc->LenChar(currentPos);
881 if (oneCharLen < 2) {
882 return; // No need to handle SBCS.
885 // ImmEscapeW() may overwrite uniChar[] with a null terminated string.
886 // So enlarge it enough to Maximum 4 as in UTF-8.
887 unsigned int const safeLength = UTF8MaxBytes+1;
888 wchar_t uniChar[safeLength] = {0};
889 int uniCharLen = 1;
890 char oneChar[safeLength] = "\0\0\0\0";
892 pdoc->GetCharRange(oneChar, currentPos, oneCharLen);
894 BytesToUniChar(oneChar, oneCharLen, uniChar, uniCharLen);
896 // Set the candidate box position since IME may show it.
897 SetCandidateWindowPos();
899 // IME_ESC_HANJA_MODE appears to receive the first character only.
900 HIMC hIMC=ImmGetContext(MainHWND());
901 if (hIMC) {
902 if (ImmEscapeW(GetKeyboardLayout(0), hIMC, IME_ESC_HANJA_MODE, &uniChar)) {
903 SetCandidateWindowPos(); // Force it again for sure.
904 SetSelection (currentPos, currentPos + oneCharLen);
906 ::ImmReleaseContext(MainHWND(), hIMC);
910 void ScintillaWin::ToggleHanja() {
911 // If selection, convert every hanja to hangul within the main range.
912 // If no selection, commit to IME.
913 if (sel.Count() > 1) {
914 return; // Do not allow multi carets.
917 int selStart = sel.RangeMain().Start().Position();
918 int documentStrLen = sel.RangeMain().Length();
919 int selEnd = selStart + documentStrLen;
920 int uniStrLen = pdoc->CountCharacters(selStart, selEnd);
922 // Convert one by one from the start of main range.
923 SetSelection(selStart, selStart);
925 if (uniStrLen > 0) {
926 RangeToHangul(uniStrLen);
927 } else {
928 EscapeHanja();
932 sptr_t ScintillaWin::HandleCompositionInline(uptr_t, sptr_t lParam) {
933 // Copy & paste by johnsonj with a lot of helps of Neil.
934 // Great thanks for my foreruners, jiniya and BLUEnLIVE.
936 HIMC hIMC = ::ImmGetContext(MainHWND());
937 if (!hIMC) {
938 return 0;
941 if (pdoc->TentativeActive()) {
942 pdoc->TentativeUndo();
943 } else {
944 // No tentative undo means start of this composition so
945 // fill in any virtual spaces.
946 FillVirtualSpace();
949 view.imeCaretBlockOverride = false;
951 if (lParam & GCS_COMPSTR) {
952 wchar_t wcs[maxLenInputIME] = { 0 };
953 long bytes = ::ImmGetCompositionStringW
954 (hIMC, GCS_COMPSTR, wcs, maxLenInputIME);
955 unsigned int wcsLen = bytes / 2;
957 if ((wcsLen == 0) || (wcsLen >= maxLenInputIME)) {
958 ShowCaretAtCurrentPosition();
959 ::ImmReleaseContext(MainHWND(), hIMC);
960 return 0;
963 pdoc->TentativeStart(); // TentativeActive from now on.
965 // Get attribute information from composition string.
966 BYTE compAttr[maxLenInputIME] = { 0 };
967 unsigned int imeCursorPos = 0;
969 if (lParam & GCS_COMPATTR) {
970 ImmGetCompositionStringW(hIMC, GCS_COMPATTR, compAttr, sizeof(compAttr));
972 if (lParam & GCS_CURSORPOS) {
973 imeCursorPos = ImmGetCompositionStringW(hIMC, GCS_CURSORPOS, NULL, 0);
976 // Display character by character.
977 int numBytes = 0;
978 int imeCharPos[maxLenInputIME + 1] = { 0 };
980 bool tmpRecordingMacro = recordingMacro;
981 recordingMacro = false;
982 for (size_t i = 0; i < wcsLen; ) {
983 const size_t ucWidth = UTF16CharLength(wcs[i]);
984 const std::wstring uniChar(wcs+i, ucWidth);
985 char oneChar[UTF8MaxBytes + 1] = "\0\0\0\0"; // Maximum 4 bytes in utf8
986 unsigned int oneCharLen = 0;
988 if (IsUnicodeMode()) {
989 oneCharLen = UTF8Length(uniChar.c_str(), static_cast<unsigned int>(uniChar.length()));
990 UTF8FromUTF16(uniChar.c_str(), static_cast<unsigned int>(uniChar.length()), oneChar, oneCharLen);
991 } else {
992 oneCharLen = ::WideCharToMultiByte(InputCodePage(), 0,
993 uniChar.c_str(), static_cast<unsigned int>(uniChar.length()), oneChar, sizeof(oneChar)-1, 0, 0);
995 oneChar[oneCharLen] = '\0';
997 // Display a character.
998 AddCharUTF(oneChar, oneCharLen);
1000 // Record compstr character positions for moving IME carets.
1001 numBytes += oneCharLen;
1002 imeCharPos[i + 1] = numBytes;
1004 // Draw an indicator on the character.
1005 int indicator = SC_INDICATOR_UNKNOWN;
1006 switch ((int)compAttr[i]) {
1007 case ATTR_INPUT:
1008 indicator = SC_INDICATOR_INPUT;
1009 break;
1010 case ATTR_TARGET_NOTCONVERTED:
1011 case ATTR_TARGET_CONVERTED:
1012 indicator = SC_INDICATOR_TARGET;
1013 break;
1014 case ATTR_CONVERTED:
1015 indicator = SC_INDICATOR_CONVERTED;
1016 break;
1018 DrawImeIndicator(indicator, oneCharLen);
1019 i += ucWidth;
1021 recordingMacro = tmpRecordingMacro;
1023 // Move IME caret position.
1024 MoveImeCarets(-imeCharPos[wcsLen] + imeCharPos[imeCursorPos]);
1025 if (KoreanIME()) {
1026 view.imeCaretBlockOverride = true;
1028 } else if (lParam & GCS_RESULTSTR) {
1029 wchar_t wcs[maxLenInputIME] = { 0 };
1030 long bytes = ::ImmGetCompositionStringW
1031 (hIMC, GCS_RESULTSTR, wcs, maxLenInputIME);
1032 unsigned int wcsLen = bytes / 2;
1034 for (size_t i = 0; i < wcsLen;) {
1035 const size_t ucWidth = UTF16CharLength(wcs[i]);
1036 const std::wstring uniChar(wcs+i, ucWidth);
1037 char oneChar[UTF8MaxBytes+1] = "\0\0\0\0"; // Maximum 4 bytes in UTF-8.
1038 unsigned int oneCharLen = 0;
1040 if (IsUnicodeMode()) {
1041 oneCharLen = UTF8Length(uniChar.c_str(), static_cast<unsigned int>(uniChar.length()));
1042 UTF8FromUTF16(uniChar.c_str(), static_cast<unsigned int>(uniChar.length()), oneChar, oneCharLen);
1043 } else {
1044 oneCharLen = ::WideCharToMultiByte(InputCodePage(), 0,
1045 uniChar.c_str(), static_cast<unsigned int>(uniChar.length()), oneChar, sizeof(oneChar)-1, 0, 0);
1047 oneChar[oneCharLen] = '\0';
1048 AddCharUTF(oneChar, oneCharLen);
1049 i += ucWidth;
1052 SetCandidateWindowPos();
1053 ShowCaretAtCurrentPosition();
1054 ::ImmReleaseContext(MainHWND(), hIMC);
1055 return 0;
1058 // Translate message IDs from WM_* and EM_* to SCI_* so can partly emulate Windows Edit control
1059 static unsigned int SciMessageFromEM(unsigned int iMessage) {
1060 switch (iMessage) {
1061 case EM_CANPASTE: return SCI_CANPASTE;
1062 case EM_CANUNDO: return SCI_CANUNDO;
1063 case EM_EMPTYUNDOBUFFER: return SCI_EMPTYUNDOBUFFER;
1064 case EM_FINDTEXTEX: return SCI_FINDTEXT;
1065 case EM_FORMATRANGE: return SCI_FORMATRANGE;
1066 case EM_GETFIRSTVISIBLELINE: return SCI_GETFIRSTVISIBLELINE;
1067 case EM_GETLINECOUNT: return SCI_GETLINECOUNT;
1068 case EM_GETSELTEXT: return SCI_GETSELTEXT;
1069 case EM_GETTEXTRANGE: return SCI_GETTEXTRANGE;
1070 case EM_HIDESELECTION: return SCI_HIDESELECTION;
1071 case EM_LINEINDEX: return SCI_POSITIONFROMLINE;
1072 case EM_LINESCROLL: return SCI_LINESCROLL;
1073 case EM_REPLACESEL: return SCI_REPLACESEL;
1074 case EM_SCROLLCARET: return SCI_SCROLLCARET;
1075 case EM_SETREADONLY: return SCI_SETREADONLY;
1076 case WM_CLEAR: return SCI_CLEAR;
1077 case WM_COPY: return SCI_COPY;
1078 case WM_CUT: return SCI_CUT;
1079 case WM_SETTEXT: return SCI_SETTEXT;
1080 case WM_PASTE: return SCI_PASTE;
1081 case WM_UNDO: return SCI_UNDO;
1083 return iMessage;
1086 UINT CodePageFromCharSet(DWORD characterSet, UINT documentCodePage) {
1087 if (documentCodePage == SC_CP_UTF8) {
1088 return SC_CP_UTF8;
1090 switch (characterSet) {
1091 case SC_CHARSET_ANSI: return 1252;
1092 case SC_CHARSET_DEFAULT: return documentCodePage;
1093 case SC_CHARSET_BALTIC: return 1257;
1094 case SC_CHARSET_CHINESEBIG5: return 950;
1095 case SC_CHARSET_EASTEUROPE: return 1250;
1096 case SC_CHARSET_GB2312: return 936;
1097 case SC_CHARSET_GREEK: return 1253;
1098 case SC_CHARSET_HANGUL: return 949;
1099 case SC_CHARSET_MAC: return 10000;
1100 case SC_CHARSET_OEM: return 437;
1101 case SC_CHARSET_RUSSIAN: return 1251;
1102 case SC_CHARSET_SHIFTJIS: return 932;
1103 case SC_CHARSET_TURKISH: return 1254;
1104 case SC_CHARSET_JOHAB: return 1361;
1105 case SC_CHARSET_HEBREW: return 1255;
1106 case SC_CHARSET_ARABIC: return 1256;
1107 case SC_CHARSET_VIETNAMESE: return 1258;
1108 case SC_CHARSET_THAI: return 874;
1109 case SC_CHARSET_8859_15: return 28605;
1110 // Not supported
1111 case SC_CHARSET_CYRILLIC: return documentCodePage;
1112 case SC_CHARSET_SYMBOL: return documentCodePage;
1114 return documentCodePage;
1117 UINT ScintillaWin::CodePageOfDocument() {
1118 return CodePageFromCharSet(vs.styles[STYLE_DEFAULT].characterSet, pdoc->dbcsCodePage);
1121 sptr_t ScintillaWin::GetTextLength() {
1122 if (::IsWindowUnicode(MainHWND())) {
1123 if (pdoc->Length() == 0)
1124 return 0;
1125 std::vector<char> docBytes(pdoc->Length(), '\0');
1126 pdoc->GetCharRange(&docBytes[0], 0, pdoc->Length());
1127 if (IsUnicodeMode()) {
1128 return UTF16Length(&docBytes[0], static_cast<unsigned int>(docBytes.size()));
1129 } else {
1130 return ::MultiByteToWideChar(CodePageOfDocument(), 0, &docBytes[0],
1131 static_cast<int>(docBytes.size()), NULL, 0);
1133 } else {
1134 return pdoc->Length();
1138 sptr_t ScintillaWin::GetText(uptr_t wParam, sptr_t lParam) {
1139 if (::IsWindowUnicode(MainHWND())) {
1140 wchar_t *ptr = reinterpret_cast<wchar_t *>(lParam);
1141 if (pdoc->Length() == 0) {
1142 *ptr = L'\0';
1143 return 0;
1145 std::vector<char> docBytes(pdoc->Length(), '\0');
1146 pdoc->GetCharRange(&docBytes[0], 0, pdoc->Length());
1147 if (IsUnicodeMode()) {
1148 size_t lengthUTF16 = UTF16Length(&docBytes[0], static_cast<unsigned int>(docBytes.size()));
1149 if (lParam == 0)
1150 return lengthUTF16;
1151 if (wParam == 0)
1152 return 0;
1153 size_t uLen = UTF16FromUTF8(&docBytes[0], docBytes.size(),
1154 ptr, static_cast<int>(wParam) - 1);
1155 ptr[uLen] = L'\0';
1156 return uLen;
1157 } else {
1158 // Not Unicode mode
1159 // Convert to Unicode using the current Scintilla code page
1160 const UINT cpSrc = CodePageOfDocument();
1161 int lengthUTF16 = ::MultiByteToWideChar(cpSrc, 0, &docBytes[0],
1162 static_cast<int>(docBytes.size()), NULL, 0);
1163 if (lengthUTF16 >= static_cast<int>(wParam))
1164 lengthUTF16 = static_cast<int>(wParam)-1;
1165 ::MultiByteToWideChar(cpSrc, 0, &docBytes[0],
1166 static_cast<int>(docBytes.size()),
1167 ptr, lengthUTF16);
1168 ptr[lengthUTF16] = L'\0';
1169 return lengthUTF16;
1171 } else {
1172 if (lParam == 0)
1173 return pdoc->Length() + 1;
1174 if (wParam == 0)
1175 return 0;
1176 char *ptr = reinterpret_cast<char *>(lParam);
1177 unsigned int iChar = 0;
1178 for (; iChar < wParam - 1; iChar++)
1179 ptr[iChar] = pdoc->CharAt(iChar);
1180 ptr[iChar] = '\0';
1181 return iChar;
1185 sptr_t ScintillaWin::WndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
1186 try {
1187 //Platform::DebugPrintf("S M:%x WP:%x L:%x\n", iMessage, wParam, lParam);
1188 iMessage = SciMessageFromEM(iMessage);
1189 switch (iMessage) {
1191 case WM_CREATE:
1192 ctrlID = ::GetDlgCtrlID(reinterpret_cast<HWND>(wMain.GetID()));
1193 // Get Intellimouse scroll line parameters
1194 GetIntelliMouseParameters();
1195 ::RegisterDragDrop(MainHWND(), reinterpret_cast<IDropTarget *>(&dt));
1196 break;
1198 case WM_COMMAND:
1199 Command(LoWord(wParam));
1200 break;
1202 case WM_PAINT:
1203 return WndPaint(wParam);
1205 case WM_PRINTCLIENT: {
1206 HDC hdc = reinterpret_cast<HDC>(wParam);
1207 if (!IsCompatibleDC(hdc)) {
1208 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1210 FullPaintDC(hdc);
1212 break;
1214 case WM_VSCROLL:
1215 ScrollMessage(wParam);
1216 break;
1218 case WM_HSCROLL:
1219 HorizontalScrollMessage(wParam);
1220 break;
1222 case WM_SIZE: {
1223 #if defined(USE_D2D)
1224 if (paintState == notPainting) {
1225 DropRenderTarget();
1226 } else {
1227 renderTargetValid = false;
1229 #endif
1230 //Platform::DebugPrintf("Scintilla WM_SIZE %d %d\n", LoWord(lParam), HiWord(lParam));
1231 ChangeSize();
1233 break;
1235 case WM_MOUSEWHEEL:
1236 // if autocomplete list active then send mousewheel message to it
1237 if (ac.Active()) {
1238 HWND hWnd = reinterpret_cast<HWND>(ac.lb->GetID());
1239 ::SendMessage(hWnd, iMessage, wParam, lParam);
1240 break;
1243 // Don't handle datazoom.
1244 // (A good idea for datazoom would be to "fold" or "unfold" details.
1245 // i.e. if datazoomed out only class structures are visible, when datazooming in the control
1246 // structures appear, then eventually the individual statements...)
1247 if (wParam & MK_SHIFT) {
1248 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1251 // Either SCROLL or ZOOM. We handle the wheel steppings calculation
1252 wheelDelta -= static_cast<short>(HiWord(wParam));
1253 if (abs(wheelDelta) >= WHEEL_DELTA && linesPerScroll > 0) {
1254 int linesToScroll = linesPerScroll;
1255 if (linesPerScroll == WHEEL_PAGESCROLL)
1256 linesToScroll = LinesOnScreen() - 1;
1257 if (linesToScroll == 0) {
1258 linesToScroll = 1;
1260 linesToScroll *= (wheelDelta / WHEEL_DELTA);
1261 if (wheelDelta >= 0)
1262 wheelDelta = wheelDelta % WHEEL_DELTA;
1263 else
1264 wheelDelta = - (-wheelDelta % WHEEL_DELTA);
1266 if (wParam & MK_CONTROL) {
1267 // Zoom! We play with the font sizes in the styles.
1268 // Number of steps/line is ignored, we just care if sizing up or down
1269 if (linesToScroll < 0) {
1270 KeyCommand(SCI_ZOOMIN);
1271 } else {
1272 KeyCommand(SCI_ZOOMOUT);
1274 } else {
1275 // Scroll
1276 ScrollTo(topLine + linesToScroll);
1279 return 0;
1281 case WM_TIMER:
1282 if (wParam == idleTimerID && idler.state) {
1283 SendMessage(MainHWND(), SC_WIN_IDLE, 0, 1);
1284 } else {
1285 TickFor(static_cast<TickReason>(wParam - fineTimerStart));
1287 break;
1289 case SC_WIN_IDLE:
1290 // wParam=dwTickCountInitial, or 0 to initialize. lParam=bSkipUserInputTest
1291 if (idler.state) {
1292 if (lParam || (WAIT_TIMEOUT == MsgWaitForMultipleObjects(0, 0, 0, 0, QS_INPUT|QS_HOTKEY))) {
1293 if (Idle()) {
1294 // User input was given priority above, but all events do get a turn. Other
1295 // messages, notifications, etc. will get interleaved with the idle messages.
1297 // However, some things like WM_PAINT are a lower priority, and will not fire
1298 // when there's a message posted. So, several times a second, we stop and let
1299 // the low priority events have a turn (after which the timer will fire again).
1301 DWORD dwCurrent = GetTickCount();
1302 DWORD dwStart = wParam ? static_cast<DWORD>(wParam) : dwCurrent;
1303 const DWORD maxWorkTime = 50;
1305 if (dwCurrent >= dwStart && dwCurrent > maxWorkTime && dwCurrent - maxWorkTime < dwStart)
1306 PostMessage(MainHWND(), SC_WIN_IDLE, dwStart, 0);
1307 } else {
1308 SetIdle(false);
1312 break;
1314 case WM_GETMINMAXINFO:
1315 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1317 case WM_LBUTTONDOWN: {
1318 // For IME, set the composition string as the result string.
1319 HIMC hIMC = ::ImmGetContext(MainHWND());
1320 ::ImmNotifyIME(hIMC, NI_COMPOSITIONSTR, CPS_COMPLETE, 0);
1321 ::ImmReleaseContext(MainHWND(), hIMC);
1323 //Platform::DebugPrintf("Buttdown %d %x %x %x %x %x\n",iMessage, wParam, lParam,
1324 // Platform::IsKeyDown(VK_SHIFT),
1325 // Platform::IsKeyDown(VK_CONTROL),
1326 // Platform::IsKeyDown(VK_MENU));
1327 ::SetFocus(MainHWND());
1328 ButtonDown(Point::FromLong(static_cast<long>(lParam)), ::GetMessageTime(),
1329 (wParam & MK_SHIFT) != 0,
1330 (wParam & MK_CONTROL) != 0,
1331 Platform::IsKeyDown(VK_MENU));
1333 break;
1335 case WM_MOUSEMOVE: {
1336 const Point pt = Point::FromLong(static_cast<long>(lParam));
1338 // Windows might send WM_MOUSEMOVE even though the mouse has not been moved:
1339 // http://blogs.msdn.com/b/oldnewthing/archive/2003/10/01/55108.aspx
1340 if (ptMouseLast.x != pt.x || ptMouseLast.y != pt.y) {
1341 SetTrackMouseLeaveEvent(true);
1342 ButtonMoveWithModifiers(pt,
1343 ((wParam & MK_SHIFT) != 0 ? SCI_SHIFT : 0) |
1344 ((wParam & MK_CONTROL) != 0 ? SCI_CTRL : 0) |
1345 (Platform::IsKeyDown(VK_MENU) ? SCI_ALT : 0));
1348 break;
1350 case WM_MOUSELEAVE:
1351 SetTrackMouseLeaveEvent(false);
1352 MouseLeave();
1353 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1355 case WM_LBUTTONUP:
1356 ButtonUp(Point::FromLong(static_cast<long>(lParam)),
1357 ::GetMessageTime(),
1358 (wParam & MK_CONTROL) != 0);
1359 break;
1361 case WM_RBUTTONDOWN:
1362 ::SetFocus(MainHWND());
1363 if (!PointInSelection(Point::FromLong(static_cast<long>(lParam)))) {
1364 CancelModes();
1365 SetEmptySelection(PositionFromLocation(Point::FromLong(static_cast<long>(lParam))));
1367 break;
1369 case WM_SETCURSOR:
1370 if (LoWord(lParam) == HTCLIENT) {
1371 if (inDragDrop == ddDragging) {
1372 DisplayCursor(Window::cursorUp);
1373 } else {
1374 // Display regular (drag) cursor over selection
1375 POINT pt;
1376 if (0 != ::GetCursorPos(&pt)) {
1377 ::ScreenToClient(MainHWND(), &pt);
1378 if (PointInSelMargin(PointFromPOINT(pt))) {
1379 DisplayCursor(GetMarginCursor(PointFromPOINT(pt)));
1380 } else if (PointInSelection(PointFromPOINT(pt)) && !SelectionEmpty()) {
1381 DisplayCursor(Window::cursorArrow);
1382 } else if (PointIsHotspot(PointFromPOINT(pt))) {
1383 DisplayCursor(Window::cursorHand);
1384 } else {
1385 DisplayCursor(Window::cursorText);
1389 return TRUE;
1390 } else {
1391 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1394 case WM_CHAR:
1395 if (((wParam >= 128) || !iscntrl(static_cast<int>(wParam))) || !lastKeyDownConsumed) {
1396 if (::IsWindowUnicode(MainHWND()) || keysAlwaysUnicode) {
1397 wchar_t wcs[2] = {static_cast<wchar_t>(wParam), 0};
1398 if (IsUnicodeMode()) {
1399 // For a wide character version of the window:
1400 char utfval[4];
1401 unsigned int len = UTF8Length(wcs, 1);
1402 UTF8FromUTF16(wcs, 1, utfval, len);
1403 AddCharUTF(utfval, len);
1404 } else {
1405 UINT cpDest = CodePageOfDocument();
1406 char inBufferCP[20];
1407 int size = ::WideCharToMultiByte(cpDest,
1408 0, wcs, 1, inBufferCP, sizeof(inBufferCP) - 1, 0, 0);
1409 inBufferCP[size] = '\0';
1410 AddCharUTF(inBufferCP, size);
1412 } else {
1413 if (IsUnicodeMode()) {
1414 AddCharBytes('\0', LOBYTE(wParam));
1415 } else {
1416 AddChar(LOBYTE(wParam));
1420 return 0;
1422 case WM_UNICHAR:
1423 if (wParam == UNICODE_NOCHAR) {
1424 return IsUnicodeMode() ? 1 : 0;
1425 } else if (lastKeyDownConsumed) {
1426 return 1;
1427 } else {
1428 if (IsUnicodeMode()) {
1429 char utfval[4];
1430 wchar_t wcs[2] = {static_cast<wchar_t>(wParam), 0};
1431 unsigned int len = UTF8Length(wcs, 1);
1432 UTF8FromUTF16(wcs, 1, utfval, len);
1433 AddCharUTF(utfval, len);
1434 return 1;
1435 } else {
1436 return 0;
1440 case WM_SYSKEYDOWN:
1441 case WM_KEYDOWN: {
1442 //Platform::DebugPrintf("S keydown %d %x %x %x %x\n",iMessage, wParam, lParam, ::IsKeyDown(VK_SHIFT), ::IsKeyDown(VK_CONTROL));
1443 lastKeyDownConsumed = false;
1444 int ret = KeyDown(KeyTranslate(static_cast<int>(wParam)),
1445 Platform::IsKeyDown(VK_SHIFT),
1446 Platform::IsKeyDown(VK_CONTROL),
1447 Platform::IsKeyDown(VK_MENU),
1448 &lastKeyDownConsumed);
1449 if (!ret && !lastKeyDownConsumed) {
1450 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1452 break;
1455 case WM_IME_KEYDOWN: {
1456 if (wParam == VK_HANJA) {
1457 ToggleHanja();
1459 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1462 case WM_KEYUP:
1463 //Platform::DebugPrintf("S keyup %d %x %x\n",iMessage, wParam, lParam);
1464 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1466 case WM_SETTINGCHANGE:
1467 //Platform::DebugPrintf("Setting Changed\n");
1468 InvalidateStyleData();
1469 // Get Intellimouse scroll line parameters
1470 GetIntelliMouseParameters();
1471 break;
1473 case WM_GETDLGCODE:
1474 return DLGC_HASSETSEL | DLGC_WANTALLKEYS;
1476 case WM_KILLFOCUS: {
1477 HWND wOther = reinterpret_cast<HWND>(wParam);
1478 HWND wThis = MainHWND();
1479 HWND wCT = reinterpret_cast<HWND>(ct.wCallTip.GetID());
1480 if (!wParam ||
1481 !(::IsChild(wThis, wOther) || (wOther == wCT))) {
1482 SetFocusState(false);
1483 DestroySystemCaret();
1485 // Explicitly complete any IME composition
1486 HIMC hIMC = ImmGetContext(MainHWND());
1487 if (hIMC) {
1488 ::ImmNotifyIME(hIMC, NI_COMPOSITIONSTR, CPS_COMPLETE, 0);
1489 ::ImmReleaseContext(MainHWND(), hIMC);
1492 break;
1494 case WM_SETFOCUS:
1495 SetFocusState(true);
1496 DestroySystemCaret();
1497 CreateSystemCaret();
1498 break;
1500 case WM_SYSCOLORCHANGE:
1501 //Platform::DebugPrintf("Setting Changed\n");
1502 InvalidateStyleData();
1503 break;
1505 case WM_IME_STARTCOMPOSITION: // dbcs
1506 if (KoreanIME() || imeInteraction == imeInline) {
1507 return 0;
1508 } else {
1509 ImeStartComposition();
1510 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1513 case WM_IME_ENDCOMPOSITION: // dbcs
1514 ImeEndComposition();
1515 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1517 case WM_IME_COMPOSITION:
1518 if (KoreanIME() || imeInteraction == imeInline) {
1519 return HandleCompositionInline(wParam, lParam);
1520 } else {
1521 return HandleCompositionWindowed(wParam, lParam);
1524 case WM_IME_CHAR: {
1525 AddCharBytes(HIBYTE(wParam), LOBYTE(wParam));
1526 return 0;
1529 case WM_CONTEXTMENU:
1530 if (displayPopupMenu) {
1531 Point pt = Point::FromLong(static_cast<long>(lParam));
1532 if ((pt.x == -1) && (pt.y == -1)) {
1533 // Caused by keyboard so display menu near caret
1534 pt = PointMainCaret();
1535 POINT spt = {static_cast<int>(pt.x), static_cast<int>(pt.y)};
1536 ::ClientToScreen(MainHWND(), &spt);
1537 pt = PointFromPOINT(spt);
1539 ContextMenu(pt);
1540 return 0;
1542 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1544 case WM_INPUTLANGCHANGE:
1545 //::SetThreadLocale(LOWORD(lParam));
1546 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1548 case WM_INPUTLANGCHANGEREQUEST:
1549 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1551 case WM_ERASEBKGND:
1552 return 1; // Avoid any background erasure as whole window painted.
1554 case WM_CAPTURECHANGED:
1555 capturedMouse = false;
1556 return 0;
1558 case WM_IME_SETCONTEXT:
1559 if (KoreanIME() || imeInteraction == imeInline) {
1560 if (wParam) {
1561 LPARAM NoImeWin = lParam;
1562 NoImeWin = NoImeWin & (~ISC_SHOWUICOMPOSITIONWINDOW);
1563 return ::DefWindowProc(MainHWND(), iMessage, wParam, NoImeWin);
1566 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1568 // These are not handled in Scintilla and its faster to dispatch them here.
1569 // Also moves time out to here so profile doesn't count lots of empty message calls.
1571 case WM_MOVE:
1572 case WM_MOUSEACTIVATE:
1573 case WM_NCHITTEST:
1574 case WM_NCCALCSIZE:
1575 case WM_NCPAINT:
1576 case WM_NCMOUSEMOVE:
1577 case WM_NCLBUTTONDOWN:
1578 case WM_IME_NOTIFY:
1579 case WM_SYSCOMMAND:
1580 case WM_WINDOWPOSCHANGING:
1581 case WM_WINDOWPOSCHANGED:
1582 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1584 case WM_GETTEXTLENGTH:
1585 return GetTextLength();
1587 case WM_GETTEXT:
1588 return GetText(wParam, lParam);
1590 case EM_LINEFROMCHAR:
1591 if (static_cast<int>(wParam) < 0) {
1592 wParam = SelectionStart().Position();
1594 return pdoc->LineFromPosition(static_cast<int>(wParam));
1596 case EM_EXLINEFROMCHAR:
1597 return pdoc->LineFromPosition(static_cast<int>(lParam));
1599 case EM_GETSEL:
1600 if (wParam) {
1601 *reinterpret_cast<int *>(wParam) = SelectionStart().Position();
1603 if (lParam) {
1604 *reinterpret_cast<int *>(lParam) = SelectionEnd().Position();
1606 return MAKELONG(SelectionStart().Position(), SelectionEnd().Position());
1608 case EM_EXGETSEL: {
1609 if (lParam == 0) {
1610 return 0;
1612 Sci_CharacterRange *pCR = reinterpret_cast<Sci_CharacterRange *>(lParam);
1613 pCR->cpMin = SelectionStart().Position();
1614 pCR->cpMax = SelectionEnd().Position();
1616 break;
1618 case EM_SETSEL: {
1619 int nStart = static_cast<int>(wParam);
1620 int nEnd = static_cast<int>(lParam);
1621 if (nStart == 0 && nEnd == -1) {
1622 nEnd = pdoc->Length();
1624 if (nStart == -1) {
1625 nStart = nEnd; // Remove selection
1627 if (nStart > nEnd) {
1628 SetSelection(nEnd, nStart);
1629 } else {
1630 SetSelection(nStart, nEnd);
1632 EnsureCaretVisible();
1634 break;
1636 case EM_EXSETSEL: {
1637 if (lParam == 0) {
1638 return 0;
1640 Sci_CharacterRange *pCR = reinterpret_cast<Sci_CharacterRange *>(lParam);
1641 sel.selType = Selection::selStream;
1642 if (pCR->cpMin == 0 && pCR->cpMax == -1) {
1643 SetSelection(pCR->cpMin, pdoc->Length());
1644 } else {
1645 SetSelection(pCR->cpMin, pCR->cpMax);
1647 EnsureCaretVisible();
1648 return pdoc->LineFromPosition(SelectionStart().Position());
1651 case SCI_GETDIRECTFUNCTION:
1652 return reinterpret_cast<sptr_t>(DirectFunction);
1654 case SCI_GETDIRECTPOINTER:
1655 return reinterpret_cast<sptr_t>(this);
1657 case SCI_GRABFOCUS:
1658 ::SetFocus(MainHWND());
1659 break;
1661 case SCI_SETKEYSUNICODE:
1662 keysAlwaysUnicode = wParam != 0;
1663 break;
1665 case SCI_GETKEYSUNICODE:
1666 return keysAlwaysUnicode;
1668 case SCI_SETTECHNOLOGY:
1669 if ((wParam == SC_TECHNOLOGY_DEFAULT) ||
1670 (wParam == SC_TECHNOLOGY_DIRECTWRITERETAIN) ||
1671 (wParam == SC_TECHNOLOGY_DIRECTWRITEDC) ||
1672 (wParam == SC_TECHNOLOGY_DIRECTWRITE)) {
1673 if (technology != static_cast<int>(wParam)) {
1674 if (static_cast<int>(wParam) > SC_TECHNOLOGY_DEFAULT) {
1675 #if defined(USE_D2D)
1676 if (!LoadD2D())
1677 // Failed to load Direct2D or DirectWrite so no effect
1678 return 0;
1679 #else
1680 return 0;
1681 #endif
1683 #if defined(USE_D2D)
1684 DropRenderTarget();
1685 #endif
1686 technology = static_cast<int>(wParam);
1687 // Invalidate all cached information including layout.
1688 DropGraphics(true);
1689 InvalidateStyleRedraw();
1692 break;
1694 #ifdef SCI_LEXER
1695 case SCI_LOADLEXERLIBRARY:
1696 LexerManager::GetInstance()->Load(reinterpret_cast<const char *>(lParam));
1697 break;
1698 #endif
1700 default:
1701 return ScintillaBase::WndProc(iMessage, wParam, lParam);
1703 } catch (std::bad_alloc &) {
1704 errorStatus = SC_STATUS_BADALLOC;
1705 } catch (...) {
1706 errorStatus = SC_STATUS_FAILURE;
1708 return 0l;
1711 bool ScintillaWin::ValidCodePage(int codePage) const {
1712 return codePage == 0 || codePage == SC_CP_UTF8 ||
1713 codePage == 932 || codePage == 936 || codePage == 949 ||
1714 codePage == 950 || codePage == 1361;
1717 sptr_t ScintillaWin::DefWndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
1718 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1722 * Report that this Editor subclass has a working implementation of FineTickerStart.
1724 bool ScintillaWin::FineTickerAvailable() {
1725 return true;
1728 bool ScintillaWin::FineTickerRunning(TickReason reason) {
1729 return timers[reason] != 0;
1732 void ScintillaWin::FineTickerStart(TickReason reason, int millis, int tolerance) {
1733 FineTickerCancel(reason);
1734 if (SetCoalescableTimerFn && tolerance) {
1735 timers[reason] = SetCoalescableTimerFn(MainHWND(), fineTimerStart + reason, millis, NULL, tolerance);
1736 } else {
1737 timers[reason] = ::SetTimer(MainHWND(), fineTimerStart + reason, millis, NULL);
1741 void ScintillaWin::FineTickerCancel(TickReason reason) {
1742 if (timers[reason]) {
1743 ::KillTimer(MainHWND(), timers[reason]);
1744 timers[reason] = 0;
1749 bool ScintillaWin::SetIdle(bool on) {
1750 // On Win32 the Idler is implemented as a Timer on the Scintilla window. This
1751 // takes advantage of the fact that WM_TIMER messages are very low priority,
1752 // and are only posted when the message queue is empty, i.e. during idle time.
1753 if (idler.state != on) {
1754 if (on) {
1755 idler.idlerID = ::SetTimer(MainHWND(), idleTimerID, 10, NULL)
1756 ? reinterpret_cast<IdlerID>(idleTimerID) : 0;
1757 } else {
1758 ::KillTimer(MainHWND(), reinterpret_cast<uptr_t>(idler.idlerID));
1759 idler.idlerID = 0;
1761 idler.state = idler.idlerID != 0;
1763 return idler.state;
1766 void ScintillaWin::SetMouseCapture(bool on) {
1767 if (mouseDownCaptures) {
1768 if (on) {
1769 ::SetCapture(MainHWND());
1770 } else {
1771 ::ReleaseCapture();
1774 capturedMouse = on;
1777 bool ScintillaWin::HaveMouseCapture() {
1778 // Cannot just see if GetCapture is this window as the scroll bar also sets capture for the window
1779 return capturedMouse;
1780 //return capturedMouse && (::GetCapture() == MainHWND());
1783 void ScintillaWin::SetTrackMouseLeaveEvent(bool on) {
1784 if (on && TrackMouseEventFn && !trackedMouseLeave) {
1785 TRACKMOUSEEVENT tme;
1786 tme.cbSize = sizeof(tme);
1787 tme.dwFlags = TME_LEAVE;
1788 tme.hwndTrack = MainHWND();
1789 tme.dwHoverTime = HOVER_DEFAULT; // Unused but triggers Dr. Memory if not initialized
1790 TrackMouseEventFn(&tme);
1792 trackedMouseLeave = on;
1795 bool ScintillaWin::PaintContains(PRectangle rc) {
1796 if (paintState == painting) {
1797 return BoundsContains(rcPaint, hRgnUpdate, rc);
1799 return true;
1802 void ScintillaWin::ScrollText(int /* linesToMove */) {
1803 //Platform::DebugPrintf("ScintillaWin::ScrollText %d\n", linesToMove);
1804 //::ScrollWindow(MainHWND(), 0,
1805 // vs.lineHeight * linesToMove, 0, 0);
1806 //::UpdateWindow(MainHWND());
1807 Redraw();
1808 UpdateSystemCaret();
1811 void ScintillaWin::UpdateSystemCaret() {
1812 if (hasFocus) {
1813 if (HasCaretSizeChanged()) {
1814 DestroySystemCaret();
1815 CreateSystemCaret();
1817 Point pos = PointMainCaret();
1818 ::SetCaretPos(static_cast<int>(pos.x), static_cast<int>(pos.y));
1822 int ScintillaWin::SetScrollInfo(int nBar, LPCSCROLLINFO lpsi, BOOL bRedraw) {
1823 return ::SetScrollInfo(MainHWND(), nBar, lpsi, bRedraw);
1826 bool ScintillaWin::GetScrollInfo(int nBar, LPSCROLLINFO lpsi) {
1827 return ::GetScrollInfo(MainHWND(), nBar, lpsi) ? true : false;
1830 // Change the scroll position but avoid repaint if changing to same value
1831 void ScintillaWin::ChangeScrollPos(int barType, int pos) {
1832 SCROLLINFO sci = {
1833 sizeof(sci), 0, 0, 0, 0, 0, 0
1835 sci.fMask = SIF_POS;
1836 GetScrollInfo(barType, &sci);
1837 if (sci.nPos != pos) {
1838 DwellEnd(true);
1839 sci.nPos = pos;
1840 SetScrollInfo(barType, &sci, TRUE);
1844 void ScintillaWin::SetVerticalScrollPos() {
1845 ChangeScrollPos(SB_VERT, topLine);
1848 void ScintillaWin::SetHorizontalScrollPos() {
1849 ChangeScrollPos(SB_HORZ, xOffset);
1852 bool ScintillaWin::ModifyScrollBars(int nMax, int nPage) {
1853 bool modified = false;
1854 SCROLLINFO sci = {
1855 sizeof(sci), 0, 0, 0, 0, 0, 0
1857 sci.fMask = SIF_PAGE | SIF_RANGE;
1858 GetScrollInfo(SB_VERT, &sci);
1859 int vertEndPreferred = nMax;
1860 if (!verticalScrollBarVisible)
1861 nPage = vertEndPreferred + 1;
1862 if ((sci.nMin != 0) ||
1863 (sci.nMax != vertEndPreferred) ||
1864 (sci.nPage != static_cast<unsigned int>(nPage)) ||
1865 (sci.nPos != 0)) {
1866 sci.fMask = SIF_PAGE | SIF_RANGE;
1867 sci.nMin = 0;
1868 sci.nMax = vertEndPreferred;
1869 sci.nPage = nPage;
1870 sci.nPos = 0;
1871 sci.nTrackPos = 1;
1872 SetScrollInfo(SB_VERT, &sci, TRUE);
1873 modified = true;
1876 PRectangle rcText = GetTextRectangle();
1877 int horizEndPreferred = scrollWidth;
1878 if (horizEndPreferred < 0)
1879 horizEndPreferred = 0;
1880 unsigned int pageWidth = static_cast<unsigned int>(rcText.Width());
1881 if (!horizontalScrollBarVisible || Wrapping())
1882 pageWidth = horizEndPreferred + 1;
1883 sci.fMask = SIF_PAGE | SIF_RANGE;
1884 GetScrollInfo(SB_HORZ, &sci);
1885 if ((sci.nMin != 0) ||
1886 (sci.nMax != horizEndPreferred) ||
1887 (sci.nPage != pageWidth) ||
1888 (sci.nPos != 0)) {
1889 sci.fMask = SIF_PAGE | SIF_RANGE;
1890 sci.nMin = 0;
1891 sci.nMax = horizEndPreferred;
1892 sci.nPage = pageWidth;
1893 sci.nPos = 0;
1894 sci.nTrackPos = 1;
1895 SetScrollInfo(SB_HORZ, &sci, TRUE);
1896 modified = true;
1897 if (scrollWidth < static_cast<int>(pageWidth)) {
1898 HorizontalScrollTo(0);
1901 return modified;
1904 void ScintillaWin::NotifyChange() {
1905 ::SendMessage(::GetParent(MainHWND()), WM_COMMAND,
1906 MAKELONG(GetCtrlID(), SCEN_CHANGE),
1907 reinterpret_cast<LPARAM>(MainHWND()));
1910 void ScintillaWin::NotifyFocus(bool focus) {
1911 ::SendMessage(::GetParent(MainHWND()), WM_COMMAND,
1912 MAKELONG(GetCtrlID(), focus ? SCEN_SETFOCUS : SCEN_KILLFOCUS),
1913 reinterpret_cast<LPARAM>(MainHWND()));
1914 Editor::NotifyFocus(focus);
1917 void ScintillaWin::SetCtrlID(int identifier) {
1918 ::SetWindowID(reinterpret_cast<HWND>(wMain.GetID()), identifier);
1921 int ScintillaWin::GetCtrlID() {
1922 return ::GetDlgCtrlID(reinterpret_cast<HWND>(wMain.GetID()));
1925 void ScintillaWin::NotifyParent(SCNotification scn) {
1926 scn.nmhdr.hwndFrom = MainHWND();
1927 scn.nmhdr.idFrom = GetCtrlID();
1928 ::SendMessage(::GetParent(MainHWND()), WM_NOTIFY,
1929 GetCtrlID(), reinterpret_cast<LPARAM>(&scn));
1932 void ScintillaWin::NotifyDoubleClick(Point pt, int modifiers) {
1933 //Platform::DebugPrintf("ScintillaWin Double click 0\n");
1934 ScintillaBase::NotifyDoubleClick(pt, modifiers);
1935 // Send myself a WM_LBUTTONDBLCLK, so the container can handle it too.
1936 ::SendMessage(MainHWND(),
1937 WM_LBUTTONDBLCLK,
1938 (modifiers & SCI_SHIFT) ? MK_SHIFT : 0,
1939 MAKELPARAM(pt.x, pt.y));
1942 class CaseFolderDBCS : public CaseFolderTable {
1943 // Allocate the expandable storage here so that it does not need to be reallocated
1944 // for each call to Fold.
1945 std::vector<wchar_t> utf16Mixed;
1946 std::vector<wchar_t> utf16Folded;
1947 UINT cp;
1948 public:
1949 explicit CaseFolderDBCS(UINT cp_) : cp(cp_) {
1950 StandardASCII();
1952 virtual size_t Fold(char *folded, size_t sizeFolded, const char *mixed, size_t lenMixed) {
1953 if ((lenMixed == 1) && (sizeFolded > 0)) {
1954 folded[0] = mapping[static_cast<unsigned char>(mixed[0])];
1955 return 1;
1956 } else {
1957 if (lenMixed > utf16Mixed.size()) {
1958 utf16Mixed.resize(lenMixed + 8);
1960 size_t nUtf16Mixed = ::MultiByteToWideChar(cp, 0, mixed,
1961 static_cast<int>(lenMixed),
1962 &utf16Mixed[0],
1963 static_cast<int>(utf16Mixed.size()));
1965 if (nUtf16Mixed == 0) {
1966 // Failed to convert -> bad input
1967 folded[0] = '\0';
1968 return 1;
1971 unsigned int lenFlat = 0;
1972 for (size_t mixIndex=0; mixIndex < nUtf16Mixed; mixIndex++) {
1973 if ((lenFlat + 20) > utf16Folded.size())
1974 utf16Folded.resize(lenFlat + 60);
1975 const char *foldedUTF8 = CaseConvert(utf16Mixed[mixIndex], CaseConversionFold);
1976 if (foldedUTF8) {
1977 // Maximum length of a case conversion is 6 bytes, 3 characters
1978 wchar_t wFolded[20];
1979 size_t charsConverted = UTF16FromUTF8(foldedUTF8,
1980 strlen(foldedUTF8),
1981 wFolded, ELEMENTS(wFolded));
1982 for (size_t j=0; j<charsConverted; j++)
1983 utf16Folded[lenFlat++] = wFolded[j];
1984 } else {
1985 utf16Folded[lenFlat++] = utf16Mixed[mixIndex];
1989 size_t lenOut = ::WideCharToMultiByte(cp, 0,
1990 &utf16Folded[0], lenFlat,
1991 NULL, 0, NULL, 0);
1993 if (lenOut < sizeFolded) {
1994 ::WideCharToMultiByte(cp, 0,
1995 &utf16Folded[0], lenFlat,
1996 folded, static_cast<int>(lenOut), NULL, 0);
1997 return lenOut;
1998 } else {
1999 return 0;
2005 CaseFolder *ScintillaWin::CaseFolderForEncoding() {
2006 UINT cpDest = CodePageOfDocument();
2007 if (cpDest == SC_CP_UTF8) {
2008 return new CaseFolderUnicode();
2009 } else {
2010 if (pdoc->dbcsCodePage == 0) {
2011 CaseFolderTable *pcf = new CaseFolderTable();
2012 pcf->StandardASCII();
2013 // Only for single byte encodings
2014 UINT cpDoc = CodePageOfDocument();
2015 for (int i=0x80; i<0x100; i++) {
2016 char sCharacter[2] = "A";
2017 sCharacter[0] = static_cast<char>(i);
2018 wchar_t wCharacter[20];
2019 unsigned int lengthUTF16 = ::MultiByteToWideChar(cpDoc, 0, sCharacter, 1,
2020 wCharacter, ELEMENTS(wCharacter));
2021 if (lengthUTF16 == 1) {
2022 const char *caseFolded = CaseConvert(wCharacter[0], CaseConversionFold);
2023 if (caseFolded) {
2024 wchar_t wLower[20];
2025 size_t charsConverted = UTF16FromUTF8(caseFolded,
2026 strlen(caseFolded),
2027 wLower, ELEMENTS(wLower));
2028 if (charsConverted == 1) {
2029 char sCharacterLowered[20];
2030 unsigned int lengthConverted = ::WideCharToMultiByte(cpDoc, 0,
2031 wLower, static_cast<int>(charsConverted),
2032 sCharacterLowered, ELEMENTS(sCharacterLowered), NULL, 0);
2033 if ((lengthConverted == 1) && (sCharacter[0] != sCharacterLowered[0])) {
2034 pcf->SetTranslation(sCharacter[0], sCharacterLowered[0]);
2040 return pcf;
2041 } else {
2042 return new CaseFolderDBCS(cpDest);
2047 std::string ScintillaWin::CaseMapString(const std::string &s, int caseMapping) {
2048 if ((s.size() == 0) || (caseMapping == cmSame))
2049 return s;
2051 UINT cpDoc = CodePageOfDocument();
2052 if (cpDoc == SC_CP_UTF8) {
2053 std::string retMapped(s.length() * maxExpansionCaseConversion, 0);
2054 size_t lenMapped = CaseConvertString(&retMapped[0], retMapped.length(), s.c_str(), s.length(),
2055 (caseMapping == cmUpper) ? CaseConversionUpper : CaseConversionLower);
2056 retMapped.resize(lenMapped);
2057 return retMapped;
2060 unsigned int lengthUTF16 = ::MultiByteToWideChar(cpDoc, 0, s.c_str(),
2061 static_cast<int>(s.size()), NULL, 0);
2062 if (lengthUTF16 == 0) // Failed to convert
2063 return s;
2065 DWORD mapFlags = LCMAP_LINGUISTIC_CASING |
2066 ((caseMapping == cmUpper) ? LCMAP_UPPERCASE : LCMAP_LOWERCASE);
2068 // Change text to UTF-16
2069 std::vector<wchar_t> vwcText(lengthUTF16);
2070 ::MultiByteToWideChar(cpDoc, 0, s.c_str(), static_cast<int>(s.size()), &vwcText[0], lengthUTF16);
2072 // Change case
2073 int charsConverted = ::LCMapStringW(LOCALE_SYSTEM_DEFAULT, mapFlags,
2074 &vwcText[0], lengthUTF16, NULL, 0);
2075 std::vector<wchar_t> vwcConverted(charsConverted);
2076 ::LCMapStringW(LOCALE_SYSTEM_DEFAULT, mapFlags,
2077 &vwcText[0], lengthUTF16, &vwcConverted[0], charsConverted);
2079 // Change back to document encoding
2080 unsigned int lengthConverted = ::WideCharToMultiByte(cpDoc, 0,
2081 &vwcConverted[0], static_cast<int>(vwcConverted.size()),
2082 NULL, 0, NULL, 0);
2083 std::vector<char> vcConverted(lengthConverted);
2084 ::WideCharToMultiByte(cpDoc, 0,
2085 &vwcConverted[0], static_cast<int>(vwcConverted.size()),
2086 &vcConverted[0], static_cast<int>(vcConverted.size()), NULL, 0);
2088 return std::string(&vcConverted[0], vcConverted.size());
2091 void ScintillaWin::Copy() {
2092 //Platform::DebugPrintf("Copy\n");
2093 if (!sel.Empty()) {
2094 SelectionText selectedText;
2095 CopySelectionRange(&selectedText);
2096 CopyToClipboard(selectedText);
2100 void ScintillaWin::CopyAllowLine() {
2101 SelectionText selectedText;
2102 CopySelectionRange(&selectedText, true);
2103 CopyToClipboard(selectedText);
2106 bool ScintillaWin::CanPaste() {
2107 if (!Editor::CanPaste())
2108 return false;
2109 if (::IsClipboardFormatAvailable(CF_TEXT))
2110 return true;
2111 if (IsUnicodeMode())
2112 return ::IsClipboardFormatAvailable(CF_UNICODETEXT) != 0;
2113 return false;
2116 class GlobalMemory {
2117 HGLOBAL hand;
2118 public:
2119 void *ptr;
2120 GlobalMemory() : hand(0), ptr(0) {
2122 explicit GlobalMemory(HGLOBAL hand_) : hand(hand_), ptr(0) {
2123 if (hand) {
2124 ptr = ::GlobalLock(hand);
2127 ~GlobalMemory() {
2128 PLATFORM_ASSERT(!ptr);
2129 assert(!hand);
2131 void Allocate(size_t bytes) {
2132 assert(!hand);
2133 hand = ::GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT, bytes);
2134 if (hand) {
2135 ptr = ::GlobalLock(hand);
2138 HGLOBAL Unlock() {
2139 PLATFORM_ASSERT(ptr);
2140 HGLOBAL handCopy = hand;
2141 ::GlobalUnlock(hand);
2142 ptr = 0;
2143 hand = 0;
2144 return handCopy;
2146 void SetClip(UINT uFormat) {
2147 ::SetClipboardData(uFormat, Unlock());
2149 operator bool() const {
2150 return ptr != 0;
2152 SIZE_T Size() {
2153 return ::GlobalSize(hand);
2157 void ScintillaWin::Paste() {
2158 if (!::OpenClipboard(MainHWND()))
2159 return;
2160 UndoGroup ug(pdoc);
2161 const bool isLine = SelectionEmpty() &&
2162 (::IsClipboardFormatAvailable(cfLineSelect) || ::IsClipboardFormatAvailable(cfVSLineTag));
2163 ClearSelection(multiPasteMode == SC_MULTIPASTE_EACH);
2164 bool isRectangular = (::IsClipboardFormatAvailable(cfColumnSelect) != 0);
2166 if (!isRectangular) {
2167 // Evaluate "Borland IDE Block Type" explicitly
2168 GlobalMemory memBorlandSelection(::GetClipboardData(cfBorlandIDEBlockType));
2169 if (memBorlandSelection) {
2170 isRectangular = (memBorlandSelection.Size() == 1) && (static_cast<BYTE *>(memBorlandSelection.ptr)[0] == 0x02);
2171 memBorlandSelection.Unlock();
2174 const PasteShape pasteShape = isRectangular ? pasteRectangular : (isLine ? pasteLine : pasteStream);
2176 // Always use CF_UNICODETEXT if available
2177 GlobalMemory memUSelection(::GetClipboardData(CF_UNICODETEXT));
2178 if (memUSelection) {
2179 wchar_t *uptr = static_cast<wchar_t *>(memUSelection.ptr);
2180 if (uptr) {
2181 unsigned int len;
2182 std::vector<char> putf;
2183 // Default Scintilla behaviour in Unicode mode
2184 if (IsUnicodeMode()) {
2185 unsigned int bytes = static_cast<unsigned int>(memUSelection.Size());
2186 len = UTF8Length(uptr, bytes / 2);
2187 putf.resize(len + 1);
2188 UTF8FromUTF16(uptr, bytes / 2, &putf[0], len);
2189 } else {
2190 // CF_UNICODETEXT available, but not in Unicode mode
2191 // Convert from Unicode to current Scintilla code page
2192 UINT cpDest = CodePageOfDocument();
2193 len = ::WideCharToMultiByte(cpDest, 0, uptr, -1,
2194 NULL, 0, NULL, NULL) - 1; // subtract 0 terminator
2195 putf.resize(len + 1);
2196 ::WideCharToMultiByte(cpDest, 0, uptr, -1,
2197 &putf[0], len + 1, NULL, NULL);
2200 InsertPasteShape(&putf[0], len, pasteShape);
2202 memUSelection.Unlock();
2203 } else {
2204 // CF_UNICODETEXT not available, paste ANSI text
2205 GlobalMemory memSelection(::GetClipboardData(CF_TEXT));
2206 if (memSelection) {
2207 char *ptr = static_cast<char *>(memSelection.ptr);
2208 if (ptr) {
2209 unsigned int bytes = static_cast<unsigned int>(memSelection.Size());
2210 unsigned int len = bytes;
2211 for (unsigned int i = 0; i < bytes; i++) {
2212 if ((len == bytes) && (0 == ptr[i]))
2213 len = i;
2216 // In Unicode mode, convert clipboard text to UTF-8
2217 if (IsUnicodeMode()) {
2218 std::vector<wchar_t> uptr(len+1);
2220 unsigned int ulen = ::MultiByteToWideChar(CP_ACP, 0,
2221 ptr, len, &uptr[0], len+1);
2223 unsigned int mlen = UTF8Length(&uptr[0], ulen);
2224 std::vector<char> putf(mlen+1);
2225 UTF8FromUTF16(&uptr[0], ulen, &putf[0], mlen);
2227 InsertPasteShape(&putf[0], mlen, pasteShape);
2228 } else {
2229 InsertPasteShape(ptr, len, pasteShape);
2232 memSelection.Unlock();
2235 ::CloseClipboard();
2236 Redraw();
2239 void ScintillaWin::CreateCallTipWindow(PRectangle) {
2240 if (!ct.wCallTip.Created()) {
2241 ct.wCallTip = ::CreateWindow(callClassName, TEXT("ACallTip"),
2242 WS_POPUP, 100, 100, 150, 20,
2243 MainHWND(), 0,
2244 GetWindowInstance(MainHWND()),
2245 this);
2246 ct.wDraw = ct.wCallTip;
2250 void ScintillaWin::AddToPopUp(const char *label, int cmd, bool enabled) {
2251 HMENU hmenuPopup = reinterpret_cast<HMENU>(popup.GetID());
2252 if (!label[0])
2253 ::AppendMenuA(hmenuPopup, MF_SEPARATOR, 0, "");
2254 else if (enabled)
2255 ::AppendMenuA(hmenuPopup, MF_STRING, cmd, label);
2256 else
2257 ::AppendMenuA(hmenuPopup, MF_STRING | MF_DISABLED | MF_GRAYED, cmd, label);
2260 void ScintillaWin::ClaimSelection() {
2261 // Windows does not have a primary selection
2264 /// Implement IUnknown
2266 STDMETHODIMP_(ULONG)FormatEnumerator_AddRef(FormatEnumerator *fe);
2267 STDMETHODIMP FormatEnumerator_QueryInterface(FormatEnumerator *fe, REFIID riid, PVOID *ppv) {
2268 //Platform::DebugPrintf("EFE QI");
2269 *ppv = NULL;
2270 if (riid == IID_IUnknown)
2271 *ppv = reinterpret_cast<IEnumFORMATETC *>(fe);
2272 if (riid == IID_IEnumFORMATETC)
2273 *ppv = reinterpret_cast<IEnumFORMATETC *>(fe);
2274 if (!*ppv)
2275 return E_NOINTERFACE;
2276 FormatEnumerator_AddRef(fe);
2277 return S_OK;
2279 STDMETHODIMP_(ULONG)FormatEnumerator_AddRef(FormatEnumerator *fe) {
2280 return ++fe->ref;
2282 STDMETHODIMP_(ULONG)FormatEnumerator_Release(FormatEnumerator *fe) {
2283 fe->ref--;
2284 if (fe->ref > 0)
2285 return fe->ref;
2286 delete fe;
2287 return 0;
2289 /// Implement IEnumFORMATETC
2290 STDMETHODIMP FormatEnumerator_Next(FormatEnumerator *fe, ULONG celt, FORMATETC *rgelt, ULONG *pceltFetched) {
2291 if (rgelt == NULL) return E_POINTER;
2292 unsigned int putPos = 0;
2293 while ((fe->pos < fe->formats.size()) && (putPos < celt)) {
2294 rgelt->cfFormat = fe->formats[fe->pos];
2295 rgelt->ptd = 0;
2296 rgelt->dwAspect = DVASPECT_CONTENT;
2297 rgelt->lindex = -1;
2298 rgelt->tymed = TYMED_HGLOBAL;
2299 rgelt++;
2300 fe->pos++;
2301 putPos++;
2303 if (pceltFetched)
2304 *pceltFetched = putPos;
2305 return putPos ? S_OK : S_FALSE;
2307 STDMETHODIMP FormatEnumerator_Skip(FormatEnumerator *fe, ULONG celt) {
2308 fe->pos += celt;
2309 return S_OK;
2311 STDMETHODIMP FormatEnumerator_Reset(FormatEnumerator *fe) {
2312 fe->pos = 0;
2313 return S_OK;
2315 STDMETHODIMP FormatEnumerator_Clone(FormatEnumerator *fe, IEnumFORMATETC **ppenum) {
2316 FormatEnumerator *pfe;
2317 try {
2318 pfe = new FormatEnumerator(fe->pos, &fe->formats[0], fe->formats.size());
2319 } catch (...) {
2320 return E_OUTOFMEMORY;
2322 return FormatEnumerator_QueryInterface(pfe, IID_IEnumFORMATETC,
2323 reinterpret_cast<void **>(ppenum));
2326 static VFunction *vtFormatEnumerator[] = {
2327 (VFunction *)(FormatEnumerator_QueryInterface),
2328 (VFunction *)(FormatEnumerator_AddRef),
2329 (VFunction *)(FormatEnumerator_Release),
2330 (VFunction *)(FormatEnumerator_Next),
2331 (VFunction *)(FormatEnumerator_Skip),
2332 (VFunction *)(FormatEnumerator_Reset),
2333 (VFunction *)(FormatEnumerator_Clone)
2336 FormatEnumerator::FormatEnumerator(int pos_, CLIPFORMAT formats_[], size_t formatsLen_) {
2337 vtbl = vtFormatEnumerator;
2338 ref = 0; // First QI adds first reference...
2339 pos = pos_;
2340 formats.insert(formats.begin(), formats_, formats_+formatsLen_);
2343 /// Implement IUnknown
2344 STDMETHODIMP DropSource_QueryInterface(DropSource *ds, REFIID riid, PVOID *ppv) {
2345 return ds->sci->QueryInterface(riid, ppv);
2347 STDMETHODIMP_(ULONG)DropSource_AddRef(DropSource *ds) {
2348 return ds->sci->AddRef();
2350 STDMETHODIMP_(ULONG)DropSource_Release(DropSource *ds) {
2351 return ds->sci->Release();
2354 /// Implement IDropSource
2355 STDMETHODIMP DropSource_QueryContinueDrag(DropSource *, BOOL fEsc, DWORD grfKeyState) {
2356 if (fEsc)
2357 return DRAGDROP_S_CANCEL;
2358 if (!(grfKeyState & MK_LBUTTON))
2359 return DRAGDROP_S_DROP;
2360 return S_OK;
2363 STDMETHODIMP DropSource_GiveFeedback(DropSource *, DWORD) {
2364 return DRAGDROP_S_USEDEFAULTCURSORS;
2367 static VFunction *vtDropSource[] = {
2368 (VFunction *)(DropSource_QueryInterface),
2369 (VFunction *)(DropSource_AddRef),
2370 (VFunction *)(DropSource_Release),
2371 (VFunction *)(DropSource_QueryContinueDrag),
2372 (VFunction *)(DropSource_GiveFeedback)
2375 DropSource::DropSource() {
2376 vtbl = vtDropSource;
2377 sci = 0;
2380 /// Implement IUnkown
2381 STDMETHODIMP DataObject_QueryInterface(DataObject *pd, REFIID riid, PVOID *ppv) {
2382 //Platform::DebugPrintf("DO QI %x\n", pd);
2383 return pd->sci->QueryInterface(riid, ppv);
2385 STDMETHODIMP_(ULONG)DataObject_AddRef(DataObject *pd) {
2386 return pd->sci->AddRef();
2388 STDMETHODIMP_(ULONG)DataObject_Release(DataObject *pd) {
2389 return pd->sci->Release();
2391 /// Implement IDataObject
2392 STDMETHODIMP DataObject_GetData(DataObject *pd, FORMATETC *pFEIn, STGMEDIUM *pSTM) {
2393 return pd->sci->GetData(pFEIn, pSTM);
2396 STDMETHODIMP DataObject_GetDataHere(DataObject *, FORMATETC *, STGMEDIUM *) {
2397 //Platform::DebugPrintf("DOB GetDataHere\n");
2398 return E_NOTIMPL;
2401 STDMETHODIMP DataObject_QueryGetData(DataObject *pd, FORMATETC *pFE) {
2402 if (pd->sci->DragIsRectangularOK(pFE->cfFormat) &&
2403 pFE->ptd == 0 &&
2404 (pFE->dwAspect & DVASPECT_CONTENT) != 0 &&
2405 pFE->lindex == -1 &&
2406 (pFE->tymed & TYMED_HGLOBAL) != 0
2408 return S_OK;
2411 bool formatOK = (pFE->cfFormat == CF_TEXT) ||
2412 ((pFE->cfFormat == CF_UNICODETEXT) && pd->sci->IsUnicodeMode());
2413 if (!formatOK ||
2414 pFE->ptd != 0 ||
2415 (pFE->dwAspect & DVASPECT_CONTENT) == 0 ||
2416 pFE->lindex != -1 ||
2417 (pFE->tymed & TYMED_HGLOBAL) == 0
2419 //Platform::DebugPrintf("DOB QueryGetData No %x\n",pFE->cfFormat);
2420 //return DATA_E_FORMATETC;
2421 return S_FALSE;
2423 //Platform::DebugPrintf("DOB QueryGetData OK %x\n",pFE->cfFormat);
2424 return S_OK;
2427 STDMETHODIMP DataObject_GetCanonicalFormatEtc(DataObject *pd, FORMATETC *, FORMATETC *pFEOut) {
2428 //Platform::DebugPrintf("DOB GetCanon\n");
2429 if (pd->sci->IsUnicodeMode())
2430 pFEOut->cfFormat = CF_UNICODETEXT;
2431 else
2432 pFEOut->cfFormat = CF_TEXT;
2433 pFEOut->ptd = 0;
2434 pFEOut->dwAspect = DVASPECT_CONTENT;
2435 pFEOut->lindex = -1;
2436 pFEOut->tymed = TYMED_HGLOBAL;
2437 return S_OK;
2440 STDMETHODIMP DataObject_SetData(DataObject *, FORMATETC *, STGMEDIUM *, BOOL) {
2441 //Platform::DebugPrintf("DOB SetData\n");
2442 return E_FAIL;
2445 STDMETHODIMP DataObject_EnumFormatEtc(DataObject *pd, DWORD dwDirection, IEnumFORMATETC **ppEnum) {
2446 try {
2447 //Platform::DebugPrintf("DOB EnumFormatEtc %d\n", dwDirection);
2448 if (dwDirection != DATADIR_GET) {
2449 *ppEnum = 0;
2450 return E_FAIL;
2452 FormatEnumerator *pfe;
2453 if (pd->sci->IsUnicodeMode()) {
2454 CLIPFORMAT formats[] = {CF_UNICODETEXT, CF_TEXT};
2455 pfe = new FormatEnumerator(0, formats, ELEMENTS(formats));
2456 } else {
2457 CLIPFORMAT formats[] = {CF_TEXT};
2458 pfe = new FormatEnumerator(0, formats, ELEMENTS(formats));
2460 return FormatEnumerator_QueryInterface(pfe, IID_IEnumFORMATETC,
2461 reinterpret_cast<void **>(ppEnum));
2462 } catch (std::bad_alloc &) {
2463 pd->sci->errorStatus = SC_STATUS_BADALLOC;
2464 return E_OUTOFMEMORY;
2465 } catch (...) {
2466 pd->sci->errorStatus = SC_STATUS_FAILURE;
2467 return E_FAIL;
2471 STDMETHODIMP DataObject_DAdvise(DataObject *, FORMATETC *, DWORD, IAdviseSink *, PDWORD) {
2472 //Platform::DebugPrintf("DOB DAdvise\n");
2473 return E_FAIL;
2476 STDMETHODIMP DataObject_DUnadvise(DataObject *, DWORD) {
2477 //Platform::DebugPrintf("DOB DUnadvise\n");
2478 return E_FAIL;
2481 STDMETHODIMP DataObject_EnumDAdvise(DataObject *, IEnumSTATDATA **) {
2482 //Platform::DebugPrintf("DOB EnumDAdvise\n");
2483 return E_FAIL;
2486 static VFunction *vtDataObject[] = {
2487 (VFunction *)(DataObject_QueryInterface),
2488 (VFunction *)(DataObject_AddRef),
2489 (VFunction *)(DataObject_Release),
2490 (VFunction *)(DataObject_GetData),
2491 (VFunction *)(DataObject_GetDataHere),
2492 (VFunction *)(DataObject_QueryGetData),
2493 (VFunction *)(DataObject_GetCanonicalFormatEtc),
2494 (VFunction *)(DataObject_SetData),
2495 (VFunction *)(DataObject_EnumFormatEtc),
2496 (VFunction *)(DataObject_DAdvise),
2497 (VFunction *)(DataObject_DUnadvise),
2498 (VFunction *)(DataObject_EnumDAdvise)
2501 DataObject::DataObject() {
2502 vtbl = vtDataObject;
2503 sci = 0;
2506 /// Implement IUnknown
2507 STDMETHODIMP DropTarget_QueryInterface(DropTarget *dt, REFIID riid, PVOID *ppv) {
2508 //Platform::DebugPrintf("DT QI %x\n", dt);
2509 return dt->sci->QueryInterface(riid, ppv);
2511 STDMETHODIMP_(ULONG)DropTarget_AddRef(DropTarget *dt) {
2512 return dt->sci->AddRef();
2514 STDMETHODIMP_(ULONG)DropTarget_Release(DropTarget *dt) {
2515 return dt->sci->Release();
2518 /// Implement IDropTarget by forwarding to Scintilla
2519 STDMETHODIMP DropTarget_DragEnter(DropTarget *dt, LPDATAOBJECT pIDataSource, DWORD grfKeyState,
2520 POINTL pt, PDWORD pdwEffect) {
2521 try {
2522 return dt->sci->DragEnter(pIDataSource, grfKeyState, pt, pdwEffect);
2523 } catch (...) {
2524 dt->sci->errorStatus = SC_STATUS_FAILURE;
2526 return E_FAIL;
2528 STDMETHODIMP DropTarget_DragOver(DropTarget *dt, DWORD grfKeyState, POINTL pt, PDWORD pdwEffect) {
2529 try {
2530 return dt->sci->DragOver(grfKeyState, pt, pdwEffect);
2531 } catch (...) {
2532 dt->sci->errorStatus = SC_STATUS_FAILURE;
2534 return E_FAIL;
2536 STDMETHODIMP DropTarget_DragLeave(DropTarget *dt) {
2537 try {
2538 return dt->sci->DragLeave();
2539 } catch (...) {
2540 dt->sci->errorStatus = SC_STATUS_FAILURE;
2542 return E_FAIL;
2544 STDMETHODIMP DropTarget_Drop(DropTarget *dt, LPDATAOBJECT pIDataSource, DWORD grfKeyState,
2545 POINTL pt, PDWORD pdwEffect) {
2546 try {
2547 return dt->sci->Drop(pIDataSource, grfKeyState, pt, pdwEffect);
2548 } catch (...) {
2549 dt->sci->errorStatus = SC_STATUS_FAILURE;
2551 return E_FAIL;
2554 static VFunction *vtDropTarget[] = {
2555 (VFunction *)(DropTarget_QueryInterface),
2556 (VFunction *)(DropTarget_AddRef),
2557 (VFunction *)(DropTarget_Release),
2558 (VFunction *)(DropTarget_DragEnter),
2559 (VFunction *)(DropTarget_DragOver),
2560 (VFunction *)(DropTarget_DragLeave),
2561 (VFunction *)(DropTarget_Drop)
2564 DropTarget::DropTarget() {
2565 vtbl = vtDropTarget;
2566 sci = 0;
2570 * DBCS: support Input Method Editor (IME).
2571 * Called when IME Window opened.
2573 void ScintillaWin::ImeStartComposition() {
2574 if (caret.active) {
2575 // Move IME Window to current caret position
2576 HIMC hIMC = ::ImmGetContext(MainHWND());
2577 Point pos = PointMainCaret();
2578 COMPOSITIONFORM CompForm;
2579 CompForm.dwStyle = CFS_POINT;
2580 CompForm.ptCurrentPos.x = static_cast<int>(pos.x);
2581 CompForm.ptCurrentPos.y = static_cast<int>(pos.y);
2583 ::ImmSetCompositionWindow(hIMC, &CompForm);
2585 // Set font of IME window to same as surrounded text.
2586 if (stylesValid) {
2587 // Since the style creation code has been made platform independent,
2588 // The logfont for the IME is recreated here.
2589 int styleHere = (pdoc->StyleAt(sel.MainCaret())) & 31;
2590 LOGFONTW lf = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, L""};
2591 int sizeZoomed = vs.styles[styleHere].size + vs.zoomLevel * SC_FONT_SIZE_MULTIPLIER;
2592 if (sizeZoomed <= 2 * SC_FONT_SIZE_MULTIPLIER) // Hangs if sizeZoomed <= 1
2593 sizeZoomed = 2 * SC_FONT_SIZE_MULTIPLIER;
2594 AutoSurface surface(this);
2595 int deviceHeight = sizeZoomed;
2596 if (surface) {
2597 deviceHeight = (sizeZoomed * surface->LogPixelsY()) / 72;
2599 // The negative is to allow for leading
2600 lf.lfHeight = -(abs(deviceHeight / SC_FONT_SIZE_MULTIPLIER));
2601 lf.lfWeight = vs.styles[styleHere].weight;
2602 lf.lfItalic = static_cast<BYTE>(vs.styles[styleHere].italic ? 1 : 0);
2603 lf.lfCharSet = DEFAULT_CHARSET;
2604 lf.lfFaceName[0] = L'\0';
2605 if (vs.styles[styleHere].fontName) {
2606 const char* fontName = vs.styles[styleHere].fontName;
2607 UTF16FromUTF8(fontName, strlen(fontName)+1, lf.lfFaceName, LF_FACESIZE);
2610 ::ImmSetCompositionFontW(hIMC, &lf);
2612 ::ImmReleaseContext(MainHWND(), hIMC);
2613 // Caret is displayed in IME window. So, caret in Scintilla is useless.
2614 DropCaret();
2618 /** Called when IME Window closed. */
2619 void ScintillaWin::ImeEndComposition() {
2620 ShowCaretAtCurrentPosition();
2623 void ScintillaWin::AddCharBytes(char b0, char b1) {
2625 int inputCodePage = InputCodePage();
2626 if (inputCodePage && IsUnicodeMode()) {
2627 char utfval[4] = "\0\0\0";
2628 char ansiChars[3];
2629 wchar_t wcs[2];
2630 if (b0) { // Two bytes from IME
2631 ansiChars[0] = b0;
2632 ansiChars[1] = b1;
2633 ansiChars[2] = '\0';
2634 ::MultiByteToWideChar(inputCodePage, 0, ansiChars, 2, wcs, 1);
2635 } else {
2636 ansiChars[0] = b1;
2637 ansiChars[1] = '\0';
2638 ::MultiByteToWideChar(inputCodePage, 0, ansiChars, 1, wcs, 1);
2640 unsigned int len = UTF8Length(wcs, 1);
2641 UTF8FromUTF16(wcs, 1, utfval, len);
2642 utfval[len] = '\0';
2643 AddCharUTF(utfval, len ? len : 1);
2644 } else if (b0) {
2645 char dbcsChars[3];
2646 dbcsChars[0] = b0;
2647 dbcsChars[1] = b1;
2648 dbcsChars[2] = '\0';
2649 AddCharUTF(dbcsChars, 2, true);
2650 } else {
2651 AddChar(b1);
2655 void ScintillaWin::GetIntelliMouseParameters() {
2656 // This retrieves the number of lines per scroll as configured inthe Mouse Properties sheet in Control Panel
2657 ::SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, &linesPerScroll, 0);
2660 void ScintillaWin::CopyToClipboard(const SelectionText &selectedText) {
2661 if (!::OpenClipboard(MainHWND()))
2662 return;
2663 ::EmptyClipboard();
2665 GlobalMemory uniText;
2667 // Default Scintilla behaviour in Unicode mode
2668 if (IsUnicodeMode()) {
2669 size_t uchars = UTF16Length(selectedText.Data(),
2670 static_cast<int>(selectedText.LengthWithTerminator()));
2671 uniText.Allocate(2 * uchars);
2672 if (uniText) {
2673 UTF16FromUTF8(selectedText.Data(), selectedText.LengthWithTerminator(),
2674 static_cast<wchar_t *>(uniText.ptr), uchars);
2676 } else {
2677 // Not Unicode mode
2678 // Convert to Unicode using the current Scintilla code page
2679 UINT cpSrc = CodePageFromCharSet(
2680 selectedText.characterSet, selectedText.codePage);
2681 int uLen = ::MultiByteToWideChar(cpSrc, 0, selectedText.Data(),
2682 static_cast<int>(selectedText.LengthWithTerminator()), 0, 0);
2683 uniText.Allocate(2 * uLen);
2684 if (uniText) {
2685 ::MultiByteToWideChar(cpSrc, 0, selectedText.Data(),
2686 static_cast<int>(selectedText.LengthWithTerminator()),
2687 static_cast<wchar_t *>(uniText.ptr), uLen);
2691 if (uniText) {
2692 uniText.SetClip(CF_UNICODETEXT);
2693 } else {
2694 // There was a failure - try to copy at least ANSI text
2695 GlobalMemory ansiText;
2696 ansiText.Allocate(selectedText.LengthWithTerminator());
2697 if (ansiText) {
2698 memcpy(static_cast<char *>(ansiText.ptr), selectedText.Data(), selectedText.LengthWithTerminator());
2699 ansiText.SetClip(CF_TEXT);
2703 if (selectedText.rectangular) {
2704 ::SetClipboardData(cfColumnSelect, 0);
2706 GlobalMemory borlandSelection;
2707 borlandSelection.Allocate(1);
2708 if (borlandSelection) {
2709 static_cast<BYTE *>(borlandSelection.ptr)[0] = 0x02;
2710 borlandSelection.SetClip(cfBorlandIDEBlockType);
2714 if (selectedText.lineCopy) {
2715 ::SetClipboardData(cfLineSelect, 0);
2716 ::SetClipboardData(cfVSLineTag, 0);
2719 ::CloseClipboard();
2722 void ScintillaWin::ScrollMessage(WPARAM wParam) {
2723 //DWORD dwStart = timeGetTime();
2724 //Platform::DebugPrintf("Scroll %x %d\n", wParam, lParam);
2726 SCROLLINFO sci = {};
2727 sci.cbSize = sizeof(sci);
2728 sci.fMask = SIF_ALL;
2730 GetScrollInfo(SB_VERT, &sci);
2732 //Platform::DebugPrintf("ScrollInfo %d mask=%x min=%d max=%d page=%d pos=%d track=%d\n", b,sci.fMask,
2733 //sci.nMin, sci.nMax, sci.nPage, sci.nPos, sci.nTrackPos);
2735 int topLineNew = topLine;
2736 switch (LoWord(wParam)) {
2737 case SB_LINEUP:
2738 topLineNew -= 1;
2739 break;
2740 case SB_LINEDOWN:
2741 topLineNew += 1;
2742 break;
2743 case SB_PAGEUP:
2744 topLineNew -= LinesToScroll(); break;
2745 case SB_PAGEDOWN: topLineNew += LinesToScroll(); break;
2746 case SB_TOP: topLineNew = 0; break;
2747 case SB_BOTTOM: topLineNew = MaxScrollPos(); break;
2748 case SB_THUMBPOSITION: topLineNew = sci.nTrackPos; break;
2749 case SB_THUMBTRACK: topLineNew = sci.nTrackPos; break;
2751 ScrollTo(topLineNew);
2754 void ScintillaWin::HorizontalScrollMessage(WPARAM wParam) {
2755 int xPos = xOffset;
2756 PRectangle rcText = GetTextRectangle();
2757 int pageWidth = static_cast<int>(rcText.Width() * 2 / 3);
2758 switch (LoWord(wParam)) {
2759 case SB_LINEUP:
2760 xPos -= 20;
2761 break;
2762 case SB_LINEDOWN: // May move past the logical end
2763 xPos += 20;
2764 break;
2765 case SB_PAGEUP:
2766 xPos -= pageWidth;
2767 break;
2768 case SB_PAGEDOWN:
2769 xPos += pageWidth;
2770 if (xPos > scrollWidth - rcText.Width()) { // Hit the end exactly
2771 xPos = scrollWidth - static_cast<int>(rcText.Width());
2773 break;
2774 case SB_TOP:
2775 xPos = 0;
2776 break;
2777 case SB_BOTTOM:
2778 xPos = scrollWidth;
2779 break;
2780 case SB_THUMBPOSITION:
2781 case SB_THUMBTRACK: {
2782 // 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 =]
2783 SCROLLINFO si;
2784 si.cbSize = sizeof(si);
2785 si.fMask = SIF_TRACKPOS;
2786 if (GetScrollInfo(SB_HORZ, &si)) {
2787 xPos = si.nTrackPos;
2790 break;
2792 HorizontalScrollTo(xPos);
2796 * Redraw all of text area.
2797 * This paint will not be abandoned.
2799 void ScintillaWin::FullPaint() {
2800 if ((technology == SC_TECHNOLOGY_DEFAULT) || (technology == SC_TECHNOLOGY_DIRECTWRITEDC)) {
2801 HDC hdc = ::GetDC(MainHWND());
2802 FullPaintDC(hdc);
2803 ::ReleaseDC(MainHWND(), hdc);
2804 } else {
2805 FullPaintDC(0);
2810 * Redraw all of text area on the specified DC.
2811 * This paint will not be abandoned.
2813 void ScintillaWin::FullPaintDC(HDC hdc) {
2814 paintState = painting;
2815 rcPaint = GetClientRectangle();
2816 paintingAllText = true;
2817 if (technology == SC_TECHNOLOGY_DEFAULT) {
2818 AutoSurface surfaceWindow(hdc, this);
2819 if (surfaceWindow) {
2820 Paint(surfaceWindow, rcPaint);
2821 surfaceWindow->Release();
2823 } else {
2824 #if defined(USE_D2D)
2825 EnsureRenderTarget(hdc);
2826 AutoSurface surfaceWindow(pRenderTarget, this);
2827 if (surfaceWindow) {
2828 pRenderTarget->BeginDraw();
2829 Paint(surfaceWindow, rcPaint);
2830 surfaceWindow->Release();
2831 HRESULT hr = pRenderTarget->EndDraw();
2832 if (hr == D2DERR_RECREATE_TARGET) {
2833 DropRenderTarget();
2836 #endif
2838 paintState = notPainting;
2841 static bool CompareDevCap(HDC hdc, HDC hOtherDC, int nIndex) {
2842 return ::GetDeviceCaps(hdc, nIndex) == ::GetDeviceCaps(hOtherDC, nIndex);
2845 bool ScintillaWin::IsCompatibleDC(HDC hOtherDC) {
2846 HDC hdc = ::GetDC(MainHWND());
2847 bool isCompatible =
2848 CompareDevCap(hdc, hOtherDC, TECHNOLOGY) &&
2849 CompareDevCap(hdc, hOtherDC, LOGPIXELSY) &&
2850 CompareDevCap(hdc, hOtherDC, LOGPIXELSX) &&
2851 CompareDevCap(hdc, hOtherDC, BITSPIXEL) &&
2852 CompareDevCap(hdc, hOtherDC, PLANES);
2853 ::ReleaseDC(MainHWND(), hdc);
2854 return isCompatible;
2857 DWORD ScintillaWin::EffectFromState(DWORD grfKeyState) const {
2858 // These are the Wordpad semantics.
2859 DWORD dwEffect;
2860 if (inDragDrop == ddDragging) // Internal defaults to move
2861 dwEffect = DROPEFFECT_MOVE;
2862 else
2863 dwEffect = DROPEFFECT_COPY;
2864 if (grfKeyState & MK_ALT)
2865 dwEffect = DROPEFFECT_MOVE;
2866 if (grfKeyState & MK_CONTROL)
2867 dwEffect = DROPEFFECT_COPY;
2868 return dwEffect;
2871 /// Implement IUnknown
2872 STDMETHODIMP ScintillaWin::QueryInterface(REFIID riid, PVOID *ppv) {
2873 *ppv = NULL;
2874 if (riid == IID_IUnknown)
2875 *ppv = reinterpret_cast<IDropTarget *>(&dt);
2876 if (riid == IID_IDropSource)
2877 *ppv = reinterpret_cast<IDropSource *>(&ds);
2878 if (riid == IID_IDropTarget)
2879 *ppv = reinterpret_cast<IDropTarget *>(&dt);
2880 if (riid == IID_IDataObject)
2881 *ppv = reinterpret_cast<IDataObject *>(&dob);
2882 if (!*ppv)
2883 return E_NOINTERFACE;
2884 return S_OK;
2887 STDMETHODIMP_(ULONG) ScintillaWin::AddRef() {
2888 return 1;
2891 STDMETHODIMP_(ULONG) ScintillaWin::Release() {
2892 return 1;
2895 /// Implement IDropTarget
2896 STDMETHODIMP ScintillaWin::DragEnter(LPDATAOBJECT pIDataSource, DWORD grfKeyState,
2897 POINTL, PDWORD pdwEffect) {
2898 if (pIDataSource == NULL)
2899 return E_POINTER;
2900 FORMATETC fmtu = {CF_UNICODETEXT, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
2901 HRESULT hrHasUText = pIDataSource->QueryGetData(&fmtu);
2902 hasOKText = (hrHasUText == S_OK);
2903 if (!hasOKText) {
2904 FORMATETC fmte = {CF_TEXT, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
2905 HRESULT hrHasText = pIDataSource->QueryGetData(&fmte);
2906 hasOKText = (hrHasText == S_OK);
2908 if (!hasOKText) {
2909 *pdwEffect = DROPEFFECT_NONE;
2910 return S_OK;
2913 *pdwEffect = EffectFromState(grfKeyState);
2914 return S_OK;
2917 STDMETHODIMP ScintillaWin::DragOver(DWORD grfKeyState, POINTL pt, PDWORD pdwEffect) {
2918 try {
2919 if (!hasOKText || pdoc->IsReadOnly()) {
2920 *pdwEffect = DROPEFFECT_NONE;
2921 return S_OK;
2924 *pdwEffect = EffectFromState(grfKeyState);
2926 // Update the cursor.
2927 POINT rpt = {pt.x, pt.y};
2928 ::ScreenToClient(MainHWND(), &rpt);
2929 SetDragPosition(SPositionFromLocation(PointFromPOINT(rpt), false, false, UserVirtualSpace()));
2931 return S_OK;
2932 } catch (...) {
2933 errorStatus = SC_STATUS_FAILURE;
2935 return E_FAIL;
2938 STDMETHODIMP ScintillaWin::DragLeave() {
2939 try {
2940 SetDragPosition(SelectionPosition(invalidPosition));
2941 return S_OK;
2942 } catch (...) {
2943 errorStatus = SC_STATUS_FAILURE;
2945 return E_FAIL;
2948 STDMETHODIMP ScintillaWin::Drop(LPDATAOBJECT pIDataSource, DWORD grfKeyState,
2949 POINTL pt, PDWORD pdwEffect) {
2950 try {
2951 *pdwEffect = EffectFromState(grfKeyState);
2953 if (pIDataSource == NULL)
2954 return E_POINTER;
2956 SetDragPosition(SelectionPosition(invalidPosition));
2958 STGMEDIUM medium = {0, {0}, 0};
2960 std::vector<char> data; // Includes terminating NUL
2962 FORMATETC fmtu = {CF_UNICODETEXT, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
2963 HRESULT hr = pIDataSource->GetData(&fmtu, &medium);
2964 if (SUCCEEDED(hr) && medium.hGlobal) {
2965 GlobalMemory memUDrop(medium.hGlobal);
2966 wchar_t *udata = static_cast<wchar_t *>(memUDrop.ptr);
2967 if (udata) {
2968 if (IsUnicodeMode()) {
2969 int tlen = static_cast<int>(memUDrop.Size());
2970 // Convert UTF-16 to UTF-8
2971 int dataLen = UTF8Length(udata, tlen/2);
2972 data.resize(dataLen+1);
2973 UTF8FromUTF16(udata, tlen/2, &data[0], dataLen);
2974 } else {
2975 // Convert UTF-16 to ANSI
2977 // Default Scintilla behavior in Unicode mode
2978 // CF_UNICODETEXT available, but not in Unicode mode
2979 // Convert from Unicode to current Scintilla code page
2980 UINT cpDest = CodePageOfDocument();
2981 int tlen = ::WideCharToMultiByte(cpDest, 0, udata, -1,
2982 NULL, 0, NULL, NULL) - 1; // subtract 0 terminator
2983 data.resize(tlen + 1);
2984 ::WideCharToMultiByte(cpDest, 0, udata, -1,
2985 &data[0], tlen + 1, NULL, NULL);
2988 memUDrop.Unlock();
2989 } else {
2990 FORMATETC fmte = {CF_TEXT, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
2991 hr = pIDataSource->GetData(&fmte, &medium);
2992 if (SUCCEEDED(hr) && medium.hGlobal) {
2993 GlobalMemory memDrop(medium.hGlobal);
2994 const char *cdata = static_cast<char *>(memDrop.ptr);
2995 if (cdata)
2996 data.assign(cdata, cdata+strlen(cdata)+1);
2997 memDrop.Unlock();
3001 if (!SUCCEEDED(hr) || data.empty()) {
3002 //Platform::DebugPrintf("Bad data format: 0x%x\n", hres);
3003 return hr;
3006 FORMATETC fmtr = {cfColumnSelect, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
3007 HRESULT hrRectangular = pIDataSource->QueryGetData(&fmtr);
3009 POINT rpt = {pt.x, pt.y};
3010 ::ScreenToClient(MainHWND(), &rpt);
3011 SelectionPosition movePos = SPositionFromLocation(PointFromPOINT(rpt), false, false, UserVirtualSpace());
3013 DropAt(movePos, &data[0], data.size() - 1, *pdwEffect == DROPEFFECT_MOVE, hrRectangular == S_OK);
3015 // Free data
3016 if (medium.pUnkForRelease != NULL)
3017 medium.pUnkForRelease->Release();
3018 else
3019 ::GlobalFree(medium.hGlobal);
3021 return S_OK;
3022 } catch (...) {
3023 errorStatus = SC_STATUS_FAILURE;
3025 return E_FAIL;
3028 /// Implement important part of IDataObject
3029 STDMETHODIMP ScintillaWin::GetData(FORMATETC *pFEIn, STGMEDIUM *pSTM) {
3030 bool formatOK = (pFEIn->cfFormat == CF_TEXT) ||
3031 ((pFEIn->cfFormat == CF_UNICODETEXT) && IsUnicodeMode());
3032 if (!formatOK ||
3033 pFEIn->ptd != 0 ||
3034 (pFEIn->dwAspect & DVASPECT_CONTENT) == 0 ||
3035 pFEIn->lindex != -1 ||
3036 (pFEIn->tymed & TYMED_HGLOBAL) == 0
3038 //Platform::DebugPrintf("DOB GetData No %d %x %x fmt=%x\n", lenDrag, pFEIn, pSTM, pFEIn->cfFormat);
3039 return DATA_E_FORMATETC;
3041 pSTM->tymed = TYMED_HGLOBAL;
3042 //Platform::DebugPrintf("DOB GetData OK %d %x %x\n", lenDrag, pFEIn, pSTM);
3044 GlobalMemory text;
3045 if (pFEIn->cfFormat == CF_UNICODETEXT) {
3046 size_t uchars = UTF16Length(drag.Data(), static_cast<int>(drag.LengthWithTerminator()));
3047 text.Allocate(2 * uchars);
3048 if (text) {
3049 UTF16FromUTF8(drag.Data(), drag.LengthWithTerminator(),
3050 static_cast<wchar_t *>(text.ptr), uchars);
3052 } else {
3053 text.Allocate(drag.LengthWithTerminator());
3054 if (text) {
3055 memcpy(static_cast<char *>(text.ptr), drag.Data(), drag.LengthWithTerminator());
3058 pSTM->hGlobal = text ? text.Unlock() : 0;
3059 pSTM->pUnkForRelease = 0;
3060 return S_OK;
3063 bool ScintillaWin::Register(HINSTANCE hInstance_) {
3065 hInstance = hInstance_;
3066 bool result;
3068 // Register the Scintilla class
3069 // Register Scintilla as a wide character window
3070 WNDCLASSEXW wndclass;
3071 wndclass.cbSize = sizeof(wndclass);
3072 wndclass.style = CS_GLOBALCLASS | CS_HREDRAW | CS_VREDRAW;
3073 wndclass.lpfnWndProc = ScintillaWin::SWndProc;
3074 wndclass.cbClsExtra = 0;
3075 wndclass.cbWndExtra = sizeof(ScintillaWin *);
3076 wndclass.hInstance = hInstance;
3077 wndclass.hIcon = NULL;
3078 wndclass.hCursor = NULL;
3079 wndclass.hbrBackground = NULL;
3080 wndclass.lpszMenuName = NULL;
3081 wndclass.lpszClassName = L"Scintilla";
3082 wndclass.hIconSm = 0;
3083 scintillaClassAtom = ::RegisterClassExW(&wndclass);
3084 result = 0 != scintillaClassAtom;
3086 if (result) {
3087 // Register the CallTip class
3088 WNDCLASSEX wndclassc;
3089 wndclassc.cbSize = sizeof(wndclassc);
3090 wndclassc.style = CS_GLOBALCLASS | CS_HREDRAW | CS_VREDRAW;
3091 wndclassc.cbClsExtra = 0;
3092 wndclassc.cbWndExtra = sizeof(ScintillaWin *);
3093 wndclassc.hInstance = hInstance;
3094 wndclassc.hIcon = NULL;
3095 wndclassc.hbrBackground = NULL;
3096 wndclassc.lpszMenuName = NULL;
3097 wndclassc.lpfnWndProc = ScintillaWin::CTWndProc;
3098 wndclassc.hCursor = ::LoadCursor(NULL, IDC_ARROW);
3099 wndclassc.lpszClassName = callClassName;
3100 wndclassc.hIconSm = 0;
3102 callClassAtom = ::RegisterClassEx(&wndclassc);
3103 result = 0 != callClassAtom;
3106 return result;
3109 bool ScintillaWin::Unregister() {
3110 bool result = true;
3111 if (0 != scintillaClassAtom) {
3112 if (::UnregisterClass(MAKEINTATOM(scintillaClassAtom), hInstance) == 0) {
3113 result = false;
3115 scintillaClassAtom = 0;
3117 if (0 != callClassAtom) {
3118 if (::UnregisterClass(MAKEINTATOM(callClassAtom), hInstance) == 0) {
3119 result = false;
3121 callClassAtom = 0;
3123 return result;
3126 bool ScintillaWin::HasCaretSizeChanged() const {
3127 if (
3128 ( (0 != vs.caretWidth) && (sysCaretWidth != vs.caretWidth) )
3129 || ((0 != vs.lineHeight) && (sysCaretHeight != vs.lineHeight))
3131 return true;
3133 return false;
3136 BOOL ScintillaWin::CreateSystemCaret() {
3137 sysCaretWidth = vs.caretWidth;
3138 if (0 == sysCaretWidth) {
3139 sysCaretWidth = 1;
3141 sysCaretHeight = vs.lineHeight;
3142 int bitmapSize = (((sysCaretWidth + 15) & ~15) >> 3) *
3143 sysCaretHeight;
3144 std::vector<char> bits(bitmapSize);
3145 sysCaretBitmap = ::CreateBitmap(sysCaretWidth, sysCaretHeight, 1,
3146 1, reinterpret_cast<BYTE *>(&bits[0]));
3147 BOOL retval = ::CreateCaret(
3148 MainHWND(), sysCaretBitmap,
3149 sysCaretWidth, sysCaretHeight);
3150 if (technology == SC_TECHNOLOGY_DEFAULT) {
3151 // System caret interferes with Direct2D drawing so only show it for GDI.
3152 ::ShowCaret(MainHWND());
3154 return retval;
3157 BOOL ScintillaWin::DestroySystemCaret() {
3158 ::HideCaret(MainHWND());
3159 BOOL retval = ::DestroyCaret();
3160 if (sysCaretBitmap) {
3161 ::DeleteObject(sysCaretBitmap);
3162 sysCaretBitmap = 0;
3164 return retval;
3167 sptr_t PASCAL ScintillaWin::CTWndProc(
3168 HWND hWnd, UINT iMessage, WPARAM wParam, sptr_t lParam) {
3169 // Find C++ object associated with window.
3170 ScintillaWin *sciThis = reinterpret_cast<ScintillaWin *>(PointerFromWindow(hWnd));
3171 try {
3172 // ctp will be zero if WM_CREATE not seen yet
3173 if (sciThis == 0) {
3174 if (iMessage == WM_CREATE) {
3175 // Associate CallTip object with window
3176 CREATESTRUCT *pCreate = reinterpret_cast<CREATESTRUCT *>(lParam);
3177 SetWindowPointer(hWnd, pCreate->lpCreateParams);
3178 return 0;
3179 } else {
3180 return ::DefWindowProc(hWnd, iMessage, wParam, lParam);
3182 } else {
3183 if (iMessage == WM_NCDESTROY) {
3184 ::SetWindowLong(hWnd, 0, 0);
3185 return ::DefWindowProc(hWnd, iMessage, wParam, lParam);
3186 } else if (iMessage == WM_PAINT) {
3187 PAINTSTRUCT ps;
3188 ::BeginPaint(hWnd, &ps);
3189 Surface *surfaceWindow = Surface::Allocate(sciThis->technology);
3190 if (surfaceWindow) {
3191 #if defined(USE_D2D)
3192 ID2D1HwndRenderTarget *pCTRenderTarget = 0;
3193 #endif
3194 RECT rc;
3195 GetClientRect(hWnd, &rc);
3196 // Create a Direct2D render target.
3197 if (sciThis->technology == SC_TECHNOLOGY_DEFAULT) {
3198 surfaceWindow->Init(ps.hdc, hWnd);
3199 } else {
3200 #if defined(USE_D2D)
3201 D2D1_HWND_RENDER_TARGET_PROPERTIES dhrtp;
3202 dhrtp.hwnd = hWnd;
3203 dhrtp.pixelSize = D2D1::SizeU(rc.right - rc.left, rc.bottom - rc.top);
3204 dhrtp.presentOptions = (sciThis->technology == SC_TECHNOLOGY_DIRECTWRITERETAIN) ?
3205 D2D1_PRESENT_OPTIONS_RETAIN_CONTENTS : D2D1_PRESENT_OPTIONS_NONE;
3207 D2D1_RENDER_TARGET_PROPERTIES drtp;
3208 drtp.type = D2D1_RENDER_TARGET_TYPE_DEFAULT;
3209 drtp.pixelFormat.format = DXGI_FORMAT_UNKNOWN;
3210 drtp.pixelFormat.alphaMode = D2D1_ALPHA_MODE_UNKNOWN;
3211 drtp.dpiX = 96.0;
3212 drtp.dpiY = 96.0;
3213 drtp.usage = D2D1_RENDER_TARGET_USAGE_NONE;
3214 drtp.minLevel = D2D1_FEATURE_LEVEL_DEFAULT;
3216 if (!SUCCEEDED(pD2DFactory->CreateHwndRenderTarget(drtp, dhrtp, &pCTRenderTarget))) {
3217 surfaceWindow->Release();
3218 delete surfaceWindow;
3219 ::EndPaint(hWnd, &ps);
3220 return 0;
3222 surfaceWindow->Init(pCTRenderTarget, hWnd);
3223 pCTRenderTarget->BeginDraw();
3224 #endif
3226 surfaceWindow->SetUnicodeMode(SC_CP_UTF8 == sciThis->ct.codePage);
3227 surfaceWindow->SetDBCSMode(sciThis->ct.codePage);
3228 sciThis->ct.PaintCT(surfaceWindow);
3229 #if defined(USE_D2D)
3230 if (pCTRenderTarget)
3231 pCTRenderTarget->EndDraw();
3232 #endif
3233 surfaceWindow->Release();
3234 delete surfaceWindow;
3235 #if defined(USE_D2D)
3236 if (pCTRenderTarget)
3237 pCTRenderTarget->Release();
3238 #endif
3240 ::EndPaint(hWnd, &ps);
3241 return 0;
3242 } else if ((iMessage == WM_NCLBUTTONDOWN) || (iMessage == WM_NCLBUTTONDBLCLK)) {
3243 POINT pt;
3244 pt.x = static_cast<short>(LOWORD(lParam));
3245 pt.y = static_cast<short>(HIWORD(lParam));
3246 ScreenToClient(hWnd, &pt);
3247 sciThis->ct.MouseClick(PointFromPOINT(pt));
3248 sciThis->CallTipClick();
3249 return 0;
3250 } else if (iMessage == WM_LBUTTONDOWN) {
3251 // This does not fire due to the hit test code
3252 sciThis->ct.MouseClick(Point::FromLong(static_cast<long>(lParam)));
3253 sciThis->CallTipClick();
3254 return 0;
3255 } else if (iMessage == WM_SETCURSOR) {
3256 ::SetCursor(::LoadCursor(NULL, IDC_ARROW));
3257 return 0;
3258 } else if (iMessage == WM_NCHITTEST) {
3259 return HTCAPTION;
3260 } else {
3261 return ::DefWindowProc(hWnd, iMessage, wParam, lParam);
3264 } catch (...) {
3265 sciThis->errorStatus = SC_STATUS_FAILURE;
3267 return ::DefWindowProc(hWnd, iMessage, wParam, lParam);
3270 sptr_t ScintillaWin::DirectFunction(
3271 sptr_t ptr, UINT iMessage, uptr_t wParam, sptr_t lParam) {
3272 PLATFORM_ASSERT(::GetCurrentThreadId() == ::GetWindowThreadProcessId(reinterpret_cast<ScintillaWin *>(ptr)->MainHWND(), NULL));
3273 return reinterpret_cast<ScintillaWin *>(ptr)->WndProc(iMessage, wParam, lParam);
3276 extern "C"
3277 #ifndef STATIC_BUILD
3278 __declspec(dllexport)
3279 #endif
3280 sptr_t __stdcall Scintilla_DirectFunction(
3281 ScintillaWin *sci, UINT iMessage, uptr_t wParam, sptr_t lParam) {
3282 return sci->WndProc(iMessage, wParam, lParam);
3285 sptr_t PASCAL ScintillaWin::SWndProc(
3286 HWND hWnd, UINT iMessage, WPARAM wParam, sptr_t lParam) {
3287 //Platform::DebugPrintf("S W:%x M:%x WP:%x L:%x\n", hWnd, iMessage, wParam, lParam);
3289 // Find C++ object associated with window.
3290 ScintillaWin *sci = reinterpret_cast<ScintillaWin *>(PointerFromWindow(hWnd));
3291 // sci will be zero if WM_CREATE not seen yet
3292 if (sci == 0) {
3293 try {
3294 if (iMessage == WM_CREATE) {
3295 // Create C++ object associated with window
3296 sci = new ScintillaWin(hWnd);
3297 SetWindowPointer(hWnd, sci);
3298 return sci->WndProc(iMessage, wParam, lParam);
3300 } catch (...) {
3302 return ::DefWindowProc(hWnd, iMessage, wParam, lParam);
3303 } else {
3304 if (iMessage == WM_NCDESTROY) {
3305 try {
3306 sci->Finalise();
3307 delete sci;
3308 } catch (...) {
3310 ::SetWindowLong(hWnd, 0, 0);
3311 return ::DefWindowProc(hWnd, iMessage, wParam, lParam);
3312 } else {
3313 return sci->WndProc(iMessage, wParam, lParam);
3318 // This function is externally visible so it can be called from container when building statically.
3319 // Must be called once only.
3320 int Scintilla_RegisterClasses(void *hInstance) {
3321 Platform_Initialise(hInstance);
3322 bool result = ScintillaWin::Register(reinterpret_cast<HINSTANCE>(hInstance));
3323 #ifdef SCI_LEXER
3324 Scintilla_LinkLexers();
3325 #endif
3326 return result;
3329 static int ResourcesRelease(bool fromDllMain) {
3330 bool result = ScintillaWin::Unregister();
3331 if (commctrl32) {
3332 FreeLibrary(commctrl32);
3333 commctrl32 = NULL;
3335 Platform_Finalise(fromDllMain);
3336 return result;
3339 // This function is externally visible so it can be called from container when building statically.
3340 int Scintilla_ReleaseResources() {
3341 return ResourcesRelease(false);
3344 #ifndef STATIC_BUILD
3345 extern "C" int APIENTRY DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpvReserved) {
3346 //Platform::DebugPrintf("Scintilla::DllMain %d %d\n", hInstance, dwReason);
3347 if (dwReason == DLL_PROCESS_ATTACH) {
3348 if (!Scintilla_RegisterClasses(hInstance))
3349 return FALSE;
3350 } else if (dwReason == DLL_PROCESS_DETACH) {
3351 if (lpvReserved == NULL) {
3352 ResourcesRelease(true);
3355 return TRUE;
3357 #endif