
struct DisplayedSubtree
{
    UserFunction *uf;
    int x, y;
};

struct System
{
    Vector<BuiltinFunction> builtins;
    BuiltinFunction bf_openpar, bf_closepar, bf_comma;
    Vector<DisplayedSubtree> fundisplaylist;
    DisplayedSubtree maintreedisplay;
    bool codechanged;
    bool aggressivefactor;

    Selection redoselect;

    MTRnd rnd;

    MyFrame *frame;

    Document *doc;
    //Cell *rootgrid;
    //Selection hover, selected, begindrag;

    int originx, originy, maxx, maxy;
    //int lasttextsize, laststylebits;

    wxString defaultfont, searchstring;

    wxConfig cfg;

    //Evaluator ev;

    //wxString clipboardcopy;
    //Cell *cellclipboard;

    //Vector<Image *> imagelist;
    //Vector<int> loadimageids;

    //wxPen pen_tinytext, pen_gridborder, pen_tinygridlines, pen_gridlines, pen_thinselect;

    struct MyPrintout : wxPrintout
    {
        MyPrintout() : wxPrintout(L"printout") {}

        bool OnPrintPage(int page)
        {
            wxDC *dc = GetDC();
            if(!dc) return false;
            sys->Print(*dc, *this);
            return true;
        }

        bool OnBeginDocument(int startPage, int endPage)
        {
            return wxPrintout::OnBeginDocument(startPage, endPage);
        }

        void GetPageInfo(int *minPage, int *maxPage, int *selPageFrom, int *selPageTo)
        {
            *minPage = 1;
            *maxPage = 1;
            *selPageFrom = 1;
            *selPageTo = 1;
        }

        bool HasPage(int pageNum)
        {
            return pageNum == 1;
        }
    };

    bool while_printing;
    wxPrintData printData;
    wxPageSetupDialogData pageSetupData;
    uint printscale;

    bool blink;

    bool redrawpending;

    //uchar versionlastloaded;

    //uint customcolor;

    //uint maxcharsperline;

    bool makebaks;
    bool totray;
    bool autosave;

    struct SaveChecker : wxTimer
    {
        void Notify() { sys->SaveCheck(); }
    } savechecker;

    //int debugdrawstat;

    int lasttextsize;
    int laststylebits;


    System() : cfg(L"Restructor"),
               defaultfont(L"Lucida Sans Unicode"),    // should simply fail and give default font on non-windows platforms
               while_printing(false),
               printscale(0),
               blink(true),
               redrawpending(false),
               makebaks(true),
               totray(false),
               autosave(true),
               doc(NULL),
               aggressivefactor(false)
    {
        ResetFont();

        defaultfont = cfg.Read(L"defaultfont", defaultfont);
        cfg.Read(L"makebaks", &makebaks, makebaks);
        cfg.Read(L"totray", &totray, totray);
        cfg.Read(L"autosave", &autosave, autosave);
    }

    ~System()
    {
        fundisplaylist.setsize_nd(0);
    }

    void Clear()
    {
        frame->NewTab(doc = new Document());
    }

    TSCanvas *GetSW() { return (TSCanvas *)frame->nb->GetCurrentPage(); }

    void TabChange(Document *newdoc)
    {
        if(doc==newdoc) return;

        doc = newdoc;

        doc->sw->SetFocus();
        SetFileName(doc->filename);
    }

