Update Scintilla to version 3.5.2
[TortoiseGit.git] / ext / scintilla / win32 / ScintillaWin.cxx
blob4eec5b44a796b5def436b91769809ec450341a67
1 // Scintilla source code edit control
2 /** @file ScintillaWin.cxx
3 ** Windows specific subclass of ScintillaBase.
4 **/
5 // Copyright 1998-2003 by Neil Hodgson <neilh@scintilla.org>
6 // The License.txt file describes the conditions under which this software may be distributed.
8 #include <stdlib.h>
9 #include <string.h>
10 #include <stdio.h>
11 #include <assert.h>
12 #include <ctype.h>
13 #include <limits.h>
15 #include <stdexcept>
16 #include <new>
17 #include <string>
18 #include <vector>
19 #include <map>
20 #include <algorithm>
22 #undef _WIN32_WINNT
23 #define _WIN32_WINNT 0x0500
24 #undef WINVER
25 #define WINVER 0x0500
26 #include <windows.h>
27 #include <commctrl.h>
28 #include <richedit.h>
29 #include <windowsx.h>
30 #include <zmouse.h>
31 #include <ole2.h>
33 #if defined(NTDDI_WIN7) && !defined(DISABLE_D2D)
34 #define USE_D2D 1
35 #endif
37 #if defined(USE_D2D)
38 #include <d2d1.h>
39 #include <dwrite.h>
40 #endif
42 #include "Platform.h"
44 #include "ILexer.h"
45 #include "Scintilla.h"
47 #ifdef SCI_LEXER
48 #include "SciLexer.h"
49 #endif
50 #include "StringCopy.h"
51 #ifdef SCI_LEXER
52 #include "LexerModule.h"
53 #endif
54 #include "SplitVector.h"
55 #include "Partitioning.h"
56 #include "RunStyles.h"
57 #include "ContractionState.h"
58 #include "CellBuffer.h"
59 #include "CallTip.h"
60 #include "KeyMap.h"
61 #include "Indicator.h"
62 #include "XPM.h"
63 #include "LineMarker.h"
64 #include "Style.h"
65 #include "ViewStyle.h"
66 #include "CharClassify.h"
67 #include "Decoration.h"
68 #include "CaseFolder.h"
69 #include "Document.h"
70 #include "CaseConvert.h"
71 #include "UniConversion.h"
72 #include "Selection.h"
73 #include "PositionCache.h"
74 #include "EditModel.h"
75 #include "MarginView.h"
76 #include "EditView.h"
77 #include "Editor.h"
79 #include "AutoComplete.h"
80 #include "ScintillaBase.h"
82 #ifdef SCI_LEXER
83 #include "ExternalLexer.h"
84 #endif
86 #include "PlatWin.h"
88 #ifndef SPI_GETWHEELSCROLLLINES
89 #define SPI_GETWHEELSCROLLLINES 104
90 #endif
92 #ifndef WM_UNICHAR
93 #define WM_UNICHAR 0x0109
94 #endif
96 #ifndef UNICODE_NOCHAR
97 #define UNICODE_NOCHAR 0xFFFF
98 #endif
100 #ifndef MK_ALT
101 #define MK_ALT 32
102 #endif
104 #define SC_WIN_IDLE 5001
106 typedef BOOL (WINAPI *TrackMouseEventSig)(LPTRACKMOUSEEVENT);
107 typedef UINT_PTR (WINAPI *SetCoalescableTimerSig)(HWND hwnd, UINT_PTR nIDEvent,
108 UINT uElapse, TIMERPROC lpTimerFunc, ULONG uToleranceDelay);
110 // GCC has trouble with the standard COM ABI so do it the old C way with explicit vtables.
112 const TCHAR scintillaClassName[] = TEXT("Scintilla");
113 const TCHAR callClassName[] = TEXT("CallTip");
115 #ifdef SCI_NAMESPACE
116 using namespace Scintilla;
117 #endif
119 static void *PointerFromWindow(HWND hWnd) {
120 return reinterpret_cast<void *>(::GetWindowLongPtr(hWnd, 0));
123 static void SetWindowPointer(HWND hWnd, void *ptr) {
124 ::SetWindowLongPtr(hWnd, 0, reinterpret_cast<LONG_PTR>(ptr));
127 static void SetWindowID(HWND hWnd, int identifier) {
128 ::SetWindowLongPtr(hWnd, GWLP_ID, identifier);
131 static Point PointFromPOINT(POINT pt) {
132 return Point::FromInts(pt.x, pt.y);
135 class ScintillaWin; // Forward declaration for COM interface subobjects
137 typedef void VFunction(void);
139 static HMODULE commctrl32 = 0;
143 class FormatEnumerator {
144 public:
145 VFunction **vtbl;
146 int ref;
147 unsigned int pos;
148 std::vector<CLIPFORMAT> formats;
149 FormatEnumerator(int pos_, CLIPFORMAT formats_[], size_t formatsLen_);
154 class DropSource {
155 public:
156 VFunction **vtbl;
157 ScintillaWin *sci;
158 DropSource();
163 class DataObject {
164 public:
165 VFunction **vtbl;
166 ScintillaWin *sci;
167 DataObject();
172 class DropTarget {
173 public:
174 VFunction **vtbl;
175 ScintillaWin *sci;
176 DropTarget();
181 class ScintillaWin :
182 public ScintillaBase {
184 bool lastKeyDownConsumed;
186 bool capturedMouse;
187 bool trackedMouseLeave;
188 TrackMouseEventSig TrackMouseEventFn;
189 SetCoalescableTimerSig SetCoalescableTimerFn;
191 unsigned int linesPerScroll; ///< Intellimouse support
192 int wheelDelta; ///< Wheel delta from roll
194 HRGN hRgnUpdate;
196 bool hasOKText;
198 CLIPFORMAT cfColumnSelect;
199 CLIPFORMAT cfBorlandIDEBlockType;
200 CLIPFORMAT cfLineSelect;
201 CLIPFORMAT cfVSLineTag;
203 HRESULT hrOle;
204 DropSource ds;
205 DataObject dob;
206 DropTarget dt;
208 static HINSTANCE hInstance;
209 static ATOM scintillaClassAtom;
210 static ATOM callClassAtom;
212 #if defined(USE_D2D)
213 ID2D1RenderTarget *pRenderTarget;
214 bool renderTargetValid;
215 #endif
217 explicit ScintillaWin(HWND hwnd);
218 ScintillaWin(const ScintillaWin &);
219 virtual ~ScintillaWin();
220 ScintillaWin &operator=(const ScintillaWin &);
222 virtual void Initialise();
223 virtual void Finalise();
224 #if defined(USE_D2D)
225 void EnsureRenderTarget(HDC hdc);
226 void DropRenderTarget();
227 #endif
228 HWND MainHWND();
230 static sptr_t DirectFunction(
231 sptr_t ptr, UINT iMessage, uptr_t wParam, sptr_t lParam);
232 static sptr_t PASCAL SWndProc(
233 HWND hWnd, UINT iMessage, WPARAM wParam, sptr_t lParam);
234 static sptr_t PASCAL CTWndProc(
235 HWND hWnd, UINT iMessage, WPARAM wParam, sptr_t lParam);
237 enum { invalidTimerID, standardTimerID, idleTimerID, fineTimerStart };
239 virtual bool DragThreshold(Point ptStart, Point ptNow);
240 virtual void StartDrag();
241 sptr_t WndPaint(uptr_t wParam);
242 sptr_t HandleComposition(uptr_t wParam, sptr_t lParam);
243 static bool KoreanIME();
244 sptr_t HandleCompositionKoreanIME(uptr_t wParam, sptr_t lParam);
245 UINT CodePageOfDocument();
246 virtual bool ValidCodePage(int codePage) const;
247 virtual sptr_t DefWndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam);
248 virtual bool SetIdle(bool on);
249 UINT_PTR timers[tickDwell+1];
250 virtual bool FineTickerAvailable();
251 virtual bool FineTickerRunning(TickReason reason);
252 virtual void FineTickerStart(TickReason reason, int millis, int tolerance);
253 virtual void FineTickerCancel(TickReason reason);
254 virtual void SetMouseCapture(bool on);
255 virtual bool HaveMouseCapture();
256 virtual void SetTrackMouseLeaveEvent(bool on);
257 virtual bool PaintContains(PRectangle rc);
258 virtual void ScrollText(int linesToMove);
259 virtual void UpdateSystemCaret();
260 virtual void SetVerticalScrollPos();
261 virtual void SetHorizontalScrollPos();
262 virtual bool ModifyScrollBars(int nMax, int nPage);
263 virtual void NotifyChange();
264 virtual void NotifyFocus(bool focus);
265 virtual void SetCtrlID(int identifier);
266 virtual int GetCtrlID();
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(HDC hdc) {
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_RENDER_TARGET_PROPERTIES drtp;
459 drtp.type = D2D1_RENDER_TARGET_TYPE_DEFAULT;
460 drtp.pixelFormat.format = DXGI_FORMAT_UNKNOWN;
461 drtp.pixelFormat.alphaMode = D2D1_ALPHA_MODE_UNKNOWN;
462 drtp.dpiX = 96.0;
463 drtp.dpiY = 96.0;
464 drtp.usage = D2D1_RENDER_TARGET_USAGE_NONE;
465 drtp.minLevel = D2D1_FEATURE_LEVEL_DEFAULT;
467 if (technology == SC_TECHNOLOGY_DIRECTWRITEDC) {
468 // Explicit pixel format needed.
469 drtp.pixelFormat = D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM,
470 D2D1_ALPHA_MODE_IGNORE);
472 ID2D1DCRenderTarget *pDCRT = NULL;
473 HRESULT hr = pD2DFactory->CreateDCRenderTarget(&drtp, &pDCRT);
474 if (FAILED(hr)) {
475 Platform::DebugPrintf("Failed CreateDCRenderTarget 0x%x\n", hr);
477 pRenderTarget = pDCRT;
479 } else {
480 D2D1_HWND_RENDER_TARGET_PROPERTIES dhrtp;
481 dhrtp.hwnd = hw;
482 dhrtp.pixelSize = size;
483 dhrtp.presentOptions = (technology == SC_TECHNOLOGY_DIRECTWRITERETAIN) ?
484 D2D1_PRESENT_OPTIONS_RETAIN_CONTENTS : D2D1_PRESENT_OPTIONS_NONE;
486 ID2D1HwndRenderTarget *pHwndRenderTarget = NULL;
487 HRESULT hr = pD2DFactory->CreateHwndRenderTarget(drtp, dhrtp, &pHwndRenderTarget);
488 if (FAILED(hr)) {
489 Platform::DebugPrintf("Failed CreateHwndRenderTarget 0x%x\n", hr);
491 pRenderTarget = pHwndRenderTarget;
493 #else
494 pD2DFactory->CreateHwndRenderTarget(
495 D2D1::RenderTargetProperties(
496 D2D1_RENDER_TARGET_TYPE_DEFAULT ,
497 D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_PREMULTIPLIED),
498 96.0f, 96.0f, D2D1_RENDER_TARGET_USAGE_NONE, D2D1_FEATURE_LEVEL_DEFAULT),
499 D2D1::HwndRenderTargetProperties(hw, size),
500 &pRenderTarget);
501 #endif
502 // Pixmaps were created to be compatible with previous render target so
503 // need to be recreated.
504 DropGraphics(false);
507 if (technology == SC_TECHNOLOGY_DIRECTWRITEDC) {
508 RECT rcWindow;
509 GetClientRect(MainHWND(), &rcWindow);
510 HRESULT hr = static_cast<ID2D1DCRenderTarget*>(pRenderTarget)->BindDC(hdc, &rcWindow);
511 if (FAILED(hr)) {
512 Platform::DebugPrintf("BindDC failed 0x%x\n", hr);
513 DropRenderTarget();
518 void ScintillaWin::DropRenderTarget() {
519 if (pRenderTarget) {
520 pRenderTarget->Release();
521 pRenderTarget = 0;
525 #endif
527 HWND ScintillaWin::MainHWND() {
528 return reinterpret_cast<HWND>(wMain.GetID());
531 bool ScintillaWin::DragThreshold(Point ptStart, Point ptNow) {
532 int xMove = static_cast<int>(abs(ptStart.x - ptNow.x));
533 int yMove = static_cast<int>(abs(ptStart.y - ptNow.y));
534 return (xMove > ::GetSystemMetrics(SM_CXDRAG)) ||
535 (yMove > ::GetSystemMetrics(SM_CYDRAG));
538 void ScintillaWin::StartDrag() {
539 inDragDrop = ddDragging;
540 DWORD dwEffect = 0;
541 dropWentOutside = true;
542 IDataObject *pDataObject = reinterpret_cast<IDataObject *>(&dob);
543 IDropSource *pDropSource = reinterpret_cast<IDropSource *>(&ds);
544 //Platform::DebugPrintf("About to DoDragDrop %x %x\n", pDataObject, pDropSource);
545 HRESULT hr = ::DoDragDrop(
546 pDataObject,
547 pDropSource,
548 DROPEFFECT_COPY | DROPEFFECT_MOVE, &dwEffect);
549 //Platform::DebugPrintf("DoDragDrop = %x\n", hr);
550 if (SUCCEEDED(hr)) {
551 if ((hr == DRAGDROP_S_DROP) && (dwEffect == DROPEFFECT_MOVE) && dropWentOutside) {
552 // Remove dragged out text
553 ClearSelection();
556 inDragDrop = ddNone;
557 SetDragPosition(SelectionPosition(invalidPosition));
560 // Avoid warnings everywhere for old style casts by concentrating them here
561 static WORD LoWord(uptr_t l) {
562 return LOWORD(l);
565 static WORD HiWord(uptr_t l) {
566 return HIWORD(l);
569 static int InputCodePage() {
570 HKL inputLocale = ::GetKeyboardLayout(0);
571 LANGID inputLang = LOWORD(inputLocale);
572 char sCodePage[10];
573 int res = ::GetLocaleInfoA(MAKELCID(inputLang, SORT_DEFAULT),
574 LOCALE_IDEFAULTANSICODEPAGE, sCodePage, sizeof(sCodePage));
575 if (!res)
576 return 0;
577 return atoi(sCodePage);
580 /** Map the key codes to their equivalent SCK_ form. */
581 static int KeyTranslate(int keyIn) {
582 //PLATFORM_ASSERT(!keyIn);
583 switch (keyIn) {
584 case VK_DOWN: return SCK_DOWN;
585 case VK_UP: return SCK_UP;
586 case VK_LEFT: return SCK_LEFT;
587 case VK_RIGHT: return SCK_RIGHT;
588 case VK_HOME: return SCK_HOME;
589 case VK_END: return SCK_END;
590 case VK_PRIOR: return SCK_PRIOR;
591 case VK_NEXT: return SCK_NEXT;
592 case VK_DELETE: return SCK_DELETE;
593 case VK_INSERT: return SCK_INSERT;
594 case VK_ESCAPE: return SCK_ESCAPE;
595 case VK_BACK: return SCK_BACK;
596 case VK_TAB: return SCK_TAB;
597 case VK_RETURN: return SCK_RETURN;
598 case VK_ADD: return SCK_ADD;
599 case VK_SUBTRACT: return SCK_SUBTRACT;
600 case VK_DIVIDE: return SCK_DIVIDE;
601 case VK_LWIN: return SCK_WIN;
602 case VK_RWIN: return SCK_RWIN;
603 case VK_APPS: return SCK_MENU;
604 case VK_OEM_2: return '/';
605 case VK_OEM_3: return '`';
606 case VK_OEM_4: return '[';
607 case VK_OEM_5: return '\\';
608 case VK_OEM_6: return ']';
609 default: return keyIn;
613 static bool BoundsContains(PRectangle rcBounds, HRGN hRgnBounds, PRectangle rcCheck) {
614 bool contains = true;
615 if (!rcCheck.Empty()) {
616 if (!rcBounds.Contains(rcCheck)) {
617 contains = false;
618 } else if (hRgnBounds) {
619 // In bounding rectangle so check more accurately using region
620 HRGN hRgnCheck = ::CreateRectRgn(static_cast<int>(rcCheck.left), static_cast<int>(rcCheck.top),
621 static_cast<int>(rcCheck.right), static_cast<int>(rcCheck.bottom));
622 if (hRgnCheck) {
623 HRGN hRgnDifference = ::CreateRectRgn(0, 0, 0, 0);
624 if (hRgnDifference) {
625 int combination = ::CombineRgn(hRgnDifference, hRgnCheck, hRgnBounds, RGN_DIFF);
626 if (combination != NULLREGION) {
627 contains = false;
629 ::DeleteRgn(hRgnDifference);
631 ::DeleteRgn(hRgnCheck);
635 return contains;
638 LRESULT ScintillaWin::WndPaint(uptr_t wParam) {
639 //ElapsedTime et;
641 // Redirect assertions to debug output and save current state
642 bool assertsPopup = Platform::ShowAssertionPopUps(false);
643 paintState = painting;
644 PAINTSTRUCT ps;
645 PAINTSTRUCT *pps;
647 bool IsOcxCtrl = (wParam != 0); // if wParam != 0, it contains
648 // a PAINSTRUCT* from the OCX
649 // Removed since this interferes with reporting other assertions as it occurs repeatedly
650 //PLATFORM_ASSERT(hRgnUpdate == NULL);
651 hRgnUpdate = ::CreateRectRgn(0, 0, 0, 0);
652 if (IsOcxCtrl) {
653 pps = reinterpret_cast<PAINTSTRUCT*>(wParam);
654 } else {
655 ::GetUpdateRgn(MainHWND(), hRgnUpdate, FALSE);
656 pps = &ps;
657 ::BeginPaint(MainHWND(), pps);
659 rcPaint = PRectangle::FromInts(pps->rcPaint.left, pps->rcPaint.top, pps->rcPaint.right, pps->rcPaint.bottom);
660 PRectangle rcClient = GetClientRectangle();
661 paintingAllText = BoundsContains(rcPaint, hRgnUpdate, rcClient);
662 if (technology == SC_TECHNOLOGY_DEFAULT) {
663 AutoSurface surfaceWindow(pps->hdc, this);
664 if (surfaceWindow) {
665 Paint(surfaceWindow, rcPaint);
666 surfaceWindow->Release();
668 } else {
669 #if defined(USE_D2D)
670 EnsureRenderTarget(pps->hdc);
671 AutoSurface surfaceWindow(pRenderTarget, this);
672 if (surfaceWindow) {
673 pRenderTarget->BeginDraw();
674 Paint(surfaceWindow, rcPaint);
675 surfaceWindow->Release();
676 HRESULT hr = pRenderTarget->EndDraw();
677 if (hr == D2DERR_RECREATE_TARGET) {
678 DropRenderTarget();
679 paintState = paintAbandoned;
682 #endif
684 if (hRgnUpdate) {
685 ::DeleteRgn(hRgnUpdate);
686 hRgnUpdate = 0;
689 if (!IsOcxCtrl)
690 ::EndPaint(MainHWND(), pps);
691 if (paintState == paintAbandoned) {
692 // Painting area was insufficient to cover new styling or brace highlight positions
693 if (IsOcxCtrl) {
694 FullPaintDC(pps->hdc);
695 } else {
696 FullPaint();
699 paintState = notPainting;
701 // Restore debug output state
702 Platform::ShowAssertionPopUps(assertsPopup);
704 //Platform::DebugPrintf("Paint took %g\n", et.Duration());
705 return 0l;
708 sptr_t ScintillaWin::HandleComposition(uptr_t wParam, sptr_t lParam) {
709 if (lParam & GCS_RESULTSTR) {
710 HIMC hIMC = ::ImmGetContext(MainHWND());
711 if (hIMC) {
712 wchar_t wcs[maxLenInputIME];
713 LONG bytes = ::ImmGetCompositionStringW(hIMC,
714 GCS_RESULTSTR, wcs, (maxLenInputIME-1)*2);
715 int wides = bytes / 2;
716 if (IsUnicodeMode()) {
717 char utfval[maxLenInputIME * 3];
718 unsigned int len = UTF8Length(wcs, wides);
719 UTF8FromUTF16(wcs, wides, utfval, len);
720 utfval[len] = '\0';
721 AddCharUTF(utfval, len);
722 } else {
723 char dbcsval[maxLenInputIME * 2];
724 int size = ::WideCharToMultiByte(InputCodePage(),
725 0, wcs, wides, dbcsval, sizeof(dbcsval) - 1, 0, 0);
726 for (int i=0; i<size; i++) {
727 AddChar(dbcsval[i]);
730 // Set new position after converted
731 Point pos = PointMainCaret();
732 COMPOSITIONFORM CompForm;
733 CompForm.dwStyle = CFS_POINT;
734 CompForm.ptCurrentPos.x = static_cast<int>(pos.x);
735 CompForm.ptCurrentPos.y = static_cast<int>(pos.y);
736 ::ImmSetCompositionWindow(hIMC, &CompForm);
737 ::ImmReleaseContext(MainHWND(), hIMC);
739 return 0;
741 return ::DefWindowProc(MainHWND(), WM_IME_COMPOSITION, wParam, lParam);
744 bool ScintillaWin::KoreanIME() {
745 const int codePage = InputCodePage();
746 return codePage == 949 || codePage == 1361;
749 sptr_t ScintillaWin::HandleCompositionKoreanIME(uptr_t, sptr_t lParam) {
751 // copy & paste by johnsonj
752 // Great thanks to
753 // jiniya from http://www.jiniya.net/tt/494 for DBCS input with AddCharUTF()
754 // BLUEnLIVE from http://zockr.tistory.com/1118 for UNDO and inOverstrike
756 HIMC hIMC = ::ImmGetContext(MainHWND());
757 if (!hIMC) {
758 return 0;
761 wchar_t wcs[maxLenInputIME];
762 int wides = 0;
763 bool compstrExist = false;
765 if (pdoc->TentativeActive()) {
766 pdoc->TentativeUndo();
767 } else {
768 // No tentative undo means start of this composition so
769 // fill in any virtual spaces.
770 bool tmpOverstrike = inOverstrike;
771 inOverstrike = false; // not allow to be deleted twice.
772 AddCharUTF("", 0);
773 inOverstrike = tmpOverstrike;
776 view.imeCaretBlockOverride = false;
777 if (lParam & GCS_COMPSTR) {
778 long bytes = ::ImmGetCompositionStringW
779 (hIMC, GCS_COMPSTR, wcs, maxLenInputIME);
780 wides = bytes / 2;
781 compstrExist = (wides != 0);
782 } else if (lParam & GCS_RESULTSTR) {
783 long bytes = ::ImmGetCompositionStringW
784 (hIMC, GCS_RESULTSTR, wcs, maxLenInputIME);
785 wides = bytes / 2;
786 compstrExist = (wides == 0);
789 if (wides >= 1) {
791 char hanval[maxLenInputIME];
792 unsigned int hanlen = 0;
794 if (IsUnicodeMode()) {
795 hanlen = UTF8Length(wcs, wides);
796 UTF8FromUTF16(wcs, wides, hanval, hanlen);
797 hanval[hanlen] = '\0';
798 } else {
799 hanlen = ::WideCharToMultiByte(InputCodePage(), 0,
800 wcs, wides, hanval, sizeof(hanval) - 1, 0, 0);
801 hanval[hanlen] = '\0';
804 if (compstrExist) {
805 view.imeCaretBlockOverride = true;
807 bool tmpRecordingMacro = recordingMacro;
808 recordingMacro = false;
809 pdoc->TentativeStart();
810 AddCharUTF(hanval, hanlen);
811 recordingMacro = tmpRecordingMacro;
813 for (size_t r = 0; r < sel.Count(); r++) { // for block caret
814 int positionInsert = sel.Range(r).Start().Position();
815 sel.Range(r).caret.SetPosition(positionInsert - hanlen);
816 sel.Range(r).anchor.SetPosition(positionInsert - hanlen);
818 } else {
819 AddCharUTF(hanval, hanlen);
823 // set the candidate window position for HANJA while composing.
824 Point pos = PointMainCaret();
825 CANDIDATEFORM CandForm;
826 CandForm.dwIndex = 0;
827 CandForm.dwStyle = CFS_CANDIDATEPOS;
828 CandForm.ptCurrentPos.x = static_cast<int>(pos.x);
829 CandForm.ptCurrentPos.y = static_cast<int>(pos.y + vs.lineHeight);
830 ::ImmSetCandidateWindow(hIMC, &CandForm);
832 ShowCaretAtCurrentPosition();
833 ::ImmReleaseContext(MainHWND(), hIMC);
834 return 0;
837 // Translate message IDs from WM_* and EM_* to SCI_* so can partly emulate Windows Edit control
838 static unsigned int SciMessageFromEM(unsigned int iMessage) {
839 switch (iMessage) {
840 case EM_CANPASTE: return SCI_CANPASTE;
841 case EM_CANUNDO: return SCI_CANUNDO;
842 case EM_EMPTYUNDOBUFFER: return SCI_EMPTYUNDOBUFFER;
843 case EM_FINDTEXTEX: return SCI_FINDTEXT;
844 case EM_FORMATRANGE: return SCI_FORMATRANGE;
845 case EM_GETFIRSTVISIBLELINE: return SCI_GETFIRSTVISIBLELINE;
846 case EM_GETLINECOUNT: return SCI_GETLINECOUNT;
847 case EM_GETSELTEXT: return SCI_GETSELTEXT;
848 case EM_GETTEXTRANGE: return SCI_GETTEXTRANGE;
849 case EM_HIDESELECTION: return SCI_HIDESELECTION;
850 case EM_LINEINDEX: return SCI_POSITIONFROMLINE;
851 case EM_LINESCROLL: return SCI_LINESCROLL;
852 case EM_REPLACESEL: return SCI_REPLACESEL;
853 case EM_SCROLLCARET: return SCI_SCROLLCARET;
854 case EM_SETREADONLY: return SCI_SETREADONLY;
855 case WM_CLEAR: return SCI_CLEAR;
856 case WM_COPY: return SCI_COPY;
857 case WM_CUT: return SCI_CUT;
858 case WM_SETTEXT: return SCI_SETTEXT;
859 case WM_PASTE: return SCI_PASTE;
860 case WM_UNDO: return SCI_UNDO;
862 return iMessage;
865 UINT CodePageFromCharSet(DWORD characterSet, UINT documentCodePage) {
866 if (documentCodePage == SC_CP_UTF8) {
867 return SC_CP_UTF8;
869 switch (characterSet) {
870 case SC_CHARSET_ANSI: return 1252;
871 case SC_CHARSET_DEFAULT: return documentCodePage;
872 case SC_CHARSET_BALTIC: return 1257;
873 case SC_CHARSET_CHINESEBIG5: return 950;
874 case SC_CHARSET_EASTEUROPE: return 1250;
875 case SC_CHARSET_GB2312: return 936;
876 case SC_CHARSET_GREEK: return 1253;
877 case SC_CHARSET_HANGUL: return 949;
878 case SC_CHARSET_MAC: return 10000;
879 case SC_CHARSET_OEM: return 437;
880 case SC_CHARSET_RUSSIAN: return 1251;
881 case SC_CHARSET_SHIFTJIS: return 932;
882 case SC_CHARSET_TURKISH: return 1254;
883 case SC_CHARSET_JOHAB: return 1361;
884 case SC_CHARSET_HEBREW: return 1255;
885 case SC_CHARSET_ARABIC: return 1256;
886 case SC_CHARSET_VIETNAMESE: return 1258;
887 case SC_CHARSET_THAI: return 874;
888 case SC_CHARSET_8859_15: return 28605;
889 // Not supported
890 case SC_CHARSET_CYRILLIC: return documentCodePage;
891 case SC_CHARSET_SYMBOL: return documentCodePage;
893 return documentCodePage;
896 UINT ScintillaWin::CodePageOfDocument() {
897 return CodePageFromCharSet(vs.styles[STYLE_DEFAULT].characterSet, pdoc->dbcsCodePage);
900 sptr_t ScintillaWin::GetTextLength() {
901 if (::IsWindowUnicode(MainHWND())) {
902 if (pdoc->Length() == 0)
903 return 0;
904 std::vector<char> docBytes(pdoc->Length(), '\0');
905 pdoc->GetCharRange(&docBytes[0], 0, pdoc->Length());
906 if (IsUnicodeMode()) {
907 return UTF16Length(&docBytes[0], static_cast<unsigned int>(docBytes.size()));
908 } else {
909 return ::MultiByteToWideChar(CodePageOfDocument(), 0, &docBytes[0],
910 static_cast<int>(docBytes.size()), NULL, 0);
912 } else {
913 return pdoc->Length();
917 sptr_t ScintillaWin::GetText(uptr_t wParam, sptr_t lParam) {
918 if (::IsWindowUnicode(MainHWND())) {
919 wchar_t *ptr = reinterpret_cast<wchar_t *>(lParam);
920 if (pdoc->Length() == 0) {
921 *ptr = L'\0';
922 return 0;
924 std::vector<char> docBytes(pdoc->Length(), '\0');
925 pdoc->GetCharRange(&docBytes[0], 0, pdoc->Length());
926 if (IsUnicodeMode()) {
927 unsigned int lengthUTF16 = UTF16Length(&docBytes[0], static_cast<unsigned int>(docBytes.size()));
928 if (lParam == 0)
929 return lengthUTF16;
930 if (wParam == 0)
931 return 0;
932 unsigned int uLen = UTF16FromUTF8(&docBytes[0], static_cast<unsigned int>(docBytes.size()),
933 ptr, static_cast<int>(wParam) - 1);
934 ptr[uLen] = L'\0';
935 return uLen;
936 } else {
937 // Not Unicode mode
938 // Convert to Unicode using the current Scintilla code page
939 const UINT cpSrc = CodePageOfDocument();
940 int lengthUTF16 = ::MultiByteToWideChar(cpSrc, 0, &docBytes[0],
941 static_cast<int>(docBytes.size()), NULL, 0);
942 if (lengthUTF16 >= static_cast<int>(wParam))
943 lengthUTF16 = static_cast<int>(wParam)-1;
944 ::MultiByteToWideChar(cpSrc, 0, &docBytes[0],
945 static_cast<int>(docBytes.size()),
946 ptr, lengthUTF16);
947 ptr[lengthUTF16] = L'\0';
948 return lengthUTF16;
950 } else {
951 if (lParam == 0)
952 return pdoc->Length() + 1;
953 if (wParam == 0)
954 return 0;
955 char *ptr = reinterpret_cast<char *>(lParam);
956 unsigned int iChar = 0;
957 for (; iChar < wParam - 1; iChar++)
958 ptr[iChar] = pdoc->CharAt(iChar);
959 ptr[iChar] = '\0';
960 return iChar;
964 sptr_t ScintillaWin::WndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
965 try {
966 //Platform::DebugPrintf("S M:%x WP:%x L:%x\n", iMessage, wParam, lParam);
967 iMessage = SciMessageFromEM(iMessage);
968 switch (iMessage) {
970 case WM_CREATE:
971 ctrlID = ::GetDlgCtrlID(reinterpret_cast<HWND>(wMain.GetID()));
972 // Get Intellimouse scroll line parameters
973 GetIntelliMouseParameters();
974 ::RegisterDragDrop(MainHWND(), reinterpret_cast<IDropTarget *>(&dt));
975 break;
977 case WM_COMMAND:
978 Command(LoWord(wParam));
979 break;
981 case WM_PAINT:
982 return WndPaint(wParam);
984 case WM_PRINTCLIENT: {
985 HDC hdc = reinterpret_cast<HDC>(wParam);
986 if (!IsCompatibleDC(hdc)) {
987 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
989 FullPaintDC(hdc);
991 break;
993 case WM_VSCROLL:
994 ScrollMessage(wParam);
995 break;
997 case WM_HSCROLL:
998 HorizontalScrollMessage(wParam);
999 break;
1001 case WM_SIZE: {
1002 #if defined(USE_D2D)
1003 if (paintState == notPainting) {
1004 DropRenderTarget();
1005 } else {
1006 renderTargetValid = false;
1008 #endif
1009 //Platform::DebugPrintf("Scintilla WM_SIZE %d %d\n", LoWord(lParam), HiWord(lParam));
1010 ChangeSize();
1012 break;
1014 case WM_MOUSEWHEEL:
1015 // if autocomplete list active then send mousewheel message to it
1016 if (ac.Active()) {
1017 HWND hWnd = reinterpret_cast<HWND>(ac.lb->GetID());
1018 ::SendMessage(hWnd, iMessage, wParam, lParam);
1019 break;
1022 // Don't handle datazoom.
1023 // (A good idea for datazoom would be to "fold" or "unfold" details.
1024 // i.e. if datazoomed out only class structures are visible, when datazooming in the control
1025 // structures appear, then eventually the individual statements...)
1026 if (wParam & MK_SHIFT) {
1027 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1030 // Either SCROLL or ZOOM. We handle the wheel steppings calculation
1031 wheelDelta -= static_cast<short>(HiWord(wParam));
1032 if (abs(wheelDelta) >= WHEEL_DELTA && linesPerScroll > 0) {
1033 int linesToScroll = linesPerScroll;
1034 if (linesPerScroll == WHEEL_PAGESCROLL)
1035 linesToScroll = LinesOnScreen() - 1;
1036 if (linesToScroll == 0) {
1037 linesToScroll = 1;
1039 linesToScroll *= (wheelDelta / WHEEL_DELTA);
1040 if (wheelDelta >= 0)
1041 wheelDelta = wheelDelta % WHEEL_DELTA;
1042 else
1043 wheelDelta = - (-wheelDelta % WHEEL_DELTA);
1045 if (wParam & MK_CONTROL) {
1046 // Zoom! We play with the font sizes in the styles.
1047 // Number of steps/line is ignored, we just care if sizing up or down
1048 if (linesToScroll < 0) {
1049 KeyCommand(SCI_ZOOMIN);
1050 } else {
1051 KeyCommand(SCI_ZOOMOUT);
1053 } else {
1054 // Scroll
1055 ScrollTo(topLine + linesToScroll);
1058 return 0;
1060 case WM_TIMER:
1061 if (wParam == idleTimerID && idler.state) {
1062 SendMessage(MainHWND(), SC_WIN_IDLE, 0, 1);
1063 } else {
1064 TickFor(static_cast<TickReason>(wParam - fineTimerStart));
1066 break;
1068 case SC_WIN_IDLE:
1069 // wParam=dwTickCountInitial, or 0 to initialize. lParam=bSkipUserInputTest
1070 if (idler.state) {
1071 if (lParam || (WAIT_TIMEOUT == MsgWaitForMultipleObjects(0, 0, 0, 0, QS_INPUT|QS_HOTKEY))) {
1072 if (Idle()) {
1073 // User input was given priority above, but all events do get a turn. Other
1074 // messages, notifications, etc. will get interleaved with the idle messages.
1076 // However, some things like WM_PAINT are a lower priority, and will not fire
1077 // when there's a message posted. So, several times a second, we stop and let
1078 // the low priority events have a turn (after which the timer will fire again).
1080 DWORD dwCurrent = GetTickCount();
1081 DWORD dwStart = wParam ? static_cast<DWORD>(wParam) : dwCurrent;
1082 const DWORD maxWorkTime = 50;
1084 if (dwCurrent >= dwStart && dwCurrent > maxWorkTime && dwCurrent - maxWorkTime < dwStart)
1085 PostMessage(MainHWND(), SC_WIN_IDLE, dwStart, 0);
1086 } else {
1087 SetIdle(false);
1091 break;
1093 case WM_GETMINMAXINFO:
1094 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1096 case WM_LBUTTONDOWN: {
1097 // For IME, set the composition string as the result string.
1098 HIMC hIMC = ::ImmGetContext(MainHWND());
1099 ::ImmNotifyIME(hIMC, NI_COMPOSITIONSTR, CPS_COMPLETE, 0);
1100 ::ImmReleaseContext(MainHWND(), hIMC);
1102 //Platform::DebugPrintf("Buttdown %d %x %x %x %x %x\n",iMessage, wParam, lParam,
1103 // Platform::IsKeyDown(VK_SHIFT),
1104 // Platform::IsKeyDown(VK_CONTROL),
1105 // Platform::IsKeyDown(VK_MENU));
1106 ::SetFocus(MainHWND());
1107 ButtonDown(Point::FromLong(static_cast<long>(lParam)), ::GetMessageTime(),
1108 (wParam & MK_SHIFT) != 0,
1109 (wParam & MK_CONTROL) != 0,
1110 Platform::IsKeyDown(VK_MENU));
1112 break;
1114 case WM_MOUSEMOVE: {
1115 const Point pt = Point::FromLong(static_cast<long>(lParam));
1117 // Windows might send WM_MOUSEMOVE even though the mouse has not been moved:
1118 // http://blogs.msdn.com/b/oldnewthing/archive/2003/10/01/55108.aspx
1119 if (ptMouseLast.x != pt.x || ptMouseLast.y != pt.y) {
1120 SetTrackMouseLeaveEvent(true);
1121 ButtonMoveWithModifiers(pt,
1122 ((wParam & MK_SHIFT) != 0 ? SCI_SHIFT : 0) |
1123 ((wParam & MK_CONTROL) != 0 ? SCI_CTRL : 0) |
1124 (Platform::IsKeyDown(VK_MENU) ? SCI_ALT : 0));
1127 break;
1129 case WM_MOUSELEAVE:
1130 SetTrackMouseLeaveEvent(false);
1131 MouseLeave();
1132 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1134 case WM_LBUTTONUP:
1135 ButtonUp(Point::FromLong(static_cast<long>(lParam)),
1136 ::GetMessageTime(),
1137 (wParam & MK_CONTROL) != 0);
1138 break;
1140 case WM_RBUTTONDOWN:
1141 ::SetFocus(MainHWND());
1142 if (!PointInSelection(Point::FromLong(static_cast<long>(lParam)))) {
1143 CancelModes();
1144 SetEmptySelection(PositionFromLocation(Point::FromLong(static_cast<long>(lParam))));
1146 break;
1148 case WM_SETCURSOR:
1149 if (LoWord(lParam) == HTCLIENT) {
1150 if (inDragDrop == ddDragging) {
1151 DisplayCursor(Window::cursorUp);
1152 } else {
1153 // Display regular (drag) cursor over selection
1154 POINT pt;
1155 if (0 != ::GetCursorPos(&pt)) {
1156 ::ScreenToClient(MainHWND(), &pt);
1157 if (PointInSelMargin(PointFromPOINT(pt))) {
1158 DisplayCursor(GetMarginCursor(PointFromPOINT(pt)));
1159 } else if (PointInSelection(PointFromPOINT(pt)) && !SelectionEmpty()) {
1160 DisplayCursor(Window::cursorArrow);
1161 } else if (PointIsHotspot(PointFromPOINT(pt))) {
1162 DisplayCursor(Window::cursorHand);
1163 } else {
1164 DisplayCursor(Window::cursorText);
1168 return TRUE;
1169 } else {
1170 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1173 case WM_CHAR:
1174 if (((wParam >= 128) || !iscntrl(static_cast<int>(wParam))) || !lastKeyDownConsumed) {
1175 if (::IsWindowUnicode(MainHWND()) || keysAlwaysUnicode) {
1176 wchar_t wcs[2] = {static_cast<wchar_t>(wParam), 0};
1177 if (IsUnicodeMode()) {
1178 // For a wide character version of the window:
1179 char utfval[4];
1180 unsigned int len = UTF8Length(wcs, 1);
1181 UTF8FromUTF16(wcs, 1, utfval, len);
1182 AddCharUTF(utfval, len);
1183 } else {
1184 UINT cpDest = CodePageOfDocument();
1185 char inBufferCP[20];
1186 int size = ::WideCharToMultiByte(cpDest,
1187 0, wcs, 1, inBufferCP, sizeof(inBufferCP) - 1, 0, 0);
1188 inBufferCP[size] = '\0';
1189 AddCharUTF(inBufferCP, size);
1191 } else {
1192 if (IsUnicodeMode()) {
1193 AddCharBytes('\0', LOBYTE(wParam));
1194 } else {
1195 AddChar(LOBYTE(wParam));
1199 return 0;
1201 case WM_UNICHAR:
1202 if (wParam == UNICODE_NOCHAR) {
1203 return IsUnicodeMode() ? 1 : 0;
1204 } else if (lastKeyDownConsumed) {
1205 return 1;
1206 } else {
1207 if (IsUnicodeMode()) {
1208 char utfval[4];
1209 wchar_t wcs[2] = {static_cast<wchar_t>(wParam), 0};
1210 unsigned int len = UTF8Length(wcs, 1);
1211 UTF8FromUTF16(wcs, 1, utfval, len);
1212 AddCharUTF(utfval, len);
1213 return 1;
1214 } else {
1215 return 0;
1219 case WM_SYSKEYDOWN:
1220 case WM_KEYDOWN: {
1221 //Platform::DebugPrintf("S keydown %d %x %x %x %x\n",iMessage, wParam, lParam, ::IsKeyDown(VK_SHIFT), ::IsKeyDown(VK_CONTROL));
1222 lastKeyDownConsumed = false;
1223 int ret = KeyDown(KeyTranslate(static_cast<int>(wParam)),
1224 Platform::IsKeyDown(VK_SHIFT),
1225 Platform::IsKeyDown(VK_CONTROL),
1226 Platform::IsKeyDown(VK_MENU),
1227 &lastKeyDownConsumed);
1228 if (!ret && !lastKeyDownConsumed) {
1229 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1231 break;
1234 case WM_IME_KEYDOWN:
1235 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1237 case WM_KEYUP:
1238 //Platform::DebugPrintf("S keyup %d %x %x\n",iMessage, wParam, lParam);
1239 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1241 case WM_SETTINGCHANGE:
1242 //Platform::DebugPrintf("Setting Changed\n");
1243 InvalidateStyleData();
1244 // Get Intellimouse scroll line parameters
1245 GetIntelliMouseParameters();
1246 break;
1248 case WM_GETDLGCODE:
1249 return DLGC_HASSETSEL | DLGC_WANTALLKEYS;
1251 case WM_KILLFOCUS: {
1252 HWND wOther = reinterpret_cast<HWND>(wParam);
1253 HWND wThis = MainHWND();
1254 HWND wCT = reinterpret_cast<HWND>(ct.wCallTip.GetID());
1255 if (!wParam ||
1256 !(::IsChild(wThis, wOther) || (wOther == wCT))) {
1257 SetFocusState(false);
1258 DestroySystemCaret();
1260 // Explicitly complete any IME composition
1261 HIMC hIMC = ImmGetContext(MainHWND());
1262 if (hIMC) {
1263 ::ImmNotifyIME(hIMC, NI_COMPOSITIONSTR, CPS_COMPLETE, 0);
1264 ::ImmReleaseContext(MainHWND(), hIMC);
1267 break;
1269 case WM_SETFOCUS:
1270 SetFocusState(true);
1271 DestroySystemCaret();
1272 CreateSystemCaret();
1273 break;
1275 case WM_SYSCOLORCHANGE:
1276 //Platform::DebugPrintf("Setting Changed\n");
1277 InvalidateStyleData();
1278 break;
1280 case WM_IME_STARTCOMPOSITION: // dbcs
1281 if (KoreanIME()) {
1282 return 0;
1284 ImeStartComposition();
1285 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1287 case WM_IME_ENDCOMPOSITION: // dbcs
1288 ImeEndComposition();
1289 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1291 case WM_IME_COMPOSITION:
1292 if (KoreanIME()) {
1293 return HandleCompositionKoreanIME(wParam, lParam);
1294 } else {
1295 return HandleComposition(wParam, lParam);
1298 case WM_IME_CHAR: {
1299 AddCharBytes(HIBYTE(wParam), LOBYTE(wParam));
1300 return 0;
1303 case WM_CONTEXTMENU:
1304 if (displayPopupMenu) {
1305 Point pt = Point::FromLong(static_cast<long>(lParam));
1306 if ((pt.x == -1) && (pt.y == -1)) {
1307 // Caused by keyboard so display menu near caret
1308 pt = PointMainCaret();
1309 POINT spt = {static_cast<int>(pt.x), static_cast<int>(pt.y)};
1310 ::ClientToScreen(MainHWND(), &spt);
1311 pt = PointFromPOINT(spt);
1313 ContextMenu(pt);
1314 return 0;
1316 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1318 case WM_INPUTLANGCHANGE:
1319 //::SetThreadLocale(LOWORD(lParam));
1320 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1322 case WM_INPUTLANGCHANGEREQUEST:
1323 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1325 case WM_ERASEBKGND:
1326 return 1; // Avoid any background erasure as whole window painted.
1328 case WM_CAPTURECHANGED:
1329 capturedMouse = false;
1330 return 0;
1332 case WM_IME_SETCONTEXT:
1333 if (KoreanIME()) {
1334 if (wParam) {
1335 LPARAM NoImeWin = lParam;
1336 NoImeWin = NoImeWin & (~ISC_SHOWUICOMPOSITIONWINDOW);
1337 return ::DefWindowProc(MainHWND(), iMessage, wParam, NoImeWin);
1340 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1342 // These are not handled in Scintilla and its faster to dispatch them here.
1343 // Also moves time out to here so profile doesn't count lots of empty message calls.
1345 case WM_MOVE:
1346 case WM_MOUSEACTIVATE:
1347 case WM_NCHITTEST:
1348 case WM_NCCALCSIZE:
1349 case WM_NCPAINT:
1350 case WM_NCMOUSEMOVE:
1351 case WM_NCLBUTTONDOWN:
1352 case WM_IME_NOTIFY:
1353 case WM_SYSCOMMAND:
1354 case WM_WINDOWPOSCHANGING:
1355 case WM_WINDOWPOSCHANGED:
1356 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1358 case WM_GETTEXTLENGTH:
1359 return GetTextLength();
1361 case WM_GETTEXT:
1362 return GetText(wParam, lParam);
1364 case EM_LINEFROMCHAR:
1365 if (static_cast<int>(wParam) < 0) {
1366 wParam = SelectionStart().Position();
1368 return pdoc->LineFromPosition(static_cast<int>(wParam));
1370 case EM_EXLINEFROMCHAR:
1371 return pdoc->LineFromPosition(static_cast<int>(lParam));
1373 case EM_GETSEL:
1374 if (wParam) {
1375 *reinterpret_cast<int *>(wParam) = SelectionStart().Position();
1377 if (lParam) {
1378 *reinterpret_cast<int *>(lParam) = SelectionEnd().Position();
1380 return MAKELONG(SelectionStart().Position(), SelectionEnd().Position());
1382 case EM_EXGETSEL: {
1383 if (lParam == 0) {
1384 return 0;
1386 Sci_CharacterRange *pCR = reinterpret_cast<Sci_CharacterRange *>(lParam);
1387 pCR->cpMin = SelectionStart().Position();
1388 pCR->cpMax = SelectionEnd().Position();
1390 break;
1392 case EM_SETSEL: {
1393 int nStart = static_cast<int>(wParam);
1394 int nEnd = static_cast<int>(lParam);
1395 if (nStart == 0 && nEnd == -1) {
1396 nEnd = pdoc->Length();
1398 if (nStart == -1) {
1399 nStart = nEnd; // Remove selection
1401 if (nStart > nEnd) {
1402 SetSelection(nEnd, nStart);
1403 } else {
1404 SetSelection(nStart, nEnd);
1406 EnsureCaretVisible();
1408 break;
1410 case EM_EXSETSEL: {
1411 if (lParam == 0) {
1412 return 0;
1414 Sci_CharacterRange *pCR = reinterpret_cast<Sci_CharacterRange *>(lParam);
1415 sel.selType = Selection::selStream;
1416 if (pCR->cpMin == 0 && pCR->cpMax == -1) {
1417 SetSelection(pCR->cpMin, pdoc->Length());
1418 } else {
1419 SetSelection(pCR->cpMin, pCR->cpMax);
1421 EnsureCaretVisible();
1422 return pdoc->LineFromPosition(SelectionStart().Position());
1425 case SCI_GETDIRECTFUNCTION:
1426 return reinterpret_cast<sptr_t>(DirectFunction);
1428 case SCI_GETDIRECTPOINTER:
1429 return reinterpret_cast<sptr_t>(this);
1431 case SCI_GRABFOCUS:
1432 ::SetFocus(MainHWND());
1433 break;
1435 case SCI_SETKEYSUNICODE:
1436 keysAlwaysUnicode = wParam != 0;
1437 break;
1439 case SCI_GETKEYSUNICODE:
1440 return keysAlwaysUnicode;
1442 case SCI_SETTECHNOLOGY:
1443 if ((wParam == SC_TECHNOLOGY_DEFAULT) ||
1444 (wParam == SC_TECHNOLOGY_DIRECTWRITERETAIN) ||
1445 (wParam == SC_TECHNOLOGY_DIRECTWRITEDC) ||
1446 (wParam == SC_TECHNOLOGY_DIRECTWRITE)) {
1447 if (technology != static_cast<int>(wParam)) {
1448 if (static_cast<int>(wParam) > SC_TECHNOLOGY_DEFAULT) {
1449 #if defined(USE_D2D)
1450 if (!LoadD2D())
1451 // Failed to load Direct2D or DirectWrite so no effect
1452 return 0;
1453 #else
1454 return 0;
1455 #endif
1457 #if defined(USE_D2D)
1458 DropRenderTarget();
1459 #endif
1460 technology = static_cast<int>(wParam);
1461 // Invalidate all cached information including layout.
1462 DropGraphics(true);
1463 InvalidateStyleRedraw();
1466 break;
1468 #ifdef SCI_LEXER
1469 case SCI_LOADLEXERLIBRARY:
1470 LexerManager::GetInstance()->Load(reinterpret_cast<const char *>(lParam));
1471 break;
1472 #endif
1474 default:
1475 return ScintillaBase::WndProc(iMessage, wParam, lParam);
1477 } catch (std::bad_alloc &) {
1478 errorStatus = SC_STATUS_BADALLOC;
1479 } catch (...) {
1480 errorStatus = SC_STATUS_FAILURE;
1482 return 0l;
1485 bool ScintillaWin::ValidCodePage(int codePage) const {
1486 return codePage == 0 || codePage == SC_CP_UTF8 ||
1487 codePage == 932 || codePage == 936 || codePage == 949 ||
1488 codePage == 950 || codePage == 1361;
1491 sptr_t ScintillaWin::DefWndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
1492 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1496 * Report that this Editor subclass has a working implementation of FineTickerStart.
1498 bool ScintillaWin::FineTickerAvailable() {
1499 return true;
1502 bool ScintillaWin::FineTickerRunning(TickReason reason) {
1503 return timers[reason] != 0;
1506 void ScintillaWin::FineTickerStart(TickReason reason, int millis, int tolerance) {
1507 FineTickerCancel(reason);
1508 if (SetCoalescableTimerFn && tolerance) {
1509 timers[reason] = SetCoalescableTimerFn(MainHWND(), fineTimerStart + reason, millis, NULL, tolerance);
1510 } else {
1511 timers[reason] = ::SetTimer(MainHWND(), fineTimerStart + reason, millis, NULL);
1515 void ScintillaWin::FineTickerCancel(TickReason reason) {
1516 if (timers[reason]) {
1517 ::KillTimer(MainHWND(), timers[reason]);
1518 timers[reason] = 0;
1523 bool ScintillaWin::SetIdle(bool on) {
1524 // On Win32 the Idler is implemented as a Timer on the Scintilla window. This
1525 // takes advantage of the fact that WM_TIMER messages are very low priority,
1526 // and are only posted when the message queue is empty, i.e. during idle time.
1527 if (idler.state != on) {
1528 if (on) {
1529 idler.idlerID = ::SetTimer(MainHWND(), idleTimerID, 10, NULL)
1530 ? reinterpret_cast<IdlerID>(idleTimerID) : 0;
1531 } else {
1532 ::KillTimer(MainHWND(), reinterpret_cast<uptr_t>(idler.idlerID));
1533 idler.idlerID = 0;
1535 idler.state = idler.idlerID != 0;
1537 return idler.state;
1540 void ScintillaWin::SetMouseCapture(bool on) {
1541 if (mouseDownCaptures) {
1542 if (on) {
1543 ::SetCapture(MainHWND());
1544 } else {
1545 ::ReleaseCapture();
1548 capturedMouse = on;
1551 bool ScintillaWin::HaveMouseCapture() {
1552 // Cannot just see if GetCapture is this window as the scroll bar also sets capture for the window
1553 return capturedMouse;
1554 //return capturedMouse && (::GetCapture() == MainHWND());
1557 void ScintillaWin::SetTrackMouseLeaveEvent(bool on) {
1558 if (on && TrackMouseEventFn && !trackedMouseLeave) {
1559 TRACKMOUSEEVENT tme;
1560 tme.cbSize = sizeof(tme);
1561 tme.dwFlags = TME_LEAVE;
1562 tme.hwndTrack = MainHWND();
1563 tme.dwHoverTime = HOVER_DEFAULT; // Unused but triggers Dr. Memory if not initialized
1564 TrackMouseEventFn(&tme);
1566 trackedMouseLeave = on;
1569 bool ScintillaWin::PaintContains(PRectangle rc) {
1570 if (paintState == painting) {
1571 return BoundsContains(rcPaint, hRgnUpdate, rc);
1573 return true;
1576 void ScintillaWin::ScrollText(int /* linesToMove */) {
1577 //Platform::DebugPrintf("ScintillaWin::ScrollText %d\n", linesToMove);
1578 //::ScrollWindow(MainHWND(), 0,
1579 // vs.lineHeight * linesToMove, 0, 0);
1580 //::UpdateWindow(MainHWND());
1581 Redraw();
1582 UpdateSystemCaret();
1585 void ScintillaWin::UpdateSystemCaret() {
1586 if (hasFocus) {
1587 if (HasCaretSizeChanged()) {
1588 DestroySystemCaret();
1589 CreateSystemCaret();
1591 Point pos = PointMainCaret();
1592 ::SetCaretPos(static_cast<int>(pos.x), static_cast<int>(pos.y));
1596 int ScintillaWin::SetScrollInfo(int nBar, LPCSCROLLINFO lpsi, BOOL bRedraw) {
1597 return ::SetScrollInfo(MainHWND(), nBar, lpsi, bRedraw);
1600 bool ScintillaWin::GetScrollInfo(int nBar, LPSCROLLINFO lpsi) {
1601 return ::GetScrollInfo(MainHWND(), nBar, lpsi) ? true : false;
1604 // Change the scroll position but avoid repaint if changing to same value
1605 void ScintillaWin::ChangeScrollPos(int barType, int pos) {
1606 SCROLLINFO sci = {
1607 sizeof(sci), 0, 0, 0, 0, 0, 0
1609 sci.fMask = SIF_POS;
1610 GetScrollInfo(barType, &sci);
1611 if (sci.nPos != pos) {
1612 DwellEnd(true);
1613 sci.nPos = pos;
1614 SetScrollInfo(barType, &sci, TRUE);
1618 void ScintillaWin::SetVerticalScrollPos() {
1619 ChangeScrollPos(SB_VERT, topLine);
1622 void ScintillaWin::SetHorizontalScrollPos() {
1623 ChangeScrollPos(SB_HORZ, xOffset);
1626 bool ScintillaWin::ModifyScrollBars(int nMax, int nPage) {
1627 bool modified = false;
1628 SCROLLINFO sci = {
1629 sizeof(sci), 0, 0, 0, 0, 0, 0
1631 sci.fMask = SIF_PAGE | SIF_RANGE;
1632 GetScrollInfo(SB_VERT, &sci);
1633 int vertEndPreferred = nMax;
1634 if (!verticalScrollBarVisible)
1635 nPage = vertEndPreferred + 1;
1636 if ((sci.nMin != 0) ||
1637 (sci.nMax != vertEndPreferred) ||
1638 (sci.nPage != static_cast<unsigned int>(nPage)) ||
1639 (sci.nPos != 0)) {
1640 sci.fMask = SIF_PAGE | SIF_RANGE;
1641 sci.nMin = 0;
1642 sci.nMax = vertEndPreferred;
1643 sci.nPage = nPage;
1644 sci.nPos = 0;
1645 sci.nTrackPos = 1;
1646 SetScrollInfo(SB_VERT, &sci, TRUE);
1647 modified = true;
1650 PRectangle rcText = GetTextRectangle();
1651 int horizEndPreferred = scrollWidth;
1652 if (horizEndPreferred < 0)
1653 horizEndPreferred = 0;
1654 unsigned int pageWidth = static_cast<unsigned int>(rcText.Width());
1655 if (!horizontalScrollBarVisible || Wrapping())
1656 pageWidth = horizEndPreferred + 1;
1657 sci.fMask = SIF_PAGE | SIF_RANGE;
1658 GetScrollInfo(SB_HORZ, &sci);
1659 if ((sci.nMin != 0) ||
1660 (sci.nMax != horizEndPreferred) ||
1661 (sci.nPage != pageWidth) ||
1662 (sci.nPos != 0)) {
1663 sci.fMask = SIF_PAGE | SIF_RANGE;
1664 sci.nMin = 0;
1665 sci.nMax = horizEndPreferred;
1666 sci.nPage = pageWidth;
1667 sci.nPos = 0;
1668 sci.nTrackPos = 1;
1669 SetScrollInfo(SB_HORZ, &sci, TRUE);
1670 modified = true;
1671 if (scrollWidth < static_cast<int>(pageWidth)) {
1672 HorizontalScrollTo(0);
1675 return modified;
1678 void ScintillaWin::NotifyChange() {
1679 ::SendMessage(::GetParent(MainHWND()), WM_COMMAND,
1680 MAKELONG(GetCtrlID(), SCEN_CHANGE),
1681 reinterpret_cast<LPARAM>(MainHWND()));
1684 void ScintillaWin::NotifyFocus(bool focus) {
1685 ::SendMessage(::GetParent(MainHWND()), WM_COMMAND,
1686 MAKELONG(GetCtrlID(), focus ? SCEN_SETFOCUS : SCEN_KILLFOCUS),
1687 reinterpret_cast<LPARAM>(MainHWND()));
1688 Editor::NotifyFocus(focus);
1691 void ScintillaWin::SetCtrlID(int identifier) {
1692 ::SetWindowID(reinterpret_cast<HWND>(wMain.GetID()), identifier);
1695 int ScintillaWin::GetCtrlID() {
1696 return ::GetDlgCtrlID(reinterpret_cast<HWND>(wMain.GetID()));
1699 void ScintillaWin::NotifyParent(SCNotification scn) {
1700 scn.nmhdr.hwndFrom = MainHWND();
1701 scn.nmhdr.idFrom = GetCtrlID();
1702 ::SendMessage(::GetParent(MainHWND()), WM_NOTIFY,
1703 GetCtrlID(), reinterpret_cast<LPARAM>(&scn));
1706 void ScintillaWin::NotifyDoubleClick(Point pt, int modifiers) {
1707 //Platform::DebugPrintf("ScintillaWin Double click 0\n");
1708 ScintillaBase::NotifyDoubleClick(pt, modifiers);
1709 // Send myself a WM_LBUTTONDBLCLK, so the container can handle it too.
1710 ::SendMessage(MainHWND(),
1711 WM_LBUTTONDBLCLK,
1712 (modifiers & SCI_SHIFT) ? MK_SHIFT : 0,
1713 MAKELPARAM(pt.x, pt.y));
1716 class CaseFolderDBCS : public CaseFolderTable {
1717 // Allocate the expandable storage here so that it does not need to be reallocated
1718 // for each call to Fold.
1719 std::vector<wchar_t> utf16Mixed;
1720 std::vector<wchar_t> utf16Folded;
1721 UINT cp;
1722 public:
1723 explicit CaseFolderDBCS(UINT cp_) : cp(cp_) {
1724 StandardASCII();
1726 virtual size_t Fold(char *folded, size_t sizeFolded, const char *mixed, size_t lenMixed) {
1727 if ((lenMixed == 1) && (sizeFolded > 0)) {
1728 folded[0] = mapping[static_cast<unsigned char>(mixed[0])];
1729 return 1;
1730 } else {
1731 if (lenMixed > utf16Mixed.size()) {
1732 utf16Mixed.resize(lenMixed + 8);
1734 size_t nUtf16Mixed = ::MultiByteToWideChar(cp, 0, mixed,
1735 static_cast<int>(lenMixed),
1736 &utf16Mixed[0],
1737 static_cast<int>(utf16Mixed.size()));
1739 if (nUtf16Mixed == 0) {
1740 // Failed to convert -> bad input
1741 folded[0] = '\0';
1742 return 1;
1745 unsigned int lenFlat = 0;
1746 for (size_t mixIndex=0; mixIndex < nUtf16Mixed; mixIndex++) {
1747 if ((lenFlat + 20) > utf16Folded.size())
1748 utf16Folded.resize(lenFlat + 60);
1749 const char *foldedUTF8 = CaseConvert(utf16Mixed[mixIndex], CaseConversionFold);
1750 if (foldedUTF8) {
1751 // Maximum length of a case conversion is 6 bytes, 3 characters
1752 wchar_t wFolded[20];
1753 unsigned int charsConverted = UTF16FromUTF8(foldedUTF8,
1754 static_cast<unsigned int>(strlen(foldedUTF8)),
1755 wFolded, ELEMENTS(wFolded));
1756 for (size_t j=0; j<charsConverted; j++)
1757 utf16Folded[lenFlat++] = wFolded[j];
1758 } else {
1759 utf16Folded[lenFlat++] = utf16Mixed[mixIndex];
1763 size_t lenOut = ::WideCharToMultiByte(cp, 0,
1764 &utf16Folded[0], lenFlat,
1765 NULL, 0, NULL, 0);
1767 if (lenOut < sizeFolded) {
1768 ::WideCharToMultiByte(cp, 0,
1769 &utf16Folded[0], lenFlat,
1770 folded, static_cast<int>(lenOut), NULL, 0);
1771 return lenOut;
1772 } else {
1773 return 0;
1779 CaseFolder *ScintillaWin::CaseFolderForEncoding() {
1780 UINT cpDest = CodePageOfDocument();
1781 if (cpDest == SC_CP_UTF8) {
1782 return new CaseFolderUnicode();
1783 } else {
1784 if (pdoc->dbcsCodePage == 0) {
1785 CaseFolderTable *pcf = new CaseFolderTable();
1786 pcf->StandardASCII();
1787 // Only for single byte encodings
1788 UINT cpDoc = CodePageOfDocument();
1789 for (int i=0x80; i<0x100; i++) {
1790 char sCharacter[2] = "A";
1791 sCharacter[0] = static_cast<char>(i);
1792 wchar_t wCharacter[20];
1793 unsigned int lengthUTF16 = ::MultiByteToWideChar(cpDoc, 0, sCharacter, 1,
1794 wCharacter, ELEMENTS(wCharacter));
1795 if (lengthUTF16 == 1) {
1796 const char *caseFolded = CaseConvert(wCharacter[0], CaseConversionFold);
1797 if (caseFolded) {
1798 wchar_t wLower[20];
1799 unsigned int charsConverted = UTF16FromUTF8(caseFolded,
1800 static_cast<unsigned int>(strlen(caseFolded)),
1801 wLower, ELEMENTS(wLower));
1802 if (charsConverted == 1) {
1803 char sCharacterLowered[20];
1804 unsigned int lengthConverted = ::WideCharToMultiByte(cpDoc, 0,
1805 wLower, charsConverted,
1806 sCharacterLowered, ELEMENTS(sCharacterLowered), NULL, 0);
1807 if ((lengthConverted == 1) && (sCharacter[0] != sCharacterLowered[0])) {
1808 pcf->SetTranslation(sCharacter[0], sCharacterLowered[0]);
1814 return pcf;
1815 } else {
1816 return new CaseFolderDBCS(cpDest);
1821 std::string ScintillaWin::CaseMapString(const std::string &s, int caseMapping) {
1822 if ((s.size() == 0) || (caseMapping == cmSame))
1823 return s;
1825 UINT cpDoc = CodePageOfDocument();
1826 if (cpDoc == SC_CP_UTF8) {
1827 std::string retMapped(s.length() * maxExpansionCaseConversion, 0);
1828 size_t lenMapped = CaseConvertString(&retMapped[0], retMapped.length(), s.c_str(), s.length(),
1829 (caseMapping == cmUpper) ? CaseConversionUpper : CaseConversionLower);
1830 retMapped.resize(lenMapped);
1831 return retMapped;
1834 unsigned int lengthUTF16 = ::MultiByteToWideChar(cpDoc, 0, s.c_str(),
1835 static_cast<int>(s.size()), NULL, 0);
1836 if (lengthUTF16 == 0) // Failed to convert
1837 return s;
1839 DWORD mapFlags = LCMAP_LINGUISTIC_CASING |
1840 ((caseMapping == cmUpper) ? LCMAP_UPPERCASE : LCMAP_LOWERCASE);
1842 // Change text to UTF-16
1843 std::vector<wchar_t> vwcText(lengthUTF16);
1844 ::MultiByteToWideChar(cpDoc, 0, s.c_str(), static_cast<int>(s.size()), &vwcText[0], lengthUTF16);
1846 // Change case
1847 int charsConverted = ::LCMapStringW(LOCALE_SYSTEM_DEFAULT, mapFlags,
1848 &vwcText[0], lengthUTF16, NULL, 0);
1849 std::vector<wchar_t> vwcConverted(charsConverted);
1850 ::LCMapStringW(LOCALE_SYSTEM_DEFAULT, mapFlags,
1851 &vwcText[0], lengthUTF16, &vwcConverted[0], charsConverted);
1853 // Change back to document encoding
1854 unsigned int lengthConverted = ::WideCharToMultiByte(cpDoc, 0,
1855 &vwcConverted[0], static_cast<int>(vwcConverted.size()),
1856 NULL, 0, NULL, 0);
1857 std::vector<char> vcConverted(lengthConverted);
1858 ::WideCharToMultiByte(cpDoc, 0,
1859 &vwcConverted[0], static_cast<int>(vwcConverted.size()),
1860 &vcConverted[0], static_cast<int>(vcConverted.size()), NULL, 0);
1862 return std::string(&vcConverted[0], vcConverted.size());
1865 void ScintillaWin::Copy() {
1866 //Platform::DebugPrintf("Copy\n");
1867 if (!sel.Empty()) {
1868 SelectionText selectedText;
1869 CopySelectionRange(&selectedText);
1870 CopyToClipboard(selectedText);
1874 void ScintillaWin::CopyAllowLine() {
1875 SelectionText selectedText;
1876 CopySelectionRange(&selectedText, true);
1877 CopyToClipboard(selectedText);
1880 bool ScintillaWin::CanPaste() {
1881 if (!Editor::CanPaste())
1882 return false;
1883 if (::IsClipboardFormatAvailable(CF_TEXT))
1884 return true;
1885 if (IsUnicodeMode())
1886 return ::IsClipboardFormatAvailable(CF_UNICODETEXT) != 0;
1887 return false;
1890 class GlobalMemory {
1891 HGLOBAL hand;
1892 public:
1893 void *ptr;
1894 GlobalMemory() : hand(0), ptr(0) {
1896 explicit GlobalMemory(HGLOBAL hand_) : hand(hand_), ptr(0) {
1897 if (hand) {
1898 ptr = ::GlobalLock(hand);
1901 ~GlobalMemory() {
1902 PLATFORM_ASSERT(!ptr);
1904 void Allocate(size_t bytes) {
1905 hand = ::GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT, bytes);
1906 if (hand) {
1907 ptr = ::GlobalLock(hand);
1910 HGLOBAL Unlock() {
1911 PLATFORM_ASSERT(ptr);
1912 HGLOBAL handCopy = hand;
1913 ::GlobalUnlock(hand);
1914 ptr = 0;
1915 hand = 0;
1916 return handCopy;
1918 void SetClip(UINT uFormat) {
1919 ::SetClipboardData(uFormat, Unlock());
1921 operator bool() const {
1922 return ptr != 0;
1924 SIZE_T Size() {
1925 return ::GlobalSize(hand);
1929 void ScintillaWin::Paste() {
1930 if (!::OpenClipboard(MainHWND()))
1931 return;
1932 UndoGroup ug(pdoc);
1933 const bool isLine = SelectionEmpty() &&
1934 (::IsClipboardFormatAvailable(cfLineSelect) || ::IsClipboardFormatAvailable(cfVSLineTag));
1935 ClearSelection(multiPasteMode == SC_MULTIPASTE_EACH);
1936 bool isRectangular = (::IsClipboardFormatAvailable(cfColumnSelect) != 0);
1938 if (!isRectangular) {
1939 // Evaluate "Borland IDE Block Type" explicitly
1940 GlobalMemory memBorlandSelection(::GetClipboardData(cfBorlandIDEBlockType));
1941 if (memBorlandSelection) {
1942 isRectangular = (memBorlandSelection.Size() == 1) && (static_cast<BYTE *>(memBorlandSelection.ptr)[0] == 0x02);
1943 memBorlandSelection.Unlock();
1946 const PasteShape pasteShape = isRectangular ? pasteRectangular : (isLine ? pasteLine : pasteStream);
1948 // Always use CF_UNICODETEXT if available
1949 GlobalMemory memUSelection(::GetClipboardData(CF_UNICODETEXT));
1950 if (memUSelection) {
1951 wchar_t *uptr = static_cast<wchar_t *>(memUSelection.ptr);
1952 if (uptr) {
1953 unsigned int len;
1954 std::vector<char> putf;
1955 // Default Scintilla behaviour in Unicode mode
1956 if (IsUnicodeMode()) {
1957 unsigned int bytes = static_cast<unsigned int>(memUSelection.Size());
1958 len = UTF8Length(uptr, bytes / 2);
1959 putf.resize(len + 1);
1960 UTF8FromUTF16(uptr, bytes / 2, &putf[0], len);
1961 } else {
1962 // CF_UNICODETEXT available, but not in Unicode mode
1963 // Convert from Unicode to current Scintilla code page
1964 UINT cpDest = CodePageOfDocument();
1965 len = ::WideCharToMultiByte(cpDest, 0, uptr, -1,
1966 NULL, 0, NULL, NULL) - 1; // subtract 0 terminator
1967 putf.resize(len + 1);
1968 ::WideCharToMultiByte(cpDest, 0, uptr, -1,
1969 &putf[0], len + 1, NULL, NULL);
1972 InsertPasteShape(&putf[0], len, pasteShape);
1974 memUSelection.Unlock();
1975 } else {
1976 // CF_UNICODETEXT not available, paste ANSI text
1977 GlobalMemory memSelection(::GetClipboardData(CF_TEXT));
1978 if (memSelection) {
1979 char *ptr = static_cast<char *>(memSelection.ptr);
1980 if (ptr) {
1981 unsigned int bytes = static_cast<unsigned int>(memSelection.Size());
1982 unsigned int len = bytes;
1983 for (unsigned int i = 0; i < bytes; i++) {
1984 if ((len == bytes) && (0 == ptr[i]))
1985 len = i;
1988 // In Unicode mode, convert clipboard text to UTF-8
1989 if (IsUnicodeMode()) {
1990 std::vector<wchar_t> uptr(len+1);
1992 unsigned int ulen = ::MultiByteToWideChar(CP_ACP, 0,
1993 ptr, len, &uptr[0], len+1);
1995 unsigned int mlen = UTF8Length(&uptr[0], ulen);
1996 std::vector<char> putf(mlen+1);
1997 // CP_UTF8 not available on Windows 95, so use UTF8FromUTF16()
1998 UTF8FromUTF16(&uptr[0], ulen, &putf[0], mlen);
2000 InsertPasteShape(&putf[0], mlen, pasteShape);
2001 } else {
2002 InsertPasteShape(ptr, len, pasteShape);
2005 memSelection.Unlock();
2008 ::CloseClipboard();
2009 Redraw();
2012 void ScintillaWin::CreateCallTipWindow(PRectangle) {
2013 if (!ct.wCallTip.Created()) {
2014 ct.wCallTip = ::CreateWindow(callClassName, TEXT("ACallTip"),
2015 WS_POPUP, 100, 100, 150, 20,
2016 MainHWND(), 0,
2017 GetWindowInstance(MainHWND()),
2018 this);
2019 ct.wDraw = ct.wCallTip;
2023 void ScintillaWin::AddToPopUp(const char *label, int cmd, bool enabled) {
2024 HMENU hmenuPopup = reinterpret_cast<HMENU>(popup.GetID());
2025 if (!label[0])
2026 ::AppendMenuA(hmenuPopup, MF_SEPARATOR, 0, "");
2027 else if (enabled)
2028 ::AppendMenuA(hmenuPopup, MF_STRING, cmd, label);
2029 else
2030 ::AppendMenuA(hmenuPopup, MF_STRING | MF_DISABLED | MF_GRAYED, cmd, label);
2033 void ScintillaWin::ClaimSelection() {
2034 // Windows does not have a primary selection
2037 /// Implement IUnknown
2039 STDMETHODIMP_(ULONG)FormatEnumerator_AddRef(FormatEnumerator *fe);
2040 STDMETHODIMP FormatEnumerator_QueryInterface(FormatEnumerator *fe, REFIID riid, PVOID *ppv) {
2041 //Platform::DebugPrintf("EFE QI");
2042 *ppv = NULL;
2043 if (riid == IID_IUnknown)
2044 *ppv = reinterpret_cast<IEnumFORMATETC *>(fe);
2045 if (riid == IID_IEnumFORMATETC)
2046 *ppv = reinterpret_cast<IEnumFORMATETC *>(fe);
2047 if (!*ppv)
2048 return E_NOINTERFACE;
2049 FormatEnumerator_AddRef(fe);
2050 return S_OK;
2052 STDMETHODIMP_(ULONG)FormatEnumerator_AddRef(FormatEnumerator *fe) {
2053 return ++fe->ref;
2055 STDMETHODIMP_(ULONG)FormatEnumerator_Release(FormatEnumerator *fe) {
2056 fe->ref--;
2057 if (fe->ref > 0)
2058 return fe->ref;
2059 delete fe;
2060 return 0;
2062 /// Implement IEnumFORMATETC
2063 STDMETHODIMP FormatEnumerator_Next(FormatEnumerator *fe, ULONG celt, FORMATETC *rgelt, ULONG *pceltFetched) {
2064 if (rgelt == NULL) return E_POINTER;
2065 unsigned int putPos = 0;
2066 while ((fe->pos < fe->formats.size()) && (putPos < celt)) {
2067 rgelt->cfFormat = fe->formats[fe->pos];
2068 rgelt->ptd = 0;
2069 rgelt->dwAspect = DVASPECT_CONTENT;
2070 rgelt->lindex = -1;
2071 rgelt->tymed = TYMED_HGLOBAL;
2072 rgelt++;
2073 fe->pos++;
2074 putPos++;
2076 if (pceltFetched)
2077 *pceltFetched = putPos;
2078 return putPos ? S_OK : S_FALSE;
2080 STDMETHODIMP FormatEnumerator_Skip(FormatEnumerator *fe, ULONG celt) {
2081 fe->pos += celt;
2082 return S_OK;
2084 STDMETHODIMP FormatEnumerator_Reset(FormatEnumerator *fe) {
2085 fe->pos = 0;
2086 return S_OK;
2088 STDMETHODIMP FormatEnumerator_Clone(FormatEnumerator *fe, IEnumFORMATETC **ppenum) {
2089 FormatEnumerator *pfe;
2090 try {
2091 pfe = new FormatEnumerator(fe->pos, &fe->formats[0], fe->formats.size());
2092 } catch (...) {
2093 return E_OUTOFMEMORY;
2095 return FormatEnumerator_QueryInterface(pfe, IID_IEnumFORMATETC,
2096 reinterpret_cast<void **>(ppenum));
2099 static VFunction *vtFormatEnumerator[] = {
2100 (VFunction *)(FormatEnumerator_QueryInterface),
2101 (VFunction *)(FormatEnumerator_AddRef),
2102 (VFunction *)(FormatEnumerator_Release),
2103 (VFunction *)(FormatEnumerator_Next),
2104 (VFunction *)(FormatEnumerator_Skip),
2105 (VFunction *)(FormatEnumerator_Reset),
2106 (VFunction *)(FormatEnumerator_Clone)
2109 FormatEnumerator::FormatEnumerator(int pos_, CLIPFORMAT formats_[], size_t formatsLen_) {
2110 vtbl = vtFormatEnumerator;
2111 ref = 0; // First QI adds first reference...
2112 pos = pos_;
2113 formats.insert(formats.begin(), formats_, formats_+formatsLen_);
2116 /// Implement IUnknown
2117 STDMETHODIMP DropSource_QueryInterface(DropSource *ds, REFIID riid, PVOID *ppv) {
2118 return ds->sci->QueryInterface(riid, ppv);
2120 STDMETHODIMP_(ULONG)DropSource_AddRef(DropSource *ds) {
2121 return ds->sci->AddRef();
2123 STDMETHODIMP_(ULONG)DropSource_Release(DropSource *ds) {
2124 return ds->sci->Release();
2127 /// Implement IDropSource
2128 STDMETHODIMP DropSource_QueryContinueDrag(DropSource *, BOOL fEsc, DWORD grfKeyState) {
2129 if (fEsc)
2130 return DRAGDROP_S_CANCEL;
2131 if (!(grfKeyState & MK_LBUTTON))
2132 return DRAGDROP_S_DROP;
2133 return S_OK;
2136 STDMETHODIMP DropSource_GiveFeedback(DropSource *, DWORD) {
2137 return DRAGDROP_S_USEDEFAULTCURSORS;
2140 static VFunction *vtDropSource[] = {
2141 (VFunction *)(DropSource_QueryInterface),
2142 (VFunction *)(DropSource_AddRef),
2143 (VFunction *)(DropSource_Release),
2144 (VFunction *)(DropSource_QueryContinueDrag),
2145 (VFunction *)(DropSource_GiveFeedback)
2148 DropSource::DropSource() {
2149 vtbl = vtDropSource;
2150 sci = 0;
2153 /// Implement IUnkown
2154 STDMETHODIMP DataObject_QueryInterface(DataObject *pd, REFIID riid, PVOID *ppv) {
2155 //Platform::DebugPrintf("DO QI %x\n", pd);
2156 return pd->sci->QueryInterface(riid, ppv);
2158 STDMETHODIMP_(ULONG)DataObject_AddRef(DataObject *pd) {
2159 return pd->sci->AddRef();
2161 STDMETHODIMP_(ULONG)DataObject_Release(DataObject *pd) {
2162 return pd->sci->Release();
2164 /// Implement IDataObject
2165 STDMETHODIMP DataObject_GetData(DataObject *pd, FORMATETC *pFEIn, STGMEDIUM *pSTM) {
2166 return pd->sci->GetData(pFEIn, pSTM);
2169 STDMETHODIMP DataObject_GetDataHere(DataObject *, FORMATETC *, STGMEDIUM *) {
2170 //Platform::DebugPrintf("DOB GetDataHere\n");
2171 return E_NOTIMPL;
2174 STDMETHODIMP DataObject_QueryGetData(DataObject *pd, FORMATETC *pFE) {
2175 if (pd->sci->DragIsRectangularOK(pFE->cfFormat) &&
2176 pFE->ptd == 0 &&
2177 (pFE->dwAspect & DVASPECT_CONTENT) != 0 &&
2178 pFE->lindex == -1 &&
2179 (pFE->tymed & TYMED_HGLOBAL) != 0
2181 return S_OK;
2184 bool formatOK = (pFE->cfFormat == CF_TEXT) ||
2185 ((pFE->cfFormat == CF_UNICODETEXT) && pd->sci->IsUnicodeMode());
2186 if (!formatOK ||
2187 pFE->ptd != 0 ||
2188 (pFE->dwAspect & DVASPECT_CONTENT) == 0 ||
2189 pFE->lindex != -1 ||
2190 (pFE->tymed & TYMED_HGLOBAL) == 0
2192 //Platform::DebugPrintf("DOB QueryGetData No %x\n",pFE->cfFormat);
2193 //return DATA_E_FORMATETC;
2194 return S_FALSE;
2196 //Platform::DebugPrintf("DOB QueryGetData OK %x\n",pFE->cfFormat);
2197 return S_OK;
2200 STDMETHODIMP DataObject_GetCanonicalFormatEtc(DataObject *pd, FORMATETC *, FORMATETC *pFEOut) {
2201 //Platform::DebugPrintf("DOB GetCanon\n");
2202 if (pd->sci->IsUnicodeMode())
2203 pFEOut->cfFormat = CF_UNICODETEXT;
2204 else
2205 pFEOut->cfFormat = CF_TEXT;
2206 pFEOut->ptd = 0;
2207 pFEOut->dwAspect = DVASPECT_CONTENT;
2208 pFEOut->lindex = -1;
2209 pFEOut->tymed = TYMED_HGLOBAL;
2210 return S_OK;
2213 STDMETHODIMP DataObject_SetData(DataObject *, FORMATETC *, STGMEDIUM *, BOOL) {
2214 //Platform::DebugPrintf("DOB SetData\n");
2215 return E_FAIL;
2218 STDMETHODIMP DataObject_EnumFormatEtc(DataObject *pd, DWORD dwDirection, IEnumFORMATETC **ppEnum) {
2219 try {
2220 //Platform::DebugPrintf("DOB EnumFormatEtc %d\n", dwDirection);
2221 if (dwDirection != DATADIR_GET) {
2222 *ppEnum = 0;
2223 return E_FAIL;
2225 FormatEnumerator *pfe;
2226 if (pd->sci->IsUnicodeMode()) {
2227 CLIPFORMAT formats[] = {CF_UNICODETEXT, CF_TEXT};
2228 pfe = new FormatEnumerator(0, formats, ELEMENTS(formats));
2229 } else {
2230 CLIPFORMAT formats[] = {CF_TEXT};
2231 pfe = new FormatEnumerator(0, formats, ELEMENTS(formats));
2233 return FormatEnumerator_QueryInterface(pfe, IID_IEnumFORMATETC,
2234 reinterpret_cast<void **>(ppEnum));
2235 } catch (std::bad_alloc &) {
2236 pd->sci->errorStatus = SC_STATUS_BADALLOC;
2237 return E_OUTOFMEMORY;
2238 } catch (...) {
2239 pd->sci->errorStatus = SC_STATUS_FAILURE;
2240 return E_FAIL;
2244 STDMETHODIMP DataObject_DAdvise(DataObject *, FORMATETC *, DWORD, IAdviseSink *, PDWORD) {
2245 //Platform::DebugPrintf("DOB DAdvise\n");
2246 return E_FAIL;
2249 STDMETHODIMP DataObject_DUnadvise(DataObject *, DWORD) {
2250 //Platform::DebugPrintf("DOB DUnadvise\n");
2251 return E_FAIL;
2254 STDMETHODIMP DataObject_EnumDAdvise(DataObject *, IEnumSTATDATA **) {
2255 //Platform::DebugPrintf("DOB EnumDAdvise\n");
2256 return E_FAIL;
2259 static VFunction *vtDataObject[] = {
2260 (VFunction *)(DataObject_QueryInterface),
2261 (VFunction *)(DataObject_AddRef),
2262 (VFunction *)(DataObject_Release),
2263 (VFunction *)(DataObject_GetData),
2264 (VFunction *)(DataObject_GetDataHere),
2265 (VFunction *)(DataObject_QueryGetData),
2266 (VFunction *)(DataObject_GetCanonicalFormatEtc),
2267 (VFunction *)(DataObject_SetData),
2268 (VFunction *)(DataObject_EnumFormatEtc),
2269 (VFunction *)(DataObject_DAdvise),
2270 (VFunction *)(DataObject_DUnadvise),
2271 (VFunction *)(DataObject_EnumDAdvise)
2274 DataObject::DataObject() {
2275 vtbl = vtDataObject;
2276 sci = 0;
2279 /// Implement IUnknown
2280 STDMETHODIMP DropTarget_QueryInterface(DropTarget *dt, REFIID riid, PVOID *ppv) {
2281 //Platform::DebugPrintf("DT QI %x\n", dt);
2282 return dt->sci->QueryInterface(riid, ppv);
2284 STDMETHODIMP_(ULONG)DropTarget_AddRef(DropTarget *dt) {
2285 return dt->sci->AddRef();
2287 STDMETHODIMP_(ULONG)DropTarget_Release(DropTarget *dt) {
2288 return dt->sci->Release();
2291 /// Implement IDropTarget by forwarding to Scintilla
2292 STDMETHODIMP DropTarget_DragEnter(DropTarget *dt, LPDATAOBJECT pIDataSource, DWORD grfKeyState,
2293 POINTL pt, PDWORD pdwEffect) {
2294 try {
2295 return dt->sci->DragEnter(pIDataSource, grfKeyState, pt, pdwEffect);
2296 } catch (...) {
2297 dt->sci->errorStatus = SC_STATUS_FAILURE;
2299 return E_FAIL;
2301 STDMETHODIMP DropTarget_DragOver(DropTarget *dt, DWORD grfKeyState, POINTL pt, PDWORD pdwEffect) {
2302 try {
2303 return dt->sci->DragOver(grfKeyState, pt, pdwEffect);
2304 } catch (...) {
2305 dt->sci->errorStatus = SC_STATUS_FAILURE;
2307 return E_FAIL;
2309 STDMETHODIMP DropTarget_DragLeave(DropTarget *dt) {
2310 try {
2311 return dt->sci->DragLeave();
2312 } catch (...) {
2313 dt->sci->errorStatus = SC_STATUS_FAILURE;
2315 return E_FAIL;
2317 STDMETHODIMP DropTarget_Drop(DropTarget *dt, LPDATAOBJECT pIDataSource, DWORD grfKeyState,
2318 POINTL pt, PDWORD pdwEffect) {
2319 try {
2320 return dt->sci->Drop(pIDataSource, grfKeyState, pt, pdwEffect);
2321 } catch (...) {
2322 dt->sci->errorStatus = SC_STATUS_FAILURE;
2324 return E_FAIL;
2327 static VFunction *vtDropTarget[] = {
2328 (VFunction *)(DropTarget_QueryInterface),
2329 (VFunction *)(DropTarget_AddRef),
2330 (VFunction *)(DropTarget_Release),
2331 (VFunction *)(DropTarget_DragEnter),
2332 (VFunction *)(DropTarget_DragOver),
2333 (VFunction *)(DropTarget_DragLeave),
2334 (VFunction *)(DropTarget_Drop)
2337 DropTarget::DropTarget() {
2338 vtbl = vtDropTarget;
2339 sci = 0;
2343 * DBCS: support Input Method Editor (IME).
2344 * Called when IME Window opened.
2346 void ScintillaWin::ImeStartComposition() {
2347 if (caret.active) {
2348 // Move IME Window to current caret position
2349 HIMC hIMC = ::ImmGetContext(MainHWND());
2350 Point pos = PointMainCaret();
2351 COMPOSITIONFORM CompForm;
2352 CompForm.dwStyle = CFS_POINT;
2353 CompForm.ptCurrentPos.x = static_cast<int>(pos.x);
2354 CompForm.ptCurrentPos.y = static_cast<int>(pos.y);
2356 ::ImmSetCompositionWindow(hIMC, &CompForm);
2358 // Set font of IME window to same as surrounded text.
2359 if (stylesValid) {
2360 // Since the style creation code has been made platform independent,
2361 // The logfont for the IME is recreated here.
2362 int styleHere = (pdoc->StyleAt(sel.MainCaret())) & 31;
2363 LOGFONTA lf = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ""};
2364 int sizeZoomed = vs.styles[styleHere].size + vs.zoomLevel * SC_FONT_SIZE_MULTIPLIER;
2365 if (sizeZoomed <= 2 * SC_FONT_SIZE_MULTIPLIER) // Hangs if sizeZoomed <= 1
2366 sizeZoomed = 2 * SC_FONT_SIZE_MULTIPLIER;
2367 AutoSurface surface(this);
2368 int deviceHeight = sizeZoomed;
2369 if (surface) {
2370 deviceHeight = (sizeZoomed * surface->LogPixelsY()) / 72;
2372 // The negative is to allow for leading
2373 lf.lfHeight = -(abs(deviceHeight / SC_FONT_SIZE_MULTIPLIER));
2374 lf.lfWeight = vs.styles[styleHere].weight;
2375 lf.lfItalic = static_cast<BYTE>(vs.styles[styleHere].italic ? 1 : 0);
2376 lf.lfCharSet = DEFAULT_CHARSET;
2377 lf.lfFaceName[0] = '\0';
2378 if (vs.styles[styleHere].fontName)
2379 StringCopy(lf.lfFaceName, vs.styles[styleHere].fontName);
2381 ::ImmSetCompositionFontA(hIMC, &lf);
2383 ::ImmReleaseContext(MainHWND(), hIMC);
2384 // Caret is displayed in IME window. So, caret in Scintilla is useless.
2385 DropCaret();
2389 /** Called when IME Window closed. */
2390 void ScintillaWin::ImeEndComposition() {
2391 ShowCaretAtCurrentPosition();
2394 void ScintillaWin::AddCharBytes(char b0, char b1) {
2396 int inputCodePage = InputCodePage();
2397 if (inputCodePage && IsUnicodeMode()) {
2398 char utfval[4] = "\0\0\0";
2399 char ansiChars[3];
2400 wchar_t wcs[2];
2401 if (b0) { // Two bytes from IME
2402 ansiChars[0] = b0;
2403 ansiChars[1] = b1;
2404 ansiChars[2] = '\0';
2405 ::MultiByteToWideChar(inputCodePage, 0, ansiChars, 2, wcs, 1);
2406 } else {
2407 ansiChars[0] = b1;
2408 ansiChars[1] = '\0';
2409 ::MultiByteToWideChar(inputCodePage, 0, ansiChars, 1, wcs, 1);
2411 unsigned int len = UTF8Length(wcs, 1);
2412 UTF8FromUTF16(wcs, 1, utfval, len);
2413 utfval[len] = '\0';
2414 AddCharUTF(utfval, len ? len : 1);
2415 } else if (b0) {
2416 char dbcsChars[3];
2417 dbcsChars[0] = b0;
2418 dbcsChars[1] = b1;
2419 dbcsChars[2] = '\0';
2420 AddCharUTF(dbcsChars, 2, true);
2421 } else {
2422 AddChar(b1);
2426 void ScintillaWin::GetIntelliMouseParameters() {
2427 // This retrieves the number of lines per scroll as configured inthe Mouse Properties sheet in Control Panel
2428 ::SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, &linesPerScroll, 0);
2431 void ScintillaWin::CopyToClipboard(const SelectionText &selectedText) {
2432 if (!::OpenClipboard(MainHWND()))
2433 return;
2434 ::EmptyClipboard();
2436 GlobalMemory uniText;
2438 // Default Scintilla behaviour in Unicode mode
2439 if (IsUnicodeMode()) {
2440 int uchars = UTF16Length(selectedText.Data(),
2441 static_cast<int>(selectedText.LengthWithTerminator()));
2442 uniText.Allocate(2 * uchars);
2443 if (uniText) {
2444 UTF16FromUTF8(selectedText.Data(), static_cast<int>(selectedText.LengthWithTerminator()),
2445 static_cast<wchar_t *>(uniText.ptr), uchars);
2447 } else {
2448 // Not Unicode mode
2449 // Convert to Unicode using the current Scintilla code page
2450 UINT cpSrc = CodePageFromCharSet(
2451 selectedText.characterSet, selectedText.codePage);
2452 int uLen = ::MultiByteToWideChar(cpSrc, 0, selectedText.Data(),
2453 static_cast<int>(selectedText.LengthWithTerminator()), 0, 0);
2454 uniText.Allocate(2 * uLen);
2455 if (uniText) {
2456 ::MultiByteToWideChar(cpSrc, 0, selectedText.Data(),
2457 static_cast<int>(selectedText.LengthWithTerminator()),
2458 static_cast<wchar_t *>(uniText.ptr), uLen);
2462 if (uniText) {
2463 if (!IsNT()) {
2464 // Copy ANSI text to clipboard on Windows 9x
2465 // Convert from Unicode text, so other ANSI programs can
2466 // paste the text
2467 // Windows NT, 2k, XP automatically generates CF_TEXT
2468 GlobalMemory ansiText;
2469 ansiText.Allocate(selectedText.LengthWithTerminator());
2470 if (ansiText) {
2471 ::WideCharToMultiByte(CP_ACP, 0, static_cast<wchar_t *>(uniText.ptr), -1,
2472 static_cast<char *>(ansiText.ptr),
2473 static_cast<int>(selectedText.LengthWithTerminator()), NULL, NULL);
2474 ansiText.SetClip(CF_TEXT);
2477 uniText.SetClip(CF_UNICODETEXT);
2478 } else {
2479 // There was a failure - try to copy at least ANSI text
2480 GlobalMemory ansiText;
2481 ansiText.Allocate(selectedText.LengthWithTerminator());
2482 if (ansiText) {
2483 memcpy(static_cast<char *>(ansiText.ptr), selectedText.Data(), selectedText.LengthWithTerminator());
2484 ansiText.SetClip(CF_TEXT);
2488 if (selectedText.rectangular) {
2489 ::SetClipboardData(cfColumnSelect, 0);
2491 GlobalMemory borlandSelection;
2492 borlandSelection.Allocate(1);
2493 if (borlandSelection) {
2494 static_cast<BYTE *>(borlandSelection.ptr)[0] = 0x02;
2495 borlandSelection.SetClip(cfBorlandIDEBlockType);
2499 if (selectedText.lineCopy) {
2500 ::SetClipboardData(cfLineSelect, 0);
2501 ::SetClipboardData(cfVSLineTag, 0);
2504 ::CloseClipboard();
2507 void ScintillaWin::ScrollMessage(WPARAM wParam) {
2508 //DWORD dwStart = timeGetTime();
2509 //Platform::DebugPrintf("Scroll %x %d\n", wParam, lParam);
2511 SCROLLINFO sci = {};
2512 sci.cbSize = sizeof(sci);
2513 sci.fMask = SIF_ALL;
2515 GetScrollInfo(SB_VERT, &sci);
2517 //Platform::DebugPrintf("ScrollInfo %d mask=%x min=%d max=%d page=%d pos=%d track=%d\n", b,sci.fMask,
2518 //sci.nMin, sci.nMax, sci.nPage, sci.nPos, sci.nTrackPos);
2520 int topLineNew = topLine;
2521 switch (LoWord(wParam)) {
2522 case SB_LINEUP:
2523 topLineNew -= 1;
2524 break;
2525 case SB_LINEDOWN:
2526 topLineNew += 1;
2527 break;
2528 case SB_PAGEUP:
2529 topLineNew -= LinesToScroll(); break;
2530 case SB_PAGEDOWN: topLineNew += LinesToScroll(); break;
2531 case SB_TOP: topLineNew = 0; break;
2532 case SB_BOTTOM: topLineNew = MaxScrollPos(); break;
2533 case SB_THUMBPOSITION: topLineNew = sci.nTrackPos; break;
2534 case SB_THUMBTRACK: topLineNew = sci.nTrackPos; break;
2536 ScrollTo(topLineNew);
2539 void ScintillaWin::HorizontalScrollMessage(WPARAM wParam) {
2540 int xPos = xOffset;
2541 PRectangle rcText = GetTextRectangle();
2542 int pageWidth = static_cast<int>(rcText.Width() * 2 / 3);
2543 switch (LoWord(wParam)) {
2544 case SB_LINEUP:
2545 xPos -= 20;
2546 break;
2547 case SB_LINEDOWN: // May move past the logical end
2548 xPos += 20;
2549 break;
2550 case SB_PAGEUP:
2551 xPos -= pageWidth;
2552 break;
2553 case SB_PAGEDOWN:
2554 xPos += pageWidth;
2555 if (xPos > scrollWidth - rcText.Width()) { // Hit the end exactly
2556 xPos = scrollWidth - static_cast<int>(rcText.Width());
2558 break;
2559 case SB_TOP:
2560 xPos = 0;
2561 break;
2562 case SB_BOTTOM:
2563 xPos = scrollWidth;
2564 break;
2565 case SB_THUMBPOSITION:
2566 case SB_THUMBTRACK: {
2567 // 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 =]
2568 SCROLLINFO si;
2569 si.cbSize = sizeof(si);
2570 si.fMask = SIF_TRACKPOS;
2571 if (GetScrollInfo(SB_HORZ, &si)) {
2572 xPos = si.nTrackPos;
2575 break;
2577 HorizontalScrollTo(xPos);
2581 * Redraw all of text area.
2582 * This paint will not be abandoned.
2584 void ScintillaWin::FullPaint() {
2585 if ((technology == SC_TECHNOLOGY_DEFAULT) || (technology == SC_TECHNOLOGY_DIRECTWRITEDC)) {
2586 HDC hdc = ::GetDC(MainHWND());
2587 FullPaintDC(hdc);
2588 ::ReleaseDC(MainHWND(), hdc);
2589 } else {
2590 FullPaintDC(0);
2595 * Redraw all of text area on the specified DC.
2596 * This paint will not be abandoned.
2598 void ScintillaWin::FullPaintDC(HDC hdc) {
2599 paintState = painting;
2600 rcPaint = GetClientRectangle();
2601 paintingAllText = true;
2602 if (technology == SC_TECHNOLOGY_DEFAULT) {
2603 AutoSurface surfaceWindow(hdc, this);
2604 if (surfaceWindow) {
2605 Paint(surfaceWindow, rcPaint);
2606 surfaceWindow->Release();
2608 } else {
2609 #if defined(USE_D2D)
2610 EnsureRenderTarget(hdc);
2611 AutoSurface surfaceWindow(pRenderTarget, this);
2612 if (surfaceWindow) {
2613 pRenderTarget->BeginDraw();
2614 Paint(surfaceWindow, rcPaint);
2615 surfaceWindow->Release();
2616 HRESULT hr = pRenderTarget->EndDraw();
2617 if (hr == D2DERR_RECREATE_TARGET) {
2618 DropRenderTarget();
2621 #endif
2623 paintState = notPainting;
2626 static bool CompareDevCap(HDC hdc, HDC hOtherDC, int nIndex) {
2627 return ::GetDeviceCaps(hdc, nIndex) == ::GetDeviceCaps(hOtherDC, nIndex);
2630 bool ScintillaWin::IsCompatibleDC(HDC hOtherDC) {
2631 HDC hdc = ::GetDC(MainHWND());
2632 bool isCompatible =
2633 CompareDevCap(hdc, hOtherDC, TECHNOLOGY) &&
2634 CompareDevCap(hdc, hOtherDC, LOGPIXELSY) &&
2635 CompareDevCap(hdc, hOtherDC, LOGPIXELSX) &&
2636 CompareDevCap(hdc, hOtherDC, BITSPIXEL) &&
2637 CompareDevCap(hdc, hOtherDC, PLANES);
2638 ::ReleaseDC(MainHWND(), hdc);
2639 return isCompatible;
2642 DWORD ScintillaWin::EffectFromState(DWORD grfKeyState) const {
2643 // These are the Wordpad semantics.
2644 DWORD dwEffect;
2645 if (inDragDrop == ddDragging) // Internal defaults to move
2646 dwEffect = DROPEFFECT_MOVE;
2647 else
2648 dwEffect = DROPEFFECT_COPY;
2649 if (grfKeyState & MK_ALT)
2650 dwEffect = DROPEFFECT_MOVE;
2651 if (grfKeyState & MK_CONTROL)
2652 dwEffect = DROPEFFECT_COPY;
2653 return dwEffect;
2656 /// Implement IUnknown
2657 STDMETHODIMP ScintillaWin::QueryInterface(REFIID riid, PVOID *ppv) {
2658 *ppv = NULL;
2659 if (riid == IID_IUnknown)
2660 *ppv = reinterpret_cast<IDropTarget *>(&dt);
2661 if (riid == IID_IDropSource)
2662 *ppv = reinterpret_cast<IDropSource *>(&ds);
2663 if (riid == IID_IDropTarget)
2664 *ppv = reinterpret_cast<IDropTarget *>(&dt);
2665 if (riid == IID_IDataObject)
2666 *ppv = reinterpret_cast<IDataObject *>(&dob);
2667 if (!*ppv)
2668 return E_NOINTERFACE;
2669 return S_OK;
2672 STDMETHODIMP_(ULONG) ScintillaWin::AddRef() {
2673 return 1;
2676 STDMETHODIMP_(ULONG) ScintillaWin::Release() {
2677 return 1;
2680 /// Implement IDropTarget
2681 STDMETHODIMP ScintillaWin::DragEnter(LPDATAOBJECT pIDataSource, DWORD grfKeyState,
2682 POINTL, PDWORD pdwEffect) {
2683 if (pIDataSource == NULL)
2684 return E_POINTER;
2685 FORMATETC fmtu = {CF_UNICODETEXT, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
2686 HRESULT hrHasUText = pIDataSource->QueryGetData(&fmtu);
2687 hasOKText = (hrHasUText == S_OK);
2688 if (!hasOKText) {
2689 FORMATETC fmte = {CF_TEXT, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
2690 HRESULT hrHasText = pIDataSource->QueryGetData(&fmte);
2691 hasOKText = (hrHasText == S_OK);
2693 if (!hasOKText) {
2694 *pdwEffect = DROPEFFECT_NONE;
2695 return S_OK;
2698 *pdwEffect = EffectFromState(grfKeyState);
2699 return S_OK;
2702 STDMETHODIMP ScintillaWin::DragOver(DWORD grfKeyState, POINTL pt, PDWORD pdwEffect) {
2703 try {
2704 if (!hasOKText || pdoc->IsReadOnly()) {
2705 *pdwEffect = DROPEFFECT_NONE;
2706 return S_OK;
2709 *pdwEffect = EffectFromState(grfKeyState);
2711 // Update the cursor.
2712 POINT rpt = {pt.x, pt.y};
2713 ::ScreenToClient(MainHWND(), &rpt);
2714 SetDragPosition(SPositionFromLocation(PointFromPOINT(rpt), false, false, UserVirtualSpace()));
2716 return S_OK;
2717 } catch (...) {
2718 errorStatus = SC_STATUS_FAILURE;
2720 return E_FAIL;
2723 STDMETHODIMP ScintillaWin::DragLeave() {
2724 try {
2725 SetDragPosition(SelectionPosition(invalidPosition));
2726 return S_OK;
2727 } catch (...) {
2728 errorStatus = SC_STATUS_FAILURE;
2730 return E_FAIL;
2733 STDMETHODIMP ScintillaWin::Drop(LPDATAOBJECT pIDataSource, DWORD grfKeyState,
2734 POINTL pt, PDWORD pdwEffect) {
2735 try {
2736 *pdwEffect = EffectFromState(grfKeyState);
2738 if (pIDataSource == NULL)
2739 return E_POINTER;
2741 SetDragPosition(SelectionPosition(invalidPosition));
2743 STGMEDIUM medium = {0, {0}, 0};
2745 std::vector<char> data; // Includes terminating NUL
2747 FORMATETC fmtu = {CF_UNICODETEXT, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
2748 HRESULT hr = pIDataSource->GetData(&fmtu, &medium);
2749 if (SUCCEEDED(hr) && medium.hGlobal) {
2750 GlobalMemory memUDrop(medium.hGlobal);
2751 wchar_t *udata = static_cast<wchar_t *>(memUDrop.ptr);
2752 if (udata) {
2753 if (IsUnicodeMode()) {
2754 int tlen = static_cast<int>(memUDrop.Size());
2755 // Convert UTF-16 to UTF-8
2756 int dataLen = UTF8Length(udata, tlen/2);
2757 data.resize(dataLen+1);
2758 UTF8FromUTF16(udata, tlen/2, &data[0], dataLen);
2759 } else {
2760 // Convert UTF-16 to ANSI
2762 // Default Scintilla behavior in Unicode mode
2763 // CF_UNICODETEXT available, but not in Unicode mode
2764 // Convert from Unicode to current Scintilla code page
2765 UINT cpDest = CodePageOfDocument();
2766 int tlen = ::WideCharToMultiByte(cpDest, 0, udata, -1,
2767 NULL, 0, NULL, NULL) - 1; // subtract 0 terminator
2768 data.resize(tlen + 1);
2769 ::WideCharToMultiByte(cpDest, 0, udata, -1,
2770 &data[0], tlen + 1, NULL, NULL);
2773 memUDrop.Unlock();
2774 } else {
2775 FORMATETC fmte = {CF_TEXT, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
2776 hr = pIDataSource->GetData(&fmte, &medium);
2777 if (SUCCEEDED(hr) && medium.hGlobal) {
2778 GlobalMemory memDrop(medium.hGlobal);
2779 const char *cdata = static_cast<char *>(memDrop.ptr);
2780 if (cdata)
2781 data.assign(cdata, cdata+strlen(cdata)+1);
2782 memDrop.Unlock();
2786 if (!SUCCEEDED(hr) || data.empty()) {
2787 //Platform::DebugPrintf("Bad data format: 0x%x\n", hres);
2788 return hr;
2791 FORMATETC fmtr = {cfColumnSelect, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
2792 HRESULT hrRectangular = pIDataSource->QueryGetData(&fmtr);
2794 POINT rpt = {pt.x, pt.y};
2795 ::ScreenToClient(MainHWND(), &rpt);
2796 SelectionPosition movePos = SPositionFromLocation(PointFromPOINT(rpt), false, false, UserVirtualSpace());
2798 DropAt(movePos, &data[0], data.size() - 1, *pdwEffect == DROPEFFECT_MOVE, hrRectangular == S_OK);
2800 // Free data
2801 if (medium.pUnkForRelease != NULL)
2802 medium.pUnkForRelease->Release();
2803 else
2804 ::GlobalFree(medium.hGlobal);
2806 return S_OK;
2807 } catch (...) {
2808 errorStatus = SC_STATUS_FAILURE;
2810 return E_FAIL;
2813 /// Implement important part of IDataObject
2814 STDMETHODIMP ScintillaWin::GetData(FORMATETC *pFEIn, STGMEDIUM *pSTM) {
2815 bool formatOK = (pFEIn->cfFormat == CF_TEXT) ||
2816 ((pFEIn->cfFormat == CF_UNICODETEXT) && IsUnicodeMode());
2817 if (!formatOK ||
2818 pFEIn->ptd != 0 ||
2819 (pFEIn->dwAspect & DVASPECT_CONTENT) == 0 ||
2820 pFEIn->lindex != -1 ||
2821 (pFEIn->tymed & TYMED_HGLOBAL) == 0
2823 //Platform::DebugPrintf("DOB GetData No %d %x %x fmt=%x\n", lenDrag, pFEIn, pSTM, pFEIn->cfFormat);
2824 return DATA_E_FORMATETC;
2826 pSTM->tymed = TYMED_HGLOBAL;
2827 //Platform::DebugPrintf("DOB GetData OK %d %x %x\n", lenDrag, pFEIn, pSTM);
2829 GlobalMemory text;
2830 if (pFEIn->cfFormat == CF_UNICODETEXT) {
2831 int uchars = UTF16Length(drag.Data(), static_cast<int>(drag.LengthWithTerminator()));
2832 text.Allocate(2 * uchars);
2833 if (text) {
2834 UTF16FromUTF8(drag.Data(), static_cast<int>(drag.LengthWithTerminator()),
2835 static_cast<wchar_t *>(text.ptr), uchars);
2837 } else {
2838 text.Allocate(drag.LengthWithTerminator());
2839 if (text) {
2840 memcpy(static_cast<char *>(text.ptr), drag.Data(), drag.LengthWithTerminator());
2843 pSTM->hGlobal = text ? text.Unlock() : 0;
2844 pSTM->pUnkForRelease = 0;
2845 return S_OK;
2848 bool ScintillaWin::Register(HINSTANCE hInstance_) {
2850 hInstance = hInstance_;
2851 bool result;
2853 // Register the Scintilla class
2854 if (IsNT()) {
2856 // Register Scintilla as a wide character window
2857 WNDCLASSEXW wndclass;
2858 wndclass.cbSize = sizeof(wndclass);
2859 wndclass.style = CS_GLOBALCLASS | CS_HREDRAW | CS_VREDRAW;
2860 wndclass.lpfnWndProc = ScintillaWin::SWndProc;
2861 wndclass.cbClsExtra = 0;
2862 wndclass.cbWndExtra = sizeof(ScintillaWin *);
2863 wndclass.hInstance = hInstance;
2864 wndclass.hIcon = NULL;
2865 wndclass.hCursor = NULL;
2866 wndclass.hbrBackground = NULL;
2867 wndclass.lpszMenuName = NULL;
2868 wndclass.lpszClassName = L"Scintilla";
2869 wndclass.hIconSm = 0;
2870 scintillaClassAtom = ::RegisterClassExW(&wndclass);
2871 result = 0 != scintillaClassAtom;
2872 } else {
2874 // Register Scintilla as a normal character window
2875 WNDCLASSEX wndclass;
2876 wndclass.cbSize = sizeof(wndclass);
2877 wndclass.style = CS_GLOBALCLASS | CS_HREDRAW | CS_VREDRAW;
2878 wndclass.lpfnWndProc = ScintillaWin::SWndProc;
2879 wndclass.cbClsExtra = 0;
2880 wndclass.cbWndExtra = sizeof(ScintillaWin *);
2881 wndclass.hInstance = hInstance;
2882 wndclass.hIcon = NULL;
2883 wndclass.hCursor = NULL;
2884 wndclass.hbrBackground = NULL;
2885 wndclass.lpszMenuName = NULL;
2886 wndclass.lpszClassName = scintillaClassName;
2887 wndclass.hIconSm = 0;
2888 scintillaClassAtom = ::RegisterClassEx(&wndclass);
2889 result = 0 != scintillaClassAtom;
2892 if (result) {
2893 // Register the CallTip class
2894 WNDCLASSEX wndclassc;
2895 wndclassc.cbSize = sizeof(wndclassc);
2896 wndclassc.style = CS_GLOBALCLASS | CS_HREDRAW | CS_VREDRAW;
2897 wndclassc.cbClsExtra = 0;
2898 wndclassc.cbWndExtra = sizeof(ScintillaWin *);
2899 wndclassc.hInstance = hInstance;
2900 wndclassc.hIcon = NULL;
2901 wndclassc.hbrBackground = NULL;
2902 wndclassc.lpszMenuName = NULL;
2903 wndclassc.lpfnWndProc = ScintillaWin::CTWndProc;
2904 wndclassc.hCursor = ::LoadCursor(NULL, IDC_ARROW);
2905 wndclassc.lpszClassName = callClassName;
2906 wndclassc.hIconSm = 0;
2908 callClassAtom = ::RegisterClassEx(&wndclassc);
2909 result = 0 != callClassAtom;
2912 return result;
2915 bool ScintillaWin::Unregister() {
2916 bool result = true;
2917 if (0 != scintillaClassAtom) {
2918 if (::UnregisterClass(MAKEINTATOM(scintillaClassAtom), hInstance) == 0) {
2919 result = false;
2921 scintillaClassAtom = 0;
2923 if (0 != callClassAtom) {
2924 if (::UnregisterClass(MAKEINTATOM(callClassAtom), hInstance) == 0) {
2925 result = false;
2927 callClassAtom = 0;
2929 return result;
2932 bool ScintillaWin::HasCaretSizeChanged() const {
2933 if (
2934 ( (0 != vs.caretWidth) && (sysCaretWidth != vs.caretWidth) )
2935 || ((0 != vs.lineHeight) && (sysCaretHeight != vs.lineHeight))
2937 return true;
2939 return false;
2942 BOOL ScintillaWin::CreateSystemCaret() {
2943 sysCaretWidth = vs.caretWidth;
2944 if (0 == sysCaretWidth) {
2945 sysCaretWidth = 1;
2947 sysCaretHeight = vs.lineHeight;
2948 int bitmapSize = (((sysCaretWidth + 15) & ~15) >> 3) *
2949 sysCaretHeight;
2950 std::vector<char> bits(bitmapSize);
2951 sysCaretBitmap = ::CreateBitmap(sysCaretWidth, sysCaretHeight, 1,
2952 1, reinterpret_cast<BYTE *>(&bits[0]));
2953 BOOL retval = ::CreateCaret(
2954 MainHWND(), sysCaretBitmap,
2955 sysCaretWidth, sysCaretHeight);
2956 if (technology == SC_TECHNOLOGY_DEFAULT) {
2957 // System caret interferes with Direct2D drawing so only show it for GDI.
2958 ::ShowCaret(MainHWND());
2960 return retval;
2963 BOOL ScintillaWin::DestroySystemCaret() {
2964 ::HideCaret(MainHWND());
2965 BOOL retval = ::DestroyCaret();
2966 if (sysCaretBitmap) {
2967 ::DeleteObject(sysCaretBitmap);
2968 sysCaretBitmap = 0;
2970 return retval;
2973 sptr_t PASCAL ScintillaWin::CTWndProc(
2974 HWND hWnd, UINT iMessage, WPARAM wParam, sptr_t lParam) {
2975 // Find C++ object associated with window.
2976 ScintillaWin *sciThis = reinterpret_cast<ScintillaWin *>(PointerFromWindow(hWnd));
2977 try {
2978 // ctp will be zero if WM_CREATE not seen yet
2979 if (sciThis == 0) {
2980 if (iMessage == WM_CREATE) {
2981 // Associate CallTip object with window
2982 CREATESTRUCT *pCreate = reinterpret_cast<CREATESTRUCT *>(lParam);
2983 SetWindowPointer(hWnd, pCreate->lpCreateParams);
2984 return 0;
2985 } else {
2986 return ::DefWindowProc(hWnd, iMessage, wParam, lParam);
2988 } else {
2989 if (iMessage == WM_NCDESTROY) {
2990 ::SetWindowLong(hWnd, 0, 0);
2991 return ::DefWindowProc(hWnd, iMessage, wParam, lParam);
2992 } else if (iMessage == WM_PAINT) {
2993 PAINTSTRUCT ps;
2994 ::BeginPaint(hWnd, &ps);
2995 Surface *surfaceWindow = Surface::Allocate(sciThis->technology);
2996 if (surfaceWindow) {
2997 #if defined(USE_D2D)
2998 ID2D1HwndRenderTarget *pCTRenderTarget = 0;
2999 #endif
3000 RECT rc;
3001 GetClientRect(hWnd, &rc);
3002 // Create a Direct2D render target.
3003 if (sciThis->technology == SC_TECHNOLOGY_DEFAULT) {
3004 surfaceWindow->Init(ps.hdc, hWnd);
3005 } else {
3006 #if defined(USE_D2D)
3007 D2D1_HWND_RENDER_TARGET_PROPERTIES dhrtp;
3008 dhrtp.hwnd = hWnd;
3009 dhrtp.pixelSize = D2D1::SizeU(rc.right - rc.left, rc.bottom - rc.top);
3010 dhrtp.presentOptions = (sciThis->technology == SC_TECHNOLOGY_DIRECTWRITERETAIN) ?
3011 D2D1_PRESENT_OPTIONS_RETAIN_CONTENTS : D2D1_PRESENT_OPTIONS_NONE;
3013 D2D1_RENDER_TARGET_PROPERTIES drtp;
3014 drtp.type = D2D1_RENDER_TARGET_TYPE_DEFAULT;
3015 drtp.pixelFormat.format = DXGI_FORMAT_UNKNOWN;
3016 drtp.pixelFormat.alphaMode = D2D1_ALPHA_MODE_UNKNOWN;
3017 drtp.dpiX = 96.0;
3018 drtp.dpiY = 96.0;
3019 drtp.usage = D2D1_RENDER_TARGET_USAGE_NONE;
3020 drtp.minLevel = D2D1_FEATURE_LEVEL_DEFAULT;
3022 if (!SUCCEEDED(pD2DFactory->CreateHwndRenderTarget(drtp, dhrtp, &pCTRenderTarget))) {
3023 surfaceWindow->Release();
3024 delete surfaceWindow;
3025 ::EndPaint(hWnd, &ps);
3026 return 0;
3028 surfaceWindow->Init(pCTRenderTarget, hWnd);
3029 pCTRenderTarget->BeginDraw();
3030 #endif
3032 surfaceWindow->SetUnicodeMode(SC_CP_UTF8 == sciThis->ct.codePage);
3033 surfaceWindow->SetDBCSMode(sciThis->ct.codePage);
3034 sciThis->ct.PaintCT(surfaceWindow);
3035 #if defined(USE_D2D)
3036 if (pCTRenderTarget)
3037 pCTRenderTarget->EndDraw();
3038 #endif
3039 surfaceWindow->Release();
3040 delete surfaceWindow;
3041 #if defined(USE_D2D)
3042 if (pCTRenderTarget)
3043 pCTRenderTarget->Release();
3044 #endif
3046 ::EndPaint(hWnd, &ps);
3047 return 0;
3048 } else if ((iMessage == WM_NCLBUTTONDOWN) || (iMessage == WM_NCLBUTTONDBLCLK)) {
3049 POINT pt;
3050 pt.x = static_cast<short>(LOWORD(lParam));
3051 pt.y = static_cast<short>(HIWORD(lParam));
3052 ScreenToClient(hWnd, &pt);
3053 sciThis->ct.MouseClick(PointFromPOINT(pt));
3054 sciThis->CallTipClick();
3055 return 0;
3056 } else if (iMessage == WM_LBUTTONDOWN) {
3057 // This does not fire due to the hit test code
3058 sciThis->ct.MouseClick(Point::FromLong(static_cast<long>(lParam)));
3059 sciThis->CallTipClick();
3060 return 0;
3061 } else if (iMessage == WM_SETCURSOR) {
3062 ::SetCursor(::LoadCursor(NULL, IDC_ARROW));
3063 return 0;
3064 } else if (iMessage == WM_NCHITTEST) {
3065 return HTCAPTION;
3066 } else {
3067 return ::DefWindowProc(hWnd, iMessage, wParam, lParam);
3070 } catch (...) {
3071 sciThis->errorStatus = SC_STATUS_FAILURE;
3073 return ::DefWindowProc(hWnd, iMessage, wParam, lParam);
3076 sptr_t ScintillaWin::DirectFunction(
3077 sptr_t ptr, UINT iMessage, uptr_t wParam, sptr_t lParam) {
3078 PLATFORM_ASSERT(::GetCurrentThreadId() == ::GetWindowThreadProcessId(reinterpret_cast<ScintillaWin *>(ptr)->MainHWND(), NULL));
3079 return reinterpret_cast<ScintillaWin *>(ptr)->WndProc(iMessage, wParam, lParam);
3082 extern "C"
3083 #ifndef STATIC_BUILD
3084 __declspec(dllexport)
3085 #endif
3086 sptr_t __stdcall Scintilla_DirectFunction(
3087 ScintillaWin *sci, UINT iMessage, uptr_t wParam, sptr_t lParam) {
3088 return sci->WndProc(iMessage, wParam, lParam);
3091 sptr_t PASCAL ScintillaWin::SWndProc(
3092 HWND hWnd, UINT iMessage, WPARAM wParam, sptr_t lParam) {
3093 //Platform::DebugPrintf("S W:%x M:%x WP:%x L:%x\n", hWnd, iMessage, wParam, lParam);
3095 // Find C++ object associated with window.
3096 ScintillaWin *sci = reinterpret_cast<ScintillaWin *>(PointerFromWindow(hWnd));
3097 // sci will be zero if WM_CREATE not seen yet
3098 if (sci == 0) {
3099 try {
3100 if (iMessage == WM_CREATE) {
3101 // Create C++ object associated with window
3102 sci = new ScintillaWin(hWnd);
3103 SetWindowPointer(hWnd, sci);
3104 return sci->WndProc(iMessage, wParam, lParam);
3106 } catch (...) {
3108 return ::DefWindowProc(hWnd, iMessage, wParam, lParam);
3109 } else {
3110 if (iMessage == WM_NCDESTROY) {
3111 try {
3112 sci->Finalise();
3113 delete sci;
3114 } catch (...) {
3116 ::SetWindowLong(hWnd, 0, 0);
3117 return ::DefWindowProc(hWnd, iMessage, wParam, lParam);
3118 } else {
3119 return sci->WndProc(iMessage, wParam, lParam);
3124 // This function is externally visible so it can be called from container when building statically.
3125 // Must be called once only.
3126 int Scintilla_RegisterClasses(void *hInstance) {
3127 Platform_Initialise(hInstance);
3128 bool result = ScintillaWin::Register(reinterpret_cast<HINSTANCE>(hInstance));
3129 #ifdef SCI_LEXER
3130 Scintilla_LinkLexers();
3131 #endif
3132 return result;
3135 static int ResourcesRelease(bool fromDllMain) {
3136 bool result = ScintillaWin::Unregister();
3137 if (commctrl32) {
3138 FreeLibrary(commctrl32);
3139 commctrl32 = NULL;
3141 Platform_Finalise(fromDllMain);
3142 return result;
3145 // This function is externally visible so it can be called from container when building statically.
3146 int Scintilla_ReleaseResources() {
3147 return ResourcesRelease(false);
3150 #ifndef STATIC_BUILD
3151 extern "C" int APIENTRY DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpvReserved) {
3152 //Platform::DebugPrintf("Scintilla::DllMain %d %d\n", hInstance, dwReason);
3153 if (dwReason == DLL_PROCESS_ATTACH) {
3154 if (!Scintilla_RegisterClasses(hInstance))
3155 return FALSE;
3156 } else if (dwReason == DLL_PROCESS_DETACH) {
3157 if (lpvReserved == NULL) {
3158 ResourcesRelease(true);
3161 return TRUE;
3163 #endif