diff options
Diffstat (limited to 'toxy/widget.c')
-rw-r--r-- | toxy/widget.c | 1146 |
1 files changed, 1146 insertions, 0 deletions
diff --git a/toxy/widget.c b/toxy/widget.c new file mode 100644 index 0000000..aff5e32 --- /dev/null +++ b/toxy/widget.c @@ -0,0 +1,1146 @@ +/* Copyright (c) 2003 krzYszcz and others. + * For information on usage and redistribution, and for a DISCLAIMER OF ALL + * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ + +/* LATER think about reloading method for .wid files */ +/* FIXME sink-binding */ + +#include <stdio.h> +#include <string.h> +#include "m_pd.h" +#include "g_canvas.h" +#include "common/loud.h" +#include "common/grow.h" +#include "unstable/forky.h" +#include "hammer/file.h" +#include "common/props.h" +#include "toxy/scriptlet.h" +#include "widgettype.h" +#include "build_counter" + +/* our proxy of the text_class (not in the API), LATER do not cheat */ +static t_class *makeshift_class; + +//#define WIDGET_DEBUG +//#define TOW_DEBUG + +enum { WIDGET_NOUPDATE = 0, WIDGET_RECONFIG, WIDGET_REVIS }; + +typedef struct _towentry +{ + struct _tow *te_tow; + struct _towentry *te_next; +} t_towentry; + +typedef struct _widgetentry +{ + struct _widget *we_widget; + struct _widgetentry *we_next; +} t_widgetentry; + +typedef struct _widget +{ + t_object x_ob; + t_glist *x_glist; /* containing glist */ + t_widgettype *x_typedef; + t_symbol *x_type; /* 1st creation arg: our type */ + t_symbol *x_tkclass; /* Tk widget class */ + t_symbol *x_name; /* 2nd creation arg: our name (common tag) */ + t_symbol *x_cbtarget; /* same, mangled (a target, and a tag) */ + t_symbol *x_rptarget; /* same, further mangled */ + t_symbol *x_cvpathname; /* see widget_getcvpathname() */ + t_props *x_options; /* instance options */ + t_props *x_handlers; /* instance handlers */ + t_props *x_arguments; /* instance arguments */ + t_scriptlet *x_iniscript; /* instance initializer */ + t_scriptlet *x_optscript; /* option scriptlet */ + t_scriptlet *x_auxscript; /* auxiliary scriptlet */ + t_scriptlet *x_transient; /* output buffer */ + t_hammerfile *x_filehandle; + int x_width; + int x_height; + t_symbol *x_background; + int x_hasstate; + int x_update; /* see widget_update() */ + int x_selected; + int x_disabled; + t_clock *x_transclock; + t_towentry *x_towlist; +} t_widget; + +typedef struct _tow +{ + t_object x_ob; + t_glist *x_glist; /* containing glist */ + t_symbol *x_cvremote; /* null if containing glist is our destination */ + t_symbol *x_cvname; + t_symbol *x_type; /* 2nd creation arg: widget's type */ + t_symbol *x_name; /* 3rd creation arg: widget's name */ + t_widgettype *x_typedef; + t_widgetentry *x_widgetlist; + struct _tow *x_next; /* next in the global towlist */ +} t_tow; + +static t_class *widget_class; +static t_class *tow_class; + +/* Global towlist, searched in widget_attach(). There is no global widgetlist, + because a destination glist is searched instead in tow_attach(). */ +static t_tow *towlist = 0; + +static char *widget_propsresolver(t_pd *z, int ac, t_atom *av) +{ + t_widget *x = (t_widget *)z; + int len; + scriptlet_reset(x->x_auxscript); + if (scriptlet_add(x->x_auxscript, 1, 0, ac, av)) + return (scriptlet_getcontents(x->x_auxscript, &len)); + else + return (0); +} + +static t_canvas *widget_cvhook(t_pd *z) +{ + return (glist_getcanvas(((t_widget *)z)->x_glist)); +} + +/* LATER move to scriptlet.c, use the scriptlet interface (.^) */ +static t_symbol *widget_getcvpathname(t_widget *x, t_glist *glist) +{ + t_canvas *cv; + if (glist && glist != x->x_glist) + { + bug("widget_getcvpathname"); + x->x_glist = glist; + } + cv = glist_getcanvas(x->x_glist); + if (cv == x->x_glist) + return (x->x_cvpathname); /* we are not in a gop */ + else + { + char buf[32]; + sprintf(buf, ".x%x.c", (int)cv); + return (gensym(buf)); + } +} + +/* LATER use the scriptlet interface (.-) */ +static t_symbol *widget_getmypathname(t_widget *x, t_glist *glist) +{ + char buf[64]; + t_symbol *cvpathname = widget_getcvpathname(x, glist); + sprintf(buf, "%s.%s%x", cvpathname->s_name, x->x_name->s_name, (int)x); + return (gensym(buf)); +} + +static void widget_postatoms(char *msg, int ac, t_atom *av) +{ + startpost(msg); + while (ac--) + { + if (av->a_type == A_FLOAT) + postfloat(av->a_w.w_float); + else if (av->a_type == A_SYMBOL) + poststring(av->a_w.w_symbol->s_name); + av++; + } + endpost(); +} + +static void widget_transtick(t_widget *x) +{ + glist_delete(x->x_glist, (t_gobj *)x); +} + +/* called from widget__failure(), LATER also bind this to F4 or something */ +static void widget_transedit(t_widget *x) +{ + t_text *newt, *oldt = (t_text *)x; + t_binbuf *bb = binbuf_new(); + int nopt, nbnd, narg; + t_atom *opt = props_getall(x->x_options, &nopt); + t_atom *bnd = props_getall(x->x_handlers, &nbnd); + t_atom *arg = props_getall(x->x_arguments, &narg); + binbuf_addv(bb, "sss", gensym("widget"), x->x_type, x->x_name); + if (narg) binbuf_add(bb, narg, arg); + if (nopt) binbuf_add(bb, nopt, opt); + if (nbnd) binbuf_add(bb, nbnd, bnd); + canvas_setcurrent(x->x_glist); + newt = (t_text *)pd_new(makeshift_class); + newt->te_width = 0; + newt->te_type = T_OBJECT; + newt->te_binbuf = bb; + newt->te_xpix = oldt->te_xpix; + newt->te_ypix = oldt->te_ypix; + glist_add(x->x_glist, &newt->te_g); + glist_noselect(x->x_glist); + glist_select(x->x_glist, &newt->te_g); + gobj_activate(&newt->te_g, x->x_glist, 1); + x->x_glist->gl_editor->e_textdirty = 1; /* force evaluation */ + canvas_unsetcurrent(x->x_glist); + canvas_dirty(x->x_glist, 1); + clock_delay(x->x_transclock, 0); /* LATER rethink */ +} + +/* FIXME x_glist field validation against glist parameter (all handlers) */ + +static void widget_getrect(t_gobj *z, t_glist *glist, + int *xp1, int *yp1, int *xp2, int *yp2) +{ + t_widget *x = (t_widget *)z; + float x1, y1, x2, y2; + x1 = text_xpix((t_text *)x, glist); + y1 = text_ypix((t_text *)x, glist); + x2 = x1 + x->x_width; + y2 = y1 + x->x_height; + *xp1 = x1; + *yp1 = y1; + *xp2 = x2; + *yp2 = y2; +} + +static void widget_displace(t_gobj *z, t_glist *glist, int dx, int dy) +{ + t_widget *x = (t_widget *)z; + t_text *t = (t_text *)z; + t->te_xpix += dx; + t->te_ypix += dy; + if (glist_isvisible(glist)) + sys_vgui("%s move %s %d %d\n", widget_getcvpathname(x, glist)->s_name, + x->x_cbtarget->s_name, dx, dy); + canvas_fixlinesfor(glist_getcanvas(glist), t); +} + +static void widget_select(t_gobj *z, t_glist *glist, int state) +{ + t_widget *x = (t_widget *)z; + char *mypathname = widget_getmypathname(x, glist)->s_name; + if (state) + { + sys_vgui("%s config -bg blue %s\n", mypathname, + (x->x_hasstate ? "-state disabled" : "")); + x->x_selected = 1; + } + else + { + if (x->x_disabled) + sys_vgui("%s config -bg %s\n", mypathname, + (x->x_background ? x->x_background->s_name : "gray")); + else + sys_vgui("%s config -bg %s\n", mypathname, + (x->x_background ? x->x_background->s_name : "gray"), + (x->x_hasstate ? "-state normal" : "")); + x->x_selected = 0; + } +} + +static void widget_delete(t_gobj *z, t_glist *glist) +{ + canvas_deletelinesfor(glist, (t_text *)z); +} + +static void widget_pushoptions(t_widget *x) +{ + char *mypathname = widget_getmypathname(x, x->x_glist)->s_name; + if (scriptlet_evaluate(x->x_optscript, x->x_transient, 0, 0, 0, 0)) + { +#ifdef WIDGET_DEBUG + int sz; + char *dp = scriptlet_getcontents(x->x_transient, &sz); + post("vis: \"%s\"", dp); +#endif + sys_vgui("%s config ", mypathname); + scriptlet_push(x->x_transient); + } + else + { + /* LATER if scriptlet not empty: bug("widget_pushoptions"); */ + } +} + +static void widget_pushinits(t_widget *x) +{ + if (masterwidget_evaluate(x->x_transient, 0, 0, 0, x->x_arguments)) + scriptlet_push(x->x_transient); + else + bug("widget_pushinits (master)"); + if (widgettype_isdefined(x->x_typedef)) + { + if (widgettype_evaluate(x->x_typedef, x->x_transient, 0, + 0, 0, x->x_arguments)) + scriptlet_push(x->x_transient); + else + { + /* LATER if scriptlet not empty: bug("widget_pushinits (type)"); */ + } + } + if (scriptlet_evaluate(x->x_iniscript, x->x_transient, 0, + 0, 0, x->x_arguments)) + scriptlet_push(x->x_transient); + else + { + /* LATER if scriptlet not empty: bug("widget_pushinits (instance)"); */ + } +} + +static void widget_vis(t_gobj *z, t_glist *glist, int vis) +{ + t_widget *x = (t_widget *)z; + t_text *t = (t_text *)z; + char *cvpathname = widget_getcvpathname(x, glist)->s_name; + char *mypathname = widget_getmypathname(x, glist)->s_name; + x->x_update = WIDGET_NOUPDATE; + if (vis) + { + float px1 = text_xpix((t_text *)x, glist); + float py1 = text_ypix((t_text *)x, glist); +#ifndef PD_MINOR_VERSION + rtext_new(glist, t, glist->gl_editor->e_rtext, 0); +#endif + sys_vgui("set ::toxy::itempath %s; set ::toxy::itemtarget %s\n\ + set ::toxy::itemfailure [catch {%s %s}]\n\ + if {$::toxy::itemfailure} {pd %s _failure\\;}\n", + mypathname, x->x_rptarget->s_name, + x->x_tkclass->s_name, mypathname, + x->x_rptarget->s_name); + widget_pushoptions(x); + sys_vgui("if {$::toxy::itemfailure == 0}\ + {%s create window %g %g\ + -anchor nw -window %s -tags {toxy%s %s}}\n", + cvpathname, px1, py1, mypathname, + x->x_name->s_name, x->x_cbtarget->s_name); + widget_pushinits(x); + sys_vgui("if {$::toxy::itemfailure == 0}\ + {pd %s _config %s [%s cget -bg]\ + [winfo reqwidth %s] [winfo reqheight %s]\ + [catch {%s config -state normal}]\\;}\n", + x->x_rptarget->s_name, x->x_rptarget->s_name, + mypathname, mypathname, mypathname, mypathname); + sys_gui("unset ::toxy::itempath; unset ::toxy::itemtarget\n"); + } + else + { +#ifndef PD_MINOR_VERSION + t_rtext *rt = glist_findrtext(glist, t); + if (rt) rtext_free(rt); +#endif + sys_vgui("destroy %s\n", mypathname); + } +} + +static void widget_save(t_gobj *z, t_binbuf *bb) +{ + t_widget *x = (t_widget *)z; + t_text *t = (t_text *)x; + int nopt, nbnd, narg; + t_atom *opt = props_getall(x->x_options, &nopt); + t_atom *bnd = props_getall(x->x_handlers, &nbnd); + t_atom *arg = props_getall(x->x_arguments, &narg); + binbuf_addv(bb, "ssiisss", gensym("#X"), gensym("obj"), + (int)t->te_xpix, (int)t->te_ypix, gensym("widget"), + x->x_type, x->x_name); + if (narg) binbuf_add(bb, narg, arg); + if (nopt) binbuf_add(bb, nopt, opt); + if (nbnd) binbuf_add(bb, nbnd, bnd); + binbuf_addsemi(bb); +} + +/* FIXME */ +static void widget_properties(t_gobj *z, t_glist *glist) +{ + t_widget *x = (t_widget *)z; + t_atom *ap; + int ac, nleft; + char *head = scriptlet_getcontents(x->x_optscript, &nleft); + char buf[MAXPDSTRING + 1]; + buf[MAXPDSTRING] = 0; + sprintf(buf, "%s %s", x->x_type->s_name, x->x_name->s_name); + hammereditor_open(x->x_filehandle, buf); + while (nleft > 0) + { + if (nleft > MAXPDSTRING) + { + strncpy(buf, head, MAXPDSTRING); + head += MAXPDSTRING; + nleft -= MAXPDSTRING; + } + else + { + strncpy(buf, head, nleft); + buf[nleft] = 0; + nleft = 0; + } + hammereditor_append(x->x_filehandle, buf); + } + scriptlet_reset(x->x_auxscript); + ap = props_getall(x->x_handlers, &ac); + if (ac) scriptlet_add(x->x_auxscript, 0, 0, ac, ap); + head = scriptlet_getcontents(x->x_auxscript, &nleft); + hammereditor_append(x->x_filehandle, "\n"); + while (nleft > 0) + { + if (nleft > MAXPDSTRING) + { + strncpy(buf, head, MAXPDSTRING); + head += MAXPDSTRING; + nleft -= MAXPDSTRING; + } + else + { + strncpy(buf, head, nleft); + buf[nleft] = 0; + nleft = 0; + } + hammereditor_append(x->x_filehandle, buf); + } +} + +static t_widgetbehavior widget_behavior = +{ + widget_getrect, + widget_displace, + widget_select, + 0, + widget_delete, + widget_vis, + 0, + FORKY_WIDGETPADDING +}; + +static void widget_update(t_widget *x) +{ + t_atom *ap; + int ac; + scriptlet_reset(x->x_optscript); + ap = props_getall(widgettype_getoptions(x->x_typedef), &ac); + if (ac) scriptlet_add(x->x_optscript, 0, 0, ac, ap); + ap = props_getall(x->x_options, &ac); + if (ac) scriptlet_add(x->x_optscript, 0, 0, ac, ap); + if (x->x_update && + glist_isvisible(x->x_glist)) /* FIXME the condition */ + { + if (x->x_update == WIDGET_REVIS) + { + widget_vis((t_gobj *)x, x->x_glist, 0); + widget_vis((t_gobj *)x, x->x_glist, 1); + } + else widget_pushoptions(x); + x->x_update = WIDGET_NOUPDATE; + } + /* LATER cache handlers */ +} + +static t_symbol *widget_addprops(t_widget *x, t_props *op, + t_symbol *s, int ac, t_atom *av) +{ + if (op) + { + t_symbol *empty; + empty = props_add(op, s, ac, av); + if (empty) + loud_error((t_pd *)x, "no value given for %s '%s'", + props_getname(op), empty->s_name); + widget_update(x); + return (empty); + } + else + { + bug("widget_addprops"); + return (0); + } +} + +static t_symbol *widget_addmessage(t_widget *x, t_symbol *s, int ac, t_atom *av) +{ + t_symbol *empty; + if (!(empty = widget_addprops(x, x->x_options, s, ac, av)) && + !(empty = widget_addprops(x, x->x_handlers, s, ac, av))) + empty = widget_addprops(x, x->x_arguments, s, ac, av); + return (empty); +} + +static void widget_anything(t_widget *x, t_symbol *s, int ac, t_atom *av) +{ + if (s && s != &s_) + { + if (*s->s_name == '-' || *s->s_name == '@' || *s->s_name == '#') + { + t_symbol *empty; + x->x_update = WIDGET_RECONFIG; + if (empty = widget_addmessage(x, s, ac, av)) + loud_errand((t_pd *)x, + "(use 'remove %s' if that is what you want).", + empty->s_name); + } + else + { + /* LATER cache this */ + int hlen; + t_atom *hp; + t_symbol *sel; + char buf[MAXPDSTRING]; + buf[0] = '@'; + strcpy(buf + 1, s->s_name); + sel = gensym(buf); + if (((hp = props_getone(x->x_handlers, sel, &hlen)) || + (hp = props_getone(widgettype_gethandlers(x->x_typedef), + sel, &hlen))) + && hlen > 1) + { + scriptlet_reset(x->x_auxscript); + scriptlet_add(x->x_auxscript, 0, 0, hlen - 1, hp + 1); + if (scriptlet_evaluate(x->x_auxscript, x->x_transient, + 1, ac, av, 0)) + scriptlet_push(x->x_transient); + } + else loud_nomethod((t_pd *)x, s); + } + } +} + +/* LATER cache this */ +static void widget_bang(t_widget *x) +{ + int ac; + t_atom *av; + t_symbol *sel = gensym("@bang"); + if ((av = props_getone(x->x_handlers, sel, &ac)) || + (av = props_getone(widgettype_gethandlers(x->x_typedef), sel, &ac))) + { + if (ac > 1) + { + scriptlet_reset(x->x_transient); + scriptlet_add(x->x_transient, 1, 1, ac - 1, av + 1); + scriptlet_push(x->x_transient); + } + } +} + +/* LATER cache this */ +static void widget_float(t_widget *x, t_float f) +{ + int ac; + t_atom *av; + t_symbol *sel = gensym("@float"); + if ((av = props_getone(x->x_handlers, sel, &ac)) || + (av = props_getone(widgettype_gethandlers(x->x_typedef), sel, &ac))) + { + if (ac > 1) + { + t_atom at; + SETFLOAT(&at, f); + scriptlet_reset(x->x_auxscript); + scriptlet_add(x->x_auxscript, 0, 0, ac - 1, av + 1); + if (scriptlet_evaluate(x->x_auxscript, + x->x_transient, 1, 1, &at, 0)) + scriptlet_push(x->x_transient); + } + } +} + +/* LATER cache this */ +static void widget_symbol(t_widget *x, t_symbol *s) +{ + int ac; + t_atom *av; + t_symbol *sel = gensym("@symbol"); + if ((av = props_getone(x->x_handlers, sel, &ac)) || + (av = props_getone(widgettype_gethandlers(x->x_typedef), sel, &ac))) + { + if (ac > 1) + { + t_atom at; + SETSYMBOL(&at, s); + scriptlet_reset(x->x_auxscript); + scriptlet_add(x->x_auxscript, 0, 0, ac - 1, av + 1); + if (scriptlet_evaluate(x->x_auxscript, + x->x_transient, 1, 1, &at, 0)) + scriptlet_push(x->x_transient); + } + } +} + +static void widget_remove(t_widget *x, t_symbol *s) +{ + if (s) + { + t_props *op; + if (*s->s_name == '-') + op = x->x_options; + else if (*s->s_name == '@') + op = x->x_handlers; + else if (*s->s_name == '#') + op = x->x_arguments; + else + op = 0; + if (op && props_remove(op, s)) + { + x->x_update = WIDGET_REVIS; + widget_update(x); + } + else loud_warning((t_pd *)x, "%s %s has not been specified", + props_getname(op), s->s_name); + } +} + +static void widget_ini(t_widget *x, t_symbol *s, int ac, t_atom *av) +{ + if (ac) + { + scriptlet_reset(x->x_iniscript); + scriptlet_add(x->x_iniscript, 0, 0, ac, av); + } +} +static void widget_tot(t_widget *x, t_symbol *s, int ac, t_atom *av) +{ + if (ac) + { + t_scriptlet *sp = x->x_transient; + scriptlet_reset(sp); + scriptlet_add(sp, 1, 1, ac, av); + scriptlet_push(sp); + } +} + +static void widget_refresh(t_widget *x) +{ + x->x_update = WIDGET_REVIS; + widget_update(x); +} + +static void widget__failure(t_widget *x) +{ + /* LATER pass error message from gui, and report here */ + loud_error((t_pd *)x, "creation failure"); + widget_transedit(x); +} + +static void widget__config(t_widget *x, t_symbol *target, t_symbol *bg, + t_floatarg fw, t_floatarg fh, t_floatarg fst) +{ +#ifdef WIDGET_DEBUG + post("config %d \"%s\" %g %g", bg->s_name, fw, fh); +#endif + x->x_width = (int)fw; + x->x_height = (int)fh; + if (bg != &s_) x->x_background = bg; + x->x_hasstate = ((int)fst == 0); + canvas_fixlinesfor(glist_getcanvas(x->x_glist), (t_text *)x); /* FIXME */ +} + +static void widget__callback(t_widget *x, t_symbol *s, int ac, t_atom *av) +{ + if (ac == 1) + { + if (av->a_type == A_FLOAT) + outlet_float(((t_object *)x)->ob_outlet, av->a_w.w_float); + else if (av->a_type == A_SYMBOL) + outlet_symbol(((t_object *)x)->ob_outlet, av->a_w.w_symbol); + } + else if (ac) + { + if (av->a_type == A_FLOAT) + outlet_list(((t_object *)x)->ob_outlet, &s_list, ac, av); + else if (av->a_type == A_SYMBOL) + outlet_anything(((t_object *)x)->ob_outlet, + av->a_w.w_symbol, ac - 1, av + 1); + } + else outlet_bang(((t_object *)x)->ob_outlet); +} + +/* FIXME this is a hack (see also widget_select) */ +/* FIXME why <Leave> is being issued on button press? */ +static void widget__inout(t_widget *x, t_floatarg f) +{ + if (x->x_disabled) + { + if (!x->x_glist->gl_edit) + { + if (!x->x_selected) + { + char *mypathname = widget_getmypathname(x, x->x_glist)->s_name; + if (x->x_hasstate) + sys_vgui("%s config -state normal\n", mypathname); + } + x->x_disabled = 0; + } + } + else if ((int)f && x->x_glist->gl_edit) + { + char *mypathname = widget_getmypathname(x, x->x_glist)->s_name; + if (x->x_hasstate) + sys_vgui("%s config -state disabled\n", mypathname); + x->x_disabled = 1; + } +} + +static void widget__click(t_widget *x, t_floatarg fx, t_floatarg fy, + t_floatarg fb, t_floatarg fm) +{ + t_text *t = (t_text *)x; + t_atom at[4]; + fx += t->te_xpix; + fy += t->te_ypix; + SETFLOAT(&at[0], fx); + SETFLOAT(&at[1], fy); + SETFLOAT(&at[2], fb); + SETFLOAT(&at[3], fm); + typedmess((t_pd *)x->x_glist, gensym("mouse"), 4, at); + widget__inout(x, 1.); +} + +static void widget__motion(t_widget *x, t_floatarg fx, t_floatarg fy) +{ + t_text *t = (t_text *)x; + t_atom at[3]; + fx += t->te_xpix; + fy += t->te_ypix; + SETFLOAT(&at[0], fx); + SETFLOAT(&at[1], fy); + SETFLOAT(&at[2], 0); + typedmess((t_pd *)x->x_glist, gensym("motion"), 3, at); +} + +int widget_iswidget(t_gobj *g, t_symbol *type, t_symbol *name) +{ + if (*(t_pd *)g == widget_class) + { + t_widget *x = (t_widget *)g; + return ((!type || type == x->x_type) && + (!name || name == x->x_name)); + } + else return (0); +} + +#ifdef WIDGET_DEBUG +static void widget_debug(t_widget *x) +{ + t_symbol *pn = widget_getcvpathname(x, 0); + t_symbol *mn = widget_getmypathname(x, 0); + int sz, i, nopt; + t_atom *ap; + char *bp, *key; + post("containing glist: %x", x->x_glist); + post("cv pathname%s %s", (pn ? ":" : ""), (pn ? pn->s_name : "unknown")); + post("my pathname%s %s", (mn ? ":" : ""), (mn ? mn->s_name : "unknown")); + if (ap = props_getall(widgettype_getoptions(x->x_typedef), &nopt)) + widget_postatoms("default options:", nopt, ap); + if (ap = props_getall(x->x_options, &nopt)) + widget_postatoms("instance options:", nopt, ap); + if (ap = props_getall(widgettype_gethandlers(x->x_typedef), &nopt)) + widget_postatoms("default handlers:", nopt, ap); + if (ap = props_getall(x->x_handlers, &nopt)) + widget_postatoms("instance handlers:", nopt, ap); + if (ap = props_getall(widgettype_getarguments(x->x_typedef), &nopt)) + widget_postatoms("default arguments:", nopt, ap); + if (ap = props_getall(x->x_arguments, &nopt)) + widget_postatoms("instance arguments:", nopt, ap); + post("dictionary:"); + bp = props_firstvalue(x->x_arguments, &key); + while (bp) + { + post("\t%s: \"%s\"", key, bp); + bp = props_nextvalue(x->x_arguments, &key); + } + bp = scriptlet_getcontents(x->x_transient, &sz); + post("transient buffer (size %d):\n\"%s\"", sz, bp); + bp = scriptlet_getcontents(x->x_optscript, &sz); + post("option buffer (size %d):\n\"%s\"", sz, bp); + bp = widgettype_getcontents(x->x_typedef, &sz); + post("type initializer (size %d):\n\"%s\"", sz, bp); + bp = scriptlet_getcontents(x->x_iniscript, &sz); + post("instance initializer (size %d):\n\"%s\"", sz, bp); +} +#endif + +static void widget_attach(t_widget *x); +static void widget_detach(t_widget *x); + +static void widget_free(t_widget *x) +{ + pd_unbind((t_pd *)x, x->x_cbtarget); + pd_unbind((t_pd *)x, x->x_rptarget); + props_freeall(x->x_options); + scriptlet_free(x->x_iniscript); + scriptlet_free(x->x_optscript); + scriptlet_free(x->x_auxscript); + scriptlet_free(x->x_transient); + hammerfile_free(x->x_filehandle); + if (x->x_transclock) clock_free(x->x_transclock); + widget_detach(x); +} + +static void *widget_new(t_symbol *s, int ac, t_atom *av) +{ + t_widget *x = (t_widget *)pd_new(widget_class); + char buf[MAXPDSTRING]; + masterwidget_initialize(); + x->x_type = 0; + x->x_name = 0; + if (ac && av->a_type == A_SYMBOL) + { + x->x_type = av->a_w.w_symbol; + ac--; av++; + } + if (ac && av->a_type == A_SYMBOL) + { + x->x_name = av->a_w.w_symbol; + ac--; av++; + } + /* LATER think about anonymous widgets (single arg, or '.') */ + if (!x->x_type || x->x_type == &s_ || + !x->x_name || x->x_name == &s_) + { + loud_error((t_pd *)x, "bad arguments for a widget"); + loud_errand((t_pd *)x, + "expecting \"widget <type> <name> [properties]\""); + return (0); + } + sprintf(buf, "%s%x", x->x_name->s_name, (int)x); + pd_bind((t_pd *)x, x->x_cbtarget = gensym(buf)); + sprintf(buf, "%s%x.rp", x->x_name->s_name, (int)x); + pd_bind((t_pd *)x, x->x_rptarget = gensym(buf)); + + x->x_typedef = widgettype_get(x->x_type); + if (!(x->x_tkclass = widgettype_tkclass(x->x_typedef))) + x->x_tkclass = x->x_type; + + x->x_iniscript = scriptlet_new((t_pd *)x, x->x_rptarget, x->x_cbtarget, + x->x_name, widget_cvhook); + x->x_optscript = scriptlet_new((t_pd *)x, x->x_rptarget, x->x_cbtarget, + x->x_name, widget_cvhook); + x->x_auxscript = scriptlet_new((t_pd *)x, x->x_rptarget, x->x_cbtarget, + x->x_name, widget_cvhook); + x->x_transient = scriptlet_new((t_pd *)x, x->x_rptarget, x->x_cbtarget, + x->x_name, widget_cvhook); + + x->x_options = props_new((t_pd *)x, "option", "-", 0, 0); + x->x_handlers = props_new((t_pd *)x, "handler", "@", x->x_options, 0); + x->x_arguments = props_new((t_pd *)x, "argument", "#", x->x_options, + widget_propsresolver); + + sprintf(buf, ".^.c.%s%x", x->x_name->s_name, (int)x); + x->x_glist = canvas_getcurrent(); + sprintf(buf, ".x%x.c", (int)x->x_glist); + x->x_cvpathname = gensym(buf); + outlet_new((t_object *)x, &s_anything); + /* LATER consider estimating these, based on widget class and options */ + x->x_width = 50; + x->x_height = 50; + props_clone(x->x_arguments, widgettype_getarguments(x->x_typedef)); + widget_addmessage(x, 0, ac, av); + x->x_filehandle = hammerfile_new((t_pd *)x, 0, 0, 0, 0); + x->x_transclock = clock_new(x, (t_method)widget_transtick); + x->x_background = 0; + x->x_hasstate = 0; + x->x_update = WIDGET_NOUPDATE; + x->x_disabled = 0; + widget_attach(x); + return (x); +} + +static t_glist *tow_getglist(t_tow *x, int complain) +{ + t_glist *glist = + (x->x_cvremote ? + (t_glist *)pd_findbyclass(x->x_cvremote, canvas_class) : x->x_glist); + if (!glist && complain) + loud_error((t_pd *)x, "bad canvas name '%s'", x->x_cvname->s_name); + return (glist); +} + +static void tow_bang(t_tow *x) +{ + t_widgetentry *we; + for (we = x->x_widgetlist; we; we = we->we_next) + widget_bang(we->we_widget); +} + +static void tow_float(t_tow *x, t_float f) +{ + t_widgetentry *we; + for (we = x->x_widgetlist; we; we = we->we_next) + widget_float(we->we_widget, f); +} + +static void tow_symbol(t_tow *x, t_symbol *s) +{ + t_widgetentry *we; + for (we = x->x_widgetlist; we; we = we->we_next) + widget_symbol(we->we_widget, s); +} + +static void tow_anything(t_tow *x, t_symbol *s, int ac, t_atom *av) +{ + t_widgetentry *we; + for (we = x->x_widgetlist; we; we = we->we_next) + typedmess((t_pd *)we->we_widget, s, ac, av); +} + +static void tow__callback(t_tow *x, t_symbol *s, int ac, t_atom *av) +{ + if (ac == 1) + { + if (av->a_type == A_FLOAT) + outlet_float(((t_object *)x)->ob_outlet, av->a_w.w_float); + else if (av->a_type == A_SYMBOL) + outlet_symbol(((t_object *)x)->ob_outlet, av->a_w.w_symbol); + } + else if (ac) + { + if (av->a_type == A_FLOAT) + outlet_list(((t_object *)x)->ob_outlet, &s_list, ac, av); + else if (av->a_type == A_SYMBOL) + outlet_anything(((t_object *)x)->ob_outlet, + av->a_w.w_symbol, ac - 1, av + 1); + } + else outlet_bang(((t_object *)x)->ob_outlet); +} + +static void tow_widgetattach(t_tow *x, t_widget *w) +{ + t_towentry *te = getbytes(sizeof(*te)); + t_widgetentry *we = getbytes(sizeof(*we)); + te->te_tow = x; + te->te_next = w->x_towlist; + w->x_towlist = te; + we->we_widget = w; + we->we_next = x->x_widgetlist; + x->x_widgetlist = we; + pd_bind((t_pd *)x, w->x_cbtarget); +#ifdef TOW_DEBUG + post("%s widget '%s' attached", w->x_type->s_name, w->x_cbtarget->s_name); +#endif +} + +static void tow_widgetdetach(t_tow *x, t_widget *w) +{ + t_widgetentry *we1, *we2; + for (we1 = 0, we2 = x->x_widgetlist; we2; we2 = we2->we_next) + { + if (we2->we_widget == w) + { +#ifdef TOW_DEBUG + post("%s widget '%s' detached by widget's destructor", + w->x_type->s_name, w->x_cbtarget->s_name); +#endif + pd_unbind((t_pd *)x, w->x_cbtarget); + if (we1) + we1->we_next = we2->we_next; + else + x->x_widgetlist = we2->we_next; + freebytes(we2, sizeof(*we2)); + return; + } + we1 = we2; + } + bug("tow_widgetdetach"); +} + +static void widget_attach(t_widget *x) +{ + t_tow *t; + for (t = towlist; t; t = t->x_next) + if (x->x_glist == tow_getglist(t, 0) && + t->x_type == x->x_type && t->x_name == x->x_name) + tow_widgetattach(t, x); +} + +static void widget_detach(t_widget *x) +{ + t_towentry *te; + while (te = x->x_towlist) + { + x->x_towlist = te->te_next; + tow_widgetdetach(te->te_tow, x); + freebytes(te, sizeof(*te)); + } +} + +static void tow_attach(t_tow *x) +{ + t_glist *glist = tow_getglist(x, 0); + if (glist) + { + t_gobj *g; + for (g = glist->gl_list; g; g = g->g_next) + { + if (*(t_pd *)g == widget_class) + { + t_widget *w = (t_widget *)g; + if (w->x_type == x->x_type && w->x_name == x->x_name) + tow_widgetattach(x, w); + } + } +#ifdef TOW_DEBUG + if (!x->x_widgetlist) + post("%s widget '%s' not found", + x->x_type->s_name, x->x_name->s_name); +#endif + } +#ifdef TOW_DEBUG + else post("glist '%s' not found", x->x_cvname->s_name); +#endif +} + +static void tow_detach(t_tow *x) +{ + t_widgetentry *we; + while (we = x->x_widgetlist) + { + t_widget *w = we->we_widget; + t_towentry *te1, *te2; + x->x_widgetlist = we->we_next; + pd_unbind((t_pd *)x, w->x_cbtarget); + freebytes(we, sizeof(*we)); + for (te1 = 0, te2 = w->x_towlist; te2; te2 = te2->te_next) + { + if (te2->te_tow == x) + { +#ifdef TOW_DEBUG + post("%s widget '%s' detached by tow's destructor", + w->x_type->s_name, w->x_cbtarget->s_name); +#endif + if (te1) + te1->te_next = te2->te_next; + else + w->x_towlist = te2->te_next; + freebytes(te2, sizeof(*te2)); + break; + } + te1 = te2; + } + if (!te2) bug("tow_detach"); + } +} + +#ifdef TOW_DEBUG +static void tow_debug(t_tow *x) +{ + t_widgetentry *we; + post("attached widgets:"); + for (we = x->x_widgetlist; we; we = we->we_next) + { + t_widget *w = we->we_widget; + t_towentry *te; + int other = 0, found = 0; + startpost("\t%s %s", w->x_type->s_name, w->x_cbtarget->s_name); + for (te = w->x_towlist; te; te = te->te_next) + if (te->te_tow == x) + found++; + else + other++; + post(" (%d other tow%s)", other, (other == 1 ? "" : "s")); + if (found != 1) post("BUG: listed %d times in widget's towlist", found); + } +} +#endif + +static void tow_free(t_tow *x) +{ + t_tow *t1, *t2; +#ifdef TOW_DEBUG + startpost("updating towlist..."); +#endif + for (t1 = 0, t2 = towlist; t2; t2 = t2->x_next) + { + if (t2 == x) + { + if (t1) + t1->x_next = t2->x_next; + else + towlist = t2->x_next; +#ifdef TOW_DEBUG + post("ok"); +#endif + break; + } + t1 = t2; + } + tow_detach(x); +} + +static void *tow_new(t_symbol *s1, t_symbol *s2, t_symbol *s3) +{ + t_tow *x = (t_tow *)pd_new(tow_class); + char buf[64]; + x->x_glist = canvas_getcurrent(); + if (s1 && s1 != &s_ && strcmp(s1->s_name, ".")) + x->x_cvremote = canvas_makebindsym(x->x_cvname = s1); + else + { + x->x_cvremote = 0; + x->x_cvname = x->x_glist->gl_name; + } + x->x_type = s2; + x->x_name = s3; + outlet_new((t_object *)x, &s_anything); + x->x_widgetlist = 0; + x->x_next = towlist; + towlist = x; + tow_attach(x); + return (x); +} + +void widget_setup(void) +{ + post("beware! this is widget %s, %s %s build...", + TOXY_VERSION, loud_ordinal(TOXY_BUILD), TOXY_RELEASE); + widgettype_setup(); + widget_class = class_new(gensym("widget"), + (t_newmethod)widget_new, + (t_method)widget_free, + sizeof(t_widget), 0, A_GIMME, 0); + class_setwidget(widget_class, &widget_behavior); + forky_setsavefn(widget_class, widget_save); + forky_setpropertiesfn(widget_class, widget_properties); + class_addbang(widget_class, widget_bang); + class_addfloat(widget_class, widget_float); + class_addsymbol(widget_class, widget_symbol); + class_addanything(widget_class, widget_anything); + class_addmethod(widget_class, (t_method)widget_remove, + gensym("remove"), A_SYMBOL, 0); + class_addmethod(widget_class, (t_method)widget_ini, + gensym("ini"), A_GIMME, 0); + class_addmethod(widget_class, (t_method)widget_tot, + gensym("tot"), A_GIMME, 0); + class_addmethod(widget_class, (t_method)widget_refresh, + gensym("refresh"), 0); + class_addmethod(widget_class, (t_method)widget__config, + gensym("_config"), + A_SYMBOL, A_SYMBOL, A_FLOAT, A_FLOAT, A_FLOAT, 0); + class_addmethod(widget_class, (t_method)widget__failure, + gensym("_failure"), 0); + class_addmethod(widget_class, (t_method)widget__callback, + gensym("_cb"), A_GIMME, 0); + class_addmethod(widget_class, (t_method)widget__inout, + gensym("_inout"), A_FLOAT, 0); + class_addmethod(widget_class, (t_method)widget__click, + gensym("_click"), A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, 0); + class_addmethod(widget_class, (t_method)widget__motion, + gensym("_motion"), A_FLOAT, A_FLOAT, 0); +#ifdef WIDGET_DEBUG + class_addmethod(widget_class, (t_method)widget_debug, + gensym("debug"), 0); +#endif + hammerfile_setup(widget_class, 0); + + makeshift_class = class_new(gensym("text"), 0, 0, + sizeof(t_text), + CLASS_NOINLET | CLASS_PATCHABLE, 0); + + tow_class = class_new(gensym("tow"), + (t_newmethod)tow_new, + (t_method)tow_free, + sizeof(t_tow), 0, A_SYMBOL, A_SYMBOL, A_SYMBOL, 0); + class_addbang(tow_class, tow_bang); + class_addfloat(tow_class, tow_float); + class_addsymbol(tow_class, tow_symbol); + class_addanything(tow_class, tow_anything); + class_addmethod(tow_class, (t_method)tow__callback, + gensym("_cb"), A_GIMME, 0); +#ifdef TOW_DEBUG + class_addmethod(tow_class, (t_method)tow_debug, + gensym("debug"), 0); +#endif +} |