1 // Scintilla source code edit control
2 /** @file ScintillaWin.cxx
3 ** Windows specific subclass of ScintillaBase.
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.
23 #define _WIN32_WINNT 0x0500
33 #if defined(NTDDI_WIN7) && !defined(DISABLE_D2D)
45 #include "Scintilla.h"
50 #include "StringCopy.h"
52 #include "LexerModule.h"
54 #include "SplitVector.h"
55 #include "Partitioning.h"
56 #include "RunStyles.h"
57 #include "ContractionState.h"
58 #include "CellBuffer.h"
61 #include "Indicator.h"
63 #include "LineMarker.h"
65 #include "ViewStyle.h"
66 #include "CharClassify.h"
67 #include "Decoration.h"
68 #include "CaseFolder.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"
79 #include "AutoComplete.h"
80 #include "ScintillaBase.h"
83 #include "ExternalLexer.h"
88 #ifndef SPI_GETWHEELSCROLLLINES
89 #define SPI_GETWHEELSCROLLLINES 104
93 #define WM_UNICHAR 0x0109
96 #ifndef UNICODE_NOCHAR
97 #define UNICODE_NOCHAR 0xFFFF
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");
116 using namespace Scintilla
;
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
{
148 std::vector
<CLIPFORMAT
> formats
;
149 FormatEnumerator(int pos_
, CLIPFORMAT formats_
[], size_t formatsLen_
);
182 public ScintillaBase
{
184 bool lastKeyDownConsumed
;
187 bool trackedMouseLeave
;
188 TrackMouseEventSig TrackMouseEventFn
;
189 SetCoalescableTimerSig SetCoalescableTimerFn
;
191 unsigned int linesPerScroll
; ///< Intellimouse support
192 int wheelDelta
; ///< Wheel delta from roll
198 CLIPFORMAT cfColumnSelect
;
199 CLIPFORMAT cfBorlandIDEBlockType
;
200 CLIPFORMAT cfLineSelect
;
201 CLIPFORMAT cfVSLineTag
;
208 static HINSTANCE hInstance
;
209 static ATOM scintillaClassAtom
;
210 static ATOM callClassAtom
;
213 ID2D1RenderTarget
*pRenderTarget
;
214 bool renderTargetValid
;
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();
225 void EnsureRenderTarget(HDC hdc
);
226 void DropRenderTarget();
230 static sptr_t
DirectFunction(
231 sptr_t ptr
, UINT iMessage
, uptr_t wParam
, sptr_t lParam
);
232 static sptr_t PASCAL
SWndProc(
233 HWND hWnd
, UINT iMessage
, WPARAM wParam
, sptr_t lParam
);
234 static sptr_t PASCAL
CTWndProc(
235 HWND hWnd
, UINT iMessage
, WPARAM wParam
, sptr_t lParam
);
237 enum { invalidTimerID
, standardTimerID
, idleTimerID
, fineTimerStart
};
239 virtual bool DragThreshold(Point ptStart
, Point ptNow
);
240 virtual void StartDrag();
241 sptr_t
WndPaint(uptr_t wParam
);
242 sptr_t
HandleComposition(uptr_t wParam
, sptr_t lParam
);
243 static bool KoreanIME();
244 sptr_t
HandleCompositionKoreanIME(uptr_t wParam
, sptr_t lParam
);
245 UINT
CodePageOfDocument();
246 virtual bool ValidCodePage(int codePage
) const;
247 virtual sptr_t
DefWndProc(unsigned int iMessage
, uptr_t wParam
, sptr_t lParam
);
248 virtual bool SetIdle(bool on
);
249 UINT_PTR timers
[tickDwell
+1];
250 virtual bool FineTickerAvailable();
251 virtual bool FineTickerRunning(TickReason reason
);
252 virtual void FineTickerStart(TickReason reason
, int millis
, int tolerance
);
253 virtual void FineTickerCancel(TickReason reason
);
254 virtual void SetMouseCapture(bool on
);
255 virtual bool HaveMouseCapture();
256 virtual void SetTrackMouseLeaveEvent(bool on
);
257 virtual bool PaintContains(PRectangle rc
);
258 virtual void ScrollText(int linesToMove
);
259 virtual void UpdateSystemCaret();
260 virtual void SetVerticalScrollPos();
261 virtual void SetHorizontalScrollPos();
262 virtual bool ModifyScrollBars(int nMax
, int nPage
);
263 virtual void NotifyChange();
264 virtual void NotifyFocus(bool focus
);
265 virtual void SetCtrlID(int identifier
);
266 virtual int GetCtrlID();
267 virtual void NotifyParent(SCNotification scn
);
268 virtual void NotifyDoubleClick(Point pt
, int modifiers
);
269 virtual CaseFolder
*CaseFolderForEncoding();
270 virtual std::string
CaseMapString(const std::string
&s
, int caseMapping
);
272 virtual void CopyAllowLine();
273 virtual bool CanPaste();
274 virtual void Paste();
275 virtual void CreateCallTipWindow(PRectangle rc
);
276 virtual void AddToPopUp(const char *label
, int cmd
= 0, bool enabled
= true);
277 virtual void ClaimSelection();
280 void ImeStartComposition();
281 void ImeEndComposition();
283 void AddCharBytes(char b0
, char b1
);
285 void GetIntelliMouseParameters();
286 virtual void CopyToClipboard(const SelectionText
&selectedText
);
287 void ScrollMessage(WPARAM wParam
);
288 void HorizontalScrollMessage(WPARAM wParam
);
290 void FullPaintDC(HDC dc
);
291 bool IsCompatibleDC(HDC dc
);
292 DWORD
EffectFromState(DWORD grfKeyState
) const;
294 virtual int SetScrollInfo(int nBar
, LPCSCROLLINFO lpsi
, BOOL bRedraw
);
295 virtual bool GetScrollInfo(int nBar
, LPSCROLLINFO lpsi
);
296 void ChangeScrollPos(int barType
, int pos
);
297 sptr_t
GetTextLength();
298 sptr_t
GetText(uptr_t wParam
, sptr_t lParam
);
301 // Public for benefit of Scintilla_DirectFunction
302 virtual sptr_t
WndProc(unsigned int iMessage
, uptr_t wParam
, sptr_t lParam
);
304 /// Implement IUnknown
305 STDMETHODIMP
QueryInterface(REFIID riid
, PVOID
*ppv
);
306 STDMETHODIMP_(ULONG
)AddRef();
307 STDMETHODIMP_(ULONG
)Release();
309 /// Implement IDropTarget
310 STDMETHODIMP
DragEnter(LPDATAOBJECT pIDataSource
, DWORD grfKeyState
,
311 POINTL pt
, PDWORD pdwEffect
);
312 STDMETHODIMP
DragOver(DWORD grfKeyState
, POINTL pt
, PDWORD pdwEffect
);
313 STDMETHODIMP
DragLeave();
314 STDMETHODIMP
Drop(LPDATAOBJECT pIDataSource
, DWORD grfKeyState
,
315 POINTL pt
, PDWORD pdwEffect
);
317 /// Implement important part of IDataObject
318 STDMETHODIMP
GetData(FORMATETC
*pFEIn
, STGMEDIUM
*pSTM
);
320 static bool Register(HINSTANCE hInstance_
);
321 static bool Unregister();
323 friend class DropSource
;
324 friend class DataObject
;
325 friend class DropTarget
;
326 bool DragIsRectangularOK(CLIPFORMAT fmt
) const {
327 return drag
.rectangular
&& (fmt
== cfColumnSelect
);
331 // For use in creating a system caret
332 bool HasCaretSizeChanged() const;
333 BOOL
CreateSystemCaret();
334 BOOL
DestroySystemCaret();
335 HBITMAP sysCaretBitmap
;
338 bool keysAlwaysUnicode
;
341 HINSTANCE
ScintillaWin::hInstance
= 0;
342 ATOM
ScintillaWin::scintillaClassAtom
= 0;
343 ATOM
ScintillaWin::callClassAtom
= 0;
345 ScintillaWin::ScintillaWin(HWND hwnd
) {
347 lastKeyDownConsumed
= false;
349 capturedMouse
= false;
350 trackedMouseLeave
= false;
351 TrackMouseEventFn
= 0;
352 SetCoalescableTimerFn
= 0;
355 wheelDelta
= 0; // Wheel delta from roll
361 // There does not seem to be a real standard for indicating that the clipboard
362 // contains a rectangular selection, so copy Developer Studio and Borland Delphi.
363 cfColumnSelect
= static_cast<CLIPFORMAT
>(
364 ::RegisterClipboardFormat(TEXT("MSDEVColumnSelect")));
365 cfBorlandIDEBlockType
= static_cast<CLIPFORMAT
>(
366 ::RegisterClipboardFormat(TEXT("Borland IDE Block Type")));
368 // Likewise for line-copy (copies a full line when no text is selected)
369 cfLineSelect
= static_cast<CLIPFORMAT
>(
370 ::RegisterClipboardFormat(TEXT("MSDEVLineSelect")));
371 cfVSLineTag
= static_cast<CLIPFORMAT
>(
372 ::RegisterClipboardFormat(TEXT("VisualStudioEditorOperationsLineCutCopyClipboardTag")));
387 renderTargetValid
= true;
390 keysAlwaysUnicode
= false;
392 caret
.period
= ::GetCaretBlinkTime();
393 if (caret
.period
< 0)
399 ScintillaWin::~ScintillaWin() {}
401 void ScintillaWin::Initialise() {
402 // Initialize COM. If the app has already done this it will have
403 // no effect. If the app hasnt, we really shouldnt ask them to call
404 // it just so this internal feature works.
405 hrOle
= ::OleInitialize(NULL
);
407 // Find TrackMouseEvent which is available on Windows > 95
408 HMODULE user32
= ::GetModuleHandle(TEXT("user32.dll"));
410 TrackMouseEventFn
= (TrackMouseEventSig
)::GetProcAddress(user32
, "TrackMouseEvent");
411 SetCoalescableTimerFn
= (SetCoalescableTimerSig
)::GetProcAddress(user32
, "SetCoalescableTimer");
413 if (TrackMouseEventFn
== NULL
) {
414 // Windows 95 has an emulation in comctl32.dll:_TrackMouseEvent
416 commctrl32
= ::LoadLibrary(TEXT("comctl32.dll"));
417 if (commctrl32
!= NULL
) {
418 TrackMouseEventFn
= (TrackMouseEventSig
)
419 ::GetProcAddress(commctrl32
, "_TrackMouseEvent");
422 for (TickReason tr
= tickCaret
; tr
<= tickDwell
; tr
= static_cast<TickReason
>(tr
+ 1)) {
427 void ScintillaWin::Finalise() {
428 ScintillaBase::Finalise();
429 for (TickReason tr
= tickCaret
; tr
<= tickDwell
; tr
= static_cast<TickReason
>(tr
+ 1)) {
430 FineTickerCancel(tr
);
436 ::RevokeDragDrop(MainHWND());
437 if (SUCCEEDED(hrOle
)) {
444 void ScintillaWin::EnsureRenderTarget(HDC hdc
) {
445 if (!renderTargetValid
) {
447 renderTargetValid
= true;
449 if (pD2DFactory
&& !pRenderTarget
) {
451 HWND hw
= MainHWND();
452 GetClientRect(hw
, &rc
);
454 D2D1_SIZE_U size
= D2D1::SizeU(rc
.right
- rc
.left
, rc
.bottom
- rc
.top
);
456 // Create a Direct2D render target.
458 D2D1_RENDER_TARGET_PROPERTIES drtp
;
459 drtp
.type
= D2D1_RENDER_TARGET_TYPE_DEFAULT
;
460 drtp
.pixelFormat
.format
= DXGI_FORMAT_UNKNOWN
;
461 drtp
.pixelFormat
.alphaMode
= D2D1_ALPHA_MODE_UNKNOWN
;
464 drtp
.usage
= D2D1_RENDER_TARGET_USAGE_NONE
;
465 drtp
.minLevel
= D2D1_FEATURE_LEVEL_DEFAULT
;
467 if (technology
== SC_TECHNOLOGY_DIRECTWRITEDC
) {
468 // Explicit pixel format needed.
469 drtp
.pixelFormat
= D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM
,
470 D2D1_ALPHA_MODE_IGNORE
);
472 ID2D1DCRenderTarget
*pDCRT
= NULL
;
473 HRESULT hr
= pD2DFactory
->CreateDCRenderTarget(&drtp
, &pDCRT
);
475 Platform::DebugPrintf("Failed CreateDCRenderTarget 0x%x\n", hr
);
477 pRenderTarget
= pDCRT
;
480 D2D1_HWND_RENDER_TARGET_PROPERTIES dhrtp
;
482 dhrtp
.pixelSize
= size
;
483 dhrtp
.presentOptions
= (technology
== SC_TECHNOLOGY_DIRECTWRITERETAIN
) ?
484 D2D1_PRESENT_OPTIONS_RETAIN_CONTENTS
: D2D1_PRESENT_OPTIONS_NONE
;
486 ID2D1HwndRenderTarget
*pHwndRenderTarget
= NULL
;
487 HRESULT hr
= pD2DFactory
->CreateHwndRenderTarget(drtp
, dhrtp
, &pHwndRenderTarget
);
489 Platform::DebugPrintf("Failed CreateHwndRenderTarget 0x%x\n", hr
);
491 pRenderTarget
= pHwndRenderTarget
;
494 pD2DFactory
->CreateHwndRenderTarget(
495 D2D1::RenderTargetProperties(
496 D2D1_RENDER_TARGET_TYPE_DEFAULT
,
497 D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM
, D2D1_ALPHA_MODE_PREMULTIPLIED
),
498 96.0f
, 96.0f
, D2D1_RENDER_TARGET_USAGE_NONE
, D2D1_FEATURE_LEVEL_DEFAULT
),
499 D2D1::HwndRenderTargetProperties(hw
, size
),
502 // Pixmaps were created to be compatible with previous render target so
503 // need to be recreated.
507 if (technology
== SC_TECHNOLOGY_DIRECTWRITEDC
) {
509 GetClientRect(MainHWND(), &rcWindow
);
510 HRESULT hr
= static_cast<ID2D1DCRenderTarget
*>(pRenderTarget
)->BindDC(hdc
, &rcWindow
);
512 Platform::DebugPrintf("BindDC failed 0x%x\n", hr
);
518 void ScintillaWin::DropRenderTarget() {
520 pRenderTarget
->Release();
527 HWND
ScintillaWin::MainHWND() {
528 return reinterpret_cast<HWND
>(wMain
.GetID());
531 bool ScintillaWin::DragThreshold(Point ptStart
, Point ptNow
) {
532 int xMove
= static_cast<int>(abs(ptStart
.x
- ptNow
.x
));
533 int yMove
= static_cast<int>(abs(ptStart
.y
- ptNow
.y
));
534 return (xMove
> ::GetSystemMetrics(SM_CXDRAG
)) ||
535 (yMove
> ::GetSystemMetrics(SM_CYDRAG
));
538 void ScintillaWin::StartDrag() {
539 inDragDrop
= ddDragging
;
541 dropWentOutside
= true;
542 IDataObject
*pDataObject
= reinterpret_cast<IDataObject
*>(&dob
);
543 IDropSource
*pDropSource
= reinterpret_cast<IDropSource
*>(&ds
);
544 //Platform::DebugPrintf("About to DoDragDrop %x %x\n", pDataObject, pDropSource);
545 HRESULT hr
= ::DoDragDrop(
548 DROPEFFECT_COPY
| DROPEFFECT_MOVE
, &dwEffect
);
549 //Platform::DebugPrintf("DoDragDrop = %x\n", hr);
551 if ((hr
== DRAGDROP_S_DROP
) && (dwEffect
== DROPEFFECT_MOVE
) && dropWentOutside
) {
552 // Remove dragged out text
557 SetDragPosition(SelectionPosition(invalidPosition
));
560 // Avoid warnings everywhere for old style casts by concentrating them here
561 static WORD
LoWord(uptr_t l
) {
565 static WORD
HiWord(uptr_t l
) {
569 static int InputCodePage() {
570 HKL inputLocale
= ::GetKeyboardLayout(0);
571 LANGID inputLang
= LOWORD(inputLocale
);
573 int res
= ::GetLocaleInfoA(MAKELCID(inputLang
, SORT_DEFAULT
),
574 LOCALE_IDEFAULTANSICODEPAGE
, sCodePage
, sizeof(sCodePage
));
577 return atoi(sCodePage
);
580 /** Map the key codes to their equivalent SCK_ form. */
581 static int KeyTranslate(int keyIn
) {
582 //PLATFORM_ASSERT(!keyIn);
584 case VK_DOWN
: return SCK_DOWN
;
585 case VK_UP
: return SCK_UP
;
586 case VK_LEFT
: return SCK_LEFT
;
587 case VK_RIGHT
: return SCK_RIGHT
;
588 case VK_HOME
: return SCK_HOME
;
589 case VK_END
: return SCK_END
;
590 case VK_PRIOR
: return SCK_PRIOR
;
591 case VK_NEXT
: return SCK_NEXT
;
592 case VK_DELETE
: return SCK_DELETE
;
593 case VK_INSERT
: return SCK_INSERT
;
594 case VK_ESCAPE
: return SCK_ESCAPE
;
595 case VK_BACK
: return SCK_BACK
;
596 case VK_TAB
: return SCK_TAB
;
597 case VK_RETURN
: return SCK_RETURN
;
598 case VK_ADD
: return SCK_ADD
;
599 case VK_SUBTRACT
: return SCK_SUBTRACT
;
600 case VK_DIVIDE
: return SCK_DIVIDE
;
601 case VK_LWIN
: return SCK_WIN
;
602 case VK_RWIN
: return SCK_RWIN
;
603 case VK_APPS
: return SCK_MENU
;
604 case VK_OEM_2
: return '/';
605 case VK_OEM_3
: return '`';
606 case VK_OEM_4
: return '[';
607 case VK_OEM_5
: return '\\';
608 case VK_OEM_6
: return ']';
609 default: return keyIn
;
613 static bool BoundsContains(PRectangle rcBounds
, HRGN hRgnBounds
, PRectangle rcCheck
) {
614 bool contains
= true;
615 if (!rcCheck
.Empty()) {
616 if (!rcBounds
.Contains(rcCheck
)) {
618 } else if (hRgnBounds
) {
619 // In bounding rectangle so check more accurately using region
620 HRGN hRgnCheck
= ::CreateRectRgn(static_cast<int>(rcCheck
.left
), static_cast<int>(rcCheck
.top
),
621 static_cast<int>(rcCheck
.right
), static_cast<int>(rcCheck
.bottom
));
623 HRGN hRgnDifference
= ::CreateRectRgn(0, 0, 0, 0);
624 if (hRgnDifference
) {
625 int combination
= ::CombineRgn(hRgnDifference
, hRgnCheck
, hRgnBounds
, RGN_DIFF
);
626 if (combination
!= NULLREGION
) {
629 ::DeleteRgn(hRgnDifference
);
631 ::DeleteRgn(hRgnCheck
);
638 LRESULT
ScintillaWin::WndPaint(uptr_t wParam
) {
641 // Redirect assertions to debug output and save current state
642 bool assertsPopup
= Platform::ShowAssertionPopUps(false);
643 paintState
= painting
;
647 bool IsOcxCtrl
= (wParam
!= 0); // if wParam != 0, it contains
648 // a PAINSTRUCT* from the OCX
649 // Removed since this interferes with reporting other assertions as it occurs repeatedly
650 //PLATFORM_ASSERT(hRgnUpdate == NULL);
651 hRgnUpdate
= ::CreateRectRgn(0, 0, 0, 0);
653 pps
= reinterpret_cast<PAINTSTRUCT
*>(wParam
);
655 ::GetUpdateRgn(MainHWND(), hRgnUpdate
, FALSE
);
657 ::BeginPaint(MainHWND(), pps
);
659 rcPaint
= PRectangle::FromInts(pps
->rcPaint
.left
, pps
->rcPaint
.top
, pps
->rcPaint
.right
, pps
->rcPaint
.bottom
);
660 PRectangle rcClient
= GetClientRectangle();
661 paintingAllText
= BoundsContains(rcPaint
, hRgnUpdate
, rcClient
);
662 if (technology
== SC_TECHNOLOGY_DEFAULT
) {
663 AutoSurface
surfaceWindow(pps
->hdc
, this);
665 Paint(surfaceWindow
, rcPaint
);
666 surfaceWindow
->Release();
670 EnsureRenderTarget(pps
->hdc
);
671 AutoSurface
surfaceWindow(pRenderTarget
, this);
673 pRenderTarget
->BeginDraw();
674 Paint(surfaceWindow
, rcPaint
);
675 surfaceWindow
->Release();
676 HRESULT hr
= pRenderTarget
->EndDraw();
677 if (hr
== D2DERR_RECREATE_TARGET
) {
679 paintState
= paintAbandoned
;
685 ::DeleteRgn(hRgnUpdate
);
690 ::EndPaint(MainHWND(), pps
);
691 if (paintState
== paintAbandoned
) {
692 // Painting area was insufficient to cover new styling or brace highlight positions
694 FullPaintDC(pps
->hdc
);
699 paintState
= notPainting
;
701 // Restore debug output state
702 Platform::ShowAssertionPopUps(assertsPopup
);
704 //Platform::DebugPrintf("Paint took %g\n", et.Duration());
708 sptr_t
ScintillaWin::HandleComposition(uptr_t wParam
, sptr_t lParam
) {
709 if (lParam
& GCS_RESULTSTR
) {
710 HIMC hIMC
= ::ImmGetContext(MainHWND());
712 wchar_t wcs
[maxLenInputIME
];
713 LONG bytes
= ::ImmGetCompositionStringW(hIMC
,
714 GCS_RESULTSTR
, wcs
, (maxLenInputIME
-1)*2);
715 int wides
= bytes
/ 2;
716 if (IsUnicodeMode()) {
717 char utfval
[maxLenInputIME
* 3];
718 unsigned int len
= UTF8Length(wcs
, wides
);
719 UTF8FromUTF16(wcs
, wides
, utfval
, len
);
721 AddCharUTF(utfval
, len
);
723 char dbcsval
[maxLenInputIME
* 2];
724 int size
= ::WideCharToMultiByte(InputCodePage(),
725 0, wcs
, wides
, dbcsval
, sizeof(dbcsval
) - 1, 0, 0);
726 for (int i
=0; i
<size
; i
++) {
730 // Set new position after converted
731 Point pos
= PointMainCaret();
732 COMPOSITIONFORM CompForm
;
733 CompForm
.dwStyle
= CFS_POINT
;
734 CompForm
.ptCurrentPos
.x
= static_cast<int>(pos
.x
);
735 CompForm
.ptCurrentPos
.y
= static_cast<int>(pos
.y
);
736 ::ImmSetCompositionWindow(hIMC
, &CompForm
);
737 ::ImmReleaseContext(MainHWND(), hIMC
);
741 return ::DefWindowProc(MainHWND(), WM_IME_COMPOSITION
, wParam
, lParam
);
744 bool ScintillaWin::KoreanIME() {
745 const int codePage
= InputCodePage();
746 return codePage
== 949 || codePage
== 1361;
749 sptr_t
ScintillaWin::HandleCompositionKoreanIME(uptr_t
, sptr_t lParam
) {
751 // copy & paste by johnsonj
753 // jiniya from http://www.jiniya.net/tt/494 for DBCS input with AddCharUTF()
754 // BLUEnLIVE from http://zockr.tistory.com/1118 for UNDO and inOverstrike
756 HIMC hIMC
= ::ImmGetContext(MainHWND());
761 wchar_t wcs
[maxLenInputIME
];
763 bool compstrExist
= false;
765 if (pdoc
->TentativeActive()) {
766 pdoc
->TentativeUndo();
768 // No tentative undo means start of this composition so
769 // fill in any virtual spaces.
770 bool tmpOverstrike
= inOverstrike
;
771 inOverstrike
= false; // not allow to be deleted twice.
773 inOverstrike
= tmpOverstrike
;
776 view
.imeCaretBlockOverride
= false;
777 if (lParam
& GCS_COMPSTR
) {
778 long bytes
= ::ImmGetCompositionStringW
779 (hIMC
, GCS_COMPSTR
, wcs
, maxLenInputIME
);
781 compstrExist
= (wides
!= 0);
782 } else if (lParam
& GCS_RESULTSTR
) {
783 long bytes
= ::ImmGetCompositionStringW
784 (hIMC
, GCS_RESULTSTR
, wcs
, maxLenInputIME
);
786 compstrExist
= (wides
== 0);
791 char hanval
[maxLenInputIME
];
792 unsigned int hanlen
= 0;
794 if (IsUnicodeMode()) {
795 hanlen
= UTF8Length(wcs
, wides
);
796 UTF8FromUTF16(wcs
, wides
, hanval
, hanlen
);
797 hanval
[hanlen
] = '\0';
799 hanlen
= ::WideCharToMultiByte(InputCodePage(), 0,
800 wcs
, wides
, hanval
, sizeof(hanval
) - 1, 0, 0);
801 hanval
[hanlen
] = '\0';
805 view
.imeCaretBlockOverride
= true;
807 bool tmpRecordingMacro
= recordingMacro
;
808 recordingMacro
= false;
809 pdoc
->TentativeStart();
810 AddCharUTF(hanval
, hanlen
);
811 recordingMacro
= tmpRecordingMacro
;
813 for (size_t r
= 0; r
< sel
.Count(); r
++) { // for block caret
814 int positionInsert
= sel
.Range(r
).Start().Position();
815 sel
.Range(r
).caret
.SetPosition(positionInsert
- hanlen
);
816 sel
.Range(r
).anchor
.SetPosition(positionInsert
- hanlen
);
819 AddCharUTF(hanval
, hanlen
);
823 // set the candidate window position for HANJA while composing.
824 Point pos
= PointMainCaret();
825 CANDIDATEFORM CandForm
;
826 CandForm
.dwIndex
= 0;
827 CandForm
.dwStyle
= CFS_CANDIDATEPOS
;
828 CandForm
.ptCurrentPos
.x
= static_cast<int>(pos
.x
);
829 CandForm
.ptCurrentPos
.y
= static_cast<int>(pos
.y
+ vs
.lineHeight
);
830 ::ImmSetCandidateWindow(hIMC
, &CandForm
);
832 ShowCaretAtCurrentPosition();
833 ::ImmReleaseContext(MainHWND(), hIMC
);
837 // Translate message IDs from WM_* and EM_* to SCI_* so can partly emulate Windows Edit control
838 static unsigned int SciMessageFromEM(unsigned int iMessage
) {
840 case EM_CANPASTE
: return SCI_CANPASTE
;
841 case EM_CANUNDO
: return SCI_CANUNDO
;
842 case EM_EMPTYUNDOBUFFER
: return SCI_EMPTYUNDOBUFFER
;
843 case EM_FINDTEXTEX
: return SCI_FINDTEXT
;
844 case EM_FORMATRANGE
: return SCI_FORMATRANGE
;
845 case EM_GETFIRSTVISIBLELINE
: return SCI_GETFIRSTVISIBLELINE
;
846 case EM_GETLINECOUNT
: return SCI_GETLINECOUNT
;
847 case EM_GETSELTEXT
: return SCI_GETSELTEXT
;
848 case EM_GETTEXTRANGE
: return SCI_GETTEXTRANGE
;
849 case EM_HIDESELECTION
: return SCI_HIDESELECTION
;
850 case EM_LINEINDEX
: return SCI_POSITIONFROMLINE
;
851 case EM_LINESCROLL
: return SCI_LINESCROLL
;
852 case EM_REPLACESEL
: return SCI_REPLACESEL
;
853 case EM_SCROLLCARET
: return SCI_SCROLLCARET
;
854 case EM_SETREADONLY
: return SCI_SETREADONLY
;
855 case WM_CLEAR
: return SCI_CLEAR
;
856 case WM_COPY
: return SCI_COPY
;
857 case WM_CUT
: return SCI_CUT
;
858 case WM_SETTEXT
: return SCI_SETTEXT
;
859 case WM_PASTE
: return SCI_PASTE
;
860 case WM_UNDO
: return SCI_UNDO
;
865 UINT
CodePageFromCharSet(DWORD characterSet
, UINT documentCodePage
) {
866 if (documentCodePage
== SC_CP_UTF8
) {
869 switch (characterSet
) {
870 case SC_CHARSET_ANSI
: return 1252;
871 case SC_CHARSET_DEFAULT
: return documentCodePage
;
872 case SC_CHARSET_BALTIC
: return 1257;
873 case SC_CHARSET_CHINESEBIG5
: return 950;
874 case SC_CHARSET_EASTEUROPE
: return 1250;
875 case SC_CHARSET_GB2312
: return 936;
876 case SC_CHARSET_GREEK
: return 1253;
877 case SC_CHARSET_HANGUL
: return 949;
878 case SC_CHARSET_MAC
: return 10000;
879 case SC_CHARSET_OEM
: return 437;
880 case SC_CHARSET_RUSSIAN
: return 1251;
881 case SC_CHARSET_SHIFTJIS
: return 932;
882 case SC_CHARSET_TURKISH
: return 1254;
883 case SC_CHARSET_JOHAB
: return 1361;
884 case SC_CHARSET_HEBREW
: return 1255;
885 case SC_CHARSET_ARABIC
: return 1256;
886 case SC_CHARSET_VIETNAMESE
: return 1258;
887 case SC_CHARSET_THAI
: return 874;
888 case SC_CHARSET_8859_15
: return 28605;
890 case SC_CHARSET_CYRILLIC
: return documentCodePage
;
891 case SC_CHARSET_SYMBOL
: return documentCodePage
;
893 return documentCodePage
;
896 UINT
ScintillaWin::CodePageOfDocument() {
897 return CodePageFromCharSet(vs
.styles
[STYLE_DEFAULT
].characterSet
, pdoc
->dbcsCodePage
);
900 sptr_t
ScintillaWin::GetTextLength() {
901 if (::IsWindowUnicode(MainHWND())) {
902 if (pdoc
->Length() == 0)
904 std::vector
<char> docBytes(pdoc
->Length(), '\0');
905 pdoc
->GetCharRange(&docBytes
[0], 0, pdoc
->Length());
906 if (IsUnicodeMode()) {
907 return UTF16Length(&docBytes
[0], static_cast<unsigned int>(docBytes
.size()));
909 return ::MultiByteToWideChar(CodePageOfDocument(), 0, &docBytes
[0],
910 static_cast<int>(docBytes
.size()), NULL
, 0);
913 return pdoc
->Length();
917 sptr_t
ScintillaWin::GetText(uptr_t wParam
, sptr_t lParam
) {
918 if (::IsWindowUnicode(MainHWND())) {
919 wchar_t *ptr
= reinterpret_cast<wchar_t *>(lParam
);
920 if (pdoc
->Length() == 0) {
924 std::vector
<char> docBytes(pdoc
->Length(), '\0');
925 pdoc
->GetCharRange(&docBytes
[0], 0, pdoc
->Length());
926 if (IsUnicodeMode()) {
927 unsigned int lengthUTF16
= UTF16Length(&docBytes
[0], static_cast<unsigned int>(docBytes
.size()));
932 unsigned int uLen
= UTF16FromUTF8(&docBytes
[0], static_cast<unsigned int>(docBytes
.size()),
933 ptr
, static_cast<int>(wParam
) - 1);
938 // Convert to Unicode using the current Scintilla code page
939 const UINT cpSrc
= CodePageOfDocument();
940 int lengthUTF16
= ::MultiByteToWideChar(cpSrc
, 0, &docBytes
[0],
941 static_cast<int>(docBytes
.size()), NULL
, 0);
942 if (lengthUTF16
>= static_cast<int>(wParam
))
943 lengthUTF16
= static_cast<int>(wParam
)-1;
944 ::MultiByteToWideChar(cpSrc
, 0, &docBytes
[0],
945 static_cast<int>(docBytes
.size()),
947 ptr
[lengthUTF16
] = L
'\0';
952 return pdoc
->Length() + 1;
955 char *ptr
= reinterpret_cast<char *>(lParam
);
956 unsigned int iChar
= 0;
957 for (; iChar
< wParam
- 1; iChar
++)
958 ptr
[iChar
] = pdoc
->CharAt(iChar
);
964 sptr_t
ScintillaWin::WndProc(unsigned int iMessage
, uptr_t wParam
, sptr_t lParam
) {
966 //Platform::DebugPrintf("S M:%x WP:%x L:%x\n", iMessage, wParam, lParam);
967 iMessage
= SciMessageFromEM(iMessage
);
971 ctrlID
= ::GetDlgCtrlID(reinterpret_cast<HWND
>(wMain
.GetID()));
972 // Get Intellimouse scroll line parameters
973 GetIntelliMouseParameters();
974 ::RegisterDragDrop(MainHWND(), reinterpret_cast<IDropTarget
*>(&dt
));
978 Command(LoWord(wParam
));
982 return WndPaint(wParam
);
984 case WM_PRINTCLIENT
: {
985 HDC hdc
= reinterpret_cast<HDC
>(wParam
);
986 if (!IsCompatibleDC(hdc
)) {
987 return ::DefWindowProc(MainHWND(), iMessage
, wParam
, lParam
);
994 ScrollMessage(wParam
);
998 HorizontalScrollMessage(wParam
);
1002 #if defined(USE_D2D)
1003 if (paintState
== notPainting
) {
1006 renderTargetValid
= false;
1009 //Platform::DebugPrintf("Scintilla WM_SIZE %d %d\n", LoWord(lParam), HiWord(lParam));
1015 // if autocomplete list active then send mousewheel message to it
1017 HWND hWnd
= reinterpret_cast<HWND
>(ac
.lb
->GetID());
1018 ::SendMessage(hWnd
, iMessage
, wParam
, lParam
);
1022 // Don't handle datazoom.
1023 // (A good idea for datazoom would be to "fold" or "unfold" details.
1024 // i.e. if datazoomed out only class structures are visible, when datazooming in the control
1025 // structures appear, then eventually the individual statements...)
1026 if (wParam
& MK_SHIFT
) {
1027 return ::DefWindowProc(MainHWND(), iMessage
, wParam
, lParam
);
1030 // Either SCROLL or ZOOM. We handle the wheel steppings calculation
1031 wheelDelta
-= static_cast<short>(HiWord(wParam
));
1032 if (abs(wheelDelta
) >= WHEEL_DELTA
&& linesPerScroll
> 0) {
1033 int linesToScroll
= linesPerScroll
;
1034 if (linesPerScroll
== WHEEL_PAGESCROLL
)
1035 linesToScroll
= LinesOnScreen() - 1;
1036 if (linesToScroll
== 0) {
1039 linesToScroll
*= (wheelDelta
/ WHEEL_DELTA
);
1040 if (wheelDelta
>= 0)
1041 wheelDelta
= wheelDelta
% WHEEL_DELTA
;
1043 wheelDelta
= - (-wheelDelta
% WHEEL_DELTA
);
1045 if (wParam
& MK_CONTROL
) {
1046 // Zoom! We play with the font sizes in the styles.
1047 // Number of steps/line is ignored, we just care if sizing up or down
1048 if (linesToScroll
< 0) {
1049 KeyCommand(SCI_ZOOMIN
);
1051 KeyCommand(SCI_ZOOMOUT
);
1055 ScrollTo(topLine
+ linesToScroll
);
1061 if (wParam
== idleTimerID
&& idler
.state
) {
1062 SendMessage(MainHWND(), SC_WIN_IDLE
, 0, 1);
1064 TickFor(static_cast<TickReason
>(wParam
- fineTimerStart
));
1069 // wParam=dwTickCountInitial, or 0 to initialize. lParam=bSkipUserInputTest
1071 if (lParam
|| (WAIT_TIMEOUT
== MsgWaitForMultipleObjects(0, 0, 0, 0, QS_INPUT
|QS_HOTKEY
))) {
1073 // User input was given priority above, but all events do get a turn. Other
1074 // messages, notifications, etc. will get interleaved with the idle messages.
1076 // However, some things like WM_PAINT are a lower priority, and will not fire
1077 // when there's a message posted. So, several times a second, we stop and let
1078 // the low priority events have a turn (after which the timer will fire again).
1080 DWORD dwCurrent
= GetTickCount();
1081 DWORD dwStart
= wParam
? static_cast<DWORD
>(wParam
) : dwCurrent
;
1082 const DWORD maxWorkTime
= 50;
1084 if (dwCurrent
>= dwStart
&& dwCurrent
> maxWorkTime
&& dwCurrent
- maxWorkTime
< dwStart
)
1085 PostMessage(MainHWND(), SC_WIN_IDLE
, dwStart
, 0);
1093 case WM_GETMINMAXINFO
:
1094 return ::DefWindowProc(MainHWND(), iMessage
, wParam
, lParam
);
1096 case WM_LBUTTONDOWN
: {
1097 // For IME, set the composition string as the result string.
1098 HIMC hIMC
= ::ImmGetContext(MainHWND());
1099 ::ImmNotifyIME(hIMC
, NI_COMPOSITIONSTR
, CPS_COMPLETE
, 0);
1100 ::ImmReleaseContext(MainHWND(), hIMC
);
1102 //Platform::DebugPrintf("Buttdown %d %x %x %x %x %x\n",iMessage, wParam, lParam,
1103 // Platform::IsKeyDown(VK_SHIFT),
1104 // Platform::IsKeyDown(VK_CONTROL),
1105 // Platform::IsKeyDown(VK_MENU));
1106 ::SetFocus(MainHWND());
1107 ButtonDown(Point::FromLong(static_cast<long>(lParam
)), ::GetMessageTime(),
1108 (wParam
& MK_SHIFT
) != 0,
1109 (wParam
& MK_CONTROL
) != 0,
1110 Platform::IsKeyDown(VK_MENU
));
1114 case WM_MOUSEMOVE
: {
1115 const Point pt
= Point::FromLong(static_cast<long>(lParam
));
1117 // Windows might send WM_MOUSEMOVE even though the mouse has not been moved:
1118 // http://blogs.msdn.com/b/oldnewthing/archive/2003/10/01/55108.aspx
1119 if (ptMouseLast
.x
!= pt
.x
|| ptMouseLast
.y
!= pt
.y
) {
1120 SetTrackMouseLeaveEvent(true);
1121 ButtonMoveWithModifiers(pt
,
1122 ((wParam
& MK_SHIFT
) != 0 ? SCI_SHIFT
: 0) |
1123 ((wParam
& MK_CONTROL
) != 0 ? SCI_CTRL
: 0) |
1124 (Platform::IsKeyDown(VK_MENU
) ? SCI_ALT
: 0));
1130 SetTrackMouseLeaveEvent(false);
1132 return ::DefWindowProc(MainHWND(), iMessage
, wParam
, lParam
);
1135 ButtonUp(Point::FromLong(static_cast<long>(lParam
)),
1137 (wParam
& MK_CONTROL
) != 0);
1140 case WM_RBUTTONDOWN
:
1141 ::SetFocus(MainHWND());
1142 if (!PointInSelection(Point::FromLong(static_cast<long>(lParam
)))) {
1144 SetEmptySelection(PositionFromLocation(Point::FromLong(static_cast<long>(lParam
))));
1149 if (LoWord(lParam
) == HTCLIENT
) {
1150 if (inDragDrop
== ddDragging
) {
1151 DisplayCursor(Window::cursorUp
);
1153 // Display regular (drag) cursor over selection
1155 if (0 != ::GetCursorPos(&pt
)) {
1156 ::ScreenToClient(MainHWND(), &pt
);
1157 if (PointInSelMargin(PointFromPOINT(pt
))) {
1158 DisplayCursor(GetMarginCursor(PointFromPOINT(pt
)));
1159 } else if (PointInSelection(PointFromPOINT(pt
)) && !SelectionEmpty()) {
1160 DisplayCursor(Window::cursorArrow
);
1161 } else if (PointIsHotspot(PointFromPOINT(pt
))) {
1162 DisplayCursor(Window::cursorHand
);
1164 DisplayCursor(Window::cursorText
);
1170 return ::DefWindowProc(MainHWND(), iMessage
, wParam
, lParam
);
1174 if (((wParam
>= 128) || !iscntrl(static_cast<int>(wParam
))) || !lastKeyDownConsumed
) {
1175 if (::IsWindowUnicode(MainHWND()) || keysAlwaysUnicode
) {
1176 wchar_t wcs
[2] = {static_cast<wchar_t>(wParam
), 0};
1177 if (IsUnicodeMode()) {
1178 // For a wide character version of the window:
1180 unsigned int len
= UTF8Length(wcs
, 1);
1181 UTF8FromUTF16(wcs
, 1, utfval
, len
);
1182 AddCharUTF(utfval
, len
);
1184 UINT cpDest
= CodePageOfDocument();
1185 char inBufferCP
[20];
1186 int size
= ::WideCharToMultiByte(cpDest
,
1187 0, wcs
, 1, inBufferCP
, sizeof(inBufferCP
) - 1, 0, 0);
1188 inBufferCP
[size
] = '\0';
1189 AddCharUTF(inBufferCP
, size
);
1192 if (IsUnicodeMode()) {
1193 AddCharBytes('\0', LOBYTE(wParam
));
1195 AddChar(LOBYTE(wParam
));
1202 if (wParam
== UNICODE_NOCHAR
) {
1203 return IsUnicodeMode() ? 1 : 0;
1204 } else if (lastKeyDownConsumed
) {
1207 if (IsUnicodeMode()) {
1209 wchar_t wcs
[2] = {static_cast<wchar_t>(wParam
), 0};
1210 unsigned int len
= UTF8Length(wcs
, 1);
1211 UTF8FromUTF16(wcs
, 1, utfval
, len
);
1212 AddCharUTF(utfval
, len
);
1221 //Platform::DebugPrintf("S keydown %d %x %x %x %x\n",iMessage, wParam, lParam, ::IsKeyDown(VK_SHIFT), ::IsKeyDown(VK_CONTROL));
1222 lastKeyDownConsumed
= false;
1223 int ret
= KeyDown(KeyTranslate(static_cast<int>(wParam
)),
1224 Platform::IsKeyDown(VK_SHIFT
),
1225 Platform::IsKeyDown(VK_CONTROL
),
1226 Platform::IsKeyDown(VK_MENU
),
1227 &lastKeyDownConsumed
);
1228 if (!ret
&& !lastKeyDownConsumed
) {
1229 return ::DefWindowProc(MainHWND(), iMessage
, wParam
, lParam
);
1234 case WM_IME_KEYDOWN
:
1235 return ::DefWindowProc(MainHWND(), iMessage
, wParam
, lParam
);
1238 //Platform::DebugPrintf("S keyup %d %x %x\n",iMessage, wParam, lParam);
1239 return ::DefWindowProc(MainHWND(), iMessage
, wParam
, lParam
);
1241 case WM_SETTINGCHANGE
:
1242 //Platform::DebugPrintf("Setting Changed\n");
1243 InvalidateStyleData();
1244 // Get Intellimouse scroll line parameters
1245 GetIntelliMouseParameters();
1249 return DLGC_HASSETSEL
| DLGC_WANTALLKEYS
;
1251 case WM_KILLFOCUS
: {
1252 HWND wOther
= reinterpret_cast<HWND
>(wParam
);
1253 HWND wThis
= MainHWND();
1254 HWND wCT
= reinterpret_cast<HWND
>(ct
.wCallTip
.GetID());
1256 !(::IsChild(wThis
, wOther
) || (wOther
== wCT
))) {
1257 SetFocusState(false);
1258 DestroySystemCaret();
1260 // Explicitly complete any IME composition
1261 HIMC hIMC
= ImmGetContext(MainHWND());
1263 ::ImmNotifyIME(hIMC
, NI_COMPOSITIONSTR
, CPS_COMPLETE
, 0);
1264 ::ImmReleaseContext(MainHWND(), hIMC
);
1270 SetFocusState(true);
1271 DestroySystemCaret();
1272 CreateSystemCaret();
1275 case WM_SYSCOLORCHANGE
:
1276 //Platform::DebugPrintf("Setting Changed\n");
1277 InvalidateStyleData();
1280 case WM_IME_STARTCOMPOSITION
: // dbcs
1284 ImeStartComposition();
1285 return ::DefWindowProc(MainHWND(), iMessage
, wParam
, lParam
);
1287 case WM_IME_ENDCOMPOSITION
: // dbcs
1288 ImeEndComposition();
1289 return ::DefWindowProc(MainHWND(), iMessage
, wParam
, lParam
);
1291 case WM_IME_COMPOSITION
:
1293 return HandleCompositionKoreanIME(wParam
, lParam
);
1295 return HandleComposition(wParam
, lParam
);
1299 AddCharBytes(HIBYTE(wParam
), LOBYTE(wParam
));
1303 case WM_CONTEXTMENU
:
1304 if (displayPopupMenu
) {
1305 Point pt
= Point::FromLong(static_cast<long>(lParam
));
1306 if ((pt
.x
== -1) && (pt
.y
== -1)) {
1307 // Caused by keyboard so display menu near caret
1308 pt
= PointMainCaret();
1309 POINT spt
= {static_cast<int>(pt
.x
), static_cast<int>(pt
.y
)};
1310 ::ClientToScreen(MainHWND(), &spt
);
1311 pt
= PointFromPOINT(spt
);
1316 return ::DefWindowProc(MainHWND(), iMessage
, wParam
, lParam
);
1318 case WM_INPUTLANGCHANGE
:
1319 //::SetThreadLocale(LOWORD(lParam));
1320 return ::DefWindowProc(MainHWND(), iMessage
, wParam
, lParam
);
1322 case WM_INPUTLANGCHANGEREQUEST
:
1323 return ::DefWindowProc(MainHWND(), iMessage
, wParam
, lParam
);
1326 return 1; // Avoid any background erasure as whole window painted.
1328 case WM_CAPTURECHANGED
:
1329 capturedMouse
= false;
1332 case WM_IME_SETCONTEXT
:
1335 LPARAM NoImeWin
= lParam
;
1336 NoImeWin
= NoImeWin
& (~ISC_SHOWUICOMPOSITIONWINDOW
);
1337 return ::DefWindowProc(MainHWND(), iMessage
, wParam
, NoImeWin
);
1340 return ::DefWindowProc(MainHWND(), iMessage
, wParam
, lParam
);
1342 // These are not handled in Scintilla and its faster to dispatch them here.
1343 // Also moves time out to here so profile doesn't count lots of empty message calls.
1346 case WM_MOUSEACTIVATE
:
1350 case WM_NCMOUSEMOVE
:
1351 case WM_NCLBUTTONDOWN
:
1354 case WM_WINDOWPOSCHANGING
:
1355 case WM_WINDOWPOSCHANGED
:
1356 return ::DefWindowProc(MainHWND(), iMessage
, wParam
, lParam
);
1358 case WM_GETTEXTLENGTH
:
1359 return GetTextLength();
1362 return GetText(wParam
, lParam
);
1364 case EM_LINEFROMCHAR
:
1365 if (static_cast<int>(wParam
) < 0) {
1366 wParam
= SelectionStart().Position();
1368 return pdoc
->LineFromPosition(static_cast<int>(wParam
));
1370 case EM_EXLINEFROMCHAR
:
1371 return pdoc
->LineFromPosition(static_cast<int>(lParam
));
1375 *reinterpret_cast<int *>(wParam
) = SelectionStart().Position();
1378 *reinterpret_cast<int *>(lParam
) = SelectionEnd().Position();
1380 return MAKELONG(SelectionStart().Position(), SelectionEnd().Position());
1386 Sci_CharacterRange
*pCR
= reinterpret_cast<Sci_CharacterRange
*>(lParam
);
1387 pCR
->cpMin
= SelectionStart().Position();
1388 pCR
->cpMax
= SelectionEnd().Position();
1393 int nStart
= static_cast<int>(wParam
);
1394 int nEnd
= static_cast<int>(lParam
);
1395 if (nStart
== 0 && nEnd
== -1) {
1396 nEnd
= pdoc
->Length();
1399 nStart
= nEnd
; // Remove selection
1401 if (nStart
> nEnd
) {
1402 SetSelection(nEnd
, nStart
);
1404 SetSelection(nStart
, nEnd
);
1406 EnsureCaretVisible();
1414 Sci_CharacterRange
*pCR
= reinterpret_cast<Sci_CharacterRange
*>(lParam
);
1415 sel
.selType
= Selection::selStream
;
1416 if (pCR
->cpMin
== 0 && pCR
->cpMax
== -1) {
1417 SetSelection(pCR
->cpMin
, pdoc
->Length());
1419 SetSelection(pCR
->cpMin
, pCR
->cpMax
);
1421 EnsureCaretVisible();
1422 return pdoc
->LineFromPosition(SelectionStart().Position());
1425 case SCI_GETDIRECTFUNCTION
:
1426 return reinterpret_cast<sptr_t
>(DirectFunction
);
1428 case SCI_GETDIRECTPOINTER
:
1429 return reinterpret_cast<sptr_t
>(this);
1432 ::SetFocus(MainHWND());
1435 case SCI_SETKEYSUNICODE
:
1436 keysAlwaysUnicode
= wParam
!= 0;
1439 case SCI_GETKEYSUNICODE
:
1440 return keysAlwaysUnicode
;
1442 case SCI_SETTECHNOLOGY
:
1443 if ((wParam
== SC_TECHNOLOGY_DEFAULT
) ||
1444 (wParam
== SC_TECHNOLOGY_DIRECTWRITERETAIN
) ||
1445 (wParam
== SC_TECHNOLOGY_DIRECTWRITEDC
) ||
1446 (wParam
== SC_TECHNOLOGY_DIRECTWRITE
)) {
1447 if (technology
!= static_cast<int>(wParam
)) {
1448 if (static_cast<int>(wParam
) > SC_TECHNOLOGY_DEFAULT
) {
1449 #if defined(USE_D2D)
1451 // Failed to load Direct2D or DirectWrite so no effect
1457 #if defined(USE_D2D)
1460 technology
= static_cast<int>(wParam
);
1461 // Invalidate all cached information including layout.
1463 InvalidateStyleRedraw();
1469 case SCI_LOADLEXERLIBRARY
:
1470 LexerManager::GetInstance()->Load(reinterpret_cast<const char *>(lParam
));
1475 return ScintillaBase::WndProc(iMessage
, wParam
, lParam
);
1477 } catch (std::bad_alloc
&) {
1478 errorStatus
= SC_STATUS_BADALLOC
;
1480 errorStatus
= SC_STATUS_FAILURE
;
1485 bool ScintillaWin::ValidCodePage(int codePage
) const {
1486 return codePage
== 0 || codePage
== SC_CP_UTF8
||
1487 codePage
== 932 || codePage
== 936 || codePage
== 949 ||
1488 codePage
== 950 || codePage
== 1361;
1491 sptr_t
ScintillaWin::DefWndProc(unsigned int iMessage
, uptr_t wParam
, sptr_t lParam
) {
1492 return ::DefWindowProc(MainHWND(), iMessage
, wParam
, lParam
);
1496 * Report that this Editor subclass has a working implementation of FineTickerStart.
1498 bool ScintillaWin::FineTickerAvailable() {
1502 bool ScintillaWin::FineTickerRunning(TickReason reason
) {
1503 return timers
[reason
] != 0;
1506 void ScintillaWin::FineTickerStart(TickReason reason
, int millis
, int tolerance
) {
1507 FineTickerCancel(reason
);
1508 if (SetCoalescableTimerFn
&& tolerance
) {
1509 timers
[reason
] = SetCoalescableTimerFn(MainHWND(), fineTimerStart
+ reason
, millis
, NULL
, tolerance
);
1511 timers
[reason
] = ::SetTimer(MainHWND(), fineTimerStart
+ reason
, millis
, NULL
);
1515 void ScintillaWin::FineTickerCancel(TickReason reason
) {
1516 if (timers
[reason
]) {
1517 ::KillTimer(MainHWND(), timers
[reason
]);
1523 bool ScintillaWin::SetIdle(bool on
) {
1524 // On Win32 the Idler is implemented as a Timer on the Scintilla window. This
1525 // takes advantage of the fact that WM_TIMER messages are very low priority,
1526 // and are only posted when the message queue is empty, i.e. during idle time.
1527 if (idler
.state
!= on
) {
1529 idler
.idlerID
= ::SetTimer(MainHWND(), idleTimerID
, 10, NULL
)
1530 ? reinterpret_cast<IdlerID
>(idleTimerID
) : 0;
1532 ::KillTimer(MainHWND(), reinterpret_cast<uptr_t
>(idler
.idlerID
));
1535 idler
.state
= idler
.idlerID
!= 0;
1540 void ScintillaWin::SetMouseCapture(bool on
) {
1541 if (mouseDownCaptures
) {
1543 ::SetCapture(MainHWND());
1551 bool ScintillaWin::HaveMouseCapture() {
1552 // Cannot just see if GetCapture is this window as the scroll bar also sets capture for the window
1553 return capturedMouse
;
1554 //return capturedMouse && (::GetCapture() == MainHWND());
1557 void ScintillaWin::SetTrackMouseLeaveEvent(bool on
) {
1558 if (on
&& TrackMouseEventFn
&& !trackedMouseLeave
) {
1559 TRACKMOUSEEVENT tme
;
1560 tme
.cbSize
= sizeof(tme
);
1561 tme
.dwFlags
= TME_LEAVE
;
1562 tme
.hwndTrack
= MainHWND();
1563 tme
.dwHoverTime
= HOVER_DEFAULT
; // Unused but triggers Dr. Memory if not initialized
1564 TrackMouseEventFn(&tme
);
1566 trackedMouseLeave
= on
;
1569 bool ScintillaWin::PaintContains(PRectangle rc
) {
1570 if (paintState
== painting
) {
1571 return BoundsContains(rcPaint
, hRgnUpdate
, rc
);
1576 void ScintillaWin::ScrollText(int /* linesToMove */) {
1577 //Platform::DebugPrintf("ScintillaWin::ScrollText %d\n", linesToMove);
1578 //::ScrollWindow(MainHWND(), 0,
1579 // vs.lineHeight * linesToMove, 0, 0);
1580 //::UpdateWindow(MainHWND());
1582 UpdateSystemCaret();
1585 void ScintillaWin::UpdateSystemCaret() {
1587 if (HasCaretSizeChanged()) {
1588 DestroySystemCaret();
1589 CreateSystemCaret();
1591 Point pos
= PointMainCaret();
1592 ::SetCaretPos(static_cast<int>(pos
.x
), static_cast<int>(pos
.y
));
1596 int ScintillaWin::SetScrollInfo(int nBar
, LPCSCROLLINFO lpsi
, BOOL bRedraw
) {
1597 return ::SetScrollInfo(MainHWND(), nBar
, lpsi
, bRedraw
);
1600 bool ScintillaWin::GetScrollInfo(int nBar
, LPSCROLLINFO lpsi
) {
1601 return ::GetScrollInfo(MainHWND(), nBar
, lpsi
) ? true : false;
1604 // Change the scroll position but avoid repaint if changing to same value
1605 void ScintillaWin::ChangeScrollPos(int barType
, int pos
) {
1607 sizeof(sci
), 0, 0, 0, 0, 0, 0
1609 sci
.fMask
= SIF_POS
;
1610 GetScrollInfo(barType
, &sci
);
1611 if (sci
.nPos
!= pos
) {
1614 SetScrollInfo(barType
, &sci
, TRUE
);
1618 void ScintillaWin::SetVerticalScrollPos() {
1619 ChangeScrollPos(SB_VERT
, topLine
);
1622 void ScintillaWin::SetHorizontalScrollPos() {
1623 ChangeScrollPos(SB_HORZ
, xOffset
);
1626 bool ScintillaWin::ModifyScrollBars(int nMax
, int nPage
) {
1627 bool modified
= false;
1629 sizeof(sci
), 0, 0, 0, 0, 0, 0
1631 sci
.fMask
= SIF_PAGE
| SIF_RANGE
;
1632 GetScrollInfo(SB_VERT
, &sci
);
1633 int vertEndPreferred
= nMax
;
1634 if (!verticalScrollBarVisible
)
1635 nPage
= vertEndPreferred
+ 1;
1636 if ((sci
.nMin
!= 0) ||
1637 (sci
.nMax
!= vertEndPreferred
) ||
1638 (sci
.nPage
!= static_cast<unsigned int>(nPage
)) ||
1640 sci
.fMask
= SIF_PAGE
| SIF_RANGE
;
1642 sci
.nMax
= vertEndPreferred
;
1646 SetScrollInfo(SB_VERT
, &sci
, TRUE
);
1650 PRectangle rcText
= GetTextRectangle();
1651 int horizEndPreferred
= scrollWidth
;
1652 if (horizEndPreferred
< 0)
1653 horizEndPreferred
= 0;
1654 unsigned int pageWidth
= static_cast<unsigned int>(rcText
.Width());
1655 if (!horizontalScrollBarVisible
|| Wrapping())
1656 pageWidth
= horizEndPreferred
+ 1;
1657 sci
.fMask
= SIF_PAGE
| SIF_RANGE
;
1658 GetScrollInfo(SB_HORZ
, &sci
);
1659 if ((sci
.nMin
!= 0) ||
1660 (sci
.nMax
!= horizEndPreferred
) ||
1661 (sci
.nPage
!= pageWidth
) ||
1663 sci
.fMask
= SIF_PAGE
| SIF_RANGE
;
1665 sci
.nMax
= horizEndPreferred
;
1666 sci
.nPage
= pageWidth
;
1669 SetScrollInfo(SB_HORZ
, &sci
, TRUE
);
1671 if (scrollWidth
< static_cast<int>(pageWidth
)) {
1672 HorizontalScrollTo(0);
1678 void ScintillaWin::NotifyChange() {
1679 ::SendMessage(::GetParent(MainHWND()), WM_COMMAND
,
1680 MAKELONG(GetCtrlID(), SCEN_CHANGE
),
1681 reinterpret_cast<LPARAM
>(MainHWND()));
1684 void ScintillaWin::NotifyFocus(bool focus
) {
1685 ::SendMessage(::GetParent(MainHWND()), WM_COMMAND
,
1686 MAKELONG(GetCtrlID(), focus
? SCEN_SETFOCUS
: SCEN_KILLFOCUS
),
1687 reinterpret_cast<LPARAM
>(MainHWND()));
1688 Editor::NotifyFocus(focus
);
1691 void ScintillaWin::SetCtrlID(int identifier
) {
1692 ::SetWindowID(reinterpret_cast<HWND
>(wMain
.GetID()), identifier
);
1695 int ScintillaWin::GetCtrlID() {
1696 return ::GetDlgCtrlID(reinterpret_cast<HWND
>(wMain
.GetID()));
1699 void ScintillaWin::NotifyParent(SCNotification scn
) {
1700 scn
.nmhdr
.hwndFrom
= MainHWND();
1701 scn
.nmhdr
.idFrom
= GetCtrlID();
1702 ::SendMessage(::GetParent(MainHWND()), WM_NOTIFY
,
1703 GetCtrlID(), reinterpret_cast<LPARAM
>(&scn
));
1706 void ScintillaWin::NotifyDoubleClick(Point pt
, int modifiers
) {
1707 //Platform::DebugPrintf("ScintillaWin Double click 0\n");
1708 ScintillaBase::NotifyDoubleClick(pt
, modifiers
);
1709 // Send myself a WM_LBUTTONDBLCLK, so the container can handle it too.
1710 ::SendMessage(MainHWND(),
1712 (modifiers
& SCI_SHIFT
) ? MK_SHIFT
: 0,
1713 MAKELPARAM(pt
.x
, pt
.y
));
1716 class CaseFolderDBCS
: public CaseFolderTable
{
1717 // Allocate the expandable storage here so that it does not need to be reallocated
1718 // for each call to Fold.
1719 std::vector
<wchar_t> utf16Mixed
;
1720 std::vector
<wchar_t> utf16Folded
;
1723 explicit CaseFolderDBCS(UINT cp_
) : cp(cp_
) {
1726 virtual size_t Fold(char *folded
, size_t sizeFolded
, const char *mixed
, size_t lenMixed
) {
1727 if ((lenMixed
== 1) && (sizeFolded
> 0)) {
1728 folded
[0] = mapping
[static_cast<unsigned char>(mixed
[0])];
1731 if (lenMixed
> utf16Mixed
.size()) {
1732 utf16Mixed
.resize(lenMixed
+ 8);
1734 size_t nUtf16Mixed
= ::MultiByteToWideChar(cp
, 0, mixed
,
1735 static_cast<int>(lenMixed
),
1737 static_cast<int>(utf16Mixed
.size()));
1739 if (nUtf16Mixed
== 0) {
1740 // Failed to convert -> bad input
1745 unsigned int lenFlat
= 0;
1746 for (size_t mixIndex
=0; mixIndex
< nUtf16Mixed
; mixIndex
++) {
1747 if ((lenFlat
+ 20) > utf16Folded
.size())
1748 utf16Folded
.resize(lenFlat
+ 60);
1749 const char *foldedUTF8
= CaseConvert(utf16Mixed
[mixIndex
], CaseConversionFold
);
1751 // Maximum length of a case conversion is 6 bytes, 3 characters
1752 wchar_t wFolded
[20];
1753 unsigned int charsConverted
= UTF16FromUTF8(foldedUTF8
,
1754 static_cast<unsigned int>(strlen(foldedUTF8
)),
1755 wFolded
, ELEMENTS(wFolded
));
1756 for (size_t j
=0; j
<charsConverted
; j
++)
1757 utf16Folded
[lenFlat
++] = wFolded
[j
];
1759 utf16Folded
[lenFlat
++] = utf16Mixed
[mixIndex
];
1763 size_t lenOut
= ::WideCharToMultiByte(cp
, 0,
1764 &utf16Folded
[0], lenFlat
,
1767 if (lenOut
< sizeFolded
) {
1768 ::WideCharToMultiByte(cp
, 0,
1769 &utf16Folded
[0], lenFlat
,
1770 folded
, static_cast<int>(lenOut
), NULL
, 0);
1779 CaseFolder
*ScintillaWin::CaseFolderForEncoding() {
1780 UINT cpDest
= CodePageOfDocument();
1781 if (cpDest
== SC_CP_UTF8
) {
1782 return new CaseFolderUnicode();
1784 if (pdoc
->dbcsCodePage
== 0) {
1785 CaseFolderTable
*pcf
= new CaseFolderTable();
1786 pcf
->StandardASCII();
1787 // Only for single byte encodings
1788 UINT cpDoc
= CodePageOfDocument();
1789 for (int i
=0x80; i
<0x100; i
++) {
1790 char sCharacter
[2] = "A";
1791 sCharacter
[0] = static_cast<char>(i
);
1792 wchar_t wCharacter
[20];
1793 unsigned int lengthUTF16
= ::MultiByteToWideChar(cpDoc
, 0, sCharacter
, 1,
1794 wCharacter
, ELEMENTS(wCharacter
));
1795 if (lengthUTF16
== 1) {
1796 const char *caseFolded
= CaseConvert(wCharacter
[0], CaseConversionFold
);
1799 unsigned int charsConverted
= UTF16FromUTF8(caseFolded
,
1800 static_cast<unsigned int>(strlen(caseFolded
)),
1801 wLower
, ELEMENTS(wLower
));
1802 if (charsConverted
== 1) {
1803 char sCharacterLowered
[20];
1804 unsigned int lengthConverted
= ::WideCharToMultiByte(cpDoc
, 0,
1805 wLower
, charsConverted
,
1806 sCharacterLowered
, ELEMENTS(sCharacterLowered
), NULL
, 0);
1807 if ((lengthConverted
== 1) && (sCharacter
[0] != sCharacterLowered
[0])) {
1808 pcf
->SetTranslation(sCharacter
[0], sCharacterLowered
[0]);
1816 return new CaseFolderDBCS(cpDest
);
1821 std::string
ScintillaWin::CaseMapString(const std::string
&s
, int caseMapping
) {
1822 if ((s
.size() == 0) || (caseMapping
== cmSame
))
1825 UINT cpDoc
= CodePageOfDocument();
1826 if (cpDoc
== SC_CP_UTF8
) {
1827 std::string
retMapped(s
.length() * maxExpansionCaseConversion
, 0);
1828 size_t lenMapped
= CaseConvertString(&retMapped
[0], retMapped
.length(), s
.c_str(), s
.length(),
1829 (caseMapping
== cmUpper
) ? CaseConversionUpper
: CaseConversionLower
);
1830 retMapped
.resize(lenMapped
);
1834 unsigned int lengthUTF16
= ::MultiByteToWideChar(cpDoc
, 0, s
.c_str(),
1835 static_cast<int>(s
.size()), NULL
, 0);
1836 if (lengthUTF16
== 0) // Failed to convert
1839 DWORD mapFlags
= LCMAP_LINGUISTIC_CASING
|
1840 ((caseMapping
== cmUpper
) ? LCMAP_UPPERCASE
: LCMAP_LOWERCASE
);
1842 // Change text to UTF-16
1843 std::vector
<wchar_t> vwcText(lengthUTF16
);
1844 ::MultiByteToWideChar(cpDoc
, 0, s
.c_str(), static_cast<int>(s
.size()), &vwcText
[0], lengthUTF16
);
1847 int charsConverted
= ::LCMapStringW(LOCALE_SYSTEM_DEFAULT
, mapFlags
,
1848 &vwcText
[0], lengthUTF16
, NULL
, 0);
1849 std::vector
<wchar_t> vwcConverted(charsConverted
);
1850 ::LCMapStringW(LOCALE_SYSTEM_DEFAULT
, mapFlags
,
1851 &vwcText
[0], lengthUTF16
, &vwcConverted
[0], charsConverted
);
1853 // Change back to document encoding
1854 unsigned int lengthConverted
= ::WideCharToMultiByte(cpDoc
, 0,
1855 &vwcConverted
[0], static_cast<int>(vwcConverted
.size()),
1857 std::vector
<char> vcConverted(lengthConverted
);
1858 ::WideCharToMultiByte(cpDoc
, 0,
1859 &vwcConverted
[0], static_cast<int>(vwcConverted
.size()),
1860 &vcConverted
[0], static_cast<int>(vcConverted
.size()), NULL
, 0);
1862 return std::string(&vcConverted
[0], vcConverted
.size());
1865 void ScintillaWin::Copy() {
1866 //Platform::DebugPrintf("Copy\n");
1868 SelectionText selectedText
;
1869 CopySelectionRange(&selectedText
);
1870 CopyToClipboard(selectedText
);
1874 void ScintillaWin::CopyAllowLine() {
1875 SelectionText selectedText
;
1876 CopySelectionRange(&selectedText
, true);
1877 CopyToClipboard(selectedText
);
1880 bool ScintillaWin::CanPaste() {
1881 if (!Editor::CanPaste())
1883 if (::IsClipboardFormatAvailable(CF_TEXT
))
1885 if (IsUnicodeMode())
1886 return ::IsClipboardFormatAvailable(CF_UNICODETEXT
) != 0;
1890 class GlobalMemory
{
1894 GlobalMemory() : hand(0), ptr(0) {
1896 explicit GlobalMemory(HGLOBAL hand_
) : hand(hand_
), ptr(0) {
1898 ptr
= ::GlobalLock(hand
);
1902 PLATFORM_ASSERT(!ptr
);
1904 void Allocate(size_t bytes
) {
1905 hand
= ::GlobalAlloc(GMEM_MOVEABLE
| GMEM_ZEROINIT
, bytes
);
1907 ptr
= ::GlobalLock(hand
);
1911 PLATFORM_ASSERT(ptr
);
1912 HGLOBAL handCopy
= hand
;
1913 ::GlobalUnlock(hand
);
1918 void SetClip(UINT uFormat
) {
1919 ::SetClipboardData(uFormat
, Unlock());
1921 operator bool() const {
1925 return ::GlobalSize(hand
);
1929 void ScintillaWin::Paste() {
1930 if (!::OpenClipboard(MainHWND()))
1933 const bool isLine
= SelectionEmpty() &&
1934 (::IsClipboardFormatAvailable(cfLineSelect
) || ::IsClipboardFormatAvailable(cfVSLineTag
));
1935 ClearSelection(multiPasteMode
== SC_MULTIPASTE_EACH
);
1936 bool isRectangular
= (::IsClipboardFormatAvailable(cfColumnSelect
) != 0);
1938 if (!isRectangular
) {
1939 // Evaluate "Borland IDE Block Type" explicitly
1940 GlobalMemory
memBorlandSelection(::GetClipboardData(cfBorlandIDEBlockType
));
1941 if (memBorlandSelection
) {
1942 isRectangular
= (memBorlandSelection
.Size() == 1) && (static_cast<BYTE
*>(memBorlandSelection
.ptr
)[0] == 0x02);
1943 memBorlandSelection
.Unlock();
1946 const PasteShape pasteShape
= isRectangular
? pasteRectangular
: (isLine
? pasteLine
: pasteStream
);
1948 // Always use CF_UNICODETEXT if available
1949 GlobalMemory
memUSelection(::GetClipboardData(CF_UNICODETEXT
));
1950 if (memUSelection
) {
1951 wchar_t *uptr
= static_cast<wchar_t *>(memUSelection
.ptr
);
1954 std::vector
<char> putf
;
1955 // Default Scintilla behaviour in Unicode mode
1956 if (IsUnicodeMode()) {
1957 unsigned int bytes
= static_cast<unsigned int>(memUSelection
.Size());
1958 len
= UTF8Length(uptr
, bytes
/ 2);
1959 putf
.resize(len
+ 1);
1960 UTF8FromUTF16(uptr
, bytes
/ 2, &putf
[0], len
);
1962 // CF_UNICODETEXT available, but not in Unicode mode
1963 // Convert from Unicode to current Scintilla code page
1964 UINT cpDest
= CodePageOfDocument();
1965 len
= ::WideCharToMultiByte(cpDest
, 0, uptr
, -1,
1966 NULL
, 0, NULL
, NULL
) - 1; // subtract 0 terminator
1967 putf
.resize(len
+ 1);
1968 ::WideCharToMultiByte(cpDest
, 0, uptr
, -1,
1969 &putf
[0], len
+ 1, NULL
, NULL
);
1972 InsertPasteShape(&putf
[0], len
, pasteShape
);
1974 memUSelection
.Unlock();
1976 // CF_UNICODETEXT not available, paste ANSI text
1977 GlobalMemory
memSelection(::GetClipboardData(CF_TEXT
));
1979 char *ptr
= static_cast<char *>(memSelection
.ptr
);
1981 unsigned int bytes
= static_cast<unsigned int>(memSelection
.Size());
1982 unsigned int len
= bytes
;
1983 for (unsigned int i
= 0; i
< bytes
; i
++) {
1984 if ((len
== bytes
) && (0 == ptr
[i
]))
1988 // In Unicode mode, convert clipboard text to UTF-8
1989 if (IsUnicodeMode()) {
1990 std::vector
<wchar_t> uptr(len
+1);
1992 unsigned int ulen
= ::MultiByteToWideChar(CP_ACP
, 0,
1993 ptr
, len
, &uptr
[0], len
+1);
1995 unsigned int mlen
= UTF8Length(&uptr
[0], ulen
);
1996 std::vector
<char> putf(mlen
+1);
1997 // CP_UTF8 not available on Windows 95, so use UTF8FromUTF16()
1998 UTF8FromUTF16(&uptr
[0], ulen
, &putf
[0], mlen
);
2000 InsertPasteShape(&putf
[0], mlen
, pasteShape
);
2002 InsertPasteShape(ptr
, len
, pasteShape
);
2005 memSelection
.Unlock();
2012 void ScintillaWin::CreateCallTipWindow(PRectangle
) {
2013 if (!ct
.wCallTip
.Created()) {
2014 ct
.wCallTip
= ::CreateWindow(callClassName
, TEXT("ACallTip"),
2015 WS_POPUP
, 100, 100, 150, 20,
2017 GetWindowInstance(MainHWND()),
2019 ct
.wDraw
= ct
.wCallTip
;
2023 void ScintillaWin::AddToPopUp(const char *label
, int cmd
, bool enabled
) {
2024 HMENU hmenuPopup
= reinterpret_cast<HMENU
>(popup
.GetID());
2026 ::AppendMenuA(hmenuPopup
, MF_SEPARATOR
, 0, "");
2028 ::AppendMenuA(hmenuPopup
, MF_STRING
, cmd
, label
);
2030 ::AppendMenuA(hmenuPopup
, MF_STRING
| MF_DISABLED
| MF_GRAYED
, cmd
, label
);
2033 void ScintillaWin::ClaimSelection() {
2034 // Windows does not have a primary selection
2037 /// Implement IUnknown
2039 STDMETHODIMP_(ULONG
)FormatEnumerator_AddRef(FormatEnumerator
*fe
);
2040 STDMETHODIMP
FormatEnumerator_QueryInterface(FormatEnumerator
*fe
, REFIID riid
, PVOID
*ppv
) {
2041 //Platform::DebugPrintf("EFE QI");
2043 if (riid
== IID_IUnknown
)
2044 *ppv
= reinterpret_cast<IEnumFORMATETC
*>(fe
);
2045 if (riid
== IID_IEnumFORMATETC
)
2046 *ppv
= reinterpret_cast<IEnumFORMATETC
*>(fe
);
2048 return E_NOINTERFACE
;
2049 FormatEnumerator_AddRef(fe
);
2052 STDMETHODIMP_(ULONG
)FormatEnumerator_AddRef(FormatEnumerator
*fe
) {
2055 STDMETHODIMP_(ULONG
)FormatEnumerator_Release(FormatEnumerator
*fe
) {
2062 /// Implement IEnumFORMATETC
2063 STDMETHODIMP
FormatEnumerator_Next(FormatEnumerator
*fe
, ULONG celt
, FORMATETC
*rgelt
, ULONG
*pceltFetched
) {
2064 if (rgelt
== NULL
) return E_POINTER
;
2065 unsigned int putPos
= 0;
2066 while ((fe
->pos
< fe
->formats
.size()) && (putPos
< celt
)) {
2067 rgelt
->cfFormat
= fe
->formats
[fe
->pos
];
2069 rgelt
->dwAspect
= DVASPECT_CONTENT
;
2071 rgelt
->tymed
= TYMED_HGLOBAL
;
2077 *pceltFetched
= putPos
;
2078 return putPos
? S_OK
: S_FALSE
;
2080 STDMETHODIMP
FormatEnumerator_Skip(FormatEnumerator
*fe
, ULONG celt
) {
2084 STDMETHODIMP
FormatEnumerator_Reset(FormatEnumerator
*fe
) {
2088 STDMETHODIMP
FormatEnumerator_Clone(FormatEnumerator
*fe
, IEnumFORMATETC
**ppenum
) {
2089 FormatEnumerator
*pfe
;
2091 pfe
= new FormatEnumerator(fe
->pos
, &fe
->formats
[0], fe
->formats
.size());
2093 return E_OUTOFMEMORY
;
2095 return FormatEnumerator_QueryInterface(pfe
, IID_IEnumFORMATETC
,
2096 reinterpret_cast<void **>(ppenum
));
2099 static VFunction
*vtFormatEnumerator
[] = {
2100 (VFunction
*)(FormatEnumerator_QueryInterface
),
2101 (VFunction
*)(FormatEnumerator_AddRef
),
2102 (VFunction
*)(FormatEnumerator_Release
),
2103 (VFunction
*)(FormatEnumerator_Next
),
2104 (VFunction
*)(FormatEnumerator_Skip
),
2105 (VFunction
*)(FormatEnumerator_Reset
),
2106 (VFunction
*)(FormatEnumerator_Clone
)
2109 FormatEnumerator::FormatEnumerator(int pos_
, CLIPFORMAT formats_
[], size_t formatsLen_
) {
2110 vtbl
= vtFormatEnumerator
;
2111 ref
= 0; // First QI adds first reference...
2113 formats
.insert(formats
.begin(), formats_
, formats_
+formatsLen_
);
2116 /// Implement IUnknown
2117 STDMETHODIMP
DropSource_QueryInterface(DropSource
*ds
, REFIID riid
, PVOID
*ppv
) {
2118 return ds
->sci
->QueryInterface(riid
, ppv
);
2120 STDMETHODIMP_(ULONG
)DropSource_AddRef(DropSource
*ds
) {
2121 return ds
->sci
->AddRef();
2123 STDMETHODIMP_(ULONG
)DropSource_Release(DropSource
*ds
) {
2124 return ds
->sci
->Release();
2127 /// Implement IDropSource
2128 STDMETHODIMP
DropSource_QueryContinueDrag(DropSource
*, BOOL fEsc
, DWORD grfKeyState
) {
2130 return DRAGDROP_S_CANCEL
;
2131 if (!(grfKeyState
& MK_LBUTTON
))
2132 return DRAGDROP_S_DROP
;
2136 STDMETHODIMP
DropSource_GiveFeedback(DropSource
*, DWORD
) {
2137 return DRAGDROP_S_USEDEFAULTCURSORS
;
2140 static VFunction
*vtDropSource
[] = {
2141 (VFunction
*)(DropSource_QueryInterface
),
2142 (VFunction
*)(DropSource_AddRef
),
2143 (VFunction
*)(DropSource_Release
),
2144 (VFunction
*)(DropSource_QueryContinueDrag
),
2145 (VFunction
*)(DropSource_GiveFeedback
)
2148 DropSource::DropSource() {
2149 vtbl
= vtDropSource
;
2153 /// Implement IUnkown
2154 STDMETHODIMP
DataObject_QueryInterface(DataObject
*pd
, REFIID riid
, PVOID
*ppv
) {
2155 //Platform::DebugPrintf("DO QI %x\n", pd);
2156 return pd
->sci
->QueryInterface(riid
, ppv
);
2158 STDMETHODIMP_(ULONG
)DataObject_AddRef(DataObject
*pd
) {
2159 return pd
->sci
->AddRef();
2161 STDMETHODIMP_(ULONG
)DataObject_Release(DataObject
*pd
) {
2162 return pd
->sci
->Release();
2164 /// Implement IDataObject
2165 STDMETHODIMP
DataObject_GetData(DataObject
*pd
, FORMATETC
*pFEIn
, STGMEDIUM
*pSTM
) {
2166 return pd
->sci
->GetData(pFEIn
, pSTM
);
2169 STDMETHODIMP
DataObject_GetDataHere(DataObject
*, FORMATETC
*, STGMEDIUM
*) {
2170 //Platform::DebugPrintf("DOB GetDataHere\n");
2174 STDMETHODIMP
DataObject_QueryGetData(DataObject
*pd
, FORMATETC
*pFE
) {
2175 if (pd
->sci
->DragIsRectangularOK(pFE
->cfFormat
) &&
2177 (pFE
->dwAspect
& DVASPECT_CONTENT
) != 0 &&
2178 pFE
->lindex
== -1 &&
2179 (pFE
->tymed
& TYMED_HGLOBAL
) != 0
2184 bool formatOK
= (pFE
->cfFormat
== CF_TEXT
) ||
2185 ((pFE
->cfFormat
== CF_UNICODETEXT
) && pd
->sci
->IsUnicodeMode());
2188 (pFE
->dwAspect
& DVASPECT_CONTENT
) == 0 ||
2189 pFE
->lindex
!= -1 ||
2190 (pFE
->tymed
& TYMED_HGLOBAL
) == 0
2192 //Platform::DebugPrintf("DOB QueryGetData No %x\n",pFE->cfFormat);
2193 //return DATA_E_FORMATETC;
2196 //Platform::DebugPrintf("DOB QueryGetData OK %x\n",pFE->cfFormat);
2200 STDMETHODIMP
DataObject_GetCanonicalFormatEtc(DataObject
*pd
, FORMATETC
*, FORMATETC
*pFEOut
) {
2201 //Platform::DebugPrintf("DOB GetCanon\n");
2202 if (pd
->sci
->IsUnicodeMode())
2203 pFEOut
->cfFormat
= CF_UNICODETEXT
;
2205 pFEOut
->cfFormat
= CF_TEXT
;
2207 pFEOut
->dwAspect
= DVASPECT_CONTENT
;
2208 pFEOut
->lindex
= -1;
2209 pFEOut
->tymed
= TYMED_HGLOBAL
;
2213 STDMETHODIMP
DataObject_SetData(DataObject
*, FORMATETC
*, STGMEDIUM
*, BOOL
) {
2214 //Platform::DebugPrintf("DOB SetData\n");
2218 STDMETHODIMP
DataObject_EnumFormatEtc(DataObject
*pd
, DWORD dwDirection
, IEnumFORMATETC
**ppEnum
) {
2220 //Platform::DebugPrintf("DOB EnumFormatEtc %d\n", dwDirection);
2221 if (dwDirection
!= DATADIR_GET
) {
2225 FormatEnumerator
*pfe
;
2226 if (pd
->sci
->IsUnicodeMode()) {
2227 CLIPFORMAT formats
[] = {CF_UNICODETEXT
, CF_TEXT
};
2228 pfe
= new FormatEnumerator(0, formats
, ELEMENTS(formats
));
2230 CLIPFORMAT formats
[] = {CF_TEXT
};
2231 pfe
= new FormatEnumerator(0, formats
, ELEMENTS(formats
));
2233 return FormatEnumerator_QueryInterface(pfe
, IID_IEnumFORMATETC
,
2234 reinterpret_cast<void **>(ppEnum
));
2235 } catch (std::bad_alloc
&) {
2236 pd
->sci
->errorStatus
= SC_STATUS_BADALLOC
;
2237 return E_OUTOFMEMORY
;
2239 pd
->sci
->errorStatus
= SC_STATUS_FAILURE
;
2244 STDMETHODIMP
DataObject_DAdvise(DataObject
*, FORMATETC
*, DWORD
, IAdviseSink
*, PDWORD
) {
2245 //Platform::DebugPrintf("DOB DAdvise\n");
2249 STDMETHODIMP
DataObject_DUnadvise(DataObject
*, DWORD
) {
2250 //Platform::DebugPrintf("DOB DUnadvise\n");
2254 STDMETHODIMP
DataObject_EnumDAdvise(DataObject
*, IEnumSTATDATA
**) {
2255 //Platform::DebugPrintf("DOB EnumDAdvise\n");
2259 static VFunction
*vtDataObject
[] = {
2260 (VFunction
*)(DataObject_QueryInterface
),
2261 (VFunction
*)(DataObject_AddRef
),
2262 (VFunction
*)(DataObject_Release
),
2263 (VFunction
*)(DataObject_GetData
),
2264 (VFunction
*)(DataObject_GetDataHere
),
2265 (VFunction
*)(DataObject_QueryGetData
),
2266 (VFunction
*)(DataObject_GetCanonicalFormatEtc
),
2267 (VFunction
*)(DataObject_SetData
),
2268 (VFunction
*)(DataObject_EnumFormatEtc
),
2269 (VFunction
*)(DataObject_DAdvise
),
2270 (VFunction
*)(DataObject_DUnadvise
),
2271 (VFunction
*)(DataObject_EnumDAdvise
)
2274 DataObject::DataObject() {
2275 vtbl
= vtDataObject
;
2279 /// Implement IUnknown
2280 STDMETHODIMP
DropTarget_QueryInterface(DropTarget
*dt
, REFIID riid
, PVOID
*ppv
) {
2281 //Platform::DebugPrintf("DT QI %x\n", dt);
2282 return dt
->sci
->QueryInterface(riid
, ppv
);
2284 STDMETHODIMP_(ULONG
)DropTarget_AddRef(DropTarget
*dt
) {
2285 return dt
->sci
->AddRef();
2287 STDMETHODIMP_(ULONG
)DropTarget_Release(DropTarget
*dt
) {
2288 return dt
->sci
->Release();
2291 /// Implement IDropTarget by forwarding to Scintilla
2292 STDMETHODIMP
DropTarget_DragEnter(DropTarget
*dt
, LPDATAOBJECT pIDataSource
, DWORD grfKeyState
,
2293 POINTL pt
, PDWORD pdwEffect
) {
2295 return dt
->sci
->DragEnter(pIDataSource
, grfKeyState
, pt
, pdwEffect
);
2297 dt
->sci
->errorStatus
= SC_STATUS_FAILURE
;
2301 STDMETHODIMP
DropTarget_DragOver(DropTarget
*dt
, DWORD grfKeyState
, POINTL pt
, PDWORD pdwEffect
) {
2303 return dt
->sci
->DragOver(grfKeyState
, pt
, pdwEffect
);
2305 dt
->sci
->errorStatus
= SC_STATUS_FAILURE
;
2309 STDMETHODIMP
DropTarget_DragLeave(DropTarget
*dt
) {
2311 return dt
->sci
->DragLeave();
2313 dt
->sci
->errorStatus
= SC_STATUS_FAILURE
;
2317 STDMETHODIMP
DropTarget_Drop(DropTarget
*dt
, LPDATAOBJECT pIDataSource
, DWORD grfKeyState
,
2318 POINTL pt
, PDWORD pdwEffect
) {
2320 return dt
->sci
->Drop(pIDataSource
, grfKeyState
, pt
, pdwEffect
);
2322 dt
->sci
->errorStatus
= SC_STATUS_FAILURE
;
2327 static VFunction
*vtDropTarget
[] = {
2328 (VFunction
*)(DropTarget_QueryInterface
),
2329 (VFunction
*)(DropTarget_AddRef
),
2330 (VFunction
*)(DropTarget_Release
),
2331 (VFunction
*)(DropTarget_DragEnter
),
2332 (VFunction
*)(DropTarget_DragOver
),
2333 (VFunction
*)(DropTarget_DragLeave
),
2334 (VFunction
*)(DropTarget_Drop
)
2337 DropTarget::DropTarget() {
2338 vtbl
= vtDropTarget
;
2343 * DBCS: support Input Method Editor (IME).
2344 * Called when IME Window opened.
2346 void ScintillaWin::ImeStartComposition() {
2348 // Move IME Window to current caret position
2349 HIMC hIMC
= ::ImmGetContext(MainHWND());
2350 Point pos
= PointMainCaret();
2351 COMPOSITIONFORM CompForm
;
2352 CompForm
.dwStyle
= CFS_POINT
;
2353 CompForm
.ptCurrentPos
.x
= static_cast<int>(pos
.x
);
2354 CompForm
.ptCurrentPos
.y
= static_cast<int>(pos
.y
);
2356 ::ImmSetCompositionWindow(hIMC
, &CompForm
);
2358 // Set font of IME window to same as surrounded text.
2360 // Since the style creation code has been made platform independent,
2361 // The logfont for the IME is recreated here.
2362 int styleHere
= (pdoc
->StyleAt(sel
.MainCaret())) & 31;
2363 LOGFONTA lf
= {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ""};
2364 int sizeZoomed
= vs
.styles
[styleHere
].size
+ vs
.zoomLevel
* SC_FONT_SIZE_MULTIPLIER
;
2365 if (sizeZoomed
<= 2 * SC_FONT_SIZE_MULTIPLIER
) // Hangs if sizeZoomed <= 1
2366 sizeZoomed
= 2 * SC_FONT_SIZE_MULTIPLIER
;
2367 AutoSurface
surface(this);
2368 int deviceHeight
= sizeZoomed
;
2370 deviceHeight
= (sizeZoomed
* surface
->LogPixelsY()) / 72;
2372 // The negative is to allow for leading
2373 lf
.lfHeight
= -(abs(deviceHeight
/ SC_FONT_SIZE_MULTIPLIER
));
2374 lf
.lfWeight
= vs
.styles
[styleHere
].weight
;
2375 lf
.lfItalic
= static_cast<BYTE
>(vs
.styles
[styleHere
].italic
? 1 : 0);
2376 lf
.lfCharSet
= DEFAULT_CHARSET
;
2377 lf
.lfFaceName
[0] = '\0';
2378 if (vs
.styles
[styleHere
].fontName
)
2379 StringCopy(lf
.lfFaceName
, vs
.styles
[styleHere
].fontName
);
2381 ::ImmSetCompositionFontA(hIMC
, &lf
);
2383 ::ImmReleaseContext(MainHWND(), hIMC
);
2384 // Caret is displayed in IME window. So, caret in Scintilla is useless.
2389 /** Called when IME Window closed. */
2390 void ScintillaWin::ImeEndComposition() {
2391 ShowCaretAtCurrentPosition();
2394 void ScintillaWin::AddCharBytes(char b0
, char b1
) {
2396 int inputCodePage
= InputCodePage();
2397 if (inputCodePage
&& IsUnicodeMode()) {
2398 char utfval
[4] = "\0\0\0";
2401 if (b0
) { // Two bytes from IME
2404 ansiChars
[2] = '\0';
2405 ::MultiByteToWideChar(inputCodePage
, 0, ansiChars
, 2, wcs
, 1);
2408 ansiChars
[1] = '\0';
2409 ::MultiByteToWideChar(inputCodePage
, 0, ansiChars
, 1, wcs
, 1);
2411 unsigned int len
= UTF8Length(wcs
, 1);
2412 UTF8FromUTF16(wcs
, 1, utfval
, len
);
2414 AddCharUTF(utfval
, len
? len
: 1);
2419 dbcsChars
[2] = '\0';
2420 AddCharUTF(dbcsChars
, 2, true);
2426 void ScintillaWin::GetIntelliMouseParameters() {
2427 // This retrieves the number of lines per scroll as configured inthe Mouse Properties sheet in Control Panel
2428 ::SystemParametersInfo(SPI_GETWHEELSCROLLLINES
, 0, &linesPerScroll
, 0);
2431 void ScintillaWin::CopyToClipboard(const SelectionText
&selectedText
) {
2432 if (!::OpenClipboard(MainHWND()))
2436 GlobalMemory uniText
;
2438 // Default Scintilla behaviour in Unicode mode
2439 if (IsUnicodeMode()) {
2440 int uchars
= UTF16Length(selectedText
.Data(),
2441 static_cast<int>(selectedText
.LengthWithTerminator()));
2442 uniText
.Allocate(2 * uchars
);
2444 UTF16FromUTF8(selectedText
.Data(), static_cast<int>(selectedText
.LengthWithTerminator()),
2445 static_cast<wchar_t *>(uniText
.ptr
), uchars
);
2449 // Convert to Unicode using the current Scintilla code page
2450 UINT cpSrc
= CodePageFromCharSet(
2451 selectedText
.characterSet
, selectedText
.codePage
);
2452 int uLen
= ::MultiByteToWideChar(cpSrc
, 0, selectedText
.Data(),
2453 static_cast<int>(selectedText
.LengthWithTerminator()), 0, 0);
2454 uniText
.Allocate(2 * uLen
);
2456 ::MultiByteToWideChar(cpSrc
, 0, selectedText
.Data(),
2457 static_cast<int>(selectedText
.LengthWithTerminator()),
2458 static_cast<wchar_t *>(uniText
.ptr
), uLen
);
2464 // Copy ANSI text to clipboard on Windows 9x
2465 // Convert from Unicode text, so other ANSI programs can
2467 // Windows NT, 2k, XP automatically generates CF_TEXT
2468 GlobalMemory ansiText
;
2469 ansiText
.Allocate(selectedText
.LengthWithTerminator());
2471 ::WideCharToMultiByte(CP_ACP
, 0, static_cast<wchar_t *>(uniText
.ptr
), -1,
2472 static_cast<char *>(ansiText
.ptr
),
2473 static_cast<int>(selectedText
.LengthWithTerminator()), NULL
, NULL
);
2474 ansiText
.SetClip(CF_TEXT
);
2477 uniText
.SetClip(CF_UNICODETEXT
);
2479 // There was a failure - try to copy at least ANSI text
2480 GlobalMemory ansiText
;
2481 ansiText
.Allocate(selectedText
.LengthWithTerminator());
2483 memcpy(static_cast<char *>(ansiText
.ptr
), selectedText
.Data(), selectedText
.LengthWithTerminator());
2484 ansiText
.SetClip(CF_TEXT
);
2488 if (selectedText
.rectangular
) {
2489 ::SetClipboardData(cfColumnSelect
, 0);
2491 GlobalMemory borlandSelection
;
2492 borlandSelection
.Allocate(1);
2493 if (borlandSelection
) {
2494 static_cast<BYTE
*>(borlandSelection
.ptr
)[0] = 0x02;
2495 borlandSelection
.SetClip(cfBorlandIDEBlockType
);
2499 if (selectedText
.lineCopy
) {
2500 ::SetClipboardData(cfLineSelect
, 0);
2501 ::SetClipboardData(cfVSLineTag
, 0);
2507 void ScintillaWin::ScrollMessage(WPARAM wParam
) {
2508 //DWORD dwStart = timeGetTime();
2509 //Platform::DebugPrintf("Scroll %x %d\n", wParam, lParam);
2511 SCROLLINFO sci
= {};
2512 sci
.cbSize
= sizeof(sci
);
2513 sci
.fMask
= SIF_ALL
;
2515 GetScrollInfo(SB_VERT
, &sci
);
2517 //Platform::DebugPrintf("ScrollInfo %d mask=%x min=%d max=%d page=%d pos=%d track=%d\n", b,sci.fMask,
2518 //sci.nMin, sci.nMax, sci.nPage, sci.nPos, sci.nTrackPos);
2520 int topLineNew
= topLine
;
2521 switch (LoWord(wParam
)) {
2529 topLineNew
-= LinesToScroll(); break;
2530 case SB_PAGEDOWN
: topLineNew
+= LinesToScroll(); break;
2531 case SB_TOP
: topLineNew
= 0; break;
2532 case SB_BOTTOM
: topLineNew
= MaxScrollPos(); break;
2533 case SB_THUMBPOSITION
: topLineNew
= sci
.nTrackPos
; break;
2534 case SB_THUMBTRACK
: topLineNew
= sci
.nTrackPos
; break;
2536 ScrollTo(topLineNew
);
2539 void ScintillaWin::HorizontalScrollMessage(WPARAM wParam
) {
2541 PRectangle rcText
= GetTextRectangle();
2542 int pageWidth
= static_cast<int>(rcText
.Width() * 2 / 3);
2543 switch (LoWord(wParam
)) {
2547 case SB_LINEDOWN
: // May move past the logical end
2555 if (xPos
> scrollWidth
- rcText
.Width()) { // Hit the end exactly
2556 xPos
= scrollWidth
- static_cast<int>(rcText
.Width());
2565 case SB_THUMBPOSITION
:
2566 case SB_THUMBTRACK
: {
2567 // Do NOT use wParam, its 16 bit and not enough for very long lines. Its still possible to overflow the 32 bit but you have to try harder =]
2569 si
.cbSize
= sizeof(si
);
2570 si
.fMask
= SIF_TRACKPOS
;
2571 if (GetScrollInfo(SB_HORZ
, &si
)) {
2572 xPos
= si
.nTrackPos
;
2577 HorizontalScrollTo(xPos
);
2581 * Redraw all of text area.
2582 * This paint will not be abandoned.
2584 void ScintillaWin::FullPaint() {
2585 if ((technology
== SC_TECHNOLOGY_DEFAULT
) || (technology
== SC_TECHNOLOGY_DIRECTWRITEDC
)) {
2586 HDC hdc
= ::GetDC(MainHWND());
2588 ::ReleaseDC(MainHWND(), hdc
);
2595 * Redraw all of text area on the specified DC.
2596 * This paint will not be abandoned.
2598 void ScintillaWin::FullPaintDC(HDC hdc
) {
2599 paintState
= painting
;
2600 rcPaint
= GetClientRectangle();
2601 paintingAllText
= true;
2602 if (technology
== SC_TECHNOLOGY_DEFAULT
) {
2603 AutoSurface
surfaceWindow(hdc
, this);
2604 if (surfaceWindow
) {
2605 Paint(surfaceWindow
, rcPaint
);
2606 surfaceWindow
->Release();
2609 #if defined(USE_D2D)
2610 EnsureRenderTarget(hdc
);
2611 AutoSurface
surfaceWindow(pRenderTarget
, this);
2612 if (surfaceWindow
) {
2613 pRenderTarget
->BeginDraw();
2614 Paint(surfaceWindow
, rcPaint
);
2615 surfaceWindow
->Release();
2616 HRESULT hr
= pRenderTarget
->EndDraw();
2617 if (hr
== D2DERR_RECREATE_TARGET
) {
2623 paintState
= notPainting
;
2626 static bool CompareDevCap(HDC hdc
, HDC hOtherDC
, int nIndex
) {
2627 return ::GetDeviceCaps(hdc
, nIndex
) == ::GetDeviceCaps(hOtherDC
, nIndex
);
2630 bool ScintillaWin::IsCompatibleDC(HDC hOtherDC
) {
2631 HDC hdc
= ::GetDC(MainHWND());
2633 CompareDevCap(hdc
, hOtherDC
, TECHNOLOGY
) &&
2634 CompareDevCap(hdc
, hOtherDC
, LOGPIXELSY
) &&
2635 CompareDevCap(hdc
, hOtherDC
, LOGPIXELSX
) &&
2636 CompareDevCap(hdc
, hOtherDC
, BITSPIXEL
) &&
2637 CompareDevCap(hdc
, hOtherDC
, PLANES
);
2638 ::ReleaseDC(MainHWND(), hdc
);
2639 return isCompatible
;
2642 DWORD
ScintillaWin::EffectFromState(DWORD grfKeyState
) const {
2643 // These are the Wordpad semantics.
2645 if (inDragDrop
== ddDragging
) // Internal defaults to move
2646 dwEffect
= DROPEFFECT_MOVE
;
2648 dwEffect
= DROPEFFECT_COPY
;
2649 if (grfKeyState
& MK_ALT
)
2650 dwEffect
= DROPEFFECT_MOVE
;
2651 if (grfKeyState
& MK_CONTROL
)
2652 dwEffect
= DROPEFFECT_COPY
;
2656 /// Implement IUnknown
2657 STDMETHODIMP
ScintillaWin::QueryInterface(REFIID riid
, PVOID
*ppv
) {
2659 if (riid
== IID_IUnknown
)
2660 *ppv
= reinterpret_cast<IDropTarget
*>(&dt
);
2661 if (riid
== IID_IDropSource
)
2662 *ppv
= reinterpret_cast<IDropSource
*>(&ds
);
2663 if (riid
== IID_IDropTarget
)
2664 *ppv
= reinterpret_cast<IDropTarget
*>(&dt
);
2665 if (riid
== IID_IDataObject
)
2666 *ppv
= reinterpret_cast<IDataObject
*>(&dob
);
2668 return E_NOINTERFACE
;
2672 STDMETHODIMP_(ULONG
) ScintillaWin::AddRef() {
2676 STDMETHODIMP_(ULONG
) ScintillaWin::Release() {
2680 /// Implement IDropTarget
2681 STDMETHODIMP
ScintillaWin::DragEnter(LPDATAOBJECT pIDataSource
, DWORD grfKeyState
,
2682 POINTL
, PDWORD pdwEffect
) {
2683 if (pIDataSource
== NULL
)
2685 FORMATETC fmtu
= {CF_UNICODETEXT
, NULL
, DVASPECT_CONTENT
, -1, TYMED_HGLOBAL
};
2686 HRESULT hrHasUText
= pIDataSource
->QueryGetData(&fmtu
);
2687 hasOKText
= (hrHasUText
== S_OK
);
2689 FORMATETC fmte
= {CF_TEXT
, NULL
, DVASPECT_CONTENT
, -1, TYMED_HGLOBAL
};
2690 HRESULT hrHasText
= pIDataSource
->QueryGetData(&fmte
);
2691 hasOKText
= (hrHasText
== S_OK
);
2694 *pdwEffect
= DROPEFFECT_NONE
;
2698 *pdwEffect
= EffectFromState(grfKeyState
);
2702 STDMETHODIMP
ScintillaWin::DragOver(DWORD grfKeyState
, POINTL pt
, PDWORD pdwEffect
) {
2704 if (!hasOKText
|| pdoc
->IsReadOnly()) {
2705 *pdwEffect
= DROPEFFECT_NONE
;
2709 *pdwEffect
= EffectFromState(grfKeyState
);
2711 // Update the cursor.
2712 POINT rpt
= {pt
.x
, pt
.y
};
2713 ::ScreenToClient(MainHWND(), &rpt
);
2714 SetDragPosition(SPositionFromLocation(PointFromPOINT(rpt
), false, false, UserVirtualSpace()));
2718 errorStatus
= SC_STATUS_FAILURE
;
2723 STDMETHODIMP
ScintillaWin::DragLeave() {
2725 SetDragPosition(SelectionPosition(invalidPosition
));
2728 errorStatus
= SC_STATUS_FAILURE
;
2733 STDMETHODIMP
ScintillaWin::Drop(LPDATAOBJECT pIDataSource
, DWORD grfKeyState
,
2734 POINTL pt
, PDWORD pdwEffect
) {
2736 *pdwEffect
= EffectFromState(grfKeyState
);
2738 if (pIDataSource
== NULL
)
2741 SetDragPosition(SelectionPosition(invalidPosition
));
2743 STGMEDIUM medium
= {0, {0}, 0};
2745 std::vector
<char> data
; // Includes terminating NUL
2747 FORMATETC fmtu
= {CF_UNICODETEXT
, NULL
, DVASPECT_CONTENT
, -1, TYMED_HGLOBAL
};
2748 HRESULT hr
= pIDataSource
->GetData(&fmtu
, &medium
);
2749 if (SUCCEEDED(hr
) && medium
.hGlobal
) {
2750 GlobalMemory
memUDrop(medium
.hGlobal
);
2751 wchar_t *udata
= static_cast<wchar_t *>(memUDrop
.ptr
);
2753 if (IsUnicodeMode()) {
2754 int tlen
= static_cast<int>(memUDrop
.Size());
2755 // Convert UTF-16 to UTF-8
2756 int dataLen
= UTF8Length(udata
, tlen
/2);
2757 data
.resize(dataLen
+1);
2758 UTF8FromUTF16(udata
, tlen
/2, &data
[0], dataLen
);
2760 // Convert UTF-16 to ANSI
2762 // Default Scintilla behavior in Unicode mode
2763 // CF_UNICODETEXT available, but not in Unicode mode
2764 // Convert from Unicode to current Scintilla code page
2765 UINT cpDest
= CodePageOfDocument();
2766 int tlen
= ::WideCharToMultiByte(cpDest
, 0, udata
, -1,
2767 NULL
, 0, NULL
, NULL
) - 1; // subtract 0 terminator
2768 data
.resize(tlen
+ 1);
2769 ::WideCharToMultiByte(cpDest
, 0, udata
, -1,
2770 &data
[0], tlen
+ 1, NULL
, NULL
);
2775 FORMATETC fmte
= {CF_TEXT
, NULL
, DVASPECT_CONTENT
, -1, TYMED_HGLOBAL
};
2776 hr
= pIDataSource
->GetData(&fmte
, &medium
);
2777 if (SUCCEEDED(hr
) && medium
.hGlobal
) {
2778 GlobalMemory
memDrop(medium
.hGlobal
);
2779 const char *cdata
= static_cast<char *>(memDrop
.ptr
);
2781 data
.assign(cdata
, cdata
+strlen(cdata
)+1);
2786 if (!SUCCEEDED(hr
) || data
.empty()) {
2787 //Platform::DebugPrintf("Bad data format: 0x%x\n", hres);
2791 FORMATETC fmtr
= {cfColumnSelect
, NULL
, DVASPECT_CONTENT
, -1, TYMED_HGLOBAL
};
2792 HRESULT hrRectangular
= pIDataSource
->QueryGetData(&fmtr
);
2794 POINT rpt
= {pt
.x
, pt
.y
};
2795 ::ScreenToClient(MainHWND(), &rpt
);
2796 SelectionPosition movePos
= SPositionFromLocation(PointFromPOINT(rpt
), false, false, UserVirtualSpace());
2798 DropAt(movePos
, &data
[0], data
.size() - 1, *pdwEffect
== DROPEFFECT_MOVE
, hrRectangular
== S_OK
);
2801 if (medium
.pUnkForRelease
!= NULL
)
2802 medium
.pUnkForRelease
->Release();
2804 ::GlobalFree(medium
.hGlobal
);
2808 errorStatus
= SC_STATUS_FAILURE
;
2813 /// Implement important part of IDataObject
2814 STDMETHODIMP
ScintillaWin::GetData(FORMATETC
*pFEIn
, STGMEDIUM
*pSTM
) {
2815 bool formatOK
= (pFEIn
->cfFormat
== CF_TEXT
) ||
2816 ((pFEIn
->cfFormat
== CF_UNICODETEXT
) && IsUnicodeMode());
2819 (pFEIn
->dwAspect
& DVASPECT_CONTENT
) == 0 ||
2820 pFEIn
->lindex
!= -1 ||
2821 (pFEIn
->tymed
& TYMED_HGLOBAL
) == 0
2823 //Platform::DebugPrintf("DOB GetData No %d %x %x fmt=%x\n", lenDrag, pFEIn, pSTM, pFEIn->cfFormat);
2824 return DATA_E_FORMATETC
;
2826 pSTM
->tymed
= TYMED_HGLOBAL
;
2827 //Platform::DebugPrintf("DOB GetData OK %d %x %x\n", lenDrag, pFEIn, pSTM);
2830 if (pFEIn
->cfFormat
== CF_UNICODETEXT
) {
2831 int uchars
= UTF16Length(drag
.Data(), static_cast<int>(drag
.LengthWithTerminator()));
2832 text
.Allocate(2 * uchars
);
2834 UTF16FromUTF8(drag
.Data(), static_cast<int>(drag
.LengthWithTerminator()),
2835 static_cast<wchar_t *>(text
.ptr
), uchars
);
2838 text
.Allocate(drag
.LengthWithTerminator());
2840 memcpy(static_cast<char *>(text
.ptr
), drag
.Data(), drag
.LengthWithTerminator());
2843 pSTM
->hGlobal
= text
? text
.Unlock() : 0;
2844 pSTM
->pUnkForRelease
= 0;
2848 bool ScintillaWin::Register(HINSTANCE hInstance_
) {
2850 hInstance
= hInstance_
;
2853 // Register the Scintilla class
2856 // Register Scintilla as a wide character window
2857 WNDCLASSEXW wndclass
;
2858 wndclass
.cbSize
= sizeof(wndclass
);
2859 wndclass
.style
= CS_GLOBALCLASS
| CS_HREDRAW
| CS_VREDRAW
;
2860 wndclass
.lpfnWndProc
= ScintillaWin::SWndProc
;
2861 wndclass
.cbClsExtra
= 0;
2862 wndclass
.cbWndExtra
= sizeof(ScintillaWin
*);
2863 wndclass
.hInstance
= hInstance
;
2864 wndclass
.hIcon
= NULL
;
2865 wndclass
.hCursor
= NULL
;
2866 wndclass
.hbrBackground
= NULL
;
2867 wndclass
.lpszMenuName
= NULL
;
2868 wndclass
.lpszClassName
= L
"Scintilla";
2869 wndclass
.hIconSm
= 0;
2870 scintillaClassAtom
= ::RegisterClassExW(&wndclass
);
2871 result
= 0 != scintillaClassAtom
;
2874 // Register Scintilla as a normal character window
2875 WNDCLASSEX wndclass
;
2876 wndclass
.cbSize
= sizeof(wndclass
);
2877 wndclass
.style
= CS_GLOBALCLASS
| CS_HREDRAW
| CS_VREDRAW
;
2878 wndclass
.lpfnWndProc
= ScintillaWin::SWndProc
;
2879 wndclass
.cbClsExtra
= 0;
2880 wndclass
.cbWndExtra
= sizeof(ScintillaWin
*);
2881 wndclass
.hInstance
= hInstance
;
2882 wndclass
.hIcon
= NULL
;
2883 wndclass
.hCursor
= NULL
;
2884 wndclass
.hbrBackground
= NULL
;
2885 wndclass
.lpszMenuName
= NULL
;
2886 wndclass
.lpszClassName
= scintillaClassName
;
2887 wndclass
.hIconSm
= 0;
2888 scintillaClassAtom
= ::RegisterClassEx(&wndclass
);
2889 result
= 0 != scintillaClassAtom
;
2893 // Register the CallTip class
2894 WNDCLASSEX wndclassc
;
2895 wndclassc
.cbSize
= sizeof(wndclassc
);
2896 wndclassc
.style
= CS_GLOBALCLASS
| CS_HREDRAW
| CS_VREDRAW
;
2897 wndclassc
.cbClsExtra
= 0;
2898 wndclassc
.cbWndExtra
= sizeof(ScintillaWin
*);
2899 wndclassc
.hInstance
= hInstance
;
2900 wndclassc
.hIcon
= NULL
;
2901 wndclassc
.hbrBackground
= NULL
;
2902 wndclassc
.lpszMenuName
= NULL
;
2903 wndclassc
.lpfnWndProc
= ScintillaWin::CTWndProc
;
2904 wndclassc
.hCursor
= ::LoadCursor(NULL
, IDC_ARROW
);
2905 wndclassc
.lpszClassName
= callClassName
;
2906 wndclassc
.hIconSm
= 0;
2908 callClassAtom
= ::RegisterClassEx(&wndclassc
);
2909 result
= 0 != callClassAtom
;
2915 bool ScintillaWin::Unregister() {
2917 if (0 != scintillaClassAtom
) {
2918 if (::UnregisterClass(MAKEINTATOM(scintillaClassAtom
), hInstance
) == 0) {
2921 scintillaClassAtom
= 0;
2923 if (0 != callClassAtom
) {
2924 if (::UnregisterClass(MAKEINTATOM(callClassAtom
), hInstance
) == 0) {
2932 bool ScintillaWin::HasCaretSizeChanged() const {
2934 ( (0 != vs
.caretWidth
) && (sysCaretWidth
!= vs
.caretWidth
) )
2935 || ((0 != vs
.lineHeight
) && (sysCaretHeight
!= vs
.lineHeight
))
2942 BOOL
ScintillaWin::CreateSystemCaret() {
2943 sysCaretWidth
= vs
.caretWidth
;
2944 if (0 == sysCaretWidth
) {
2947 sysCaretHeight
= vs
.lineHeight
;
2948 int bitmapSize
= (((sysCaretWidth
+ 15) & ~15) >> 3) *
2950 std::vector
<char> bits(bitmapSize
);
2951 sysCaretBitmap
= ::CreateBitmap(sysCaretWidth
, sysCaretHeight
, 1,
2952 1, reinterpret_cast<BYTE
*>(&bits
[0]));
2953 BOOL retval
= ::CreateCaret(
2954 MainHWND(), sysCaretBitmap
,
2955 sysCaretWidth
, sysCaretHeight
);
2956 if (technology
== SC_TECHNOLOGY_DEFAULT
) {
2957 // System caret interferes with Direct2D drawing so only show it for GDI.
2958 ::ShowCaret(MainHWND());
2963 BOOL
ScintillaWin::DestroySystemCaret() {
2964 ::HideCaret(MainHWND());
2965 BOOL retval
= ::DestroyCaret();
2966 if (sysCaretBitmap
) {
2967 ::DeleteObject(sysCaretBitmap
);
2973 sptr_t PASCAL
ScintillaWin::CTWndProc(
2974 HWND hWnd
, UINT iMessage
, WPARAM wParam
, sptr_t lParam
) {
2975 // Find C++ object associated with window.
2976 ScintillaWin
*sciThis
= reinterpret_cast<ScintillaWin
*>(PointerFromWindow(hWnd
));
2978 // ctp will be zero if WM_CREATE not seen yet
2980 if (iMessage
== WM_CREATE
) {
2981 // Associate CallTip object with window
2982 CREATESTRUCT
*pCreate
= reinterpret_cast<CREATESTRUCT
*>(lParam
);
2983 SetWindowPointer(hWnd
, pCreate
->lpCreateParams
);
2986 return ::DefWindowProc(hWnd
, iMessage
, wParam
, lParam
);
2989 if (iMessage
== WM_NCDESTROY
) {
2990 ::SetWindowLong(hWnd
, 0, 0);
2991 return ::DefWindowProc(hWnd
, iMessage
, wParam
, lParam
);
2992 } else if (iMessage
== WM_PAINT
) {
2994 ::BeginPaint(hWnd
, &ps
);
2995 Surface
*surfaceWindow
= Surface::Allocate(sciThis
->technology
);
2996 if (surfaceWindow
) {
2997 #if defined(USE_D2D)
2998 ID2D1HwndRenderTarget
*pCTRenderTarget
= 0;
3001 GetClientRect(hWnd
, &rc
);
3002 // Create a Direct2D render target.
3003 if (sciThis
->technology
== SC_TECHNOLOGY_DEFAULT
) {
3004 surfaceWindow
->Init(ps
.hdc
, hWnd
);
3006 #if defined(USE_D2D)
3007 D2D1_HWND_RENDER_TARGET_PROPERTIES dhrtp
;
3009 dhrtp
.pixelSize
= D2D1::SizeU(rc
.right
- rc
.left
, rc
.bottom
- rc
.top
);
3010 dhrtp
.presentOptions
= (sciThis
->technology
== SC_TECHNOLOGY_DIRECTWRITERETAIN
) ?
3011 D2D1_PRESENT_OPTIONS_RETAIN_CONTENTS
: D2D1_PRESENT_OPTIONS_NONE
;
3013 D2D1_RENDER_TARGET_PROPERTIES drtp
;
3014 drtp
.type
= D2D1_RENDER_TARGET_TYPE_DEFAULT
;
3015 drtp
.pixelFormat
.format
= DXGI_FORMAT_UNKNOWN
;
3016 drtp
.pixelFormat
.alphaMode
= D2D1_ALPHA_MODE_UNKNOWN
;
3019 drtp
.usage
= D2D1_RENDER_TARGET_USAGE_NONE
;
3020 drtp
.minLevel
= D2D1_FEATURE_LEVEL_DEFAULT
;
3022 if (!SUCCEEDED(pD2DFactory
->CreateHwndRenderTarget(drtp
, dhrtp
, &pCTRenderTarget
))) {
3023 surfaceWindow
->Release();
3024 delete surfaceWindow
;
3025 ::EndPaint(hWnd
, &ps
);
3028 surfaceWindow
->Init(pCTRenderTarget
, hWnd
);
3029 pCTRenderTarget
->BeginDraw();
3032 surfaceWindow
->SetUnicodeMode(SC_CP_UTF8
== sciThis
->ct
.codePage
);
3033 surfaceWindow
->SetDBCSMode(sciThis
->ct
.codePage
);
3034 sciThis
->ct
.PaintCT(surfaceWindow
);
3035 #if defined(USE_D2D)
3036 if (pCTRenderTarget
)
3037 pCTRenderTarget
->EndDraw();
3039 surfaceWindow
->Release();
3040 delete surfaceWindow
;
3041 #if defined(USE_D2D)
3042 if (pCTRenderTarget
)
3043 pCTRenderTarget
->Release();
3046 ::EndPaint(hWnd
, &ps
);
3048 } else if ((iMessage
== WM_NCLBUTTONDOWN
) || (iMessage
== WM_NCLBUTTONDBLCLK
)) {
3050 pt
.x
= static_cast<short>(LOWORD(lParam
));
3051 pt
.y
= static_cast<short>(HIWORD(lParam
));
3052 ScreenToClient(hWnd
, &pt
);
3053 sciThis
->ct
.MouseClick(PointFromPOINT(pt
));
3054 sciThis
->CallTipClick();
3056 } else if (iMessage
== WM_LBUTTONDOWN
) {
3057 // This does not fire due to the hit test code
3058 sciThis
->ct
.MouseClick(Point::FromLong(static_cast<long>(lParam
)));
3059 sciThis
->CallTipClick();
3061 } else if (iMessage
== WM_SETCURSOR
) {
3062 ::SetCursor(::LoadCursor(NULL
, IDC_ARROW
));
3064 } else if (iMessage
== WM_NCHITTEST
) {
3067 return ::DefWindowProc(hWnd
, iMessage
, wParam
, lParam
);
3071 sciThis
->errorStatus
= SC_STATUS_FAILURE
;
3073 return ::DefWindowProc(hWnd
, iMessage
, wParam
, lParam
);
3076 sptr_t
ScintillaWin::DirectFunction(
3077 sptr_t ptr
, UINT iMessage
, uptr_t wParam
, sptr_t lParam
) {
3078 PLATFORM_ASSERT(::GetCurrentThreadId() == ::GetWindowThreadProcessId(reinterpret_cast<ScintillaWin
*>(ptr
)->MainHWND(), NULL
));
3079 return reinterpret_cast<ScintillaWin
*>(ptr
)->WndProc(iMessage
, wParam
, lParam
);
3083 #ifndef STATIC_BUILD
3084 __declspec(dllexport
)
3086 sptr_t __stdcall
Scintilla_DirectFunction(
3087 ScintillaWin
*sci
, UINT iMessage
, uptr_t wParam
, sptr_t lParam
) {
3088 return sci
->WndProc(iMessage
, wParam
, lParam
);
3091 sptr_t PASCAL
ScintillaWin::SWndProc(
3092 HWND hWnd
, UINT iMessage
, WPARAM wParam
, sptr_t lParam
) {
3093 //Platform::DebugPrintf("S W:%x M:%x WP:%x L:%x\n", hWnd, iMessage, wParam, lParam);
3095 // Find C++ object associated with window.
3096 ScintillaWin
*sci
= reinterpret_cast<ScintillaWin
*>(PointerFromWindow(hWnd
));
3097 // sci will be zero if WM_CREATE not seen yet
3100 if (iMessage
== WM_CREATE
) {
3101 // Create C++ object associated with window
3102 sci
= new ScintillaWin(hWnd
);
3103 SetWindowPointer(hWnd
, sci
);
3104 return sci
->WndProc(iMessage
, wParam
, lParam
);
3108 return ::DefWindowProc(hWnd
, iMessage
, wParam
, lParam
);
3110 if (iMessage
== WM_NCDESTROY
) {
3116 ::SetWindowLong(hWnd
, 0, 0);
3117 return ::DefWindowProc(hWnd
, iMessage
, wParam
, lParam
);
3119 return sci
->WndProc(iMessage
, wParam
, lParam
);
3124 // This function is externally visible so it can be called from container when building statically.
3125 // Must be called once only.
3126 int Scintilla_RegisterClasses(void *hInstance
) {
3127 Platform_Initialise(hInstance
);
3128 bool result
= ScintillaWin::Register(reinterpret_cast<HINSTANCE
>(hInstance
));
3130 Scintilla_LinkLexers();
3135 static int ResourcesRelease(bool fromDllMain
) {
3136 bool result
= ScintillaWin::Unregister();
3138 FreeLibrary(commctrl32
);
3141 Platform_Finalise(fromDllMain
);
3145 // This function is externally visible so it can be called from container when building statically.
3146 int Scintilla_ReleaseResources() {
3147 return ResourcesRelease(false);
3150 #ifndef STATIC_BUILD
3151 extern "C" int APIENTRY
DllMain(HINSTANCE hInstance
, DWORD dwReason
, LPVOID lpvReserved
) {
3152 //Platform::DebugPrintf("Scintilla::DllMain %d %d\n", hInstance, dwReason);
3153 if (dwReason
== DLL_PROCESS_ATTACH
) {
3154 if (!Scintilla_RegisterClasses(hInstance
))
3156 } else if (dwReason
== DLL_PROCESS_DETACH
) {
3157 if (lpvReserved
== NULL
) {
3158 ResourcesRelease(true);