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.
26 #define _WIN32_WINNT 0x0500
36 #if defined(NTDDI_WIN7) && !defined(DISABLE_D2D)
48 #include "Scintilla.h"
53 #include "StringCopy.h"
55 #include "LexerModule.h"
58 #include "UniqueString.h"
59 #include "SplitVector.h"
60 #include "Partitioning.h"
61 #include "RunStyles.h"
62 #include "ContractionState.h"
63 #include "CellBuffer.h"
66 #include "Indicator.h"
68 #include "LineMarker.h"
70 #include "ViewStyle.h"
71 #include "CharClassify.h"
72 #include "Decoration.h"
73 #include "CaseFolder.h"
75 #include "CaseConvert.h"
76 #include "UniConversion.h"
77 #include "Selection.h"
78 #include "PositionCache.h"
79 #include "EditModel.h"
80 #include "MarginView.h"
84 #include "AutoComplete.h"
85 #include "ScintillaBase.h"
88 #include "ExternalLexer.h"
94 #ifndef SPI_GETWHEELSCROLLLINES
95 #define SPI_GETWHEELSCROLLLINES 104
99 #define WM_UNICHAR 0x0109
102 #ifndef UNICODE_NOCHAR
103 #define UNICODE_NOCHAR 0xFFFF
106 #ifndef IS_HIGH_SURROGATE
107 #define IS_HIGH_SURROGATE(x) ((x) >= SURROGATE_LEAD_FIRST && (x) <= SURROGATE_LEAD_LAST)
110 #ifndef IS_LOW_SURROGATE
111 #define IS_LOW_SURROGATE(x) ((x) >= SURROGATE_TRAIL_FIRST && (x) <= SURROGATE_TRAIL_LAST)
118 #define SC_WIN_IDLE 5001
120 #define SC_INDICATOR_INPUT INDIC_IME
121 #define SC_INDICATOR_TARGET INDIC_IME+1
122 #define SC_INDICATOR_CONVERTED INDIC_IME+2
123 #define SC_INDICATOR_UNKNOWN INDIC_IME_MAX
125 #ifndef SCS_CAP_SETRECONVERTSTRING
126 #define SCS_CAP_SETRECONVERTSTRING 0x00000004
127 #define SCS_QUERYRECONVERTSTRING 0x00020000
128 #define SCS_SETRECONVERTSTRING 0x00010000
131 typedef BOOL (WINAPI
*TrackMouseEventSig
)(LPTRACKMOUSEEVENT
);
132 typedef UINT_PTR (WINAPI
*SetCoalescableTimerSig
)(HWND hwnd
, UINT_PTR nIDEvent
,
133 UINT uElapse
, TIMERPROC lpTimerFunc
, ULONG uToleranceDelay
);
135 // GCC has trouble with the standard COM ABI so do it the old C way with explicit vtables.
137 const TCHAR callClassName
[] = TEXT("CallTip");
140 using namespace Scintilla
;
143 static void *PointerFromWindow(HWND hWnd
) {
144 return reinterpret_cast<void *>(::GetWindowLongPtr(hWnd
, 0));
147 static void SetWindowPointer(HWND hWnd
, void *ptr
) {
148 ::SetWindowLongPtr(hWnd
, 0, reinterpret_cast<LONG_PTR
>(ptr
));
151 static void SetWindowID(HWND hWnd
, int identifier
) {
152 ::SetWindowLongPtr(hWnd
, GWLP_ID
, identifier
);
155 static Point
PointFromPOINT(POINT pt
) {
156 return Point::FromInts(pt
.x
, pt
.y
);
159 class ScintillaWin
; // Forward declaration for COM interface subobjects
161 typedef void VFunction(void);
166 class FormatEnumerator
{
171 std::vector
<CLIPFORMAT
> formats
;
172 FormatEnumerator(int pos_
, CLIPFORMAT formats_
[], size_t formatsLen_
);
208 IMContext(HWND hwnd_
) :
209 hwnd(hwnd_
), hIMC(::ImmGetContext(hwnd_
)) {
213 ::ImmReleaseContext(hwnd
, hIMC
);
216 unsigned int GetImeCaretPos() {
217 return ImmGetCompositionStringW(hIMC
, GCS_CURSORPOS
, NULL
, 0);
220 std::vector
<BYTE
> GetImeAttributes() {
221 int attrLen
= ::ImmGetCompositionStringW(hIMC
, GCS_COMPATTR
, NULL
, 0);
222 std::vector
<BYTE
> attr(attrLen
, 0);
223 ::ImmGetCompositionStringW(hIMC
, GCS_COMPATTR
, &attr
[0], static_cast<DWORD
>(attr
.size()));
227 std::wstring
GetCompositionString(DWORD dwIndex
) {
228 const LONG byteLen
= ::ImmGetCompositionStringW(hIMC
, dwIndex
, NULL
, 0);
229 std::wstring
wcs(byteLen
/ 2, 0);
230 ::ImmGetCompositionStringW(hIMC
, dwIndex
, &wcs
[0], byteLen
);
240 public ScintillaBase
{
242 bool lastKeyDownConsumed
;
243 wchar_t lastHighSurrogateChar
;
246 bool trackedMouseLeave
;
247 SetCoalescableTimerSig SetCoalescableTimerFn
;
249 unsigned int linesPerScroll
; ///< Intellimouse support
250 int wheelDelta
; ///< Wheel delta from roll
256 CLIPFORMAT cfColumnSelect
;
257 CLIPFORMAT cfBorlandIDEBlockType
;
258 CLIPFORMAT cfLineSelect
;
259 CLIPFORMAT cfVSLineTag
;
266 static HINSTANCE hInstance
;
267 static ATOM scintillaClassAtom
;
268 static ATOM callClassAtom
;
271 ID2D1RenderTarget
*pRenderTarget
;
272 bool renderTargetValid
;
275 explicit ScintillaWin(HWND hwnd
);
276 // Deleted so ScintillaWin objects can not be copied.
277 ScintillaWin(const ScintillaWin
&) = delete;
278 ScintillaWin
&operator=(const ScintillaWin
&) = delete;
279 ~ScintillaWin() override
;
282 void Finalise() override
;
284 void EnsureRenderTarget(HDC hdc
);
285 void DropRenderTarget();
289 static sptr_t
DirectFunction(
290 sptr_t ptr
, UINT iMessage
, uptr_t wParam
, sptr_t lParam
);
291 static LRESULT PASCAL
SWndProc(
292 HWND hWnd
, UINT iMessage
, WPARAM wParam
, LPARAM lParam
);
293 static LRESULT PASCAL
CTWndProc(
294 HWND hWnd
, UINT iMessage
, WPARAM wParam
, LPARAM lParam
);
296 enum { invalidTimerID
, standardTimerID
, idleTimerID
, fineTimerStart
};
298 bool DragThreshold(Point ptStart
, Point ptNow
) override
;
299 void StartDrag() override
;
300 Sci::Position
TargetAsUTF8(char *text
);
301 void AddCharUTF16(wchar_t const *wcs
, unsigned int wclen
);
302 Sci::Position
EncodedFromUTF8(char *utf8
, char *encoded
) const;
303 sptr_t
WndPaint(uptr_t wParam
);
305 sptr_t
HandleCompositionWindowed(uptr_t wParam
, sptr_t lParam
);
306 sptr_t
HandleCompositionInline(uptr_t wParam
, sptr_t lParam
);
307 static bool KoreanIME();
308 void MoveImeCarets(Sci::Position offset
);
309 void DrawImeIndicator(int indicator
, int len
);
310 void SetCandidateWindowPos();
311 void SelectionToHangul();
314 void AddWString(std::wstring wcs
);
316 UINT
CodePageOfDocument() const;
317 bool ValidCodePage(int codePage
) const override
;
318 sptr_t
DefWndProc(unsigned int iMessage
, uptr_t wParam
, sptr_t lParam
) override
;
319 bool SetIdle(bool on
) override
;
320 UINT_PTR timers
[tickDwell
+1];
321 bool FineTickerAvailable() override
;
322 bool FineTickerRunning(TickReason reason
) override
;
323 void FineTickerStart(TickReason reason
, int millis
, int tolerance
) override
;
324 void FineTickerCancel(TickReason reason
) override
;
325 void SetMouseCapture(bool on
) override
;
326 bool HaveMouseCapture() override
;
327 void SetTrackMouseLeaveEvent(bool on
);
328 bool PaintContains(PRectangle rc
) override
;
329 void ScrollText(Sci::Line linesToMove
) override
;
330 void NotifyCaretMove() override
;
331 void UpdateSystemCaret() override
;
332 void SetVerticalScrollPos() override
;
333 void SetHorizontalScrollPos() override
;
334 bool ModifyScrollBars(Sci::Line nMax
, Sci::Line nPage
) override
;
335 void NotifyChange() override
;
336 void NotifyFocus(bool focus
) override
;
337 void SetCtrlID(int identifier
) override
;
338 int GetCtrlID() override
;
339 void NotifyParent(SCNotification scn
) override
;
340 void NotifyParent(SCNotification
* scn
) override
;
341 void NotifyDoubleClick(Point pt
, int modifiers
) override
;
342 CaseFolder
*CaseFolderForEncoding() override
;
343 std::string
CaseMapString(const std::string
&s
, int caseMapping
) override
;
344 void Copy() override
;
345 void CopyAllowLine() override
;
346 bool CanPaste() override
;
347 void Paste() override
;
348 void CreateCallTipWindow(PRectangle rc
) override
;
349 void AddToPopUp(const char *label
, int cmd
= 0, bool enabled
= true) override
;
350 void ClaimSelection() override
;
353 void ImeStartComposition();
354 void ImeEndComposition();
355 LRESULT
ImeOnReconvert(LPARAM lParam
);
357 void GetIntelliMouseParameters();
358 void CopyToClipboard(const SelectionText
&selectedText
) override
;
359 void ScrollMessage(WPARAM wParam
);
360 void HorizontalScrollMessage(WPARAM wParam
);
362 void FullPaintDC(HDC hdc
);
363 bool IsCompatibleDC(HDC hOtherDC
);
364 DWORD
EffectFromState(DWORD grfKeyState
) const;
366 int SetScrollInfo(int nBar
, LPCSCROLLINFO lpsi
, BOOL bRedraw
);
367 bool GetScrollInfo(int nBar
, LPSCROLLINFO lpsi
);
368 void ChangeScrollPos(int barType
, Sci::Position pos
);
369 sptr_t
GetTextLength();
370 sptr_t
GetText(uptr_t wParam
, sptr_t lParam
);
373 // Public for benefit of Scintilla_DirectFunction
374 sptr_t
WndProc(unsigned int iMessage
, uptr_t wParam
, sptr_t lParam
) override
;
376 /// Implement IUnknown
377 STDMETHODIMP
QueryInterface(REFIID riid
, PVOID
*ppv
);
378 STDMETHODIMP_(ULONG
)AddRef();
379 STDMETHODIMP_(ULONG
)Release();
381 /// Implement IDropTarget
382 STDMETHODIMP
DragEnter(LPDATAOBJECT pIDataSource
, DWORD grfKeyState
,
383 POINTL pt
, PDWORD pdwEffect
);
384 STDMETHODIMP
DragOver(DWORD grfKeyState
, POINTL pt
, PDWORD pdwEffect
);
385 STDMETHODIMP
DragLeave();
386 STDMETHODIMP
Drop(LPDATAOBJECT pIDataSource
, DWORD grfKeyState
,
387 POINTL pt
, PDWORD pdwEffect
);
389 /// Implement important part of IDataObject
390 STDMETHODIMP
GetData(FORMATETC
*pFEIn
, STGMEDIUM
*pSTM
);
392 static bool Register(HINSTANCE hInstance_
);
393 static bool Unregister();
395 friend class DropSource
;
396 friend class DataObject
;
397 friend class DropTarget
;
398 bool DragIsRectangularOK(CLIPFORMAT fmt
) const {
399 return drag
.rectangular
&& (fmt
== cfColumnSelect
);
403 // For use in creating a system caret
404 bool HasCaretSizeChanged() const;
405 BOOL
CreateSystemCaret();
406 BOOL
DestroySystemCaret();
407 HBITMAP sysCaretBitmap
;
412 HINSTANCE
ScintillaWin::hInstance
= 0;
413 ATOM
ScintillaWin::scintillaClassAtom
= 0;
414 ATOM
ScintillaWin::callClassAtom
= 0;
416 ScintillaWin::ScintillaWin(HWND hwnd
) {
418 lastKeyDownConsumed
= false;
419 lastHighSurrogateChar
= 0;
421 capturedMouse
= false;
422 trackedMouseLeave
= false;
423 SetCoalescableTimerFn
= 0;
426 wheelDelta
= 0; // Wheel delta from roll
432 // There does not seem to be a real standard for indicating that the clipboard
433 // contains a rectangular selection, so copy Developer Studio and Borland Delphi.
434 cfColumnSelect
= static_cast<CLIPFORMAT
>(
435 ::RegisterClipboardFormat(TEXT("MSDEVColumnSelect")));
436 cfBorlandIDEBlockType
= static_cast<CLIPFORMAT
>(
437 ::RegisterClipboardFormat(TEXT("Borland IDE Block Type")));
439 // Likewise for line-copy (copies a full line when no text is selected)
440 cfLineSelect
= static_cast<CLIPFORMAT
>(
441 ::RegisterClipboardFormat(TEXT("MSDEVLineSelect")));
442 cfVSLineTag
= static_cast<CLIPFORMAT
>(
443 ::RegisterClipboardFormat(TEXT("VisualStudioEditorOperationsLineCutCopyClipboardTag")));
458 renderTargetValid
= true;
461 caret
.period
= ::GetCaretBlinkTime();
462 if (caret
.period
< 0)
468 ScintillaWin::~ScintillaWin() {}
470 void ScintillaWin::Init() {
471 // Initialize COM. If the app has already done this it will have
472 // no effect. If the app hasn't, we really shouldn't ask them to call
473 // it just so this internal feature works.
474 hrOle
= ::OleInitialize(NULL
);
476 // Find SetCoalescableTimer which is only available from Windows 8+
477 HMODULE user32
= ::GetModuleHandle(TEXT("user32.dll"));
479 SetCoalescableTimerFn
= (SetCoalescableTimerSig
)::GetProcAddress(user32
, "SetCoalescableTimer");
482 for (TickReason tr
= tickCaret
; tr
<= tickDwell
; tr
= static_cast<TickReason
>(tr
+ 1)) {
485 vs
.indicators
[SC_INDICATOR_UNKNOWN
] = Indicator(INDIC_HIDDEN
, ColourDesired(0, 0, 0xff));
486 vs
.indicators
[SC_INDICATOR_INPUT
] = Indicator(INDIC_DOTS
, ColourDesired(0, 0, 0xff));
487 vs
.indicators
[SC_INDICATOR_CONVERTED
] = Indicator(INDIC_COMPOSITIONTHICK
, ColourDesired(0, 0, 0xff));
488 vs
.indicators
[SC_INDICATOR_TARGET
] = Indicator(INDIC_STRAIGHTBOX
, ColourDesired(0, 0, 0xff));
491 void ScintillaWin::Finalise() {
492 ScintillaBase::Finalise();
493 for (TickReason tr
= tickCaret
; tr
<= tickDwell
; tr
= static_cast<TickReason
>(tr
+ 1)) {
494 FineTickerCancel(tr
);
500 ::RevokeDragDrop(MainHWND());
501 if (SUCCEEDED(hrOle
)) {
508 void ScintillaWin::EnsureRenderTarget(HDC hdc
) {
509 if (!renderTargetValid
) {
511 renderTargetValid
= true;
513 if (pD2DFactory
&& !pRenderTarget
) {
515 HWND hw
= MainHWND();
516 GetClientRect(hw
, &rc
);
518 D2D1_SIZE_U size
= D2D1::SizeU(rc
.right
- rc
.left
, rc
.bottom
- rc
.top
);
520 // Create a Direct2D render target.
522 D2D1_RENDER_TARGET_PROPERTIES drtp
;
523 drtp
.type
= D2D1_RENDER_TARGET_TYPE_DEFAULT
;
524 drtp
.pixelFormat
.format
= DXGI_FORMAT_UNKNOWN
;
525 drtp
.pixelFormat
.alphaMode
= D2D1_ALPHA_MODE_UNKNOWN
;
528 drtp
.usage
= D2D1_RENDER_TARGET_USAGE_NONE
;
529 drtp
.minLevel
= D2D1_FEATURE_LEVEL_DEFAULT
;
531 if (technology
== SC_TECHNOLOGY_DIRECTWRITEDC
) {
532 // Explicit pixel format needed.
533 drtp
.pixelFormat
= D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM
,
534 D2D1_ALPHA_MODE_IGNORE
);
536 ID2D1DCRenderTarget
*pDCRT
= NULL
;
537 HRESULT hr
= pD2DFactory
->CreateDCRenderTarget(&drtp
, &pDCRT
);
539 pRenderTarget
= pDCRT
;
541 Platform::DebugPrintf("Failed CreateDCRenderTarget 0x%x\n", hr
);
542 pRenderTarget
= NULL
;
546 D2D1_HWND_RENDER_TARGET_PROPERTIES dhrtp
;
548 dhrtp
.pixelSize
= size
;
549 dhrtp
.presentOptions
= (technology
== SC_TECHNOLOGY_DIRECTWRITERETAIN
) ?
550 D2D1_PRESENT_OPTIONS_RETAIN_CONTENTS
: D2D1_PRESENT_OPTIONS_NONE
;
552 ID2D1HwndRenderTarget
*pHwndRenderTarget
= NULL
;
553 HRESULT hr
= pD2DFactory
->CreateHwndRenderTarget(drtp
, dhrtp
, &pHwndRenderTarget
);
555 pRenderTarget
= pHwndRenderTarget
;
557 Platform::DebugPrintf("Failed CreateHwndRenderTarget 0x%x\n", hr
);
558 pRenderTarget
= NULL
;
562 pD2DFactory
->CreateHwndRenderTarget(
563 D2D1::RenderTargetProperties(
564 D2D1_RENDER_TARGET_TYPE_DEFAULT
,
565 D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM
, D2D1_ALPHA_MODE_PREMULTIPLIED
),
566 96.0f
, 96.0f
, D2D1_RENDER_TARGET_USAGE_NONE
, D2D1_FEATURE_LEVEL_DEFAULT
),
567 D2D1::HwndRenderTargetProperties(hw
, size
),
570 // Pixmaps were created to be compatible with previous render target so
571 // need to be recreated.
575 if ((technology
== SC_TECHNOLOGY_DIRECTWRITEDC
) && pRenderTarget
) {
577 GetClientRect(MainHWND(), &rcWindow
);
578 HRESULT hr
= static_cast<ID2D1DCRenderTarget
*>(pRenderTarget
)->BindDC(hdc
, &rcWindow
);
580 Platform::DebugPrintf("BindDC failed 0x%x\n", hr
);
586 void ScintillaWin::DropRenderTarget() {
588 pRenderTarget
->Release();
595 HWND
ScintillaWin::MainHWND() {
596 return static_cast<HWND
>(wMain
.GetID());
599 bool ScintillaWin::DragThreshold(Point ptStart
, Point ptNow
) {
600 const int xMove
= static_cast<int>(std::abs(ptStart
.x
- ptNow
.x
));
601 const int yMove
= static_cast<int>(std::abs(ptStart
.y
- ptNow
.y
));
602 return (xMove
> ::GetSystemMetrics(SM_CXDRAG
)) ||
603 (yMove
> ::GetSystemMetrics(SM_CYDRAG
));
606 void ScintillaWin::StartDrag() {
607 inDragDrop
= ddDragging
;
609 dropWentOutside
= true;
610 IDataObject
*pDataObject
= reinterpret_cast<IDataObject
*>(&dob
);
611 IDropSource
*pDropSource
= reinterpret_cast<IDropSource
*>(&ds
);
612 //Platform::DebugPrintf("About to DoDragDrop %x %x\n", pDataObject, pDropSource);
613 const HRESULT hr
= ::DoDragDrop(
616 DROPEFFECT_COPY
| DROPEFFECT_MOVE
, &dwEffect
);
617 //Platform::DebugPrintf("DoDragDrop = %x\n", hr);
619 if ((hr
== DRAGDROP_S_DROP
) && (dwEffect
== DROPEFFECT_MOVE
) && dropWentOutside
) {
620 // Remove dragged out text
625 SetDragPosition(SelectionPosition(Sci::invalidPosition
));
628 // Avoid warnings everywhere for old style casts by concentrating them here
629 static WORD
LoWord(uptr_t l
) {
633 static WORD
HiWord(uptr_t l
) {
637 static int InputCodePage() {
638 HKL inputLocale
= ::GetKeyboardLayout(0);
639 LANGID inputLang
= LOWORD(inputLocale
);
641 const int res
= ::GetLocaleInfoA(MAKELCID(inputLang
, SORT_DEFAULT
),
642 LOCALE_IDEFAULTANSICODEPAGE
, sCodePage
, sizeof(sCodePage
));
645 return atoi(sCodePage
);
648 /** Map the key codes to their equivalent SCK_ form. */
649 static int KeyTranslate(int keyIn
) {
650 //PLATFORM_ASSERT(!keyIn);
652 case VK_DOWN
: return SCK_DOWN
;
653 case VK_UP
: return SCK_UP
;
654 case VK_LEFT
: return SCK_LEFT
;
655 case VK_RIGHT
: return SCK_RIGHT
;
656 case VK_HOME
: return SCK_HOME
;
657 case VK_END
: return SCK_END
;
658 case VK_PRIOR
: return SCK_PRIOR
;
659 case VK_NEXT
: return SCK_NEXT
;
660 case VK_DELETE
: return SCK_DELETE
;
661 case VK_INSERT
: return SCK_INSERT
;
662 case VK_ESCAPE
: return SCK_ESCAPE
;
663 case VK_BACK
: return SCK_BACK
;
664 case VK_TAB
: return SCK_TAB
;
665 case VK_RETURN
: return SCK_RETURN
;
666 case VK_ADD
: return SCK_ADD
;
667 case VK_SUBTRACT
: return SCK_SUBTRACT
;
668 case VK_DIVIDE
: return SCK_DIVIDE
;
669 case VK_LWIN
: return SCK_WIN
;
670 case VK_RWIN
: return SCK_RWIN
;
671 case VK_APPS
: return SCK_MENU
;
672 case VK_OEM_2
: return '/';
673 case VK_OEM_3
: return '`';
674 case VK_OEM_4
: return '[';
675 case VK_OEM_5
: return '\\';
676 case VK_OEM_6
: return ']';
677 default: return keyIn
;
681 static bool BoundsContains(PRectangle rcBounds
, HRGN hRgnBounds
, PRectangle rcCheck
) {
682 bool contains
= true;
683 if (!rcCheck
.Empty()) {
684 if (!rcBounds
.Contains(rcCheck
)) {
686 } else if (hRgnBounds
) {
687 // In bounding rectangle so check more accurately using region
688 HRGN hRgnCheck
= ::CreateRectRgn(static_cast<int>(rcCheck
.left
), static_cast<int>(rcCheck
.top
),
689 static_cast<int>(rcCheck
.right
), static_cast<int>(rcCheck
.bottom
));
691 HRGN hRgnDifference
= ::CreateRectRgn(0, 0, 0, 0);
692 if (hRgnDifference
) {
693 const int combination
= ::CombineRgn(hRgnDifference
, hRgnCheck
, hRgnBounds
, RGN_DIFF
);
694 if (combination
!= NULLREGION
) {
697 ::DeleteRgn(hRgnDifference
);
699 ::DeleteRgn(hRgnCheck
);
706 static std::string
StringEncode(const std::wstring
&s
, int codePage
) {
707 const int cchMulti
= s
.length() ? ::WideCharToMultiByte(codePage
, 0, s
.c_str(), static_cast<int>(s
.length()), NULL
, 0, NULL
, NULL
) : 0;
708 std::string
sMulti(cchMulti
, 0);
710 ::WideCharToMultiByte(codePage
, 0, s
.c_str(), static_cast<int>(s
.size()), &sMulti
[0], cchMulti
, NULL
, NULL
);
715 static std::wstring
StringDecode(const std::string
&s
, int codePage
) {
716 const int cchWide
= s
.length() ? ::MultiByteToWideChar(codePage
, 0, s
.c_str(), static_cast<int>(s
.length()), NULL
, 0) : 0;
717 std::wstring
sWide(cchWide
, 0);
719 ::MultiByteToWideChar(codePage
, 0, s
.c_str(), static_cast<int>(s
.length()), &sWide
[0], cchWide
);
724 static std::wstring
StringMapCase(const std::wstring
&ws
, DWORD mapFlags
) {
725 const int charsConverted
= ::LCMapStringW(LOCALE_SYSTEM_DEFAULT
, mapFlags
,
726 ws
.c_str(), static_cast<int>(ws
.length()), NULL
, 0);
727 std::wstring
wsConverted(charsConverted
, 0);
728 if (charsConverted
) {
729 ::LCMapStringW(LOCALE_SYSTEM_DEFAULT
, mapFlags
,
730 ws
.c_str(), static_cast<int>(ws
.length()), &wsConverted
[0], charsConverted
);
735 // Returns the target converted to UTF8.
736 // Return the length in bytes.
737 Sci::Position
ScintillaWin::TargetAsUTF8(char *text
) {
738 Sci::Position targetLength
= targetEnd
- targetStart
;
739 if (IsUnicodeMode()) {
741 pdoc
->GetCharRange(text
, targetStart
, targetLength
);
745 const std::string s
= RangeText(targetStart
, targetEnd
);
746 const std::wstring characters
= StringDecode(s
, CodePageOfDocument());
747 const int utf8Len
= ::WideCharToMultiByte(CP_UTF8
, 0, characters
.c_str(), static_cast<int>(characters
.length()), NULL
, 0, 0, 0);
749 ::WideCharToMultiByte(CP_UTF8
, 0, characters
.c_str(), static_cast<int>(characters
.length()), text
, utf8Len
, 0, 0);
750 text
[utf8Len
] = '\0';
757 // Translates a nul terminated UTF8 string into the document encoding.
758 // Return the length of the result in bytes.
759 Sci::Position
ScintillaWin::EncodedFromUTF8(char *utf8
, char *encoded
) const {
760 Sci::Position inputLength
= (lengthForEncode
>= 0) ? lengthForEncode
: static_cast<Sci::Position
>(strlen(utf8
));
761 if (IsUnicodeMode()) {
763 memcpy(encoded
, utf8
, inputLength
);
768 int charsLen
= ::MultiByteToWideChar(CP_UTF8
, 0, utf8
, inputLength
, NULL
, 0);
769 std::wstring
characters(charsLen
, '\0');
770 ::MultiByteToWideChar(CP_UTF8
, 0, utf8
, inputLength
, &characters
[0], charsLen
);
772 int encodedLen
= ::WideCharToMultiByte(CodePageOfDocument(),
773 0, &characters
[0], charsLen
, NULL
, 0, 0, 0);
775 ::WideCharToMultiByte(CodePageOfDocument(), 0, &characters
[0], charsLen
, encoded
, encodedLen
, 0, 0);
776 encoded
[encodedLen
] = '\0';
782 // Add one character from a UTF-16 string, by converting to either UTF-8 or
783 // the current codepage. Code is similar to HandleCompositionWindowed().
784 void ScintillaWin::AddCharUTF16(wchar_t const *wcs
, unsigned int wclen
) {
785 if (IsUnicodeMode()) {
786 char utfval
[maxLenInputIME
* 3];
787 size_t len
= UTF8Length(wcs
, wclen
);
788 UTF8FromUTF16(wcs
, wclen
, utfval
, len
);
790 AddCharUTF(utfval
, static_cast<unsigned int>(len
));
792 UINT cpDest
= CodePageOfDocument();
793 char inBufferCP
[maxLenInputIME
* 2];
794 const int size
= ::WideCharToMultiByte(cpDest
,
795 0, wcs
, wclen
, inBufferCP
, sizeof(inBufferCP
) - 1, 0, 0);
796 for (int i
=0; i
<size
; i
++) {
797 AddChar(inBufferCP
[i
]);
802 sptr_t
ScintillaWin::WndPaint(uptr_t wParam
) {
805 // Redirect assertions to debug output and save current state
806 const bool assertsPopup
= Platform::ShowAssertionPopUps(false);
807 paintState
= painting
;
811 const bool IsOcxCtrl
= (wParam
!= 0); // if wParam != 0, it contains
812 // a PAINSTRUCT* from the OCX
813 // Removed since this interferes with reporting other assertions as it occurs repeatedly
814 //PLATFORM_ASSERT(hRgnUpdate == NULL);
815 hRgnUpdate
= ::CreateRectRgn(0, 0, 0, 0);
817 pps
= reinterpret_cast<PAINTSTRUCT
*>(wParam
);
819 ::GetUpdateRgn(MainHWND(), hRgnUpdate
, FALSE
);
821 ::BeginPaint(MainHWND(), pps
);
823 rcPaint
= PRectangle::FromInts(pps
->rcPaint
.left
, pps
->rcPaint
.top
, pps
->rcPaint
.right
, pps
->rcPaint
.bottom
);
824 const PRectangle rcClient
= GetClientRectangle();
825 paintingAllText
= BoundsContains(rcPaint
, hRgnUpdate
, rcClient
);
826 if (technology
== SC_TECHNOLOGY_DEFAULT
) {
827 AutoSurface
surfaceWindow(pps
->hdc
, this);
829 Paint(surfaceWindow
, rcPaint
);
830 surfaceWindow
->Release();
834 EnsureRenderTarget(pps
->hdc
);
835 AutoSurface
surfaceWindow(pRenderTarget
, this);
837 pRenderTarget
->BeginDraw();
838 Paint(surfaceWindow
, rcPaint
);
839 surfaceWindow
->Release();
840 const HRESULT hr
= pRenderTarget
->EndDraw();
841 if (hr
== static_cast<HRESULT
>(D2DERR_RECREATE_TARGET
)) {
843 paintState
= paintAbandoned
;
849 ::DeleteRgn(hRgnUpdate
);
854 ::EndPaint(MainHWND(), pps
);
855 if (paintState
== paintAbandoned
) {
856 // Painting area was insufficient to cover new styling or brace highlight positions
858 FullPaintDC(pps
->hdc
);
863 paintState
= notPainting
;
865 // Restore debug output state
866 Platform::ShowAssertionPopUps(assertsPopup
);
868 //Platform::DebugPrintf("Paint took %g\n", et.Duration());
872 sptr_t
ScintillaWin::HandleCompositionWindowed(uptr_t wParam
, sptr_t lParam
) {
873 if (lParam
& GCS_RESULTSTR
) {
874 IMContext
imc(MainHWND());
876 AddWString(imc
.GetCompositionString(GCS_RESULTSTR
));
878 // Set new position after converted
879 Point pos
= PointMainCaret();
880 COMPOSITIONFORM CompForm
;
881 CompForm
.dwStyle
= CFS_POINT
;
882 CompForm
.ptCurrentPos
.x
= static_cast<int>(pos
.x
);
883 CompForm
.ptCurrentPos
.y
= static_cast<int>(pos
.y
);
884 ::ImmSetCompositionWindow(imc
.hIMC
, &CompForm
);
888 return ::DefWindowProc(MainHWND(), WM_IME_COMPOSITION
, wParam
, lParam
);
891 bool ScintillaWin::KoreanIME() {
892 const int codePage
= InputCodePage();
893 return codePage
== 949 || codePage
== 1361;
896 void ScintillaWin::MoveImeCarets(Sci::Position offset
) {
897 // Move carets relatively by bytes.
898 for (size_t r
=0; r
<sel
.Count(); r
++) {
899 Sci::Position positionInsert
= sel
.Range(r
).Start().Position();
900 sel
.Range(r
).caret
.SetPosition(positionInsert
+ offset
);
901 sel
.Range(r
).anchor
.SetPosition(positionInsert
+ offset
);
905 void ScintillaWin::DrawImeIndicator(int indicator
, int len
) {
906 // Emulate the visual style of IME characters with indicators.
907 // Draw an indicator on the character before caret by the character bytes of len
908 // so it should be called after addCharUTF().
909 // It does not affect caret positions.
910 if (indicator
< 8 || indicator
> INDIC_MAX
) {
913 pdoc
->DecorationSetCurrentIndicator(indicator
);
914 for (size_t r
=0; r
<sel
.Count(); r
++) {
915 Sci::Position positionInsert
= sel
.Range(r
).Start().Position();
916 pdoc
->DecorationFillRange(positionInsert
- len
, 1, len
);
920 void ScintillaWin::SetCandidateWindowPos() {
921 IMContext
imc(MainHWND());
923 Point pos
= PointMainCaret();
924 CANDIDATEFORM CandForm
;
925 CandForm
.dwIndex
= 0;
926 CandForm
.dwStyle
= CFS_CANDIDATEPOS
;
927 CandForm
.ptCurrentPos
.x
= static_cast<int>(pos
.x
);
928 CandForm
.ptCurrentPos
.y
= static_cast<int>(pos
.y
+ vs
.lineHeight
);
929 ::ImmSetCandidateWindow(imc
.hIMC
, &CandForm
);
933 void ScintillaWin::SelectionToHangul() {
934 // Convert every hanja to hangul within the main range.
935 const Sci::Position selStart
= sel
.RangeMain().Start().Position();
936 const Sci::Position documentStrLen
= sel
.RangeMain().Length();
937 const Sci::Position selEnd
= selStart
+ documentStrLen
;
938 const Sci::Position utf16Len
= pdoc
->CountUTF16(selStart
, selEnd
);
941 std::string
documentStr(documentStrLen
, '\0');
942 pdoc
->GetCharRange(&documentStr
[0], selStart
, documentStrLen
);
944 std::wstring uniStr
= StringDecode(documentStr
, CodePageOfDocument());
945 const int converted
= HanjaDict::GetHangulOfHanja(&uniStr
[0]);
946 documentStr
= StringEncode(uniStr
, CodePageOfDocument());
949 pdoc
->BeginUndoAction();
951 InsertPaste(&documentStr
[0], static_cast<int>(documentStr
.size()));
952 pdoc
->EndUndoAction();
957 void ScintillaWin::EscapeHanja() {
958 // The candidate box pops up to user to select a hanja.
959 // It comes into WM_IME_COMPOSITION with GCS_RESULTSTR.
960 // The existing hangul or hanja is replaced with it.
961 if (sel
.Count() > 1) {
962 return; // Do not allow multi carets.
964 Sci::Position currentPos
= CurrentPosition();
965 int oneCharLen
= pdoc
->LenChar(currentPos
);
967 if (oneCharLen
< 2) {
968 return; // No need to handle SBCS.
971 // ImmEscapeW() may overwrite uniChar[] with a null terminated string.
972 // So enlarge it enough to Maximum 4 as in UTF-8.
973 unsigned int const safeLength
= UTF8MaxBytes
+1;
974 std::string
oneChar(safeLength
, '\0');
975 pdoc
->GetCharRange(&oneChar
[0], currentPos
, oneCharLen
);
977 std::wstring uniChar
= StringDecode(oneChar
, CodePageOfDocument());
979 IMContext
imc(MainHWND());
981 // Set the candidate box position since IME may show it.
982 SetCandidateWindowPos();
983 // IME_ESC_HANJA_MODE appears to receive the first character only.
984 if (ImmEscapeW(GetKeyboardLayout(0), imc
.hIMC
, IME_ESC_HANJA_MODE
, &uniChar
[0])) {
985 SetSelection(currentPos
, currentPos
+ oneCharLen
);
990 void ScintillaWin::ToggleHanja() {
991 // If selection, convert every hanja to hangul within the main range.
992 // If no selection, commit to IME.
993 if (sel
.Count() > 1) {
994 return; // Do not allow multi carets.
1000 SelectionToHangul();
1006 std::vector
<int> MapImeIndicators(std::vector
<BYTE
> inputStyle
) {
1007 std::vector
<int> imeIndicator(inputStyle
.size(), SC_INDICATOR_UNKNOWN
);
1008 for (size_t i
= 0; i
< inputStyle
.size(); i
++) {
1009 switch (static_cast<int>(inputStyle
.at(i
))) {
1011 imeIndicator
[i
] = SC_INDICATOR_INPUT
;
1013 case ATTR_TARGET_NOTCONVERTED
:
1014 case ATTR_TARGET_CONVERTED
:
1015 imeIndicator
[i
] = SC_INDICATOR_TARGET
;
1017 case ATTR_CONVERTED
:
1018 imeIndicator
[i
] = SC_INDICATOR_CONVERTED
;
1021 imeIndicator
[i
] = SC_INDICATOR_UNKNOWN
;
1025 return imeIndicator
;
1030 void ScintillaWin::AddWString(std::wstring wcs
) {
1034 const int codePage
= CodePageOfDocument();
1035 for (size_t i
= 0; i
< wcs
.size(); ) {
1036 const size_t ucWidth
= UTF16CharLength(wcs
[i
]);
1037 const std::wstring
uniChar(wcs
, i
, ucWidth
);
1038 std::string docChar
= StringEncode(uniChar
, codePage
);
1040 AddCharUTF(docChar
.c_str(), static_cast<unsigned int>(docChar
.size()));
1045 sptr_t
ScintillaWin::HandleCompositionInline(uptr_t
, sptr_t lParam
) {
1046 // Copy & paste by johnsonj with a lot of helps of Neil.
1047 // Great thanks for my foreruners, jiniya and BLUEnLIVE.
1049 IMContext
imc(MainHWND());
1052 if (pdoc
->IsReadOnly() || SelectionContainsProtected()) {
1053 ::ImmNotifyIME(imc
.hIMC
, NI_COMPOSITIONSTR
, CPS_CANCEL
, 0);
1057 bool initialCompose
= false;
1058 if (pdoc
->TentativeActive()) {
1059 pdoc
->TentativeUndo();
1061 // No tentative undo means start of this composition so
1062 // fill in any virtual spaces.
1063 initialCompose
= true;
1066 view
.imeCaretBlockOverride
= false;
1068 if (lParam
& GCS_COMPSTR
) {
1069 const std::wstring wcs
= imc
.GetCompositionString(GCS_COMPSTR
);
1070 if ((wcs
.size() == 0) || (wcs
.size() >= maxLenInputIME
)) {
1071 ShowCaretAtCurrentPosition();
1076 ClearBeforeTentativeStart();
1077 pdoc
->TentativeStart(); // TentativeActive from now on.
1079 std::vector
<int> imeIndicator
= MapImeIndicators(imc
.GetImeAttributes());
1081 const bool tmpRecordingMacro
= recordingMacro
;
1082 recordingMacro
= false;
1083 const int codePage
= CodePageOfDocument();
1084 for (size_t i
= 0; i
< wcs
.size(); ) {
1085 const size_t ucWidth
= UTF16CharLength(wcs
[i
]);
1086 const std::wstring
uniChar(wcs
, i
, ucWidth
);
1087 std::string docChar
= StringEncode(uniChar
, codePage
);
1089 AddCharUTF(docChar
.c_str(), static_cast<unsigned int>(docChar
.size()));
1091 DrawImeIndicator(imeIndicator
[i
], static_cast<unsigned int>(docChar
.size()));
1094 recordingMacro
= tmpRecordingMacro
;
1096 // Move IME caret from current last position to imeCaretPos.
1097 const int imeEndToImeCaretU16
= imc
.GetImeCaretPos() - static_cast<unsigned int>(wcs
.size());
1098 Sci::Position imeCaretPosDoc
= pdoc
->GetRelativePositionUTF16(CurrentPosition(), imeEndToImeCaretU16
);
1100 MoveImeCarets(- CurrentPosition() + imeCaretPosDoc
);
1103 view
.imeCaretBlockOverride
= true;
1105 } else if (lParam
& GCS_RESULTSTR
) {
1106 AddWString(imc
.GetCompositionString(GCS_RESULTSTR
));
1108 EnsureCaretVisible();
1109 SetCandidateWindowPos();
1110 ShowCaretAtCurrentPosition();
1114 // Translate message IDs from WM_* and EM_* to SCI_* so can partly emulate Windows Edit control
1115 static unsigned int SciMessageFromEM(unsigned int iMessage
) {
1117 case EM_CANPASTE
: return SCI_CANPASTE
;
1118 case EM_CANUNDO
: return SCI_CANUNDO
;
1119 case EM_EMPTYUNDOBUFFER
: return SCI_EMPTYUNDOBUFFER
;
1120 case EM_FINDTEXTEX
: return SCI_FINDTEXT
;
1121 case EM_FORMATRANGE
: return SCI_FORMATRANGE
;
1122 case EM_GETFIRSTVISIBLELINE
: return SCI_GETFIRSTVISIBLELINE
;
1123 case EM_GETLINECOUNT
: return SCI_GETLINECOUNT
;
1124 case EM_GETSELTEXT
: return SCI_GETSELTEXT
;
1125 case EM_GETTEXTRANGE
: return SCI_GETTEXTRANGE
;
1126 case EM_HIDESELECTION
: return SCI_HIDESELECTION
;
1127 case EM_LINEINDEX
: return SCI_POSITIONFROMLINE
;
1128 case EM_LINESCROLL
: return SCI_LINESCROLL
;
1129 case EM_REPLACESEL
: return SCI_REPLACESEL
;
1130 case EM_SCROLLCARET
: return SCI_SCROLLCARET
;
1131 case EM_SETREADONLY
: return SCI_SETREADONLY
;
1132 case WM_CLEAR
: return SCI_CLEAR
;
1133 case WM_COPY
: return SCI_COPY
;
1134 case WM_CUT
: return SCI_CUT
;
1135 case WM_SETTEXT
: return SCI_SETTEXT
;
1136 case WM_PASTE
: return SCI_PASTE
;
1137 case WM_UNDO
: return SCI_UNDO
;
1142 UINT
CodePageFromCharSet(DWORD characterSet
, UINT documentCodePage
) {
1143 if (documentCodePage
== SC_CP_UTF8
) {
1146 switch (characterSet
) {
1147 case SC_CHARSET_ANSI
: return 1252;
1148 case SC_CHARSET_DEFAULT
: return documentCodePage
? documentCodePage
: 1252;
1149 case SC_CHARSET_BALTIC
: return 1257;
1150 case SC_CHARSET_CHINESEBIG5
: return 950;
1151 case SC_CHARSET_EASTEUROPE
: return 1250;
1152 case SC_CHARSET_GB2312
: return 936;
1153 case SC_CHARSET_GREEK
: return 1253;
1154 case SC_CHARSET_HANGUL
: return 949;
1155 case SC_CHARSET_MAC
: return 10000;
1156 case SC_CHARSET_OEM
: return 437;
1157 case SC_CHARSET_RUSSIAN
: return 1251;
1158 case SC_CHARSET_SHIFTJIS
: return 932;
1159 case SC_CHARSET_TURKISH
: return 1254;
1160 case SC_CHARSET_JOHAB
: return 1361;
1161 case SC_CHARSET_HEBREW
: return 1255;
1162 case SC_CHARSET_ARABIC
: return 1256;
1163 case SC_CHARSET_VIETNAMESE
: return 1258;
1164 case SC_CHARSET_THAI
: return 874;
1165 case SC_CHARSET_8859_15
: return 28605;
1167 case SC_CHARSET_CYRILLIC
: return documentCodePage
;
1168 case SC_CHARSET_SYMBOL
: return documentCodePage
;
1170 return documentCodePage
;
1173 UINT
ScintillaWin::CodePageOfDocument() const {
1174 return CodePageFromCharSet(vs
.styles
[STYLE_DEFAULT
].characterSet
, pdoc
->dbcsCodePage
);
1177 sptr_t
ScintillaWin::GetTextLength() {
1178 if (pdoc
->Length() == 0)
1180 std::vector
<char> docBytes(pdoc
->Length(), '\0');
1181 pdoc
->GetCharRange(&docBytes
[0], 0, pdoc
->Length());
1182 if (IsUnicodeMode()) {
1183 return UTF16Length(&docBytes
[0], docBytes
.size());
1185 return ::MultiByteToWideChar(CodePageOfDocument(), 0, &docBytes
[0],
1186 static_cast<int>(docBytes
.size()), NULL
, 0);
1190 sptr_t
ScintillaWin::GetText(uptr_t wParam
, sptr_t lParam
) {
1191 wchar_t *ptr
= reinterpret_cast<wchar_t *>(lParam
);
1192 if (pdoc
->Length() == 0) {
1196 std::vector
<char> docBytes(pdoc
->Length(), '\0');
1197 pdoc
->GetCharRange(&docBytes
[0], 0, pdoc
->Length());
1198 if (IsUnicodeMode()) {
1199 size_t lengthUTF16
= UTF16Length(&docBytes
[0], static_cast<unsigned int>(docBytes
.size()));
1204 size_t uLen
= UTF16FromUTF8(&docBytes
[0], docBytes
.size(),
1210 // Convert to Unicode using the current Scintilla code page
1211 const UINT cpSrc
= CodePageOfDocument();
1212 int lengthUTF16
= ::MultiByteToWideChar(cpSrc
, 0, &docBytes
[0],
1213 static_cast<int>(docBytes
.size()), NULL
, 0);
1214 if (lengthUTF16
>= static_cast<int>(wParam
))
1215 lengthUTF16
= static_cast<int>(wParam
)-1;
1216 ::MultiByteToWideChar(cpSrc
, 0, &docBytes
[0],
1217 static_cast<int>(docBytes
.size()),
1219 ptr
[lengthUTF16
] = L
'\0';
1224 sptr_t
ScintillaWin::WndProc(unsigned int iMessage
, uptr_t wParam
, sptr_t lParam
) {
1226 //Platform::DebugPrintf("S M:%x WP:%x L:%x\n", iMessage, wParam, lParam);
1227 iMessage
= SciMessageFromEM(iMessage
);
1231 ctrlID
= ::GetDlgCtrlID(static_cast<HWND
>(wMain
.GetID()));
1232 // Get Intellimouse scroll line parameters
1233 GetIntelliMouseParameters();
1234 ::RegisterDragDrop(MainHWND(), reinterpret_cast<IDropTarget
*>(&dt
));
1238 Command(LoWord(wParam
));
1242 return WndPaint(wParam
);
1244 case WM_PRINTCLIENT
: {
1245 HDC hdc
= reinterpret_cast<HDC
>(wParam
);
1246 if (!IsCompatibleDC(hdc
)) {
1247 return ::DefWindowProc(MainHWND(), iMessage
, wParam
, lParam
);
1254 ScrollMessage(wParam
);
1258 HorizontalScrollMessage(wParam
);
1262 #if defined(USE_D2D)
1263 if (paintState
== notPainting
) {
1266 renderTargetValid
= false;
1269 //Platform::DebugPrintf("Scintilla WM_SIZE %d %d\n", LoWord(lParam), HiWord(lParam));
1275 if (!mouseWheelCaptures
) {
1276 // if the mouse wheel is not captured, test if the mouse
1277 // pointer is over the editor window and if not, don't
1278 // handle the message but pass it on.
1280 GetWindowRect(MainHWND(), &rc
);
1282 pt
.x
= GET_X_LPARAM(lParam
);
1283 pt
.y
= GET_Y_LPARAM(lParam
);
1284 if (!PtInRect(&rc
, pt
))
1285 return ::DefWindowProc(MainHWND(), iMessage
, wParam
, lParam
);
1287 // if autocomplete list active then send mousewheel message to it
1289 HWND hWnd
= static_cast<HWND
>(ac
.lb
->GetID());
1290 ::SendMessage(hWnd
, iMessage
, wParam
, lParam
);
1294 // Don't handle datazoom.
1295 // (A good idea for datazoom would be to "fold" or "unfold" details.
1296 // i.e. if datazoomed out only class structures are visible, when datazooming in the control
1297 // structures appear, then eventually the individual statements...)
1298 if (wParam
& MK_SHIFT
) {
1299 return ::DefWindowProc(MainHWND(), iMessage
, wParam
, lParam
);
1302 // Either SCROLL or ZOOM. We handle the wheel steppings calculation
1303 wheelDelta
-= static_cast<short>(HiWord(wParam
));
1304 if (abs(wheelDelta
) >= WHEEL_DELTA
&& linesPerScroll
> 0) {
1305 Sci::Line linesToScroll
= linesPerScroll
;
1306 if (linesPerScroll
== WHEEL_PAGESCROLL
)
1307 linesToScroll
= LinesOnScreen() - 1;
1308 if (linesToScroll
== 0) {
1311 linesToScroll
*= (wheelDelta
/ WHEEL_DELTA
);
1312 if (wheelDelta
>= 0)
1313 wheelDelta
= wheelDelta
% WHEEL_DELTA
;
1315 wheelDelta
= - (-wheelDelta
% WHEEL_DELTA
);
1317 if (wParam
& MK_CONTROL
) {
1318 // Zoom! We play with the font sizes in the styles.
1319 // Number of steps/line is ignored, we just care if sizing up or down
1320 if (linesToScroll
< 0) {
1321 KeyCommand(SCI_ZOOMIN
);
1323 KeyCommand(SCI_ZOOMOUT
);
1327 ScrollTo(topLine
+ linesToScroll
);
1333 if (wParam
== idleTimerID
&& idler
.state
) {
1334 SendMessage(MainHWND(), SC_WIN_IDLE
, 0, 1);
1336 TickFor(static_cast<TickReason
>(wParam
- fineTimerStart
));
1341 // wParam=dwTickCountInitial, or 0 to initialize. lParam=bSkipUserInputTest
1343 if (lParam
|| (WAIT_TIMEOUT
== MsgWaitForMultipleObjects(0, 0, 0, 0, QS_INPUT
|QS_HOTKEY
))) {
1345 // User input was given priority above, but all events do get a turn. Other
1346 // messages, notifications, etc. will get interleaved with the idle messages.
1348 // However, some things like WM_PAINT are a lower priority, and will not fire
1349 // when there's a message posted. So, several times a second, we stop and let
1350 // the low priority events have a turn (after which the timer will fire again).
1352 // Suppress a warning from Code Analysis that the GetTickCount function
1353 // wraps after 49 days. The WM_TIMER will kick off another SC_WIN_IDLE
1356 #pragma warning(suppress: 28159)
1358 const DWORD dwCurrent
= GetTickCount();
1359 const DWORD dwStart
= wParam
? static_cast<DWORD
>(wParam
) : dwCurrent
;
1360 const DWORD maxWorkTime
= 50;
1362 if (dwCurrent
>= dwStart
&& dwCurrent
> maxWorkTime
&& dwCurrent
- maxWorkTime
< dwStart
)
1363 PostMessage(MainHWND(), SC_WIN_IDLE
, dwStart
, 0);
1371 case WM_GETMINMAXINFO
:
1372 return ::DefWindowProc(MainHWND(), iMessage
, wParam
, lParam
);
1374 case WM_LBUTTONDOWN
: {
1375 // For IME, set the composition string as the result string.
1376 IMContext
imc(MainHWND());
1377 ::ImmNotifyIME(imc
.hIMC
, NI_COMPOSITIONSTR
, CPS_COMPLETE
, 0);
1379 //Platform::DebugPrintf("Buttdown %d %x %x %x %x %x\n",iMessage, wParam, lParam,
1380 // Platform::IsKeyDown(VK_SHIFT),
1381 // Platform::IsKeyDown(VK_CONTROL),
1382 // Platform::IsKeyDown(VK_MENU));
1383 ::SetFocus(MainHWND());
1384 ButtonDown(Point::FromLong(static_cast<long>(lParam
)), ::GetMessageTime(),
1385 (wParam
& MK_SHIFT
) != 0,
1386 (wParam
& MK_CONTROL
) != 0,
1387 Platform::IsKeyDown(VK_MENU
));
1391 case WM_MOUSEMOVE
: {
1392 const Point pt
= Point::FromLong(static_cast<long>(lParam
));
1394 // Windows might send WM_MOUSEMOVE even though the mouse has not been moved:
1395 // http://blogs.msdn.com/b/oldnewthing/archive/2003/10/01/55108.aspx
1396 if (ptMouseLast
.x
!= pt
.x
|| ptMouseLast
.y
!= pt
.y
) {
1397 SetTrackMouseLeaveEvent(true);
1398 ButtonMoveWithModifiers(pt
,
1399 ((wParam
& MK_SHIFT
) != 0 ? SCI_SHIFT
: 0) |
1400 ((wParam
& MK_CONTROL
) != 0 ? SCI_CTRL
: 0) |
1401 (Platform::IsKeyDown(VK_MENU
) ? SCI_ALT
: 0));
1407 SetTrackMouseLeaveEvent(false);
1409 return ::DefWindowProc(MainHWND(), iMessage
, wParam
, lParam
);
1412 ButtonUp(Point::FromLong(static_cast<long>(lParam
)),
1414 (wParam
& MK_CONTROL
) != 0);
1417 case WM_RBUTTONDOWN
: {
1418 ::SetFocus(MainHWND());
1419 Point pt
= Point::FromLong(static_cast<long>(lParam
));
1420 if (!PointInSelection(pt
)) {
1422 SetEmptySelection(PositionFromLocation(Point::FromLong(static_cast<long>(lParam
))));
1425 RightButtonDownWithModifiers(pt
, ::GetMessageTime(), ModifierFlags((wParam
& MK_SHIFT
) != 0,
1426 (wParam
& MK_CONTROL
) != 0,
1427 Platform::IsKeyDown(VK_MENU
)));
1432 if (LoWord(lParam
) == HTCLIENT
) {
1433 if (inDragDrop
== ddDragging
) {
1434 DisplayCursor(Window::cursorUp
);
1436 // Display regular (drag) cursor over selection
1438 if (0 != ::GetCursorPos(&pt
)) {
1439 ::ScreenToClient(MainHWND(), &pt
);
1440 if (PointInSelMargin(PointFromPOINT(pt
))) {
1441 DisplayCursor(GetMarginCursor(PointFromPOINT(pt
)));
1442 } else if (PointInSelection(PointFromPOINT(pt
)) && !SelectionEmpty()) {
1443 DisplayCursor(Window::cursorArrow
);
1444 } else if (PointIsHotspot(PointFromPOINT(pt
))) {
1445 DisplayCursor(Window::cursorHand
);
1447 DisplayCursor(Window::cursorText
);
1453 return ::DefWindowProc(MainHWND(), iMessage
, wParam
, lParam
);
1457 if (((wParam
>= 128) || !iscntrl(static_cast<int>(wParam
))) || !lastKeyDownConsumed
) {
1458 wchar_t wcs
[3] = {static_cast<wchar_t>(wParam
), 0};
1459 unsigned int wclen
= 1;
1460 if (IS_HIGH_SURROGATE(wcs
[0])) {
1461 // If this is a high surrogate character, we need a second one
1462 lastHighSurrogateChar
= wcs
[0];
1464 } else if (IS_LOW_SURROGATE(wcs
[0])) {
1466 wcs
[0] = lastHighSurrogateChar
;
1467 lastHighSurrogateChar
= 0;
1470 AddCharUTF16(wcs
, wclen
);
1475 if (wParam
== UNICODE_NOCHAR
) {
1477 } else if (lastKeyDownConsumed
) {
1480 wchar_t wcs
[3] = {0};
1481 unsigned int wclen
= UTF16FromUTF32Character(static_cast<unsigned int>(wParam
), wcs
);
1482 AddCharUTF16(wcs
, wclen
);
1488 //Platform::DebugPrintf("S keydown %d %x %x %x %x\n",iMessage, wParam, lParam, ::IsKeyDown(VK_SHIFT), ::IsKeyDown(VK_CONTROL));
1489 lastKeyDownConsumed
= false;
1490 const int ret
= KeyDown(KeyTranslate(static_cast<int>(wParam
)),
1491 Platform::IsKeyDown(VK_SHIFT
),
1492 Platform::IsKeyDown(VK_CONTROL
),
1493 Platform::IsKeyDown(VK_MENU
),
1494 &lastKeyDownConsumed
);
1495 if (!ret
&& !lastKeyDownConsumed
) {
1496 return ::DefWindowProc(MainHWND(), iMessage
, wParam
, lParam
);
1501 case WM_IME_KEYDOWN
: {
1502 if (wParam
== VK_HANJA
) {
1505 return ::DefWindowProc(MainHWND(), iMessage
, wParam
, lParam
);
1508 case WM_IME_REQUEST
: {
1509 if (wParam
== IMR_RECONVERTSTRING
) {
1510 return ImeOnReconvert(lParam
);
1512 return ::DefWindowProc(MainHWND(), iMessage
, wParam
, lParam
);
1516 //Platform::DebugPrintf("S keyup %d %x %x\n",iMessage, wParam, lParam);
1517 return ::DefWindowProc(MainHWND(), iMessage
, wParam
, lParam
);
1519 case WM_SETTINGCHANGE
:
1520 //Platform::DebugPrintf("Setting Changed\n");
1521 InvalidateStyleData();
1522 // Get Intellimouse scroll line parameters
1523 GetIntelliMouseParameters();
1527 return DLGC_HASSETSEL
| DLGC_WANTALLKEYS
;
1529 case WM_KILLFOCUS
: {
1530 HWND wOther
= reinterpret_cast<HWND
>(wParam
);
1531 HWND wThis
= MainHWND();
1532 const HWND wCT
= static_cast<HWND
>(ct
.wCallTip
.GetID());
1534 !(::IsChild(wThis
, wOther
) || (wOther
== wCT
))) {
1535 SetFocusState(false);
1536 DestroySystemCaret();
1538 // Explicitly complete any IME composition
1539 IMContext
imc(MainHWND());
1541 ::ImmNotifyIME(imc
.hIMC
, NI_COMPOSITIONSTR
, CPS_COMPLETE
, 0);
1547 SetFocusState(true);
1548 DestroySystemCaret();
1549 CreateSystemCaret();
1552 case WM_SYSCOLORCHANGE
:
1553 //Platform::DebugPrintf("Setting Changed\n");
1554 InvalidateStyleData();
1557 case WM_IME_STARTCOMPOSITION
: // dbcs
1558 if (KoreanIME() || imeInteraction
== imeInline
) {
1561 ImeStartComposition();
1562 return ::DefWindowProc(MainHWND(), iMessage
, wParam
, lParam
);
1565 case WM_IME_ENDCOMPOSITION
: // dbcs
1566 ImeEndComposition();
1567 return ::DefWindowProc(MainHWND(), iMessage
, wParam
, lParam
);
1569 case WM_IME_COMPOSITION
:
1570 if (KoreanIME() || imeInteraction
== imeInline
) {
1571 return HandleCompositionInline(wParam
, lParam
);
1573 return HandleCompositionWindowed(wParam
, lParam
);
1576 case WM_CONTEXTMENU
: {
1577 Point pt
= Point::FromLong(static_cast<long>(lParam
));
1578 POINT rpt
= {static_cast<int>(pt
.x
), static_cast<int>(pt
.y
)};
1579 ::ScreenToClient(MainHWND(), &rpt
);
1580 const Point ptClient
= PointFromPOINT(rpt
);
1581 if (ShouldDisplayPopup(ptClient
)) {
1582 if ((pt
.x
== -1) && (pt
.y
== -1)) {
1583 // Caused by keyboard so display menu near caret
1584 pt
= PointMainCaret();
1585 POINT spt
= {static_cast<int>(pt
.x
), static_cast<int>(pt
.y
)};
1586 ::ClientToScreen(MainHWND(), &spt
);
1587 pt
= PointFromPOINT(spt
);
1593 return ::DefWindowProc(MainHWND(), iMessage
, wParam
, lParam
);
1595 case WM_INPUTLANGCHANGE
:
1596 //::SetThreadLocale(LOWORD(lParam));
1597 return ::DefWindowProc(MainHWND(), iMessage
, wParam
, lParam
);
1599 case WM_INPUTLANGCHANGEREQUEST
:
1600 return ::DefWindowProc(MainHWND(), iMessage
, wParam
, lParam
);
1603 return 1; // Avoid any background erasure as whole window painted.
1605 case WM_CAPTURECHANGED
:
1606 capturedMouse
= false;
1609 case WM_IME_SETCONTEXT
:
1610 if (KoreanIME() || imeInteraction
== imeInline
) {
1612 LPARAM NoImeWin
= lParam
;
1613 NoImeWin
= NoImeWin
& (~ISC_SHOWUICOMPOSITIONWINDOW
);
1614 return ::DefWindowProc(MainHWND(), iMessage
, wParam
, NoImeWin
);
1617 return ::DefWindowProc(MainHWND(), iMessage
, wParam
, lParam
);
1619 // These are not handled in Scintilla and its faster to dispatch them here.
1620 // Also moves time out to here so profile doesn't count lots of empty message calls.
1623 case WM_MOUSEACTIVATE
:
1627 case WM_NCMOUSEMOVE
:
1628 case WM_NCLBUTTONDOWN
:
1631 case WM_WINDOWPOSCHANGING
:
1632 case WM_WINDOWPOSCHANGED
:
1633 return ::DefWindowProc(MainHWND(), iMessage
, wParam
, lParam
);
1635 case WM_GETTEXTLENGTH
:
1636 return GetTextLength();
1639 return GetText(wParam
, lParam
);
1641 case EM_LINEFROMCHAR
:
1642 if (static_cast<int>(wParam
) < 0) {
1643 wParam
= SelectionStart().Position();
1645 return pdoc
->LineFromPosition(static_cast<int>(wParam
));
1647 case EM_EXLINEFROMCHAR
:
1648 return pdoc
->LineFromPosition(static_cast<int>(lParam
));
1652 *reinterpret_cast<int *>(wParam
) = SelectionStart().Position();
1655 *reinterpret_cast<int *>(lParam
) = SelectionEnd().Position();
1657 return MAKELONG(SelectionStart().Position(), SelectionEnd().Position());
1663 Sci_CharacterRange
*pCR
= reinterpret_cast<Sci_CharacterRange
*>(lParam
);
1664 pCR
->cpMin
= SelectionStart().Position();
1665 pCR
->cpMax
= SelectionEnd().Position();
1670 Sci::Position nStart
= static_cast<Sci::Position
>(wParam
);
1671 Sci::Position nEnd
= static_cast<Sci::Position
>(lParam
);
1672 if (nStart
== 0 && nEnd
== -1) {
1673 nEnd
= pdoc
->Length();
1676 nStart
= nEnd
; // Remove selection
1678 SetSelection(nEnd
, nStart
);
1679 EnsureCaretVisible();
1687 Sci_CharacterRange
*pCR
= reinterpret_cast<Sci_CharacterRange
*>(lParam
);
1688 sel
.selType
= Selection::selStream
;
1689 if (pCR
->cpMin
== 0 && pCR
->cpMax
== -1) {
1690 SetSelection(pCR
->cpMin
, pdoc
->Length());
1692 SetSelection(pCR
->cpMin
, pCR
->cpMax
);
1694 EnsureCaretVisible();
1695 return pdoc
->LineFromPosition(SelectionStart().Position());
1698 case SCI_GETDIRECTFUNCTION
:
1699 return reinterpret_cast<sptr_t
>(DirectFunction
);
1701 case SCI_GETDIRECTPOINTER
:
1702 return reinterpret_cast<sptr_t
>(this);
1705 ::SetFocus(MainHWND());
1708 #ifdef INCLUDE_DEPRECATED_FEATURES
1709 case SCI_SETKEYSUNICODE
:
1712 case SCI_GETKEYSUNICODE
:
1716 case SCI_SETTECHNOLOGY
:
1717 if ((wParam
== SC_TECHNOLOGY_DEFAULT
) ||
1718 (wParam
== SC_TECHNOLOGY_DIRECTWRITERETAIN
) ||
1719 (wParam
== SC_TECHNOLOGY_DIRECTWRITEDC
) ||
1720 (wParam
== SC_TECHNOLOGY_DIRECTWRITE
)) {
1721 if (technology
!= static_cast<int>(wParam
)) {
1722 if (static_cast<int>(wParam
) > SC_TECHNOLOGY_DEFAULT
) {
1723 #if defined(USE_D2D)
1725 // Failed to load Direct2D or DirectWrite so no effect
1731 #if defined(USE_D2D)
1734 technology
= static_cast<int>(wParam
);
1735 // Invalidate all cached information including layout.
1737 InvalidateStyleRedraw();
1743 case SCI_LOADLEXERLIBRARY
:
1744 LexerManager::GetInstance()->Load(reinterpret_cast<const char *>(lParam
));
1748 case SCI_TARGETASUTF8
:
1749 return TargetAsUTF8(reinterpret_cast<char*>(lParam
));
1751 case SCI_ENCODEDFROMUTF8
:
1752 return EncodedFromUTF8(reinterpret_cast<char*>(wParam
),
1753 reinterpret_cast<char*>(lParam
));
1756 return ScintillaBase::WndProc(iMessage
, wParam
, lParam
);
1758 } catch (std::bad_alloc
&) {
1759 errorStatus
= SC_STATUS_BADALLOC
;
1761 errorStatus
= SC_STATUS_FAILURE
;
1766 bool ScintillaWin::ValidCodePage(int codePage
) const {
1767 return codePage
== 0 || codePage
== SC_CP_UTF8
||
1768 codePage
== 932 || codePage
== 936 || codePage
== 949 ||
1769 codePage
== 950 || codePage
== 1361;
1772 sptr_t
ScintillaWin::DefWndProc(unsigned int iMessage
, uptr_t wParam
, sptr_t lParam
) {
1773 return ::DefWindowProc(MainHWND(), iMessage
, wParam
, lParam
);
1777 * Report that this Editor subclass has a working implementation of FineTickerStart.
1779 bool ScintillaWin::FineTickerAvailable() {
1783 bool ScintillaWin::FineTickerRunning(TickReason reason
) {
1784 return timers
[reason
] != 0;
1787 void ScintillaWin::FineTickerStart(TickReason reason
, int millis
, int tolerance
) {
1788 FineTickerCancel(reason
);
1789 if (SetCoalescableTimerFn
&& tolerance
) {
1790 timers
[reason
] = SetCoalescableTimerFn(MainHWND(), fineTimerStart
+ reason
, millis
, NULL
, tolerance
);
1792 timers
[reason
] = ::SetTimer(MainHWND(), fineTimerStart
+ reason
, millis
, NULL
);
1796 void ScintillaWin::FineTickerCancel(TickReason reason
) {
1797 if (timers
[reason
]) {
1798 ::KillTimer(MainHWND(), timers
[reason
]);
1804 bool ScintillaWin::SetIdle(bool on
) {
1805 // On Win32 the Idler is implemented as a Timer on the Scintilla window. This
1806 // takes advantage of the fact that WM_TIMER messages are very low priority,
1807 // and are only posted when the message queue is empty, i.e. during idle time.
1808 if (idler
.state
!= on
) {
1810 idler
.idlerID
= ::SetTimer(MainHWND(), idleTimerID
, 10, NULL
)
1811 ? reinterpret_cast<IdlerID
>(idleTimerID
) : 0;
1813 ::KillTimer(MainHWND(), reinterpret_cast<uptr_t
>(idler
.idlerID
));
1816 idler
.state
= idler
.idlerID
!= 0;
1821 void ScintillaWin::SetMouseCapture(bool on
) {
1822 if (mouseDownCaptures
) {
1824 ::SetCapture(MainHWND());
1832 bool ScintillaWin::HaveMouseCapture() {
1833 // Cannot just see if GetCapture is this window as the scroll bar also sets capture for the window
1834 return capturedMouse
;
1835 //return capturedMouse && (::GetCapture() == MainHWND());
1838 void ScintillaWin::SetTrackMouseLeaveEvent(bool on
) {
1839 if (on
&& !trackedMouseLeave
) {
1840 TRACKMOUSEEVENT tme
;
1841 tme
.cbSize
= sizeof(tme
);
1842 tme
.dwFlags
= TME_LEAVE
;
1843 tme
.hwndTrack
= MainHWND();
1844 tme
.dwHoverTime
= HOVER_DEFAULT
; // Unused but triggers Dr. Memory if not initialized
1845 TrackMouseEvent(&tme
);
1847 trackedMouseLeave
= on
;
1850 bool ScintillaWin::PaintContains(PRectangle rc
) {
1851 if (paintState
== painting
) {
1852 return BoundsContains(rcPaint
, hRgnUpdate
, rc
);
1857 void ScintillaWin::ScrollText(Sci::Line
/* linesToMove */) {
1858 //Platform::DebugPrintf("ScintillaWin::ScrollText %d\n", linesToMove);
1859 //::ScrollWindow(MainHWND(), 0,
1860 // vs.lineHeight * linesToMove, 0, 0);
1861 //::UpdateWindow(MainHWND());
1863 UpdateSystemCaret();
1866 void ScintillaWin::NotifyCaretMove() {
1867 NotifyWinEvent(EVENT_OBJECT_LOCATIONCHANGE
, MainHWND(), OBJID_CARET
, CHILDID_SELF
);
1870 void ScintillaWin::UpdateSystemCaret() {
1872 if (HasCaretSizeChanged()) {
1873 DestroySystemCaret();
1874 CreateSystemCaret();
1876 Point pos
= PointMainCaret();
1877 ::SetCaretPos(static_cast<int>(pos
.x
), static_cast<int>(pos
.y
));
1881 int ScintillaWin::SetScrollInfo(int nBar
, LPCSCROLLINFO lpsi
, BOOL bRedraw
) {
1882 return ::SetScrollInfo(MainHWND(), nBar
, lpsi
, bRedraw
);
1885 bool ScintillaWin::GetScrollInfo(int nBar
, LPSCROLLINFO lpsi
) {
1886 return ::GetScrollInfo(MainHWND(), nBar
, lpsi
) ? true : false;
1889 // Change the scroll position but avoid repaint if changing to same value
1890 void ScintillaWin::ChangeScrollPos(int barType
, Sci::Position pos
) {
1892 sizeof(sci
), 0, 0, 0, 0, 0, 0
1894 sci
.fMask
= SIF_POS
;
1895 GetScrollInfo(barType
, &sci
);
1896 if (sci
.nPos
!= pos
) {
1899 SetScrollInfo(barType
, &sci
, TRUE
);
1903 void ScintillaWin::SetVerticalScrollPos() {
1904 ChangeScrollPos(SB_VERT
, topLine
);
1907 void ScintillaWin::SetHorizontalScrollPos() {
1908 ChangeScrollPos(SB_HORZ
, xOffset
);
1911 bool ScintillaWin::ModifyScrollBars(Sci::Line nMax
, Sci::Line nPage
) {
1912 bool modified
= false;
1914 sizeof(sci
), 0, 0, 0, 0, 0, 0
1916 sci
.fMask
= SIF_PAGE
| SIF_RANGE
;
1917 GetScrollInfo(SB_VERT
, &sci
);
1918 Sci::Line vertEndPreferred
= nMax
;
1919 if (!verticalScrollBarVisible
)
1920 nPage
= vertEndPreferred
+ 1;
1921 if ((sci
.nMin
!= 0) ||
1922 (sci
.nMax
!= vertEndPreferred
) ||
1923 (sci
.nPage
!= static_cast<unsigned int>(nPage
)) ||
1925 sci
.fMask
= SIF_PAGE
| SIF_RANGE
;
1927 sci
.nMax
= vertEndPreferred
;
1931 SetScrollInfo(SB_VERT
, &sci
, TRUE
);
1935 const PRectangle rcText
= GetTextRectangle();
1936 int horizEndPreferred
= scrollWidth
;
1937 if (horizEndPreferred
< 0)
1938 horizEndPreferred
= 0;
1939 int pageWidth
= static_cast<int>(rcText
.Width());
1940 if (!horizontalScrollBarVisible
|| Wrapping())
1941 pageWidth
= horizEndPreferred
+ 1;
1942 sci
.fMask
= SIF_PAGE
| SIF_RANGE
;
1943 GetScrollInfo(SB_HORZ
, &sci
);
1944 if ((sci
.nMin
!= 0) ||
1945 (sci
.nMax
!= horizEndPreferred
) ||
1946 (sci
.nPage
!= static_cast<unsigned int>(pageWidth
)) ||
1948 sci
.fMask
= SIF_PAGE
| SIF_RANGE
;
1950 sci
.nMax
= horizEndPreferred
;
1951 sci
.nPage
= pageWidth
;
1954 SetScrollInfo(SB_HORZ
, &sci
, TRUE
);
1956 if (scrollWidth
< pageWidth
) {
1957 HorizontalScrollTo(0);
1963 void ScintillaWin::NotifyChange() {
1964 ::SendMessage(::GetParent(MainHWND()), WM_COMMAND
,
1965 MAKELONG(GetCtrlID(), SCEN_CHANGE
),
1966 reinterpret_cast<LPARAM
>(MainHWND()));
1969 void ScintillaWin::NotifyFocus(bool focus
) {
1970 ::SendMessage(::GetParent(MainHWND()), WM_COMMAND
,
1971 MAKELONG(GetCtrlID(), focus
? SCEN_SETFOCUS
: SCEN_KILLFOCUS
),
1972 reinterpret_cast<LPARAM
>(MainHWND()));
1973 Editor::NotifyFocus(focus
);
1976 void ScintillaWin::SetCtrlID(int identifier
) {
1977 ::SetWindowID(static_cast<HWND
>(wMain
.GetID()), identifier
);
1980 int ScintillaWin::GetCtrlID() {
1981 return ::GetDlgCtrlID(static_cast<HWND
>(wMain
.GetID()));
1984 void ScintillaWin::NotifyParent(SCNotification scn
) {
1985 scn
.nmhdr
.hwndFrom
= MainHWND();
1986 scn
.nmhdr
.idFrom
= GetCtrlID();
1987 ::SendMessage(::GetParent(MainHWND()), WM_NOTIFY
,
1988 GetCtrlID(), reinterpret_cast<LPARAM
>(&scn
));
1991 void ScintillaWin::NotifyParent(SCNotification
* scn
) {
1992 scn
->nmhdr
.hwndFrom
= MainHWND();
1993 scn
->nmhdr
.idFrom
= GetCtrlID();
1994 ::SendMessage(::GetParent(MainHWND()), WM_NOTIFY
,
1995 GetCtrlID(), reinterpret_cast<LPARAM
>(scn
));
1998 void ScintillaWin::NotifyDoubleClick(Point pt
, int modifiers
) {
1999 //Platform::DebugPrintf("ScintillaWin Double click 0\n");
2000 ScintillaBase::NotifyDoubleClick(pt
, modifiers
);
2001 // Send myself a WM_LBUTTONDBLCLK, so the container can handle it too.
2002 ::SendMessage(MainHWND(),
2004 (modifiers
& SCI_SHIFT
) ? MK_SHIFT
: 0,
2005 MAKELPARAM(pt
.x
, pt
.y
));
2008 class CaseFolderDBCS
: public CaseFolderTable
{
2009 // Allocate the expandable storage here so that it does not need to be reallocated
2010 // for each call to Fold.
2011 std::vector
<wchar_t> utf16Mixed
;
2012 std::vector
<wchar_t> utf16Folded
;
2015 explicit CaseFolderDBCS(UINT cp_
) : cp(cp_
) {
2018 size_t Fold(char *folded
, size_t sizeFolded
, const char *mixed
, size_t lenMixed
) override
{
2019 if ((lenMixed
== 1) && (sizeFolded
> 0)) {
2020 folded
[0] = mapping
[static_cast<unsigned char>(mixed
[0])];
2023 if (lenMixed
> utf16Mixed
.size()) {
2024 utf16Mixed
.resize(lenMixed
+ 8);
2026 const size_t nUtf16Mixed
= ::MultiByteToWideChar(cp
, 0, mixed
,
2027 static_cast<int>(lenMixed
),
2029 static_cast<int>(utf16Mixed
.size()));
2031 if (nUtf16Mixed
== 0) {
2032 // Failed to convert -> bad input
2037 unsigned int lenFlat
= 0;
2038 for (size_t mixIndex
=0; mixIndex
< nUtf16Mixed
; mixIndex
++) {
2039 if ((lenFlat
+ 20) > utf16Folded
.size())
2040 utf16Folded
.resize(lenFlat
+ 60);
2041 const char *foldedUTF8
= CaseConvert(utf16Mixed
[mixIndex
], CaseConversionFold
);
2043 // Maximum length of a case conversion is 6 bytes, 3 characters
2044 wchar_t wFolded
[20];
2045 const size_t charsConverted
= UTF16FromUTF8(foldedUTF8
,
2047 wFolded
, ELEMENTS(wFolded
));
2048 for (size_t j
=0; j
<charsConverted
; j
++)
2049 utf16Folded
[lenFlat
++] = wFolded
[j
];
2051 utf16Folded
[lenFlat
++] = utf16Mixed
[mixIndex
];
2055 size_t lenOut
= ::WideCharToMultiByte(cp
, 0,
2056 &utf16Folded
[0], lenFlat
,
2059 if (lenOut
< sizeFolded
) {
2060 ::WideCharToMultiByte(cp
, 0,
2061 &utf16Folded
[0], lenFlat
,
2062 folded
, static_cast<int>(lenOut
), NULL
, 0);
2071 CaseFolder
*ScintillaWin::CaseFolderForEncoding() {
2072 UINT cpDest
= CodePageOfDocument();
2073 if (cpDest
== SC_CP_UTF8
) {
2074 return new CaseFolderUnicode();
2076 if (pdoc
->dbcsCodePage
== 0) {
2077 CaseFolderTable
*pcf
= new CaseFolderTable();
2078 pcf
->StandardASCII();
2079 // Only for single byte encodings
2080 UINT cpDoc
= CodePageOfDocument();
2081 for (int i
=0x80; i
<0x100; i
++) {
2082 char sCharacter
[2] = "A";
2083 sCharacter
[0] = static_cast<char>(i
);
2084 wchar_t wCharacter
[20];
2085 const unsigned int lengthUTF16
= ::MultiByteToWideChar(cpDoc
, 0, sCharacter
, 1,
2086 wCharacter
, ELEMENTS(wCharacter
));
2087 if (lengthUTF16
== 1) {
2088 const char *caseFolded
= CaseConvert(wCharacter
[0], CaseConversionFold
);
2091 const size_t charsConverted
= UTF16FromUTF8(caseFolded
,
2093 wLower
, ELEMENTS(wLower
));
2094 if (charsConverted
== 1) {
2095 char sCharacterLowered
[20];
2096 const unsigned int lengthConverted
= ::WideCharToMultiByte(cpDoc
, 0,
2097 wLower
, static_cast<int>(charsConverted
),
2098 sCharacterLowered
, ELEMENTS(sCharacterLowered
), NULL
, 0);
2099 if ((lengthConverted
== 1) && (sCharacter
[0] != sCharacterLowered
[0])) {
2100 pcf
->SetTranslation(sCharacter
[0], sCharacterLowered
[0]);
2108 return new CaseFolderDBCS(cpDest
);
2113 std::string
ScintillaWin::CaseMapString(const std::string
&s
, int caseMapping
) {
2114 if ((s
.size() == 0) || (caseMapping
== cmSame
))
2117 const UINT cpDoc
= CodePageOfDocument();
2118 if (cpDoc
== SC_CP_UTF8
) {
2119 return CaseConvertString(s
, (caseMapping
== cmUpper
) ? CaseConversionUpper
: CaseConversionLower
);
2122 // Change text to UTF-16
2123 const std::wstring wsText
= StringDecode(s
, cpDoc
);
2125 const DWORD mapFlags
= LCMAP_LINGUISTIC_CASING
|
2126 ((caseMapping
== cmUpper
) ? LCMAP_UPPERCASE
: LCMAP_LOWERCASE
);
2129 const std::wstring wsConverted
= StringMapCase(wsText
, mapFlags
);
2131 // Change back to document encoding
2132 std::string sConverted
= StringEncode(wsConverted
, cpDoc
);
2137 void ScintillaWin::Copy() {
2138 //Platform::DebugPrintf("Copy\n");
2140 SelectionText selectedText
;
2141 CopySelectionRange(&selectedText
);
2142 CopyToClipboard(selectedText
);
2146 void ScintillaWin::CopyAllowLine() {
2147 SelectionText selectedText
;
2148 CopySelectionRange(&selectedText
, true);
2149 CopyToClipboard(selectedText
);
2152 bool ScintillaWin::CanPaste() {
2153 if (!Editor::CanPaste())
2155 if (::IsClipboardFormatAvailable(CF_TEXT
))
2157 if (IsUnicodeMode())
2158 return ::IsClipboardFormatAvailable(CF_UNICODETEXT
) != 0;
2162 class GlobalMemory
{
2166 GlobalMemory() : hand(0), ptr(0) {
2168 explicit GlobalMemory(HGLOBAL hand_
) : hand(hand_
), ptr(0) {
2170 ptr
= ::GlobalLock(hand
);
2174 PLATFORM_ASSERT(!ptr
);
2177 void Allocate(size_t bytes
) {
2179 hand
= ::GlobalAlloc(GMEM_MOVEABLE
| GMEM_ZEROINIT
, bytes
);
2181 ptr
= ::GlobalLock(hand
);
2185 PLATFORM_ASSERT(ptr
);
2186 HGLOBAL handCopy
= hand
;
2187 ::GlobalUnlock(hand
);
2192 void SetClip(UINT uFormat
) {
2193 ::SetClipboardData(uFormat
, Unlock());
2195 operator bool() const {
2199 return ::GlobalSize(hand
);
2203 // OpenClipboard may fail if another application has opened the clipboard.
2204 // Try up to 8 times, with an initial delay of 1 ms and an exponential back off
2205 // for a maximum total delay of 127 ms (1+2+4+8+16+32+64).
2206 static bool OpenClipboardRetry(HWND hwnd
) {
2207 for (int attempt
=0; attempt
<8; attempt
++) {
2209 ::Sleep(1 << (attempt
-1));
2211 if (::OpenClipboard(hwnd
)) {
2218 void ScintillaWin::Paste() {
2219 if (!::OpenClipboardRetry(MainHWND())) {
2223 const bool isLine
= SelectionEmpty() &&
2224 (::IsClipboardFormatAvailable(cfLineSelect
) || ::IsClipboardFormatAvailable(cfVSLineTag
));
2225 ClearSelection(multiPasteMode
== SC_MULTIPASTE_EACH
);
2226 bool isRectangular
= (::IsClipboardFormatAvailable(cfColumnSelect
) != 0);
2228 if (!isRectangular
) {
2229 // Evaluate "Borland IDE Block Type" explicitly
2230 GlobalMemory
memBorlandSelection(::GetClipboardData(cfBorlandIDEBlockType
));
2231 if (memBorlandSelection
) {
2232 isRectangular
= (memBorlandSelection
.Size() == 1) && (static_cast<BYTE
*>(memBorlandSelection
.ptr
)[0] == 0x02);
2233 memBorlandSelection
.Unlock();
2236 const PasteShape pasteShape
= isRectangular
? pasteRectangular
: (isLine
? pasteLine
: pasteStream
);
2238 // Always use CF_UNICODETEXT if available
2239 GlobalMemory
memUSelection(::GetClipboardData(CF_UNICODETEXT
));
2240 if (memUSelection
) {
2241 wchar_t *uptr
= static_cast<wchar_t *>(memUSelection
.ptr
);
2244 std::vector
<char> putf
;
2245 // Default Scintilla behaviour in Unicode mode
2246 if (IsUnicodeMode()) {
2247 const size_t bytes
= memUSelection
.Size();
2248 len
= UTF8Length(uptr
, bytes
/ 2);
2249 putf
.resize(len
+ 1);
2250 UTF8FromUTF16(uptr
, bytes
/ 2, &putf
[0], len
);
2252 // CF_UNICODETEXT available, but not in Unicode mode
2253 // Convert from Unicode to current Scintilla code page
2254 UINT cpDest
= CodePageOfDocument();
2255 len
= ::WideCharToMultiByte(cpDest
, 0, uptr
, -1,
2256 NULL
, 0, NULL
, NULL
) - 1; // subtract 0 terminator
2257 putf
.resize(len
+ 1);
2258 ::WideCharToMultiByte(cpDest
, 0, uptr
, -1,
2259 &putf
[0], static_cast<int>(len
) + 1, NULL
, NULL
);
2262 InsertPasteShape(&putf
[0], static_cast<int>(len
), pasteShape
);
2264 memUSelection
.Unlock();
2266 // CF_UNICODETEXT not available, paste ANSI text
2267 GlobalMemory
memSelection(::GetClipboardData(CF_TEXT
));
2269 char *ptr
= static_cast<char *>(memSelection
.ptr
);
2271 const size_t bytes
= memSelection
.Size();
2273 for (size_t i
= 0; i
< bytes
; i
++) {
2274 if ((len
== bytes
) && (0 == ptr
[i
]))
2277 const int ilen
= static_cast<int>(len
);
2279 // In Unicode mode, convert clipboard text to UTF-8
2280 if (IsUnicodeMode()) {
2281 std::vector
<wchar_t> uptr(len
+1);
2283 const size_t ulen
= ::MultiByteToWideChar(CP_ACP
, 0,
2284 ptr
, ilen
, &uptr
[0], ilen
+1);
2286 const size_t mlen
= UTF8Length(&uptr
[0], ulen
);
2287 std::vector
<char> putf(mlen
+1);
2288 UTF8FromUTF16(&uptr
[0], ulen
, &putf
[0], mlen
);
2290 InsertPasteShape(&putf
[0], static_cast<int>(mlen
), pasteShape
);
2292 InsertPasteShape(ptr
, ilen
, pasteShape
);
2295 memSelection
.Unlock();
2302 void ScintillaWin::CreateCallTipWindow(PRectangle
) {
2303 if (!ct
.wCallTip
.Created()) {
2304 ct
.wCallTip
= ::CreateWindow(callClassName
, TEXT("ACallTip"),
2305 WS_POPUP
, 100, 100, 150, 20,
2307 GetWindowInstance(MainHWND()),
2309 ct
.wDraw
= ct
.wCallTip
;
2313 void ScintillaWin::AddToPopUp(const char *label
, int cmd
, bool enabled
) {
2314 HMENU hmenuPopup
= static_cast<HMENU
>(popup
.GetID());
2316 ::AppendMenuA(hmenuPopup
, MF_SEPARATOR
, 0, "");
2318 ::AppendMenuA(hmenuPopup
, MF_STRING
, cmd
, label
);
2320 ::AppendMenuA(hmenuPopup
, MF_STRING
| MF_DISABLED
| MF_GRAYED
, cmd
, label
);
2323 void ScintillaWin::ClaimSelection() {
2324 // Windows does not have a primary selection
2327 /// Implement IUnknown
2329 STDMETHODIMP_(ULONG
)FormatEnumerator_AddRef(FormatEnumerator
*fe
);
2330 STDMETHODIMP
FormatEnumerator_QueryInterface(FormatEnumerator
*fe
, REFIID riid
, PVOID
*ppv
) {
2331 //Platform::DebugPrintf("EFE QI");
2333 if (riid
== IID_IUnknown
)
2334 *ppv
= reinterpret_cast<IEnumFORMATETC
*>(fe
);
2335 if (riid
== IID_IEnumFORMATETC
)
2336 *ppv
= reinterpret_cast<IEnumFORMATETC
*>(fe
);
2338 return E_NOINTERFACE
;
2339 FormatEnumerator_AddRef(fe
);
2342 STDMETHODIMP_(ULONG
)FormatEnumerator_AddRef(FormatEnumerator
*fe
) {
2345 STDMETHODIMP_(ULONG
)FormatEnumerator_Release(FormatEnumerator
*fe
) {
2352 /// Implement IEnumFORMATETC
2353 STDMETHODIMP
FormatEnumerator_Next(FormatEnumerator
*fe
, ULONG celt
, FORMATETC
*rgelt
, ULONG
*pceltFetched
) {
2354 if (rgelt
== NULL
) return E_POINTER
;
2355 unsigned int putPos
= 0;
2356 while ((fe
->pos
< fe
->formats
.size()) && (putPos
< celt
)) {
2357 rgelt
->cfFormat
= fe
->formats
[fe
->pos
];
2359 rgelt
->dwAspect
= DVASPECT_CONTENT
;
2361 rgelt
->tymed
= TYMED_HGLOBAL
;
2367 *pceltFetched
= putPos
;
2368 return putPos
? S_OK
: S_FALSE
;
2370 STDMETHODIMP
FormatEnumerator_Skip(FormatEnumerator
*fe
, ULONG celt
) {
2374 STDMETHODIMP
FormatEnumerator_Reset(FormatEnumerator
*fe
) {
2378 STDMETHODIMP
FormatEnumerator_Clone(FormatEnumerator
*fe
, IEnumFORMATETC
**ppenum
) {
2379 FormatEnumerator
*pfe
;
2381 pfe
= new FormatEnumerator(fe
->pos
, &fe
->formats
[0], fe
->formats
.size());
2383 return E_OUTOFMEMORY
;
2385 return FormatEnumerator_QueryInterface(pfe
, IID_IEnumFORMATETC
,
2386 reinterpret_cast<void **>(ppenum
));
2389 static VFunction
*vtFormatEnumerator
[] = {
2390 (VFunction
*)(FormatEnumerator_QueryInterface
),
2391 (VFunction
*)(FormatEnumerator_AddRef
),
2392 (VFunction
*)(FormatEnumerator_Release
),
2393 (VFunction
*)(FormatEnumerator_Next
),
2394 (VFunction
*)(FormatEnumerator_Skip
),
2395 (VFunction
*)(FormatEnumerator_Reset
),
2396 (VFunction
*)(FormatEnumerator_Clone
)
2399 FormatEnumerator::FormatEnumerator(int pos_
, CLIPFORMAT formats_
[], size_t formatsLen_
) {
2400 vtbl
= vtFormatEnumerator
;
2401 ref
= 0; // First QI adds first reference...
2403 formats
.insert(formats
.begin(), formats_
, formats_
+formatsLen_
);
2406 /// Implement IUnknown
2407 STDMETHODIMP
DropSource_QueryInterface(DropSource
*ds
, REFIID riid
, PVOID
*ppv
) {
2408 return ds
->sci
->QueryInterface(riid
, ppv
);
2410 STDMETHODIMP_(ULONG
)DropSource_AddRef(DropSource
*ds
) {
2411 return ds
->sci
->AddRef();
2413 STDMETHODIMP_(ULONG
)DropSource_Release(DropSource
*ds
) {
2414 return ds
->sci
->Release();
2417 /// Implement IDropSource
2418 STDMETHODIMP
DropSource_QueryContinueDrag(DropSource
*, BOOL fEsc
, DWORD grfKeyState
) {
2420 return DRAGDROP_S_CANCEL
;
2421 if (!(grfKeyState
& MK_LBUTTON
))
2422 return DRAGDROP_S_DROP
;
2426 STDMETHODIMP
DropSource_GiveFeedback(DropSource
*, DWORD
) {
2427 return DRAGDROP_S_USEDEFAULTCURSORS
;
2430 static VFunction
*vtDropSource
[] = {
2431 (VFunction
*)(DropSource_QueryInterface
),
2432 (VFunction
*)(DropSource_AddRef
),
2433 (VFunction
*)(DropSource_Release
),
2434 (VFunction
*)(DropSource_QueryContinueDrag
),
2435 (VFunction
*)(DropSource_GiveFeedback
)
2438 DropSource::DropSource() {
2439 vtbl
= vtDropSource
;
2443 /// Implement IUnkown
2444 STDMETHODIMP
DataObject_QueryInterface(DataObject
*pd
, REFIID riid
, PVOID
*ppv
) {
2445 //Platform::DebugPrintf("DO QI %x\n", pd);
2446 return pd
->sci
->QueryInterface(riid
, ppv
);
2448 STDMETHODIMP_(ULONG
)DataObject_AddRef(DataObject
*pd
) {
2449 return pd
->sci
->AddRef();
2451 STDMETHODIMP_(ULONG
)DataObject_Release(DataObject
*pd
) {
2452 return pd
->sci
->Release();
2454 /// Implement IDataObject
2455 STDMETHODIMP
DataObject_GetData(DataObject
*pd
, FORMATETC
*pFEIn
, STGMEDIUM
*pSTM
) {
2456 return pd
->sci
->GetData(pFEIn
, pSTM
);
2459 STDMETHODIMP
DataObject_GetDataHere(DataObject
*, FORMATETC
*, STGMEDIUM
*) {
2460 //Platform::DebugPrintf("DOB GetDataHere\n");
2464 STDMETHODIMP
DataObject_QueryGetData(DataObject
*pd
, FORMATETC
*pFE
) {
2465 if (pd
->sci
->DragIsRectangularOK(pFE
->cfFormat
) &&
2467 (pFE
->dwAspect
& DVASPECT_CONTENT
) != 0 &&
2468 pFE
->lindex
== -1 &&
2469 (pFE
->tymed
& TYMED_HGLOBAL
) != 0
2474 const bool formatOK
= (pFE
->cfFormat
== CF_TEXT
) ||
2475 ((pFE
->cfFormat
== CF_UNICODETEXT
) && pd
->sci
->IsUnicodeMode());
2478 (pFE
->dwAspect
& DVASPECT_CONTENT
) == 0 ||
2479 pFE
->lindex
!= -1 ||
2480 (pFE
->tymed
& TYMED_HGLOBAL
) == 0
2482 //Platform::DebugPrintf("DOB QueryGetData No %x\n",pFE->cfFormat);
2483 //return DATA_E_FORMATETC;
2486 //Platform::DebugPrintf("DOB QueryGetData OK %x\n",pFE->cfFormat);
2490 STDMETHODIMP
DataObject_GetCanonicalFormatEtc(DataObject
*pd
, FORMATETC
*, FORMATETC
*pFEOut
) {
2491 //Platform::DebugPrintf("DOB GetCanon\n");
2492 if (pd
->sci
->IsUnicodeMode())
2493 pFEOut
->cfFormat
= CF_UNICODETEXT
;
2495 pFEOut
->cfFormat
= CF_TEXT
;
2497 pFEOut
->dwAspect
= DVASPECT_CONTENT
;
2498 pFEOut
->lindex
= -1;
2499 pFEOut
->tymed
= TYMED_HGLOBAL
;
2503 STDMETHODIMP
DataObject_SetData(DataObject
*, FORMATETC
*, STGMEDIUM
*, BOOL
) {
2504 //Platform::DebugPrintf("DOB SetData\n");
2508 STDMETHODIMP
DataObject_EnumFormatEtc(DataObject
*pd
, DWORD dwDirection
, IEnumFORMATETC
**ppEnum
) {
2510 //Platform::DebugPrintf("DOB EnumFormatEtc %d\n", dwDirection);
2511 if (dwDirection
!= DATADIR_GET
) {
2515 FormatEnumerator
*pfe
;
2516 if (pd
->sci
->IsUnicodeMode()) {
2517 CLIPFORMAT formats
[] = {CF_UNICODETEXT
, CF_TEXT
};
2518 pfe
= new FormatEnumerator(0, formats
, ELEMENTS(formats
));
2520 CLIPFORMAT formats
[] = {CF_TEXT
};
2521 pfe
= new FormatEnumerator(0, formats
, ELEMENTS(formats
));
2523 return FormatEnumerator_QueryInterface(pfe
, IID_IEnumFORMATETC
,
2524 reinterpret_cast<void **>(ppEnum
));
2525 } catch (std::bad_alloc
&) {
2526 pd
->sci
->errorStatus
= SC_STATUS_BADALLOC
;
2527 return E_OUTOFMEMORY
;
2529 pd
->sci
->errorStatus
= SC_STATUS_FAILURE
;
2534 STDMETHODIMP
DataObject_DAdvise(DataObject
*, FORMATETC
*, DWORD
, IAdviseSink
*, PDWORD
) {
2535 //Platform::DebugPrintf("DOB DAdvise\n");
2539 STDMETHODIMP
DataObject_DUnadvise(DataObject
*, DWORD
) {
2540 //Platform::DebugPrintf("DOB DUnadvise\n");
2544 STDMETHODIMP
DataObject_EnumDAdvise(DataObject
*, IEnumSTATDATA
**) {
2545 //Platform::DebugPrintf("DOB EnumDAdvise\n");
2549 static VFunction
*vtDataObject
[] = {
2550 (VFunction
*)(DataObject_QueryInterface
),
2551 (VFunction
*)(DataObject_AddRef
),
2552 (VFunction
*)(DataObject_Release
),
2553 (VFunction
*)(DataObject_GetData
),
2554 (VFunction
*)(DataObject_GetDataHere
),
2555 (VFunction
*)(DataObject_QueryGetData
),
2556 (VFunction
*)(DataObject_GetCanonicalFormatEtc
),
2557 (VFunction
*)(DataObject_SetData
),
2558 (VFunction
*)(DataObject_EnumFormatEtc
),
2559 (VFunction
*)(DataObject_DAdvise
),
2560 (VFunction
*)(DataObject_DUnadvise
),
2561 (VFunction
*)(DataObject_EnumDAdvise
)
2564 DataObject::DataObject() {
2565 vtbl
= vtDataObject
;
2569 /// Implement IUnknown
2570 STDMETHODIMP
DropTarget_QueryInterface(DropTarget
*dt
, REFIID riid
, PVOID
*ppv
) {
2571 //Platform::DebugPrintf("DT QI %x\n", dt);
2572 return dt
->sci
->QueryInterface(riid
, ppv
);
2574 STDMETHODIMP_(ULONG
)DropTarget_AddRef(DropTarget
*dt
) {
2575 return dt
->sci
->AddRef();
2577 STDMETHODIMP_(ULONG
)DropTarget_Release(DropTarget
*dt
) {
2578 return dt
->sci
->Release();
2581 /// Implement IDropTarget by forwarding to Scintilla
2582 STDMETHODIMP
DropTarget_DragEnter(DropTarget
*dt
, LPDATAOBJECT pIDataSource
, DWORD grfKeyState
,
2583 POINTL pt
, PDWORD pdwEffect
) {
2585 return dt
->sci
->DragEnter(pIDataSource
, grfKeyState
, pt
, pdwEffect
);
2587 dt
->sci
->errorStatus
= SC_STATUS_FAILURE
;
2591 STDMETHODIMP
DropTarget_DragOver(DropTarget
*dt
, DWORD grfKeyState
, POINTL pt
, PDWORD pdwEffect
) {
2593 return dt
->sci
->DragOver(grfKeyState
, pt
, pdwEffect
);
2595 dt
->sci
->errorStatus
= SC_STATUS_FAILURE
;
2599 STDMETHODIMP
DropTarget_DragLeave(DropTarget
*dt
) {
2601 return dt
->sci
->DragLeave();
2603 dt
->sci
->errorStatus
= SC_STATUS_FAILURE
;
2607 STDMETHODIMP
DropTarget_Drop(DropTarget
*dt
, LPDATAOBJECT pIDataSource
, DWORD grfKeyState
,
2608 POINTL pt
, PDWORD pdwEffect
) {
2610 return dt
->sci
->Drop(pIDataSource
, grfKeyState
, pt
, pdwEffect
);
2612 dt
->sci
->errorStatus
= SC_STATUS_FAILURE
;
2617 static VFunction
*vtDropTarget
[] = {
2618 (VFunction
*)(DropTarget_QueryInterface
),
2619 (VFunction
*)(DropTarget_AddRef
),
2620 (VFunction
*)(DropTarget_Release
),
2621 (VFunction
*)(DropTarget_DragEnter
),
2622 (VFunction
*)(DropTarget_DragOver
),
2623 (VFunction
*)(DropTarget_DragLeave
),
2624 (VFunction
*)(DropTarget_Drop
)
2627 DropTarget::DropTarget() {
2628 vtbl
= vtDropTarget
;
2633 * DBCS: support Input Method Editor (IME).
2634 * Called when IME Window opened.
2636 void ScintillaWin::ImeStartComposition() {
2638 // Move IME Window to current caret position
2639 IMContext
imc(MainHWND());
2640 Point pos
= PointMainCaret();
2641 COMPOSITIONFORM CompForm
;
2642 CompForm
.dwStyle
= CFS_POINT
;
2643 CompForm
.ptCurrentPos
.x
= static_cast<int>(pos
.x
);
2644 CompForm
.ptCurrentPos
.y
= static_cast<int>(pos
.y
);
2646 ::ImmSetCompositionWindow(imc
.hIMC
, &CompForm
);
2648 // Set font of IME window to same as surrounded text.
2650 // Since the style creation code has been made platform independent,
2651 // The logfont for the IME is recreated here.
2652 const int styleHere
= pdoc
->StyleIndexAt(sel
.MainCaret());
2653 LOGFONTW lf
= {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, L
""};
2654 int sizeZoomed
= vs
.styles
[styleHere
].size
+ vs
.zoomLevel
* SC_FONT_SIZE_MULTIPLIER
;
2655 if (sizeZoomed
<= 2 * SC_FONT_SIZE_MULTIPLIER
) // Hangs if sizeZoomed <= 1
2656 sizeZoomed
= 2 * SC_FONT_SIZE_MULTIPLIER
;
2657 AutoSurface
surface(this);
2658 int deviceHeight
= sizeZoomed
;
2660 deviceHeight
= (sizeZoomed
* surface
->LogPixelsY()) / 72;
2662 // The negative is to allow for leading
2663 lf
.lfHeight
= -(abs(deviceHeight
/ SC_FONT_SIZE_MULTIPLIER
));
2664 lf
.lfWeight
= vs
.styles
[styleHere
].weight
;
2665 lf
.lfItalic
= static_cast<BYTE
>(vs
.styles
[styleHere
].italic
? 1 : 0);
2666 lf
.lfCharSet
= DEFAULT_CHARSET
;
2667 lf
.lfFaceName
[0] = L
'\0';
2668 if (vs
.styles
[styleHere
].fontName
) {
2669 const char* fontName
= vs
.styles
[styleHere
].fontName
;
2670 UTF16FromUTF8(fontName
, strlen(fontName
)+1, lf
.lfFaceName
, LF_FACESIZE
);
2673 ::ImmSetCompositionFontW(imc
.hIMC
, &lf
);
2675 // Caret is displayed in IME window. So, caret in Scintilla is useless.
2680 /** Called when IME Window closed. */
2681 void ScintillaWin::ImeEndComposition() {
2682 ShowCaretAtCurrentPosition();
2685 LRESULT
ScintillaWin::ImeOnReconvert(LPARAM lParam
) {
2686 // Reconversion on windows limits within one line without eol.
2687 // Look around: baseStart <-- (|mainStart| -- mainEnd) --> baseEnd.
2688 const Sci::Position mainStart
= sel
.RangeMain().Start().Position();
2689 const Sci::Position mainEnd
= sel
.RangeMain().End().Position();
2690 const Sci::Line curLine
= pdoc
->LineFromPosition(mainStart
);
2691 if (curLine
!= pdoc
->LineFromPosition(mainEnd
))
2693 const Sci::Position baseStart
= pdoc
->LineStart(curLine
);
2694 const Sci::Position baseEnd
= pdoc
->LineEnd(curLine
);
2695 if ((baseStart
== baseEnd
) || (mainEnd
> baseEnd
))
2698 const int codePage
= CodePageOfDocument();
2699 const std::wstring rcFeed
= StringDecode(RangeText(baseStart
, baseEnd
), codePage
);
2700 const int rcFeedLen
= static_cast<int>(rcFeed
.length()) * sizeof(wchar_t);
2701 const int rcSize
= sizeof(RECONVERTSTRING
) + rcFeedLen
+ sizeof(wchar_t);
2703 RECONVERTSTRING
*rc
= reinterpret_cast<RECONVERTSTRING
*>(lParam
);
2705 return rcSize
; // Immediately be back with rcSize of memory block.
2707 wchar_t *rcFeedStart
= reinterpret_cast<wchar_t*>(rc
+ 1);
2708 memcpy(rcFeedStart
, &rcFeed
[0], rcFeedLen
);
2710 std::string rcCompString
= RangeText(mainStart
, mainEnd
);
2711 std::wstring rcCompWstring
= StringDecode(rcCompString
, codePage
);
2712 std::string rcCompStart
= RangeText(baseStart
, mainStart
);
2713 std::wstring rcCompWstart
= StringDecode(rcCompStart
, codePage
);
2715 // Map selection to dwCompStr.
2716 // No selection assumes current caret as rcCompString without length.
2717 rc
->dwVersion
= 0; // It should be absolutely 0.
2718 rc
->dwStrLen
= static_cast<DWORD
>(rcFeed
.length());
2719 rc
->dwStrOffset
= sizeof(RECONVERTSTRING
);
2720 rc
->dwCompStrLen
= static_cast<DWORD
>(rcCompWstring
.length());
2721 rc
->dwCompStrOffset
= static_cast<DWORD
>(rcCompWstart
.length()) * sizeof(wchar_t);
2722 rc
->dwTargetStrLen
= rc
->dwCompStrLen
;
2723 rc
->dwTargetStrOffset
=rc
->dwCompStrOffset
;
2725 IMContext
imc(MainHWND());
2729 if (!::ImmSetCompositionStringW(imc
.hIMC
, SCS_QUERYRECONVERTSTRING
, rc
, rcSize
, NULL
, 0))
2732 // No selection asks IME to fill target fields with its own value.
2733 int tgWlen
= rc
->dwTargetStrLen
;
2734 int tgWstart
= rc
->dwTargetStrOffset
/ sizeof(wchar_t);
2736 std::string tgCompStart
= StringEncode(rcFeed
.substr(0, tgWstart
), codePage
);
2737 std::string tgComp
= StringEncode(rcFeed
.substr(tgWstart
, tgWlen
), codePage
);
2739 // No selection needs to adjust reconvert start position for IME set.
2740 int adjust
= static_cast<int>(tgCompStart
.length() - rcCompStart
.length());
2741 int docCompLen
= static_cast<int>(tgComp
.length());
2743 // Make place for next composition string to sit in.
2744 for (size_t r
=0; r
<sel
.Count(); r
++) {
2745 Sci::Position rBase
= sel
.Range(r
).Start().Position();
2746 Sci::Position docCompStart
= rBase
+ adjust
;
2748 if (inOverstrike
) { // the docCompLen of bytes will be overstriked.
2749 sel
.Range(r
).caret
.SetPosition(docCompStart
);
2750 sel
.Range(r
).anchor
.SetPosition(docCompStart
);
2752 // Ensure docCompStart+docCompLen be not beyond lineEnd.
2753 // since docCompLen by byte might break eol.
2754 Sci::Position lineEnd
= pdoc
->LineEnd(pdoc
->LineFromPosition(rBase
));
2755 Sci::Position overflow
= (docCompStart
+ docCompLen
) - lineEnd
;
2757 pdoc
->DeleteChars(docCompStart
, docCompLen
- overflow
);
2759 pdoc
->DeleteChars(docCompStart
, docCompLen
);
2763 // Immediately Target Input or candidate box choice with GCS_COMPSTR.
2767 void ScintillaWin::GetIntelliMouseParameters() {
2768 // This retrieves the number of lines per scroll as configured inthe Mouse Properties sheet in Control Panel
2769 ::SystemParametersInfo(SPI_GETWHEELSCROLLLINES
, 0, &linesPerScroll
, 0);
2772 void ScintillaWin::CopyToClipboard(const SelectionText
&selectedText
) {
2773 if (!::OpenClipboardRetry(MainHWND())) {
2778 GlobalMemory uniText
;
2780 // Default Scintilla behaviour in Unicode mode
2781 if (IsUnicodeMode()) {
2782 size_t uchars
= UTF16Length(selectedText
.Data(),
2783 selectedText
.LengthWithTerminator());
2784 uniText
.Allocate(2 * uchars
);
2786 UTF16FromUTF8(selectedText
.Data(), selectedText
.LengthWithTerminator(),
2787 static_cast<wchar_t *>(uniText
.ptr
), uchars
);
2791 // Convert to Unicode using the current Scintilla code page
2792 UINT cpSrc
= CodePageFromCharSet(
2793 selectedText
.characterSet
, selectedText
.codePage
);
2794 int uLen
= ::MultiByteToWideChar(cpSrc
, 0, selectedText
.Data(),
2795 static_cast<int>(selectedText
.LengthWithTerminator()), 0, 0);
2796 uniText
.Allocate(2 * uLen
);
2798 ::MultiByteToWideChar(cpSrc
, 0, selectedText
.Data(),
2799 static_cast<int>(selectedText
.LengthWithTerminator()),
2800 static_cast<wchar_t *>(uniText
.ptr
), uLen
);
2805 uniText
.SetClip(CF_UNICODETEXT
);
2807 // There was a failure - try to copy at least ANSI text
2808 GlobalMemory ansiText
;
2809 ansiText
.Allocate(selectedText
.LengthWithTerminator());
2811 memcpy(static_cast<char *>(ansiText
.ptr
), selectedText
.Data(), selectedText
.LengthWithTerminator());
2812 ansiText
.SetClip(CF_TEXT
);
2816 if (selectedText
.rectangular
) {
2817 ::SetClipboardData(cfColumnSelect
, 0);
2819 GlobalMemory borlandSelection
;
2820 borlandSelection
.Allocate(1);
2821 if (borlandSelection
) {
2822 static_cast<BYTE
*>(borlandSelection
.ptr
)[0] = 0x02;
2823 borlandSelection
.SetClip(cfBorlandIDEBlockType
);
2827 if (selectedText
.lineCopy
) {
2828 ::SetClipboardData(cfLineSelect
, 0);
2829 ::SetClipboardData(cfVSLineTag
, 0);
2835 void ScintillaWin::ScrollMessage(WPARAM wParam
) {
2836 //DWORD dwStart = timeGetTime();
2837 //Platform::DebugPrintf("Scroll %x %d\n", wParam, lParam);
2839 SCROLLINFO sci
= {};
2840 sci
.cbSize
= sizeof(sci
);
2841 sci
.fMask
= SIF_ALL
;
2843 GetScrollInfo(SB_VERT
, &sci
);
2845 //Platform::DebugPrintf("ScrollInfo %d mask=%x min=%d max=%d page=%d pos=%d track=%d\n", b,sci.fMask,
2846 //sci.nMin, sci.nMax, sci.nPage, sci.nPos, sci.nTrackPos);
2848 Sci::Line topLineNew
= topLine
;
2849 switch (LoWord(wParam
)) {
2857 topLineNew
-= LinesToScroll(); break;
2858 case SB_PAGEDOWN
: topLineNew
+= LinesToScroll(); break;
2859 case SB_TOP
: topLineNew
= 0; break;
2860 case SB_BOTTOM
: topLineNew
= MaxScrollPos(); break;
2861 case SB_THUMBPOSITION
: topLineNew
= sci
.nTrackPos
; break;
2862 case SB_THUMBTRACK
: topLineNew
= sci
.nTrackPos
; break;
2864 ScrollTo(topLineNew
);
2867 void ScintillaWin::HorizontalScrollMessage(WPARAM wParam
) {
2869 const PRectangle rcText
= GetTextRectangle();
2870 const int pageWidth
= static_cast<int>(rcText
.Width() * 2 / 3);
2871 switch (LoWord(wParam
)) {
2875 case SB_LINEDOWN
: // May move past the logical end
2883 if (xPos
> scrollWidth
- rcText
.Width()) { // Hit the end exactly
2884 xPos
= scrollWidth
- static_cast<int>(rcText
.Width());
2893 case SB_THUMBPOSITION
:
2894 case SB_THUMBTRACK
: {
2895 // 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 =]
2897 si
.cbSize
= sizeof(si
);
2898 si
.fMask
= SIF_TRACKPOS
;
2899 if (GetScrollInfo(SB_HORZ
, &si
)) {
2900 xPos
= si
.nTrackPos
;
2905 HorizontalScrollTo(xPos
);
2909 * Redraw all of text area.
2910 * This paint will not be abandoned.
2912 void ScintillaWin::FullPaint() {
2913 if ((technology
== SC_TECHNOLOGY_DEFAULT
) || (technology
== SC_TECHNOLOGY_DIRECTWRITEDC
)) {
2914 HDC hdc
= ::GetDC(MainHWND());
2916 ::ReleaseDC(MainHWND(), hdc
);
2923 * Redraw all of text area on the specified DC.
2924 * This paint will not be abandoned.
2926 void ScintillaWin::FullPaintDC(HDC hdc
) {
2927 paintState
= painting
;
2928 rcPaint
= GetClientRectangle();
2929 paintingAllText
= true;
2930 if (technology
== SC_TECHNOLOGY_DEFAULT
) {
2931 AutoSurface
surfaceWindow(hdc
, this);
2932 if (surfaceWindow
) {
2933 Paint(surfaceWindow
, rcPaint
);
2934 surfaceWindow
->Release();
2937 #if defined(USE_D2D)
2938 EnsureRenderTarget(hdc
);
2939 AutoSurface
surfaceWindow(pRenderTarget
, this);
2940 if (surfaceWindow
) {
2941 pRenderTarget
->BeginDraw();
2942 Paint(surfaceWindow
, rcPaint
);
2943 surfaceWindow
->Release();
2944 const HRESULT hr
= pRenderTarget
->EndDraw();
2945 if (hr
== static_cast<HRESULT
>(D2DERR_RECREATE_TARGET
)) {
2951 paintState
= notPainting
;
2954 static bool CompareDevCap(HDC hdc
, HDC hOtherDC
, int nIndex
) {
2955 return ::GetDeviceCaps(hdc
, nIndex
) == ::GetDeviceCaps(hOtherDC
, nIndex
);
2958 bool ScintillaWin::IsCompatibleDC(HDC hOtherDC
) {
2959 HDC hdc
= ::GetDC(MainHWND());
2960 const bool isCompatible
=
2961 CompareDevCap(hdc
, hOtherDC
, TECHNOLOGY
) &&
2962 CompareDevCap(hdc
, hOtherDC
, LOGPIXELSY
) &&
2963 CompareDevCap(hdc
, hOtherDC
, LOGPIXELSX
) &&
2964 CompareDevCap(hdc
, hOtherDC
, BITSPIXEL
) &&
2965 CompareDevCap(hdc
, hOtherDC
, PLANES
);
2966 ::ReleaseDC(MainHWND(), hdc
);
2967 return isCompatible
;
2970 DWORD
ScintillaWin::EffectFromState(DWORD grfKeyState
) const {
2971 // These are the Wordpad semantics.
2973 if (inDragDrop
== ddDragging
) // Internal defaults to move
2974 dwEffect
= DROPEFFECT_MOVE
;
2976 dwEffect
= DROPEFFECT_COPY
;
2977 if (grfKeyState
& MK_ALT
)
2978 dwEffect
= DROPEFFECT_MOVE
;
2979 if (grfKeyState
& MK_CONTROL
)
2980 dwEffect
= DROPEFFECT_COPY
;
2984 /// Implement IUnknown
2985 STDMETHODIMP
ScintillaWin::QueryInterface(REFIID riid
, PVOID
*ppv
) {
2987 if (riid
== IID_IUnknown
)
2988 *ppv
= reinterpret_cast<IDropTarget
*>(&dt
);
2989 if (riid
== IID_IDropSource
)
2990 *ppv
= reinterpret_cast<IDropSource
*>(&ds
);
2991 if (riid
== IID_IDropTarget
)
2992 *ppv
= reinterpret_cast<IDropTarget
*>(&dt
);
2993 if (riid
== IID_IDataObject
)
2994 *ppv
= reinterpret_cast<IDataObject
*>(&dob
);
2996 return E_NOINTERFACE
;
3000 STDMETHODIMP_(ULONG
) ScintillaWin::AddRef() {
3004 STDMETHODIMP_(ULONG
) ScintillaWin::Release() {
3008 /// Implement IDropTarget
3009 STDMETHODIMP
ScintillaWin::DragEnter(LPDATAOBJECT pIDataSource
, DWORD grfKeyState
,
3010 POINTL
, PDWORD pdwEffect
) {
3011 if (pIDataSource
== NULL
)
3013 FORMATETC fmtu
= {CF_UNICODETEXT
, NULL
, DVASPECT_CONTENT
, -1, TYMED_HGLOBAL
};
3014 const HRESULT hrHasUText
= pIDataSource
->QueryGetData(&fmtu
);
3015 hasOKText
= (hrHasUText
== S_OK
);
3017 FORMATETC fmte
= {CF_TEXT
, NULL
, DVASPECT_CONTENT
, -1, TYMED_HGLOBAL
};
3018 const HRESULT hrHasText
= pIDataSource
->QueryGetData(&fmte
);
3019 hasOKText
= (hrHasText
== S_OK
);
3022 *pdwEffect
= DROPEFFECT_NONE
;
3026 *pdwEffect
= EffectFromState(grfKeyState
);
3030 STDMETHODIMP
ScintillaWin::DragOver(DWORD grfKeyState
, POINTL pt
, PDWORD pdwEffect
) {
3032 if (!hasOKText
|| pdoc
->IsReadOnly()) {
3033 *pdwEffect
= DROPEFFECT_NONE
;
3037 *pdwEffect
= EffectFromState(grfKeyState
);
3039 // Update the cursor.
3040 POINT rpt
= {pt
.x
, pt
.y
};
3041 ::ScreenToClient(MainHWND(), &rpt
);
3042 SetDragPosition(SPositionFromLocation(PointFromPOINT(rpt
), false, false, UserVirtualSpace()));
3046 errorStatus
= SC_STATUS_FAILURE
;
3051 STDMETHODIMP
ScintillaWin::DragLeave() {
3053 SetDragPosition(SelectionPosition(Sci::invalidPosition
));
3056 errorStatus
= SC_STATUS_FAILURE
;
3061 STDMETHODIMP
ScintillaWin::Drop(LPDATAOBJECT pIDataSource
, DWORD grfKeyState
,
3062 POINTL pt
, PDWORD pdwEffect
) {
3064 *pdwEffect
= EffectFromState(grfKeyState
);
3066 if (pIDataSource
== NULL
)
3069 SetDragPosition(SelectionPosition(Sci::invalidPosition
));
3071 STGMEDIUM medium
= {0, {0}, 0};
3073 std::vector
<char> data
; // Includes terminating NUL
3075 FORMATETC fmtu
= {CF_UNICODETEXT
, NULL
, DVASPECT_CONTENT
, -1, TYMED_HGLOBAL
};
3076 HRESULT hr
= pIDataSource
->GetData(&fmtu
, &medium
);
3077 if (SUCCEEDED(hr
) && medium
.hGlobal
) {
3078 GlobalMemory
memUDrop(medium
.hGlobal
);
3079 wchar_t *udata
= static_cast<wchar_t *>(memUDrop
.ptr
);
3081 if (IsUnicodeMode()) {
3082 const size_t tlen
= memUDrop
.Size();
3083 // Convert UTF-16 to UTF-8
3084 const size_t dataLen
= UTF8Length(udata
, tlen
/2);
3085 data
.resize(dataLen
+1);
3086 UTF8FromUTF16(udata
, tlen
/2, &data
[0], dataLen
);
3088 // Convert UTF-16 to ANSI
3090 // Default Scintilla behavior in Unicode mode
3091 // CF_UNICODETEXT available, but not in Unicode mode
3092 // Convert from Unicode to current Scintilla code page
3093 UINT cpDest
= CodePageOfDocument();
3094 int tlen
= ::WideCharToMultiByte(cpDest
, 0, udata
, -1,
3095 NULL
, 0, NULL
, NULL
) - 1; // subtract 0 terminator
3096 data
.resize(tlen
+ 1);
3097 ::WideCharToMultiByte(cpDest
, 0, udata
, -1,
3098 &data
[0], tlen
+ 1, NULL
, NULL
);
3103 FORMATETC fmte
= {CF_TEXT
, NULL
, DVASPECT_CONTENT
, -1, TYMED_HGLOBAL
};
3104 hr
= pIDataSource
->GetData(&fmte
, &medium
);
3105 if (SUCCEEDED(hr
) && medium
.hGlobal
) {
3106 GlobalMemory
memDrop(medium
.hGlobal
);
3107 const char *cdata
= static_cast<char *>(memDrop
.ptr
);
3109 data
.assign(cdata
, cdata
+strlen(cdata
)+1);
3114 if (!SUCCEEDED(hr
) || data
.empty()) {
3115 //Platform::DebugPrintf("Bad data format: 0x%x\n", hres);
3119 FORMATETC fmtr
= {cfColumnSelect
, NULL
, DVASPECT_CONTENT
, -1, TYMED_HGLOBAL
};
3120 HRESULT hrRectangular
= pIDataSource
->QueryGetData(&fmtr
);
3122 POINT rpt
= {pt
.x
, pt
.y
};
3123 ::ScreenToClient(MainHWND(), &rpt
);
3124 SelectionPosition movePos
= SPositionFromLocation(PointFromPOINT(rpt
), false, false, UserVirtualSpace());
3126 DropAt(movePos
, &data
[0], data
.size() - 1, *pdwEffect
== DROPEFFECT_MOVE
, hrRectangular
== S_OK
);
3129 if (medium
.pUnkForRelease
!= NULL
)
3130 medium
.pUnkForRelease
->Release();
3132 ::GlobalFree(medium
.hGlobal
);
3136 errorStatus
= SC_STATUS_FAILURE
;
3141 /// Implement important part of IDataObject
3142 STDMETHODIMP
ScintillaWin::GetData(FORMATETC
*pFEIn
, STGMEDIUM
*pSTM
) {
3143 const bool formatOK
= (pFEIn
->cfFormat
== CF_TEXT
) ||
3144 ((pFEIn
->cfFormat
== CF_UNICODETEXT
) && IsUnicodeMode());
3147 (pFEIn
->dwAspect
& DVASPECT_CONTENT
) == 0 ||
3148 pFEIn
->lindex
!= -1 ||
3149 (pFEIn
->tymed
& TYMED_HGLOBAL
) == 0
3151 //Platform::DebugPrintf("DOB GetData No %d %x %x fmt=%x\n", lenDrag, pFEIn, pSTM, pFEIn->cfFormat);
3152 return DATA_E_FORMATETC
;
3154 pSTM
->tymed
= TYMED_HGLOBAL
;
3155 //Platform::DebugPrintf("DOB GetData OK %d %x %x\n", lenDrag, pFEIn, pSTM);
3158 if (pFEIn
->cfFormat
== CF_UNICODETEXT
) {
3159 size_t uchars
= UTF16Length(drag
.Data(), drag
.LengthWithTerminator());
3160 text
.Allocate(2 * uchars
);
3162 UTF16FromUTF8(drag
.Data(), drag
.LengthWithTerminator(),
3163 static_cast<wchar_t *>(text
.ptr
), uchars
);
3166 text
.Allocate(drag
.LengthWithTerminator());
3168 memcpy(static_cast<char *>(text
.ptr
), drag
.Data(), drag
.LengthWithTerminator());
3171 pSTM
->hGlobal
= text
? text
.Unlock() : 0;
3172 pSTM
->pUnkForRelease
= 0;
3176 bool ScintillaWin::Register(HINSTANCE hInstance_
) {
3178 hInstance
= hInstance_
;
3181 // Register the Scintilla class
3182 // Register Scintilla as a wide character window
3183 WNDCLASSEXW wndclass
;
3184 wndclass
.cbSize
= sizeof(wndclass
);
3185 wndclass
.style
= CS_GLOBALCLASS
| CS_HREDRAW
| CS_VREDRAW
;
3186 wndclass
.lpfnWndProc
= ScintillaWin::SWndProc
;
3187 wndclass
.cbClsExtra
= 0;
3188 wndclass
.cbWndExtra
= sizeof(ScintillaWin
*);
3189 wndclass
.hInstance
= hInstance
;
3190 wndclass
.hIcon
= NULL
;
3191 wndclass
.hCursor
= NULL
;
3192 wndclass
.hbrBackground
= NULL
;
3193 wndclass
.lpszMenuName
= NULL
;
3194 wndclass
.lpszClassName
= L
"Scintilla";
3195 wndclass
.hIconSm
= 0;
3196 scintillaClassAtom
= ::RegisterClassExW(&wndclass
);
3197 result
= 0 != scintillaClassAtom
;
3200 // Register the CallTip class
3201 WNDCLASSEX wndclassc
;
3202 wndclassc
.cbSize
= sizeof(wndclassc
);
3203 wndclassc
.style
= CS_GLOBALCLASS
| CS_HREDRAW
| CS_VREDRAW
;
3204 wndclassc
.cbClsExtra
= 0;
3205 wndclassc
.cbWndExtra
= sizeof(ScintillaWin
*);
3206 wndclassc
.hInstance
= hInstance
;
3207 wndclassc
.hIcon
= NULL
;
3208 wndclassc
.hbrBackground
= NULL
;
3209 wndclassc
.lpszMenuName
= NULL
;
3210 wndclassc
.lpfnWndProc
= ScintillaWin::CTWndProc
;
3211 wndclassc
.hCursor
= ::LoadCursor(NULL
, IDC_ARROW
);
3212 wndclassc
.lpszClassName
= callClassName
;
3213 wndclassc
.hIconSm
= 0;
3215 callClassAtom
= ::RegisterClassEx(&wndclassc
);
3216 result
= 0 != callClassAtom
;
3222 bool ScintillaWin::Unregister() {
3224 if (0 != scintillaClassAtom
) {
3225 if (::UnregisterClass(MAKEINTATOM(scintillaClassAtom
), hInstance
) == 0) {
3228 scintillaClassAtom
= 0;
3230 if (0 != callClassAtom
) {
3231 if (::UnregisterClass(MAKEINTATOM(callClassAtom
), hInstance
) == 0) {
3239 bool ScintillaWin::HasCaretSizeChanged() const {
3241 ( (0 != vs
.caretWidth
) && (sysCaretWidth
!= vs
.caretWidth
) )
3242 || ((0 != vs
.lineHeight
) && (sysCaretHeight
!= vs
.lineHeight
))
3249 BOOL
ScintillaWin::CreateSystemCaret() {
3250 sysCaretWidth
= vs
.caretWidth
;
3251 if (0 == sysCaretWidth
) {
3254 sysCaretHeight
= vs
.lineHeight
;
3255 int bitmapSize
= (((sysCaretWidth
+ 15) & ~15) >> 3) *
3257 std::vector
<char> bits(bitmapSize
);
3258 sysCaretBitmap
= ::CreateBitmap(sysCaretWidth
, sysCaretHeight
, 1,
3259 1, reinterpret_cast<BYTE
*>(&bits
[0]));
3260 BOOL retval
= ::CreateCaret(
3261 MainHWND(), sysCaretBitmap
,
3262 sysCaretWidth
, sysCaretHeight
);
3263 if (technology
== SC_TECHNOLOGY_DEFAULT
) {
3264 // System caret interferes with Direct2D drawing so only show it for GDI.
3265 ::ShowCaret(MainHWND());
3270 BOOL
ScintillaWin::DestroySystemCaret() {
3271 ::HideCaret(MainHWND());
3272 BOOL retval
= ::DestroyCaret();
3273 if (sysCaretBitmap
) {
3274 ::DeleteObject(sysCaretBitmap
);
3280 LRESULT PASCAL
ScintillaWin::CTWndProc(
3281 HWND hWnd
, UINT iMessage
, WPARAM wParam
, LPARAM lParam
) {
3282 // Find C++ object associated with window.
3283 ScintillaWin
*sciThis
= static_cast<ScintillaWin
*>(PointerFromWindow(hWnd
));
3285 // ctp will be zero if WM_CREATE not seen yet
3287 if (iMessage
== WM_CREATE
) {
3288 // Associate CallTip object with window
3289 CREATESTRUCT
*pCreate
= reinterpret_cast<CREATESTRUCT
*>(lParam
);
3290 SetWindowPointer(hWnd
, pCreate
->lpCreateParams
);
3293 return ::DefWindowProc(hWnd
, iMessage
, wParam
, lParam
);
3296 if (iMessage
== WM_NCDESTROY
) {
3297 ::SetWindowLong(hWnd
, 0, 0);
3298 return ::DefWindowProc(hWnd
, iMessage
, wParam
, lParam
);
3299 } else if (iMessage
== WM_PAINT
) {
3301 ::BeginPaint(hWnd
, &ps
);
3302 std::unique_ptr
<Surface
> surfaceWindow(Surface::Allocate(sciThis
->technology
));
3303 #if defined(USE_D2D)
3304 ID2D1HwndRenderTarget
*pCTRenderTarget
= 0;
3307 GetClientRect(hWnd
, &rc
);
3308 // Create a Direct2D render target.
3309 if (sciThis
->technology
== SC_TECHNOLOGY_DEFAULT
) {
3310 surfaceWindow
->Init(ps
.hdc
, hWnd
);
3312 #if defined(USE_D2D)
3313 D2D1_HWND_RENDER_TARGET_PROPERTIES dhrtp
;
3315 dhrtp
.pixelSize
= D2D1::SizeU(rc
.right
- rc
.left
, rc
.bottom
- rc
.top
);
3316 dhrtp
.presentOptions
= (sciThis
->technology
== SC_TECHNOLOGY_DIRECTWRITERETAIN
) ?
3317 D2D1_PRESENT_OPTIONS_RETAIN_CONTENTS
: D2D1_PRESENT_OPTIONS_NONE
;
3319 D2D1_RENDER_TARGET_PROPERTIES drtp
;
3320 drtp
.type
= D2D1_RENDER_TARGET_TYPE_DEFAULT
;
3321 drtp
.pixelFormat
.format
= DXGI_FORMAT_UNKNOWN
;
3322 drtp
.pixelFormat
.alphaMode
= D2D1_ALPHA_MODE_UNKNOWN
;
3325 drtp
.usage
= D2D1_RENDER_TARGET_USAGE_NONE
;
3326 drtp
.minLevel
= D2D1_FEATURE_LEVEL_DEFAULT
;
3328 if (!SUCCEEDED(pD2DFactory
->CreateHwndRenderTarget(drtp
, dhrtp
, &pCTRenderTarget
))) {
3329 surfaceWindow
->Release();
3330 ::EndPaint(hWnd
, &ps
);
3333 surfaceWindow
->Init(pCTRenderTarget
, hWnd
);
3334 pCTRenderTarget
->BeginDraw();
3337 surfaceWindow
->SetUnicodeMode(SC_CP_UTF8
== sciThis
->ct
.codePage
);
3338 surfaceWindow
->SetDBCSMode(sciThis
->ct
.codePage
);
3339 sciThis
->ct
.PaintCT(surfaceWindow
.get());
3340 #if defined(USE_D2D)
3341 if (pCTRenderTarget
)
3342 pCTRenderTarget
->EndDraw();
3344 surfaceWindow
->Release();
3345 #if defined(USE_D2D)
3346 if (pCTRenderTarget
)
3347 pCTRenderTarget
->Release();
3349 ::EndPaint(hWnd
, &ps
);
3351 } else if ((iMessage
== WM_NCLBUTTONDOWN
) || (iMessage
== WM_NCLBUTTONDBLCLK
)) {
3353 pt
.x
= static_cast<short>(LOWORD(lParam
));
3354 pt
.y
= static_cast<short>(HIWORD(lParam
));
3355 ScreenToClient(hWnd
, &pt
);
3356 sciThis
->ct
.MouseClick(PointFromPOINT(pt
));
3357 sciThis
->CallTipClick();
3359 } else if (iMessage
== WM_LBUTTONDOWN
) {
3360 // This does not fire due to the hit test code
3361 sciThis
->ct
.MouseClick(Point::FromLong(static_cast<long>(lParam
)));
3362 sciThis
->CallTipClick();
3364 } else if (iMessage
== WM_SETCURSOR
) {
3365 ::SetCursor(::LoadCursor(NULL
, IDC_ARROW
));
3367 } else if (iMessage
== WM_NCHITTEST
) {
3370 return ::DefWindowProc(hWnd
, iMessage
, wParam
, lParam
);
3374 sciThis
->errorStatus
= SC_STATUS_FAILURE
;
3376 return ::DefWindowProc(hWnd
, iMessage
, wParam
, lParam
);
3379 sptr_t
ScintillaWin::DirectFunction(
3380 sptr_t ptr
, UINT iMessage
, uptr_t wParam
, sptr_t lParam
) {
3381 PLATFORM_ASSERT(::GetCurrentThreadId() == ::GetWindowThreadProcessId(reinterpret_cast<ScintillaWin
*>(ptr
)->MainHWND(), NULL
));
3382 return reinterpret_cast<ScintillaWin
*>(ptr
)->WndProc(iMessage
, wParam
, lParam
);
3386 #ifndef STATIC_BUILD
3387 __declspec(dllexport
)
3389 sptr_t __stdcall
Scintilla_DirectFunction(
3390 ScintillaWin
*sci
, UINT iMessage
, uptr_t wParam
, sptr_t lParam
) {
3391 return sci
->WndProc(iMessage
, wParam
, lParam
);
3394 LRESULT PASCAL
ScintillaWin::SWndProc(
3395 HWND hWnd
, UINT iMessage
, WPARAM wParam
, LPARAM lParam
) {
3396 //Platform::DebugPrintf("S W:%x M:%x WP:%x L:%x\n", hWnd, iMessage, wParam, lParam);
3398 // Find C++ object associated with window.
3399 ScintillaWin
*sci
= static_cast<ScintillaWin
*>(PointerFromWindow(hWnd
));
3400 // sci will be zero if WM_CREATE not seen yet
3403 if (iMessage
== WM_CREATE
) {
3404 // Create C++ object associated with window
3405 sci
= new ScintillaWin(hWnd
);
3406 SetWindowPointer(hWnd
, sci
);
3407 return sci
->WndProc(iMessage
, wParam
, lParam
);
3411 return ::DefWindowProc(hWnd
, iMessage
, wParam
, lParam
);
3413 if (iMessage
== WM_NCDESTROY
) {
3419 ::SetWindowLong(hWnd
, 0, 0);
3420 return ::DefWindowProc(hWnd
, iMessage
, wParam
, lParam
);
3422 return sci
->WndProc(iMessage
, wParam
, lParam
);
3427 // This function is externally visible so it can be called from container when building statically.
3428 // Must be called once only.
3429 int Scintilla_RegisterClasses(void *hInstance
) {
3430 Platform_Initialise(hInstance
);
3431 const bool result
= ScintillaWin::Register(static_cast<HINSTANCE
>(hInstance
));
3433 Scintilla_LinkLexers();
3438 static int ResourcesRelease(bool fromDllMain
) {
3439 const bool result
= ScintillaWin::Unregister();
3440 Platform_Finalise(fromDllMain
);
3444 // This function is externally visible so it can be called from container when building statically.
3445 int Scintilla_ReleaseResources() {
3446 return ResourcesRelease(false);
3449 #ifndef STATIC_BUILD
3450 extern "C" int APIENTRY
DllMain(HINSTANCE hInstance
, DWORD dwReason
, LPVOID lpvReserved
) {
3451 //Platform::DebugPrintf("Scintilla::DllMain %d %d\n", hInstance, dwReason);
3452 if (dwReason
== DLL_PROCESS_ATTACH
) {
3453 if (!Scintilla_RegisterClasses(hInstance
))
3455 } else if (dwReason
== DLL_PROCESS_DETACH
) {
3456 if (lpvReserved
== NULL
) {
3457 ResourcesRelease(true);