1////////////////////////////////////////////////////////////////////////////
2// Name:        ScintillaWX.cxx
3// Purpose:     A wxWidgets implementation of Scintilla.  A class derived
4//              from ScintillaBase that uses the "wx platform" defined in
5//              PlatformWX.cxx  This class is one end of a bridge between
6//              the wx world and the Scintilla world.  It needs a peer
7//              object of type wxStyledTextCtrl to function.
8//
9// Author:      Robin Dunn
10//
11// Created:     13-Jan-2000
12// RCS-ID:      $Id: ScintillaWX.cpp 62074 2009-09-24 13:16:56Z JS $
13// Copyright:   (c) 2000 by Total Control Software
14// Licence:     wxWindows license
15/////////////////////////////////////////////////////////////////////////////
16
17#include "wx/wx.h"
18#include "wx/textbuf.h"
19#include "wx/dataobj.h"
20#include "wx/clipbrd.h"
21#include "wx/dnd.h"
22
23#include "ScintillaWX.h"
24#include "ExternalLexer.h"
25#include "wx/stc/stc.h"
26#include "PlatWX.h"
27
28#ifdef __WXMSW__
29    // GetHwndOf()
30    #include "wx/msw/private.h"
31#endif
32
33//----------------------------------------------------------------------
34// Helper classes
35
36class wxSTCTimer : public wxTimer {
37public:
38    wxSTCTimer(ScintillaWX* swx) {
39        this->swx = swx;
40    }
41
42    void Notify() {
43        swx->DoTick();
44    }
45
46private:
47    ScintillaWX* swx;
48};
49
50
51#if wxUSE_DRAG_AND_DROP
52class wxStartDragTimer : public wxTimer {
53public:
54    wxStartDragTimer(ScintillaWX* swx) {
55        this->swx = swx;
56    }
57
58    void Notify() {
59        swx->DoStartDrag();
60    }
61
62private:
63    ScintillaWX* swx;
64};
65
66
67bool wxSTCDropTarget::OnDropText(wxCoord x, wxCoord y, const wxString& data) {
68    return swx->DoDropText(x, y, data);
69}
70
71wxDragResult  wxSTCDropTarget::OnEnter(wxCoord x, wxCoord y, wxDragResult def) {
72    return swx->DoDragEnter(x, y, def);
73}
74
75wxDragResult  wxSTCDropTarget::OnDragOver(wxCoord x, wxCoord y, wxDragResult def) {
76    return swx->DoDragOver(x, y, def);
77}
78
79void  wxSTCDropTarget::OnLeave() {
80    swx->DoDragLeave();
81}
82#endif // wxUSE_DRAG_AND_DROP
83
84
85#if wxUSE_POPUPWIN && wxSTC_USE_POPUP
86#include <wx/popupwin.h>
87#define wxSTCCallTipBase wxPopupWindow
88#define param2  wxBORDER_NONE  // popup's 2nd param is flags
89#else
90#define wxSTCCallTipBase wxFrame
91#define param2 -1 // wxWindow's 2nd param is ID
92#endif
93
94#include "wx/dcbuffer.h"
95
96class wxSTCCallTip : public wxSTCCallTipBase {
97public:
98    wxSTCCallTip(wxWindow* parent, CallTip* ct, ScintillaWX* swx) :
99#if wxUSE_POPUPWIN && wxSTC_USE_POPUP
100        wxSTCCallTipBase(parent, wxBORDER_NONE),
101#else
102        wxSTCCallTipBase(parent, -1, wxEmptyString, wxDefaultPosition, wxDefaultSize,
103                         wxFRAME_NO_TASKBAR
104                         | wxFRAME_FLOAT_ON_PARENT
105                         | wxBORDER_NONE
106#ifdef __WXMAC__
107                         | wxPOPUP_WINDOW
108#endif
109            ),
110#endif
111          m_ct(ct), m_swx(swx), m_cx(wxDefaultCoord), m_cy(wxDefaultCoord)
112        {
113        }
114
115    ~wxSTCCallTip() {
116#if wxUSE_POPUPWIN && wxSTC_USE_POPUP && defined(__WXGTK__)
117        wxRect rect = GetRect();
118        rect.x = m_cx;
119        rect.y = m_cy;
120        GetParent()->Refresh(false, &rect);
121#endif
122    }
123
124    bool AcceptsFocus() const { return false; }
125
126    void OnPaint(wxPaintEvent& WXUNUSED(evt))
127    {
128        wxBufferedPaintDC dc(this);
129        Surface* surfaceWindow = Surface::Allocate();
130        surfaceWindow->Init(&dc, m_ct->wDraw.GetID());
131        m_ct->PaintCT(surfaceWindow);
132        surfaceWindow->Release();
133        delete surfaceWindow;
134    }
135
136    void OnFocus(wxFocusEvent& event)
137    {
138        GetParent()->SetFocus();
139        event.Skip();
140    }
141
142    void OnLeftDown(wxMouseEvent& event)
143    {
144        wxPoint pt = event.GetPosition();
145        Point p(pt.x, pt.y);
146        m_ct->MouseClick(p);
147        m_swx->CallTipClick();
148    }
149
150    virtual void DoSetSize(int x, int y,
151                           int width, int height,
152                           int sizeFlags = wxSIZE_AUTO)
153    {
154        // convert coords to screen coords since we're a top-level window
155        if (x != wxDefaultCoord) {
156            m_cx = x;
157            GetParent()->ClientToScreen(&x, NULL);
158        }
159        if (y != wxDefaultCoord) {
160            m_cy = y;
161            GetParent()->ClientToScreen(NULL, &y);
162        }
163        wxSTCCallTipBase::DoSetSize(x, y, width, height, sizeFlags);
164    }
165
166#if wxUSE_POPUPWIN && wxSTC_USE_POPUP
167#else
168    virtual bool Show( bool show = true )
169    {
170        // Although we're a frame, we always want the parent to be active, so
171        // raise it whenever we get shown.
172        bool rv = wxSTCCallTipBase::Show(show);
173        if (rv && show)
174        {
175            wxTopLevelWindow *frame = wxDynamicCast(
176                wxGetTopLevelParent(GetParent()), wxTopLevelWindow);
177            if (frame)
178                frame->Raise();
179        }
180        return rv;
181    }
182#endif
183
184    wxPoint GetMyPosition()
185    {
186        return wxPoint(m_cx, m_cy);
187    }
188
189private:
190    CallTip*      m_ct;
191    ScintillaWX*  m_swx;
192    int           m_cx, m_cy;
193    DECLARE_EVENT_TABLE()
194};
195
196BEGIN_EVENT_TABLE(wxSTCCallTip, wxSTCCallTipBase)
197    EVT_PAINT(wxSTCCallTip::OnPaint)
198    EVT_SET_FOCUS(wxSTCCallTip::OnFocus)
199    EVT_LEFT_DOWN(wxSTCCallTip::OnLeftDown)
200END_EVENT_TABLE()
201
202
203//----------------------------------------------------------------------
204
205#if wxUSE_DATAOBJ
206static wxTextFileType wxConvertEOLMode(int scintillaMode)
207{
208    wxTextFileType type;
209
210    switch (scintillaMode) {
211        case wxSTC_EOL_CRLF:
212            type = wxTextFileType_Dos;
213            break;
214
215        case wxSTC_EOL_CR:
216            type = wxTextFileType_Mac;
217            break;
218
219        case wxSTC_EOL_LF:
220            type = wxTextFileType_Unix;
221            break;
222
223        default:
224            type = wxTextBuffer::typeDefault;
225            break;
226    }
227    return type;
228}
229#endif // wxUSE_DATAOBJ
230
231
232//----------------------------------------------------------------------
233// Constructor/Destructor
234
235
236ScintillaWX::ScintillaWX(wxStyledTextCtrl* win) {
237    capturedMouse = false;
238    focusEvent = false;
239    wMain = win;
240    stc   = win;
241    wheelRotation = 0;
242    Initialise();
243#ifdef __WXMSW__
244    sysCaretBitmap = 0;
245    sysCaretWidth = 0;
246    sysCaretHeight = 0;
247#endif
248#if wxUSE_DRAG_AND_DROP
249    startDragTimer = new wxStartDragTimer(this);
250#endif // wxUSE_DRAG_AND_DROP
251}
252
253
254ScintillaWX::~ScintillaWX() {
255#if wxUSE_DRAG_AND_DROP
256    delete startDragTimer;
257#endif // wxUSE_DRAG_AND_DROP
258    Finalise();
259}
260
261//----------------------------------------------------------------------
262// base class virtuals
263
264
265void ScintillaWX::Initialise() {
266    //ScintillaBase::Initialise();
267#if wxUSE_DRAG_AND_DROP
268    dropTarget = new wxSTCDropTarget;
269    dropTarget->SetScintilla(this);
270    stc->SetDropTarget(dropTarget);
271#endif // wxUSE_DRAG_AND_DROP
272#ifdef __WXMAC__
273    vs.extraFontFlag = false;  // UseAntiAliasing
274#else
275    vs.extraFontFlag = true;   // UseAntiAliasing
276#endif
277}
278
279
280void ScintillaWX::Finalise() {
281    ScintillaBase::Finalise();
282    SetTicking(false);
283    SetIdle(false);
284    DestroySystemCaret();
285}
286
287
288void ScintillaWX::StartDrag() {
289#if wxUSE_DRAG_AND_DROP
290    // We defer the starting of the DnD, otherwise the LeftUp of a normal
291    // click could be lost and the STC will think it is doing a DnD when the
292    // user just wanted a normal click.
293    startDragTimer->Start(200, true);
294#endif // wxUSE_DRAG_AND_DROP
295}
296
297void ScintillaWX::DoStartDrag() {
298#if wxUSE_DRAG_AND_DROP
299    wxString dragText = stc2wx(drag.s, drag.len);
300
301    // Send an event to allow the drag text to be changed
302    wxStyledTextEvent evt(wxEVT_STC_START_DRAG, stc->GetId());
303    evt.SetEventObject(stc);
304    evt.SetDragText(dragText);
305    evt.SetDragAllowMove(true);
306    evt.SetPosition(wxMin(stc->GetSelectionStart(),
307                          stc->GetSelectionEnd()));
308    stc->GetEventHandler()->ProcessEvent(evt);
309    dragText = evt.GetDragText();
310
311    if (dragText.length()) {
312        wxDropSource        source(stc);
313        wxTextDataObject    data(dragText);
314        wxDragResult        result;
315
316        source.SetData(data);
317        dropWentOutside = true;
318        result = source.DoDragDrop(evt.GetDragAllowMove());
319        if (result == wxDragMove && dropWentOutside)
320            ClearSelection();
321        inDragDrop = false;
322        SetDragPosition(invalidPosition);
323    }
324#endif // wxUSE_DRAG_AND_DROP
325}
326
327
328bool ScintillaWX::SetIdle(bool on) {
329    if (idler.state != on) {
330        // connect or disconnect the EVT_IDLE handler
331        if (on)
332            stc->Connect(wxID_ANY, wxEVT_IDLE,
333                         (wxObjectEventFunction) (wxEventFunction) (wxIdleEventFunction) &wxStyledTextCtrl::OnIdle);
334        else
335            stc->Disconnect(wxID_ANY, wxEVT_IDLE,
336                            (wxObjectEventFunction) (wxEventFunction) (wxIdleEventFunction) &wxStyledTextCtrl::OnIdle);
337        idler.state = on;
338    }
339    return idler.state;
340}
341
342
343void ScintillaWX::SetTicking(bool on) {
344    wxSTCTimer* steTimer;
345    if (timer.ticking != on) {
346        timer.ticking = on;
347        if (timer.ticking) {
348            steTimer = new wxSTCTimer(this);
349            steTimer->Start(timer.tickSize);
350            timer.tickerID = steTimer;
351        } else {
352            steTimer = (wxSTCTimer*)timer.tickerID;
353            steTimer->Stop();
354            delete steTimer;
355            timer.tickerID = 0;
356        }
357    }
358    timer.ticksToWait = caret.period;
359}
360
361
362void ScintillaWX::SetMouseCapture(bool on) {
363    if (mouseDownCaptures) {
364        if (on && !capturedMouse)
365            stc->CaptureMouse();
366        else if (!on && capturedMouse && stc->HasCapture())
367            stc->ReleaseMouse();
368        capturedMouse = on;
369    }
370}
371
372
373bool ScintillaWX::HaveMouseCapture() {
374    return capturedMouse;
375}
376
377
378void ScintillaWX::ScrollText(int linesToMove) {
379    int dy = vs.lineHeight * (linesToMove);
380    stc->ScrollWindow(0, dy);
381    stc->Update();
382}
383
384void ScintillaWX::SetVerticalScrollPos() {
385    if (stc->m_vScrollBar == NULL) {  // Use built-in scrollbar
386        stc->SetScrollPos(wxVERTICAL, topLine);
387    }
388    else { // otherwise use the one that's been given to us
389        stc->m_vScrollBar->SetThumbPosition(topLine);
390    }
391}
392
393void ScintillaWX::SetHorizontalScrollPos() {
394    if (stc->m_hScrollBar == NULL) {  // Use built-in scrollbar
395        stc->SetScrollPos(wxHORIZONTAL, xOffset);
396    }
397    else { // otherwise use the one that's been given to us
398        stc->m_hScrollBar->SetThumbPosition(xOffset);
399    }
400}
401
402
403const int H_SCROLL_STEP = 20;
404
405bool ScintillaWX::ModifyScrollBars(int nMax, int nPage) {
406    bool modified = false;
407
408    int vertEnd = nMax;
409    if (!verticalScrollBarVisible)
410        vertEnd = 0;
411
412    // Check the vertical scrollbar
413    if (stc->m_vScrollBar == NULL) {  // Use built-in scrollbar
414        int  sbMax    = stc->GetScrollRange(wxVERTICAL);
415        int  sbThumb  = stc->GetScrollThumb(wxVERTICAL);
416        int  sbPos    = stc->GetScrollPos(wxVERTICAL);
417        if (sbMax != vertEnd || sbThumb != nPage) {
418            stc->SetScrollbar(wxVERTICAL, sbPos, nPage, vertEnd+1);
419            modified = true;
420        }
421    }
422    else { // otherwise use the one that's been given to us
423        int  sbMax    = stc->m_vScrollBar->GetRange();
424        int  sbPage   = stc->m_vScrollBar->GetPageSize();
425        int  sbPos    = stc->m_vScrollBar->GetThumbPosition();
426        if (sbMax != vertEnd || sbPage != nPage) {
427            stc->m_vScrollBar->SetScrollbar(sbPos, nPage, vertEnd+1, nPage);
428            modified = true;
429        }
430    }
431
432
433    // Check the horizontal scrollbar
434    PRectangle rcText = GetTextRectangle();
435    int horizEnd = scrollWidth;
436    if (horizEnd < 0)
437        horizEnd = 0;
438    if (!horizontalScrollBarVisible || (wrapState != eWrapNone))
439        horizEnd = 0;
440    int pageWidth = rcText.Width();
441
442    if (stc->m_hScrollBar == NULL) {  // Use built-in scrollbar
443        int sbMax    = stc->GetScrollRange(wxHORIZONTAL);
444        int sbThumb  = stc->GetScrollThumb(wxHORIZONTAL);
445        int sbPos    = stc->GetScrollPos(wxHORIZONTAL);
446        if ((sbMax != horizEnd) || (sbThumb != pageWidth) || (sbPos != 0)) {
447            stc->SetScrollbar(wxHORIZONTAL, sbPos, pageWidth, horizEnd);
448            modified = true;
449            if (scrollWidth < pageWidth) {
450                HorizontalScrollTo(0);
451            }
452        }
453    }
454    else { // otherwise use the one that's been given to us
455        int sbMax    = stc->m_hScrollBar->GetRange();
456        int sbThumb  = stc->m_hScrollBar->GetPageSize();
457        int sbPos    = stc->m_hScrollBar->GetThumbPosition();
458        if ((sbMax != horizEnd) || (sbThumb != pageWidth) || (sbPos != 0)) {
459            stc->m_hScrollBar->SetScrollbar(sbPos, pageWidth, horizEnd, pageWidth);
460            modified = true;
461            if (scrollWidth < pageWidth) {
462                HorizontalScrollTo(0);
463            }
464        }
465    }
466
467    return modified;
468}
469
470
471void ScintillaWX::NotifyChange() {
472    stc->NotifyChange();
473}
474
475
476void ScintillaWX::NotifyParent(SCNotification scn) {
477    stc->NotifyParent(&scn);
478}
479
480
481// This method is overloaded from ScintillaBase in order to prevent the
482// AutoComplete window from being destroyed when it gets the focus.  There is
483// a side effect that the AutoComp will also not be destroyed when switching
484// to another window, but I think that is okay.
485void ScintillaWX::CancelModes() {
486    if (! focusEvent)
487        AutoCompleteCancel();
488    ct.CallTipCancel();
489    Editor::CancelModes();
490}
491
492
493
494void ScintillaWX::Copy() {
495    if (currentPos != anchor) {
496        SelectionText st;
497        CopySelectionRange(&st);
498        CopyToClipboard(st);
499    }
500}
501
502
503void ScintillaWX::Paste() {
504    pdoc->BeginUndoAction();
505    ClearSelection();
506
507#if wxUSE_DATAOBJ
508    wxTextDataObject data;
509    bool gotData = false;
510
511    if (wxTheClipboard->Open()) {
512        wxTheClipboard->UsePrimarySelection(false);
513        gotData = wxTheClipboard->GetData(data);
514        wxTheClipboard->Close();
515    }
516    if (gotData) {
517        wxString   text = wxTextBuffer::Translate(data.GetText(),
518                                                  wxConvertEOLMode(pdoc->eolMode));
519        wxWX2MBbuf buf = (wxWX2MBbuf)wx2stc(text);
520
521#if wxUSE_UNICODE
522        // free up the old character buffer in case the text is real big
523        data.SetText(wxEmptyString);
524        text = wxEmptyString;
525#endif
526        int len = strlen(buf);
527        pdoc->InsertString(currentPos, buf, len);
528        SetEmptySelection(currentPos + len);
529    }
530#endif // wxUSE_DATAOBJ
531
532    pdoc->EndUndoAction();
533    NotifyChange();
534    Redraw();
535}
536
537
538void ScintillaWX::CopyToClipboard(const SelectionText& st) {
539#if wxUSE_CLIPBOARD
540    if ( !st.len )
541        return;
542    if (wxTheClipboard->Open()) {
543        wxTheClipboard->UsePrimarySelection(false);
544        wxString text = wxTextBuffer::Translate(stc2wx(st.s, st.len-1));
545        wxTheClipboard->SetData(new wxTextDataObject(text));
546        wxTheClipboard->Close();
547    }
548#else
549    wxUnusedVar(st);
550#endif // wxUSE_CLIPBOARD
551}
552
553
554bool ScintillaWX::CanPaste() {
555#if wxUSE_CLIPBOARD
556    bool canPaste = false;
557    bool didOpen;
558
559    if (Editor::CanPaste()) {
560        didOpen = !wxTheClipboard->IsOpened();
561        if ( didOpen )
562            wxTheClipboard->Open();
563
564        if (wxTheClipboard->IsOpened()) {
565            wxTheClipboard->UsePrimarySelection(false);
566            canPaste = wxTheClipboard->IsSupported(wxUSE_UNICODE ? wxDF_UNICODETEXT : wxDF_TEXT);
567            if (didOpen)
568                wxTheClipboard->Close();
569        }
570    }
571    return canPaste;
572#else
573    return false;
574#endif // wxUSE_CLIPBOARD
575}
576
577void ScintillaWX::CreateCallTipWindow(PRectangle) {
578    if (! ct.wCallTip.Created() ) {
579        ct.wCallTip = new wxSTCCallTip(stc, &ct, this);
580        ct.wDraw = ct.wCallTip;
581    }
582}
583
584
585void ScintillaWX::AddToPopUp(const char *label, int cmd, bool enabled) {
586    if (!label[0])
587        ((wxMenu*)popup.GetID())->AppendSeparator();
588    else
589        ((wxMenu*)popup.GetID())->Append(cmd, wxGetTranslation(stc2wx(label)));
590
591    if (!enabled)
592        ((wxMenu*)popup.GetID())->Enable(cmd, enabled);
593}
594
595
596// This is called by the Editor base class whenever something is selected
597void ScintillaWX::ClaimSelection() {
598#if 0
599    // Until wxGTK is able to support using both the primary selection and the
600    // clipboard at the same time I think it causes more problems than it is
601    // worth to implement this method.  Selecting text should not clear the
602    // clipboard.  --Robin
603#ifdef __WXGTK__
604    // Put the selected text in the PRIMARY selection
605    if (currentPos != anchor) {
606        SelectionText st;
607        CopySelectionRange(&st);
608        if (wxTheClipboard->Open()) {
609            wxTheClipboard->UsePrimarySelection(true);
610            wxString text = stc2wx(st.s, st.len);
611            wxTheClipboard->SetData(new wxTextDataObject(text));
612            wxTheClipboard->UsePrimarySelection(false);
613            wxTheClipboard->Close();
614        }
615    }
616#endif
617#endif
618}
619
620
621void ScintillaWX::UpdateSystemCaret() {
622#ifdef __WXMSW__
623    if (hasFocus) {
624        if (HasCaretSizeChanged()) {
625            DestroySystemCaret();
626            CreateSystemCaret();
627        }
628        Point pos = LocationFromPosition(currentPos);
629        ::SetCaretPos(pos.x, pos.y);
630    }
631#endif
632}
633
634
635bool ScintillaWX::HasCaretSizeChanged() {
636#ifdef __WXMSW__
637    if (( (0 != vs.caretWidth) && (sysCaretWidth != vs.caretWidth) )
638        || (0 != vs.lineHeight) && (sysCaretHeight != vs.lineHeight)) {
639        return true;
640    }
641#endif
642    return false;
643}
644
645bool ScintillaWX::CreateSystemCaret() {
646#ifdef __WXMSW__
647    sysCaretWidth = vs.caretWidth;
648    if (0 == sysCaretWidth) {
649        sysCaretWidth = 1;
650    }
651    sysCaretHeight = vs.lineHeight;
652    int bitmapSize = (((sysCaretWidth + 15) & ~15) >> 3) * sysCaretHeight;
653    char *bits = new char[bitmapSize];
654    memset(bits, 0, bitmapSize);
655    sysCaretBitmap = ::CreateBitmap(sysCaretWidth, sysCaretHeight, 1,
656                                    1, reinterpret_cast<BYTE *>(bits));
657    delete [] bits;
658    BOOL retval = ::CreateCaret(GetHwndOf(stc), sysCaretBitmap,
659                                sysCaretWidth, sysCaretHeight);
660    ::ShowCaret(GetHwndOf(stc));
661    return retval != 0;
662#else
663    return false;
664#endif
665}
666
667bool ScintillaWX::DestroySystemCaret() {
668#ifdef __WXMSW__
669    ::HideCaret(GetHwndOf(stc));
670    BOOL retval = ::DestroyCaret();
671    if (sysCaretBitmap) {
672        ::DeleteObject(sysCaretBitmap);
673        sysCaretBitmap = 0;
674    }
675    return retval != 0;
676#else
677    return false;
678#endif
679}
680
681
682//----------------------------------------------------------------------
683
684
685sptr_t ScintillaWX::DefWndProc(unsigned int /*iMessage*/,
686                               uptr_t /*wParam*/,
687                               sptr_t /*lParam*/) {
688    return 0;
689}
690
691sptr_t ScintillaWX::WndProc(unsigned int iMessage,
692                            uptr_t wParam,
693                            sptr_t lParam) {
694      switch (iMessage) {
695      case SCI_CALLTIPSHOW: {
696          // NOTE: This is copied here from scintilla/src/ScintillaBase.cxx
697          // because of the little tweak that needs done below for wxGTK.
698          // When updating new versions double check that this is still
699          // needed, and that any new code there is copied here too.
700          Point pt = LocationFromPosition(wParam);
701          char* defn = reinterpret_cast<char *>(lParam);
702          AutoCompleteCancel();
703          pt.y += vs.lineHeight;
704          int ctStyle = ct.UseStyleCallTip() ? STYLE_CALLTIP : STYLE_DEFAULT;
705          if (ct.UseStyleCallTip())
706          {
707              ct.SetForeBack(vs.styles[STYLE_CALLTIP].fore, vs.styles[STYLE_CALLTIP].back);
708
709          }
710          PRectangle rc = ct.CallTipStart(currentPos, pt,
711                                          defn,
712                                          vs.styles[ctStyle].fontName,
713                                          vs.styles[ctStyle].sizeZoomed,
714                                          CodePage(),
715                                          vs.styles[ctStyle].characterSet,
716                                          wMain);
717          // If the call-tip window would be out of the client
718          // space, adjust so it displays above the text.
719          PRectangle rcClient = GetClientRectangle();
720          if (rc.bottom > rcClient.bottom) {
721#ifdef __WXGTK__
722              int offset = int(vs.lineHeight * 1.25)  + rc.Height();
723#else
724              int offset = vs.lineHeight + rc.Height();
725#endif
726              rc.top -= offset;
727              rc.bottom -= offset;
728          }
729          // Now display the window.
730          CreateCallTipWindow(rc);
731          ct.wCallTip.SetPositionRelative(rc, wMain);
732          ct.wCallTip.Show();
733          break;
734      }
735
736#ifdef SCI_LEXER
737      case SCI_LOADLEXERLIBRARY:
738            LexerManager::GetInstance()->Load((const char*)lParam);
739            break;
740#endif
741
742      default:
743          return ScintillaBase::WndProc(iMessage, wParam, lParam);
744      }
745      return 0;
746}
747
748
749
750//----------------------------------------------------------------------
751// Event delegates
752
753void ScintillaWX::DoPaint(wxDC* dc, wxRect rect) {
754
755    paintState = painting;
756    Surface* surfaceWindow = Surface::Allocate();
757    surfaceWindow->Init(dc, wMain.GetID());
758    rcPaint = PRectangleFromwxRect(rect);
759    PRectangle rcClient = GetClientRectangle();
760    paintingAllText = rcPaint.Contains(rcClient);
761
762    ClipChildren(*dc, rcPaint);
763    Paint(surfaceWindow, rcPaint);
764
765    delete surfaceWindow;
766    if (paintState == paintAbandoned) {
767        // Painting area was insufficient to cover new styling or brace
768        // highlight positions
769        FullPaint();
770    }
771    paintState = notPainting;
772}
773
774
775void ScintillaWX::DoHScroll(int type, int pos) {
776    int xPos = xOffset;
777    PRectangle rcText = GetTextRectangle();
778    int pageWidth = rcText.Width() * 2 / 3;
779    if (type == wxEVT_SCROLLWIN_LINEUP || type == wxEVT_SCROLL_LINEUP)
780        xPos -= H_SCROLL_STEP;
781    else if (type == wxEVT_SCROLLWIN_LINEDOWN || type == wxEVT_SCROLL_LINEDOWN)
782        xPos += H_SCROLL_STEP;
783    else if (type == wxEVT_SCROLLWIN_PAGEUP || type == wxEVT_SCROLL_PAGEUP)
784        xPos -= pageWidth;
785    else if (type == wxEVT_SCROLLWIN_PAGEDOWN || type == wxEVT_SCROLL_PAGEDOWN) {
786        xPos += pageWidth;
787        if (xPos > scrollWidth - rcText.Width()) {
788            xPos = scrollWidth - rcText.Width();
789        }
790    }
791    else if (type == wxEVT_SCROLLWIN_TOP || type == wxEVT_SCROLL_TOP)
792        xPos = 0;
793    else if (type == wxEVT_SCROLLWIN_BOTTOM || type == wxEVT_SCROLL_BOTTOM)
794        xPos = scrollWidth;
795    else if (type == wxEVT_SCROLLWIN_THUMBTRACK || type == wxEVT_SCROLL_THUMBTRACK)
796        xPos = pos;
797
798    HorizontalScrollTo(xPos);
799}
800
801void ScintillaWX::DoVScroll(int type, int pos) {
802    int topLineNew = topLine;
803    if (type == wxEVT_SCROLLWIN_LINEUP || type == wxEVT_SCROLL_LINEUP)
804        topLineNew -= 1;
805    else if (type == wxEVT_SCROLLWIN_LINEDOWN || type == wxEVT_SCROLL_LINEDOWN)
806        topLineNew += 1;
807    else if (type ==  wxEVT_SCROLLWIN_PAGEUP || type == wxEVT_SCROLL_PAGEUP)
808        topLineNew -= LinesToScroll();
809    else if (type ==  wxEVT_SCROLLWIN_PAGEDOWN || type == wxEVT_SCROLL_PAGEDOWN)
810        topLineNew += LinesToScroll();
811    else if (type ==  wxEVT_SCROLLWIN_TOP || type == wxEVT_SCROLL_TOP)
812        topLineNew = 0;
813    else if (type ==  wxEVT_SCROLLWIN_BOTTOM || type == wxEVT_SCROLL_BOTTOM)
814        topLineNew = MaxScrollPos();
815    else if (type ==   wxEVT_SCROLLWIN_THUMBTRACK || type == wxEVT_SCROLL_THUMBTRACK)
816        topLineNew = pos;
817
818    ScrollTo(topLineNew);
819}
820
821void ScintillaWX::DoMouseWheel(int rotation, int delta,
822                               int linesPerAction, int ctrlDown,
823                               bool isPageScroll ) {
824    int topLineNew = topLine;
825    int lines;
826
827    if (ctrlDown) {  // Zoom the fonts if Ctrl key down
828        if (rotation < 0) {
829            KeyCommand(SCI_ZOOMIN);
830        }
831        else {
832            KeyCommand(SCI_ZOOMOUT);
833        }
834    }
835    else { // otherwise just scroll the window
836        if ( !delta )
837            delta = 120;
838        wheelRotation += rotation;
839        lines = wheelRotation / delta;
840        wheelRotation -= lines * delta;
841        if (lines != 0) {
842            if (isPageScroll)
843                lines = lines * LinesOnScreen();  // lines is either +1 or -1
844            else
845                lines *= linesPerAction;
846            topLineNew -= lines;
847            ScrollTo(topLineNew);
848        }
849    }
850}
851
852
853void ScintillaWX::DoSize(int WXUNUSED(width), int WXUNUSED(height)) {
854    ChangeSize();
855}
856
857void ScintillaWX::DoLoseFocus(){
858    focusEvent = true;
859    SetFocusState(false);
860    focusEvent = false;
861    DestroySystemCaret();
862}
863
864void ScintillaWX::DoGainFocus(){
865    focusEvent = true;
866    SetFocusState(true);
867    focusEvent = false;
868    DestroySystemCaret();
869    CreateSystemCaret();
870}
871
872void ScintillaWX::DoSysColourChange() {
873    InvalidateStyleData();
874}
875
876void ScintillaWX::DoLeftButtonDown(Point pt, unsigned int curTime, bool shift, bool ctrl, bool alt) {
877    ButtonDown(pt, curTime, shift, ctrl, alt);
878}
879
880void ScintillaWX::DoLeftButtonUp(Point pt, unsigned int curTime, bool ctrl) {
881    ButtonUp(pt, curTime, ctrl);
882#if wxUSE_DRAG_AND_DROP
883    if (startDragTimer->IsRunning()) {
884        startDragTimer->Stop();
885        SetDragPosition(invalidPosition);
886        SetEmptySelection(PositionFromLocation(pt));
887        ShowCaretAtCurrentPosition();
888    }
889#endif // wxUSE_DRAG_AND_DROP
890}
891
892void ScintillaWX::DoLeftButtonMove(Point pt) {
893    ButtonMove(pt);
894}
895
896#ifdef __WXGTK__
897void ScintillaWX::DoMiddleButtonUp(Point pt) {
898    // Set the current position to the mouse click point and
899    // then paste in the PRIMARY selection, if any.  wxGTK only.
900    int newPos = PositionFromLocation(pt);
901    MovePositionTo(newPos, noSel, true);
902
903    pdoc->BeginUndoAction();
904    wxTextDataObject data;
905    bool gotData = false;
906    if (wxTheClipboard->Open()) {
907        wxTheClipboard->UsePrimarySelection(true);
908        gotData = wxTheClipboard->GetData(data);
909        wxTheClipboard->UsePrimarySelection(false);
910        wxTheClipboard->Close();
911    }
912    if (gotData) {
913        wxString   text = wxTextBuffer::Translate(data.GetText(),
914                                                  wxConvertEOLMode(pdoc->eolMode));
915        wxWX2MBbuf buf = (wxWX2MBbuf)wx2stc(text);
916        int        len = strlen(buf);
917        pdoc->InsertString(currentPos, buf, len);
918        SetEmptySelection(currentPos + len);
919    }
920    pdoc->EndUndoAction();
921    NotifyChange();
922    Redraw();
923
924    ShowCaretAtCurrentPosition();
925    EnsureCaretVisible();
926}
927#else
928void ScintillaWX::DoMiddleButtonUp(Point WXUNUSED(pt)) {
929}
930#endif
931
932
933void ScintillaWX::DoAddChar(int key) {
934#if wxUSE_UNICODE
935    wxChar wszChars[2];
936    wszChars[0] = (wxChar)key;
937    wszChars[1] = 0;
938    wxWX2MBbuf buf = (wxWX2MBbuf)wx2stc(wszChars);
939    AddCharUTF((char*)buf.data(), strlen(buf));
940#else
941    AddChar((char)key);
942#endif
943}
944
945
946int  ScintillaWX::DoKeyDown(const wxKeyEvent& evt, bool* consumed)
947{
948    int key = evt.GetKeyCode();
949    bool shift = evt.ShiftDown(),
950         ctrl  = evt.ControlDown(),
951         alt   = evt.AltDown();
952
953    if (ctrl && key >= 1 && key <= 26 && key != WXK_BACK)
954        key += 'A' - 1;
955
956    switch (key) {
957    case WXK_DOWN:              key = SCK_DOWN;     break;
958    case WXK_UP:                key = SCK_UP;       break;
959    case WXK_LEFT:              key = SCK_LEFT;     break;
960    case WXK_RIGHT:             key = SCK_RIGHT;    break;
961    case WXK_HOME:              key = SCK_HOME;     break;
962    case WXK_END:               key = SCK_END;      break;
963    case WXK_PAGEUP:            key = SCK_PRIOR;    break;
964    case WXK_PAGEDOWN:          key = SCK_NEXT;     break;
965    case WXK_NUMPAD_DOWN:       key = SCK_DOWN;     break;
966    case WXK_NUMPAD_UP:         key = SCK_UP;       break;
967    case WXK_NUMPAD_LEFT:       key = SCK_LEFT;     break;
968    case WXK_NUMPAD_RIGHT:      key = SCK_RIGHT;    break;
969    case WXK_NUMPAD_HOME:       key = SCK_HOME;     break;
970    case WXK_NUMPAD_END:        key = SCK_END;      break;
971    case WXK_NUMPAD_PAGEUP:     key = SCK_PRIOR;    break;
972    case WXK_NUMPAD_PAGEDOWN:   key = SCK_NEXT;     break;
973    case WXK_NUMPAD_DELETE:     key = SCK_DELETE;   break;
974    case WXK_NUMPAD_INSERT:     key = SCK_INSERT;   break;
975    case WXK_DELETE:            key = SCK_DELETE;   break;
976    case WXK_INSERT:            key = SCK_INSERT;   break;
977    case WXK_ESCAPE:            key = SCK_ESCAPE;   break;
978    case WXK_BACK:              key = SCK_BACK;     break;
979    case WXK_TAB:               key = SCK_TAB;      break;
980    case WXK_NUMPAD_ENTER:      // fall through
981    case WXK_RETURN:            key = SCK_RETURN;   break;
982    case WXK_ADD:               // fall through
983    case WXK_NUMPAD_ADD:        key = SCK_ADD;      break;
984    case WXK_SUBTRACT:          // fall through
985    case WXK_NUMPAD_SUBTRACT:   key = SCK_SUBTRACT; break;
986    case WXK_DIVIDE:            // fall through
987    case WXK_NUMPAD_DIVIDE:     key = SCK_DIVIDE;   break;
988    case WXK_CONTROL:           key = 0; break;
989    case WXK_ALT:               key = 0; break;
990    case WXK_SHIFT:             key = 0; break;
991    case WXK_MENU:              key = 0; break;
992    }
993
994#ifdef __WXMAC__
995    if ( evt.MetaDown() ) {
996        // check for a few common Mac Meta-key combos and remap them to Ctrl
997        // for Scintilla
998        switch ( key ) {
999        case 'Z':       // Undo
1000        case 'X':       // Cut
1001        case 'C':       // Copy
1002        case 'V':       // Paste
1003        case 'A':       // Select All
1004            ctrl = true;
1005            break;
1006        }
1007    }
1008#endif
1009
1010    int rv = KeyDown(key, shift, ctrl, alt, consumed);
1011
1012    if (key)
1013        return rv;
1014    else
1015        return 1;
1016}
1017
1018
1019void ScintillaWX::DoCommand(int ID) {
1020    Command(ID);
1021}
1022
1023
1024void ScintillaWX::DoContextMenu(Point pt) {
1025    if (displayPopupMenu)
1026        ContextMenu(pt);
1027}
1028
1029void ScintillaWX::DoOnListBox() {
1030    AutoCompleteCompleted();
1031}
1032
1033
1034void ScintillaWX::DoOnIdle(wxIdleEvent& evt) {
1035
1036    if ( Idle() )
1037        evt.RequestMore();
1038    else
1039        SetIdle(false);
1040}
1041
1042//----------------------------------------------------------------------
1043
1044#if wxUSE_DRAG_AND_DROP
1045bool ScintillaWX::DoDropText(long x, long y, const wxString& data) {
1046    SetDragPosition(invalidPosition);
1047
1048    wxString text = wxTextBuffer::Translate(data,
1049                                            wxConvertEOLMode(pdoc->eolMode));
1050
1051    // Send an event to allow the drag details to be changed
1052    wxStyledTextEvent evt(wxEVT_STC_DO_DROP, stc->GetId());
1053    evt.SetEventObject(stc);
1054    evt.SetDragResult(dragResult);
1055    evt.SetX(x);
1056    evt.SetY(y);
1057    evt.SetPosition(PositionFromLocation(Point(x,y)));
1058    evt.SetDragText(text);
1059    stc->GetEventHandler()->ProcessEvent(evt);
1060
1061    dragResult = evt.GetDragResult();
1062    if (dragResult == wxDragMove || dragResult == wxDragCopy) {
1063        DropAt(evt.GetPosition(),
1064               wx2stc(evt.GetDragText()),
1065               dragResult == wxDragMove,
1066               false); // TODO: rectangular?
1067        return true;
1068    }
1069    return false;
1070}
1071
1072
1073wxDragResult ScintillaWX::DoDragEnter(wxCoord WXUNUSED(x), wxCoord WXUNUSED(y), wxDragResult def) {
1074    dragResult = def;
1075    return dragResult;
1076}
1077
1078
1079wxDragResult ScintillaWX::DoDragOver(wxCoord x, wxCoord y, wxDragResult def) {
1080    SetDragPosition(PositionFromLocation(Point(x, y)));
1081
1082    // Send an event to allow the drag result to be changed
1083    wxStyledTextEvent evt(wxEVT_STC_DRAG_OVER, stc->GetId());
1084    evt.SetEventObject(stc);
1085    evt.SetDragResult(def);
1086    evt.SetX(x);
1087    evt.SetY(y);
1088    evt.SetPosition(PositionFromLocation(Point(x,y)));
1089    stc->GetEventHandler()->ProcessEvent(evt);
1090
1091    dragResult = evt.GetDragResult();
1092    return dragResult;
1093}
1094
1095
1096void ScintillaWX::DoDragLeave() {
1097    SetDragPosition(invalidPosition);
1098}
1099#endif // wxUSE_DRAG_AND_DROP
1100//----------------------------------------------------------------------
1101
1102// Force the whole window to be repainted
1103void ScintillaWX::FullPaint() {
1104#ifndef __WXMAC__
1105    stc->Refresh(false);
1106#endif
1107    stc->Update();
1108}
1109
1110
1111void ScintillaWX::DoScrollToLine(int line) {
1112    ScrollTo(line);
1113}
1114
1115
1116void ScintillaWX::DoScrollToColumn(int column) {
1117    HorizontalScrollTo(column * vs.spaceWidth);
1118}
1119
1120// wxGTK doesn't appear to need this explicit clipping code any longer, but I
1121// will leave it here commented out for a while just in case...
1122void ScintillaWX::ClipChildren(wxDC& WXUNUSED(dc), PRectangle WXUNUSED(rect))
1123{
1124//     wxRegion rgn(wxRectFromPRectangle(rect));
1125//     if (ac.Active()) {
1126//         wxRect childRect = ((wxWindow*)ac.lb->GetID())->GetRect();
1127//         rgn.Subtract(childRect);
1128//     }
1129//     if (ct.inCallTipMode) {
1130//         wxSTCCallTip* tip = (wxSTCCallTip*)ct.wCallTip.GetID();
1131//         wxRect childRect = tip->GetRect();
1132// #if wxUSE_POPUPWIN && wxSTC_USE_POPUP
1133//         childRect.SetPosition(tip->GetMyPosition());
1134// #endif
1135//         rgn.Subtract(childRect);
1136//     }
1137//     dc.SetClippingRegion(rgn);
1138}
1139
1140
1141void ScintillaWX::SetUseAntiAliasing(bool useAA) {
1142    vs.extraFontFlag = useAA;
1143    InvalidateStyleRedraw();
1144}
1145
1146bool ScintillaWX::GetUseAntiAliasing() {
1147    return vs.extraFontFlag;
1148}
1149
1150//----------------------------------------------------------------------
1151//----------------------------------------------------------------------
1152