    void Init(int argc, wxChar **argv)
    {
        bf_openpar      = BuiltinFunction(L"( ", L"");
        bf_closepar     = BuiltinFunction(L" )", L"");
        bf_comma        = BuiltinFunction(L", ", L"");
        builtins.push() = BuiltinFunction(L"add", L"ii");
        builtins.push() = BuiltinFunction(L"mul", L"ii");
        builtins.push() = BuiltinFunction(L"sub", L"ii");
        builtins.push() = BuiltinFunction(L"div", L"ii");

        pageSetupData = printData;
        pageSetupData.SetMarginTopLeft(wxPoint(15, 15));
        pageSetupData.SetMarginBottomRight(wxPoint(15, 15));

        wxString lof;
        if(LoadDB(argc==2 ? argv[1] : (cfg.Read(L"lastopenfile", &lof) ? lof : wxFileName(wxFileName(*argv).GetPath()+L"/examples/tutorial.rs").GetFullPath()))) InitDB(1);;

        //doc->rootnode = Parser(L"add(add(3, 4), mul(add(3, 4), 3), 7, 7)").Parse();
        //doc->rootnode = Parser(L"div(add(mul(3, 4), mul(sub(4, 1), 4)), add(mul(3, 5), mul(sub(5, 1), 5)))").Parse();
        //doc->rootnode = Parser(L"add(div(add(3,1),4),mul(div(add(2,1),4),3))").Parse();
        //doc->rootnode = Parser(L"add(1,1)").Parse();
        /*
        doc->rootnode = Parser(L"sub(add(div(add(3,1),add(add(3, 4), mul(add(3, 4), 3), 7, 7)),"
                               L"mul(div(add(add(div(add(add(mul(3, 4), mul(7, 4)),1),4),mul(div(add(2,1),4),3)),1),4),3))"
                               L",add(mul(add(2,1),7),sub(div(add(add(mul(3, 4), mul(7, 4)),1),add(add(3, 4), mul(add(3,"
                               L"sub(add(div(add(3,1),4),mul(div(add(2,1),4),3)),add(mul(add(2,1),7),sub(div(add(add(mul(3, 4),"
                               L"mul(7, 4)),1),add(add(3, 4), mul(add(3, 4), 3), 7, 7)),3)))), 3), 7, 7)),3)))").Parse();
        */

        //doc->rootnode = Parser(L"add(add(3, 4, 4, 6), mul(add(3, 4, 4, 7), add(3, 5, 5, 8), 5))").Parse();

        // DOESN'T REFACTOR WELL:
        //doc->rootnode = Parser(L"mul(add(4, 4), add(4, 4), add(1, 1), 1)").Parse();



        wxFFile f(L"test.txt");
        ASSERT(f.IsOpened())
        wxString s;
        f.ReadAll(&s);
        doc->rootnode = Parser(s).Parse();


        //rnd.SeedMT(time(NULL));
        //doc->rootnode = Parser(RandomCode(0)).Parse();

        /*
        doc->rootnode = Parser(L"sub(add(div(add(33,1),add(add(3, 4), mul(add(3, 4), 3), 7, 7)),"
            L"mul(div(add(add(div(add(11,1),4),mul(div(add(2,1),4),3)),1),4),3))"
            L",add(mul(add(2,1),7),sub(div(add(add(mul(3, 4), mul(add(14,mul(div(add(2,1),4),3)), 4)),1),add(add(3, 4), mul(add(3,"
            L"sub(add(div(add(3,1),4),mul(div(15,4),3)),add(mul(add(2,1),7),sub(div(add(add(mul(3, 4),"
            L"mul(7, 4)),1),add(add(div(add(mul(3, 4), mul(sub(4, add(1,1)), 4)), add(mul(3, 5), mul(sub(5, 1), 5))), 4), mul(add(3, 4), 3), 77, 7)),3)))), 3), 7, 7)),3)))").Parse();
        */


        AutoRefactor(doc->rootnode);

        Refresh();

        frame->bt.Start(400);
        savechecker.Start(1000);
    }

    wxString RandomCode(int depth)
    {
        if(rnd(35)<depth)
        {
            return wxString::Format(L"%d", rnd(5));
        }
        else
        {
            return builtins[rnd(builtins.size())].name + L"(" + RandomCode(depth+1) + L"," + RandomCode(depth+1) + L")";
        }
    }

    Node *ProgramRoot() { return doc->rootnode; }

    void InitDB(int sizex, int sizey = 0)
    {
        Clear();
        //rootgrid = new Cell(NULL, CT_DATA, new Grid(sizex, sizey ? sizey : sizex));
        //rootgrid->grid->InitCells();
        SetFileName(L"");
    }

    wxString BakName(const wxString &filename) { return ExtName(filename, L".bak"); }
    wxString TmpName(const wxString &filename) { return ExtName(filename, L".tmp"); }
    wxString ExtName(const wxString &filename, wxString ext) { wxFileName fn(filename); return fn.GetPathWithSep()+fn.GetName()+ext; }

