
#include <../../nrnconf.h>

#include <InterViews/resource.h>
#include "oc2iv.h"
#include "classreg.h"
double (*nrnpy_guigetval)(Object*);
void (*nrnpy_guisetval)(Object*, double);
int (*nrnpy_guigetstr)(Object*, char**);

#include "gui-redirect.h"

Object** (*nrnpy_gui_helper_)(const char* name, Object* obj) = NULL;
double (*nrnpy_object_to_double_)(Object*) = NULL;
Object** (*nrnpy_gui_helper3_)(const char* name, Object* obj, int handle_strptr) = NULL;
char** (*nrnpy_gui_helper3_str_)(const char* name, Object* obj, int handle_strptr) = NULL;

#if HAVE_IV  // to end of file except for a few small fragments.

#include <stdio.h>
#include <string.h>
#include <math.h>
#include <ivstream.h>
#include <ctype.h>
#include <errno.h>

#include <InterViews/box.h>
#include <IV-look/kit.h>
#include <InterViews/event.h>
#include <InterViews/layout.h>
#include <InterViews/style.h>
#include <InterViews/background.h>
#include <InterViews/border.h>
#include <InterViews/dialog.h>
#include <InterViews/printer.h>
#include <InterViews/geometry.h>
#include <InterViews/transformer.h>
#include <InterViews/patch.h>
#include <InterViews/color.h>
#include <InterViews/telltale.h>
#include <InterViews/hit.h>

#include <InterViews/display.h>
#include "mymath.h"
#include "xmenu.h"
#include "datapath.h"
#include "ivoc.h"
#include "bndedval.h"
#include "objcmd.h"
#include "parse.hpp"
#include "utility.h"
#include "scenepic.h"


// The problem this overcomes is that the pick of an input handler normally
// succeeds for a keystroke only if the mouse is over one of the child
// handlers. We want it to succeed whenever it is inside the panel.
class PanelInputHandler: public InputHandler {
  public:
    PanelInputHandler(Glyph*, Style*);
    virtual ~PanelInputHandler();
    virtual void pick(Canvas*, const Allocation&, int depth, Hit&);
    virtual void focus(InputHandler*);
    static bool has_old_focus() {
        bool old = sema_;
        sema_ = false;
        return old;
    }
    static void handle_old_focus();

  private:
    static InputHandler* focus_;
    static bool sema_;
};

void handle_old_focus() {
    PanelInputHandler::handle_old_focus();
}

InputHandler* PanelInputHandler::focus_ = NULL;
bool PanelInputHandler::sema_ = false;

PanelInputHandler::PanelInputHandler(Glyph* g, Style* s)
    : InputHandler(g, s) {}
PanelInputHandler::~PanelInputHandler() {}
void PanelInputHandler::pick(Canvas* c, const Allocation& a, int depth, Hit& h) {
    const Event* e = h.event();
    if (focus_ && e && e->type() == Event::key && focus_->handler()) {
        h.target(depth, this, 0, focus_->handler());
    } else {
        InputHandler::pick(c, a, depth, h);
    }
}
void PanelInputHandler::focus(InputHandler* h) {
    if (focus_ && focus_ != h) {
        if (h) {
            sema_ = true;
        }
        InputHandler* f = focus_;
        focus_ = NULL;
        f->focus_out();
    }
    focus_ = h;
    InputHandler::focus(h);
}

void PanelInputHandler::handle_old_focus() {
    if (focus_) {
        // printf("handle_old_focus %p\n", focus_);
        sema_ = true;
        InputHandler* f = focus_;
        focus_ = NULL;
        f->focus_out();
    }
}

class ValEdLabel: public MonoGlyph {
  public:
    ValEdLabel(Glyph*);
    virtual ~ValEdLabel();
    virtual void draw(Canvas*, const Allocation&) const;
    void state(bool);
    void tts(TelltaleState*);

  private:
    static const Color* color_;
    bool state_;
    TelltaleState* tts_;
};

const Color* ValEdLabel::color_;

ValEdLabel::ValEdLabel(Glyph* g)
    : MonoGlyph(g) {
    state_ = false;
    if (!color_) {
        color_ = Color::lookup(Session::instance()->default_display(), "yellow");
        Resource::ref(color_);
    }
    tts_ = NULL;
}

ValEdLabel::~ValEdLabel() {}
void ValEdLabel::draw(Canvas* c, const Allocation& a) const {
    if (state_) {
        c->fill_rect(a.left(), a.bottom(), a.right(), a.top(), color_);
    }
    MonoGlyph::draw(c, a);
}
void ValEdLabel::state(bool s) {
    if (state_ != s) {
        state_ = s;
        tts_->notify();
    }
}

void ValEdLabel::tts(TelltaleState* t) {
    tts_ = t;  // not reffed
}

static void hoc_ivpanelPlace(Coord, Coord, int scroll = -1);

static String* xvalue_format;

#define Editor_Default "DefaultValueEditor"
#define Editor_Stepper "DefaultValueEditor"


extern int units_on_flag_;
extern Symbol* hoc_get_last_pointer_symbol();
extern "C" double* nrn_recalc_ptr(double*);
void hoc_notify_value() {
    Oc oc;
    oc.notify();
}

void hoc_xpanel() {
    TRY_GUI_REDIRECT_DOUBLE("xpanel", NULL);
    IFGUI
    if (ifarg(1) && hoc_is_str_arg(1)) {  // begin spec
        bool h = false;
        if (ifarg(2)) {
            h = (int) chkarg(2, 0, 1) ? true : false;
        }
        hoc_ivpanel(gargstr(1), h);
    } else {              // map
        int scroll = -1;  // leave up to panel_scroll attribute
        if (ifarg(2)) {
            if (ifarg(3)) {
                scroll = (int) chkarg(3, -1, 1);
            }
            hoc_ivpanelPlace((Coord) *getarg(1), (Coord) *getarg(2), scroll);
        } else {
            if (ifarg(1)) {
                scroll = (int) chkarg(1, -1, 1);
            }
            hoc_ivpanelmap(scroll);
        }
    }
    ENDGUI

    hoc_ret();
    hoc_pushx(0.);
}

void hoc_xmenu() {
    TRY_GUI_REDIRECT_DOUBLE("xmenu", NULL);
    IFGUI
    bool add2menubar = false;
    char* mk = NULL;
    Object* pyact = NULL;
    int i = 2;
    if (ifarg(i)) {
        if (hoc_is_str_arg(i)) {
            mk = gargstr(i);
            ++i;
        } else if (hoc_is_object_arg(i)) {
            pyact = *hoc_objgetarg(i);
            ++i;
        }
        if (ifarg(i)) {
            add2menubar = int(chkarg(i, 0, 1));
        }
    }
    if (ifarg(1)) {
        if (mk || pyact) {
            hoc_ivvarmenu(gargstr(1), mk, add2menubar, pyact);
        } else {
            hoc_ivmenu(gargstr(1), add2menubar);
        }
    } else {
        hoc_ivmenu((char*) 0);
    }
    ENDGUI
    hoc_ret();
    hoc_pushx(0.);
}

void hoc_xbutton() {
    TRY_GUI_REDIRECT_DOUBLE("xbutton", NULL);

    IFGUI
    char* s1;
    s1 = gargstr(1);
    if (ifarg(2)) {
        if (hoc_is_object_arg(2)) {
            hoc_ivbutton(s1, NULL, *hoc_objgetarg(2));
        } else {
            hoc_ivbutton(s1, gargstr(2));
        }
    } else {
        hoc_ivbutton(s1, s1);
    }
    ENDGUI
    hoc_ret();
    hoc_pushx(0.);
}

/*
xstatebutton("prompt",&var [,"action"])
   like xbutton, but var is set to 0 or 1 depending to match the
   telltale state of the button
*/

void hoc_xstatebutton() {
    TRY_GUI_REDIRECT_DOUBLE("xstatebutton", NULL);
    IFGUI
    char *s1, *s2 = (char*) 0;

    s1 = gargstr(1);

    if (hoc_is_object_arg(2)) {
        hoc_ivstatebutton(NULL,
                          s1,
                          NULL,
                          HocStateButton::PALETTE,
                          *hoc_objgetarg(2),
                          ifarg(3) ? *hoc_objgetarg(3) : NULL);
    } else {
        if (ifarg(3)) {
            s2 = gargstr(3);
        }
        hoc_ivstatebutton(hoc_pgetarg(2), s1, s2, HocStateButton::PALETTE);
    }
    ENDGUI
    hoc_ret();
    hoc_pushx(0.);
}


/*
xcheckbox("prompt",&var [,"action"])
   like xbutton, but var is set to 0 or 1 depending to match the
   telltale state of the button
*/

void hoc_xcheckbox() {
    TRY_GUI_REDIRECT_DOUBLE("xcheckbox", NULL);
    IFGUI

    char *s1, *s2 = (char*) 0;

    s1 = gargstr(1);

    if (hoc_is_object_arg(2)) {
        hoc_ivstatebutton(NULL,
                          s1,
                          NULL,
                          HocStateButton::CHECKBOX,
                          *hoc_objgetarg(2),
                          ifarg(3) ? *hoc_objgetarg(3) : 0);
    } else {
        if (ifarg(3)) {
            s2 = gargstr(3);
        }
        hoc_ivstatebutton(hoc_pgetarg(2), s1, s2, HocStateButton::CHECKBOX);
    }
    ENDGUI
    hoc_ret();
    hoc_pushx(0.);
}

void hoc_xradiobutton() {
    TRY_GUI_REDIRECT_DOUBLE("xradiobutton", NULL);

    IFGUI
    char *s1, *s2 = (char*) 0;
    Object* po = NULL;
    bool activate = false;
    s1 = gargstr(1);
    if (ifarg(2)) {
        if (hoc_is_object_arg(2)) {
            po = *hoc_objgetarg(2);
        } else {
            s2 = gargstr(2);
        }
        if (ifarg(3)) {
            activate = (chkarg(3, 0, 1) != 0.);
        }
    } else {
        s2 = s1;
    }
    if (po) {
        hoc_ivradiobutton(s1, NULL, activate, po);
    } else {
        hoc_ivradiobutton(s1, s2, activate);
    }
    ENDGUI
    hoc_ret();
    hoc_pushx(0.);
}

static void hoc_xvalue_helper() {
    IFGUI  // prompt, variable, deflt,action,canrun,usepointer
        char *s1,
        *s2, *s3;
    double* ptr2 = NULL; /*allow variable arg2 to be double* */
    Object* pyvar = NULL;
    Object* pyact = NULL;
    s2 = s3 = NULL;
    s1 = gargstr(1);
    if (ifarg(2)) {
        if (hoc_is_object_arg(2)) {
            pyvar = *hoc_objgetarg(2);
        } else if (hoc_is_pdouble_arg(2)) {
            ptr2 = hoc_pgetarg(2);
        } else {
            s2 = gargstr(2);
        }
    } else {
        s2 = s1;
    }
    bool deflt = false;
    if (ifarg(3) && *getarg(3)) {
        if (*getarg(3) == 2.) {
            if (pyvar) {
                hoc_ivvalue_keep_updated(s1, NULL, pyvar);
            } else {
                hoc_ivvalue_keep_updated(s1, s2);
            }
            return;
        }
        deflt = true;
    }
    bool canRun = false, usepointer = false;
    if (ifarg(4)) {
        if (hoc_is_object_arg(4)) {
            pyact = *hoc_objgetarg(4);
        } else {
            s3 = gargstr(4);
        }
        if (ifarg(5) && *getarg(5)) {
            canRun = true;
        }
        if (ifarg(6) && *getarg(6)) {
            usepointer = true;
        }
    }
    hoc_ivvaluerun_ex(s1, s2, ptr2, pyvar, s3, pyact, deflt, canRun, usepointer);

    ENDGUI
}

