📄 scintillawin.cxx
字号:
// 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 <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <ctype.h>
#include <assert.h>
#include <limits.h>
#define _WIN32_WINNT 0x0400
#include <windows.h>
#include <commctrl.h>
#include <richedit.h>
#include <windowsx.h>
#include "Platform.h"
#include "Scintilla.h"
#include "SString.h"
#ifdef SCI_LEXER
#include "SciLexer.h"
#include "PropSet.h"
#include "Accessor.h"
#include "KeyWords.h"
#endif
#include "ContractionState.h"
#include "SVector.h"
#include "CellBuffer.h"
#include "CallTip.h"
#include "KeyMap.h"
#include "Indicator.h"
#include "XPM.h"
#include "LineMarker.h"
#include "Style.h"
#include "AutoComplete.h"
#include "ViewStyle.h"
#include "Document.h"
#include "Editor.h"
#include "ScintillaBase.h"
#include "UniConversion.h"
#ifdef SCI_LEXER
#include "ExternalLexer.h"
#endif
#ifndef SPI_GETWHEELSCROLLLINES
#define SPI_GETWHEELSCROLLLINES 104
#endif
#ifndef WM_UNICHAR
#define WM_UNICHAR 0x0109
#endif
#ifndef UNICODE_NOCHAR
#define UNICODE_NOCHAR 0xFFFF
#endif
// These undefinitions are required to work around differences between different versions
// of the mingw headers, some of which define these twice, in both winuser.h and imm.h.
#ifdef __MINGW_H
#if __MINGW32_MAJOR_VERSION == 1
#undef WM_IME_STARTCOMPOSITION
#undef WM_IME_ENDCOMPOSITION
#undef WM_IME_COMPOSITION
#undef WM_IME_KEYLAST
#undef WM_IME_SETCONTEXT
#undef WM_IME_NOTIFY
#undef WM_IME_CONTROL
#undef WM_IME_COMPOSITIONFULL
#undef WM_IME_SELECT
#undef WM_IME_CHAR
#undef WM_IME_KEYDOWN
#undef WM_IME_KEYUP
#endif
#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
// Functions imported from PlatWin
extern bool IsNT();
extern void Platform_Initialise(void *hInstance);
extern void Platform_Finalise();
/** TOTAL_CONTROL ifdef surrounds code that will only work when ScintillaWin
* is derived from ScintillaBase (all features) rather than directly from Editor
* (lightweight editor).
*/
#define TOTAL_CONTROL
// GCC has trouble with the standard COM ABI so do it the old C way with explicit vtables.
const char scintillaClassName[] = "Scintilla";
const char callClassName[] = "CallTip";
class ScintillaWin; // Forward declaration for COM interface subobjects
/**
*/
class FormatEnumerator {
public:
void **vtbl;
int ref;
int pos;
CLIPFORMAT formats[2];
int formatsLen;
FormatEnumerator(int pos_, CLIPFORMAT formats_[], int formatsLen_);
};
/**
*/
class DropSource {
public:
void **vtbl;
ScintillaWin *sci;
DropSource();
};
/**
*/
class DataObject {
public:
void **vtbl;
ScintillaWin *sci;
DataObject();
};
/**
*/
class DropTarget {
public:
void **vtbl;
ScintillaWin *sci;
DropTarget();
};
/**
*/
class ScintillaWin :
public ScintillaBase {
bool lastKeyDownConsumed;
bool capturedMouse;
unsigned int linesPerScroll; ///< Intellimouse support
int wheelDelta; ///< Wheel delta from roll
bool hasOKText;
CLIPFORMAT cfColumnSelect;
DropSource ds;
DataObject dob;
DropTarget dt;
static HINSTANCE hInstance;
ScintillaWin(HWND hwnd);
ScintillaWin(const ScintillaWin &) : ScintillaBase() {}
virtual ~ScintillaWin();
ScintillaWin &operator=(const ScintillaWin &) { return *this; }
virtual void Initialise();
virtual void Finalise();
HWND MainHWND();
static sptr_t DirectFunction(
ScintillaWin *sci, UINT iMessage, uptr_t wParam, sptr_t lParam);
static sptr_t PASCAL SWndProc(
HWND hWnd, UINT iMessage, WPARAM wParam, sptr_t lParam);
static sptr_t PASCAL CTWndProc(
HWND hWnd, UINT iMessage, WPARAM wParam, sptr_t lParam);
virtual void StartDrag();
sptr_t WndPaint(uptr_t wParam);
sptr_t HandleComposition(uptr_t wParam, sptr_t lParam);
virtual sptr_t DefWndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam);
virtual void SetTicking(bool on);
virtual void SetMouseCapture(bool on);
virtual bool HaveMouseCapture();
virtual void ScrollText(int linesToMove);
virtual void SetVerticalScrollPos();
virtual void SetHorizontalScrollPos();
virtual bool ModifyScrollBars(int nMax, int nPage);
virtual void NotifyChange();
virtual void NotifyFocus(bool focus);
virtual int GetCtrlID();
virtual void NotifyParent(SCNotification scn);
virtual void NotifyDoubleClick(Point pt, bool shift);
virtual void Copy();
virtual bool CanPaste();
virtual void Paste();
virtual void CreateCallTipWindow(PRectangle rc);
virtual void AddToPopUp(const char *label, int cmd = 0, bool enabled = true);
virtual void ClaimSelection();
// DBCS
void ImeStartComposition();
void ImeEndComposition();
void AddCharBytes(char b0, char b1=0);
void GetIntelliMouseParameters();
virtual void CopyToClipboard(const SelectionText &selectedText);
void ScrollMessage(WPARAM wParam);
void HorizontalScrollMessage(WPARAM wParam);
void RealizeWindowPalette(bool inBackGround);
void FullPaint();
public:
// Public for benefit of Scintilla_DirectFunction
virtual sptr_t WndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam);
/// Implement IUnknown
STDMETHODIMP QueryInterface(REFIID riid, PVOID *ppv);
STDMETHODIMP_(ULONG)AddRef();
STDMETHODIMP_(ULONG)Release();
/// Implement IDropTarget
STDMETHODIMP DragEnter(LPDATAOBJECT pIDataSource, DWORD grfKeyState,
POINTL pt, PDWORD pdwEffect);
STDMETHODIMP DragOver(DWORD grfKeyState, POINTL pt, PDWORD pdwEffect);
STDMETHODIMP DragLeave();
STDMETHODIMP Drop(LPDATAOBJECT pIDataSource, DWORD grfKeyState,
POINTL pt, PDWORD pdwEffect);
/// Implement important part of IDataObject
STDMETHODIMP GetData(FORMATETC *pFEIn, STGMEDIUM *pSTM);
// External Lexers
#ifdef SCI_LEXER
void SetLexerLanguage(const char *languageName);
void SetLexer(uptr_t wParam);
#endif
static bool Register(HINSTANCE hInstance_);
static bool Unregister();
friend class DropSource;
friend class DataObject;
friend class DropTarget;
bool DragIsRectangularOK(CLIPFORMAT fmt) {
return drag.rectangular && (fmt == cfColumnSelect);
}
};
HINSTANCE ScintillaWin::hInstance = 0;
ScintillaWin::ScintillaWin(HWND hwnd) {
lastKeyDownConsumed = false;
capturedMouse = false;
linesPerScroll = 0;
wheelDelta = 0; // Wheel delta from roll
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("MSDEVColumnSelect"));
wMain = hwnd;
dob.sci = this;
ds.sci = this;
dt.sci = this;
Initialise();
}
ScintillaWin::~ScintillaWin() {}
void ScintillaWin::Initialise() {
// Initialize COM. If the app has already done this it will have
// no effect. If the app hasnt, we really shouldnt ask them to call
// it just so this internal feature works.
::OleInitialize(NULL);
}
void ScintillaWin::Finalise() {
ScintillaBase::Finalise();
SetTicking(false);
::RevokeDragDrop(MainHWND());
::OleUninitialize();
}
HWND ScintillaWin::MainHWND() {
return reinterpret_cast<HWND>(wMain.GetID());
}
void ScintillaWin::StartDrag() {
DWORD dwEffect = 0;
dropWentOutside = true;
IDataObject *pDataObject = reinterpret_cast<IDataObject *>(&dob);
IDropSource *pDropSource = reinterpret_cast<IDropSource *>(&ds);
//Platform::DebugPrintf("About to DoDragDrop %x %x\n", pDataObject, pDropSource);
HRESULT hr = ::DoDragDrop(
pDataObject,
pDropSource,
DROPEFFECT_COPY | DROPEFFECT_MOVE, &dwEffect);
//Platform::DebugPrintf("DoDragDrop = %x\n", hr);
if (SUCCEEDED(hr)) {
if ((hr == DRAGDROP_S_DROP) && (dwEffect == DROPEFFECT_MOVE) && dropWentOutside) {
// Remove dragged out text
ClearSelection();
}
}
inDragDrop = false;
SetDragPosition(invalidPosition);
}
// Avoid warnings everywhere for old style casts by concentrating them here
static WORD LoWord(DWORD l) {
return LOWORD(l);
}
static WORD HiWord(DWORD l) {
return HIWORD(l);
}
static int InputCodePage() {
HKL inputLocale = ::GetKeyboardLayout(0);
LANGID inputLang = LOWORD(inputLocale);
char sCodePage[10];
int res = ::GetLocaleInfo(MAKELCID(inputLang, SORT_DEFAULT),
LOCALE_IDEFAULTANSICODEPAGE, sCodePage, sizeof(sCodePage));
if (!res)
return 0;
return atoi(sCodePage);
}
/** 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;
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
if (IsOcxCtrl) {
pps = reinterpret_cast<PAINTSTRUCT*>(wParam);
} else {
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(!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
sptr_t ret;
if ((lParam & GCS_RESULTSTR) && (IsNT())) {
HIMC hIMC = ::ImmGetContext(MainHWND());
if (hIMC) {
const int maxLenInputIME = 200;
wchar_t wcs[maxLenInputIME];
LONG bytes = ::ImmGetCompositionStringW(hIMC,
GCS_RESULTSTR, wcs, (maxLenInputIME-1)*2);
int wides = bytes / 2;
if (IsUnicodeMode()) {
char utfval[maxLenInputIME * 3];
unsigned int len = UTF8Length(wcs, wides);
UTF8FromUCS2(wcs, wides, utfval, len);
utfval[len] = '\0';
AddCharUTF(utfval, len);
} else {
char dbcsval[maxLenInputIME * 2];
int size = ::WideCharToMultiByte(InputCodePage(),
0, wcs, wides, dbcsval, sizeof(dbcsval) - 1, 0, 0);
for (int i=0; i<size; i++) {
AddChar(dbcsval[i]);
}
}
::ImmReleaseContext(MainHWND(), hIMC);
}
ret = 0;
} else {
ret = ::DefWindowProc(MainHWND(), WM_IME_COMPOSITION, wParam, lParam);
}
if ((lParam & GCS_RESULTSTR)) {
HIMC hIMC = ::ImmGetContext(MainHWND());
Point pos = LocationFromPosition(currentPos);
COMPOSITIONFORM CompForm;
CompForm.dwStyle = CFS_POINT;
CompForm.ptCurrentPos.x = pos.x;
CompForm.ptCurrentPos.y = pos.y;
::ImmSetCompositionWindow(hIMC, &CompForm);
::ImmReleaseContext(MainHWND(), hIMC);
DropCaret();
}
return ret;
#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_GETTEXTLENGTH: return SCI_GETTEXTLENGTH;
case WM_PASTE: return SCI_PASTE;
case WM_UNDO: return SCI_UNDO;
}
return iMessage;
}
sptr_t ScintillaWin::WndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
//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:
#ifdef TOTAL_CONTROL
if (LoWord(wParam) == idAutoComplete) {
int cmd = HiWord(wParam);
if (cmd == LBN_DBLCLK) {
AutoCompleteCompleted();
} else {
if (cmd != LBN_SETFOCUS)
::SetFocus(MainHWND());
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -