Applied unicodefont.patch
[TortoiseGit.git] / ext / scintilla / win32 / ScintillaWin.cxx
blob394aec1eddd631d28442a2bc642c9572c6e866b1
1 // Scintilla source code edit control
2 /** @file ScintillaWin.cxx
3 ** Windows specific subclass of ScintillaBase.
4 **/
5 // Copyright 1998-2003 by Neil Hodgson <neilh@scintilla.org>
6 // The License.txt file describes the conditions under which this software may be distributed.
8 #include <stdlib.h>
9 #include <string.h>
10 #include <stdio.h>
11 #include <assert.h>
12 #include <ctype.h>
13 #include <limits.h>
15 #include <new>
16 #include <string>
17 #include <vector>
18 #include <map>
19 #include <algorithm>
21 #undef _WIN32_WINNT
22 #define _WIN32_WINNT 0x0500
23 #undef WINVER
24 #define WINVER 0x0500
25 #include <windows.h>
26 #include <commctrl.h>
27 #include <richedit.h>
28 #include <windowsx.h>
29 #include <zmouse.h>
30 #include <ole2.h>
32 #if defined(NTDDI_WIN7) && !defined(DISABLE_D2D)
33 #define USE_D2D 1
34 #endif
36 #if defined(USE_D2D)
37 #include <d2d1.h>
38 #include <dwrite.h>
39 #endif
41 #include "Platform.h"
43 #include "ILexer.h"
44 #include "Scintilla.h"
46 #ifdef SCI_LEXER
47 #include "SciLexer.h"
48 #endif
49 #include "StringCopy.h"
50 #ifdef SCI_LEXER
51 #include "LexerModule.h"
52 #endif
53 #include "SplitVector.h"
54 #include "Partitioning.h"
55 #include "RunStyles.h"
56 #include "ContractionState.h"
57 #include "CellBuffer.h"
58 #include "CallTip.h"
59 #include "KeyMap.h"
60 #include "Indicator.h"
61 #include "XPM.h"
62 #include "LineMarker.h"
63 #include "Style.h"
64 #include "ViewStyle.h"
65 #include "CharClassify.h"
66 #include "Decoration.h"
67 #include "CaseFolder.h"
68 #include "Document.h"
69 #include "CaseConvert.h"
70 #include "UniConversion.h"
71 #include "Selection.h"
72 #include "PositionCache.h"
73 #include "EditModel.h"
74 #include "MarginView.h"
75 #include "EditView.h"
76 #include "Editor.h"
78 #include "AutoComplete.h"
79 #include "ScintillaBase.h"
81 #ifdef SCI_LEXER
82 #include "ExternalLexer.h"
83 #endif
85 #include "PlatWin.h"
87 #ifndef SPI_GETWHEELSCROLLLINES
88 #define SPI_GETWHEELSCROLLLINES 104
89 #endif
91 #ifndef WM_UNICHAR
92 #define WM_UNICHAR 0x0109
93 #endif
95 #ifndef UNICODE_NOCHAR
96 #define UNICODE_NOCHAR 0xFFFF
97 #endif
99 #ifndef MK_ALT
100 #define MK_ALT 32
101 #endif
103 #define SC_WIN_IDLE 5001
105 typedef BOOL (WINAPI *TrackMouseEventSig)(LPTRACKMOUSEEVENT);
106 typedef UINT_PTR (WINAPI *SetCoalescableTimerSig)(HWND hwnd, UINT_PTR nIDEvent,
107 UINT uElapse, TIMERPROC lpTimerFunc, ULONG uToleranceDelay);
109 // GCC has trouble with the standard COM ABI so do it the old C way with explicit vtables.
111 const TCHAR scintillaClassName[] = TEXT("Scintilla");
112 const TCHAR callClassName[] = TEXT("CallTip");
114 #ifdef SCI_NAMESPACE
115 using namespace Scintilla;
116 #endif
118 static void *PointerFromWindow(HWND hWnd) {
119 return reinterpret_cast<void *>(::GetWindowLongPtr(hWnd, 0));
122 static void SetWindowPointer(HWND hWnd, void *ptr) {
123 ::SetWindowLongPtr(hWnd, 0, reinterpret_cast<LONG_PTR>(ptr));
126 static void SetWindowID(HWND hWnd, int identifier) {
127 ::SetWindowLongPtr(hWnd, GWLP_ID, identifier);
130 static Point PointFromPOINT(POINT pt) {
131 return Point::FromInts(pt.x, pt.y);
134 class ScintillaWin; // Forward declaration for COM interface subobjects
136 typedef void VFunction(void);
138 static HMODULE commctrl32 = 0;
142 class FormatEnumerator {
143 public:
144 VFunction **vtbl;
145 int ref;
146 unsigned int pos;
147 std::vector<CLIPFORMAT> formats;
148 FormatEnumerator(int pos_, CLIPFORMAT formats_[], size_t formatsLen_);
153 class DropSource {
154 public:
155 VFunction **vtbl;
156 ScintillaWin *sci;
157 DropSource();
162 class DataObject {
163 public:
164 VFunction **vtbl;
165 ScintillaWin *sci;
166 DataObject();
171 class DropTarget {
172 public:
173 VFunction **vtbl;
174 ScintillaWin *sci;
175 DropTarget();
180 class ScintillaWin :
181 public ScintillaBase {
183 bool lastKeyDownConsumed;
185 bool capturedMouse;
186 bool trackedMouseLeave;
187 TrackMouseEventSig TrackMouseEventFn;
188 SetCoalescableTimerSig SetCoalescableTimerFn;
190 unsigned int linesPerScroll; ///< Intellimouse support
191 int wheelDelta; ///< Wheel delta from roll
193 HRGN hRgnUpdate;
195 bool hasOKText;
197 CLIPFORMAT cfColumnSelect;
198 CLIPFORMAT cfBorlandIDEBlockType;
199 CLIPFORMAT cfLineSelect;
200 CLIPFORMAT cfVSLineTag;
202 HRESULT hrOle;
203 DropSource ds;
204 DataObject dob;
205 DropTarget dt;
207 static HINSTANCE hInstance;
208 static ATOM scintillaClassAtom;
209 static ATOM callClassAtom;
211 #if defined(USE_D2D)
212 ID2D1HwndRenderTarget *pRenderTarget;
213 bool renderTargetValid;
214 #endif
216 explicit ScintillaWin(HWND hwnd);
217 ScintillaWin(const ScintillaWin &);
218 virtual ~ScintillaWin();
219 ScintillaWin &operator=(const ScintillaWin &);
221 virtual void Initialise();
222 virtual void Finalise();
223 #if defined(USE_D2D)
224 void EnsureRenderTarget();
225 void DropRenderTarget();
226 #endif
227 HWND MainHWND();
229 static sptr_t DirectFunction(
230 sptr_t ptr, UINT iMessage, uptr_t wParam, sptr_t lParam);
231 static sptr_t PASCAL SWndProc(
232 HWND hWnd, UINT iMessage, WPARAM wParam, sptr_t lParam);
233 static sptr_t PASCAL CTWndProc(
234 HWND hWnd, UINT iMessage, WPARAM wParam, sptr_t lParam);
236 enum { invalidTimerID, standardTimerID, idleTimerID, fineTimerStart };
238 virtual bool DragThreshold(Point ptStart, Point ptNow);
239 virtual void StartDrag();
240 sptr_t WndPaint(uptr_t wParam);
241 sptr_t HandleComposition(uptr_t wParam, sptr_t lParam);
242 static bool KoreanIME();
243 sptr_t HandleCompositionKoreanIME(uptr_t wParam, sptr_t lParam);
244 UINT CodePageOfDocument();
245 virtual bool ValidCodePage(int codePage) const;
246 virtual sptr_t DefWndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam);
247 virtual bool SetIdle(bool on);
248 UINT_PTR timers[tickDwell+1];
249 virtual bool FineTickerAvailable();
250 virtual bool FineTickerRunning(TickReason reason);
251 virtual void FineTickerStart(TickReason reason, int millis, int tolerance);
252 virtual void FineTickerCancel(TickReason reason);
253 virtual void SetMouseCapture(bool on);
254 virtual bool HaveMouseCapture();
255 virtual void SetTrackMouseLeaveEvent(bool on);
256 virtual bool PaintContains(PRectangle rc);
257 virtual void ScrollText(int linesToMove);
258 virtual void UpdateSystemCaret();
259 virtual void SetVerticalScrollPos();
260 virtual void SetHorizontalScrollPos();
261 virtual bool ModifyScrollBars(int nMax, int nPage);
262 virtual void NotifyChange();
263 virtual void NotifyFocus(bool focus);
264 virtual void SetCtrlID(int identifier);
265 virtual int GetCtrlID();
266 virtual void NotifyParent(SCNotification scn);
267 virtual void NotifyParent(SCNotification * scn);
268 virtual void NotifyDoubleClick(Point pt, int modifiers);
269 virtual CaseFolder *CaseFolderForEncoding();
270 virtual std::string CaseMapString(const std::string &s, int caseMapping);
271 virtual void Copy();
272 virtual void CopyAllowLine();
273 virtual bool CanPaste();
274 virtual void Paste();
275 virtual void CreateCallTipWindow(PRectangle rc);
276 virtual void AddToPopUp(const char *label, int cmd = 0, bool enabled = true);
277 virtual void ClaimSelection();
279 // DBCS
280 void ImeStartComposition();
281 void ImeEndComposition();
283 void AddCharBytes(char b0, char b1);
285 void GetIntelliMouseParameters();
286 virtual void CopyToClipboard(const SelectionText &selectedText);
287 void ScrollMessage(WPARAM wParam);
288 void HorizontalScrollMessage(WPARAM wParam);
289 void FullPaint();
290 void FullPaintDC(HDC dc);
291 bool IsCompatibleDC(HDC dc);
292 DWORD EffectFromState(DWORD grfKeyState) const;
294 virtual int SetScrollInfo(int nBar, LPCSCROLLINFO lpsi, BOOL bRedraw);
295 virtual bool GetScrollInfo(int nBar, LPSCROLLINFO lpsi);
296 void ChangeScrollPos(int barType, int pos);
297 sptr_t GetTextLength();
298 sptr_t GetText(uptr_t wParam, sptr_t lParam);
300 public:
301 // Public for benefit of Scintilla_DirectFunction
302 virtual sptr_t WndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam);
304 /// Implement IUnknown
305 STDMETHODIMP QueryInterface(REFIID riid, PVOID *ppv);
306 STDMETHODIMP_(ULONG)AddRef();
307 STDMETHODIMP_(ULONG)Release();
309 /// Implement IDropTarget
310 STDMETHODIMP DragEnter(LPDATAOBJECT pIDataSource, DWORD grfKeyState,
311 POINTL pt, PDWORD pdwEffect);
312 STDMETHODIMP DragOver(DWORD grfKeyState, POINTL pt, PDWORD pdwEffect);
313 STDMETHODIMP DragLeave();
314 STDMETHODIMP Drop(LPDATAOBJECT pIDataSource, DWORD grfKeyState,
315 POINTL pt, PDWORD pdwEffect);
317 /// Implement important part of IDataObject
318 STDMETHODIMP GetData(FORMATETC *pFEIn, STGMEDIUM *pSTM);
320 static bool Register(HINSTANCE hInstance_);
321 static bool Unregister();
323 friend class DropSource;
324 friend class DataObject;
325 friend class DropTarget;
326 bool DragIsRectangularOK(CLIPFORMAT fmt) const {
327 return drag.rectangular && (fmt == cfColumnSelect);
330 private:
331 // For use in creating a system caret
332 bool HasCaretSizeChanged() const;
333 BOOL CreateSystemCaret();
334 BOOL DestroySystemCaret();
335 HBITMAP sysCaretBitmap;
336 int sysCaretWidth;
337 int sysCaretHeight;
338 bool keysAlwaysUnicode;
341 HINSTANCE ScintillaWin::hInstance = 0;
342 ATOM ScintillaWin::scintillaClassAtom = 0;
343 ATOM ScintillaWin::callClassAtom = 0;
345 ScintillaWin::ScintillaWin(HWND hwnd) {
347 lastKeyDownConsumed = false;
349 capturedMouse = false;
350 trackedMouseLeave = false;
351 TrackMouseEventFn = 0;
352 SetCoalescableTimerFn = 0;
354 linesPerScroll = 0;
355 wheelDelta = 0; // Wheel delta from roll
357 hRgnUpdate = 0;
359 hasOKText = false;
361 // There does not seem to be a real standard for indicating that the clipboard
362 // contains a rectangular selection, so copy Developer Studio and Borland Delphi.
363 cfColumnSelect = static_cast<CLIPFORMAT>(
364 ::RegisterClipboardFormat(TEXT("MSDEVColumnSelect")));
365 cfBorlandIDEBlockType = static_cast<CLIPFORMAT>(
366 ::RegisterClipboardFormat(TEXT("Borland IDE Block Type")));
368 // Likewise for line-copy (copies a full line when no text is selected)
369 cfLineSelect = static_cast<CLIPFORMAT>(
370 ::RegisterClipboardFormat(TEXT("MSDEVLineSelect")));
371 cfVSLineTag = static_cast<CLIPFORMAT>(
372 ::RegisterClipboardFormat(TEXT("VisualStudioEditorOperationsLineCutCopyClipboardTag")));
373 hrOle = E_FAIL;
375 wMain = hwnd;
377 dob.sci = this;
378 ds.sci = this;
379 dt.sci = this;
381 sysCaretBitmap = 0;
382 sysCaretWidth = 0;
383 sysCaretHeight = 0;
385 #if defined(USE_D2D)
386 pRenderTarget = 0;
387 renderTargetValid = true;
388 #endif
390 keysAlwaysUnicode = false;
392 caret.period = ::GetCaretBlinkTime();
393 if (caret.period < 0)
394 caret.period = 0;
396 Initialise();
399 ScintillaWin::~ScintillaWin() {}
401 void ScintillaWin::Initialise() {
402 // Initialize COM. If the app has already done this it will have
403 // no effect. If the app hasnt, we really shouldnt ask them to call
404 // it just so this internal feature works.
405 hrOle = ::OleInitialize(NULL);
407 // Find TrackMouseEvent which is available on Windows > 95
408 HMODULE user32 = ::GetModuleHandle(TEXT("user32.dll"));
409 if (user32) {
410 TrackMouseEventFn = (TrackMouseEventSig)::GetProcAddress(user32, "TrackMouseEvent");
411 SetCoalescableTimerFn = (SetCoalescableTimerSig)::GetProcAddress(user32, "SetCoalescableTimer");
413 if (TrackMouseEventFn == NULL) {
414 // Windows 95 has an emulation in comctl32.dll:_TrackMouseEvent
415 if (!commctrl32)
416 commctrl32 = ::LoadLibrary(TEXT("comctl32.dll"));
417 if (commctrl32 != NULL) {
418 TrackMouseEventFn = (TrackMouseEventSig)
419 ::GetProcAddress(commctrl32, "_TrackMouseEvent");
422 for (TickReason tr = tickCaret; tr <= tickDwell; tr = static_cast<TickReason>(tr + 1)) {
423 timers[tr] = 0;
427 void ScintillaWin::Finalise() {
428 ScintillaBase::Finalise();
429 for (TickReason tr = tickCaret; tr <= tickDwell; tr = static_cast<TickReason>(tr + 1)) {
430 FineTickerCancel(tr);
432 SetIdle(false);
433 #if defined(USE_D2D)
434 DropRenderTarget();
435 #endif
436 ::RevokeDragDrop(MainHWND());
437 if (SUCCEEDED(hrOle)) {
438 ::OleUninitialize();
442 #if defined(USE_D2D)
444 void ScintillaWin::EnsureRenderTarget() {
445 if (!renderTargetValid) {
446 DropRenderTarget();
447 renderTargetValid = true;
449 if (pD2DFactory && !pRenderTarget) {
450 RECT rc;
451 HWND hw = MainHWND();
452 GetClientRect(hw, &rc);
454 D2D1_SIZE_U size = D2D1::SizeU(rc.right - rc.left, rc.bottom - rc.top);
456 // Create a Direct2D render target.
457 #if 1
458 D2D1_HWND_RENDER_TARGET_PROPERTIES dhrtp;
459 dhrtp.hwnd = hw;
460 dhrtp.pixelSize = size;
461 dhrtp.presentOptions = (technology == SC_TECHNOLOGY_DIRECTWRITERETAIN) ?
462 D2D1_PRESENT_OPTIONS_RETAIN_CONTENTS : D2D1_PRESENT_OPTIONS_NONE;
464 D2D1_RENDER_TARGET_PROPERTIES drtp;
465 drtp.type = D2D1_RENDER_TARGET_TYPE_DEFAULT;
466 drtp.pixelFormat.format = DXGI_FORMAT_UNKNOWN;
467 drtp.pixelFormat.alphaMode = D2D1_ALPHA_MODE_UNKNOWN;
468 drtp.dpiX = 96.0;
469 drtp.dpiY = 96.0;
470 drtp.usage = D2D1_RENDER_TARGET_USAGE_NONE;
471 drtp.minLevel = D2D1_FEATURE_LEVEL_DEFAULT;
473 pD2DFactory->CreateHwndRenderTarget(drtp, dhrtp, &pRenderTarget);
474 #else
475 pD2DFactory->CreateHwndRenderTarget(
476 D2D1::RenderTargetProperties(
477 D2D1_RENDER_TARGET_TYPE_DEFAULT ,
478 D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_PREMULTIPLIED),
479 96.0f, 96.0f, D2D1_RENDER_TARGET_USAGE_NONE, D2D1_FEATURE_LEVEL_DEFAULT),
480 D2D1::HwndRenderTargetProperties(hw, size),
481 &pRenderTarget);
482 #endif
483 // Pixmaps were created to be compatible with previous render target so
484 // need to be recreated.
485 DropGraphics(false);
489 void ScintillaWin::DropRenderTarget() {
490 if (pRenderTarget) {
491 pRenderTarget->Release();
492 pRenderTarget = 0;
496 #endif
498 HWND ScintillaWin::MainHWND() {
499 return reinterpret_cast<HWND>(wMain.GetID());
502 bool ScintillaWin::DragThreshold(Point ptStart, Point ptNow) {
503 int xMove = static_cast<int>(abs(ptStart.x - ptNow.x));
504 int yMove = static_cast<int>(abs(ptStart.y - ptNow.y));
505 return (xMove > ::GetSystemMetrics(SM_CXDRAG)) ||
506 (yMove > ::GetSystemMetrics(SM_CYDRAG));
509 void ScintillaWin::StartDrag() {
510 inDragDrop = ddDragging;
511 DWORD dwEffect = 0;
512 dropWentOutside = true;
513 IDataObject *pDataObject = reinterpret_cast<IDataObject *>(&dob);
514 IDropSource *pDropSource = reinterpret_cast<IDropSource *>(&ds);
515 //Platform::DebugPrintf("About to DoDragDrop %x %x\n", pDataObject, pDropSource);
516 HRESULT hr = ::DoDragDrop(
517 pDataObject,
518 pDropSource,
519 DROPEFFECT_COPY | DROPEFFECT_MOVE, &dwEffect);
520 //Platform::DebugPrintf("DoDragDrop = %x\n", hr);
521 if (SUCCEEDED(hr)) {
522 if ((hr == DRAGDROP_S_DROP) && (dwEffect == DROPEFFECT_MOVE) && dropWentOutside) {
523 // Remove dragged out text
524 ClearSelection();
527 inDragDrop = ddNone;
528 SetDragPosition(SelectionPosition(invalidPosition));
531 // Avoid warnings everywhere for old style casts by concentrating them here
532 static WORD LoWord(uptr_t l) {
533 return LOWORD(l);
536 static WORD HiWord(uptr_t l) {
537 return HIWORD(l);
540 static int InputCodePage() {
541 HKL inputLocale = ::GetKeyboardLayout(0);
542 LANGID inputLang = LOWORD(inputLocale);
543 char sCodePage[10];
544 int res = ::GetLocaleInfoA(MAKELCID(inputLang, SORT_DEFAULT),
545 LOCALE_IDEFAULTANSICODEPAGE, sCodePage, sizeof(sCodePage));
546 if (!res)
547 return 0;
548 return atoi(sCodePage);
551 /** Map the key codes to their equivalent SCK_ form. */
552 static int KeyTranslate(int keyIn) {
553 //PLATFORM_ASSERT(!keyIn);
554 switch (keyIn) {
555 case VK_DOWN: return SCK_DOWN;
556 case VK_UP: return SCK_UP;
557 case VK_LEFT: return SCK_LEFT;
558 case VK_RIGHT: return SCK_RIGHT;
559 case VK_HOME: return SCK_HOME;
560 case VK_END: return SCK_END;
561 case VK_PRIOR: return SCK_PRIOR;
562 case VK_NEXT: return SCK_NEXT;
563 case VK_DELETE: return SCK_DELETE;
564 case VK_INSERT: return SCK_INSERT;
565 case VK_ESCAPE: return SCK_ESCAPE;
566 case VK_BACK: return SCK_BACK;
567 case VK_TAB: return SCK_TAB;
568 case VK_RETURN: return SCK_RETURN;
569 case VK_ADD: return SCK_ADD;
570 case VK_SUBTRACT: return SCK_SUBTRACT;
571 case VK_DIVIDE: return SCK_DIVIDE;
572 case VK_LWIN: return SCK_WIN;
573 case VK_RWIN: return SCK_RWIN;
574 case VK_APPS: return SCK_MENU;
575 case VK_OEM_2: return '/';
576 case VK_OEM_3: return '`';
577 case VK_OEM_4: return '[';
578 case VK_OEM_5: return '\\';
579 case VK_OEM_6: return ']';
580 default: return keyIn;
584 static bool BoundsContains(PRectangle rcBounds, HRGN hRgnBounds, PRectangle rcCheck) {
585 bool contains = true;
586 if (!rcCheck.Empty()) {
587 if (!rcBounds.Contains(rcCheck)) {
588 contains = false;
589 } else if (hRgnBounds) {
590 // In bounding rectangle so check more accurately using region
591 HRGN hRgnCheck = ::CreateRectRgn(static_cast<int>(rcCheck.left), static_cast<int>(rcCheck.top),
592 static_cast<int>(rcCheck.right), static_cast<int>(rcCheck.bottom));
593 if (hRgnCheck) {
594 HRGN hRgnDifference = ::CreateRectRgn(0, 0, 0, 0);
595 if (hRgnDifference) {
596 int combination = ::CombineRgn(hRgnDifference, hRgnCheck, hRgnBounds, RGN_DIFF);
597 if (combination != NULLREGION) {
598 contains = false;
600 ::DeleteRgn(hRgnDifference);
602 ::DeleteRgn(hRgnCheck);
606 return contains;
609 LRESULT ScintillaWin::WndPaint(uptr_t wParam) {
610 //ElapsedTime et;
612 // Redirect assertions to debug output and save current state
613 bool assertsPopup = Platform::ShowAssertionPopUps(false);
614 paintState = painting;
615 PAINTSTRUCT ps;
616 PAINTSTRUCT *pps;
618 bool IsOcxCtrl = (wParam != 0); // if wParam != 0, it contains
619 // a PAINSTRUCT* from the OCX
620 // Removed since this interferes with reporting other assertions as it occurs repeatedly
621 //PLATFORM_ASSERT(hRgnUpdate == NULL);
622 hRgnUpdate = ::CreateRectRgn(0, 0, 0, 0);
623 if (IsOcxCtrl) {
624 pps = reinterpret_cast<PAINTSTRUCT*>(wParam);
625 } else {
626 ::GetUpdateRgn(MainHWND(), hRgnUpdate, FALSE);
627 pps = &ps;
628 ::BeginPaint(MainHWND(), pps);
630 rcPaint = PRectangle::FromInts(pps->rcPaint.left, pps->rcPaint.top, pps->rcPaint.right, pps->rcPaint.bottom);
631 PRectangle rcClient = GetClientRectangle();
632 paintingAllText = BoundsContains(rcPaint, hRgnUpdate, rcClient);
633 if (technology == SC_TECHNOLOGY_DEFAULT) {
634 AutoSurface surfaceWindow(pps->hdc, this);
635 if (surfaceWindow) {
636 Paint(surfaceWindow, rcPaint);
637 surfaceWindow->Release();
639 } else {
640 #if defined(USE_D2D)
641 EnsureRenderTarget();
642 AutoSurface surfaceWindow(pRenderTarget, this);
643 if (surfaceWindow) {
644 pRenderTarget->BeginDraw();
645 Paint(surfaceWindow, rcPaint);
646 surfaceWindow->Release();
647 HRESULT hr = pRenderTarget->EndDraw();
648 if (hr == D2DERR_RECREATE_TARGET) {
649 DropRenderTarget();
650 paintState = paintAbandoned;
653 #endif
655 if (hRgnUpdate) {
656 ::DeleteRgn(hRgnUpdate);
657 hRgnUpdate = 0;
660 if (!IsOcxCtrl)
661 ::EndPaint(MainHWND(), pps);
662 if (paintState == paintAbandoned) {
663 // Painting area was insufficient to cover new styling or brace highlight positions
664 if (IsOcxCtrl) {
665 FullPaintDC(pps->hdc);
666 } else {
667 FullPaint();
670 paintState = notPainting;
672 // Restore debug output state
673 Platform::ShowAssertionPopUps(assertsPopup);
675 //Platform::DebugPrintf("Paint took %g\n", et.Duration());
676 return 0l;
679 sptr_t ScintillaWin::HandleComposition(uptr_t wParam, sptr_t lParam) {
680 if (lParam & GCS_RESULTSTR) {
681 HIMC hIMC = ::ImmGetContext(MainHWND());
682 if (hIMC) {
683 wchar_t wcs[maxLenInputIME];
684 LONG bytes = ::ImmGetCompositionStringW(hIMC,
685 GCS_RESULTSTR, wcs, (maxLenInputIME-1)*2);
686 int wides = bytes / 2;
687 if (IsUnicodeMode()) {
688 char utfval[maxLenInputIME * 3];
689 unsigned int len = UTF8Length(wcs, wides);
690 UTF8FromUTF16(wcs, wides, utfval, len);
691 utfval[len] = '\0';
692 AddCharUTF(utfval, len);
693 } else {
694 char dbcsval[maxLenInputIME * 2];
695 int size = ::WideCharToMultiByte(InputCodePage(),
696 0, wcs, wides, dbcsval, sizeof(dbcsval) - 1, 0, 0);
697 for (int i=0; i<size; i++) {
698 AddChar(dbcsval[i]);
701 // Set new position after converted
702 Point pos = PointMainCaret();
703 COMPOSITIONFORM CompForm;
704 CompForm.dwStyle = CFS_POINT;
705 CompForm.ptCurrentPos.x = static_cast<int>(pos.x);
706 CompForm.ptCurrentPos.y = static_cast<int>(pos.y);
707 ::ImmSetCompositionWindow(hIMC, &CompForm);
708 ::ImmReleaseContext(MainHWND(), hIMC);
710 return 0;
712 return ::DefWindowProc(MainHWND(), WM_IME_COMPOSITION, wParam, lParam);
715 bool ScintillaWin::KoreanIME() {
716 const int codePage = InputCodePage();
717 return codePage == 949 || codePage == 1361;
720 sptr_t ScintillaWin::HandleCompositionKoreanIME(uptr_t, sptr_t lParam) {
722 // copy & paste by johnsonj
723 // Great thanks to
724 // jiniya from http://www.jiniya.net/tt/494 for DBCS input with AddCharUTF()
725 // BLUEnLIVE from http://zockr.tistory.com/1118 for UNDO and inOverstrike
727 HIMC hIMC = ::ImmGetContext(MainHWND());
728 if (!hIMC) {
729 return 0;
732 wchar_t wcs[maxLenInputIME];
733 int wides = 0;
734 bool compstrExist = false;
736 if (pdoc->TentativeActive()) {
737 pdoc->TentativeUndo();
738 } else {
739 // No tentative undo means start of this composition so
740 // fill in any virtual spaces.
741 bool tmpOverstrike = inOverstrike;
742 inOverstrike = false; // not allow to be deleted twice.
743 AddCharUTF("", 0);
744 inOverstrike = tmpOverstrike;
747 view.imeCaretBlockOverride = false;
748 if (lParam & GCS_COMPSTR) {
749 long bytes = ::ImmGetCompositionStringW
750 (hIMC, GCS_COMPSTR, wcs, maxLenInputIME);
751 wides = bytes / 2;
752 compstrExist = (wides != 0);
753 } else if (lParam & GCS_RESULTSTR) {
754 long bytes = ::ImmGetCompositionStringW
755 (hIMC, GCS_RESULTSTR, wcs, maxLenInputIME);
756 wides = bytes / 2;
757 compstrExist = (wides == 0);
760 if (wides >= 1) {
762 char hanval[maxLenInputIME];
763 unsigned int hanlen = 0;
765 if (IsUnicodeMode()) {
766 hanlen = UTF8Length(wcs, wides);
767 UTF8FromUTF16(wcs, wides, hanval, hanlen);
768 hanval[hanlen] = '\0';
769 } else {
770 hanlen = ::WideCharToMultiByte(InputCodePage(), 0,
771 wcs, wides, hanval, sizeof(hanval) - 1, 0, 0);
772 hanval[hanlen] = '\0';
775 if (compstrExist) {
776 view.imeCaretBlockOverride = true;
778 bool tmpRecordingMacro = recordingMacro;
779 recordingMacro = false;
780 pdoc->TentativeStart();
781 AddCharUTF(hanval, hanlen);
782 recordingMacro = tmpRecordingMacro;
784 for (size_t r = 0; r < sel.Count(); r++) { // for block caret
785 int positionInsert = sel.Range(r).Start().Position();
786 sel.Range(r).caret.SetPosition(positionInsert - hanlen);
787 sel.Range(r).anchor.SetPosition(positionInsert - hanlen);
789 } else {
790 AddCharUTF(hanval, hanlen);
794 // set the candidate window position for HANJA while composing.
795 Point pos = PointMainCaret();
796 CANDIDATEFORM CandForm;
797 CandForm.dwIndex = 0;
798 CandForm.dwStyle = CFS_CANDIDATEPOS;
799 CandForm.ptCurrentPos.x = static_cast<int>(pos.x);
800 CandForm.ptCurrentPos.y = static_cast<int>(pos.y + vs.lineHeight);
801 ::ImmSetCandidateWindow(hIMC, &CandForm);
803 ShowCaretAtCurrentPosition();
804 ::ImmReleaseContext(MainHWND(), hIMC);
805 return 0;
808 // Translate message IDs from WM_* and EM_* to SCI_* so can partly emulate Windows Edit control
809 static unsigned int SciMessageFromEM(unsigned int iMessage) {
810 switch (iMessage) {
811 case EM_CANPASTE: return SCI_CANPASTE;
812 case EM_CANUNDO: return SCI_CANUNDO;
813 case EM_EMPTYUNDOBUFFER: return SCI_EMPTYUNDOBUFFER;
814 case EM_FINDTEXTEX: return SCI_FINDTEXT;
815 case EM_FORMATRANGE: return SCI_FORMATRANGE;
816 case EM_GETFIRSTVISIBLELINE: return SCI_GETFIRSTVISIBLELINE;
817 case EM_GETLINECOUNT: return SCI_GETLINECOUNT;
818 case EM_GETSELTEXT: return SCI_GETSELTEXT;
819 case EM_GETTEXTRANGE: return SCI_GETTEXTRANGE;
820 case EM_HIDESELECTION: return SCI_HIDESELECTION;
821 case EM_LINEINDEX: return SCI_POSITIONFROMLINE;
822 case EM_LINESCROLL: return SCI_LINESCROLL;
823 case EM_REPLACESEL: return SCI_REPLACESEL;
824 case EM_SCROLLCARET: return SCI_SCROLLCARET;
825 case EM_SETREADONLY: return SCI_SETREADONLY;
826 case WM_CLEAR: return SCI_CLEAR;
827 case WM_COPY: return SCI_COPY;
828 case WM_CUT: return SCI_CUT;
829 case WM_SETTEXT: return SCI_SETTEXT;
830 case WM_PASTE: return SCI_PASTE;
831 case WM_UNDO: return SCI_UNDO;
833 return iMessage;
836 UINT CodePageFromCharSet(DWORD characterSet, UINT documentCodePage) {
837 if (documentCodePage == SC_CP_UTF8) {
838 return SC_CP_UTF8;
840 switch (characterSet) {
841 case SC_CHARSET_ANSI: return 1252;
842 case SC_CHARSET_DEFAULT: return documentCodePage;
843 case SC_CHARSET_BALTIC: return 1257;
844 case SC_CHARSET_CHINESEBIG5: return 950;
845 case SC_CHARSET_EASTEUROPE: return 1250;
846 case SC_CHARSET_GB2312: return 936;
847 case SC_CHARSET_GREEK: return 1253;
848 case SC_CHARSET_HANGUL: return 949;
849 case SC_CHARSET_MAC: return 10000;
850 case SC_CHARSET_OEM: return 437;
851 case SC_CHARSET_RUSSIAN: return 1251;
852 case SC_CHARSET_SHIFTJIS: return 932;
853 case SC_CHARSET_TURKISH: return 1254;
854 case SC_CHARSET_JOHAB: return 1361;
855 case SC_CHARSET_HEBREW: return 1255;
856 case SC_CHARSET_ARABIC: return 1256;
857 case SC_CHARSET_VIETNAMESE: return 1258;
858 case SC_CHARSET_THAI: return 874;
859 case SC_CHARSET_8859_15: return 28605;
860 // Not supported
861 case SC_CHARSET_CYRILLIC: return documentCodePage;
862 case SC_CHARSET_SYMBOL: return documentCodePage;
864 return documentCodePage;
867 UINT ScintillaWin::CodePageOfDocument() {
868 return CodePageFromCharSet(vs.styles[STYLE_DEFAULT].characterSet, pdoc->dbcsCodePage);
871 sptr_t ScintillaWin::GetTextLength() {
872 if (::IsWindowUnicode(MainHWND())) {
873 if (pdoc->Length() == 0)
874 return 0;
875 std::vector<char> docBytes(pdoc->Length(), '\0');
876 pdoc->GetCharRange(&docBytes[0], 0, pdoc->Length());
877 if (IsUnicodeMode()) {
878 return UTF16Length(&docBytes[0], static_cast<unsigned int>(docBytes.size()));
879 } else {
880 return ::MultiByteToWideChar(CodePageOfDocument(), 0, &docBytes[0],
881 static_cast<int>(docBytes.size()), NULL, 0);
883 } else {
884 return pdoc->Length();
888 sptr_t ScintillaWin::GetText(uptr_t wParam, sptr_t lParam) {
889 if (::IsWindowUnicode(MainHWND())) {
890 wchar_t *ptr = reinterpret_cast<wchar_t *>(lParam);
891 if (pdoc->Length() == 0) {
892 *ptr = L'\0';
893 return 0;
895 std::vector<char> docBytes(pdoc->Length(), '\0');
896 pdoc->GetCharRange(&docBytes[0], 0, pdoc->Length());
897 if (IsUnicodeMode()) {
898 unsigned int lengthUTF16 = UTF16Length(&docBytes[0], static_cast<unsigned int>(docBytes.size()));
899 if (lParam == 0)
900 return lengthUTF16;
901 if (wParam == 0)
902 return 0;
903 unsigned int uLen = UTF16FromUTF8(&docBytes[0], static_cast<unsigned int>(docBytes.size()),
904 ptr, static_cast<int>(wParam) - 1);
905 ptr[uLen] = L'\0';
906 return uLen;
907 } else {
908 // Not Unicode mode
909 // Convert to Unicode using the current Scintilla code page
910 const UINT cpSrc = CodePageOfDocument();
911 int lengthUTF16 = ::MultiByteToWideChar(cpSrc, 0, &docBytes[0],
912 static_cast<int>(docBytes.size()), NULL, 0);
913 if (lengthUTF16 >= static_cast<int>(wParam))
914 lengthUTF16 = static_cast<int>(wParam)-1;
915 ::MultiByteToWideChar(cpSrc, 0, &docBytes[0],
916 static_cast<int>(docBytes.size()),
917 ptr, lengthUTF16);
918 ptr[lengthUTF16] = L'\0';
919 return lengthUTF16;
921 } else {
922 if (lParam == 0)
923 return pdoc->Length() + 1;
924 if (wParam == 0)
925 return 0;
926 char *ptr = reinterpret_cast<char *>(lParam);
927 unsigned int iChar = 0;
928 for (; iChar < wParam - 1; iChar++)
929 ptr[iChar] = pdoc->CharAt(iChar);
930 ptr[iChar] = '\0';
931 return iChar;
935 sptr_t ScintillaWin::WndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
936 try {
937 //Platform::DebugPrintf("S M:%x WP:%x L:%x\n", iMessage, wParam, lParam);
938 iMessage = SciMessageFromEM(iMessage);
939 switch (iMessage) {
941 case WM_CREATE:
942 ctrlID = ::GetDlgCtrlID(reinterpret_cast<HWND>(wMain.GetID()));
943 // Get Intellimouse scroll line parameters
944 GetIntelliMouseParameters();
945 ::RegisterDragDrop(MainHWND(), reinterpret_cast<IDropTarget *>(&dt));
946 break;
948 case WM_COMMAND:
949 Command(LoWord(wParam));
950 break;
952 case WM_PAINT:
953 return WndPaint(wParam);
955 case WM_PRINTCLIENT: {
956 HDC hdc = reinterpret_cast<HDC>(wParam);
957 if (!IsCompatibleDC(hdc)) {
958 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
960 FullPaintDC(hdc);
962 break;
964 case WM_VSCROLL:
965 ScrollMessage(wParam);
966 break;
968 case WM_HSCROLL:
969 HorizontalScrollMessage(wParam);
970 break;
972 case WM_SIZE: {
973 #if defined(USE_D2D)
974 if (paintState == notPainting) {
975 DropRenderTarget();
976 } else {
977 renderTargetValid = false;
979 #endif
980 //Platform::DebugPrintf("Scintilla WM_SIZE %d %d\n", LoWord(lParam), HiWord(lParam));
981 ChangeSize();
983 break;
985 case WM_MOUSEWHEEL:
986 // if autocomplete list active then send mousewheel message to it
987 if (ac.Active()) {
988 HWND hWnd = reinterpret_cast<HWND>(ac.lb->GetID());
989 ::SendMessage(hWnd, iMessage, wParam, lParam);
990 break;
993 // Don't handle datazoom.
994 // (A good idea for datazoom would be to "fold" or "unfold" details.
995 // i.e. if datazoomed out only class structures are visible, when datazooming in the control
996 // structures appear, then eventually the individual statements...)
997 if (wParam & MK_SHIFT) {
998 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1001 // Either SCROLL or ZOOM. We handle the wheel steppings calculation
1002 wheelDelta -= static_cast<short>(HiWord(wParam));
1003 if (abs(wheelDelta) >= WHEEL_DELTA && linesPerScroll > 0) {
1004 int linesToScroll = linesPerScroll;
1005 if (linesPerScroll == WHEEL_PAGESCROLL)
1006 linesToScroll = LinesOnScreen() - 1;
1007 if (linesToScroll == 0) {
1008 linesToScroll = 1;
1010 linesToScroll *= (wheelDelta / WHEEL_DELTA);
1011 if (wheelDelta >= 0)
1012 wheelDelta = wheelDelta % WHEEL_DELTA;
1013 else
1014 wheelDelta = - (-wheelDelta % WHEEL_DELTA);
1016 if (wParam & MK_CONTROL) {
1017 // Zoom! We play with the font sizes in the styles.
1018 // Number of steps/line is ignored, we just care if sizing up or down
1019 if (linesToScroll < 0) {
1020 KeyCommand(SCI_ZOOMIN);
1021 } else {
1022 KeyCommand(SCI_ZOOMOUT);
1024 } else {
1025 // Scroll
1026 ScrollTo(topLine + linesToScroll);
1029 return 0;
1031 case WM_TIMER:
1032 if (wParam == idleTimerID && idler.state) {
1033 SendMessage(MainHWND(), SC_WIN_IDLE, 0, 1);
1034 } else {
1035 TickFor(static_cast<TickReason>(wParam - fineTimerStart));
1037 break;
1039 case SC_WIN_IDLE:
1040 // wParam=dwTickCountInitial, or 0 to initialize. lParam=bSkipUserInputTest
1041 if (idler.state) {
1042 if (lParam || (WAIT_TIMEOUT == MsgWaitForMultipleObjects(0, 0, 0, 0, QS_INPUT|QS_HOTKEY))) {
1043 if (Idle()) {
1044 // User input was given priority above, but all events do get a turn. Other
1045 // messages, notifications, etc. will get interleaved with the idle messages.
1047 // However, some things like WM_PAINT are a lower priority, and will not fire
1048 // when there's a message posted. So, several times a second, we stop and let
1049 // the low priority events have a turn (after which the timer will fire again).
1051 DWORD dwCurrent = GetTickCount();
1052 DWORD dwStart = wParam ? static_cast<DWORD>(wParam) : dwCurrent;
1053 const DWORD maxWorkTime = 50;
1055 if (dwCurrent >= dwStart && dwCurrent > maxWorkTime && dwCurrent - maxWorkTime < dwStart)
1056 PostMessage(MainHWND(), SC_WIN_IDLE, dwStart, 0);
1057 } else {
1058 SetIdle(false);
1062 break;
1064 case WM_GETMINMAXINFO:
1065 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1067 case WM_LBUTTONDOWN: {
1068 // For IME, set the composition string as the result string.
1069 HIMC hIMC = ::ImmGetContext(MainHWND());
1070 ::ImmNotifyIME(hIMC, NI_COMPOSITIONSTR, CPS_COMPLETE, 0);
1071 ::ImmReleaseContext(MainHWND(), hIMC);
1073 //Platform::DebugPrintf("Buttdown %d %x %x %x %x %x\n",iMessage, wParam, lParam,
1074 // Platform::IsKeyDown(VK_SHIFT),
1075 // Platform::IsKeyDown(VK_CONTROL),
1076 // Platform::IsKeyDown(VK_MENU));
1077 ::SetFocus(MainHWND());
1078 ButtonDown(Point::FromLong(static_cast<long>(lParam)), ::GetMessageTime(),
1079 (wParam & MK_SHIFT) != 0,
1080 (wParam & MK_CONTROL) != 0,
1081 Platform::IsKeyDown(VK_MENU));
1083 break;
1085 case WM_MOUSEMOVE:
1086 SetTrackMouseLeaveEvent(true);
1087 ButtonMoveWithModifiers(Point::FromLong(static_cast<long>(lParam)),
1088 ((wParam & MK_SHIFT) != 0 ? SCI_SHIFT : 0) |
1089 ((wParam & MK_CONTROL) != 0 ? SCI_CTRL : 0) |
1090 (Platform::IsKeyDown(VK_MENU) ? SCI_ALT : 0));
1091 break;
1093 case WM_MOUSELEAVE:
1094 SetTrackMouseLeaveEvent(false);
1095 MouseLeave();
1096 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1098 case WM_LBUTTONUP:
1099 ButtonUp(Point::FromLong(static_cast<long>(lParam)),
1100 ::GetMessageTime(),
1101 (wParam & MK_CONTROL) != 0);
1102 break;
1104 case WM_RBUTTONDOWN:
1105 ::SetFocus(MainHWND());
1106 if (!PointInSelection(Point::FromLong(static_cast<long>(lParam)))) {
1107 CancelModes();
1108 SetEmptySelection(PositionFromLocation(Point::FromLong(static_cast<long>(lParam))));
1110 break;
1112 case WM_SETCURSOR:
1113 if (LoWord(lParam) == HTCLIENT) {
1114 if (inDragDrop == ddDragging) {
1115 DisplayCursor(Window::cursorUp);
1116 } else {
1117 // Display regular (drag) cursor over selection
1118 POINT pt;
1119 if (0 != ::GetCursorPos(&pt)) {
1120 ::ScreenToClient(MainHWND(), &pt);
1121 if (PointInSelMargin(PointFromPOINT(pt))) {
1122 DisplayCursor(GetMarginCursor(PointFromPOINT(pt)));
1123 } else if (PointInSelection(PointFromPOINT(pt)) && !SelectionEmpty()) {
1124 DisplayCursor(Window::cursorArrow);
1125 } else if (PointIsHotspot(PointFromPOINT(pt))) {
1126 DisplayCursor(Window::cursorHand);
1127 } else {
1128 DisplayCursor(Window::cursorText);
1132 return TRUE;
1133 } else {
1134 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1137 case WM_CHAR:
1138 if (((wParam >= 128) || !iscntrl(static_cast<int>(wParam))) || !lastKeyDownConsumed) {
1139 if (::IsWindowUnicode(MainHWND()) || keysAlwaysUnicode) {
1140 wchar_t wcs[2] = {static_cast<wchar_t>(wParam), 0};
1141 if (IsUnicodeMode()) {
1142 // For a wide character version of the window:
1143 char utfval[4];
1144 unsigned int len = UTF8Length(wcs, 1);
1145 UTF8FromUTF16(wcs, 1, utfval, len);
1146 AddCharUTF(utfval, len);
1147 } else {
1148 UINT cpDest = CodePageOfDocument();
1149 char inBufferCP[20];
1150 int size = ::WideCharToMultiByte(cpDest,
1151 0, wcs, 1, inBufferCP, sizeof(inBufferCP) - 1, 0, 0);
1152 inBufferCP[size] = '\0';
1153 AddCharUTF(inBufferCP, size);
1155 } else {
1156 if (IsUnicodeMode()) {
1157 AddCharBytes('\0', LOBYTE(wParam));
1158 } else {
1159 AddChar(LOBYTE(wParam));
1163 return 0;
1165 case WM_UNICHAR:
1166 if (wParam == UNICODE_NOCHAR) {
1167 return IsUnicodeMode() ? 1 : 0;
1168 } else if (lastKeyDownConsumed) {
1169 return 1;
1170 } else {
1171 if (IsUnicodeMode()) {
1172 char utfval[4];
1173 wchar_t wcs[2] = {static_cast<wchar_t>(wParam), 0};
1174 unsigned int len = UTF8Length(wcs, 1);
1175 UTF8FromUTF16(wcs, 1, utfval, len);
1176 AddCharUTF(utfval, len);
1177 return 1;
1178 } else {
1179 return 0;
1183 case WM_SYSKEYDOWN:
1184 case WM_KEYDOWN: {
1185 //Platform::DebugPrintf("S keydown %d %x %x %x %x\n",iMessage, wParam, lParam, ::IsKeyDown(VK_SHIFT), ::IsKeyDown(VK_CONTROL));
1186 lastKeyDownConsumed = false;
1187 int ret = KeyDown(KeyTranslate(static_cast<int>(wParam)),
1188 Platform::IsKeyDown(VK_SHIFT),
1189 Platform::IsKeyDown(VK_CONTROL),
1190 Platform::IsKeyDown(VK_MENU),
1191 &lastKeyDownConsumed);
1192 if (!ret && !lastKeyDownConsumed) {
1193 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1195 break;
1198 case WM_IME_KEYDOWN:
1199 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1201 case WM_KEYUP:
1202 //Platform::DebugPrintf("S keyup %d %x %x\n",iMessage, wParam, lParam);
1203 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1205 case WM_SETTINGCHANGE:
1206 //Platform::DebugPrintf("Setting Changed\n");
1207 InvalidateStyleData();
1208 // Get Intellimouse scroll line parameters
1209 GetIntelliMouseParameters();
1210 break;
1212 case WM_GETDLGCODE:
1213 return DLGC_HASSETSEL | DLGC_WANTALLKEYS;
1215 case WM_KILLFOCUS: {
1216 HWND wOther = reinterpret_cast<HWND>(wParam);
1217 HWND wThis = MainHWND();
1218 HWND wCT = reinterpret_cast<HWND>(ct.wCallTip.GetID());
1219 if (!wParam ||
1220 !(::IsChild(wThis, wOther) || (wOther == wCT))) {
1221 SetFocusState(false);
1222 DestroySystemCaret();
1224 // Explicitly complete any IME composition
1225 HIMC hIMC = ImmGetContext(MainHWND());
1226 if (hIMC) {
1227 ::ImmNotifyIME(hIMC, NI_COMPOSITIONSTR, CPS_COMPLETE, 0);
1228 ::ImmReleaseContext(MainHWND(), hIMC);
1231 break;
1233 case WM_SETFOCUS:
1234 SetFocusState(true);
1235 DestroySystemCaret();
1236 CreateSystemCaret();
1237 break;
1239 case WM_SYSCOLORCHANGE:
1240 //Platform::DebugPrintf("Setting Changed\n");
1241 InvalidateStyleData();
1242 break;
1244 case WM_IME_STARTCOMPOSITION: // dbcs
1245 if (KoreanIME()) {
1246 return 0;
1248 ImeStartComposition();
1249 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1251 case WM_IME_ENDCOMPOSITION: // dbcs
1252 ImeEndComposition();
1253 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1255 case WM_IME_COMPOSITION:
1256 if (KoreanIME()) {
1257 return HandleCompositionKoreanIME(wParam, lParam);
1258 } else {
1259 return HandleComposition(wParam, lParam);
1262 case WM_IME_CHAR: {
1263 AddCharBytes(HIBYTE(wParam), LOBYTE(wParam));
1264 return 0;
1267 case WM_CONTEXTMENU:
1268 if (displayPopupMenu) {
1269 Point pt = Point::FromLong(static_cast<long>(lParam));
1270 if ((pt.x == -1) && (pt.y == -1)) {
1271 // Caused by keyboard so display menu near caret
1272 pt = PointMainCaret();
1273 POINT spt = {static_cast<int>(pt.x), static_cast<int>(pt.y)};
1274 ::ClientToScreen(MainHWND(), &spt);
1275 pt = PointFromPOINT(spt);
1277 ContextMenu(pt);
1278 return 0;
1280 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1282 case WM_INPUTLANGCHANGE:
1283 //::SetThreadLocale(LOWORD(lParam));
1284 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1286 case WM_INPUTLANGCHANGEREQUEST:
1287 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1289 case WM_ERASEBKGND:
1290 return 1; // Avoid any background erasure as whole window painted.
1292 case WM_CAPTURECHANGED:
1293 capturedMouse = false;
1294 return 0;
1296 case WM_IME_SETCONTEXT:
1297 if (KoreanIME()) {
1298 if (wParam) {
1299 LPARAM NoImeWin = lParam;
1300 NoImeWin = NoImeWin & (~ISC_SHOWUICOMPOSITIONWINDOW);
1301 return ::DefWindowProc(MainHWND(), iMessage, wParam, NoImeWin);
1304 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1306 // These are not handled in Scintilla and its faster to dispatch them here.
1307 // Also moves time out to here so profile doesn't count lots of empty message calls.
1309 case WM_MOVE:
1310 case WM_MOUSEACTIVATE:
1311 case WM_NCHITTEST:
1312 case WM_NCCALCSIZE:
1313 case WM_NCPAINT:
1314 case WM_NCMOUSEMOVE:
1315 case WM_NCLBUTTONDOWN:
1316 case WM_IME_NOTIFY:
1317 case WM_SYSCOMMAND:
1318 case WM_WINDOWPOSCHANGING:
1319 case WM_WINDOWPOSCHANGED:
1320 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1322 case WM_GETTEXTLENGTH:
1323 return GetTextLength();
1325 case WM_GETTEXT:
1326 return GetText(wParam, lParam);
1328 case EM_LINEFROMCHAR:
1329 if (static_cast<int>(wParam) < 0) {
1330 wParam = SelectionStart().Position();
1332 return pdoc->LineFromPosition(static_cast<int>(wParam));
1334 case EM_EXLINEFROMCHAR:
1335 return pdoc->LineFromPosition(static_cast<int>(lParam));
1337 case EM_GETSEL:
1338 if (wParam) {
1339 *reinterpret_cast<int *>(wParam) = SelectionStart().Position();
1341 if (lParam) {
1342 *reinterpret_cast<int *>(lParam) = SelectionEnd().Position();
1344 return MAKELONG(SelectionStart().Position(), SelectionEnd().Position());
1346 case EM_EXGETSEL: {
1347 if (lParam == 0) {
1348 return 0;
1350 Sci_CharacterRange *pCR = reinterpret_cast<Sci_CharacterRange *>(lParam);
1351 pCR->cpMin = SelectionStart().Position();
1352 pCR->cpMax = SelectionEnd().Position();
1354 break;
1356 case EM_SETSEL: {
1357 int nStart = static_cast<int>(wParam);
1358 int nEnd = static_cast<int>(lParam);
1359 if (nStart == 0 && nEnd == -1) {
1360 nEnd = pdoc->Length();
1362 if (nStart == -1) {
1363 nStart = nEnd; // Remove selection
1365 if (nStart > nEnd) {
1366 SetSelection(nEnd, nStart);
1367 } else {
1368 SetSelection(nStart, nEnd);
1370 EnsureCaretVisible();
1372 break;
1374 case EM_EXSETSEL: {
1375 if (lParam == 0) {
1376 return 0;
1378 Sci_CharacterRange *pCR = reinterpret_cast<Sci_CharacterRange *>(lParam);
1379 sel.selType = Selection::selStream;
1380 if (pCR->cpMin == 0 && pCR->cpMax == -1) {
1381 SetSelection(pCR->cpMin, pdoc->Length());
1382 } else {
1383 SetSelection(pCR->cpMin, pCR->cpMax);
1385 EnsureCaretVisible();
1386 return pdoc->LineFromPosition(SelectionStart().Position());
1389 case SCI_GETDIRECTFUNCTION:
1390 return reinterpret_cast<sptr_t>(DirectFunction);
1392 case SCI_GETDIRECTPOINTER:
1393 return reinterpret_cast<sptr_t>(this);
1395 case SCI_GRABFOCUS:
1396 ::SetFocus(MainHWND());
1397 break;
1399 case SCI_SETKEYSUNICODE:
1400 keysAlwaysUnicode = wParam != 0;
1401 break;
1403 case SCI_GETKEYSUNICODE:
1404 return keysAlwaysUnicode;
1406 case SCI_SETTECHNOLOGY:
1407 if ((wParam == SC_TECHNOLOGY_DEFAULT) ||
1408 (wParam == SC_TECHNOLOGY_DIRECTWRITERETAIN) ||
1409 (wParam == SC_TECHNOLOGY_DIRECTWRITE)) {
1410 if (technology != static_cast<int>(wParam)) {
1411 if (static_cast<int>(wParam) > SC_TECHNOLOGY_DEFAULT) {
1412 #if defined(USE_D2D)
1413 if (!LoadD2D())
1414 // Failed to load Direct2D or DirectWrite so no effect
1415 return 0;
1416 #else
1417 return 0;
1418 #endif
1420 technology = static_cast<int>(wParam);
1421 // Invalidate all cached information including layout.
1422 DropGraphics(true);
1423 InvalidateStyleRedraw();
1426 break;
1428 #ifdef SCI_LEXER
1429 case SCI_LOADLEXERLIBRARY:
1430 LexerManager::GetInstance()->Load(reinterpret_cast<const char *>(lParam));
1431 break;
1432 #endif
1434 default:
1435 return ScintillaBase::WndProc(iMessage, wParam, lParam);
1437 } catch (std::bad_alloc &) {
1438 errorStatus = SC_STATUS_BADALLOC;
1439 } catch (...) {
1440 errorStatus = SC_STATUS_FAILURE;
1442 return 0l;
1445 bool ScintillaWin::ValidCodePage(int codePage) const {
1446 return codePage == 0 || codePage == SC_CP_UTF8 ||
1447 codePage == 932 || codePage == 936 || codePage == 949 ||
1448 codePage == 950 || codePage == 1361;
1451 sptr_t ScintillaWin::DefWndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
1452 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1456 * Report that this Editor subclass has a working implementation of FineTickerStart.
1458 bool ScintillaWin::FineTickerAvailable() {
1459 return true;
1462 bool ScintillaWin::FineTickerRunning(TickReason reason) {
1463 return timers[reason] != 0;
1466 void ScintillaWin::FineTickerStart(TickReason reason, int millis, int tolerance) {
1467 FineTickerCancel(reason);
1468 if (SetCoalescableTimerFn && tolerance) {
1469 timers[reason] = SetCoalescableTimerFn(MainHWND(), fineTimerStart + reason, millis, NULL, tolerance);
1470 } else {
1471 timers[reason] = ::SetTimer(MainHWND(), fineTimerStart + reason, millis, NULL);
1475 void ScintillaWin::FineTickerCancel(TickReason reason) {
1476 if (timers[reason]) {
1477 ::KillTimer(MainHWND(), timers[reason]);
1478 timers[reason] = 0;
1483 bool ScintillaWin::SetIdle(bool on) {
1484 // On Win32 the Idler is implemented as a Timer on the Scintilla window. This
1485 // takes advantage of the fact that WM_TIMER messages are very low priority,
1486 // and are only posted when the message queue is empty, i.e. during idle time.
1487 if (idler.state != on) {
1488 if (on) {
1489 idler.idlerID = ::SetTimer(MainHWND(), idleTimerID, 10, NULL)
1490 ? reinterpret_cast<IdlerID>(idleTimerID) : 0;
1491 } else {
1492 ::KillTimer(MainHWND(), reinterpret_cast<uptr_t>(idler.idlerID));
1493 idler.idlerID = 0;
1495 idler.state = idler.idlerID != 0;
1497 return idler.state;
1500 void ScintillaWin::SetMouseCapture(bool on) {
1501 if (mouseDownCaptures) {
1502 if (on) {
1503 ::SetCapture(MainHWND());
1504 } else {
1505 ::ReleaseCapture();
1508 capturedMouse = on;
1511 bool ScintillaWin::HaveMouseCapture() {
1512 // Cannot just see if GetCapture is this window as the scroll bar also sets capture for the window
1513 return capturedMouse;
1514 //return capturedMouse && (::GetCapture() == MainHWND());
1517 void ScintillaWin::SetTrackMouseLeaveEvent(bool on) {
1518 if (on && TrackMouseEventFn && !trackedMouseLeave) {
1519 TRACKMOUSEEVENT tme;
1520 tme.cbSize = sizeof(tme);
1521 tme.dwFlags = TME_LEAVE;
1522 tme.hwndTrack = MainHWND();
1523 tme.dwHoverTime = HOVER_DEFAULT; // Unused but triggers Dr. Memory if not initialized
1524 TrackMouseEventFn(&tme);
1526 trackedMouseLeave = on;
1529 bool ScintillaWin::PaintContains(PRectangle rc) {
1530 if (paintState == painting) {
1531 return BoundsContains(rcPaint, hRgnUpdate, rc);
1533 return true;
1536 void ScintillaWin::ScrollText(int /* linesToMove */) {
1537 //Platform::DebugPrintf("ScintillaWin::ScrollText %d\n", linesToMove);
1538 //::ScrollWindow(MainHWND(), 0,
1539 // vs.lineHeight * linesToMove, 0, 0);
1540 //::UpdateWindow(MainHWND());
1541 Redraw();
1542 UpdateSystemCaret();
1545 void ScintillaWin::UpdateSystemCaret() {
1546 if (hasFocus) {
1547 if (HasCaretSizeChanged()) {
1548 DestroySystemCaret();
1549 CreateSystemCaret();
1551 Point pos = PointMainCaret();
1552 ::SetCaretPos(static_cast<int>(pos.x), static_cast<int>(pos.y));
1556 int ScintillaWin::SetScrollInfo(int nBar, LPCSCROLLINFO lpsi, BOOL bRedraw) {
1557 return ::SetScrollInfo(MainHWND(), nBar, lpsi, bRedraw);
1560 bool ScintillaWin::GetScrollInfo(int nBar, LPSCROLLINFO lpsi) {
1561 return ::GetScrollInfo(MainHWND(), nBar, lpsi) ? true : false;
1564 // Change the scroll position but avoid repaint if changing to same value
1565 void ScintillaWin::ChangeScrollPos(int barType, int pos) {
1566 SCROLLINFO sci = {
1567 sizeof(sci), 0, 0, 0, 0, 0, 0
1569 sci.fMask = SIF_POS;
1570 GetScrollInfo(barType, &sci);
1571 if (sci.nPos != pos) {
1572 DwellEnd(true);
1573 sci.nPos = pos;
1574 SetScrollInfo(barType, &sci, TRUE);
1578 void ScintillaWin::SetVerticalScrollPos() {
1579 ChangeScrollPos(SB_VERT, topLine);
1582 void ScintillaWin::SetHorizontalScrollPos() {
1583 ChangeScrollPos(SB_HORZ, xOffset);
1586 bool ScintillaWin::ModifyScrollBars(int nMax, int nPage) {
1587 bool modified = false;
1588 SCROLLINFO sci = {
1589 sizeof(sci), 0, 0, 0, 0, 0, 0
1591 sci.fMask = SIF_PAGE | SIF_RANGE;
1592 GetScrollInfo(SB_VERT, &sci);
1593 int vertEndPreferred = nMax;
1594 if (!verticalScrollBarVisible)
1595 nPage = vertEndPreferred + 1;
1596 if ((sci.nMin != 0) ||
1597 (sci.nMax != vertEndPreferred) ||
1598 (sci.nPage != static_cast<unsigned int>(nPage)) ||
1599 (sci.nPos != 0)) {
1600 sci.fMask = SIF_PAGE | SIF_RANGE;
1601 sci.nMin = 0;
1602 sci.nMax = vertEndPreferred;
1603 sci.nPage = nPage;
1604 sci.nPos = 0;
1605 sci.nTrackPos = 1;
1606 SetScrollInfo(SB_VERT, &sci, TRUE);
1607 modified = true;
1610 PRectangle rcText = GetTextRectangle();
1611 int horizEndPreferred = scrollWidth;
1612 if (horizEndPreferred < 0)
1613 horizEndPreferred = 0;
1614 unsigned int pageWidth = static_cast<unsigned int>(rcText.Width());
1615 if (!horizontalScrollBarVisible || Wrapping())
1616 pageWidth = horizEndPreferred + 1;
1617 sci.fMask = SIF_PAGE | SIF_RANGE;
1618 GetScrollInfo(SB_HORZ, &sci);
1619 if ((sci.nMin != 0) ||
1620 (sci.nMax != horizEndPreferred) ||
1621 (sci.nPage != pageWidth) ||
1622 (sci.nPos != 0)) {
1623 sci.fMask = SIF_PAGE | SIF_RANGE;
1624 sci.nMin = 0;
1625 sci.nMax = horizEndPreferred;
1626 sci.nPage = pageWidth;
1627 sci.nPos = 0;
1628 sci.nTrackPos = 1;
1629 SetScrollInfo(SB_HORZ, &sci, TRUE);
1630 modified = true;
1631 if (scrollWidth < static_cast<int>(pageWidth)) {
1632 HorizontalScrollTo(0);
1635 return modified;
1638 void ScintillaWin::NotifyChange() {
1639 ::SendMessage(::GetParent(MainHWND()), WM_COMMAND,
1640 MAKELONG(GetCtrlID(), SCEN_CHANGE),
1641 reinterpret_cast<LPARAM>(MainHWND()));
1644 void ScintillaWin::NotifyFocus(bool focus) {
1645 ::SendMessage(::GetParent(MainHWND()), WM_COMMAND,
1646 MAKELONG(GetCtrlID(), focus ? SCEN_SETFOCUS : SCEN_KILLFOCUS),
1647 reinterpret_cast<LPARAM>(MainHWND()));
1648 Editor::NotifyFocus(focus);
1651 void ScintillaWin::SetCtrlID(int identifier) {
1652 ::SetWindowID(reinterpret_cast<HWND>(wMain.GetID()), identifier);
1655 int ScintillaWin::GetCtrlID() {
1656 return ::GetDlgCtrlID(reinterpret_cast<HWND>(wMain.GetID()));
1659 void ScintillaWin::NotifyParent(SCNotification scn) {
1660 scn.nmhdr.hwndFrom = MainHWND();
1661 scn.nmhdr.idFrom = GetCtrlID();
1662 ::SendMessage(::GetParent(MainHWND()), WM_NOTIFY,
1663 GetCtrlID(), reinterpret_cast<LPARAM>(&scn));
1666 void ScintillaWin::NotifyParent(SCNotification * scn) {
1667 scn->nmhdr.hwndFrom = MainHWND();
1668 scn->nmhdr.idFrom = GetCtrlID();
1669 ::SendMessage(::GetParent(MainHWND()), WM_NOTIFY,
1670 GetCtrlID(), reinterpret_cast<LPARAM>(scn));
1673 void ScintillaWin::NotifyDoubleClick(Point pt, int modifiers) {
1674 //Platform::DebugPrintf("ScintillaWin Double click 0\n");
1675 ScintillaBase::NotifyDoubleClick(pt, modifiers);
1676 // Send myself a WM_LBUTTONDBLCLK, so the container can handle it too.
1677 ::SendMessage(MainHWND(),
1678 WM_LBUTTONDBLCLK,
1679 (modifiers & SCI_SHIFT) ? MK_SHIFT : 0,
1680 MAKELPARAM(pt.x, pt.y));
1683 class CaseFolderDBCS : public CaseFolderTable {
1684 // Allocate the expandable storage here so that it does not need to be reallocated
1685 // for each call to Fold.
1686 std::vector<wchar_t> utf16Mixed;
1687 std::vector<wchar_t> utf16Folded;
1688 UINT cp;
1689 public:
1690 explicit CaseFolderDBCS(UINT cp_) : cp(cp_) {
1691 StandardASCII();
1693 virtual size_t Fold(char *folded, size_t sizeFolded, const char *mixed, size_t lenMixed) {
1694 if ((lenMixed == 1) && (sizeFolded > 0)) {
1695 folded[0] = mapping[static_cast<unsigned char>(mixed[0])];
1696 return 1;
1697 } else {
1698 if (lenMixed > utf16Mixed.size()) {
1699 utf16Mixed.resize(lenMixed + 8);
1701 size_t nUtf16Mixed = ::MultiByteToWideChar(cp, 0, mixed,
1702 static_cast<int>(lenMixed),
1703 &utf16Mixed[0],
1704 static_cast<int>(utf16Mixed.size()));
1706 if (nUtf16Mixed == 0) {
1707 // Failed to convert -> bad input
1708 folded[0] = '\0';
1709 return 1;
1712 unsigned int lenFlat = 0;
1713 for (size_t mixIndex=0; mixIndex < nUtf16Mixed; mixIndex++) {
1714 if ((lenFlat + 20) > utf16Folded.size())
1715 utf16Folded.resize(lenFlat + 60);
1716 const char *foldedUTF8 = CaseConvert(utf16Mixed[mixIndex], CaseConversionFold);
1717 if (foldedUTF8) {
1718 // Maximum length of a case conversion is 6 bytes, 3 characters
1719 wchar_t wFolded[20];
1720 unsigned int charsConverted = UTF16FromUTF8(foldedUTF8,
1721 static_cast<unsigned int>(strlen(foldedUTF8)),
1722 wFolded, ELEMENTS(wFolded));
1723 for (size_t j=0; j<charsConverted; j++)
1724 utf16Folded[lenFlat++] = wFolded[j];
1725 } else {
1726 utf16Folded[lenFlat++] = utf16Mixed[mixIndex];
1730 size_t lenOut = ::WideCharToMultiByte(cp, 0,
1731 &utf16Folded[0], lenFlat,
1732 NULL, 0, NULL, 0);
1734 if (lenOut < sizeFolded) {
1735 ::WideCharToMultiByte(cp, 0,
1736 &utf16Folded[0], lenFlat,
1737 folded, static_cast<int>(lenOut), NULL, 0);
1738 return lenOut;
1739 } else {
1740 return 0;
1746 CaseFolder *ScintillaWin::CaseFolderForEncoding() {
1747 UINT cpDest = CodePageOfDocument();
1748 if (cpDest == SC_CP_UTF8) {
1749 return new CaseFolderUnicode();
1750 } else {
1751 if (pdoc->dbcsCodePage == 0) {
1752 CaseFolderTable *pcf = new CaseFolderTable();
1753 pcf->StandardASCII();
1754 // Only for single byte encodings
1755 UINT cpDoc = CodePageOfDocument();
1756 for (int i=0x80; i<0x100; i++) {
1757 char sCharacter[2] = "A";
1758 sCharacter[0] = static_cast<char>(i);
1759 wchar_t wCharacter[20];
1760 unsigned int lengthUTF16 = ::MultiByteToWideChar(cpDoc, 0, sCharacter, 1,
1761 wCharacter, ELEMENTS(wCharacter));
1762 if (lengthUTF16 == 1) {
1763 const char *caseFolded = CaseConvert(wCharacter[0], CaseConversionFold);
1764 if (caseFolded) {
1765 wchar_t wLower[20];
1766 unsigned int charsConverted = UTF16FromUTF8(caseFolded,
1767 static_cast<unsigned int>(strlen(caseFolded)),
1768 wLower, ELEMENTS(wLower));
1769 if (charsConverted == 1) {
1770 char sCharacterLowered[20];
1771 unsigned int lengthConverted = ::WideCharToMultiByte(cpDoc, 0,
1772 wLower, charsConverted,
1773 sCharacterLowered, ELEMENTS(sCharacterLowered), NULL, 0);
1774 if ((lengthConverted == 1) && (sCharacter[0] != sCharacterLowered[0])) {
1775 pcf->SetTranslation(sCharacter[0], sCharacterLowered[0]);
1781 return pcf;
1782 } else {
1783 return new CaseFolderDBCS(cpDest);
1788 std::string ScintillaWin::CaseMapString(const std::string &s, int caseMapping) {
1789 if ((s.size() == 0) || (caseMapping == cmSame))
1790 return s;
1792 UINT cpDoc = CodePageOfDocument();
1793 if (cpDoc == SC_CP_UTF8) {
1794 std::string retMapped(s.length() * maxExpansionCaseConversion, 0);
1795 size_t lenMapped = CaseConvertString(&retMapped[0], retMapped.length(), s.c_str(), s.length(),
1796 (caseMapping == cmUpper) ? CaseConversionUpper : CaseConversionLower);
1797 retMapped.resize(lenMapped);
1798 return retMapped;
1801 unsigned int lengthUTF16 = ::MultiByteToWideChar(cpDoc, 0, s.c_str(),
1802 static_cast<int>(s.size()), NULL, 0);
1803 if (lengthUTF16 == 0) // Failed to convert
1804 return s;
1806 DWORD mapFlags = LCMAP_LINGUISTIC_CASING |
1807 ((caseMapping == cmUpper) ? LCMAP_UPPERCASE : LCMAP_LOWERCASE);
1809 // Change text to UTF-16
1810 std::vector<wchar_t> vwcText(lengthUTF16);
1811 ::MultiByteToWideChar(cpDoc, 0, s.c_str(), static_cast<int>(s.size()), &vwcText[0], lengthUTF16);
1813 // Change case
1814 int charsConverted = ::LCMapStringW(LOCALE_SYSTEM_DEFAULT, mapFlags,
1815 &vwcText[0], lengthUTF16, NULL, 0);
1816 std::vector<wchar_t> vwcConverted(charsConverted);
1817 ::LCMapStringW(LOCALE_SYSTEM_DEFAULT, mapFlags,
1818 &vwcText[0], lengthUTF16, &vwcConverted[0], charsConverted);
1820 // Change back to document encoding
1821 unsigned int lengthConverted = ::WideCharToMultiByte(cpDoc, 0,
1822 &vwcConverted[0], static_cast<int>(vwcConverted.size()),
1823 NULL, 0, NULL, 0);
1824 std::vector<char> vcConverted(lengthConverted);
1825 ::WideCharToMultiByte(cpDoc, 0,
1826 &vwcConverted[0], static_cast<int>(vwcConverted.size()),
1827 &vcConverted[0], static_cast<int>(vcConverted.size()), NULL, 0);
1829 return std::string(&vcConverted[0], vcConverted.size());
1832 void ScintillaWin::Copy() {
1833 //Platform::DebugPrintf("Copy\n");
1834 if (!sel.Empty()) {
1835 SelectionText selectedText;
1836 CopySelectionRange(&selectedText);
1837 CopyToClipboard(selectedText);
1841 void ScintillaWin::CopyAllowLine() {
1842 SelectionText selectedText;
1843 CopySelectionRange(&selectedText, true);
1844 CopyToClipboard(selectedText);
1847 bool ScintillaWin::CanPaste() {
1848 if (!Editor::CanPaste())
1849 return false;
1850 if (::IsClipboardFormatAvailable(CF_TEXT))
1851 return true;
1852 if (IsUnicodeMode())
1853 return ::IsClipboardFormatAvailable(CF_UNICODETEXT) != 0;
1854 return false;
1857 class GlobalMemory {
1858 HGLOBAL hand;
1859 public:
1860 void *ptr;
1861 GlobalMemory() : hand(0), ptr(0) {
1863 explicit GlobalMemory(HGLOBAL hand_) : hand(hand_), ptr(0) {
1864 if (hand) {
1865 ptr = ::GlobalLock(hand);
1868 ~GlobalMemory() {
1869 PLATFORM_ASSERT(!ptr);
1871 void Allocate(size_t bytes) {
1872 hand = ::GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT, bytes);
1873 if (hand) {
1874 ptr = ::GlobalLock(hand);
1877 HGLOBAL Unlock() {
1878 PLATFORM_ASSERT(ptr);
1879 HGLOBAL handCopy = hand;
1880 ::GlobalUnlock(hand);
1881 ptr = 0;
1882 hand = 0;
1883 return handCopy;
1885 void SetClip(UINT uFormat) {
1886 ::SetClipboardData(uFormat, Unlock());
1888 operator bool() const {
1889 return ptr != 0;
1891 SIZE_T Size() {
1892 return ::GlobalSize(hand);
1896 void ScintillaWin::Paste() {
1897 if (!::OpenClipboard(MainHWND()))
1898 return;
1899 UndoGroup ug(pdoc);
1900 const bool isLine = SelectionEmpty() &&
1901 (::IsClipboardFormatAvailable(cfLineSelect) || ::IsClipboardFormatAvailable(cfVSLineTag));
1902 ClearSelection(multiPasteMode == SC_MULTIPASTE_EACH);
1903 bool isRectangular = (::IsClipboardFormatAvailable(cfColumnSelect) != 0);
1905 if (!isRectangular) {
1906 // Evaluate "Borland IDE Block Type" explicitly
1907 GlobalMemory memBorlandSelection(::GetClipboardData(cfBorlandIDEBlockType));
1908 if (memBorlandSelection) {
1909 isRectangular = (memBorlandSelection.Size() == 1) && (static_cast<BYTE *>(memBorlandSelection.ptr)[0] == 0x02);
1910 memBorlandSelection.Unlock();
1913 const PasteShape pasteShape = isRectangular ? pasteRectangular : (isLine ? pasteLine : pasteStream);
1915 // Always use CF_UNICODETEXT if available
1916 GlobalMemory memUSelection(::GetClipboardData(CF_UNICODETEXT));
1917 if (memUSelection) {
1918 wchar_t *uptr = static_cast<wchar_t *>(memUSelection.ptr);
1919 if (uptr) {
1920 unsigned int len;
1921 std::vector<char> putf;
1922 // Default Scintilla behaviour in Unicode mode
1923 if (IsUnicodeMode()) {
1924 unsigned int bytes = static_cast<unsigned int>(memUSelection.Size());
1925 len = UTF8Length(uptr, bytes / 2);
1926 putf.resize(len + 1);
1927 UTF8FromUTF16(uptr, bytes / 2, &putf[0], len);
1928 } else {
1929 // CF_UNICODETEXT available, but not in Unicode mode
1930 // Convert from Unicode to current Scintilla code page
1931 UINT cpDest = CodePageOfDocument();
1932 len = ::WideCharToMultiByte(cpDest, 0, uptr, -1,
1933 NULL, 0, NULL, NULL) - 1; // subtract 0 terminator
1934 putf.resize(len + 1);
1935 ::WideCharToMultiByte(cpDest, 0, uptr, -1,
1936 &putf[0], len + 1, NULL, NULL);
1939 InsertPasteShape(&putf[0], len, pasteShape);
1941 memUSelection.Unlock();
1942 } else {
1943 // CF_UNICODETEXT not available, paste ANSI text
1944 GlobalMemory memSelection(::GetClipboardData(CF_TEXT));
1945 if (memSelection) {
1946 char *ptr = static_cast<char *>(memSelection.ptr);
1947 if (ptr) {
1948 unsigned int bytes = static_cast<unsigned int>(memSelection.Size());
1949 unsigned int len = bytes;
1950 for (unsigned int i = 0; i < bytes; i++) {
1951 if ((len == bytes) && (0 == ptr[i]))
1952 len = i;
1955 // In Unicode mode, convert clipboard text to UTF-8
1956 if (IsUnicodeMode()) {
1957 std::vector<wchar_t> uptr(len+1);
1959 unsigned int ulen = ::MultiByteToWideChar(CP_ACP, 0,
1960 ptr, len, &uptr[0], len+1);
1962 unsigned int mlen = UTF8Length(&uptr[0], ulen);
1963 std::vector<char> putf(mlen+1);
1964 // CP_UTF8 not available on Windows 95, so use UTF8FromUTF16()
1965 UTF8FromUTF16(&uptr[0], ulen, &putf[0], mlen);
1967 InsertPasteShape(&putf[0], mlen, pasteShape);
1968 } else {
1969 InsertPasteShape(ptr, len, pasteShape);
1972 memSelection.Unlock();
1975 ::CloseClipboard();
1976 Redraw();
1979 void ScintillaWin::CreateCallTipWindow(PRectangle) {
1980 if (!ct.wCallTip.Created()) {
1981 ct.wCallTip = ::CreateWindow(callClassName, TEXT("ACallTip"),
1982 WS_POPUP, 100, 100, 150, 20,
1983 MainHWND(), 0,
1984 GetWindowInstance(MainHWND()),
1985 this);
1986 ct.wDraw = ct.wCallTip;
1990 void ScintillaWin::AddToPopUp(const char *label, int cmd, bool enabled) {
1991 HMENU hmenuPopup = reinterpret_cast<HMENU>(popup.GetID());
1992 if (!label[0])
1993 ::AppendMenuA(hmenuPopup, MF_SEPARATOR, 0, "");
1994 else if (enabled)
1995 ::AppendMenuA(hmenuPopup, MF_STRING, cmd, label);
1996 else
1997 ::AppendMenuA(hmenuPopup, MF_STRING | MF_DISABLED | MF_GRAYED, cmd, label);
2000 void ScintillaWin::ClaimSelection() {
2001 // Windows does not have a primary selection
2004 /// Implement IUnknown
2006 STDMETHODIMP_(ULONG)FormatEnumerator_AddRef(FormatEnumerator *fe);
2007 STDMETHODIMP FormatEnumerator_QueryInterface(FormatEnumerator *fe, REFIID riid, PVOID *ppv) {
2008 //Platform::DebugPrintf("EFE QI");
2009 *ppv = NULL;
2010 if (riid == IID_IUnknown)
2011 *ppv = reinterpret_cast<IEnumFORMATETC *>(fe);
2012 if (riid == IID_IEnumFORMATETC)
2013 *ppv = reinterpret_cast<IEnumFORMATETC *>(fe);
2014 if (!*ppv)
2015 return E_NOINTERFACE;
2016 FormatEnumerator_AddRef(fe);
2017 return S_OK;
2019 STDMETHODIMP_(ULONG)FormatEnumerator_AddRef(FormatEnumerator *fe) {
2020 return ++fe->ref;
2022 STDMETHODIMP_(ULONG)FormatEnumerator_Release(FormatEnumerator *fe) {
2023 fe->ref--;
2024 if (fe->ref > 0)
2025 return fe->ref;
2026 delete fe;
2027 return 0;
2029 /// Implement IEnumFORMATETC
2030 STDMETHODIMP FormatEnumerator_Next(FormatEnumerator *fe, ULONG celt, FORMATETC *rgelt, ULONG *pceltFetched) {
2031 if (rgelt == NULL) return E_POINTER;
2032 unsigned int putPos = 0;
2033 while ((fe->pos < fe->formats.size()) && (putPos < celt)) {
2034 rgelt->cfFormat = fe->formats[fe->pos];
2035 rgelt->ptd = 0;
2036 rgelt->dwAspect = DVASPECT_CONTENT;
2037 rgelt->lindex = -1;
2038 rgelt->tymed = TYMED_HGLOBAL;
2039 rgelt++;
2040 fe->pos++;
2041 putPos++;
2043 if (pceltFetched)
2044 *pceltFetched = putPos;
2045 return putPos ? S_OK : S_FALSE;
2047 STDMETHODIMP FormatEnumerator_Skip(FormatEnumerator *fe, ULONG celt) {
2048 fe->pos += celt;
2049 return S_OK;
2051 STDMETHODIMP FormatEnumerator_Reset(FormatEnumerator *fe) {
2052 fe->pos = 0;
2053 return S_OK;
2055 STDMETHODIMP FormatEnumerator_Clone(FormatEnumerator *fe, IEnumFORMATETC **ppenum) {
2056 FormatEnumerator *pfe;
2057 try {
2058 pfe = new FormatEnumerator(fe->pos, &fe->formats[0], fe->formats.size());
2059 } catch (...) {
2060 return E_OUTOFMEMORY;
2062 return FormatEnumerator_QueryInterface(pfe, IID_IEnumFORMATETC,
2063 reinterpret_cast<void **>(ppenum));
2066 static VFunction *vtFormatEnumerator[] = {
2067 (VFunction *)(FormatEnumerator_QueryInterface),
2068 (VFunction *)(FormatEnumerator_AddRef),
2069 (VFunction *)(FormatEnumerator_Release),
2070 (VFunction *)(FormatEnumerator_Next),
2071 (VFunction *)(FormatEnumerator_Skip),
2072 (VFunction *)(FormatEnumerator_Reset),
2073 (VFunction *)(FormatEnumerator_Clone)
2076 FormatEnumerator::FormatEnumerator(int pos_, CLIPFORMAT formats_[], size_t formatsLen_) {
2077 vtbl = vtFormatEnumerator;
2078 ref = 0; // First QI adds first reference...
2079 pos = pos_;
2080 formats.insert(formats.begin(), formats_, formats_+formatsLen_);
2083 /// Implement IUnknown
2084 STDMETHODIMP DropSource_QueryInterface(DropSource *ds, REFIID riid, PVOID *ppv) {
2085 return ds->sci->QueryInterface(riid, ppv);
2087 STDMETHODIMP_(ULONG)DropSource_AddRef(DropSource *ds) {
2088 return ds->sci->AddRef();
2090 STDMETHODIMP_(ULONG)DropSource_Release(DropSource *ds) {
2091 return ds->sci->Release();
2094 /// Implement IDropSource
2095 STDMETHODIMP DropSource_QueryContinueDrag(DropSource *, BOOL fEsc, DWORD grfKeyState) {
2096 if (fEsc)
2097 return DRAGDROP_S_CANCEL;
2098 if (!(grfKeyState & MK_LBUTTON))
2099 return DRAGDROP_S_DROP;
2100 return S_OK;
2103 STDMETHODIMP DropSource_GiveFeedback(DropSource *, DWORD) {
2104 return DRAGDROP_S_USEDEFAULTCURSORS;
2107 static VFunction *vtDropSource[] = {
2108 (VFunction *)(DropSource_QueryInterface),
2109 (VFunction *)(DropSource_AddRef),
2110 (VFunction *)(DropSource_Release),
2111 (VFunction *)(DropSource_QueryContinueDrag),
2112 (VFunction *)(DropSource_GiveFeedback)
2115 DropSource::DropSource() {
2116 vtbl = vtDropSource;
2117 sci = 0;
2120 /// Implement IUnkown
2121 STDMETHODIMP DataObject_QueryInterface(DataObject *pd, REFIID riid, PVOID *ppv) {
2122 //Platform::DebugPrintf("DO QI %x\n", pd);
2123 return pd->sci->QueryInterface(riid, ppv);
2125 STDMETHODIMP_(ULONG)DataObject_AddRef(DataObject *pd) {
2126 return pd->sci->AddRef();
2128 STDMETHODIMP_(ULONG)DataObject_Release(DataObject *pd) {
2129 return pd->sci->Release();
2131 /// Implement IDataObject
2132 STDMETHODIMP DataObject_GetData(DataObject *pd, FORMATETC *pFEIn, STGMEDIUM *pSTM) {
2133 return pd->sci->GetData(pFEIn, pSTM);
2136 STDMETHODIMP DataObject_GetDataHere(DataObject *, FORMATETC *, STGMEDIUM *) {
2137 //Platform::DebugPrintf("DOB GetDataHere\n");
2138 return E_NOTIMPL;
2141 STDMETHODIMP DataObject_QueryGetData(DataObject *pd, FORMATETC *pFE) {
2142 if (pd->sci->DragIsRectangularOK(pFE->cfFormat) &&
2143 pFE->ptd == 0 &&
2144 (pFE->dwAspect & DVASPECT_CONTENT) != 0 &&
2145 pFE->lindex == -1 &&
2146 (pFE->tymed & TYMED_HGLOBAL) != 0
2148 return S_OK;
2151 bool formatOK = (pFE->cfFormat == CF_TEXT) ||
2152 ((pFE->cfFormat == CF_UNICODETEXT) && pd->sci->IsUnicodeMode());
2153 if (!formatOK ||
2154 pFE->ptd != 0 ||
2155 (pFE->dwAspect & DVASPECT_CONTENT) == 0 ||
2156 pFE->lindex != -1 ||
2157 (pFE->tymed & TYMED_HGLOBAL) == 0
2159 //Platform::DebugPrintf("DOB QueryGetData No %x\n",pFE->cfFormat);
2160 //return DATA_E_FORMATETC;
2161 return S_FALSE;
2163 //Platform::DebugPrintf("DOB QueryGetData OK %x\n",pFE->cfFormat);
2164 return S_OK;
2167 STDMETHODIMP DataObject_GetCanonicalFormatEtc(DataObject *pd, FORMATETC *, FORMATETC *pFEOut) {
2168 //Platform::DebugPrintf("DOB GetCanon\n");
2169 if (pd->sci->IsUnicodeMode())
2170 pFEOut->cfFormat = CF_UNICODETEXT;
2171 else
2172 pFEOut->cfFormat = CF_TEXT;
2173 pFEOut->ptd = 0;
2174 pFEOut->dwAspect = DVASPECT_CONTENT;
2175 pFEOut->lindex = -1;
2176 pFEOut->tymed = TYMED_HGLOBAL;
2177 return S_OK;
2180 STDMETHODIMP DataObject_SetData(DataObject *, FORMATETC *, STGMEDIUM *, BOOL) {
2181 //Platform::DebugPrintf("DOB SetData\n");
2182 return E_FAIL;
2185 STDMETHODIMP DataObject_EnumFormatEtc(DataObject *pd, DWORD dwDirection, IEnumFORMATETC **ppEnum) {
2186 try {
2187 //Platform::DebugPrintf("DOB EnumFormatEtc %d\n", dwDirection);
2188 if (dwDirection != DATADIR_GET) {
2189 *ppEnum = 0;
2190 return E_FAIL;
2192 FormatEnumerator *pfe;
2193 if (pd->sci->IsUnicodeMode()) {
2194 CLIPFORMAT formats[] = {CF_UNICODETEXT, CF_TEXT};
2195 pfe = new FormatEnumerator(0, formats, ELEMENTS(formats));
2196 } else {
2197 CLIPFORMAT formats[] = {CF_TEXT};
2198 pfe = new FormatEnumerator(0, formats, ELEMENTS(formats));
2200 return FormatEnumerator_QueryInterface(pfe, IID_IEnumFORMATETC,
2201 reinterpret_cast<void **>(ppEnum));
2202 } catch (std::bad_alloc &) {
2203 pd->sci->errorStatus = SC_STATUS_BADALLOC;
2204 return E_OUTOFMEMORY;
2205 } catch (...) {
2206 pd->sci->errorStatus = SC_STATUS_FAILURE;
2207 return E_FAIL;
2211 STDMETHODIMP DataObject_DAdvise(DataObject *, FORMATETC *, DWORD, IAdviseSink *, PDWORD) {
2212 //Platform::DebugPrintf("DOB DAdvise\n");
2213 return E_FAIL;
2216 STDMETHODIMP DataObject_DUnadvise(DataObject *, DWORD) {
2217 //Platform::DebugPrintf("DOB DUnadvise\n");
2218 return E_FAIL;
2221 STDMETHODIMP DataObject_EnumDAdvise(DataObject *, IEnumSTATDATA **) {
2222 //Platform::DebugPrintf("DOB EnumDAdvise\n");
2223 return E_FAIL;
2226 static VFunction *vtDataObject[] = {
2227 (VFunction *)(DataObject_QueryInterface),
2228 (VFunction *)(DataObject_AddRef),
2229 (VFunction *)(DataObject_Release),
2230 (VFunction *)(DataObject_GetData),
2231 (VFunction *)(DataObject_GetDataHere),
2232 (VFunction *)(DataObject_QueryGetData),
2233 (VFunction *)(DataObject_GetCanonicalFormatEtc),
2234 (VFunction *)(DataObject_SetData),
2235 (VFunction *)(DataObject_EnumFormatEtc),
2236 (VFunction *)(DataObject_DAdvise),
2237 (VFunction *)(DataObject_DUnadvise),
2238 (VFunction *)(DataObject_EnumDAdvise)
2241 DataObject::DataObject() {
2242 vtbl = vtDataObject;
2243 sci = 0;
2246 /// Implement IUnknown
2247 STDMETHODIMP DropTarget_QueryInterface(DropTarget *dt, REFIID riid, PVOID *ppv) {
2248 //Platform::DebugPrintf("DT QI %x\n", dt);
2249 return dt->sci->QueryInterface(riid, ppv);
2251 STDMETHODIMP_(ULONG)DropTarget_AddRef(DropTarget *dt) {
2252 return dt->sci->AddRef();
2254 STDMETHODIMP_(ULONG)DropTarget_Release(DropTarget *dt) {
2255 return dt->sci->Release();
2258 /// Implement IDropTarget by forwarding to Scintilla
2259 STDMETHODIMP DropTarget_DragEnter(DropTarget *dt, LPDATAOBJECT pIDataSource, DWORD grfKeyState,
2260 POINTL pt, PDWORD pdwEffect) {
2261 try {
2262 return dt->sci->DragEnter(pIDataSource, grfKeyState, pt, pdwEffect);
2263 } catch (...) {
2264 dt->sci->errorStatus = SC_STATUS_FAILURE;
2266 return E_FAIL;
2268 STDMETHODIMP DropTarget_DragOver(DropTarget *dt, DWORD grfKeyState, POINTL pt, PDWORD pdwEffect) {
2269 try {
2270 return dt->sci->DragOver(grfKeyState, pt, pdwEffect);
2271 } catch (...) {
2272 dt->sci->errorStatus = SC_STATUS_FAILURE;
2274 return E_FAIL;
2276 STDMETHODIMP DropTarget_DragLeave(DropTarget *dt) {
2277 try {
2278 return dt->sci->DragLeave();
2279 } catch (...) {
2280 dt->sci->errorStatus = SC_STATUS_FAILURE;
2282 return E_FAIL;
2284 STDMETHODIMP DropTarget_Drop(DropTarget *dt, LPDATAOBJECT pIDataSource, DWORD grfKeyState,
2285 POINTL pt, PDWORD pdwEffect) {
2286 try {
2287 return dt->sci->Drop(pIDataSource, grfKeyState, pt, pdwEffect);
2288 } catch (...) {
2289 dt->sci->errorStatus = SC_STATUS_FAILURE;
2291 return E_FAIL;
2294 static VFunction *vtDropTarget[] = {
2295 (VFunction *)(DropTarget_QueryInterface),
2296 (VFunction *)(DropTarget_AddRef),
2297 (VFunction *)(DropTarget_Release),
2298 (VFunction *)(DropTarget_DragEnter),
2299 (VFunction *)(DropTarget_DragOver),
2300 (VFunction *)(DropTarget_DragLeave),
2301 (VFunction *)(DropTarget_Drop)
2304 DropTarget::DropTarget() {
2305 vtbl = vtDropTarget;
2306 sci = 0;
2310 * DBCS: support Input Method Editor (IME).
2311 * Called when IME Window opened.
2313 void ScintillaWin::ImeStartComposition() {
2314 if (caret.active) {
2315 // Move IME Window to current caret position
2316 HIMC hIMC = ::ImmGetContext(MainHWND());
2317 Point pos = PointMainCaret();
2318 COMPOSITIONFORM CompForm;
2319 CompForm.dwStyle = CFS_POINT;
2320 CompForm.ptCurrentPos.x = static_cast<int>(pos.x);
2321 CompForm.ptCurrentPos.y = static_cast<int>(pos.y);
2323 ::ImmSetCompositionWindow(hIMC, &CompForm);
2325 // Set font of IME window to same as surrounded text.
2326 if (stylesValid) {
2327 // Since the style creation code has been made platform independent,
2328 // The logfont for the IME is recreated here.
2329 int styleHere = (pdoc->StyleAt(sel.MainCaret())) & 31;
2330 LOGFONTW lf = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, L""};
2331 int sizeZoomed = vs.styles[styleHere].size + vs.zoomLevel * SC_FONT_SIZE_MULTIPLIER;
2332 if (sizeZoomed <= 2 * SC_FONT_SIZE_MULTIPLIER) // Hangs if sizeZoomed <= 1
2333 sizeZoomed = 2 * SC_FONT_SIZE_MULTIPLIER;
2334 AutoSurface surface(this);
2335 int deviceHeight = sizeZoomed;
2336 if (surface) {
2337 deviceHeight = (sizeZoomed * surface->LogPixelsY()) / 72;
2339 // The negative is to allow for leading
2340 lf.lfHeight = -(abs(deviceHeight / SC_FONT_SIZE_MULTIPLIER));
2341 lf.lfWeight = vs.styles[styleHere].weight;
2342 lf.lfItalic = static_cast<BYTE>(vs.styles[styleHere].italic ? 1 : 0);
2343 lf.lfCharSet = DEFAULT_CHARSET;
2344 lf.lfFaceName[0] = '\0';
2345 if (vs.styles[styleHere].fontName) {
2346 const char* fontName = vs.styles[styleHere].fontName;
2347 UTF16FromUTF8(fontName, strlen(fontName)+1, lf.lfFaceName, LF_FACESIZE);
2349 ::ImmSetCompositionFontW(hIMC, &lf);
2351 ::ImmReleaseContext(MainHWND(), hIMC);
2352 // Caret is displayed in IME window. So, caret in Scintilla is useless.
2353 DropCaret();
2357 /** Called when IME Window closed. */
2358 void ScintillaWin::ImeEndComposition() {
2359 ShowCaretAtCurrentPosition();
2362 void ScintillaWin::AddCharBytes(char b0, char b1) {
2364 int inputCodePage = InputCodePage();
2365 if (inputCodePage && IsUnicodeMode()) {
2366 char utfval[4] = "\0\0\0";
2367 char ansiChars[3];
2368 wchar_t wcs[2];
2369 if (b0) { // Two bytes from IME
2370 ansiChars[0] = b0;
2371 ansiChars[1] = b1;
2372 ansiChars[2] = '\0';
2373 ::MultiByteToWideChar(inputCodePage, 0, ansiChars, 2, wcs, 1);
2374 } else {
2375 ansiChars[0] = b1;
2376 ansiChars[1] = '\0';
2377 ::MultiByteToWideChar(inputCodePage, 0, ansiChars, 1, wcs, 1);
2379 unsigned int len = UTF8Length(wcs, 1);
2380 UTF8FromUTF16(wcs, 1, utfval, len);
2381 utfval[len] = '\0';
2382 AddCharUTF(utfval, len ? len : 1);
2383 } else if (b0) {
2384 char dbcsChars[3];
2385 dbcsChars[0] = b0;
2386 dbcsChars[1] = b1;
2387 dbcsChars[2] = '\0';
2388 AddCharUTF(dbcsChars, 2, true);
2389 } else {
2390 AddChar(b1);
2394 void ScintillaWin::GetIntelliMouseParameters() {
2395 // This retrieves the number of lines per scroll as configured inthe Mouse Properties sheet in Control Panel
2396 ::SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, &linesPerScroll, 0);
2399 void ScintillaWin::CopyToClipboard(const SelectionText &selectedText) {
2400 if (!::OpenClipboard(MainHWND()))
2401 return;
2402 ::EmptyClipboard();
2404 GlobalMemory uniText;
2406 // Default Scintilla behaviour in Unicode mode
2407 if (IsUnicodeMode()) {
2408 int uchars = UTF16Length(selectedText.Data(),
2409 static_cast<int>(selectedText.LengthWithTerminator()));
2410 uniText.Allocate(2 * uchars);
2411 if (uniText) {
2412 UTF16FromUTF8(selectedText.Data(), static_cast<int>(selectedText.LengthWithTerminator()),
2413 static_cast<wchar_t *>(uniText.ptr), uchars);
2415 } else {
2416 // Not Unicode mode
2417 // Convert to Unicode using the current Scintilla code page
2418 UINT cpSrc = CodePageFromCharSet(
2419 selectedText.characterSet, selectedText.codePage);
2420 int uLen = ::MultiByteToWideChar(cpSrc, 0, selectedText.Data(),
2421 static_cast<int>(selectedText.LengthWithTerminator()), 0, 0);
2422 uniText.Allocate(2 * uLen);
2423 if (uniText) {
2424 ::MultiByteToWideChar(cpSrc, 0, selectedText.Data(),
2425 static_cast<int>(selectedText.LengthWithTerminator()),
2426 static_cast<wchar_t *>(uniText.ptr), uLen);
2430 if (uniText) {
2431 if (!IsNT()) {
2432 // Copy ANSI text to clipboard on Windows 9x
2433 // Convert from Unicode text, so other ANSI programs can
2434 // paste the text
2435 // Windows NT, 2k, XP automatically generates CF_TEXT
2436 GlobalMemory ansiText;
2437 ansiText.Allocate(selectedText.LengthWithTerminator());
2438 if (ansiText) {
2439 ::WideCharToMultiByte(CP_ACP, 0, static_cast<wchar_t *>(uniText.ptr), -1,
2440 static_cast<char *>(ansiText.ptr),
2441 static_cast<int>(selectedText.LengthWithTerminator()), NULL, NULL);
2442 ansiText.SetClip(CF_TEXT);
2445 uniText.SetClip(CF_UNICODETEXT);
2446 } else {
2447 // There was a failure - try to copy at least ANSI text
2448 GlobalMemory ansiText;
2449 ansiText.Allocate(selectedText.LengthWithTerminator());
2450 if (ansiText) {
2451 memcpy(static_cast<char *>(ansiText.ptr), selectedText.Data(), selectedText.LengthWithTerminator());
2452 ansiText.SetClip(CF_TEXT);
2456 if (selectedText.rectangular) {
2457 ::SetClipboardData(cfColumnSelect, 0);
2459 GlobalMemory borlandSelection;
2460 borlandSelection.Allocate(1);
2461 if (borlandSelection) {
2462 static_cast<BYTE *>(borlandSelection.ptr)[0] = 0x02;
2463 borlandSelection.SetClip(cfBorlandIDEBlockType);
2467 if (selectedText.lineCopy) {
2468 ::SetClipboardData(cfLineSelect, 0);
2469 ::SetClipboardData(cfVSLineTag, 0);
2472 ::CloseClipboard();
2475 void ScintillaWin::ScrollMessage(WPARAM wParam) {
2476 //DWORD dwStart = timeGetTime();
2477 //Platform::DebugPrintf("Scroll %x %d\n", wParam, lParam);
2479 SCROLLINFO sci = {};
2480 sci.cbSize = sizeof(sci);
2481 sci.fMask = SIF_ALL;
2483 GetScrollInfo(SB_VERT, &sci);
2485 //Platform::DebugPrintf("ScrollInfo %d mask=%x min=%d max=%d page=%d pos=%d track=%d\n", b,sci.fMask,
2486 //sci.nMin, sci.nMax, sci.nPage, sci.nPos, sci.nTrackPos);
2488 int topLineNew = topLine;
2489 switch (LoWord(wParam)) {
2490 case SB_LINEUP:
2491 topLineNew -= 1;
2492 break;
2493 case SB_LINEDOWN:
2494 topLineNew += 1;
2495 break;
2496 case SB_PAGEUP:
2497 topLineNew -= LinesToScroll(); break;
2498 case SB_PAGEDOWN: topLineNew += LinesToScroll(); break;
2499 case SB_TOP: topLineNew = 0; break;
2500 case SB_BOTTOM: topLineNew = MaxScrollPos(); break;
2501 case SB_THUMBPOSITION: topLineNew = sci.nTrackPos; break;
2502 case SB_THUMBTRACK: topLineNew = sci.nTrackPos; break;
2504 ScrollTo(topLineNew);
2507 void ScintillaWin::HorizontalScrollMessage(WPARAM wParam) {
2508 int xPos = xOffset;
2509 PRectangle rcText = GetTextRectangle();
2510 int pageWidth = static_cast<int>(rcText.Width() * 2 / 3);
2511 switch (LoWord(wParam)) {
2512 case SB_LINEUP:
2513 xPos -= 20;
2514 break;
2515 case SB_LINEDOWN: // May move past the logical end
2516 xPos += 20;
2517 break;
2518 case SB_PAGEUP:
2519 xPos -= pageWidth;
2520 break;
2521 case SB_PAGEDOWN:
2522 xPos += pageWidth;
2523 if (xPos > scrollWidth - rcText.Width()) { // Hit the end exactly
2524 xPos = scrollWidth - static_cast<int>(rcText.Width());
2526 break;
2527 case SB_TOP:
2528 xPos = 0;
2529 break;
2530 case SB_BOTTOM:
2531 xPos = scrollWidth;
2532 break;
2533 case SB_THUMBPOSITION:
2534 case SB_THUMBTRACK: {
2535 // 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 =]
2536 SCROLLINFO si;
2537 si.cbSize = sizeof(si);
2538 si.fMask = SIF_TRACKPOS;
2539 if (GetScrollInfo(SB_HORZ, &si)) {
2540 xPos = si.nTrackPos;
2543 break;
2545 HorizontalScrollTo(xPos);
2549 * Redraw all of text area.
2550 * This paint will not be abandoned.
2552 void ScintillaWin::FullPaint() {
2553 if (technology == SC_TECHNOLOGY_DEFAULT) {
2554 HDC hdc = ::GetDC(MainHWND());
2555 FullPaintDC(hdc);
2556 ::ReleaseDC(MainHWND(), hdc);
2557 } else {
2558 FullPaintDC(0);
2563 * Redraw all of text area on the specified DC.
2564 * This paint will not be abandoned.
2566 void ScintillaWin::FullPaintDC(HDC hdc) {
2567 paintState = painting;
2568 rcPaint = GetClientRectangle();
2569 paintingAllText = true;
2570 if (technology == SC_TECHNOLOGY_DEFAULT) {
2571 AutoSurface surfaceWindow(hdc, this);
2572 if (surfaceWindow) {
2573 Paint(surfaceWindow, rcPaint);
2574 surfaceWindow->Release();
2576 } else {
2577 #if defined(USE_D2D)
2578 EnsureRenderTarget();
2579 AutoSurface surfaceWindow(pRenderTarget, this);
2580 if (surfaceWindow) {
2581 pRenderTarget->BeginDraw();
2582 Paint(surfaceWindow, rcPaint);
2583 surfaceWindow->Release();
2584 HRESULT hr = pRenderTarget->EndDraw();
2585 if (hr == D2DERR_RECREATE_TARGET) {
2586 DropRenderTarget();
2589 #endif
2591 paintState = notPainting;
2594 static bool CompareDevCap(HDC hdc, HDC hOtherDC, int nIndex) {
2595 return ::GetDeviceCaps(hdc, nIndex) == ::GetDeviceCaps(hOtherDC, nIndex);
2598 bool ScintillaWin::IsCompatibleDC(HDC hOtherDC) {
2599 HDC hdc = ::GetDC(MainHWND());
2600 bool isCompatible =
2601 CompareDevCap(hdc, hOtherDC, TECHNOLOGY) &&
2602 CompareDevCap(hdc, hOtherDC, LOGPIXELSY) &&
2603 CompareDevCap(hdc, hOtherDC, LOGPIXELSX) &&
2604 CompareDevCap(hdc, hOtherDC, BITSPIXEL) &&
2605 CompareDevCap(hdc, hOtherDC, PLANES);
2606 ::ReleaseDC(MainHWND(), hdc);
2607 return isCompatible;
2610 DWORD ScintillaWin::EffectFromState(DWORD grfKeyState) const {
2611 // These are the Wordpad semantics.
2612 DWORD dwEffect;
2613 if (inDragDrop == ddDragging) // Internal defaults to move
2614 dwEffect = DROPEFFECT_MOVE;
2615 else
2616 dwEffect = DROPEFFECT_COPY;
2617 if (grfKeyState & MK_ALT)
2618 dwEffect = DROPEFFECT_MOVE;
2619 if (grfKeyState & MK_CONTROL)
2620 dwEffect = DROPEFFECT_COPY;
2621 return dwEffect;
2624 /// Implement IUnknown
2625 STDMETHODIMP ScintillaWin::QueryInterface(REFIID riid, PVOID *ppv) {
2626 *ppv = NULL;
2627 if (riid == IID_IUnknown)
2628 *ppv = reinterpret_cast<IDropTarget *>(&dt);
2629 if (riid == IID_IDropSource)
2630 *ppv = reinterpret_cast<IDropSource *>(&ds);
2631 if (riid == IID_IDropTarget)
2632 *ppv = reinterpret_cast<IDropTarget *>(&dt);
2633 if (riid == IID_IDataObject)
2634 *ppv = reinterpret_cast<IDataObject *>(&dob);
2635 if (!*ppv)
2636 return E_NOINTERFACE;
2637 return S_OK;
2640 STDMETHODIMP_(ULONG) ScintillaWin::AddRef() {
2641 return 1;
2644 STDMETHODIMP_(ULONG) ScintillaWin::Release() {
2645 return 1;
2648 /// Implement IDropTarget
2649 STDMETHODIMP ScintillaWin::DragEnter(LPDATAOBJECT pIDataSource, DWORD grfKeyState,
2650 POINTL, PDWORD pdwEffect) {
2651 if (pIDataSource == NULL)
2652 return E_POINTER;
2653 FORMATETC fmtu = {CF_UNICODETEXT, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
2654 HRESULT hrHasUText = pIDataSource->QueryGetData(&fmtu);
2655 hasOKText = (hrHasUText == S_OK);
2656 if (!hasOKText) {
2657 FORMATETC fmte = {CF_TEXT, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
2658 HRESULT hrHasText = pIDataSource->QueryGetData(&fmte);
2659 hasOKText = (hrHasText == S_OK);
2661 if (!hasOKText) {
2662 *pdwEffect = DROPEFFECT_NONE;
2663 return S_OK;
2666 *pdwEffect = EffectFromState(grfKeyState);
2667 return S_OK;
2670 STDMETHODIMP ScintillaWin::DragOver(DWORD grfKeyState, POINTL pt, PDWORD pdwEffect) {
2671 try {
2672 if (!hasOKText || pdoc->IsReadOnly()) {
2673 *pdwEffect = DROPEFFECT_NONE;
2674 return S_OK;
2677 *pdwEffect = EffectFromState(grfKeyState);
2679 // Update the cursor.
2680 POINT rpt = {pt.x, pt.y};
2681 ::ScreenToClient(MainHWND(), &rpt);
2682 SetDragPosition(SPositionFromLocation(PointFromPOINT(rpt), false, false, UserVirtualSpace()));
2684 return S_OK;
2685 } catch (...) {
2686 errorStatus = SC_STATUS_FAILURE;
2688 return E_FAIL;
2691 STDMETHODIMP ScintillaWin::DragLeave() {
2692 try {
2693 SetDragPosition(SelectionPosition(invalidPosition));
2694 return S_OK;
2695 } catch (...) {
2696 errorStatus = SC_STATUS_FAILURE;
2698 return E_FAIL;
2701 STDMETHODIMP ScintillaWin::Drop(LPDATAOBJECT pIDataSource, DWORD grfKeyState,
2702 POINTL pt, PDWORD pdwEffect) {
2703 try {
2704 *pdwEffect = EffectFromState(grfKeyState);
2706 if (pIDataSource == NULL)
2707 return E_POINTER;
2709 SetDragPosition(SelectionPosition(invalidPosition));
2711 STGMEDIUM medium = {0, {0}, 0};
2713 std::vector<char> data; // Includes terminating NUL
2715 FORMATETC fmtu = {CF_UNICODETEXT, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
2716 HRESULT hr = pIDataSource->GetData(&fmtu, &medium);
2717 if (SUCCEEDED(hr) && medium.hGlobal) {
2718 GlobalMemory memUDrop(medium.hGlobal);
2719 wchar_t *udata = static_cast<wchar_t *>(memUDrop.ptr);
2720 if (udata) {
2721 if (IsUnicodeMode()) {
2722 int tlen = static_cast<int>(memUDrop.Size());
2723 // Convert UTF-16 to UTF-8
2724 int dataLen = UTF8Length(udata, tlen/2);
2725 data.resize(dataLen+1);
2726 UTF8FromUTF16(udata, tlen/2, &data[0], dataLen);
2727 } else {
2728 // Convert UTF-16 to ANSI
2730 // Default Scintilla behavior in Unicode mode
2731 // CF_UNICODETEXT available, but not in Unicode mode
2732 // Convert from Unicode to current Scintilla code page
2733 UINT cpDest = CodePageOfDocument();
2734 int tlen = ::WideCharToMultiByte(cpDest, 0, udata, -1,
2735 NULL, 0, NULL, NULL) - 1; // subtract 0 terminator
2736 data.resize(tlen + 1);
2737 ::WideCharToMultiByte(cpDest, 0, udata, -1,
2738 &data[0], tlen + 1, NULL, NULL);
2741 memUDrop.Unlock();
2742 } else {
2743 FORMATETC fmte = {CF_TEXT, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
2744 hr = pIDataSource->GetData(&fmte, &medium);
2745 if (SUCCEEDED(hr) && medium.hGlobal) {
2746 GlobalMemory memDrop(medium.hGlobal);
2747 const char *cdata = static_cast<char *>(memDrop.ptr);
2748 if (cdata)
2749 data.assign(cdata, cdata+strlen(cdata)+1);
2750 memDrop.Unlock();
2754 if (!SUCCEEDED(hr) || data.empty()) {
2755 //Platform::DebugPrintf("Bad data format: 0x%x\n", hres);
2756 return hr;
2759 FORMATETC fmtr = {cfColumnSelect, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
2760 HRESULT hrRectangular = pIDataSource->QueryGetData(&fmtr);
2762 POINT rpt = {pt.x, pt.y};
2763 ::ScreenToClient(MainHWND(), &rpt);
2764 SelectionPosition movePos = SPositionFromLocation(PointFromPOINT(rpt), false, false, UserVirtualSpace());
2766 DropAt(movePos, &data[0], data.size() - 1, *pdwEffect == DROPEFFECT_MOVE, hrRectangular == S_OK);
2768 // Free data
2769 if (medium.pUnkForRelease != NULL)
2770 medium.pUnkForRelease->Release();
2771 else
2772 ::GlobalFree(medium.hGlobal);
2774 return S_OK;
2775 } catch (...) {
2776 errorStatus = SC_STATUS_FAILURE;
2778 return E_FAIL;
2781 /// Implement important part of IDataObject
2782 STDMETHODIMP ScintillaWin::GetData(FORMATETC *pFEIn, STGMEDIUM *pSTM) {
2783 bool formatOK = (pFEIn->cfFormat == CF_TEXT) ||
2784 ((pFEIn->cfFormat == CF_UNICODETEXT) && IsUnicodeMode());
2785 if (!formatOK ||
2786 pFEIn->ptd != 0 ||
2787 (pFEIn->dwAspect & DVASPECT_CONTENT) == 0 ||
2788 pFEIn->lindex != -1 ||
2789 (pFEIn->tymed & TYMED_HGLOBAL) == 0
2791 //Platform::DebugPrintf("DOB GetData No %d %x %x fmt=%x\n", lenDrag, pFEIn, pSTM, pFEIn->cfFormat);
2792 return DATA_E_FORMATETC;
2794 pSTM->tymed = TYMED_HGLOBAL;
2795 //Platform::DebugPrintf("DOB GetData OK %d %x %x\n", lenDrag, pFEIn, pSTM);
2797 GlobalMemory text;
2798 if (pFEIn->cfFormat == CF_UNICODETEXT) {
2799 int uchars = UTF16Length(drag.Data(), static_cast<int>(drag.LengthWithTerminator()));
2800 text.Allocate(2 * uchars);
2801 if (text) {
2802 UTF16FromUTF8(drag.Data(), static_cast<int>(drag.LengthWithTerminator()),
2803 static_cast<wchar_t *>(text.ptr), uchars);
2805 } else {
2806 text.Allocate(drag.LengthWithTerminator());
2807 if (text) {
2808 memcpy(static_cast<char *>(text.ptr), drag.Data(), drag.LengthWithTerminator());
2811 pSTM->hGlobal = text ? text.Unlock() : 0;
2812 pSTM->pUnkForRelease = 0;
2813 return S_OK;
2816 bool ScintillaWin::Register(HINSTANCE hInstance_) {
2818 hInstance = hInstance_;
2819 bool result;
2821 // Register the Scintilla class
2822 if (IsNT()) {
2824 // Register Scintilla as a wide character window
2825 WNDCLASSEXW wndclass;
2826 wndclass.cbSize = sizeof(wndclass);
2827 wndclass.style = CS_GLOBALCLASS | CS_HREDRAW | CS_VREDRAW;
2828 wndclass.lpfnWndProc = ScintillaWin::SWndProc;
2829 wndclass.cbClsExtra = 0;
2830 wndclass.cbWndExtra = sizeof(ScintillaWin *);
2831 wndclass.hInstance = hInstance;
2832 wndclass.hIcon = NULL;
2833 wndclass.hCursor = NULL;
2834 wndclass.hbrBackground = NULL;
2835 wndclass.lpszMenuName = NULL;
2836 wndclass.lpszClassName = L"Scintilla";
2837 wndclass.hIconSm = 0;
2838 scintillaClassAtom = ::RegisterClassExW(&wndclass);
2839 result = 0 != scintillaClassAtom;
2840 } else {
2842 // Register Scintilla as a normal character window
2843 WNDCLASSEX wndclass;
2844 wndclass.cbSize = sizeof(wndclass);
2845 wndclass.style = CS_GLOBALCLASS | CS_HREDRAW | CS_VREDRAW;
2846 wndclass.lpfnWndProc = ScintillaWin::SWndProc;
2847 wndclass.cbClsExtra = 0;
2848 wndclass.cbWndExtra = sizeof(ScintillaWin *);
2849 wndclass.hInstance = hInstance;
2850 wndclass.hIcon = NULL;
2851 wndclass.hCursor = NULL;
2852 wndclass.hbrBackground = NULL;
2853 wndclass.lpszMenuName = NULL;
2854 wndclass.lpszClassName = scintillaClassName;
2855 wndclass.hIconSm = 0;
2856 scintillaClassAtom = ::RegisterClassEx(&wndclass);
2857 result = 0 != scintillaClassAtom;
2860 if (result) {
2861 // Register the CallTip class
2862 WNDCLASSEX wndclassc;
2863 wndclassc.cbSize = sizeof(wndclassc);
2864 wndclassc.style = CS_GLOBALCLASS | CS_HREDRAW | CS_VREDRAW;
2865 wndclassc.cbClsExtra = 0;
2866 wndclassc.cbWndExtra = sizeof(ScintillaWin *);
2867 wndclassc.hInstance = hInstance;
2868 wndclassc.hIcon = NULL;
2869 wndclassc.hbrBackground = NULL;
2870 wndclassc.lpszMenuName = NULL;
2871 wndclassc.lpfnWndProc = ScintillaWin::CTWndProc;
2872 wndclassc.hCursor = ::LoadCursor(NULL, IDC_ARROW);
2873 wndclassc.lpszClassName = callClassName;
2874 wndclassc.hIconSm = 0;
2876 callClassAtom = ::RegisterClassEx(&wndclassc);
2877 result = 0 != callClassAtom;
2880 return result;
2883 bool ScintillaWin::Unregister() {
2884 bool result = true;
2885 if (0 != scintillaClassAtom) {
2886 if (::UnregisterClass(MAKEINTATOM(scintillaClassAtom), hInstance) == 0) {
2887 result = false;
2889 scintillaClassAtom = 0;
2891 if (0 != callClassAtom) {
2892 if (::UnregisterClass(MAKEINTATOM(callClassAtom), hInstance) == 0) {
2893 result = false;
2895 callClassAtom = 0;
2897 return result;
2900 bool ScintillaWin::HasCaretSizeChanged() const {
2901 if (
2902 ( (0 != vs.caretWidth) && (sysCaretWidth != vs.caretWidth) )
2903 || ((0 != vs.lineHeight) && (sysCaretHeight != vs.lineHeight))
2905 return true;
2907 return false;
2910 BOOL ScintillaWin::CreateSystemCaret() {
2911 sysCaretWidth = vs.caretWidth;
2912 if (0 == sysCaretWidth) {
2913 sysCaretWidth = 1;
2915 sysCaretHeight = vs.lineHeight;
2916 int bitmapSize = (((sysCaretWidth + 15) & ~15) >> 3) *
2917 sysCaretHeight;
2918 std::vector<char> bits(bitmapSize);
2919 sysCaretBitmap = ::CreateBitmap(sysCaretWidth, sysCaretHeight, 1,
2920 1, reinterpret_cast<BYTE *>(&bits[0]));
2921 BOOL retval = ::CreateCaret(
2922 MainHWND(), sysCaretBitmap,
2923 sysCaretWidth, sysCaretHeight);
2924 ::ShowCaret(MainHWND());
2925 return retval;
2928 BOOL ScintillaWin::DestroySystemCaret() {
2929 ::HideCaret(MainHWND());
2930 BOOL retval = ::DestroyCaret();
2931 if (sysCaretBitmap) {
2932 ::DeleteObject(sysCaretBitmap);
2933 sysCaretBitmap = 0;
2935 return retval;
2938 sptr_t PASCAL ScintillaWin::CTWndProc(
2939 HWND hWnd, UINT iMessage, WPARAM wParam, sptr_t lParam) {
2940 // Find C++ object associated with window.
2941 ScintillaWin *sciThis = reinterpret_cast<ScintillaWin *>(PointerFromWindow(hWnd));
2942 try {
2943 // ctp will be zero if WM_CREATE not seen yet
2944 if (sciThis == 0) {
2945 if (iMessage == WM_CREATE) {
2946 // Associate CallTip object with window
2947 CREATESTRUCT *pCreate = reinterpret_cast<CREATESTRUCT *>(lParam);
2948 SetWindowPointer(hWnd, pCreate->lpCreateParams);
2949 return 0;
2950 } else {
2951 return ::DefWindowProc(hWnd, iMessage, wParam, lParam);
2953 } else {
2954 if (iMessage == WM_NCDESTROY) {
2955 ::SetWindowLong(hWnd, 0, 0);
2956 return ::DefWindowProc(hWnd, iMessage, wParam, lParam);
2957 } else if (iMessage == WM_PAINT) {
2958 PAINTSTRUCT ps;
2959 ::BeginPaint(hWnd, &ps);
2960 Surface *surfaceWindow = Surface::Allocate(sciThis->technology);
2961 if (surfaceWindow) {
2962 #if defined(USE_D2D)
2963 ID2D1HwndRenderTarget *pCTRenderTarget = 0;
2964 #endif
2965 RECT rc;
2966 GetClientRect(hWnd, &rc);
2967 // Create a Direct2D render target.
2968 if (sciThis->technology == SC_TECHNOLOGY_DEFAULT) {
2969 surfaceWindow->Init(ps.hdc, hWnd);
2970 } else {
2971 #if defined(USE_D2D)
2972 D2D1_HWND_RENDER_TARGET_PROPERTIES dhrtp;
2973 dhrtp.hwnd = hWnd;
2974 dhrtp.pixelSize = D2D1::SizeU(rc.right - rc.left, rc.bottom - rc.top);
2975 dhrtp.presentOptions = (sciThis->technology == SC_TECHNOLOGY_DIRECTWRITERETAIN) ?
2976 D2D1_PRESENT_OPTIONS_RETAIN_CONTENTS : D2D1_PRESENT_OPTIONS_NONE;
2978 D2D1_RENDER_TARGET_PROPERTIES drtp;
2979 drtp.type = D2D1_RENDER_TARGET_TYPE_DEFAULT;
2980 drtp.pixelFormat.format = DXGI_FORMAT_UNKNOWN;
2981 drtp.pixelFormat.alphaMode = D2D1_ALPHA_MODE_UNKNOWN;
2982 drtp.dpiX = 96.0;
2983 drtp.dpiY = 96.0;
2984 drtp.usage = D2D1_RENDER_TARGET_USAGE_NONE;
2985 drtp.minLevel = D2D1_FEATURE_LEVEL_DEFAULT;
2987 if (!SUCCEEDED(pD2DFactory->CreateHwndRenderTarget(drtp, dhrtp, &pCTRenderTarget))) {
2988 surfaceWindow->Release();
2989 delete surfaceWindow;
2990 ::EndPaint(hWnd, &ps);
2991 return 0;
2993 surfaceWindow->Init(pCTRenderTarget, hWnd);
2994 pCTRenderTarget->BeginDraw();
2995 #endif
2997 surfaceWindow->SetUnicodeMode(SC_CP_UTF8 == sciThis->ct.codePage);
2998 surfaceWindow->SetDBCSMode(sciThis->ct.codePage);
2999 sciThis->ct.PaintCT(surfaceWindow);
3000 #if defined(USE_D2D)
3001 if (pCTRenderTarget)
3002 pCTRenderTarget->EndDraw();
3003 #endif
3004 surfaceWindow->Release();
3005 delete surfaceWindow;
3006 #if defined(USE_D2D)
3007 if (pCTRenderTarget)
3008 pCTRenderTarget->Release();
3009 #endif
3011 ::EndPaint(hWnd, &ps);
3012 return 0;
3013 } else if ((iMessage == WM_NCLBUTTONDOWN) || (iMessage == WM_NCLBUTTONDBLCLK)) {
3014 POINT pt;
3015 pt.x = static_cast<short>(LOWORD(lParam));
3016 pt.y = static_cast<short>(HIWORD(lParam));
3017 ScreenToClient(hWnd, &pt);
3018 sciThis->ct.MouseClick(PointFromPOINT(pt));
3019 sciThis->CallTipClick();
3020 return 0;
3021 } else if (iMessage == WM_LBUTTONDOWN) {
3022 // This does not fire due to the hit test code
3023 sciThis->ct.MouseClick(Point::FromLong(static_cast<long>(lParam)));
3024 sciThis->CallTipClick();
3025 return 0;
3026 } else if (iMessage == WM_SETCURSOR) {
3027 ::SetCursor(::LoadCursor(NULL, IDC_ARROW));
3028 return 0;
3029 } else if (iMessage == WM_NCHITTEST) {
3030 return HTCAPTION;
3031 } else {
3032 return ::DefWindowProc(hWnd, iMessage, wParam, lParam);
3035 } catch (...) {
3036 sciThis->errorStatus = SC_STATUS_FAILURE;
3038 return ::DefWindowProc(hWnd, iMessage, wParam, lParam);
3041 sptr_t ScintillaWin::DirectFunction(
3042 sptr_t ptr, UINT iMessage, uptr_t wParam, sptr_t lParam) {
3043 PLATFORM_ASSERT(::GetCurrentThreadId() == ::GetWindowThreadProcessId(reinterpret_cast<ScintillaWin *>(ptr)->MainHWND(), NULL));
3044 return reinterpret_cast<ScintillaWin *>(ptr)->WndProc(iMessage, wParam, lParam);
3047 extern "C"
3048 #ifndef STATIC_BUILD
3049 __declspec(dllexport)
3050 #endif
3051 sptr_t __stdcall Scintilla_DirectFunction(
3052 ScintillaWin *sci, UINT iMessage, uptr_t wParam, sptr_t lParam) {
3053 return sci->WndProc(iMessage, wParam, lParam);
3056 sptr_t PASCAL ScintillaWin::SWndProc(
3057 HWND hWnd, UINT iMessage, WPARAM wParam, sptr_t lParam) {
3058 //Platform::DebugPrintf("S W:%x M:%x WP:%x L:%x\n", hWnd, iMessage, wParam, lParam);
3060 // Find C++ object associated with window.
3061 ScintillaWin *sci = reinterpret_cast<ScintillaWin *>(PointerFromWindow(hWnd));
3062 // sci will be zero if WM_CREATE not seen yet
3063 if (sci == 0) {
3064 try {
3065 if (iMessage == WM_CREATE) {
3066 // Create C++ object associated with window
3067 sci = new ScintillaWin(hWnd);
3068 SetWindowPointer(hWnd, sci);
3069 return sci->WndProc(iMessage, wParam, lParam);
3071 } catch (...) {
3073 return ::DefWindowProc(hWnd, iMessage, wParam, lParam);
3074 } else {
3075 if (iMessage == WM_NCDESTROY) {
3076 try {
3077 sci->Finalise();
3078 delete sci;
3079 } catch (...) {
3081 ::SetWindowLong(hWnd, 0, 0);
3082 return ::DefWindowProc(hWnd, iMessage, wParam, lParam);
3083 } else {
3084 return sci->WndProc(iMessage, wParam, lParam);
3089 // This function is externally visible so it can be called from container when building statically.
3090 // Must be called once only.
3091 int Scintilla_RegisterClasses(void *hInstance) {
3092 Platform_Initialise(hInstance);
3093 bool result = ScintillaWin::Register(reinterpret_cast<HINSTANCE>(hInstance));
3094 #ifdef SCI_LEXER
3095 Scintilla_LinkLexers();
3096 #endif
3097 return result;
3100 static int ResourcesRelease(bool fromDllMain) {
3101 bool result = ScintillaWin::Unregister();
3102 if (commctrl32) {
3103 FreeLibrary(commctrl32);
3104 commctrl32 = NULL;
3106 Platform_Finalise(fromDllMain);
3107 return result;
3110 // This function is externally visible so it can be called from container when building statically.
3111 int Scintilla_ReleaseResources() {
3112 return ResourcesRelease(false);
3115 #ifndef STATIC_BUILD
3116 extern "C" int APIENTRY DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpvReserved) {
3117 //Platform::DebugPrintf("Scintilla::DllMain %d %d\n", hInstance, dwReason);
3118 if (dwReason == DLL_PROCESS_ATTACH) {
3119 if (!Scintilla_RegisterClasses(hInstance))
3120 return FALSE;
3121 } else if (dwReason == DLL_PROCESS_DETACH) {
3122 if (lpvReserved == NULL) {
3123 ResourcesRelease(true);
3126 return TRUE;
3128 #endif