    const char *LoadDB(const wxString &filename)
    {
        if(frame->GetTabByFileName(filename)) return "this file is already loaded";

        wxString fn = filename;

        if(::wxFileExists(TmpName(filename)))
        {
            if(::wxMessageBox(L"A temporary autosave file exists, would you like to load it instead?", L"Autosave load", wxYES_NO)==wxYES)
                fn = TmpName(filename);
        }

        wxBusyCursor wait;
        /*
        wxFFileInputStream fis(fn);
        if(!fis.IsOk()) return "cannot open file";
        wxDataInputStream dis(fis);

        char buf[4];
        fis.Read(buf, 4);
        if(strncmp(buf, "TSFF", 4)) return "not a Restructor file";
        fis.Read(&versionlastloaded, 1);
        if(versionlastloaded>TS_VERSION) return "file of newer version";

        loadimageids.setsize(0);
        Cell *root = NULL;

        while(!root)
        {
            fis.Read(buf, 1);
            switch(*buf)
            {
                case 'I':
                {
                    if(versionlastloaded<9) dis.ReadString();
                    wxImage im;
                    if(!im.LoadFile(fis)) return "images in file unloadable";
                    loadimageids.push() = AddImageToList(im);
                    break;
                }

                case 'D':
                {
                    wxZlibInputStream zis(fis);
                    if(!zis.IsOk()) return "cannot decompress file";
                    wxDataInputStream dis(zis);
                    root = Cell::LoadWhich(dis, NULL);
                    if(!root) return "file corrupted!";
                    break;
                }

                default:
                    return "corrupt block header";
            }
        }
        */
        Clear();

        //rootgrid = root;

        SetFileName(filename);
        frame->filehistory.AddFileToHistory(filename);
        cfg.Write(L"lastopenfile", filename);

        return NULL;
    }

    const char *SaveDB(Document *doc, const wxString &filename, bool *success, bool istempfile = false)
    {
        if(filename.empty()) return "save cancelled";

        wxBusyCursor wait;

        if(!istempfile && makebaks && ::wxFileExists(filename))
        {
            ::wxRenameFile(filename, BakName(filename));
        }

        wxString sfn = istempfile ? TmpName(filename) : filename;
        /*
        wxFFileOutputStream fos(sfn);
        if(!fos.IsOk()) { wxMessageBox(L"Error writing Restructor file!", sfn.wx_str()); return "error writing to file"; }
        wxDataOutputStream sos(fos);

        fos.Write("TSFF", 4);
        char vers = TS_VERSION;
        fos.Write(&vers, 1);

        loopv(i, imagelist) imagelist[i]->trefc = 0;

        rootgrid->ImageRefCount();

        int realindex = 0;

        loopv(i, imagelist)
        {
            Image &image = *imagelist[i];
            if(image.trefc)
            {
                fos.Write("I", 1);
                wxImage im = image.bm.ConvertToImage();
                im.SaveFile(fos, wxBITMAP_TYPE_PNG);
                image.savedindex = realindex++;
            }
        }

        fos.Write("D", 1);

        wxZlibOutputStream zos(fos, 9);
        if(!zos.IsOk()) return "zlib error while writing file";
        wxDataOutputStream dos(zos);
        rootgrid->Save(dos);
        */
        doc->lastmodsinceautosave = 0;
        doc->lastsave = wxGetLocalTime();

        if(!istempfile)
        {
            doc->undolistsizeatfullsave = 0;//doc->undolist.size();
            doc->modified = false;

            frame->filehistory.AddFileToHistory(filename);
            cfg.Write(L"lastopenfile", filename);

            ::wxRemoveFile(TmpName(filename));
        }

        SetFileName(filename);

        if(success) *success = true;
        return "file saved succesfully";
    }

    void SetFileName(const wxString &fn)
    {
        doc->filename = fn;
        wxString mods = doc->modified ? (doc->lastmodsinceautosave ? L"*" : L"+") : L"";
        frame->SetTitle(L"Restructor - "+fn+mods);
        int page = frame->nb->GetSelection();
        if(page>=0) frame->nb->SetPageText(page, (fn.empty() ? L"<unnamed>" : wxFileName(fn).GetName())+mods);
    }

