updated Scintilla to 2.29
[TortoiseGit.git] / ext / scintilla / win32 / ScintillaWin.cxx
blobd99d408e4cf87902268c1514a489b5d14e9fdb52
1 // Scintilla source code edit control
2 /** @file ScintillaWin.cxx
3 ** Windows specific subclass of ScintillaBase.
4 **/
5 // Copyright 1998-2003 by Neil Hodgson <neilh@scintilla.org>
6 // The License.txt file describes the conditions under which this software may be distributed.
8 #include <new>
9 #include <stdlib.h>
10 #include <string.h>
11 #include <stdio.h>
12 #include <ctype.h>
13 #include <assert.h>
14 #include <limits.h>
16 #include <string>
17 #include <vector>
18 #include <map>
20 #undef _WIN32_WINNT
21 #define _WIN32_WINNT 0x0500
22 #include <windows.h>
23 #include <commctrl.h>
24 #include <richedit.h>
25 #include <windowsx.h>
27 #include "Platform.h"
29 #include "ILexer.h"
30 #include "Scintilla.h"
32 #ifdef SCI_LEXER
33 #include "SciLexer.h"
34 #include "LexerModule.h"
35 #endif
36 #include "SplitVector.h"
37 #include "Partitioning.h"
38 #include "RunStyles.h"
39 #include "ContractionState.h"
40 #include "CellBuffer.h"
41 #include "CallTip.h"
42 #include "KeyMap.h"
43 #include "Indicator.h"
44 #include "XPM.h"
45 #include "LineMarker.h"
46 #include "Style.h"
47 #include "AutoComplete.h"
48 #include "ViewStyle.h"
49 #include "CharClassify.h"
50 #include "Decoration.h"
51 #include "Document.h"
52 #include "Selection.h"
53 #include "PositionCache.h"
54 #include "Editor.h"
55 #include "ScintillaBase.h"
56 #include "UniConversion.h"
58 #ifdef SCI_LEXER
59 #include "ExternalLexer.h"
60 #endif
62 #ifndef SPI_GETWHEELSCROLLLINES
63 #define SPI_GETWHEELSCROLLLINES 104
64 #endif
66 #ifndef WM_UNICHAR
67 #define WM_UNICHAR 0x0109
68 #endif
70 #ifndef UNICODE_NOCHAR
71 #define UNICODE_NOCHAR 0xFFFF
72 #endif
74 #ifndef WM_IME_STARTCOMPOSITION
75 #include <imm.h>
76 #endif
78 #include <commctrl.h>
79 #ifndef __DMC__
80 #include <zmouse.h>
81 #endif
82 #include <ole2.h>
84 #ifndef MK_ALT
85 #define MK_ALT 32
86 #endif
88 #define SC_WIN_IDLE 5001
90 // Functions imported from PlatWin
91 extern bool IsNT();
92 extern void Platform_Initialise(void *hInstance);
93 extern void Platform_Finalise();
95 typedef BOOL (WINAPI *TrackMouseEventSig)(LPTRACKMOUSEEVENT);
97 // GCC has trouble with the standard COM ABI so do it the old C way with explicit vtables.
99 const TCHAR scintillaClassName[] = TEXT("Scintilla");
100 const TCHAR callClassName[] = TEXT("CallTip");
102 #ifdef SCI_NAMESPACE
103 using namespace Scintilla;
104 #endif
106 // Take care of 32/64 bit pointers
107 #ifdef GetWindowLongPtr
108 static void *PointerFromWindow(HWND hWnd) {
109 return reinterpret_cast<void *>(::GetWindowLongPtr(hWnd, 0));
111 static void SetWindowPointer(HWND hWnd, void *ptr) {
112 ::SetWindowLongPtr(hWnd, 0, reinterpret_cast<LONG_PTR>(ptr));
114 static void SetWindowID(HWND hWnd, int identifier) {
115 ::SetWindowLongPtr(hWnd, GWLP_ID, identifier);
117 #else
118 static void *PointerFromWindow(HWND hWnd) {
119 return reinterpret_cast<void *>(::GetWindowLong(hWnd, 0));
121 static void SetWindowPointer(HWND hWnd, void *ptr) {
122 ::SetWindowLong(hWnd, 0, reinterpret_cast<LONG>(ptr));
124 static void SetWindowID(HWND hWnd, int identifier) {
125 ::SetWindowLong(hWnd, GWL_ID, identifier);
127 #endif
129 class ScintillaWin; // Forward declaration for COM interface subobjects
131 typedef void VFunction(void);
135 class FormatEnumerator {
136 public:
137 VFunction **vtbl;
138 int ref;
139 int pos;
140 CLIPFORMAT formats[2];
141 int formatsLen;
142 FormatEnumerator(int pos_, CLIPFORMAT formats_[], int formatsLen_);
147 class DropSource {
148 public:
149 VFunction **vtbl;
150 ScintillaWin *sci;
151 DropSource();
156 class DataObject {
157 public:
158 VFunction **vtbl;
159 ScintillaWin *sci;
160 DataObject();
165 class DropTarget {
166 public:
167 VFunction **vtbl;
168 ScintillaWin *sci;
169 DropTarget();
174 class ScintillaWin :
175 public ScintillaBase {
177 bool lastKeyDownConsumed;
179 bool capturedMouse;
180 bool trackedMouseLeave;
181 TrackMouseEventSig TrackMouseEventFn;
183 unsigned int linesPerScroll; ///< Intellimouse support
184 int wheelDelta; ///< Wheel delta from roll
186 HRGN hRgnUpdate;
188 bool hasOKText;
190 CLIPFORMAT cfColumnSelect;
191 CLIPFORMAT cfLineSelect;
193 HRESULT hrOle;
194 DropSource ds;
195 DataObject dob;
196 DropTarget dt;
198 static HINSTANCE hInstance;
200 ScintillaWin(HWND hwnd);
201 ScintillaWin(const ScintillaWin &);
202 virtual ~ScintillaWin();
203 ScintillaWin &operator=(const ScintillaWin &);
205 virtual void Initialise();
206 virtual void Finalise();
207 HWND MainHWND();
209 static sptr_t DirectFunction(
210 ScintillaWin *sci, UINT iMessage, uptr_t wParam, sptr_t lParam);
211 static sptr_t PASCAL SWndProc(
212 HWND hWnd, UINT iMessage, WPARAM wParam, sptr_t lParam);
213 static sptr_t PASCAL CTWndProc(
214 HWND hWnd, UINT iMessage, WPARAM wParam, sptr_t lParam);
216 enum { invalidTimerID, standardTimerID, idleTimerID };
218 virtual bool DragThreshold(Point ptStart, Point ptNow);
219 virtual void StartDrag();
220 sptr_t WndPaint(uptr_t wParam);
221 sptr_t HandleComposition(uptr_t wParam, sptr_t lParam);
222 UINT CodePageOfDocument();
223 virtual bool ValidCodePage(int codePage) const;
224 virtual sptr_t DefWndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam);
225 virtual bool SetIdle(bool on);
226 virtual void SetTicking(bool on);
227 virtual void SetMouseCapture(bool on);
228 virtual bool HaveMouseCapture();
229 virtual void SetTrackMouseLeaveEvent(bool on);
230 virtual bool PaintContains(PRectangle rc);
231 virtual void ScrollText(int linesToMove);
232 virtual void UpdateSystemCaret();
233 virtual void SetVerticalScrollPos();
234 virtual void SetHorizontalScrollPos();
235 virtual bool ModifyScrollBars(int nMax, int nPage);
236 virtual void NotifyChange();
237 virtual void NotifyFocus(bool focus);
238 virtual void SetCtrlID(int identifier);
239 virtual int GetCtrlID();
240 virtual void NotifyParent(SCNotification scn);
241 virtual void NotifyDoubleClick(Point pt, bool shift, bool ctrl, bool alt);
242 virtual CaseFolder *CaseFolderForEncoding();
243 virtual std::string CaseMapString(const std::string &s, int caseMapping);
244 virtual void Copy();
245 virtual void CopyAllowLine();
246 virtual bool CanPaste();
247 virtual void Paste();
248 virtual void CreateCallTipWindow(PRectangle rc);
249 virtual void AddToPopUp(const char *label, int cmd = 0, bool enabled = true);
250 virtual void ClaimSelection();
252 // DBCS
253 void ImeStartComposition();
254 void ImeEndComposition();
256 void AddCharBytes(char b0, char b1);
258 void GetIntelliMouseParameters();
259 virtual void CopyToClipboard(const SelectionText &selectedText);
260 void ScrollMessage(WPARAM wParam);
261 void HorizontalScrollMessage(WPARAM wParam);
262 void RealizeWindowPalette(bool inBackGround);
263 void FullPaint();
264 void FullPaintDC(HDC dc);
265 bool IsCompatibleDC(HDC dc);
266 DWORD EffectFromState(DWORD grfKeyState);
268 virtual int SetScrollInfo(int nBar, LPCSCROLLINFO lpsi, BOOL bRedraw);
269 virtual bool GetScrollInfo(int nBar, LPSCROLLINFO lpsi);
270 void ChangeScrollPos(int barType, int pos);
272 void InsertPasteText(const char *text, int len, SelectionPosition selStart, bool isRectangular, bool isLine);
274 public:
275 // Public for benefit of Scintilla_DirectFunction
276 virtual sptr_t WndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam);
278 /// Implement IUnknown
279 STDMETHODIMP QueryInterface(REFIID riid, PVOID *ppv);
280 STDMETHODIMP_(ULONG)AddRef();
281 STDMETHODIMP_(ULONG)Release();
283 /// Implement IDropTarget
284 STDMETHODIMP DragEnter(LPDATAOBJECT pIDataSource, DWORD grfKeyState,
285 POINTL pt, PDWORD pdwEffect);
286 STDMETHODIMP DragOver(DWORD grfKeyState, POINTL pt, PDWORD pdwEffect);
287 STDMETHODIMP DragLeave();
288 STDMETHODIMP Drop(LPDATAOBJECT pIDataSource, DWORD grfKeyState,
289 POINTL pt, PDWORD pdwEffect);
291 /// Implement important part of IDataObject
292 STDMETHODIMP GetData(FORMATETC *pFEIn, STGMEDIUM *pSTM);
294 static bool Register(HINSTANCE hInstance_);
295 static bool Unregister();
297 friend class DropSource;
298 friend class DataObject;
299 friend class DropTarget;
300 bool DragIsRectangularOK(CLIPFORMAT fmt) {
301 return drag.rectangular && (fmt == cfColumnSelect);
304 private:
305 // For use in creating a system caret
306 bool HasCaretSizeChanged();
307 BOOL CreateSystemCaret();
308 BOOL DestroySystemCaret();
309 HBITMAP sysCaretBitmap;
310 int sysCaretWidth;
311 int sysCaretHeight;
312 bool keysAlwaysUnicode;
315 HINSTANCE ScintillaWin::hInstance = 0;
317 ScintillaWin::ScintillaWin(HWND hwnd) {
319 lastKeyDownConsumed = false;
321 capturedMouse = false;
322 trackedMouseLeave = false;
323 TrackMouseEventFn = 0;
325 linesPerScroll = 0;
326 wheelDelta = 0; // Wheel delta from roll
328 hRgnUpdate = 0;
330 hasOKText = false;
332 // There does not seem to be a real standard for indicating that the clipboard
333 // contains a rectangular selection, so copy Developer Studio.
334 cfColumnSelect = static_cast<CLIPFORMAT>(
335 ::RegisterClipboardFormat(TEXT("MSDEVColumnSelect")));
337 // Likewise for line-copy (copies a full line when no text is selected)
338 cfLineSelect = static_cast<CLIPFORMAT>(
339 ::RegisterClipboardFormat(TEXT("MSDEVLineSelect")));
341 hrOle = E_FAIL;
343 wMain = hwnd;
345 dob.sci = this;
346 ds.sci = this;
347 dt.sci = this;
349 sysCaretBitmap = 0;
350 sysCaretWidth = 0;
351 sysCaretHeight = 0;
353 keysAlwaysUnicode = false;
355 caret.period = ::GetCaretBlinkTime();
356 if (caret.period < 0)
357 caret.period = 0;
359 Initialise();
362 ScintillaWin::~ScintillaWin() {}
364 void ScintillaWin::Initialise() {
365 // Initialize COM. If the app has already done this it will have
366 // no effect. If the app hasnt, we really shouldnt ask them to call
367 // it just so this internal feature works.
368 hrOle = ::OleInitialize(NULL);
370 // Find TrackMouseEvent which is available on Windows > 95
371 HMODULE user32 = ::GetModuleHandle(TEXT("user32.dll"));
372 TrackMouseEventFn = (TrackMouseEventSig)::GetProcAddress(user32, "TrackMouseEvent");
373 if (TrackMouseEventFn == NULL) {
374 // Windows 95 has an emulation in comctl32.dll:_TrackMouseEvent
375 HMODULE commctrl32 = ::LoadLibrary(TEXT("comctl32.dll"));
376 if (commctrl32 != NULL) {
377 TrackMouseEventFn = (TrackMouseEventSig)
378 ::GetProcAddress(commctrl32, "_TrackMouseEvent");
383 void ScintillaWin::Finalise() {
384 ScintillaBase::Finalise();
385 SetTicking(false);
386 SetIdle(false);
387 ::RevokeDragDrop(MainHWND());
388 if (SUCCEEDED(hrOle)) {
389 ::OleUninitialize();
393 HWND ScintillaWin::MainHWND() {
394 return reinterpret_cast<HWND>(wMain.GetID());
397 bool ScintillaWin::DragThreshold(Point ptStart, Point ptNow) {
398 int xMove = abs(ptStart.x - ptNow.x);
399 int yMove = abs(ptStart.y - ptNow.y);
400 return (xMove > ::GetSystemMetrics(SM_CXDRAG)) ||
401 (yMove > ::GetSystemMetrics(SM_CYDRAG));
404 void ScintillaWin::StartDrag() {
405 inDragDrop = ddDragging;
406 DWORD dwEffect = 0;
407 dropWentOutside = true;
408 IDataObject *pDataObject = reinterpret_cast<IDataObject *>(&dob);
409 IDropSource *pDropSource = reinterpret_cast<IDropSource *>(&ds);
410 //Platform::DebugPrintf("About to DoDragDrop %x %x\n", pDataObject, pDropSource);
411 HRESULT hr = ::DoDragDrop(
412 pDataObject,
413 pDropSource,
414 DROPEFFECT_COPY | DROPEFFECT_MOVE, &dwEffect);
415 //Platform::DebugPrintf("DoDragDrop = %x\n", hr);
416 if (SUCCEEDED(hr)) {
417 if ((hr == DRAGDROP_S_DROP) && (dwEffect == DROPEFFECT_MOVE) && dropWentOutside) {
418 // Remove dragged out text
419 ClearSelection();
422 inDragDrop = ddNone;
423 SetDragPosition(SelectionPosition(invalidPosition));
426 // Avoid warnings everywhere for old style casts by concentrating them here
427 static WORD LoWord(DWORD l) {
428 return LOWORD(l);
431 static WORD HiWord(DWORD l) {
432 return HIWORD(l);
435 static int InputCodePage() {
436 HKL inputLocale = ::GetKeyboardLayout(0);
437 LANGID inputLang = LOWORD(inputLocale);
438 char sCodePage[10];
439 int res = ::GetLocaleInfoA(MAKELCID(inputLang, SORT_DEFAULT),
440 LOCALE_IDEFAULTANSICODEPAGE, sCodePage, sizeof(sCodePage));
441 if (!res)
442 return 0;
443 return atoi(sCodePage);
446 #ifndef VK_OEM_2
447 static const int VK_OEM_2=0xbf;
448 static const int VK_OEM_3=0xc0;
449 static const int VK_OEM_4=0xdb;
450 static const int VK_OEM_5=0xdc;
451 static const int VK_OEM_6=0xdd;
452 #endif
454 /** Map the key codes to their equivalent SCK_ form. */
455 static int KeyTranslate(int keyIn) {
456 //PLATFORM_ASSERT(!keyIn);
457 switch (keyIn) {
458 case VK_DOWN: return SCK_DOWN;
459 case VK_UP: return SCK_UP;
460 case VK_LEFT: return SCK_LEFT;
461 case VK_RIGHT: return SCK_RIGHT;
462 case VK_HOME: return SCK_HOME;
463 case VK_END: return SCK_END;
464 case VK_PRIOR: return SCK_PRIOR;
465 case VK_NEXT: return SCK_NEXT;
466 case VK_DELETE: return SCK_DELETE;
467 case VK_INSERT: return SCK_INSERT;
468 case VK_ESCAPE: return SCK_ESCAPE;
469 case VK_BACK: return SCK_BACK;
470 case VK_TAB: return SCK_TAB;
471 case VK_RETURN: return SCK_RETURN;
472 case VK_ADD: return SCK_ADD;
473 case VK_SUBTRACT: return SCK_SUBTRACT;
474 case VK_DIVIDE: return SCK_DIVIDE;
475 case VK_LWIN: return SCK_WIN;
476 case VK_RWIN: return SCK_RWIN;
477 case VK_APPS: return SCK_MENU;
478 case VK_OEM_2: return '/';
479 case VK_OEM_3: return '`';
480 case VK_OEM_4: return '[';
481 case VK_OEM_5: return '\\';
482 case VK_OEM_6: return ']';
483 default: return keyIn;
487 LRESULT ScintillaWin::WndPaint(uptr_t wParam) {
488 //ElapsedTime et;
490 // Redirect assertions to debug output and save current state
491 bool assertsPopup = Platform::ShowAssertionPopUps(false);
492 paintState = painting;
493 PAINTSTRUCT ps;
494 PAINTSTRUCT *pps;
496 bool IsOcxCtrl = (wParam != 0); // if wParam != 0, it contains
497 // a PAINSTRUCT* from the OCX
498 // Removed since this interferes with reporting other assertions as it occurs repeatedly
499 //PLATFORM_ASSERT(hRgnUpdate == NULL);
500 hRgnUpdate = ::CreateRectRgn(0, 0, 0, 0);
501 if (IsOcxCtrl) {
502 pps = reinterpret_cast<PAINTSTRUCT*>(wParam);
503 } else {
504 ::GetUpdateRgn(MainHWND(), hRgnUpdate, FALSE);
505 pps = &ps;
506 ::BeginPaint(MainHWND(), pps);
508 AutoSurface surfaceWindow(pps->hdc, this);
509 if (surfaceWindow) {
510 rcPaint = PRectangle(pps->rcPaint.left, pps->rcPaint.top, pps->rcPaint.right, pps->rcPaint.bottom);
511 PRectangle rcClient = GetClientRectangle();
512 paintingAllText = rcPaint.Contains(rcClient);
513 if (paintingAllText) {
514 //Platform::DebugPrintf("Performing full text paint\n");
515 } else {
516 //Platform::DebugPrintf("Performing partial paint %d .. %d\n", rcPaint.top, rcPaint.bottom);
518 Paint(surfaceWindow, rcPaint);
519 surfaceWindow->Release();
521 if (hRgnUpdate) {
522 ::DeleteRgn(hRgnUpdate);
523 hRgnUpdate = 0;
526 if (!IsOcxCtrl)
527 ::EndPaint(MainHWND(), pps);
528 if (paintState == paintAbandoned) {
529 // Painting area was insufficient to cover new styling or brace highlight positions
530 FullPaint();
532 paintState = notPainting;
534 // Restore debug output state
535 Platform::ShowAssertionPopUps(assertsPopup);
537 //Platform::DebugPrintf("Paint took %g\n", et.Duration());
538 return 0l;
541 sptr_t ScintillaWin::HandleComposition(uptr_t wParam, sptr_t lParam) {
542 #ifdef __DMC__
543 // Digital Mars compiler does not include Imm library
544 return 0;
545 #else
546 if (lParam & GCS_RESULTSTR) {
547 HIMC hIMC = ::ImmGetContext(MainHWND());
548 if (hIMC) {
549 const int maxLenInputIME = 200;
550 wchar_t wcs[maxLenInputIME];
551 LONG bytes = ::ImmGetCompositionStringW(hIMC,
552 GCS_RESULTSTR, wcs, (maxLenInputIME-1)*2);
553 int wides = bytes / 2;
554 if (IsUnicodeMode()) {
555 char utfval[maxLenInputIME * 3];
556 unsigned int len = UTF8Length(wcs, wides);
557 UTF8FromUTF16(wcs, wides, utfval, len);
558 utfval[len] = '\0';
559 AddCharUTF(utfval, len);
560 } else {
561 char dbcsval[maxLenInputIME * 2];
562 int size = ::WideCharToMultiByte(InputCodePage(),
563 0, wcs, wides, dbcsval, sizeof(dbcsval) - 1, 0, 0);
564 for (int i=0; i<size; i++) {
565 AddChar(dbcsval[i]);
568 // Set new position after converted
569 Point pos = PointMainCaret();
570 COMPOSITIONFORM CompForm;
571 CompForm.dwStyle = CFS_POINT;
572 CompForm.ptCurrentPos.x = pos.x;
573 CompForm.ptCurrentPos.y = pos.y;
574 ::ImmSetCompositionWindow(hIMC, &CompForm);
575 ::ImmReleaseContext(MainHWND(), hIMC);
577 return 0;
579 return ::DefWindowProc(MainHWND(), WM_IME_COMPOSITION, wParam, lParam);
580 #endif
583 // Translate message IDs from WM_* and EM_* to SCI_* so can partly emulate Windows Edit control
584 static unsigned int SciMessageFromEM(unsigned int iMessage) {
585 switch (iMessage) {
586 case EM_CANPASTE: return SCI_CANPASTE;
587 case EM_CANUNDO: return SCI_CANUNDO;
588 case EM_EMPTYUNDOBUFFER: return SCI_EMPTYUNDOBUFFER;
589 case EM_FINDTEXTEX: return SCI_FINDTEXT;
590 case EM_FORMATRANGE: return SCI_FORMATRANGE;
591 case EM_GETFIRSTVISIBLELINE: return SCI_GETFIRSTVISIBLELINE;
592 case EM_GETLINECOUNT: return SCI_GETLINECOUNT;
593 case EM_GETSELTEXT: return SCI_GETSELTEXT;
594 case EM_GETTEXTRANGE: return SCI_GETTEXTRANGE;
595 case EM_HIDESELECTION: return SCI_HIDESELECTION;
596 case EM_LINEINDEX: return SCI_POSITIONFROMLINE;
597 case EM_LINESCROLL: return SCI_LINESCROLL;
598 case EM_REPLACESEL: return SCI_REPLACESEL;
599 case EM_SCROLLCARET: return SCI_SCROLLCARET;
600 case EM_SETREADONLY: return SCI_SETREADONLY;
601 case WM_CLEAR: return SCI_CLEAR;
602 case WM_COPY: return SCI_COPY;
603 case WM_CUT: return SCI_CUT;
604 case WM_GETTEXT: return SCI_GETTEXT;
605 case WM_SETTEXT: return SCI_SETTEXT;
606 case WM_GETTEXTLENGTH: return SCI_GETTEXTLENGTH;
607 case WM_PASTE: return SCI_PASTE;
608 case WM_UNDO: return SCI_UNDO;
610 return iMessage;
613 static UINT CodePageFromCharSet(DWORD characterSet, UINT documentCodePage) {
614 if (documentCodePage == SC_CP_UTF8) {
615 // The system calls here are a little slow so avoid if known case.
616 return SC_CP_UTF8;
618 CHARSETINFO ci = { 0, 0, { { 0, 0, 0, 0 }, { 0, 0 } } };
619 BOOL bci = ::TranslateCharsetInfo((DWORD*)characterSet,
620 &ci, TCI_SRCCHARSET);
622 UINT cp;
623 if (bci)
624 cp = ci.ciACP;
625 else
626 cp = documentCodePage;
628 CPINFO cpi;
629 if (!IsValidCodePage(cp) && !GetCPInfo(cp, &cpi))
630 cp = CP_ACP;
632 return cp;
635 UINT ScintillaWin::CodePageOfDocument() {
636 return CodePageFromCharSet(vs.styles[STYLE_DEFAULT].characterSet, pdoc->dbcsCodePage);
639 sptr_t ScintillaWin::WndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
640 try {
641 //Platform::DebugPrintf("S M:%x WP:%x L:%x\n", iMessage, wParam, lParam);
642 iMessage = SciMessageFromEM(iMessage);
643 switch (iMessage) {
645 case WM_CREATE:
646 ctrlID = ::GetDlgCtrlID(reinterpret_cast<HWND>(wMain.GetID()));
647 // Get Intellimouse scroll line parameters
648 GetIntelliMouseParameters();
649 ::RegisterDragDrop(MainHWND(), reinterpret_cast<IDropTarget *>(&dt));
650 break;
652 case WM_COMMAND:
653 Command(LoWord(wParam));
654 break;
656 case WM_PAINT:
657 return WndPaint(wParam);
659 case WM_PRINTCLIENT: {
660 HDC hdc = reinterpret_cast<HDC>(wParam);
661 if (!IsCompatibleDC(hdc)) {
662 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
664 FullPaintDC(hdc);
666 break;
668 case WM_VSCROLL:
669 ScrollMessage(wParam);
670 break;
672 case WM_HSCROLL:
673 HorizontalScrollMessage(wParam);
674 break;
676 case WM_SIZE: {
677 //Platform::DebugPrintf("Scintilla WM_SIZE %d %d\n", LoWord(lParam), HiWord(lParam));
678 ChangeSize();
680 break;
682 case WM_MOUSEWHEEL:
683 // if autocomplete list active then send mousewheel message to it
684 if (ac.Active()) {
685 HWND hWnd = reinterpret_cast<HWND>(ac.lb->GetID());
686 ::SendMessage(hWnd, iMessage, wParam, lParam);
687 break;
690 // Don't handle datazoom.
691 // (A good idea for datazoom would be to "fold" or "unfold" details.
692 // i.e. if datazoomed out only class structures are visible, when datazooming in the control
693 // structures appear, then eventually the individual statements...)
694 if (wParam & MK_SHIFT) {
695 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
698 // Either SCROLL or ZOOM. We handle the wheel steppings calculation
699 wheelDelta -= static_cast<short>(HiWord(wParam));
700 if (abs(wheelDelta) >= WHEEL_DELTA && linesPerScroll > 0) {
701 int linesToScroll = linesPerScroll;
702 if (linesPerScroll == WHEEL_PAGESCROLL)
703 linesToScroll = LinesOnScreen() - 1;
704 if (linesToScroll == 0) {
705 linesToScroll = 1;
707 linesToScroll *= (wheelDelta / WHEEL_DELTA);
708 if (wheelDelta >= 0)
709 wheelDelta = wheelDelta % WHEEL_DELTA;
710 else
711 wheelDelta = - (-wheelDelta % WHEEL_DELTA);
713 if (wParam & MK_CONTROL) {
714 // Zoom! We play with the font sizes in the styles.
715 // Number of steps/line is ignored, we just care if sizing up or down
716 if (linesToScroll < 0) {
717 KeyCommand(SCI_ZOOMIN);
718 } else {
719 KeyCommand(SCI_ZOOMOUT);
721 } else {
722 // Scroll
723 ScrollTo(topLine + linesToScroll);
726 return 0;
728 case WM_TIMER:
729 if (wParam == standardTimerID && timer.ticking) {
730 Tick();
731 } else if (wParam == idleTimerID && idler.state) {
732 SendMessage(MainHWND(), SC_WIN_IDLE, 0, 1);
733 } else {
734 return 1;
736 break;
738 case SC_WIN_IDLE:
739 // wParam=dwTickCountInitial, or 0 to initialize. lParam=bSkipUserInputTest
740 if (idler.state) {
741 if (lParam || (WAIT_TIMEOUT == MsgWaitForMultipleObjects(0, 0, 0, 0, QS_INPUT|QS_HOTKEY))) {
742 if (Idle()) {
743 // User input was given priority above, but all events do get a turn. Other
744 // messages, notifications, etc. will get interleaved with the idle messages.
746 // However, some things like WM_PAINT are a lower priority, and will not fire
747 // when there's a message posted. So, several times a second, we stop and let
748 // the low priority events have a turn (after which the timer will fire again).
750 DWORD dwCurrent = GetTickCount();
751 DWORD dwStart = wParam ? wParam : dwCurrent;
753 if (dwCurrent >= dwStart && dwCurrent > 200 && dwCurrent - 200 < dwStart)
754 PostMessage(MainHWND(), SC_WIN_IDLE, dwStart, 0);
755 } else {
756 SetIdle(false);
760 break;
762 case WM_GETMINMAXINFO:
763 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
765 case WM_LBUTTONDOWN: {
766 #ifndef __DMC__
767 // Digital Mars compiler does not include Imm library
768 // For IME, set the composition string as the result string.
769 HIMC hIMC = ::ImmGetContext(MainHWND());
770 ::ImmNotifyIME(hIMC, NI_COMPOSITIONSTR, CPS_COMPLETE, 0);
771 ::ImmReleaseContext(MainHWND(), hIMC);
772 #endif
774 //Platform::DebugPrintf("Buttdown %d %x %x %x %x %x\n",iMessage, wParam, lParam,
775 // Platform::IsKeyDown(VK_SHIFT),
776 // Platform::IsKeyDown(VK_CONTROL),
777 // Platform::IsKeyDown(VK_MENU));
778 ::SetFocus(MainHWND());
779 ButtonDown(Point::FromLong(lParam), ::GetMessageTime(),
780 (wParam & MK_SHIFT) != 0,
781 (wParam & MK_CONTROL) != 0,
782 Platform::IsKeyDown(VK_MENU));
784 break;
786 case WM_MOUSEMOVE:
787 SetTrackMouseLeaveEvent(true);
788 ButtonMove(Point::FromLong(lParam));
789 break;
791 case WM_MOUSELEAVE:
792 SetTrackMouseLeaveEvent(false);
793 MouseLeave();
794 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
796 case WM_LBUTTONUP:
797 ButtonUp(Point::FromLong(lParam),
798 ::GetMessageTime(),
799 (wParam & MK_CONTROL) != 0);
800 break;
802 case WM_RBUTTONDOWN:
803 ::SetFocus(MainHWND());
804 if (!PointInSelection(Point::FromLong(lParam))) {
805 CancelModes();
806 SetEmptySelection(PositionFromLocation(Point::FromLong(lParam)));
808 break;
810 case WM_SETCURSOR:
811 if (LoWord(lParam) == HTCLIENT) {
812 if (inDragDrop == ddDragging) {
813 DisplayCursor(Window::cursorUp);
814 } else {
815 // Display regular (drag) cursor over selection
816 POINT pt;
817 ::GetCursorPos(&pt);
818 ::ScreenToClient(MainHWND(), &pt);
819 if (PointInSelMargin(Point(pt.x, pt.y))) {
820 DisplayCursor(GetMarginCursor(Point(pt.x, pt.y)));
821 } else if (PointInSelection(Point(pt.x, pt.y)) && !SelectionEmpty()) {
822 DisplayCursor(Window::cursorArrow);
823 } else if (PointIsHotspot(Point(pt.x, pt.y))) {
824 DisplayCursor(Window::cursorHand);
825 } else {
826 DisplayCursor(Window::cursorText);
829 return TRUE;
830 } else {
831 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
834 case WM_CHAR:
835 if (((wParam >= 128) || !iscntrl(wParam)) || !lastKeyDownConsumed) {
836 if (::IsWindowUnicode(MainHWND()) || keysAlwaysUnicode) {
837 wchar_t wcs[2] = {wParam, 0};
838 if (IsUnicodeMode()) {
839 // For a wide character version of the window:
840 char utfval[4];
841 unsigned int len = UTF8Length(wcs, 1);
842 UTF8FromUTF16(wcs, 1, utfval, len);
843 AddCharUTF(utfval, len);
844 } else {
845 UINT cpDest = CodePageOfDocument();
846 char inBufferCP[20];
847 int size = ::WideCharToMultiByte(cpDest,
848 0, wcs, 1, inBufferCP, sizeof(inBufferCP) - 1, 0, 0);
849 inBufferCP[size] = '\0';
850 AddCharUTF(inBufferCP, size);
852 } else {
853 if (IsUnicodeMode()) {
854 AddCharBytes('\0', LOBYTE(wParam));
855 } else {
856 AddChar(LOBYTE(wParam));
860 return 0;
862 case WM_UNICHAR:
863 if (wParam == UNICODE_NOCHAR) {
864 return IsUnicodeMode() ? 1 : 0;
865 } else if (lastKeyDownConsumed) {
866 return 1;
867 } else {
868 if (IsUnicodeMode()) {
869 char utfval[4];
870 wchar_t wcs[2] = {static_cast<wchar_t>(wParam), 0};
871 unsigned int len = UTF8Length(wcs, 1);
872 UTF8FromUTF16(wcs, 1, utfval, len);
873 AddCharUTF(utfval, len);
874 return 1;
875 } else {
876 return 0;
880 case WM_SYSKEYDOWN:
881 case WM_KEYDOWN: {
882 //Platform::DebugPrintf("S keydown %d %x %x %x %x\n",iMessage, wParam, lParam, ::IsKeyDown(VK_SHIFT), ::IsKeyDown(VK_CONTROL));
883 lastKeyDownConsumed = false;
884 int ret = KeyDown(KeyTranslate(wParam),
885 Platform::IsKeyDown(VK_SHIFT),
886 Platform::IsKeyDown(VK_CONTROL),
887 Platform::IsKeyDown(VK_MENU),
888 &lastKeyDownConsumed);
889 if (!ret && !lastKeyDownConsumed) {
890 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
892 break;
895 case WM_IME_KEYDOWN:
896 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
898 case WM_KEYUP:
899 //Platform::DebugPrintf("S keyup %d %x %x\n",iMessage, wParam, lParam);
900 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
902 case WM_SETTINGCHANGE:
903 //Platform::DebugPrintf("Setting Changed\n");
904 InvalidateStyleData();
905 // Get Intellimouse scroll line parameters
906 GetIntelliMouseParameters();
907 break;
909 case WM_GETDLGCODE:
910 return DLGC_HASSETSEL | DLGC_WANTALLKEYS;
912 case WM_KILLFOCUS: {
913 HWND wOther = reinterpret_cast<HWND>(wParam);
914 HWND wThis = MainHWND();
915 HWND wCT = reinterpret_cast<HWND>(ct.wCallTip.GetID());
916 if (!wParam ||
917 !(::IsChild(wThis, wOther) || (wOther == wCT))) {
918 SetFocusState(false);
919 DestroySystemCaret();
922 //RealizeWindowPalette(true);
923 break;
925 case WM_SETFOCUS:
926 SetFocusState(true);
927 RealizeWindowPalette(false);
928 DestroySystemCaret();
929 CreateSystemCaret();
930 break;
932 case WM_SYSCOLORCHANGE:
933 //Platform::DebugPrintf("Setting Changed\n");
934 InvalidateStyleData();
935 break;
937 case WM_PALETTECHANGED:
938 if (wParam != reinterpret_cast<uptr_t>(MainHWND())) {
939 //Platform::DebugPrintf("** Palette Changed\n");
940 RealizeWindowPalette(true);
942 break;
944 case WM_QUERYNEWPALETTE:
945 //Platform::DebugPrintf("** Query palette\n");
946 RealizeWindowPalette(false);
947 break;
949 case WM_IME_STARTCOMPOSITION: // dbcs
950 ImeStartComposition();
951 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
953 case WM_IME_ENDCOMPOSITION: // dbcs
954 ImeEndComposition();
955 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
957 case WM_IME_COMPOSITION:
958 return HandleComposition(wParam, lParam);
960 case WM_IME_CHAR: {
961 AddCharBytes(HIBYTE(wParam), LOBYTE(wParam));
962 return 0;
965 case WM_CONTEXTMENU:
966 if (displayPopupMenu) {
967 Point pt = Point::FromLong(lParam);
968 if ((pt.x == -1) && (pt.y == -1)) {
969 // Caused by keyboard so display menu near caret
970 pt = PointMainCaret();
971 POINT spt = {pt.x, pt.y};
972 ::ClientToScreen(MainHWND(), &spt);
973 pt = Point(spt.x, spt.y);
975 ContextMenu(pt);
976 return 0;
978 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
980 case WM_INPUTLANGCHANGE:
981 //::SetThreadLocale(LOWORD(lParam));
982 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
984 case WM_INPUTLANGCHANGEREQUEST:
985 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
987 case WM_ERASEBKGND:
988 return 1; // Avoid any background erasure as whole window painted.
990 case WM_CAPTURECHANGED:
991 capturedMouse = false;
992 return 0;
994 // These are not handled in Scintilla and its faster to dispatch them here.
995 // Also moves time out to here so profile doesn't count lots of empty message calls.
997 case WM_MOVE:
998 case WM_MOUSEACTIVATE:
999 case WM_NCHITTEST:
1000 case WM_NCCALCSIZE:
1001 case WM_NCPAINT:
1002 case WM_NCMOUSEMOVE:
1003 case WM_NCLBUTTONDOWN:
1004 case WM_IME_SETCONTEXT:
1005 case WM_IME_NOTIFY:
1006 case WM_SYSCOMMAND:
1007 case WM_WINDOWPOSCHANGING:
1008 case WM_WINDOWPOSCHANGED:
1009 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1011 case EM_LINEFROMCHAR:
1012 if (static_cast<int>(wParam) < 0) {
1013 wParam = SelectionStart().Position();
1015 return pdoc->LineFromPosition(wParam);
1017 case EM_EXLINEFROMCHAR:
1018 return pdoc->LineFromPosition(lParam);
1020 case EM_GETSEL:
1021 if (wParam) {
1022 *reinterpret_cast<int *>(wParam) = SelectionStart().Position();
1024 if (lParam) {
1025 *reinterpret_cast<int *>(lParam) = SelectionEnd().Position();
1027 return MAKELONG(SelectionStart().Position(), SelectionEnd().Position());
1029 case EM_EXGETSEL: {
1030 if (lParam == 0) {
1031 return 0;
1033 Sci_CharacterRange *pCR = reinterpret_cast<Sci_CharacterRange *>(lParam);
1034 pCR->cpMin = SelectionStart().Position();
1035 pCR->cpMax = SelectionEnd().Position();
1037 break;
1039 case EM_SETSEL: {
1040 int nStart = static_cast<int>(wParam);
1041 int nEnd = static_cast<int>(lParam);
1042 if (nStart == 0 && nEnd == -1) {
1043 nEnd = pdoc->Length();
1045 if (nStart == -1) {
1046 nStart = nEnd; // Remove selection
1048 if (nStart > nEnd) {
1049 SetSelection(nEnd, nStart);
1050 } else {
1051 SetSelection(nStart, nEnd);
1053 EnsureCaretVisible();
1055 break;
1057 case EM_EXSETSEL: {
1058 if (lParam == 0) {
1059 return 0;
1061 Sci_CharacterRange *pCR = reinterpret_cast<Sci_CharacterRange *>(lParam);
1062 sel.selType = Selection::selStream;
1063 if (pCR->cpMin == 0 && pCR->cpMax == -1) {
1064 SetSelection(pCR->cpMin, pdoc->Length());
1065 } else {
1066 SetSelection(pCR->cpMin, pCR->cpMax);
1068 EnsureCaretVisible();
1069 return pdoc->LineFromPosition(SelectionStart().Position());
1072 case SCI_GETDIRECTFUNCTION:
1073 return reinterpret_cast<sptr_t>(DirectFunction);
1075 case SCI_GETDIRECTPOINTER:
1076 return reinterpret_cast<sptr_t>(this);
1078 case SCI_GRABFOCUS:
1079 ::SetFocus(MainHWND());
1080 break;
1082 case SCI_SETKEYSUNICODE:
1083 keysAlwaysUnicode = wParam != 0;
1084 break;
1086 case SCI_GETKEYSUNICODE:
1087 return keysAlwaysUnicode;
1089 #ifdef SCI_LEXER
1090 case SCI_LOADLEXERLIBRARY:
1091 LexerManager::GetInstance()->Load(reinterpret_cast<const char *>(lParam));
1092 break;
1093 #endif
1095 default:
1096 return ScintillaBase::WndProc(iMessage, wParam, lParam);
1098 } catch (std::bad_alloc &) {
1099 errorStatus = SC_STATUS_BADALLOC;
1100 } catch (...) {
1101 errorStatus = SC_STATUS_FAILURE;
1103 return 0l;
1106 bool ScintillaWin::ValidCodePage(int codePage) const {
1107 return codePage == 0 || codePage == SC_CP_UTF8 ||
1108 codePage == 932 || codePage == 936 || codePage == 949 ||
1109 codePage == 950 || codePage == 1361;
1112 sptr_t ScintillaWin::DefWndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
1113 return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
1116 void ScintillaWin::SetTicking(bool on) {
1117 if (timer.ticking != on) {
1118 timer.ticking = on;
1119 if (timer.ticking) {
1120 timer.tickerID = ::SetTimer(MainHWND(), standardTimerID, timer.tickSize, NULL)
1121 ? reinterpret_cast<TickerID>(standardTimerID) : 0;
1122 } else {
1123 ::KillTimer(MainHWND(), reinterpret_cast<uptr_t>(timer.tickerID));
1124 timer.tickerID = 0;
1127 timer.ticksToWait = caret.period;
1130 bool ScintillaWin::SetIdle(bool on) {
1131 // On Win32 the Idler is implemented as a Timer on the Scintilla window. This
1132 // takes advantage of the fact that WM_TIMER messages are very low priority,
1133 // and are only posted when the message queue is empty, i.e. during idle time.
1134 if (idler.state != on) {
1135 if (on) {
1136 idler.idlerID = ::SetTimer(MainHWND(), idleTimerID, 10, NULL)
1137 ? reinterpret_cast<IdlerID>(idleTimerID) : 0;
1138 } else {
1139 ::KillTimer(MainHWND(), reinterpret_cast<uptr_t>(idler.idlerID));
1140 idler.idlerID = 0;
1142 idler.state = idler.idlerID != 0;
1144 return idler.state;
1147 void ScintillaWin::SetMouseCapture(bool on) {
1148 if (mouseDownCaptures) {
1149 if (on) {
1150 ::SetCapture(MainHWND());
1151 } else {
1152 ::ReleaseCapture();
1155 capturedMouse = on;
1158 bool ScintillaWin::HaveMouseCapture() {
1159 // Cannot just see if GetCapture is this window as the scroll bar also sets capture for the window
1160 return capturedMouse;
1161 //return capturedMouse && (::GetCapture() == MainHWND());
1164 void ScintillaWin::SetTrackMouseLeaveEvent(bool on) {
1165 if (on && TrackMouseEventFn && !trackedMouseLeave) {
1166 TRACKMOUSEEVENT tme;
1167 tme.cbSize = sizeof(tme);
1168 tme.dwFlags = TME_LEAVE;
1169 tme.hwndTrack = MainHWND();
1170 TrackMouseEventFn(&tme);
1172 trackedMouseLeave = on;
1175 bool ScintillaWin::PaintContains(PRectangle rc) {
1176 bool contains = true;
1177 if ((paintState == painting) && (!rc.Empty())) {
1178 if (!rcPaint.Contains(rc)) {
1179 contains = false;
1180 } else {
1181 // In bounding rectangle so check more accurately using region
1182 HRGN hRgnRange = ::CreateRectRgn(rc.left, rc.top, rc.right, rc.bottom);
1183 if (hRgnRange) {
1184 HRGN hRgnDest = ::CreateRectRgn(0, 0, 0, 0);
1185 if (hRgnDest) {
1186 int combination = ::CombineRgn(hRgnDest, hRgnRange, hRgnUpdate, RGN_DIFF);
1187 if (combination != NULLREGION) {
1188 contains = false;
1190 ::DeleteRgn(hRgnDest);
1192 ::DeleteRgn(hRgnRange);
1196 return contains;
1199 void ScintillaWin::ScrollText(int linesToMove) {
1200 //Platform::DebugPrintf("ScintillaWin::ScrollText %d\n", linesToMove);
1201 ::ScrollWindow(MainHWND(), 0,
1202 vs.lineHeight * linesToMove, 0, 0);
1203 ::UpdateWindow(MainHWND());
1206 void ScintillaWin::UpdateSystemCaret() {
1207 if (hasFocus) {
1208 if (HasCaretSizeChanged()) {
1209 DestroySystemCaret();
1210 CreateSystemCaret();
1212 Point pos = PointMainCaret();
1213 ::SetCaretPos(pos.x, pos.y);
1217 int ScintillaWin::SetScrollInfo(int nBar, LPCSCROLLINFO lpsi, BOOL bRedraw) {
1218 return ::SetScrollInfo(MainHWND(), nBar, lpsi, bRedraw);
1221 bool ScintillaWin::GetScrollInfo(int nBar, LPSCROLLINFO lpsi) {
1222 return ::GetScrollInfo(MainHWND(), nBar, lpsi) ? true : false;
1225 // Change the scroll position but avoid repaint if changing to same value
1226 void ScintillaWin::ChangeScrollPos(int barType, int pos) {
1227 SCROLLINFO sci = {
1228 sizeof(sci), 0, 0, 0, 0, 0, 0
1230 sci.fMask = SIF_POS;
1231 GetScrollInfo(barType, &sci);
1232 if (sci.nPos != pos) {
1233 DwellEnd(true);
1234 sci.nPos = pos;
1235 SetScrollInfo(barType, &sci, TRUE);
1239 void ScintillaWin::SetVerticalScrollPos() {
1240 ChangeScrollPos(SB_VERT, topLine);
1243 void ScintillaWin::SetHorizontalScrollPos() {
1244 ChangeScrollPos(SB_HORZ, xOffset);
1247 bool ScintillaWin::ModifyScrollBars(int nMax, int nPage) {
1248 bool modified = false;
1249 SCROLLINFO sci = {
1250 sizeof(sci), 0, 0, 0, 0, 0, 0
1252 sci.fMask = SIF_PAGE | SIF_RANGE;
1253 GetScrollInfo(SB_VERT, &sci);
1254 int vertEndPreferred = nMax;
1255 if (!verticalScrollBarVisible)
1256 vertEndPreferred = 0;
1257 if ((sci.nMin != 0) ||
1258 (sci.nMax != vertEndPreferred) ||
1259 (sci.nPage != static_cast<unsigned int>(nPage)) ||
1260 (sci.nPos != 0)) {
1261 //Platform::DebugPrintf("Scroll info changed %d %d %d %d %d\n",
1262 // sci.nMin, sci.nMax, sci.nPage, sci.nPos, sci.nTrackPos);
1263 sci.fMask = SIF_PAGE | SIF_RANGE;
1264 sci.nMin = 0;
1265 sci.nMax = vertEndPreferred;
1266 sci.nPage = nPage;
1267 sci.nPos = 0;
1268 sci.nTrackPos = 1;
1269 SetScrollInfo(SB_VERT, &sci, TRUE);
1270 modified = true;
1273 PRectangle rcText = GetTextRectangle();
1274 int horizEndPreferred = scrollWidth;
1275 if (horizEndPreferred < 0)
1276 horizEndPreferred = 0;
1277 if (!horizontalScrollBarVisible || (wrapState != eWrapNone))
1278 horizEndPreferred = 0;
1279 unsigned int pageWidth = rcText.Width();
1280 sci.fMask = SIF_PAGE | SIF_RANGE;
1281 GetScrollInfo(SB_HORZ, &sci);
1282 if ((sci.nMin != 0) ||
1283 (sci.nMax != horizEndPreferred) ||
1284 (sci.nPage != pageWidth) ||
1285 (sci.nPos != 0)) {
1286 sci.fMask = SIF_PAGE | SIF_RANGE;
1287 sci.nMin = 0;
1288 sci.nMax = horizEndPreferred;
1289 sci.nPage = pageWidth;
1290 sci.nPos = 0;
1291 sci.nTrackPos = 1;
1292 SetScrollInfo(SB_HORZ, &sci, TRUE);
1293 modified = true;
1294 if (scrollWidth < static_cast<int>(pageWidth)) {
1295 HorizontalScrollTo(0);
1298 return modified;
1301 void ScintillaWin::NotifyChange() {
1302 ::SendMessage(::GetParent(MainHWND()), WM_COMMAND,
1303 MAKELONG(GetCtrlID(), SCEN_CHANGE),
1304 reinterpret_cast<LPARAM>(MainHWND()));
1307 void ScintillaWin::NotifyFocus(bool focus) {
1308 ::SendMessage(::GetParent(MainHWND()), WM_COMMAND,
1309 MAKELONG(GetCtrlID(), focus ? SCEN_SETFOCUS : SCEN_KILLFOCUS),
1310 reinterpret_cast<LPARAM>(MainHWND()));
1313 void ScintillaWin::SetCtrlID(int identifier) {
1314 ::SetWindowID(reinterpret_cast<HWND>(wMain.GetID()), identifier);
1317 int ScintillaWin::GetCtrlID() {
1318 return ::GetDlgCtrlID(reinterpret_cast<HWND>(wMain.GetID()));
1321 void ScintillaWin::NotifyParent(SCNotification scn) {
1322 scn.nmhdr.hwndFrom = MainHWND();
1323 scn.nmhdr.idFrom = GetCtrlID();
1324 ::SendMessage(::GetParent(MainHWND()), WM_NOTIFY,
1325 GetCtrlID(), reinterpret_cast<LPARAM>(&scn));
1328 void ScintillaWin::NotifyDoubleClick(Point pt, bool shift, bool ctrl, bool alt) {
1329 //Platform::DebugPrintf("ScintillaWin Double click 0\n");
1330 ScintillaBase::NotifyDoubleClick(pt, shift, ctrl, alt);
1331 // Send myself a WM_LBUTTONDBLCLK, so the container can handle it too.
1332 ::SendMessage(MainHWND(),
1333 WM_LBUTTONDBLCLK,
1334 shift ? MK_SHIFT : 0,
1335 MAKELPARAM(pt.x, pt.y));
1338 class CaseFolderUTF8 : public CaseFolderTable {
1339 // Allocate the expandable storage here so that it does not need to be reallocated
1340 // for each call to Fold.
1341 std::vector<wchar_t> utf16Mixed;
1342 std::vector<wchar_t> utf16Folded;
1343 public:
1344 CaseFolderUTF8() {
1345 StandardASCII();
1347 virtual size_t Fold(char *folded, size_t sizeFolded, const char *mixed, size_t lenMixed) {
1348 if ((lenMixed == 1) && (sizeFolded > 0)) {
1349 folded[0] = mapping[static_cast<unsigned char>(mixed[0])];
1350 return 1;
1351 } else {
1352 if (lenMixed > utf16Mixed.size()) {
1353 utf16Mixed.resize(lenMixed + 8);
1355 size_t nUtf16Mixed = ::MultiByteToWideChar(65001, 0, mixed,
1356 static_cast<int>(lenMixed),
1357 &utf16Mixed[0],
1358 static_cast<int>(utf16Mixed.size()));
1360 if (nUtf16Mixed == 0) {
1361 // Failed to convert -> bad UTF-8
1362 folded[0] = '\0';
1363 return 1;
1366 if (nUtf16Mixed * 4 > utf16Folded.size()) { // Maximum folding expansion factor of 4
1367 utf16Folded.resize(nUtf16Mixed * 4 + 8);
1369 int lenFlat = ::LCMapStringW(LOCALE_SYSTEM_DEFAULT,
1370 LCMAP_LINGUISTIC_CASING | LCMAP_LOWERCASE,
1371 &utf16Mixed[0],
1372 static_cast<int>(nUtf16Mixed),
1373 &utf16Folded[0],
1374 static_cast<int>(utf16Folded.size()));
1376 size_t lenOut = UTF8Length(&utf16Folded[0], lenFlat);
1377 if (lenOut < sizeFolded) {
1378 UTF8FromUTF16(&utf16Folded[0], lenFlat, folded, static_cast<int>(lenOut));
1379 return lenOut;
1380 } else {
1381 return 0;
1387 class CaseFolderDBCS : public CaseFolderTable {
1388 // Allocate the expandable storage here so that it does not need to be reallocated
1389 // for each call to Fold.
1390 std::vector<wchar_t> utf16Mixed;
1391 std::vector<wchar_t> utf16Folded;
1392 UINT cp;
1393 public:
1394 CaseFolderDBCS(UINT cp_) : cp(cp_) {
1395 StandardASCII();
1397 virtual size_t Fold(char *folded, size_t sizeFolded, const char *mixed, size_t lenMixed) {
1398 if ((lenMixed == 1) && (sizeFolded > 0)) {
1399 folded[0] = mapping[static_cast<unsigned char>(mixed[0])];
1400 return 1;
1401 } else {
1402 if (lenMixed > utf16Mixed.size()) {
1403 utf16Mixed.resize(lenMixed + 8);
1405 size_t nUtf16Mixed = ::MultiByteToWideChar(cp, 0, mixed,
1406 static_cast<int>(lenMixed),
1407 &utf16Mixed[0],
1408 static_cast<int>(utf16Mixed.size()));
1410 if (nUtf16Mixed == 0) {
1411 // Failed to convert -> bad input
1412 folded[0] = '\0';
1413 return 1;
1416 if (nUtf16Mixed * 4 > utf16Folded.size()) { // Maximum folding expansion factor of 4
1417 utf16Folded.resize(nUtf16Mixed * 4 + 8);
1419 int lenFlat = ::LCMapStringW(LOCALE_SYSTEM_DEFAULT,
1420 LCMAP_LINGUISTIC_CASING | LCMAP_LOWERCASE,
1421 &utf16Mixed[0],
1422 static_cast<int>(nUtf16Mixed),
1423 &utf16Folded[0],
1424 static_cast<int>(utf16Folded.size()));
1426 size_t lenOut = ::WideCharToMultiByte(cp, 0,
1427 &utf16Folded[0], lenFlat,
1428 NULL, 0, NULL, 0);
1430 if (lenOut < sizeFolded) {
1431 ::WideCharToMultiByte(cp, 0,
1432 &utf16Folded[0], lenFlat,
1433 folded, static_cast<int>(lenOut), NULL, 0);
1434 return lenOut;
1435 } else {
1436 return 0;
1442 CaseFolder *ScintillaWin::CaseFolderForEncoding() {
1443 UINT cpDest = CodePageOfDocument();
1444 if (cpDest == SC_CP_UTF8) {
1445 return new CaseFolderUTF8();
1446 } else {
1447 if (pdoc->dbcsCodePage == 0) {
1448 CaseFolderTable *pcf = new CaseFolderTable();
1449 pcf->StandardASCII();
1450 // Only for single byte encodings
1451 UINT cpDoc = CodePageOfDocument();
1452 for (int i=0x80; i<0x100; i++) {
1453 char sCharacter[2] = "A";
1454 sCharacter[0] = static_cast<char>(i);
1455 wchar_t wCharacter[20];
1456 unsigned int lengthUTF16 = ::MultiByteToWideChar(cpDoc, 0, sCharacter, 1,
1457 wCharacter, sizeof(wCharacter)/sizeof(wCharacter[0]));
1458 if (lengthUTF16 == 1) {
1459 wchar_t wLower[20];
1460 int charsConverted = ::LCMapStringW(LOCALE_SYSTEM_DEFAULT,
1461 LCMAP_LINGUISTIC_CASING | LCMAP_LOWERCASE,
1462 wCharacter, lengthUTF16, wLower, sizeof(wLower)/sizeof(wLower[0]));
1463 char sCharacterLowered[20];
1464 unsigned int lengthConverted = ::WideCharToMultiByte(cpDoc, 0,
1465 wLower, charsConverted,
1466 sCharacterLowered, sizeof(sCharacterLowered), NULL, 0);
1467 if ((lengthConverted == 1) && (sCharacter[0] != sCharacterLowered[0])) {
1468 pcf->SetTranslation(sCharacter[0], sCharacterLowered[0]);
1472 return pcf;
1473 } else {
1474 return new CaseFolderDBCS(cpDest);
1479 std::string ScintillaWin::CaseMapString(const std::string &s, int caseMapping) {
1480 if (s.size() == 0)
1481 return std::string();
1483 if (caseMapping == cmSame)
1484 return s;
1486 UINT cpDoc = CodePageOfDocument();
1488 unsigned int lengthUTF16 = ::MultiByteToWideChar(cpDoc, 0, s.c_str(),
1489 static_cast<int>(s.size()), NULL, 0);
1490 if (lengthUTF16 == 0) // Failed to convert
1491 return s;
1493 DWORD mapFlags = LCMAP_LINGUISTIC_CASING |
1494 ((caseMapping == cmUpper) ? LCMAP_UPPERCASE : LCMAP_LOWERCASE);
1496 // Many conversions performed by search function are short so optimize this case.
1497 enum { shortSize=20 };
1499 if (s.size() > shortSize) {
1500 // Use dynamic allocations for long strings
1502 // Change text to UTF-16
1503 std::vector<wchar_t> vwcText(lengthUTF16);
1504 ::MultiByteToWideChar(cpDoc, 0, s.c_str(), static_cast<int>(s.size()), &vwcText[0], lengthUTF16);
1506 // Change case
1507 int charsConverted = ::LCMapStringW(LOCALE_SYSTEM_DEFAULT, mapFlags,
1508 &vwcText[0], lengthUTF16, NULL, 0);
1509 std::vector<wchar_t> vwcConverted(charsConverted);
1510 ::LCMapStringW(LOCALE_SYSTEM_DEFAULT, mapFlags,
1511 &vwcText[0], lengthUTF16, &vwcConverted[0], charsConverted);
1513 // Change back to document encoding
1514 unsigned int lengthConverted = ::WideCharToMultiByte(cpDoc, 0,
1515 &vwcConverted[0], static_cast<int>(vwcConverted.size()),
1516 NULL, 0, NULL, 0);
1517 std::vector<char> vcConverted(lengthConverted);
1518 ::WideCharToMultiByte(cpDoc, 0,
1519 &vwcConverted[0], static_cast<int>(vwcConverted.size()),
1520 &vcConverted[0], static_cast<int>(vcConverted.size()), NULL, 0);
1522 return std::string(&vcConverted[0], vcConverted.size());
1524 } else {
1525 // Use static allocations for short strings as much faster
1526 // A factor of 15 for single character strings
1528 // Change text to UTF-16
1529 wchar_t vwcText[shortSize];
1530 ::MultiByteToWideChar(cpDoc, 0, s.c_str(), static_cast<int>(s.size()),
1531 vwcText, lengthUTF16);
1533 // Change case
1534 int charsConverted = ::LCMapStringW(LOCALE_SYSTEM_DEFAULT, mapFlags,
1535 vwcText, lengthUTF16, NULL, 0);
1536 // Full mapping may produce up to 3 characters per input character
1537 wchar_t vwcConverted[shortSize*3];
1538 ::LCMapStringW(LOCALE_SYSTEM_DEFAULT, mapFlags, vwcText, lengthUTF16,
1539 vwcConverted, charsConverted);
1541 // Change back to document encoding
1542 unsigned int lengthConverted = ::WideCharToMultiByte(cpDoc, 0,
1543 vwcConverted, charsConverted,
1544 NULL, 0, NULL, 0);
1545 // Each UTF-16 code unit may need up to 3 bytes in UTF-8
1546 char vcConverted[shortSize * 3 * 3];
1547 ::WideCharToMultiByte(cpDoc, 0,
1548 vwcConverted, charsConverted,
1549 vcConverted, lengthConverted, NULL, 0);
1551 return std::string(vcConverted, lengthConverted);
1555 void ScintillaWin::Copy() {
1556 //Platform::DebugPrintf("Copy\n");
1557 if (!sel.Empty()) {
1558 SelectionText selectedText;
1559 CopySelectionRange(&selectedText);
1560 CopyToClipboard(selectedText);
1564 void ScintillaWin::CopyAllowLine() {
1565 SelectionText selectedText;
1566 CopySelectionRange(&selectedText, true);
1567 CopyToClipboard(selectedText);
1570 bool ScintillaWin::CanPaste() {
1571 if (!Editor::CanPaste())
1572 return false;
1573 if (::IsClipboardFormatAvailable(CF_TEXT))
1574 return true;
1575 if (IsUnicodeMode())
1576 return ::IsClipboardFormatAvailable(CF_UNICODETEXT) != 0;
1577 return false;
1580 class GlobalMemory {
1581 HGLOBAL hand;
1582 public:
1583 void *ptr;
1584 GlobalMemory() : hand(0), ptr(0) {
1586 GlobalMemory(HGLOBAL hand_) : hand(hand_), ptr(0) {
1587 if (hand) {
1588 ptr = ::GlobalLock(hand);
1591 ~GlobalMemory() {
1592 PLATFORM_ASSERT(!ptr);
1594 void Allocate(size_t bytes) {
1595 hand = ::GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT, bytes);
1596 if (hand) {
1597 ptr = ::GlobalLock(hand);
1600 HGLOBAL Unlock() {
1601 PLATFORM_ASSERT(ptr);
1602 HGLOBAL handCopy = hand;
1603 ::GlobalUnlock(hand);
1604 ptr = 0;
1605 hand = 0;
1606 return handCopy;
1608 void SetClip(UINT uFormat) {
1609 ::SetClipboardData(uFormat, Unlock());
1611 operator bool() const {
1612 return ptr != 0;
1614 SIZE_T Size() {
1615 return ::GlobalSize(hand);
1619 void ScintillaWin::InsertPasteText(const char *text, int len, SelectionPosition selStart, bool isRectangular, bool isLine) {
1620 if (isRectangular) {
1621 PasteRectangular(selStart, text, len);
1622 } else {
1623 char *convertedText = 0;
1624 if (convertPastes) {
1625 // Convert line endings of the paste into our local line-endings mode
1626 convertedText = Document::TransformLineEnds(&len, text, len, pdoc->eolMode);
1627 text = convertedText;
1629 if (isLine) {
1630 int insertPos = pdoc->LineStart(pdoc->LineFromPosition(sel.MainCaret()));
1631 pdoc->InsertString(insertPos, text, len);
1632 // add the newline if necessary
1633 if ((len > 0) && (text[len-1] != '\n' && text[len-1] != '\r')) {
1634 const char *endline = StringFromEOLMode(pdoc->eolMode);
1635 pdoc->InsertString(insertPos + len, endline, static_cast<int>(strlen(endline)));
1636 len += static_cast<int>(strlen(endline));
1638 if (sel.MainCaret() == insertPos) {
1639 SetEmptySelection(sel.MainCaret() + len);
1641 } else {
1642 InsertPaste(selStart, text, len);
1644 delete []convertedText;
1648 void ScintillaWin::Paste() {
1649 if (!::OpenClipboard(MainHWND()))
1650 return;
1651 UndoGroup ug(pdoc);
1652 bool isLine = SelectionEmpty() && (::IsClipboardFormatAvailable(cfLineSelect) != 0);
1653 ClearSelection(multiPasteMode == SC_MULTIPASTE_EACH);
1654 SelectionPosition selStart = sel.IsRectangular() ?
1655 sel.Rectangular().Start() :
1656 sel.Range(sel.Main()).Start();
1657 bool isRectangular = ::IsClipboardFormatAvailable(cfColumnSelect) != 0;
1659 // Always use CF_UNICODETEXT if available
1660 GlobalMemory memUSelection(::GetClipboardData(CF_UNICODETEXT));
1661 if (memUSelection) {
1662 wchar_t *uptr = static_cast<wchar_t *>(memUSelection.ptr);
1663 if (uptr) {
1664 unsigned int len;
1665 char *putf;
1666 // Default Scintilla behaviour in Unicode mode
1667 if (IsUnicodeMode()) {
1668 unsigned int bytes = memUSelection.Size();
1669 len = UTF8Length(uptr, bytes / 2);
1670 putf = new char[len + 1];
1671 UTF8FromUTF16(uptr, bytes / 2, putf, len);
1672 } else {
1673 // CF_UNICODETEXT available, but not in Unicode mode
1674 // Convert from Unicode to current Scintilla code page
1675 UINT cpDest = CodePageOfDocument();
1676 len = ::WideCharToMultiByte(cpDest, 0, uptr, -1,
1677 NULL, 0, NULL, NULL) - 1; // subtract 0 terminator
1678 putf = new char[len + 1];
1679 ::WideCharToMultiByte(cpDest, 0, uptr, -1,
1680 putf, len + 1, NULL, NULL);
1683 InsertPasteText(putf, len, selStart, isRectangular, isLine);
1684 delete []putf;
1686 memUSelection.Unlock();
1687 } else {
1688 // CF_UNICODETEXT not available, paste ANSI text
1689 GlobalMemory memSelection(::GetClipboardData(CF_TEXT));
1690 if (memSelection) {
1691 char *ptr = static_cast<char *>(memSelection.ptr);
1692 if (ptr) {
1693 unsigned int bytes = memSelection.Size();
1694 unsigned int len = bytes;
1695 for (unsigned int i = 0; i < bytes; i++) {
1696 if ((len == bytes) && (0 == ptr[i]))
1697 len = i;
1700 // In Unicode mode, convert clipboard text to UTF-8
1701 if (IsUnicodeMode()) {
1702 wchar_t *uptr = new wchar_t[len+1];
1704 unsigned int ulen = ::MultiByteToWideChar(CP_ACP, 0,
1705 ptr, len, uptr, len+1);
1707 unsigned int mlen = UTF8Length(uptr, ulen);
1708 char *putf = new char[mlen + 1];
1709 if (putf) {
1710 // CP_UTF8 not available on Windows 95, so use UTF8FromUTF16()
1711 UTF8FromUTF16(uptr, ulen, putf, mlen);
1714 delete []uptr;
1716 if (putf) {
1717 InsertPasteText(putf, mlen, selStart, isRectangular, isLine);
1718 delete []putf;
1720 } else {
1721 InsertPasteText(ptr, len, selStart, isRectangular, isLine);
1724 memSelection.Unlock();
1727 ::CloseClipboard();
1728 Redraw();
1731 void ScintillaWin::CreateCallTipWindow(PRectangle) {
1732 if (!ct.wCallTip.Created()) {
1733 ct.wCallTip = ::CreateWindow(callClassName, TEXT("ACallTip"),
1734 WS_POPUP, 100, 100, 150, 20,
1735 MainHWND(), 0,
1736 GetWindowInstance(MainHWND()),
1737 this);
1738 ct.wDraw = ct.wCallTip;
1742 void ScintillaWin::AddToPopUp(const char *label, int cmd, bool enabled) {
1743 HMENU hmenuPopup = reinterpret_cast<HMENU>(popup.GetID());
1744 if (!label[0])
1745 ::AppendMenuA(hmenuPopup, MF_SEPARATOR, 0, "");
1746 else if (enabled)
1747 ::AppendMenuA(hmenuPopup, MF_STRING, cmd, label);
1748 else
1749 ::AppendMenuA(hmenuPopup, MF_STRING | MF_DISABLED | MF_GRAYED, cmd, label);
1752 void ScintillaWin::ClaimSelection() {
1753 // Windows does not have a primary selection
1756 /// Implement IUnknown
1758 STDMETHODIMP_(ULONG)FormatEnumerator_AddRef(FormatEnumerator *fe);
1759 STDMETHODIMP FormatEnumerator_QueryInterface(FormatEnumerator *fe, REFIID riid, PVOID *ppv) {
1760 //Platform::DebugPrintf("EFE QI");
1761 *ppv = NULL;
1762 if (riid == IID_IUnknown)
1763 *ppv = reinterpret_cast<IEnumFORMATETC *>(fe);
1764 if (riid == IID_IEnumFORMATETC)
1765 *ppv = reinterpret_cast<IEnumFORMATETC *>(fe);
1766 if (!*ppv)
1767 return E_NOINTERFACE;
1768 FormatEnumerator_AddRef(fe);
1769 return S_OK;
1771 STDMETHODIMP_(ULONG)FormatEnumerator_AddRef(FormatEnumerator *fe) {
1772 return ++fe->ref;
1774 STDMETHODIMP_(ULONG)FormatEnumerator_Release(FormatEnumerator *fe) {
1775 fe->ref--;
1776 if (fe->ref > 0)
1777 return fe->ref;
1778 delete fe;
1779 return 0;
1781 /// Implement IEnumFORMATETC
1782 STDMETHODIMP FormatEnumerator_Next(FormatEnumerator *fe, ULONG celt, FORMATETC *rgelt, ULONG *pceltFetched) {
1783 //Platform::DebugPrintf("EFE Next %d %d", fe->pos, celt);
1784 if (rgelt == NULL) return E_POINTER;
1785 // We only support one format, so this is simple.
1786 unsigned int putPos = 0;
1787 while ((fe->pos < fe->formatsLen) && (putPos < celt)) {
1788 rgelt->cfFormat = fe->formats[fe->pos];
1789 rgelt->ptd = 0;
1790 rgelt->dwAspect = DVASPECT_CONTENT;
1791 rgelt->lindex = -1;
1792 rgelt->tymed = TYMED_HGLOBAL;
1793 fe->pos++;
1794 putPos++;
1796 if (pceltFetched)
1797 *pceltFetched = putPos;
1798 return putPos ? S_OK : S_FALSE;
1800 STDMETHODIMP FormatEnumerator_Skip(FormatEnumerator *fe, ULONG celt) {
1801 fe->pos += celt;
1802 return S_OK;
1804 STDMETHODIMP FormatEnumerator_Reset(FormatEnumerator *fe) {
1805 fe->pos = 0;
1806 return S_OK;
1808 STDMETHODIMP FormatEnumerator_Clone(FormatEnumerator *fe, IEnumFORMATETC **ppenum) {
1809 FormatEnumerator *pfe;
1810 try {
1811 pfe = new FormatEnumerator(fe->pos, fe->formats, fe->formatsLen);
1812 } catch (...) {
1813 return E_OUTOFMEMORY;
1815 return FormatEnumerator_QueryInterface(pfe, IID_IEnumFORMATETC,
1816 reinterpret_cast<void **>(ppenum));
1819 static VFunction *vtFormatEnumerator[] = {
1820 (VFunction *)(FormatEnumerator_QueryInterface),
1821 (VFunction *)(FormatEnumerator_AddRef),
1822 (VFunction *)(FormatEnumerator_Release),
1823 (VFunction *)(FormatEnumerator_Next),
1824 (VFunction *)(FormatEnumerator_Skip),
1825 (VFunction *)(FormatEnumerator_Reset),
1826 (VFunction *)(FormatEnumerator_Clone)
1829 FormatEnumerator::FormatEnumerator(int pos_, CLIPFORMAT formats_[], int formatsLen_) {
1830 vtbl = vtFormatEnumerator;
1831 ref = 0; // First QI adds first reference...
1832 pos = pos_;
1833 formatsLen = formatsLen_;
1834 for (int i=0; i<formatsLen; i++)
1835 formats[i] = formats_[i];
1838 /// Implement IUnknown
1839 STDMETHODIMP DropSource_QueryInterface(DropSource *ds, REFIID riid, PVOID *ppv) {
1840 return ds->sci->QueryInterface(riid, ppv);
1842 STDMETHODIMP_(ULONG)DropSource_AddRef(DropSource *ds) {
1843 return ds->sci->AddRef();
1845 STDMETHODIMP_(ULONG)DropSource_Release(DropSource *ds) {
1846 return ds->sci->Release();
1849 /// Implement IDropSource
1850 STDMETHODIMP DropSource_QueryContinueDrag(DropSource *, BOOL fEsc, DWORD grfKeyState) {
1851 if (fEsc)
1852 return DRAGDROP_S_CANCEL;
1853 if (!(grfKeyState & MK_LBUTTON))
1854 return DRAGDROP_S_DROP;
1855 return S_OK;
1858 STDMETHODIMP DropSource_GiveFeedback(DropSource *, DWORD) {
1859 return DRAGDROP_S_USEDEFAULTCURSORS;
1862 static VFunction *vtDropSource[] = {
1863 (VFunction *)(DropSource_QueryInterface),
1864 (VFunction *)(DropSource_AddRef),
1865 (VFunction *)(DropSource_Release),
1866 (VFunction *)(DropSource_QueryContinueDrag),
1867 (VFunction *)(DropSource_GiveFeedback)
1870 DropSource::DropSource() {
1871 vtbl = vtDropSource;
1872 sci = 0;
1875 /// Implement IUnkown
1876 STDMETHODIMP DataObject_QueryInterface(DataObject *pd, REFIID riid, PVOID *ppv) {
1877 //Platform::DebugPrintf("DO QI %x\n", pd);
1878 return pd->sci->QueryInterface(riid, ppv);
1880 STDMETHODIMP_(ULONG)DataObject_AddRef(DataObject *pd) {
1881 return pd->sci->AddRef();
1883 STDMETHODIMP_(ULONG)DataObject_Release(DataObject *pd) {
1884 return pd->sci->Release();
1886 /// Implement IDataObject
1887 STDMETHODIMP DataObject_GetData(DataObject *pd, FORMATETC *pFEIn, STGMEDIUM *pSTM) {
1888 return pd->sci->GetData(pFEIn, pSTM);
1891 STDMETHODIMP DataObject_GetDataHere(DataObject *, FORMATETC *, STGMEDIUM *) {
1892 //Platform::DebugPrintf("DOB GetDataHere\n");
1893 return E_NOTIMPL;
1896 STDMETHODIMP DataObject_QueryGetData(DataObject *pd, FORMATETC *pFE) {
1897 if (pd->sci->DragIsRectangularOK(pFE->cfFormat) &&
1898 pFE->ptd == 0 &&
1899 (pFE->dwAspect & DVASPECT_CONTENT) != 0 &&
1900 pFE->lindex == -1 &&
1901 (pFE->tymed & TYMED_HGLOBAL) != 0
1903 return S_OK;
1906 bool formatOK = (pFE->cfFormat == CF_TEXT) ||
1907 ((pFE->cfFormat == CF_UNICODETEXT) && pd->sci->IsUnicodeMode());
1908 if (!formatOK ||
1909 pFE->ptd != 0 ||
1910 (pFE->dwAspect & DVASPECT_CONTENT) == 0 ||
1911 pFE->lindex != -1 ||
1912 (pFE->tymed & TYMED_HGLOBAL) == 0
1914 //Platform::DebugPrintf("DOB QueryGetData No %x\n",pFE->cfFormat);
1915 //return DATA_E_FORMATETC;
1916 return S_FALSE;
1918 //Platform::DebugPrintf("DOB QueryGetData OK %x\n",pFE->cfFormat);
1919 return S_OK;
1922 STDMETHODIMP DataObject_GetCanonicalFormatEtc(DataObject *pd, FORMATETC *, FORMATETC *pFEOut) {
1923 //Platform::DebugPrintf("DOB GetCanon\n");
1924 if (pd->sci->IsUnicodeMode())
1925 pFEOut->cfFormat = CF_UNICODETEXT;
1926 else
1927 pFEOut->cfFormat = CF_TEXT;
1928 pFEOut->ptd = 0;
1929 pFEOut->dwAspect = DVASPECT_CONTENT;
1930 pFEOut->lindex = -1;
1931 pFEOut->tymed = TYMED_HGLOBAL;
1932 return S_OK;
1935 STDMETHODIMP DataObject_SetData(DataObject *, FORMATETC *, STGMEDIUM *, BOOL) {
1936 //Platform::DebugPrintf("DOB SetData\n");
1937 return E_FAIL;
1940 STDMETHODIMP DataObject_EnumFormatEtc(DataObject *pd, DWORD dwDirection, IEnumFORMATETC **ppEnum) {
1941 try {
1942 //Platform::DebugPrintf("DOB EnumFormatEtc %d\n", dwDirection);
1943 if (dwDirection != DATADIR_GET) {
1944 *ppEnum = 0;
1945 return E_FAIL;
1947 FormatEnumerator *pfe;
1948 if (pd->sci->IsUnicodeMode()) {
1949 CLIPFORMAT formats[] = {CF_UNICODETEXT, CF_TEXT};
1950 pfe = new FormatEnumerator(0, formats, 2);
1951 } else {
1952 CLIPFORMAT formats[] = {CF_TEXT};
1953 pfe = new FormatEnumerator(0, formats, 1);
1955 return FormatEnumerator_QueryInterface(pfe, IID_IEnumFORMATETC,
1956 reinterpret_cast<void **>(ppEnum));
1957 } catch (std::bad_alloc &) {
1958 pd->sci->errorStatus = SC_STATUS_BADALLOC;
1959 return E_OUTOFMEMORY;
1960 } catch (...) {
1961 pd->sci->errorStatus = SC_STATUS_FAILURE;
1962 return E_FAIL;
1966 STDMETHODIMP DataObject_DAdvise(DataObject *, FORMATETC *, DWORD, IAdviseSink *, PDWORD) {
1967 //Platform::DebugPrintf("DOB DAdvise\n");
1968 return E_FAIL;
1971 STDMETHODIMP DataObject_DUnadvise(DataObject *, DWORD) {
1972 //Platform::DebugPrintf("DOB DUnadvise\n");
1973 return E_FAIL;
1976 STDMETHODIMP DataObject_EnumDAdvise(DataObject *, IEnumSTATDATA **) {
1977 //Platform::DebugPrintf("DOB EnumDAdvise\n");
1978 return E_FAIL;
1981 static VFunction *vtDataObject[] = {
1982 (VFunction *)(DataObject_QueryInterface),
1983 (VFunction *)(DataObject_AddRef),
1984 (VFunction *)(DataObject_Release),
1985 (VFunction *)(DataObject_GetData),
1986 (VFunction *)(DataObject_GetDataHere),
1987 (VFunction *)(DataObject_QueryGetData),
1988 (VFunction *)(DataObject_GetCanonicalFormatEtc),
1989 (VFunction *)(DataObject_SetData),
1990 (VFunction *)(DataObject_EnumFormatEtc),
1991 (VFunction *)(DataObject_DAdvise),
1992 (VFunction *)(DataObject_DUnadvise),
1993 (VFunction *)(DataObject_EnumDAdvise)
1996 DataObject::DataObject() {
1997 vtbl = vtDataObject;
1998 sci = 0;
2001 /// Implement IUnknown
2002 STDMETHODIMP DropTarget_QueryInterface(DropTarget *dt, REFIID riid, PVOID *ppv) {
2003 //Platform::DebugPrintf("DT QI %x\n", dt);
2004 return dt->sci->QueryInterface(riid, ppv);
2006 STDMETHODIMP_(ULONG)DropTarget_AddRef(DropTarget *dt) {
2007 return dt->sci->AddRef();
2009 STDMETHODIMP_(ULONG)DropTarget_Release(DropTarget *dt) {
2010 return dt->sci->Release();
2013 /// Implement IDropTarget by forwarding to Scintilla
2014 STDMETHODIMP DropTarget_DragEnter(DropTarget *dt, LPDATAOBJECT pIDataSource, DWORD grfKeyState,
2015 POINTL pt, PDWORD pdwEffect) {
2016 try {
2017 return dt->sci->DragEnter(pIDataSource, grfKeyState, pt, pdwEffect);
2018 } catch (...) {
2019 dt->sci->errorStatus = SC_STATUS_FAILURE;
2021 return E_FAIL;
2023 STDMETHODIMP DropTarget_DragOver(DropTarget *dt, DWORD grfKeyState, POINTL pt, PDWORD pdwEffect) {
2024 try {
2025 return dt->sci->DragOver(grfKeyState, pt, pdwEffect);
2026 } catch (...) {
2027 dt->sci->errorStatus = SC_STATUS_FAILURE;
2029 return E_FAIL;
2031 STDMETHODIMP DropTarget_DragLeave(DropTarget *dt) {
2032 try {
2033 return dt->sci->DragLeave();
2034 } catch (...) {
2035 dt->sci->errorStatus = SC_STATUS_FAILURE;
2037 return E_FAIL;
2039 STDMETHODIMP DropTarget_Drop(DropTarget *dt, LPDATAOBJECT pIDataSource, DWORD grfKeyState,
2040 POINTL pt, PDWORD pdwEffect) {
2041 try {
2042 return dt->sci->Drop(pIDataSource, grfKeyState, pt, pdwEffect);
2043 } catch (...) {
2044 dt->sci->errorStatus = SC_STATUS_FAILURE;
2046 return E_FAIL;
2049 static VFunction *vtDropTarget[] = {
2050 (VFunction *)(DropTarget_QueryInterface),
2051 (VFunction *)(DropTarget_AddRef),
2052 (VFunction *)(DropTarget_Release),
2053 (VFunction *)(DropTarget_DragEnter),
2054 (VFunction *)(DropTarget_DragOver),
2055 (VFunction *)(DropTarget_DragLeave),
2056 (VFunction *)(DropTarget_Drop)
2059 DropTarget::DropTarget() {
2060 vtbl = vtDropTarget;
2061 sci = 0;
2065 * DBCS: support Input Method Editor (IME).
2066 * Called when IME Window opened.
2068 void ScintillaWin::ImeStartComposition() {
2069 #ifndef __DMC__
2070 // Digital Mars compiler does not include Imm library
2071 if (caret.active) {
2072 // Move IME Window to current caret position
2073 HIMC hIMC = ::ImmGetContext(MainHWND());
2074 Point pos = PointMainCaret();
2075 COMPOSITIONFORM CompForm;
2076 CompForm.dwStyle = CFS_POINT;
2077 CompForm.ptCurrentPos.x = pos.x;
2078 CompForm.ptCurrentPos.y = pos.y;
2080 ::ImmSetCompositionWindow(hIMC, &CompForm);
2082 // Set font of IME window to same as surrounded text.
2083 if (stylesValid) {
2084 // Since the style creation code has been made platform independent,
2085 // The logfont for the IME is recreated here.
2086 int styleHere = (pdoc->StyleAt(sel.MainCaret())) & 31;
2087 LOGFONTA lf = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ""};
2088 int sizeZoomed = vs.styles[styleHere].size + vs.zoomLevel;
2089 if (sizeZoomed <= 2) // Hangs if sizeZoomed <= 1
2090 sizeZoomed = 2;
2091 AutoSurface surface(this);
2092 int deviceHeight = sizeZoomed;
2093 if (surface) {
2094 deviceHeight = (sizeZoomed * surface->LogPixelsY()) / 72;
2096 // The negative is to allow for leading
2097 lf.lfHeight = -(abs(deviceHeight));
2098 lf.lfWeight = vs.styles[styleHere].bold ? FW_BOLD : FW_NORMAL;
2099 lf.lfItalic = static_cast<BYTE>(vs.styles[styleHere].italic ? 1 : 0);
2100 lf.lfCharSet = DEFAULT_CHARSET;
2101 lf.lfFaceName[0] = '\0';
2102 if (vs.styles[styleHere].fontName)
2103 strcpy(lf.lfFaceName, vs.styles[styleHere].fontName);
2105 ::ImmSetCompositionFontA(hIMC, &lf);
2107 ::ImmReleaseContext(MainHWND(), hIMC);
2108 // Caret is displayed in IME window. So, caret in Scintilla is useless.
2109 DropCaret();
2111 #endif
2114 /** Called when IME Window closed. */
2115 void ScintillaWin::ImeEndComposition() {
2116 ShowCaretAtCurrentPosition();
2119 void ScintillaWin::AddCharBytes(char b0, char b1) {
2121 int inputCodePage = InputCodePage();
2122 if (inputCodePage && IsUnicodeMode()) {
2123 char utfval[4] = "\0\0\0";
2124 char ansiChars[3];
2125 wchar_t wcs[2];
2126 if (b0) { // Two bytes from IME
2127 ansiChars[0] = b0;
2128 ansiChars[1] = b1;
2129 ansiChars[2] = '\0';
2130 ::MultiByteToWideChar(inputCodePage, 0, ansiChars, 2, wcs, 1);
2131 } else {
2132 ansiChars[0] = b1;
2133 ansiChars[1] = '\0';
2134 ::MultiByteToWideChar(inputCodePage, 0, ansiChars, 1, wcs, 1);
2136 unsigned int len = UTF8Length(wcs, 1);
2137 UTF8FromUTF16(wcs, 1, utfval, len);
2138 utfval[len] = '\0';
2139 AddCharUTF(utfval, len ? len : 1);
2140 } else if (b0) {
2141 char dbcsChars[3];
2142 dbcsChars[0] = b0;
2143 dbcsChars[1] = b1;
2144 dbcsChars[2] = '\0';
2145 AddCharUTF(dbcsChars, 2, true);
2146 } else {
2147 AddChar(b1);
2151 void ScintillaWin::GetIntelliMouseParameters() {
2152 // This retrieves the number of lines per scroll as configured inthe Mouse Properties sheet in Control Panel
2153 ::SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, &linesPerScroll, 0);
2156 void ScintillaWin::CopyToClipboard(const SelectionText &selectedText) {
2157 if (!::OpenClipboard(MainHWND()))
2158 return;
2159 ::EmptyClipboard();
2161 GlobalMemory uniText;
2163 // Default Scintilla behaviour in Unicode mode
2164 if (IsUnicodeMode()) {
2165 int uchars = UTF16Length(selectedText.s, selectedText.len);
2166 uniText.Allocate(2 * uchars);
2167 if (uniText) {
2168 UTF16FromUTF8(selectedText.s, selectedText.len, static_cast<wchar_t *>(uniText.ptr), uchars);
2170 } else {
2171 // Not Unicode mode
2172 // Convert to Unicode using the current Scintilla code page
2173 UINT cpSrc = CodePageFromCharSet(
2174 selectedText.characterSet, selectedText.codePage);
2175 int uLen = ::MultiByteToWideChar(cpSrc, 0, selectedText.s, selectedText.len, 0, 0);
2176 uniText.Allocate(2 * uLen);
2177 if (uniText) {
2178 ::MultiByteToWideChar(cpSrc, 0, selectedText.s, selectedText.len,
2179 static_cast<wchar_t *>(uniText.ptr), uLen);
2183 if (uniText) {
2184 if (!IsNT()) {
2185 // Copy ANSI text to clipboard on Windows 9x
2186 // Convert from Unicode text, so other ANSI programs can
2187 // paste the text
2188 // Windows NT, 2k, XP automatically generates CF_TEXT
2189 GlobalMemory ansiText;
2190 ansiText.Allocate(selectedText.len);
2191 if (ansiText) {
2192 ::WideCharToMultiByte(CP_ACP, 0, static_cast<wchar_t *>(uniText.ptr), -1,
2193 static_cast<char *>(ansiText.ptr), selectedText.len, NULL, NULL);
2194 ansiText.SetClip(CF_TEXT);
2197 uniText.SetClip(CF_UNICODETEXT);
2198 } else {
2199 // There was a failure - try to copy at least ANSI text
2200 GlobalMemory ansiText;
2201 ansiText.Allocate(selectedText.len);
2202 if (ansiText) {
2203 memcpy(static_cast<char *>(ansiText.ptr), selectedText.s, selectedText.len);
2204 ansiText.SetClip(CF_TEXT);
2208 if (selectedText.rectangular) {
2209 ::SetClipboardData(cfColumnSelect, 0);
2212 if (selectedText.lineCopy) {
2213 ::SetClipboardData(cfLineSelect, 0);
2216 ::CloseClipboard();
2219 void ScintillaWin::ScrollMessage(WPARAM wParam) {
2220 //DWORD dwStart = timeGetTime();
2221 //Platform::DebugPrintf("Scroll %x %d\n", wParam, lParam);
2223 SCROLLINFO sci;
2224 memset(&sci, 0, sizeof(sci));
2225 sci.cbSize = sizeof(sci);
2226 sci.fMask = SIF_ALL;
2228 GetScrollInfo(SB_VERT, &sci);
2230 //Platform::DebugPrintf("ScrollInfo %d mask=%x min=%d max=%d page=%d pos=%d track=%d\n", b,sci.fMask,
2231 //sci.nMin, sci.nMax, sci.nPage, sci.nPos, sci.nTrackPos);
2233 int topLineNew = topLine;
2234 switch (LoWord(wParam)) {
2235 case SB_LINEUP:
2236 topLineNew -= 1;
2237 break;
2238 case SB_LINEDOWN:
2239 topLineNew += 1;
2240 break;
2241 case SB_PAGEUP:
2242 topLineNew -= LinesToScroll(); break;
2243 case SB_PAGEDOWN: topLineNew += LinesToScroll(); break;
2244 case SB_TOP: topLineNew = 0; break;
2245 case SB_BOTTOM: topLineNew = MaxScrollPos(); break;
2246 case SB_THUMBPOSITION: topLineNew = sci.nTrackPos; break;
2247 case SB_THUMBTRACK: topLineNew = sci.nTrackPos; break;
2249 ScrollTo(topLineNew);
2252 void ScintillaWin::HorizontalScrollMessage(WPARAM wParam) {
2253 int xPos = xOffset;
2254 PRectangle rcText = GetTextRectangle();
2255 int pageWidth = rcText.Width() * 2 / 3;
2256 switch (LoWord(wParam)) {
2257 case SB_LINEUP:
2258 xPos -= 20;
2259 break;
2260 case SB_LINEDOWN: // May move past the logical end
2261 xPos += 20;
2262 break;
2263 case SB_PAGEUP:
2264 xPos -= pageWidth;
2265 break;
2266 case SB_PAGEDOWN:
2267 xPos += pageWidth;
2268 if (xPos > scrollWidth - rcText.Width()) { // Hit the end exactly
2269 xPos = scrollWidth - rcText.Width();
2271 break;
2272 case SB_TOP:
2273 xPos = 0;
2274 break;
2275 case SB_BOTTOM:
2276 xPos = scrollWidth;
2277 break;
2278 case SB_THUMBPOSITION:
2279 case SB_THUMBTRACK: {
2280 // 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 =]
2281 SCROLLINFO si;
2282 si.cbSize = sizeof(si);
2283 si.fMask = SIF_TRACKPOS;
2284 if (GetScrollInfo(SB_HORZ, &si)) {
2285 xPos = si.nTrackPos;
2288 break;
2290 HorizontalScrollTo(xPos);
2293 void ScintillaWin::RealizeWindowPalette(bool inBackGround) {
2294 RefreshStyleData();
2295 HDC hdc = ::GetDC(MainHWND());
2296 // Select a stock font to prevent warnings from BoundsChecker
2297 ::SelectObject(hdc, GetStockFont(DEFAULT_GUI_FONT));
2298 AutoSurface surfaceWindow(hdc, this);
2299 if (surfaceWindow) {
2300 int changes = surfaceWindow->SetPalette(&palette, inBackGround);
2301 if (changes > 0)
2302 Redraw();
2303 surfaceWindow->Release();
2305 ::ReleaseDC(MainHWND(), hdc);
2309 * Redraw all of text area.
2310 * This paint will not be abandoned.
2312 void ScintillaWin::FullPaint() {
2313 HDC hdc = ::GetDC(MainHWND());
2314 FullPaintDC(hdc);
2315 ::ReleaseDC(MainHWND(), hdc);
2319 * Redraw all of text area on the specified DC.
2320 * This paint will not be abandoned.
2322 void ScintillaWin::FullPaintDC(HDC hdc) {
2323 paintState = painting;
2324 rcPaint = GetClientRectangle();
2325 paintingAllText = true;
2326 AutoSurface surfaceWindow(hdc, this);
2327 if (surfaceWindow) {
2328 Paint(surfaceWindow, rcPaint);
2329 surfaceWindow->Release();
2331 paintState = notPainting;
2334 static bool CompareDevCap(HDC hdc, HDC hOtherDC, int nIndex) {
2335 return ::GetDeviceCaps(hdc, nIndex) == ::GetDeviceCaps(hOtherDC, nIndex);
2338 bool ScintillaWin::IsCompatibleDC(HDC hOtherDC) {
2339 HDC hdc = ::GetDC(MainHWND());
2340 bool isCompatible =
2341 CompareDevCap(hdc, hOtherDC, TECHNOLOGY) &&
2342 CompareDevCap(hdc, hOtherDC, LOGPIXELSY) &&
2343 CompareDevCap(hdc, hOtherDC, LOGPIXELSX) &&
2344 CompareDevCap(hdc, hOtherDC, BITSPIXEL) &&
2345 CompareDevCap(hdc, hOtherDC, PLANES);
2346 ::ReleaseDC(MainHWND(), hdc);
2347 return isCompatible;
2350 DWORD ScintillaWin::EffectFromState(DWORD grfKeyState) {
2351 // These are the Wordpad semantics.
2352 DWORD dwEffect;
2353 if (inDragDrop == ddDragging) // Internal defaults to move
2354 dwEffect = DROPEFFECT_MOVE;
2355 else
2356 dwEffect = DROPEFFECT_COPY;
2357 if (grfKeyState & MK_ALT)
2358 dwEffect = DROPEFFECT_MOVE;
2359 if (grfKeyState & MK_CONTROL)
2360 dwEffect = DROPEFFECT_COPY;
2361 return dwEffect;
2364 /// Implement IUnknown
2365 STDMETHODIMP ScintillaWin::QueryInterface(REFIID riid, PVOID *ppv) {
2366 *ppv = NULL;
2367 if (riid == IID_IUnknown)
2368 *ppv = reinterpret_cast<IDropTarget *>(&dt);
2369 if (riid == IID_IDropSource)
2370 *ppv = reinterpret_cast<IDropSource *>(&ds);
2371 if (riid == IID_IDropTarget)
2372 *ppv = reinterpret_cast<IDropTarget *>(&dt);
2373 if (riid == IID_IDataObject)
2374 *ppv = reinterpret_cast<IDataObject *>(&dob);
2375 if (!*ppv)
2376 return E_NOINTERFACE;
2377 return S_OK;
2380 STDMETHODIMP_(ULONG) ScintillaWin::AddRef() {
2381 return 1;
2384 STDMETHODIMP_(ULONG) ScintillaWin::Release() {
2385 return 1;
2388 /// Implement IDropTarget
2389 STDMETHODIMP ScintillaWin::DragEnter(LPDATAOBJECT pIDataSource, DWORD grfKeyState,
2390 POINTL, PDWORD pdwEffect) {
2391 if (pIDataSource == NULL)
2392 return E_POINTER;
2393 FORMATETC fmtu = {CF_UNICODETEXT, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
2394 HRESULT hrHasUText = pIDataSource->QueryGetData(&fmtu);
2395 hasOKText = (hrHasUText == S_OK);
2396 if (!hasOKText) {
2397 FORMATETC fmte = {CF_TEXT, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
2398 HRESULT hrHasText = pIDataSource->QueryGetData(&fmte);
2399 hasOKText = (hrHasText == S_OK);
2401 if (!hasOKText) {
2402 *pdwEffect = DROPEFFECT_NONE;
2403 return S_OK;
2406 *pdwEffect = EffectFromState(grfKeyState);
2407 return S_OK;
2410 STDMETHODIMP ScintillaWin::DragOver(DWORD grfKeyState, POINTL pt, PDWORD pdwEffect) {
2411 try {
2412 if (!hasOKText || pdoc->IsReadOnly()) {
2413 *pdwEffect = DROPEFFECT_NONE;
2414 return S_OK;
2417 *pdwEffect = EffectFromState(grfKeyState);
2419 // Update the cursor.
2420 POINT rpt = {pt.x, pt.y};
2421 ::ScreenToClient(MainHWND(), &rpt);
2422 SetDragPosition(SPositionFromLocation(Point(rpt.x, rpt.y), false, false, UserVirtualSpace()));
2424 return S_OK;
2425 } catch (...) {
2426 errorStatus = SC_STATUS_FAILURE;
2428 return E_FAIL;
2431 STDMETHODIMP ScintillaWin::DragLeave() {
2432 try {
2433 SetDragPosition(SelectionPosition(invalidPosition));
2434 return S_OK;
2435 } catch (...) {
2436 errorStatus = SC_STATUS_FAILURE;
2438 return E_FAIL;
2441 STDMETHODIMP ScintillaWin::Drop(LPDATAOBJECT pIDataSource, DWORD grfKeyState,
2442 POINTL pt, PDWORD pdwEffect) {
2443 try {
2444 *pdwEffect = EffectFromState(grfKeyState);
2446 if (pIDataSource == NULL)
2447 return E_POINTER;
2449 SetDragPosition(SelectionPosition(invalidPosition));
2451 STGMEDIUM medium = {0, {0}, 0};
2453 char *data = 0;
2454 bool dataAllocated = false;
2456 FORMATETC fmtu = {CF_UNICODETEXT, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
2457 HRESULT hr = pIDataSource->GetData(&fmtu, &medium);
2458 if (SUCCEEDED(hr) && medium.hGlobal) {
2459 wchar_t *udata = static_cast<wchar_t *>(::GlobalLock(medium.hGlobal));
2460 if (IsUnicodeMode()) {
2461 int tlen = ::GlobalSize(medium.hGlobal);
2462 // Convert UTF-16 to UTF-8
2463 int dataLen = UTF8Length(udata, tlen/2);
2464 data = new char[dataLen+1];
2465 UTF8FromUTF16(udata, tlen/2, data, dataLen);
2466 dataAllocated = true;
2467 } else {
2468 // Convert UTF-16 to ANSI
2470 // Default Scintilla behavior in Unicode mode
2471 // CF_UNICODETEXT available, but not in Unicode mode
2472 // Convert from Unicode to current Scintilla code page
2473 UINT cpDest = CodePageOfDocument();
2474 int tlen = ::WideCharToMultiByte(cpDest, 0, udata, -1,
2475 NULL, 0, NULL, NULL) - 1; // subtract 0 terminator
2476 data = new char[tlen + 1];
2477 memset(data, 0, (tlen+1));
2478 ::WideCharToMultiByte(cpDest, 0, udata, -1,
2479 data, tlen + 1, NULL, NULL);
2480 dataAllocated = true;
2484 if (!data) {
2485 FORMATETC fmte = {CF_TEXT, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
2486 hr = pIDataSource->GetData(&fmte, &medium);
2487 if (SUCCEEDED(hr) && medium.hGlobal) {
2488 data = static_cast<char *>(::GlobalLock(medium.hGlobal));
2492 if (data && convertPastes) {
2493 // Convert line endings of the drop into our local line-endings mode
2494 int len = static_cast<int>(strlen(data));
2495 char *convertedText = Document::TransformLineEnds(&len, data, len, pdoc->eolMode);
2496 if (dataAllocated)
2497 delete []data;
2498 data = convertedText;
2499 dataAllocated = true;
2502 if (!data) {
2503 //Platform::DebugPrintf("Bad data format: 0x%x\n", hres);
2504 return hr;
2507 FORMATETC fmtr = {cfColumnSelect, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
2508 HRESULT hrRectangular = pIDataSource->QueryGetData(&fmtr);
2510 POINT rpt = {pt.x, pt.y};
2511 ::ScreenToClient(MainHWND(), &rpt);
2512 SelectionPosition movePos = SPositionFromLocation(Point(rpt.x, rpt.y), false, false, UserVirtualSpace());
2514 DropAt(movePos, data, *pdwEffect == DROPEFFECT_MOVE, hrRectangular == S_OK);
2516 ::GlobalUnlock(medium.hGlobal);
2518 // Free data
2519 if (medium.pUnkForRelease != NULL)
2520 medium.pUnkForRelease->Release();
2521 else
2522 ::GlobalFree(medium.hGlobal);
2524 if (dataAllocated)
2525 delete []data;
2527 return S_OK;
2528 } catch (...) {
2529 errorStatus = SC_STATUS_FAILURE;
2531 return E_FAIL;
2534 /// Implement important part of IDataObject
2535 STDMETHODIMP ScintillaWin::GetData(FORMATETC *pFEIn, STGMEDIUM *pSTM) {
2536 bool formatOK = (pFEIn->cfFormat == CF_TEXT) ||
2537 ((pFEIn->cfFormat == CF_UNICODETEXT) && IsUnicodeMode());
2538 if (!formatOK ||
2539 pFEIn->ptd != 0 ||
2540 (pFEIn->dwAspect & DVASPECT_CONTENT) == 0 ||
2541 pFEIn->lindex != -1 ||
2542 (pFEIn->tymed & TYMED_HGLOBAL) == 0
2544 //Platform::DebugPrintf("DOB GetData No %d %x %x fmt=%x\n", lenDrag, pFEIn, pSTM, pFEIn->cfFormat);
2545 return DATA_E_FORMATETC;
2547 pSTM->tymed = TYMED_HGLOBAL;
2548 //Platform::DebugPrintf("DOB GetData OK %d %x %x\n", lenDrag, pFEIn, pSTM);
2550 GlobalMemory text;
2551 if (pFEIn->cfFormat == CF_UNICODETEXT) {
2552 int uchars = UTF16Length(drag.s, drag.len);
2553 text.Allocate(2 * uchars);
2554 if (text) {
2555 UTF16FromUTF8(drag.s, drag.len, static_cast<wchar_t *>(text.ptr), uchars);
2557 } else {
2558 text.Allocate(drag.len);
2559 if (text) {
2560 memcpy(static_cast<char *>(text.ptr), drag.s, drag.len);
2563 pSTM->hGlobal = text ? text.Unlock() : 0;
2564 pSTM->pUnkForRelease = 0;
2565 return S_OK;
2568 bool ScintillaWin::Register(HINSTANCE hInstance_) {
2570 hInstance = hInstance_;
2571 bool result;
2573 // Register the Scintilla class
2574 if (IsNT()) {
2576 // Register Scintilla as a wide character window
2577 WNDCLASSEXW wndclass;
2578 wndclass.cbSize = sizeof(wndclass);
2579 wndclass.style = CS_GLOBALCLASS | CS_HREDRAW | CS_VREDRAW;
2580 wndclass.lpfnWndProc = ScintillaWin::SWndProc;
2581 wndclass.cbClsExtra = 0;
2582 wndclass.cbWndExtra = sizeof(ScintillaWin *);
2583 wndclass.hInstance = hInstance;
2584 wndclass.hIcon = NULL;
2585 wndclass.hCursor = NULL;
2586 wndclass.hbrBackground = NULL;
2587 wndclass.lpszMenuName = NULL;
2588 wndclass.lpszClassName = L"Scintilla";
2589 wndclass.hIconSm = 0;
2590 result = ::RegisterClassExW(&wndclass) != 0;
2591 } else {
2593 // Register Scintilla as a normal character window
2594 WNDCLASSEX wndclass;
2595 wndclass.cbSize = sizeof(wndclass);
2596 wndclass.style = CS_GLOBALCLASS | CS_HREDRAW | CS_VREDRAW;
2597 wndclass.lpfnWndProc = ScintillaWin::SWndProc;
2598 wndclass.cbClsExtra = 0;
2599 wndclass.cbWndExtra = sizeof(ScintillaWin *);
2600 wndclass.hInstance = hInstance;
2601 wndclass.hIcon = NULL;
2602 wndclass.hCursor = NULL;
2603 wndclass.hbrBackground = NULL;
2604 wndclass.lpszMenuName = NULL;
2605 wndclass.lpszClassName = scintillaClassName;
2606 wndclass.hIconSm = 0;
2607 result = ::RegisterClassEx(&wndclass) != 0;
2610 if (result) {
2611 // Register the CallTip class
2612 WNDCLASSEX wndclassc;
2613 wndclassc.cbSize = sizeof(wndclassc);
2614 wndclassc.style = CS_GLOBALCLASS | CS_HREDRAW | CS_VREDRAW;
2615 wndclassc.cbClsExtra = 0;
2616 wndclassc.cbWndExtra = sizeof(ScintillaWin *);
2617 wndclassc.hInstance = hInstance;
2618 wndclassc.hIcon = NULL;
2619 wndclassc.hbrBackground = NULL;
2620 wndclassc.lpszMenuName = NULL;
2621 wndclassc.lpfnWndProc = ScintillaWin::CTWndProc;
2622 wndclassc.hCursor = ::LoadCursor(NULL, IDC_ARROW);
2623 wndclassc.lpszClassName = callClassName;
2624 wndclassc.hIconSm = 0;
2626 result = ::RegisterClassEx(&wndclassc) != 0;
2629 return result;
2632 bool ScintillaWin::Unregister() {
2633 bool result = ::UnregisterClass(scintillaClassName, hInstance) != 0;
2634 if (::UnregisterClass(callClassName, hInstance) == 0)
2635 result = false;
2636 return result;
2639 bool ScintillaWin::HasCaretSizeChanged() {
2640 if (
2641 ( (0 != vs.caretWidth) && (sysCaretWidth != vs.caretWidth) )
2642 || ((0 != vs.lineHeight) && (sysCaretHeight != vs.lineHeight))
2644 return true;
2646 return false;
2649 BOOL ScintillaWin::CreateSystemCaret() {
2650 sysCaretWidth = vs.caretWidth;
2651 if (0 == sysCaretWidth) {
2652 sysCaretWidth = 1;
2654 sysCaretHeight = vs.lineHeight;
2655 int bitmapSize = (((sysCaretWidth + 15) & ~15) >> 3) *
2656 sysCaretHeight;
2657 char *bits = new char[bitmapSize];
2658 memset(bits, 0, bitmapSize);
2659 sysCaretBitmap = ::CreateBitmap(sysCaretWidth, sysCaretHeight, 1,
2660 1, reinterpret_cast<BYTE *>(bits));
2661 delete []bits;
2662 BOOL retval = ::CreateCaret(
2663 MainHWND(), sysCaretBitmap,
2664 sysCaretWidth, sysCaretHeight);
2665 ::ShowCaret(MainHWND());
2666 return retval;
2669 BOOL ScintillaWin::DestroySystemCaret() {
2670 ::HideCaret(MainHWND());
2671 BOOL retval = ::DestroyCaret();
2672 if (sysCaretBitmap) {
2673 ::DeleteObject(sysCaretBitmap);
2674 sysCaretBitmap = 0;
2676 return retval;
2679 sptr_t PASCAL ScintillaWin::CTWndProc(
2680 HWND hWnd, UINT iMessage, WPARAM wParam, sptr_t lParam) {
2681 // Find C++ object associated with window.
2682 ScintillaWin *sciThis = reinterpret_cast<ScintillaWin *>(PointerFromWindow(hWnd));
2683 try {
2684 // ctp will be zero if WM_CREATE not seen yet
2685 if (sciThis == 0) {
2686 if (iMessage == WM_CREATE) {
2687 // Associate CallTip object with window
2688 CREATESTRUCT *pCreate = reinterpret_cast<CREATESTRUCT *>(lParam);
2689 SetWindowPointer(hWnd, pCreate->lpCreateParams);
2690 return 0;
2691 } else {
2692 return ::DefWindowProc(hWnd, iMessage, wParam, lParam);
2694 } else {
2695 if (iMessage == WM_NCDESTROY) {
2696 ::SetWindowLong(hWnd, 0, 0);
2697 return ::DefWindowProc(hWnd, iMessage, wParam, lParam);
2698 } else if (iMessage == WM_PAINT) {
2699 PAINTSTRUCT ps;
2700 ::BeginPaint(hWnd, &ps);
2701 Surface *surfaceWindow = Surface::Allocate();
2702 if (surfaceWindow) {
2703 surfaceWindow->Init(ps.hdc, hWnd);
2704 surfaceWindow->SetUnicodeMode(SC_CP_UTF8 == sciThis->ct.codePage);
2705 surfaceWindow->SetDBCSMode(sciThis->ct.codePage);
2706 sciThis->ct.PaintCT(surfaceWindow);
2707 surfaceWindow->Release();
2708 delete surfaceWindow;
2710 ::EndPaint(hWnd, &ps);
2711 return 0;
2712 } else if ((iMessage == WM_NCLBUTTONDOWN) || (iMessage == WM_NCLBUTTONDBLCLK)) {
2713 POINT pt;
2714 pt.x = static_cast<short>(LOWORD(lParam));
2715 pt.y = static_cast<short>(HIWORD(lParam));
2716 ScreenToClient(hWnd, &pt);
2717 sciThis->ct.MouseClick(Point(pt.x, pt.y));
2718 sciThis->CallTipClick();
2719 return 0;
2720 } else if (iMessage == WM_LBUTTONDOWN) {
2721 // This does not fire due to the hit test code
2722 sciThis->ct.MouseClick(Point::FromLong(lParam));
2723 sciThis->CallTipClick();
2724 return 0;
2725 } else if (iMessage == WM_SETCURSOR) {
2726 ::SetCursor(::LoadCursor(NULL, IDC_ARROW));
2727 return 0;
2728 } else if (iMessage == WM_NCHITTEST) {
2729 return HTCAPTION;
2730 } else {
2731 return ::DefWindowProc(hWnd, iMessage, wParam, lParam);
2734 } catch (...) {
2735 sciThis->errorStatus = SC_STATUS_FAILURE;
2737 return ::DefWindowProc(hWnd, iMessage, wParam, lParam);
2740 sptr_t ScintillaWin::DirectFunction(
2741 ScintillaWin *sci, UINT iMessage, uptr_t wParam, sptr_t lParam) {
2742 PLATFORM_ASSERT(::GetCurrentThreadId() == ::GetWindowThreadProcessId(sci->MainHWND(), NULL));
2743 return sci->WndProc(iMessage, wParam, lParam);
2746 extern "C"
2747 #ifndef STATIC_BUILD
2748 __declspec(dllexport)
2749 #endif
2750 sptr_t __stdcall Scintilla_DirectFunction(
2751 ScintillaWin *sci, UINT iMessage, uptr_t wParam, sptr_t lParam) {
2752 return sci->WndProc(iMessage, wParam, lParam);
2755 sptr_t PASCAL ScintillaWin::SWndProc(
2756 HWND hWnd, UINT iMessage, WPARAM wParam, sptr_t lParam) {
2757 //Platform::DebugPrintf("S W:%x M:%x WP:%x L:%x\n", hWnd, iMessage, wParam, lParam);
2759 // Find C++ object associated with window.
2760 ScintillaWin *sci = reinterpret_cast<ScintillaWin *>(PointerFromWindow(hWnd));
2761 // sci will be zero if WM_CREATE not seen yet
2762 if (sci == 0) {
2763 try {
2764 if (iMessage == WM_CREATE) {
2765 // Create C++ object associated with window
2766 sci = new ScintillaWin(hWnd);
2767 SetWindowPointer(hWnd, sci);
2768 return sci->WndProc(iMessage, wParam, lParam);
2770 } catch (...) {
2772 return ::DefWindowProc(hWnd, iMessage, wParam, lParam);
2773 } else {
2774 if (iMessage == WM_NCDESTROY) {
2775 try {
2776 sci->Finalise();
2777 delete sci;
2778 } catch (...) {
2780 ::SetWindowLong(hWnd, 0, 0);
2781 return ::DefWindowProc(hWnd, iMessage, wParam, lParam);
2782 } else {
2783 return sci->WndProc(iMessage, wParam, lParam);
2788 // This function is externally visible so it can be called from container when building statically.
2789 // Must be called once only.
2790 int Scintilla_RegisterClasses(void *hInstance) {
2791 Platform_Initialise(hInstance);
2792 bool result = ScintillaWin::Register(reinterpret_cast<HINSTANCE>(hInstance));
2793 #ifdef SCI_LEXER
2794 Scintilla_LinkLexers();
2795 #endif
2796 return result;
2799 // This function is externally visible so it can be called from container when building statically.
2800 int Scintilla_ReleaseResources() {
2801 bool result = ScintillaWin::Unregister();
2802 Platform_Finalise();
2803 return result;
2806 #ifndef STATIC_BUILD
2807 extern "C" int APIENTRY DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID) {
2808 //Platform::DebugPrintf("Scintilla::DllMain %d %d\n", hInstance, dwReason);
2809 if (dwReason == DLL_PROCESS_ATTACH) {
2810 if (!Scintilla_RegisterClasses(hInstance))
2811 return FALSE;
2812 } else if (dwReason == DLL_PROCESS_DETACH) {
2813 Scintilla_ReleaseResources();
2815 return TRUE;
2817 #endif