struct TSCanvas : public wxScrolledWindow
{
    MyFrame *frame;
    Document *doc;

    int scrollwheelaccum;

    TSCanvas(MyFrame *fr, wxWindow *parent) : frame(fr), wxScrolledWindow(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxScrolledWindowStyle|wxWANTS_CHARS), scrollwheelaccum(0), doc(NULL)
    {
        SetBackgroundStyle(wxBG_STYLE_CUSTOM);
        SetBackgroundColour(*wxWHITE);
    }

    ~TSCanvas() { DELETEP(doc); }

    void OnPaint( wxPaintEvent &event )
    {
        wxAutoBufferedPaintDC dc(this);
        DoPrepareDC(dc);
        sys->Draw(dc);
    };

    void UpdateHover(int mx, int my, wxDC &dc)
    {
        int x, y;
        CalcUnscrolledPosition(mx, my, &x, &y);
        DoPrepareDC(dc);
        sys->doc->Hover(x, y, dc);
    }

    void OnMotion(wxMouseEvent &me)
    {
        wxClientDC dc(this);
        UpdateHover(me.GetX(), me.GetY(), dc);
        if((me.LeftIsDown() || me.RightIsDown()) && sys->doc) Status(sys->doc->Drag(dc));
    }

    void SelectClick(int mx, int my, bool right)
    {
        wxClientDC dc(this);
        UpdateHover(mx, my, dc);
        if(sys->doc) Status(sys->doc->Select(dc, right));
    }

    void OnLeftDown(wxMouseEvent &me)
    {
        #ifndef __WXMSW__    // seems to not want to give the sw focus otherwise (thinks its already in focus when its not?)
                if(frame->filter) frame->filter->SetFocus();
        #endif
        SetFocus();
        if(me.ShiftDown()) OnMotion(me);
        else SelectClick(me.GetX(), me.GetY(), false);
    }

    void OnRightDown(wxMouseEvent &me)
    {
        SetFocus();
        SelectClick(me.GetX(), me.GetY(), true);
        #ifndef __WXMSW__
                me.Skip();  // otherwise EVT_CONTEXT_MENU won't be triggered?
        #endif
    }

    void OnLeftDoubleClick(wxMouseEvent &me)
    {
        wxClientDC dc(this);
        UpdateHover(me.GetX(), me.GetY(), dc);
        Status(sys->DoubleClick(dc));
    }

    void OnChar(wxKeyEvent &ce)
    {
        wxClientDC dc(this);
        DoPrepareDC(dc);
        Status(sys->Key(dc, ce.GetUnicodeKey(), ce.GetKeyCode(), ce.AltDown(), ce.CmdDown(), ce.ShiftDown()));
    }

    void OnMouseWheel(wxMouseEvent &me)
    {
        scrollwheelaccum += me.GetWheelRotation();
        int steps = scrollwheelaccum/me.GetWheelDelta();
        if(!steps) return;
        scrollwheelaccum -= steps*me.GetWheelDelta();

        if(frame->tradscroll)
        {
            CursorScroll2(0, -steps);
        }
        else
        {
            if(me.AltDown()) { CursorScroll(0, -steps); return; }
            wxClientDC dc(this);
            UpdateHover(me.GetX(), me.GetY(), dc);
            Status(sys->Wheel(dc, steps, me.AltDown(), me.CmdDown(), me.ShiftDown()), &me);
        }
    }

    void OnSize(wxSizeEvent &se)
    {
        sys->Refresh();
    }

    void OnContextMenuClick(wxContextMenuEvent &cme)
    {
        PopupMenu(frame->editmenu);
    }

    void CursorScroll(int dx, int dy)
    {
        if(frame->tradscroll && !dx)
        {
            wxClientDC dc(this);
            sys->Wheel(dc, dy, false, false, false);
        }
        else
        {
            CursorScroll2(dx, dy);
        }
    }

    void CursorScroll2(int dx, int dy)
    {
        int x, y;
        GetViewStart(&x, &y);
        x += dx*g_scrollrate;
        y += dy*g_scrollrate;
        Scroll(x, y);
    }


    void Status(const char *msg, wxMouseEvent *me = NULL)
    {/*
     if(msg)
     {
     FILE *f = fopen("statuslog.txt", "a");
     if(f)
     {
     if(me) fprintf(f, "ME: %d %d %d\n", me->AltDown(), me->ControlDown(), me->ShiftDown());
     fprintf(f, "S: %s\n", msg);
     fclose(f);
     }
     }*/
        if(frame->GetStatusBar()) frame->SetStatusText(msg ? wxString::FromAscii(msg) : L"", 0);
    }

    DECLARE_EVENT_TABLE()
};

