Applied unicodefont.patch
[TortoiseGit.git] / ext / scintilla / win32 / ScintillaWin.cxx
blobcc4e9c27abf449935a8c575c17a004ec6ff1cdac
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 NotifyParent(SCNotification * scn);
269 virtual void NotifyDoubleClick(Point pt, int modifiers);
270 virtual CaseFolder *CaseFolderForEncoding();
271 virtual std::string CaseMapString(const std::string &s, int caseMapping);
272 virtual void Copy();
273 virtual void CopyAllowLine();
274 virtual bool CanPaste();
275 virtual void Paste();
276 virtual void CreateCallTipWindow(PRectangle rc);
277 virtual void AddToPopUp(const char *label, int cmd = 0, bool enabled = true);
278 virtual void ClaimSelection();
280 // DBCS
281 void ImeStartComposition();
282 void ImeEndComposition();
284 void AddCharBytes(char b0, char b1);
286 void GetIntelliMouseParameters();
287 virtual void CopyToClipboard(const SelectionText &selectedText);
288 void ScrollMessage(WPARAM wParam);
289 void HorizontalScrollMessage(WPARAM wParam);
290 void FullPaint();
291 void FullPaintDC(HDC dc);
292 bool IsCompatibleDC(HDC dc);
293 DWORD EffectFromState(DWORD grfKeyState) const;
295 virtual int SetScrollInfo(int nBar, LPCSCROLLINFO lpsi, BOOL bRedraw);
296 virtual bool GetScrollInfo(int nBar, LPSCROLLINFO lpsi);
297 void ChangeScrollPos(int barType, int pos);
298 sptr_t GetTextLength();
299 sptr_t GetText(uptr_t wParam, sptr_t lParam);
301 public:
302 // Public for benefit of Scintilla_DirectFunction
303 virtual sptr_t WndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam);
305 /// Implement IUnknown
306 STDMETHODIMP QueryInterface(REFIID riid, PVOID *ppv);
307 STDMETHODIMP_(ULONG)AddRef();
308 STDMETHODIMP_(ULONG)Release();
310 /// Implement IDropTarget
311 STDMETHODIMP DragEnter(LPDATAOBJECT pIDataSource, DWORD grfKeyState,
312 POINTL pt, PDWORD pdwEffect);
313 STDMETHODIMP DragOver(DWORD grfKeyState, POINTL pt, PDWORD pdwEffect);
314 STDMETHODIMP DragLeave();
315 STDMETHODIMP Drop(LPDATAOBJECT pIDataSource, DWORD grfKeyState,
316 POINTL pt, PDWORD pdwEffect);
318 /// Implement important part of IDataObject
319 STDMETHODIMP GetData(FORMATETC *pFEIn, STGMEDIUM *pSTM);
321 static bool Register(HINSTANCE hInstance_);
322 static bool Unregister();
324 friend class DropSource;
325 friend class DataObject;
326 friend class DropTarget;
327 bool DragIsRectangularOK(CLIPFORMAT fmt) const {
328 return drag.rectangular && (fmt == cfColumnSelect);
331 private:
332 // For use in creating a system caret
333 bool HasCaretSizeChanged() const;
334 BOOL CreateSystemCaret();
335 BOOL DestroySystemCaret();
336 HBITMAP sysCaretBitmap;
337 int sysCaretWidth;
338 int sysCaretHeight;
339 bool keysAlwaysUnicode;
342 HINSTANCE ScintillaWin::hInstance = 0;
343 ATOM ScintillaWin::scintillaClassAtom = 0;
344 ATOM ScintillaWin::callClassAtom = 0;
346 ScintillaWin::ScintillaWin(HWND hwnd) {
348 lastKeyDownConsumed = false;
350 capturedMouse = false;
351 trackedMouseLeave = false;
352 TrackMouseEventFn = 0;
353 SetCoalescableTimerFn = 0;
355 linesPerScroll = 0;
356 wheelDelta = 0; // Wheel delta from roll
358 hRgnUpdate = 0;
360 hasOKText = false;
362 // There does not seem to be a real standard for indicating that the clipboard
363 // contains a rectangular selection, so copy Developer Studio and Borland Delphi.
364 cfColumnSelect = static_cast<CLIPFORMAT>(
365 ::RegisterClipboardFormat(TEXT("MSDEVColumnSelect")));
366 cfBorlandIDEBlockType = static_cast<CLIPFORMAT>(
367 ::RegisterClipboardFormat(TEXT("Borland IDE Block Type")));
369 // Likewise for line-copy (copies a full line when no text is selected)
370 cfLineSelect = static_cast<CLIPFORMAT>(
371 ::RegisterClipboardFormat(TEXT("MSDEVLineSelect")));
372 cfVSLineTag = static_cast<CLIPFORMAT>(
373 ::RegisterClipboardFormat(TEXT("VisualStudioEditorOperationsLineCutCopyClipboardTag")));
374 hrOle = E_FAIL;
376 wMain = hwnd;
378 dob.sci = this;
379 ds.sci = this;
380 dt.sci = this;
382 sysCaretBitmap = 0;
383 sysCaretWidth = 0;
384 sysCaretHeight = 0;
386 #if defined(USE_D2D)
387 pRenderTarget = 0;
388 renderTargetValid = true;
389 #endif
391 keysAlwaysUnicode = false;
393 caret.period = ::GetCaretBlinkTime();
394 if (caret.period < 0)
395 caret.period = 0;
397 Initialise();
400 ScintillaWin::~ScintillaWin() {}
402 void ScintillaWin::Initialise() {
403 // Initialize COM. If the app has already done this it will have
404 // no effect. If the app hasnt, we really shouldnt ask them to call
405 // it just so this internal feature works.
406 hrOle = ::OleInitialize(NULL);
408 // Find TrackMouseEvent which is available on Windows > 95
409 HMODULE user32 = ::GetModuleHandle(TEXT("user32.dll"));
410 if (user32) {
411 TrackMouseEventFn = (TrackMouseEventSig)::GetProcAddress(user32, "TrackMouseEvent");
412 SetCoalescableTimerFn = (SetCoalescableTimerSig)::GetProcAddress(user32, "SetCoalescableTimer");
414 if (TrackMouseEventFn == NULL) {
415 // Windows 95 has an emulation in comctl32.dll:_TrackMouseEvent
416 if (!commctrl32)
417 commctrl32 = ::LoadLibrary(TEXT("comctl32.dll"));
418 if (commctrl32 != NULL) {
419 TrackMouseEventFn = (TrackMouseEventSig)
420 ::GetProcAddress(commctrl32, "_TrackMouseEvent");
423 for (TickReason tr = tickCaret; tr <= tickDwell; tr = static_cast<TickReason>(tr + 1)) {
424 timers[tr] = 0;
428 void ScintillaWin::Finalise() {
429 ScintillaBase::Finalise();
430 for (TickReason tr = tickCaret; tr <= tickDwell; tr = static_cast<TickReason>(tr + 1)) {
431 FineTickerCancel(tr);
433 SetIdle(false);
434 #if defined(USE_D2D)
435 DropRenderTarget();
436 #endif
437 ::RevokeDragDrop(MainHWND());
438 if (SUCCEEDED(hrOle)) {
439 ::OleUninitialize();
443 #if defined(USE_D2D)
445 void ScintillaWin::EnsureRenderTarget(HDC hdc) {
446 if (!renderTargetValid) {
447 DropRenderTarget();
448 renderTargetValid = true;
450 if (pD2DFactory && !pRenderTarget) {
451 RECT rc;
452 HWND hw = MainHWND();
453 GetClientRect(hw, &rc);
455 D2D1_SIZE_U size = D2D1::SizeU(rc.right - rc.left, rc.bottom - rc.top);
457 // Create a Direct2D render target.
458 #if 1
459 D2D1_RENDER_TARGET_PROPERTIES drtp;
460 drtp.type = D2D1_RENDER_TARGET_TYPE_DEFAULT;
461 drtp.pixelFormat.format = DXGI_FORMAT_UNKNOWN;
462 drtp.pixelFormat.alphaMode = D2D1_ALPHA_MODE_UNKNOWN;
463 drtp.dpiX = 96.0;
464 drtp.dpiY = 96.0;
465 drtp.usage = D2D1_RENDER_TARGET_USAGE_NONE;
466 drtp.minLevel = D2D1_FEATURE_LEVEL_DEFAULT;
468 if (technology == SC_TECHNOLOGY_DIRECTWRITEDC) {
469 // Explicit pixel format needed.
470 drtp.pixelFormat = D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM,
471 D2D1_ALPHA_MODE_IGNORE);
473 ID2D1DCRenderTarget *pDCRT = NULL;
474 HRESULT hr = pD2DFactory->CreateDCRenderTarget(&drtp, &pDCRT);
475 if (FAILED(hr)) {
476 Platform::DebugPrintf("Failed CreateDCRenderTarget 0x%x\n", hr);
478 pRenderTarget = pDCRT;
480 } else {
481 D2D1_HWND_RENDER_TARGET_PROPERTIES dhrtp;
482 dhrtp.hwnd = hw;
483 dhrtp.pixelSize = size;
484 dhrtp.presentOptions = (technology == SC_TECHNOLOGY_DIRECTWRITERETAIN) ?
485 D2D1_PRESENT_OPTIONS_RETAIN_CONTENTS : D2D1_PRESENT_OPTIONS_NONE;
487 ID2D1HwndRenderTarget *pHwndRenderTarget = NULL;
488 HRESULT hr = pD2DFactory->CreateHwndRenderTarget(drtp, dhrtp, &pHwndRenderTarget);
489 if (FAILED(hr)) {
490 Platform::DebugPrintf("Failed CreateHwndRenderTarget 0x%x\n", hr);
492 pRenderTarget = pHwndRenderTarget;
494 #else
495 pD2DFactory->CreateHwndRenderTarget(
496 D2D1::RenderTargetProperties(
497 D2D1_RENDER_TARGET_TYPE_DEFAULT ,
498 D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_PREMULTIPLIED),
499 96.0f, 96.0f, D2D1_RENDER_TARGET_USAGE_NONE, D2D1_FEATURE_LEVEL_DEFAULT),
500 D2D1::HwndRenderTargetProperties(hw, size),
501 &pRenderTarget);
502 #endif
503 // Pixmaps were created to be compatible with previous render target so
504 // need to be recreated.
505 DropGraphics(false);
508 if (technology == SC_TECHNOLOGY_DIRECTWRITEDC) {
509 RECT rcWindow;
510 GetClientRect(MainHWND(), &rcWindow);
511 HRESULT hr = static_cast<ID2D1DCRenderTarget*>(pRenderTarget)->BindDC(hdc, &rcWindow);
512 if (FAILED(hr)) {
513 Platform::DebugPrintf("BindDC failed 0x%x\n", hr);
514 DropRenderTarget();
519 void ScintillaWin::DropRenderTarget() {
520 if (pRenderTarget) {
521 pRenderTarget->Release();
522 pRenderTarget = 0;
526 #endif
528 HWND ScintillaWin::MainHWND() {
529 return reinterpret_cast<HWND>(wMain.GetID());
532 bool ScintillaWin::DragThreshold(Point ptStart, Point ptNow) {
533 int xMove = static_cast<int>(abs(ptStart.x - ptNow.x));
534 int yMove = static_cast<int>(abs(ptStart.y - ptNow.y));
535 return (xMove > ::GetSystemMetrics(SM_CXDRAG)) ||
536 (yMove > ::GetSystemMetrics(SM_CYDRAG));
539 void ScintillaWin::StartDrag() {
540 inDragDrop = ddDragging;
541 DWORD dwEffect = 0;
542 dropWentOutside = true;
543 IDataObject *pDataObject = reinterpret_cast<IDataObject *>(&dob);
544 IDropSource *pDropSource = reinterpret_cast<IDropSource *>(&ds);
545 //Platform::DebugPrintf("About to DoDragDrop %x %x\n", pDataObject, pDropSource);
546 HRESULT hr = ::DoDragDrop(
547 pDataObject,
548 pDropSource,
549 DROPEFFECT_COPY | DROPEFFECT_MOVE, &dwEffect);
550 //Platform::DebugPrintf("DoDragDrop = %x\n", hr);
551 if (SUCCEEDED(hr)) {
552 if ((hr == DRAGDROP_S_DROP) && (dwEffect == DROPEFFECT_MOVE) && dropWentOutside) {
553 // Remove dragged out text
554 ClearSelection();
557 inDragDrop = ddNone;
558 SetDragPosition(SelectionPosition(invalidPosition));
561 // Avoid warnings everywhere for old style casts by concentrating them here
562 static WORD LoWord(uptr_t l) {
563 return LOWORD(l);
566 static WORD HiWord(uptr_t l) {
567 return HIWORD(l);
570 static int InputCodePage() {
571 HKL inputLocale = ::GetKeyboardLayout(0);
572 LANGID inputLang = LOWORD(inputLocale);
573 char sCodePage[10];
574 int res = ::GetLocaleInfoA(MAKELCID(inputLang, SORT_DEFAULT),
575 LOCALE_IDEFAULTANSICODEPAGE, sCodePage, sizeof(sCodePage));
576 if (!res)
577 return 0;
578 return atoi(sCodePage);
581 /** Map the key codes to their equivalent SCK_ form. */
582 static int KeyTranslate(int keyIn) {
583 //PLATFORM_ASSERT(!keyIn);
584 switch (keyIn) {
585 case VK_DOWN: return SCK_DOWN;
586 case VK_UP: return SCK_UP;
587 case VK_LEFT: return SCK_LEFT;
588 case VK_RIGHT: return SCK_RIGHT;
589 case VK_HOME: return SCK_HOME;
590 case VK_END: return SCK_END;
591 case VK_PRIOR: return SCK_PRIOR;
592 case VK_NEXT: return SCK_NEXT;
593 case VK_DELETE: return SCK_DELETE;
594 case VK_INSERT: return SCK_INSERT;
595 case VK_ESCAPE: return SCK_ESCAPE;
596 case VK_BACK: return SCK_BACK;
597 case VK_TAB: return SCK_TAB;
598 case VK_RETURN: return SCK_RETURN;
599 case VK_ADD: return SCK_ADD;
600 case VK_SUBTRACT: return SCK_SUBTRACT;
601 case VK_DIVIDE: return SCK_DIVIDE;
602 case VK_LWIN: return SCK_WIN;
603 case VK_RWIN: return SCK_RWIN;
604 case VK_APPS: return SCK_MENU;
605 case VK_OEM_2: return '/';
606 case VK_OEM_3: return '`';
607 case VK_OEM_4: return '[';
608 case VK_OEM_5: return '\\';
609 case VK_OEM_6: return ']';
610 default: return keyIn;
614 static bool BoundsContains(PRectangle rcBounds, HRGN hRgnBounds, PRectangle rcCheck) {
615 bool contains = true;
616 if (!rcCheck.Empty()) {
617 if (!rcBounds.Contains(rcCheck)) {
618 contains = false;
619 } else if (hRgnBounds) {
620 // In bounding rectangle so check more accurately using region
621 HRGN hRgnCheck = ::CreateRectRgn(static_cast<int>(rcCheck.left), static_cast<int>(rcCheck.top),
622 static_cast<int>(rcCheck.right), static_cast<int>(rcCheck.bottom));
623 if (hRgnCheck) {
624 HRGN hRgnDifference = ::CreateRectRgn(0, 0, 0, 0);
625 if (hRgnDifference) {
626 int combination = ::CombineRgn(hRgnDifference, hRgnCheck, hRgnBounds, RGN_DIFF);
627 if (combination != NULLREGION) {
628 contains = false;
630 ::DeleteRgn(hRgnDifference);
632 ::DeleteRgn(hRgnCheck);
636 return contains;
639 LRESULT ScintillaWin::WndPaint(uptr_t wParam) {
640 //ElapsedTime et;
642 // Redirect assertions to debug output and save current state
643 bool assertsPopup = Platform::ShowAssertionPopUps(false);
644 paintState = painting;
645 PAINTSTRUCT ps;
646 PAINTSTRUCT *pps;
648 bool IsOcxCtrl = (wParam != 0); // if wParam != 0, it contains
649 // a PAINSTRUCT* from the OCX
650 // Removed since this interferes with reporting other assertions as it occurs repeatedly
651 //PLATFORM_ASSERT(hRgnUpdate == NULL);
652 hRgnUpdate = ::CreateRectRgn(0, 0, 0, 0);
653 if (IsOcxCtrl) {
654 pps = reinterpret_cast<PAINTSTRUCT*>(wParam);
655 } else {
656 ::GetUpdateRgn(MainHWND(), hRgnUpdate, FALSE);
657 pps = &ps;
658 ::BeginPaint(MainHWND(), pps);
660 rcPaint = PRectangle::FromInts(pps->rcPaint.left, pps->rcPaint.top, pps->rcPaint.right, pps->rcPaint.bottom);
661 PRectangle rcClient = GetClientRectangle();
662 paintingAllText = BoundsContains(rcPaint, hRgnUpdate, rcClient);
663 if (technology == SC_TECHNOLOGY_DEFAULT) {
664 AutoSurface surfaceWindow(pps->hdc, this);
665 if (surfaceWindow) {
666 Paint(surfaceWindow, rcPaint);
667 surfaceWindow->Release();
669 } else {
670 #if defined(USE_D2D)
671 EnsureRenderTarget(pps->hdc);
672 AutoSurface surfaceWindow(pRenderTarget, this);
673 if (surfaceWindow) {
674 pRenderTarget->BeginDraw();
675 Paint(surfaceWindow, rcPaint);
676 surfaceWindow->Release();
677 HRESULT hr = pRenderTarget->EndDraw();
678 if (hr == D2DERR_RECREATE_TARGET) {
679 DropRenderTarget();
680 paintState = paintAbandoned;
683 #endif
685 if (hRgnUpdate) {
686 ::DeleteRgn(hRgnUpdate);
687 hRgnUpdate = 0;
690 if (!IsOcxCtrl)
691 ::EndPaint(MainHWND(), pps);
692 if (paintState == paintAbandoned) {
693 // Painting area was insufficient to cover new styling or brace highlight positions
694 if (IsOcxCtrl) {
695 FullPaintDC(pps->hdc);
696 } else {
697 FullPaint();
700 paintState = notPainting;
702 // Restore debug output state
703 Platform::ShowAssertionPopUps(assertsPopup);
705 //Platform::DebugPrintf("Paint took %g\n", et.Duration());
706 return 0l;
709 sptr_t ScintillaWin::HandleComposition(uptr_t wParam, sptr_t lParam) {
710 if (lParam & GCS_RESULTSTR) {
711 HIMC hIMC = ::ImmGetContext(MainHWND());
712 if (hIMC) {
713 wchar_t wcs[maxLenInputIME];
714 LONG bytes = ::ImmGetCompositionStringW(hIMC,
715 GCS_RESULTSTR, wcs, (maxLenInputIME-1)*2);
716 int wides = bytes / 2;
717 if (IsUnicodeMode()) {
718 char utfval[maxLenInputIME * 3];
719 unsigned int len = UTF8Length(wcs, wides);
720 UTF8FromUTF16(wcs, wides, utfval, len);
721 utfval[len] = '\0';
722 AddCharUTF(utfval, len);
723 } else {
724 char dbcsval[maxLenInputIME * 2];
725 int size = ::WideCharToMultiByte(InputCodePage(),
726 0, wcs, wides, dbcsval, sizeof(dbcsval) - 1, 0, 0);
727 for (int i=0; i<size; i++) {
728 AddChar(dbcsval[i]);
731 // Set new position after converted
732 Point pos = PointMainCaret();
733 COMPOSITIONFORM CompForm;
734 CompForm.dwStyle = CFS_POINT;
735 CompForm.ptCurrentPos.x = static_cast<int>(pos.x);
736 CompForm.ptCurrentPos.y = static_cast<int>(pos.y);
737 ::ImmSetCompositionWindow(hIMC, &CompForm);
738 ::ImmReleaseContext(MainHWND(), hIMC);
740 return 0;
742 return ::DefWindowProc(MainHWND(), WM_IME_COMPOSITION, wParam, lParam);
745 bool ScintillaWin::KoreanIME() {
746 const int codePage = InputCodePage();
747 return codePage == 949 || codePage == 1361;
750 sptr_t ScintillaWin::HandleCompositionKoreanIME(uptr_t, sptr_t lParam) {
752 // copy & paste by johnsonj
753 // Great thanks to
754 // jiniya from http://www.jiniya.net/tt/494 for DBCS input with AddCharUTF()
755 // BLUEnLIVE from http://zockr.tistory.com/1118 for UNDO and inOverstrike
757 HIMC hIMC = ::ImmGetContext(MainHWND());
758 if (!hIMC) {
759 return 0;
762 wchar_t wcs[maxLenInputIME];
763 int wides = 0;
764 bool compstrExist = false;
766 if (pdoc->TentativeActive()) {
767 pdoc->TentativeUndo();
768 } else {
769 // No tentative undo means start of this composition so
770 // fill in any virtual spaces.
771 bool tmpOverstrike = inOverstrike;
772 inOverstrike = false; // not allow to be deleted twice.
773 AddCharUTF("", 0);
774 inOverstrike = tmpOverstrike;
777 view.imeCaretBlockOverride = false;
778 if (lParam & GCS_COMPSTR) {
779 long bytes = ::ImmGetCompositionStringW
780 (hIMC, GCS_COMPSTR, wcs, maxLenInputIME);
781 wides = bytes / 2;
782 compstrExist = (wides != 0);
783 } else if (lParam & GCS_RESULTSTR) {
784 long bytes = ::ImmGetCompositionStringW
785 (hIMC, GCS_RESULTSTR, wcs, maxLenInputIME);
786 wides = bytes / 2;
787 compstrExist = (wides == 0);
790 if (wides >= 1) {
792 char hanval[maxLenInputIME];
793 unsigned int hanlen = 0;
795 if (IsUnicodeMode()) {
796 hanlen = UTF8Length(wcs, wides);
797 UTF8FromUTF16(wcs, wides, hanval, hanlen);
798 hanval[hanlen] = '\0';
799 } else {
800 hanlen = ::WideCharToMultiByte(InputCodePage(), 0,
801 wcs, wides, hanval, sizeof(hanval) - 1, 0, 0);
802 hanval[hanlen] = '\0';
805 if (compstrExist) {
806 view.imeCaretBlockOverride = true;
808 bool tmpRecordingMacro = recordingMacro;
809 recordingMacro = false;
810 pdoc->TentativeStart();
811 AddCharUTF(hanval, hanlen);
812 recordingMacro = tmpRecordingMacro;
814 for (size_t r = 0; r < sel.Count(); r++) { // for block caret
815 int positionInsert = sel.Range(r).Start().Position();
816 sel.Range(r).caret.SetPosition(positionInsert - hanlen);
817 sel.Range(r).anchor.SetPosition(positionInsert - hanlen);
819 } else {
820 AddCharUTF(hanval, hanlen);
824 // set the candidate window position for HANJA while composing.
825 Point pos = PointMainCaret();
826 CANDIDATEFORM CandForm;
827 CandForm.dwIndex = 0;
828 CandForm.dwStyle = CFS_CANDIDATEPOS;
829 CandForm.ptCurrentPos.x = static_cast<int>(pos.x);
830 CandForm.ptCurrentPos.y = static_cast<int>(pos.y + vs.lineHeight);
831 ::ImmSetCandidateWindow(hIMC, &CandForm);
833 ShowCaretAtCurrentPosition();
834 ::ImmReleaseContext(MainHWND(), hIMC);
835 return 0;
838 // Translate message IDs from WM_* and EM_* to SCI_* so can partly emulate Windows Edit control
839 static unsigned int SciMessageFromEM(unsigned int iMessage) {
840 switch (iMessage) {
841 case EM_CANPASTE: return SCI_CANPASTE;
842 case EM_CANUNDO: return SCI_CANUNDO;
843 case EM_EMPTYUNDOBUFFER: return SCI_EMPTYUNDOBUFFER;
844 case EM_FINDTEXTEX: return SCI_FINDTEXT;
845 case EM_FORMATRANGE: return SCI_FORMATRANGE;
846 case EM_GETFIRSTVISIBLELINE: return SCI_GETFIRSTVISIBLELINE;
847 case EM_GETLINECOUNT: return SCI_GETLINECOUNT;
848 case EM_GETSELTEXT: return SCI_GETSELTEXT;
849 case EM_GETTEXTRANGE: return SCI_GETTEXTRANGE;
850 case EM_HIDESELECTION: return SCI_HIDESELECTION;
851 case EM_LINEINDEX: return SCI_POSITIONFROMLINE;
852 case EM_LINESCROLL: return SCI_LINESCROLL;
853 case EM_REPLACESEL: return SCI_REPLACESEL;
854 case EM_SCROLLCARET: return SCI_SCROLLCARET;
855 case EM_SETREADONLY: return SCI_SETREADONLY;
856 case WM_CLEAR: return SCI_CLEAR;
857 case WM_COPY: return SCI_COPY;
858 case WM_CUT: return SCI_CUT;
859 case WM_SETTEXT: return SCI_SETTEXT;
860 case WM_PASTE: return SCI_PASTE;
861 case WM_UNDO: return SCI_UNDO;
863 return iMessage;
866 UINT CodePageFromCharSet(DWORD characterSet, UINT documentCodePage) {
867 if (documentCodePage == SC_CP_UTF8) {
868 return SC_CP_UTF8;
870 switch (characterSet) {
871 case SC_CHARSET_ANSI: return 1252;
872 case SC_CHARSET_DEFAULT: return documentCodePage;
873 case SC_CHARSET_BALTIC: return 1257;
874 case SC_CHARSET_CHINESEBIG5: return 950;
875 case SC_CHARSET_EASTEUROPE: return 1250;
876 case SC_CHARSET_GB2312: return 936;
877 case SC_CHARSET_GREEK: return 1253;
878 case SC_CHARSET_HANGUL: return 949;
879 case SC_CHARSET_MAC: return 10000;
880 case SC_CHARSET_OEM: return 437;
881 case SC_CHARSET_RUSSIAN: return 1251;
882 case SC_CHARSET_SHIFTJIS: return 932;
883 case SC_CHARSET_TURKISH: return 1254;
884 case SC_CHARSET_JOHAB: return 1361;
885 case SC_CHARSET_HEBREW: return 1255;
886 case SC_CHARSET_ARABIC: return 1256;
887 case SC_CHARSET_VIETNAMESE: return 1258;
888 case SC_CHARSET_THAI: return 874;
889 case SC_CHARSET_8859_15: return 28605;
890 // Not supported
891 case SC_CHARSET_CYRILLIC: return documentCodePage;
892 case SC_CHARSET_SYMBOL: return documentCodePage;
894 return documentCodePage;
897 UINT ScintillaWin::CodePageOfDocument() {
898 return CodePageFromCharSet(vs.styles[STYLE_DEFAULT].characterSet, pdoc->dbcsCodePage);
901 sptr_t ScintillaWin::GetTextLength() {
902 if (::IsWindowUnicode(MainHWND())) {
903 if (pdoc->Length() == 0)
904 return 0;
905 std::vector<char> docBytes(pdoc->Length(), '\0');
906 pdoc->GetCharRange(&docBytes[0], 0, pdoc->Length());
907 if (IsUnicodeMode()) {
908 return UTF16Length(&docBytes[0], static_cast<unsigned int>(docBytes.size()));
909 } else {
910 return ::MultiByteToWideChar(CodePageOfDocument(), 0, &docBytes[0],
911 static_cast<int>(docBytes.size()), NULL, 0);
913 } else {
914 return pdoc->Length();
918 sptr_t ScintillaWin::GetText(uptr_t wParam, sptr_t lParam) {
919 if (::IsWindowUnicode(MainHWND())) {
920 wchar_t *ptr = reinterpret_cast<wchar_t *>(lParam);
921 if (pdoc->Length() == 0) {
922 *ptr = L'\0';
923 return 0;
925 std::vector<char> docBytes(pdoc->Length(), '\0');
926 pdoc->GetCharRange(&docBytes[0], 0, pdoc->Length());
927 if (IsUnicodeMode()) {
928 unsigned int lengthUTF16 = UTF16Length(&docBytes[0], static_cast<unsigned int>(docBytes.size()));
929 if (lParam == 0)
930 return lengthUTF16;
931 if (wParam == 0)
932 return 0;
933 unsigned int uLen = UTF16FromUTF8(&docBytes[0], static_cast<unsigned int>(docBytes.size()),
934 ptr, static_cast<int>(wParam) - 1);
935 ptr[uLen] = L'\0';
936 return uLen;
937 } else {
938 // Not Unicode mode
939 // Convert to Unicode using the current Scintilla code page
940 const UINT cpSrc = CodePageOfDocument();
941 int lengthUTF16 = ::MultiByteToWideChar(cpSrc, 0, &docBytes[0],
942 static_cast<int>(docBytes.size()), NULL, 0);
943 if (lengthUTF16 >= static_cast<int>(wParam))
944 lengthUTF16 = static_cast<int>(wParam)-1;
945 ::MultiByteToWideChar(cpSrc, 0, &docBytes[0],
946 static_cast<int>(docBytes.size()),
947 ptr, lengthUTF16);
948 ptr[lengthUTF16] = L'\0';
949 return lengthUTF16;
951 } else {
952 if (lParam == 0)
953 return pdoc->Length() + 1;
954 if (wParam == 0)
955 return 0;
956 char *ptr = reinterpret_cast<char *>(lParam);
957 unsigned int iChar = 0;
958 for (; iChar < wParam - 1; iChar++)
959 ptr[iChar] = pdoc->CharAt(iChar);
960 ptr[iChar] = '\0';
961 return iChar;
965 sptr_t ScintillaWin::WndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
966 try {
967 //Platform::DebugPrintf("S M:%x WP:%x L:%x\n", iMessage, wParam, lParam);
968 iMessage = SciMessageFromEM(iMessage);
969 switch (iMessage) {
971 case WM_CREATE:
972 ctrlID = ::GetDlgCtrlID(reinterpret_cast<HWND>(wMain.GetID()));
973 // Get Intellimouse scroll line parameters
974 GetIntelliMouseParameters();
975 ::RegisterDragDrop(MainHWND(), reinterpret_cast<IDropTarget *>(&dt));
976 break;
978 case WM_COMMAND:
979 Command(LoWord(wParam));
980 break;
982 case WM_PAINT:
983 return WndPaint(wParam);
985 case WM_PRINTCLIENT: {
986 HDC hdc = reinterpret_cast<HDC>(wParam);
987 if (!IsCompatibleDC(hdc)) {
988 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
990 FullPaintDC(hdc);
992 break;
994 case WM_VSCROLL:
995 ScrollMessage(wParam);
996 break;
998 case WM_HSCROLL:
999 HorizontalScrollMessage(wParam);
1000 break;
1002 case WM_SIZE: {
1003 #if defined(USE_D2D)
1004 if (paintState == notPainting) {
1005 DropRenderTarget();
1006 } else {
1007 renderTargetValid = false;
1009 #endif
1010 //Platform::DebugPrintf("Scintilla WM_SIZE %d %d\n", LoWord(lParam), HiWord(lParam));
1011 ChangeSize();
1013 break;
1015 case WM_MOUSEWHEEL:
1016 // if autocomplete list active then send mousewheel message to it
1017 if (ac.Active()) {
1018 HWND hWnd = reinterpret_cast<HWND>(ac.lb->GetID());
1019 ::SendMessage(hWnd, iMessage, wParam, lParam);
1020 break;
1023 // Don't handle datazoom.
1024 // (A good idea for datazoom would be to "fold" or "unfold" details.
1025 // i.e. if datazoomed out only class structures are visible, when datazooming in the control
1026 // structures appear, then eventually the individual statements...)
1027 if (wParam & MK_SHIFT) {
1028 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1031 // Either SCROLL or ZOOM. We handle the wheel steppings calculation
1032 wheelDelta -= static_cast<short>(HiWord(wParam));
1033 if (abs(wheelDelta) >= WHEEL_DELTA && linesPerScroll > 0) {
1034 int linesToScroll = linesPerScroll;
1035 if (linesPerScroll == WHEEL_PAGESCROLL)
1036 linesToScroll = LinesOnScreen() - 1;
1037 if (linesToScroll == 0) {
1038 linesToScroll = 1;
1040 linesToScroll *= (wheelDelta / WHEEL_DELTA);
1041 if (wheelDelta >= 0)
1042 wheelDelta = wheelDelta % WHEEL_DELTA;
1043 else
1044 wheelDelta = - (-wheelDelta % WHEEL_DELTA);
1046 if (wParam & MK_CONTROL) {
1047 // Zoom! We play with the font sizes in the styles.
1048 // Number of steps/line is ignored, we just care if sizing up or down
1049 if (linesToScroll < 0) {
1050 KeyCommand(SCI_ZOOMIN);
1051 } else {
1052 KeyCommand(SCI_ZOOMOUT);
1054 } else {
1055 // Scroll
1056 ScrollTo(topLine + linesToScroll);
1059 return 0;
1061 case WM_TIMER:
1062 if (wParam == idleTimerID && idler.state) {
1063 SendMessage(MainHWND(), SC_WIN_IDLE, 0, 1);
1064 } else {
1065 TickFor(static_cast<TickReason>(wParam - fineTimerStart));
1067 break;
1069 case SC_WIN_IDLE:
1070 // wParam=dwTickCountInitial, or 0 to initialize. lParam=bSkipUserInputTest
1071 if (idler.state) {
1072 if (lParam || (WAIT_TIMEOUT == MsgWaitForMultipleObjects(0, 0, 0, 0, QS_INPUT|QS_HOTKEY))) {
1073 if (Idle()) {
1074 // User input was given priority above, but all events do get a turn. Other
1075 // messages, notifications, etc. will get interleaved with the idle messages.
1077 // However, some things like WM_PAINT are a lower priority, and will not fire
1078 // when there's a message posted. So, several times a second, we stop and let
1079 // the low priority events have a turn (after which the timer will fire again).
1081 DWORD dwCurrent = GetTickCount();
1082 DWORD dwStart = wParam ? static_cast<DWORD>(wParam) : dwCurrent;
1083 const DWORD maxWorkTime = 50;
1085 if (dwCurrent >= dwStart && dwCurrent > maxWorkTime && dwCurrent - maxWorkTime < dwStart)
1086 PostMessage(MainHWND(), SC_WIN_IDLE, dwStart, 0);
1087 } else {
1088 SetIdle(false);
1092 break;
1094 case WM_GETMINMAXINFO:
1095 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1097 case WM_LBUTTONDOWN: {
1098 // For IME, set the composition string as the result string.
1099 HIMC hIMC = ::ImmGetContext(MainHWND());
1100 ::ImmNotifyIME(hIMC, NI_COMPOSITIONSTR, CPS_COMPLETE, 0);
1101 ::ImmReleaseContext(MainHWND(), hIMC);
1103 //Platform::DebugPrintf("Buttdown %d %x %x %x %x %x\n",iMessage, wParam, lParam,
1104 // Platform::IsKeyDown(VK_SHIFT),
1105 // Platform::IsKeyDown(VK_CONTROL),
1106 // Platform::IsKeyDown(VK_MENU));
1107 ::SetFocus(MainHWND());
1108 ButtonDown(Point::FromLong(static_cast<long>(lParam)), ::GetMessageTime(),
1109 (wParam & MK_SHIFT) != 0,
1110 (wParam & MK_CONTROL) != 0,
1111 Platform::IsKeyDown(VK_MENU));
1113 break;
1115 case WM_MOUSEMOVE: {
1116 const Point pt = Point::FromLong(static_cast<long>(lParam));
1118 // Windows might send WM_MOUSEMOVE even though the mouse has not been moved:
1119 // http://blogs.msdn.com/b/oldnewthing/archive/2003/10/01/55108.aspx
1120 if (ptMouseLast.x != pt.x || ptMouseLast.y != pt.y) {
1121 SetTrackMouseLeaveEvent(true);
1122 ButtonMoveWithModifiers(pt,
1123 ((wParam & MK_SHIFT) != 0 ? SCI_SHIFT : 0) |
1124 ((wParam & MK_CONTROL) != 0 ? SCI_CTRL : 0) |
1125 (Platform::IsKeyDown(VK_MENU) ? SCI_ALT : 0));
1128 break;
1130 case WM_MOUSELEAVE:
1131 SetTrackMouseLeaveEvent(false);
1132 MouseLeave();
1133 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1135 case WM_LBUTTONUP:
1136 ButtonUp(Point::FromLong(static_cast<long>(lParam)),
1137 ::GetMessageTime(),
1138 (wParam & MK_CONTROL) != 0);
1139 break;
1141 case WM_RBUTTONDOWN:
1142 ::SetFocus(MainHWND());
1143 if (!PointInSelection(Point::FromLong(static_cast<long>(lParam)))) {
1144 CancelModes();
1145 SetEmptySelection(PositionFromLocation(Point::FromLong(static_cast<long>(lParam))));
1147 break;
1149 case WM_SETCURSOR:
1150 if (LoWord(lParam) == HTCLIENT) {
1151 if (inDragDrop == ddDragging) {
1152 DisplayCursor(Window::cursorUp);
1153 } else {
1154 // Display regular (drag) cursor over selection
1155 POINT pt;
1156 if (0 != ::GetCursorPos(&pt)) {
1157 ::ScreenToClient(MainHWND(), &pt);
1158 if (PointInSelMargin(PointFromPOINT(pt))) {
1159 DisplayCursor(GetMarginCursor(PointFromPOINT(pt)));
1160 } else if (PointInSelection(PointFromPOINT(pt)) && !SelectionEmpty()) {
1161 DisplayCursor(Window::cursorArrow);
1162 } else if (PointIsHotspot(PointFromPOINT(pt))) {
1163 DisplayCursor(Window::cursorHand);
1164 } else {
1165 DisplayCursor(Window::cursorText);
1169 return TRUE;
1170 } else {
1171 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1174 case WM_CHAR:
1175 if (((wParam >= 128) || !iscntrl(static_cast<int>(wParam))) || !lastKeyDownConsumed) {
1176 if (::IsWindowUnicode(MainHWND()) || keysAlwaysUnicode) {
1177 wchar_t wcs[2] = {static_cast<wchar_t>(wParam), 0};
1178 if (IsUnicodeMode()) {
1179 // For a wide character version of the window:
1180 char utfval[4];
1181 unsigned int len = UTF8Length(wcs, 1);
1182 UTF8FromUTF16(wcs, 1, utfval, len);
1183 AddCharUTF(utfval, len);
1184 } else {
1185 UINT cpDest = CodePageOfDocument();
1186 char inBufferCP[20];
1187 int size = ::WideCharToMultiByte(cpDest,
1188 0, wcs, 1, inBufferCP, sizeof(inBufferCP) - 1, 0, 0);
1189 inBufferCP[size] = '\0';
1190 AddCharUTF(inBufferCP, size);
1192 } else {
1193 if (IsUnicodeMode()) {
1194 AddCharBytes('\0', LOBYTE(wParam));
1195 } else {
1196 AddChar(LOBYTE(wParam));
1200 return 0;
1202 case WM_UNICHAR:
1203 if (wParam == UNICODE_NOCHAR) {
1204 return IsUnicodeMode() ? 1 : 0;
1205 } else if (lastKeyDownConsumed) {
1206 return 1;
1207 } else {
1208 if (IsUnicodeMode()) {
1209 char utfval[4];
1210 wchar_t wcs[2] = {static_cast<wchar_t>(wParam), 0};
1211 unsigned int len = UTF8Length(wcs, 1);
1212 UTF8FromUTF16(wcs, 1, utfval, len);
1213 AddCharUTF(utfval, len);
1214 return 1;
1215 } else {
1216 return 0;
1220 case WM_SYSKEYDOWN:
1221 case WM_KEYDOWN: {
1222 //Platform::DebugPrintf("S keydown %d %x %x %x %x\n",iMessage, wParam, lParam, ::IsKeyDown(VK_SHIFT), ::IsKeyDown(VK_CONTROL));
1223 lastKeyDownConsumed = false;
1224 int ret = KeyDown(KeyTranslate(static_cast<int>(wParam)),
1225 Platform::IsKeyDown(VK_SHIFT),
1226 Platform::IsKeyDown(VK_CONTROL),
1227 Platform::IsKeyDown(VK_MENU),
1228 &lastKeyDownConsumed);
1229 if (!ret && !lastKeyDownConsumed) {
1230 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1232 break;
1235 case WM_IME_KEYDOWN:
1236 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1238 case WM_KEYUP:
1239 //Platform::DebugPrintf("S keyup %d %x %x\n",iMessage, wParam, lParam);
1240 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1242 case WM_SETTINGCHANGE:
1243 //Platform::DebugPrintf("Setting Changed\n");
1244 InvalidateStyleData();
1245 // Get Intellimouse scroll line parameters
1246 GetIntelliMouseParameters();
1247 break;
1249 case WM_GETDLGCODE:
1250 return DLGC_HASSETSEL | DLGC_WANTALLKEYS;
1252 case WM_KILLFOCUS: {
1253 HWND wOther = reinterpret_cast<HWND>(wParam);
1254 HWND wThis = MainHWND();
1255 HWND wCT = reinterpret_cast<HWND>(ct.wCallTip.GetID());
1256 if (!wParam ||
1257 !(::IsChild(wThis, wOther) || (wOther == wCT))) {
1258 SetFocusState(false);
1259 DestroySystemCaret();
1261 // Explicitly complete any IME composition
1262 HIMC hIMC = ImmGetContext(MainHWND());
1263 if (hIMC) {
1264 ::ImmNotifyIME(hIMC, NI_COMPOSITIONSTR, CPS_COMPLETE, 0);
1265 ::ImmReleaseContext(MainHWND(), hIMC);
1268 break;
1270 case WM_SETFOCUS:
1271 SetFocusState(true);
1272 DestroySystemCaret();
1273 CreateSystemCaret();
1274 break;
1276 case WM_SYSCOLORCHANGE:
1277 //Platform::DebugPrintf("Setting Changed\n");
1278 InvalidateStyleData();
1279 break;
1281 case WM_IME_STARTCOMPOSITION: // dbcs
1282 if (KoreanIME()) {
1283 return 0;
1285 ImeStartComposition();
1286 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1288 case WM_IME_ENDCOMPOSITION: // dbcs
1289 ImeEndComposition();
1290 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1292 case WM_IME_COMPOSITION:
1293 if (KoreanIME()) {
1294 return HandleCompositionKoreanIME(wParam, lParam);
1295 } else {
1296 return HandleComposition(wParam, lParam);
1299 case WM_IME_CHAR: {
1300 AddCharBytes(HIBYTE(wParam), LOBYTE(wParam));
1301 return 0;
1304 case WM_CONTEXTMENU:
1305 if (displayPopupMenu) {
1306 Point pt = Point::FromLong(static_cast<long>(lParam));
1307 if ((pt.x == -1) && (pt.y == -1)) {
1308 // Caused by keyboard so display menu near caret
1309 pt = PointMainCaret();
1310 POINT spt = {static_cast<int>(pt.x), static_cast<int>(pt.y)};
1311 ::ClientToScreen(MainHWND(), &spt);
1312 pt = PointFromPOINT(spt);
1314 ContextMenu(pt);
1315 return 0;
1317 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1319 case WM_INPUTLANGCHANGE:
1320 //::SetThreadLocale(LOWORD(lParam));
1321 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1323 case WM_INPUTLANGCHANGEREQUEST:
1324 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1326 case WM_ERASEBKGND:
1327 return 1; // Avoid any background erasure as whole window painted.
1329 case WM_CAPTURECHANGED:
1330 capturedMouse = false;
1331 return 0;
1333 case WM_IME_SETCONTEXT:
1334 if (KoreanIME()) {
1335 if (wParam) {
1336 LPARAM NoImeWin = lParam;
1337 NoImeWin = NoImeWin & (~ISC_SHOWUICOMPOSITIONWINDOW);
1338 return ::DefWindowProc(MainHWND(), iMessage, wParam, NoImeWin);
1341 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1343 // These are not handled in Scintilla and its faster to dispatch them here.
1344 // Also moves time out to here so profile doesn't count lots of empty message calls.
1346 case WM_MOVE:
1347 case WM_MOUSEACTIVATE:
1348 case WM_NCHITTEST:
1349 case WM_NCCALCSIZE:
1350 case WM_NCPAINT:
1351 case WM_NCMOUSEMOVE:
1352 case WM_NCLBUTTONDOWN:
1353 case WM_IME_NOTIFY:
1354 case WM_SYSCOMMAND:
1355 case WM_WINDOWPOSCHANGING:
1356 case WM_WINDOWPOSCHANGED:
1357 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1359 case WM_GETTEXTLENGTH:
1360 return GetTextLength();
1362 case WM_GETTEXT:
1363 return GetText(wParam, lParam);
1365 case EM_LINEFROMCHAR:
1366 if (static_cast<int>(wParam) < 0) {
1367 wParam = SelectionStart().Position();
1369 return pdoc->LineFromPosition(static_cast<int>(wParam));
1371 case EM_EXLINEFROMCHAR:
1372 return pdoc->LineFromPosition(static_cast<int>(lParam));
1374 case EM_GETSEL:
1375 if (wParam) {
1376 *reinterpret_cast<int *>(wParam) = SelectionStart().Position();
1378 if (lParam) {
1379 *reinterpret_cast<int *>(lParam) = SelectionEnd().Position();
1381 return MAKELONG(SelectionStart().Position(), SelectionEnd().Position());
1383 case EM_EXGETSEL: {
1384 if (lParam == 0) {
1385 return 0;
1387 Sci_CharacterRange *pCR = reinterpret_cast<Sci_CharacterRange *>(lParam);
1388 pCR->cpMin = SelectionStart().Position();
1389 pCR->cpMax = SelectionEnd().Position();
1391 break;
1393 case EM_SETSEL: {
1394 int nStart = static_cast<int>(wParam);
1395 int nEnd = static_cast<int>(lParam);
1396 if (nStart == 0 && nEnd == -1) {
1397 nEnd = pdoc->Length();
1399 if (nStart == -1) {
1400 nStart = nEnd; // Remove selection
1402 if (nStart > nEnd) {
1403 SetSelection(nEnd, nStart);
1404 } else {
1405 SetSelection(nStart, nEnd);
1407 EnsureCaretVisible();
1409 break;
1411 case EM_EXSETSEL: {
1412 if (lParam == 0) {
1413 return 0;
1415 Sci_CharacterRange *pCR = reinterpret_cast<Sci_CharacterRange *>(lParam);
1416 sel.selType = Selection::selStream;
1417 if (pCR->cpMin == 0 && pCR->cpMax == -1) {
1418 SetSelection(pCR->cpMin, pdoc->Length());
1419 } else {
1420 SetSelection(pCR->cpMin, pCR->cpMax);
1422 EnsureCaretVisible();
1423 return pdoc->LineFromPosition(SelectionStart().Position());
1426 case SCI_GETDIRECTFUNCTION:
1427 return reinterpret_cast<sptr_t>(DirectFunction);
1429 case SCI_GETDIRECTPOINTER:
1430 return reinterpret_cast<sptr_t>(this);
1432 case SCI_GRABFOCUS:
1433 ::SetFocus(MainHWND());
1434 break;
1436 case SCI_SETKEYSUNICODE:
1437 keysAlwaysUnicode = wParam != 0;
1438 break;
1440 case SCI_GETKEYSUNICODE:
1441 return keysAlwaysUnicode;
1443 case SCI_SETTECHNOLOGY:
1444 if ((wParam == SC_TECHNOLOGY_DEFAULT) ||
1445 (wParam == SC_TECHNOLOGY_DIRECTWRITERETAIN) ||
1446 (wParam == SC_TECHNOLOGY_DIRECTWRITEDC) ||
1447 (wParam == SC_TECHNOLOGY_DIRECTWRITE)) {
1448 if (technology != static_cast<int>(wParam)) {
1449 if (static_cast<int>(wParam) > SC_TECHNOLOGY_DEFAULT) {
1450 #if defined(USE_D2D)
1451 if (!LoadD2D())
1452 // Failed to load Direct2D or DirectWrite so no effect
1453 return 0;
1454 #else
1455 return 0;
1456 #endif
1458 #if defined(USE_D2D)
1459 DropRenderTarget();
1460 #endif
1461 technology = static_cast<int>(wParam);
1462 // Invalidate all cached information including layout.
1463 DropGraphics(true);
1464 InvalidateStyleRedraw();
1467 break;
1469 #ifdef SCI_LEXER
1470 case SCI_LOADLEXERLIBRARY:
1471 LexerManager::GetInstance()->Load(reinterpret_cast<const char *>(lParam));
1472 break;
1473 #endif
1475 default:
1476 return ScintillaBase::WndProc(iMessage, wParam, lParam);
1478 } catch (std::bad_alloc &) {
1479 errorStatus = SC_STATUS_BADALLOC;
1480 } catch (...) {
1481 errorStatus = SC_STATUS_FAILURE;
1483 return 0l;
1486 bool ScintillaWin::ValidCodePage(int codePage) const {
1487 return codePage == 0 || codePage == SC_CP_UTF8 ||
1488 codePage == 932 || codePage == 936 || codePage == 949 ||
1489 codePage == 950 || codePage == 1361;
1492 sptr_t ScintillaWin::DefWndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
1493 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1497 * Report that this Editor subclass has a working implementation of FineTickerStart.
1499 bool ScintillaWin::FineTickerAvailable() {
1500 return true;
1503 bool ScintillaWin::FineTickerRunning(TickReason reason) {
1504 return timers[reason] != 0;
1507 void ScintillaWin::FineTickerStart(TickReason reason, int millis, int tolerance) {
1508 FineTickerCancel(reason);
1509 if (SetCoalescableTimerFn && tolerance) {
1510 timers[reason] = SetCoalescableTimerFn(MainHWND(), fineTimerStart + reason, millis, NULL, tolerance);
1511 } else {
1512 timers[reason] = ::SetTimer(MainHWND(), fineTimerStart + reason, millis, NULL);
1516 void ScintillaWin::FineTickerCancel(TickReason reason) {
1517 if (timers[reason]) {
1518 ::KillTimer(MainHWND(), timers[reason]);
1519 timers[reason] = 0;
1524 bool ScintillaWin::SetIdle(bool on) {
1525 // On Win32 the Idler is implemented as a Timer on the Scintilla window. This
1526 // takes advantage of the fact that WM_TIMER messages are very low priority,
1527 // and are only posted when the message queue is empty, i.e. during idle time.
1528 if (idler.state != on) {
1529 if (on) {
1530 idler.idlerID = ::SetTimer(MainHWND(), idleTimerID, 10, NULL)
1531 ? reinterpret_cast<IdlerID>(idleTimerID) : 0;
1532 } else {
1533 ::KillTimer(MainHWND(), reinterpret_cast<uptr_t>(idler.idlerID));
1534 idler.idlerID = 0;
1536 idler.state = idler.idlerID != 0;
1538 return idler.state;
1541 void ScintillaWin::SetMouseCapture(bool on) {
1542 if (mouseDownCaptures) {
1543 if (on) {
1544 ::SetCapture(MainHWND());
1545 } else {
1546 ::ReleaseCapture();
1549 capturedMouse = on;
1552 bool ScintillaWin::HaveMouseCapture() {
1553 // Cannot just see if GetCapture is this window as the scroll bar also sets capture for the window
1554 return capturedMouse;
1555 //return capturedMouse && (::GetCapture() == MainHWND());
1558 void ScintillaWin::SetTrackMouseLeaveEvent(bool on) {
1559 if (on && TrackMouseEventFn && !trackedMouseLeave) {
1560 TRACKMOUSEEVENT tme;
1561 tme.cbSize = sizeof(tme);
1562 tme.dwFlags = TME_LEAVE;
1563 tme.hwndTrack = MainHWND();
1564 tme.dwHoverTime = HOVER_DEFAULT; // Unused but triggers Dr. Memory if not initialized
1565 TrackMouseEventFn(&tme);
1567 trackedMouseLeave = on;
1570 bool ScintillaWin::PaintContains(PRectangle rc) {
1571 if (paintState == painting) {
1572 return BoundsContains(rcPaint, hRgnUpdate, rc);
1574 return true;
1577 void ScintillaWin::ScrollText(int /* linesToMove */) {
1578 //Platform::DebugPrintf("ScintillaWin::ScrollText %d\n", linesToMove);
1579 //::ScrollWindow(MainHWND(), 0,
1580 // vs.lineHeight * linesToMove, 0, 0);
1581 //::UpdateWindow(MainHWND());
1582 Redraw();
1583 UpdateSystemCaret();
1586 void ScintillaWin::UpdateSystemCaret() {
1587 if (hasFocus) {
1588 if (HasCaretSizeChanged()) {
1589 DestroySystemCaret();
1590 CreateSystemCaret();
1592 Point pos = PointMainCaret();
1593 ::SetCaretPos(static_cast<int>(pos.x), static_cast<int>(pos.y));
1597 int ScintillaWin::SetScrollInfo(int nBar, LPCSCROLLINFO lpsi, BOOL bRedraw) {
1598 return ::SetScrollInfo(MainHWND(), nBar, lpsi, bRedraw);
1601 bool ScintillaWin::GetScrollInfo(int nBar, LPSCROLLINFO lpsi) {
1602 return ::GetScrollInfo(MainHWND(), nBar, lpsi) ? true : false;
1605 // Change the scroll position but avoid repaint if changing to same value
1606 void ScintillaWin::ChangeScrollPos(int barType, int pos) {
1607 SCROLLINFO sci = {
1608 sizeof(sci), 0, 0, 0, 0, 0, 0
1610 sci.fMask = SIF_POS;
1611 GetScrollInfo(barType, &sci);
1612 if (sci.nPos != pos) {
1613 DwellEnd(true);
1614 sci.nPos = pos;
1615 SetScrollInfo(barType, &sci, TRUE);
1619 void ScintillaWin::SetVerticalScrollPos() {
1620 ChangeScrollPos(SB_VERT, topLine);
1623 void ScintillaWin::SetHorizontalScrollPos() {
1624 ChangeScrollPos(SB_HORZ, xOffset);
1627 bool ScintillaWin::ModifyScrollBars(int nMax, int nPage) {
1628 bool modified = false;
1629 SCROLLINFO sci = {
1630 sizeof(sci), 0, 0, 0, 0, 0, 0
1632 sci.fMask = SIF_PAGE | SIF_RANGE;
1633 GetScrollInfo(SB_VERT, &sci);
1634 int vertEndPreferred = nMax;
1635 if (!verticalScrollBarVisible)
1636 nPage = vertEndPreferred + 1;
1637 if ((sci.nMin != 0) ||
1638 (sci.nMax != vertEndPreferred) ||
1639 (sci.nPage != static_cast<unsigned int>(nPage)) ||
1640 (sci.nPos != 0)) {
1641 sci.fMask = SIF_PAGE | SIF_RANGE;
1642 sci.nMin = 0;
1643 sci.nMax = vertEndPreferred;
1644 sci.nPage = nPage;
1645 sci.nPos = 0;
1646 sci.nTrackPos = 1;
1647 SetScrollInfo(SB_VERT, &sci, TRUE);
1648 modified = true;
1651 PRectangle rcText = GetTextRectangle();
1652 int horizEndPreferred = scrollWidth;
1653 if (horizEndPreferred < 0)
1654 horizEndPreferred = 0;
1655 unsigned int pageWidth = static_cast<unsigned int>(rcText.Width());
1656 if (!horizontalScrollBarVisible || Wrapping())
1657 pageWidth = horizEndPreferred + 1;
1658 sci.fMask = SIF_PAGE | SIF_RANGE;
1659 GetScrollInfo(SB_HORZ, &sci);
1660 if ((sci.nMin != 0) ||
1661 (sci.nMax != horizEndPreferred) ||
1662 (sci.nPage != pageWidth) ||
1663 (sci.nPos != 0)) {
1664 sci.fMask = SIF_PAGE | SIF_RANGE;
1665 sci.nMin = 0;
1666 sci.nMax = horizEndPreferred;
1667 sci.nPage = pageWidth;
1668 sci.nPos = 0;
1669 sci.nTrackPos = 1;
1670 SetScrollInfo(SB_HORZ, &sci, TRUE);
1671 modified = true;
1672 if (scrollWidth < static_cast<int>(pageWidth)) {
1673 HorizontalScrollTo(0);
1676 return modified;
1679 void ScintillaWin::NotifyChange() {
1680 ::SendMessage(::GetParent(MainHWND()), WM_COMMAND,
1681 MAKELONG(GetCtrlID(), SCEN_CHANGE),
1682 reinterpret_cast<LPARAM>(MainHWND()));
1685 void ScintillaWin::NotifyFocus(bool focus) {
1686 ::SendMessage(::GetParent(MainHWND()), WM_COMMAND,
1687 MAKELONG(GetCtrlID(), focus ? SCEN_SETFOCUS : SCEN_KILLFOCUS),
1688 reinterpret_cast<LPARAM>(MainHWND()));
1689 Editor::NotifyFocus(focus);
1692 void ScintillaWin::SetCtrlID(int identifier) {
1693 ::SetWindowID(reinterpret_cast<HWND>(wMain.GetID()), identifier);
1696 int ScintillaWin::GetCtrlID() {
1697 return ::GetDlgCtrlID(reinterpret_cast<HWND>(wMain.GetID()));
1700 void ScintillaWin::NotifyParent(SCNotification scn) {
1701 scn.nmhdr.hwndFrom = MainHWND();
1702 scn.nmhdr.idFrom = GetCtrlID();
1703 ::SendMessage(::GetParent(MainHWND()), WM_NOTIFY,
1704 GetCtrlID(), reinterpret_cast<LPARAM>(&scn));
1707 void ScintillaWin::NotifyParent(SCNotification * scn) {
1708 scn->nmhdr.hwndFrom = MainHWND();
1709 scn->nmhdr.idFrom = GetCtrlID();
1710 ::SendMessage(::GetParent(MainHWND()), WM_NOTIFY,
1711 GetCtrlID(), reinterpret_cast<LPARAM>(scn));
1714 void ScintillaWin::NotifyDoubleClick(Point pt, int modifiers) {
1715 //Platform::DebugPrintf("ScintillaWin Double click 0\n");
1716 ScintillaBase::NotifyDoubleClick(pt, modifiers);
1717 // Send myself a WM_LBUTTONDBLCLK, so the container can handle it too.
1718 ::SendMessage(MainHWND(),
1719 WM_LBUTTONDBLCLK,
1720 (modifiers & SCI_SHIFT) ? MK_SHIFT : 0,
1721 MAKELPARAM(pt.x, pt.y));
1724 class CaseFolderDBCS : public CaseFolderTable {
1725 // Allocate the expandable storage here so that it does not need to be reallocated
1726 // for each call to Fold.
1727 std::vector<wchar_t> utf16Mixed;
1728 std::vector<wchar_t> utf16Folded;
1729 UINT cp;
1730 public:
1731 explicit CaseFolderDBCS(UINT cp_) : cp(cp_) {
1732 StandardASCII();
1734 virtual size_t Fold(char *folded, size_t sizeFolded, const char *mixed, size_t lenMixed) {
1735 if ((lenMixed == 1) && (sizeFolded > 0)) {
1736 folded[0] = mapping[static_cast<unsigned char>(mixed[0])];
1737 return 1;
1738 } else {
1739 if (lenMixed > utf16Mixed.size()) {
1740 utf16Mixed.resize(lenMixed + 8);
1742 size_t nUtf16Mixed = ::MultiByteToWideChar(cp, 0, mixed,
1743 static_cast<int>(lenMixed),
1744 &utf16Mixed[0],
1745 static_cast<int>(utf16Mixed.size()));
1747 if (nUtf16Mixed == 0) {
1748 // Failed to convert -> bad input
1749 folded[0] = '\0';
1750 return 1;
1753 unsigned int lenFlat = 0;
1754 for (size_t mixIndex=0; mixIndex < nUtf16Mixed; mixIndex++) {
1755 if ((lenFlat + 20) > utf16Folded.size())
1756 utf16Folded.resize(lenFlat + 60);
1757 const char *foldedUTF8 = CaseConvert(utf16Mixed[mixIndex], CaseConversionFold);
1758 if (foldedUTF8) {
1759 // Maximum length of a case conversion is 6 bytes, 3 characters
1760 wchar_t wFolded[20];
1761 unsigned int charsConverted = UTF16FromUTF8(foldedUTF8,
1762 static_cast<unsigned int>(strlen(foldedUTF8)),
1763 wFolded, ELEMENTS(wFolded));
1764 for (size_t j=0; j<charsConverted; j++)
1765 utf16Folded[lenFlat++] = wFolded[j];
1766 } else {
1767 utf16Folded[lenFlat++] = utf16Mixed[mixIndex];
1771 size_t lenOut = ::WideCharToMultiByte(cp, 0,
1772 &utf16Folded[0], lenFlat,
1773 NULL, 0, NULL, 0);
1775 if (lenOut < sizeFolded) {
1776 ::WideCharToMultiByte(cp, 0,
1777 &utf16Folded[0], lenFlat,
1778 folded, static_cast<int>(lenOut), NULL, 0);
1779 return lenOut;
1780 } else {
1781 return 0;
1787 CaseFolder *ScintillaWin::CaseFolderForEncoding() {
1788 UINT cpDest = CodePageOfDocument();
1789 if (cpDest == SC_CP_UTF8) {
1790 return new CaseFolderUnicode();
1791 } else {
1792 if (pdoc->dbcsCodePage == 0) {
1793 CaseFolderTable *pcf = new CaseFolderTable();
1794 pcf->StandardASCII();
1795 // Only for single byte encodings
1796 UINT cpDoc = CodePageOfDocument();
1797 for (int i=0x80; i<0x100; i++) {
1798 char sCharacter[2] = "A";
1799 sCharacter[0] = static_cast<char>(i);
1800 wchar_t wCharacter[20];
1801 unsigned int lengthUTF16 = ::MultiByteToWideChar(cpDoc, 0, sCharacter, 1,
1802 wCharacter, ELEMENTS(wCharacter));
1803 if (lengthUTF16 == 1) {
1804 const char *caseFolded = CaseConvert(wCharacter[0], CaseConversionFold);
1805 if (caseFolded) {
1806 wchar_t wLower[20];
1807 unsigned int charsConverted = UTF16FromUTF8(caseFolded,
1808 static_cast<unsigned int>(strlen(caseFolded)),
1809 wLower, ELEMENTS(wLower));
1810 if (charsConverted == 1) {
1811 char sCharacterLowered[20];
1812 unsigned int lengthConverted = ::WideCharToMultiByte(cpDoc, 0,
1813 wLower, charsConverted,
1814 sCharacterLowered, ELEMENTS(sCharacterLowered), NULL, 0);
1815 if ((lengthConverted == 1) && (sCharacter[0] != sCharacterLowered[0])) {
1816 pcf->SetTranslation(sCharacter[0], sCharacterLowered[0]);
1822 return pcf;
1823 } else {
1824 return new CaseFolderDBCS(cpDest);
1829 std::string ScintillaWin::CaseMapString(const std::string &s, int caseMapping) {
1830 if ((s.size() == 0) || (caseMapping == cmSame))
1831 return s;
1833 UINT cpDoc = CodePageOfDocument();
1834 if (cpDoc == SC_CP_UTF8) {
1835 std::string retMapped(s.length() * maxExpansionCaseConversion, 0);
1836 size_t lenMapped = CaseConvertString(&retMapped[0], retMapped.length(), s.c_str(), s.length(),
1837 (caseMapping == cmUpper) ? CaseConversionUpper : CaseConversionLower);
1838 retMapped.resize(lenMapped);
1839 return retMapped;
1842 unsigned int lengthUTF16 = ::MultiByteToWideChar(cpDoc, 0, s.c_str(),
1843 static_cast<int>(s.size()), NULL, 0);
1844 if (lengthUTF16 == 0) // Failed to convert
1845 return s;
1847 DWORD mapFlags = LCMAP_LINGUISTIC_CASING |
1848 ((caseMapping == cmUpper) ? LCMAP_UPPERCASE : LCMAP_LOWERCASE);
1850 // Change text to UTF-16
1851 std::vector<wchar_t> vwcText(lengthUTF16);
1852 ::MultiByteToWideChar(cpDoc, 0, s.c_str(), static_cast<int>(s.size()), &vwcText[0], lengthUTF16);
1854 // Change case
1855 int charsConverted = ::LCMapStringW(LOCALE_SYSTEM_DEFAULT, mapFlags,
1856 &vwcText[0], lengthUTF16, NULL, 0);
1857 std::vector<wchar_t> vwcConverted(charsConverted);
1858 ::LCMapStringW(LOCALE_SYSTEM_DEFAULT, mapFlags,
1859 &vwcText[0], lengthUTF16, &vwcConverted[0], charsConverted);
1861 // Change back to document encoding
1862 unsigned int lengthConverted = ::WideCharToMultiByte(cpDoc, 0,
1863 &vwcConverted[0], static_cast<int>(vwcConverted.size()),
1864 NULL, 0, NULL, 0);
1865 std::vector<char> vcConverted(lengthConverted);
1866 ::WideCharToMultiByte(cpDoc, 0,
1867 &vwcConverted[0], static_cast<int>(vwcConverted.size()),
1868 &vcConverted[0], static_cast<int>(vcConverted.size()), NULL, 0);
1870 return std::string(&vcConverted[0], vcConverted.size());
1873 void ScintillaWin::Copy() {
1874 //Platform::DebugPrintf("Copy\n");
1875 if (!sel.Empty()) {
1876 SelectionText selectedText;
1877 CopySelectionRange(&selectedText);
1878 CopyToClipboard(selectedText);
1882 void ScintillaWin::CopyAllowLine() {
1883 SelectionText selectedText;
1884 CopySelectionRange(&selectedText, true);
1885 CopyToClipboard(selectedText);
1888 bool ScintillaWin::CanPaste() {
1889 if (!Editor::CanPaste())
1890 return false;
1891 if (::IsClipboardFormatAvailable(CF_TEXT))
1892 return true;
1893 if (IsUnicodeMode())
1894 return ::IsClipboardFormatAvailable(CF_UNICODETEXT) != 0;
1895 return false;
1898 class GlobalMemory {
1899 HGLOBAL hand;
1900 public:
1901 void *ptr;
1902 GlobalMemory() : hand(0), ptr(0) {
1904 explicit GlobalMemory(HGLOBAL hand_) : hand(hand_), ptr(0) {
1905 if (hand) {
1906 ptr = ::GlobalLock(hand);
1909 ~GlobalMemory() {
1910 PLATFORM_ASSERT(!ptr);
1912 void Allocate(size_t bytes) {
1913 hand = ::GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT, bytes);
1914 if (hand) {
1915 ptr = ::GlobalLock(hand);
1918 HGLOBAL Unlock() {
1919 PLATFORM_ASSERT(ptr);
1920 HGLOBAL handCopy = hand;
1921 ::GlobalUnlock(hand);
1922 ptr = 0;
1923 hand = 0;
1924 return handCopy;
1926 void SetClip(UINT uFormat) {
1927 ::SetClipboardData(uFormat, Unlock());
1929 operator bool() const {
1930 return ptr != 0;
1932 SIZE_T Size() {
1933 return ::GlobalSize(hand);
1937 void ScintillaWin::Paste() {
1938 if (!::OpenClipboard(MainHWND()))
1939 return;
1940 UndoGroup ug(pdoc);
1941 const bool isLine = SelectionEmpty() &&
1942 (::IsClipboardFormatAvailable(cfLineSelect) || ::IsClipboardFormatAvailable(cfVSLineTag));
1943 ClearSelection(multiPasteMode == SC_MULTIPASTE_EACH);
1944 bool isRectangular = (::IsClipboardFormatAvailable(cfColumnSelect) != 0);
1946 if (!isRectangular) {
1947 // Evaluate "Borland IDE Block Type" explicitly
1948 GlobalMemory memBorlandSelection(::GetClipboardData(cfBorlandIDEBlockType));
1949 if (memBorlandSelection) {
1950 isRectangular = (memBorlandSelection.Size() == 1) && (static_cast<BYTE *>(memBorlandSelection.ptr)[0] == 0x02);
1951 memBorlandSelection.Unlock();
1954 const PasteShape pasteShape = isRectangular ? pasteRectangular : (isLine ? pasteLine : pasteStream);
1956 // Always use CF_UNICODETEXT if available
1957 GlobalMemory memUSelection(::GetClipboardData(CF_UNICODETEXT));
1958 if (memUSelection) {
1959 wchar_t *uptr = static_cast<wchar_t *>(memUSelection.ptr);
1960 if (uptr) {
1961 unsigned int len;
1962 std::vector<char> putf;
1963 // Default Scintilla behaviour in Unicode mode
1964 if (IsUnicodeMode()) {
1965 unsigned int bytes = static_cast<unsigned int>(memUSelection.Size());
1966 len = UTF8Length(uptr, bytes / 2);
1967 putf.resize(len + 1);
1968 UTF8FromUTF16(uptr, bytes / 2, &putf[0], len);
1969 } else {
1970 // CF_UNICODETEXT available, but not in Unicode mode
1971 // Convert from Unicode to current Scintilla code page
1972 UINT cpDest = CodePageOfDocument();
1973 len = ::WideCharToMultiByte(cpDest, 0, uptr, -1,
1974 NULL, 0, NULL, NULL) - 1; // subtract 0 terminator
1975 putf.resize(len + 1);
1976 ::WideCharToMultiByte(cpDest, 0, uptr, -1,
1977 &putf[0], len + 1, NULL, NULL);
1980 InsertPasteShape(&putf[0], len, pasteShape);
1982 memUSelection.Unlock();
1983 } else {
1984 // CF_UNICODETEXT not available, paste ANSI text
1985 GlobalMemory memSelection(::GetClipboardData(CF_TEXT));
1986 if (memSelection) {
1987 char *ptr = static_cast<char *>(memSelection.ptr);
1988 if (ptr) {
1989 unsigned int bytes = static_cast<unsigned int>(memSelection.Size());
1990 unsigned int len = bytes;
1991 for (unsigned int i = 0; i < bytes; i++) {
1992 if ((len == bytes) && (0 == ptr[i]))
1993 len = i;
1996 // In Unicode mode, convert clipboard text to UTF-8
1997 if (IsUnicodeMode()) {
1998 std::vector<wchar_t> uptr(len+1);
2000 unsigned int ulen = ::MultiByteToWideChar(CP_ACP, 0,
2001 ptr, len, &uptr[0], len+1);
2003 unsigned int mlen = UTF8Length(&uptr[0], ulen);
2004 std::vector<char> putf(mlen+1);
2005 // CP_UTF8 not available on Windows 95, so use UTF8FromUTF16()
2006 UTF8FromUTF16(&uptr[0], ulen, &putf[0], mlen);
2008 InsertPasteShape(&putf[0], mlen, pasteShape);
2009 } else {
2010 InsertPasteShape(ptr, len, pasteShape);
2013 memSelection.Unlock();
2016 ::CloseClipboard();
2017 Redraw();
2020 void ScintillaWin::CreateCallTipWindow(PRectangle) {
2021 if (!ct.wCallTip.Created()) {
2022 ct.wCallTip = ::CreateWindow(callClassName, TEXT("ACallTip"),
2023 WS_POPUP, 100, 100, 150, 20,
2024 MainHWND(), 0,
2025 GetWindowInstance(MainHWND()),
2026 this);
2027 ct.wDraw = ct.wCallTip;
2031 void ScintillaWin::AddToPopUp(const char *label, int cmd, bool enabled) {
2032 HMENU hmenuPopup = reinterpret_cast<HMENU>(popup.GetID());
2033 if (!label[0])
2034 ::AppendMenuA(hmenuPopup, MF_SEPARATOR, 0, "");
2035 else if (enabled)
2036 ::AppendMenuA(hmenuPopup, MF_STRING, cmd, label);
2037 else
2038 ::AppendMenuA(hmenuPopup, MF_STRING | MF_DISABLED | MF_GRAYED, cmd, label);
2041 void ScintillaWin::ClaimSelection() {
2042 // Windows does not have a primary selection
2045 /// Implement IUnknown
2047 STDMETHODIMP_(ULONG)FormatEnumerator_AddRef(FormatEnumerator *fe);
2048 STDMETHODIMP FormatEnumerator_QueryInterface(FormatEnumerator *fe, REFIID riid, PVOID *ppv) {
2049 //Platform::DebugPrintf("EFE QI");
2050 *ppv = NULL;
2051 if (riid == IID_IUnknown)
2052 *ppv = reinterpret_cast<IEnumFORMATETC *>(fe);
2053 if (riid == IID_IEnumFORMATETC)
2054 *ppv = reinterpret_cast<IEnumFORMATETC *>(fe);
2055 if (!*ppv)
2056 return E_NOINTERFACE;
2057 FormatEnumerator_AddRef(fe);
2058 return S_OK;
2060 STDMETHODIMP_(ULONG)FormatEnumerator_AddRef(FormatEnumerator *fe) {
2061 return ++fe->ref;
2063 STDMETHODIMP_(ULONG)FormatEnumerator_Release(FormatEnumerator *fe) {
2064 fe->ref--;
2065 if (fe->ref > 0)
2066 return fe->ref;
2067 delete fe;
2068 return 0;
2070 /// Implement IEnumFORMATETC
2071 STDMETHODIMP FormatEnumerator_Next(FormatEnumerator *fe, ULONG celt, FORMATETC *rgelt, ULONG *pceltFetched) {
2072 if (rgelt == NULL) return E_POINTER;
2073 unsigned int putPos = 0;
2074 while ((fe->pos < fe->formats.size()) && (putPos < celt)) {
2075 rgelt->cfFormat = fe->formats[fe->pos];
2076 rgelt->ptd = 0;
2077 rgelt->dwAspect = DVASPECT_CONTENT;
2078 rgelt->lindex = -1;
2079 rgelt->tymed = TYMED_HGLOBAL;
2080 rgelt++;
2081 fe->pos++;
2082 putPos++;
2084 if (pceltFetched)
2085 *pceltFetched = putPos;
2086 return putPos ? S_OK : S_FALSE;
2088 STDMETHODIMP FormatEnumerator_Skip(FormatEnumerator *fe, ULONG celt) {
2089 fe->pos += celt;
2090 return S_OK;
2092 STDMETHODIMP FormatEnumerator_Reset(FormatEnumerator *fe) {
2093 fe->pos = 0;
2094 return S_OK;
2096 STDMETHODIMP FormatEnumerator_Clone(FormatEnumerator *fe, IEnumFORMATETC **ppenum) {
2097 FormatEnumerator *pfe;
2098 try {
2099 pfe = new FormatEnumerator(fe->pos, &fe->formats[0], fe->formats.size());
2100 } catch (...) {
2101 return E_OUTOFMEMORY;
2103 return FormatEnumerator_QueryInterface(pfe, IID_IEnumFORMATETC,
2104 reinterpret_cast<void **>(ppenum));
2107 static VFunction *vtFormatEnumerator[] = {
2108 (VFunction *)(FormatEnumerator_QueryInterface),
2109 (VFunction *)(FormatEnumerator_AddRef),
2110 (VFunction *)(FormatEnumerator_Release),
2111 (VFunction *)(FormatEnumerator_Next),
2112 (VFunction *)(FormatEnumerator_Skip),
2113 (VFunction *)(FormatEnumerator_Reset),
2114 (VFunction *)(FormatEnumerator_Clone)
2117 FormatEnumerator::FormatEnumerator(int pos_, CLIPFORMAT formats_[], size_t formatsLen_) {
2118 vtbl = vtFormatEnumerator;
2119 ref = 0; // First QI adds first reference...
2120 pos = pos_;
2121 formats.insert(formats.begin(), formats_, formats_+formatsLen_);
2124 /// Implement IUnknown
2125 STDMETHODIMP DropSource_QueryInterface(DropSource *ds, REFIID riid, PVOID *ppv) {
2126 return ds->sci->QueryInterface(riid, ppv);
2128 STDMETHODIMP_(ULONG)DropSource_AddRef(DropSource *ds) {
2129 return ds->sci->AddRef();
2131 STDMETHODIMP_(ULONG)DropSource_Release(DropSource *ds) {
2132 return ds->sci->Release();
2135 /// Implement IDropSource
2136 STDMETHODIMP DropSource_QueryContinueDrag(DropSource *, BOOL fEsc, DWORD grfKeyState) {
2137 if (fEsc)
2138 return DRAGDROP_S_CANCEL;
2139 if (!(grfKeyState & MK_LBUTTON))
2140 return DRAGDROP_S_DROP;
2141 return S_OK;
2144 STDMETHODIMP DropSource_GiveFeedback(DropSource *, DWORD) {
2145 return DRAGDROP_S_USEDEFAULTCURSORS;
2148 static VFunction *vtDropSource[] = {
2149 (VFunction *)(DropSource_QueryInterface),
2150 (VFunction *)(DropSource_AddRef),
2151 (VFunction *)(DropSource_Release),
2152 (VFunction *)(DropSource_QueryContinueDrag),
2153 (VFunction *)(DropSource_GiveFeedback)
2156 DropSource::DropSource() {
2157 vtbl = vtDropSource;
2158 sci = 0;
2161 /// Implement IUnkown
2162 STDMETHODIMP DataObject_QueryInterface(DataObject *pd, REFIID riid, PVOID *ppv) {
2163 //Platform::DebugPrintf("DO QI %x\n", pd);
2164 return pd->sci->QueryInterface(riid, ppv);
2166 STDMETHODIMP_(ULONG)DataObject_AddRef(DataObject *pd) {
2167 return pd->sci->AddRef();
2169 STDMETHODIMP_(ULONG)DataObject_Release(DataObject *pd) {
2170 return pd->sci->Release();
2172 /// Implement IDataObject
2173 STDMETHODIMP DataObject_GetData(DataObject *pd, FORMATETC *pFEIn, STGMEDIUM *pSTM) {
2174 return pd->sci->GetData(pFEIn, pSTM);
2177 STDMETHODIMP DataObject_GetDataHere(DataObject *, FORMATETC *, STGMEDIUM *) {
2178 //Platform::DebugPrintf("DOB GetDataHere\n");
2179 return E_NOTIMPL;
2182 STDMETHODIMP DataObject_QueryGetData(DataObject *pd, FORMATETC *pFE) {
2183 if (pd->sci->DragIsRectangularOK(pFE->cfFormat) &&
2184 pFE->ptd == 0 &&
2185 (pFE->dwAspect & DVASPECT_CONTENT) != 0 &&
2186 pFE->lindex == -1 &&
2187 (pFE->tymed & TYMED_HGLOBAL) != 0
2189 return S_OK;
2192 bool formatOK = (pFE->cfFormat == CF_TEXT) ||
2193 ((pFE->cfFormat == CF_UNICODETEXT) && pd->sci->IsUnicodeMode());
2194 if (!formatOK ||
2195 pFE->ptd != 0 ||
2196 (pFE->dwAspect & DVASPECT_CONTENT) == 0 ||
2197 pFE->lindex != -1 ||
2198 (pFE->tymed & TYMED_HGLOBAL) == 0
2200 //Platform::DebugPrintf("DOB QueryGetData No %x\n",pFE->cfFormat);
2201 //return DATA_E_FORMATETC;
2202 return S_FALSE;
2204 //Platform::DebugPrintf("DOB QueryGetData OK %x\n",pFE->cfFormat);
2205 return S_OK;
2208 STDMETHODIMP DataObject_GetCanonicalFormatEtc(DataObject *pd, FORMATETC *, FORMATETC *pFEOut) {
2209 //Platform::DebugPrintf("DOB GetCanon\n");
2210 if (pd->sci->IsUnicodeMode())
2211 pFEOut->cfFormat = CF_UNICODETEXT;
2212 else
2213 pFEOut->cfFormat = CF_TEXT;
2214 pFEOut->ptd = 0;
2215 pFEOut->dwAspect = DVASPECT_CONTENT;
2216 pFEOut->lindex = -1;
2217 pFEOut->tymed = TYMED_HGLOBAL;
2218 return S_OK;
2221 STDMETHODIMP DataObject_SetData(DataObject *, FORMATETC *, STGMEDIUM *, BOOL) {
2222 //Platform::DebugPrintf("DOB SetData\n");
2223 return E_FAIL;
2226 STDMETHODIMP DataObject_EnumFormatEtc(DataObject *pd, DWORD dwDirection, IEnumFORMATETC **ppEnum) {
2227 try {
2228 //Platform::DebugPrintf("DOB EnumFormatEtc %d\n", dwDirection);
2229 if (dwDirection != DATADIR_GET) {
2230 *ppEnum = 0;
2231 return E_FAIL;
2233 FormatEnumerator *pfe;
2234 if (pd->sci->IsUnicodeMode()) {
2235 CLIPFORMAT formats[] = {CF_UNICODETEXT, CF_TEXT};
2236 pfe = new FormatEnumerator(0, formats, ELEMENTS(formats));
2237 } else {
2238 CLIPFORMAT formats[] = {CF_TEXT};
2239 pfe = new FormatEnumerator(0, formats, ELEMENTS(formats));
2241 return FormatEnumerator_QueryInterface(pfe, IID_IEnumFORMATETC,
2242 reinterpret_cast<void **>(ppEnum));
2243 } catch (std::bad_alloc &) {
2244 pd->sci->errorStatus = SC_STATUS_BADALLOC;
2245 return E_OUTOFMEMORY;
2246 } catch (...) {
2247 pd->sci->errorStatus = SC_STATUS_FAILURE;
2248 return E_FAIL;
2252 STDMETHODIMP DataObject_DAdvise(DataObject *, FORMATETC *, DWORD, IAdviseSink *, PDWORD) {
2253 //Platform::DebugPrintf("DOB DAdvise\n");
2254 return E_FAIL;
2257 STDMETHODIMP DataObject_DUnadvise(DataObject *, DWORD) {
2258 //Platform::DebugPrintf("DOB DUnadvise\n");
2259 return E_FAIL;
2262 STDMETHODIMP DataObject_EnumDAdvise(DataObject *, IEnumSTATDATA **) {
2263 //Platform::DebugPrintf("DOB EnumDAdvise\n");
2264 return E_FAIL;
2267 static VFunction *vtDataObject[] = {
2268 (VFunction *)(DataObject_QueryInterface),
2269 (VFunction *)(DataObject_AddRef),
2270 (VFunction *)(DataObject_Release),
2271 (VFunction *)(DataObject_GetData),
2272 (VFunction *)(DataObject_GetDataHere),
2273 (VFunction *)(DataObject_QueryGetData),
2274 (VFunction *)(DataObject_GetCanonicalFormatEtc),
2275 (VFunction *)(DataObject_SetData),
2276 (VFunction *)(DataObject_EnumFormatEtc),
2277 (VFunction *)(DataObject_DAdvise),
2278 (VFunction *)(DataObject_DUnadvise),
2279 (VFunction *)(DataObject_EnumDAdvise)
2282 DataObject::DataObject() {
2283 vtbl = vtDataObject;
2284 sci = 0;
2287 /// Implement IUnknown
2288 STDMETHODIMP DropTarget_QueryInterface(DropTarget *dt, REFIID riid, PVOID *ppv) {
2289 //Platform::DebugPrintf("DT QI %x\n", dt);
2290 return dt->sci->QueryInterface(riid, ppv);
2292 STDMETHODIMP_(ULONG)DropTarget_AddRef(DropTarget *dt) {
2293 return dt->sci->AddRef();
2295 STDMETHODIMP_(ULONG)DropTarget_Release(DropTarget *dt) {
2296 return dt->sci->Release();
2299 /// Implement IDropTarget by forwarding to Scintilla
2300 STDMETHODIMP DropTarget_DragEnter(DropTarget *dt, LPDATAOBJECT pIDataSource, DWORD grfKeyState,
2301 POINTL pt, PDWORD pdwEffect) {
2302 try {
2303 return dt->sci->DragEnter(pIDataSource, grfKeyState, pt, pdwEffect);
2304 } catch (...) {
2305 dt->sci->errorStatus = SC_STATUS_FAILURE;
2307 return E_FAIL;
2309 STDMETHODIMP DropTarget_DragOver(DropTarget *dt, DWORD grfKeyState, POINTL pt, PDWORD pdwEffect) {
2310 try {
2311 return dt->sci->DragOver(grfKeyState, pt, pdwEffect);
2312 } catch (...) {
2313 dt->sci->errorStatus = SC_STATUS_FAILURE;
2315 return E_FAIL;
2317 STDMETHODIMP DropTarget_DragLeave(DropTarget *dt) {
2318 try {
2319 return dt->sci->DragLeave();
2320 } catch (...) {
2321 dt->sci->errorStatus = SC_STATUS_FAILURE;
2323 return E_FAIL;
2325 STDMETHODIMP DropTarget_Drop(DropTarget *dt, LPDATAOBJECT pIDataSource, DWORD grfKeyState,
2326 POINTL pt, PDWORD pdwEffect) {
2327 try {
2328 return dt->sci->Drop(pIDataSource, grfKeyState, pt, pdwEffect);
2329 } catch (...) {
2330 dt->sci->errorStatus = SC_STATUS_FAILURE;
2332 return E_FAIL;
2335 static VFunction *vtDropTarget[] = {
2336 (VFunction *)(DropTarget_QueryInterface),
2337 (VFunction *)(DropTarget_AddRef),
2338 (VFunction *)(DropTarget_Release),
2339 (VFunction *)(DropTarget_DragEnter),
2340 (VFunction *)(DropTarget_DragOver),
2341 (VFunction *)(DropTarget_DragLeave),
2342 (VFunction *)(DropTarget_Drop)
2345 DropTarget::DropTarget() {
2346 vtbl = vtDropTarget;
2347 sci = 0;
2351 * DBCS: support Input Method Editor (IME).
2352 * Called when IME Window opened.
2354 void ScintillaWin::ImeStartComposition() {
2355 if (caret.active) {
2356 // Move IME Window to current caret position
2357 HIMC hIMC = ::ImmGetContext(MainHWND());
2358 Point pos = PointMainCaret();
2359 COMPOSITIONFORM CompForm;
2360 CompForm.dwStyle = CFS_POINT;
2361 CompForm.ptCurrentPos.x = static_cast<int>(pos.x);
2362 CompForm.ptCurrentPos.y = static_cast<int>(pos.y);
2364 ::ImmSetCompositionWindow(hIMC, &CompForm);
2366 // Set font of IME window to same as surrounded text.
2367 if (stylesValid) {
2368 // Since the style creation code has been made platform independent,
2369 // The logfont for the IME is recreated here.
2370 int styleHere = (pdoc->StyleAt(sel.MainCaret())) & 31;
2371 LOGFONTW lf = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, L""};
2372 int sizeZoomed = vs.styles[styleHere].size + vs.zoomLevel * SC_FONT_SIZE_MULTIPLIER;
2373 if (sizeZoomed <= 2 * SC_FONT_SIZE_MULTIPLIER) // Hangs if sizeZoomed <= 1
2374 sizeZoomed = 2 * SC_FONT_SIZE_MULTIPLIER;
2375 AutoSurface surface(this);
2376 int deviceHeight = sizeZoomed;
2377 if (surface) {
2378 deviceHeight = (sizeZoomed * surface->LogPixelsY()) / 72;
2380 // The negative is to allow for leading
2381 lf.lfHeight = -(abs(deviceHeight / SC_FONT_SIZE_MULTIPLIER));
2382 lf.lfWeight = vs.styles[styleHere].weight;
2383 lf.lfItalic = static_cast<BYTE>(vs.styles[styleHere].italic ? 1 : 0);
2384 lf.lfCharSet = DEFAULT_CHARSET;
2385 lf.lfFaceName[0] = '\0';
2386 if (vs.styles[styleHere].fontName) {
2387 const char* fontName = vs.styles[styleHere].fontName;
2388 UTF16FromUTF8(fontName, strlen(fontName)+1, lf.lfFaceName, LF_FACESIZE);
2390 ::ImmSetCompositionFontW(hIMC, &lf);
2392 ::ImmReleaseContext(MainHWND(), hIMC);
2393 // Caret is displayed in IME window. So, caret in Scintilla is useless.
2394 DropCaret();
2398 /** Called when IME Window closed. */
2399 void ScintillaWin::ImeEndComposition() {
2400 ShowCaretAtCurrentPosition();
2403 void ScintillaWin::AddCharBytes(char b0, char b1) {
2405 int inputCodePage = InputCodePage();
2406 if (inputCodePage && IsUnicodeMode()) {
2407 char utfval[4] = "\0\0\0";
2408 char ansiChars[3];
2409 wchar_t wcs[2];
2410 if (b0) { // Two bytes from IME
2411 ansiChars[0] = b0;
2412 ansiChars[1] = b1;
2413 ansiChars[2] = '\0';
2414 ::MultiByteToWideChar(inputCodePage, 0, ansiChars, 2, wcs, 1);
2415 } else {
2416 ansiChars[0] = b1;
2417 ansiChars[1] = '\0';
2418 ::MultiByteToWideChar(inputCodePage, 0, ansiChars, 1, wcs, 1);
2420 unsigned int len = UTF8Length(wcs, 1);
2421 UTF8FromUTF16(wcs, 1, utfval, len);
2422 utfval[len] = '\0';
2423 AddCharUTF(utfval, len ? len : 1);
2424 } else if (b0) {
2425 char dbcsChars[3];
2426 dbcsChars[0] = b0;
2427 dbcsChars[1] = b1;
2428 dbcsChars[2] = '\0';
2429 AddCharUTF(dbcsChars, 2, true);
2430 } else {
2431 AddChar(b1);
2435 void ScintillaWin::GetIntelliMouseParameters() {
2436 // This retrieves the number of lines per scroll as configured inthe Mouse Properties sheet in Control Panel
2437 ::SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, &linesPerScroll, 0);
2440 void ScintillaWin::CopyToClipboard(const SelectionText &selectedText) {
2441 if (!::OpenClipboard(MainHWND()))
2442 return;
2443 ::EmptyClipboard();
2445 GlobalMemory uniText;
2447 // Default Scintilla behaviour in Unicode mode
2448 if (IsUnicodeMode()) {
2449 int uchars = UTF16Length(selectedText.Data(),
2450 static_cast<int>(selectedText.LengthWithTerminator()));
2451 uniText.Allocate(2 * uchars);
2452 if (uniText) {
2453 UTF16FromUTF8(selectedText.Data(), static_cast<int>(selectedText.LengthWithTerminator()),
2454 static_cast<wchar_t *>(uniText.ptr), uchars);
2456 } else {
2457 // Not Unicode mode
2458 // Convert to Unicode using the current Scintilla code page
2459 UINT cpSrc = CodePageFromCharSet(
2460 selectedText.characterSet, selectedText.codePage);
2461 int uLen = ::MultiByteToWideChar(cpSrc, 0, selectedText.Data(),
2462 static_cast<int>(selectedText.LengthWithTerminator()), 0, 0);
2463 uniText.Allocate(2 * uLen);
2464 if (uniText) {
2465 ::MultiByteToWideChar(cpSrc, 0, selectedText.Data(),
2466 static_cast<int>(selectedText.LengthWithTerminator()),
2467 static_cast<wchar_t *>(uniText.ptr), uLen);
2471 if (uniText) {
2472 if (!IsNT()) {
2473 // Copy ANSI text to clipboard on Windows 9x
2474 // Convert from Unicode text, so other ANSI programs can
2475 // paste the text
2476 // Windows NT, 2k, XP automatically generates CF_TEXT
2477 GlobalMemory ansiText;
2478 ansiText.Allocate(selectedText.LengthWithTerminator());
2479 if (ansiText) {
2480 ::WideCharToMultiByte(CP_ACP, 0, static_cast<wchar_t *>(uniText.ptr), -1,
2481 static_cast<char *>(ansiText.ptr),
2482 static_cast<int>(selectedText.LengthWithTerminator()), NULL, NULL);
2483 ansiText.SetClip(CF_TEXT);
2486 uniText.SetClip(CF_UNICODETEXT);
2487 } else {
2488 // There was a failure - try to copy at least ANSI text
2489 GlobalMemory ansiText;
2490 ansiText.Allocate(selectedText.LengthWithTerminator());
2491 if (ansiText) {
2492 memcpy(static_cast<char *>(ansiText.ptr), selectedText.Data(), selectedText.LengthWithTerminator());
2493 ansiText.SetClip(CF_TEXT);
2497 if (selectedText.rectangular) {
2498 ::SetClipboardData(cfColumnSelect, 0);
2500 GlobalMemory borlandSelection;
2501 borlandSelection.Allocate(1);
2502 if (borlandSelection) {
2503 static_cast<BYTE *>(borlandSelection.ptr)[0] = 0x02;
2504 borlandSelection.SetClip(cfBorlandIDEBlockType);
2508 if (selectedText.lineCopy) {
2509 ::SetClipboardData(cfLineSelect, 0);
2510 ::SetClipboardData(cfVSLineTag, 0);
2513 ::CloseClipboard();
2516 void ScintillaWin::ScrollMessage(WPARAM wParam) {
2517 //DWORD dwStart = timeGetTime();
2518 //Platform::DebugPrintf("Scroll %x %d\n", wParam, lParam);
2520 SCROLLINFO sci = {};
2521 sci.cbSize = sizeof(sci);
2522 sci.fMask = SIF_ALL;
2524 GetScrollInfo(SB_VERT, &sci);
2526 //Platform::DebugPrintf("ScrollInfo %d mask=%x min=%d max=%d page=%d pos=%d track=%d\n", b,sci.fMask,
2527 //sci.nMin, sci.nMax, sci.nPage, sci.nPos, sci.nTrackPos);
2529 int topLineNew = topLine;
2530 switch (LoWord(wParam)) {
2531 case SB_LINEUP:
2532 topLineNew -= 1;
2533 break;
2534 case SB_LINEDOWN:
2535 topLineNew += 1;
2536 break;
2537 case SB_PAGEUP:
2538 topLineNew -= LinesToScroll(); break;
2539 case SB_PAGEDOWN: topLineNew += LinesToScroll(); break;
2540 case SB_TOP: topLineNew = 0; break;
2541 case SB_BOTTOM: topLineNew = MaxScrollPos(); break;
2542 case SB_THUMBPOSITION: topLineNew = sci.nTrackPos; break;
2543 case SB_THUMBTRACK: topLineNew = sci.nTrackPos; break;
2545 ScrollTo(topLineNew);
2548 void ScintillaWin::HorizontalScrollMessage(WPARAM wParam) {
2549 int xPos = xOffset;
2550 PRectangle rcText = GetTextRectangle();
2551 int pageWidth = static_cast<int>(rcText.Width() * 2 / 3);
2552 switch (LoWord(wParam)) {
2553 case SB_LINEUP:
2554 xPos -= 20;
2555 break;
2556 case SB_LINEDOWN: // May move past the logical end
2557 xPos += 20;
2558 break;
2559 case SB_PAGEUP:
2560 xPos -= pageWidth;
2561 break;
2562 case SB_PAGEDOWN:
2563 xPos += pageWidth;
2564 if (xPos > scrollWidth - rcText.Width()) { // Hit the end exactly
2565 xPos = scrollWidth - static_cast<int>(rcText.Width());
2567 break;
2568 case SB_TOP:
2569 xPos = 0;
2570 break;
2571 case SB_BOTTOM:
2572 xPos = scrollWidth;
2573 break;
2574 case SB_THUMBPOSITION:
2575 case SB_THUMBTRACK: {
2576 // 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 =]
2577 SCROLLINFO si;
2578 si.cbSize = sizeof(si);
2579 si.fMask = SIF_TRACKPOS;
2580 if (GetScrollInfo(SB_HORZ, &si)) {
2581 xPos = si.nTrackPos;
2584 break;
2586 HorizontalScrollTo(xPos);
2590 * Redraw all of text area.
2591 * This paint will not be abandoned.
2593 void ScintillaWin::FullPaint() {
2594 if ((technology == SC_TECHNOLOGY_DEFAULT) || (technology == SC_TECHNOLOGY_DIRECTWRITEDC)) {
2595 HDC hdc = ::GetDC(MainHWND());
2596 FullPaintDC(hdc);
2597 ::ReleaseDC(MainHWND(), hdc);
2598 } else {
2599 FullPaintDC(0);
2604 * Redraw all of text area on the specified DC.
2605 * This paint will not be abandoned.
2607 void ScintillaWin::FullPaintDC(HDC hdc) {
2608 paintState = painting;
2609 rcPaint = GetClientRectangle();
2610 paintingAllText = true;
2611 if (technology == SC_TECHNOLOGY_DEFAULT) {
2612 AutoSurface surfaceWindow(hdc, this);
2613 if (surfaceWindow) {
2614 Paint(surfaceWindow, rcPaint);
2615 surfaceWindow->Release();
2617 } else {
2618 #if defined(USE_D2D)
2619 EnsureRenderTarget(hdc);
2620 AutoSurface surfaceWindow(pRenderTarget, this);
2621 if (surfaceWindow) {
2622 pRenderTarget->BeginDraw();
2623 Paint(surfaceWindow, rcPaint);
2624 surfaceWindow->Release();
2625 HRESULT hr = pRenderTarget->EndDraw();
2626 if (hr == D2DERR_RECREATE_TARGET) {
2627 DropRenderTarget();
2630 #endif
2632 paintState = notPainting;
2635 static bool CompareDevCap(HDC hdc, HDC hOtherDC, int nIndex) {
2636 return ::GetDeviceCaps(hdc, nIndex) == ::GetDeviceCaps(hOtherDC, nIndex);
2639 bool ScintillaWin::IsCompatibleDC(HDC hOtherDC) {
2640 HDC hdc = ::GetDC(MainHWND());
2641 bool isCompatible =
2642 CompareDevCap(hdc, hOtherDC, TECHNOLOGY) &&
2643 CompareDevCap(hdc, hOtherDC, LOGPIXELSY) &&
2644 CompareDevCap(hdc, hOtherDC, LOGPIXELSX) &&
2645 CompareDevCap(hdc, hOtherDC, BITSPIXEL) &&
2646 CompareDevCap(hdc, hOtherDC, PLANES);
2647 ::ReleaseDC(MainHWND(), hdc);
2648 return isCompatible;
2651 DWORD ScintillaWin::EffectFromState(DWORD grfKeyState) const {
2652 // These are the Wordpad semantics.
2653 DWORD dwEffect;
2654 if (inDragDrop == ddDragging) // Internal defaults to move
2655 dwEffect = DROPEFFECT_MOVE;
2656 else
2657 dwEffect = DROPEFFECT_COPY;
2658 if (grfKeyState & MK_ALT)
2659 dwEffect = DROPEFFECT_MOVE;
2660 if (grfKeyState & MK_CONTROL)
2661 dwEffect = DROPEFFECT_COPY;
2662 return dwEffect;
2665 /// Implement IUnknown
2666 STDMETHODIMP ScintillaWin::QueryInterface(REFIID riid, PVOID *ppv) {
2667 *ppv = NULL;
2668 if (riid == IID_IUnknown)
2669 *ppv = reinterpret_cast<IDropTarget *>(&dt);
2670 if (riid == IID_IDropSource)
2671 *ppv = reinterpret_cast<IDropSource *>(&ds);
2672 if (riid == IID_IDropTarget)
2673 *ppv = reinterpret_cast<IDropTarget *>(&dt);
2674 if (riid == IID_IDataObject)
2675 *ppv = reinterpret_cast<IDataObject *>(&dob);
2676 if (!*ppv)
2677 return E_NOINTERFACE;
2678 return S_OK;
2681 STDMETHODIMP_(ULONG) ScintillaWin::AddRef() {
2682 return 1;
2685 STDMETHODIMP_(ULONG) ScintillaWin::Release() {
2686 return 1;
2689 /// Implement IDropTarget
2690 STDMETHODIMP ScintillaWin::DragEnter(LPDATAOBJECT pIDataSource, DWORD grfKeyState,
2691 POINTL, PDWORD pdwEffect) {
2692 if (pIDataSource == NULL)
2693 return E_POINTER;
2694 FORMATETC fmtu = {CF_UNICODETEXT, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
2695 HRESULT hrHasUText = pIDataSource->QueryGetData(&fmtu);
2696 hasOKText = (hrHasUText == S_OK);
2697 if (!hasOKText) {
2698 FORMATETC fmte = {CF_TEXT, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
2699 HRESULT hrHasText = pIDataSource->QueryGetData(&fmte);
2700 hasOKText = (hrHasText == S_OK);
2702 if (!hasOKText) {
2703 *pdwEffect = DROPEFFECT_NONE;
2704 return S_OK;
2707 *pdwEffect = EffectFromState(grfKeyState);
2708 return S_OK;
2711 STDMETHODIMP ScintillaWin::DragOver(DWORD grfKeyState, POINTL pt, PDWORD pdwEffect) {
2712 try {
2713 if (!hasOKText || pdoc->IsReadOnly()) {
2714 *pdwEffect = DROPEFFECT_NONE;
2715 return S_OK;
2718 *pdwEffect = EffectFromState(grfKeyState);
2720 // Update the cursor.
2721 POINT rpt = {pt.x, pt.y};
2722 ::ScreenToClient(MainHWND(), &rpt);
2723 SetDragPosition(SPositionFromLocation(PointFromPOINT(rpt), false, false, UserVirtualSpace()));
2725 return S_OK;
2726 } catch (...) {
2727 errorStatus = SC_STATUS_FAILURE;
2729 return E_FAIL;
2732 STDMETHODIMP ScintillaWin::DragLeave() {
2733 try {
2734 SetDragPosition(SelectionPosition(invalidPosition));
2735 return S_OK;
2736 } catch (...) {
2737 errorStatus = SC_STATUS_FAILURE;
2739 return E_FAIL;
2742 STDMETHODIMP ScintillaWin::Drop(LPDATAOBJECT pIDataSource, DWORD grfKeyState,
2743 POINTL pt, PDWORD pdwEffect) {
2744 try {
2745 *pdwEffect = EffectFromState(grfKeyState);
2747 if (pIDataSource == NULL)
2748 return E_POINTER;
2750 SetDragPosition(SelectionPosition(invalidPosition));
2752 STGMEDIUM medium = {0, {0}, 0};
2754 std::vector<char> data; // Includes terminating NUL
2756 FORMATETC fmtu = {CF_UNICODETEXT, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
2757 HRESULT hr = pIDataSource->GetData(&fmtu, &medium);
2758 if (SUCCEEDED(hr) && medium.hGlobal) {
2759 GlobalMemory memUDrop(medium.hGlobal);
2760 wchar_t *udata = static_cast<wchar_t *>(memUDrop.ptr);
2761 if (udata) {
2762 if (IsUnicodeMode()) {
2763 int tlen = static_cast<int>(memUDrop.Size());
2764 // Convert UTF-16 to UTF-8
2765 int dataLen = UTF8Length(udata, tlen/2);
2766 data.resize(dataLen+1);
2767 UTF8FromUTF16(udata, tlen/2, &data[0], dataLen);
2768 } else {
2769 // Convert UTF-16 to ANSI
2771 // Default Scintilla behavior in Unicode mode
2772 // CF_UNICODETEXT available, but not in Unicode mode
2773 // Convert from Unicode to current Scintilla code page
2774 UINT cpDest = CodePageOfDocument();
2775 int tlen = ::WideCharToMultiByte(cpDest, 0, udata, -1,
2776 NULL, 0, NULL, NULL) - 1; // subtract 0 terminator
2777 data.resize(tlen + 1);
2778 ::WideCharToMultiByte(cpDest, 0, udata, -1,
2779 &data[0], tlen + 1, NULL, NULL);
2782 memUDrop.Unlock();
2783 } else {
2784 FORMATETC fmte = {CF_TEXT, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
2785 hr = pIDataSource->GetData(&fmte, &medium);
2786 if (SUCCEEDED(hr) && medium.hGlobal) {
2787 GlobalMemory memDrop(medium.hGlobal);
2788 const char *cdata = static_cast<char *>(memDrop.ptr);
2789 if (cdata)
2790 data.assign(cdata, cdata+strlen(cdata)+1);
2791 memDrop.Unlock();
2795 if (!SUCCEEDED(hr) || data.empty()) {
2796 //Platform::DebugPrintf("Bad data format: 0x%x\n", hres);
2797 return hr;
2800 FORMATETC fmtr = {cfColumnSelect, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
2801 HRESULT hrRectangular = pIDataSource->QueryGetData(&fmtr);
2803 POINT rpt = {pt.x, pt.y};
2804 ::ScreenToClient(MainHWND(), &rpt);
2805 SelectionPosition movePos = SPositionFromLocation(PointFromPOINT(rpt), false, false, UserVirtualSpace());
2807 DropAt(movePos, &data[0], data.size() - 1, *pdwEffect == DROPEFFECT_MOVE, hrRectangular == S_OK);
2809 // Free data
2810 if (medium.pUnkForRelease != NULL)
2811 medium.pUnkForRelease->Release();
2812 else
2813 ::GlobalFree(medium.hGlobal);
2815 return S_OK;
2816 } catch (...) {
2817 errorStatus = SC_STATUS_FAILURE;
2819 return E_FAIL;
2822 /// Implement important part of IDataObject
2823 STDMETHODIMP ScintillaWin::GetData(FORMATETC *pFEIn, STGMEDIUM *pSTM) {
2824 bool formatOK = (pFEIn->cfFormat == CF_TEXT) ||
2825 ((pFEIn->cfFormat == CF_UNICODETEXT) && IsUnicodeMode());
2826 if (!formatOK ||
2827 pFEIn->ptd != 0 ||
2828 (pFEIn->dwAspect & DVASPECT_CONTENT) == 0 ||
2829 pFEIn->lindex != -1 ||
2830 (pFEIn->tymed & TYMED_HGLOBAL) == 0
2832 //Platform::DebugPrintf("DOB GetData No %d %x %x fmt=%x\n", lenDrag, pFEIn, pSTM, pFEIn->cfFormat);
2833 return DATA_E_FORMATETC;
2835 pSTM->tymed = TYMED_HGLOBAL;
2836 //Platform::DebugPrintf("DOB GetData OK %d %x %x\n", lenDrag, pFEIn, pSTM);
2838 GlobalMemory text;
2839 if (pFEIn->cfFormat == CF_UNICODETEXT) {
2840 int uchars = UTF16Length(drag.Data(), static_cast<int>(drag.LengthWithTerminator()));
2841 text.Allocate(2 * uchars);
2842 if (text) {
2843 UTF16FromUTF8(drag.Data(), static_cast<int>(drag.LengthWithTerminator()),
2844 static_cast<wchar_t *>(text.ptr), uchars);
2846 } else {
2847 text.Allocate(drag.LengthWithTerminator());
2848 if (text) {
2849 memcpy(static_cast<char *>(text.ptr), drag.Data(), drag.LengthWithTerminator());
2852 pSTM->hGlobal = text ? text.Unlock() : 0;
2853 pSTM->pUnkForRelease = 0;
2854 return S_OK;
2857 bool ScintillaWin::Register(HINSTANCE hInstance_) {
2859 hInstance = hInstance_;
2860 bool result;
2862 // Register the Scintilla class
2863 if (IsNT()) {
2865 // Register Scintilla as a wide character window
2866 WNDCLASSEXW wndclass;
2867 wndclass.cbSize = sizeof(wndclass);
2868 wndclass.style = CS_GLOBALCLASS | CS_HREDRAW | CS_VREDRAW;
2869 wndclass.lpfnWndProc = ScintillaWin::SWndProc;
2870 wndclass.cbClsExtra = 0;
2871 wndclass.cbWndExtra = sizeof(ScintillaWin *);
2872 wndclass.hInstance = hInstance;
2873 wndclass.hIcon = NULL;
2874 wndclass.hCursor = NULL;
2875 wndclass.hbrBackground = NULL;
2876 wndclass.lpszMenuName = NULL;
2877 wndclass.lpszClassName = L"Scintilla";
2878 wndclass.hIconSm = 0;
2879 scintillaClassAtom = ::RegisterClassExW(&wndclass);
2880 result = 0 != scintillaClassAtom;
2881 } else {
2883 // Register Scintilla as a normal character window
2884 WNDCLASSEX wndclass;
2885 wndclass.cbSize = sizeof(wndclass);
2886 wndclass.style = CS_GLOBALCLASS | CS_HREDRAW | CS_VREDRAW;
2887 wndclass.lpfnWndProc = ScintillaWin::SWndProc;
2888 wndclass.cbClsExtra = 0;
2889 wndclass.cbWndExtra = sizeof(ScintillaWin *);
2890 wndclass.hInstance = hInstance;
2891 wndclass.hIcon = NULL;
2892 wndclass.hCursor = NULL;
2893 wndclass.hbrBackground = NULL;
2894 wndclass.lpszMenuName = NULL;
2895 wndclass.lpszClassName = scintillaClassName;
2896 wndclass.hIconSm = 0;
2897 scintillaClassAtom = ::RegisterClassEx(&wndclass);
2898 result = 0 != scintillaClassAtom;
2901 if (result) {
2902 // Register the CallTip class
2903 WNDCLASSEX wndclassc;
2904 wndclassc.cbSize = sizeof(wndclassc);
2905 wndclassc.style = CS_GLOBALCLASS | CS_HREDRAW | CS_VREDRAW;
2906 wndclassc.cbClsExtra = 0;
2907 wndclassc.cbWndExtra = sizeof(ScintillaWin *);
2908 wndclassc.hInstance = hInstance;
2909 wndclassc.hIcon = NULL;
2910 wndclassc.hbrBackground = NULL;
2911 wndclassc.lpszMenuName = NULL;
2912 wndclassc.lpfnWndProc = ScintillaWin::CTWndProc;
2913 wndclassc.hCursor = ::LoadCursor(NULL, IDC_ARROW);
2914 wndclassc.lpszClassName = callClassName;
2915 wndclassc.hIconSm = 0;
2917 callClassAtom = ::RegisterClassEx(&wndclassc);
2918 result = 0 != callClassAtom;
2921 return result;
2924 bool ScintillaWin::Unregister() {
2925 bool result = true;
2926 if (0 != scintillaClassAtom) {
2927 if (::UnregisterClass(MAKEINTATOM(scintillaClassAtom), hInstance) == 0) {
2928 result = false;
2930 scintillaClassAtom = 0;
2932 if (0 != callClassAtom) {
2933 if (::UnregisterClass(MAKEINTATOM(callClassAtom), hInstance) == 0) {
2934 result = false;
2936 callClassAtom = 0;
2938 return result;
2941 bool ScintillaWin::HasCaretSizeChanged() const {
2942 if (
2943 ( (0 != vs.caretWidth) && (sysCaretWidth != vs.caretWidth) )
2944 || ((0 != vs.lineHeight) && (sysCaretHeight != vs.lineHeight))
2946 return true;
2948 return false;
2951 BOOL ScintillaWin::CreateSystemCaret() {
2952 sysCaretWidth = vs.caretWidth;
2953 if (0 == sysCaretWidth) {
2954 sysCaretWidth = 1;
2956 sysCaretHeight = vs.lineHeight;
2957 int bitmapSize = (((sysCaretWidth + 15) & ~15) >> 3) *
2958 sysCaretHeight;
2959 std::vector<char> bits(bitmapSize);
2960 sysCaretBitmap = ::CreateBitmap(sysCaretWidth, sysCaretHeight, 1,
2961 1, reinterpret_cast<BYTE *>(&bits[0]));
2962 BOOL retval = ::CreateCaret(
2963 MainHWND(), sysCaretBitmap,
2964 sysCaretWidth, sysCaretHeight);
2965 if (technology == SC_TECHNOLOGY_DEFAULT) {
2966 // System caret interferes with Direct2D drawing so only show it for GDI.
2967 ::ShowCaret(MainHWND());
2969 return retval;
2972 BOOL ScintillaWin::DestroySystemCaret() {
2973 ::HideCaret(MainHWND());
2974 BOOL retval = ::DestroyCaret();
2975 if (sysCaretBitmap) {
2976 ::DeleteObject(sysCaretBitmap);
2977 sysCaretBitmap = 0;
2979 return retval;
2982 sptr_t PASCAL ScintillaWin::CTWndProc(
2983 HWND hWnd, UINT iMessage, WPARAM wParam, sptr_t lParam) {
2984 // Find C++ object associated with window.
2985 ScintillaWin *sciThis = reinterpret_cast<ScintillaWin *>(PointerFromWindow(hWnd));
2986 try {
2987 // ctp will be zero if WM_CREATE not seen yet
2988 if (sciThis == 0) {
2989 if (iMessage == WM_CREATE) {
2990 // Associate CallTip object with window
2991 CREATESTRUCT *pCreate = reinterpret_cast<CREATESTRUCT *>(lParam);
2992 SetWindowPointer(hWnd, pCreate->lpCreateParams);
2993 return 0;
2994 } else {
2995 return ::DefWindowProc(hWnd, iMessage, wParam, lParam);
2997 } else {
2998 if (iMessage == WM_NCDESTROY) {
2999 ::SetWindowLong(hWnd, 0, 0);
3000 return ::DefWindowProc(hWnd, iMessage, wParam, lParam);
3001 } else if (iMessage == WM_PAINT) {
3002 PAINTSTRUCT ps;
3003 ::BeginPaint(hWnd, &ps);
3004 Surface *surfaceWindow = Surface::Allocate(sciThis->technology);
3005 if (surfaceWindow) {
3006 #if defined(USE_D2D)
3007 ID2D1HwndRenderTarget *pCTRenderTarget = 0;
3008 #endif
3009 RECT rc;
3010 GetClientRect(hWnd, &rc);
3011 // Create a Direct2D render target.
3012 if (sciThis->technology == SC_TECHNOLOGY_DEFAULT) {
3013 surfaceWindow->Init(ps.hdc, hWnd);
3014 } else {
3015 #if defined(USE_D2D)
3016 D2D1_HWND_RENDER_TARGET_PROPERTIES dhrtp;
3017 dhrtp.hwnd = hWnd;
3018 dhrtp.pixelSize = D2D1::SizeU(rc.right - rc.left, rc.bottom - rc.top);
3019 dhrtp.presentOptions = (sciThis->technology == SC_TECHNOLOGY_DIRECTWRITERETAIN) ?
3020 D2D1_PRESENT_OPTIONS_RETAIN_CONTENTS : D2D1_PRESENT_OPTIONS_NONE;
3022 D2D1_RENDER_TARGET_PROPERTIES drtp;
3023 drtp.type = D2D1_RENDER_TARGET_TYPE_DEFAULT;
3024 drtp.pixelFormat.format = DXGI_FORMAT_UNKNOWN;
3025 drtp.pixelFormat.alphaMode = D2D1_ALPHA_MODE_UNKNOWN;
3026 drtp.dpiX = 96.0;
3027 drtp.dpiY = 96.0;
3028 drtp.usage = D2D1_RENDER_TARGET_USAGE_NONE;
3029 drtp.minLevel = D2D1_FEATURE_LEVEL_DEFAULT;
3031 if (!SUCCEEDED(pD2DFactory->CreateHwndRenderTarget(drtp, dhrtp, &pCTRenderTarget))) {
3032 surfaceWindow->Release();
3033 delete surfaceWindow;
3034 ::EndPaint(hWnd, &ps);
3035 return 0;
3037 surfaceWindow->Init(pCTRenderTarget, hWnd);
3038 pCTRenderTarget->BeginDraw();
3039 #endif
3041 surfaceWindow->SetUnicodeMode(SC_CP_UTF8 == sciThis->ct.codePage);
3042 surfaceWindow->SetDBCSMode(sciThis->ct.codePage);
3043 sciThis->ct.PaintCT(surfaceWindow);
3044 #if defined(USE_D2D)
3045 if (pCTRenderTarget)
3046 pCTRenderTarget->EndDraw();
3047 #endif
3048 surfaceWindow->Release();
3049 delete surfaceWindow;
3050 #if defined(USE_D2D)
3051 if (pCTRenderTarget)
3052 pCTRenderTarget->Release();
3053 #endif
3055 ::EndPaint(hWnd, &ps);
3056 return 0;
3057 } else if ((iMessage == WM_NCLBUTTONDOWN) || (iMessage == WM_NCLBUTTONDBLCLK)) {
3058 POINT pt;
3059 pt.x = static_cast<short>(LOWORD(lParam));
3060 pt.y = static_cast<short>(HIWORD(lParam));
3061 ScreenToClient(hWnd, &pt);
3062 sciThis->ct.MouseClick(PointFromPOINT(pt));
3063 sciThis->CallTipClick();
3064 return 0;
3065 } else if (iMessage == WM_LBUTTONDOWN) {
3066 // This does not fire due to the hit test code
3067 sciThis->ct.MouseClick(Point::FromLong(static_cast<long>(lParam)));
3068 sciThis->CallTipClick();
3069 return 0;
3070 } else if (iMessage == WM_SETCURSOR) {
3071 ::SetCursor(::LoadCursor(NULL, IDC_ARROW));
3072 return 0;
3073 } else if (iMessage == WM_NCHITTEST) {
3074 return HTCAPTION;
3075 } else {
3076 return ::DefWindowProc(hWnd, iMessage, wParam, lParam);
3079 } catch (...) {
3080 sciThis->errorStatus = SC_STATUS_FAILURE;
3082 return ::DefWindowProc(hWnd, iMessage, wParam, lParam);
3085 sptr_t ScintillaWin::DirectFunction(
3086 sptr_t ptr, UINT iMessage, uptr_t wParam, sptr_t lParam) {
3087 PLATFORM_ASSERT(::GetCurrentThreadId() == ::GetWindowThreadProcessId(reinterpret_cast<ScintillaWin *>(ptr)->MainHWND(), NULL));
3088 return reinterpret_cast<ScintillaWin *>(ptr)->WndProc(iMessage, wParam, lParam);
3091 extern "C"
3092 #ifndef STATIC_BUILD
3093 __declspec(dllexport)
3094 #endif
3095 sptr_t __stdcall Scintilla_DirectFunction(
3096 ScintillaWin *sci, UINT iMessage, uptr_t wParam, sptr_t lParam) {
3097 return sci->WndProc(iMessage, wParam, lParam);
3100 sptr_t PASCAL ScintillaWin::SWndProc(
3101 HWND hWnd, UINT iMessage, WPARAM wParam, sptr_t lParam) {
3102 //Platform::DebugPrintf("S W:%x M:%x WP:%x L:%x\n", hWnd, iMessage, wParam, lParam);
3104 // Find C++ object associated with window.
3105 ScintillaWin *sci = reinterpret_cast<ScintillaWin *>(PointerFromWindow(hWnd));
3106 // sci will be zero if WM_CREATE not seen yet
3107 if (sci == 0) {
3108 try {
3109 if (iMessage == WM_CREATE) {
3110 // Create C++ object associated with window
3111 sci = new ScintillaWin(hWnd);
3112 SetWindowPointer(hWnd, sci);
3113 return sci->WndProc(iMessage, wParam, lParam);
3115 } catch (...) {
3117 return ::DefWindowProc(hWnd, iMessage, wParam, lParam);
3118 } else {
3119 if (iMessage == WM_NCDESTROY) {
3120 try {
3121 sci->Finalise();
3122 delete sci;
3123 } catch (...) {
3125 ::SetWindowLong(hWnd, 0, 0);
3126 return ::DefWindowProc(hWnd, iMessage, wParam, lParam);
3127 } else {
3128 return sci->WndProc(iMessage, wParam, lParam);
3133 // This function is externally visible so it can be called from container when building statically.
3134 // Must be called once only.
3135 int Scintilla_RegisterClasses(void *hInstance) {
3136 Platform_Initialise(hInstance);
3137 bool result = ScintillaWin::Register(reinterpret_cast<HINSTANCE>(hInstance));
3138 #ifdef SCI_LEXER
3139 Scintilla_LinkLexers();
3140 #endif
3141 return result;
3144 static int ResourcesRelease(bool fromDllMain) {
3145 bool result = ScintillaWin::Unregister();
3146 if (commctrl32) {
3147 FreeLibrary(commctrl32);
3148 commctrl32 = NULL;
3150 Platform_Finalise(fromDllMain);
3151 return result;
3154 // This function is externally visible so it can be called from container when building statically.
3155 int Scintilla_ReleaseResources() {
3156 return ResourcesRelease(false);
3159 #ifndef STATIC_BUILD
3160 extern "C" int APIENTRY DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpvReserved) {
3161 //Platform::DebugPrintf("Scintilla::DllMain %d %d\n", hInstance, dwReason);
3162 if (dwReason == DLL_PROCESS_ATTACH) {
3163 if (!Scintilla_RegisterClasses(hInstance))
3164 return FALSE;
3165 } else if (dwReason == DLL_PROCESS_DETACH) {
3166 if (lpvReserved == NULL) {
3167 ResourcesRelease(true);
3170 return TRUE;
3172 #endif