diff options
Diffstat (limited to 'scintilla/win32/ScintillaWin.cxx')
-rw-r--r-- | scintilla/win32/ScintillaWin.cxx | 2722 |
1 files changed, 2722 insertions, 0 deletions
diff --git a/scintilla/win32/ScintillaWin.cxx b/scintilla/win32/ScintillaWin.cxx new file mode 100644 index 0000000..2aa17f8 --- /dev/null +++ b/scintilla/win32/ScintillaWin.cxx @@ -0,0 +1,2722 @@ +// Scintilla source code edit control
+/** @file ScintillaWin.cxx
+ ** Windows specific subclass of ScintillaBase.
+ **/
+// Copyright 1998-2003 by Neil Hodgson <neilh@scintilla.org>
+// The License.txt file describes the conditions under which this software may be distributed.
+
+#include <new>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <assert.h>
+#include <limits.h>
+
+#include <string>
+#include <vector>
+
+#define _WIN32_WINNT 0x0500
+#include <windows.h>
+#include <commctrl.h>
+#include <richedit.h>
+#include <windowsx.h>
+
+#include "Platform.h"
+
+#include "Scintilla.h"
+#ifdef SCI_LEXER
+#include "SciLexer.h"
+#include "PropSet.h"
+#include "PropSetSimple.h"
+#include "Accessor.h"
+#include "KeyWords.h"
+#endif
+#include "SplitVector.h"
+#include "Partitioning.h"
+#include "RunStyles.h"
+#include "ContractionState.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 "Decoration.h"
+#include "Document.h"
+#include "Selection.h"
+#include "PositionCache.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 <imm.h>
+#endif
+
+#include <commctrl.h>
+#ifndef __BORLANDC__
+#ifndef __DMC__
+#include <zmouse.h>
+#endif
+#endif
+#include <ole2.h>
+
+#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();
+
+typedef BOOL (WINAPI *TrackMouseEventSig)(LPTRACKMOUSEEVENT);
+
+// 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");
+
+#ifdef SCI_NAMESPACE
+using namespace Scintilla;
+#endif
+
+class ScintillaWin; // Forward declaration for COM interface subobjects
+
+typedef void VFunction(void);
+
+/**
+ */
+class FormatEnumerator {
+public:
+ VFunction **vtbl;
+ int ref;
+ int pos;
+ CLIPFORMAT formats[2];
+ int formatsLen;
+ FormatEnumerator(int pos_, CLIPFORMAT formats_[], int formatsLen_);
+};
+
+/**
+ */
+class DropSource {
+public:
+ VFunction **vtbl;
+ ScintillaWin *sci;
+ DropSource();
+};
+
+/**
+ */
+class DataObject {
+public:
+ VFunction **vtbl;
+ ScintillaWin *sci;
+ DataObject();
+};
+
+/**
+ */
+class DropTarget {
+public:
+ VFunction **vtbl;
+ ScintillaWin *sci;
+ DropTarget();
+};
+
+/**
+ */
+class ScintillaWin :
+ public ScintillaBase {
+
+ bool lastKeyDownConsumed;
+
+ bool capturedMouse;
+ bool trackedMouseLeave;
+ TrackMouseEventSig TrackMouseEventFn;
+
+ unsigned int linesPerScroll; ///< Intellimouse support
+ int wheelDelta; ///< Wheel delta from roll
+
+ HRGN hRgnUpdate;
+
+ bool hasOKText;
+
+ CLIPFORMAT cfColumnSelect;
+ CLIPFORMAT cfLineSelect;
+
+ HRESULT hrOle;
+ DropSource ds;
+ DataObject dob;
+ DropTarget dt;
+
+ static HINSTANCE hInstance;
+
+ ScintillaWin(HWND hwnd);
+ ScintillaWin(const ScintillaWin &);
+ virtual ~ScintillaWin();
+ ScintillaWin &operator=(const ScintillaWin &);
+
+ 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 bool DragThreshold(Point ptStart, Point ptNow);
+ virtual void StartDrag();
+ sptr_t WndPaint(uptr_t wParam);
+ sptr_t HandleComposition(uptr_t wParam, sptr_t lParam);
+ UINT CodePageOfDocument();
+ 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 void SetTrackMouseLeaveEvent(bool on);
+ 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, bool ctrl, bool alt);
+ virtual CaseFolder *CaseFolderForEncoding();
+ virtual std::string CaseMapString(const std::string &s, int caseMapping);
+ virtual void Copy();
+ virtual void CopyAllowLine();
+ 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);
+ DWORD EffectFromState(DWORD grfKeyState);
+
+ 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, SelectionPosition selStart, bool isRectangular, bool isLine);
+
+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);
+
+ 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;
+ bool keysAlwaysUnicode;
+};
+
+HINSTANCE ScintillaWin::hInstance = 0;
+
+ScintillaWin::ScintillaWin(HWND hwnd) {
+
+ lastKeyDownConsumed = false;
+
+ capturedMouse = false;
+ trackedMouseLeave = false;
+ TrackMouseEventFn = 0;
+
+ 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<CLIPFORMAT>(
+ ::RegisterClipboardFormat(TEXT("MSDEVColumnSelect")));
+
+ // Likewise for line-copy (copies a full line when no text is selected)
+ cfLineSelect = static_cast<CLIPFORMAT>(
+ ::RegisterClipboardFormat(TEXT("MSDEVLineSelect")));
+
+ hrOle = E_FAIL;
+
+ wMain = hwnd;
+
+ dob.sci = this;
+ ds.sci = this;
+ dt.sci = this;
+
+ sysCaretBitmap = 0;
+ sysCaretWidth = 0;
+ sysCaretHeight = 0;
+
+ keysAlwaysUnicode = false;
+
+ 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.
+ hrOle = ::OleInitialize(NULL);
+
+ // Find TrackMouseEvent which is available on Windows > 95
+ HMODULE user32 = ::GetModuleHandle(TEXT("user32.dll"));
+ TrackMouseEventFn = (TrackMouseEventSig)::GetProcAddress(user32, "TrackMouseEvent");
+ if (TrackMouseEventFn == NULL) {
+ // Windows 95 has an emulation in comctl32.dll:_TrackMouseEvent
+ HMODULE commctrl32 = ::LoadLibrary(TEXT("comctl32.dll"));
+ if (commctrl32 != NULL) {
+ TrackMouseEventFn = (TrackMouseEventSig)
+ ::GetProcAddress(commctrl32, "_TrackMouseEvent");
+ }
+ }
+}
+
+void ScintillaWin::Finalise() {
+ ScintillaBase::Finalise();
+ SetTicking(false);
+ SetIdle(false);
+ ::RevokeDragDrop(MainHWND());
+ if (SUCCEEDED(hrOle)) {
+ ::OleUninitialize();
+ }
+}
+
+HWND ScintillaWin::MainHWND() {
+ return reinterpret_cast<HWND>(wMain.GetID());
+}
+
+bool ScintillaWin::DragThreshold(Point ptStart, Point ptNow) {
+ int xMove = abs(ptStart.x - ptNow.x);
+ int yMove = abs(ptStart.y - ptNow.y);
+ return (xMove > ::GetSystemMetrics(SM_CXDRAG)) ||
+ (yMove > ::GetSystemMetrics(SM_CYDRAG));
+}
+
+void ScintillaWin::StartDrag() {
+ inDragDrop = ddDragging;
+ 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 = ddNone;
+ SetDragPosition(SelectionPosition(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 = ::GetLocaleInfoA(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_LWIN: return SCK_WIN;
+ case VK_RWIN: return SCK_RWIN;
+ case VK_APPS: return SCK_MENU;
+ 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<PAINTSTRUCT*>(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);
+ UTF8FromUTF16(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<size; i++) {
+ AddChar(dbcsval[i]);
+ }
+ }
+ // Set new position after converted
+ Point pos = PointMainCaret();
+ COMPOSITIONFORM CompForm;
+ CompForm.dwStyle = CFS_POINT;
+ CompForm.ptCurrentPos.x = pos.x;
+ CompForm.ptCurrentPos.y = pos.y;
+ ::ImmSetCompositionWindow(hIMC, &CompForm);
+ ::ImmReleaseContext(MainHWND(), hIMC);
+ }
+ return 0;
+ }
+ return ::DefWindowProc(MainHWND(), WM_IME_COMPOSITION, wParam, lParam);
+#endif
+}
+
+// Translate message IDs from WM_* and EM_* to SCI_* so can partly emulate Windows Edit control
+static unsigned int SciMessageFromEM(unsigned int iMessage) {
+ switch (iMessage) {
+ case EM_CANPASTE: return SCI_CANPASTE;
+ case EM_CANUNDO: return SCI_CANUNDO;
+ case EM_EMPTYUNDOBUFFER: return SCI_EMPTYUNDOBUFFER;
+ case EM_FINDTEXTEX: return SCI_FINDTEXT;
+ case EM_FORMATRANGE: return SCI_FORMATRANGE;
+ case EM_GETFIRSTVISIBLELINE: return SCI_GETFIRSTVISIBLELINE;
+ case EM_GETLINECOUNT: return SCI_GETLINECOUNT;
+ case EM_GETSELTEXT: return SCI_GETSELTEXT;
+ case EM_GETTEXTRANGE: return SCI_GETTEXTRANGE;
+ case EM_HIDESELECTION: return SCI_HIDESELECTION;
+ case EM_LINEINDEX: return SCI_POSITIONFROMLINE;
+ case EM_LINESCROLL: return SCI_LINESCROLL;
+ case EM_REPLACESEL: return SCI_REPLACESEL;
+ case EM_SCROLLCARET: return SCI_SCROLLCARET;
+ case EM_SETREADONLY: return SCI_SETREADONLY;
+ case WM_CLEAR: return SCI_CLEAR;
+ case WM_COPY: return SCI_COPY;
+ case WM_CUT: return SCI_CUT;
+ case WM_GETTEXT: return SCI_GETTEXT;
+ case WM_SETTEXT: return SCI_SETTEXT;
+ case WM_GETTEXTLENGTH: return SCI_GETTEXTLENGTH;
+ case WM_PASTE: return SCI_PASTE;
+ case WM_UNDO: return SCI_UNDO;
+ }
+ return iMessage;
+}
+
+static UINT CodePageFromCharSet(DWORD characterSet, UINT documentCodePage) {
+ if (documentCodePage == SC_CP_UTF8) {
+ // The system calls here are a little slow so avoid if known case.
+ return SC_CP_UTF8;
+ }
+ 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;
+}
+
+UINT ScintillaWin::CodePageOfDocument() {
+ return CodePageFromCharSet(vs.styles[STYLE_DEFAULT].characterSet, pdoc->dbcsCodePage);
+}
+
+sptr_t ScintillaWin::WndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
+ try {
+ //Platform::DebugPrintf("S M:%x WP:%x L:%x\n", iMessage, wParam, lParam);
+ iMessage = SciMessageFromEM(iMessage);
+ switch (iMessage) {
+
+ case WM_CREATE:
+ ctrlID = ::GetDlgCtrlID(reinterpret_cast<HWND>(wMain.GetID()));
+ // Get Intellimouse scroll line parameters
+ GetIntelliMouseParameters();
+ ::RegisterDragDrop(MainHWND(), reinterpret_cast<IDropTarget *>(&dt));
+ break;
+
+ case WM_COMMAND:
+ Command(LoWord(wParam));
+ break;
+
+ case WM_PAINT:
+ return WndPaint(wParam);
+
+ case WM_PRINTCLIENT: {
+ HDC hdc = reinterpret_cast<HDC>(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<short>(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:
+ SetTrackMouseLeaveEvent(true);
+ ButtonMove(Point::FromLong(lParam));
+ break;
+
+ case WM_MOUSELEAVE:
+ SetTrackMouseLeaveEvent(false);
+ MouseLeave();
+ return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
+
+ case WM_LBUTTONUP:
+ ButtonUp(Point::FromLong(lParam),
+ ::GetMessageTime(),
+ (wParam & MK_CONTROL) != 0);
+ break;
+
+ case WM_RBUTTONDOWN:
+ if (!PointInSelection(Point::FromLong(lParam)))
+ SetEmptySelection(PositionFromLocation(Point::FromLong(lParam)));
+ break;
+
+ case WM_SETCURSOR:
+ if (LoWord(lParam) == HTCLIENT) {
+ if (inDragDrop == ddDragging) {
+ 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 (((wParam >= 128) || !iscntrl(wParam)) || !lastKeyDownConsumed) {
+ if (::IsWindowUnicode(MainHWND()) || keysAlwaysUnicode) {
+ wchar_t wcs[2] = {wParam, 0};
+ if (IsUnicodeMode()) {
+ // For a wide character version of the window:
+ char utfval[4];
+ unsigned int len = UTF8Length(wcs, 1);
+ UTF8FromUTF16(wcs, 1, utfval, len);
+ AddCharUTF(utfval, len);
+ } else {
+ UINT cpDest = CodePageOfDocument();
+ char inBufferCP[20];
+ int size = ::WideCharToMultiByte(cpDest,
+ 0, wcs, 1, inBufferCP, sizeof(inBufferCP) - 1, 0, 0);
+ AddCharUTF(inBufferCP, size);
+ }
+ } else {
+ if (IsUnicodeMode()) {
+ AddCharBytes('\0', LOBYTE(wParam));
+ } else {
+ AddChar(LOBYTE(wParam));
+ }
+ }
+ }
+ return 0;
+
+ case WM_UNICHAR:
+ if (wParam == UNICODE_NOCHAR) {
+ return IsUnicodeMode() ? 1 : 0;
+ } else if (lastKeyDownConsumed) {
+ return 1;
+ } else {
+ if (IsUnicodeMode()) {
+ char utfval[4];
+ wchar_t wcs[2] = {static_cast<wchar_t>(wParam), 0};
+ unsigned int len = UTF8Length(wcs, 1);
+ UTF8FromUTF16(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<HWND>(wParam);
+ HWND wThis = MainHWND();
+ HWND wCT = reinterpret_cast<HWND>(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<uptr_t>(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:
+ if (displayPopupMenu) {
+ Point pt = Point::FromLong(lParam);
+ if ((pt.x == -1) && (pt.y == -1)) {
+ // Caused by keyboard so display menu near caret
+ pt = PointMainCaret();
+ POINT spt = {pt.x, pt.y};
+ ::ClientToScreen(MainHWND(), &spt);
+ pt = Point(spt.x, spt.y);
+ }
+ ContextMenu(pt);
+ return 0;
+ }
+ 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<int>(wParam) < 0) {
+ wParam = SelectionStart().Position();
+ }
+ return pdoc->LineFromPosition(wParam);
+
+ case EM_EXLINEFROMCHAR:
+ return pdoc->LineFromPosition(lParam);
+
+ case EM_GETSEL:
+ if (wParam) {
+ *reinterpret_cast<int *>(wParam) = SelectionStart().Position();
+ }
+ if (lParam) {
+ *reinterpret_cast<int *>(lParam) = SelectionEnd().Position();
+ }
+ return MAKELONG(SelectionStart().Position(), SelectionEnd().Position());
+
+ case EM_EXGETSEL: {
+ if (lParam == 0) {
+ return 0;
+ }
+ Sci_CharacterRange *pCR = reinterpret_cast<Sci_CharacterRange *>(lParam);
+ pCR->cpMin = SelectionStart().Position();
+ pCR->cpMax = SelectionEnd().Position();
+ }
+ break;
+
+ case EM_SETSEL: {
+ int nStart = static_cast<int>(wParam);
+ int nEnd = static_cast<int>(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;
+ }
+ Sci_CharacterRange *pCR = reinterpret_cast<Sci_CharacterRange *>(lParam);
+ sel.selType = Selection::selStream;
+ if (pCR->cpMin == 0 && pCR->cpMax == -1) {
+ SetSelection(pCR->cpMin, pdoc->Length());
+ } else {
+ SetSelection(pCR->cpMin, pCR->cpMax);
+ }
+ EnsureCaretVisible();
+ return pdoc->LineFromPosition(SelectionStart().Position());
+ }
+
+ case SCI_GETDIRECTFUNCTION:
+ return reinterpret_cast<sptr_t>(DirectFunction);
+
+ case SCI_GETDIRECTPOINTER:
+ return reinterpret_cast<sptr_t>(this);
+
+ case SCI_GRABFOCUS:
+ ::SetFocus(MainHWND());
+ break;
+
+ case SCI_SETKEYSUNICODE:
+ keysAlwaysUnicode = wParam != 0;
+ break;
+
+ case SCI_GETKEYSUNICODE:
+ return keysAlwaysUnicode;
+
+#ifdef SCI_LEXER
+ case SCI_LOADLEXERLIBRARY:
+ LexerManager::GetInstance()->Load(reinterpret_cast<const char *>(lParam));
+ break;
+#endif
+
+ default:
+ return ScintillaBase::WndProc(iMessage, wParam, lParam);
+ }
+ } catch (std::bad_alloc &) {
+ errorStatus = SC_STATUS_BADALLOC;
+ } catch (...) {
+ errorStatus = SC_STATUS_FAILURE;
+ }
+ 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<TickerID>(standardTimerID) : 0;
+ } else {
+ ::KillTimer(MainHWND(), reinterpret_cast<uptr_t>(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<IdlerID>(idleTimerID) : 0;
+ } else {
+ ::KillTimer(MainHWND(), reinterpret_cast<uptr_t>(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());
+}
+
+void ScintillaWin::SetTrackMouseLeaveEvent(bool on) {
+ if (on && TrackMouseEventFn && !trackedMouseLeave) {
+ TRACKMOUSEEVENT tme;
+ tme.cbSize = sizeof(tme);
+ tme.dwFlags = TME_LEAVE;
+ tme.hwndTrack = MainHWND();
+ TrackMouseEventFn(&tme);
+ }
+ trackedMouseLeave = on;
+}
+
+bool ScintillaWin::PaintContains(PRectangle rc) {
+ bool contains = true;
+ if ((paintState == painting) && (!rc.Empty())) {
+ 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 = PointMainCaret();
+ ::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<unsigned int>(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<int>(pageWidth)) {
+ HorizontalScrollTo(0);
+ }
+ }
+ return modified;
+}
+
+void ScintillaWin::NotifyChange() {
+ ::SendMessage(::GetParent(MainHWND()), WM_COMMAND,
+ MAKELONG(GetCtrlID(), SCEN_CHANGE),
+ reinterpret_cast<LPARAM>(MainHWND()));
+}
+
+void ScintillaWin::NotifyFocus(bool focus) {
+ ::SendMessage(::GetParent(MainHWND()), WM_COMMAND,
+ MAKELONG(GetCtrlID(), focus ? SCEN_SETFOCUS : SCEN_KILLFOCUS),
+ reinterpret_cast<LPARAM>(MainHWND()));
+}
+
+int ScintillaWin::GetCtrlID() {
+ return ::GetDlgCtrlID(reinterpret_cast<HWND>(wMain.GetID()));
+}
+
+void ScintillaWin::NotifyParent(SCNotification scn) {
+ scn.nmhdr.hwndFrom = MainHWND();
+ scn.nmhdr.idFrom = GetCtrlID();
+ ::SendMessage(::GetParent(MainHWND()), WM_NOTIFY,
+ GetCtrlID(), reinterpret_cast<LPARAM>(&scn));
+}
+
+void ScintillaWin::NotifyDoubleClick(Point pt, bool shift, bool ctrl, bool alt) {
+ //Platform::DebugPrintf("ScintillaWin Double click 0\n");
+ ScintillaBase::NotifyDoubleClick(pt, shift, ctrl, alt);
+ // 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));
+}
+
+class CaseFolderUTF8 : public CaseFolderTable {
+ // Allocate the expandable storage here so that it does not need to be reallocated
+ // for each call to Fold.
+ std::vector<wchar_t> utf16Mixed;
+ std::vector<wchar_t> utf16Folded;
+public:
+ CaseFolderUTF8() {
+ StandardASCII();
+ }
+ virtual size_t Fold(char *folded, size_t sizeFolded, const char *mixed, size_t lenMixed) {
+ if ((lenMixed == 1) && (sizeFolded > 0)) {
+ folded[0] = mapping[static_cast<unsigned char>(mixed[0])];
+ return 1;
+ } else {
+ if (lenMixed > utf16Mixed.size()) {
+ utf16Mixed.resize(lenMixed + 8);
+ }
+ size_t nUtf16Mixed = ::MultiByteToWideChar(65001, 0, mixed, lenMixed,
+ &utf16Mixed[0], utf16Mixed.size());
+
+ if (nUtf16Mixed * 4 > utf16Folded.size()) { // Maximum folding expansion factor of 4
+ utf16Folded.resize(nUtf16Mixed * 4 + 8);
+ }
+ int lenFlat = ::LCMapStringW(LOCALE_SYSTEM_DEFAULT,
+ LCMAP_LINGUISTIC_CASING | LCMAP_LOWERCASE,
+ &utf16Mixed[0], nUtf16Mixed, &utf16Folded[0], utf16Folded.size());
+
+ size_t lenOut = UTF8Length(&utf16Folded[0], lenFlat);
+ if (lenOut < sizeFolded) {
+ UTF8FromUTF16(&utf16Folded[0], lenFlat, folded, lenOut);
+ return lenOut;
+ } else {
+ return 0;
+ }
+ }
+ }
+};
+
+CaseFolder *ScintillaWin::CaseFolderForEncoding() {
+ UINT cpDest = CodePageOfDocument();
+ if (cpDest == SC_CP_UTF8) {
+ return new CaseFolderUTF8();
+ } else {
+ CaseFolderTable *pcf = new CaseFolderTable();
+ if (pdoc->dbcsCodePage == 0) {
+ pcf->StandardASCII();
+ // Only for single byte encodings
+ UINT cpDoc = CodePageOfDocument();
+ for (int i=0x80; i<0x100; i++) {
+ char sCharacter[2] = "A";
+ sCharacter[0] = static_cast<char>(i);
+ wchar_t wCharacter[20];
+ unsigned int lengthUTF16 = ::MultiByteToWideChar(cpDoc, 0, sCharacter, 1,
+ wCharacter, sizeof(wCharacter)/sizeof(wCharacter[0]));
+ if (lengthUTF16 == 1) {
+ wchar_t wLower[20];
+ int charsConverted = ::LCMapStringW(LOCALE_SYSTEM_DEFAULT,
+ LCMAP_LINGUISTIC_CASING | LCMAP_LOWERCASE,
+ wCharacter, lengthUTF16, wLower, sizeof(wLower)/sizeof(wLower[0]));
+ char sCharacterLowered[20];
+ unsigned int lengthConverted = ::WideCharToMultiByte(cpDoc, 0,
+ wLower, charsConverted,
+ sCharacterLowered, sizeof(sCharacterLowered), NULL, 0);
+ if ((lengthConverted == 1) && (sCharacter[0] != sCharacterLowered[0])) {
+ pcf->SetTranslation(sCharacter[0], sCharacterLowered[0]);
+ }
+ }
+ }
+ }
+ return pcf;
+ }
+}
+
+std::string ScintillaWin::CaseMapString(const std::string &s, int caseMapping) {
+ if (s.size() == 0)
+ return std::string();
+
+ if (caseMapping == cmSame)
+ return s;
+
+ UINT cpDoc = CodePageOfDocument();
+
+ unsigned int lengthUTF16 = ::MultiByteToWideChar(cpDoc, 0, s.c_str(), s.size(), NULL, 0);
+ if (lengthUTF16 == 0) // Failed to convert
+ return s;
+
+ DWORD mapFlags = LCMAP_LINGUISTIC_CASING |
+ ((caseMapping == cmUpper) ? LCMAP_UPPERCASE : LCMAP_LOWERCASE);
+
+ // Many conversions performed by search function are short so optimize this case.
+ enum { shortSize=20 };
+
+ if (s.size() > shortSize) {
+ // Use dynamic allocations for long strings
+
+ // Change text to UTF-16
+ std::vector<wchar_t> vwcText(lengthUTF16);
+ ::MultiByteToWideChar(cpDoc, 0, s.c_str(), s.size(), &vwcText[0], lengthUTF16);
+
+ // Change case
+ int charsConverted = ::LCMapStringW(LOCALE_SYSTEM_DEFAULT, mapFlags,
+ &vwcText[0], lengthUTF16, NULL, 0);
+ std::vector<wchar_t> vwcConverted(charsConverted);
+ ::LCMapStringW(LOCALE_SYSTEM_DEFAULT, mapFlags,
+ &vwcText[0], lengthUTF16, &vwcConverted[0], charsConverted);
+
+ // Change back to document encoding
+ unsigned int lengthConverted = ::WideCharToMultiByte(cpDoc, 0,
+ &vwcConverted[0], vwcConverted.size(),
+ NULL, 0, NULL, 0);
+ std::vector<char> vcConverted(lengthConverted);
+ ::WideCharToMultiByte(cpDoc, 0,
+ &vwcConverted[0], vwcConverted.size(),
+ &vcConverted[0], vcConverted.size(), NULL, 0);
+
+ return std::string(&vcConverted[0], vcConverted.size());
+
+ } else {
+ // Use static allocations for short strings as much faster
+ // A factor of 15 for single character strings
+
+ // Change text to UTF-16
+ wchar_t vwcText[shortSize];
+ ::MultiByteToWideChar(cpDoc, 0, s.c_str(), s.size(), vwcText, lengthUTF16);
+
+ // Change case
+ int charsConverted = ::LCMapStringW(LOCALE_SYSTEM_DEFAULT, mapFlags,
+ vwcText, lengthUTF16, NULL, 0);
+ // Full mapping may produce up to 3 characters per input character
+ wchar_t vwcConverted[shortSize*3];
+ ::LCMapStringW(LOCALE_SYSTEM_DEFAULT, mapFlags, vwcText, lengthUTF16,
+ vwcConverted, charsConverted);
+
+ // Change back to document encoding
+ unsigned int lengthConverted = ::WideCharToMultiByte(cpDoc, 0,
+ vwcConverted, charsConverted,
+ NULL, 0, NULL, 0);
+ // Each UTF-16 code unit may need up to 3 bytes in UTF-8
+ char vcConverted[shortSize * 3 * 3];
+ ::WideCharToMultiByte(cpDoc, 0,
+ vwcConverted, charsConverted,
+ vcConverted, lengthConverted, NULL, 0);
+
+ return std::string(vcConverted, lengthConverted);
+ }
+}
+
+void ScintillaWin::Copy() {
+ //Platform::DebugPrintf("Copy\n");
+ if (!sel.Empty()) {
+ SelectionText selectedText;
+ CopySelectionRange(&selectedText);
+ CopyToClipboard(selectedText);
+ }
+}
+
+void ScintillaWin::CopyAllowLine() {
+ SelectionText selectedText;
+ CopySelectionRange(&selectedText, true);
+ 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;
+}
+
+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() const {
+ return ptr != 0;
+ }
+ SIZE_T Size() {
+ return ::GlobalSize(hand);
+ }
+};
+
+void ScintillaWin::InsertPasteText(const char *text, int len, SelectionPosition selStart, bool isRectangular, bool isLine) {
+ if (isRectangular) {
+ PasteRectangular(selStart, text, len);
+ } else {
+ char *convertedText = 0;
+ if (convertPastes) {
+ // Convert line endings of the paste into our local line-endings mode
+ convertedText = Document::TransformLineEnds(&len, text, len, pdoc->eolMode);
+ text = convertedText;
+ }
+ if (isLine) {
+ int insertPos = pdoc->LineStart(pdoc->LineFromPosition(sel.MainCaret()));
+ pdoc->InsertString(insertPos, text, len);
+ // add the newline if necessary
+ if ((len > 0) && (text[len-1] != '\n' && text[len-1] != '\r')) {
+ const char *endline = StringFromEOLMode(pdoc->eolMode);
+ pdoc->InsertString(insertPos + len, endline, strlen(endline));
+ len += strlen(endline);
+ }
+ if (sel.MainCaret() == insertPos) {
+ SetEmptySelection(sel.MainCaret() + len);
+ }
+ } else {
+ InsertPaste(selStart, text, len);
+ }
+ delete []convertedText;
+ }
+}
+
+void ScintillaWin::Paste() {
+ if (!::OpenClipboard(MainHWND()))
+ return;
+ UndoGroup ug(pdoc);
+ bool isLine = SelectionEmpty() && (::IsClipboardFormatAvailable(cfLineSelect) != 0);
+ ClearSelection();
+ SelectionPosition selStart = sel.IsRectangular() ?
+ sel.Rectangular().Start() :
+ sel.Range(sel.Main()).Start();
+ bool isRectangular = ::IsClipboardFormatAvailable(cfColumnSelect) != 0;
+
+ // Always use CF_UNICODETEXT if available
+ GlobalMemory memUSelection(::GetClipboardData(CF_UNICODETEXT));
+ if (memUSelection) {
+ wchar_t *uptr = static_cast<wchar_t *>(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];
+ UTF8FromUTF16(uptr, bytes / 2, putf, len);
+ } else {
+ // CF_UNICODETEXT available, but not in Unicode mode
+ // Convert from Unicode to current Scintilla code page
+ UINT cpDest = CodePageOfDocument();
+ len = ::WideCharToMultiByte(cpDest, 0, uptr, -1,
+ NULL, 0, NULL, NULL) - 1; // subtract 0 terminator
+ putf = new char[len + 1];
+ ::WideCharToMultiByte(cpDest, 0, uptr, -1,
+ putf, len + 1, NULL, NULL);
+ }
+
+ InsertPasteText(putf, len, selStart, isRectangular, isLine);
+ delete []putf;
+ }
+ memUSelection.Unlock();
+ } else {
+ // CF_UNICODETEXT not available, paste ANSI text
+ GlobalMemory memSelection(::GetClipboardData(CF_TEXT));
+ if (memSelection) {
+ char *ptr = static_cast<char *>(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 UTF8FromUTF16()
+ UTF8FromUTF16(uptr, ulen, putf, mlen);
+ }
+
+ delete []uptr;
+
+ if (putf) {
+ InsertPasteText(putf, mlen, selStart, isRectangular, isLine);
+ delete []putf;
+ }
+ } else {
+ InsertPasteText(ptr, len, selStart, isRectangular, isLine);
+ }
+ }
+ memSelection.Unlock();
+ }
+ }
+ ::CloseClipboard();
+ Redraw();
+}
+
+void ScintillaWin::CreateCallTipWindow(PRectangle) {
+ 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;
+ }
+}
+
+void ScintillaWin::AddToPopUp(const char *label, int cmd, bool enabled) {
+ HMENU hmenuPopup = reinterpret_cast<HMENU>(popup.GetID());
+ if (!label[0])
+ ::AppendMenuA(hmenuPopup, MF_SEPARATOR, 0, "");
+ else if (enabled)
+ ::AppendMenuA(hmenuPopup, MF_STRING, cmd, label);
+ else
+ ::AppendMenuA(hmenuPopup, MF_STRING | MF_DISABLED | MF_GRAYED, cmd, label);
+}
+
+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 < 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;
+ try {
+ pfe = new FormatEnumerator(fe->pos, fe->formats, fe->formatsLen);
+ } catch (...) {
+ return E_OUTOFMEMORY;
+ }
+ return FormatEnumerator_QueryInterface(pfe, IID_IEnumFORMATETC,
+ reinterpret_cast<void **>(ppenum));
+}
+
+static VFunction *vtFormatEnumerator[] = {
+ (VFunction *)(FormatEnumerator_QueryInterface),
+ (VFunction *)(FormatEnumerator_AddRef),
+ (VFunction *)(FormatEnumerator_Release),
+ (VFunction *)(FormatEnumerator_Next),
+ (VFunction *)(FormatEnumerator_Skip),
+ (VFunction *)(FormatEnumerator_Reset),
+ (VFunction *)(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; i<formatsLen; i++)
+ formats[i] = formats_[i];
+}
+
+/// 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 VFunction *vtDropSource[] = {
+ (VFunction *)(DropSource_QueryInterface),
+ (VFunction *)(DropSource_AddRef),
+ (VFunction *)(DropSource_Release),
+ (VFunction *)(DropSource_QueryContinueDrag),
+ (VFunction *)(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) {
+ try {
+ //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<void **>(ppEnum));
+ } catch (std::bad_alloc &) {
+ pd->sci->errorStatus = SC_STATUS_BADALLOC;
+ return E_OUTOFMEMORY;
+ } catch (...) {
+ pd->sci->errorStatus = SC_STATUS_FAILURE;
+ return E_FAIL;
+ }
+}
+
+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 VFunction *vtDataObject[] = {
+ (VFunction *)(DataObject_QueryInterface),
+ (VFunction *)(DataObject_AddRef),
+ (VFunction *)(DataObject_Release),
+ (VFunction *)(DataObject_GetData),
+ (VFunction *)(DataObject_GetDataHere),
+ (VFunction *)(DataObject_QueryGetData),
+ (VFunction *)(DataObject_GetCanonicalFormatEtc),
+ (VFunction *)(DataObject_SetData),
+ (VFunction *)(DataObject_EnumFormatEtc),
+ (VFunction *)(DataObject_DAdvise),
+ (VFunction *)(DataObject_DUnadvise),
+ (VFunction *)(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) {
+ try {
+ return dt->sci->DragEnter(pIDataSource, grfKeyState, pt, pdwEffect);
+ } catch (...) {
+ dt->sci->errorStatus = SC_STATUS_FAILURE;
+ }
+ return E_FAIL;
+}
+STDMETHODIMP DropTarget_DragOver(DropTarget *dt, DWORD grfKeyState, POINTL pt, PDWORD pdwEffect) {
+ try {
+ return dt->sci->DragOver(grfKeyState, pt, pdwEffect);
+ } catch (...) {
+ dt->sci->errorStatus = SC_STATUS_FAILURE;
+ }
+ return E_FAIL;
+}
+STDMETHODIMP DropTarget_DragLeave(DropTarget *dt) {
+ try {
+ return dt->sci->DragLeave();
+ } catch (...) {
+ dt->sci->errorStatus = SC_STATUS_FAILURE;
+ }
+ return E_FAIL;
+}
+STDMETHODIMP DropTarget_Drop(DropTarget *dt, LPDATAOBJECT pIDataSource, DWORD grfKeyState,
+ POINTL pt, PDWORD pdwEffect) {
+ try {
+ return dt->sci->Drop(pIDataSource, grfKeyState, pt, pdwEffect);
+ } catch (...) {
+ dt->sci->errorStatus = SC_STATUS_FAILURE;
+ }
+ return E_FAIL;
+}
+
+static VFunction *vtDropTarget[] = {
+ (VFunction *)(DropTarget_QueryInterface),
+ (VFunction *)(DropTarget_AddRef),
+ (VFunction *)(DropTarget_Release),
+ (VFunction *)(DropTarget_DragEnter),
+ (VFunction *)(DropTarget_DragOver),
+ (VFunction *)(DropTarget_DragLeave),
+ (VFunction *)(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 = PointMainCaret();
+ 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(sel.MainCaret())) & 31;
+ LOGFONTA lf = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ""};
+ 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<BYTE>(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);
+
+ ::ImmSetCompositionFontA(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);
+ UTF8FromUTF16(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 = UTF16Length(selectedText.s, selectedText.len);
+ uniText.Allocate(2 * uchars);
+ if (uniText) {
+ UTF16FromUTF8(selectedText.s, selectedText.len, static_cast<wchar_t *>(uniText.ptr), uchars);
+ }
+ } else {
+ // Not Unicode mode
+ // Convert to Unicode using the current Scintilla code page
+ UINT cpSrc = CodePageFromCharSet(
+ selectedText.characterSet, selectedText.codePage);
+ int uLen = ::MultiByteToWideChar(cpSrc, 0, selectedText.s, selectedText.len, 0, 0);
+ uniText.Allocate(2 * uLen);
+ if (uniText) {
+ ::MultiByteToWideChar(cpSrc, 0, selectedText.s, selectedText.len,
+ static_cast<wchar_t *>(uniText.ptr), uLen);
+ }
+ }
+
+ 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<wchar_t *>(uniText.ptr), -1,
+ static_cast<char *>(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<char *>(ansiText.ptr), selectedText.s, selectedText.len);
+ ansiText.SetClip(CF_TEXT);
+ }
+ }
+
+ if (selectedText.rectangular) {
+ ::SetClipboardData(cfColumnSelect, 0);
+ }
+
+ if (selectedText.lineCopy) {
+ ::SetClipboardData(cfLineSelect, 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:
+ case SB_THUMBTRACK: {
+ // Do NOT use wParam, its 16 bit and not enough for very long lines. Its still possible to overflow the 32 bit but you have to try harder =]
+ SCROLLINFO si;
+ si.cbSize = sizeof(si);
+ si.fMask = SIF_TRACKPOS;
+ if (GetScrollInfo(SB_HORZ, &si)) {
+ xPos = si.nTrackPos;
+ }
+ }
+ 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;
+}
+
+DWORD ScintillaWin::EffectFromState(DWORD grfKeyState) {
+ // These are the Wordpad semantics.
+ DWORD dwEffect;
+ if (inDragDrop == ddDragging) // Internal defaults to move
+ dwEffect = DROPEFFECT_MOVE;
+ else
+ dwEffect = DROPEFFECT_COPY;
+ if (grfKeyState & MK_ALT)
+ dwEffect = DROPEFFECT_MOVE;
+ if (grfKeyState & MK_CONTROL)
+ dwEffect = DROPEFFECT_COPY;
+ return dwEffect;
+}
+
+/// 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 pIDataSource, DWORD grfKeyState,
+ POINTL, PDWORD pdwEffect) {
+ if (pIDataSource == NULL)
+ return E_POINTER;
+ 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;
+ }
+
+ *pdwEffect = EffectFromState(grfKeyState);
+ return S_OK;
+}
+
+STDMETHODIMP ScintillaWin::DragOver(DWORD grfKeyState, POINTL pt, PDWORD pdwEffect) {
+ try {
+ if (!hasOKText || pdoc->IsReadOnly()) {
+ *pdwEffect = DROPEFFECT_NONE;
+ return S_OK;
+ }
+
+ *pdwEffect = EffectFromState(grfKeyState);
+
+ // Update the cursor.
+ POINT rpt = {pt.x, pt.y};
+ ::ScreenToClient(MainHWND(), &rpt);
+ SetDragPosition(SPositionFromLocation(Point(rpt.x, rpt.y), false, false, UserVirtualSpace()));
+
+ return S_OK;
+ } catch (...) {
+ errorStatus = SC_STATUS_FAILURE;
+ }
+ return E_FAIL;
+}
+
+STDMETHODIMP ScintillaWin::DragLeave() {
+ try {
+ SetDragPosition(SelectionPosition(invalidPosition));
+ return S_OK;
+ } catch (...) {
+ errorStatus = SC_STATUS_FAILURE;
+ }
+ return E_FAIL;
+}
+
+STDMETHODIMP ScintillaWin::Drop(LPDATAOBJECT pIDataSource, DWORD grfKeyState,
+ POINTL pt, PDWORD pdwEffect) {
+ try {
+ *pdwEffect = EffectFromState(grfKeyState);
+
+ if (pIDataSource == NULL)
+ return E_POINTER;
+
+ SetDragPosition(SelectionPosition(invalidPosition));
+
+ STGMEDIUM medium = {0, {0}, 0};
+
+ char *data = 0;
+ bool dataAllocated = false;
+
+ FORMATETC fmtu = {CF_UNICODETEXT, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
+ HRESULT hr = pIDataSource->GetData(&fmtu, &medium);
+ if (SUCCEEDED(hr) && medium.hGlobal) {
+ wchar_t *udata = static_cast<wchar_t *>(::GlobalLock(medium.hGlobal));
+ if (IsUnicodeMode()) {
+ int tlen = ::GlobalSize(medium.hGlobal);
+ // Convert UTF-16 to UTF-8
+ int dataLen = UTF8Length(udata, tlen/2);
+ data = new char[dataLen+1];
+ UTF8FromUTF16(udata, tlen/2, data, dataLen);
+ dataAllocated = true;
+ } else {
+ // Convert UTF-16 to ANSI
+ //
+ // Default Scintilla behavior in Unicode mode
+ // CF_UNICODETEXT available, but not in Unicode mode
+ // Convert from Unicode to current Scintilla code page
+ UINT cpDest = CodePageOfDocument();
+ int tlen = ::WideCharToMultiByte(cpDest, 0, udata, -1,
+ NULL, 0, NULL, NULL) - 1; // subtract 0 terminator
+ data = new char[tlen + 1];
+ memset(data, 0, (tlen+1));
+ ::WideCharToMultiByte(cpDest, 0, udata, -1,
+ data, tlen + 1, NULL, NULL);
+ 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<char *>(::GlobalLock(medium.hGlobal));
+ }
+ }
+
+ if (data && convertPastes) {
+ // Convert line endings of the drop into our local line-endings mode
+ int len = strlen(data);
+ char *convertedText = Document::TransformLineEnds(&len, data, len, pdoc->eolMode);
+ if (dataAllocated)
+ delete []data;
+ data = convertedText;
+ dataAllocated = true;
+ }
+
+ 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);
+ SelectionPosition movePos = SPositionFromLocation(Point(rpt.x, rpt.y), false, false, UserVirtualSpace());
+
+ 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;
+ } catch (...) {
+ errorStatus = SC_STATUS_FAILURE;
+ }
+ return E_FAIL;
+}
+
+/// 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 = UTF16Length(drag.s, drag.len);
+ text.Allocate(2 * uchars);
+ if (text) {
+ UTF16FromUTF8(drag.s, drag.len, static_cast<wchar_t *>(text.ptr), uchars);
+ }
+ } else {
+ text.Allocate(drag.len);
+ if (text) {
+ memcpy(static_cast<char *>(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;
+
+ // Register the Scintilla class
+ if (IsNT()) {
+
+ // 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) != 0;
+ } else {
+
+ // 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<BYTE *>(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<void *>(::GetWindowLongPtr(hWnd, 0));
+}
+static void SetWindowPointer(HWND hWnd, void *ptr) {
+ ::SetWindowLongPtr(hWnd, 0, reinterpret_cast<LONG_PTR>(ptr));
+}
+#else
+static void *PointerFromWindow(HWND hWnd) {
+ return reinterpret_cast<void *>(::GetWindowLong(hWnd, 0));
+}
+static void SetWindowPointer(HWND hWnd, void *ptr) {
+ ::SetWindowLong(hWnd, 0, reinterpret_cast<LONG>(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<ScintillaWin *>(PointerFromWindow(hWnd));
+ try {
+ // 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<CREATESTRUCT *>(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<short>(LOWORD(lParam));
+ pt.y = static_cast<short>(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);
+ }
+ }
+ } catch (...) {
+ sciThis->errorStatus = SC_STATUS_FAILURE;
+ }
+ 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"
+#ifndef STATIC_BUILD
+__declspec(dllexport)
+#endif
+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<ScintillaWin *>(PointerFromWindow(hWnd));
+ // sci will be zero if WM_CREATE not seen yet
+ if (sci == 0) {
+ try {
+ if (iMessage == WM_CREATE) {
+ // Create C++ object associated with window
+ sci = new ScintillaWin(hWnd);
+ SetWindowPointer(hWnd, sci);
+ return sci->WndProc(iMessage, wParam, lParam);
+ }
+ } catch (...) {
+ }
+ return ::DefWindowProc(hWnd, iMessage, wParam, lParam);
+ } else {
+ if (iMessage == WM_NCDESTROY) {
+ try {
+ sci->Finalise();
+ delete sci;
+ } catch (...) {
+ }
+ ::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.
+int Scintilla_RegisterClasses(void *hInstance) {
+ Platform_Initialise(hInstance);
+ bool result = ScintillaWin::Register(reinterpret_cast<HINSTANCE>(hInstance));
+#ifdef SCI_LEXER
+ Scintilla_LinkLexers();
+#endif
+ return result;
+}
+
+// This function is externally visible so it can be called from container when building statically.
+int 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
|