void hoc_xfixedvalue() {
    TRY_GUI_REDIRECT_DOUBLE("xfixedvalue", NULL);

    IFGUI  // prompt, variable, deflt,action,canrun,usepointer
        char *s1,
        *s2;
    s1 = gargstr(1);
    if (ifarg(2)) {
        s2 = gargstr(2);
    } else {
        s2 = s1;
    }
    bool deflt = false;
    if (ifarg(3) && *getarg(3)) {
        deflt = true;
    }
    bool usepointer = false;
    if (ifarg(4) && *getarg(4)) {
        usepointer = true;
    }
    hoc_ivfixedvalue(s1, s2, deflt, usepointer);

    ENDGUI
    hoc_ret();
    hoc_pushx(0.);
}

static void hoc_xpvalue_helper() {
    IFGUI  // prompt,variable,deflt,action,canrun
        char *s1,
        *s3;
    double* pd;
    HocSymExtension* extra = NULL;
    Symbol* sym;
    s1 = gargstr(1);
    if (ifarg(2)) {
        pd = hoc_pgetarg(2);
        sym = hoc_get_last_pointer_symbol();
    } else {
        pd = hoc_val_pointer(s1);
        sym = hoc_get_symbol(s1);
    }
    if (sym) {
        extra = sym->extra;
    }
    bool deflt = false;
    if (ifarg(3) && *getarg(3)) {
        deflt = true;
    }
    if (ifarg(4)) {
        s3 = gargstr(4);
        bool canRun = false;
        if (ifarg(5) && *getarg(5)) {
            canRun = true;
        }
        hoc_ivpvaluerun(s1, pd, s3, deflt, canRun, extra);
    } else {
        hoc_ivpvalue(s1, pd, deflt, extra);
    }
    ENDGUI
}

void hoc_xvalue() {
    TRY_GUI_REDIRECT_DOUBLE("xvalue", NULL);

    hoc_xvalue_helper();
    hoc_ret();
    hoc_pushx(0.);
}

void hoc_xpvalue() {
    TRY_GUI_REDIRECT_DOUBLE("xpvalue", NULL);
    hoc_xpvalue_helper();
    hoc_ret();
    hoc_pushx(0.);
}

void hoc_xlabel() {
    TRY_GUI_REDIRECT_DOUBLE("xlabel", NULL);
    IFGUI
    char* s1;
    s1 = gargstr(1);
    hoc_ivlabel(s1);
    ENDGUI
    hoc_ret();
    hoc_pushx(0.);
}

void hoc_xvarlabel() {
    TRY_GUI_REDIRECT_DOUBLE_SEND_STRREF("xvarlabel", NULL);
    IFGUI
    if (hoc_is_object_arg(1)) {
        hoc_ivvarlabel(NULL, *hoc_objgetarg(1));
    } else {
        hoc_ivvarlabel(hoc_pgargstr(1));
    }
    ENDGUI
    hoc_ret();
    hoc_pushx(0.);
}

// ZFM modified to add vertical vs. horizontal
void hoc_xslider() {
    TRY_GUI_REDIRECT_DOUBLE("xslider", NULL);
    IFGUI
    float low = 0, high = 100;
    float resolution = 1;
    int nsteps = 10;
    char* send = NULL;
    Object* pysend = NULL;
    double* pval = NULL;
    Object* pyvar = NULL;
    bool vert = 0;
    if (ifarg(3)) {
        low = *getarg(2);
        high = *getarg(3);
        resolution = (high - low) / 100.;
    }
    int iarg = 4;
    if (ifarg(iarg)) {
        if (hoc_is_str_arg(iarg)) {
            send = gargstr(4);
            ++iarg;
        } else if (hoc_is_object_arg(iarg)) {
            pysend = *hoc_objgetarg(iarg);
            ++iarg;
        }
    }
    if (ifarg(iarg)) {
        vert = int(chkarg(iarg, 0, 1));
    }
    bool slow = false;
    if (ifarg(++iarg)) {
        slow = int(chkarg(iarg, 0, 1));
    }
    if (hoc_is_object_arg(1)) {
        pyvar = *hoc_objgetarg(1);
    } else {
        pval = hoc_pgetarg(1);
    }
    hoc_ivslider(pval, low, high, resolution, nsteps, send, vert, slow, pyvar, pysend);
    ENDGUI
    hoc_ret();
    hoc_pushx(0.);
}


class HocButton: public Button {
  public:
    HocButton(const char*, Glyph*, Style*, TelltaleState*, Action*);
    virtual ~HocButton();
    static HocButton* instance(const char*, Action*);
    virtual void print(Printer*, const Allocation&) const;

  private:
    Glyph* l_;
};
HocButton::HocButton(const char* text, Glyph* g, Style* s, TelltaleState* t, Action* a)
    : Button(g, s, t, a) {
    l_ = WidgetKit::instance()->label(text);
    l_->ref();
}
HocButton::~HocButton() {
    Resource::unref(l_);
}
HocButton* HocButton::instance(const char* s, Action* a) {
    Button* b = WidgetKit::instance()->push_button(s, a);
    b->ref();
    HocButton* hb = new HocButton(s, b->body(), b->style(), b->state(), b->action());
    b->unref();
    return hb;
}
void HocButton::print(Printer* pr, const Allocation& a) const {
    l_->print(pr, a);
}

implementPtrList(HocPanelList, HocPanel);
static HocPanelList* hoc_panel_list;
static HocPanel* curHocPanel;
static HocValEditor* last_fe_constructed_;
static void checkOpenPanel() {
    if (!curHocPanel) {
        hoc_execerror("No panel is open", NULL);
    }
}

declarePtrList(HocMenuList, HocMenu)
implementPtrList(HocMenuList, HocMenu)
/*static*/ class MenuStack {
  public:
    bool isEmpty() {
        return l_.count() == 0;
    }
    void push(HocMenu* m);
    void pop() {
        if (l_.count()) {
            l_.item(0)->unref();
            l_.remove(0);
        }
    }
    Menu* top() {
        return (l_.count()) ? l_.item(0)->menu() : NULL;
    }
    HocItem* hoc_item() {
        return (l_.count()) ? l_.item(0) : NULL;
    }
    void clean();

  private:
    HocMenuList l_;
};
void MenuStack::push(HocMenu* m) {
    m->ref();
    l_.prepend(m);
}
void MenuStack::clean() {
    for (long i = 0; i < l_.count(); i++) {
        l_.item(i)->unref();
    }
    l_.remove_all();
}
static MenuStack* menuStack;
static Menu* hocmenubar;

class OcTelltaleGroup: public TelltaleGroup {
  public:
    OcTelltaleGroup();
    virtual ~OcTelltaleGroup();
    virtual void update(TelltaleState*);
    virtual void remove(TelltaleState*);
    virtual void restore();

  private:
    TelltaleState* previous_;
    TelltaleState* current_;
};

OcTelltaleGroup::OcTelltaleGroup() {
    previous_ = NULL;
    current_ = NULL;
}
OcTelltaleGroup::~OcTelltaleGroup() {}
void OcTelltaleGroup::update(TelltaleState* t) {
    if (t != current_ && t->test(TelltaleState::is_chosen)) {
        previous_ = current_;
        current_ = t;
    }
    TelltaleGroup::update(t);
}
void OcTelltaleGroup::remove(TelltaleState* t) {
    if (previous_ == t) {
        previous_ = NULL;
    }
    if (current_ == t) {
        current_ = NULL;
    }
    TelltaleGroup::remove(t);
}
void OcTelltaleGroup::restore() {
    if (previous_) {
        previous_->set(TelltaleState::is_chosen, true);
    } else if (current_) {
        TelltaleGroup::update(current_);
        current_->set(TelltaleState::is_chosen, false);
        current_ = NULL;
    }
}

class HocRadioAction: public HocAction {
  public:
    HocRadioAction(const char* action, OcTelltaleGroup*, Object* pyact = NULL);
    virtual ~HocRadioAction();
    virtual void help();

  private:
    OcTelltaleGroup* tg_;
};

HocRadioAction::HocRadioAction(const char* action, OcTelltaleGroup* tg, Object* pyact)
    : HocAction(action, pyact) {
    tg_ = tg;
    Resource::ref(tg_);
}

HocRadioAction::~HocRadioAction() {
    Resource::unref(tg_);
}
void HocRadioAction::help() {
    tg_->restore();
    HocAction::help();
}

/*static*/ class HocRadio {
  public:
    HocRadio();
    virtual ~HocRadio();

    OcTelltaleGroup* group() {
        return g_;
    }
    void start();
    void stop();

  private:
    OcTelltaleGroup* g_;
};

HocRadio::HocRadio() {
    g_ = NULL;
}
HocRadio::~HocRadio() {
    Resource::unref(g_);
}
void HocRadio::start() {
    Resource::unref(g_);
    g_ = new OcTelltaleGroup();
    g_->ref();
}

void HocRadio::stop() {
    Resource::unref(g_);
    g_ = NULL;
}

static HocRadio* hoc_radio;

void hoc_ivpanel(const char* name, bool h) {
    if (!hoc_radio) {
        hoc_radio = new HocRadio();
    }
    if (curHocPanel) {
        fprintf(stderr, "%s not closed\n", curHocPanel->getName());
        if (menuStack) {
            menuStack->clean();
        }
        curHocPanel->unref();
        curHocPanel = NULL;
        hoc_execerror("Didn't close the previous panel", NULL);
    } else {
        curHocPanel = new HocPanel(name, h);
        curHocPanel->ref();
    }
    hoc_radio->stop();
}

void hoc_ivpanelmap(int scroll) {
    checkOpenPanel();
    curHocPanel->map_window(scroll);
    curHocPanel->unref();
    curHocPanel = NULL;
    if (menuStack && !menuStack->isEmpty()) {
        fprintf(stderr, "%s menu not closed\n", menuStack->hoc_item()->getStr());
        menuStack->clean();
        hoc_execerror("A menu is still open", 0);
    }
    hoc_radio->stop();
}

void hoc_ivpanelPlace(Coord left, Coord bottom, int scroll) {
    checkOpenPanel();
    curHocPanel->left_ = left;
    curHocPanel->bottom_ = bottom;
    hoc_ivpanelmap(scroll);
}

void hoc_ivbutton(const char* name, const char* action, Object* pyact) {
    checkOpenPanel();
    hoc_radio->stop();
    if (menuStack && !menuStack->isEmpty()) {
        menuStack->top()->append_item(curHocPanel->menuItem(name, action, false, pyact));
    } else {
        curHocPanel->pushButton(name, action, false, pyact);
    }
}

void hoc_ivstatebutton(double* pd,
                       const char* name,
                       const char* action,
                       int style,
                       Object* pyvar,
                       Object* pyact) {
    checkOpenPanel();
    hoc_radio->stop();
    if (menuStack && !menuStack->isEmpty()) {
        menuStack->top()->append_item(curHocPanel->menuStateItem(pd, name, action, pyvar, pyact));
    } else {
        curHocPanel->stateButton(pd, name, action, style, pyvar, pyact);
    }
}

