/* Copyright (c) 2003-2005 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 */ #include #include #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 "widget.h" #include "build_counter" /* our proxy of the text_class (not in the API), LATER do not cheat */ static t_class *makeshift_class; #ifdef KRZYSZCZ #define WIDGET_DEBUG //#define TOW_DEBUG //#define WIDGET_PROFILE #endif enum { WIDGET_NOVIS = 0, WIDGET_PUSHVIS, 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_symbol *x_cvtarget; /* for gui commands to be (re)sent to */ t_symbol *x_varname; /* tcl variable holding our data */ t_props *x_options; /* instance options */ t_props *x_handlers; /* instance handlers */ t_props *x_arguments; /* instance arguments */ t_props *x_xargs; /* type and instance arguments, resolved */ t_props *x_diffoptions; /* type options minus instance options */ t_props *x_diffhandlers; /* same for handlers */ t_props *x_diffarguments; /* same for arguments */ t_widgethandlers *x_hooks; /* actual handlers (short definitions) */ 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; /* no longer used, LATER rethink */ int x_disabled; int x_selected; int x_update; /* see widget_update() */ int x_vised; int x_constructed; t_clock *x_transclock; t_towentry *x_towlist; } t_widget; typedef struct _tow { t_object x_ob; t_glist *x_glist; /* containing glist */ t_glist *x_targetglist; /* containing or parent glist */ t_symbol *x_cvremote; /* null if targetglist is set */ 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_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 *widget_towlist = 0; static t_symbol *widgetps_mouse; static t_symbol *widgetps_motion; static t_symbol *widgetps_vis; static t_symbol *widgetps_new; static t_symbol *widgetps_free; static t_symbol *widgetps_data; static t_symbol *widgetps_add; static t_symbol *widgetps_delete; static t_symbol *widgetps_set; static t_symbol *widgetps_get; #ifdef WIDGET_PROFILE static double widgetprofile_lasttime; static double widgetprofile_step(void) { double newtime = sys_getrealtime(), delta = newtime - widgetprofile_lasttime; widgetprofile_lasttime = newtime; return (delta); } static int widgetprofile_handlerphase = 0; static double widgetprofile_handlerslice[3]; static double widgetprofile_handlerdelta[2]; static void widgetprofile_handler_enter(void) { widgetprofile_handlerphase = 1; widgetprofile_step(); } static void widgetprofile_handler_eval(void) { widgetprofile_handlerphase = 2; widgetprofile_handlerdelta[0] = widgetprofile_step(); } static void widgetprofile_handler_push(void) { widgetprofile_handlerphase = 3; widgetprofile_handlerdelta[1] = widgetprofile_step(); } static void widgetprofile_handler_quit(void) { if (widgetprofile_handlerphase == 3) { widgetprofile_handlerslice[2] += widgetprofile_step(); widgetprofile_handlerslice[0] += widgetprofile_handlerdelta[0]; widgetprofile_handlerslice[1] += widgetprofile_handlerdelta[1]; } widgetprofile_handlerphase = 0; } static void widget_profile(t_widget *x) { loudbug_post("total time in ms:"); loudbug_post("\thandler get %g", widgetprofile_handlerslice[0] * 1000.); loudbug_post("\thandler eval %g", widgetprofile_handlerslice[1] * 1000.); loudbug_post("\thandler push %g", widgetprofile_handlerslice[2] * 1000.); } #define WIDGETPROFILE_HANDLER_ENTER widgetprofile_handler_enter() #define WIDGETPROFILE_HANDLER_EVAL widgetprofile_handler_eval() #define WIDGETPROFILE_HANDLER_PUSH widgetprofile_handler_push() #define WIDGETPROFILE_HANDLER_QUIT widgetprofile_handler_quit() #else #define WIDGETPROFILE_HANDLER_ENTER #define WIDGETPROFILE_HANDLER_EVAL #define WIDGETPROFILE_HANDLER_PUSH #define WIDGETPROFILE_HANDLER_QUIT #endif /* resolving type and instance arguments into x_xargs */ static char *widget_propsresolver(t_pd *owner, int ac, t_atom *av) { t_widget *x = (t_widget *)owner; 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 *caller) { return (glist_getcanvas(((t_widget *)caller)->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) { loudbug_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%lx.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)); } /* If Tk widget creation fails, gui will send the '_failure' message to the Pd widget object, asking the receiving object to transform itself into a regular text object. Due to the 'bindlist' corruption danger, this cannot be done directly from the '_failure' call, but has to be scheduled through a 'transclock', instead. When the clock fires, the widget object creates, and glist_adds a 'makeshift' text object, then glist_deletes itself. */ /* this lock prevents glist_noselect() from reevaluating failure boxes */ static int widget_transforming = 0; /* LATER also bind this to F4 or something */ static void widget_transtick(t_widget *x) { t_text *newt, *oldt = (t_text *)x; t_binbuf *bb = binbuf_new(); int nopt, nhnd, narg; t_atom *opt = props_getall(x->x_options, &nopt); t_atom *hnd = props_getall(x->x_handlers, &nhnd); t_atom *arg = props_getall(x->x_arguments, &narg); if (widget_transforming++) loudbug_bug("widget_transtick"); 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 (nhnd) binbuf_add(bb, nhnd, hnd); 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; outlet_new(newt, &s_); inlet_new(newt, &newt->ob_pd, &s_, &s_); /* LATER preserve connections (although connected widget is a bad thing) */ glist_add(x->x_glist, &newt->te_g); if (glist_isvisible(x->x_glist)) { 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); glist_delete(x->x_glist, (t_gobj *)x); widget_transforming--; } /* 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; #if 0 loudbug_post("displace %d %d (%d %d -> %d %d)", dx, dy, t->te_xpix, t->te_ypix, t->te_xpix + dx, t->te_ypix + dy); #endif 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, t); } /* LATER handle subitems */ static void widget_select(t_gobj *z, t_glist *glist, int flag) { t_widget *x = (t_widget *)z; char *mypathname = widget_getmypathname(x, glist)->s_name; if (flag) { sys_vgui("%s config -bg blue\n", mypathname); sys_vgui("event generate %s <>\n", mypathname); 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")); sys_vgui("event generate %s <>\n", mypathname); 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, int doit) { char *mypathname = widget_getmypathname(x, x->x_glist)->s_name; if (scriptlet_evaluate(x->x_optscript, x->x_transient, 0, 0, 0, x->x_xargs)) { #ifdef WIDGET_DEBUG int sz; char *dp = scriptlet_getcontents(x->x_transient, &sz); loudbug_post("vis: \"%s\"", dp); #endif if (doit) { sys_vgui("%s config ", mypathname); scriptlet_push(x->x_transient); } else scriptlet_vpush(x->x_transient, "itemoptions"); } else if (!scriptlet_isempty(x->x_optscript)) loudbug_bug("widget_pushoptions"); } static void widget_pushonehook(t_widget *x, t_scriptlet *sp, char *vname) { if (scriptlet_evaluate(sp, x->x_transient, 0, 0, 0, x->x_xargs)) scriptlet_vpush(x->x_transient, vname); else if (!scriptlet_isempty(sp)) loudbug_bug("widget_pushonehook (%s)", vname); } static void widget_pushvishooks(t_widget *x) { if (widgettype_isdefined(x->x_typedef)) { t_widgethandlers *wh = widgettype_getscripts(x->x_typedef); widget_pushonehook(x, widgethandlers_getvis(wh), "longvishook"); } widget_pushonehook(x, widgethandlers_getvis(x->x_hooks), "shortvishook"); } static void widget_pushnewhooks(t_widget *x) { /* LATER master constructor */ if (widgettype_isdefined(x->x_typedef)) { t_widgethandlers *wh = widgettype_getscripts(x->x_typedef); widget_pushonehook(x, widgethandlers_getnew(wh), "longnewhook"); } widget_pushonehook(x, widgethandlers_getnew(x->x_hooks), "shortnewhook"); } static void widget_pushfreehooks(t_widget *x) { /* LATER master destructor */ if (widgettype_isdefined(x->x_typedef)) { t_widgethandlers *wh = widgettype_getscripts(x->x_typedef); widget_pushonehook(x, widgethandlers_getfree(wh), "longfreehook"); } widget_pushonehook(x, widgethandlers_getfree(x->x_hooks), "shortfreehook"); } static void widget_pushdatahooks(t_widget *x, int ac, t_atom *av) { t_scriptlet *sp; WIDGETPROFILE_HANDLER_ENTER; if (!widgettype_isdefined(x->x_typedef) || !(sp = widgethandlers_getdata(widgettype_getscripts(x->x_typedef))) || scriptlet_isempty(sp)) sp = widgethandlers_getdata(x->x_hooks); if (sp) { WIDGETPROFILE_HANDLER_EVAL; if (scriptlet_evaluate(sp, x->x_transient, 0, ac, av, x->x_xargs)) { WIDGETPROFILE_HANDLER_PUSH; scriptlet_push(x->x_transient); } else if (!scriptlet_isempty(sp)) loudbug_bug("widget_pushdatahooks (%s)", (sp == widgethandlers_getdata(x->x_hooks) ? "short" : "long")); } WIDGETPROFILE_HANDLER_QUIT; } static void widget_getconfig(t_widget *x) { sys_vgui("::toxy::item_getconfig %s %s\n", widget_getmypathname(x, x->x_glist)->s_name, x->x_cbtarget->s_name); } 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_NOVIS; 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 widget_pushoptions(x, 0); widget_pushvishooks(x); if (!x->x_constructed) { widget_pushnewhooks(x); x->x_constructed = 1; } sys_vgui("::toxy::item_vis %s %s %s %s %s %s %g %g\n", x->x_tkclass->s_name, mypathname, x->x_cbtarget->s_name, x->x_name->s_name, x->x_varname->s_name, cvpathname, px1, py1); x->x_vised = 1; } else { #ifndef PD_MINOR_VERSION t_rtext *rt = glist_findrtext(glist, t); if (rt) rtext_free(rt); #endif x->x_vised = 0; } } static void widget_save(t_gobj *z, t_binbuf *bb) { t_widget *x = (t_widget *)z; t_text *t = (t_text *)x; int nopt, nhnd, narg; t_atom *opt = props_getall(x->x_options, &nopt); t_atom *hnd = props_getall(x->x_handlers, &nhnd); 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, atom_getsymbol(binbuf_getvec(t->te_binbuf)), x->x_type, x->x_name); if (narg) binbuf_add(bb, narg, arg); if (nopt) binbuf_add(bb, nopt, opt); if (nhnd) binbuf_add(bb, nhnd, hnd); binbuf_addsemi(bb); } static void widget_editorappend(t_widget *x, t_props *pp) { int ac; t_atom *ap; if (ap = props_getfirst(pp, &ac)) { if (pp != x->x_diffoptions) hammereditor_append(x->x_filehandle, "\n"); do { int nleft; char buf[MAXPDSTRING + 1], *head; buf[MAXPDSTRING] = 0; scriptlet_reset(x->x_auxscript); scriptlet_add(x->x_auxscript, 0, 0, ac, ap); head = scriptlet_getcontents(x->x_auxscript, &nleft); 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); } hammereditor_append(x->x_filehandle, "\n"); } while (ap = props_getnext(pp, &ac)); } } static void widget_properties(t_gobj *z, t_glist *glist) { t_widget *x = (t_widget *)z; char buf[MAXPDSTRING]; sprintf(buf, "%s %s", x->x_type->s_name, x->x_name->s_name); hammereditor_open(x->x_filehandle, buf, 0); widget_editorappend(x, x->x_diffoptions); widget_editorappend(x, x->x_options); widget_editorappend(x, x->x_diffhandlers); widget_editorappend(x, x->x_handlers); widget_editorappend(x, x->x_diffarguments); widget_editorappend(x, x->x_arguments); hammereditor_setdirty(x->x_filehandle, 0); } static t_widgetbehavior widget_behavior = { widget_getrect, widget_displace, widget_select, 0, widget_delete, widget_vis, 0, FORKY_WIDGETPADDING }; static void widget_novis(t_widget *x) { sys_vgui("::toxy::item_destroy %s %s\n", widget_getmypathname(x, x->x_glist)->s_name, x->x_varname->s_name); } static void widget_update(t_widget *x, t_props *op) { if (op == x->x_options) { t_atom *ap; int ac; props_diff(x->x_diffoptions, widgettype_getoptions(x->x_typedef), x->x_options); scriptlet_reset(x->x_optscript); ap = props_getall(x->x_diffoptions, &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_novis(x); widget_vis((t_gobj *)x, x->x_glist, 1); } else if (x->x_update == WIDGET_PUSHVIS) { widget_pushoptions(x, 1); widget_getconfig(x); } x->x_update = WIDGET_NOVIS; } } else if (op == x->x_handlers) { props_diff(x->x_diffhandlers, widgettype_gethandlers(x->x_typedef), x->x_handlers); /* This is the only point where mirroring of handlers is performed. We get here both during construction, and after any change in our handlers -- the mirror never stales. */ widgethandlers_reset(x->x_hooks); widgethandlers_fill(x->x_hooks, x->x_diffhandlers); widgethandlers_fill(x->x_hooks, x->x_handlers); } else if (op == x->x_arguments) { props_diff(x->x_diffarguments, widgettype_getarguments(x->x_typedef), x->x_arguments); props_clearvalues(x->x_xargs); props_clonevalues(x->x_xargs, x->x_diffarguments); props_clonevalues(x->x_xargs, x->x_arguments); } } static t_symbol *widget_addprops(t_widget *x, t_props *op, int single, t_props *filter, t_symbol *s, int ac, t_atom *av) { if (op) { t_symbol *empty; empty = props_add(op, single, filter, 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, op); return (empty); } else { loudbug_bug("widget_addprops"); return (0); } } static t_symbol *widget_addmessage(t_widget *x, int unique, t_symbol *s, int ac, t_atom *av) { t_symbol *empty; if (s) { /* FIXME mixed messages */ if (*s->s_name == '-') x->x_update = WIDGET_PUSHVIS; else if (*s->s_name == '#') x->x_update = WIDGET_REVIS; else x->x_update = WIDGET_NOVIS; } /* Instance-type duplicates are not removed, unless 'unique' is set. If it is set, we are called from editorhook, so we assume duplicates were not specified explicitly. In other cases we keep duplicates, because type may change until next time this widget is created or refreshed. */ if (!(empty = widget_addprops(x, x->x_arguments, 0, (unique ? x->x_diffarguments : 0), s, ac, av)) && !(empty = widget_addprops(x, x->x_handlers, 0, (unique ? x->x_diffhandlers : 0), s, ac, av))) empty = widget_addprops(x, x->x_options, 0, (unique ? x->x_diffoptions : 0), 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; if (empty = widget_addmessage(x, 0, s, ac, av)) loud_errand((t_pd *)x, "(use 'remove %s' if that is what you want).", empty->s_name); } else { /* FIXME use long defs too, cf widget_pushdatahooks() */ t_scriptlet *sp; WIDGETPROFILE_HANDLER_ENTER; if (sp = widgethandlers_getother(x->x_hooks, s)) { WIDGETPROFILE_HANDLER_EVAL; if (scriptlet_evaluate(sp, x->x_transient, 1, ac, av, x->x_xargs)) { WIDGETPROFILE_HANDLER_PUSH; scriptlet_push(x->x_transient); } else if (!scriptlet_isempty(sp)) loudbug_bug("widget_anything"); } else if (s == widgetps_vis || s == widgetps_new || s == widgetps_free || s == widgetps_data) loud_error((t_pd *)x, "explicit call of the special handler \"%s\"", s->s_name); else loud_nomethod((t_pd *)x, s); WIDGETPROFILE_HANDLER_QUIT; } } } /* FIXME use long defs too, cf widget_pushdatahooks() */ static void widget_bang(t_widget *x) { t_scriptlet *sp; WIDGETPROFILE_HANDLER_ENTER; sp = widgethandlers_getbang(x->x_hooks); WIDGETPROFILE_HANDLER_EVAL; if (scriptlet_evaluate(sp, x->x_transient, 1, 0, 0, x->x_xargs)) { WIDGETPROFILE_HANDLER_PUSH; scriptlet_push(x->x_transient); } else if (!scriptlet_isempty(sp)) loudbug_bug("widget_bang"); WIDGETPROFILE_HANDLER_QUIT; } /* FIXME use long defs too, cf widget_pushdatahooks() */ static void widget_float(t_widget *x, t_float f) { t_scriptlet *sp; t_atom at; WIDGETPROFILE_HANDLER_ENTER; sp = widgethandlers_getfloat(x->x_hooks); WIDGETPROFILE_HANDLER_EVAL; SETFLOAT(&at, f); if (scriptlet_evaluate(sp, x->x_transient, 1, 1, &at, x->x_xargs)) { WIDGETPROFILE_HANDLER_PUSH; scriptlet_push(x->x_transient); } else if (!scriptlet_isempty(sp)) loudbug_bug("widget_float"); WIDGETPROFILE_HANDLER_QUIT; } /* FIXME use long defs too, cf widget_pushdatahooks() */ static void widget_symbol(t_widget *x, t_symbol *s) { t_scriptlet *sp; t_atom at; WIDGETPROFILE_HANDLER_ENTER; sp = widgethandlers_getsymbol(x->x_hooks); WIDGETPROFILE_HANDLER_EVAL; SETSYMBOL(&at, s); if (scriptlet_evaluate(sp, x->x_transient, 1, 1, &at, x->x_xargs)) { WIDGETPROFILE_HANDLER_PUSH; scriptlet_push(x->x_transient); } else if (!scriptlet_isempty(sp)) loudbug_bug("widget_symbol"); WIDGETPROFILE_HANDLER_QUIT; } static void widget_set(t_widget *x, t_symbol *s, int ac, t_atom *av) { t_symbol *prp; if (ac && av->a_type == A_SYMBOL && (prp = av->a_w.w_symbol)) { t_symbol *empty = 0; ac--; av++; if (*prp->s_name == '-') { x->x_update = WIDGET_PUSHVIS; empty = widget_addprops(x, x->x_options, 1, 0, prp, ac, av); } else if (*prp->s_name == '@') empty = widget_addprops(x, x->x_handlers, 1, 0, prp, ac, av); else if (*prp->s_name == '#') empty = widget_addprops(x, x->x_arguments, 1, 0, prp, ac, av); if (empty) loud_errand((t_pd *)x, "(use 'remove %s' if that is what you want).", empty->s_name); } else loud_messarg((t_pd *)x, s); } 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)) { if (op == x->x_options) /* LATER rethink */ x->x_update = WIDGET_REVIS; widget_update(x, op); } else loud_warning((t_pd *)x, 0, "%s %s has not been specified", props_getname(op), s->s_name); } } static void widget_tot(t_widget *x, t_symbol *s, int ac, t_atom *av) { if (ac) { scriptlet_reset(x->x_auxscript); scriptlet_add(x->x_auxscript, 1, 1, ac, av); if (scriptlet_evaluate(x->x_auxscript, x->x_transient, 1, 0, 0, x->x_xargs)) scriptlet_push(x->x_transient); } } static void widget_refresh(t_widget *x) { x->x_update = WIDGET_REVIS; widget_update(x, x->x_arguments); widget_update(x, x->x_handlers); widget_update(x, x->x_options); } static int widget_resettype(t_widget *x, t_widgettype *wt) { if (!wt || /* LATER rethink, cf widgettype_reload() */ wt == x->x_typedef) { widget_pushfreehooks(x); if (!(x->x_tkclass = widgettype_tkclass(x->x_typedef))) x->x_tkclass = x->x_type; x->x_update = WIDGET_REVIS; widget_update(x, x->x_arguments); widget_pushnewhooks(x); widget_update(x, x->x_handlers); widget_update(x, x->x_options); return (1); } else { loudbug_bug("widget_resettype"); return (0); } } static void widget_redefine(t_widget *x) { widget_resettype(x, widgettype_reload(x->x_type, x->x_glist)); } static void widget_editorhook(t_pd *z, t_symbol *s, int ac, t_atom *av) { t_widget *x = (t_widget *)z; props_clearall(x->x_options); widget_addmessage(x, 1, 0, ac, av); widget_refresh(x); } static void widget__failure(t_widget *x) { loud_error((t_pd *)x, "creation failure"); /* details printed at the gui side, in order to support special chars in error message */ loud_errand((t_pd *)x, "see standard error for details"); x->x_vised = 0; clock_delay(x->x_transclock, 0); } /* LATER handle subitems */ 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 loudbug_post("config %x %s \"%s\" %g %g", (int)x, target->s_name, 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(x->x_glist, (t_text *)x); /* FIXME */ } /* FIXME this is only a template */ static void widget__data(t_widget *x, t_symbol *s, int ac, t_atom *av) { #ifdef WIDGET_DEBUG loudbug_startpost("_data:"); loudbug_postatom(ac, av); loudbug_endpost(); #endif if (ac && av->a_type == A_SYMBOL) { s = av->a_w.w_symbol; if (s == widgetps_add) { widget_pushdatahooks(x, ac, av); } else if (s == widgetps_delete) { widget_pushdatahooks(x, ac, av); } else if (s == widgetps_set) { widget_pushdatahooks(x, ac, av); } else if (s == widgetps_get) { widget_pushdatahooks(x, ac, av); } else loud_error((t_pd *)x, "invalid \"_data\" subcommand \"%s\"", s->s_name); } else loud_error((t_pd *)x, "missing \"_data\" subcommand"); } 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); } /* see also widget_select() */ /* LATER handle subitems */ static void widget__inout(t_widget *x, t_floatarg f) { int disable = (int)f && x->x_glist->gl_edit; if (x->x_disabled) { if (!disable) { if (!x->x_selected) { char *mypathname = widget_getmypathname(x, x->x_glist)->s_name; sys_vgui("event generate %s <>\n", mypathname); } x->x_disabled = 0; } } else if (disable) { if (!x->x_selected) { char *mypathname = widget_getmypathname(x, x->x_glist)->s_name; sys_vgui("event generate %s <>\n", mypathname); } x->x_disabled = 1; } } static void widget__click(t_widget *x, t_symbol *s, int ac, t_atom *av) { if (ac != 4) { loud_error((t_pd *)x, "bad arguments to the '%s' method", s->s_name); return; } if (x->x_glist->gl_havewindow) /* LATER calculate on-parent coords */ { if (x->x_cvtarget->s_thing) /* LATER rethink */ typedmess(x->x_cvtarget->s_thing, widgetps_mouse, ac, av); else typedmess((t_pd *)x->x_glist, widgetps_mouse, ac, av); widget__inout(x, 2.); } } /* LATER think how to grab the mouse when dragging */ static void widget__motion(t_widget *x, t_symbol *s, int ac, t_atom *av) { if (ac != 3) { loud_error((t_pd *)x, "bad arguments to the '%s' method", s->s_name); return; } if (x->x_glist->gl_havewindow) /* LATER calculate on-parent coords */ { #if 0 loudbug_post("motion %g %g", av[0].a_w.w_float, av[1].a_w.w_float); #endif if (x->x_cvtarget->s_thing) /* LATER rethink */ typedmess(x->x_cvtarget->s_thing, widgetps_motion, ac, av); else typedmess((t_pd *)x->x_glist, widgetps_motion, ac, av); } } 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 widgetbug_postprops(char *msg, t_props *pp) { int ac; t_atom *av = props_getall(pp, &ac); if (av) { loudbug_startpost(msg); loudbug_postatom(ac, av); loudbug_endpost(); } } static void widget_debug(t_widget *x) { t_widgethandlers *wh = widgettype_getscripts(x->x_typedef); t_symbol *pn = widget_getcvpathname(x, 0); t_symbol *mn = widget_getmypathname(x, 0); int sz, i, nopt; t_atom *ap; static char bempty[] = ""; char *bp, *key; loudbug_post("containing glist: %x", (int)x->x_glist); loudbug_post("cv pathname%s %s", (pn ? ":" : ""), (pn ? pn->s_name : "unknown")); loudbug_post("my pathname%s %s", (mn ? ":" : ""), (mn ? mn->s_name : "unknown")); widgetbug_postprops("default options:", widgettype_getoptions(x->x_typedef)); widgetbug_postprops("instance options:", x->x_options); widgetbug_postprops("diff options:", x->x_diffoptions); widgetbug_postprops("default handlers:", widgettype_gethandlers(x->x_typedef)); widgetbug_postprops("instance handlers:", x->x_handlers); widgetbug_postprops("diff handlers:", x->x_diffhandlers); widgetbug_postprops("default arguments:", widgettype_getarguments(x->x_typedef)); widgetbug_postprops("instance arguments:", x->x_arguments); widgetbug_postprops("diff arguments:", x->x_diffarguments); loudbug_post("dictionary:"); bp = props_firstvalue(x->x_xargs, &key); while (bp) { loudbug_post("\t%s: \"%s\"", key, bp); bp = props_nextvalue(x->x_xargs, &key); } bp = scriptlet_getcontents(x->x_transient, &sz); loudbug_post("transient buffer (size %d):\n\"%s\"", sz, (bp ? bp : bempty)); bp = scriptlet_getcontents(x->x_optscript, &sz); loudbug_post("option buffer (size %d):\n\"%s\"", sz, (bp ? bp : bempty)); bp = scriptlet_getcontents(widgethandlers_getnew(wh), &sz); loudbug_post("long newhook (size %d):\n\"%s\"", sz, (bp ? bp : bempty)); bp = scriptlet_getcontents(widgethandlers_getfree(wh), &sz); loudbug_post("long freehook (size %d):\n\"%s\"", sz, (bp ? bp : bempty)); bp = scriptlet_getcontents(widgethandlers_getdata(wh), &sz); loudbug_post("long datahook (size %d):\n\"%s\"", sz, (bp ? bp : bempty)); bp = scriptlet_getcontents(widgethandlers_getvis(wh), &sz); loudbug_post("long vishook (size %d):\n\"%s\"", sz, (bp ? bp : bempty)); bp = scriptlet_getcontents(widgethandlers_getvis(x->x_hooks), &sz); loudbug_post("short vishook (size %d):\n\"%s\"", sz, (bp ? bp : bempty)); bp = masterwidget_getcontents(&sz); loudbug_post("setup definitions (size %d):\n\"%s\"", sz, (bp ? bp : bempty)); } #endif static void widget_attach(t_widget *x); static void widget_detach(t_widget *x); /* FIXME for all gui objects use a single sink (with a single clock) */ static void gui_unbind(t_pd *x, t_symbol *s) { t_guiconnect *gc = guiconnect_new(0, s); guiconnect_notarget(gc, 1000.); pd_unbind(x, s); } static void widget_free(t_widget *x) { widget_novis(x); widget_pushfreehooks(x); gui_unbind((t_pd *)x, x->x_cbtarget); gui_unbind((t_pd *)x, x->x_rptarget); widgethandlers_free(x->x_hooks); props_freeall(x->x_options); props_freeall(x->x_xargs); props_freeall(x->x_diffoptions); 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_symbol *type = 0, *name = 0; char buf[MAXPDSTRING]; if (widget_transforming) return (0); masterwidget_validate(); if (ac && av->a_type == A_SYMBOL) { type = av->a_w.w_symbol; ac--; av++; } if (ac && av->a_type == A_SYMBOL) { name = av->a_w.w_symbol; ac--; av++; } /* LATER think about anonymous widgets (single arg, or '.') */ if (!type || type == &s_ || !name || name == &s_) { loud_error(0, "bad arguments for a widget"); loud_errand(0, "expecting \"widget [properties]\""); return (0); } x = (t_widget *)pd_new(widget_class); x->x_type = type; x->x_name = name; 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_glist = canvas_getcurrent(); x->x_typedef = widgettype_get(x->x_type, 0, 0, x->x_glist); if (!(x->x_tkclass = widgettype_tkclass(x->x_typedef))) x->x_tkclass = x->x_type; sprintf(buf, ".x%lx.c", (int)x->x_glist); x->x_cvpathname = gensym(buf); sprintf(buf, ".x%lx", (int)x->x_glist); x->x_cvtarget = gensym(buf); sprintf(buf, "::toxy::v%x", (int)x); x->x_varname = gensym(buf); x->x_auxscript = scriptlet_new((t_pd *)x, x->x_rptarget, x->x_cbtarget, x->x_name, x->x_glist, widget_cvhook); x->x_transient = scriptlet_newalike(x->x_auxscript); x->x_optscript = scriptlet_newalike(x->x_auxscript); 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, 0); x->x_xargs = props_new((t_pd *)x, "argument", "#", 0, widget_propsresolver); x->x_diffoptions = props_new((t_pd *)x, "option", "-", 0, 0); x->x_diffhandlers = props_new((t_pd *)x, "handler", "@", x->x_diffoptions, 0); x->x_diffarguments = props_new((t_pd *)x, "argument", "#", x->x_diffoptions, 0); x->x_hooks = widgethandlers_new(x->x_auxscript); outlet_new((t_object *)x, &s_anything); /* LATER consider estimating these, based on widget class and options. The default used to be 50x50, which confused people wanting widgets in small gops, of size exactly as specified by the 'coords' message, but finding gops stretched, to accomodate the widget's default area. */ x->x_width = 5; x->x_height = 5; x->x_filehandle = hammerfile_new((t_pd *)x, 0, 0, 0, widget_editorhook); x->x_transclock = clock_new(x, (t_method)widget_transtick); x->x_background = 0; x->x_hasstate = 0; x->x_update = WIDGET_NOVIS; x->x_disabled = 0; x->x_vised = 0; widget_attach(x); widget_addmessage(x, 0, 0, ac, av); x->x_constructed = 0; 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_targetglist); if (!glist && x->x_cvname && complain) loud_error((t_pd *)x, "bad canvas name '%s'", x->x_cvname->s_name); return (glist); } 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 loudbug_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 loudbug_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; } loudbug_bug("tow_widgetdetach"); } static void widget_attach(t_widget *x) { t_tow *t; for (t = widget_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) loudbug_post("%s widget '%s' not found", x->x_type->s_name, x->x_name->s_name); #endif } #ifdef TOW_DEBUG else if (x->x_cvname) loudbug_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 loudbug_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) loudbug_bug("tow_detach"); } } 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_redefine(t_tow *x) { t_widgettype *wt = widgettype_reload(x->x_type, x->x_glist); t_widgetentry *we; for (we = x->x_widgetlist; we; we = we->we_next) if (!widget_resettype(we->we_widget, wt)) break; } /* LATER broadcasting: canvas-wide or type-on-canvas-wide */ static void tow_settarget(t_tow *x, t_symbol *s1, t_symbol *s2, t_symbol *s3) { char buf[64]; if (s1 == &s_ || !strcmp(s1->s_name, ".")) s1 = 0; if (s1 && strcmp(s1->s_name, ".parent")) { x->x_cvremote = canvas_makebindsym(x->x_cvname = s1); x->x_targetglist = 0; } else { x->x_cvremote = 0; if (s1) { if (x->x_glist->gl_owner) x->x_targetglist = x->x_glist->gl_owner; else { /* The case of a tow pointing out from an abstraction, targeting its parent, is considered invalid (otherwise, opening an abstraction as a top-level patch should not be flagged as error). LATER rethink. */ loud_error((t_pd *)x, "parent of a top level patch requested,"); loud_errand((t_pd *)x, "this is a dangling tow..."); x->x_cvname = 0; x->x_targetglist = 0; } } else x->x_targetglist = x->x_glist; } if (x->x_targetglist) x->x_cvname = x->x_targetglist->gl_name; x->x_type = s2; x->x_name = s3; tow_attach(x); } static void tow_retarget(t_tow *x, t_symbol *s1, t_symbol *s2, t_symbol *s3) { tow_detach(x); tow_settarget(x, s1, s2, s3); } static void tow_pwd(t_tow *x, t_symbol *s) { t_glist *glist; t_symbol *dir; if (s && s->s_thing && (glist = tow_getglist(x, 1)) && (dir = canvas_getdir(glist))) pd_symbol(s->s_thing, dir); } 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); } #ifdef TOW_DEBUG static void tow_debug(t_tow *x) { t_widgetentry *we; loudbug_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; loudbug_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++; loudbug_post(" (%d other tow%s)", other, (other == 1 ? "" : "s")); if (found != 1) loudbug_bug("listed %d times in widget's towlist", found); } } #endif static void tow_free(t_tow *x) { t_tow *t1, *t2; #ifdef TOW_DEBUG loudbug_startpost("updating towlist..."); #endif for (t1 = 0, t2 = widget_towlist; t2; t2 = t2->x_next) { if (t2 == x) { if (t1) t1->x_next = t2->x_next; else widget_towlist = t2->x_next; #ifdef TOW_DEBUG loudbug_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); x->x_glist = canvas_getcurrent(); x->x_targetglist = 0; x->x_widgetlist = 0; x->x_next = widget_towlist; widget_towlist = x; outlet_new((t_object *)x, &s_anything); tow_settarget(x, s1, s2, s3); return (x); } void widget_setup(void) { post("beware! this is widget %s, %s %s build...", TOXY_VERSION, loud_ordinal(TOXY_BUILD), TOXY_RELEASE); widgetps_mouse = gensym("mouse"); widgetps_motion = gensym("motion"); widgetps_vis = gensym("vis"); widgetps_new = gensym("new"); widgetps_free = gensym("free"); widgetps_data = gensym("data"); widgetps_add = gensym("add"); widgetps_delete = gensym("delete"); widgetps_set = gensym("set"); widgetps_get = gensym("get"); 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_set, gensym("set"), A_GIMME, 0); class_addmethod(widget_class, (t_method)widget_remove, gensym("remove"), A_SYMBOL, 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_redefine, gensym("redefine"), 0); class_addmethod(widget_class, (t_method)widget__failure, gensym("_failure"), 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__data, gensym("_data"), A_GIMME, 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_GIMME, 0); class_addmethod(widget_class, (t_method)widget__motion, gensym("_motion"), A_GIMME, 0); #ifdef WIDGET_DEBUG class_addmethod(widget_class, (t_method)widget_debug, gensym("debug"), 0); #endif #ifdef WIDGET_PROFILE class_addmethod(widget_class, (t_method)widget_profile, gensym("profile"), 0); #endif hammerfile_setup(widget_class, 0); makeshift_class = class_new(gensym("text"), 0, 0, sizeof(t_text), /* inlet added explicitly (cf text_class) */ 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_DEFSYM, A_DEFSYM, 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_redefine, gensym("redefine"), 0); class_addmethod(tow_class, (t_method)tow_retarget, gensym("retarget"), A_SYMBOL, A_SYMBOL, A_SYMBOL, 0); class_addmethod(tow_class, (t_method)tow_pwd, gensym("pwd"), A_SYMBOL, 0); 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 }