Files
wxWidgets/src/stc/ScintillaWX.cpp
New Pagodi d24f58cc55 Add AUTOCOMP_SELECTION_CHANGE event for wxSTC
The SCN_AUTOCSELECTIONCHANGE notification was added in Scintilla 4.0 to
notify an application when a new item is selected in an autocompletion
or user list. However the version of Scintilla currently used is 3.7.2,
so this potentially useful notification is not available.

These changes allow an event corresponding to this notification to be
generated completely in the wxWidgets portion of wxSTC. If the Scintilla
library is ever updated to 4.0 or later, only one method should need to
be modified let Scintilla generate the event instead.
2019-07-13 12:23:06 -05:00

1414 lines
43 KiB
C++

////////////////////////////////////////////////////////////////////////////
// Name: src/stc/ScintillaWX.cpp
// Purpose: A wxWidgets implementation of Scintilla. A class derived
// from ScintillaBase that uses the "wx platform" defined in
// PlatformWX.cxx This class is one end of a bridge between
// the wx world and the Scintilla world. It needs a peer
// object of type wxStyledTextCtrl to function.
//
// Author: Robin Dunn
//
// Created: 13-Jan-2000
// Copyright: (c) 2000 by Total Control Software
// Licence: wxWindows licence
/////////////////////////////////////////////////////////////////////////////
// For compilers that support precompilation, includes "wx.h".
#include "wx/wxprec.h"
#ifdef __BORLANDC__
#pragma hdrstop
#endif
#if wxUSE_STC
#ifndef WX_PRECOMP
#include "wx/scrolbar.h"
#include "wx/math.h"
#include "wx/menu.h"
#include "wx/timer.h"
#endif // WX_PRECOMP
#include "wx/textbuf.h"
#include "wx/dataobj.h"
#include "wx/clipbrd.h"
#include "wx/dnd.h"
#include "wx/image.h"
#include "wx/scopedarray.h"
#include "wx/dcbuffer.h"
#if !wxUSE_STD_CONTAINERS && !wxUSE_STD_IOSTREAM && !wxUSE_STD_STRING
#include "wx/beforestd.h"
#include <string>
#include "wx/afterstd.h"
#endif
#include "ScintillaWX.h"
#include "ExternalLexer.h"
#include "wx/stc/stc.h"
#include "wx/stc/private.h"
#include "PlatWX.h"
#ifdef __WXMSW__
// GetHwndOf()
#include "wx/msw/private.h"
#endif
#ifdef __WXGTK20__
#include <gdk/gdk.h>
#endif
//----------------------------------------------------------------------
// Helper classes
class wxSTCTimer : public wxTimer {
public:
wxSTCTimer(ScintillaWX* swx, ScintillaWX::TickReason reason) {
m_swx = swx;
m_reason = reason;
}
void Notify() wxOVERRIDE {
m_swx->TickFor(m_reason);
}
private:
ScintillaWX* m_swx;
ScintillaWX::TickReason m_reason;
};
#if wxUSE_DRAG_AND_DROP
bool wxSTCDropTarget::OnDropText(wxCoord x, wxCoord y, const wxString& data) {
return m_swx->DoDropText(x, y, data);
}
wxDragResult wxSTCDropTarget::OnEnter(wxCoord x, wxCoord y, wxDragResult def) {
return m_swx->DoDragEnter(x, y, def);
}
wxDragResult wxSTCDropTarget::OnDragOver(wxCoord x, wxCoord y, wxDragResult def) {
return m_swx->DoDragOver(x, y, def);
}
void wxSTCDropTarget::OnLeave() {
m_swx->DoDragLeave();
}
#endif // wxUSE_DRAG_AND_DROP
class wxSTCCallTip : public wxSTCPopupWindow {
public:
wxSTCCallTip(wxWindow* parent, CallTip* ct, ScintillaWX* swx) :
wxSTCPopupWindow(parent), m_ct(ct), m_swx(swx)
{
Bind(wxEVT_LEFT_DOWN, &wxSTCCallTip::OnLeftDown, this);
Bind(wxEVT_SIZE, &wxSTCCallTip::OnSize, this);
Bind(wxEVT_PAINT, &wxSTCCallTip::OnPaint, this);
#ifdef __WXMSW__
Bind(wxEVT_ERASE_BACKGROUND, &wxSTCCallTip::OnEraseBackground, this);
SetBackgroundStyle(wxBG_STYLE_ERASE);
#else
SetBackgroundStyle(wxBG_STYLE_PAINT);
#endif
SetName("wxSTCCallTip");
}
void DrawBack(const wxSize& size)
{
m_back = wxBitmap(size);
wxMemoryDC mem(m_back);
Surface* surfaceWindow = Surface::Allocate(m_swx->technology);
surfaceWindow->Init(&mem, m_ct->wDraw.GetID());
m_ct->PaintCT(surfaceWindow);
surfaceWindow->Release();
delete surfaceWindow;
}
virtual void Refresh(bool eraseBg=true, const wxRect *rect=NULL) wxOVERRIDE
{
if ( rect == NULL )
DrawBack(GetSize());
wxSTCPopupWindow::Refresh(eraseBg, rect);
}
void OnLeftDown(wxMouseEvent& event)
{
wxPoint pt = event.GetPosition();
Point p(pt.x, pt.y);
m_ct->MouseClick(p);
m_swx->CallTipClick();
}
void OnSize(wxSizeEvent& event)
{
DrawBack(event.GetSize());
event.Skip();
}
#ifdef __WXMSW__
void OnPaint(wxPaintEvent& WXUNUSED(evt))
{
wxRect upd = GetUpdateClientRect();
wxMemoryDC mem(m_back);
wxPaintDC dc(this);
dc.Blit(upd.GetX(), upd.GetY(), upd.GetWidth(), upd.GetHeight(), &mem,
upd.GetX(), upd.GetY());
}
void OnEraseBackground(wxEraseEvent& event)
{
event.GetDC()->DrawBitmap(m_back, 0, 0);
}
#else
void OnPaint(wxPaintEvent& WXUNUSED(evt))
{
wxAutoBufferedPaintDC dc(this);
dc.DrawBitmap(m_back, 0, 0);
}
#endif // __WXMSW__
private:
CallTip* m_ct;
ScintillaWX* m_swx;
wxBitmap m_back;
};
//----------------------------------------------------------------------
#if wxUSE_DATAOBJ
static wxTextFileType wxConvertEOLMode(int scintillaMode)
{
wxTextFileType type;
switch (scintillaMode) {
case wxSTC_EOL_CRLF:
type = wxTextFileType_Dos;
break;
case wxSTC_EOL_CR:
type = wxTextFileType_Mac;
break;
case wxSTC_EOL_LF:
type = wxTextFileType_Unix;
break;
default:
type = wxTextBuffer::typeDefault;
break;
}
return type;
}
#endif // wxUSE_DATAOBJ
//----------------------------------------------------------------------
// Constructor/Destructor
ScintillaWX::ScintillaWX(wxStyledTextCtrl* win) {
capturedMouse = false;
focusEvent = false;
wMain = win;
stc = win;
wheelVRotation = 0;
wheelHRotation = 0;
Initialise();
#ifdef __WXMSW__
sysCaretBitmap = 0;
sysCaretWidth = 0;
sysCaretHeight = 0;
#endif
#ifdef wxHAVE_STC_RECT_FORMAT
#if defined(__WXMSW__)
m_clipRectTextFormat = wxDataFormat(wxT("MSDEVColumnSelect"));
#elif defined(__WXGTK__)
m_clipRectTextFormat = wxDataFormat(wxT("SECONDARY"));
#else
#error "Must define rectangular text selection clipboard format."
#endif
#endif // wxHAVE_STC_RECT_FORMAT
//A timer is needed for each member of TickReason enum except tickPlatform
timers[tickCaret] = new wxSTCTimer(this,tickCaret);
timers[tickScroll] = new wxSTCTimer(this,tickScroll);
timers[tickWiden] = new wxSTCTimer(this,tickWiden);
timers[tickDwell] = new wxSTCTimer(this,tickDwell);
m_surfaceData = NULL;
}
ScintillaWX::~ScintillaWX() {
for ( TimersHash::iterator i=timers.begin(); i!=timers.end(); ++i ) {
delete i->second;
}
timers.clear();
if ( m_surfaceData != NULL ) {
delete m_surfaceData;
}
Finalise();
}
//----------------------------------------------------------------------
// base class virtuals
void ScintillaWX::Initialise() {
//ScintillaBase::Initialise();
#if wxUSE_DRAG_AND_DROP
dropTarget = new wxSTCDropTarget;
dropTarget->SetScintilla(this);
stc->SetDropTarget(dropTarget);
#endif // wxUSE_DRAG_AND_DROP
vs.extraFontFlag = true; // UseAntiAliasing
// Set up default OS X key mappings. Remember that SCI_CTRL stands for
// "Cmd" key here, as elsewhere in wx API, while SCI_ALT is the "Option"
// key (and "Ctrl" key, if we ever need it, should be represented by
// SCI_META).
#ifdef __WXMAC__
kmap.AssignCmdKey(SCK_LEFT, SCI_CTRL, SCI_VCHOME);
kmap.AssignCmdKey(SCK_RIGHT, SCI_CTRL, SCI_LINEEND);
kmap.AssignCmdKey(SCK_LEFT, SCI_ALT, SCI_WORDLEFT);
kmap.AssignCmdKey(SCK_RIGHT, SCI_ALT, SCI_WORDRIGHT);
kmap.AssignCmdKey(SCK_LEFT, SCI_ALT | SCI_SHIFT, SCI_WORDLEFTEXTEND);
kmap.AssignCmdKey(SCK_RIGHT, SCI_ALT | SCI_SHIFT, SCI_WORDRIGHTEXTEND);
kmap.AssignCmdKey(SCK_LEFT, SCI_CTRL | SCI_SHIFT, SCI_VCHOMEEXTEND);
kmap.AssignCmdKey(SCK_RIGHT, SCI_CTRL | SCI_SHIFT, SCI_LINEENDEXTEND);
kmap.AssignCmdKey(SCK_UP, SCI_CTRL | SCI_SHIFT, SCI_DOCUMENTSTARTEXTEND);
kmap.AssignCmdKey(SCK_DOWN, SCI_CTRL | SCI_SHIFT, SCI_DOCUMENTENDEXTEND);
kmap.AssignCmdKey(SCK_UP, SCI_CTRL, SCI_DOCUMENTSTART);
kmap.AssignCmdKey(SCK_DOWN, SCI_CTRL, SCI_DOCUMENTEND);
#endif // __WXMAC__
static_cast<ListBoxImpl*>(ac.lb)->SetListInfo(&listType, &(ac.posStart),
&(ac.startLen));
}
void ScintillaWX::Finalise() {
ScintillaBase::Finalise();
SetIdle(false);
DestroySystemCaret();
}
void ScintillaWX::StartDrag() {
#if wxUSE_DRAG_AND_DROP
wxString dragText = stc2wx(drag.Data(), drag.Length());
// Send an event to allow the drag text to be changed
wxStyledTextEvent evt(wxEVT_STC_START_DRAG, stc->GetId());
evt.SetEventObject(stc);
evt.SetString(dragText);
evt.SetDragFlags(wxDrag_DefaultMove);
evt.SetPosition(wxMin(stc->GetSelectionStart(),
stc->GetSelectionEnd()));
stc->GetEventHandler()->ProcessEvent(evt);
dragText = evt.GetString();
if ( !dragText.empty() ) {
wxDropSource source(stc);
wxTextDataObject data(dragText);
wxDragResult result;
source.SetData(data);
dropWentOutside = true;
inDragDrop = ddDragging;
result = source.DoDragDrop(evt.GetDragFlags());
if (result == wxDragMove && dropWentOutside)
ClearSelection();
inDragDrop = ddNone;
SetDragPosition(SelectionPosition(invalidPosition));
}
#endif // wxUSE_DRAG_AND_DROP
}
bool ScintillaWX::SetIdle(bool on) {
if (idler.state != on) {
// connect or disconnect the EVT_IDLE handler
if (on)
stc->Bind(wxEVT_IDLE, &wxStyledTextCtrl::OnIdle, stc);
else
stc->Unbind(wxEVT_IDLE, &wxStyledTextCtrl::OnIdle, stc);
idler.state = on;
}
return idler.state;
}
void ScintillaWX::SetMouseCapture(bool on) {
if (mouseDownCaptures) {
if (on && !capturedMouse)
stc->CaptureMouse();
else if (!on && capturedMouse && stc->HasCapture())
stc->ReleaseMouse();
capturedMouse = on;
}
}
bool ScintillaWX::HaveMouseCapture() {
return capturedMouse;
}
void ScintillaWX::ScrollText(int linesToMove) {
int dy = vs.lineHeight * (linesToMove);
stc->ScrollWindow(0, dy);
}
void ScintillaWX::SetVerticalScrollPos() {
if (stc->m_vScrollBar == NULL) { // Use built-in scrollbar
stc->SetScrollPos(wxVERTICAL, topLine);
}
else { // otherwise use the one that's been given to us
stc->m_vScrollBar->SetThumbPosition(topLine);
}
}
void ScintillaWX::SetHorizontalScrollPos() {
if (stc->m_hScrollBar == NULL) { // Use built-in scrollbar
stc->SetScrollPos(wxHORIZONTAL, xOffset);
}
else { // otherwise use the one that's been given to us
stc->m_hScrollBar->SetThumbPosition(xOffset);
}
}
const int H_SCROLL_STEP = 20;
bool ScintillaWX::ModifyScrollBars(int nMax, int nPage) {
bool modified = false;
int vertEnd = nMax+1;
if (!verticalScrollBarVisible)
nPage = vertEnd + 1;
// Check the vertical scrollbar
if (stc->m_vScrollBar == NULL) { // Use built-in scrollbar
int sbMax = stc->GetScrollRange(wxVERTICAL);
int sbThumb = stc->GetScrollThumb(wxVERTICAL);
int sbPos = stc->GetScrollPos(wxVERTICAL);
if (sbMax != vertEnd || sbThumb != nPage) {
stc->SetScrollbar(wxVERTICAL, sbPos, nPage, vertEnd);
modified = true;
}
}
else { // otherwise use the one that's been given to us
int sbMax = stc->m_vScrollBar->GetRange();
int sbPage = stc->m_vScrollBar->GetPageSize();
int sbPos = stc->m_vScrollBar->GetThumbPosition();
if (sbMax != vertEnd || sbPage != nPage) {
stc->m_vScrollBar->SetScrollbar(sbPos, nPage, vertEnd, nPage);
modified = true;
}
}
// Check the horizontal scrollbar
PRectangle rcText = GetTextRectangle();
int horizEnd = scrollWidth;
if (horizEnd < 0)
horizEnd = 0;
int pageWidth = static_cast<int>(rcText.Width());
if (!horizontalScrollBarVisible || Wrapping())
pageWidth = horizEnd + 1;
if (stc->m_hScrollBar == NULL) { // Use built-in scrollbar
int sbMax = stc->GetScrollRange(wxHORIZONTAL);
int sbThumb = stc->GetScrollThumb(wxHORIZONTAL);
int sbPos = stc->GetScrollPos(wxHORIZONTAL);
if ((sbMax != horizEnd) || (sbThumb != pageWidth)) {
stc->SetScrollbar(wxHORIZONTAL, sbPos, pageWidth, horizEnd);
modified = true;
if (scrollWidth < pageWidth) {
HorizontalScrollTo(0);
}
}
}
else { // otherwise use the one that's been given to us
int sbMax = stc->m_hScrollBar->GetRange();
int sbThumb = stc->m_hScrollBar->GetPageSize();
int sbPos = stc->m_hScrollBar->GetThumbPosition();
if ((sbMax != horizEnd) || (sbThumb != pageWidth)) {
stc->m_hScrollBar->SetScrollbar(sbPos, pageWidth, horizEnd, pageWidth);
modified = true;
if (scrollWidth < pageWidth) {
HorizontalScrollTo(0);
}
}
}
return modified;
}
void ScintillaWX::NotifyChange() {
stc->NotifyChange();
}
void ScintillaWX::NotifyParent(SCNotification scn) {
stc->NotifyParent(&scn);
}
// This method is overloaded from ScintillaBase in order to prevent the
// AutoComplete window from being destroyed when it gets the focus. There is
// a side effect that the AutoComp will also not be destroyed when switching
// to another window, but I think that is okay.
void ScintillaWX::CancelModes() {
if (! focusEvent) {
AutoCompleteCancel();
ct.CallTipCancel();
}
Editor::CancelModes();
}
void ScintillaWX::Copy() {
if (!sel.Empty()) {
SelectionText st;
CopySelectionRange(&st);
CopyToClipboard(st);
}
}
void ScintillaWX::Paste() {
pdoc->BeginUndoAction();
ClearSelection(multiPasteMode == SC_MULTIPASTE_EACH);
#if wxUSE_CLIPBOARD
wxTextDataObject data;
bool gotData = false;
bool isRectangularClipboard = false;
wxTheClipboard->UsePrimarySelection(false);
if (wxTheClipboard->Open()) {
#ifdef wxHAVE_STC_RECT_FORMAT
isRectangularClipboard = wxTheClipboard->IsSupported(m_clipRectTextFormat);
#endif
gotData = wxTheClipboard->GetData(data);
wxTheClipboard->Close();
}
if (gotData) {
wxString text = wxTextBuffer::Translate(data.GetText(),
wxConvertEOLMode(pdoc->eolMode));
// Send an event to allow the pasted text to be changed
wxStyledTextEvent evt(wxEVT_STC_CLIPBOARD_PASTE, stc->GetId());
evt.SetEventObject(stc);
evt.SetPosition(sel.MainCaret());
evt.SetString(text);
stc->GetEventHandler()->ProcessEvent(evt);
const wxCharBuffer buf(wx2stc(evt.GetString()));
#if wxUSE_UNICODE
// free up the old character buffer in case the text is real big
text.clear();
data.SetText(text);
#endif
const size_t len = buf.length();
SelectionPosition selStart = sel.IsRectangular() ?
sel.Rectangular().Start() :
sel.Range(sel.Main()).Start();
// call the appropriate scintilla paste method if the
// depending on whether the text was copied FROM a rectangular selection
// or not.
if (isRectangularClipboard)
{
PasteRectangular(selStart, buf, len);
}
else
{
InsertPaste(buf, len);
}
}
#endif // wxUSE_CLIPBOARD
pdoc->EndUndoAction();
NotifyChange();
Redraw();
}
void ScintillaWX::CopyToClipboard(const SelectionText& st) {
#if wxUSE_CLIPBOARD
if ( !st.LengthWithTerminator() )
return;
// Send an event to allow the copied text to be changed
wxStyledTextEvent evt(wxEVT_STC_CLIPBOARD_COPY, stc->GetId());
evt.SetEventObject(stc);
evt.SetString(wxTextBuffer::Translate(stc2wx(st.Data(), st.Length())));
stc->GetEventHandler()->ProcessEvent(evt);
wxTheClipboard->UsePrimarySelection(false);
if (wxTheClipboard->Open()) {
wxString text = evt.GetString();
#ifdef wxHAVE_STC_RECT_FORMAT
if (st.rectangular)
{
// when copying the text to the clipboard, add extra meta-data that
// tells the Paste() method that the user copied a rectangular
// block of text, as opposed to a stream of text.
wxDataObjectComposite* composite = new wxDataObjectComposite();
composite->Add(new wxTextDataObject(text), true);
composite->Add(new wxCustomDataObject(m_clipRectTextFormat));
wxTheClipboard->SetData(composite);
}
else
#endif // wxHAVE_STC_RECT_FORMAT
wxTheClipboard->SetData(new wxTextDataObject(text));
wxTheClipboard->Close();
}
#else
wxUnusedVar(st);
#endif // wxUSE_CLIPBOARD
}
bool ScintillaWX::CanPaste() {
#if wxUSE_CLIPBOARD
bool canPaste = false;
bool didOpen;
if (Editor::CanPaste()) {
wxTheClipboard->UsePrimarySelection(false);
didOpen = !wxTheClipboard->IsOpened();
if ( didOpen )
wxTheClipboard->Open();
if (wxTheClipboard->IsOpened()) {
canPaste = wxTheClipboard->IsSupported(wxUSE_UNICODE ? wxDF_UNICODETEXT : wxDF_TEXT);
if (didOpen)
wxTheClipboard->Close();
}
}
return canPaste;
#else
return false;
#endif // wxUSE_CLIPBOARD
}
void ScintillaWX::CreateCallTipWindow(PRectangle) {
if (! ct.wCallTip.Created() ) {
ct.wCallTip = new wxSTCCallTip(stc, &ct, this);
ct.wDraw = ct.wCallTip;
}
}
void ScintillaWX::AddToPopUp(const char *label, int cmd, bool enabled) {
if (!label[0])
((wxMenu*)popup.GetID())->AppendSeparator();
else
((wxMenu*)popup.GetID())->Append(cmd, wxGetTranslation(stc2wx(label)));
if (!enabled)
((wxMenu*)popup.GetID())->Enable(cmd, enabled);
}
// This is called by the Editor base class whenever something is selected.
// For wxGTK we can put this text in the primary selection and then other apps
// can paste with the middle button.
void ScintillaWX::ClaimSelection() {
#ifdef __WXGTK__
#if wxUSE_CLIPBOARD
// Put the selected text in the PRIMARY selection
if (!sel.Empty()) {
SelectionText st;
CopySelectionRange(&st);
wxTheClipboard->UsePrimarySelection(true);
if (wxTheClipboard->Open()) {
wxString text = stc2wx(st.Data(), st.Length());
wxTheClipboard->SetData(new wxTextDataObject(text));
wxTheClipboard->Close();
}
wxTheClipboard->UsePrimarySelection(false);
}
#endif // wxUSE_CLIPBOARD
#endif
}
void ScintillaWX::UpdateSystemCaret() {
#ifdef __WXMSW__
if (hasFocus) {
if (HasCaretSizeChanged()) {
DestroySystemCaret();
CreateSystemCaret();
}
Point pos = PointMainCaret();
::SetCaretPos(wxRound(pos.x), wxRound(pos.y));
}
#endif
}
bool ScintillaWX::HasCaretSizeChanged() {
#ifdef __WXMSW__
if ( (vs.caretWidth && (sysCaretWidth != vs.caretWidth))
|| (vs.lineHeight && (sysCaretHeight != vs.lineHeight)) ) {
return true;
}
#endif
return false;
}
bool ScintillaWX::CreateSystemCaret() {
#ifdef __WXMSW__
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(GetHwndOf(stc), sysCaretBitmap,
sysCaretWidth, sysCaretHeight);
::ShowCaret(GetHwndOf(stc));
return retval != 0;
#else
return false;
#endif
}
bool ScintillaWX::DestroySystemCaret() {
#ifdef __WXMSW__
if (sysCaretBitmap)
{
::HideCaret(GetHwndOf(stc));
BOOL retval = ::DestroyCaret();
if (sysCaretBitmap) {
::DeleteObject(sysCaretBitmap);
sysCaretBitmap = 0;
}
return retval != 0;
}
else
return false;
#else
return false;
#endif
}
bool ScintillaWX::FineTickerAvailable() {
return true;
}
bool ScintillaWX::FineTickerRunning(TickReason reason) {
bool running = false;
TimersHash::iterator i = timers.find(reason);
wxASSERT_MSG( i != timers.end(), "At least one TickReason is missing a timer.");
if ( i != timers.end() ) {
running = i->second->IsRunning();
}
return running;
}
void ScintillaWX::FineTickerStart(TickReason reason, int millis,
int WXUNUSED(tolerance)) {
TimersHash::iterator i = timers.find(reason);
wxASSERT_MSG( i != timers.end(), "At least one TickReason is missing a timer." );
if ( i != timers.end() ) {
i->second->Start(millis);
}
}
void ScintillaWX::FineTickerCancel(TickReason reason) {
TimersHash::iterator i = timers.find(reason);
wxASSERT_MSG( i != timers.end(), "At least one TickReason is missing a timer." );
if ( i != timers.end() ) {
i->second->Stop();
}
}
//----------------------------------------------------------------------
sptr_t ScintillaWX::DefWndProc(unsigned int /*iMessage*/, uptr_t /*wParam*/, sptr_t /*lParam*/) {
return 0;
}
sptr_t ScintillaWX::WndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
switch (iMessage) {
#if 0 // TODO: check this
case SCI_CALLTIPSHOW: {
// NOTE: This is copied here from scintilla/src/ScintillaBase.cxx
// because of the little tweak that needs done below for wxGTK.
// When updating new versions double check that this is still
// needed, and that any new code there is copied here too.
Point pt = LocationFromPosition(wParam);
char* defn = reinterpret_cast<char *>(lParam);
AutoCompleteCancel();
pt.y += vs.lineHeight;
int ctStyle = ct.UseStyleCallTip() ? STYLE_CALLTIP : STYLE_DEFAULT;
if (ct.UseStyleCallTip())
{
ct.SetForeBack(vs.styles[STYLE_CALLTIP].fore, vs.styles[STYLE_CALLTIP].back);
}
int caretMain = sel.MainCaret();
PRectangle rc = ct.CallTipStart(caretMain, pt,
defn,
vs.styles[ctStyle].fontName,
vs.styles[ctStyle].sizeZoomed,
CodePage(),
vs.styles[ctStyle].characterSet,
wMain);
// If the call-tip window would be out of the client
// space, adjust so it displays above the text.
PRectangle rcClient = GetClientRectangle();
if (rc.bottom > rcClient.bottom) {
#ifdef __WXGTK__
int offset = int(vs.lineHeight * 1.25) + rc.Height();
#else
int offset = vs.lineHeight + rc.Height();
#endif
rc.top -= offset;
rc.bottom -= offset;
}
// Now display the window.
CreateCallTipWindow(rc);
ct.wCallTip.SetPositionRelative(rc, wMain);
ct.wCallTip.Show();
break;
}
#endif
#if defined(__WXMSW__) && wxUSE_GRAPHICS_DIRECT2D
case SCI_SETTECHNOLOGY:
if ((wParam == SC_TECHNOLOGY_DEFAULT) || (wParam == SC_TECHNOLOGY_DIRECTWRITE)) {
if (technology != static_cast<int>(wParam)) {
SurfaceDataD2D* newSurfaceData(NULL);
if (static_cast<int>(wParam) > SC_TECHNOLOGY_DEFAULT) {
newSurfaceData = new SurfaceDataD2D(this);
if (!newSurfaceData->Initialised()) {
// Failed to load Direct2D or DirectWrite so no effect
delete newSurfaceData;
return 0;
}
}
technology = static_cast<int>(wParam);
if ( m_surfaceData ) {
delete m_surfaceData;
}
m_surfaceData = newSurfaceData;
// Invalidate all cached information including layout.
DropGraphics(true);
InvalidateStyleRedraw();
}
}
break;
#endif
#ifdef SCI_LEXER
case SCI_LOADLEXERLIBRARY:
LexerManager::GetInstance()->Load((const char*)lParam);
break;
#endif
case SCI_GETDIRECTFUNCTION:
return reinterpret_cast<sptr_t>(DirectFunction);
case SCI_GETDIRECTPOINTER:
return reinterpret_cast<sptr_t>(this);
default:
return ScintillaBase::WndProc(iMessage, wParam, lParam);
}
return 0;
}
//----------------------------------------------------------------------
// Event delegates
void ScintillaWX::DoPaint(wxDC* dc, wxRect rect) {
paintState = painting;
AutoSurface surfaceWindow(dc, this);
if (surfaceWindow) {
rcPaint = PRectangleFromwxRect(rect);
PRectangle rcClient = GetClientRectangle();
paintingAllText = rcPaint.Contains(rcClient);
ClipChildren(*dc, rcPaint);
Paint(surfaceWindow, rcPaint);
surfaceWindow->Release();
}
if (paintState == paintAbandoned) {
// Painting area was insufficient to cover new styling or brace
// highlight positions. So trigger a new paint event that will
// repaint the whole window.
stc->Refresh(false);
#if defined(__WXOSX__)
// On Mac we also need to finish the current paint to make sure that
// everything is on the screen that needs to be there between now and
// when the next paint event arrives.
FullPaintDC(dc);
#endif
}
paintState = notPainting;
}
// Force the whole window to be repainted
void ScintillaWX::FullPaint() {
stc->Refresh(false);
stc->Update();
}
void ScintillaWX::FullPaintDC(wxDC* dc) {
paintState = painting;
rcPaint = GetClientRectangle();
paintingAllText = true;
AutoSurface surfaceWindow(dc, this);
if (surfaceWindow) {
Paint(surfaceWindow, rcPaint);
surfaceWindow->Release();
}
paintState = notPainting;
}
void ScintillaWX::DoHScroll(int type, int pos) {
int xPos = xOffset;
PRectangle rcText = GetTextRectangle();
int pageWidth = wxRound(rcText.Width() * 2 / 3);
if (type == wxEVT_SCROLLWIN_LINEUP || type == wxEVT_SCROLL_LINEUP)
xPos -= H_SCROLL_STEP;
else if (type == wxEVT_SCROLLWIN_LINEDOWN || type == wxEVT_SCROLL_LINEDOWN)
xPos += H_SCROLL_STEP;
else if (type == wxEVT_SCROLLWIN_PAGEUP || type == wxEVT_SCROLL_PAGEUP)
xPos -= pageWidth;
else if (type == wxEVT_SCROLLWIN_PAGEDOWN || type == wxEVT_SCROLL_PAGEDOWN) {
xPos += pageWidth;
if (xPos > scrollWidth - rcText.Width()) {
xPos = wxRound(scrollWidth - rcText.Width());
}
}
else if (type == wxEVT_SCROLLWIN_TOP || type == wxEVT_SCROLL_TOP)
xPos = 0;
else if (type == wxEVT_SCROLLWIN_BOTTOM || type == wxEVT_SCROLL_BOTTOM)
xPos = scrollWidth;
else if (type == wxEVT_SCROLLWIN_THUMBTRACK || type == wxEVT_SCROLL_THUMBTRACK)
xPos = pos;
HorizontalScrollTo(xPos);
}
void ScintillaWX::DoVScroll(int type, int pos) {
int topLineNew = topLine;
if (type == wxEVT_SCROLLWIN_LINEUP || type == wxEVT_SCROLL_LINEUP)
topLineNew -= 1;
else if (type == wxEVT_SCROLLWIN_LINEDOWN || type == wxEVT_SCROLL_LINEDOWN)
topLineNew += 1;
else if (type == wxEVT_SCROLLWIN_PAGEUP || type == wxEVT_SCROLL_PAGEUP)
topLineNew -= LinesToScroll();
else if (type == wxEVT_SCROLLWIN_PAGEDOWN || type == wxEVT_SCROLL_PAGEDOWN)
topLineNew += LinesToScroll();
else if (type == wxEVT_SCROLLWIN_TOP || type == wxEVT_SCROLL_TOP)
topLineNew = 0;
else if (type == wxEVT_SCROLLWIN_BOTTOM || type == wxEVT_SCROLL_BOTTOM)
topLineNew = MaxScrollPos();
else if (type == wxEVT_SCROLLWIN_THUMBTRACK || type == wxEVT_SCROLL_THUMBTRACK)
topLineNew = pos;
ScrollTo(topLineNew);
}
void ScintillaWX::DoMouseWheel(wxMouseWheelAxis axis, int rotation, int delta,
int linesPerAction, int columnsPerAction,
bool ctrlDown, bool isPageScroll) {
int topLineNew = topLine;
int lines;
int xPos = xOffset;
int pixels;
if (axis == wxMOUSE_WHEEL_HORIZONTAL) {
wheelHRotation += wxRound(rotation * (columnsPerAction * vs.spaceWidth));
pixels = wheelHRotation / delta;
wheelHRotation -= pixels * delta;
if (pixels != 0) {
xPos += pixels;
PRectangle rcText = GetTextRectangle();
if (xPos > scrollWidth - rcText.Width()) {
xPos = wxRound(scrollWidth - rcText.Width());
}
HorizontalScrollTo(xPos);
}
}
else if (ctrlDown) { // Zoom the fonts if Ctrl key down
if (rotation > 0) {
KeyCommand(SCI_ZOOMIN);
}
else {
KeyCommand(SCI_ZOOMOUT);
}
}
else { // otherwise just scroll the window
if ( !delta )
delta = 120;
wheelVRotation += rotation;
lines = wheelVRotation / delta;
wheelVRotation -= lines * delta;
if (lines != 0) {
if (isPageScroll)
lines = lines * LinesOnScreen(); // lines is either +1 or -1
else
lines *= linesPerAction;
topLineNew -= lines;
ScrollTo(topLineNew);
}
}
}
void ScintillaWX::DoSize(int WXUNUSED(width), int WXUNUSED(height)) {
ChangeSize();
}
void ScintillaWX::DoLoseFocus(){
focusEvent = true;
SetFocusState(false);
focusEvent = false;
DestroySystemCaret();
}
void ScintillaWX::DoGainFocus(){
focusEvent = true;
SetFocusState(true);
focusEvent = false;
DestroySystemCaret();
CreateSystemCaret();
}
void ScintillaWX::DoSysColourChange() {
InvalidateStyleData();
}
void ScintillaWX::DoLeftButtonDown(Point pt, unsigned int curTime, bool shift, bool ctrl, bool alt) {
ButtonDown(pt, curTime, shift, ctrl, alt);
}
void ScintillaWX::DoRightButtonDown(Point pt, unsigned int curTime, bool shift, bool ctrl, bool alt) {
if (!PointInSelection(pt)) {
CancelModes();
SetEmptySelection(PositionFromLocation(pt));
}
RightButtonDownWithModifiers(pt, curTime, ModifierFlags(shift, ctrl, alt));
}
void ScintillaWX::DoLeftButtonUp(Point pt, unsigned int curTime, bool ctrl) {
ButtonUp(pt, curTime, ctrl);
}
void ScintillaWX::DoLeftButtonMove(Point pt) {
ButtonMove(pt);
}
#ifdef __WXGTK__
void ScintillaWX::DoMiddleButtonUp(Point pt) {
// Set the current position to the mouse click point and
// then paste in the PRIMARY selection, if any. wxGTK only.
int newPos = PositionFromLocation(pt);
MovePositionTo(newPos, Selection::noSel, true);
#if wxUSE_CLIPBOARD
pdoc->BeginUndoAction();
wxTextDataObject data;
bool gotData = false;
wxTheClipboard->UsePrimarySelection(true);
if (wxTheClipboard->Open()) {
gotData = wxTheClipboard->GetData(data);
wxTheClipboard->Close();
}
wxTheClipboard->UsePrimarySelection(false);
if (gotData) {
wxString text = wxTextBuffer::Translate(data.GetText(),
wxConvertEOLMode(pdoc->eolMode));
const wxCharBuffer buf(wx2stc(text));
const size_t len = buf.length();
int caretMain = sel.MainCaret();
pdoc->InsertString(caretMain, buf, len);
SetEmptySelection(caretMain + len);
}
pdoc->EndUndoAction();
NotifyChange();
Redraw();
#endif // wxUSE_CLIPBOARD
ShowCaretAtCurrentPosition();
EnsureCaretVisible();
}
#else
void ScintillaWX::DoMiddleButtonUp(Point WXUNUSED(pt)) {
}
#endif
void ScintillaWX::DoAddChar(int key) {
#if wxUSE_UNICODE
wxChar wszChars[2];
wszChars[0] = (wxChar)key;
wszChars[1] = 0;
const wxCharBuffer buf(wx2stc(wszChars));
AddCharUTF(buf, buf.length());
#else
AddChar((char)key);
#endif
}
int ScintillaWX::DoKeyDown(const wxKeyEvent& evt, bool* consumed)
{
int key = evt.GetKeyCode();
if (evt.RawControlDown() && key >= 1 && key <= 26 && key != WXK_BACK)
key += 'A' - 1;
switch (key) {
case WXK_DOWN: key = SCK_DOWN; break;
case WXK_UP: key = SCK_UP; break;
case WXK_LEFT: key = SCK_LEFT; break;
case WXK_RIGHT: key = SCK_RIGHT; break;
case WXK_HOME: key = SCK_HOME; break;
case WXK_END: key = SCK_END; break;
case WXK_PAGEUP: key = SCK_PRIOR; break;
case WXK_PAGEDOWN: key = SCK_NEXT; break;
case WXK_NUMPAD_DOWN: key = SCK_DOWN; break;
case WXK_NUMPAD_UP: key = SCK_UP; break;
case WXK_NUMPAD_LEFT: key = SCK_LEFT; break;
case WXK_NUMPAD_RIGHT: key = SCK_RIGHT; break;
case WXK_NUMPAD_HOME: key = SCK_HOME; break;
case WXK_NUMPAD_END: key = SCK_END; break;
case WXK_NUMPAD_PAGEUP: key = SCK_PRIOR; break;
case WXK_NUMPAD_PAGEDOWN: key = SCK_NEXT; break;
case WXK_NUMPAD_DELETE: key = SCK_DELETE; break;
case WXK_NUMPAD_INSERT: key = SCK_INSERT; break;
case WXK_DELETE: key = SCK_DELETE; break;
case WXK_INSERT: key = SCK_INSERT; break;
case WXK_ESCAPE: key = SCK_ESCAPE; break;
case WXK_BACK: key = SCK_BACK; break;
case WXK_TAB: key = SCK_TAB; break;
case WXK_NUMPAD_ENTER: wxFALLTHROUGH;
case WXK_RETURN: key = SCK_RETURN; break;
case WXK_ADD: wxFALLTHROUGH;
case WXK_NUMPAD_ADD: key = SCK_ADD; break;
case WXK_SUBTRACT: wxFALLTHROUGH;
case WXK_NUMPAD_SUBTRACT: key = SCK_SUBTRACT; break;
case WXK_DIVIDE: wxFALLTHROUGH;
case WXK_NUMPAD_DIVIDE: key = SCK_DIVIDE; break;
case WXK_CONTROL: key = 0; break;
case WXK_ALT: key = 0; break;
case WXK_SHIFT: key = 0; break;
case WXK_MENU: key = SCK_MENU; break;
case WXK_NONE:
#ifdef __WXGTK20__
if (evt.RawControlDown())
{
// To allow Ctrl-key shortcuts to work with non-Latin keyboard layouts,
// look for any available layout that would produce an ASCII letter for
// the given hardware keycode
const unsigned keycode = evt.GetRawKeyFlags();
GdkKeymap* keymap = gdk_keymap_get_for_display(gdk_display_get_default());
GdkKeymapKey keymapKey = { keycode, 0, 1 };
do {
const unsigned keyval = gdk_keymap_lookup_key(keymap, &keymapKey);
if (keyval >= 'A' && keyval <= 'Z')
{
key = keyval;
break;
}
keymapKey.group++;
} while (keymapKey.group < 4);
if (key == WXK_NONE)
{
// There may be no keyboard layouts with Latin keys available,
// fall back to a hard-coded mapping for the common pc105
static const char keycodeToKeyval[] = {
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I',
'O', 'P', 0, 0, 0, 0, 'A', 'S',
'D', 'F', 'G', 'H', 'J', 'K', 'L', 0,
0, 0, 0, 0, 'Z', 'X', 'C', 'V',
'B', 'N', 'M'
};
if (keycode < sizeof(keycodeToKeyval))
key = keycodeToKeyval[keycode];
}
}
if (key == WXK_NONE)
#endif
{
// This is a Unicode character not representable in Latin-1 or some key
// without key code at all (e.g. dead key or VK_PROCESSKEY under MSW).
if (consumed)
*consumed = false;
return 0;
}
}
int rv = KeyDownWithModifiers
(
key,
ModifierFlags
(
evt.ShiftDown(),
evt.ControlDown(),
evt.AltDown(),
// Under Mac, ControlDown() returns the status of Cmd key,
// so we sneak the status of the real Ctrl key via SCI_META
// but we shouldn't be doing this elsewhere.
#ifdef __WXMAC__
evt.RawControlDown()
#else
0
#endif
),
consumed
);
if (key)
return rv;
else
return 1;
}
void ScintillaWX::DoCommand(int ID) {
Command(ID);
}
bool ScintillaWX::DoContextMenu(Point pt) {
if (ShouldDisplayPopup(pt))
{
// To prevent generating EVT_MOUSE_CAPTURE_LOST.
if ( HaveMouseCapture() ) {
SetMouseCapture(false);
}
ContextMenu(pt);
return true;
}
return false;
}
void ScintillaWX::DoOnListBox() {
AutoCompleteCompleted(0, SC_AC_COMMAND);
}
void ScintillaWX::DoMouseCaptureLost()
{
capturedMouse = false;
}
void ScintillaWX::DoOnIdle(wxIdleEvent& evt) {
if ( Idle() )
evt.RequestMore();
else
SetIdle(false);
}
//----------------------------------------------------------------------
#if wxUSE_DRAG_AND_DROP
bool ScintillaWX::DoDropText(long x, long y, const wxString& data) {
SetDragPosition(SelectionPosition(invalidPosition));
wxString text = wxTextBuffer::Translate(data,
wxConvertEOLMode(pdoc->eolMode));
// Send an event to allow the drag details to be changed
wxStyledTextEvent evt(wxEVT_STC_DO_DROP, stc->GetId());
evt.SetEventObject(stc);
evt.SetDragResult(dragResult);
evt.SetX(x);
evt.SetY(y);
evt.SetPosition(PositionFromLocation(Point(x,y)));
evt.SetString(text);
stc->GetEventHandler()->ProcessEvent(evt);
dragResult = evt.GetDragResult();
if (dragResult == wxDragMove || dragResult == wxDragCopy) {
DropAt(SelectionPosition(evt.GetPosition()),
wx2stc(evt.GetString()),
dragResult == wxDragMove,
false); // TODO: rectangular?
return true;
}
return false;
}
wxDragResult ScintillaWX::DoDragEnter(wxCoord WXUNUSED(x), wxCoord WXUNUSED(y), wxDragResult def) {
dragResult = def;
return dragResult;
}
wxDragResult ScintillaWX::DoDragOver(wxCoord x, wxCoord y, wxDragResult def) {
SetDragPosition(SelectionPosition(PositionFromLocation(Point(x, y))));
// Send an event to allow the drag result to be changed
wxStyledTextEvent evt(wxEVT_STC_DRAG_OVER, stc->GetId());
evt.SetEventObject(stc);
evt.SetDragResult(def);
evt.SetX(x);
evt.SetY(y);
evt.SetPosition(PositionFromLocation(Point(x,y)));
stc->GetEventHandler()->ProcessEvent(evt);
dragResult = evt.GetDragResult();
return dragResult;
}
void ScintillaWX::DoDragLeave() {
SetDragPosition(SelectionPosition(invalidPosition));
}
#endif // wxUSE_DRAG_AND_DROP
//----------------------------------------------------------------------
void ScintillaWX::DoScrollToLine(int line) {
ScrollTo(line);
}
void ScintillaWX::DoScrollToColumn(int column) {
HorizontalScrollTo(wxRound(column * vs.spaceWidth));
}
// wxGTK doesn't appear to need this explicit clipping code any longer, but I
// will leave it here commented out for a while just in case...
void ScintillaWX::ClipChildren(wxDC& WXUNUSED(dc), PRectangle WXUNUSED(rect))
{
// wxRegion rgn(wxRectFromPRectangle(rect));
// if (ac.Active()) {
// wxRect childRect = ((wxWindow*)ac.lb->GetID())->GetRect();
// rgn.Subtract(childRect);
// }
// if (ct.inCallTipMode) {
// wxSTCCallTip* tip = (wxSTCCallTip*)ct.wCallTip.GetID();
// wxRect childRect = tip->GetRect();
// #if wxUSE_POPUPWIN
// childRect.SetPosition(tip->GetMyPosition());
// #endif
// rgn.Subtract(childRect);
// }
// dc.SetClippingRegion(rgn);
}
void ScintillaWX::SetUseAntiAliasing(bool useAA) {
vs.extraFontFlag = useAA;
InvalidateStyleRedraw();
}
bool ScintillaWX::GetUseAntiAliasing() {
return vs.extraFontFlag != 0;
}
void ScintillaWX::DoMarkerDefineBitmap(int markerNumber, const wxBitmap& bmp) {
if ( 0 <= markerNumber && markerNumber <= MARKER_MAX) {
// Build an RGBA buffer from bmp.
const int totalPixels = bmp.GetWidth() * bmp.GetHeight();
wxScopedArray<unsigned char> rgba(4*bmp.GetWidth()*bmp.GetHeight());
wxImage img = bmp.ConvertToImage();
int curRGBALoc = 0, curDataLoc = 0, curAlphaLoc = 0;
if ( img.HasMask() ) {
for ( int y = 0; y < bmp.GetHeight(); ++y ) {
for ( int x = 0 ; x < bmp.GetWidth(); ++x ) {
rgba[curRGBALoc++] = img.GetData()[curDataLoc++];
rgba[curRGBALoc++] = img.GetData()[curDataLoc++];
rgba[curRGBALoc++] = img.GetData()[curDataLoc++];
rgba[curRGBALoc++] = img.IsTransparent(x,y)
? wxALPHA_TRANSPARENT : wxALPHA_OPAQUE ;
}
}
}
else if ( img.HasAlpha() ) {
for ( int i = 0; i < totalPixels; ++i ) {
rgba[curRGBALoc++] = img.GetData()[curDataLoc++];
rgba[curRGBALoc++] = img.GetData()[curDataLoc++];
rgba[curRGBALoc++] = img.GetData()[curDataLoc++];
rgba[curRGBALoc++] = img.GetAlpha()[curAlphaLoc++];
}
}
else {
for ( int i = 0; i < totalPixels; ++i ) {
rgba[curRGBALoc++] = img.GetData()[curDataLoc++];
rgba[curRGBALoc++] = img.GetData()[curDataLoc++];
rgba[curRGBALoc++] = img.GetData()[curDataLoc++];
rgba[curRGBALoc++] = wxALPHA_OPAQUE ;
}
}
// Now follow the same procedure used for handling the
// SCI_MARKERDEFINERGBAIMAGE message, except use the bitmap's width and
// height instead of the values stored in sizeRGBAImage.
Point bitmapSize = Point::FromInts(bmp.GetWidth(), bmp.GetHeight());
vs.markers[markerNumber].SetRGBAImage(bitmapSize, 1.0f, rgba.get());
vs.CalcLargestMarkerHeight();
}
InvalidateStyleData();
RedrawSelMargin();
}
void ScintillaWX::DoRegisterImage(int type, const wxBitmap& bmp) {
static_cast<ListBoxImpl*>(ac.lb)->RegisterImageHelper(type, bmp);
}
sptr_t ScintillaWX::DirectFunction(
ScintillaWX* swx, unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
return swx->WndProc(iMessage, wParam, lParam);
}
//----------------------------------------------------------------------
//----------------------------------------------------------------------
#endif // wxUSE_STC