void hoc_ivradiobutton(const char* name, const char* action, bool activate, Object* pyact) {
    checkOpenPanel();
    if (!hoc_radio->group()) {
        hoc_radio->start();
    }
    if (menuStack && !menuStack->isEmpty()) {
        menuStack->top()->append_item(curHocPanel->menuItem(name, action, activate, pyact));
    } else {
        curHocPanel->pushButton(name, action, activate, pyact);
    }
}

void hoc_ivmenu(const char* name, bool add2menubar) {
    if (!menuStack) {
        menuStack = new MenuStack();
    }
    checkOpenPanel();
    hoc_radio->stop();
    if (name) {
        HocMenu* m = curHocPanel->menu(name, add2menubar);
        menuStack->push(m);
    } else {
        curHocPanel->itemAppend("xmenu()");
        menuStack->pop();
    }
}

void hoc_ivvarmenu(const char* name, const char* action, bool add2menubar, Object* pyvar) {
    if (!menuStack) {
        menuStack = new MenuStack();
    }
    checkOpenPanel();
    hoc_radio->stop();
    HocMenu* m = curHocPanel->menu(name, add2menubar);
    HocMenuAction* hma = new HocMenuAction(action, pyvar, m);
    m->item()->action(hma);
}

void hoc_ivvalue_keep_updated(const char* name, const char* variable, Object* pyvar) {
    checkOpenPanel();
    hoc_radio->stop();
    Symbol* s = hoc_get_symbol(variable);
    curHocPanel->valueEd(name,
                         variable,
                         NULL,
                         false,
                         hoc_val_pointer(variable),
                         false,
                         true,
                         (s ? s->extra : NULL),
                         pyvar);
}

void hoc_ivvalue(const char* name, const char* variable, bool deflt, Object* pyvar) {
    hoc_ivvaluerun(name, variable, NULL, deflt, false, false);
}

void hoc_ivfixedvalue(const char* name, const char* variable, bool deflt, bool usepointer) {
    hoc_ivvaluerun(name, variable, NULL, deflt, false, usepointer);
}

void hoc_ivpvalue(const char* name, double* pd, bool deflt, HocSymExtension* extra) {
    hoc_ivpvaluerun(name, pd, 0, deflt, false, extra);
}

void hoc_ivvaluerun(const char* name,
                    const char* variable,
                    const char* action,
                    bool deflt,
                    bool canRun,
                    bool usepointer,
                    Object* pyvar,
                    Object* pyact) {
    hoc_ivvaluerun_ex(name, variable, NULL, pyvar, action, pyact, deflt, canRun, usepointer);
}

void hoc_ivvaluerun_ex(CChar* name,
                       CChar* variable,
                       double* pvar,
                       Object* pyvar,
                       CChar* action,
                       Object* pyact,
                       bool deflt,
                       bool canrun,
                       bool usepointer,
                       HocSymExtension* extra) {
    checkOpenPanel();
    hoc_radio->stop();
    Symbol* s = NULL;
    if (!pvar && !pyvar) {
        s = hoc_get_symbol(variable);
        if (usepointer) {
            pvar = hoc_val_pointer(variable);
        }
    }
    HocSymExtension* xtra = extra;
    if (!xtra) {
        xtra = s ? s->extra : NULL;
    }
    curHocPanel->valueEd(name, variable, action, canrun, pvar, deflt, false, xtra, pyvar, pyact);
}

void hoc_ivpvaluerun(const char* name,
                     double* pd,
                     const char* action,
                     bool deflt,
                     bool canRun,
                     HocSymExtension* extra) {
    checkOpenPanel();
    hoc_radio->stop();
    curHocPanel->valueEd(name, 0, action, canRun, pd, deflt, false, extra);
}

void hoc_ivlabel(const char* s) {
    checkOpenPanel();
    hoc_radio->stop();
    curHocPanel->label(s);
}

void hoc_ivvarlabel(char** s, Object* pyvar) {
    checkOpenPanel();
    hoc_radio->stop();
    curHocPanel->var_label(s, pyvar);
}

// ZFM added vert
void hoc_ivslider(double* pd,
                  float low,
                  float high,
                  float resolution,
                  int nsteps,
                  const char* s,
                  bool vert,
                  bool slow,
                  Object* pyvar,
                  Object* pyact) {
    checkOpenPanel();
    curHocPanel->slider(pd, low, high, resolution, nsteps, s, vert, slow, pyvar, pyact);
}

static char* hideQuote(const char* s) {
    static char buf[200];
    const char* cp1;
    char* cp2;

    cp2 = buf;
    if (s)
        for (cp1 = s; *cp1; cp1++, cp2++) {
            if (*cp1 == '"') {
                *cp2++ = '\\';
            }
            *cp2 = *cp1;
        }
    *cp2 = '\0';
    return buf;
}

static void saveMenuFile() {}

void HocPanel::save_all(ostream&) {
    if (!hoc_panel_list)
        return;

    long i, cnt;

    HocDataPaths* data_paths = new HocDataPaths();
    cnt = hoc_panel_list->count();
    if (hoc_panel_list)
        for (i = 0; i < cnt; ++i) {
            hoc_panel_list->item(i)->data_path(data_paths, true);
        }
    data_paths->search();
    if (hoc_panel_list)
        for (i = 0; i < cnt; ++i) {
            hoc_panel_list->item(i)->data_path(data_paths, false);
        }
    delete data_paths;
}

void HocPanel::update_ptrs() {
    if (!hoc_panel_list)
        return;
    int i, j;
    for (i = 0; i < hoc_panel_list->count(); ++i) {
        HocUpdateItemList& ul = hoc_panel_list->item(i)->elist_;
        for (j = 0; j < ul.count(); ++j) {
            ul.item(j)->update_ptrs();
        }
    }
}

#if MAC
void HocPanel::mac_menubar() {
    int i = 1;
    int mindex = 0;
    printf("menubar 0 %s\n", getName());
    mac_menubar(mindex, i, 0);
}

void HocPanel::mac_menubar(int& mindex, int& i, int m) {
    int mr;
    int mi = 0;
    while (i < ilist_.count()) {
        mr = ilist_.item(i)->mac_menubar(mindex, m, mi);
        ++i;
        ++mi;
        if (mr > m) {
            mac_menubar(mindex, i, mr);
        } else if (mr < m) {
            return;
        }
    }
    return;
}
#endif

void HocPanel::map_window(int scroll) {
    // switch to scrollbox if too many items
    static GlyphIndex maxcnt = -1;
    if (1 || maxcnt == -1) {
        maxcnt = 12;
        Style* s = WidgetKit::instance()->style();
        s->find_attribute("panel_scroll", maxcnt);
    }
    if ((scroll == -1 && box_->count() > maxcnt) || scroll == 1) {
        LayoutKit& lk = *LayoutKit::instance();
        WidgetKit& wk = *WidgetKit::instance();
        ScrollBox* vsb = lk.vscrollbox(box_->count());
        while (box_->count()) {
            vsb->append(box_->component(0));
            box_->remove(0);
        }
        box_->append(lk.hbox(vsb, lk.hspace(4), wk.vscroll_bar(vsb)));
    }

    PrintableWindow* w = OcGlyph::make_window(left_, bottom_);
    w->style(new Style(WidgetKit::instance()->style()));
    w->style()->attribute("name", getName());
    w->map();
}

// HocPanel

implementPtrList(HocUpdateItemList, HocUpdateItem);
implementPtrList(HocItemList, HocItem);

static void var_freed(void* pd, int size) {
    if (hoc_panel_list)
        for (long i = hoc_panel_list->count() - 1; i >= 0; --i) {
            hoc_panel_list->item(i)->check_valid_pointers(pd, size);
        }
}

HocPanel::HocPanel(const char* name, bool h)
    : OcGlyph(NULL) {
    LayoutKit& lk = *LayoutKit::instance();
    WidgetKit& wk = *WidgetKit::instance();
    horizontal_ = h;
    hocmenubar = NULL;
    if (h) {
        box_ = lk.hbox();
    } else {
        box_ = lk.vbox();
    }
    box_->ref();
    body(ih_ = new PanelInputHandler(
             new Background(new Border(lk.margin(lk.hflexible(box_, fil, 0), 3), wk.foreground()),
                            wk.background()),
             wk.style()));
    if (!hoc_panel_list) {
        hoc_panel_list = new HocPanelList;
        Oc oc;
        oc.notify_freed(var_freed);
    }
    hoc_panel_list->append(this);
    item_append(new HocItem(name));
    left_ = -1000.;
    bottom_ = -1000.;
    errno = 0;
}

HocPanel::~HocPanel() {
    long i;
    box_->unref();
    for (i = 0; i < ilist_.count(); i++) {
        ilist_.item(i)->HocItem::unref();
    }
    for (i = 0; i < elist_.count(); i++) {
        elist_.item(i)->HocItem::unref();
    }
    for (i = 0; i < hoc_panel_list->count(); ++i) {
        if (hoc_panel_list->item(i) == this) {
            hoc_panel_list->remove(i);
            break;
        }
    }
    ilist_.remove_all();
    elist_.remove_all();
    //	printf("~HocPanel\n");
}

// HocUpdateItem
HocUpdateItem::HocUpdateItem(const char* name, HocItem* hi)
    : HocItem(name, hi) {}
HocUpdateItem::~HocUpdateItem() {
    HocPanel::keep_updated(this, false);
}
void HocUpdateItem::update_hoc_item() {}
void HocUpdateItem::check_pointer(void*, int) {}
void HocUpdateItem::data_path(HocDataPaths*, bool) {}

// ones that get updated on every doEvents()
HocUpdateItemList* HocPanel::update_list_;

void HocPanel::keep_updated() {
    static int cnt = 0;
    if (update_list_ && (++cnt % 10 == 0)) {
        long i, cnt = update_list_->count();
        if (cnt)
            for (i = 0; i < cnt; ++i) {
                update_list_->item(i)->update_hoc_item();
            }
    }
}
void HocPanel::keep_updated(HocUpdateItem* hui, bool add) {
    if (!update_list_) {
        update_list_ = new HocUpdateItemList();
    }
    if (add) {
        update_list_->append(hui);
    } else {
        for (long i = 0; i < update_list_->count(); ++i) {
            if (update_list_->item(i) == hui) {
                update_list_->remove(i);
                break;
            }
        }
    }
}

void HocPanel::paneltool(const char* name,
                         const char* proc,
                         const char* selact,
                         ScenePicker* sp,
                         Object* pycallback,
                         Object* pyselact) {
    HocCommand* hc = pycallback ? new HocCommand(pycallback) : new HocCommand(proc);
    HocCommandTool* hct = new HocCommandTool(hc);
    HocAction* ha = NULL;
    if (selact || pyselact) {
        ha = new HocAction(selact, pyselact);
    }
    if (curHocPanel && (!menuStack || menuStack->isEmpty())) {
        Button* b = sp->radio_button(name, hct, ha);
        curHocPanel->box()->append(b);
    } else {
        sp->add_radio_menu(gargstr(1), hct, ha);
    }
}

void HocPanel::itemAppend(const char* str) {
    item_append(new HocItem(str));
}

PolyGlyph* HocPanel::box() {
    //	return (PolyGlyph*)(((MonoGlyph*)body())->body());
    return box_;
}

const char* HocPanel::getName() {
    return ilist_.item(0)->getStr();
}

HocItem* HocPanel::hoc_item() {
    return ilist_.item(0);
}

