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