Update Scintilla to version 3.5.7
[TortoiseGit.git] / ext / scintilla / win32 / ScintillaWin.cxx
blob56ef1e3dbbe0966d55235cb5c3d5fc1eaedd4075
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 <cmath>
16 #include <stdexcept>
17 #include <new>
18 #include <string>
19 #include <vector>
20 #include <map>
21 #include <algorithm>
23 #undef _WIN32_WINNT
24 #define _WIN32_WINNT 0x0500
25 #undef WINVER
26 #define WINVER 0x0500
27 #include <windows.h>
28 #include <commctrl.h>
29 #include <richedit.h>
30 #include <windowsx.h>
31 #include <zmouse.h>
32 #include <ole2.h>
34 #if defined(NTDDI_WIN7) && !defined(DISABLE_D2D)
35 #define USE_D2D 1
36 #endif
38 #if defined(USE_D2D)
39 #include <d2d1.h>
40 #include <dwrite.h>
41 #endif
43 #include "Platform.h"
45 #include "ILexer.h"
46 #include "Scintilla.h"
48 #ifdef SCI_LEXER
49 #include "SciLexer.h"
50 #endif
51 #include "StringCopy.h"
52 #ifdef SCI_LEXER
53 #include "LexerModule.h"
54 #endif
55 #include "SplitVector.h"
56 #include "Partitioning.h"
57 #include "RunStyles.h"
58 #include "ContractionState.h"
59 #include "CellBuffer.h"
60 #include "CallTip.h"
61 #include "KeyMap.h"
62 #include "Indicator.h"
63 #include "XPM.h"
64 #include "LineMarker.h"
65 #include "Style.h"
66 #include "ViewStyle.h"
67 #include "CharClassify.h"
68 #include "Decoration.h"
69 #include "CaseFolder.h"
70 #include "Document.h"
71 #include "CaseConvert.h"
72 #include "UniConversion.h"
73 #include "Selection.h"
74 #include "PositionCache.h"
75 #include "EditModel.h"
76 #include "MarginView.h"
77 #include "EditView.h"
78 #include "Editor.h"
80 #include "AutoComplete.h"
81 #include "ScintillaBase.h"
83 #ifdef SCI_LEXER
84 #include "ExternalLexer.h"
85 #endif
87 #include "PlatWin.h"
88 #include "HanjaDic.h"
90 #ifndef SPI_GETWHEELSCROLLLINES
91 #define SPI_GETWHEELSCROLLLINES 104
92 #endif
94 #ifndef WM_UNICHAR
95 #define WM_UNICHAR 0x0109
96 #endif
98 #ifndef UNICODE_NOCHAR
99 #define UNICODE_NOCHAR 0xFFFF
100 #endif
102 #ifndef MK_ALT
103 #define MK_ALT 32
104 #endif
106 #define SC_WIN_IDLE 5001
108 #define SC_INDICATOR_INPUT INDIC_IME
109 #define SC_INDICATOR_TARGET INDIC_IME+1
110 #define SC_INDICATOR_CONVERTED INDIC_IME+2
111 #define SC_INDICATOR_UNKNOWN INDIC_IME_MAX
113 typedef BOOL (WINAPI *TrackMouseEventSig)(LPTRACKMOUSEEVENT);
114 typedef UINT_PTR (WINAPI *SetCoalescableTimerSig)(HWND hwnd, UINT_PTR nIDEvent,
115 UINT uElapse, TIMERPROC lpTimerFunc, ULONG uToleranceDelay);
117 // GCC has trouble with the standard COM ABI so do it the old C way with explicit vtables.
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 int TargetAsUTF8(char *text);
248 int EncodedFromUTF8(char *utf8, char *encoded) const;
249 sptr_t WndPaint(uptr_t wParam);
251 sptr_t HandleCompositionWindowed(uptr_t wParam, sptr_t lParam);
252 sptr_t HandleCompositionInline(uptr_t wParam, sptr_t lParam);
253 static bool KoreanIME();
254 void MoveImeCarets(int offset);
255 void DrawImeIndicator(int indicator, int len);
256 void SetCandidateWindowPos();
257 void SelectionToHangul();
258 void EscapeHanja();
259 void ToggleHanja();
261 UINT CodePageOfDocument() const;
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 GetIntelliMouseParameters();
300 virtual void CopyToClipboard(const SelectionText &selectedText);
301 void ScrollMessage(WPARAM wParam);
302 void HorizontalScrollMessage(WPARAM wParam);
303 void FullPaint();
304 void FullPaintDC(HDC dc);
305 bool IsCompatibleDC(HDC dc);
306 DWORD EffectFromState(DWORD grfKeyState) const;
308 virtual int SetScrollInfo(int nBar, LPCSCROLLINFO lpsi, BOOL bRedraw);
309 virtual bool GetScrollInfo(int nBar, LPSCROLLINFO lpsi);
310 void ChangeScrollPos(int barType, int pos);
311 sptr_t GetTextLength();
312 sptr_t GetText(uptr_t wParam, sptr_t lParam);
314 public:
315 // Public for benefit of Scintilla_DirectFunction
316 virtual sptr_t WndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam);
318 /// Implement IUnknown
319 STDMETHODIMP QueryInterface(REFIID riid, PVOID *ppv);
320 STDMETHODIMP_(ULONG)AddRef();
321 STDMETHODIMP_(ULONG)Release();
323 /// Implement IDropTarget
324 STDMETHODIMP DragEnter(LPDATAOBJECT pIDataSource, DWORD grfKeyState,
325 POINTL pt, PDWORD pdwEffect);
326 STDMETHODIMP DragOver(DWORD grfKeyState, POINTL pt, PDWORD pdwEffect);
327 STDMETHODIMP DragLeave();
328 STDMETHODIMP Drop(LPDATAOBJECT pIDataSource, DWORD grfKeyState,
329 POINTL pt, PDWORD pdwEffect);
331 /// Implement important part of IDataObject
332 STDMETHODIMP GetData(FORMATETC *pFEIn, STGMEDIUM *pSTM);
334 static bool Register(HINSTANCE hInstance_);
335 static bool Unregister();
337 friend class DropSource;
338 friend class DataObject;
339 friend class DropTarget;
340 bool DragIsRectangularOK(CLIPFORMAT fmt) const {
341 return drag.rectangular && (fmt == cfColumnSelect);
344 private:
345 // For use in creating a system caret
346 bool HasCaretSizeChanged() const;
347 BOOL CreateSystemCaret();
348 BOOL DestroySystemCaret();
349 HBITMAP sysCaretBitmap;
350 int sysCaretWidth;
351 int sysCaretHeight;
354 HINSTANCE ScintillaWin::hInstance = 0;
355 ATOM ScintillaWin::scintillaClassAtom = 0;
356 ATOM ScintillaWin::callClassAtom = 0;
358 ScintillaWin::ScintillaWin(HWND hwnd) {
360 lastKeyDownConsumed = false;
362 capturedMouse = false;
363 trackedMouseLeave = false;
364 TrackMouseEventFn = 0;
365 SetCoalescableTimerFn = 0;
367 linesPerScroll = 0;
368 wheelDelta = 0; // Wheel delta from roll
370 hRgnUpdate = 0;
372 hasOKText = false;
374 // There does not seem to be a real standard for indicating that the clipboard
375 // contains a rectangular selection, so copy Developer Studio and Borland Delphi.
376 cfColumnSelect = static_cast<CLIPFORMAT>(
377 ::RegisterClipboardFormat(TEXT("MSDEVColumnSelect")));
378 cfBorlandIDEBlockType = static_cast<CLIPFORMAT>(
379 ::RegisterClipboardFormat(TEXT("Borland IDE Block Type")));
381 // Likewise for line-copy (copies a full line when no text is selected)
382 cfLineSelect = static_cast<CLIPFORMAT>(
383 ::RegisterClipboardFormat(TEXT("MSDEVLineSelect")));
384 cfVSLineTag = static_cast<CLIPFORMAT>(
385 ::RegisterClipboardFormat(TEXT("VisualStudioEditorOperationsLineCutCopyClipboardTag")));
386 hrOle = E_FAIL;
388 wMain = hwnd;
390 dob.sci = this;
391 ds.sci = this;
392 dt.sci = this;
394 sysCaretBitmap = 0;
395 sysCaretWidth = 0;
396 sysCaretHeight = 0;
398 #if defined(USE_D2D)
399 pRenderTarget = 0;
400 renderTargetValid = true;
401 #endif
403 caret.period = ::GetCaretBlinkTime();
404 if (caret.period < 0)
405 caret.period = 0;
407 Initialise();
410 ScintillaWin::~ScintillaWin() {}
412 void ScintillaWin::Initialise() {
413 // Initialize COM. If the app has already done this it will have
414 // no effect. If the app hasn't, we really shouldn't ask them to call
415 // it just so this internal feature works.
416 hrOle = ::OleInitialize(NULL);
418 // Find TrackMouseEvent which is available on Windows > 95
419 HMODULE user32 = ::GetModuleHandle(TEXT("user32.dll"));
420 if (user32) {
421 TrackMouseEventFn = (TrackMouseEventSig)::GetProcAddress(user32, "TrackMouseEvent");
422 SetCoalescableTimerFn = (SetCoalescableTimerSig)::GetProcAddress(user32, "SetCoalescableTimer");
424 if (TrackMouseEventFn == NULL) {
425 // Windows 95 has an emulation in comctl32.dll:_TrackMouseEvent
426 if (!commctrl32)
427 commctrl32 = ::LoadLibrary(TEXT("comctl32.dll"));
428 if (commctrl32 != NULL) {
429 TrackMouseEventFn = (TrackMouseEventSig)
430 ::GetProcAddress(commctrl32, "_TrackMouseEvent");
433 for (TickReason tr = tickCaret; tr <= tickDwell; tr = static_cast<TickReason>(tr + 1)) {
434 timers[tr] = 0;
436 vs.indicators[SC_INDICATOR_UNKNOWN] = Indicator(INDIC_HIDDEN, ColourDesired(0, 0, 0xff));
437 vs.indicators[SC_INDICATOR_INPUT] = Indicator(INDIC_DOTS, ColourDesired(0, 0, 0xff));
438 vs.indicators[SC_INDICATOR_CONVERTED] = Indicator(INDIC_COMPOSITIONTHICK, ColourDesired(0, 0, 0xff));
439 vs.indicators[SC_INDICATOR_TARGET] = Indicator(INDIC_STRAIGHTBOX, ColourDesired(0, 0, 0xff));
442 void ScintillaWin::Finalise() {
443 ScintillaBase::Finalise();
444 for (TickReason tr = tickCaret; tr <= tickDwell; tr = static_cast<TickReason>(tr + 1)) {
445 FineTickerCancel(tr);
447 SetIdle(false);
448 #if defined(USE_D2D)
449 DropRenderTarget();
450 #endif
451 ::RevokeDragDrop(MainHWND());
452 if (SUCCEEDED(hrOle)) {
453 ::OleUninitialize();
457 #if defined(USE_D2D)
459 void ScintillaWin::EnsureRenderTarget(HDC hdc) {
460 if (!renderTargetValid) {
461 DropRenderTarget();
462 renderTargetValid = true;
464 if (pD2DFactory && !pRenderTarget) {
465 RECT rc;
466 HWND hw = MainHWND();
467 GetClientRect(hw, &rc);
469 D2D1_SIZE_U size = D2D1::SizeU(rc.right - rc.left, rc.bottom - rc.top);
471 // Create a Direct2D render target.
472 #if 1
473 D2D1_RENDER_TARGET_PROPERTIES drtp;
474 drtp.type = D2D1_RENDER_TARGET_TYPE_DEFAULT;
475 drtp.pixelFormat.format = DXGI_FORMAT_UNKNOWN;
476 drtp.pixelFormat.alphaMode = D2D1_ALPHA_MODE_UNKNOWN;
477 drtp.dpiX = 96.0;
478 drtp.dpiY = 96.0;
479 drtp.usage = D2D1_RENDER_TARGET_USAGE_NONE;
480 drtp.minLevel = D2D1_FEATURE_LEVEL_DEFAULT;
482 if (technology == SC_TECHNOLOGY_DIRECTWRITEDC) {
483 // Explicit pixel format needed.
484 drtp.pixelFormat = D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM,
485 D2D1_ALPHA_MODE_IGNORE);
487 ID2D1DCRenderTarget *pDCRT = NULL;
488 HRESULT hr = pD2DFactory->CreateDCRenderTarget(&drtp, &pDCRT);
489 if (SUCCEEDED(hr)) {
490 pRenderTarget = pDCRT;
491 } else {
492 Platform::DebugPrintf("Failed CreateDCRenderTarget 0x%x\n", hr);
493 pRenderTarget = NULL;
496 } else {
497 D2D1_HWND_RENDER_TARGET_PROPERTIES dhrtp;
498 dhrtp.hwnd = hw;
499 dhrtp.pixelSize = size;
500 dhrtp.presentOptions = (technology == SC_TECHNOLOGY_DIRECTWRITERETAIN) ?
501 D2D1_PRESENT_OPTIONS_RETAIN_CONTENTS : D2D1_PRESENT_OPTIONS_NONE;
503 ID2D1HwndRenderTarget *pHwndRenderTarget = NULL;
504 HRESULT hr = pD2DFactory->CreateHwndRenderTarget(drtp, dhrtp, &pHwndRenderTarget);
505 if (SUCCEEDED(hr)) {
506 pRenderTarget = pHwndRenderTarget;
507 } else {
508 Platform::DebugPrintf("Failed CreateHwndRenderTarget 0x%x\n", hr);
509 pRenderTarget = NULL;
512 #else
513 pD2DFactory->CreateHwndRenderTarget(
514 D2D1::RenderTargetProperties(
515 D2D1_RENDER_TARGET_TYPE_DEFAULT ,
516 D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_PREMULTIPLIED),
517 96.0f, 96.0f, D2D1_RENDER_TARGET_USAGE_NONE, D2D1_FEATURE_LEVEL_DEFAULT),
518 D2D1::HwndRenderTargetProperties(hw, size),
519 &pRenderTarget);
520 #endif
521 // Pixmaps were created to be compatible with previous render target so
522 // need to be recreated.
523 DropGraphics(false);
526 if ((technology == SC_TECHNOLOGY_DIRECTWRITEDC) && pRenderTarget) {
527 RECT rcWindow;
528 GetClientRect(MainHWND(), &rcWindow);
529 HRESULT hr = static_cast<ID2D1DCRenderTarget*>(pRenderTarget)->BindDC(hdc, &rcWindow);
530 if (FAILED(hr)) {
531 Platform::DebugPrintf("BindDC failed 0x%x\n", hr);
532 DropRenderTarget();
537 void ScintillaWin::DropRenderTarget() {
538 if (pRenderTarget) {
539 pRenderTarget->Release();
540 pRenderTarget = 0;
544 #endif
546 HWND ScintillaWin::MainHWND() {
547 return reinterpret_cast<HWND>(wMain.GetID());
550 bool ScintillaWin::DragThreshold(Point ptStart, Point ptNow) {
551 int xMove = static_cast<int>(std::abs(ptStart.x - ptNow.x));
552 int yMove = static_cast<int>(std::abs(ptStart.y - ptNow.y));
553 return (xMove > ::GetSystemMetrics(SM_CXDRAG)) ||
554 (yMove > ::GetSystemMetrics(SM_CYDRAG));
557 void ScintillaWin::StartDrag() {
558 inDragDrop = ddDragging;
559 DWORD dwEffect = 0;
560 dropWentOutside = true;
561 IDataObject *pDataObject = reinterpret_cast<IDataObject *>(&dob);
562 IDropSource *pDropSource = reinterpret_cast<IDropSource *>(&ds);
563 //Platform::DebugPrintf("About to DoDragDrop %x %x\n", pDataObject, pDropSource);
564 HRESULT hr = ::DoDragDrop(
565 pDataObject,
566 pDropSource,
567 DROPEFFECT_COPY | DROPEFFECT_MOVE, &dwEffect);
568 //Platform::DebugPrintf("DoDragDrop = %x\n", hr);
569 if (SUCCEEDED(hr)) {
570 if ((hr == DRAGDROP_S_DROP) && (dwEffect == DROPEFFECT_MOVE) && dropWentOutside) {
571 // Remove dragged out text
572 ClearSelection();
575 inDragDrop = ddNone;
576 SetDragPosition(SelectionPosition(invalidPosition));
579 // Avoid warnings everywhere for old style casts by concentrating them here
580 static WORD LoWord(uptr_t l) {
581 return LOWORD(l);
584 static WORD HiWord(uptr_t l) {
585 return HIWORD(l);
588 static int InputCodePage() {
589 HKL inputLocale = ::GetKeyboardLayout(0);
590 LANGID inputLang = LOWORD(inputLocale);
591 char sCodePage[10];
592 int res = ::GetLocaleInfoA(MAKELCID(inputLang, SORT_DEFAULT),
593 LOCALE_IDEFAULTANSICODEPAGE, sCodePage, sizeof(sCodePage));
594 if (!res)
595 return 0;
596 return atoi(sCodePage);
599 /** Map the key codes to their equivalent SCK_ form. */
600 static int KeyTranslate(int keyIn) {
601 //PLATFORM_ASSERT(!keyIn);
602 switch (keyIn) {
603 case VK_DOWN: return SCK_DOWN;
604 case VK_UP: return SCK_UP;
605 case VK_LEFT: return SCK_LEFT;
606 case VK_RIGHT: return SCK_RIGHT;
607 case VK_HOME: return SCK_HOME;
608 case VK_END: return SCK_END;
609 case VK_PRIOR: return SCK_PRIOR;
610 case VK_NEXT: return SCK_NEXT;
611 case VK_DELETE: return SCK_DELETE;
612 case VK_INSERT: return SCK_INSERT;
613 case VK_ESCAPE: return SCK_ESCAPE;
614 case VK_BACK: return SCK_BACK;
615 case VK_TAB: return SCK_TAB;
616 case VK_RETURN: return SCK_RETURN;
617 case VK_ADD: return SCK_ADD;
618 case VK_SUBTRACT: return SCK_SUBTRACT;
619 case VK_DIVIDE: return SCK_DIVIDE;
620 case VK_LWIN: return SCK_WIN;
621 case VK_RWIN: return SCK_RWIN;
622 case VK_APPS: return SCK_MENU;
623 case VK_OEM_2: return '/';
624 case VK_OEM_3: return '`';
625 case VK_OEM_4: return '[';
626 case VK_OEM_5: return '\\';
627 case VK_OEM_6: return ']';
628 default: return keyIn;
632 static bool BoundsContains(PRectangle rcBounds, HRGN hRgnBounds, PRectangle rcCheck) {
633 bool contains = true;
634 if (!rcCheck.Empty()) {
635 if (!rcBounds.Contains(rcCheck)) {
636 contains = false;
637 } else if (hRgnBounds) {
638 // In bounding rectangle so check more accurately using region
639 HRGN hRgnCheck = ::CreateRectRgn(static_cast<int>(rcCheck.left), static_cast<int>(rcCheck.top),
640 static_cast<int>(rcCheck.right), static_cast<int>(rcCheck.bottom));
641 if (hRgnCheck) {
642 HRGN hRgnDifference = ::CreateRectRgn(0, 0, 0, 0);
643 if (hRgnDifference) {
644 int combination = ::CombineRgn(hRgnDifference, hRgnCheck, hRgnBounds, RGN_DIFF);
645 if (combination != NULLREGION) {
646 contains = false;
648 ::DeleteRgn(hRgnDifference);
650 ::DeleteRgn(hRgnCheck);
654 return contains;
657 // Returns the target converted to UTF8.
658 // Return the length in bytes.
659 int ScintillaWin::TargetAsUTF8(char *text) {
660 int targetLength = targetEnd - targetStart;
661 if (IsUnicodeMode()) {
662 if (text) {
663 pdoc->GetCharRange(text, targetStart, targetLength);
665 } else {
666 // Need to convert
667 std::string s = RangeText(targetStart, targetEnd);
668 int charsLen = ::MultiByteToWideChar(CodePageOfDocument(), 0, &s[0], targetLength, NULL, 0);
669 std::wstring characters(charsLen, '\0');
670 ::MultiByteToWideChar(CodePageOfDocument(), 0, &s[0], targetLength, &characters[0], charsLen);
672 int utf8Len = ::WideCharToMultiByte(CP_UTF8, 0, &characters[0], charsLen, NULL, 0, 0, 0);
673 if (text) {
674 ::WideCharToMultiByte(CP_UTF8, 0, &characters[0], charsLen, text, utf8Len, 0, 0);
675 text[utf8Len] = '\0';
677 return utf8Len;
679 return targetLength;
682 // Translates a nul terminated UTF8 string into the document encoding.
683 // Return the length of the result in bytes.
684 int ScintillaWin::EncodedFromUTF8(char *utf8, char *encoded) const {
685 int inputLength = (lengthForEncode >= 0) ? lengthForEncode : static_cast<int>(strlen(utf8));
686 if (IsUnicodeMode()) {
687 if (encoded) {
688 memcpy(encoded, utf8, inputLength);
690 return inputLength;
691 } else {
692 // Need to convert
693 int charsLen = ::MultiByteToWideChar(CP_UTF8, 0, utf8, inputLength, NULL, 0);
694 std::wstring characters(charsLen, '\0');
695 ::MultiByteToWideChar(CP_UTF8, 0, utf8, inputLength, &characters[0], charsLen);
697 int encodedLen = ::WideCharToMultiByte(CodePageOfDocument(),
698 0, &characters[0], charsLen, NULL, 0, 0, 0);
699 if (encoded) {
700 ::WideCharToMultiByte(CodePageOfDocument(), 0, &characters[0], charsLen, encoded, encodedLen, 0, 0);
701 encoded[encodedLen] = '\0';
703 return encodedLen;
707 LRESULT ScintillaWin::WndPaint(uptr_t wParam) {
708 //ElapsedTime et;
710 // Redirect assertions to debug output and save current state
711 bool assertsPopup = Platform::ShowAssertionPopUps(false);
712 paintState = painting;
713 PAINTSTRUCT ps;
714 PAINTSTRUCT *pps;
716 bool IsOcxCtrl = (wParam != 0); // if wParam != 0, it contains
717 // a PAINSTRUCT* from the OCX
718 // Removed since this interferes with reporting other assertions as it occurs repeatedly
719 //PLATFORM_ASSERT(hRgnUpdate == NULL);
720 hRgnUpdate = ::CreateRectRgn(0, 0, 0, 0);
721 if (IsOcxCtrl) {
722 pps = reinterpret_cast<PAINTSTRUCT*>(wParam);
723 } else {
724 ::GetUpdateRgn(MainHWND(), hRgnUpdate, FALSE);
725 pps = &ps;
726 ::BeginPaint(MainHWND(), pps);
728 rcPaint = PRectangle::FromInts(pps->rcPaint.left, pps->rcPaint.top, pps->rcPaint.right, pps->rcPaint.bottom);
729 PRectangle rcClient = GetClientRectangle();
730 paintingAllText = BoundsContains(rcPaint, hRgnUpdate, rcClient);
731 if (technology == SC_TECHNOLOGY_DEFAULT) {
732 AutoSurface surfaceWindow(pps->hdc, this);
733 if (surfaceWindow) {
734 Paint(surfaceWindow, rcPaint);
735 surfaceWindow->Release();
737 } else {
738 #if defined(USE_D2D)
739 EnsureRenderTarget(pps->hdc);
740 AutoSurface surfaceWindow(pRenderTarget, this);
741 if (surfaceWindow) {
742 pRenderTarget->BeginDraw();
743 Paint(surfaceWindow, rcPaint);
744 surfaceWindow->Release();
745 HRESULT hr = pRenderTarget->EndDraw();
746 if (hr == D2DERR_RECREATE_TARGET) {
747 DropRenderTarget();
748 paintState = paintAbandoned;
751 #endif
753 if (hRgnUpdate) {
754 ::DeleteRgn(hRgnUpdate);
755 hRgnUpdate = 0;
758 if (!IsOcxCtrl)
759 ::EndPaint(MainHWND(), pps);
760 if (paintState == paintAbandoned) {
761 // Painting area was insufficient to cover new styling or brace highlight positions
762 if (IsOcxCtrl) {
763 FullPaintDC(pps->hdc);
764 } else {
765 FullPaint();
768 paintState = notPainting;
770 // Restore debug output state
771 Platform::ShowAssertionPopUps(assertsPopup);
773 //Platform::DebugPrintf("Paint took %g\n", et.Duration());
774 return 0l;
777 sptr_t ScintillaWin::HandleCompositionWindowed(uptr_t wParam, sptr_t lParam) {
778 if (lParam & GCS_RESULTSTR) {
779 HIMC hIMC = ::ImmGetContext(MainHWND());
780 if (hIMC) {
781 wchar_t wcs[maxLenInputIME];
782 LONG bytes = ::ImmGetCompositionStringW(hIMC,
783 GCS_RESULTSTR, wcs, (maxLenInputIME-1)*2);
784 int wides = bytes / 2;
785 if (IsUnicodeMode()) {
786 char utfval[maxLenInputIME * 3];
787 unsigned int len = UTF8Length(wcs, wides);
788 UTF8FromUTF16(wcs, wides, utfval, len);
789 utfval[len] = '\0';
790 AddCharUTF(utfval, len);
791 } else {
792 char dbcsval[maxLenInputIME * 2];
793 int size = ::WideCharToMultiByte(InputCodePage(),
794 0, wcs, wides, dbcsval, sizeof(dbcsval) - 1, 0, 0);
795 for (int i=0; i<size; i++) {
796 AddChar(dbcsval[i]);
799 // Set new position after converted
800 Point pos = PointMainCaret();
801 COMPOSITIONFORM CompForm;
802 CompForm.dwStyle = CFS_POINT;
803 CompForm.ptCurrentPos.x = static_cast<int>(pos.x);
804 CompForm.ptCurrentPos.y = static_cast<int>(pos.y);
805 ::ImmSetCompositionWindow(hIMC, &CompForm);
806 ::ImmReleaseContext(MainHWND(), hIMC);
808 return 0;
810 return ::DefWindowProc(MainHWND(), WM_IME_COMPOSITION, wParam, lParam);
813 bool ScintillaWin::KoreanIME() {
814 const int codePage = InputCodePage();
815 return codePage == 949 || codePage == 1361;
818 void ScintillaWin::MoveImeCarets(int offset) {
819 // Move carets relatively by bytes.
820 for (size_t r=0; r<sel.Count(); r++) {
821 int positionInsert = sel.Range(r).Start().Position();
822 sel.Range(r).caret.SetPosition(positionInsert + offset);
823 sel.Range(r).anchor.SetPosition(positionInsert + offset);
827 void ScintillaWin::DrawImeIndicator(int indicator, int len) {
828 // Emulate the visual style of IME characters with indicators.
829 // Draw an indicator on the character before caret by the character bytes of len
830 // so it should be called after addCharUTF().
831 // It does not affect caret positions.
832 if (indicator < 8 || indicator > INDIC_MAX) {
833 return;
835 pdoc->decorations.SetCurrentIndicator(indicator);
836 for (size_t r=0; r<sel.Count(); r++) {
837 int positionInsert = sel.Range(r).Start().Position();
838 pdoc->DecorationFillRange(positionInsert - len, 1, len);
842 void ScintillaWin::SetCandidateWindowPos() {
843 HIMC hIMC = ::ImmGetContext(MainHWND());
844 if (hIMC) {
845 Point pos = PointMainCaret();
846 CANDIDATEFORM CandForm;
847 CandForm.dwIndex = 0;
848 CandForm.dwStyle = CFS_CANDIDATEPOS;
849 CandForm.ptCurrentPos.x = static_cast<int>(pos.x);
850 CandForm.ptCurrentPos.y = static_cast<int>(pos.y + vs.lineHeight);
851 ::ImmSetCandidateWindow(hIMC, &CandForm);
852 ::ImmReleaseContext(MainHWND(), hIMC);
856 static std::string StringEncode(std::wstring s, int codePage) {
857 if (s.length()) {
858 int cchMulti = ::WideCharToMultiByte(codePage, 0, s.c_str(), static_cast<int>(s.length()), NULL, 0, NULL, NULL);
859 std::string sMulti(cchMulti, 0);
860 ::WideCharToMultiByte(codePage, 0, s.c_str(), static_cast<int>(s.size()), &sMulti[0], cchMulti, NULL, NULL);
861 return sMulti;
862 } else {
863 return std::string();
867 static std::wstring StringDecode(std::string s, int codePage) {
868 if (s.length()) {
869 int cchWide = ::MultiByteToWideChar(codePage, 0, s.c_str(), static_cast<int>(s.length()), NULL, 0);
870 std::wstring sWide(cchWide, 0);
871 ::MultiByteToWideChar(codePage, 0, s.c_str(), static_cast<int>(s.length()), &sWide[0], cchWide);
872 return sWide;
873 } else {
874 return std::wstring();
878 void ScintillaWin::SelectionToHangul() {
879 // Convert every hanja to hangul within the main range.
880 const int selStart = sel.RangeMain().Start().Position();
881 const int documentStrLen = sel.RangeMain().Length();
882 const int selEnd = selStart + documentStrLen;
883 const int utf16Len = pdoc->CountUTF16(selStart, selEnd);
885 if (utf16Len > 0) {
886 std::string documentStr(documentStrLen, '\0');
887 pdoc->GetCharRange(&documentStr[0], selStart, documentStrLen);
889 std::wstring uniStr = StringDecode(documentStr, CodePageOfDocument());
890 int converted = HanjaDict::GetHangulOfHanja(&uniStr[0]);
891 documentStr = StringEncode(uniStr, CodePageOfDocument());
893 if (converted > 0) {
894 pdoc->BeginUndoAction();
895 ClearSelection();
896 InsertPaste(&documentStr[0], static_cast<int>(documentStr.size()));
897 pdoc->EndUndoAction();
902 void ScintillaWin::EscapeHanja() {
903 // The candidate box pops up to user to select a hanja.
904 // It comes into WM_IME_COMPOSITION with GCS_RESULTSTR.
905 // The existing hangul or hanja is replaced with it.
906 if (sel.Count() > 1) {
907 return; // Do not allow multi carets.
909 int currentPos = CurrentPosition();
910 int oneCharLen = pdoc->LenChar(currentPos);
912 if (oneCharLen < 2) {
913 return; // No need to handle SBCS.
916 // ImmEscapeW() may overwrite uniChar[] with a null terminated string.
917 // So enlarge it enough to Maximum 4 as in UTF-8.
918 unsigned int const safeLength = UTF8MaxBytes+1;
919 std::string oneChar(safeLength, '\0');
920 pdoc->GetCharRange(&oneChar[0], currentPos, oneCharLen);
922 std::wstring uniChar = StringDecode(oneChar, CodePageOfDocument());
924 HIMC hIMC=ImmGetContext(MainHWND());
925 if (hIMC) {
926 // Set the candidate box position since IME may show it.
927 SetCandidateWindowPos();
928 // IME_ESC_HANJA_MODE appears to receive the first character only.
929 if (ImmEscapeW(GetKeyboardLayout(0), hIMC, IME_ESC_HANJA_MODE, &uniChar[0])) {
930 SetSelection (currentPos, currentPos + oneCharLen);
932 ::ImmReleaseContext(MainHWND(), hIMC);
936 void ScintillaWin::ToggleHanja() {
937 // If selection, convert every hanja to hangul within the main range.
938 // If no selection, commit to IME.
939 if (sel.Count() > 1) {
940 return; // Do not allow multi carets.
943 if (sel.Empty()) {
944 EscapeHanja();
945 } else {
946 SelectionToHangul();
950 sptr_t ScintillaWin::HandleCompositionInline(uptr_t, sptr_t lParam) {
951 // Copy & paste by johnsonj with a lot of helps of Neil.
952 // Great thanks for my foreruners, jiniya and BLUEnLIVE.
954 HIMC hIMC = ::ImmGetContext(MainHWND());
955 if (!hIMC) {
956 return 0;
959 if (pdoc->TentativeActive()) {
960 pdoc->TentativeUndo();
961 } else {
962 // No tentative undo means start of this composition so
963 // fill in any virtual spaces.
964 FillVirtualSpace();
967 view.imeCaretBlockOverride = false;
969 if (lParam & GCS_COMPSTR) {
970 wchar_t wcs[maxLenInputIME] = { 0 };
971 long bytes = ::ImmGetCompositionStringW
972 (hIMC, GCS_COMPSTR, wcs, maxLenInputIME);
973 unsigned int wcsLen = bytes / 2;
975 if ((wcsLen == 0) || (wcsLen >= maxLenInputIME)) {
976 ShowCaretAtCurrentPosition();
977 ::ImmReleaseContext(MainHWND(), hIMC);
978 return 0;
981 pdoc->TentativeStart(); // TentativeActive from now on.
983 // Get attribute information from composition string.
984 BYTE compAttr[maxLenInputIME] = { 0 };
985 unsigned int imeCursorPos = 0;
987 if (lParam & GCS_COMPATTR) {
988 ImmGetCompositionStringW(hIMC, GCS_COMPATTR, compAttr, sizeof(compAttr));
990 if (lParam & GCS_CURSORPOS) {
991 imeCursorPos = ImmGetCompositionStringW(hIMC, GCS_CURSORPOS, NULL, 0);
994 // Display character by character.
995 int numBytes = 0;
996 int imeCharPos[maxLenInputIME + 1] = { 0 };
998 bool tmpRecordingMacro = recordingMacro;
999 recordingMacro = false;
1000 for (size_t i = 0; i < wcsLen; ) {
1001 const size_t ucWidth = UTF16CharLength(wcs[i]);
1002 const std::wstring uniChar(wcs+i, ucWidth);
1003 char oneChar[UTF8MaxBytes + 1] = "\0\0\0\0"; // Maximum 4 bytes in utf8
1004 unsigned int oneCharLen = 0;
1006 if (IsUnicodeMode()) {
1007 oneCharLen = UTF8Length(uniChar.c_str(), static_cast<unsigned int>(uniChar.length()));
1008 UTF8FromUTF16(uniChar.c_str(), static_cast<unsigned int>(uniChar.length()), oneChar, oneCharLen);
1009 } else {
1010 oneCharLen = ::WideCharToMultiByte(InputCodePage(), 0,
1011 uniChar.c_str(), static_cast<unsigned int>(uniChar.length()), oneChar, sizeof(oneChar)-1, 0, 0);
1013 oneChar[oneCharLen] = '\0';
1015 // Display a character.
1016 AddCharUTF(oneChar, oneCharLen);
1018 // Record compstr character positions for moving IME carets.
1019 numBytes += oneCharLen;
1020 imeCharPos[i + 1] = numBytes;
1022 // Draw an indicator on the character.
1023 int indicator = SC_INDICATOR_UNKNOWN;
1024 switch ((int)compAttr[i]) {
1025 case ATTR_INPUT:
1026 indicator = SC_INDICATOR_INPUT;
1027 break;
1028 case ATTR_TARGET_NOTCONVERTED:
1029 case ATTR_TARGET_CONVERTED:
1030 indicator = SC_INDICATOR_TARGET;
1031 break;
1032 case ATTR_CONVERTED:
1033 indicator = SC_INDICATOR_CONVERTED;
1034 break;
1036 DrawImeIndicator(indicator, oneCharLen);
1037 i += ucWidth;
1039 recordingMacro = tmpRecordingMacro;
1041 // Move IME caret position.
1042 MoveImeCarets(-imeCharPos[wcsLen] + imeCharPos[imeCursorPos]);
1043 if (KoreanIME()) {
1044 view.imeCaretBlockOverride = true;
1046 } else if (lParam & GCS_RESULTSTR) {
1047 wchar_t wcs[maxLenInputIME] = { 0 };
1048 long bytes = ::ImmGetCompositionStringW
1049 (hIMC, GCS_RESULTSTR, wcs, maxLenInputIME);
1050 unsigned int wcsLen = bytes / 2;
1052 for (size_t i = 0; i < wcsLen;) {
1053 const size_t ucWidth = UTF16CharLength(wcs[i]);
1054 const std::wstring uniChar(wcs+i, ucWidth);
1055 char oneChar[UTF8MaxBytes+1] = "\0\0\0\0"; // Maximum 4 bytes in UTF-8.
1056 unsigned int oneCharLen = 0;
1058 if (IsUnicodeMode()) {
1059 oneCharLen = UTF8Length(uniChar.c_str(), static_cast<unsigned int>(uniChar.length()));
1060 UTF8FromUTF16(uniChar.c_str(), static_cast<unsigned int>(uniChar.length()), oneChar, oneCharLen);
1061 } else {
1062 oneCharLen = ::WideCharToMultiByte(InputCodePage(), 0,
1063 uniChar.c_str(), static_cast<unsigned int>(uniChar.length()), oneChar, sizeof(oneChar)-1, 0, 0);
1065 oneChar[oneCharLen] = '\0';
1066 AddCharUTF(oneChar, oneCharLen);
1067 i += ucWidth;
1070 SetCandidateWindowPos();
1071 ShowCaretAtCurrentPosition();
1072 ::ImmReleaseContext(MainHWND(), hIMC);
1073 return 0;
1076 // Translate message IDs from WM_* and EM_* to SCI_* so can partly emulate Windows Edit control
1077 static unsigned int SciMessageFromEM(unsigned int iMessage) {
1078 switch (iMessage) {
1079 case EM_CANPASTE: return SCI_CANPASTE;
1080 case EM_CANUNDO: return SCI_CANUNDO;
1081 case EM_EMPTYUNDOBUFFER: return SCI_EMPTYUNDOBUFFER;
1082 case EM_FINDTEXTEX: return SCI_FINDTEXT;
1083 case EM_FORMATRANGE: return SCI_FORMATRANGE;
1084 case EM_GETFIRSTVISIBLELINE: return SCI_GETFIRSTVISIBLELINE;
1085 case EM_GETLINECOUNT: return SCI_GETLINECOUNT;
1086 case EM_GETSELTEXT: return SCI_GETSELTEXT;
1087 case EM_GETTEXTRANGE: return SCI_GETTEXTRANGE;
1088 case EM_HIDESELECTION: return SCI_HIDESELECTION;
1089 case EM_LINEINDEX: return SCI_POSITIONFROMLINE;
1090 case EM_LINESCROLL: return SCI_LINESCROLL;
1091 case EM_REPLACESEL: return SCI_REPLACESEL;
1092 case EM_SCROLLCARET: return SCI_SCROLLCARET;
1093 case EM_SETREADONLY: return SCI_SETREADONLY;
1094 case WM_CLEAR: return SCI_CLEAR;
1095 case WM_COPY: return SCI_COPY;
1096 case WM_CUT: return SCI_CUT;
1097 case WM_SETTEXT: return SCI_SETTEXT;
1098 case WM_PASTE: return SCI_PASTE;
1099 case WM_UNDO: return SCI_UNDO;
1101 return iMessage;
1104 UINT CodePageFromCharSet(DWORD characterSet, UINT documentCodePage) {
1105 if (documentCodePage == SC_CP_UTF8) {
1106 return SC_CP_UTF8;
1108 switch (characterSet) {
1109 case SC_CHARSET_ANSI: return 1252;
1110 case SC_CHARSET_DEFAULT: return documentCodePage;
1111 case SC_CHARSET_BALTIC: return 1257;
1112 case SC_CHARSET_CHINESEBIG5: return 950;
1113 case SC_CHARSET_EASTEUROPE: return 1250;
1114 case SC_CHARSET_GB2312: return 936;
1115 case SC_CHARSET_GREEK: return 1253;
1116 case SC_CHARSET_HANGUL: return 949;
1117 case SC_CHARSET_MAC: return 10000;
1118 case SC_CHARSET_OEM: return 437;
1119 case SC_CHARSET_RUSSIAN: return 1251;
1120 case SC_CHARSET_SHIFTJIS: return 932;
1121 case SC_CHARSET_TURKISH: return 1254;
1122 case SC_CHARSET_JOHAB: return 1361;
1123 case SC_CHARSET_HEBREW: return 1255;
1124 case SC_CHARSET_ARABIC: return 1256;
1125 case SC_CHARSET_VIETNAMESE: return 1258;
1126 case SC_CHARSET_THAI: return 874;
1127 case SC_CHARSET_8859_15: return 28605;
1128 // Not supported
1129 case SC_CHARSET_CYRILLIC: return documentCodePage;
1130 case SC_CHARSET_SYMBOL: return documentCodePage;
1132 return documentCodePage;
1135 UINT ScintillaWin::CodePageOfDocument() const {
1136 return CodePageFromCharSet(vs.styles[STYLE_DEFAULT].characterSet, pdoc->dbcsCodePage);
1139 sptr_t ScintillaWin::GetTextLength() {
1140 if (pdoc->Length() == 0)
1141 return 0;
1142 std::vector<char> docBytes(pdoc->Length(), '\0');
1143 pdoc->GetCharRange(&docBytes[0], 0, pdoc->Length());
1144 if (IsUnicodeMode()) {
1145 return UTF16Length(&docBytes[0], static_cast<unsigned int>(docBytes.size()));
1146 } else {
1147 return ::MultiByteToWideChar(CodePageOfDocument(), 0, &docBytes[0],
1148 static_cast<int>(docBytes.size()), NULL, 0);
1152 sptr_t ScintillaWin::GetText(uptr_t wParam, sptr_t lParam) {
1153 wchar_t *ptr = reinterpret_cast<wchar_t *>(lParam);
1154 if (pdoc->Length() == 0) {
1155 *ptr = L'\0';
1156 return 0;
1158 std::vector<char> docBytes(pdoc->Length(), '\0');
1159 pdoc->GetCharRange(&docBytes[0], 0, pdoc->Length());
1160 if (IsUnicodeMode()) {
1161 size_t lengthUTF16 = UTF16Length(&docBytes[0], static_cast<unsigned int>(docBytes.size()));
1162 if (lParam == 0)
1163 return lengthUTF16;
1164 if (wParam == 0)
1165 return 0;
1166 size_t uLen = UTF16FromUTF8(&docBytes[0], docBytes.size(),
1167 ptr, static_cast<int>(wParam) - 1);
1168 ptr[uLen] = L'\0';
1169 return uLen;
1170 } else {
1171 // Not Unicode mode
1172 // Convert to Unicode using the current Scintilla code page
1173 const UINT cpSrc = CodePageOfDocument();
1174 int lengthUTF16 = ::MultiByteToWideChar(cpSrc, 0, &docBytes[0],
1175 static_cast<int>(docBytes.size()), NULL, 0);
1176 if (lengthUTF16 >= static_cast<int>(wParam))
1177 lengthUTF16 = static_cast<int>(wParam)-1;
1178 ::MultiByteToWideChar(cpSrc, 0, &docBytes[0],
1179 static_cast<int>(docBytes.size()),
1180 ptr, lengthUTF16);
1181 ptr[lengthUTF16] = L'\0';
1182 return lengthUTF16;
1186 sptr_t ScintillaWin::WndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
1187 try {
1188 //Platform::DebugPrintf("S M:%x WP:%x L:%x\n", iMessage, wParam, lParam);
1189 iMessage = SciMessageFromEM(iMessage);
1190 switch (iMessage) {
1192 case WM_CREATE:
1193 ctrlID = ::GetDlgCtrlID(reinterpret_cast<HWND>(wMain.GetID()));
1194 // Get Intellimouse scroll line parameters
1195 GetIntelliMouseParameters();
1196 ::RegisterDragDrop(MainHWND(), reinterpret_cast<IDropTarget *>(&dt));
1197 break;
1199 case WM_COMMAND:
1200 Command(LoWord(wParam));
1201 break;
1203 case WM_PAINT:
1204 return WndPaint(wParam);
1206 case WM_PRINTCLIENT: {
1207 HDC hdc = reinterpret_cast<HDC>(wParam);
1208 if (!IsCompatibleDC(hdc)) {
1209 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1211 FullPaintDC(hdc);
1213 break;
1215 case WM_VSCROLL:
1216 ScrollMessage(wParam);
1217 break;
1219 case WM_HSCROLL:
1220 HorizontalScrollMessage(wParam);
1221 break;
1223 case WM_SIZE: {
1224 #if defined(USE_D2D)
1225 if (paintState == notPainting) {
1226 DropRenderTarget();
1227 } else {
1228 renderTargetValid = false;
1230 #endif
1231 //Platform::DebugPrintf("Scintilla WM_SIZE %d %d\n", LoWord(lParam), HiWord(lParam));
1232 ChangeSize();
1234 break;
1236 case WM_MOUSEWHEEL:
1237 // if autocomplete list active then send mousewheel message to it
1238 if (ac.Active()) {
1239 HWND hWnd = reinterpret_cast<HWND>(ac.lb->GetID());
1240 ::SendMessage(hWnd, iMessage, wParam, lParam);
1241 break;
1244 // Don't handle datazoom.
1245 // (A good idea for datazoom would be to "fold" or "unfold" details.
1246 // i.e. if datazoomed out only class structures are visible, when datazooming in the control
1247 // structures appear, then eventually the individual statements...)
1248 if (wParam & MK_SHIFT) {
1249 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1252 // Either SCROLL or ZOOM. We handle the wheel steppings calculation
1253 wheelDelta -= static_cast<short>(HiWord(wParam));
1254 if (abs(wheelDelta) >= WHEEL_DELTA && linesPerScroll > 0) {
1255 int linesToScroll = linesPerScroll;
1256 if (linesPerScroll == WHEEL_PAGESCROLL)
1257 linesToScroll = LinesOnScreen() - 1;
1258 if (linesToScroll == 0) {
1259 linesToScroll = 1;
1261 linesToScroll *= (wheelDelta / WHEEL_DELTA);
1262 if (wheelDelta >= 0)
1263 wheelDelta = wheelDelta % WHEEL_DELTA;
1264 else
1265 wheelDelta = - (-wheelDelta % WHEEL_DELTA);
1267 if (wParam & MK_CONTROL) {
1268 // Zoom! We play with the font sizes in the styles.
1269 // Number of steps/line is ignored, we just care if sizing up or down
1270 if (linesToScroll < 0) {
1271 KeyCommand(SCI_ZOOMIN);
1272 } else {
1273 KeyCommand(SCI_ZOOMOUT);
1275 } else {
1276 // Scroll
1277 ScrollTo(topLine + linesToScroll);
1280 return 0;
1282 case WM_TIMER:
1283 if (wParam == idleTimerID && idler.state) {
1284 SendMessage(MainHWND(), SC_WIN_IDLE, 0, 1);
1285 } else {
1286 TickFor(static_cast<TickReason>(wParam - fineTimerStart));
1288 break;
1290 case SC_WIN_IDLE:
1291 // wParam=dwTickCountInitial, or 0 to initialize. lParam=bSkipUserInputTest
1292 if (idler.state) {
1293 if (lParam || (WAIT_TIMEOUT == MsgWaitForMultipleObjects(0, 0, 0, 0, QS_INPUT|QS_HOTKEY))) {
1294 if (Idle()) {
1295 // User input was given priority above, but all events do get a turn. Other
1296 // messages, notifications, etc. will get interleaved with the idle messages.
1298 // However, some things like WM_PAINT are a lower priority, and will not fire
1299 // when there's a message posted. So, several times a second, we stop and let
1300 // the low priority events have a turn (after which the timer will fire again).
1302 DWORD dwCurrent = GetTickCount();
1303 DWORD dwStart = wParam ? static_cast<DWORD>(wParam) : dwCurrent;
1304 const DWORD maxWorkTime = 50;
1306 if (dwCurrent >= dwStart && dwCurrent > maxWorkTime && dwCurrent - maxWorkTime < dwStart)
1307 PostMessage(MainHWND(), SC_WIN_IDLE, dwStart, 0);
1308 } else {
1309 SetIdle(false);
1313 break;
1315 case WM_GETMINMAXINFO:
1316 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1318 case WM_LBUTTONDOWN: {
1319 // For IME, set the composition string as the result string.
1320 HIMC hIMC = ::ImmGetContext(MainHWND());
1321 ::ImmNotifyIME(hIMC, NI_COMPOSITIONSTR, CPS_COMPLETE, 0);
1322 ::ImmReleaseContext(MainHWND(), hIMC);
1324 //Platform::DebugPrintf("Buttdown %d %x %x %x %x %x\n",iMessage, wParam, lParam,
1325 // Platform::IsKeyDown(VK_SHIFT),
1326 // Platform::IsKeyDown(VK_CONTROL),
1327 // Platform::IsKeyDown(VK_MENU));
1328 ::SetFocus(MainHWND());
1329 ButtonDown(Point::FromLong(static_cast<long>(lParam)), ::GetMessageTime(),
1330 (wParam & MK_SHIFT) != 0,
1331 (wParam & MK_CONTROL) != 0,
1332 Platform::IsKeyDown(VK_MENU));
1334 break;
1336 case WM_MOUSEMOVE: {
1337 const Point pt = Point::FromLong(static_cast<long>(lParam));
1339 // Windows might send WM_MOUSEMOVE even though the mouse has not been moved:
1340 // http://blogs.msdn.com/b/oldnewthing/archive/2003/10/01/55108.aspx
1341 if (ptMouseLast.x != pt.x || ptMouseLast.y != pt.y) {
1342 SetTrackMouseLeaveEvent(true);
1343 ButtonMoveWithModifiers(pt,
1344 ((wParam & MK_SHIFT) != 0 ? SCI_SHIFT : 0) |
1345 ((wParam & MK_CONTROL) != 0 ? SCI_CTRL : 0) |
1346 (Platform::IsKeyDown(VK_MENU) ? SCI_ALT : 0));
1349 break;
1351 case WM_MOUSELEAVE:
1352 SetTrackMouseLeaveEvent(false);
1353 MouseLeave();
1354 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1356 case WM_LBUTTONUP:
1357 ButtonUp(Point::FromLong(static_cast<long>(lParam)),
1358 ::GetMessageTime(),
1359 (wParam & MK_CONTROL) != 0);
1360 break;
1362 case WM_RBUTTONDOWN:
1363 ::SetFocus(MainHWND());
1364 if (!PointInSelection(Point::FromLong(static_cast<long>(lParam)))) {
1365 CancelModes();
1366 SetEmptySelection(PositionFromLocation(Point::FromLong(static_cast<long>(lParam))));
1368 break;
1370 case WM_SETCURSOR:
1371 if (LoWord(lParam) == HTCLIENT) {
1372 if (inDragDrop == ddDragging) {
1373 DisplayCursor(Window::cursorUp);
1374 } else {
1375 // Display regular (drag) cursor over selection
1376 POINT pt;
1377 if (0 != ::GetCursorPos(&pt)) {
1378 ::ScreenToClient(MainHWND(), &pt);
1379 if (PointInSelMargin(PointFromPOINT(pt))) {
1380 DisplayCursor(GetMarginCursor(PointFromPOINT(pt)));
1381 } else if (PointInSelection(PointFromPOINT(pt)) && !SelectionEmpty()) {
1382 DisplayCursor(Window::cursorArrow);
1383 } else if (PointIsHotspot(PointFromPOINT(pt))) {
1384 DisplayCursor(Window::cursorHand);
1385 } else {
1386 DisplayCursor(Window::cursorText);
1390 return TRUE;
1391 } else {
1392 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1395 case WM_CHAR:
1396 if (((wParam >= 128) || !iscntrl(static_cast<int>(wParam))) || !lastKeyDownConsumed) {
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[UTF8MaxBytes];
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);
1413 return 0;
1415 case WM_UNICHAR:
1416 if (wParam == UNICODE_NOCHAR) {
1417 return IsUnicodeMode() ? 1 : 0;
1418 } else if (lastKeyDownConsumed) {
1419 return 1;
1420 } else {
1421 if (IsUnicodeMode()) {
1422 char utfval[UTF8MaxBytes];
1423 wchar_t wcs[2] = {static_cast<wchar_t>(wParam), 0};
1424 unsigned int len = UTF8Length(wcs, 1);
1425 UTF8FromUTF16(wcs, 1, utfval, len);
1426 AddCharUTF(utfval, len);
1427 return 1;
1428 } else {
1429 return 0;
1433 case WM_SYSKEYDOWN:
1434 case WM_KEYDOWN: {
1435 //Platform::DebugPrintf("S keydown %d %x %x %x %x\n",iMessage, wParam, lParam, ::IsKeyDown(VK_SHIFT), ::IsKeyDown(VK_CONTROL));
1436 lastKeyDownConsumed = false;
1437 int ret = KeyDown(KeyTranslate(static_cast<int>(wParam)),
1438 Platform::IsKeyDown(VK_SHIFT),
1439 Platform::IsKeyDown(VK_CONTROL),
1440 Platform::IsKeyDown(VK_MENU),
1441 &lastKeyDownConsumed);
1442 if (!ret && !lastKeyDownConsumed) {
1443 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1445 break;
1448 case WM_IME_KEYDOWN: {
1449 if (wParam == VK_HANJA) {
1450 ToggleHanja();
1452 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1455 case WM_KEYUP:
1456 //Platform::DebugPrintf("S keyup %d %x %x\n",iMessage, wParam, lParam);
1457 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1459 case WM_SETTINGCHANGE:
1460 //Platform::DebugPrintf("Setting Changed\n");
1461 InvalidateStyleData();
1462 // Get Intellimouse scroll line parameters
1463 GetIntelliMouseParameters();
1464 break;
1466 case WM_GETDLGCODE:
1467 return DLGC_HASSETSEL | DLGC_WANTALLKEYS;
1469 case WM_KILLFOCUS: {
1470 HWND wOther = reinterpret_cast<HWND>(wParam);
1471 HWND wThis = MainHWND();
1472 HWND wCT = reinterpret_cast<HWND>(ct.wCallTip.GetID());
1473 if (!wParam ||
1474 !(::IsChild(wThis, wOther) || (wOther == wCT))) {
1475 SetFocusState(false);
1476 DestroySystemCaret();
1478 // Explicitly complete any IME composition
1479 HIMC hIMC = ImmGetContext(MainHWND());
1480 if (hIMC) {
1481 ::ImmNotifyIME(hIMC, NI_COMPOSITIONSTR, CPS_COMPLETE, 0);
1482 ::ImmReleaseContext(MainHWND(), hIMC);
1485 break;
1487 case WM_SETFOCUS:
1488 SetFocusState(true);
1489 DestroySystemCaret();
1490 CreateSystemCaret();
1491 break;
1493 case WM_SYSCOLORCHANGE:
1494 //Platform::DebugPrintf("Setting Changed\n");
1495 InvalidateStyleData();
1496 break;
1498 case WM_IME_STARTCOMPOSITION: // dbcs
1499 if (KoreanIME() || imeInteraction == imeInline) {
1500 return 0;
1501 } else {
1502 ImeStartComposition();
1503 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1506 case WM_IME_ENDCOMPOSITION: // dbcs
1507 ImeEndComposition();
1508 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1510 case WM_IME_COMPOSITION:
1511 if (KoreanIME() || imeInteraction == imeInline) {
1512 return HandleCompositionInline(wParam, lParam);
1513 } else {
1514 return HandleCompositionWindowed(wParam, lParam);
1517 case WM_CONTEXTMENU:
1518 if (displayPopupMenu) {
1519 Point pt = Point::FromLong(static_cast<long>(lParam));
1520 if ((pt.x == -1) && (pt.y == -1)) {
1521 // Caused by keyboard so display menu near caret
1522 pt = PointMainCaret();
1523 POINT spt = {static_cast<int>(pt.x), static_cast<int>(pt.y)};
1524 ::ClientToScreen(MainHWND(), &spt);
1525 pt = PointFromPOINT(spt);
1527 ContextMenu(pt);
1528 return 0;
1530 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1532 case WM_INPUTLANGCHANGE:
1533 //::SetThreadLocale(LOWORD(lParam));
1534 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1536 case WM_INPUTLANGCHANGEREQUEST:
1537 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1539 case WM_ERASEBKGND:
1540 return 1; // Avoid any background erasure as whole window painted.
1542 case WM_CAPTURECHANGED:
1543 capturedMouse = false;
1544 return 0;
1546 case WM_IME_SETCONTEXT:
1547 if (KoreanIME() || imeInteraction == imeInline) {
1548 if (wParam) {
1549 LPARAM NoImeWin = lParam;
1550 NoImeWin = NoImeWin & (~ISC_SHOWUICOMPOSITIONWINDOW);
1551 return ::DefWindowProc(MainHWND(), iMessage, wParam, NoImeWin);
1554 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1556 // These are not handled in Scintilla and its faster to dispatch them here.
1557 // Also moves time out to here so profile doesn't count lots of empty message calls.
1559 case WM_MOVE:
1560 case WM_MOUSEACTIVATE:
1561 case WM_NCHITTEST:
1562 case WM_NCCALCSIZE:
1563 case WM_NCPAINT:
1564 case WM_NCMOUSEMOVE:
1565 case WM_NCLBUTTONDOWN:
1566 case WM_IME_NOTIFY:
1567 case WM_SYSCOMMAND:
1568 case WM_WINDOWPOSCHANGING:
1569 case WM_WINDOWPOSCHANGED:
1570 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1572 case WM_GETTEXTLENGTH:
1573 return GetTextLength();
1575 case WM_GETTEXT:
1576 return GetText(wParam, lParam);
1578 case EM_LINEFROMCHAR:
1579 if (static_cast<int>(wParam) < 0) {
1580 wParam = SelectionStart().Position();
1582 return pdoc->LineFromPosition(static_cast<int>(wParam));
1584 case EM_EXLINEFROMCHAR:
1585 return pdoc->LineFromPosition(static_cast<int>(lParam));
1587 case EM_GETSEL:
1588 if (wParam) {
1589 *reinterpret_cast<int *>(wParam) = SelectionStart().Position();
1591 if (lParam) {
1592 *reinterpret_cast<int *>(lParam) = SelectionEnd().Position();
1594 return MAKELONG(SelectionStart().Position(), SelectionEnd().Position());
1596 case EM_EXGETSEL: {
1597 if (lParam == 0) {
1598 return 0;
1600 Sci_CharacterRange *pCR = reinterpret_cast<Sci_CharacterRange *>(lParam);
1601 pCR->cpMin = SelectionStart().Position();
1602 pCR->cpMax = SelectionEnd().Position();
1604 break;
1606 case EM_SETSEL: {
1607 int nStart = static_cast<int>(wParam);
1608 int nEnd = static_cast<int>(lParam);
1609 if (nStart == 0 && nEnd == -1) {
1610 nEnd = pdoc->Length();
1612 if (nStart == -1) {
1613 nStart = nEnd; // Remove selection
1615 if (nStart > nEnd) {
1616 SetSelection(nEnd, nStart);
1617 } else {
1618 SetSelection(nStart, nEnd);
1620 EnsureCaretVisible();
1622 break;
1624 case EM_EXSETSEL: {
1625 if (lParam == 0) {
1626 return 0;
1628 Sci_CharacterRange *pCR = reinterpret_cast<Sci_CharacterRange *>(lParam);
1629 sel.selType = Selection::selStream;
1630 if (pCR->cpMin == 0 && pCR->cpMax == -1) {
1631 SetSelection(pCR->cpMin, pdoc->Length());
1632 } else {
1633 SetSelection(pCR->cpMin, pCR->cpMax);
1635 EnsureCaretVisible();
1636 return pdoc->LineFromPosition(SelectionStart().Position());
1639 case SCI_GETDIRECTFUNCTION:
1640 return reinterpret_cast<sptr_t>(DirectFunction);
1642 case SCI_GETDIRECTPOINTER:
1643 return reinterpret_cast<sptr_t>(this);
1645 case SCI_GRABFOCUS:
1646 ::SetFocus(MainHWND());
1647 break;
1649 #ifdef INCLUDE_DEPRECATED_FEATURES
1650 case SCI_SETKEYSUNICODE:
1651 break;
1653 case SCI_GETKEYSUNICODE:
1654 return true;
1655 #endif
1657 case SCI_SETTECHNOLOGY:
1658 if ((wParam == SC_TECHNOLOGY_DEFAULT) ||
1659 (wParam == SC_TECHNOLOGY_DIRECTWRITERETAIN) ||
1660 (wParam == SC_TECHNOLOGY_DIRECTWRITEDC) ||
1661 (wParam == SC_TECHNOLOGY_DIRECTWRITE)) {
1662 if (technology != static_cast<int>(wParam)) {
1663 if (static_cast<int>(wParam) > SC_TECHNOLOGY_DEFAULT) {
1664 #if defined(USE_D2D)
1665 if (!LoadD2D())
1666 // Failed to load Direct2D or DirectWrite so no effect
1667 return 0;
1668 #else
1669 return 0;
1670 #endif
1672 #if defined(USE_D2D)
1673 DropRenderTarget();
1674 #endif
1675 technology = static_cast<int>(wParam);
1676 // Invalidate all cached information including layout.
1677 DropGraphics(true);
1678 InvalidateStyleRedraw();
1681 break;
1683 #ifdef SCI_LEXER
1684 case SCI_LOADLEXERLIBRARY:
1685 LexerManager::GetInstance()->Load(reinterpret_cast<const char *>(lParam));
1686 break;
1687 #endif
1689 case SCI_TARGETASUTF8:
1690 return TargetAsUTF8(reinterpret_cast<char*>(lParam));
1692 case SCI_ENCODEDFROMUTF8:
1693 return EncodedFromUTF8(reinterpret_cast<char*>(wParam),
1694 reinterpret_cast<char*>(lParam));
1696 default:
1697 return ScintillaBase::WndProc(iMessage, wParam, lParam);
1699 } catch (std::bad_alloc &) {
1700 errorStatus = SC_STATUS_BADALLOC;
1701 } catch (...) {
1702 errorStatus = SC_STATUS_FAILURE;
1704 return 0l;
1707 bool ScintillaWin::ValidCodePage(int codePage) const {
1708 return codePage == 0 || codePage == SC_CP_UTF8 ||
1709 codePage == 932 || codePage == 936 || codePage == 949 ||
1710 codePage == 950 || codePage == 1361;
1713 sptr_t ScintillaWin::DefWndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
1714 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1718 * Report that this Editor subclass has a working implementation of FineTickerStart.
1720 bool ScintillaWin::FineTickerAvailable() {
1721 return true;
1724 bool ScintillaWin::FineTickerRunning(TickReason reason) {
1725 return timers[reason] != 0;
1728 void ScintillaWin::FineTickerStart(TickReason reason, int millis, int tolerance) {
1729 FineTickerCancel(reason);
1730 if (SetCoalescableTimerFn && tolerance) {
1731 timers[reason] = SetCoalescableTimerFn(MainHWND(), fineTimerStart + reason, millis, NULL, tolerance);
1732 } else {
1733 timers[reason] = ::SetTimer(MainHWND(), fineTimerStart + reason, millis, NULL);
1737 void ScintillaWin::FineTickerCancel(TickReason reason) {
1738 if (timers[reason]) {
1739 ::KillTimer(MainHWND(), timers[reason]);
1740 timers[reason] = 0;
1745 bool ScintillaWin::SetIdle(bool on) {
1746 // On Win32 the Idler is implemented as a Timer on the Scintilla window. This
1747 // takes advantage of the fact that WM_TIMER messages are very low priority,
1748 // and are only posted when the message queue is empty, i.e. during idle time.
1749 if (idler.state != on) {
1750 if (on) {
1751 idler.idlerID = ::SetTimer(MainHWND(), idleTimerID, 10, NULL)
1752 ? reinterpret_cast<IdlerID>(idleTimerID) : 0;
1753 } else {
1754 ::KillTimer(MainHWND(), reinterpret_cast<uptr_t>(idler.idlerID));
1755 idler.idlerID = 0;
1757 idler.state = idler.idlerID != 0;
1759 return idler.state;
1762 void ScintillaWin::SetMouseCapture(bool on) {
1763 if (mouseDownCaptures) {
1764 if (on) {
1765 ::SetCapture(MainHWND());
1766 } else {
1767 ::ReleaseCapture();
1770 capturedMouse = on;
1773 bool ScintillaWin::HaveMouseCapture() {
1774 // Cannot just see if GetCapture is this window as the scroll bar also sets capture for the window
1775 return capturedMouse;
1776 //return capturedMouse && (::GetCapture() == MainHWND());
1779 void ScintillaWin::SetTrackMouseLeaveEvent(bool on) {
1780 if (on && TrackMouseEventFn && !trackedMouseLeave) {
1781 TRACKMOUSEEVENT tme;
1782 tme.cbSize = sizeof(tme);
1783 tme.dwFlags = TME_LEAVE;
1784 tme.hwndTrack = MainHWND();
1785 tme.dwHoverTime = HOVER_DEFAULT; // Unused but triggers Dr. Memory if not initialized
1786 TrackMouseEventFn(&tme);
1788 trackedMouseLeave = on;
1791 bool ScintillaWin::PaintContains(PRectangle rc) {
1792 if (paintState == painting) {
1793 return BoundsContains(rcPaint, hRgnUpdate, rc);
1795 return true;
1798 void ScintillaWin::ScrollText(int /* linesToMove */) {
1799 //Platform::DebugPrintf("ScintillaWin::ScrollText %d\n", linesToMove);
1800 //::ScrollWindow(MainHWND(), 0,
1801 // vs.lineHeight * linesToMove, 0, 0);
1802 //::UpdateWindow(MainHWND());
1803 Redraw();
1804 UpdateSystemCaret();
1807 void ScintillaWin::UpdateSystemCaret() {
1808 if (hasFocus) {
1809 if (HasCaretSizeChanged()) {
1810 DestroySystemCaret();
1811 CreateSystemCaret();
1813 Point pos = PointMainCaret();
1814 ::SetCaretPos(static_cast<int>(pos.x), static_cast<int>(pos.y));
1818 int ScintillaWin::SetScrollInfo(int nBar, LPCSCROLLINFO lpsi, BOOL bRedraw) {
1819 return ::SetScrollInfo(MainHWND(), nBar, lpsi, bRedraw);
1822 bool ScintillaWin::GetScrollInfo(int nBar, LPSCROLLINFO lpsi) {
1823 return ::GetScrollInfo(MainHWND(), nBar, lpsi) ? true : false;
1826 // Change the scroll position but avoid repaint if changing to same value
1827 void ScintillaWin::ChangeScrollPos(int barType, int pos) {
1828 SCROLLINFO sci = {
1829 sizeof(sci), 0, 0, 0, 0, 0, 0
1831 sci.fMask = SIF_POS;
1832 GetScrollInfo(barType, &sci);
1833 if (sci.nPos != pos) {
1834 DwellEnd(true);
1835 sci.nPos = pos;
1836 SetScrollInfo(barType, &sci, TRUE);
1840 void ScintillaWin::SetVerticalScrollPos() {
1841 ChangeScrollPos(SB_VERT, topLine);
1844 void ScintillaWin::SetHorizontalScrollPos() {
1845 ChangeScrollPos(SB_HORZ, xOffset);
1848 bool ScintillaWin::ModifyScrollBars(int nMax, int nPage) {
1849 bool modified = false;
1850 SCROLLINFO sci = {
1851 sizeof(sci), 0, 0, 0, 0, 0, 0
1853 sci.fMask = SIF_PAGE | SIF_RANGE;
1854 GetScrollInfo(SB_VERT, &sci);
1855 int vertEndPreferred = nMax;
1856 if (!verticalScrollBarVisible)
1857 nPage = vertEndPreferred + 1;
1858 if ((sci.nMin != 0) ||
1859 (sci.nMax != vertEndPreferred) ||
1860 (sci.nPage != static_cast<unsigned int>(nPage)) ||
1861 (sci.nPos != 0)) {
1862 sci.fMask = SIF_PAGE | SIF_RANGE;
1863 sci.nMin = 0;
1864 sci.nMax = vertEndPreferred;
1865 sci.nPage = nPage;
1866 sci.nPos = 0;
1867 sci.nTrackPos = 1;
1868 SetScrollInfo(SB_VERT, &sci, TRUE);
1869 modified = true;
1872 PRectangle rcText = GetTextRectangle();
1873 int horizEndPreferred = scrollWidth;
1874 if (horizEndPreferred < 0)
1875 horizEndPreferred = 0;
1876 unsigned int pageWidth = static_cast<unsigned int>(rcText.Width());
1877 if (!horizontalScrollBarVisible || Wrapping())
1878 pageWidth = horizEndPreferred + 1;
1879 sci.fMask = SIF_PAGE | SIF_RANGE;
1880 GetScrollInfo(SB_HORZ, &sci);
1881 if ((sci.nMin != 0) ||
1882 (sci.nMax != horizEndPreferred) ||
1883 (sci.nPage != pageWidth) ||
1884 (sci.nPos != 0)) {
1885 sci.fMask = SIF_PAGE | SIF_RANGE;
1886 sci.nMin = 0;
1887 sci.nMax = horizEndPreferred;
1888 sci.nPage = pageWidth;
1889 sci.nPos = 0;
1890 sci.nTrackPos = 1;
1891 SetScrollInfo(SB_HORZ, &sci, TRUE);
1892 modified = true;
1893 if (scrollWidth < static_cast<int>(pageWidth)) {
1894 HorizontalScrollTo(0);
1897 return modified;
1900 void ScintillaWin::NotifyChange() {
1901 ::SendMessage(::GetParent(MainHWND()), WM_COMMAND,
1902 MAKELONG(GetCtrlID(), SCEN_CHANGE),
1903 reinterpret_cast<LPARAM>(MainHWND()));
1906 void ScintillaWin::NotifyFocus(bool focus) {
1907 ::SendMessage(::GetParent(MainHWND()), WM_COMMAND,
1908 MAKELONG(GetCtrlID(), focus ? SCEN_SETFOCUS : SCEN_KILLFOCUS),
1909 reinterpret_cast<LPARAM>(MainHWND()));
1910 Editor::NotifyFocus(focus);
1913 void ScintillaWin::SetCtrlID(int identifier) {
1914 ::SetWindowID(reinterpret_cast<HWND>(wMain.GetID()), identifier);
1917 int ScintillaWin::GetCtrlID() {
1918 return ::GetDlgCtrlID(reinterpret_cast<HWND>(wMain.GetID()));
1921 void ScintillaWin::NotifyParent(SCNotification scn) {
1922 scn.nmhdr.hwndFrom = MainHWND();
1923 scn.nmhdr.idFrom = GetCtrlID();
1924 ::SendMessage(::GetParent(MainHWND()), WM_NOTIFY,
1925 GetCtrlID(), reinterpret_cast<LPARAM>(&scn));
1928 void ScintillaWin::NotifyDoubleClick(Point pt, int modifiers) {
1929 //Platform::DebugPrintf("ScintillaWin Double click 0\n");
1930 ScintillaBase::NotifyDoubleClick(pt, modifiers);
1931 // Send myself a WM_LBUTTONDBLCLK, so the container can handle it too.
1932 ::SendMessage(MainHWND(),
1933 WM_LBUTTONDBLCLK,
1934 (modifiers & SCI_SHIFT) ? MK_SHIFT : 0,
1935 MAKELPARAM(pt.x, pt.y));
1938 class CaseFolderDBCS : public CaseFolderTable {
1939 // Allocate the expandable storage here so that it does not need to be reallocated
1940 // for each call to Fold.
1941 std::vector<wchar_t> utf16Mixed;
1942 std::vector<wchar_t> utf16Folded;
1943 UINT cp;
1944 public:
1945 explicit CaseFolderDBCS(UINT cp_) : cp(cp_) {
1946 StandardASCII();
1948 virtual size_t Fold(char *folded, size_t sizeFolded, const char *mixed, size_t lenMixed) {
1949 if ((lenMixed == 1) && (sizeFolded > 0)) {
1950 folded[0] = mapping[static_cast<unsigned char>(mixed[0])];
1951 return 1;
1952 } else {
1953 if (lenMixed > utf16Mixed.size()) {
1954 utf16Mixed.resize(lenMixed + 8);
1956 size_t nUtf16Mixed = ::MultiByteToWideChar(cp, 0, mixed,
1957 static_cast<int>(lenMixed),
1958 &utf16Mixed[0],
1959 static_cast<int>(utf16Mixed.size()));
1961 if (nUtf16Mixed == 0) {
1962 // Failed to convert -> bad input
1963 folded[0] = '\0';
1964 return 1;
1967 unsigned int lenFlat = 0;
1968 for (size_t mixIndex=0; mixIndex < nUtf16Mixed; mixIndex++) {
1969 if ((lenFlat + 20) > utf16Folded.size())
1970 utf16Folded.resize(lenFlat + 60);
1971 const char *foldedUTF8 = CaseConvert(utf16Mixed[mixIndex], CaseConversionFold);
1972 if (foldedUTF8) {
1973 // Maximum length of a case conversion is 6 bytes, 3 characters
1974 wchar_t wFolded[20];
1975 size_t charsConverted = UTF16FromUTF8(foldedUTF8,
1976 strlen(foldedUTF8),
1977 wFolded, ELEMENTS(wFolded));
1978 for (size_t j=0; j<charsConverted; j++)
1979 utf16Folded[lenFlat++] = wFolded[j];
1980 } else {
1981 utf16Folded[lenFlat++] = utf16Mixed[mixIndex];
1985 size_t lenOut = ::WideCharToMultiByte(cp, 0,
1986 &utf16Folded[0], lenFlat,
1987 NULL, 0, NULL, 0);
1989 if (lenOut < sizeFolded) {
1990 ::WideCharToMultiByte(cp, 0,
1991 &utf16Folded[0], lenFlat,
1992 folded, static_cast<int>(lenOut), NULL, 0);
1993 return lenOut;
1994 } else {
1995 return 0;
2001 CaseFolder *ScintillaWin::CaseFolderForEncoding() {
2002 UINT cpDest = CodePageOfDocument();
2003 if (cpDest == SC_CP_UTF8) {
2004 return new CaseFolderUnicode();
2005 } else {
2006 if (pdoc->dbcsCodePage == 0) {
2007 CaseFolderTable *pcf = new CaseFolderTable();
2008 pcf->StandardASCII();
2009 // Only for single byte encodings
2010 UINT cpDoc = CodePageOfDocument();
2011 for (int i=0x80; i<0x100; i++) {
2012 char sCharacter[2] = "A";
2013 sCharacter[0] = static_cast<char>(i);
2014 wchar_t wCharacter[20];
2015 unsigned int lengthUTF16 = ::MultiByteToWideChar(cpDoc, 0, sCharacter, 1,
2016 wCharacter, ELEMENTS(wCharacter));
2017 if (lengthUTF16 == 1) {
2018 const char *caseFolded = CaseConvert(wCharacter[0], CaseConversionFold);
2019 if (caseFolded) {
2020 wchar_t wLower[20];
2021 size_t charsConverted = UTF16FromUTF8(caseFolded,
2022 strlen(caseFolded),
2023 wLower, ELEMENTS(wLower));
2024 if (charsConverted == 1) {
2025 char sCharacterLowered[20];
2026 unsigned int lengthConverted = ::WideCharToMultiByte(cpDoc, 0,
2027 wLower, static_cast<int>(charsConverted),
2028 sCharacterLowered, ELEMENTS(sCharacterLowered), NULL, 0);
2029 if ((lengthConverted == 1) && (sCharacter[0] != sCharacterLowered[0])) {
2030 pcf->SetTranslation(sCharacter[0], sCharacterLowered[0]);
2036 return pcf;
2037 } else {
2038 return new CaseFolderDBCS(cpDest);
2043 std::string ScintillaWin::CaseMapString(const std::string &s, int caseMapping) {
2044 if ((s.size() == 0) || (caseMapping == cmSame))
2045 return s;
2047 UINT cpDoc = CodePageOfDocument();
2048 if (cpDoc == SC_CP_UTF8) {
2049 std::string retMapped(s.length() * maxExpansionCaseConversion, 0);
2050 size_t lenMapped = CaseConvertString(&retMapped[0], retMapped.length(), s.c_str(), s.length(),
2051 (caseMapping == cmUpper) ? CaseConversionUpper : CaseConversionLower);
2052 retMapped.resize(lenMapped);
2053 return retMapped;
2056 unsigned int lengthUTF16 = ::MultiByteToWideChar(cpDoc, 0, s.c_str(),
2057 static_cast<int>(s.size()), NULL, 0);
2058 if (lengthUTF16 == 0) // Failed to convert
2059 return s;
2061 DWORD mapFlags = LCMAP_LINGUISTIC_CASING |
2062 ((caseMapping == cmUpper) ? LCMAP_UPPERCASE : LCMAP_LOWERCASE);
2064 // Change text to UTF-16
2065 std::vector<wchar_t> vwcText(lengthUTF16);
2066 ::MultiByteToWideChar(cpDoc, 0, s.c_str(), static_cast<int>(s.size()), &vwcText[0], lengthUTF16);
2068 // Change case
2069 int charsConverted = ::LCMapStringW(LOCALE_SYSTEM_DEFAULT, mapFlags,
2070 &vwcText[0], lengthUTF16, NULL, 0);
2071 std::vector<wchar_t> vwcConverted(charsConverted);
2072 ::LCMapStringW(LOCALE_SYSTEM_DEFAULT, mapFlags,
2073 &vwcText[0], lengthUTF16, &vwcConverted[0], charsConverted);
2075 // Change back to document encoding
2076 unsigned int lengthConverted = ::WideCharToMultiByte(cpDoc, 0,
2077 &vwcConverted[0], static_cast<int>(vwcConverted.size()),
2078 NULL, 0, NULL, 0);
2079 std::vector<char> vcConverted(lengthConverted);
2080 ::WideCharToMultiByte(cpDoc, 0,
2081 &vwcConverted[0], static_cast<int>(vwcConverted.size()),
2082 &vcConverted[0], static_cast<int>(vcConverted.size()), NULL, 0);
2084 return std::string(&vcConverted[0], vcConverted.size());
2087 void ScintillaWin::Copy() {
2088 //Platform::DebugPrintf("Copy\n");
2089 if (!sel.Empty()) {
2090 SelectionText selectedText;
2091 CopySelectionRange(&selectedText);
2092 CopyToClipboard(selectedText);
2096 void ScintillaWin::CopyAllowLine() {
2097 SelectionText selectedText;
2098 CopySelectionRange(&selectedText, true);
2099 CopyToClipboard(selectedText);
2102 bool ScintillaWin::CanPaste() {
2103 if (!Editor::CanPaste())
2104 return false;
2105 if (::IsClipboardFormatAvailable(CF_TEXT))
2106 return true;
2107 if (IsUnicodeMode())
2108 return ::IsClipboardFormatAvailable(CF_UNICODETEXT) != 0;
2109 return false;
2112 class GlobalMemory {
2113 HGLOBAL hand;
2114 public:
2115 void *ptr;
2116 GlobalMemory() : hand(0), ptr(0) {
2118 explicit GlobalMemory(HGLOBAL hand_) : hand(hand_), ptr(0) {
2119 if (hand) {
2120 ptr = ::GlobalLock(hand);
2123 ~GlobalMemory() {
2124 PLATFORM_ASSERT(!ptr);
2125 assert(!hand);
2127 void Allocate(size_t bytes) {
2128 assert(!hand);
2129 hand = ::GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT, bytes);
2130 if (hand) {
2131 ptr = ::GlobalLock(hand);
2134 HGLOBAL Unlock() {
2135 PLATFORM_ASSERT(ptr);
2136 HGLOBAL handCopy = hand;
2137 ::GlobalUnlock(hand);
2138 ptr = 0;
2139 hand = 0;
2140 return handCopy;
2142 void SetClip(UINT uFormat) {
2143 ::SetClipboardData(uFormat, Unlock());
2145 operator bool() const {
2146 return ptr != 0;
2148 SIZE_T Size() {
2149 return ::GlobalSize(hand);
2153 void ScintillaWin::Paste() {
2154 if (!::OpenClipboard(MainHWND()))
2155 return;
2156 UndoGroup ug(pdoc);
2157 const bool isLine = SelectionEmpty() &&
2158 (::IsClipboardFormatAvailable(cfLineSelect) || ::IsClipboardFormatAvailable(cfVSLineTag));
2159 ClearSelection(multiPasteMode == SC_MULTIPASTE_EACH);
2160 bool isRectangular = (::IsClipboardFormatAvailable(cfColumnSelect) != 0);
2162 if (!isRectangular) {
2163 // Evaluate "Borland IDE Block Type" explicitly
2164 GlobalMemory memBorlandSelection(::GetClipboardData(cfBorlandIDEBlockType));
2165 if (memBorlandSelection) {
2166 isRectangular = (memBorlandSelection.Size() == 1) && (static_cast<BYTE *>(memBorlandSelection.ptr)[0] == 0x02);
2167 memBorlandSelection.Unlock();
2170 const PasteShape pasteShape = isRectangular ? pasteRectangular : (isLine ? pasteLine : pasteStream);
2172 // Always use CF_UNICODETEXT if available
2173 GlobalMemory memUSelection(::GetClipboardData(CF_UNICODETEXT));
2174 if (memUSelection) {
2175 wchar_t *uptr = static_cast<wchar_t *>(memUSelection.ptr);
2176 if (uptr) {
2177 unsigned int len;
2178 std::vector<char> putf;
2179 // Default Scintilla behaviour in Unicode mode
2180 if (IsUnicodeMode()) {
2181 unsigned int bytes = static_cast<unsigned int>(memUSelection.Size());
2182 len = UTF8Length(uptr, bytes / 2);
2183 putf.resize(len + 1);
2184 UTF8FromUTF16(uptr, bytes / 2, &putf[0], len);
2185 } else {
2186 // CF_UNICODETEXT available, but not in Unicode mode
2187 // Convert from Unicode to current Scintilla code page
2188 UINT cpDest = CodePageOfDocument();
2189 len = ::WideCharToMultiByte(cpDest, 0, uptr, -1,
2190 NULL, 0, NULL, NULL) - 1; // subtract 0 terminator
2191 putf.resize(len + 1);
2192 ::WideCharToMultiByte(cpDest, 0, uptr, -1,
2193 &putf[0], len + 1, NULL, NULL);
2196 InsertPasteShape(&putf[0], len, pasteShape);
2198 memUSelection.Unlock();
2199 } else {
2200 // CF_UNICODETEXT not available, paste ANSI text
2201 GlobalMemory memSelection(::GetClipboardData(CF_TEXT));
2202 if (memSelection) {
2203 char *ptr = static_cast<char *>(memSelection.ptr);
2204 if (ptr) {
2205 unsigned int bytes = static_cast<unsigned int>(memSelection.Size());
2206 unsigned int len = bytes;
2207 for (unsigned int i = 0; i < bytes; i++) {
2208 if ((len == bytes) && (0 == ptr[i]))
2209 len = i;
2212 // In Unicode mode, convert clipboard text to UTF-8
2213 if (IsUnicodeMode()) {
2214 std::vector<wchar_t> uptr(len+1);
2216 unsigned int ulen = ::MultiByteToWideChar(CP_ACP, 0,
2217 ptr, len, &uptr[0], len+1);
2219 unsigned int mlen = UTF8Length(&uptr[0], ulen);
2220 std::vector<char> putf(mlen+1);
2221 UTF8FromUTF16(&uptr[0], ulen, &putf[0], mlen);
2223 InsertPasteShape(&putf[0], mlen, pasteShape);
2224 } else {
2225 InsertPasteShape(ptr, len, pasteShape);
2228 memSelection.Unlock();
2231 ::CloseClipboard();
2232 Redraw();
2235 void ScintillaWin::CreateCallTipWindow(PRectangle) {
2236 if (!ct.wCallTip.Created()) {
2237 ct.wCallTip = ::CreateWindow(callClassName, TEXT("ACallTip"),
2238 WS_POPUP, 100, 100, 150, 20,
2239 MainHWND(), 0,
2240 GetWindowInstance(MainHWND()),
2241 this);
2242 ct.wDraw = ct.wCallTip;
2246 void ScintillaWin::AddToPopUp(const char *label, int cmd, bool enabled) {
2247 HMENU hmenuPopup = reinterpret_cast<HMENU>(popup.GetID());
2248 if (!label[0])
2249 ::AppendMenuA(hmenuPopup, MF_SEPARATOR, 0, "");
2250 else if (enabled)
2251 ::AppendMenuA(hmenuPopup, MF_STRING, cmd, label);
2252 else
2253 ::AppendMenuA(hmenuPopup, MF_STRING | MF_DISABLED | MF_GRAYED, cmd, label);
2256 void ScintillaWin::ClaimSelection() {
2257 // Windows does not have a primary selection
2260 /// Implement IUnknown
2262 STDMETHODIMP_(ULONG)FormatEnumerator_AddRef(FormatEnumerator *fe);
2263 STDMETHODIMP FormatEnumerator_QueryInterface(FormatEnumerator *fe, REFIID riid, PVOID *ppv) {
2264 //Platform::DebugPrintf("EFE QI");
2265 *ppv = NULL;
2266 if (riid == IID_IUnknown)
2267 *ppv = reinterpret_cast<IEnumFORMATETC *>(fe);
2268 if (riid == IID_IEnumFORMATETC)
2269 *ppv = reinterpret_cast<IEnumFORMATETC *>(fe);
2270 if (!*ppv)
2271 return E_NOINTERFACE;
2272 FormatEnumerator_AddRef(fe);
2273 return S_OK;
2275 STDMETHODIMP_(ULONG)FormatEnumerator_AddRef(FormatEnumerator *fe) {
2276 return ++fe->ref;
2278 STDMETHODIMP_(ULONG)FormatEnumerator_Release(FormatEnumerator *fe) {
2279 fe->ref--;
2280 if (fe->ref > 0)
2281 return fe->ref;
2282 delete fe;
2283 return 0;
2285 /// Implement IEnumFORMATETC
2286 STDMETHODIMP FormatEnumerator_Next(FormatEnumerator *fe, ULONG celt, FORMATETC *rgelt, ULONG *pceltFetched) {
2287 if (rgelt == NULL) return E_POINTER;
2288 unsigned int putPos = 0;
2289 while ((fe->pos < fe->formats.size()) && (putPos < celt)) {
2290 rgelt->cfFormat = fe->formats[fe->pos];
2291 rgelt->ptd = 0;
2292 rgelt->dwAspect = DVASPECT_CONTENT;
2293 rgelt->lindex = -1;
2294 rgelt->tymed = TYMED_HGLOBAL;
2295 rgelt++;
2296 fe->pos++;
2297 putPos++;
2299 if (pceltFetched)
2300 *pceltFetched = putPos;
2301 return putPos ? S_OK : S_FALSE;
2303 STDMETHODIMP FormatEnumerator_Skip(FormatEnumerator *fe, ULONG celt) {
2304 fe->pos += celt;
2305 return S_OK;
2307 STDMETHODIMP FormatEnumerator_Reset(FormatEnumerator *fe) {
2308 fe->pos = 0;
2309 return S_OK;
2311 STDMETHODIMP FormatEnumerator_Clone(FormatEnumerator *fe, IEnumFORMATETC **ppenum) {
2312 FormatEnumerator *pfe;
2313 try {
2314 pfe = new FormatEnumerator(fe->pos, &fe->formats[0], fe->formats.size());
2315 } catch (...) {
2316 return E_OUTOFMEMORY;
2318 return FormatEnumerator_QueryInterface(pfe, IID_IEnumFORMATETC,
2319 reinterpret_cast<void **>(ppenum));
2322 static VFunction *vtFormatEnumerator[] = {
2323 (VFunction *)(FormatEnumerator_QueryInterface),
2324 (VFunction *)(FormatEnumerator_AddRef),
2325 (VFunction *)(FormatEnumerator_Release),
2326 (VFunction *)(FormatEnumerator_Next),
2327 (VFunction *)(FormatEnumerator_Skip),
2328 (VFunction *)(FormatEnumerator_Reset),
2329 (VFunction *)(FormatEnumerator_Clone)
2332 FormatEnumerator::FormatEnumerator(int pos_, CLIPFORMAT formats_[], size_t formatsLen_) {
2333 vtbl = vtFormatEnumerator;
2334 ref = 0; // First QI adds first reference...
2335 pos = pos_;
2336 formats.insert(formats.begin(), formats_, formats_+formatsLen_);
2339 /// Implement IUnknown
2340 STDMETHODIMP DropSource_QueryInterface(DropSource *ds, REFIID riid, PVOID *ppv) {
2341 return ds->sci->QueryInterface(riid, ppv);
2343 STDMETHODIMP_(ULONG)DropSource_AddRef(DropSource *ds) {
2344 return ds->sci->AddRef();
2346 STDMETHODIMP_(ULONG)DropSource_Release(DropSource *ds) {
2347 return ds->sci->Release();
2350 /// Implement IDropSource
2351 STDMETHODIMP DropSource_QueryContinueDrag(DropSource *, BOOL fEsc, DWORD grfKeyState) {
2352 if (fEsc)
2353 return DRAGDROP_S_CANCEL;
2354 if (!(grfKeyState & MK_LBUTTON))
2355 return DRAGDROP_S_DROP;
2356 return S_OK;
2359 STDMETHODIMP DropSource_GiveFeedback(DropSource *, DWORD) {
2360 return DRAGDROP_S_USEDEFAULTCURSORS;
2363 static VFunction *vtDropSource[] = {
2364 (VFunction *)(DropSource_QueryInterface),
2365 (VFunction *)(DropSource_AddRef),
2366 (VFunction *)(DropSource_Release),
2367 (VFunction *)(DropSource_QueryContinueDrag),
2368 (VFunction *)(DropSource_GiveFeedback)
2371 DropSource::DropSource() {
2372 vtbl = vtDropSource;
2373 sci = 0;
2376 /// Implement IUnkown
2377 STDMETHODIMP DataObject_QueryInterface(DataObject *pd, REFIID riid, PVOID *ppv) {
2378 //Platform::DebugPrintf("DO QI %x\n", pd);
2379 return pd->sci->QueryInterface(riid, ppv);
2381 STDMETHODIMP_(ULONG)DataObject_AddRef(DataObject *pd) {
2382 return pd->sci->AddRef();
2384 STDMETHODIMP_(ULONG)DataObject_Release(DataObject *pd) {
2385 return pd->sci->Release();
2387 /// Implement IDataObject
2388 STDMETHODIMP DataObject_GetData(DataObject *pd, FORMATETC *pFEIn, STGMEDIUM *pSTM) {
2389 return pd->sci->GetData(pFEIn, pSTM);
2392 STDMETHODIMP DataObject_GetDataHere(DataObject *, FORMATETC *, STGMEDIUM *) {
2393 //Platform::DebugPrintf("DOB GetDataHere\n");
2394 return E_NOTIMPL;
2397 STDMETHODIMP DataObject_QueryGetData(DataObject *pd, FORMATETC *pFE) {
2398 if (pd->sci->DragIsRectangularOK(pFE->cfFormat) &&
2399 pFE->ptd == 0 &&
2400 (pFE->dwAspect & DVASPECT_CONTENT) != 0 &&
2401 pFE->lindex == -1 &&
2402 (pFE->tymed & TYMED_HGLOBAL) != 0
2404 return S_OK;
2407 bool formatOK = (pFE->cfFormat == CF_TEXT) ||
2408 ((pFE->cfFormat == CF_UNICODETEXT) && pd->sci->IsUnicodeMode());
2409 if (!formatOK ||
2410 pFE->ptd != 0 ||
2411 (pFE->dwAspect & DVASPECT_CONTENT) == 0 ||
2412 pFE->lindex != -1 ||
2413 (pFE->tymed & TYMED_HGLOBAL) == 0
2415 //Platform::DebugPrintf("DOB QueryGetData No %x\n",pFE->cfFormat);
2416 //return DATA_E_FORMATETC;
2417 return S_FALSE;
2419 //Platform::DebugPrintf("DOB QueryGetData OK %x\n",pFE->cfFormat);
2420 return S_OK;
2423 STDMETHODIMP DataObject_GetCanonicalFormatEtc(DataObject *pd, FORMATETC *, FORMATETC *pFEOut) {
2424 //Platform::DebugPrintf("DOB GetCanon\n");
2425 if (pd->sci->IsUnicodeMode())
2426 pFEOut->cfFormat = CF_UNICODETEXT;
2427 else
2428 pFEOut->cfFormat = CF_TEXT;
2429 pFEOut->ptd = 0;
2430 pFEOut->dwAspect = DVASPECT_CONTENT;
2431 pFEOut->lindex = -1;
2432 pFEOut->tymed = TYMED_HGLOBAL;
2433 return S_OK;
2436 STDMETHODIMP DataObject_SetData(DataObject *, FORMATETC *, STGMEDIUM *, BOOL) {
2437 //Platform::DebugPrintf("DOB SetData\n");
2438 return E_FAIL;
2441 STDMETHODIMP DataObject_EnumFormatEtc(DataObject *pd, DWORD dwDirection, IEnumFORMATETC **ppEnum) {
2442 try {
2443 //Platform::DebugPrintf("DOB EnumFormatEtc %d\n", dwDirection);
2444 if (dwDirection != DATADIR_GET) {
2445 *ppEnum = 0;
2446 return E_FAIL;
2448 FormatEnumerator *pfe;
2449 if (pd->sci->IsUnicodeMode()) {
2450 CLIPFORMAT formats[] = {CF_UNICODETEXT, CF_TEXT};
2451 pfe = new FormatEnumerator(0, formats, ELEMENTS(formats));
2452 } else {
2453 CLIPFORMAT formats[] = {CF_TEXT};
2454 pfe = new FormatEnumerator(0, formats, ELEMENTS(formats));
2456 return FormatEnumerator_QueryInterface(pfe, IID_IEnumFORMATETC,
2457 reinterpret_cast<void **>(ppEnum));
2458 } catch (std::bad_alloc &) {
2459 pd->sci->errorStatus = SC_STATUS_BADALLOC;
2460 return E_OUTOFMEMORY;
2461 } catch (...) {
2462 pd->sci->errorStatus = SC_STATUS_FAILURE;
2463 return E_FAIL;
2467 STDMETHODIMP DataObject_DAdvise(DataObject *, FORMATETC *, DWORD, IAdviseSink *, PDWORD) {
2468 //Platform::DebugPrintf("DOB DAdvise\n");
2469 return E_FAIL;
2472 STDMETHODIMP DataObject_DUnadvise(DataObject *, DWORD) {
2473 //Platform::DebugPrintf("DOB DUnadvise\n");
2474 return E_FAIL;
2477 STDMETHODIMP DataObject_EnumDAdvise(DataObject *, IEnumSTATDATA **) {
2478 //Platform::DebugPrintf("DOB EnumDAdvise\n");
2479 return E_FAIL;
2482 static VFunction *vtDataObject[] = {
2483 (VFunction *)(DataObject_QueryInterface),
2484 (VFunction *)(DataObject_AddRef),
2485 (VFunction *)(DataObject_Release),
2486 (VFunction *)(DataObject_GetData),
2487 (VFunction *)(DataObject_GetDataHere),
2488 (VFunction *)(DataObject_QueryGetData),
2489 (VFunction *)(DataObject_GetCanonicalFormatEtc),
2490 (VFunction *)(DataObject_SetData),
2491 (VFunction *)(DataObject_EnumFormatEtc),
2492 (VFunction *)(DataObject_DAdvise),
2493 (VFunction *)(DataObject_DUnadvise),
2494 (VFunction *)(DataObject_EnumDAdvise)
2497 DataObject::DataObject() {
2498 vtbl = vtDataObject;
2499 sci = 0;
2502 /// Implement IUnknown
2503 STDMETHODIMP DropTarget_QueryInterface(DropTarget *dt, REFIID riid, PVOID *ppv) {
2504 //Platform::DebugPrintf("DT QI %x\n", dt);
2505 return dt->sci->QueryInterface(riid, ppv);
2507 STDMETHODIMP_(ULONG)DropTarget_AddRef(DropTarget *dt) {
2508 return dt->sci->AddRef();
2510 STDMETHODIMP_(ULONG)DropTarget_Release(DropTarget *dt) {
2511 return dt->sci->Release();
2514 /// Implement IDropTarget by forwarding to Scintilla
2515 STDMETHODIMP DropTarget_DragEnter(DropTarget *dt, LPDATAOBJECT pIDataSource, DWORD grfKeyState,
2516 POINTL pt, PDWORD pdwEffect) {
2517 try {
2518 return dt->sci->DragEnter(pIDataSource, grfKeyState, pt, pdwEffect);
2519 } catch (...) {
2520 dt->sci->errorStatus = SC_STATUS_FAILURE;
2522 return E_FAIL;
2524 STDMETHODIMP DropTarget_DragOver(DropTarget *dt, DWORD grfKeyState, POINTL pt, PDWORD pdwEffect) {
2525 try {
2526 return dt->sci->DragOver(grfKeyState, pt, pdwEffect);
2527 } catch (...) {
2528 dt->sci->errorStatus = SC_STATUS_FAILURE;
2530 return E_FAIL;
2532 STDMETHODIMP DropTarget_DragLeave(DropTarget *dt) {
2533 try {
2534 return dt->sci->DragLeave();
2535 } catch (...) {
2536 dt->sci->errorStatus = SC_STATUS_FAILURE;
2538 return E_FAIL;
2540 STDMETHODIMP DropTarget_Drop(DropTarget *dt, LPDATAOBJECT pIDataSource, DWORD grfKeyState,
2541 POINTL pt, PDWORD pdwEffect) {
2542 try {
2543 return dt->sci->Drop(pIDataSource, grfKeyState, pt, pdwEffect);
2544 } catch (...) {
2545 dt->sci->errorStatus = SC_STATUS_FAILURE;
2547 return E_FAIL;
2550 static VFunction *vtDropTarget[] = {
2551 (VFunction *)(DropTarget_QueryInterface),
2552 (VFunction *)(DropTarget_AddRef),
2553 (VFunction *)(DropTarget_Release),
2554 (VFunction *)(DropTarget_DragEnter),
2555 (VFunction *)(DropTarget_DragOver),
2556 (VFunction *)(DropTarget_DragLeave),
2557 (VFunction *)(DropTarget_Drop)
2560 DropTarget::DropTarget() {
2561 vtbl = vtDropTarget;
2562 sci = 0;
2566 * DBCS: support Input Method Editor (IME).
2567 * Called when IME Window opened.
2569 void ScintillaWin::ImeStartComposition() {
2570 if (caret.active) {
2571 // Move IME Window to current caret position
2572 HIMC hIMC = ::ImmGetContext(MainHWND());
2573 Point pos = PointMainCaret();
2574 COMPOSITIONFORM CompForm;
2575 CompForm.dwStyle = CFS_POINT;
2576 CompForm.ptCurrentPos.x = static_cast<int>(pos.x);
2577 CompForm.ptCurrentPos.y = static_cast<int>(pos.y);
2579 ::ImmSetCompositionWindow(hIMC, &CompForm);
2581 // Set font of IME window to same as surrounded text.
2582 if (stylesValid) {
2583 // Since the style creation code has been made platform independent,
2584 // The logfont for the IME is recreated here.
2585 int styleHere = (pdoc->StyleAt(sel.MainCaret())) & 31;
2586 LOGFONTW lf = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, L""};
2587 int sizeZoomed = vs.styles[styleHere].size + vs.zoomLevel * SC_FONT_SIZE_MULTIPLIER;
2588 if (sizeZoomed <= 2 * SC_FONT_SIZE_MULTIPLIER) // Hangs if sizeZoomed <= 1
2589 sizeZoomed = 2 * SC_FONT_SIZE_MULTIPLIER;
2590 AutoSurface surface(this);
2591 int deviceHeight = sizeZoomed;
2592 if (surface) {
2593 deviceHeight = (sizeZoomed * surface->LogPixelsY()) / 72;
2595 // The negative is to allow for leading
2596 lf.lfHeight = -(abs(deviceHeight / SC_FONT_SIZE_MULTIPLIER));
2597 lf.lfWeight = vs.styles[styleHere].weight;
2598 lf.lfItalic = static_cast<BYTE>(vs.styles[styleHere].italic ? 1 : 0);
2599 lf.lfCharSet = DEFAULT_CHARSET;
2600 lf.lfFaceName[0] = L'\0';
2601 if (vs.styles[styleHere].fontName) {
2602 const char* fontName = vs.styles[styleHere].fontName;
2603 UTF16FromUTF8(fontName, strlen(fontName)+1, lf.lfFaceName, LF_FACESIZE);
2606 ::ImmSetCompositionFontW(hIMC, &lf);
2608 ::ImmReleaseContext(MainHWND(), hIMC);
2609 // Caret is displayed in IME window. So, caret in Scintilla is useless.
2610 DropCaret();
2614 /** Called when IME Window closed. */
2615 void ScintillaWin::ImeEndComposition() {
2616 ShowCaretAtCurrentPosition();
2619 void ScintillaWin::GetIntelliMouseParameters() {
2620 // This retrieves the number of lines per scroll as configured inthe Mouse Properties sheet in Control Panel
2621 ::SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, &linesPerScroll, 0);
2624 void ScintillaWin::CopyToClipboard(const SelectionText &selectedText) {
2625 if (!::OpenClipboard(MainHWND()))
2626 return;
2627 ::EmptyClipboard();
2629 GlobalMemory uniText;
2631 // Default Scintilla behaviour in Unicode mode
2632 if (IsUnicodeMode()) {
2633 size_t uchars = UTF16Length(selectedText.Data(),
2634 static_cast<int>(selectedText.LengthWithTerminator()));
2635 uniText.Allocate(2 * uchars);
2636 if (uniText) {
2637 UTF16FromUTF8(selectedText.Data(), selectedText.LengthWithTerminator(),
2638 static_cast<wchar_t *>(uniText.ptr), uchars);
2640 } else {
2641 // Not Unicode mode
2642 // Convert to Unicode using the current Scintilla code page
2643 UINT cpSrc = CodePageFromCharSet(
2644 selectedText.characterSet, selectedText.codePage);
2645 int uLen = ::MultiByteToWideChar(cpSrc, 0, selectedText.Data(),
2646 static_cast<int>(selectedText.LengthWithTerminator()), 0, 0);
2647 uniText.Allocate(2 * uLen);
2648 if (uniText) {
2649 ::MultiByteToWideChar(cpSrc, 0, selectedText.Data(),
2650 static_cast<int>(selectedText.LengthWithTerminator()),
2651 static_cast<wchar_t *>(uniText.ptr), uLen);
2655 if (uniText) {
2656 uniText.SetClip(CF_UNICODETEXT);
2657 } else {
2658 // There was a failure - try to copy at least ANSI text
2659 GlobalMemory ansiText;
2660 ansiText.Allocate(selectedText.LengthWithTerminator());
2661 if (ansiText) {
2662 memcpy(static_cast<char *>(ansiText.ptr), selectedText.Data(), selectedText.LengthWithTerminator());
2663 ansiText.SetClip(CF_TEXT);
2667 if (selectedText.rectangular) {
2668 ::SetClipboardData(cfColumnSelect, 0);
2670 GlobalMemory borlandSelection;
2671 borlandSelection.Allocate(1);
2672 if (borlandSelection) {
2673 static_cast<BYTE *>(borlandSelection.ptr)[0] = 0x02;
2674 borlandSelection.SetClip(cfBorlandIDEBlockType);
2678 if (selectedText.lineCopy) {
2679 ::SetClipboardData(cfLineSelect, 0);
2680 ::SetClipboardData(cfVSLineTag, 0);
2683 ::CloseClipboard();
2686 void ScintillaWin::ScrollMessage(WPARAM wParam) {
2687 //DWORD dwStart = timeGetTime();
2688 //Platform::DebugPrintf("Scroll %x %d\n", wParam, lParam);
2690 SCROLLINFO sci = {};
2691 sci.cbSize = sizeof(sci);
2692 sci.fMask = SIF_ALL;
2694 GetScrollInfo(SB_VERT, &sci);
2696 //Platform::DebugPrintf("ScrollInfo %d mask=%x min=%d max=%d page=%d pos=%d track=%d\n", b,sci.fMask,
2697 //sci.nMin, sci.nMax, sci.nPage, sci.nPos, sci.nTrackPos);
2699 int topLineNew = topLine;
2700 switch (LoWord(wParam)) {
2701 case SB_LINEUP:
2702 topLineNew -= 1;
2703 break;
2704 case SB_LINEDOWN:
2705 topLineNew += 1;
2706 break;
2707 case SB_PAGEUP:
2708 topLineNew -= LinesToScroll(); break;
2709 case SB_PAGEDOWN: topLineNew += LinesToScroll(); break;
2710 case SB_TOP: topLineNew = 0; break;
2711 case SB_BOTTOM: topLineNew = MaxScrollPos(); break;
2712 case SB_THUMBPOSITION: topLineNew = sci.nTrackPos; break;
2713 case SB_THUMBTRACK: topLineNew = sci.nTrackPos; break;
2715 ScrollTo(topLineNew);
2718 void ScintillaWin::HorizontalScrollMessage(WPARAM wParam) {
2719 int xPos = xOffset;
2720 PRectangle rcText = GetTextRectangle();
2721 int pageWidth = static_cast<int>(rcText.Width() * 2 / 3);
2722 switch (LoWord(wParam)) {
2723 case SB_LINEUP:
2724 xPos -= 20;
2725 break;
2726 case SB_LINEDOWN: // May move past the logical end
2727 xPos += 20;
2728 break;
2729 case SB_PAGEUP:
2730 xPos -= pageWidth;
2731 break;
2732 case SB_PAGEDOWN:
2733 xPos += pageWidth;
2734 if (xPos > scrollWidth - rcText.Width()) { // Hit the end exactly
2735 xPos = scrollWidth - static_cast<int>(rcText.Width());
2737 break;
2738 case SB_TOP:
2739 xPos = 0;
2740 break;
2741 case SB_BOTTOM:
2742 xPos = scrollWidth;
2743 break;
2744 case SB_THUMBPOSITION:
2745 case SB_THUMBTRACK: {
2746 // 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 =]
2747 SCROLLINFO si;
2748 si.cbSize = sizeof(si);
2749 si.fMask = SIF_TRACKPOS;
2750 if (GetScrollInfo(SB_HORZ, &si)) {
2751 xPos = si.nTrackPos;
2754 break;
2756 HorizontalScrollTo(xPos);
2760 * Redraw all of text area.
2761 * This paint will not be abandoned.
2763 void ScintillaWin::FullPaint() {
2764 if ((technology == SC_TECHNOLOGY_DEFAULT) || (technology == SC_TECHNOLOGY_DIRECTWRITEDC)) {
2765 HDC hdc = ::GetDC(MainHWND());
2766 FullPaintDC(hdc);
2767 ::ReleaseDC(MainHWND(), hdc);
2768 } else {
2769 FullPaintDC(0);
2774 * Redraw all of text area on the specified DC.
2775 * This paint will not be abandoned.
2777 void ScintillaWin::FullPaintDC(HDC hdc) {
2778 paintState = painting;
2779 rcPaint = GetClientRectangle();
2780 paintingAllText = true;
2781 if (technology == SC_TECHNOLOGY_DEFAULT) {
2782 AutoSurface surfaceWindow(hdc, this);
2783 if (surfaceWindow) {
2784 Paint(surfaceWindow, rcPaint);
2785 surfaceWindow->Release();
2787 } else {
2788 #if defined(USE_D2D)
2789 EnsureRenderTarget(hdc);
2790 AutoSurface surfaceWindow(pRenderTarget, this);
2791 if (surfaceWindow) {
2792 pRenderTarget->BeginDraw();
2793 Paint(surfaceWindow, rcPaint);
2794 surfaceWindow->Release();
2795 HRESULT hr = pRenderTarget->EndDraw();
2796 if (hr == D2DERR_RECREATE_TARGET) {
2797 DropRenderTarget();
2800 #endif
2802 paintState = notPainting;
2805 static bool CompareDevCap(HDC hdc, HDC hOtherDC, int nIndex) {
2806 return ::GetDeviceCaps(hdc, nIndex) == ::GetDeviceCaps(hOtherDC, nIndex);
2809 bool ScintillaWin::IsCompatibleDC(HDC hOtherDC) {
2810 HDC hdc = ::GetDC(MainHWND());
2811 bool isCompatible =
2812 CompareDevCap(hdc, hOtherDC, TECHNOLOGY) &&
2813 CompareDevCap(hdc, hOtherDC, LOGPIXELSY) &&
2814 CompareDevCap(hdc, hOtherDC, LOGPIXELSX) &&
2815 CompareDevCap(hdc, hOtherDC, BITSPIXEL) &&
2816 CompareDevCap(hdc, hOtherDC, PLANES);
2817 ::ReleaseDC(MainHWND(), hdc);
2818 return isCompatible;
2821 DWORD ScintillaWin::EffectFromState(DWORD grfKeyState) const {
2822 // These are the Wordpad semantics.
2823 DWORD dwEffect;
2824 if (inDragDrop == ddDragging) // Internal defaults to move
2825 dwEffect = DROPEFFECT_MOVE;
2826 else
2827 dwEffect = DROPEFFECT_COPY;
2828 if (grfKeyState & MK_ALT)
2829 dwEffect = DROPEFFECT_MOVE;
2830 if (grfKeyState & MK_CONTROL)
2831 dwEffect = DROPEFFECT_COPY;
2832 return dwEffect;
2835 /// Implement IUnknown
2836 STDMETHODIMP ScintillaWin::QueryInterface(REFIID riid, PVOID *ppv) {
2837 *ppv = NULL;
2838 if (riid == IID_IUnknown)
2839 *ppv = reinterpret_cast<IDropTarget *>(&dt);
2840 if (riid == IID_IDropSource)
2841 *ppv = reinterpret_cast<IDropSource *>(&ds);
2842 if (riid == IID_IDropTarget)
2843 *ppv = reinterpret_cast<IDropTarget *>(&dt);
2844 if (riid == IID_IDataObject)
2845 *ppv = reinterpret_cast<IDataObject *>(&dob);
2846 if (!*ppv)
2847 return E_NOINTERFACE;
2848 return S_OK;
2851 STDMETHODIMP_(ULONG) ScintillaWin::AddRef() {
2852 return 1;
2855 STDMETHODIMP_(ULONG) ScintillaWin::Release() {
2856 return 1;
2859 /// Implement IDropTarget
2860 STDMETHODIMP ScintillaWin::DragEnter(LPDATAOBJECT pIDataSource, DWORD grfKeyState,
2861 POINTL, PDWORD pdwEffect) {
2862 if (pIDataSource == NULL)
2863 return E_POINTER;
2864 FORMATETC fmtu = {CF_UNICODETEXT, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
2865 HRESULT hrHasUText = pIDataSource->QueryGetData(&fmtu);
2866 hasOKText = (hrHasUText == S_OK);
2867 if (!hasOKText) {
2868 FORMATETC fmte = {CF_TEXT, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
2869 HRESULT hrHasText = pIDataSource->QueryGetData(&fmte);
2870 hasOKText = (hrHasText == S_OK);
2872 if (!hasOKText) {
2873 *pdwEffect = DROPEFFECT_NONE;
2874 return S_OK;
2877 *pdwEffect = EffectFromState(grfKeyState);
2878 return S_OK;
2881 STDMETHODIMP ScintillaWin::DragOver(DWORD grfKeyState, POINTL pt, PDWORD pdwEffect) {
2882 try {
2883 if (!hasOKText || pdoc->IsReadOnly()) {
2884 *pdwEffect = DROPEFFECT_NONE;
2885 return S_OK;
2888 *pdwEffect = EffectFromState(grfKeyState);
2890 // Update the cursor.
2891 POINT rpt = {pt.x, pt.y};
2892 ::ScreenToClient(MainHWND(), &rpt);
2893 SetDragPosition(SPositionFromLocation(PointFromPOINT(rpt), false, false, UserVirtualSpace()));
2895 return S_OK;
2896 } catch (...) {
2897 errorStatus = SC_STATUS_FAILURE;
2899 return E_FAIL;
2902 STDMETHODIMP ScintillaWin::DragLeave() {
2903 try {
2904 SetDragPosition(SelectionPosition(invalidPosition));
2905 return S_OK;
2906 } catch (...) {
2907 errorStatus = SC_STATUS_FAILURE;
2909 return E_FAIL;
2912 STDMETHODIMP ScintillaWin::Drop(LPDATAOBJECT pIDataSource, DWORD grfKeyState,
2913 POINTL pt, PDWORD pdwEffect) {
2914 try {
2915 *pdwEffect = EffectFromState(grfKeyState);
2917 if (pIDataSource == NULL)
2918 return E_POINTER;
2920 SetDragPosition(SelectionPosition(invalidPosition));
2922 STGMEDIUM medium = {0, {0}, 0};
2924 std::vector<char> data; // Includes terminating NUL
2926 FORMATETC fmtu = {CF_UNICODETEXT, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
2927 HRESULT hr = pIDataSource->GetData(&fmtu, &medium);
2928 if (SUCCEEDED(hr) && medium.hGlobal) {
2929 GlobalMemory memUDrop(medium.hGlobal);
2930 wchar_t *udata = static_cast<wchar_t *>(memUDrop.ptr);
2931 if (udata) {
2932 if (IsUnicodeMode()) {
2933 int tlen = static_cast<int>(memUDrop.Size());
2934 // Convert UTF-16 to UTF-8
2935 int dataLen = UTF8Length(udata, tlen/2);
2936 data.resize(dataLen+1);
2937 UTF8FromUTF16(udata, tlen/2, &data[0], dataLen);
2938 } else {
2939 // Convert UTF-16 to ANSI
2941 // Default Scintilla behavior in Unicode mode
2942 // CF_UNICODETEXT available, but not in Unicode mode
2943 // Convert from Unicode to current Scintilla code page
2944 UINT cpDest = CodePageOfDocument();
2945 int tlen = ::WideCharToMultiByte(cpDest, 0, udata, -1,
2946 NULL, 0, NULL, NULL) - 1; // subtract 0 terminator
2947 data.resize(tlen + 1);
2948 ::WideCharToMultiByte(cpDest, 0, udata, -1,
2949 &data[0], tlen + 1, NULL, NULL);
2952 memUDrop.Unlock();
2953 } else {
2954 FORMATETC fmte = {CF_TEXT, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
2955 hr = pIDataSource->GetData(&fmte, &medium);
2956 if (SUCCEEDED(hr) && medium.hGlobal) {
2957 GlobalMemory memDrop(medium.hGlobal);
2958 const char *cdata = static_cast<char *>(memDrop.ptr);
2959 if (cdata)
2960 data.assign(cdata, cdata+strlen(cdata)+1);
2961 memDrop.Unlock();
2965 if (!SUCCEEDED(hr) || data.empty()) {
2966 //Platform::DebugPrintf("Bad data format: 0x%x\n", hres);
2967 return hr;
2970 FORMATETC fmtr = {cfColumnSelect, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
2971 HRESULT hrRectangular = pIDataSource->QueryGetData(&fmtr);
2973 POINT rpt = {pt.x, pt.y};
2974 ::ScreenToClient(MainHWND(), &rpt);
2975 SelectionPosition movePos = SPositionFromLocation(PointFromPOINT(rpt), false, false, UserVirtualSpace());
2977 DropAt(movePos, &data[0], data.size() - 1, *pdwEffect == DROPEFFECT_MOVE, hrRectangular == S_OK);
2979 // Free data
2980 if (medium.pUnkForRelease != NULL)
2981 medium.pUnkForRelease->Release();
2982 else
2983 ::GlobalFree(medium.hGlobal);
2985 return S_OK;
2986 } catch (...) {
2987 errorStatus = SC_STATUS_FAILURE;
2989 return E_FAIL;
2992 /// Implement important part of IDataObject
2993 STDMETHODIMP ScintillaWin::GetData(FORMATETC *pFEIn, STGMEDIUM *pSTM) {
2994 bool formatOK = (pFEIn->cfFormat == CF_TEXT) ||
2995 ((pFEIn->cfFormat == CF_UNICODETEXT) && IsUnicodeMode());
2996 if (!formatOK ||
2997 pFEIn->ptd != 0 ||
2998 (pFEIn->dwAspect & DVASPECT_CONTENT) == 0 ||
2999 pFEIn->lindex != -1 ||
3000 (pFEIn->tymed & TYMED_HGLOBAL) == 0
3002 //Platform::DebugPrintf("DOB GetData No %d %x %x fmt=%x\n", lenDrag, pFEIn, pSTM, pFEIn->cfFormat);
3003 return DATA_E_FORMATETC;
3005 pSTM->tymed = TYMED_HGLOBAL;
3006 //Platform::DebugPrintf("DOB GetData OK %d %x %x\n", lenDrag, pFEIn, pSTM);
3008 GlobalMemory text;
3009 if (pFEIn->cfFormat == CF_UNICODETEXT) {
3010 size_t uchars = UTF16Length(drag.Data(), static_cast<int>(drag.LengthWithTerminator()));
3011 text.Allocate(2 * uchars);
3012 if (text) {
3013 UTF16FromUTF8(drag.Data(), drag.LengthWithTerminator(),
3014 static_cast<wchar_t *>(text.ptr), uchars);
3016 } else {
3017 text.Allocate(drag.LengthWithTerminator());
3018 if (text) {
3019 memcpy(static_cast<char *>(text.ptr), drag.Data(), drag.LengthWithTerminator());
3022 pSTM->hGlobal = text ? text.Unlock() : 0;
3023 pSTM->pUnkForRelease = 0;
3024 return S_OK;
3027 bool ScintillaWin::Register(HINSTANCE hInstance_) {
3029 hInstance = hInstance_;
3030 bool result;
3032 // Register the Scintilla class
3033 // Register Scintilla as a wide character window
3034 WNDCLASSEXW wndclass;
3035 wndclass.cbSize = sizeof(wndclass);
3036 wndclass.style = CS_GLOBALCLASS | CS_HREDRAW | CS_VREDRAW;
3037 wndclass.lpfnWndProc = ScintillaWin::SWndProc;
3038 wndclass.cbClsExtra = 0;
3039 wndclass.cbWndExtra = sizeof(ScintillaWin *);
3040 wndclass.hInstance = hInstance;
3041 wndclass.hIcon = NULL;
3042 wndclass.hCursor = NULL;
3043 wndclass.hbrBackground = NULL;
3044 wndclass.lpszMenuName = NULL;
3045 wndclass.lpszClassName = L"Scintilla";
3046 wndclass.hIconSm = 0;
3047 scintillaClassAtom = ::RegisterClassExW(&wndclass);
3048 result = 0 != scintillaClassAtom;
3050 if (result) {
3051 // Register the CallTip class
3052 WNDCLASSEX wndclassc;
3053 wndclassc.cbSize = sizeof(wndclassc);
3054 wndclassc.style = CS_GLOBALCLASS | CS_HREDRAW | CS_VREDRAW;
3055 wndclassc.cbClsExtra = 0;
3056 wndclassc.cbWndExtra = sizeof(ScintillaWin *);
3057 wndclassc.hInstance = hInstance;
3058 wndclassc.hIcon = NULL;
3059 wndclassc.hbrBackground = NULL;
3060 wndclassc.lpszMenuName = NULL;
3061 wndclassc.lpfnWndProc = ScintillaWin::CTWndProc;
3062 wndclassc.hCursor = ::LoadCursor(NULL, IDC_ARROW);
3063 wndclassc.lpszClassName = callClassName;
3064 wndclassc.hIconSm = 0;
3066 callClassAtom = ::RegisterClassEx(&wndclassc);
3067 result = 0 != callClassAtom;
3070 return result;
3073 bool ScintillaWin::Unregister() {
3074 bool result = true;
3075 if (0 != scintillaClassAtom) {
3076 if (::UnregisterClass(MAKEINTATOM(scintillaClassAtom), hInstance) == 0) {
3077 result = false;
3079 scintillaClassAtom = 0;
3081 if (0 != callClassAtom) {
3082 if (::UnregisterClass(MAKEINTATOM(callClassAtom), hInstance) == 0) {
3083 result = false;
3085 callClassAtom = 0;
3087 return result;
3090 bool ScintillaWin::HasCaretSizeChanged() const {
3091 if (
3092 ( (0 != vs.caretWidth) && (sysCaretWidth != vs.caretWidth) )
3093 || ((0 != vs.lineHeight) && (sysCaretHeight != vs.lineHeight))
3095 return true;
3097 return false;
3100 BOOL ScintillaWin::CreateSystemCaret() {
3101 sysCaretWidth = vs.caretWidth;
3102 if (0 == sysCaretWidth) {
3103 sysCaretWidth = 1;
3105 sysCaretHeight = vs.lineHeight;
3106 int bitmapSize = (((sysCaretWidth + 15) & ~15) >> 3) *
3107 sysCaretHeight;
3108 std::vector<char> bits(bitmapSize);
3109 sysCaretBitmap = ::CreateBitmap(sysCaretWidth, sysCaretHeight, 1,
3110 1, reinterpret_cast<BYTE *>(&bits[0]));
3111 BOOL retval = ::CreateCaret(
3112 MainHWND(), sysCaretBitmap,
3113 sysCaretWidth, sysCaretHeight);
3114 if (technology == SC_TECHNOLOGY_DEFAULT) {
3115 // System caret interferes with Direct2D drawing so only show it for GDI.
3116 ::ShowCaret(MainHWND());
3118 return retval;
3121 BOOL ScintillaWin::DestroySystemCaret() {
3122 ::HideCaret(MainHWND());
3123 BOOL retval = ::DestroyCaret();
3124 if (sysCaretBitmap) {
3125 ::DeleteObject(sysCaretBitmap);
3126 sysCaretBitmap = 0;
3128 return retval;
3131 sptr_t PASCAL ScintillaWin::CTWndProc(
3132 HWND hWnd, UINT iMessage, WPARAM wParam, sptr_t lParam) {
3133 // Find C++ object associated with window.
3134 ScintillaWin *sciThis = reinterpret_cast<ScintillaWin *>(PointerFromWindow(hWnd));
3135 try {
3136 // ctp will be zero if WM_CREATE not seen yet
3137 if (sciThis == 0) {
3138 if (iMessage == WM_CREATE) {
3139 // Associate CallTip object with window
3140 CREATESTRUCT *pCreate = reinterpret_cast<CREATESTRUCT *>(lParam);
3141 SetWindowPointer(hWnd, pCreate->lpCreateParams);
3142 return 0;
3143 } else {
3144 return ::DefWindowProc(hWnd, iMessage, wParam, lParam);
3146 } else {
3147 if (iMessage == WM_NCDESTROY) {
3148 ::SetWindowLong(hWnd, 0, 0);
3149 return ::DefWindowProc(hWnd, iMessage, wParam, lParam);
3150 } else if (iMessage == WM_PAINT) {
3151 PAINTSTRUCT ps;
3152 ::BeginPaint(hWnd, &ps);
3153 Surface *surfaceWindow = Surface::Allocate(sciThis->technology);
3154 if (surfaceWindow) {
3155 #if defined(USE_D2D)
3156 ID2D1HwndRenderTarget *pCTRenderTarget = 0;
3157 #endif
3158 RECT rc;
3159 GetClientRect(hWnd, &rc);
3160 // Create a Direct2D render target.
3161 if (sciThis->technology == SC_TECHNOLOGY_DEFAULT) {
3162 surfaceWindow->Init(ps.hdc, hWnd);
3163 } else {
3164 #if defined(USE_D2D)
3165 D2D1_HWND_RENDER_TARGET_PROPERTIES dhrtp;
3166 dhrtp.hwnd = hWnd;
3167 dhrtp.pixelSize = D2D1::SizeU(rc.right - rc.left, rc.bottom - rc.top);
3168 dhrtp.presentOptions = (sciThis->technology == SC_TECHNOLOGY_DIRECTWRITERETAIN) ?
3169 D2D1_PRESENT_OPTIONS_RETAIN_CONTENTS : D2D1_PRESENT_OPTIONS_NONE;
3171 D2D1_RENDER_TARGET_PROPERTIES drtp;
3172 drtp.type = D2D1_RENDER_TARGET_TYPE_DEFAULT;
3173 drtp.pixelFormat.format = DXGI_FORMAT_UNKNOWN;
3174 drtp.pixelFormat.alphaMode = D2D1_ALPHA_MODE_UNKNOWN;
3175 drtp.dpiX = 96.0;
3176 drtp.dpiY = 96.0;
3177 drtp.usage = D2D1_RENDER_TARGET_USAGE_NONE;
3178 drtp.minLevel = D2D1_FEATURE_LEVEL_DEFAULT;
3180 if (!SUCCEEDED(pD2DFactory->CreateHwndRenderTarget(drtp, dhrtp, &pCTRenderTarget))) {
3181 surfaceWindow->Release();
3182 delete surfaceWindow;
3183 ::EndPaint(hWnd, &ps);
3184 return 0;
3186 surfaceWindow->Init(pCTRenderTarget, hWnd);
3187 pCTRenderTarget->BeginDraw();
3188 #endif
3190 surfaceWindow->SetUnicodeMode(SC_CP_UTF8 == sciThis->ct.codePage);
3191 surfaceWindow->SetDBCSMode(sciThis->ct.codePage);
3192 sciThis->ct.PaintCT(surfaceWindow);
3193 #if defined(USE_D2D)
3194 if (pCTRenderTarget)
3195 pCTRenderTarget->EndDraw();
3196 #endif
3197 surfaceWindow->Release();
3198 delete surfaceWindow;
3199 #if defined(USE_D2D)
3200 if (pCTRenderTarget)
3201 pCTRenderTarget->Release();
3202 #endif
3204 ::EndPaint(hWnd, &ps);
3205 return 0;
3206 } else if ((iMessage == WM_NCLBUTTONDOWN) || (iMessage == WM_NCLBUTTONDBLCLK)) {
3207 POINT pt;
3208 pt.x = static_cast<short>(LOWORD(lParam));
3209 pt.y = static_cast<short>(HIWORD(lParam));
3210 ScreenToClient(hWnd, &pt);
3211 sciThis->ct.MouseClick(PointFromPOINT(pt));
3212 sciThis->CallTipClick();
3213 return 0;
3214 } else if (iMessage == WM_LBUTTONDOWN) {
3215 // This does not fire due to the hit test code
3216 sciThis->ct.MouseClick(Point::FromLong(static_cast<long>(lParam)));
3217 sciThis->CallTipClick();
3218 return 0;
3219 } else if (iMessage == WM_SETCURSOR) {
3220 ::SetCursor(::LoadCursor(NULL, IDC_ARROW));
3221 return 0;
3222 } else if (iMessage == WM_NCHITTEST) {
3223 return HTCAPTION;
3224 } else {
3225 return ::DefWindowProc(hWnd, iMessage, wParam, lParam);
3228 } catch (...) {
3229 sciThis->errorStatus = SC_STATUS_FAILURE;
3231 return ::DefWindowProc(hWnd, iMessage, wParam, lParam);
3234 sptr_t ScintillaWin::DirectFunction(
3235 sptr_t ptr, UINT iMessage, uptr_t wParam, sptr_t lParam) {
3236 PLATFORM_ASSERT(::GetCurrentThreadId() == ::GetWindowThreadProcessId(reinterpret_cast<ScintillaWin *>(ptr)->MainHWND(), NULL));
3237 return reinterpret_cast<ScintillaWin *>(ptr)->WndProc(iMessage, wParam, lParam);
3240 extern "C"
3241 #ifndef STATIC_BUILD
3242 __declspec(dllexport)
3243 #endif
3244 sptr_t __stdcall Scintilla_DirectFunction(
3245 ScintillaWin *sci, UINT iMessage, uptr_t wParam, sptr_t lParam) {
3246 return sci->WndProc(iMessage, wParam, lParam);
3249 sptr_t PASCAL ScintillaWin::SWndProc(
3250 HWND hWnd, UINT iMessage, WPARAM wParam, sptr_t lParam) {
3251 //Platform::DebugPrintf("S W:%x M:%x WP:%x L:%x\n", hWnd, iMessage, wParam, lParam);
3253 // Find C++ object associated with window.
3254 ScintillaWin *sci = reinterpret_cast<ScintillaWin *>(PointerFromWindow(hWnd));
3255 // sci will be zero if WM_CREATE not seen yet
3256 if (sci == 0) {
3257 try {
3258 if (iMessage == WM_CREATE) {
3259 // Create C++ object associated with window
3260 sci = new ScintillaWin(hWnd);
3261 SetWindowPointer(hWnd, sci);
3262 return sci->WndProc(iMessage, wParam, lParam);
3264 } catch (...) {
3266 return ::DefWindowProc(hWnd, iMessage, wParam, lParam);
3267 } else {
3268 if (iMessage == WM_NCDESTROY) {
3269 try {
3270 sci->Finalise();
3271 delete sci;
3272 } catch (...) {
3274 ::SetWindowLong(hWnd, 0, 0);
3275 return ::DefWindowProc(hWnd, iMessage, wParam, lParam);
3276 } else {
3277 return sci->WndProc(iMessage, wParam, lParam);
3282 // This function is externally visible so it can be called from container when building statically.
3283 // Must be called once only.
3284 int Scintilla_RegisterClasses(void *hInstance) {
3285 Platform_Initialise(hInstance);
3286 bool result = ScintillaWin::Register(reinterpret_cast<HINSTANCE>(hInstance));
3287 #ifdef SCI_LEXER
3288 Scintilla_LinkLexers();
3289 #endif
3290 return result;
3293 static int ResourcesRelease(bool fromDllMain) {
3294 bool result = ScintillaWin::Unregister();
3295 if (commctrl32) {
3296 FreeLibrary(commctrl32);
3297 commctrl32 = NULL;
3299 Platform_Finalise(fromDllMain);
3300 return result;
3303 // This function is externally visible so it can be called from container when building statically.
3304 int Scintilla_ReleaseResources() {
3305 return ResourcesRelease(false);
3308 #ifndef STATIC_BUILD
3309 extern "C" int APIENTRY DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpvReserved) {
3310 //Platform::DebugPrintf("Scintilla::DllMain %d %d\n", hInstance, dwReason);
3311 if (dwReason == DLL_PROCESS_ATTACH) {
3312 if (!Scintilla_RegisterClasses(hInstance))
3313 return FALSE;
3314 } else if (dwReason == DLL_PROCESS_DETACH) {
3315 if (lpvReserved == NULL) {
3316 ResourcesRelease(true);
3319 return TRUE;
3321 #endif