void HocPanel::pushButton(const char* name, const char* action, bool activate, Object* pyact) {
    if (hoc_radio->group()) {
        HocRadioAction* a = new HocRadioAction(action, hoc_radio->group(), pyact);
        Button* b = WidgetKit::instance()->radio_button(hoc_radio->group(), name, a);
        box()->append(b);
        item_append(new HocRadioButton(name, a, hoc_item()));
        if (activate) {
            TelltaleState* tts = b->state();
            tts->set(TelltaleState::is_chosen, true);
            hoc_radio->group()->update(tts);
        }
    } else {
        HocAction* a = new HocAction(action, pyact);
        box()->append(WidgetKit::instance()->push_button(name, a));
        item_append(new HocPushButton(name, a, hoc_item()));
    }
}

HocPushButton::HocPushButton(const char* name, HocAction* a, HocItem* hi)
    : HocItem(name, hi) {
    a_ = a;
    Resource::ref(a);
    a->hoc_item(this);
}
HocPushButton::~HocPushButton() {
    Resource::unref(a_);
}
void HocPushButton::write(ostream& o) {
    char buf[200];
    nrn_assert(snprintf(buf, 200, "xbutton(\"%s\",\"%s\")", getStr(), hideQuote(a_->name())) < 200);
    o << buf << endl;
}

#if MAC
int HocPushButton::mac_menubar(int&, int m, int mi) {
    printf("button item %d in menu %d \"%s\", \"%s\"\n", mi, m, getStr(), hideQuote(a_->name()));
    return m;
}
#endif

HocRadioButton::HocRadioButton(const char* name, HocRadioAction* a, HocItem* hi)
    : HocItem(name, hi) {
    a_ = a;
    Resource::ref(a);
    a->hoc_item(this);
}
HocRadioButton::~HocRadioButton() {
    Resource::unref(a_);
}
void HocRadioButton::write(ostream& o) {
    char buf[200];
    nrn_assert(snprintf(buf, 200, "xradiobutton(\"%s\",\"%s\")", getStr(), hideQuote(a_->name())) <
               200);
    o << buf << endl;
}

#if MAC
int HocRadioButton::mac_menubar(int&, int m, int mi) {
    printf("radio item %d in menu %d \"%s\", \"%s\"\n", mi, m, getStr(), hideQuote(a_->name()));
    return m;
}
#endif

void HocPanel::label(const char* name) {
    box()->append(LayoutKit::instance()->margin(WidgetKit::instance()->label(name), 3));
    item_append(new HocLabel(name));
}

void HocPanel::var_label(char** name, Object* pyvar) {
    HocVarLabel* l = new HocVarLabel(name, box(), pyvar);
    item_append(l);
    elist_.append(l);
    l->ref();
}

// ZFM added vert
void HocPanel::slider(double* pd,
                      float low,
                      float high,
                      float resolution,
                      int nsteps,
                      const char* send,
                      bool vert,
                      bool slow,
                      Object* pyvar,
                      Object* pysend) {
    OcSlider* s = new OcSlider(pd, low, high, resolution, nsteps, send, vert, slow, pyvar, pysend);
    LayoutKit* lk = LayoutKit::instance();
    WidgetKit* wk = WidgetKit::instance();
    if (slow) {
        wk->begin_style("SlowSlider");
    }
    if (vert) {
        box()->append(lk->hflexible(WidgetKit::instance()->vscroll_bar(s->adjustable())));
    } else {
        box()->append(lk->hflexible(WidgetKit::instance()->hscroll_bar(s->adjustable())));
    }
    if (slow) {
        wk->end_style();
    }
    item_append(s);
    elist_.append(s);
    s->ref();
}

HocMenu* HocPanel::menu(const char* name, bool add2menubar) {
    WidgetKit* wk = WidgetKit::instance();
    Menu* m = wk->pulldown();
    MenuItem* mi;
    HocMenu* hm;
    if (menuStack->isEmpty()) {
        Menu* m0;
        if (!add2menubar) {
            hocmenubar = NULL;
        }
        if (hocmenubar) {
            m0 = hocmenubar;
        } else {
            m0 = wk->menubar();
            hocmenubar = m0;
            LayoutKit* lk = LayoutKit::instance();
            box()->append(lk->hbox(m0, lk->hglue()));
        }
        mi = wk->menubar_item(name);
        m0->append_item(mi);
        hm = new HocMenu(name, m, mi, hoc_item(), add2menubar);
    } else {
        mi = K::menu_item(name);
        menuStack->top()->append_item(mi);
        hm = new HocMenu(name, m, mi, menuStack->hoc_item());
    }
    item_append(hm);
    mi->menu(m);
    return hm;
}

MenuItem* HocPanel::menuItem(const char* name, const char* act, bool activate, Object* pyact) {
    MenuItem* mi;
    if (hoc_radio->group()) {
        HocRadioAction* a = new HocRadioAction(act, hoc_radio->group(), pyact);
        mi = K::radio_menu_item(hoc_radio->group(), name);
        mi->action(a);
        item_append(new HocRadioButton(name, a, menuStack->hoc_item()));
        if (activate) {
            TelltaleState* tts = mi->state();
            tts->set(TelltaleState::is_chosen, true);
            hoc_radio->group()->update(tts);
        }
    } else {
        HocAction* a = new HocAction(act, pyact);
        mi = K::menu_item(name);
        mi->action(a);
        item_append(new HocPushButton(name, a, menuStack->hoc_item()));
    }
    return mi;
}

HocMenu::HocMenu(const char* name, Menu* m, MenuItem* mi, HocItem* hi, bool add2menubar)
    : HocItem(name, hi) {
    menu_ = m;
    mi_ = mi;
    add2menubar_ = add2menubar;
    m->ref();
}
HocMenu::~HocMenu() {
    menu_->unref();
}
void HocMenu::write(ostream& o) {
    char buf[200];
    sprintf(buf, "xmenu(\"%s\", %d)", getStr(), add2menubar_);
    o << buf << endl;
}

#if MAC
int HocMenu::mac_menubar(int& mindex, int m, int mi) {
    ++mindex;
    printf("menu %d is item %d in %d %s\n", mindex, mi, m, getStr());
    return mindex;
}
#endif

static Coord xvalue_field_size;

void HocPanel::valueEd(const char* prompt,
                       Object* pyvar,
                       Object* pyact,
                       bool canrun,
                       bool deflt,
                       bool keep_updated) {
    valueEd(prompt, NULL, NULL, canrun, NULL, deflt, keep_updated, NULL, pyvar, pyact);
}

void HocPanel::valueEd(const char* name,
                       const char* variable,
                       const char* action,
                       bool canrun,
                       double* pd,
                       bool deflt,
                       bool keep_updated,
                       HocSymExtension* extra,
                       Object* pyvar,
                       Object* pyact) {
    HocValAction* act;
    if (pyact || action) {
        act = new HocValAction(action, pyact);
    } else {
        act = new HocValAction("");
    }
    ValEdLabel* vel;
    float* limits = 0;
    if (extra && extra->parmlimits) {
        limits = extra->parmlimits;
    }
    if (extra && extra->units && units_on_flag_) {
        char nu[256];
        sprintf(nu, "%s (%s)", name, extra->units);
        vel = new ValEdLabel(WidgetKit::instance()->label(nu));
    } else {
        vel = new ValEdLabel(WidgetKit::instance()->label(name));
    }
    Button* prompt;
    if (canrun) {
        prompt = WidgetKit::instance()->default_button(vel, act);
    } else {
        prompt = WidgetKit::instance()->push_button(vel, act);
    }
    vel->tts(prompt->state());
    HocValEditor* fe;
    Button* def;
    if (deflt) {
        HocDefaultValEditor* dve =
            new HocDefaultValEditor(name, variable, vel, act, pd, canrun, hoc_item(), pyvar);
        def = dve->checkbox();
        fe = dve;
    } else if (keep_updated) {
        fe = new HocValEditorKeepUpdated(name, variable, vel, act, pd, hoc_item(), pyvar);
    } else {
        fe = new HocValEditor(name, variable, vel, act, pd, canrun, hoc_item(), pyvar);
    }
    ih_->append_input_handler(fe->field_editor());
    elist_.append(fe);
    fe->ref();
    act->setFieldSEditor(fe);  // so button can change the editor
    LayoutKit* lk = LayoutKit::instance();
    // from mike_neubig_ivoc_xmenu
    float fct;
    Style* s = WidgetKit::instance()->style();
    if (!s->find_attribute("stepper_size", fct)) {
        fct = 20;
    }
    if (deflt) {
        box()->append(lk->hbox(lk->vcenter(prompt),
                               lk->vcenter(def),
                               lk->vcenter(lk->h_fixed_span(fe->field_editor(), xvalue_field_size)),
                               lk->vcenter(lk->fixed(fe->stepper(), (int) fct, (int) fct))));
    } else {
        box()->append(
            lk->hbox(prompt,
                     lk->h_fixed_span(fe->field_editor(), xvalue_field_size),
                     (fe->stepper() ? lk->fixed(fe->stepper(), int(fct), int(fct)) : NULL)));
    }
    item_append(fe);
    if (limits) {
        fe->setlimits(limits);
    }
    last_fe_constructed_ = fe;
}

void HocPanel::save(ostream& o) {
    o << "{" << endl;
    write(o);
    o << "}" << endl;
}

void HocPanel::write(ostream& o) {
    Oc oc;
    char buf[200];
    long i;
    //	o << "xpanel(\"" << getName() << "\")" << endl;
    sprintf(buf, "xpanel(\"%s\", %d)", getName(), horizontal_);
    o << buf << endl;
    for (i = 1; i < ilist_.count(); i++) {
        ilist_.item(i)->write(o);
    }
    if (has_window()) {
        sprintf(buf, "xpanel(%g,%g)", window()->save_left(), window()->save_bottom());
        o << buf << endl;
    } else {
        o << "xpanel()" << endl;
    }
}

void HocPanel::item_append(HocItem* hi) {
    hi->ref();
    ilist_.append(hi);
}

// HocItem
HocItem::HocItem(const char* str, HocItem* hi)
    : str_(str) {
    help_parent_ = hi;
}
HocItem::~HocItem() {
    //	printf("~HocItem %s\n", str_.string());
}
void HocItem::write(ostream& o) {
    o << str_.string() << endl;
}

#if MAC
int HocItem::mac_menubar(int&, int m, int mi) {
    if (strcmp(getStr(), "xmenu()") == 0) {
        printf("end menu %d\n", m);
        return -1;
    } else {
        printf("invalid menuitem %s\n", getStr());
    }
    return m;
}
#endif

const char* HocItem::getStr() {
    return str_.string();
}

void HocItem::help_parent(HocItem* hi) {
    help_parent_ = hi;  // not reffed
}

void HocItem::help(const char* child) {
    const char* c1;
    char buf[200], *c2 = buf;
    char path[512];
    for (c1 = getStr(); *c1; ++c1) {
        if (isalnum(*c1)) {
            *c2++ = *c1;
        }
    }
    *c2 = '\0';
    if (child) {
        sprintf(path, "%s %s", child, buf);
    } else {
        strcpy(path, buf);
    }
    if (help_parent_) {
        help_parent_->help(path);
    } else {
        Oc::help(path);
    }
}

// HocLabel
HocLabel::HocLabel(const char* s)
    : HocItem(s) {}
HocLabel::~HocLabel() {}
void HocLabel::write(ostream& o) {
    char buf[210];
    sprintf(buf, "xlabel(\"%s\")", hideQuote(getStr()));
    o << buf << endl;
}

#if 0
extern void purify_watch_rw_4(char**);
#endif