    /*
    void DrawSelectMove(wxDC &dc, Selection &s, bool refreshalways = false, bool refreshinstead = true)
    {
        int canvasw, canvash;
        GetSW()->GetClientSize(&canvasw, &canvash);
        Cell *drawroot = WalkPath(doc->drawpath);
        if((drawroot->sy>canvash || drawroot->sx>canvasw) && s.g)
        {
            wxRect r = s.g->GetRect(s);
            if(r.y<originy || r.y+r.height>maxy || r.x<originx || r.x+r.width>maxx)
            {

                GetSW()->EnableScrolling(false, false);
                GetSW()->SetScrollbars(1, 1, drawroot->sx, drawroot->sy,
                    r.width >canvasw ? r.x : r.x+r.width /2-canvasw/2,
                    r.height>canvash ? r.y : r.y+r.height/2-canvash/2, true);
                GetSW()->EnableScrolling(true, true);

                RefreshReset();
                return;
            }
        }
        if(refreshalways) RefreshReset();
        else sys->DrawSelect(dc, s, refreshinstead);
    }

    void ScrollOrZoom(wxDC &dc, bool zoomiftiny = false)
    {
        if(!selected.g) return;



        Cell *drawroot = WalkPath(doc->drawpath);
        for(Cell *cg = selected.g->cell; cg; cg = cg->parent) if(cg==drawroot)
        {
            DrawSelectMove(dc, selected, true);
            return;
        }

        Zoom(-100, dc);
    }
    */

    void Blink()
    {
        if(redrawpending) return;
        #ifndef SIMPLERENDER
            wxClientDC dc(GetSW());
            GetSW()->DoPrepareDC(dc);
            doc->DrawSelect(dc, doc->selected, false, true);
            blink = !blink;
            doc->DrawSelect(dc, doc->selected, true, true);
        #endif
    }

    //void ResetCursor() { if(selected.g) selected.SetCursorEdit(selected.TextEdit()); }

    /*
    void UpdateStatus(Cell *c)
    {
        if(c && frame->GetStatusBar()) frame->SetStatusText(wxString::Format(L"size %d", -c->text.relsize), 1);
    }
    */


    /*
    void Zoom(int dir, wxDC &dc, bool fromroot = false)
    {
        int len = max(0, (fromroot ? 0 : doc->drawpath.size())+dir);
        if(dir>0)
        {
            if(!selected.g) return;
            Cell *c = selected.GetCell();
            CreatePath(c && c->grid ? c : selected.g->cell, doc->drawpath);
        }
        while(len<doc->drawpath.size()) doc->drawpath.remove(0);
        WalkPath(doc->drawpath)->ResetChildren();
        Layout(dc);
        DrawSelectMove(dc, selected, true, false);
    }
    */

    DisplayedSubtree &GetSubTree(int i) { return i<0 ? maintreedisplay : fundisplaylist[i]; }

    void ReLayout(wxDC &dc, int &xs, int &ys)
    {
        bf_openpar.Layout(dc);
        bf_closepar.Layout(dc);
        bf_comma.Layout(dc);
        loopv(i, builtins) builtins[i].Layout(dc);

        fundisplaylist.setsize_nd(0);
        doc->rootnode->ConstrainedLayout(dc);

        xs = doc->rootnode->xs;
        ys = doc->rootnode->ys;

        loopv(i, fundisplaylist)
        {
            DisplayedSubtree &dst = fundisplaylist[i];
            xs = max(xs, dst.uf->xs+dst.uf->body->xs);
            ys += dst.uf->body->ys;
        }
    }

    void RenderFocus(wxDC &dc)
    {
        int leftmargin = 40;
        int cury = 40;
        doc->rootnode->Render(dc, maintreedisplay.x = leftmargin, maintreedisplay.y = cury);
        cury += doc->rootnode->ys;
        loopv(i, fundisplaylist)
        {
            DisplayedSubtree &dst = fundisplaylist[i];
            int xs = dst.uf->Render(dc, leftmargin, cury, dst.uf->body->ys);
            dst.uf->body->Render(dc, dst.x = leftmargin+xs, dst.y = cury);
            cury += dst.uf->body->ys;
        }
    }

    const char *NoSel() { return "this operation requires a selection"; }
    const char *OneCell() { return "this operation works on a single selected cell only"; }

    const char *Wheel(wxDC &dc, int dir, bool alt, bool ctrl, bool shift)
    {
        return NULL;
    }

