
struct Document
{
    Node *rootnode;
    Selection hover, selected, begindrag;

    TSCanvas *sw;

    wxString filename;

    long lastmodsinceautosave, undolistsizeatfullsave, lastsave;
    bool modified;

    wxDataObjectComposite *dataobjc;
    wxTextDataObject      *dataobjt;
    wxBitmapDataObject    *dataobji;
    wxFileDataObject      *dataobjf;

    Document() : sw(NULL), rootnode(NULL), filename(L""),
        lastmodsinceautosave(0), undolistsizeatfullsave(0), lastsave(wxGetLocalTime()), modified(false)
    {
        dataobjc = new wxDataObjectComposite(); // deleted by DropTarget
        dataobjc->Add(dataobji = new wxBitmapDataObject());
        dataobjc->Add(dataobjt = new wxTextDataObject());
        dataobjc->Add(dataobjf = new wxFileDataObject());
    }

    ~Document()
    {
        DELETEP(rootnode);
    }

    void FindFocusXY(int x, int y, wxDC &dc)
    {
        int leftmargin = 40;
        int cury = 40;

        hover = Selection();

        if(rootnode->FindXY(dc, x-leftmargin, y-cury, hover)) return;
        cury += rootnode->ys;
        loopv(i, sys->fundisplaylist)
        {
            DisplayedSubtree &dst = sys->fundisplaylist[i];
            hover.subtree = i;
            //if(uf.FindXY(dc, leftmargin, cury)) return;
            if(dst.uf->body->FindXY(dc, x-leftmargin-dst.uf->xs, y-cury, hover)) return;
            cury += dst.uf->body->ys;
        }
    }

    char *Select(wxDC &dc, bool right)
    {
        if(selected==hover) return NULL;

        //if(right && hover.IsInside(selected)) return NULL;
        DrawSelect(dc, selected);

        selected = hover;
        begindrag = hover;

        DrawSelect(dc, selected);

        //ResetCursor();

        return NULL;
    }

    void Hover(int x, int y, wxDC &dc)
    {
        if(sys->redrawpending) return;

        sys->ResetFont();
        Selection prev = hover;
        hover = Selection();
        FindFocusXY(x, y, dc);

        if(!(prev==hover))
        {
            if(prev.n)  DrawSelect(dc, prev, false, false, true);
            if(hover.n) DrawSelect(dc, hover, false, false, true);
        }
    }

    char *Drag(wxDC &dc)
    {
        if(!selected.n || !hover.n || !begindrag.n || hover.Thin()) return NULL;
        if(begindrag.Thin() || selected.Thin())
        {
            DrawSelect(dc, selected);
            begindrag = selected = hover;
            DrawSelect(dc, selected);
        }
        else
        {
            Selection old = selected;
            selected.Merge(begindrag, hover);
            if(!(old==selected))
            {
                DrawSelect(dc, old);
                DrawSelect(dc, selected);
            }
        }
        //ResetCursor();
        return NULL;
    }

    void DrawSelect(wxDC &dc, Selection &s, bool refreshinstead = false, bool cursoronly = false, bool ishover = false)
    {
        #ifdef SIMPLERENDER
            if(refreshinstead) { Refresh(); return; }
        #endif
        sys->ResetFont();

        s.Draw(dc, ishover, cursoronly);
    }


    void Insert()
    {
        if(!selected.Thin()) selected.DelOp(true);

        Node *nn = new Node(T_UNPARSED);
        nn->up = new Unparsed();

        if(selected.Thin())
        {
            selected.n->InsertAt(nn, selected.start);
        }
        else if(selected.NumNodes()==1 && selected.n->parent==NULL)
        {
            if(UserFunction *uf = selected.n->fparent)
            {
                delete selected.n;
                uf->body = nn;
                nn->fparent = uf;
            }
            else if(selected.n==rootnode)
            {
                delete rootnode;
                rootnode = nn;
            }
            else
            {
                ASSERT(0);
            }
        }
        else
        {
            ASSERT(0);
        }

        selected = Selection(nn);
    }
};