// HocVarLabel
HocVarLabel::HocVarLabel(char** cpp, PolyGlyph* pg, Object* pyvar)
    : HocUpdateItem("") {
    // purify_watch_rw_4(cpp);
    pyvar_ = pyvar;
    cpp_ = cpp;
    cp_ = NULL;
    if (pyvar_) {
        hoc_obj_ref(pyvar_);
        (*nrnpy_guigetstr)(pyvar_, &cp_);
    } else {
        cp_ = *cpp_;
    }
    variable_ = NULL;
    p_ = new Patch(LayoutKit::instance()->margin(WidgetKit::instance()->label(cp_), 3));
    p_->ref();
    pg->append(p_);
}

HocVarLabel::~HocVarLabel() {
    p_->unref();
    if (variable_) {
        delete variable_;
    }
    if (pyvar_) {
        hoc_obj_unref(pyvar_);
        if (cp_) {
            delete[] cp_;
        }
    }
}

void HocVarLabel::write(ostream& o) {
    if (variable_ && cpp_) {
        char buf[256];
        sprintf(buf, "xvarlabel(%s)", variable_->string());
        o << buf << endl;
    } else {
        o << "xlabel(\"<can't retrieve>\")" << endl;
    }
}

void HocVarLabel::update_hoc_item() {
    if (pyvar_) {
        if ((*nrnpy_guigetstr)(pyvar_, &cp_)) {
            p_->body(LayoutKit::instance()->margin(WidgetKit::instance()->label(cp_), 3));
            p_->redraw();
            p_->reallocate();
            p_->redraw();
        }
    } else if (cpp_) {
        // printf("update %s\n", cp_);
        if (*cpp_ != cp_) {
            cp_ = *cpp_;
            // printf("replacing with %s\n", cp_);
            p_->body(LayoutKit::instance()->margin(WidgetKit::instance()->label(cp_), 3));
            p_->redraw();
            p_->reallocate();
            p_->redraw();
        }
    } else if (cp_) {
        cp_ = 0;
        // printf("HocVarLabel::update() freed\n");
        p_->body(LayoutKit::instance()->margin(WidgetKit::instance()->label("Free'd"), 3));
        p_->redraw();
        p_->reallocate();
        p_->redraw();
    }
}

HocMenuAction::HocMenuAction(const char* action, Object* pyact, HocMenu* hm)
    : HocAction(action, pyact) {
    hm_ = hm;
    hp_ = NULL;
}
HocMenuAction::~HocMenuAction() {
    Resource::unref(hp_);
}
void HocMenuAction::execute() {
    while (hm_->menu()->item_count()) {
        hm_->menu()->remove_item(0);
    }
    Resource::unref(hp_);
    hp_ = NULL;
    hoc_ivpanel("");
    menuStack->push(hm_);
    HocAction::execute();
    menuStack->pop();
    checkOpenPanel();
    hp_ = curHocPanel;
    curHocPanel = NULL;
    hm_->item()->menu(hm_->menu());
}

// HocAction
HocAction::HocAction(const char* action, Object* pyact) {
    hi_ = NULL;
    if (pyact) {
        action_ = new HocCommand(pyact);
    } else if (action && action[0] != '\0') {
        action_ = new HocCommand(action);
    } else {
        action_ = NULL;
    }
}

HocAction::~HocAction() {
    if (action_) {
        delete action_;
    }
}

void HocAction::hoc_item(HocItem* hi) {
    hi_ = hi;
}

void HocAction::execute() {
    if (Oc::helpmode()) {
        help();
        return;
    }
    PanelInputHandler::handle_old_focus();
    if (action_) {
        action_->audit();
        action_->execute();
    } else {
        Oc oc;
        oc.notify();
    }
}

void HocAction::help() {
    if (hi_) {
        hi_->help();
    }
}

const char* HocAction::name() const {
    if (action_) {
        return action_->name();
    } else {
        return "";
    }
}

