// Scintilla source code edit control /** @file ScintillaWin.cxx ** Windows specific subclass of ScintillaBase. **/ // Copyright 1998-2003 by Neil Hodgson // The License.txt file describes the conditions under which this software may be distributed. #include #include #include #include #include #include #define _WIN32_WINNT 0x0500 #include #include #include #include #include "Platform.h" #include "Scintilla.h" #include "SString.h" #ifdef SCI_LEXER #include "SciLexer.h" #include "PropSet.h" #include "Accessor.h" #include "KeyWords.h" #endif #include "ContractionState.h" #include "SVector.h" #include "CellBuffer.h" #include "CallTip.h" #include "KeyMap.h" #include "Indicator.h" #include "XPM.h" #include "LineMarker.h" #include "Style.h" #include "AutoComplete.h" #include "ViewStyle.h" #include "CharClassify.h" #include "Document.h" #include "Editor.h" #include "ScintillaBase.h" #include "UniConversion.h" #ifdef SCI_LEXER #include "ExternalLexer.h" #endif #ifndef SPI_GETWHEELSCROLLLINES #define SPI_GETWHEELSCROLLLINES 104 #endif #ifndef WM_UNICHAR #define WM_UNICHAR 0x0109 #endif #ifndef UNICODE_NOCHAR #define UNICODE_NOCHAR 0xFFFF #endif #ifndef WM_IME_STARTCOMPOSITION #include #endif #include #ifndef __BORLANDC__ #ifndef __DMC__ #include #endif #endif #include #ifndef MK_ALT #define MK_ALT 32 #endif #define SC_WIN_IDLE 5001 // Functions imported from PlatWin extern bool IsNT(); extern void Platform_Initialise(void *hInstance); extern void Platform_Finalise(); /** TOTAL_CONTROL ifdef surrounds code that will only work when ScintillaWin * is derived from ScintillaBase (all features) rather than directly from Editor * (lightweight editor). */ #define TOTAL_CONTROL // GCC has trouble with the standard COM ABI so do it the old C way with explicit vtables. const TCHAR scintillaClassName[] = TEXT("Scintilla"); const TCHAR callClassName[] = TEXT("CallTip"); class ScintillaWin; // Forward declaration for COM interface subobjects /** */ class FormatEnumerator { public: void **vtbl; int ref; int pos; CLIPFORMAT formats[2]; int formatsLen; FormatEnumerator(int pos_, CLIPFORMAT formats_[], int formatsLen_); }; /** */ class DropSource { public: void **vtbl; ScintillaWin *sci; DropSource(); }; /** */ class DataObject { public: void **vtbl; ScintillaWin *sci; DataObject(); }; /** */ class DropTarget { public: void **vtbl; ScintillaWin *sci; DropTarget(); }; /** */ class ScintillaWin : public ScintillaBase { bool lastKeyDownConsumed; bool capturedMouse; unsigned int linesPerScroll; ///< Intellimouse support int wheelDelta; ///< Wheel delta from roll HRGN hRgnUpdate; bool hasOKText; CLIPFORMAT cfColumnSelect; DropSource ds; DataObject dob; DropTarget dt; static HINSTANCE hInstance; ScintillaWin(HWND hwnd); ScintillaWin(const ScintillaWin &) : ScintillaBase() {} virtual ~ScintillaWin(); ScintillaWin &operator=(const ScintillaWin &) { return *this; } virtual void Initialise(); virtual void Finalise(); HWND MainHWND(); static sptr_t DirectFunction( ScintillaWin *sci, UINT iMessage, uptr_t wParam, sptr_t lParam); static sptr_t PASCAL SWndProc( HWND hWnd, UINT iMessage, WPARAM wParam, sptr_t lParam); static sptr_t PASCAL CTWndProc( HWND hWnd, UINT iMessage, WPARAM wParam, sptr_t lParam); enum { invalidTimerID, standardTimerID, idleTimerID }; virtual void StartDrag(); sptr_t WndPaint(uptr_t wParam); sptr_t HandleComposition(uptr_t wParam, sptr_t lParam); virtual bool ValidCodePage(int codePage) const; virtual sptr_t DefWndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam); virtual bool SetIdle(bool on); virtual void SetTicking(bool on); virtual void SetMouseCapture(bool on); virtual bool HaveMouseCapture(); virtual bool PaintContains(PRectangle rc); virtual void ScrollText(int linesToMove); virtual void UpdateSystemCaret(); virtual void SetVerticalScrollPos(); virtual void SetHorizontalScrollPos(); virtual bool ModifyScrollBars(int nMax, int nPage); virtual void NotifyChange(); virtual void NotifyFocus(bool focus); virtual int GetCtrlID(); virtual void NotifyParent(SCNotification scn); virtual void NotifyDoubleClick(Point pt, bool shift); virtual void Copy(); virtual bool CanPaste(); virtual void Paste(); virtual void CreateCallTipWindow(PRectangle rc); virtual void AddToPopUp(const char *label, int cmd = 0, bool enabled = true); virtual void ClaimSelection(); // DBCS void ImeStartComposition(); void ImeEndComposition(); void AddCharBytes(char b0, char b1); void GetIntelliMouseParameters(); virtual void CopyToClipboard(const SelectionText &selectedText); void ScrollMessage(WPARAM wParam); void HorizontalScrollMessage(WPARAM wParam); void RealizeWindowPalette(bool inBackGround); void FullPaint(); void FullPaintDC(HDC dc); bool IsCompatibleDC(HDC dc); virtual int SetScrollInfo(int nBar, LPCSCROLLINFO lpsi, BOOL bRedraw); virtual bool GetScrollInfo(int nBar, LPSCROLLINFO lpsi); void ChangeScrollPos(int barType, int pos); void InsertPasteText(const char *text, int len, int selStart, bool isRectangular); public: // Public for benefit of Scintilla_DirectFunction virtual sptr_t WndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam); /// Implement IUnknown STDMETHODIMP QueryInterface(REFIID riid, PVOID *ppv); STDMETHODIMP_(ULONG)AddRef(); STDMETHODIMP_(ULONG)Release(); /// Implement IDropTarget STDMETHODIMP DragEnter(LPDATAOBJECT pIDataSource, DWORD grfKeyState, POINTL pt, PDWORD pdwEffect); STDMETHODIMP DragOver(DWORD grfKeyState, POINTL pt, PDWORD pdwEffect); STDMETHODIMP DragLeave(); STDMETHODIMP Drop(LPDATAOBJECT pIDataSource, DWORD grfKeyState, POINTL pt, PDWORD pdwEffect); /// Implement important part of IDataObject STDMETHODIMP GetData(FORMATETC *pFEIn, STGMEDIUM *pSTM); // External Lexers #ifdef SCI_LEXER void SetLexerLanguage(const char *languageName); void SetLexer(uptr_t wParam); #endif static bool Register(HINSTANCE hInstance_); static bool Unregister(); friend class DropSource; friend class DataObject; friend class DropTarget; bool DragIsRectangularOK(CLIPFORMAT fmt) { return drag.rectangular && (fmt == cfColumnSelect); } private: // For use in creating a system caret bool HasCaretSizeChanged(); BOOL CreateSystemCaret(); BOOL DestroySystemCaret(); HBITMAP sysCaretBitmap; int sysCaretWidth; int sysCaretHeight; }; HINSTANCE ScintillaWin::hInstance = 0; ScintillaWin::ScintillaWin(HWND hwnd) { lastKeyDownConsumed = false; capturedMouse = false; linesPerScroll = 0; wheelDelta = 0; // Wheel delta from roll hRgnUpdate = 0; hasOKText = false; // There does not seem to be a real standard for indicating that the clipboard // contains a rectangular selection, so copy Developer Studio. cfColumnSelect = static_cast( ::RegisterClipboardFormat(TEXT("MSDEVColumnSelect"))); wMain = hwnd; dob.sci = this; ds.sci = this; dt.sci = this; sysCaretBitmap = 0; sysCaretWidth = 0; sysCaretHeight = 0; Initialise(); } ScintillaWin::~ScintillaWin() {} void ScintillaWin::Initialise() { // Initialize COM. If the app has already done this it will have // no effect. If the app hasnt, we really shouldnt ask them to call // it just so this internal feature works. ::OleInitialize(NULL); } void ScintillaWin::Finalise() { ScintillaBase::Finalise(); SetTicking(false); SetIdle(false); DestroySystemCaret(); ::RevokeDragDrop(MainHWND()); ::OleUninitialize(); } HWND ScintillaWin::MainHWND() { return reinterpret_cast(wMain.GetID()); } void ScintillaWin::StartDrag() { DWORD dwEffect = 0; dropWentOutside = true; IDataObject *pDataObject = reinterpret_cast(&dob); IDropSource *pDropSource = reinterpret_cast(&ds); //Platform::DebugPrintf("About to DoDragDrop %x %x\n", pDataObject, pDropSource); HRESULT hr = ::DoDragDrop( pDataObject, pDropSource, DROPEFFECT_COPY | DROPEFFECT_MOVE, &dwEffect); //Platform::DebugPrintf("DoDragDrop = %x\n", hr); if (SUCCEEDED(hr)) { if ((hr == DRAGDROP_S_DROP) && (dwEffect == DROPEFFECT_MOVE) && dropWentOutside) { // Remove dragged out text ClearSelection(); } } inDragDrop = false; SetDragPosition(invalidPosition); } // Avoid warnings everywhere for old style casts by concentrating them here static WORD LoWord(DWORD l) { return LOWORD(l); } static WORD HiWord(DWORD l) { return HIWORD(l); } static int InputCodePage() { HKL inputLocale = ::GetKeyboardLayout(0); LANGID inputLang = LOWORD(inputLocale); char sCodePage[10]; int res = ::GetLocaleInfo(MAKELCID(inputLang, SORT_DEFAULT), LOCALE_IDEFAULTANSICODEPAGE, sCodePage, sizeof(sCodePage)); if (!res) return 0; return atoi(sCodePage); } #ifndef VK_OEM_2 static const int VK_OEM_2=0xbf; static const int VK_OEM_3=0xc0; static const int VK_OEM_4=0xdb; static const int VK_OEM_5=0xdc; static const int VK_OEM_6=0xdd; #endif /** Map the key codes to their equivalent SCK_ form. */ static int KeyTranslate(int keyIn) { //PLATFORM_ASSERT(!keyIn); switch (keyIn) { case VK_DOWN: return SCK_DOWN; case VK_UP: return SCK_UP; case VK_LEFT: return SCK_LEFT; case VK_RIGHT: return SCK_RIGHT; case VK_HOME: return SCK_HOME; case VK_END: return SCK_END; case VK_PRIOR: return SCK_PRIOR; case VK_NEXT: return SCK_NEXT; case VK_DELETE: return SCK_DELETE; case VK_INSERT: return SCK_INSERT; case VK_ESCAPE: return SCK_ESCAPE; case VK_BACK: return SCK_BACK; case VK_TAB: return SCK_TAB; case VK_RETURN: return SCK_RETURN; case VK_ADD: return SCK_ADD; case VK_SUBTRACT: return SCK_SUBTRACT; case VK_DIVIDE: return SCK_DIVIDE; case VK_OEM_2: return '/'; case VK_OEM_3: return '`'; case VK_OEM_4: return '['; case VK_OEM_5: return '\\'; case VK_OEM_6: return ']'; default: return keyIn; } } LRESULT ScintillaWin::WndPaint(uptr_t wParam) { //ElapsedTime et; // Redirect assertions to debug output and save current state bool assertsPopup = Platform::ShowAssertionPopUps(false); paintState = painting; PAINTSTRUCT ps; PAINTSTRUCT *pps; bool IsOcxCtrl = (wParam != 0); // if wParam != 0, it contains // a PAINSTRUCT* from the OCX // Removed since this interferes with reporting other assertions as it occurs repeatedly //PLATFORM_ASSERT(hRgnUpdate == NULL); hRgnUpdate = ::CreateRectRgn(0, 0, 0, 0); if (IsOcxCtrl) { pps = reinterpret_cast(wParam); } else { ::GetUpdateRgn(MainHWND(), hRgnUpdate, FALSE); pps = &ps; ::BeginPaint(MainHWND(), pps); } AutoSurface surfaceWindow(pps->hdc, this); if (surfaceWindow) { rcPaint = PRectangle(pps->rcPaint.left, pps->rcPaint.top, pps->rcPaint.right, pps->rcPaint.bottom); PRectangle rcClient = GetClientRectangle(); paintingAllText = rcPaint.Contains(rcClient); if (paintingAllText) { //Platform::DebugPrintf("Performing full text paint\n"); } else { //Platform::DebugPrintf("Performing partial paint %d .. %d\n", rcPaint.top, rcPaint.bottom); } Paint(surfaceWindow, rcPaint); surfaceWindow->Release(); } if (hRgnUpdate) { ::DeleteRgn(hRgnUpdate); hRgnUpdate = 0; } if(!IsOcxCtrl) ::EndPaint(MainHWND(), pps); if (paintState == paintAbandoned) { // Painting area was insufficient to cover new styling or brace highlight positions FullPaint(); } paintState = notPainting; // Restore debug output state Platform::ShowAssertionPopUps(assertsPopup); //Platform::DebugPrintf("Paint took %g\n", et.Duration()); return 0l; } sptr_t ScintillaWin::HandleComposition(uptr_t wParam, sptr_t lParam) { #ifdef __DMC__ // Digital Mars compiler does not include Imm library return 0; #else if (lParam & GCS_RESULTSTR) { HIMC hIMC = ::ImmGetContext(MainHWND()); if (hIMC) { const int maxLenInputIME = 200; wchar_t wcs[maxLenInputIME]; LONG bytes = ::ImmGetCompositionStringW(hIMC, GCS_RESULTSTR, wcs, (maxLenInputIME-1)*2); int wides = bytes / 2; if (IsUnicodeMode()) { char utfval[maxLenInputIME * 3]; unsigned int len = UTF8Length(wcs, wides); UTF8FromUCS2(wcs, wides, utfval, len); utfval[len] = '\0'; AddCharUTF(utfval, len); } else { char dbcsval[maxLenInputIME * 2]; int size = ::WideCharToMultiByte(InputCodePage(), 0, wcs, wides, dbcsval, sizeof(dbcsval) - 1, 0, 0); for (int i=0; i(wMain.GetID())); // Get Intellimouse scroll line parameters GetIntelliMouseParameters(); ::RegisterDragDrop(MainHWND(), reinterpret_cast(&dt)); break; case WM_COMMAND: #ifdef TOTAL_CONTROL Command(LoWord(wParam)); #endif break; case WM_PAINT: return WndPaint(wParam); case WM_PRINTCLIENT: { HDC hdc = reinterpret_cast(wParam); if (!IsCompatibleDC(hdc)) { return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam); } FullPaintDC(hdc); } break; case WM_VSCROLL: ScrollMessage(wParam); break; case WM_HSCROLL: HorizontalScrollMessage(wParam); break; case WM_SIZE: { //Platform::DebugPrintf("Scintilla WM_SIZE %d %d\n", LoWord(lParam), HiWord(lParam)); ChangeSize(); } break; case WM_MOUSEWHEEL: // Don't handle datazoom. // (A good idea for datazoom would be to "fold" or "unfold" details. // i.e. if datazoomed out only class structures are visible, when datazooming in the control // structures appear, then eventually the individual statements...) if (wParam & MK_SHIFT) { return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam); } // Either SCROLL or ZOOM. We handle the wheel steppings calculation wheelDelta -= static_cast(HiWord(wParam)); if (abs(wheelDelta) >= WHEEL_DELTA && linesPerScroll > 0) { int linesToScroll = linesPerScroll; if (linesPerScroll == WHEEL_PAGESCROLL) linesToScroll = LinesOnScreen() - 1; if (linesToScroll == 0) { linesToScroll = 1; } linesToScroll *= (wheelDelta / WHEEL_DELTA); if (wheelDelta >= 0) wheelDelta = wheelDelta % WHEEL_DELTA; else wheelDelta = - (-wheelDelta % WHEEL_DELTA); if (wParam & MK_CONTROL) { // Zoom! We play with the font sizes in the styles. // Number of steps/line is ignored, we just care if sizing up or down if (linesToScroll < 0) { KeyCommand(SCI_ZOOMIN); } else { KeyCommand(SCI_ZOOMOUT); } } else { // Scroll ScrollTo(topLine + linesToScroll); } } return 0; case WM_TIMER: if (wParam == standardTimerID && timer.ticking) { Tick(); } else if (wParam == idleTimerID && idler.state) { SendMessage(MainHWND(), SC_WIN_IDLE, 0, 1); } else { return 1; } break; case SC_WIN_IDLE: // wParam=dwTickCountInitial, or 0 to initialize. lParam=bSkipUserInputTest if (idler.state) { if (lParam || (WAIT_TIMEOUT==MsgWaitForMultipleObjects(0,0,0,0, QS_INPUT|QS_HOTKEY))) { if (Idle()) { // User input was given priority above, but all events do get a turn. Other // messages, notifications, etc. will get interleaved with the idle messages. // However, some things like WM_PAINT are a lower priority, and will not fire // when there's a message posted. So, several times a second, we stop and let // the low priority events have a turn (after which the timer will fire again). DWORD dwCurrent = GetTickCount(); DWORD dwStart = wParam ? wParam : dwCurrent; if (dwCurrent >= dwStart && dwCurrent > 200 && dwCurrent - 200 < dwStart) PostMessage(MainHWND(), SC_WIN_IDLE, dwStart, 0); } else { SetIdle(false); } } } break; case WM_GETMINMAXINFO: return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam); case WM_LBUTTONDOWN: { #ifndef __DMC__ // Digital Mars compiler does not include Imm library // For IME, set the composition string as the result string. HIMC hIMC = ::ImmGetContext(MainHWND()); ::ImmNotifyIME(hIMC, NI_COMPOSITIONSTR, CPS_COMPLETE, 0); ::ImmReleaseContext(MainHWND(), hIMC); #endif // //Platform::DebugPrintf("Buttdown %d %x %x %x %x %x\n",iMessage, wParam, lParam, // Platform::IsKeyDown(VK_SHIFT), // Platform::IsKeyDown(VK_CONTROL), // Platform::IsKeyDown(VK_MENU)); ButtonDown(Point::FromLong(lParam), ::GetMessageTime(), (wParam & MK_SHIFT) != 0, (wParam & MK_CONTROL) != 0, Platform::IsKeyDown(VK_MENU)); ::SetFocus(MainHWND()); } break; case WM_MOUSEMOVE: ButtonMove(Point::FromLong(lParam)); break; case WM_LBUTTONUP: ButtonUp(Point::FromLong(lParam), ::GetMessageTime(), (wParam & MK_CONTROL) != 0); break; case WM_SETCURSOR: if (LoWord(lParam) == HTCLIENT) { if (inDragDrop) { DisplayCursor(Window::cursorUp); } else { // Display regular (drag) cursor over selection POINT pt; ::GetCursorPos(&pt); ::ScreenToClient(MainHWND(), &pt); if (PointInSelMargin(Point(pt.x, pt.y))) { DisplayCursor(Window::cursorReverseArrow); } else if (PointInSelection(Point(pt.x, pt.y)) && !SelectionEmpty()) { DisplayCursor(Window::cursorArrow); } else if (PointIsHotspot(Point(pt.x, pt.y))) { DisplayCursor(Window::cursorHand); } else { DisplayCursor(Window::cursorText); } } return TRUE; } else { return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam); } case WM_CHAR: if (!iscntrl(wParam&0xff) || !lastKeyDownConsumed) { if (IsUnicodeMode()) { // For a wide character version of the window: //char utfval[4]; //wchar_t wcs[2] = {wParam, 0}; //unsigned int len = UTF8Length(wcs, 1); //UTF8FromUCS2(wcs, 1, utfval, len); //AddCharUTF(utfval, len); AddCharBytes('\0', LOBYTE(wParam)); } else { AddChar(LOBYTE(wParam)); } } return 0; case WM_UNICHAR: if (wParam == UNICODE_NOCHAR) { return 1; } else if (lastKeyDownConsumed) { return 1; } else { if (IsUnicodeMode()) { char utfval[4]; wchar_t wcs[2] = {wParam, 0}; unsigned int len = UTF8Length(wcs, 1); UTF8FromUCS2(wcs, 1, utfval, len); AddCharUTF(utfval, len); return 1; } else { return 0; } } case WM_SYSKEYDOWN: case WM_KEYDOWN: { //Platform::DebugPrintf("S keydown %d %x %x %x %x\n",iMessage, wParam, lParam, ::IsKeyDown(VK_SHIFT), ::IsKeyDown(VK_CONTROL)); lastKeyDownConsumed = false; int ret = KeyDown(KeyTranslate(wParam), Platform::IsKeyDown(VK_SHIFT), Platform::IsKeyDown(VK_CONTROL), Platform::IsKeyDown(VK_MENU), &lastKeyDownConsumed); if (!ret && !lastKeyDownConsumed) { return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam); } break; } case WM_IME_KEYDOWN: return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam); case WM_KEYUP: //Platform::DebugPrintf("S keyup %d %x %x\n",iMessage, wParam, lParam); return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam); case WM_SETTINGCHANGE: //Platform::DebugPrintf("Setting Changed\n"); InvalidateStyleData(); // Get Intellimouse scroll line parameters GetIntelliMouseParameters(); break; case WM_GETDLGCODE: return DLGC_HASSETSEL | DLGC_WANTALLKEYS; case WM_KILLFOCUS: { HWND wOther = reinterpret_cast(wParam); HWND wThis = MainHWND(); HWND wCT = reinterpret_cast(ct.wCallTip.GetID()); if (!wParam || !(::IsChild(wThis,wOther) || (wOther == wCT))) { SetFocusState(false); DestroySystemCaret(); } } //RealizeWindowPalette(true); break; case WM_SETFOCUS: SetFocusState(true); RealizeWindowPalette(false); DestroySystemCaret(); CreateSystemCaret(); break; case WM_SYSCOLORCHANGE: //Platform::DebugPrintf("Setting Changed\n"); InvalidateStyleData(); break; case WM_PALETTECHANGED: if (wParam != reinterpret_cast(MainHWND())) { //Platform::DebugPrintf("** Palette Changed\n"); RealizeWindowPalette(true); } break; case WM_QUERYNEWPALETTE: //Platform::DebugPrintf("** Query palette\n"); RealizeWindowPalette(false); break; case WM_IME_STARTCOMPOSITION: // dbcs ImeStartComposition(); return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam); case WM_IME_ENDCOMPOSITION: // dbcs ImeEndComposition(); return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam); case WM_IME_COMPOSITION: return HandleComposition(wParam, lParam); case WM_IME_CHAR: { AddCharBytes(HIBYTE(wParam), LOBYTE(wParam)); return 0; } case WM_CONTEXTMENU: #ifdef TOTAL_CONTROL if (displayPopupMenu) { Point pt = Point::FromLong(lParam); if ((pt.x == -1) && (pt.y == -1)) { // Caused by keyboard so display menu near caret pt = LocationFromPosition(currentPos); POINT spt = {pt.x, pt.y}; ::ClientToScreen(MainHWND(), &spt); pt = Point(spt.x, spt.y); } ContextMenu(pt); return 0; } #endif return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam); case WM_INPUTLANGCHANGE: //::SetThreadLocale(LOWORD(lParam)); return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam); case WM_INPUTLANGCHANGEREQUEST: return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam); case WM_ERASEBKGND: return 1; // Avoid any background erasure as whole window painted. case WM_CAPTURECHANGED: capturedMouse = false; return 0; // These are not handled in Scintilla and its faster to dispatch them here. // Also moves time out to here so profile doesn't count lots of empty message calls. case WM_MOVE: case WM_MOUSEACTIVATE: case WM_NCHITTEST: case WM_NCCALCSIZE: case WM_NCPAINT: case WM_NCMOUSEMOVE: case WM_NCLBUTTONDOWN: case WM_IME_SETCONTEXT: case WM_IME_NOTIFY: case WM_SYSCOMMAND: case WM_WINDOWPOSCHANGING: case WM_WINDOWPOSCHANGED: return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam); case EM_LINEFROMCHAR: if (static_cast(wParam) < 0) { wParam = SelectionStart(); } return pdoc->LineFromPosition(wParam); case EM_EXLINEFROMCHAR: return pdoc->LineFromPosition(lParam); case EM_GETSEL: if (wParam) { *reinterpret_cast(wParam) = SelectionStart(); } if (lParam) { *reinterpret_cast(lParam) = SelectionEnd(); } return MAKELONG(SelectionStart(), SelectionEnd()); case EM_EXGETSEL: { if (lParam == 0) { return 0; } CharacterRange *pCR = reinterpret_cast(lParam); pCR->cpMin = SelectionStart(); pCR->cpMax = SelectionEnd(); } break; case EM_SETSEL: { int nStart = static_cast(wParam); int nEnd = static_cast(lParam); if (nStart == 0 && nEnd == -1) { nEnd = pdoc->Length(); } if (nStart == -1) { nStart = nEnd; // Remove selection } if (nStart > nEnd) { SetSelection(nEnd, nStart); } else { SetSelection(nStart, nEnd); } EnsureCaretVisible(); } break; case EM_EXSETSEL: { if (lParam == 0) { return 0; } CharacterRange *pCR = reinterpret_cast(lParam); selType = selStream; if (pCR->cpMin == 0 && pCR->cpMax == -1) { SetSelection(pCR->cpMin, pdoc->Length()); } else { SetSelection(pCR->cpMin, pCR->cpMax); } EnsureCaretVisible(); return pdoc->LineFromPosition(SelectionStart()); } case SCI_GETDIRECTFUNCTION: return reinterpret_cast(DirectFunction); case SCI_GETDIRECTPOINTER: return reinterpret_cast(this); case SCI_GRABFOCUS: ::SetFocus(MainHWND()); break; #ifdef SCI_LEXER case SCI_LOADLEXERLIBRARY: LexerManager::GetInstance()->Load(reinterpret_cast(lParam)); break; #endif default: return ScintillaBase::WndProc(iMessage, wParam, lParam); } return 0l; } bool ScintillaWin::ValidCodePage(int codePage) const { return codePage == 0 || codePage == SC_CP_UTF8 || codePage == 932 || codePage == 936 || codePage == 949 || codePage == 950 || codePage == 1361; } sptr_t ScintillaWin::DefWndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam) { return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam); } void ScintillaWin::SetTicking(bool on) { if (timer.ticking != on) { timer.ticking = on; if (timer.ticking) { timer.tickerID = ::SetTimer(MainHWND(), standardTimerID, timer.tickSize, NULL) ? reinterpret_cast(standardTimerID) : 0; } else { ::KillTimer(MainHWND(), reinterpret_cast(timer.tickerID)); timer.tickerID = 0; } } timer.ticksToWait = caret.period; } bool ScintillaWin::SetIdle(bool on) { // On Win32 the Idler is implemented as a Timer on the Scintilla window. This // takes advantage of the fact that WM_TIMER messages are very low priority, // and are only posted when the message queue is empty, i.e. during idle time. if (idler.state != on) { if (on) { idler.idlerID = ::SetTimer(MainHWND(), idleTimerID, 10, NULL) ? reinterpret_cast(idleTimerID) : 0; } else { ::KillTimer(MainHWND(), reinterpret_cast(idler.idlerID)); idler.idlerID = 0; } idler.state = idler.idlerID != 0; } return idler.state; } void ScintillaWin::SetMouseCapture(bool on) { if (mouseDownCaptures) { if (on) { ::SetCapture(MainHWND()); } else { ::ReleaseCapture(); } } capturedMouse = on; } bool ScintillaWin::HaveMouseCapture() { // Cannot just see if GetCapture is this window as the scroll bar also sets capture for the window return capturedMouse; //return capturedMouse && (::GetCapture() == MainHWND()); } bool ScintillaWin::PaintContains(PRectangle rc) { bool contains = true; if (paintState == painting) { if (!rcPaint.Contains(rc)) { contains = false; } else { // In bounding rectangle so check more accurately using region HRGN hRgnRange = ::CreateRectRgn(rc.left, rc.top, rc.right, rc.bottom); if (hRgnRange) { HRGN hRgnDest = ::CreateRectRgn(0, 0, 0, 0); if (hRgnDest) { int combination = ::CombineRgn(hRgnDest, hRgnRange, hRgnUpdate, RGN_DIFF); if (combination != NULLREGION) { contains = false; } ::DeleteRgn(hRgnDest); } ::DeleteRgn(hRgnRange); } } } return contains; } void ScintillaWin::ScrollText(int linesToMove) { //Platform::DebugPrintf("ScintillaWin::ScrollText %d\n", linesToMove); ::ScrollWindow(MainHWND(), 0, vs.lineHeight * linesToMove, 0, 0); ::UpdateWindow(MainHWND()); } void ScintillaWin::UpdateSystemCaret() { if (hasFocus) { if (HasCaretSizeChanged()) { DestroySystemCaret(); CreateSystemCaret(); } Point pos = LocationFromPosition(currentPos); ::SetCaretPos(pos.x, pos.y); } } int ScintillaWin::SetScrollInfo(int nBar, LPCSCROLLINFO lpsi, BOOL bRedraw) { return ::SetScrollInfo(MainHWND(), nBar, lpsi, bRedraw); } bool ScintillaWin::GetScrollInfo(int nBar, LPSCROLLINFO lpsi) { return ::GetScrollInfo(MainHWND(), nBar, lpsi) ? true : false; } // Change the scroll position but avoid repaint if changing to same value void ScintillaWin::ChangeScrollPos(int barType, int pos) { SCROLLINFO sci = { sizeof(sci),0,0,0,0,0,0 }; sci.fMask = SIF_POS; GetScrollInfo(barType, &sci); if (sci.nPos != pos) { DwellEnd(true); sci.nPos = pos; SetScrollInfo(barType, &sci, TRUE); } } void ScintillaWin::SetVerticalScrollPos() { ChangeScrollPos(SB_VERT, topLine); } void ScintillaWin::SetHorizontalScrollPos() { ChangeScrollPos(SB_HORZ, xOffset); } bool ScintillaWin::ModifyScrollBars(int nMax, int nPage) { bool modified = false; SCROLLINFO sci = { sizeof(sci),0,0,0,0,0,0 }; sci.fMask = SIF_PAGE | SIF_RANGE; GetScrollInfo(SB_VERT, &sci); int vertEndPreferred = nMax; if (!verticalScrollBarVisible) vertEndPreferred = 0; if ((sci.nMin != 0) || (sci.nMax != vertEndPreferred) || (sci.nPage != static_cast(nPage)) || (sci.nPos != 0)) { //Platform::DebugPrintf("Scroll info changed %d %d %d %d %d\n", // sci.nMin, sci.nMax, sci.nPage, sci.nPos, sci.nTrackPos); sci.fMask = SIF_PAGE | SIF_RANGE; sci.nMin = 0; sci.nMax = vertEndPreferred; sci.nPage = nPage; sci.nPos = 0; sci.nTrackPos = 1; SetScrollInfo(SB_VERT, &sci, TRUE); modified = true; } PRectangle rcText = GetTextRectangle(); int horizEndPreferred = scrollWidth; if (horizEndPreferred < 0) horizEndPreferred = 0; if (!horizontalScrollBarVisible || (wrapState != eWrapNone)) horizEndPreferred = 0; unsigned int pageWidth = rcText.Width(); sci.fMask = SIF_PAGE | SIF_RANGE; GetScrollInfo(SB_HORZ, &sci); if ((sci.nMin != 0) || (sci.nMax != horizEndPreferred) || (sci.nPage != pageWidth) || (sci.nPos != 0)) { sci.fMask = SIF_PAGE | SIF_RANGE; sci.nMin = 0; sci.nMax = horizEndPreferred; sci.nPage = pageWidth; sci.nPos = 0; sci.nTrackPos = 1; SetScrollInfo(SB_HORZ, &sci, TRUE); modified = true; if (scrollWidth < static_cast(pageWidth)) { HorizontalScrollTo(0); } } return modified; } void ScintillaWin::NotifyChange() { ::SendMessage(::GetParent(MainHWND()), WM_COMMAND, MAKELONG(GetCtrlID(), SCEN_CHANGE), reinterpret_cast(MainHWND())); } void ScintillaWin::NotifyFocus(bool focus) { ::SendMessage(::GetParent(MainHWND()), WM_COMMAND, MAKELONG(GetCtrlID(), focus ? SCEN_SETFOCUS : SCEN_KILLFOCUS), reinterpret_cast(MainHWND())); } int ScintillaWin::GetCtrlID() { return ::GetDlgCtrlID(reinterpret_cast(wMain.GetID())); } void ScintillaWin::NotifyParent(SCNotification scn) { scn.nmhdr.hwndFrom = MainHWND(); scn.nmhdr.idFrom = GetCtrlID(); ::SendMessage(::GetParent(MainHWND()), WM_NOTIFY, GetCtrlID(), reinterpret_cast(&scn)); } void ScintillaWin::NotifyDoubleClick(Point pt, bool shift) { //Platform::DebugPrintf("ScintillaWin Double click 0\n"); ScintillaBase::NotifyDoubleClick(pt, shift); // Send myself a WM_LBUTTONDBLCLK, so the container can handle it too. ::SendMessage(MainHWND(), WM_LBUTTONDBLCLK, shift ? MK_SHIFT : 0, MAKELPARAM(pt.x, pt.y)); } void ScintillaWin::Copy() { //Platform::DebugPrintf("Copy\n"); if (currentPos != anchor) { SelectionText selectedText; CopySelectionRange(&selectedText); CopyToClipboard(selectedText); } } bool ScintillaWin::CanPaste() { if (!Editor::CanPaste()) return false; if (::IsClipboardFormatAvailable(CF_TEXT)) return true; if (IsUnicodeMode()) return ::IsClipboardFormatAvailable(CF_UNICODETEXT) != 0; return false; } static UINT CodePageFromCharSet(DWORD characterSet, UINT documentCodePage) { CHARSETINFO ci = { 0, 0, { { 0, 0, 0, 0 }, { 0, 0 } } }; BOOL bci = ::TranslateCharsetInfo((DWORD*)characterSet, &ci, TCI_SRCCHARSET); UINT cp; if (bci) cp = ci.ciACP; else cp = documentCodePage; CPINFO cpi; if (!IsValidCodePage(cp) && !GetCPInfo(cp, &cpi)) cp = CP_ACP; return cp; } class GlobalMemory { HGLOBAL hand; public: void *ptr; GlobalMemory() : hand(0), ptr(0) { } GlobalMemory(HGLOBAL hand_) : hand(hand_), ptr(0) { if (hand) { ptr = ::GlobalLock(hand); } } ~GlobalMemory() { PLATFORM_ASSERT(!ptr); } void Allocate(size_t bytes) { hand = ::GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT, bytes); if (hand) { ptr = ::GlobalLock(hand); } } HGLOBAL Unlock() { PLATFORM_ASSERT(ptr); HGLOBAL handCopy = hand; ::GlobalUnlock(hand); ptr = 0; hand = 0; return handCopy; } void SetClip(UINT uFormat) { ::SetClipboardData(uFormat, Unlock()); } operator bool() { return ptr != 0; } SIZE_T Size() { return ::GlobalSize(hand); } }; void ScintillaWin::InsertPasteText(const char *text, int len, int selStart, bool isRectangular) { if (isRectangular) { PasteRectangular(selStart, text, len); } else { if (convertPastes) { // Convert line endings of the paste into our local line-endings mode char *convertedString = Document::TransformLineEnds(&len, text, len, pdoc->eolMode); if (pdoc->InsertString(currentPos, convertedString, len)) { SetEmptySelection(currentPos + len); } delete []convertedString; } else { if (pdoc->InsertString(currentPos, text, len)) { SetEmptySelection(currentPos + len); } } } } void ScintillaWin::Paste() { if (!::OpenClipboard(MainHWND())) return; pdoc->BeginUndoAction(); ClearSelection(); int selStart = SelectionStart(); bool isRectangular = ::IsClipboardFormatAvailable(cfColumnSelect) != 0; // Always use CF_UNICODETEXT if available GlobalMemory memUSelection(::GetClipboardData(CF_UNICODETEXT)); if (memUSelection) { wchar_t *uptr = static_cast(memUSelection.ptr); if (uptr) { unsigned int len; char *putf; // Default Scintilla behaviour in Unicode mode if (IsUnicodeMode()) { unsigned int bytes = memUSelection.Size(); len = UTF8Length(uptr, bytes / 2); putf = new char[len + 1]; if (putf) { UTF8FromUCS2(uptr, bytes / 2, putf, len); } } else { // CF_UNICODETEXT available, but not in Unicode mode // Convert from Unicode to current Scintilla code page UINT cpDest = CodePageFromCharSet( vs.styles[STYLE_DEFAULT].characterSet, pdoc->dbcsCodePage); len = ::WideCharToMultiByte(cpDest, 0, uptr, -1, NULL, 0, NULL, NULL) - 1; // subtract 0 terminator putf = new char[len + 1]; if (putf) { ::WideCharToMultiByte(cpDest, 0, uptr, -1, putf, len + 1, NULL, NULL); } } if (putf) { InsertPasteText(putf, len, selStart, isRectangular); delete []putf; } } memUSelection.Unlock(); } else { // CF_UNICODETEXT not available, paste ANSI text GlobalMemory memSelection(::GetClipboardData(CF_TEXT)); if (memSelection) { char *ptr = static_cast(memSelection.ptr); if (ptr) { unsigned int bytes = memSelection.Size(); unsigned int len = bytes; for (unsigned int i = 0; i < bytes; i++) { if ((len == bytes) && (0 == ptr[i])) len = i; } // In Unicode mode, convert clipboard text to UTF-8 if (IsUnicodeMode()) { wchar_t *uptr = new wchar_t[len+1]; unsigned int ulen = ::MultiByteToWideChar(CP_ACP, 0, ptr, len, uptr, len+1); unsigned int mlen = UTF8Length(uptr, ulen); char *putf = new char[mlen + 1]; if (putf) { // CP_UTF8 not available on Windows 95, so use UTF8FromUCS2() UTF8FromUCS2(uptr, ulen, putf, mlen); } delete []uptr; if (putf) { InsertPasteText(putf, mlen, selStart, isRectangular); delete []putf; } } else { InsertPasteText(ptr, len, selStart, isRectangular); } } memSelection.Unlock(); } } ::CloseClipboard(); pdoc->EndUndoAction(); NotifyChange(); Redraw(); } void ScintillaWin::CreateCallTipWindow(PRectangle) { #ifdef TOTAL_CONTROL if (!ct.wCallTip.Created()) { ct.wCallTip = ::CreateWindow(callClassName, TEXT("ACallTip"), WS_POPUP, 100, 100, 150, 20, MainHWND(), 0, GetWindowInstance(MainHWND()), this); ct.wDraw = ct.wCallTip; } #endif } void ScintillaWin::AddToPopUp(const char *label, int cmd, bool enabled) { #ifdef TOTAL_CONTROL HMENU hmenuPopup = reinterpret_cast(popup.GetID()); if (!label[0]) ::AppendMenu(hmenuPopup, MF_SEPARATOR, 0, ""); else if (enabled) ::AppendMenu(hmenuPopup, MF_STRING, cmd, label); else ::AppendMenu(hmenuPopup, MF_STRING | MF_DISABLED | MF_GRAYED, cmd, label); #endif } void ScintillaWin::ClaimSelection() { // Windows does not have a primary selection } #ifdef SCI_LEXER /* Initial Windows-Only implementation of the external lexer system in ScintillaWin class. Intention is to create a LexerModule subclass (?) to have lex and fold methods which will call out to their relevant DLLs... */ void ScintillaWin::SetLexer(uptr_t wParam) { lexLanguage = wParam; lexCurrent = LexerModule::Find(lexLanguage); if (!lexCurrent) lexCurrent = LexerModule::Find(SCLEX_NULL); } void ScintillaWin::SetLexerLanguage(const char *languageName) { lexLanguage = SCLEX_CONTAINER; lexCurrent = LexerModule::Find(languageName); if (!lexCurrent) lexCurrent = LexerModule::Find(SCLEX_NULL); if (lexCurrent) lexLanguage = lexCurrent->GetLanguage(); } #endif /// Implement IUnknown STDMETHODIMP_(ULONG)FormatEnumerator_AddRef(FormatEnumerator *fe); STDMETHODIMP FormatEnumerator_QueryInterface(FormatEnumerator *fe, REFIID riid, PVOID *ppv) { //Platform::DebugPrintf("EFE QI"); *ppv = NULL; if (riid == IID_IUnknown) *ppv = reinterpret_cast(fe); if (riid == IID_IEnumFORMATETC) *ppv = reinterpret_cast(fe); if (!*ppv) return E_NOINTERFACE; FormatEnumerator_AddRef(fe); return S_OK; } STDMETHODIMP_(ULONG)FormatEnumerator_AddRef(FormatEnumerator *fe) { return ++fe->ref; } STDMETHODIMP_(ULONG)FormatEnumerator_Release(FormatEnumerator *fe) { fe->ref--; if (fe->ref > 0) return fe->ref; delete fe; return 0; } /// Implement IEnumFORMATETC STDMETHODIMP FormatEnumerator_Next(FormatEnumerator *fe, ULONG celt, FORMATETC *rgelt, ULONG *pceltFetched) { //Platform::DebugPrintf("EFE Next %d %d", fe->pos, celt); if (rgelt == NULL) return E_POINTER; // We only support one format, so this is simple. unsigned int putPos = 0; while ((fe->pos < fe->formatsLen) && (putPos < celt)) { rgelt->cfFormat = fe->formats[fe->pos]; rgelt->ptd = 0; rgelt->dwAspect = DVASPECT_CONTENT; rgelt->lindex = -1; rgelt->tymed = TYMED_HGLOBAL; fe->pos++; putPos++; } if (pceltFetched) *pceltFetched = putPos; return putPos ? S_OK : S_FALSE; } STDMETHODIMP FormatEnumerator_Skip(FormatEnumerator *fe, ULONG celt) { fe->pos += celt; return S_OK; } STDMETHODIMP FormatEnumerator_Reset(FormatEnumerator *fe) { fe->pos = 0; return S_OK; } STDMETHODIMP FormatEnumerator_Clone(FormatEnumerator *fe, IEnumFORMATETC **ppenum) { FormatEnumerator *pfe = new FormatEnumerator(fe->pos, fe->formats, fe->formatsLen); return FormatEnumerator_QueryInterface(pfe, IID_IEnumFORMATETC, reinterpret_cast(ppenum)); } static void *vtFormatEnumerator[] = { (void *)(FormatEnumerator_QueryInterface), (void *)(FormatEnumerator_AddRef), (void *)(FormatEnumerator_Release), (void *)(FormatEnumerator_Next), (void *)(FormatEnumerator_Skip), (void *)(FormatEnumerator_Reset), (void *)(FormatEnumerator_Clone) }; FormatEnumerator::FormatEnumerator(int pos_, CLIPFORMAT formats_[], int formatsLen_) { vtbl = vtFormatEnumerator; ref = 0; // First QI adds first reference... pos = pos_; formatsLen = formatsLen_; for (int i=0;isci->QueryInterface(riid, ppv); } STDMETHODIMP_(ULONG)DropSource_AddRef(DropSource *ds) { return ds->sci->AddRef(); } STDMETHODIMP_(ULONG)DropSource_Release(DropSource *ds) { return ds->sci->Release(); } /// Implement IDropSource STDMETHODIMP DropSource_QueryContinueDrag(DropSource *, BOOL fEsc, DWORD grfKeyState) { if (fEsc) return DRAGDROP_S_CANCEL; if (!(grfKeyState & MK_LBUTTON)) return DRAGDROP_S_DROP; return S_OK; } STDMETHODIMP DropSource_GiveFeedback(DropSource *, DWORD) { return DRAGDROP_S_USEDEFAULTCURSORS; } static void *vtDropSource[] = { (void *)(DropSource_QueryInterface), (void *)(DropSource_AddRef), (void *)(DropSource_Release), (void *)(DropSource_QueryContinueDrag), (void *)(DropSource_GiveFeedback) }; DropSource::DropSource() { vtbl = vtDropSource; sci = 0; } /// Implement IUnkown STDMETHODIMP DataObject_QueryInterface(DataObject *pd, REFIID riid, PVOID *ppv) { //Platform::DebugPrintf("DO QI %x\n", pd); return pd->sci->QueryInterface(riid, ppv); } STDMETHODIMP_(ULONG)DataObject_AddRef(DataObject *pd) { return pd->sci->AddRef(); } STDMETHODIMP_(ULONG)DataObject_Release(DataObject *pd) { return pd->sci->Release(); } /// Implement IDataObject STDMETHODIMP DataObject_GetData(DataObject *pd, FORMATETC *pFEIn, STGMEDIUM *pSTM) { return pd->sci->GetData(pFEIn, pSTM); } STDMETHODIMP DataObject_GetDataHere(DataObject *, FORMATETC *, STGMEDIUM *) { //Platform::DebugPrintf("DOB GetDataHere\n"); return E_NOTIMPL; } STDMETHODIMP DataObject_QueryGetData(DataObject *pd, FORMATETC *pFE) { if (pd->sci->DragIsRectangularOK(pFE->cfFormat) && pFE->ptd == 0 && (pFE->dwAspect & DVASPECT_CONTENT) != 0 && pFE->lindex == -1 && (pFE->tymed & TYMED_HGLOBAL) != 0 ) { return S_OK; } bool formatOK = (pFE->cfFormat == CF_TEXT) || ((pFE->cfFormat == CF_UNICODETEXT) && pd->sci->IsUnicodeMode()); if (!formatOK || pFE->ptd != 0 || (pFE->dwAspect & DVASPECT_CONTENT) == 0 || pFE->lindex != -1 || (pFE->tymed & TYMED_HGLOBAL) == 0 ) { //Platform::DebugPrintf("DOB QueryGetData No %x\n",pFE->cfFormat); //return DATA_E_FORMATETC; return S_FALSE; } //Platform::DebugPrintf("DOB QueryGetData OK %x\n",pFE->cfFormat); return S_OK; } STDMETHODIMP DataObject_GetCanonicalFormatEtc(DataObject *pd, FORMATETC *, FORMATETC *pFEOut) { //Platform::DebugPrintf("DOB GetCanon\n"); if (pd->sci->IsUnicodeMode()) pFEOut->cfFormat = CF_UNICODETEXT; else pFEOut->cfFormat = CF_TEXT; pFEOut->ptd = 0; pFEOut->dwAspect = DVASPECT_CONTENT; pFEOut->lindex = -1; pFEOut->tymed = TYMED_HGLOBAL; return S_OK; } STDMETHODIMP DataObject_SetData(DataObject *, FORMATETC *, STGMEDIUM *, BOOL) { //Platform::DebugPrintf("DOB SetData\n"); return E_FAIL; } STDMETHODIMP DataObject_EnumFormatEtc(DataObject *pd, DWORD dwDirection, IEnumFORMATETC **ppEnum) { //Platform::DebugPrintf("DOB EnumFormatEtc %d\n", dwDirection); if (dwDirection != DATADIR_GET) { *ppEnum = 0; return E_FAIL; } FormatEnumerator *pfe; if (pd->sci->IsUnicodeMode()) { CLIPFORMAT formats[] = {CF_UNICODETEXT, CF_TEXT}; pfe = new FormatEnumerator(0, formats, 2); } else { CLIPFORMAT formats[] = {CF_TEXT}; pfe = new FormatEnumerator(0, formats, 1); } return FormatEnumerator_QueryInterface(pfe, IID_IEnumFORMATETC, reinterpret_cast(ppEnum)); } STDMETHODIMP DataObject_DAdvise(DataObject *, FORMATETC *, DWORD, IAdviseSink *, PDWORD) { //Platform::DebugPrintf("DOB DAdvise\n"); return E_FAIL; } STDMETHODIMP DataObject_DUnadvise(DataObject *, DWORD) { //Platform::DebugPrintf("DOB DUnadvise\n"); return E_FAIL; } STDMETHODIMP DataObject_EnumDAdvise(DataObject *, IEnumSTATDATA **) { //Platform::DebugPrintf("DOB EnumDAdvise\n"); return E_FAIL; } static void *vtDataObject[] = { (void *)(DataObject_QueryInterface), (void *)(DataObject_AddRef), (void *)(DataObject_Release), (void *)(DataObject_GetData), (void *)(DataObject_GetDataHere), (void *)(DataObject_QueryGetData), (void *)(DataObject_GetCanonicalFormatEtc), (void *)(DataObject_SetData), (void *)(DataObject_EnumFormatEtc), (void *)(DataObject_DAdvise), (void *)(DataObject_DUnadvise), (void *)(DataObject_EnumDAdvise) }; DataObject::DataObject() { vtbl = vtDataObject; sci = 0; } /// Implement IUnknown STDMETHODIMP DropTarget_QueryInterface(DropTarget *dt, REFIID riid, PVOID *ppv) { //Platform::DebugPrintf("DT QI %x\n", dt); return dt->sci->QueryInterface(riid, ppv); } STDMETHODIMP_(ULONG)DropTarget_AddRef(DropTarget *dt) { return dt->sci->AddRef(); } STDMETHODIMP_(ULONG)DropTarget_Release(DropTarget *dt) { return dt->sci->Release(); } /// Implement IDropTarget by forwarding to Scintilla STDMETHODIMP DropTarget_DragEnter(DropTarget *dt, LPDATAOBJECT pIDataSource, DWORD grfKeyState, POINTL pt, PDWORD pdwEffect) { return dt->sci->DragEnter(pIDataSource, grfKeyState, pt, pdwEffect); } STDMETHODIMP DropTarget_DragOver(DropTarget *dt, DWORD grfKeyState, POINTL pt, PDWORD pdwEffect) { return dt->sci->DragOver(grfKeyState, pt, pdwEffect); } STDMETHODIMP DropTarget_DragLeave(DropTarget *dt) { return dt->sci->DragLeave(); } STDMETHODIMP DropTarget_Drop(DropTarget *dt, LPDATAOBJECT pIDataSource, DWORD grfKeyState, POINTL pt, PDWORD pdwEffect) { return dt->sci->Drop(pIDataSource, grfKeyState, pt, pdwEffect); } static void *vtDropTarget[] = { (void *)(DropTarget_QueryInterface), (void *)(DropTarget_AddRef), (void *)(DropTarget_Release), (void *)(DropTarget_DragEnter), (void *)(DropTarget_DragOver), (void *)(DropTarget_DragLeave), (void *)(DropTarget_Drop) }; DropTarget::DropTarget() { vtbl = vtDropTarget; sci = 0; } /** * DBCS: support Input Method Editor (IME). * Called when IME Window opened. */ void ScintillaWin::ImeStartComposition() { #ifndef __DMC__ // Digital Mars compiler does not include Imm library if (caret.active) { // Move IME Window to current caret position HIMC hIMC = ::ImmGetContext(MainHWND()); Point pos = LocationFromPosition(currentPos); COMPOSITIONFORM CompForm; CompForm.dwStyle = CFS_POINT; CompForm.ptCurrentPos.x = pos.x; CompForm.ptCurrentPos.y = pos.y; ::ImmSetCompositionWindow(hIMC, &CompForm); // Set font of IME window to same as surrounded text. if (stylesValid) { // Since the style creation code has been made platform independent, // The logfont for the IME is recreated here. int styleHere = (pdoc->StyleAt(currentPos)) & 31; LOGFONT lf = {0,0,0,0,0,0,0,0,0,0,0,0,0,TEXT("")}; int sizeZoomed = vs.styles[styleHere].size + vs.zoomLevel; if (sizeZoomed <= 2) // Hangs if sizeZoomed <= 1 sizeZoomed = 2; AutoSurface surface(this); int deviceHeight = sizeZoomed; if (surface) { deviceHeight = (sizeZoomed * surface->LogPixelsY()) / 72; } // The negative is to allow for leading lf.lfHeight = -(abs(deviceHeight)); lf.lfWeight = vs.styles[styleHere].bold ? FW_BOLD : FW_NORMAL; lf.lfItalic = static_cast(vs.styles[styleHere].italic ? 1 : 0); lf.lfCharSet = DEFAULT_CHARSET; lf.lfFaceName[0] = '\0'; if (vs.styles[styleHere].fontName) strcpy(lf.lfFaceName, vs.styles[styleHere].fontName); ::ImmSetCompositionFont(hIMC, &lf); } ::ImmReleaseContext(MainHWND(), hIMC); // Caret is displayed in IME window. So, caret in Scintilla is useless. DropCaret(); } #endif } /** Called when IME Window closed. */ void ScintillaWin::ImeEndComposition() { ShowCaretAtCurrentPosition(); } void ScintillaWin::AddCharBytes(char b0, char b1) { int inputCodePage = InputCodePage(); if (inputCodePage && IsUnicodeMode()) { char utfval[4]="\0\0\0"; char ansiChars[3]; wchar_t wcs[2]; if (b0) { // Two bytes from IME ansiChars[0] = b0; ansiChars[1] = b1; ansiChars[2] = '\0'; ::MultiByteToWideChar(inputCodePage, 0, ansiChars, 2, wcs, 1); } else { ansiChars[0] = b1; ansiChars[1] = '\0'; ::MultiByteToWideChar(inputCodePage, 0, ansiChars, 1, wcs, 1); } unsigned int len = UTF8Length(wcs, 1); UTF8FromUCS2(wcs, 1, utfval, len); utfval[len] = '\0'; AddCharUTF(utfval, len ? len : 1); } else if (b0) { char dbcsChars[3]; dbcsChars[0] = b0; dbcsChars[1] = b1; dbcsChars[2] = '\0'; AddCharUTF(dbcsChars, 2, true); } else { AddChar(b1); } } void ScintillaWin::GetIntelliMouseParameters() { // This retrieves the number of lines per scroll as configured inthe Mouse Properties sheet in Control Panel ::SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, &linesPerScroll, 0); } void ScintillaWin::CopyToClipboard(const SelectionText &selectedText) { if (!::OpenClipboard(MainHWND())) return ; ::EmptyClipboard(); GlobalMemory uniText; // Default Scintilla behaviour in Unicode mode if (IsUnicodeMode()) { int uchars = UCS2Length(selectedText.s, selectedText.len); uniText.Allocate(2 * uchars); if (uniText) { UCS2FromUTF8(selectedText.s, selectedText.len, static_cast(uniText.ptr), uchars); } } else { // Not Unicode mode // Convert to Unicode using the current Scintilla code page UINT cpSrc = CodePageFromCharSet( selectedText.characterSet, selectedText.codePage); uniText.Allocate(2 * selectedText.len); if (uniText) { ::MultiByteToWideChar(cpSrc, 0, selectedText.s, selectedText.len, static_cast(uniText.ptr), selectedText.len); } } if (uniText) { if (!IsNT()) { // Copy ANSI text to clipboard on Windows 9x // Convert from Unicode text, so other ANSI programs can // paste the text // Windows NT, 2k, XP automatically generates CF_TEXT GlobalMemory ansiText; ansiText.Allocate(selectedText.len); if (ansiText) { ::WideCharToMultiByte(CP_ACP, 0, static_cast(uniText.ptr), -1, static_cast(ansiText.ptr), selectedText.len, NULL, NULL); ansiText.SetClip(CF_TEXT); } } uniText.SetClip(CF_UNICODETEXT); } else { // There was a failure - try to copy at least ANSI text GlobalMemory ansiText; ansiText.Allocate(selectedText.len); if (ansiText) { memcpy(static_cast(ansiText.ptr), selectedText.s, selectedText.len); ansiText.SetClip(CF_TEXT); } } if (selectedText.rectangular) { ::SetClipboardData(cfColumnSelect, 0); } ::CloseClipboard(); } void ScintillaWin::ScrollMessage(WPARAM wParam) { //DWORD dwStart = timeGetTime(); //Platform::DebugPrintf("Scroll %x %d\n", wParam, lParam); SCROLLINFO sci; memset(&sci, 0, sizeof(sci)); sci.cbSize = sizeof(sci); sci.fMask = SIF_ALL; GetScrollInfo(SB_VERT, &sci); //Platform::DebugPrintf("ScrollInfo %d mask=%x min=%d max=%d page=%d pos=%d track=%d\n", b,sci.fMask, //sci.nMin, sci.nMax, sci.nPage, sci.nPos, sci.nTrackPos); int topLineNew = topLine; switch (LoWord(wParam)) { case SB_LINEUP: topLineNew -= 1; break; case SB_LINEDOWN: topLineNew += 1; break; case SB_PAGEUP: topLineNew -= LinesToScroll(); break; case SB_PAGEDOWN: topLineNew += LinesToScroll(); break; case SB_TOP: topLineNew = 0; break; case SB_BOTTOM: topLineNew = MaxScrollPos(); break; case SB_THUMBPOSITION: topLineNew = sci.nTrackPos; break; case SB_THUMBTRACK: topLineNew = sci.nTrackPos; break; } ScrollTo(topLineNew); } void ScintillaWin::HorizontalScrollMessage(WPARAM wParam) { int xPos = xOffset; PRectangle rcText = GetTextRectangle(); int pageWidth = rcText.Width() * 2 / 3; switch (LoWord(wParam)) { case SB_LINEUP: xPos -= 20; break; case SB_LINEDOWN: // May move past the logical end xPos += 20; break; case SB_PAGEUP: xPos -= pageWidth; break; case SB_PAGEDOWN: xPos += pageWidth; if (xPos > scrollWidth - rcText.Width()) { // Hit the end exactly xPos = scrollWidth - rcText.Width(); } break; case SB_TOP: xPos = 0; break; case SB_BOTTOM: xPos = scrollWidth; break; case SB_THUMBPOSITION: xPos = HiWord(wParam); break; case SB_THUMBTRACK: xPos = HiWord(wParam); break; } HorizontalScrollTo(xPos); } void ScintillaWin::RealizeWindowPalette(bool inBackGround) { RefreshStyleData(); HDC hdc = ::GetDC(MainHWND()); // Select a stock font to prevent warnings from BoundsChecker ::SelectObject(hdc, GetStockFont(DEFAULT_GUI_FONT)); AutoSurface surfaceWindow(hdc, this); if (surfaceWindow) { int changes = surfaceWindow->SetPalette(&palette, inBackGround); if (changes > 0) Redraw(); surfaceWindow->Release(); } ::ReleaseDC(MainHWND(), hdc); } /** * Redraw all of text area. * This paint will not be abandoned. */ void ScintillaWin::FullPaint() { HDC hdc = ::GetDC(MainHWND()); FullPaintDC(hdc); ::ReleaseDC(MainHWND(), hdc); } /** * Redraw all of text area on the specified DC. * This paint will not be abandoned. */ void ScintillaWin::FullPaintDC(HDC hdc) { paintState = painting; rcPaint = GetClientRectangle(); paintingAllText = true; AutoSurface surfaceWindow(hdc, this); if (surfaceWindow) { Paint(surfaceWindow, rcPaint); surfaceWindow->Release(); } paintState = notPainting; } static bool CompareDevCap(HDC hdc, HDC hOtherDC, int nIndex) { return ::GetDeviceCaps(hdc, nIndex) == ::GetDeviceCaps(hOtherDC, nIndex); } bool ScintillaWin::IsCompatibleDC(HDC hOtherDC) { HDC hdc = ::GetDC(MainHWND()); bool isCompatible = CompareDevCap(hdc, hOtherDC, TECHNOLOGY) && CompareDevCap(hdc, hOtherDC, LOGPIXELSY) && CompareDevCap(hdc, hOtherDC, LOGPIXELSX) && CompareDevCap(hdc, hOtherDC, BITSPIXEL) && CompareDevCap(hdc, hOtherDC, PLANES); ::ReleaseDC(MainHWND(), hdc); return isCompatible; } /// Implement IUnknown STDMETHODIMP ScintillaWin::QueryInterface(REFIID riid, PVOID *ppv) { *ppv = NULL; if (riid == IID_IUnknown) *ppv = reinterpret_cast(&dt); if (riid == IID_IDropSource) *ppv = reinterpret_cast(&ds); if (riid == IID_IDropTarget) *ppv = reinterpret_cast(&dt); if (riid == IID_IDataObject) *ppv = reinterpret_cast(&dob); if (!*ppv) return E_NOINTERFACE; return S_OK; } STDMETHODIMP_(ULONG) ScintillaWin::AddRef() { return 1; } STDMETHODIMP_(ULONG) ScintillaWin::Release() { return 1; } /// Implement IDropTarget STDMETHODIMP ScintillaWin::DragEnter(LPDATAOBJECT pIDataSource, DWORD grfKeyState, POINTL, PDWORD pdwEffect) { if (pIDataSource == NULL) return E_POINTER; if (IsUnicodeMode()) { FORMATETC fmtu = {CF_UNICODETEXT, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL }; HRESULT hrHasUText = pIDataSource->QueryGetData(&fmtu); hasOKText = (hrHasUText == S_OK); } if (!hasOKText) { FORMATETC fmte = {CF_TEXT, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL }; HRESULT hrHasText = pIDataSource->QueryGetData(&fmte); hasOKText = (hrHasText == S_OK); } if (!hasOKText) { *pdwEffect = DROPEFFECT_NONE; return S_OK; } if (inDragDrop) // Internal defaults to move *pdwEffect = DROPEFFECT_MOVE; else *pdwEffect = DROPEFFECT_COPY; if (grfKeyState & MK_ALT) *pdwEffect = DROPEFFECT_MOVE; if (grfKeyState & MK_CONTROL) *pdwEffect = DROPEFFECT_COPY; return S_OK; } STDMETHODIMP ScintillaWin::DragOver(DWORD grfKeyState, POINTL pt, PDWORD pdwEffect) { if (!hasOKText || pdoc->IsReadOnly()) { *pdwEffect = DROPEFFECT_NONE; return S_OK; } // These are the Wordpad semantics. if (inDragDrop) // Internal defaults to move *pdwEffect = DROPEFFECT_MOVE; else *pdwEffect = DROPEFFECT_COPY; if (grfKeyState & MK_ALT) *pdwEffect = DROPEFFECT_MOVE; if (grfKeyState & MK_CONTROL) *pdwEffect = DROPEFFECT_COPY; // Update the cursor. POINT rpt = {pt.x, pt.y}; ::ScreenToClient(MainHWND(), &rpt); SetDragPosition(PositionFromLocation(Point(rpt.x, rpt.y))); return S_OK; } STDMETHODIMP ScintillaWin::DragLeave() { SetDragPosition(invalidPosition); return S_OK; } STDMETHODIMP ScintillaWin::Drop(LPDATAOBJECT pIDataSource, DWORD grfKeyState, POINTL pt, PDWORD pdwEffect) { if (inDragDrop) // Internal defaults to move *pdwEffect = DROPEFFECT_MOVE; else *pdwEffect = DROPEFFECT_COPY; if (grfKeyState & MK_ALT) *pdwEffect = DROPEFFECT_MOVE; if (grfKeyState & MK_CONTROL) *pdwEffect = DROPEFFECT_COPY; if (pIDataSource == NULL) return E_POINTER; SetDragPosition(invalidPosition); STGMEDIUM medium={0,{0},0}; HRESULT hr = S_OK; char *data = 0; bool dataAllocated = false; if (IsUnicodeMode()) { FORMATETC fmtu = {CF_UNICODETEXT, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL }; hr = pIDataSource->GetData(&fmtu, &medium); if (SUCCEEDED(hr) && medium.hGlobal) { wchar_t *udata = static_cast(::GlobalLock(medium.hGlobal)); int tlen = ::GlobalSize(medium.hGlobal); // Convert UCS-2 to UTF-8 int dataLen = UTF8Length(udata, tlen/2); data = new char[dataLen+1]; if (data) { UTF8FromUCS2(udata, tlen/2, data, dataLen); dataAllocated = true; } } } if (!data) { FORMATETC fmte = {CF_TEXT, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL }; hr = pIDataSource->GetData(&fmte, &medium); if (SUCCEEDED(hr) && medium.hGlobal) { data = static_cast(::GlobalLock(medium.hGlobal)); } } if (!data) { //Platform::DebugPrintf("Bad data format: 0x%x\n", hres); return hr; } FORMATETC fmtr = {cfColumnSelect, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL}; HRESULT hrRectangular = pIDataSource->QueryGetData(&fmtr); POINT rpt = {pt.x, pt.y}; ::ScreenToClient(MainHWND(), &rpt); int movePos = PositionFromLocation(Point(rpt.x, rpt.y)); DropAt(movePos, data, *pdwEffect == DROPEFFECT_MOVE, hrRectangular == S_OK); ::GlobalUnlock(medium.hGlobal); // Free data if (medium.pUnkForRelease != NULL) medium.pUnkForRelease->Release(); else ::GlobalFree(medium.hGlobal); if (dataAllocated) delete []data; return S_OK; } /// Implement important part of IDataObject STDMETHODIMP ScintillaWin::GetData(FORMATETC *pFEIn, STGMEDIUM *pSTM) { bool formatOK = (pFEIn->cfFormat == CF_TEXT) || ((pFEIn->cfFormat == CF_UNICODETEXT) && IsUnicodeMode()); if (!formatOK || pFEIn->ptd != 0 || (pFEIn->dwAspect & DVASPECT_CONTENT) == 0 || pFEIn->lindex != -1 || (pFEIn->tymed & TYMED_HGLOBAL) == 0 ) { //Platform::DebugPrintf("DOB GetData No %d %x %x fmt=%x\n", lenDrag, pFEIn, pSTM, pFEIn->cfFormat); return DATA_E_FORMATETC; } pSTM->tymed = TYMED_HGLOBAL; //Platform::DebugPrintf("DOB GetData OK %d %x %x\n", lenDrag, pFEIn, pSTM); GlobalMemory text; if (pFEIn->cfFormat == CF_UNICODETEXT) { int uchars = UCS2Length(drag.s, drag.len); text.Allocate(2 * uchars); if (text) { UCS2FromUTF8(drag.s, drag.len, static_cast(text.ptr), uchars); } } else { text.Allocate(drag.len); if (text) { memcpy(static_cast(text.ptr), drag.s, drag.len); } } pSTM->hGlobal = text ? text.Unlock() : 0; pSTM->pUnkForRelease = 0; return S_OK; } bool ScintillaWin::Register(HINSTANCE hInstance_) { hInstance = hInstance_; bool result; #if 0 // Register the Scintilla class if (IsNT()) { //if (0) { // Register Scintilla as a wide character window WNDCLASSEXW wndclass; wndclass.cbSize = sizeof(wndclass); wndclass.style = CS_GLOBALCLASS | CS_HREDRAW | CS_VREDRAW; wndclass.lpfnWndProc = ::ScintillaWin::SWndProc; wndclass.cbClsExtra = 0; wndclass.cbWndExtra = sizeof(ScintillaWin *); wndclass.hInstance = hInstance; wndclass.hIcon = NULL; wndclass.hCursor = NULL; wndclass.hbrBackground = NULL; wndclass.lpszMenuName = NULL; wndclass.lpszClassName = L"Scintilla"; wndclass.hIconSm = 0; result = ::RegisterClassExW(&wndclass); } else { #endif // Register Scintilla as a normal character window WNDCLASSEX wndclass; wndclass.cbSize = sizeof(wndclass); wndclass.style = CS_GLOBALCLASS | CS_HREDRAW | CS_VREDRAW; wndclass.lpfnWndProc = ::ScintillaWin::SWndProc; wndclass.cbClsExtra = 0; wndclass.cbWndExtra = sizeof(ScintillaWin *); wndclass.hInstance = hInstance; wndclass.hIcon = NULL; wndclass.hCursor = NULL; wndclass.hbrBackground = NULL; wndclass.lpszMenuName = NULL; wndclass.lpszClassName = scintillaClassName; wndclass.hIconSm = 0; result = ::RegisterClassEx(&wndclass) != 0; //} if (result) { // Register the CallTip class WNDCLASSEX wndclassc; wndclassc.cbSize = sizeof(wndclassc); wndclassc.style = CS_GLOBALCLASS | CS_HREDRAW | CS_VREDRAW; wndclassc.cbClsExtra = 0; wndclassc.cbWndExtra = sizeof(ScintillaWin *); wndclassc.hInstance = hInstance; wndclassc.hIcon = NULL; wndclassc.hbrBackground = NULL; wndclassc.lpszMenuName = NULL; wndclassc.lpfnWndProc = ScintillaWin::CTWndProc; wndclassc.hCursor = ::LoadCursor(NULL, IDC_ARROW); wndclassc.lpszClassName = callClassName; wndclassc.hIconSm = 0; result = ::RegisterClassEx(&wndclassc) != 0; } return result; } bool ScintillaWin::Unregister() { bool result = ::UnregisterClass(scintillaClassName, hInstance) != 0; if (::UnregisterClass(callClassName, hInstance) == 0) result = false; return result; } bool ScintillaWin::HasCaretSizeChanged() { if ( ( (0 != vs.caretWidth) && (sysCaretWidth != vs.caretWidth) ) || (0 != vs.lineHeight) && (sysCaretHeight != vs.lineHeight) ) { return true; } return false; } BOOL ScintillaWin::CreateSystemCaret() { sysCaretWidth = vs.caretWidth; if (0 == sysCaretWidth) { sysCaretWidth = 1; } sysCaretHeight = vs.lineHeight; int bitmapSize = (((sysCaretWidth + 15) & ~15) >> 3) * sysCaretHeight; char *bits = new char[bitmapSize]; memset(bits, 0, bitmapSize); sysCaretBitmap = ::CreateBitmap(sysCaretWidth, sysCaretHeight, 1, 1, reinterpret_cast(bits)); delete []bits; BOOL retval = ::CreateCaret( MainHWND(), sysCaretBitmap, sysCaretWidth, sysCaretHeight); ::ShowCaret(MainHWND()); return retval; } BOOL ScintillaWin::DestroySystemCaret() { ::HideCaret(MainHWND()); BOOL retval = ::DestroyCaret(); if (sysCaretBitmap) { ::DeleteObject(sysCaretBitmap); sysCaretBitmap = 0; } return retval; } // Take care of 32/64 bit pointers #ifdef GetWindowLongPtr static void *PointerFromWindow(HWND hWnd) { return reinterpret_cast(::GetWindowLongPtr(hWnd, 0)); } static void SetWindowPointer(HWND hWnd, void *ptr) { ::SetWindowLongPtr(hWnd, 0, reinterpret_cast(ptr)); } #else static void *PointerFromWindow(HWND hWnd) { return reinterpret_cast(::GetWindowLong(hWnd, 0)); } static void SetWindowPointer(HWND hWnd, void *ptr) { ::SetWindowLong(hWnd, 0, reinterpret_cast(ptr)); } #endif sptr_t PASCAL ScintillaWin::CTWndProc( HWND hWnd, UINT iMessage, WPARAM wParam, sptr_t lParam) { // Find C++ object associated with window. ScintillaWin *sciThis = reinterpret_cast(PointerFromWindow(hWnd)); // ctp will be zero if WM_CREATE not seen yet if (sciThis == 0) { if (iMessage == WM_CREATE) { // Associate CallTip object with window CREATESTRUCT *pCreate = reinterpret_cast(lParam); SetWindowPointer(hWnd, pCreate->lpCreateParams); return 0; } else { return ::DefWindowProc(hWnd, iMessage, wParam, lParam); } } else { if (iMessage == WM_NCDESTROY) { ::SetWindowLong(hWnd, 0, 0); return ::DefWindowProc(hWnd, iMessage, wParam, lParam); } else if (iMessage == WM_PAINT) { PAINTSTRUCT ps; ::BeginPaint(hWnd, &ps); Surface *surfaceWindow = Surface::Allocate(); if (surfaceWindow) { surfaceWindow->Init(ps.hdc, hWnd); surfaceWindow->SetUnicodeMode(SC_CP_UTF8 == sciThis->ct.codePage); surfaceWindow->SetDBCSMode(sciThis->ct.codePage); sciThis->ct.PaintCT(surfaceWindow); surfaceWindow->Release(); delete surfaceWindow; } ::EndPaint(hWnd, &ps); return 0; } else if ((iMessage == WM_NCLBUTTONDOWN) || (iMessage == WM_NCLBUTTONDBLCLK)) { POINT pt; pt.x = static_cast(LOWORD(lParam)); pt.y = static_cast(HIWORD(lParam)); ScreenToClient(hWnd, &pt); sciThis->ct.MouseClick(Point(pt.x, pt.y)); sciThis->CallTipClick(); return 0; } else if (iMessage == WM_LBUTTONDOWN) { // This does not fire due to the hit test code sciThis->ct.MouseClick(Point::FromLong(lParam)); sciThis->CallTipClick(); return 0; } else if (iMessage == WM_SETCURSOR) { ::SetCursor(::LoadCursor(NULL,IDC_ARROW)); return 0; } else if (iMessage == WM_NCHITTEST) { return HTCAPTION; } else { return ::DefWindowProc(hWnd, iMessage, wParam, lParam); } } } sptr_t ScintillaWin::DirectFunction( ScintillaWin *sci, UINT iMessage, uptr_t wParam, sptr_t lParam) { PLATFORM_ASSERT(::GetCurrentThreadId() == ::GetWindowThreadProcessId(sci->MainHWND(), NULL)); return sci->WndProc(iMessage, wParam, lParam); } extern "C" __declspec(dllexport) sptr_t __stdcall Scintilla_DirectFunction( ScintillaWin *sci, UINT iMessage, uptr_t wParam, sptr_t lParam) { return sci->WndProc(iMessage, wParam, lParam); } sptr_t PASCAL ScintillaWin::SWndProc( HWND hWnd, UINT iMessage, WPARAM wParam, sptr_t lParam) { //Platform::DebugPrintf("S W:%x M:%x WP:%x L:%x\n", hWnd, iMessage, wParam, lParam); // Find C++ object associated with window. ScintillaWin *sci = reinterpret_cast(PointerFromWindow(hWnd)); // sci will be zero if WM_CREATE not seen yet if (sci == 0) { if (iMessage == WM_CREATE) { // Create C++ object associated with window sci = new ScintillaWin(hWnd); SetWindowPointer(hWnd, sci); return sci->WndProc(iMessage, wParam, lParam); } else { return ::DefWindowProc(hWnd, iMessage, wParam, lParam); } } else { if (iMessage == WM_NCDESTROY) { sci->Finalise(); delete sci; ::SetWindowLong(hWnd, 0, 0); return ::DefWindowProc(hWnd, iMessage, wParam, lParam); } else { return sci->WndProc(iMessage, wParam, lParam); } } } // This function is externally visible so it can be called from container when building statically. // Must be called once only. bool Scintilla_RegisterClasses(void *hInstance) { Platform_Initialise(hInstance); bool result = ScintillaWin::Register(reinterpret_cast(hInstance)); #ifdef SCI_LEXER Scintilla_LinkLexers(); #endif return result; } // This function is externally visible so it can be called from container when building statically. bool Scintilla_ReleaseResources() { bool result = ScintillaWin::Unregister(); Platform_Finalise(); return result; } #ifndef STATIC_BUILD extern "C" int APIENTRY DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID) { //Platform::DebugPrintf("Scintilla::DllMain %d %d\n", hInstance, dwReason); if (dwReason == DLL_PROCESS_ATTACH) { if (!Scintilla_RegisterClasses(hInstance)) return FALSE; } else if (dwReason == DLL_PROCESS_DETACH) { Scintilla_ReleaseResources(); } return TRUE; } #endif