    void Draw(wxDC &dc)
    {
        redrawpending = false;
        GetSW()->GetClientSize(&maxx, &maxy);
        if(doc && doc->rootnode)
        {
            ResetFont();

            int xs, ys;
            ReLayout(dc, xs, ys);

            int drx = max(xs, maxx);
            int dry = max(ys, maxy);
            GetSW()->EnableScrolling(false, false);
            GetSW()->SetVirtualSize(drx, dry);
            GetSW()->EnableScrolling(true, true);
            DrawRectangle(dc, 0xFFFFFF, 0, 0, drx, dry);
            GetSW()->CalcUnscrolledPosition(0, 0, &originx, &originy);
            maxx += originx;
            maxy += originy;

            RenderFocus(dc);

            if(redoselect.n)
            {
                redoselect.subtree = -1;
                if(!doc->rootnode->FindNodeNoFN(redoselect.n))
                {
                    loopv(i, fundisplaylist)
                    {
                        DisplayedSubtree &dst = fundisplaylist[i];
                        redoselect.subtree = i;
                        if(dst.uf->body->FindNodeNoFN(redoselect.n)) goto found;
                    }
                    goto notfound;
                }

                found:
                doc->selected = redoselect;

                notfound:
                redoselect = Selection();
            }

            doc->DrawSelect(dc, doc->selected);
            doc->hover.Draw(dc, true);

        }
        else
        {
            DrawRectangle(dc, 0xFFFFFF, 0, 0, maxx, maxy);
        }
    }

    void Print(wxDC &dc, wxPrintout &po)
    {
    }

    void PickFont(wxDC &dc, int textsize, int stylebits)
    {
        textsize += g_deftextsize;
        if(textsize!=lasttextsize || stylebits!=laststylebits)
        {
            dc.SetFont(wxFont(while_printing ? textsize-1 : textsize,
                              stylebits&STYLE_FIXED ? wxFONTFAMILY_TELETYPE : wxFONTFAMILY_DEFAULT,
                              stylebits&STYLE_ITALIC ? wxFONTSTYLE_ITALIC: wxFONTSTYLE_NORMAL,
                              stylebits&STYLE_BOLD ? wxFONTWEIGHT_BOLD : wxFONTWEIGHT_NORMAL,
                              (stylebits&STYLE_UNDERLINE)!=0,
                              stylebits&STYLE_FIXED ? L"Courier New" : defaultfont));
            lasttextsize = textsize;
            laststylebits = stylebits;
        }
    }

    void ResetFont()
    {
        lasttextsize = INT_MAX;
        laststylebits = -1;
    }

    void RefreshReset()
    {
        //WalkPath(drawpath)->ResetChildren();
        Refresh();
    }

    void Refresh()
    {
        //wxClientDC dc(sys->frame);
        //ReLayout(dc);

        doc->hover.n = NULL;
        RefreshHover();
    }

    void RefreshHover()
    {
        redrawpending = true;
        #ifdef __WXGTK__
            if(GetSW()) GetSW()->Refresh(false);
        #endif
        frame->nb->Refresh(false);
    }

    void ClearSelectionRefresh()
    {
        doc->selected.n = NULL;
        Refresh();
    }

    void RedoSelectionRefresh()
    {
        redoselect = doc->selected;
        ClearSelectionRefresh();
    }

    bool CheckForChanges()
    {
        if(doc->modified)
        {
            ThreeChoiceDialog tcd(frame,
                                  L"Save changes?",
                                  L"Changes have been made, are you sure you wish to continue?",
                                  L"Save and Close",
                                  L"Discard Changes",
                                  L"Cancel");
            switch(tcd.Run())
            {
                case 0: { bool success = false; Save(false, &success); return !success; }
                case 1: return false;
                default:
                case 2: return true;
            }
        }
        return false;
    }

    bool CloseDocument()
    {
        bool keep = CheckForChanges();
        if(!keep && !doc->filename.empty()) ::wxRemoveFile(TmpName(doc->filename));
        return keep;
    }

    const char *DoubleClick(wxDC &dc)
    {
        return NULL;
    }

    const char *Export(wxDC &dc, const wxChar *fmt, const wxChar *pat, const wxChar *msg, int k)
    {
        return "file exported successfully";
    }

    const char *Save(bool saveas, bool *success = NULL)
    {
        if(!saveas && !doc->filename.empty())
        {
            return SaveDB(doc, doc->filename, success);
        }
        return SaveDB(doc, ::wxFileSelector(L"Choose Restructor file to save:", L"", L"", L"rs",	L"*.rs", wxFD_SAVE|wxFD_OVERWRITE_PROMPT|wxFD_CHANGE_DIR), success);
    }

    void SaveCheck()
    {
        loop(i, frame->nb->GetPageCount())
        {
            Document *doc = ((TSCanvas *)frame->nb->GetPage(i))->doc;
            if(!doc->filename.empty() && doc->lastmodsinceautosave && (doc->lastmodsinceautosave+60<wxGetLocalTime() || doc->lastsave+300<wxGetLocalTime() || !frame->IsActive()))
                SaveDB(doc, doc->filename, NULL, true);
        }
    }