#if UseFieldEditor
declareFieldEditorCallback(HocValAction) implementFieldEditorCallback(HocValAction)
#else
declareFieldSEditorCallback(HocValAction);
implementFieldSEditorCallback(HocValAction);
#endif
    // HocValAction
    HocValAction::HocValAction(const char* action, Object* pyact)
    : HocAction(action, pyact) {
    fe_ = NULL;
#if UseFieldEditor
    fea_ = new FieldEditorCallback(HocValAction)(
#else
    fea_ = new FieldSEditorCallback(HocValAction)(
#endif
        this, &HocValAction::accept, NULL);
    fea_->ref();
}

HocValAction::~HocValAction() {
    // printf("~HocValAction\n");
    fea_->unref();
}

void HocValAction::setFieldSEditor(HocValEditor* fe) {
    fe_ = fe;  // but not referenced since this action is referenced by fe
}

void HocValAction::accept(FieldSEditor*) {
    if (fe_->active()) {
        fe_->field_editor()->parent()->focus(NULL);
    } else {
        fe_->evalField();
    }
    fe_->audit();
    HocAction::execute();
}

void HocValAction::execute() {
    if (Oc::helpmode()) {
        fe_->help();
        return;
    }
    accept(fe_->field_editor());
}

class HocDefaultCheckbox: public Button {
  public:
    HocDefaultCheckbox(HocDefaultValEditor*, Glyph*, Style*, TelltaleState*, Action*);
    virtual ~HocDefaultCheckbox();
    static HocDefaultCheckbox* instance(HocDefaultValEditor*);
    virtual void release(const Event&);

  private:
    HocDefaultValEditor* dve_;
};

HocDefaultCheckbox::HocDefaultCheckbox(HocDefaultValEditor* dve,
                                       Glyph* g,
                                       Style* s,
                                       TelltaleState* t,
                                       Action* a)
    : Button(g, s, t, a) {
    dve_ = dve;
}

HocDefaultCheckbox::~HocDefaultCheckbox() {}

HocDefaultCheckbox* HocDefaultCheckbox::instance(HocDefaultValEditor* dve) {
    Glyph* g;
    TelltaleState* t;
    WidgetKit& k = *WidgetKit::instance();
    Style* s;
    k.begin_style("ToggleButton", "Button");
    t = new TelltaleState(TelltaleState::is_enabled | TelltaleState::is_toggle);
    g = k.check_box_look(NULL, t);
    s = k.style();
    HocDefaultCheckbox* cb = new HocDefaultCheckbox(dve, g, s, t, NULL);
    k.end_style();
    return cb;
}

void HocDefaultCheckbox::release(const Event& e) {
    if (Oc::helpmode()) {
        Button::release(e);
    }
    if (e.pointer_button() == Event::right) {
        dve_->def_change(e.pointer_root_x(), e.pointer_root_y());
    }
    Button::release(e);
}

declareActionCallback(HocDefaultValEditor);
implementActionCallback(HocDefaultValEditor);

// HocDefaultValEditor
HocDefaultValEditor::HocDefaultValEditor(const char* name,
                                         const char* variable,
                                         ValEdLabel* prompt,
                                         HocValAction* a,
                                         double* pd,
                                         bool canrun,
                                         HocItem* hi,
                                         Object* pyvar)
    : HocValEditor(name, variable, prompt, a, pd, canrun, hi, pyvar) {
    checkbox_ = HocDefaultCheckbox::instance(this);
    checkbox_->ref();
    checkbox_->action(
        new ActionCallback(HocDefaultValEditor)(this, &HocDefaultValEditor::def_action));
    evalField();
    deflt_ = most_recent_ = get_val();
    vs_ = HocValStepper::instance(this);
    Resource::ref(vs_);
}

HocDefaultValEditor::~HocDefaultValEditor() {
    checkbox_->unref();
    vs_->unref();
}

void HocDefaultValEditor::def_change(float x0, float y0) {
    evalField();
    double x = get_val();
    if (x != deflt_) {
        char form[200], buf[200];
        sprintf(form,
                "Permanently replace default value %s with %s",
                xvalue_format->string(),
                xvalue_format->string());
        sprintf(buf, form, deflt_, x);
        if (boolean_dialog(buf, "Replace", "Cancel", NULL, x0, y0)) {
            deflt_ = x;
            most_recent_ = x;
        }
    }
}

void HocDefaultValEditor::updateField() {
    HocValEditor::updateField();
    TelltaleState* t = checkbox_->state();
    //	printf("telltale flag %x\n", t->flags());
    bool same = (hoc_ac_ == deflt_);
    bool chosen = t->test(TelltaleState::is_chosen);
    if (same && chosen) {
        t->set(TelltaleState::is_chosen, false);
    } else if (!same) {
        most_recent_ = hoc_ac_;
        if (!chosen) {
            t->set(TelltaleState::is_chosen, true);
        }
    }
}

void HocDefaultValEditor::deflt(double d) {
    deflt_ = d;
}

void HocDefaultValEditor::def_action() {
    if (Oc::helpmode()) {
        checkbox_->state()->set(TelltaleState::is_chosen,
                                !checkbox_->state()->test(TelltaleState::is_chosen));
        Oc::help(Editor_Default);
        return;
    }
    bool chosen = checkbox_->state()->test(TelltaleState::is_chosen);
    if (chosen) {
        if (most_recent_ != deflt_) {
            set_val(most_recent_);
        }
    } else {
        double x = get_val();
        if (deflt_ != x) {
            most_recent_ = x;
            set_val(deflt_);
        }
    }
    //	Oc oc;
    //	oc.notifyHocValue();
    updateField();
    exec_action();
}

// HocValEditorKeepUpdated
HocValEditorKeepUpdated::HocValEditorKeepUpdated(const char* name,
                                                 const char* variable,
                                                 ValEdLabel* prompt,
                                                 HocValAction* act,
                                                 double* pd,
                                                 HocItem* hi,
                                                 Object* pyvar)
    : HocValEditor(name, variable, prompt, act, pd, false, hi, pyvar) {
    //	printf("~HocValEditorKeepUpdated\n");
    HocPanel::keep_updated(this, true);
}
HocValEditorKeepUpdated::~HocValEditorKeepUpdated() {
    //	printf("~HocValEditorKeepUpdated\n");
    HocPanel::keep_updated(this, false);
}

// HocEditorForItem g++ doesn't do multiple inheritance of same base classes.

HocEditorForItem::HocEditorForItem(HocValEditor* he, HocValAction* a)
    : FieldSEditor("", WidgetKit::instance(), Session::instance()->style(), a->fea()) {
    hve_ = he;
#ifdef WIN32
    FieldSEditor::focus_out();
#endif
    //	hve_->ref();
}
HocEditorForItem::~HocEditorForItem() {
    //	hve_->unref();
}


static void set_format() {
    static Coord len;
    if (!xvalue_format) {
        xvalue_format = new String("%.5g");
        WidgetKit::instance()->style()->find_attribute("xvalue_format", *xvalue_format);
        char buf[100];
        sprintf(buf, xvalue_format->string(), -8.888888888888888e-18);
        Glyph* g = WidgetKit::instance()->label(buf);
        g->ref();
        Requisition r;
        g->request(r);

        // mike_neubig_ivoc_xmenu
        float fct;
        Style* s = WidgetKit::instance()->style();
        if (!s->find_attribute("xvalue_field_size_increase", fct)) {
            fct = 10;
        }
        xvalue_field_size = r.x_requirement().natural() + fct;
        g->unref();
    }
}

double MyMath::resolution(double x) {
    if (!xvalue_format) {
        set_format();
    }
    char buf[100];
    sprintf(buf, xvalue_format->string(), Math::abs(x));
    char* cp;
    char* least = NULL;
    for (cp = buf; *cp; ++cp) {
        if (isdigit(*cp)) {
            least = cp;
            break;
        }
    }
    for (; *cp; ++cp) {
        if (*cp >= '1' && *cp <= '9') {
            *cp = '0';
            least = cp;
        }
        if (isalpha(*cp)) {
            break;
        }
    }
    assert(least);
    *least = '1';
    double y;
    sscanf(buf, "%lf", &y);
    return y;
}

// HocValEditor
HocValEditor::HocValEditor(const char* name,
                           const char* variable,
                           ValEdLabel* prompt,
                           HocValAction* a,
                           double* pd,
                           bool canrun,
                           HocItem* hi,
                           Object* pyvar)
    : HocUpdateItem(name, hi) {
    if (!xvalue_format) {
        set_format();
    }
    action_ = a;
    fe_ = new HocEditorForItem(this, a);
    fe_->ref();
    Resource::ref(a);
    prompt_ = prompt;
    prompt->ref();
    canrun_ = canrun;
    active_ = false;
    domain_limits_ = NULL;
    variable_ = NULL;
    pyvar_ = pyvar;
    pval_ = NULL;
    if (pd) {
        pval_ = pd;
    }
    if (pyvar) {
        hoc_obj_ref(pyvar);
    } else if (variable) {
        variable_ = new CopyString(variable);
        Symbol* sym = hoc_get_symbol(variable);
        if (sym && sym->extra) {
            domain_limits_ = sym->extra->parmlimits;
        }
    }
    HocValEditor::updateField();
    fe_->focus_out();
}

HocValEditor::~HocValEditor() {
    // printf("~HocValEditor\n");
    if (variable_) {
        delete variable_;
    }
    if (pyvar_) {
        hoc_obj_unref(pyvar_);
    }
    Resource::unref(action_);
    Resource::unref(prompt_);
    fe_->unref();
}

void HocValEditor::setlimits(float* limits) {
    domain_limits_ = limits;
}

void HocValEditor::update_hoc_item() {
    updateField();
}

void HocValEditor::exec_action() {
    if (action_) {
        action_->execute();
    } else {
        Oc oc;
        oc.notify();
    }
}

void HocValEditor::print(Printer* p, const Allocation& a) const {
    //	printf("HocvalEditor::print\n");
    Glyph* l = WidgetKit::instance()->label(*fe_->text());
    l->ref();
    l->print(p, a);
    l->unref();
}

void HocValEditor::set_val(double x) {
    char buf[200];
    if (pyvar_) {
        (*nrnpy_guisetval)(pyvar_, x);
        return;
    }
    hoc_ac_ = x;
    Oc oc;
    if (pval_) {
        *pval_ = hoc_ac_;
    } else if (variable_) {
        sprintf(buf, "%s = hoc_ac_\n", variable_->string());
        oc.run(buf);
    }
}

double HocValEditor::get_val() {
    char buf[200];
    if (pyvar_) {
        return (*nrnpy_guigetval)(pyvar_);
    } else if (pval_) {
        return *pval_;
    } else if (variable_) {
        Oc oc;
        sprintf(buf, "hoc_ac_ = %s\n", variable_->string());
        oc.run(buf);
        return hoc_ac_;
    } else {
        return 0.;
    }
}

double HocValEditor::domain_limits(double val) {
    return check_domain_limits(domain_limits_, val);
}

void HocValEditor::evalField() {
    char buf[200];
    Oc oc;
    sprintf(buf, "hoc_ac_ = %s\n", fe_->text()->string());
    oc.run(buf);
    hoc_ac_ = domain_limits(hoc_ac_);
    set_val(hoc_ac_);
    // prompt_->state()->set(TelltaleState::is_active, false);
    prompt_->state(false);
}

void HocValEditor::audit() {
    char buf[200];
    if (pyvar_) {
        return;
    } else if (variable_) {
        sprintf(buf, "%s = %s\n", variable_->string(), fe_->text()->string());
    } else if (pval_) {
        sprintf(buf, "// %p pointer set to %s\n", pval_, fe_->text()->string());
    }
    hoc_audit_command(buf);
}

void HocValEditor::updateField() {
    if (active_)
        return;
    char buf[200];
    if (pyvar_) {
        hoc_ac_ = get_val();
        sprintf(buf, xvalue_format->string(), hoc_ac_);
    } else if (pval_) {
        sprintf(buf, xvalue_format->string(), *pval_);
        hoc_ac_ = *pval_;
    } else if (variable_) {
        Oc oc;
        sprintf(buf, "hoc_ac_ = %s\n", variable_->string());
        if (oc.run(buf, 0)) {
            strcpy(buf, "Doesn't exist");
        } else {
            sprintf(buf, xvalue_format->string(), hoc_ac_);
        }
    } else {
        sprintf(buf, "Free'd");
    }
    if (strcmp(buf, fe_->text()->string()) != 0) {
        fe_->field(buf);
    }
}

void HocValEditor::write(ostream& o) {
    char buf[200];
    Oc oc;
    if (variable_) {
        sprintf(buf, "hoc_ac_ = %s\n", variable_->string());
        oc.run(buf);
        sprintf(buf, "%s = %g", variable_->string(), hoc_ac_);
    } else if (pval_) {
        sprintf(buf, "/* don't know the hoc path to %g", *pval_);
        return;
    } else {
        sprintf(buf, "/* variable freed */");
        return;
    }
    o << buf << endl;

    int usepointer;
    if (pval_) {
        usepointer = 1;
    } else {
        usepointer = 0;
    }
    nrn_assert(snprintf(buf,
                        200,
                        "xvalue(\"%s\",\"%s\", %d,\"%s\", %d, %d )",
                        getStr(),
                        variable_->string(),
                        hoc_default_val_editor(),
                        hideQuote(action_->name()),
                        (int) canrun_,
                        usepointer) < 200);
    o << buf << endl;
}

const char* HocValEditor::variable() const {
    if (variable_) {
        return variable_->string();
    } else {
        return NULL;
    }
}


void HocValEditorKeepUpdated::write(ostream& o) {
    char buf[200];
    Oc oc;
    sprintf(buf, "hoc_ac_ = %s\n", variable());
    oc.run(buf);
    sprintf(buf, "%s = %g", variable(), hoc_ac_);
    o << buf << endl;
    sprintf(buf, "xvalue(\"%s\",\"%s\", 2 )", getStr(), variable());
    o << buf << endl;
}

void HocEditorForItem::keystroke(const Event& e) {
    bool unfocus = false;
    if (!hve_->active_) {
        return;
    }
    if (Oc::helpmode()) {
        hve_->help();
        return;
    }
    char buf[2];
    if (e.mapkey(buf, 1) > 0)
        switch (buf[0]) {
        case '\n':
        case '\r':
            unfocus = true;
            break;
        case '\033':
            hve_->active_ = false;
            hve_->updateField();
            hve_->active_ = true;
            parent()->focus(NULL);
            return;
        case '\007':
            hve_->active_ = false;
            hve_->updateField();
            hve_->active_ = true;
            return;
        default:
            break;
        }
    FieldSEditor::keystroke(e);
    if (unfocus) {
        parent()->focus(NULL);
    }
}
class HocEditorTempData {
  public:
    void init(const Event&);
    int sn(const Event&);
#if 0
	Coord x_, y_;
	Coord xd_, yd_;
#endif
    int sn_;
    int index_;
    EventButton b_;
};

static HocEditorTempData etd;

void HocEditorTempData::init(const Event& e) {
#if 0
	x_ = e.pointer_x();
	y_ = e.pointer_y();
	xd_ = 1.;
	yd_ = 1.;
#endif
    b_ = e.pointer_button();
    if (b_ == Event::right) {
        sn_ = -1;
    } else {
        sn_ = 1;
    }
}

int HocEditorTempData::sn(const Event&) {
#if 0
	float xnew = e.pointer_x();
	float ynew = e.pointer_y();
#undef RES
#define RES 3
	if (Math::equal(x_, xnew, float(RES)) && Math::equal(y_, ynew, float(RES))) {
		return 0;
	}
	sn_ = ( ((xnew - x_)*xd_ + (ynew - y_)*yd_ >= 0.)) ? sn_ : -sn_;
	xd_ = xnew - x_;
	yd_ = ynew - y_;
	x_ = xnew;
	y_ = ynew;
#endif
    return sn_;
}

void HocEditorForItem::press(const Event& e) {
    if (Oc::helpmode()) {
        hve_->help();
        return;
    }
    //	if (!hve_->active_) {
    //		focus_in();
    //	}
    FieldSEditor::press(e);
#if !UseFieldEditor
    int start;
    FieldSEditor::selection(start, index_);
    etd.init(e);
#endif
}

void HocEditorForItem::drag(const Event& e) {
#ifdef WIN32
    FieldSEditor::drag(e);
#else
    if (etd.b_ == Event::left) {
        FieldSEditor::drag(e);
    } else {
        val_inc(e);
    }
#endif
}

void HocEditorForItem::val_inc(const Event& e) {
    int index = index_;
    int i, sn;
    i = index;
    sn = etd.sn(e);
    if (sn == 0) {
        return;
    }
    const char* s = text()->string();
    char abuf[100];
    char* buf = abuf + 1;
    strcpy(buf, s);
    if (i == strlen(buf)) {
        buf[i] = '0';
        buf[i + 1] = '\0';
    }
    while (i >= 0) {
        if (isdigit(buf[i])) {
            buf[i] = (((buf[i] - '0') + sn + 100) % 10) + '0';
            if (sn == 1 && buf[i] != '0') {
                break;
            } else if (sn == -1 && buf[i] != '9') {
                break;
            }
        }
        --i;
    }
    if (i < 0) {
        if (buf[0] == '-') {
            if (sn == 1) {
                buf[0] = '1';
                abuf[0] = '-';
                buf = abuf;
                ++index_;
            } else {
                strcpy(buf, s);
            }
        } else {
            if (sn == 1) {
                abuf[0] = '1';
                buf = abuf;
                ++index_;
            } else {
                strcpy(buf, s);
            }
        }
    }
    field(buf);
}

void HocEditorForItem::release(const Event& e) {
    FieldSEditor::release(e);
}


InputHandler* HocEditorForItem::focus_in() {
    // printf("HocEditorForItem::focus_in()\n");
    if (Oc::helpmode()) {
        return NULL;
    }
    if (!hve_->active_) {
        // hve_->prompt_->state()->set(TelltaleState::is_active, true);
        hve_->prompt_->state(true);
        hve_->active_ = true;
        return FieldSEditor::focus_in();
    } else {
        return InputHandler::focus_in();
    }
}

void HocEditorForItem::focus_out() {
    if (hve_->active_) {
        hve_->active_ = false;
        // hve_->prompt_->state()->set(TelltaleState::is_active, false);
        hve_->prompt_->state(false);
        hve_->evalField();
    }
    FieldSEditor::focus_out();
    if (PanelInputHandler::has_old_focus()) {
        // printf("old focus out %p\n", (InputHandler*)this);
        hve_->exec_action();
    }
}

void Oc::notifyHocValue() {
    // static int j=0;
    // printf("notifyHocValue %d\n", ++j);
    ParseTopLevel ptl;
    ptl.save();
    if (hoc_panel_list)
        for (long i = hoc_panel_list->count() - 1; i >= 0; --i) {
            hoc_panel_list->item(i)->notifyHocValue();
        }
    ptl.restore();
}

void HocPanel::notifyHocValue() {
    for (long i = elist_.count() - 1; i >= 0; --i) {
        elist_.item(i)->update_hoc_item();
    }
}

void HocPanel::check_valid_pointers(void* v, int size) {
    for (long i = elist_.count() - 1; i >= 0; --i) {
        elist_.item(i)->check_pointer(v, size);
    }
}

void HocValEditor::check_pointer(void* v, int size) {
    if (pval_) {
        double* pd = (double*) v;
        if (size == 1) {
            if (pd != pval_)
                return;
        } else {
            if (pval_ < pd || pval_ >= pd + size)
                return;
        }
        pval_ = 0;
    }
}
void HocVarLabel::check_pointer(void* v, int) {
    char** cpp = (char**) v;
    if (cpp_ == cpp) {
        cpp_ = 0;
    }
}

void HocPanel::data_path(HocDataPaths* hdp, bool append) {
    for (long i = elist_.count() - 1; i >= 0; --i) {
        elist_.item(i)->data_path(hdp, append);
    }
}

void HocValEditor::data_path(HocDataPaths* hdp, bool append) {
    if (!variable_) {
        if (append) {
            hdp->append(pval_);
        } else {
            String* s = hdp->retrieve(pval_);
            if (s) {
                variable_ = new CopyString(s->string());
            }
        }
    }
}

void HocVarLabel::data_path(HocDataPaths* hdp, bool append) {
    if (cpp_ && !variable_) {
        if (append) {
            hdp->append(cpp_);
        } else {
            String* s = hdp->retrieve(cpp_);
            if (s) {
                variable_ = new CopyString(s->string());
            }
        }
    }
}

class StepperMenuAction: public Action {
  public:
    StepperMenuAction(bool, double);
    virtual ~StepperMenuAction();
    virtual void execute();

  private:
    double x_;
    bool geometric_;
};

class StepperMenu: public PopupMenu {
  public:
    StepperMenu();
    virtual ~StepperMenu();
    void stepper(HocValStepper* vs) {
        vs_ = vs;
    }
    HocValStepper* stepper() {
        return vs_;
    }
    virtual bool event(Event&);
    void active(bool b) {
        active_ = b;
    }
    bool active() {
        return active_;
    }

  private:
    bool active_;
    HocValStepper* vs_;
};

StepperMenu* HocValStepper::menu_;

StepperMenuAction::StepperMenuAction(bool b, double x) {
    x_ = x;
    geometric_ = b;
}
StepperMenuAction::~StepperMenuAction() {}
void StepperMenuAction::execute() {
    HocValStepper::menu()->stepper()->default_inc(geometric_, x_);
}
StepperMenu::StepperMenu() {
    WidgetKit& k = *WidgetKit::instance();
    char buf[50];
    active_ = false;
    vs_ = NULL;
    MenuItem* m;
    m = K::menu_item("Res");
    m->action(new StepperMenuAction(false, 0));
    append_item(m);
    m = K::menu_item("*10");
    m->action(new StepperMenuAction(true, 10));
    append_item(m);
    m = K::menu_item("*10^.1");
    m->action(new StepperMenuAction(true, pow(10., .1)));
    append_item(m);
    m = K::menu_item("*e");
    m->action(new StepperMenuAction(true, exp(1.)));
    append_item(m);
    m = K::menu_item("*e^.1");
    m->action(new StepperMenuAction(true, exp(.1)));
    append_item(m);
    m = K::menu_item("*2");
    m->action(new StepperMenuAction(true, 2));
    append_item(m);
    m = K::menu_item("*2^.1");
    m->action(new StepperMenuAction(true, pow(2., .1)));
    append_item(m);
    for (double x = 1000; x > .0005; x /= 10.) {
        sprintf(buf, "+%g", x);
        m = K::menu_item(buf);
        m->action(new StepperMenuAction(false, x));
        append_item(m);
    }
}
StepperMenu::~StepperMenu() {}
bool StepperMenu::event(Event& e) {
    PopupMenu::event(e);
    if (e.type() == Event::up) {
        vs_->menu_up(e);
    }
    return true;
}

/* static */ class NrnUpDown: public Glyph {
  public:
    static NrnUpDown* instance();
    virtual ~NrnUpDown();
    virtual void request(Requisition&) const;
    virtual void draw(Canvas*, const Allocation&) const;

  private:
    NrnUpDown(const Color*);
    static NrnUpDown* instance_;
    const Color* color_;
};

NrnUpDown* NrnUpDown::instance_;

NrnUpDown::NrnUpDown(const Color* c)
    : Glyph() {
    color_ = c;
    Resource::ref(c);
}

NrnUpDown::~NrnUpDown() {
    Resource::unref(color_);
}

NrnUpDown* NrnUpDown::instance() {
    if (!instance_) {
        instance_ = new NrnUpDown(WidgetKit::instance()->foreground());
        instance_->ref();
    }
    return instance_;
}

#if 1
void NrnUpDown::request(Requisition& r) const {
    Requirement x(10);
    Requirement y(20);
    r.require_x(x);
    r.require_y(y);
}
#endif

void NrnUpDown::draw(Canvas* c, const Allocation& a) const {
    Coord x1 = a.left();
    Coord y1 = a.bottom();
    Coord x2 = a.right();
    Coord y2 = a.top();
    // printf("draw %g %g %g %g\n", x1, y1, x2, y2);
    Coord x = (x1 + x2) * .5;
    Coord y = (y1 + y2) * .5;

    c->new_path();
    c->move_to(x1, y + 1);
    c->line_to(x, y2);
    c->line_to(x2, y + 1);
    c->close_path();
    c->fill(color_);

#if 1
    c->new_path();
    c->move_to(x1, y - 1);
    c->line_to(x, y1);
    c->line_to(x2, y - 1);
    c->close_path();
    c->fill(color_);
#endif
}

static Glyph* up_down_mover_look(TelltaleState* t) {
    return WidgetKit::instance()->push_button_look(NrnUpDown::instance(), t);
}

HocValStepper* HocValStepper::instance(HocValEditor* ve) {
    Glyph* g;
    TelltaleState* t;
    WidgetKit& k = *WidgetKit::instance();
    Style* s;
    k.begin_style("UpMover", "Button");
    t = new TelltaleState(TelltaleState::is_enabled);
    g = up_down_mover_look(t);
    //	g = k.up_mover_look(t);
    s = k.style();
    HocValStepper* vs = new HocValStepper(ve, g, s, t);
    k.end_style();
    return vs;
}

HocValStepper::HocValStepper(HocValEditor* ve, Glyph* g, Style* s, TelltaleState* t)
    : Stepper(g, s, t) {
    if (!menu_) {
        menu_ = new StepperMenu();
        menu_->ref();
    }
    hve_ = ve;
    default_inc_ = MyMath::resolution(hve_->get_val());
    geometric_ = false;
}
HocValStepper::~HocValStepper() {}
void HocValStepper::press(const Event& e) {
    steps_ = 0;
    inc_ = default_inc_;
    menu_->active(false);
    if (Oc::helpmode()) {
        return;
    }
    switch (e.pointer_button()) {
    case Event::left:
    case Event::middle: {
        const Allocation& a = allocation();
        if (e.pointer_y() < (a.bottom() + a.top()) * .5) {
            if (geometric_) {
                inc_ = 1. / default_inc_;
            } else {
                inc_ *= -1;
            }
        }
        menu_->stepper(this);
        Stepper::press(e);
        break;
    }
    case Event::right: {
        menu_->active(true);
        menu_->stepper(this);
        Event e1(e);
        menu_->event(e1);
    } break;
    }
}
void HocValStepper::default_inc(bool g, double x) {
    if (x == 0.) {
        default_inc_ = MyMath::resolution(hve_->get_val());
        geometric_ = false;
    } else {
        default_inc_ = x;
        geometric_ = g;
    }
}

void HocValStepper::release(const Event& e) {
    if (Oc::helpmode()) {
        Oc::help(Editor_Stepper);
        return;
    }
    if (menu_->active()) {
        menu_->active(false);
        Button::release(e);
        return;
    }
    Stepper::release(e);
    Oc oc;
    hve_->exec_action();
    oc.notify();
}
void HocValStepper::menu_up(Event& e) {
    menu_->active(true);
#ifdef WIN32
    handler()->event(e);
#else
    e.unread();
#endif
}

void HocValStepper::adjust() {
    double x, y;
    x = hve_->get_val();
    if (geometric_) {
        y = x * inc_;
    } else {
        y = x + inc_;
    }
    y = hve_->domain_limits(y);
    if (steps_ > 0 && x * y <= 0.) {
        y = 0.;
        inc_ = 0.;
    }
    hve_->set_val(y);
    hve_->updateField();
    if (!geometric_ && ((++steps_) % 20) == 0) {
        inc_ *= 10.;
    }
}
void HocValStepper::left() {
    inc_ = default_inc_;
}
void HocValStepper::middle() {
    inc_ = default_inc_;
}
void HocValStepper::right() {}

// OcSlider

// ZFM added vert_
OcSlider::OcSlider(double* pd,
                   float low,
                   float high,
                   float resolution,
                   int nsteps,
                   const char* send,
                   bool vert,
                   bool slow,
                   Object* pyvar,
                   Object* pysend)
    : HocUpdateItem("") {
    resolution_ = resolution;
    variable_ = NULL;
    pval_ = pd;
    pyvar_ = pyvar;
    if (pyvar_) {
        hoc_obj_ref(pyvar_);
    }
    vert_ = vert;
    slow_ = slow;
    bv_ = new BoundedValue(low, high);
    bv_->scroll_incr((high - low) / nsteps);
    if (send) {
        send_ = new HocCommand(send);
    } else if (pysend) {
        send_ = new HocCommand(pysend);
    } else {
        send_ = NULL;
    }
    bv_->attach(Dimension_X, this);
    scrolling_ = false;
}
OcSlider::~OcSlider() {
    if (send_) {
        delete send_;
    }
    delete bv_;
    if (variable_) {
        delete variable_;
    }
    if (pyvar_) {
        hoc_obj_unref(pyvar_);
    }
}
Adjustable* OcSlider::adjustable() {
    return bv_;
}

static double last_send;
void OcSlider::update(Observable*) {
    double x = slider_val();
    if (pval_) {
        *pval_ = x;
    } else if (pyvar_) {
        (*nrnpy_guisetval)(pyvar_, x);
    } else {
        return;
    }
    if (!scrolling_) {
        scrolling_ = true;
        while (Coord(x) != last_send) {
            audit();
            last_send = Coord(x);
            if (send_) {
                send_->execute();
            } else {
                Oc oc;
                oc.notify();
            }
        }
        scrolling_ = false;
    }
}

void OcSlider::audit() {
    char buf[200];
    if (variable_) {
        sprintf(buf, "%s = %g\n", variable_->string(), *pval_);
    } else if (pval_) {
        sprintf(buf, "// %p pointer set to %g\n", pval_, *pval_);
    }
    hoc_audit_command(buf);
    if (send_) {
        send_->audit();
    }
}

double OcSlider::slider_val() {
    double x = double(bv_->cur_lower(Dimension_X));
    x = MyMath::anint(x / resolution_);
    x = x * resolution_;
    if (x > bv_->upper(Dimension_X) - resolution_ / 2.)
        x = bv_->upper(Dimension_X);
    if (x < bv_->lower(Dimension_X) + resolution_ / 2.)
        x = bv_->lower(Dimension_X);

    return x;
}

void OcSlider::update_hoc_item() {
    Coord x = 0.;
    if (pyvar_) {
        x = Coord((*nrnpy_guigetval)(pyvar_));
    } else if (pval_) {
        x = Coord(*pval_);
    } else {
        return;
    }
    if (x != bv_->cur_lower(Dimension_X)) {
        bool old = scrolling_;
        scrolling_ = true;
        bv_->scroll_to(Dimension_X, x);
        scrolling_ = old;
    }
}
void OcSlider::check_pointer(void* v, int size) {
    if (pval_) {
        double* pd = (double*) v;
        if (size == 1) {
            if (pd != pval_)
                return;
        } else {
            if (pval_ < pd || pval_ >= pd + size)
                return;
        }
        pval_ = 0;
    }
}
void OcSlider::data_path(HocDataPaths* hdp, bool append) {
    if (!variable_ && pval_) {
        if (append) {
            hdp->append(pval_);
        } else {
            String* s = hdp->retrieve(pval_);
            if (s) {
                variable_ = new CopyString(s->string());
            }
        }
    }
}
void OcSlider::write(ostream& o) {
    if (variable_) {
        char buf[256];
        if (send_) {
            sprintf(buf,
                    "xslider(&%s, %g, %g, \"%s\", %d, %d)",
                    variable_->string(),
                    bv_->lower(Dimension_X),
                    bv_->upper(Dimension_X),
                    hideQuote(send_->name()),
                    vert_,
                    slow_);
        } else {
            sprintf(buf,
                    "xslider(&%s, %g, %g, %d, %d)",
                    variable_->string(),
                    bv_->lower(Dimension_X),
                    bv_->upper(Dimension_X),
                    vert_,
                    slow_);
        }
        o << buf << endl;
    }
}


//  Button with state

void HocPanel::stateButton(double* pd,
                           const char* name,
                           const char* action,
                           int style,
                           Object* pyvar,
                           Object* pyact) {
    HocAction* act = new HocAction(action, pyact);
    Button* button;
    if (style == HocStateButton::PALETTE) {
        button = WidgetKit::instance()->palette_button(name, act);
    } else {
        button = WidgetKit::instance()->check_box(name, act);
    }
    box()->append(button);
    HocStateButton* hsb = new HocStateButton(pd, name, button, act, style, hoc_item(), pyvar);
    item_append(hsb);
    elist_.append(hsb);
    hsb->ref();
}

declareActionCallback(HocStateButton);
implementActionCallback(HocStateButton);

HocStateButton::HocStateButton(double* pd,
                               const char* text,
                               Button* button,
                               HocAction* action,
                               int style,
                               HocItem* hi,
                               Object* pyvar)
    : HocUpdateItem("", hi) {
    style_ = style;
    pval_ = pd;
    pyvar_ = pyvar;
    if (pyvar_) {
        hoc_obj_ref(pyvar_);
    }
    variable_ = NULL;
    name_ = new CopyString(text);
    action_ = action;
    action->hoc_item(this);
    Resource::ref(action_);

    b_ = button;
    //	b_->ref();	// mutually reffed so don't
    b_->action(new ActionCallback(HocStateButton)(this, &HocStateButton::button_action));
}


HocStateButton::~HocStateButton() {
    if (variable_)
        delete variable_;
    if (pyvar_) {
        hoc_obj_unref(pyvar_);
    }
    delete name_;
    Resource::unref(action_);
    //	only come here when b is being deleted
    //    Resource::unref(b_);
    //   delete b_;
}

void HocStateButton::print(Printer* pr, const Allocation& a) const {
    Glyph* l = WidgetKit::instance()->label(name_->string());
    l->ref();
    l->print(pr, a);
    l->unref();
}

bool HocStateButton::chosen() {
    return b_->state()->test(TelltaleState::is_chosen);
}

void HocStateButton::button_action() {
    if (Oc::helpmode()) {
        help();
        b_->state()->set(TelltaleState::is_chosen, !chosen());
        return;
    }
    if (pval_) {
        TelltaleState* t = b_->state();
        if (chosen() != bool(*pval_)) {
            *pval_ = double(chosen());
        }
    }
    if (pyvar_) {
        TelltaleState* t = b_->state();
        if (chosen() != bool((*nrnpy_guigetval)(pyvar_))) {
            (*nrnpy_guisetval)(pyvar_, double(chosen()));
        }
    }
    if (action_) {
        action_->execute();
    } else {
        Oc oc;
        oc.notify();
    }
}


// set state of button to match variable
void HocStateButton::update_hoc_item() {
    double x = 0.;
    if (pyvar_) {
        x = nrnpy_guigetval(pyvar_);
    } else if (pval_) {
        x = *pval_;
    }
    if (x) {
        b_->state()->set(TelltaleState::is_chosen, true);
    } else {
        b_->state()->set(TelltaleState::is_chosen, false);
    }
}

void HocStateButton::check_pointer(void* v, int size) {
    if (pval_) {
        double* pd = (double*) v;
        if (size == 1) {
            if (pd != pval_)
                return;
        } else {
            if (pval_ < pd || pval_ >= pd + size)
                return;
        }
        pval_ = 0;
    }
}

void HocStateButton::data_path(HocDataPaths* hdp, bool append) {
    if (!variable_ && pval_) {
        if (append) {
            hdp->append(pval_);
        } else {
            String* s = hdp->retrieve(pval_);
            if (s) {
                variable_ = new CopyString(s->string());
            }
        }
    }
}
void HocStateButton::write(ostream& o) {
    if (variable_) {
        char buf[256];
        if (style_ == PALETTE) {
            sprintf(buf,
                    "xstatebutton(\"%s\",&%s,\"%s\")",
                    name_->string(),
                    variable_->string(),
                    hideQuote(action_->name()));
        } else {
            sprintf(buf,
                    "xcheckbox(\"%s\",&%s,\"%s\")",
                    name_->string(),
                    variable_->string(),
                    hideQuote(action_->name()));
        }
        o << buf << endl;
    }
}


// menu item with state


MenuItem* HocPanel::menuStateItem(double* pd,
                                  const char* name,
                                  const char* action,
                                  Object* pyvar,
                                  Object* pyact) {
    MenuItem* mi = WidgetKit::instance()->check_menu_item(name);
    HocAction* act = new HocAction(action, pyact);
    HocStateMenuItem* hsb = new HocStateMenuItem(pd, name, mi, act, hoc_item(), pyvar);
    item_append(hsb);
    elist_.append(hsb);
    hsb->ref();
    return mi;
}


declareActionCallback(HocStateMenuItem);
implementActionCallback(HocStateMenuItem);

HocStateMenuItem::HocStateMenuItem(double* pd,
                                   const char* text,
                                   MenuItem* mi,
                                   HocAction* action,
                                   HocItem* hi,
                                   Object* pyvar)
    : HocUpdateItem("", hi) {
    pval_ = pd;
    pyvar_ = pyvar;
    if (pyvar_) {
        hoc_obj_ref(pyvar_);
    }
    variable_ = NULL;
    name_ = new CopyString(text);
    action_ = action;
    action->hoc_item(this);
    Resource::ref(action_);

    b_ = mi;
    //	Resource::ref(b_);
    b_->action(new ActionCallback(HocStateMenuItem)(this, &HocStateMenuItem::button_action));
}


HocStateMenuItem::~HocStateMenuItem() {
    if (variable_)
        delete variable_;
    delete name_;
    if (pyvar_) {
        hoc_obj_unref(pyvar_);
    }
    Resource::unref(action_);
    //    Resource::unref(b_);
    //   delete b_;
}

void HocStateMenuItem::print(Printer* pr, const Allocation& a) const {
    Glyph* l = WidgetKit::instance()->label(name_->string());
    l->ref();
    l->print(pr, a);
    l->unref();
}

bool HocStateMenuItem::chosen() {
    return b_->state()->test(TelltaleState::is_chosen);
}


void HocStateMenuItem::button_action() {
    if (Oc::helpmode()) {
        help();
        b_->state()->set(TelltaleState::is_chosen, !chosen());
        return;
    }
    if (pval_) {
        TelltaleState* t = b_->state();
        if (chosen() != bool(*pval_)) {
            *pval_ = double(chosen());
        }
    }
    if (pyvar_) {
        TelltaleState* t = b_->state();
        if (chosen() != bool((*nrnpy_guigetval)(pyvar_))) {
            (*nrnpy_guisetval)(pyvar_, double(chosen()));
        }
    }
    if (action_) {
        action_->execute();
    } else {
        Oc oc;
        oc.notify();
    }
}


// set state of button to match variable
void HocStateMenuItem::update_hoc_item() {
    double x = 0.;
    if (pyvar_) {
        x = nrnpy_guigetval(pyvar_);
    } else if (pval_) {
        x = *pval_;
    }
    if (x) {
        b_->state()->set(TelltaleState::is_chosen, true);
    } else {
        b_->state()->set(TelltaleState::is_chosen, false);
    }
}

void HocStateMenuItem::check_pointer(void* v, int size) {
    if (pval_) {
        double* pd = (double*) v;
        if (size == 1) {
            if (pd != pval_)
                return;
        } else {
            if (pval_ < pd || pval_ >= pd + size)
                return;
        }
        pval_ = 0;
    }
}

void HocStateMenuItem::data_path(HocDataPaths* hdp, bool append) {
    if (!variable_ && pval_) {
        if (append) {
            hdp->append(pval_);
        } else {
            String* s = hdp->retrieve(pval_);
            if (s) {
                variable_ = new CopyString(s->string());
            }
        }
    }
}

void HocStateMenuItem::write(ostream& o) {
    if (variable_) {
        char buf[256];
        sprintf(buf,
                "xcheckbox(\"%s\",&%s,\"%s\")",
                name_->string(),
                variable_->string(),
                hideQuote(action_->name()));

        o << buf << endl;
    }
}


#endif  // HAVE_IV
static void* vfe_cons(Object*) {
    TRY_GUI_REDIRECT_OBJ("ValueFieldEditor", NULL);
#if HAVE_IV
    IFGUI
    if (!ifarg(2) || hoc_is_str_arg(2)) {
        hoc_xvalue_helper();
    } else {
        hoc_xpvalue_helper();
    }
    HocValEditor* fe = last_fe_constructed_;
    Resource::ref(fe);
    return (void*) fe;
    ENDGUI
#endif
    return 0;
}
static void vfe_destruct(void* v) {
    TRY_GUI_REDIRECT_NO_RETURN("~ValueFieldEditor", v);
#if HAVE_IV
    IFGUI
    HocValEditor* fe = (HocValEditor*) v;
    Resource::unref(fe);
    ENDGUI
#endif
}
static double vfe_default(void* v) {
    TRY_GUI_REDIRECT_ACTUAL_DOUBLE("ValueFieldEditor.default", v);
    double x = 0.;
#if HAVE_IV
    IFGUI
    if (((HocValEditor*) v)->hoc_default_val_editor()) {
        HocDefaultValEditor* dfe = (HocDefaultValEditor*) v;
        dfe->deflt(x = dfe->get_val());
    }
    ENDGUI
#endif
    return x;
}
static Member_func vfe_members[] = {"default", vfe_default, 0, 0};
void ValueFieldEditor_reg() {
    class2oc("ValueFieldEditor", vfe_cons, vfe_destruct, vfe_members, NULL, NULL, NULL);
}

#if HAVE_IV
void HocValEditor::update_ptrs() {
    update_ptrs_helper(&pval_);
}

void OcSlider::update_ptrs() {
    update_ptrs_helper(&pval_);
}

void HocStateButton::update_ptrs() {
    update_ptrs_helper(&pval_);
}

void HocStateMenuItem::update_ptrs() {
    update_ptrs_helper(&pval_);
}

void HocUpdateItem::update_ptrs_helper(double** p) {
    if (*p) {
        double* pd = nrn_recalc_ptr(*p);
        *p = pd;
    }
}

#endif
