diff options
Diffstat (limited to 'win32/ScintillaWin.cxx')
-rw-r--r-- | win32/ScintillaWin.cxx | 1349 |
1 files changed, 1349 insertions, 0 deletions
diff --git a/win32/ScintillaWin.cxx b/win32/ScintillaWin.cxx new file mode 100644 index 000000000..fd4126249 --- /dev/null +++ b/win32/ScintillaWin.cxx @@ -0,0 +1,1349 @@ +// Scintilla source code edit control +// ScintillaWin.cxx - Windows specific subclass of ScintillaBase +// Copyright 1998-2000 by Neil Hodgson <neilh@scintilla.org> +// The License.txt file describes the conditions under which this software may be distributed. + +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <ctype.h> + +#include "Platform.h" + +#include "Scintilla.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 "LineMarker.h" +#include "Style.h" +#include "AutoComplete.h" +#include "ViewStyle.h" +#include "Document.h" +#include "Editor.h" +#include "ScintillaBase.h" + +//#include "CElapsed.h" + +#ifndef SPI_GETWHEELSCROLLLINES +#define SPI_GETWHEELSCROLLLINES 104 +#endif + +#ifndef WM_IME_STARTCOMPOSITION +#include <imm.h> +#endif + +#include <commctrl.h> +#ifndef __BORLANDC__ +#include <zmouse.h> +#endif +#include <ole2.h> + +#ifndef MK_ALT +#define MK_ALT 32 +#endif + +// 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. + +class ScintillaWin; // Forward declaration for COM interface subobjects + +class FormatEnumerator { +public: + void **vtbl; + int ref; + int pos; + FormatEnumerator(int pos_); +}; + +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 capturedMouse; + + UINT cfColumnSelect; + + DropSource ds; + DataObject dob; + DropTarget dt; + + static HINSTANCE hInstance; + + ScintillaWin(HWND hwnd); + virtual ~ScintillaWin(); + + virtual void Initialise(); + virtual void Finalise(); + + static LRESULT PASCAL SWndProc( + HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam); + static LRESULT PASCAL CTWndProc( + HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam); + + virtual void StartDrag(); + virtual LRESULT WndProc(UINT iMessage, WPARAM wParam, LPARAM lParam); + virtual LRESULT DefWndProc(UINT iMessage, WPARAM wParam, LPARAM lParam); + virtual void SetTicking(bool on); + virtual void SetMouseCapture(bool on); + virtual bool HaveMouseCapture(); + virtual void ScrollText(int linesToMove); + virtual void SetVerticalScrollPos(); + virtual void SetHorizontalScrollPos(); + virtual bool ModifyScrollBars(int nMax, int nPage); + virtual void NotifyChange(); + virtual void NotifyFocus(bool focus); + virtual void NotifyParent(SCNotification scn); + virtual void NotifyDoubleClick(Point pt, bool shift); + virtual void Copy(); + 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 GetIntelliMouseParameters(); + HGLOBAL GetSelText(); + void ScrollMessage(WPARAM wParam); + void HorizontalScrollMessage(WPARAM wParam); + void RealizeWindowPalette(bool inBackGround); + void FullPaint(); + +public: + // 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); + + static void Register(HINSTANCE hInstance_); + friend class DropSource; + friend class DataObject; + friend class DropTarget; + bool DragIsRectangularOK(UINT fmt) { + return dragIsRectangle && (fmt == cfColumnSelect); + } +}; + +HINSTANCE ScintillaWin::hInstance = 0; + +ScintillaWin::ScintillaWin(HWND hwnd) { + + capturedMouse = false; + + // There does not seem to be a real standard for indicating that the clipboard contains a rectangular + // selection, so copy Developer Studio. + cfColumnSelect = ::RegisterClipboardFormat("MSDEVColumnSelect"); + + wMain = hwnd; + wDraw = hwnd; + + dob.sci = this; + ds.sci = this; + dt.sci = this; + + 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); + RevokeDragDrop(wMain.GetID()); + OleUninitialize(); +} + +void ScintillaWin::StartDrag() { + DWORD dwEffect = 0; + dropWentOutside = true; + IDataObject *pDataObject = reinterpret_cast<IDataObject *>(&dob); + IDropSource *pDropSource = reinterpret_cast<IDropSource *>(&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 conecntrating them here +static WORD LoWord(DWORD l) { + return LOWORD(l); +} + +static WORD HiWord(DWORD l) { + return HIWORD(l); +} + +LRESULT ScintillaWin::WndProc(UINT iMessage, WPARAM wParam, LPARAM lParam) { + switch (iMessage) { + + case WM_CREATE: + ctrlID = wMain.GetDlgCtrlID(); + // Get Intellimouse scroll line parameters + GetIntelliMouseParameters(); + RegisterDragDrop(wMain.GetID(), reinterpret_cast<IDropTarget *>(&dt)); + break; + + case WM_COMMAND: +#ifdef TOTAL_CONTROL + if (LoWord(wParam) == idAutoComplete) { + int cmd = HiWord(wParam); + if (cmd == LBN_DBLCLK) { + AutoCompleteCompleted(); + } else { + if (cmd != LBN_SETFOCUS) + SetFocus(wMain.GetID()); + } + } + Command(LoWord(wParam)); +#endif + break; + + case WM_PAINT: { + //CElapsed ce; ce.Begin(); + paintState = painting; + PAINTSTRUCT ps; + BeginPaint(wMain.GetID(), &ps); + Surface surfaceWindow; + surfaceWindow.Init(ps.hdc); + rcPaint = PRectangle(ps.rcPaint.left, ps.rcPaint.top, ps.rcPaint.right, ps.rcPaint.bottom); + PRectangle rcText = GetTextRectangle(); + paintingAllText = rcPaint.Contains(rcText); + 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(); + EndPaint(wMain.GetID(), &ps); + if (paintState == paintAbandoned) { + // Painting area was insufficient to cover new styling or brace highlight positions + FullPaint(); + } + paintState = notPainting; + //Platform::DebugPrintf("Paint took %g\n", ce.End()); + } + break; + + case WM_VSCROLL: + ScrollMessage(wParam); + break; + + case WM_HSCROLL: + HorizontalScrollMessage(wParam); + break; + + case WM_SIZE: { + //Platform::DebugPrintf("S start wnd proc %d %d %d\n",iMessage, wParam, lParam); + PRectangle rsClient(0, 0, LoWord(lParam), HiWord(lParam)); + SetScrollBarsTo(rsClient); + DropGraphics(); + } + 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(wMain.GetID(), iMessage, wParam, lParam); + } + + // Either SCROLL or ZOOM. We handle the wheel steppings calculation + cWheelDelta -= static_cast<short>(HiWord(wParam)); + if (abs(cWheelDelta) >= WHEEL_DELTA && ucWheelScrollLines > 0) { + int cLineScroll; + cLineScroll = ucWheelScrollLines; + if (cLineScroll == 0) { + cLineScroll++; + } + cLineScroll *= (cWheelDelta / WHEEL_DELTA); + cWheelDelta = cWheelDelta % 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 (cLineScroll < 0) { + KeyCommand(SCI_ZOOMIN); + } else { + KeyCommand(SCI_ZOOMOUT); + } + } else { + // Scroll + ScrollTo(topLine + cLineScroll); + } + } + return 0; + + case WM_TIMER: + Tick(); + break; + + case WM_GETMINMAXINFO: + return DefWindowProc(wMain.GetID(), iMessage, wParam, lParam); + + case WM_LBUTTONDOWN: + //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), GetTickCount(), + wParam & MK_SHIFT, wParam & MK_CONTROL, Platform::IsKeyDown(VK_MENU)); + SetFocus(wMain.GetID()); + break; + + case WM_MOUSEMOVE: + ButtonMove(Point::FromLong(lParam)); + break; + + case WM_LBUTTONUP: + ButtonUp(Point::FromLong(lParam), GetTickCount(), wParam & MK_CONTROL); + break; + + case WM_SETCURSOR: + if (LoWord(lParam) == HTCLIENT) { + if (inDragDrop) { + wDraw.SetCursor(Window::cursorUp); + } else { + // Display regular (drag) cursor over selection + POINT pt; + ::GetCursorPos(&pt); + ::ScreenToClient(wMain.GetID(), &pt); + if (PointInSelMargin(Point(pt.x, pt.y))) { + wDraw.SetCursor(Window::cursorReverseArrow); + } else if (PointInSelection(Point(pt.x, pt.y))) { + wDraw.SetCursor(Window::cursorArrow); + } else { + wDraw.SetCursor(Window::cursorText); + } + } + return TRUE; + } else + return DefWindowProc(wMain.GetID(), iMessage, wParam, lParam); + + case WM_CHAR: + //Platform::DebugPrintf("S char proc %d %x %x\n",iMessage, wParam, lParam); + if (!iscntrl(wParam&0xff)) + AddChar(static_cast<char>(wParam&0xff)); + return 1; + + case WM_KEYDOWN: + //Platform::DebugPrintf("S keydown %d %x %x %x %x\n",iMessage, wParam, lParam, ::IsKeyDown(VK_SHIFT), ::IsKeyDown(VK_CONTROL)); + return KeyDown(wParam, Platform::IsKeyDown(VK_SHIFT), + Platform::IsKeyDown(VK_CONTROL), false); + + case WM_KEYUP: + //Platform::DebugPrintf("S keyup %d %x %x\n",iMessage, wParam, lParam); + break; + + 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: + NotifyFocus(false); + DropCaret(); + //RealizeWindowPalette(true); + break; + + case WM_SETFOCUS: + NotifyFocus(true); + ShowCaretAtCurrentPosition(); + RealizeWindowPalette(false); + break; + + case WM_SYSCOLORCHANGE: + //Platform::DebugPrintf("Setting Changed\n"); + InvalidateStyleData(); + break; + + case WM_PALETTECHANGED: + if (wParam != reinterpret_cast<unsigned int>(wMain.GetID())) { + //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(wMain.GetID(), iMessage, wParam, lParam); + + case WM_IME_ENDCOMPOSITION: // dbcs + ImeEndComposition(); + return DefWindowProc(wMain.GetID(), iMessage, wParam, lParam); + + case WM_CONTEXTMENU: +#ifdef TOTAL_CONTROL + ContextMenu(Point::FromLong(lParam)); +#endif + break; + + case EM_CANPASTE: { + OpenClipboard(wMain.GetID()); + HGLOBAL hmemSelection = GetClipboardData(CF_TEXT); + if (hmemSelection) + GlobalUnlock(hmemSelection); + CloseClipboard(); + return hmemSelection != 0; + } + + case EM_SCROLL: { + int topStart = topLine; + ScrollMessage(wParam); + return MAKELONG(topLine - topStart, TRUE); + } + + default: + return ScintillaBase::WndProc(iMessage, wParam, lParam); + } + return 0l; +} + +LRESULT ScintillaWin::DefWndProc(UINT iMessage, WPARAM wParam, LPARAM lParam) { + return ::DefWindowProc(wMain.GetID(), iMessage, wParam, lParam); +} + +void ScintillaWin::SetTicking(bool on) { + if (timer.ticking != on) { + timer.ticking = on; + if (timer.ticking) { + timer.tickerID = ::SetTimer(wMain.GetID(), 1, timer.tickSize, NULL); + } else { + ::KillTimer(wMain.GetID(), timer.tickerID); + timer.tickerID = 0; + } + } + timer.ticksToWait = caret.period; +} + +void ScintillaWin::SetMouseCapture(bool on) { + if (on) { + ::SetCapture(wMain.GetID()); + } 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 && (::GetCapture() == wMain.GetID()); +} + +void ScintillaWin::ScrollText(int linesToMove) { + //Platform::DebugPrintf("ScintillaWin::ScrollText %d\n", linesToMove); + ::ScrollWindow(wMain.GetID(), 0, + vs.lineHeight * (linesToMove), 0, 0); + ::UpdateWindow(wMain.GetID()); +} + +void ScintillaWin::SetVerticalScrollPos() { + ::SetScrollPos(wMain.GetID(), SB_VERT, topLine, TRUE); +} + +void ScintillaWin::SetHorizontalScrollPos() { + ::SetScrollPos(wMain.GetID(), SB_HORZ, xOffset, TRUE); +} + +bool ScintillaWin::ModifyScrollBars(int nMax, int nPage) { + bool modified = false; + SCROLLINFO sci = { + sizeof(sci) + }; + sci.fMask = SIF_PAGE | SIF_RANGE; + BOOL bz = ::GetScrollInfo(wMain.GetID(), SB_VERT, &sci); + if ((sci.nMin != 0) || (sci.nMax != pdoc->LinesTotal()) || + (sci.nPage != (pdoc->LinesTotal() - MaxScrollPos() + 1)) || + (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 = nMax; + sci.nPage = nPage; + sci.nPos = 0; + sci.nTrackPos = 1; + ::SetScrollInfo(wMain.GetID(), SB_VERT, &sci, TRUE); + modified = true; + } + int horizStart = 0; + int horizEnd = 2000; + if (!::GetScrollRange(wMain.GetID(), SB_HORZ, &horizStart, &horizEnd) || + horizStart != 0 || horizEnd != 2000) { + ::SetScrollRange(wMain.GetID(), SB_HORZ, 0, 2000, TRUE); + //Platform::DebugPrintf("Horiz Scroll info changed\n"); + modified = true; + } + return modified; +} + +void ScintillaWin::NotifyChange() { + ::SendMessage(GetParent(wMain.GetID()), WM_COMMAND, + MAKELONG(wMain.GetDlgCtrlID(), EN_CHANGE), + reinterpret_cast<LPARAM>(wMain.GetID())); +} + +void ScintillaWin::NotifyFocus(bool focus) { + ::SendMessage(GetParent(wMain.GetID()), WM_COMMAND, + MAKELONG(wMain.GetDlgCtrlID(), focus ? EN_SETFOCUS : EN_KILLFOCUS), + reinterpret_cast<LPARAM>(wMain.GetID())); +} + +void ScintillaWin::NotifyParent(SCNotification scn) { + scn.nmhdr.hwndFrom = wMain.GetID(); + scn.nmhdr.idFrom = ctrlID; + ::SendMessage(GetParent(wMain.GetID()), WM_NOTIFY, + wMain.GetDlgCtrlID(), reinterpret_cast<LPARAM>(&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. + wMain.SendMessage(WM_LBUTTONDBLCLK, + shift ? MK_SHIFT : 0, + MAKELPARAM(pt.x, pt.y)); +} + +void ScintillaWin::Copy() { + //Platform::DebugPrintf("Copy\n"); + if (currentPos != anchor) { + HGLOBAL hmemSelection = GetSelText(); + ::OpenClipboard(wMain.GetID()); + ::EmptyClipboard(); + ::SetClipboardData(CF_TEXT, hmemSelection); + if (selType == selRectangle) { + ::SetClipboardData(cfColumnSelect, 0); + } + ::CloseClipboard(); + } +} + +void ScintillaWin::Paste() { + pdoc->BeginUndoAction(); + int selStart = SelectionStart(); + ClearSelection(); + ::OpenClipboard(wMain.GetID()); + bool isRectangular = ::IsClipboardFormatAvailable(cfColumnSelect); + HGLOBAL hmemSelection = ::GetClipboardData(CF_TEXT); + if (hmemSelection) { + char *ptr = static_cast<char *>( + ::GlobalLock(hmemSelection)); + if (ptr) { + unsigned int bytes = ::GlobalSize(hmemSelection); + unsigned int len = bytes; + for (unsigned int i = 0; i < bytes; i++) { + if ((len == bytes) && (0 == ptr[i])) + len = i; + } + if (isRectangular) { + PasteRectangular(selStart, ptr, len); + } else { + pdoc->InsertString(currentPos, ptr, len); + SetEmptySelection(currentPos + len); + } + } + ::GlobalUnlock(hmemSelection); + } + ::CloseClipboard(); + pdoc->EndUndoAction(); + NotifyChange(); + Redraw(); +} + +void ScintillaWin::CreateCallTipWindow(PRectangle) { +#ifdef TOTAL_CONTROL + ct.wCallTip = ::CreateWindow(callClassName, "ACallTip", + WS_VISIBLE | WS_CHILD, 100, 100, 150, 20, + wDraw.GetID(), reinterpret_cast<HMENU>(idCallTip), wDraw.GetInstance(), &ct); + ct.wDraw = ct.wCallTip; +#endif +} + +void ScintillaWin::AddToPopUp(const char *label, int cmd, bool enabled) { +#ifdef TOTAL_CONTROL + if (!label[0]) + ::AppendMenu(popup.GetID(), MF_SEPARATOR, 0, ""); + else if (enabled) + ::AppendMenu(popup.GetID(), MF_STRING, cmd, label); + else + ::AppendMenu(popup.GetID(), MF_STRING | MF_DISABLED | MF_GRAYED, cmd, label); +#endif +} + +void ScintillaWin::ClaimSelection() { + // Windows does not have a primary selection +} + +// 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<IEnumFORMATETC *>(fe); + if (riid == IID_IEnumFORMATETC) + *ppv = reinterpret_cast<IEnumFORMATETC *>(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 < 1) && (putPos < celt)) { + rgelt->cfFormat = CF_TEXT; + 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); + return FormatEnumerator_QueryInterface(pfe, IID_IEnumFORMATETC, + reinterpret_cast<void **>(ppenum)); +} + +static void *vtFormatEnumerator[] = { + FormatEnumerator_QueryInterface, + FormatEnumerator_AddRef, + FormatEnumerator_Release, + FormatEnumerator_Next, + FormatEnumerator_Skip, + FormatEnumerator_Reset, + FormatEnumerator_Clone +}; + +FormatEnumerator::FormatEnumerator(int pos_) { + vtbl = vtFormatEnumerator; + ref = 0; // First QI adds first reference... + pos = pos_; +} + +// Implement IUnknown +STDMETHODIMP DropSource_QueryInterface(DropSource *ds, REFIID riid, PVOID *ppv) { + return ds->sci->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[] = { + DropSource_QueryInterface, + DropSource_AddRef, + DropSource_Release, + DropSource_QueryContinueDrag, + 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; + } + + if ( + ((pFE->cfFormat != CF_TEXT) && (pFE->cfFormat != CF_HDROP)) || + 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 *, FORMATETC *, FORMATETC *pFEOut) { + //Platform::DebugPrintf("DOB GetCanon\n"); + 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 *, DWORD dwDirection, IEnumFORMATETC **ppEnum) { + //Platform::DebugPrintf("DOB EnumFormatEtc %d\n", dwDirection); + if (dwDirection != DATADIR_GET) { + *ppEnum = 0; + return E_FAIL; + } + FormatEnumerator *pfe = new FormatEnumerator(0); + return FormatEnumerator_QueryInterface(pfe, IID_IEnumFORMATETC, + reinterpret_cast<void **>(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[] = { + DataObject_QueryInterface, + DataObject_AddRef, + DataObject_Release, + DataObject_GetData, + DataObject_GetDataHere, + DataObject_QueryGetData, + DataObject_GetCanonicalFormatEtc, + DataObject_SetData, + DataObject_EnumFormatEtc, + DataObject_DAdvise, + DataObject_DUnadvise, + 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[] = { + DropTarget_QueryInterface, + DropTarget_AddRef, + DropTarget_Release, + DropTarget_DragEnter, + DropTarget_DragOver, + DropTarget_DragLeave, + DropTarget_Drop +}; + +DropTarget::DropTarget() { + vtbl = vtDropTarget; + sci = 0; +} + +// DBCS: support Input Method Editor (IME) +// Called when IME Window opened. +void ScintillaWin::ImeStartComposition() { + if (caret.active) { + // Move IME Window to current caret position + HIMC hIMC = ::ImmGetContext(wMain.GetID()); + 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}; + int sizeZoomed = vs.styles[styleHere].size + vs.zoomLevel; + if (sizeZoomed <= 2) // Hangs if sizeZoomed <= 1 + sizeZoomed = 2; + Surface surface; + int 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 = vs.styles[styleHere].italic ? 1 : 0; + lf.lfCharSet = DEFAULT_CHARSET; + strcpy(lf.lfFaceName, vs.styles[styleHere].fontName); + + ::ImmSetCompositionFont(hIMC, &lf); + ::ImmReleaseContext(wMain.GetID(), hIMC); + } + // Caret is displayed in IME window. So, caret in Scintilla is useless. + DropCaret(); + } +} + +// Called when IME Window closed. +void ScintillaWin::ImeEndComposition() { + ShowCaretAtCurrentPosition(); +} + +void ScintillaWin::GetIntelliMouseParameters() { + // This retrieves the number of lines per scroll as configured inthe Mouse Properties sheet in Control Panel + ::SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, &ucWheelScrollLines, 0); +} + +HGLOBAL ScintillaWin::GetSelText() { + int bytes = SelectionRangeLength(); + + HGLOBAL hand = ::GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT, + bytes + 1); + if (hand) { + char *ptr = static_cast<char *>(::GlobalLock(hand)); + char *selChars = CopySelectionRange(); + if (selChars) { + memcpy(ptr, selChars, bytes); + delete []selChars; + //for (int i = 0; i < bytes; i++) { + // ptr[i] = pdoc->CharAt(startPos + i); + //} + } + ptr[bytes] = '\0'; + ::GlobalUnlock(hand); + } + return hand; +} + +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; + + BOOL b = ::GetScrollInfo(wMain.GetID(), 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; + switch (LoWord(wParam)) { + case SB_LINEUP: + xPos -= 20; + break; + case SB_LINEDOWN: + xPos += 20; + break; + case SB_PAGEUP: + xPos -= 200; + break; + case SB_PAGEDOWN: + xPos += 200; + break; + case SB_TOP: + xPos = 0; + break; + case SB_BOTTOM: + xPos = 2000; + break; + case SB_THUMBPOSITION: + xPos = HiWord(wParam); + break; + case SB_THUMBTRACK: + xPos = HiWord(wParam); + break; + } + HorizontalScrollTo(xPos); +} + +void ScintillaWin::RealizeWindowPalette(bool inBackGround) { + RefreshStyleData(); + Surface surfaceWindow; + HDC hdc = ::GetDC(wMain.GetID()); + surfaceWindow.Init(hdc); + int changes = surfaceWindow.SetPalette(&palette, inBackGround); + if (changes > 0) + Redraw(); + surfaceWindow.Release(); + ::ReleaseDC(wMain.GetID(), hdc); +} + +// Redraw all of text area. This paint will not be abandoned. +void ScintillaWin::FullPaint() { + paintState = painting; + rcPaint = GetTextRectangle(); + paintingAllText = true; + HDC hdc = ::GetDC(wMain.GetID()); + Surface surfaceWindow; + surfaceWindow.Init(hdc); + Paint(&surfaceWindow, rcPaint); + surfaceWindow.Release(); + ::ReleaseDC(wMain.GetID(), hdc); + paintState = notPainting; +} + +// Implement IUnknown +STDMETHODIMP ScintillaWin::QueryInterface(REFIID riid, PVOID *ppv) { + *ppv = NULL; + if (riid == IID_IUnknown) + *ppv = reinterpret_cast<IDropTarget *>(&dt); + if (riid == IID_IDropSource) + *ppv = reinterpret_cast<IDropSource *>(&ds); + if (riid == IID_IDropTarget) + *ppv = reinterpret_cast<IDropTarget *>(&dt); + if (riid == IID_IDataObject) + *ppv = reinterpret_cast<IDataObject *>(&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, DWORD grfKeyState, + POINTL, 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; + return S_OK; +} + +STDMETHODIMP ScintillaWin::DragOver(DWORD grfKeyState, POINTL pt, PDWORD pdwEffect) { + // 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(wMain.GetID(), &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); + + FORMATETC fmte = {CF_TEXT, + NULL, + DVASPECT_CONTENT, + -1, + TYMED_HGLOBAL + }; + STGMEDIUM medium; + + HRESULT hres = pIDataSource->GetData(&fmte, &medium); + if (FAILED(hres)) { + //Platform::DebugPrintf("Bad data format: 0x%x\n", hres); + return hres; + } + if (medium.hGlobal == 0) { + return E_OUTOFMEMORY; + } + char *data = static_cast<char *>(::GlobalLock(medium.hGlobal)); + + FORMATETC fmtr = {cfColumnSelect, + NULL, + DVASPECT_CONTENT, + -1, + TYMED_HGLOBAL + }; + HRESULT hrRectangular = pIDataSource->QueryGetData(&fmtr); + + POINT rpt = {pt.x, pt.y}; + ::ScreenToClient(wMain.GetID(), &rpt); + Point npt(rpt.x, rpt.y); + 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(); + + return S_OK; +} + +// Implement important part of IDataObject +STDMETHODIMP ScintillaWin::GetData(FORMATETC *pFEIn, STGMEDIUM *pSTM) { + if ( + ((pFEIn->cfFormat != CF_TEXT) && (pFEIn->cfFormat != CF_HDROP)) || + 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; + if (pFEIn->cfFormat == CF_HDROP) { + pSTM->hGlobal = 0; + pSTM->pUnkForRelease = 0; + return S_OK; + } + //Platform::DebugPrintf("DOB GetData OK %d %x %x\n", lenDrag, pFEIn, pSTM); + + HGLOBAL hand = ::GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT, + lenDrag + 1); + if (hand) { + char *ptr = static_cast<char *>(::GlobalLock(hand)); + for (int i = 0; i < lenDrag; i++) { + ptr[i] = dragChars[i]; + } + ptr[lenDrag] = '\0'; + ::GlobalUnlock(hand); + } + pSTM->hGlobal = hand; + pSTM->pUnkForRelease = 0; + return S_OK; +} + +const char scintillaClassName[] = "Scintilla"; + +void ScintillaWin::Register(HINSTANCE hInstance_) { + + hInstance = hInstance_; + + InitCommonControls(); + + WNDCLASS wndclass; + + // Register the Scintilla class + + wndclass.style = CS_GLOBALCLASS | CS_HREDRAW | CS_VREDRAW; + wndclass.lpfnWndProc = ::ScintillaWin::SWndProc; + wndclass.cbClsExtra = 0; + // Reserve extra bytes for each instance of the window; + // we will use these bytes to store a pointer to the C++ + // (ScintillaWin) object corresponding to the window. + wndclass.cbWndExtra = sizeof(ScintillaWin *); + wndclass.hInstance = hInstance; + wndclass.hIcon = NULL; + //wndclass.hCursor = LoadCursor(NULL,IDC_IBEAM); + wndclass.hCursor = NULL; + wndclass.hbrBackground = NULL; + wndclass.lpszMenuName = NULL; + wndclass.lpszClassName = scintillaClassName; + + if (!RegisterClass(&wndclass)) { + //Platform::DebugPrintf("Could not register class\n"); + // TODO: fail nicely + return; + } + + // Register the CallTip class + + wndclass.lpfnWndProc = ScintillaWin::CTWndProc; + wndclass.hCursor = LoadCursor(NULL, IDC_ARROW); + wndclass.lpszClassName = callClassName; + + if (!RegisterClass(&wndclass)) { + //Platform::DebugPrintf("Could not register class\n"); + return; + } +} + +LRESULT PASCAL ScintillaWin::CTWndProc( + HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam) { + + // Find C++ object associated with window. + CallTip *ctp = reinterpret_cast<CallTip *>(GetWindowLong(hWnd, 0)); + // ctp will be zero if WM_CREATE not seen yet + if (ctp == 0) { + if (iMessage == WM_CREATE) { + // Associate CallTip object with window + CREATESTRUCT *pCreate = reinterpret_cast<CREATESTRUCT *>(lParam); + SetWindowLong(hWnd, 0, + reinterpret_cast<LONG>(pCreate->lpCreateParams)); + return 0; + } else { + return DefWindowProc(hWnd, iMessage, wParam, lParam); + } + } else { + if (iMessage == WM_DESTROY) { + SetWindowLong(hWnd, 0, 0); + return DefWindowProc(hWnd, iMessage, wParam, lParam); + } else if (iMessage == WM_PAINT) { + PAINTSTRUCT ps; + ::BeginPaint(hWnd, &ps); + Surface surfaceWindow; + surfaceWindow.Init(ps.hdc); + ctp->PaintCT(&surfaceWindow); + surfaceWindow.Release(); + ::EndPaint(hWnd, &ps); + return 0; + } else { + return DefWindowProc(hWnd, iMessage, wParam, lParam); + } + } +} + +LRESULT PASCAL ScintillaWin::SWndProc( + HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam) { + //Platform::DebugPrintf("S W:%x M:%d WP:%x L:%x\n", hWnd, iMessage, wParam, lParam); + + // Find C++ object associated with window. + ScintillaWin *sci = reinterpret_cast<ScintillaWin *>(GetWindowLong(hWnd, 0)); + // 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); + SetWindowLong(hWnd, 0, reinterpret_cast<LONG>(sci)); + return sci->WndProc(iMessage, wParam, lParam); + } else { + return DefWindowProc(hWnd, iMessage, wParam, lParam); + } + } else { + if (iMessage == WM_DESTROY) { + 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 +void Scintilla_RegisterClasses(HINSTANCE hInstance) { + ScintillaWin::Register(hInstance); +} + +#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) { + Scintilla_RegisterClasses(hInstance); + } + return TRUE; +} +#endif |