    const char *Open(const wxString &fn)
    {
        if(!fn.empty())
        {
            const char *msg = LoadDB(fn);
            if(msg) wxMessageBox(wxString::FromAscii(msg), fn.wx_str());
            ClearSelectionRefresh();
            return msg;
        }
        return "open file cancelled";
    }

    const char *Key(wxDC &dc, wxChar uk, int k, bool alt, bool ctrl, bool shift)
    {
        if(!doc->selected.n) return NoSel();

        switch(k)
        {
            //case WXK_NUMPAD_DELETE:     // potentially interferes with a unicode key, no solution for this? -> doesn't ever get triggered? get WXK_NUMPAD_DECIMAL instead
            case WXK_DELETE:            // this one can't be caught by a menu always, unlike INS? but doesn't interfere.
                return Action(dc, A_DELETE);

            case WXK_BACK:              // no menu shortcut available in wxwidgets, but doesn't interfere
                return Action(dc, A_BACKSPACE);

            case WXK_RETURN:            // doesn't interfere
                return Action(dc, A_ENTERCELL);

            case WXK_ESCAPE:            // docs say it can be used as a menu accellerator, but it does not trigger from there?
                return Action(dc, A_REFACTOR);

            #ifdef __WXGTK__        // should not be needed... another wxwidgets incompatability
            case WXK_LEFT:  return Action(dc, A_LEFT);
            case WXK_RIGHT: return Action(dc, A_RIGHT);
            case WXK_UP:    return Action(dc, A_UP);
            case WXK_DOWN:  return Action(dc, A_DOWN);
            case WXK_TAB:   return Action(dc, A_NEXT);
            #endif

            default:
            {
                if(k<' ') break;
                if(k!=uk) break; // FIXME: I see no other way to filter out unused control keys (such as F1) being interpreted as unicode keys

                if(!doc->selected.GetEditBox()) doc->Insert();

                if(Unparsed *up = doc->selected.GetEditBox())
                {
                    //c->AddUndo();
                    up->Key(k);
                }
                RedoSelectionRefresh();
            }
        }
        return NULL;
    }

