You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2897 lines
84 KiB
2897 lines
84 KiB
// 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>
|
|
#include <map>
|
|
#include <algorithm>
|
|
|
|
#undef _WIN32_WINNT
|
|
#define _WIN32_WINNT 0x0500
|
|
#undef WINVER
|
|
#define WINVER 0x0500
|
|
#include <windows.h>
|
|
#include <commctrl.h>
|
|
#include <richedit.h>
|
|
#include <windowsx.h>
|
|
|
|
#if defined(NTDDI_WIN7) && !defined(DISABLE_D2D)
|
|
#define USE_D2D 1
|
|
#endif
|
|
|
|
#if defined(USE_D2D)
|
|
#include <d2d1.h>
|
|
#include <dwrite.h>
|
|
#endif
|
|
|
|
#include "Platform.h"
|
|
|
|
#include "ILexer.h"
|
|
#include "Scintilla.h"
|
|
|
|
#ifdef SCI_LEXER
|
|
#include "SciLexer.h"
|
|
#include "LexerModule.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 "CaseFolder.h"
|
|
#include "Document.h"
|
|
#include "Selection.h"
|
|
#include "PositionCache.h"
|
|
#include "Editor.h"
|
|
#include "ScintillaBase.h"
|
|
#include "UniConversion.h"
|
|
#include "CaseConvert.h"
|
|
|
|
#include "PlatWin.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>
|
|
#include <zmouse.h>
|
|
#include <ole2.h>
|
|
|
|
#ifndef MK_ALT
|
|
#define MK_ALT 32
|
|
#endif
|
|
|
|
#define SC_WIN_IDLE 5001
|
|
|
|
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
|
|
|
|
// 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));
|
|
}
|
|
static void SetWindowID(HWND hWnd, int identifier) {
|
|
::SetWindowLongPtr(hWnd, GWLP_ID, identifier);
|
|
}
|
|
#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));
|
|
}
|
|
static void SetWindowID(HWND hWnd, int identifier) {
|
|
::SetWindowLong(hWnd, GWL_ID, identifier);
|
|
}
|
|
#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;
|
|
|
|
#if defined(USE_D2D)
|
|
ID2D1HwndRenderTarget *pRenderTarget;
|
|
bool renderTargetValid;
|
|
#endif
|
|
|
|
ScintillaWin(HWND hwnd);
|
|
ScintillaWin(const ScintillaWin &);
|
|
virtual ~ScintillaWin();
|
|
ScintillaWin &operator=(const ScintillaWin &);
|
|
|
|
virtual void Initialise();
|
|
virtual void Finalise();
|
|
#if defined(USE_D2D)
|
|
void EnsureRenderTarget();
|
|
void DropRenderTarget();
|
|
#endif
|
|
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 void SetCtrlID(int identifier);
|
|
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 FullPaint();
|
|
void FullPaintDC(HDC dc);
|
|
bool IsCompatibleDC(HDC dc);
|
|
DWORD EffectFromState(DWORD grfKeyState) const;
|
|
|
|
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) const {
|
|
return drag.rectangular && (fmt == cfColumnSelect);
|
|
}
|
|
|
|
private:
|
|
// For use in creating a system caret
|
|
bool HasCaretSizeChanged() const;
|
|
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;
|
|
|
|
#if defined(USE_D2D)
|
|
pRenderTarget = 0;
|
|
renderTargetValid = true;
|
|
#endif
|
|
|
|
keysAlwaysUnicode = false;
|
|
|
|
caret.period = ::GetCaretBlinkTime();
|
|
if (caret.period < 0)
|
|
caret.period = 0;
|
|
|
|
Initialise();
|
|
}
|
|
|
|
ScintillaWin::~ScintillaWin() {}
|
|
|
|
void ScintillaWin::Initialise() {
|
|
// Initialize COM. If the app has already done this it will have
|
|
// no effect. If the app hasnt, we really shouldnt ask them to call
|
|
// it just so this internal feature works.
|
|
hrOle = ::OleInitialize(NULL);
|
|
|
|
// Find TrackMouseEvent which is available on Windows > 95
|
|
HMODULE user32 = ::GetModuleHandle(TEXT("user32.dll"));
|
|
if (user32)
|
|
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);
|
|
#if defined(USE_D2D)
|
|
DropRenderTarget();
|
|
#endif
|
|
::RevokeDragDrop(MainHWND());
|
|
if (SUCCEEDED(hrOle)) {
|
|
::OleUninitialize();
|
|
}
|
|
}
|
|
|
|
#if defined(USE_D2D)
|
|
|
|
void ScintillaWin::EnsureRenderTarget() {
|
|
if (!renderTargetValid) {
|
|
DropRenderTarget();
|
|
renderTargetValid = true;
|
|
}
|
|
if (pD2DFactory && !pRenderTarget) {
|
|
RECT rc;
|
|
HWND hw = MainHWND();
|
|
GetClientRect(hw, &rc);
|
|
|
|
D2D1_SIZE_U size = D2D1::SizeU(rc.right - rc.left, rc.bottom - rc.top);
|
|
|
|
// Create a Direct2D render target.
|
|
#if 1
|
|
D2D1_HWND_RENDER_TARGET_PROPERTIES dhrtp;
|
|
dhrtp.hwnd = hw;
|
|
dhrtp.pixelSize = size;
|
|
dhrtp.presentOptions = D2D1_PRESENT_OPTIONS_NONE;
|
|
|
|
D2D1_RENDER_TARGET_PROPERTIES drtp;
|
|
drtp.type = D2D1_RENDER_TARGET_TYPE_DEFAULT;
|
|
drtp.pixelFormat.format = DXGI_FORMAT_UNKNOWN;
|
|
drtp.pixelFormat.alphaMode = D2D1_ALPHA_MODE_UNKNOWN;
|
|
drtp.dpiX = 96.0;
|
|
drtp.dpiY = 96.0;
|
|
drtp.usage = D2D1_RENDER_TARGET_USAGE_NONE;
|
|
drtp.minLevel = D2D1_FEATURE_LEVEL_DEFAULT;
|
|
|
|
pD2DFactory->CreateHwndRenderTarget(drtp, dhrtp, &pRenderTarget);
|
|
#else
|
|
pD2DFactory->CreateHwndRenderTarget(
|
|
D2D1::RenderTargetProperties(
|
|
D2D1_RENDER_TARGET_TYPE_DEFAULT ,
|
|
D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_PREMULTIPLIED),
|
|
96.0f, 96.0f, D2D1_RENDER_TARGET_USAGE_NONE, D2D1_FEATURE_LEVEL_DEFAULT),
|
|
D2D1::HwndRenderTargetProperties(hw, size),
|
|
&pRenderTarget);
|
|
#endif
|
|
// Pixmaps were created to be compatible with previous render target so
|
|
// need to be recreated.
|
|
DropGraphics(false);
|
|
}
|
|
}
|
|
|
|
void ScintillaWin::DropRenderTarget() {
|
|
if (pRenderTarget) {
|
|
pRenderTarget->Release();
|
|
pRenderTarget = 0;
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
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);
|
|
}
|
|
rcPaint = PRectangle(pps->rcPaint.left, pps->rcPaint.top, pps->rcPaint.right, pps->rcPaint.bottom);
|
|
PRectangle rcClient = GetClientRectangle();
|
|
paintingAllText = rcPaint.Contains(rcClient);
|
|
if (technology == SC_TECHNOLOGY_DEFAULT) {
|
|
AutoSurface surfaceWindow(pps->hdc, this);
|
|
if (surfaceWindow) {
|
|
Paint(surfaceWindow, rcPaint);
|
|
surfaceWindow->Release();
|
|
}
|
|
} else {
|
|
#if defined(USE_D2D)
|
|
for (int attempt=0;attempt<2;attempt++) {
|
|
EnsureRenderTarget();
|
|
AutoSurface surfaceWindow(pRenderTarget, this);
|
|
if (surfaceWindow) {
|
|
pRenderTarget->BeginDraw();
|
|
Paint(surfaceWindow, rcPaint);
|
|
surfaceWindow->Release();
|
|
HRESULT hr = pRenderTarget->EndDraw();
|
|
if (hr == D2DERR_RECREATE_TARGET) {
|
|
DropRenderTarget();
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
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) {
|
|
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);
|
|
}
|
|
|
|
// 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;
|
|
}
|
|
|
|
UINT CodePageFromCharSet(DWORD characterSet, UINT documentCodePage) {
|
|
if (documentCodePage == SC_CP_UTF8) {
|
|
return SC_CP_UTF8;
|
|
}
|
|
switch (characterSet) {
|
|
case SC_CHARSET_ANSI: return 1252;
|
|
case SC_CHARSET_DEFAULT: return documentCodePage;
|
|
case SC_CHARSET_BALTIC: return 1257;
|
|
case SC_CHARSET_CHINESEBIG5: return 950;
|
|
case SC_CHARSET_EASTEUROPE: return 1250;
|
|
case SC_CHARSET_GB2312: return 936;
|
|
case SC_CHARSET_GREEK: return 1253;
|
|
case SC_CHARSET_HANGUL: return 949;
|
|
case SC_CHARSET_MAC: return 10000;
|
|
case SC_CHARSET_OEM: return 437;
|
|
case SC_CHARSET_RUSSIAN: return 1251;
|
|
case SC_CHARSET_SHIFTJIS: return 932;
|
|
case SC_CHARSET_TURKISH: return 1254;
|
|
case SC_CHARSET_JOHAB: return 1361;
|
|
case SC_CHARSET_HEBREW: return 1255;
|
|
case SC_CHARSET_ARABIC: return 1256;
|
|
case SC_CHARSET_VIETNAMESE: return 1258;
|
|
case SC_CHARSET_THAI: return 874;
|
|
case SC_CHARSET_8859_15: return 28605;
|
|
// Not supported
|
|
case SC_CHARSET_CYRILLIC: return documentCodePage;
|
|
case SC_CHARSET_SYMBOL: return documentCodePage;
|
|
}
|
|
return documentCodePage;
|
|
}
|
|
|
|
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: {
|
|
#if defined(USE_D2D)
|
|
if (paintState == notPainting) {
|
|
DropRenderTarget();
|
|
} else {
|
|
renderTargetValid = false;
|
|
}
|
|
#endif
|
|
//Platform::DebugPrintf("Scintilla WM_SIZE %d %d\n", LoWord(lParam), HiWord(lParam));
|
|
ChangeSize();
|
|
}
|
|
break;
|
|
|
|
case WM_MOUSEWHEEL:
|
|
// if autocomplete list active then send mousewheel message to it
|
|
if (ac.Active()) {
|
|
HWND hWnd = reinterpret_cast<HWND>(ac.lb->GetID());
|
|
::SendMessage(hWnd, iMessage, wParam, lParam);
|
|
break;
|
|
}
|
|
|
|
// 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;
|
|
const DWORD maxWorkTime = 50;
|
|
|
|
if (dwCurrent >= dwStart && dwCurrent > maxWorkTime && dwCurrent - maxWorkTime < dwStart)
|
|
PostMessage(MainHWND(), SC_WIN_IDLE, dwStart, 0);
|
|
} else {
|
|
SetIdle(false);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case WM_GETMINMAXINFO:
|
|
return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
|
|
|
|
case WM_LBUTTONDOWN: {
|
|
// For IME, set the composition string as the result string.
|
|
HIMC hIMC = ::ImmGetContext(MainHWND());
|
|
::ImmNotifyIME(hIMC, NI_COMPOSITIONSTR, CPS_COMPLETE, 0);
|
|
::ImmReleaseContext(MainHWND(), hIMC);
|
|
//
|
|
//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));
|
|
::SetFocus(MainHWND());
|
|
ButtonDown(Point::FromLong(lParam), ::GetMessageTime(),
|
|
(wParam & MK_SHIFT) != 0,
|
|
(wParam & MK_CONTROL) != 0,
|
|
Platform::IsKeyDown(VK_MENU));
|
|
}
|
|
break;
|
|
|
|
case WM_MBUTTONDOWN:
|
|
::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:
|
|
::SetFocus(MainHWND());
|
|
if (!PointInSelection(Point::FromLong(lParam))) {
|
|
CancelModes();
|
|
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;
|
|
if (0 != ::GetCursorPos(&pt)) {
|
|
::ScreenToClient(MainHWND(), &pt);
|
|
if (PointInSelMargin(Point(pt.x, pt.y))) {
|
|
DisplayCursor(GetMarginCursor(Point(pt.x, pt.y)));
|
|
} 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] = {static_cast<wchar_t>(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);
|
|
inBufferCP[size] = '\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();
|
|
}
|
|
}
|
|
break;
|
|
|
|
case WM_SETFOCUS:
|
|
SetFocusState(true);
|
|
DestroySystemCaret();
|
|
CreateSystemCaret();
|
|
break;
|
|
|
|
case WM_SYSCOLORCHANGE:
|
|
//Platform::DebugPrintf("Setting Changed\n");
|
|
InvalidateStyleData();
|
|
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 = {static_cast<int>(pt.x), static_cast<int>(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;
|
|
|
|
case SCI_SETTECHNOLOGY:
|
|
if ((wParam == SC_TECHNOLOGY_DEFAULT) || (wParam == SC_TECHNOLOGY_DIRECTWRITE)) {
|
|
if (technology != static_cast<int>(wParam)) {
|
|
if (static_cast<int>(wParam) == SC_TECHNOLOGY_DIRECTWRITE) {
|
|
#if defined(USE_D2D)
|
|
if (!LoadD2D())
|
|
// Failed to load Direct2D or DirectWrite so no effect
|
|
return 0;
|
|
#else
|
|
return 0;
|
|
#endif
|
|
}
|
|
technology = wParam;
|
|
// Invalidate all cached information including layout.
|
|
DropGraphics(true);
|
|
InvalidateStyleRedraw();
|
|
}
|
|
}
|
|
break;
|
|
|
|
#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());
|
|
Redraw();
|
|
}
|
|
|
|
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)
|
|
nPage = vertEndPreferred + 1;
|
|
if ((sci.nMin != 0) ||
|
|
(sci.nMax != vertEndPreferred) ||
|
|
(sci.nPage != static_cast<unsigned int>(nPage)) ||
|
|
(sci.nPos != 0)) {
|
|
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;
|
|
unsigned int pageWidth = rcText.Width();
|
|
if (!horizontalScrollBarVisible || (wrapState != eWrapNone))
|
|
pageWidth = horizEndPreferred + 1;
|
|
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()));
|
|
}
|
|
|
|
void ScintillaWin::SetCtrlID(int identifier) {
|
|
::SetWindowID(reinterpret_cast<HWND>(wMain.GetID()), identifier);
|
|
}
|
|
|
|
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 CaseFolderDBCS : 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;
|
|
UINT cp;
|
|
public:
|
|
CaseFolderDBCS(UINT cp_) : cp(cp_) {
|
|
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(cp, 0, mixed,
|
|
static_cast<int>(lenMixed),
|
|
&utf16Mixed[0],
|
|
static_cast<int>(utf16Mixed.size()));
|
|
|
|
if (nUtf16Mixed == 0) {
|
|
// Failed to convert -> bad input
|
|
folded[0] = '\0';
|
|
return 1;
|
|
}
|
|
|
|
unsigned int lenFlat = 0;
|
|
for (size_t mixIndex=0; mixIndex < nUtf16Mixed; mixIndex++) {
|
|
if ((lenFlat + 20) > utf16Folded.size())
|
|
utf16Folded.resize(lenFlat + 60);
|
|
const char *foldedUTF8 = CaseConvert(utf16Mixed[mixIndex], CaseConversionFold);
|
|
if (foldedUTF8) {
|
|
// Maximum length of a case conversion is 6 bytes, 3 characters
|
|
wchar_t wFolded[20];
|
|
unsigned int charsConverted = UTF16FromUTF8(foldedUTF8,
|
|
static_cast<unsigned int>(strlen(foldedUTF8)),
|
|
wFolded, sizeof(wFolded)/sizeof(wFolded[0]));
|
|
for (size_t j=0;j<charsConverted;j++)
|
|
utf16Folded[lenFlat++] = wFolded[j];
|
|
} else {
|
|
utf16Folded[lenFlat++] = utf16Mixed[mixIndex];
|
|
}
|
|
}
|
|
|
|
size_t lenOut = ::WideCharToMultiByte(cp, 0,
|
|
&utf16Folded[0], lenFlat,
|
|
NULL, 0, NULL, 0);
|
|
|
|
if (lenOut < sizeFolded) {
|
|
::WideCharToMultiByte(cp, 0,
|
|
&utf16Folded[0], lenFlat,
|
|
folded, static_cast<int>(lenOut), NULL, 0);
|
|
return lenOut;
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
CaseFolder *ScintillaWin::CaseFolderForEncoding() {
|
|
UINT cpDest = CodePageOfDocument();
|
|
if (cpDest == SC_CP_UTF8) {
|
|
return new CaseFolderUnicode();
|
|
} else {
|
|
if (pdoc->dbcsCodePage == 0) {
|
|
CaseFolderTable *pcf = new CaseFolderTable();
|
|
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) {
|
|
const char *caseFolded = CaseConvert(wCharacter[0], CaseConversionFold);
|
|
if (caseFolded) {
|
|
wchar_t wLower[20];
|
|
unsigned int charsConverted = UTF16FromUTF8(caseFolded,
|
|
static_cast<unsigned int>(strlen(caseFolded)),
|
|
wLower, sizeof(wLower)/sizeof(wLower[0]));
|
|
if (charsConverted == 1) {
|
|
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;
|
|
} else {
|
|
return new CaseFolderDBCS(cpDest);
|
|
}
|
|
}
|
|
}
|
|
|
|
std::string ScintillaWin::CaseMapString(const std::string &s, int caseMapping) {
|
|
if ((s.size() == 0) || (caseMapping == cmSame))
|
|
return s;
|
|
|
|
UINT cpDoc = CodePageOfDocument();
|
|
if (cpDoc == SC_CP_UTF8) {
|
|
std::string retMapped(s.length() * maxExpansionCaseConversion, 0);
|
|
size_t lenMapped = CaseConvertString(&retMapped[0], retMapped.length(), s.c_str(), s.length(),
|
|
(caseMapping == cmUpper) ? CaseConversionUpper : CaseConversionLower);
|
|
retMapped.resize(lenMapped);
|
|
return retMapped;
|
|
}
|
|
|
|
unsigned int lengthUTF16 = ::MultiByteToWideChar(cpDoc, 0, s.c_str(),
|
|
static_cast<int>(s.size()), NULL, 0);
|
|
if (lengthUTF16 == 0) // Failed to convert
|
|
return s;
|
|
|
|
DWORD mapFlags = LCMAP_LINGUISTIC_CASING |
|
|
((caseMapping == cmUpper) ? LCMAP_UPPERCASE : LCMAP_LOWERCASE);
|
|
|
|
// Change text to UTF-16
|
|
std::vector<wchar_t> vwcText(lengthUTF16);
|
|
::MultiByteToWideChar(cpDoc, 0, s.c_str(), static_cast<int>(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], static_cast<int>(vwcConverted.size()),
|
|
NULL, 0, NULL, 0);
|
|
std::vector<char> vcConverted(lengthConverted);
|
|
::WideCharToMultiByte(cpDoc, 0,
|
|
&vwcConverted[0], static_cast<int>(vwcConverted.size()),
|
|
&vcConverted[0], static_cast<int>(vcConverted.size()), NULL, 0);
|
|
|
|
return std::string(&vcConverted[0], vcConverted.size());
|
|
}
|
|
|
|
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 {
|
|
std::string convertedText;
|
|
if (convertPastes) {
|
|
// Convert line endings of the paste into our local line-endings mode
|
|
convertedText = Document::TransformLineEnds(text, len, pdoc->eolMode);
|
|
len = static_cast<int>(convertedText.length());
|
|
text = convertedText.c_str();
|
|
}
|
|
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, static_cast<int>(strlen(endline)));
|
|
len += static_cast<int>(strlen(endline));
|
|
}
|
|
if (sel.MainCaret() == insertPos) {
|
|
SetEmptySelection(sel.MainCaret() + len);
|
|
}
|
|
} else {
|
|
InsertPaste(selStart, text, len);
|
|
}
|
|
}
|
|
}
|
|
|
|
void ScintillaWin::Paste() {
|
|
if (!::OpenClipboard(MainHWND()))
|
|
return;
|
|
UndoGroup ug(pdoc);
|
|
bool isLine = SelectionEmpty() && (::IsClipboardFormatAvailable(cfLineSelect) != 0);
|
|
ClearSelection(multiPasteMode == SC_MULTIPASTE_EACH);
|
|
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;
|
|
std::vector<char> putf;
|
|
// Default Scintilla behaviour in Unicode mode
|
|
if (IsUnicodeMode()) {
|
|
unsigned int bytes = memUSelection.Size();
|
|
len = UTF8Length(uptr, bytes / 2);
|
|
putf.resize(len + 1);
|
|
UTF8FromUTF16(uptr, bytes / 2, &putf[0], 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.resize(len + 1);
|
|
::WideCharToMultiByte(cpDest, 0, uptr, -1,
|
|
&putf[0], len + 1, NULL, NULL);
|
|
}
|
|
|
|
InsertPasteText(&putf[0], len, selStart, isRectangular, isLine);
|
|
}
|
|
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()) {
|
|
std::vector<wchar_t> uptr(len+1);
|
|
|
|
unsigned int ulen = ::MultiByteToWideChar(CP_ACP, 0,
|
|
ptr, len, &uptr[0], len+1);
|
|
|
|
unsigned int mlen = UTF8Length(&uptr[0], ulen);
|
|
std::vector<char> putf(mlen+1);
|
|
// CP_UTF8 not available on Windows 95, so use UTF8FromUTF16()
|
|
UTF8FromUTF16(&uptr[0], ulen, &putf[0], mlen);
|
|
|
|
InsertPasteText(&putf[0], mlen, selStart, isRectangular, isLine);
|
|
} 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() {
|
|
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 * SC_FONT_SIZE_MULTIPLIER;
|
|
if (sizeZoomed <= 2 * SC_FONT_SIZE_MULTIPLIER) // Hangs if sizeZoomed <= 1
|
|
sizeZoomed = 2 * SC_FONT_SIZE_MULTIPLIER;
|
|
AutoSurface surface(this);
|
|
int deviceHeight = sizeZoomed;
|
|
if (surface) {
|
|
deviceHeight = (sizeZoomed * surface->LogPixelsY()) / 72;
|
|
}
|
|
// The negative is to allow for leading
|
|
lf.lfHeight = -(abs(deviceHeight / SC_FONT_SIZE_MULTIPLIER));
|
|
lf.lfWeight = vs.styles[styleHere].weight;
|
|
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();
|
|
}
|
|
}
|
|
|
|
/** 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.Data(),
|
|
static_cast<int>(selectedText.LengthWithTerminator()));
|
|
uniText.Allocate(2 * uchars);
|
|
if (uniText) {
|
|
UTF16FromUTF8(selectedText.Data(), static_cast<int>(selectedText.LengthWithTerminator()),
|
|
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.Data(),
|
|
static_cast<int>(selectedText.LengthWithTerminator()), 0, 0);
|
|
uniText.Allocate(2 * uLen);
|
|
if (uniText) {
|
|
::MultiByteToWideChar(cpSrc, 0, selectedText.Data(),
|
|
static_cast<int>(selectedText.LengthWithTerminator()),
|
|
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.LengthWithTerminator());
|
|
if (ansiText) {
|
|
::WideCharToMultiByte(CP_ACP, 0, static_cast<wchar_t *>(uniText.ptr), -1,
|
|
static_cast<char *>(ansiText.ptr),
|
|
static_cast<int>(selectedText.LengthWithTerminator()), 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.LengthWithTerminator());
|
|
if (ansiText) {
|
|
memcpy(static_cast<char *>(ansiText.ptr), selectedText.Data(), selectedText.LengthWithTerminator());
|
|
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);
|
|
}
|
|
|
|
/**
|
|
* Redraw all of text area.
|
|
* This paint will not be abandoned.
|
|
*/
|
|
void ScintillaWin::FullPaint() {
|
|
if (technology == SC_TECHNOLOGY_DEFAULT) {
|
|
HDC hdc = ::GetDC(MainHWND());
|
|
FullPaintDC(hdc);
|
|
::ReleaseDC(MainHWND(), hdc);
|
|
} else {
|
|
FullPaintDC(0);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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;
|
|
if (technology == SC_TECHNOLOGY_DEFAULT) {
|
|
AutoSurface surfaceWindow(hdc, this);
|
|
if (surfaceWindow) {
|
|
Paint(surfaceWindow, rcPaint);
|
|
surfaceWindow->Release();
|
|
}
|
|
} else {
|
|
#if defined(USE_D2D)
|
|
EnsureRenderTarget();
|
|
AutoSurface surfaceWindow(pRenderTarget, this);
|
|
if (surfaceWindow) {
|
|
pRenderTarget->BeginDraw();
|
|
Paint(surfaceWindow, rcPaint);
|
|
surfaceWindow->Release();
|
|
HRESULT hr = pRenderTarget->EndDraw();
|
|
if (hr == D2DERR_RECREATE_TARGET) {
|
|
DropRenderTarget();
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
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) const {
|
|
// 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};
|
|
|
|
std::vector<char> data; // Includes terminating NUL
|
|
|
|
FORMATETC fmtu = {CF_UNICODETEXT, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
|
|
HRESULT hr = pIDataSource->GetData(&fmtu, &medium);
|
|
if (SUCCEEDED(hr) && medium.hGlobal) {
|
|
GlobalMemory memUDrop(medium.hGlobal);
|
|
wchar_t *udata = static_cast<wchar_t *>(memUDrop.ptr);
|
|
if (udata) {
|
|
if (IsUnicodeMode()) {
|
|
int tlen = memUDrop.Size();
|
|
// Convert UTF-16 to UTF-8
|
|
int dataLen = UTF8Length(udata, tlen/2);
|
|
data.resize(dataLen+1);
|
|
UTF8FromUTF16(udata, tlen/2, &data[0], dataLen);
|
|
} 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.resize(tlen + 1);
|
|
::WideCharToMultiByte(cpDest, 0, udata, -1,
|
|
&data[0], tlen + 1, NULL, NULL);
|
|
}
|
|
}
|
|
memUDrop.Unlock();
|
|
} else {
|
|
FORMATETC fmte = {CF_TEXT, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
|
|
hr = pIDataSource->GetData(&fmte, &medium);
|
|
if (SUCCEEDED(hr) && medium.hGlobal) {
|
|
GlobalMemory memDrop(medium.hGlobal);
|
|
const char *cdata = static_cast<char *>(memDrop.ptr);
|
|
if (cdata)
|
|
data.assign(cdata, cdata+strlen(cdata)+1);
|
|
memDrop.Unlock();
|
|
}
|
|
}
|
|
|
|
if (!data.empty() && convertPastes) {
|
|
// Convert line endings of the drop into our local line-endings mode
|
|
std::string convertedText = Document::TransformLineEnds(&data[0], data.size() - 1, pdoc->eolMode);
|
|
data.assign(convertedText.c_str(), convertedText.c_str()+convertedText.length()+1);
|
|
}
|
|
|
|
if (!SUCCEEDED(hr) || data.empty()) {
|
|
//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[0], data.size() - 1, *pdwEffect == DROPEFFECT_MOVE, hrRectangular == S_OK);
|
|
|
|
// Free data
|
|
if (medium.pUnkForRelease != NULL)
|
|
medium.pUnkForRelease->Release();
|
|
else
|
|
::GlobalFree(medium.hGlobal);
|
|
|
|
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.Data(), static_cast<int>(drag.LengthWithTerminator()));
|
|
text.Allocate(2 * uchars);
|
|
if (text) {
|
|
UTF16FromUTF8(drag.Data(), static_cast<int>(drag.LengthWithTerminator()),
|
|
static_cast<wchar_t *>(text.ptr), uchars);
|
|
}
|
|
} else {
|
|
text.Allocate(drag.LengthWithTerminator());
|
|
if (text) {
|
|
memcpy(static_cast<char *>(text.ptr), drag.Data(), drag.LengthWithTerminator());
|
|
}
|
|
}
|
|
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() const {
|
|
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;
|
|
std::vector<char> bits(bitmapSize);
|
|
sysCaretBitmap = ::CreateBitmap(sysCaretWidth, sysCaretHeight, 1,
|
|
1, reinterpret_cast<BYTE *>(&bits[0]));
|
|
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;
|
|
}
|
|
|
|
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(sciThis->technology);
|
|
if (surfaceWindow) {
|
|
#if defined(USE_D2D)
|
|
ID2D1HwndRenderTarget *pCTRenderTarget = 0;
|
|
#endif
|
|
RECT rc;
|
|
GetClientRect(hWnd, &rc);
|
|
// Create a Direct2D render target.
|
|
if (sciThis->technology == SC_TECHNOLOGY_DEFAULT) {
|
|
surfaceWindow->Init(ps.hdc, hWnd);
|
|
} else {
|
|
#if defined(USE_D2D)
|
|
D2D1_HWND_RENDER_TARGET_PROPERTIES dhrtp;
|
|
dhrtp.hwnd = hWnd;
|
|
dhrtp.pixelSize = D2D1::SizeU(rc.right - rc.left, rc.bottom - rc.top);
|
|
dhrtp.presentOptions = D2D1_PRESENT_OPTIONS_NONE;
|
|
|
|
D2D1_RENDER_TARGET_PROPERTIES drtp;
|
|
drtp.type = D2D1_RENDER_TARGET_TYPE_DEFAULT;
|
|
drtp.pixelFormat.format = DXGI_FORMAT_UNKNOWN;
|
|
drtp.pixelFormat.alphaMode = D2D1_ALPHA_MODE_UNKNOWN;
|
|
drtp.dpiX = 96.0;
|
|
drtp.dpiY = 96.0;
|
|
drtp.usage = D2D1_RENDER_TARGET_USAGE_NONE;
|
|
drtp.minLevel = D2D1_FEATURE_LEVEL_DEFAULT;
|
|
|
|
if (!SUCCEEDED(pD2DFactory->CreateHwndRenderTarget(drtp, dhrtp, &pCTRenderTarget))) {
|
|
surfaceWindow->Release();
|
|
delete surfaceWindow;
|
|
::EndPaint(hWnd, &ps);
|
|
return 0;
|
|
}
|
|
surfaceWindow->Init(pCTRenderTarget, hWnd);
|
|
pCTRenderTarget->BeginDraw();
|
|
#endif
|
|
}
|
|
surfaceWindow->SetUnicodeMode(SC_CP_UTF8 == sciThis->ct.codePage);
|
|
surfaceWindow->SetDBCSMode(sciThis->ct.codePage);
|
|
sciThis->ct.PaintCT(surfaceWindow);
|
|
#if defined(USE_D2D)
|
|
if (pCTRenderTarget)
|
|
pCTRenderTarget->EndDraw();
|
|
#endif
|
|
surfaceWindow->Release();
|
|
delete surfaceWindow;
|
|
#if defined(USE_D2D)
|
|
if (pCTRenderTarget)
|
|
pCTRenderTarget->Release();
|
|
#endif
|
|
}
|
|
::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
|