    const char *Action(wxDC &dc, int k)
    {
        switch(k)
        {
            case A_REFACTORAGGR:
                aggressivefactor = true;
                AutoRefactor(doc->rootnode);
                RedoSelectionRefresh();
                return "code restructored aggressively";

            case A_REFACTOR:
                aggressivefactor = false;
                AutoRefactor(doc->rootnode);
                RedoSelectionRefresh();
                return "code restructored";

            case A_RUN:
                //ev.Eval(rootgrid);
                //rootgrid->ResetChildren();
                ClearSelectionRefresh();
                return "evaluation finished";

            case A_UNDO:
                /*if(doc->undolist.size())
                {
                    Undo(dc);
                    return NULL;
                }
                else*/
                {
                    return "nothing more to undo";
                }

            case A_SAVE:   return Save(false);
            case A_SAVEAS: return Save(true);

            case A_EXPXML:   return Export(dc, L"xml",  L"*.xml",  L"Choose XML file to write",  k);
            case A_EXPHTMLT:
            case A_EXPHTMLO: return Export(dc, L"html", L"*.html", L"Choose HTML file to write", k);
            case A_EXPTEXT:  return Export(dc, L"txt",  L"*.txt",  L"Choose Text file to write", k);
            case A_EXPIMAGE: return Export(dc, L"png",  L"*.png",  L"Choose PNG file to write", k);

            case A_IMPXML:
            case A_IMPXMLA:
            case A_IMPTXTI:
            case A_IMPTXTC:
            case A_IMPTXTT:
            {
                wxString fn = ::wxFileSelector(L"Please select file to import:", L"", L"", L"", L"*.*", wxFD_OPEN|wxFD_FILE_MUST_EXIST|wxFD_CHANGE_DIR);
                if(!fn.empty())
                {
                    wxBusyCursor wait;
                    SetFileName(fn.BeforeLast(L'.').Append(L".rs"));
                    ClearSelectionRefresh();
                }
                return NULL;
                //problem:
                wxMessageBox(L"couldn't import file!", fn);
                return "file load error";
            }

            case A_OPEN:
            {
                wxString fn = ::wxFileSelector(L"Please select a Restructor file to load:", L"", L"", L"rs", L"*.rs", wxFD_OPEN|wxFD_FILE_MUST_EXIST|wxFD_CHANGE_DIR);
                return Open(fn);
            }

            case A_CLOSE:
            {
                if(frame->nb->GetPageCount()>1 && !CloseDocument())
                {
                    int p = frame->nb->GetSelection();
                    frame->nb->AdvanceSelection();
                    frame->nb->RemovePage(p);
                }
                return NULL;
            }

            case A_NEW:
            {
                int size = ::wxGetNumberFromUser(L"What size grid would you like to start with?", L"size:", L"New Sheet", 10, 1, 25, frame);
                InitDB(size);
                ClearSelectionRefresh();
                return NULL;
            }

            case A_ABOUT:
            {
                wxAboutDialogInfo info;
                info.SetName(L"Restructor");
                info.SetVersion(wxT(__DATE__));
                info.SetCopyright(L"(C) 2009 Wouter van Oortmerssen");
                info.SetDescription(L"The automatic restructuring programming language");
                wxAboutBox(info);
                return NULL;
            }

            case A_HELP:
                #ifdef __WXMAC__
                    wxLaunchDefaultBrowser(L"file://"+frame->exepath+L"/docs/tutorial.html"); //RbrtPntn
                #else
                    wxLaunchDefaultBrowser(frame->exepath+L"/docs/tutorial.html");
                #endif
                return NULL;

            case A_ZOOMIN:   return "zoomed in (menu)";
            case A_ZOOMOUT:  return "zoomed out (menu)";
            case A_INCSIZE:  return Wheel(dc,  1, false, true, false);
            case A_DECSIZE:  return Wheel(dc, -1, false, true, false);
            case A_INCWIDTH: return Wheel(dc,  1, false, false, true);
            case A_DECWIDTH: return Wheel(dc, -1, false, false, true);

            case A_DEFFONT:
            {
                wxFontData fdat;
                fdat.SetInitialFont(wxFont(g_deftextsize, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL, false, defaultfont));
                wxFontDialog fd(frame, fdat);
                if(fd.ShowModal()==wxID_OK)
                {
                    defaultfont   = fd.GetFontData().GetChosenFont().GetFaceName();
                    //g_deftextsize = fd.GetFontData().GetChosenFont().GetPointSize();  // not a good idea as it makes files look different on different machines, and doesn't give any other effect than just a global resize
                    cfg.Write(L"defaultfont", defaultfont);
                    //rootgrid->ResetChildren();
                    Refresh();
                }
                return NULL;
            }

            case A_PRINT:
            {
                wxPrintDialogData printDialogData(printData);
                wxPrinter printer(&printDialogData);
                MyPrintout printout;
                if(printer.Print(frame, &printout, true))
                {
                    printData = printer.GetPrintDialogData().GetPrintData();
                }
                return NULL;
            }

            case A_PRINTSCALE:
            {
                printscale = ::wxGetNumberFromUser(L"How many pixels wide should a page be? (0 for auto fit)", L"scale:", L"Set Print Scale", 0, 0, 5000, frame);
                return NULL;
            }

            case A_PREVIEW:
            {
                wxPrintDialogData printDialogData(printData);
                wxPrintPreview *preview = new wxPrintPreview(new MyPrintout, new MyPrintout, &printDialogData);
                wxPreviewFrame *pframe = new wxPreviewFrame(preview, frame, L"Print Preview", wxPoint(100, 100), wxSize(600, 650));
                pframe->Centre(wxBOTH);
                pframe->Initialize();
                pframe->Show(true);
                return NULL;
            }

            case A_PAGESETUP:
            {
                pageSetupData = printData;

                wxPageSetupDialog pageSetupDialog(frame, &pageSetupData);
                pageSetupDialog.ShowModal();

                printData = pageSetupDialog.GetPageSetupDialogData().GetPrintData();
                pageSetupData = pageSetupDialog.GetPageSetupDialogData();
                return NULL;
            }

            case A_CUSTCOL:
            {
                return NULL;
            }

            case A_SEARCHNEXT:
            {
                if(!searchstring.Len()) return "no search string";
                return NULL;
            }

            case A_AW1:
            case A_AW2:
            case A_AW3:
            case A_AW4:
            case A_AW5:
            case A_AW6:
            case A_AW7:
                //cfg.Write(L"maxcharsperline", long(maxcharsperline = (k-A_AW1)*20+40));
                return NULL;

            case A_REPLACEALL:
            {
                if(!searchstring.Len()) return "no search";
                return NULL;
            }
        }

        if(!doc->selected.n) return NoSel();

        //Cell *c = selected.GetCell();

        switch(k)
        {
            case A_BACKSPACE:
                if(Unparsed *up = doc->selected.GetEditBox())
                {
                    up->Backspace();
                    Refresh();
                }
                else
                {
                    doc->selected.DelOp(false);
                    RedoSelectionRefresh();
                }
                return NULL;

            case A_DELETE:
                if(Unparsed *up = doc->selected.GetEditBox())
                {
                    up->Delete();
                    Refresh();
                }
                else
                {
                    doc->selected.DelOp(true);
                    RedoSelectionRefresh();
                }
                return NULL;

            case A_CUT:
            case A_COPY:
                return NULL;;

            case A_SELALL:
                //selected.SelAll();
                Refresh();
                return NULL;

            case A_UP:
            case A_DOWN:
            case A_LEFT:
            case A_RIGHT:
                return doc->selected.Cursor(k, false, false, dc);

            case A_MUP:
            case A_MDOWN:
            case A_MLEFT:
            case A_MRIGHT:
                return doc->selected.Cursor(k-A_MUP+A_UP, true, false, dc);

            case A_SUP:
            case A_SDOWN:
            case A_SLEFT:
            case A_SRIGHT:
                return doc->selected.Cursor(k-A_SUP+A_UP, false, true, dc);

            case A_SCLEFT:
            case A_SCRIGHT:
                return doc->selected.Cursor(k-A_SCUP+A_UP, true, true, dc);

            //case A_BOLD:   return selected.g->SetStyle(selected, STYLE_BOLD);
            //case A_ITALIC: return selected.g->SetStyle(selected, STYLE_ITALIC);
            //case A_TT:     return selected.g->SetStyle(selected, STYLE_FIXED);
            //case A_UNDERL: return selected.g->SetStyle(selected, STYLE_UNDERLINE);

            case A_CANCELEDIT:
                //if(selected.TextEdit()) return NULL;
                //if(selected.g->cell->parent) { selected = selected.g->cell->parent->grid->FindCell(selected.g->cell); ScrollOrZoom(dc); }
                return NULL;

            case A_NEWGRID:
                doc->Insert();
                sys->RedoSelectionRefresh();
                return NULL;

            case A_NEWCELL:
            /*
                if(!(c = ThinExpand(c))) return OneCell();
                c->AddUndo();
                c->text.Clear(selected);
                Refresh();
                */
                return NULL;

            case A_PASTE:
                return NULL;

            case A_ENTERCELL:
            /*
                if(!(c = ThinExpand(c))) return OneCell();
                if(selected.TextEdit())
                {
                    selected.Cursor(A_DOWN, false, false, dc, true);
                }
                else
                {
                    selected.EnterEdit(0, c->text.t.Len());
                    Refresh();
                }
                */
                return NULL;

            case A_REPLACEONCE:
            {
                //selected.g->ReplaceStr(frame->replaces->GetValue(), selected);
                return NULL;
            }
        }

        return "internal error: unimplemented operation!";
    }

    void PasteOrDrop()
    {
        //Cell *c = selected.GetCell();
        //if(!(c = ThinExpand(c))) return;

        wxBusyCursor wait;

        switch(doc->dataobjc->GetReceivedFormat().GetType())
        {
            case wxDF_FILENAME:
            {
                const wxArrayString &as = doc->dataobjf->GetFilenames();
                if(as.size())
                {
                    if(as.size()>1) GetSW()->Status("cannot drag & drop more than 1 file");
                    //if(!LoadImageIntoCell(as[0], c)) PasteSingleText(c, as[0]);
                    Refresh();
                }
                break;
            }

            case wxDF_BITMAP:
            case wxDF_DIB:
            case wxDF_TIFF:
                if(doc->dataobji->GetBitmap().GetRefData()!=wxNullBitmap.GetRefData())
                {
                    //SetImageBM(c, doc->dataobji->GetBitmap().ConvertToImage());
                    doc->dataobji->SetBitmap(wxNullBitmap);
                    Refresh();
                }
                break;

            default:    // several text formats
                if(doc->dataobjt->GetText()!=wxEmptyString)
                {
                    doc->dataobjt->SetText(wxEmptyString);
                }
                break;
        }
    }
};
