From 4d84d14ac1aa13958eaa2971b03f7f929a519105 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IOhannes=20m=20zm=C3=B6lnig?= Date: Fri, 8 Feb 2008 13:00:32 +0000 Subject: reorganized svn path=/trunk/; revision=9400 --- desiredata/src/desire.c | 7332 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 7332 insertions(+) create mode 100644 desiredata/src/desire.c (limited to 'desiredata/src/desire.c') diff --git a/desiredata/src/desire.c b/desiredata/src/desire.c new file mode 100644 index 00000000..cc04ad5e --- /dev/null +++ b/desiredata/src/desire.c @@ -0,0 +1,7332 @@ +/* $Id: desire.c,v 1.1.2.217.2.235 2007-08-21 19:50:25 matju Exp $ + + This file is part of DesireData. + Copyright (c) 2004-2007 by Mathieu Bouchard. + Portions Copyright (c) 1997-2005 Miller Puckette, Günter Geiger, Krzysztof Czaja, + Johannes Zmoelnig, Thomas Musil, Joseph Sarlo, etc. + The remains of IEMGUI Copyright (c) 2000-2001 Thomas Musil (IEM KUG Graz Austria) + For information on usage and redistribution, and for a DISCLAIMER OF ALL + WARRANTIES, see the file, "LICENSE.txt," in this distribution. +*/ + +#define PD_PLUSPLUS_FACE +#include "m_pd.h" +#include "desire.h" +#include "s_stuff.h" +#include +#include +#include +#include +#include +#include +#include "m_simd.h" +#include +#include +#include +#include + +#ifdef MSW +#include +#define snprintf _snprintf +#else +#include +#endif + +/* +#define sys_vgui(args...) do { \ + fprintf(stderr,"\e[0;1;31m"); \ + L fprintf(stderr,args); \ + fprintf(stderr,"\e[0m"); \ + sys_vgui(args); } while(0) +*/ + +#define foreach(ITER,COLL) for(typeof(COLL.begin()) ITER = COLL.begin(); ITER != (COLL).end(); ITER++) + +#define boxes_each(CHILD,BOXES) for(t_gobj *CHILD=(BOXES)->first(); CHILD; CHILD=CHILD->next()) +#define canvas_each(CHILD,CANVAS) for(t_gobj *CHILD=(CANVAS)->boxes->first(); CHILD; CHILD=CHILD->next()) +#define canvas_wires_each(WIRE,TRAV,CANVAS) \ + for (t_outconnect *WIRE=(t_outconnect *)666; WIRE==(t_outconnect *)666; ) \ + for (t_linetraverser TRAV(CANVAS); (WIRE=linetraverser_next(&TRAV)); ) + +#undef SET +#define SET(attr,value) do {gobj_changed(x,#attr); x->attr = (value);} while(0) + +#define a_float a_w.w_float +#define a_symbol a_w.w_symbol + +#define CLAMP(_var,_min,_max) do { if (_var<_min) _var=_min; else if (_var>_max) _var=_max; } while(0) +#define IS_A_FLOAT(atom,index) ((atom+index)->a_type == A_FLOAT) +#define IS_A_SYMBOL(atom,index) ((atom+index)->a_type == A_SYMBOL) + +int imin(int a, int b) {return ab?a:b;} +t_symbol *s_empty, *s_pd, *s_Pd; + +std::ostream &operator << (std::ostream &str, t_pd *self) { + t_binbuf *b; + if (self->_class->patchable && (b = ((t_text *)self)->binbuf)) { + char *buf; int bufn; + binbuf_gettext(b,&buf,&bufn); + str << "[" << buf << "]"; + free(buf); + } else str << "<" << self->_class->name->name << ":" << std::hex << (long)self << ">"; + return str; +} + +//-------------------------------------------------------------------------- + +t_class *boxes_class; + +struct t_boxes : t_gobj { + typedef std::map M; + typedef std::pair KV; +private: + std::map map; +public: + void invariant () {size();} + t_boxes() {} + size_t size() { + size_t n=0; + boxes_each(g,this) n++; + if (map.size()!=n) post("map size=%d list size=%d",map.size(),n); + return n; + } + t_gobj *first() {return map.begin() != map.end() ? map.begin()->second : 0;} + t_gobj *last() {M::iterator iter = map.end(); iter--; return iter->second;} + t_gobj *next(t_gobj *x) { + M::iterator iter = map.begin(); + while (iter->second != x) iter++; + iter++; + return iter == map.end() ? 0 : iter->second; + } + void add(t_gobj *x) {map.insert(KV(x->dix->index,x)); invariant();} + void remove(int i) {map.erase(i); invariant();} + void remove_by_value(t_gobj *x) {map.erase(x->dix->index); invariant();} +}; + +t_boxes *boxes_new() { + t_boxes *self = (t_boxes *)pd_new(boxes_class); + new(self) t_boxes; + return self; +} + +void boxes_notice(t_boxes *self, t_gobj *origin, int argc, t_atom *argv) { +} + +void boxes_free(t_boxes *self) {self->~t_boxes();} + +t_gobj *_gobj::next() {return dix->canvas->boxes->next(this);} + +//-------------------------------------------------------------------------- + +t_class *gop_filtre_class; + +struct t_gop_filtre : t_gobj { + t_canvas *canvas; +}; + +// test for half-open interval membership +bool inside (int x, int x0, int x1) {return x0<=x && xcanvas; + if (0/* check for messagebox, comment, but you can't check for objectboxes in general */) return; + if (c->goprect) { + if (!inside(o->x, c->xmargin, c->xmargin + c->pixwidth )) return; + if (!inside(o->y, c->ymargin, c->ymargin + c->pixheight)) return; + } + gobj_changed3(self,origin,argc,argv); +} + +t_gobj *gop_filtre_new(t_canvas *canvas) { + t_gop_filtre *self = (t_gop_filtre *)pd_new(gop_filtre_class); + new(self) t_gop_filtre; + self->canvas = canvas; + gobj_subscribe(canvas->boxes,self); + return self; +} + +void gop_filtre_free(t_boxes *self) {} + +//-------------------------------------------------------------------------- +// t_appendix: an extension to t_gobj made by matju so that all t_gobj's may have new fields +// without sacrificing binary compat with externals compiled for PureMSP. + +typedef t_hash t_visual; + +t_appendix *appendix_new (t_gobj *master) { + //fprintf(stderr,"appendix_new %p\n",master); + t_appendix *self = (t_appendix *)malloc(sizeof(t_appendix)); + self->canvas = 0; + self->nobs = 0; + self->obs = 0; + self->visual = new t_visual(1); + self->index = 0; + self->elapsed = 0; + return self; +} + +void appendix_save (t_gobj *master, t_binbuf *b) { + t_visual *h = master->dix->visual; + //fprintf(stderr,"appendix_save %p size=%ld\n",master,hash_size(h)); + if (!h->size()) return; + t_symbol *k; + t_arglist *v; + int i=0; + binbuf_addv(b,"t","#V"); + hash_foreach(k,v,h) { + t_arglist *al = (t_arglist *)v; + binbuf_addv(b,"s",k); + binbuf_add(b,al->c,al->v); + if (size_t(i+1)==h->size()) binbuf_addv(b, ";"); else binbuf_addv(b,"t",","); + i++; + } +} + +void appendix_free (t_gobj *master) { + t_appendix *self = master->dix; + t_symbol *k; + t_arglist *v; + if (self->visual) { + hash_foreach(k,v,self->visual) free(v); + delete self->visual; + } + free(self); +} + +/* subscribing N spies takes N*N time, but it's not important for now */ +/* subscription could become just a special use of t_outlet in the future, or sometimes become implied. */ +/* perhaps use [bindelem] here? */ +void gobj_subscribe(t_gobj *self, t_gobj *observer) { + t_appendix *d = self->dix; + for (size_t i=0; inobs; i++) if (d->obs[i]) return; + d->obs=(t_gobj **)realloc(d->obs,sizeof(t_gobj *)*(1+d->nobs)); + d->obs[d->nobs++] = observer; + t_onsubscribe ons = self->_class->onsubscribe; + ons(self,observer); + //post("x%p has %d observers",self,(int)d->nobs); +} + +void gobj_unsubscribe (t_gobj *self, t_gobj *observer) { + t_appendix *d = self->dix; + size_t i; + for (i=0; inobs; i++) if (d->obs[i]) break; + if (i==d->nobs) return; + d->nobs--; + for (; inobs; i++) d->obs[i] = d->obs[i+1]; + // should have something like onunsubscribe too, to handle delete?... or just use onsubscribe differently. +} + +void gobj_setcanvas (t_gobj *self, t_canvas *c) { + if (self->dix->canvas == c) return; + if (self->dix->canvas) gobj_unsubscribe(self,self->dix->canvas); + self->dix->canvas = c; + if (self->dix->canvas) gobj_subscribe(self,self->dix->canvas); +} + +/* for future use */ +t_canvas *gobj_canvas (t_gobj *self) {return self->dix->canvas;} + +// if !k then suppose all of the object might have changed. +void gobj_changed (t_gobj *self, const char *k) { + int dirty = k ? (1<_class,k)) : -1; + t_atom argv[1]; + SETFLOAT(argv,(float)dirty); + gobj_changed3(self,self,1,argv); +} +//#define gobj_changed(SELF,K) do {L; gobj_changed(SELF,K);} while(0) + +// if only a float is sent, it's a bitset of at most 25 elements +// else it may mean whatever else... +void gobj_changed2 (t_gobj *self, int argc, t_atom *argv) { + gobj_changed3(self,self,argc,argv); +} + +void gobj_changed3 (t_gobj *self, t_gobj *origin, int argc, t_atom *argv) { + t_appendix *d = self->dix; + std::ostringstream s; + for (int i=0; inobs; i++) { + t_gobj *obs = d->obs[i]; + t_notice ice = obs->_class->notice; + if (ice) ice(obs,origin,argc,argv); + else post("null func ptr for class %s",self->_class->name->name); + } +} + +//-------------------------------------------------------------------------- +// some simple ringbuffer-style queue +// when this becomes too small, use a bigger constant or a better impl. + +#define QUEUE_SIZE 16384 +struct t_queue { + int start,len; + t_pd *o[QUEUE_SIZE]; +}; + +t_queue *queue_new () { + t_queue *self = (t_queue *)getbytes(sizeof(t_queue)); + self->start = self->len = 0; + return self; +} + +static bool debug_queue=0; + +static void pd_print (t_pd *self, char *header) { + if (self->_class->gobj && (object_table->get(self)&1)==0) {printf("%s %p dead\n",header,self); return;} + if (self->_class->patchable) { + t_binbuf *b = ((t_text *)self)->binbuf; + if (b) { + char *buf; int bufn; + binbuf_gettext(b,&buf,&bufn); + printf("%s %p [%.*s]\n",header,self,bufn,buf); + return; + } + } + printf("%s %p (%s)\n",header,self,self->_class->name->name); +} + +void queue_put (t_queue *self, t_pd *stuff) { + if (debug_queue) pd_print(stuff,"queue_put"); + if (self->len==QUEUE_SIZE) {bug("queue full"); return;} + self->o[(self->start+self->len)%QUEUE_SIZE] = stuff; + self->len++; + if (debug_queue) post("queue_put: items in queue: %d",self->len); +} + +void *queue_get (t_queue *self) { + t_pd *stuff = self->o[self->start]; + self->start = (self->start+1)%QUEUE_SIZE; + self->len--; + if (debug_queue) {post("queue_get: items in queue: %d",self->len); pd_print(stuff,"queue_get");} + return stuff; +} + +#define queue_each(i,self) \ + for (int i=self->start; i!=(self->start+self->len)%QUEUE_SIZE; i=(i+1)%QUEUE_SIZE) + +void queue_free (t_queue *self) { + abort(); + free(self); +} + +int queue_empty (t_queue *self) {return self->len==0;} +int queue_full (t_queue *self) {return self->len==QUEUE_SIZE;} + +//-------------------------------------------------------------------------- +// reply system: + +struct t_reply : t_gobj { + short serial; + void *answer; +}; + +t_class *reply_class; + +static t_reply *reply_new (short serial, void *answer) { + t_reply *self = (t_reply *)pd_new(reply_class); + self->dix = appendix_new(self); + self->serial = serial; + self->answer = answer; + return self; +} + +static void reply_send (t_reply *self) { + sys_vgui("serial %ld x%lx\n",(long)self->serial,(long)self->answer); +} + +static void reply_free (t_reply *self) {} + +//-------------------------------------------------------------------------- +// update manager: + +struct t_dirtyentry { + long fields; + size_t start,end; + t_dirtyentry() {} +}; + +typedef t_hash t_dirtyset; + +struct t_manager : t_text { + t_queue *q; + t_clock *clock; + t_binbuf *b; /* reusable, for processing messages from the gui */ + t_dirtyset *dirty; +}; + +static t_class *manager_class; +t_manager *manager; + +void manager_call (void *foo) { + t_manager *self = (t_manager *)foo; + while (!queue_empty(self->q)) { + t_gobj *o = (t_gobj *)queue_get(self->q); + if (!o) continue; /* cancelled notice */ + //fprintf(stderr,"manager_call, o->_class=%s\n",o->_class->c_name->name); + if (o->_class == reply_class) { + reply_send((t_reply *)o); + pd_free(o); + } else { + if (self->dirty->exists(o)) { + pd_upload(o); + self->dirty->del(o); + } + } + } + clock_delay(self->clock,50); +} + +void manager_notice (t_gobj *self_, t_gobj *origin, int argc, t_atom *argv) { + t_manager *self = (t_manager *)self_; + if (!self->dirty->exists(origin)) { + //std::cerr << "manager_notice:"; + //for (int i=0; iq,origin); + self->dirty->set(origin,0); + } +} + +t_manager *manager_new (t_symbol *s, int argc, t_atom *argv) { + t_manager *self = (t_manager *)pd_new(manager_class); + self->q = queue_new(); + self->clock = clock_new(self,(t_method)manager_call); + self->b = binbuf_new(); + clock_delay(self->clock,0); + self->dirty = new t_dirtyset(127); + return self; +} + +void manager_free (t_manager *self) { + clock_free(self->clock); + queue_free(self->q); + binbuf_free(self->b); + pd_free(self); +} + +extern "C" void manager_anything (t_manager *self, t_symbol *s, int argc, t_atom *argv) { + binbuf_clear(self->b); + binbuf_addv(self->b,"s",s); + binbuf_add(self->b,argc,argv); + binbuf_eval(self->b,0,0,0); +} + +//-------------------------------------------------------------------------- +/* +IOhannes changed the canvas_restore, so that it might accept $args as well +(like "pd $0_test") so you can make multiple & distinguishable templates. +*/ + +#define CANVAS_DEFCANVASWIDTH 450 +#define CANVAS_DEFCANVASHEIGHT 300 + +#ifdef __APPLE__ +#define CANVAS_DEFCANVASYLOC 22 +#else +#define CANVAS_DEFCANVASYLOC 0 +#endif + +extern short next_object; +extern t_pd *newest; +t_class *canvas_class; +int canvas_dspstate; /* whether DSP is on or off */ +t_canvas *canvas_whichfind; /* last canvas we did a find in */ +std::map windowed_canvases; /* where int is dummy */ +static void canvas_setbounds(t_canvas *x, t_floatarg x1, t_floatarg y1, t_floatarg x2, t_floatarg y2); +static t_symbol *canvas_newfilename = &s_; +static t_symbol *canvas_newdirectory = &s_; +static int canvas_newargc; +static t_atom *canvas_newargv; + +/* add a canvas the list of "root" canvases (toplevels without parents.) */ +/* should those two functions still exist? */ +static void canvas_addtolist(t_canvas *x) { + windowed_canvases.insert(std::pair(x,42)); + if (x->havewindow) gobj_subscribe(x,manager); +} +static void canvas_takeofflist(t_canvas *x) {windowed_canvases.erase(x);} + +/* if there's an old one lying around free it here. + This happens if an abstraction is loaded but never gets as far as calling canvas_new(). */ +void canvas_setargs(int argc, t_atom *argv) { + if (canvas_newargv) free(canvas_newargv); + canvas_newargc = argc; + canvas_newargv = (t_atom *)copybytes(argv, argc * sizeof(t_atom)); +} + +void glob_setfilename(void *self, t_symbol *filesym, t_symbol *dirsym) { + canvas_newfilename = filesym; + canvas_newdirectory = dirsym; +} + +t_canvas *canvas_getcurrent () {return ((t_canvas *)pd_findbyclass(&s__X, canvas_class));} + +t_canvasenvironment *canvas_getenv(t_canvas *x) { + while (!x->env) x = x->dix->canvas; + return x->env; +} + +int canvas_getdollarzero () { + t_canvas *x = canvas_getcurrent(); + t_canvasenvironment *env = x ? canvas_getenv(x) : 0; + return env ? env->dollarzero : 0; +} + +t_symbol *canvas_realizedollar(t_canvas *x, t_symbol *s) { + if (strchr(s->name,'$')) { + t_canvasenvironment *env = canvas_getenv(x); + pd_pushsym(x); + t_symbol *ret = binbuf_realizedollsym(s, env->argc, env->argv, 1); + pd_popsym(x); + return ret; + } + return s; +} + +t_symbol *canvas_getcurrentdir () {return canvas_getenv(canvas_getcurrent())->dir;} +t_symbol *canvas_getdir(t_canvas *x) {return canvas_getenv( x)->dir;} + +char *canvas_makefilename(t_canvas *x, char *file, char *result, int resultsize) { + char *dir = canvas_getenv(x)->dir->name; + if (file[0] == '/' || (file[0] && file[1] == ':') || !*dir) { + if (!result) return strdup(file); + strncpy(result, file, resultsize); + result[resultsize-1] = 0; + } else { + if (result) {snprintf(result,resultsize,"%s/%s",dir,file); result[resultsize-1] = 0;} + else asprintf(&result, "%s/%s",dir,file); + } + return result; +} + +static void canvas_rename(t_canvas *x, t_symbol *s, t_symbol *dir) { + t_symbol *bs = canvas_makebindsym(x->name); + if (x->name!=s_Pd) pd_unbind(x, bs); + SET(name,s); + if (x->name!=s_Pd) pd_bind(x, bs); + if (dir && dir != &s_) { + canvas_getenv(x)->dir = dir; + gobj_changed(x,"dir"); + } +} + +/* --------------- traversing the set of lines in a canvas ----------- */ + +t_linetraverser::t_linetraverser(t_canvas *canvas) {linetraverser_start(this,canvas);} + +void linetraverser_start(t_linetraverser *t, t_canvas *x) { + t->from = 0; + t->canvas = x; + t->next = 0; + t->nextoutno = t->nout = 0; +} + +t_outconnect *linetraverser_next(t_linetraverser *t) { + t_outconnect *rval = t->next; + while (!rval) { + int outno = t->nextoutno; + while (outno == t->nout) { + t_object *ob = 0; + t_gobj *y = t->from ? t->from->next() : t->canvas->boxes->first(); + for (; y; y = y->next()) if ((ob = pd_checkobject(y))) break; + if (!ob) return 0; + t->from = ob; + t->nout = obj_noutlets(ob); + outno = 0; + } + t->nextoutno = outno + 1; + rval = obj_starttraverseoutlet(t->from, &t->outletp, outno); + t->outlet = outno; + } + t->next = obj_nexttraverseoutlet(rval, &t->to, &t->inletp, &t->inlet); + t->nin = obj_ninlets(t->to); + if (!t->nin) bug("linetraverser_next"); + return rval; +} + +/* -------------------- the canvas object -------------------------- */ +static int hack = 1; + +static t_canvas *canvas_new2() { + t_canvas *x = (t_canvas *)pd_new(canvas_class); + /* zero out every field except "pd" and "dix" */ + memset(((char *)x) + sizeof(t_gobj), 0, sizeof(*x) - sizeof(t_gobj)); + x->xlabel = (t_symbol **)getbytes(0); + x->ylabel = (t_symbol **)getbytes(0); + // only manage this canvas if it's not one of the 3 invisible builtin canvases + x->boxes = boxes_new(); + return x; +} + +static void canvas_vis(t_canvas *x, t_floatarg f); + +/* make a new canvas. It will either be a "root" canvas or else it appears as + a "text" object in another window (canvas_getcurrent() tells us which.) */ +static t_canvas *canvas_new(void *self, t_symbol *sel, int argc, t_atom *argv) { + t_canvas *x = canvas_new2(); + t_canvas *owner = canvas_getcurrent(); + t_symbol *s = &s_; + int width = CANVAS_DEFCANVASWIDTH, xloc = 0; + int height = CANVAS_DEFCANVASHEIGHT, yloc = CANVAS_DEFCANVASYLOC; + int vis=0, font = owner?owner->font:10; + if (!owner) canvas_addtolist(x); + /* toplevel vs subwindow */ + if (argc==5) pd_scanargs(argc,argv,"iiiii", &xloc,&yloc,&width,&height,&font); + else if (argc==6) pd_scanargs(argc,argv,"iiiisi",&xloc,&yloc,&width,&height,&s,&vis); + /* (otherwise assume we're being created from the menu.) */ + if (canvas_newdirectory->name[0]) { + static long dollarzero = 1000; + t_canvasenvironment *env = x->env = (t_canvasenvironment *)getbytes(sizeof(*x->env)); + if (!canvas_newargv) canvas_newargv = (t_atom *)getbytes(0); + env->dir = canvas_newdirectory; + env->argc = canvas_newargc; + env->argv = canvas_newargv; + env->dollarzero = dollarzero++; + env->path = 0; + canvas_newdirectory = &s_; + canvas_newargc = 0; + canvas_newargv = 0; + } else x->env = 0; + + if (yloc < CANVAS_DEFCANVASYLOC) yloc = CANVAS_DEFCANVASYLOC; + if (xloc < 0) xloc = 0; + x->x1 = 0; x->y1 = 0; + x->x2 = 1; x->y2 = 1; + canvas_setbounds(x, xloc, yloc, xloc + width, yloc + height); + gobj_setcanvas(x,owner); + x->name = *s->name ? s : canvas_newfilename ? canvas_newfilename : s_Pd; + if (x->name != s_Pd) pd_bind(x, canvas_makebindsym(x->name)); + x->goprect = 0; /* no GOP rectangle unless it's turned on later */ + /* cancel "vis" flag if we're a subpatch of an abstraction inside another patch. + A separate mechanism prevents the toplevel abstraction from showing up. */ + if (vis && gensym("#X")->thing && gensym("#X")->thing->_class == canvas_class) { + t_canvas *z = (t_canvas *)(gensym("#X")->thing); + while (z && !z->env) z = z->dix->canvas; + if (z && canvas_isabstraction(z) && z->dix->canvas) vis = 0; + } + if (vis) canvas_vis(x,vis); + x->font = 10 /*sys_nearestfontsize(font)*/; + pd_pushsym(x); + newest = x; + return x; +} + +void canvas_setgraph(t_canvas *x, int flag, int nogoprect); + +static void canvas_coords(t_canvas *x, t_symbol *s, int argc, t_atom *argv) { + pd_scanargs(argc,argv,"ffffii*",&x->x1,&x->y1,&x->x2,&x->y2,&x->pixwidth,&x->pixheight); + if (argc <= 7) canvas_setgraph(x, atom_getintarg(6, argc, argv), 1); + else { + canvas_setgraph(x, atom_getintarg(6, argc, argv), 0); + SET(xmargin, atom_getintarg(7, argc, argv)); + SET(ymargin, atom_getintarg(8, argc, argv)); + } + gobj_changed(x,0); +} + +template void swap(T &a, T &b) {T c=a; a=b; b=c;} + +#define CANVAS_DEFGRAPHWIDTH 200 +#define CANVAS_DEFGRAPHHEIGHT 140 +/* make a new canvas and add it to this canvas. It will appear as a "graph", not a text object. */ +static t_canvas *canvas_addcanvas(t_canvas *g, t_symbol *sym, +float x1, float y1, float x2, float y2, +float px1, float py1, float px2, float py2) { + static int gcount = 0; + int menu = 0; + t_canvas *x = canvas_new2(); + if (!*sym->name) { + sym = symprintf("graph%d", ++gcount); + menu = 1; + } else if (!strncmp(sym->name,"graph",5)) { + int zz = atoi(sym->name+5); + if (zz>gcount) gcount = zz; + } + /* in 0.34 and earlier, the pixel rectangle and the y bounds were reversed; this would behave the same, + except that the dialog window would be confusing. The "correct" way is to have "py1" be the value + that is higher on the screen. */ + if (py2 < py1) {swap(y1,y2); swap(py1,py2);} + if (x1 == x2 || y1 == y2) {x1=0; x2=100; y1=1; y2=-1;} + if (px1 >= px2 || py1 >= py2) { + px1=100; px2=px1+CANVAS_DEFGRAPHWIDTH; + py1=20; py2=py1+CANVAS_DEFGRAPHHEIGHT; + } + x->name = sym; + SET(x1,x1); SET(y1,y1); SET(x,short(px1)); SET(pixwidth ,int(px2-px1)); + SET(x2,x2); SET(y2,y2); SET(y,short(py1)); SET(pixheight,int(py2-py1)); + x->font = (canvas_getcurrent() ? canvas_getcurrent()->font : 42 /*sys_defaultfont*/); + x->screenx1 = x->screeny1 = 0; x->screenx2 = 450; x->screeny2 = 300; + if (x->name != s_Pd) pd_bind(x, canvas_makebindsym(x->name)); + gobj_setcanvas(x,g); + x->gop = 1; + x->goprect = 0; + x->binbuf = binbuf_new(); + binbuf_addv(x->binbuf,"t","graph"); + if (!menu) pd_pushsym(x); + canvas_add(g,x); + return x; +} + +static void canvas_canvas(t_canvas *g, t_symbol *s, int argc, t_atom *argv) { + t_symbol *sym; + float x1,y1,x2,y2,px1,py1,px2,py2; + pd_scanargs(argc,argv,"sffffffff",&sym,&x1,&y1,&x2,&y2,&px1,&py1,&px2,&py2); + canvas_addcanvas(g, sym, x1, y1, x2, y2, px1, py1, px2, py2); +} + +static void canvas_redraw(t_canvas *x) { + gobj_changed(x,0); + canvas_each(y,x) if (y->_class==canvas_class) canvas_redraw((t_canvas *)y); else gobj_changed(y,0); +} + +/* This is sent from the GUI to inform a toplevel that its window has been moved or resized. */ +static void canvas_setbounds(t_canvas *x, t_floatarg x1, t_floatarg y1, t_floatarg x2, t_floatarg y2) { + int heightwas = int(y2-y1); + if (x->screenx1 == x1 && x->screeny1 == y1 && + x->screenx2 == x2 && x->screeny2 == y2) return; + x->screenx1 = int(x1); x->screeny1 = int(y1); + x->screenx2 = int(x2); x->screeny2 = int(y2); + if (!x->gop && x->y2 < x->y1) { + /* if it's flipped so that y grows upward, fix so that zero is bottom edge and redraw. + This is only appropriate if we're a regular "text" object on the parent. */ + float diff = x->y1 - x->y2; + x->y1 = heightwas * diff; + x->y2 = x->y1 - diff; + canvas_redraw(x); + } +} + +t_symbol *canvas_makebindsym(t_symbol *s) {return symprintf("pd-%s",s->name);} + +static void canvas_vis(t_canvas *x, t_floatarg f) { + int hadwindow = x->havewindow; + SET(havewindow,!!f); + if (hadwindow && !x->havewindow) gobj_unsubscribe(x,manager); + if (!hadwindow && x->havewindow) gobj_subscribe(x,manager); +} + +/* we call this on a non-toplevel canvas to "open" it into its own window. */ +static void canvas_menu_open(t_canvas *x) { + if (canvas_isvisible(x) && !canvas_istoplevel(x)) { + if (!x->dix->canvas) {error("this works only on subpatch or abstraction"); return;} + SET(havewindow,1); + } +} + +int canvas_isvisible(t_canvas *x) {return gstack_empty() && canvas_getcanvas(x)->havewindow;} + +/* we consider a graph "toplevel" if it has its own window or if it appears as a box in its parent window + so that we don't draw the actual contents there. */ +int canvas_istoplevel(t_canvas *x) {return x->havewindow || !x->gop;} + +static void canvas_free(t_canvas *x) { + int dspstate = canvas_suspend_dsp(); + if (canvas_whichfind == x) canvas_whichfind = 0; + t_gobj *y; + while ((y = x->boxes->first())) canvas_delete(x, y); + canvas_vis(x, 0); + if (x->name != s_Pd) pd_unbind(x,canvas_makebindsym(x->name)); + if (x->env) { + free(x->env->argv); + free(x->env); + } + canvas_resume_dsp(dspstate); + free(x->xlabel); + free(x->ylabel); + if (!x->dix->canvas) canvas_takeofflist(x); +} + +/* kill all lines for one inlet or outlet */ +void canvas_deletelinesforio(t_canvas *x, t_text *text, t_inlet *inp, t_outlet *outp) { + canvas_wires_each(oc,t,x) + if ((t.from == text && t.outletp == outp) || + (t.to == text && t.inletp == inp)) + obj_disconnect(t.from, t.outlet, t.to, t.inlet); +} +void canvas_deletelinesfor(t_canvas *x, t_text *text) { + canvas_wires_each(oc,t,x) + if (t.from == text || t.to == text) + obj_disconnect(t.from, t.outlet, t.to, t.inlet); +} + +static void canvas_resortinlets(t_canvas *x); +static void canvas_resortoutlets(t_canvas *x); +static void canvas_push(t_canvas *x, t_floatarg f) {pd_pushsym(x);} +/* assuming that this only ever gets called on toplevel canvases (?) */ +static void canvas_pop(t_canvas *x, t_floatarg fvis) { + pd_popsym(x); canvas_resortinlets(x); canvas_resortoutlets(x); + if (fvis) canvas_vis(x, 1); +} +/* called by m_class.c */ +extern "C" void canvas_popabstraction(t_canvas *x) { + pd_set_newest(x); + pd_popsym(x); canvas_resortinlets(x); canvas_resortoutlets(x); +} + +void canvas_objfor(t_canvas *gl, t_text *x, int argc, t_atom *argv); + +void canvas_restore(t_canvas *x, t_symbol *s, int argc, t_atom *argv) { + if (argc > 3) { + t_atom *ap=argv+3; + if (ap->a_type == A_SYMBOL) { + t_canvasenvironment *e = canvas_getenv(canvas_getcurrent()); + canvas_rename(x, binbuf_realizedollsym(ap->a_symbol, e->argc, e->argv, 1), 0); + } + } + canvas_pop(x,0); /* 0 means "don't touch" here. */ + t_pd *z = gensym("#X")->thing; + if (!z) {error("out of context"); return;} + if (z->_class != canvas_class) {error("wasn't a canvas"); return;} + gobj_setcanvas(x,(t_canvas *)z); + canvas_objfor((t_canvas *)z, x, argc, argv); + newest = x; +} + +static void canvas_loadbang(t_canvas *x); + +static void canvas_loadbangabstractions(t_canvas *x) { + canvas_each(y,x) if (y->_class == canvas_class) { + t_canvas *z = (t_canvas *)y; + if (canvas_isabstraction(z)) canvas_loadbang(z); else canvas_loadbangabstractions(z); + } +} + +void canvas_loadbangsubpatches(t_canvas *x) { + t_symbol *s = gensym("loadbang"); + canvas_each(y,x) if (y->_class == canvas_class) { + t_canvas *z = (t_canvas *)y; + if (!canvas_isabstraction(z)) canvas_loadbangsubpatches(z); + } + canvas_each(y,x) if ((y->_class != canvas_class) && zgetfn(y,s)) pd_vmess(y,s,""); +} + +static void canvas_loadbang(t_canvas *x) { + canvas_loadbangabstractions(x); + canvas_loadbangsubpatches(x); +} + +/* When you ask a canvas its size the result is 2 pixels more than what you gave it to open it; + perhaps there's a 1-pixel border all around it or something. Anyway, we just add the 2 pixels back here; + seems we have to do this for linux but not MSW; not sure about MacOS. */ +#ifdef MSW +#define HORIZBORDER 0 +#define VERTBORDER 0 +#else +#define HORIZBORDER 2 +#define VERTBORDER 2 +#endif + +static void canvas_relocate(t_canvas *x, t_symbol *canvasgeom, t_symbol *topgeom) { + int cxpix, cypix, cw, ch, txpix, typix, tw, th; + if (sscanf(canvasgeom->name, "%dx%d+%d+%d", &cw, &ch, &cxpix, &cypix) < 4 || + sscanf( topgeom->name, "%dx%d+%d+%d", &tw, &th, &txpix, &typix) < 4) + bug("canvas_relocate"); + /* for some reason this is initially called with cw=ch=1 so we just suppress that here. */ + if (cw>5 && ch>5) canvas_setbounds(x, txpix, typix, txpix + cw - HORIZBORDER, typix + ch - VERTBORDER); +} + +void pd_set_newest (t_pd *x) {newest = x;} + +static void *subcanvas_new(t_symbol *s) { + t_atom a[6]; + t_canvas *z = canvas_getcurrent(); + if (!*s->name) s = gensym("/SUBPATCH/"); + SETFLOAT(a, 0); + SETFLOAT(a+1, CANVAS_DEFCANVASYLOC); + SETFLOAT(a+2, CANVAS_DEFCANVASWIDTH); + SETFLOAT(a+3, CANVAS_DEFCANVASHEIGHT); + SETSYMBOL(a+4, s); + SETFLOAT(a+5, 1); + t_canvas *x = canvas_new(0, 0, 6, a); + gobj_setcanvas(x,z); + canvas_pop(x, 1); + return x; +} + +static void canvas_rename_method(t_canvas *x, t_symbol *s, int ac, t_atom *av) { + if (ac && av->a_type == A_SYMBOL) canvas_rename(x, av->a_symbol, 0); + else if (ac && av->a_type == A_DOLLSYM) { + t_canvasenvironment *e = canvas_getenv(x); + pd_pushsym(x); + canvas_rename(x, binbuf_realizedollsym(av->a_symbol, e->argc, e->argv, 1), 0); + pd_popsym(x); + } else canvas_rename(x, gensym("Pd"), 0); +} + +/* ------------------ table ---------------------------*/ + +static t_garray *graph_array(t_canvas *gl, t_symbol *s, t_symbol *tmpl, t_floatarg f, t_floatarg saveit); + +static int tabcount = 0; + +static void *table_new(t_symbol *s, t_floatarg f) { + t_atom a[9]; + t_canvas *z = canvas_getcurrent(); + if (s == &s_) s = symprintf("table%d", tabcount++); + if (f <= 1) f = 100; + SETFLOAT(a, 0); + SETFLOAT(a+1, CANVAS_DEFCANVASYLOC); + SETFLOAT(a+2, 600); + SETFLOAT(a+3, 400); + SETSYMBOL(a+4, s); + SETFLOAT(a+5, 0); + t_canvas *x = canvas_new(0,0,6,a); + gobj_setcanvas(x,z); + /* create a graph for the table */ + t_canvas *gl = canvas_addcanvas(x, &s_, 0, -1, (f > 1 ? f-1 : 1), 1, 50, 350, 550, 50); + graph_array(gl, s, &s_float, f, 0); + canvas_pop(x, 0); + return x; +} + +/* return true if the "canvas" object is an abstraction (so we don't save its contents, fogr example.) */ +int canvas_isabstraction(t_canvas *x) {return x->env!=0;} + +/* return true if the "canvas" object is a "table". */ +int canvas_istable(t_canvas *x) { + t_atom *argv = x->binbuf ? binbuf_getvec( x->binbuf) : 0; + int argc = x->binbuf ? binbuf_getnatom(x->binbuf) : 0; + return argc && argv[0].a_type == A_SYMBOL && argv[0].a_symbol == gensym("table"); +} + +/* return true if the "canvas" object should be treated as a text + object. This is true for abstractions but also for "table"s... */ +/* JMZ: add a flag to gop-abstractions to hide the title */ +static int canvas_showtext(t_canvas *x) { + t_atom *argv = x->binbuf? binbuf_getvec( x->binbuf) : 0; + int argc = x->binbuf? binbuf_getnatom(x->binbuf) : 0; + int isarray = argc && argv[0].a_type == A_SYMBOL && argv[0].a_symbol == gensym("graph"); + return x->hidetext ? 0 : !isarray; +} + +/* get the document containing this canvas */ +t_canvas *canvas_getrootfor(t_canvas *x) { + if (!x->dix->canvas || canvas_isabstraction(x)) return x; + return canvas_getrootfor(x->dix->canvas); +} + +/* ------------------------- DSP chain handling ------------------------- */ + +typedef struct _dspcontext t_dspcontext; + +extern void ugen_start (); +extern void ugen_stop (); +extern "C" t_dspcontext *ugen_start_graph(int toplevel, t_signal **sp, int ninlets, int noutlets); +extern "C" void ugen_add(t_dspcontext *dc, t_object *x); +extern "C" void ugen_connect(t_dspcontext *dc, t_object *from, int outlet, t_object *to, int inlet); +extern "C" void ugen_done_graph(t_dspcontext *dc); + +/* schedule one canvas for DSP. This is called below for all "root" + canvases, but is also called from the "dsp" method for sub- + canvases, which are treated almost like any other tilde object. */ +static void canvas_dodsp(t_canvas *x, int toplevel, t_signal **sp) { + t_object *ob; + t_symbol *dspsym = gensym("dsp"); + /* create a new "DSP graph" object to use in sorting this canvas. + If we aren't toplevel, there are already other dspcontexts around. */ + t_dspcontext *dc = ugen_start_graph(toplevel, sp, obj_nsiginlets(x), obj_nsigoutlets(x)); + /* find all the "dsp" boxes and add them to the graph */ + canvas_each(y,x) if ((ob = pd_checkobject(y)) && zgetfn(y,dspsym)) ugen_add(dc, ob); + /* ... and all dsp interconnections */ + canvas_wires_each(oc,t,x) + if (obj_issignaloutlet(t.from, t.outlet)) + ugen_connect(dc, t.from, t.outlet, t.to, t.inlet); + /* finally, sort them and add them to the DSP chain */ + ugen_done_graph(dc); +} + +static void canvas_dsp(t_canvas *x, t_signal **sp) {canvas_dodsp(x, 0, sp);} + +/* this routine starts DSP for all root canvases. */ +static void canvas_start_dsp() { + if (canvas_dspstate) ugen_stop(); + else sys_gui("pdtk_pd_dsp 1\n"); + ugen_start(); + //timeval v0,v1; gettimeofday(&v0,0); + foreach(x,windowed_canvases) canvas_dodsp(x->first,1,0); + //gettimeofday(&v1,0); printf("canvas_start_dsp took %ld us\n",(v1.tv_sec-v0.tv_sec)*1000000+(v1.tv_usec-v0.tv_usec)); + canvas_dspstate = 1; +} + +/*static*/ void canvas_stop_dsp() { + if (canvas_dspstate) { + ugen_stop(); + sys_gui("pdtk_pd_dsp 0\n"); + canvas_dspstate = 0; + } +} + +/* DSP can be suspended before, and resumed after, operations which might affect the DSP chain. + For example, we suspend before loading and resume afterwards, so that DSP doesn't get resorted for every DSP object in the patch. */ +int canvas_suspend_dsp () { + int rval = canvas_dspstate; + if (rval) canvas_stop_dsp(); + return rval; +} +void canvas_resume_dsp(int oldstate) {if (oldstate) canvas_start_dsp();} +/* this is equivalent to suspending and resuming in one step. */ +void canvas_update_dsp () {if (canvas_dspstate) canvas_start_dsp();} + +extern "C" void glob_dsp(void *self, t_symbol *s, int argc, t_atom *argv) { + if (argc) { + int newstate = atom_getintarg(0, argc, argv); + if (newstate && !canvas_dspstate) { + canvas_start_dsp(); + sys_set_audio_state(1); + } else if (!newstate && canvas_dspstate) { + sys_set_audio_state(0); + canvas_stop_dsp(); + } + } else post("dsp state %d", canvas_dspstate); +} + +extern "C" void *canvas_getblock(t_class *blockclass, t_canvas **canvasp) { + t_canvas *canvas = *canvasp; + void *ret = 0; + canvas_each(g,canvas) if (g->_class == blockclass) ret = g; + *canvasp = canvas->dix->canvas; + return ret; +} + +/******************* redrawing data *********************/ + +static t_float slot_getcoord(t_slot *f, t_template *, t_word *wp, int loud); +static void slot_setcoord(t_slot *f, t_template *, t_word *wp, float pix, int loud); +static t_float slot_cvttocoord(t_slot *f, float val); +static t_template *template_new(t_symbol *sym, int argc, t_atom *argv); +static void template_free(t_template *x); +static int template_match(t_template *x1, t_template *x2); +static int template_find_field(t_template *x, t_symbol*name, int*p_onset, int*p_type, t_symbol **p_arraytype); +static t_float template_getfloat( t_template *x, t_symbol *fieldname, t_word *wp, int loud); +static void template_setfloat( t_template *x, t_symbol *fieldname, t_word *wp, t_float f, int loud); +static t_symbol *template_getsymbol(t_template *x, t_symbol *fieldname, t_word *wp, int loud); +static void template_setsymbol(t_template *x, t_symbol *fieldname, t_word *wp, t_symbol *s, int loud); +static t_template *gtemplate_get(t_gtemplate *x); +static t_template *template_findbyname(t_symbol *s); +static t_canvas *template_findcanvas(t_template *tmpl); +static void template_notify(t_template *, t_symbol *s, int argc, t_atom *argv); + +/* find the template defined by a canvas, and redraw all elements for that */ +void canvas_redrawallfortemplatecanvas(t_canvas *x, int action) { + t_template *tmpl; + t_symbol *s1 = gensym("struct"); + canvas_each(g,x) { + t_object *ob = pd_checkobject(g); + if (!ob || /* ob->type != T_OBJECT || */ binbuf_getnatom(ob->binbuf) < 2) continue; + t_atom *argv = binbuf_getvec(ob->binbuf); + if (argv[0].a_type != A_SYMBOL || argv[1].a_type != A_SYMBOL || argv[0].a_symbol != s1) + continue; + tmpl = template_findbyname(argv[1].a_symbol); + //canvas_redrawallfortemplate(tmpl, action); + } + //canvas_redrawallfortemplate(0, action); +} + +//#define canvas_each2(CHILD,CANVAS) for(CHILD=&(CANVAS)->list; *CHILD; CHILD=&(*CHILD)->next()) +/* just a raw remove, no other business */ +/* doesn't work (why?) */ +static t_gobj *canvas_remove_nth(t_canvas *x, int n) { +/* + t_gobj **y; + canvas_each2(y,x) { + fprintf(stderr,"n=%i *y=%p\n",n,*y); + if (!n) { + t_gobj *z = *y; + *y = z->next(); + z->next() = 0; + return z; + } else n--; + } + return 0; +*/ +} + +/* just a raw insert, no other business */ +/* doesn't work (why?) */ +static void canvas_insert_nth(t_canvas *x, int n, t_gobj *nu) { +/* + t_gobj **y; + canvas_each2(y,x) if (!n) { + t_gobj *z = *y; + *y = nu; + nu->next() = z; + return; + } else n--; + *y = nu; +*/ +} + +void canvas_disconnect(t_canvas *x, float from_, float outlet, float to_, float inlet) { + int ifrom=(int)from_, ito=int(to_); + t_gobj *from=0, *to=0; + canvas_each(gfrom,x) if (gfrom->dix->index == ifrom) {from=gfrom; break;} + canvas_each( gto,x) if ( gto->dix->index == ito) { to= gto; break;} + if (!from || !to) goto bad; + obj_disconnect((t_object *)from, int(outlet), (t_object *)to, int(inlet)); + return; +bad: + post("dumb."); +} + +static t_binbuf *canvas_cut_wires(t_canvas *x, t_gobj *o); +static void canvas_paste_wires(t_canvas *x, t_binbuf *buf); + +/* recursively check for abstractions to reload as result of a save. + Don't reload the one we just saved ("except") though. */ +/* LATER try to do the same trick for externs. */ +static void canvas_doreload(t_canvas *gl, t_symbol *name, t_symbol *dir, t_gobj *except) { + int i=0, nobj = gl->boxes->size(); + for (t_gobj *g = gl->boxes->first(); g && i < nobj; i++) { + if (g != except && g->_class == canvas_class) { + t_canvas *c = (t_canvas *)g; + if (canvas_isabstraction(c) && c->name==name && canvas_getdir(c) == dir) { + /* we're going to remake the object, so "g" will go stale. Get its index here, and afterwards restore g. + Also, the replacement will be at the end of the list, so we don't do g = g->next() in this case. */ + int j = g->dix->index; + int hadwindow = gl->havewindow; + if (!hadwindow) canvas_vis(canvas_getcanvas(gl), 1); + t_binbuf *buf = canvas_cut_wires(gl,g); + gl->boxes->remove(j); + //MISSING: remake the object here. + canvas_paste_wires(gl,buf); + if (!hadwindow) canvas_vis(canvas_getcanvas(gl), 0); + continue; + } + canvas_doreload(c,name,dir,except); + } + g = g->next(); + } +} + +void canvas_reload(t_symbol *name, t_symbol *dir, t_gobj *except) { + foreach(x,windowed_canvases) canvas_doreload(x->first, name, dir, except); +} + +/* ------------------------ event handling ------------------------ */ + +/* set a canvas up as a graph-on-parent. + Set reasonable defaults for any missing paramters and redraw things if necessary. */ +void canvas_setgraph(t_canvas *x, int flag, int nogoprect) { + if (!flag && x->gop) { + x->gop = 0; + } else if (flag) { + if (x->pixwidth <= 0) x->pixwidth = CANVAS_DEFGRAPHWIDTH; + if (x->pixheight <= 0) x->pixheight = CANVAS_DEFGRAPHHEIGHT; + SET(gop,1); + SET(hidetext,!!(flag&2)); + if (!nogoprect && !x->goprect) canvas_each(g,x) if (pd_checkobject(g)) {SET(goprect,1); break;} + if (canvas_isvisible(x) && x->goprect) gobj_changed(x,0); + } +} + +/* keep me */ +static int canvas_isconnected (t_canvas *x, t_text *ob1, int n1, t_text *ob2, int n2) { + canvas_wires_each(oc,t,x) + if (t.from == ob1 && t.outlet == n1 && t.to == ob2 && t.inlet == n2) return 1; + return 0; +} + +/* ----------------------------- window stuff ----------------------- */ + +void canvas_close(t_canvas *x) { + if (x->dix->canvas) canvas_vis(x, 0); else pd_free(x); +} + +static int canvas_find_index1, canvas_find_index2; +static t_binbuf *canvas_findbuf; +int binbuf_match(t_binbuf *inbuf, t_binbuf *searchbuf); + +/* find an atom or string of atoms */ +static int canvas_dofind(t_canvas *x, int *myindex1p) { + int myindex1 = *myindex1p, myindex2=0; + if (myindex1 >= canvas_find_index1) { + canvas_each(y,x) { + t_object *ob = pd_checkobject(y); + if (ob && binbuf_match(ob->ob_binbuf, canvas_findbuf)) { + if (myindex1 > canvas_find_index1 || + (myindex1 == canvas_find_index1 && myindex2 > canvas_find_index2)) { + canvas_find_index1 = myindex1; + canvas_find_index2 = myindex2; + vmess(x,gensym("menu-open"),""); + return 1; + } + } + myindex2++; + } + } + myindex2=0; + canvas_each(y,x) { + if (y->_class == canvas_class) { + (*myindex1p)++; + if (canvas_dofind((t_canvas *)y, myindex1p)) return 1; + } + myindex2++; + } + return 0; +} + +static void canvas_find_parent(t_canvas *x) { + if (x->dix->canvas) canvas_vis(canvas_getcanvas(x->dix->canvas), 1); +} + +static int canvas_dofinderror(t_canvas *gl, void *error_object) { + canvas_each(g,gl) { + if (g==error_object) { + /* got it... now show it. */ + canvas_vis(canvas_getcanvas(gl), 1); + return 1; + } else if (g->_class == canvas_class) { + if (canvas_dofinderror((t_canvas *)g, error_object)) return 1; + } + } + return 0; +} + +void canvas_finderror(void *error_object) { + foreach(x,windowed_canvases) if (canvas_dofinderror(x->first, error_object)) return; + post("... sorry, I couldn't find the source of that error."); +} + +extern t_class *text_class; +extern t_class *dummy_class; + +static int is_dummy (t_text *x) {return x->_class==dummy_class;} + +long canvas_base_o_index(void); + +void canvas_connect(t_canvas *x, t_floatarg ffrom, t_floatarg foutlet, t_floatarg fto,t_floatarg finlet) { + int base = canvas_base_o_index(); + int ifrom=base+int(ffrom), outlet=(int)foutlet; + int ito=base+int(fto), inlet=(int)finlet; + t_gobj *gfrom=0, *gto=0; + t_object *from=0, *to=0; + t_outconnect *oc; + if (ifrom<0) goto bad; + if (ito <0) goto bad; + canvas_each(zfrom,x) if (zfrom->dix->index == ifrom) {gfrom=zfrom; break;} + if (!gfrom) goto bad; + canvas_each( zto,x) if ( zto->dix->index == ito) { gto= zto; break;} + if (!gto) goto bad; + from = pd_checkobject(gfrom); + to = pd_checkobject( gto); + if (!from || !to) goto bad; + /* if object creation failed, make dummy inlets or outlets as needed */ + if (is_dummy(from)) while (outlet >= obj_noutlets(from)) outlet_new(from, &s_); + if (is_dummy(to)) while ( inlet >= obj_ninlets(to)) inlet_new(to,to,&s_,&s_); + if (!(oc = obj_connect(from,outlet,to,inlet))) goto bad; + pd_set_newest(oc); + gobj_setcanvas(oc,x); + oc->dix->index = x->next_w_index++; + return; +bad: + post("%s %d %d %d %d (%s->%s) connection failed", x->name->name,ifrom,outlet,ito,inlet, + from ? class_getname(from->_class) : "???", + to ? class_getname( to->_class) : "???"); +} + +#define ARRAYPAGESIZE 1000 /* this should match the page size in u_main.tk */ +/* aux routine to bash leading '#' to '$' for dialogs in u_main.tk which can't send symbols + starting with '$' (because the Pd message interpreter would change them!) */ +static t_symbol *sharptodollar(t_symbol *s) { + if (*s->name != '#') return s; + return symprintf("$%s",s->name+1); +} + +/* --------- "pure" arrays with scalars for elements. --------------- */ + +/* Pure arrays have no a priori graphical capabilities. +They are instantiated by "garrays" below or can be elements of other +scalars (g_scalar.c); their graphical behavior is defined accordingly. */ + +t_class *array_class; + +static t_array *array_new(t_symbol *templatesym, t_gpointer *parent) { + t_array *x = (t_array *)pd_new(array_class); + t_template *t = template_findbyname(templatesym); + x->templatesym = templatesym; + x->n = 1; + x->elemsize = sizeof(t_word) * t->n; + /* aligned allocation */ + x->vec = (char *)getalignedbytes(x->elemsize); + /* note here we blithely copy a gpointer instead of "setting" a new one; this gpointer isn't accounted for + and needn't be since we'll be deleted before the thing pointed to gets deleted anyway; see array_free. */ + x->gp = *parent; + word_init((t_word *)x->vec, t, parent); + return x; +} + +void array_resize(t_array *x, int n) { + t_template *t = template_findbyname(x->templatesym); + if (n < 1) n = 1; + int oldn = x->n; + int elemsize = sizeof(t_word) * t->n; + x->vec = (char *)resizealignedbytes(x->vec, oldn * elemsize, n * elemsize); + x->n = n; + if (n > oldn) { + char *cp = x->vec + elemsize * oldn; + for (int i = n-oldn; i--; cp += elemsize) { + t_word *wp = (t_word *)cp; + word_init(wp, t, &x->gp); + } + } +} + +static void array_resize_and_redraw(t_array *array, int n) { +/* what was that for??? */ + array_resize(array,n); + gobj_changed(array,0); +} + +void word_free(t_word *wp, t_template *t); + +static void array_free(t_array *x) { + t_template *scalartemplate = template_findbyname(x->templatesym); + for (int i=0; i < x->n; i++) word_free((t_word *)(x->vec + x->elemsize*i), scalartemplate); + freealignedbytes(x->vec, x->elemsize * x->n); +} + +/* --------------------- graphical arrays (garrays) ------------------- */ + +t_class *garray_class; + +static t_pd *garray_arraytemplatecanvas; + +/* create invisible, built-in canvases to determine the templates for floats +and float-arrays. */ + +void pd_eval_text2(char *s) {pd_eval_text(s,strlen(s));} + +extern "C" void garray_init () { + hack = 0; /* invisible canvases must be, uh, invisible */ + if (garray_arraytemplatecanvas) return; + t_binbuf *b = binbuf_new(); + glob_setfilename(0, gensym("_float"), gensym(".")); + pd_eval_text2( + "#N canvas 0 0 458 153 10;\n" + "#X obj 43 31 struct _float_array array z float float style float linewidth float color;\n" + "#X obj 43 70 plot z color linewidth 0 0 1 style;\n"); + vmess(s__X.thing, gensym("pop"), "i", 0); + glob_setfilename(0, gensym("_float_array"), gensym(".")); + pd_eval_text2( + "#N canvas 0 0 458 153 10;\n" + "#X obj 39 26 struct float float y;\n"); + garray_arraytemplatecanvas = s__X.thing; + vmess(s__X.thing, gensym("pop"), "i", 0); + glob_setfilename(0, &s_, &s_); + binbuf_free(b); + hack = 1; /* enable canvas visibility for upcoming canvases */ +} + +/* create a new scalar attached to a symbol. Used to make floating-point +arrays (the scalar will be of type "_float_array"). Currently this is +always called by graph_array() below; but when we make a more general way +to save and create arrays this might get called more directly. */ + +static t_garray *graph_scalar(t_canvas *gl, t_symbol *s, t_symbol *templatesym, int saveit) { + if (!template_findbyname(templatesym)) return 0; + t_garray *x = (t_garray *)pd_new(garray_class); + x->scalar = scalar_new(gl, templatesym); + x->realname = s; + x->realname = canvas_realizedollar(gl, s); + pd_bind(x,x->realname); + x->usedindsp = 0; + x->saveit = saveit; + x->listviewing = 0; + canvas_add(gl,x); + x->canvas = gl; + return x; +} + +#define TEMPLATE_CHECK(tsym,ret) if (!t) {\ + error("couldn't find template %s", tsym->name); return ret;} + +#define TEMPLATE_FLOATY(a,ret) if (!a) {\ + error("%s: needs floating-point 'y' field", x->realname); return ret;} + + /* get a garray's "array" structure. */ +t_array *garray_getarray(t_garray *x) { + int zonset, ztype; + t_symbol *zarraytype; + t_scalar *sc = x->scalar; + t_template *t = template_findbyname(sc->t); + TEMPLATE_CHECK(sc->t,0) + if (!template_find_field(t, gensym("z"), &zonset, &ztype, &zarraytype)) { + error("template %s has no 'z' field", sc->t->name); + return 0; + } + if (ztype != DT_ARRAY) { + error("template %s, 'z' field is not an array", sc->t->name); + return 0; + } + return sc->v[zonset].w_array; +} + + /* get the "array" structure and furthermore check it's float */ +static t_array *garray_getarray_floatonly(t_garray *x, int *yonsetp, int *elemsizep) { + t_array *a = garray_getarray(x); + int yonset, type; + t_symbol *arraytype; + t_template *t = template_findbyname(a->templatesym); + if (!template_find_field(t,&s_y,&yonset,&type,&arraytype) || type != DT_FLOAT) + return 0; + *yonsetp = yonset; + *elemsizep = a->elemsize; + return a; +} + +/* get the array's name. Return nonzero if it should be hidden */ +int garray_getname(t_garray *x, t_symbol **namep) { +// *namep = x->name; + *namep = x->realname; + return x->hidename; +} + + +/* if there is one garray in a graph, reset the graph's coordinates + to fit a new size and style for the garray */ +static void garray_fittograph(t_garray *x, int n, int style) { + t_array *array = garray_getarray(x); + t_canvas *gl = x->canvas; + if (gl->boxes->first() == x && !x->next()) { + vmess(gl,gensym("bounds"),"ffff",0.,gl->y1, double(style == PLOTSTYLE_POINTS || n == 1 ? n : n-1), gl->y2); + /* close any dialogs that might have the wrong info now... */ + } + array_resize_and_redraw(array, n); +} + +/* handle "array" message to canvases; call graph_scalar above with +an appropriate template; then set size and flags. This is called +from the menu and in the file format for patches. LATER replace this +by a more coherent (and general) invocation. */ + +t_garray *graph_array(t_canvas *gl, t_symbol *s, t_symbol *templateargsym, t_floatarg fsize, t_floatarg fflags) { + int n = (int)fsize, zonset, ztype, saveit; + t_symbol *zarraytype; + t_symbol *templatesym = gensym("pd-_float_array"); + int flags = (int)fflags; + int filestyle = (flags & 6)>>1; + int style = filestyle == 0 ? PLOTSTYLE_POLY : filestyle == 1 ? PLOTSTYLE_POINTS : filestyle; + if (templateargsym != &s_float) {error("%s: only 'float' type understood", templateargsym->name); return 0;} + t_template *t = template_findbyname(templatesym); + TEMPLATE_CHECK(templatesym,0) + if (!template_find_field(t, gensym("z"), &zonset, &ztype, &zarraytype)) { + error("template %s has no 'z' field", templatesym->name); + return 0; + } + if (ztype != DT_ARRAY) {error("template %s, 'z' field is not an array", templatesym->name); return 0;} + t_template *ztemplate = template_findbyname(zarraytype); + if (!ztemplate) {error("no template of type %s", zarraytype->name); return 0;} + saveit = (flags & 1) != 0; + t_garray *x = graph_scalar(gl, s, templatesym, saveit); + x->hidename = (flags>>3)&1; + if (n <= 0) n = 100; + array_resize(x->scalar->v[zonset].w_array, n); + template_setfloat(t, gensym("style"), x->scalar->v, style, 1); + template_setfloat(t, gensym("linewidth"), x->scalar->v, style==PLOTSTYLE_POINTS?2:1, 1); + t_pd *x2 = pd_findbyclass(gensym("#A"), garray_class); + if (x2) pd_unbind(x2,gensym("#A")); + pd_bind(x,gensym("#A")); + garray_redraw(x); + return x; +} + +/* find the graph most recently added to this canvas; if none exists, return 0. */ +static t_canvas *canvas_findgraph(t_canvas *x) { + t_gobj *y = 0; + canvas_each(z,x) if (z->_class==canvas_class && ((t_canvas *)z)->gop) y = z; + return (t_canvas *)y; +} + +/* this is called back from the dialog window to create a garray. + The otherflag requests that we find an existing graph to put it in. */ +static void canvas_arraydialog(t_canvas *parent, t_symbol *name, t_floatarg size, t_floatarg fflags, t_floatarg otherflag) { + t_canvas *gl; + int flags = (int)fflags; + if (size < 1) size = 1; + if (otherflag == 0 || !(gl = canvas_findgraph(parent))) + gl = canvas_addcanvas(parent, &s_, 0, 1, (size>1 ? size-1 : size), -1, 0, 0, 0, 0); + graph_array(gl, sharptodollar(name), &s_float, size, flags); +} + +void garray_arrayviewlist_close(t_garray *x) { + x->listviewing = 0; + sys_vgui("pdtk_array_listview_closeWindow %s\n", x->realname->name); +} + +/* this is called from the properties dialog window for an existing array */ +void garray_arraydialog(t_garray *x, t_symbol *name, t_floatarg fsize, t_floatarg fflags, t_floatarg deleteit) { + int flags = (int)fflags; + int saveit = (flags&1)!=0; + int style = (flags>>1)&3; + float stylewas = template_getfloat(template_findbyname(x->scalar->t), gensym("style"), x->scalar->v, 1); + if (deleteit) {canvas_delete(x->canvas,x); return;} + t_symbol *argname = sharptodollar(name); + t_array *a = garray_getarray(x); + t_template *scalartemplate; + if (!a) {error("can't find array"); return;} + if (!(scalartemplate = template_findbyname(x->scalar->t))) {error("no template of type %s", x->scalar->t->name); return;} + if (argname != x->realname) { + if (x->listviewing) garray_arrayviewlist_close(x); + x->realname = argname; /* is this line supposed to exist? */ + pd_unbind(x,x->realname); + x->realname = canvas_realizedollar(x->canvas, argname); + pd_bind(x,x->realname); + gobj_changed(x,0); + } + int size = max(1,int(fsize)); + if (size != a->n) garray_resize(x, size); + else if (style != stylewas) garray_fittograph(x, size, style); + template_setfloat(scalartemplate, gensym("style"), x->scalar->v, (float)style, 0); + garray_setsaveit(x, saveit!=0); + garray_redraw(x); +} + +void garray_arrayviewlist_new(t_garray *x) { + char *s = x->realname->name; + int yonset=0, elemsize=0; + char cmdbuf[200]; + t_array *a = garray_getarray_floatonly(x, &yonset, &elemsize); + if (!a) {error("garray_arrayviewlist_new()"); return;} + x->listviewing = 1; + sprintf(cmdbuf, "pdtk_array_listview_new %%s %s %d\n",s,0); + for (int i=0; i < ARRAYPAGESIZE && i < a->n; i++) { + float yval = *(float *)(a->vec + elemsize*i + yonset); + sys_vgui(".%sArrayWindow.lb insert %d {%d) %g}\n",s,i,i,yval); + } +} + +void garray_arrayviewlist_fillpage(t_garray *x, t_float page, t_float fTopItem) { + char *s = x->realname->name; + int yonset=0, elemsize=0, topItem=(int)fTopItem; + t_array *a = garray_getarray_floatonly(x, &yonset, &elemsize); + if (!a) {error("garray_arrayviewlist_fillpage()"); return;} + if (page < 0) { + page = 0; + sys_vgui("pdtk_array_listview_setpage %s %d\n",s,(int)page); + } else if ((page * ARRAYPAGESIZE) >= a->n) { + page = (int)(((int)a->n - 1)/ (int)ARRAYPAGESIZE); + sys_vgui("pdtk_array_listview_setpage %s %d\n",s,(int)page); + } + sys_vgui(".%sArrayWindow.lb delete 0 %d\n",s,ARRAYPAGESIZE-1); + for (int i = (int)page * ARRAYPAGESIZE; (i < (page+1)*ARRAYPAGESIZE && i < a->n); i++) { + float yval = *(float *)(a->vec + elemsize*i + yonset); + sys_vgui(".%sArrayWindow.lb insert %d {%d) %g}\n",s,i%ARRAYPAGESIZE,i,yval); + } + sys_vgui(".%sArrayWindow.lb yview %d\n",s,topItem); +} + +static void garray_free(t_garray *x) { + if (x->listviewing) garray_arrayviewlist_close(x); + pd_unbind(x,x->realname); + /* LATER find a way to get #A unbound earlier (at end of load?) */ + t_pd *x2; + while ((x2 = pd_findbyclass(gensym("#A"), garray_class))) pd_unbind(x2, gensym("#A")); +} + +/* ------------- code used by both array and plot widget functions ---- */ + +static void array_redraw(t_array *a, t_canvas *canvas) { + /* what was that for? */ + scalar_redraw(a->gp.scalar, canvas); +} + +static int canvas_xtopixels(t_canvas *x, float xval); +static int canvas_ytopixels(t_canvas *x, float yval); + + /* routine to get screen coordinates of a point in an array */ +static void array_getcoordinate(t_canvas *canvas, char *elem, int xonset, int yonset, int wonset, int indx, +float basex, float basey, float xinc, t_slot *xslot, t_slot *yslot, t_slot *wslot, +float *xp, float *yp, float *wp) { + float xval, yval, ypix, wpix; + if (xonset >= 0) xval = *(float *)(elem + xonset); else xval = indx * xinc; + if (yonset >= 0) yval = *(float *)(elem + yonset); else yval = 0; + ypix = canvas_ytopixels(canvas, basey + slot_cvttocoord(yslot, yval)); + if (wonset >= 0) { + /* found "w" field which controls linewidth. */ + float wval = *(float *)(elem + wonset); + wpix = canvas_ytopixels(canvas, basey + slot_cvttocoord(yslot,yval) + slot_cvttocoord(wslot,wval)) - ypix; + if (wpix < 0) wpix = -wpix; + } else wpix = 1; + *xp = canvas_xtopixels(canvas, basex + slot_cvttocoord(xslot, xval)); + *yp = ypix; + *wp = wpix; +} + +static struct { + float xcumulative, ycumulative; + t_slot *xfield, *yfield; + t_canvas *canvas; + t_scalar *scalar; + t_array *array; + t_word *wp; + t_template *t; + int npoints, elemsize; + float initx, xperpix, yperpix; + int lastx, fatten; +} ammo; + +/* LATER protect against the template changing or the scalar disappearing + probably by attaching a gpointer here ... */ +#if 0 +static void array_motion(void *z, t_floatarg dx, t_floatarg dy) { + ammo.xcumulative += dx * ammo.xperpix; + ammo.ycumulative += dy * ammo.yperpix; + if (ammo.xfield) {// xy plot + for (int i=0; i ammo.lastx ? -1 : 1; + nchange = 1 + increment * (ammo.lastx - thisx); + x2 = thisx; + for (int i=0; i 1) newy -= ydiff/(nchange-1); + } + ammo.lastx = thisx; + } + if (ammo.scalar) scalar_redraw(ammo.scalar, ammo.canvas); + if (ammo.array) array_redraw(ammo.array, ammo.canvas); +} +#endif + +int scalar_doclick(t_word *data, t_template *t, t_scalar *sc, t_array *ap, t_canvas *owner, float xloc, float yloc, +int xpix, int ypix, int shift, int alt, int dbl, int doit); + +static int array_getfields(t_symbol *elemtemplatesym, t_canvas **elemtemplatecanvasp, +t_template **elemtemplatep, int *elemsizep, t_slot *xslot, t_slot *yslot, t_slot *wslot, +int *xonsetp, int *yonsetp, int *wonsetp); + +/* try clicking on an element of the array as a scalar (if clicking + on the trace of the array failed) */ +static int array_doclick_element(t_array *array, t_canvas *canvas, t_scalar *sc, t_array *ap, +t_symbol *elemtemplatesym, float linewidth, float xloc, float xinc, float yloc, +t_slot *xfield, t_slot *yfield, t_slot *wfield, int xpix, int ypix, int shift, int alt, int dbl, int doit) { + t_canvas *elemtemplatecanvas; + t_template *elemtemplate; + int elemsize, yonset, wonset, xonset, incr; + //float xsum=0; + if (elemtemplatesym == &s_float) return 0; + if (array_getfields(elemtemplatesym, &elemtemplatecanvas, + &elemtemplate, &elemsize, xfield, yfield, wfield, &xonset, &yonset, &wonset)) + return 0; + /* if it has more than 2000 points, just check 300 of them. */ + if (array->n < 2000) incr=1; else incr = array->n/300; + for (int i=0; i < array->n; i += incr) { + //float usexloc = xonset>=0 ? xloc + slot_cvttocoord(xfield, *(float *)&array->vec[elemsize*i+xonset]) : xloc + xsum; + //if (xonset>=0) xsum += xinc; + //float useyloc = yloc + (yonset>=0 ? slot_cvttocoord(yfield, *(float *)&array->vec[elemsize*i+yonset]):0); + int hit = 0; + /* hit = scalar_doclick((t_word *)&array->vec[elemsize*i], + elemtemplate, 0, array, canvas, usexloc, useyloc, xpix, ypix, shift, alt, dbl, doit);*/ + if (hit) return hit; + } + return 0; +} + +static float canvas_pixelstox(t_canvas *x, float xpix); +static float canvas_pixelstoy(t_canvas *x, float xpix); + +/* convert an X screen distance to an X coordinate increment. */ +static float canvas_dpixtodx(t_canvas*x,float dxpix){return dxpix*(canvas_pixelstox(x,1)-canvas_pixelstox(x,0));} +static float canvas_dpixtody(t_canvas*x,float dypix){return dypix*(canvas_pixelstoy(x,1)-canvas_pixelstoy(x,0));} + +/* LATER move this and others back into plot parentwidget code, so + they can be static (look in g_canvas.h for candidates). */ +int array_doclick(t_array *array, t_canvas *canvas, t_scalar *sc, t_array *ap, +t_symbol *elemtemplatesym, float linewidth, float xloc, float xinc, float yloc, float scalarvis, +t_slot *xfield, t_slot *yfield, t_slot *wfield, int xpix, int ypix, int shift, int alt, int dbl, int doit) { + t_canvas *elemtemplatecanvas; + t_template *elemtemplate; + int elemsize, yonset, wonset, xonset; + if (!array_getfields(elemtemplatesym, &elemtemplatecanvas, &elemtemplate, &elemsize, + xfield, yfield, wfield, &xonset, &yonset, &wonset)) { + float best = 100; + /* if it has more than 2000 points, just check 1000 of them. */ + int incr = (array->n <= 2000 ? 1 : array->n / 1000); + for (int i=0; i < array->n; i += incr) { + float pxpix, pypix, pwpix, dx, dy; + array_getcoordinate(canvas, &array->vec[elemsize*i], xonset, yonset, wonset, i, + xloc, yloc, xinc, xfield, yfield, wfield, &pxpix, &pypix, &pwpix); + if (pwpix < 4) pwpix = 4; + dx = fabs(pxpix-xpix); if (dx>8) continue; + dy = fabs(pypix-ypix); if (dx+dy= 0) { + dy = fabs(pypix+pwpix-ypix); if (dx+dy < best) best = dx+dy; + dy = fabs(pypix-pwpix-ypix); if (dx+dy < best) best = dx+dy; + } + } if (best > 8) { + if (scalarvis != 0) return array_doclick_element(array, canvas, sc, ap, elemtemplatesym, + linewidth, xloc, xinc, yloc, xfield, yfield, wfield, xpix, ypix, shift, alt, dbl, doit); + return 0; + } + best += 0.001; /* add truncation error margin */ + for (int i=0; i < array->n; i += incr) { + float pxpix, pypix, pwpix, dx, dy, dy2, dy3; + array_getcoordinate(canvas, &array->vec[elemsize*i], xonset, yonset, wonset, i, + xloc, yloc, xinc, xfield, yfield, wfield, &pxpix, &pypix, &pwpix); + if (pwpix < 4) pwpix = 4; + dx = fabs(pxpix-xpix); + dy = fabs(pypix-ypix); + if (wonset >= 0) { + dy2 = fabs(pypix+pwpix-ypix); + dy3 = fabs(pypix-pwpix-ypix); + if (yonset < 0) dy = 100; + } else dy2 = dy3 = 100; + if (dx + dy <= best || dx + dy2 <= best || dx + dy3 <= best) { + if (dyvec; + ammo.elemsize = elemsize; + ammo.canvas = canvas; + ammo.scalar = sc; + ammo.array = ap; + ammo.t = elemtemplate; + ammo.xperpix = canvas_dpixtodx(canvas, 1); + ammo.yperpix = canvas_dpixtody(canvas, 1); + if (alt && xpix < pxpix) { /* delete a point */ + if (array->n <= 1) return 0; + memmove(&array->vec[elemsize*i], &array->vec[elemsize*(i+1)], (array->n-1-i) * elemsize); + array_resize_and_redraw(array, array->n - 1); + return 0; + } else if (alt) { + /* add a point (after the clicked-on one) */ + array_resize_and_redraw(array, array->n + 1); + elem = array->vec; + memmove(elem + elemsize * (i+1), elem + elemsize*i, (array->n-i-1) * elemsize); + i++; + } + if (xonset >= 0) { + ammo.xfield = xfield; + ammo.xcumulative = slot_getcoord(xfield,ammo.t,(t_word *)(elem+elemsize*i),1); + ammo.wp = (t_word *)(elem + elemsize*i); + if (shift) ammo.npoints = array->n - i; + else ammo.npoints = 1; + } else { + ammo.xfield = 0; + ammo.xcumulative = 0; + ammo.wp = (t_word *)elem; + ammo.npoints = array->n; + ammo.initx = i; + ammo.lastx = i; + ammo.xperpix *= (xinc == 0 ? 1 : 1./xinc); + } + if (ammo.fatten) { + ammo.yfield = wfield; + ammo.ycumulative = slot_getcoord(wfield,ammo.t,(t_word *)(elem+elemsize*i),1); + ammo.yperpix *= -ammo.fatten; + } else if (yonset >= 0) { + ammo.yfield = yfield; + ammo.ycumulative = slot_getcoord(yfield,ammo.t,(t_word *)(elem+elemsize*i),1); + } else { + ammo.yfield = 0; + ammo.ycumulative = 0; + } + /* canvas_grab(canvas, 0, array_motion, 0, xpix, ypix); */ + } + return 0; + } + } + } + return 0; +} + +static void garray_save(t_gobj *z, t_binbuf *b) { + t_garray *x = (t_garray *)z; + t_array *array = garray_getarray(x); + t_template *scalartemplate; + /* LATER "save" the scalar as such */ + if (x->scalar->t != gensym("pd-_float_array")) {error("can't save arrays of type %s yet", x->scalar->t->name); return;} + if (!(scalartemplate = template_findbyname(x->scalar->t))) {error("no template of type %s", x->scalar->t->name); return;} + int style = (int)template_getfloat(scalartemplate, gensym("style"), x->scalar->v, 0); + int filestyle = (style == PLOTSTYLE_POINTS ? 1 : (style == PLOTSTYLE_POLY ? 0 : style)); + binbuf_addv(b, "ttsisi;","#X","array", x->realname, array->n, &s_float, x->saveit+2*filestyle+8*x->hidename); + if (x->saveit) { + int n = array->n, n2 = 0; + while (n2 < n) { + int chunk = imin(n-n2,1000); + binbuf_addv(b,"ti","#A",n2); + for (int i=0; ivec)[n2+i]); + binbuf_addv(b, ";"); + n2 += chunk; + } + } +} + +/* required by d_array.c and d_soundfile */ +void garray_redraw(t_garray *x) {gobj_changed(x,0);} + +/* those three required by d_array.c */ +void garray_usedindsp(t_garray *x) {x->usedindsp = 1;} +int garray_npoints(t_garray *x) {return garray_getarray(x)->n;} /* get the length */ +char *garray_vec(t_garray *x) {return (char *)garray_getarray(x)->vec;} /* get the contents */ + +/* routine that checks if we're just an array of floats and if so returns the goods */ +int garray_getfloatarray(t_garray *x, int *size, t_float **vec) { + int yonset, elemsize; + t_array *a = garray_getarray_floatonly(x, &yonset, &elemsize); + TEMPLATE_FLOATY(a,0) + if (elemsize != sizeof(t_word)) {error("%s: has more than one field", x->realname); return 0;} + *size = garray_npoints(x); + *vec = (float *)garray_vec(x); + return 1; +} + +/* set the "saveit" flag */ +void garray_setsaveit(t_garray *x, int saveit) { + if (x->saveit && !saveit) post("warning: array %s: clearing save-in-patch flag", x->realname->name); + x->saveit = saveit; +} + +static void garray_const(t_garray *x, t_floatarg g) { + int yonset, elemsize; + t_array *array = garray_getarray_floatonly(x, &yonset, &elemsize); + TEMPLATE_FLOATY(array,) + for (int i=0; in; i++) *((float *)(array->vec + elemsize*i) + yonset) = g; + garray_redraw(x); +} + +/* sum of Fourier components; called from functions below */ +static void garray_dofo(t_garray *x, int npoints, float dcval, int nsin, t_float *vsin, int sineflag) { + double phase, fj; + int yonset, i, j, elemsize; + t_array *array = garray_getarray_floatonly(x, &yonset, &elemsize); + TEMPLATE_FLOATY(array,) + if (npoints == 0) npoints = 512; /* dunno what a good default would be... */ + if (npoints != (1 << ilog2(npoints))) + post("%s: rounnding to %d points", array->templatesym->name, (npoints = (1<n; i++, phase += phaseincr) { + double sum = dcval; + if (sineflag) for (j=0, fj=phase; jvec + elemsize*i) + yonset) = sum; + } + garray_redraw(x); +} + +static void garray_sinesum(t_garray *x, t_symbol *s, int argc, t_atom *argv) { + if (argc < 2) {error("%s: need number of points and partial strengths", x->realname->name); return;} + t_float *svec = (t_float *)getbytes(sizeof(t_float) * argc); + int npoints = atom_getintarg(0,argc--,argv++); + argv++, argc--; /* is it normal that this happens a second time? */ + for (int i=0; i < argc; i++) svec[i] = atom_getfloatarg(i, argc, argv); + garray_dofo(x, npoints, 0, argc, svec, 1); + free(svec); +} +static void garray_cosinesum(t_garray *x, t_symbol *s, int argc, t_atom *argv) { + if (argc < 2) {error("%s: need number of points and partial strengths", x->realname->name); return;} + t_float *svec = (t_float *)getbytes(sizeof(t_float) * argc); + int npoints = atom_getintarg(0,argc--,argv++); + for (int i=0; i < argc; i++) svec[i] = atom_getfloatarg(i, argc, argv); + garray_dofo(x, npoints, 0, argc, svec, 0); + free(svec); +} + +static void garray_normalize(t_garray *x, t_float f) { + double maxv=0; + int yonset, elemsize; + t_array *array = garray_getarray_floatonly(x, &yonset, &elemsize); + TEMPLATE_FLOATY(array,) + if (f <= 0) f = 1; + for (int i=0; i < array->n; i++) { + double v = *((float *)(array->vec + elemsize*i) + yonset); + if ( v > maxv) maxv = v; + if (-v > maxv) maxv = -v; + } + if (maxv > 0) { + double renormer = f/maxv; + for (int i=0; i < array->n; i++) *((float *)(array->vec + elemsize*i) + yonset) *= renormer; + } + garray_redraw(x); +} + +/* list: the first value is an index; subsequent values are put in the "y" slot of the array. */ +static void garray_list(t_garray *x, t_symbol *s, int argc, t_atom *argv) { + int yonset, elemsize; + t_array *array = garray_getarray_floatonly(x, &yonset, &elemsize); + TEMPLATE_FLOATY(array,) + if (argc < 2) return; + else { + int firstindex = atom_getintarg(0,argc--,argv++); + if (firstindex < 0) { /* drop negative x values */ + argc += firstindex; + argv -= firstindex; + firstindex = 0; + } + if (argc + firstindex > array->n) argc = array->n - firstindex; + for (int i=0; i < argc; i++) + *((float *)(array->vec + elemsize * (i + firstindex)) + yonset) = atom_getfloat(argv + i); + } + garray_redraw(x); +} + +static void garray_bounds(t_garray *x, t_floatarg x1, t_floatarg y1, t_floatarg x2, t_floatarg y2) +{vmess(x->canvas, gensym("bounds"), "ffff", x1, y1, x2, y2);} +static void garray_xticks(t_garray *x, t_floatarg point, t_floatarg inc, t_floatarg f) +{vmess(x->canvas, gensym("xticks"), "fff", point, inc, f);} +static void garray_yticks(t_garray *x, t_floatarg point, t_floatarg inc, t_floatarg f) +{vmess(x->canvas, gensym("yticks"), "fff", point, inc, f);} +static void garray_xlabel(t_garray *x, t_symbol *s, int argc, t_atom *argv) {typedmess(x->canvas, s, argc, argv);} +static void garray_ylabel(t_garray *x, t_symbol *s, int argc, t_atom *argv) {typedmess(x->canvas, s, argc, argv);} + +static void garray_rename(t_garray *x, t_symbol *s) { + if (x->listviewing) garray_arrayviewlist_close(x); + pd_unbind(x,x->realname); + x->realname = s; + pd_bind(x,x->realname); + garray_redraw(x); +} + +static void garray_read(t_garray *x, t_symbol *filename) { + FILE *fd; + char *buf, *bufptr; + int yonset, elemsize; + t_array *array = garray_getarray_floatonly(x, &yonset, &elemsize); + TEMPLATE_FLOATY(array,) + int nelem = array->n; + int filedesc = canvas_open2(canvas_getcanvas(x->canvas), filename->name, "", &buf, &bufptr, 0); + if (filedesc<0) {error("%s: can't open", filename->name); free(buf); return;} + if (!(fd = fdopen(filedesc, "r"))) {error("%s: can't open", filename->name); free(buf); return;} + int i; + for (i=0; i < nelem; i++) { + if (!fscanf(fd, "%f", (float *)(array->vec + elemsize*i) + yonset)) { + post("%s: read %d elements into table of size %d", filename->name, i, nelem); + break; + } + } + while (i < nelem) *((float *)(array->vec + elemsize*i) + yonset) = 0, i++; + fclose(fd); + garray_redraw(x); + free(buf); +} + +static void garray_write(t_garray *x, t_symbol *filename) { + int yonset, elemsize; + t_array *array = garray_getarray_floatonly(x, &yonset, &elemsize); + TEMPLATE_FLOATY(array,) + char *buf = canvas_makefilename(canvas_getcanvas(x->canvas),filename->name,0,0); + sys_bashfilename(buf, buf); + FILE *fd = fopen(buf, "w"); + if (!fd) {error("can't create file '%s'", buf); free(buf); return;} + free(buf); + for (int i=0; i < array->n; i++) { + if (fprintf(fd, "%g\n", *(float *)(((array->vec + sizeof(t_word) * i)) + yonset)) < 1) { + post("%s: write error", filename->name); + break; + } + } + fclose(fd); +} + +/* d_soundfile.c uses this! */ +int garray_ambigendian () { + unsigned short s = 1; + unsigned char c = *(char *)(&s); + return c==0; +} + +/* d_soundfile.c uses this! */ +void garray_resize(t_garray *x, t_floatarg f) { + t_array *array = garray_getarray(x); + int n = f<1?1:(int)f; + garray_fittograph(x, n, (int)template_getfloat(template_findbyname(x->scalar->t), gensym("style"), x->scalar->v, 1)); + array_resize_and_redraw(array, n); + if (x->usedindsp) canvas_update_dsp(); +} + +static void garray_print(t_garray *x) { + t_array *array = garray_getarray(x); + post("garray %s: template %s, length %d", x->realname->name, array->templatesym->name, array->n); +} + +static void g_array_setup() { + t_class *c = garray_class = class_new2("array",0,garray_free,sizeof(t_garray),CLASS_GOBJ,""); + class_addlist(garray_class, garray_list); + class_addmethod2(c, garray_const, "const", "F"); + class_addmethod2(c, garray_bounds, "bounds", "ffff"); + class_addmethod2(c, garray_xticks, "xticks", "fff"); + class_addmethod2(c, garray_xlabel, "xlabel", "*"); + class_addmethod2(c, garray_yticks, "yticks", "fff"); + class_addmethod2(c, garray_ylabel, "ylabel", "*"); + class_addmethod2(c, garray_rename, "rename", "s"); + class_addmethod2(c, garray_read, "read", "s"); + class_addmethod2(c, garray_write, "write", "s"); + class_addmethod2(c, garray_resize, "resize", "f"); + class_addmethod2(c, garray_print, "print", ""); + class_addmethod2(c, garray_sinesum, "sinesum", "*"); + class_addmethod2(c, garray_cosinesum, "cosinesum", "*"); + class_addmethod2(c, garray_normalize, "normalize", "F"); + class_addmethod2(c, garray_arraydialog, "arraydialog", "sfff"); + class_addmethod2(c, garray_arrayviewlist_new, "arrayviewlistnew", ""); + class_addmethod2(c, garray_arrayviewlist_fillpage, "arrayviewlistfillpage", "fF"); + class_addmethod2(c, garray_arrayviewlist_close, "arrayviewclose", ""); + class_setsavefn(c, garray_save); + array_class = class_new2("array_really",0,array_free,sizeof(t_array),CLASS_GOBJ,""); +} + +static void graph_graphrect(t_gobj *z, t_canvas *canvas, int *xp1, int *yp1, int *xp2, int *yp2); + +void canvas_add_debug(t_canvas *x, t_gobj *y) { + if (!y->_class->patchable) { + printf("canvas_add %p %p class=%s (non-t_text)\n",x,y,y->_class->name->name); + } else { + t_binbuf *bb = ((t_text *)y)->binbuf; + if (binbuf_getvec(bb)) { + char *buf; int bufn; + binbuf_gettext(bb,&buf,&bufn); + printf("canvas_add %p %p [%.*s]\n",x,y,bufn,buf); + free(buf); + } else { + printf("canvas_add %p %p class=%s (binbuf without b_vec !)\n",x,y,y->_class->name->name); + } + } +} + +void canvas_add(t_canvas *x, t_gobj *y, int index) { + gobj_setcanvas(y,x); + if (index<0) y->dix->index = x->next_o_index++; + else y->dix->index = index; + x->boxes->add(y); + if (x->gop && !x->goprect && pd_checkobject(y)) SET(goprect,1); + //if (class_isdrawcommand(y->_class)) canvas_redrawallfortemplate(template_findbyname(canvas_makebindsym(canvas_getcanvas(x)->name)), 0); +} + +/* delete an object from a canvas and free it */ +void canvas_delete(t_canvas *x, t_gobj *y) { + bool chkdsp = !!zgetfn(y,gensym("dsp")); + //int drawcommand = class_isdrawcommand(y->_class); + /* if we're a drawing command, erase all scalars now, before deleting it; we'll redraw them once it's deleted below. */ + //if (drawcommand) canvas_redrawallfortemplate(template_findbyname(canvas_makebindsym(canvas_getcanvas(x)->name)), 2); + canvas_deletelinesfor(x,(t_text *)y); + x->boxes->remove_by_value(y); + /* BUG: should call gobj_onsubscribe here, to flush the zombie */ + pd_free(y); + if (chkdsp) canvas_update_dsp(); + //if (drawcommand) canvas_redrawallfortemplate(template_findbyname(canvas_makebindsym(canvas_getcanvas(x)->name)), 1); +} + +static void canvas_clear(t_canvas *x) { + t_gobj *y; + int dspstate = 0, suspended = 0; + t_symbol *dspsym = gensym("dsp"); + /* to avoid unnecessary DSP resorting, we suspend DSP only if we find a DSP object. */ + canvas_each(y,x) if (!suspended && pd_checkobject(y) && zgetfn(y,dspsym)) {dspstate = canvas_suspend_dsp(); suspended=1;} + while ((y = x->boxes->first())) x->boxes->remove_by_value(y); + if (suspended) canvas_resume_dsp(dspstate); +} + + +t_canvas *canvas_getcanvas(t_canvas *x) { + while (x->dix->canvas && !x->havewindow && x->gop) x = x->dix->canvas; + return x; +} + +static void scalar_getbasexy(t_scalar *x, float *basex, float *basey); + +static float gobj_getxforsort(t_gobj *g) { + if (g->_class!=scalar_class) return 0; + float x1, y1; + scalar_getbasexy((t_scalar *)g, &x1, &y1); + return x1; +} + +static t_gobj *canvas_merge(t_canvas *x, t_gobj *g1, t_gobj *g2) { +/* + t_gobj *g = 0, *g9 = 0; + float f1 = g1 ? gobj_getxforsort(g1) : 0; + float f2 = g2 ? gobj_getxforsort(g2) : 0; + while (1) { + if (g1 && !(g2 && f1>f2)) { + if (g9) {g9->g_next = g1; g9 = g1;} else g9 = g = g1; + if ((g1 = g1->next())) f1 = gobj_getxforsort(g1); + g9->g_next = 0; + continue; + } + if (g1 || g2) { + if (g9) {g9->g_next = g2; g9 = g2;} else g9 = g = g2; + if ((g2 = g2->next())) f2 = gobj_getxforsort(g2); + g9->g_next = 0; + continue; + } + break; + } + return g; +*/ +} + +/* +static t_gobj *canvas_dosort(t_canvas *x, t_gobj *g, int nitems) { + t_gobj *g2, *g3; + int n1 = nitems/2, n2 = nitems - n1, i; + if (nitems < 2) return g; + int i=n1-1; + for (g2 = g; i--; g2 = g2->next()) {} + g3 = g2->next(); + g2->g_next = 0; + g = canvas_dosort(x, g, n1); + g3 = canvas_dosort(x, g3, n2); + return canvas_merge(x, g, g3); +} + +void canvas_sort(t_canvas *x) { + int nitems = 0, foo = 0; + float lastx = -1e37; + canvas_each(g,x) { + float x1 = gobj_getxforsort(g); + if (x1 < lastx) foo = 1; + lastx = x1; + nitems++; + } + if (foo) x->list = canvas_dosort(x, x->list, nitems); +} +*/ + +static t_inlet *canvas_addinlet(t_canvas *x, t_pd *who, t_symbol *s, t_symbol* h) { + t_inlet *ip = inlet_new(x,who,s,0); inlet_settip(ip,h); + if (gstack_empty()) canvas_resortinlets(x); + gobj_changed(x,0); return ip; +} +static t_outlet *canvas_addoutlet(t_canvas *x, t_pd *who, t_symbol *s) { + t_outlet *op = outlet_new(x,s); + if (gstack_empty()) canvas_resortoutlets(x); + gobj_changed(x,0); return op; +} + +static void canvas_rminlet(t_canvas *x, t_inlet *ip) { + if (x->dix->canvas) canvas_deletelinesforio(x->dix->canvas,x,ip,0); + inlet_free(ip); /*gobj_changed(x,0);*/ +} +static void canvas_rmoutlet(t_canvas *x, t_outlet *op) { + if (x->dix->canvas) canvas_deletelinesforio(x->dix->canvas,x,0,op); + outlet_free(op); /*gobj_changed(x,0);*/ +} + +extern "C" t_inlet *vinlet_getit(t_pd *x); +extern "C" t_outlet *voutlet_getit(t_pd *x); + +typedef int (*t_order)(const void *, const void *); +int gobj_order_x (t_object **a, t_object **b) {return (*a)->x - (*b)->x;} + +//{std::ostringstream s; s<<"disorder:"; for (int i=0; ix; post("%s",s.str().data());} + +void obj_moveinletfirst(t_object *x, t_inlet *i); +void obj_moveoutletfirst(t_object *x, t_outlet *o); + +static void canvas_resortinlets(t_canvas *x) { + int n=0; canvas_each(y,x) if (y->_class==vinlet_class) n++; + t_object **vec = new t_object *[n], **vp = vec; + canvas_each(y,x) if (y->_class==vinlet_class) *vp++ = (t_object *)y; + qsort(vec,n,sizeof(t_object *),(t_order)gobj_order_x); + for (int i=n; i--;) obj_moveinletfirst(x,vinlet_getit(vec[i])); + delete[] vec; +} +static void canvas_resortoutlets(t_canvas *x) { + int n=0; canvas_each(y,x) if (y->_class==voutlet_class) n++; + t_object **vec = new t_object *[n], **vp = vec; + canvas_each(y,x) if (y->_class==voutlet_class) *vp++ = (t_object *)y; + qsort(vec,n,sizeof(t_object *),(t_order)gobj_order_x); + for (int i=n; i--;) obj_moveoutletfirst(x,voutlet_getit(vec[i])); + delete[] vec; +} + +static void graph_bounds(t_canvas *x, t_floatarg x1, t_floatarg y1, t_floatarg x2, t_floatarg y2) { + x->x1 = x1; x->y1 = y1; + x->x2 = x2; x->y2 = y2; + if (x->x2 == x->x1 || x->y2 == x->y1) { + error("empty bounds rectangle"); + x->x1 = x->y1 = 0; + x->x2 = x->y2 = 1; + } + gobj_changed(x,0); +} + +static void graph_xticks(t_canvas *x, t_floatarg point, t_floatarg inc, t_floatarg f) +{t_tick *t = &x->xtick; t->point = point; t->inc = inc; t->lperb = (int)f; gobj_changed(x,"xticks");} +static void graph_yticks(t_canvas *x, t_floatarg point, t_floatarg inc, t_floatarg f) +{t_tick *t = &x->ytick; t->point = point; t->inc = inc; t->lperb = (int)f; gobj_changed(x,"yticks");} + +static void graph_xlabel(t_canvas *x, t_symbol *s, int argc, t_atom *argv) { + if (argc < 1) {error("graph_xlabel: no y value given"); return;} + x->xlabely = atom_getfloatarg(0,argc--,argv++); + x->xlabel = (t_symbol **)realloc(x->xlabel,argc*sizeof(void*)); + x->nxlabels = argc; + for (int i=0; i < argc; i++) x->xlabel[i] = atom_gensym(&argv[i]); + gobj_changed(x,"xlabel"); +} +static void graph_ylabel(t_canvas *x, t_symbol *s, int argc, t_atom *argv) { + if (argc < 1) {error("graph_ylabel: no x value given"); return;} + x->ylabelx = atom_getfloatarg(0,argc--,argv++); + x->ylabel = (t_symbol **)realloc(x->ylabel,argc*sizeof(void*)); + x->nylabels = argc; + for (int i=0; i < argc; i++) x->ylabel[i] = atom_gensym(&argv[i]); + gobj_changed(x,"ylabel"); +} + +/* if we appear as a text box on parent, our range in our coordinates (x1, etc.) + specifies the coordinate range of a one-pixel square at top left of the window. + if we're a graph when shown on parent, but own our own window right now, our range + in our coordinates (x1, etc.) is spread over the visible window size, given by screenx1, etc. + otherwise, we appear in a graph within a parent canvas, so get our screen rectangle on parent and transform. */ +static float canvas_pixelstox(t_canvas *x, float xpix) { + int x1, y1, x2, y2; float width = x->x2-x->x1; + if (!x->gop) return x->x1 + width * xpix; + if (x->havewindow) return x->x1 + width * xpix / (x->screenx2-x->screenx1); + graph_graphrect(x, x->dix->canvas, &x1, &y1, &x2, &y2); + return x->x1 + width * (xpix-x1) / (x2-x1); +} +static float canvas_pixelstoy(t_canvas *x, float ypix) { + int x1, y1, x2, y2; float height = x->y2-x->y1; + if (!x->gop) return x->y1 + height * ypix; + if (x->havewindow) return x->y1 + height * ypix / (x->screeny2-x->screeny1); + graph_graphrect(x, x->dix->canvas, &x1, &y1, &x2, &y2); + return x->y1 + height * (ypix-y1) / (y2-y1); +} + +/* convert an x coordinate value to an x pixel location in window */ +static int canvas_xtopixels(t_canvas *x, float xval) { + int x1, y1, x2, y2; float width = x->x2-x->x1; + if (!x->gop) return int((xval-x->x1)/width); + if (x->havewindow) return int((x->screenx2-x->screenx1) * (xval-x->x1) / width); + graph_graphrect(x, x->dix->canvas, &x1, &y1, &x2, &y2); + return int(x1 + (x2-x1) * (xval-x->x1) / width); +} +static int canvas_ytopixels(t_canvas *x, float yval) { + int x1, y1, x2, y2; float height = x->y2-x->y1; + if (!x->gop) return int((yval-x->y1)/height); + if (x->havewindow) return int((x->screeny2-x->screeny1) * (yval-x->y1) / height); + graph_graphrect(x, x->dix->canvas, &x1, &y1, &x2, &y2); + return int(y1 + (y2-y1) * (yval-x->y1) / height); +} + +/* --------------------------- widget behavior ------------------- */ +/* don't remove this code yet: has to be rewritten in tcl */ +#if 1 +#define FONT "pourier" +static void graph_vis(t_gobj *gr, int vis) { + t_canvas *x = (t_canvas *)gr; + t_canvas *c = canvas_getcanvas(x->dix->canvas); + char tag[50]; + int x1=69, y1=69, x2=69, y2=69; + sprintf(tag, "graph%lx", (t_int)x); + if (vis) { + sys_mgui(x,"ninlets=","i",0/*obj_ninlets(x)*/); + sys_mgui(x,"noutlets=","i",0/*obj_noutlets(x)*/); + } + /* if we look like a graph but have been moved to a toplevel, just show the bounding rectangle */ + if (x->havewindow) { + /*if (vis) sys_vgui(".x%lx.c create polygon %d %d %d %d %d %d %d %d %d %d -tags %s -fill #c0c0c0\n", + (long)c, x1, y1, x1, y2, x2, y2, x2, y1, x1, y1, tag);*/ + return; + } + /* draw a rectangle around the graph */ + sys_vgui(".x%lx.c create line %d %d %d %d %d %d %d %d %d %d -tags %s\n", + (long)c, x1, y1, x1, y2, x2, y2, x2, y1, x1, y1, tag); + /* if there's just one "garray" in the graph, write its name along the top */ + int i = min(y1,y2)-1; + t_symbol *arrayname; + canvas_each(g,x) if (g->g_pd == garray_class && !garray_getname((t_garray *)g, &arrayname)) { + // i -= sys_fontheight(glist_getfont(x)); + sys_vgui(".x%lx.c create text %d %d -text {%s} -anchor nw\ + -font -*-courier-bold--normal--%d-* -tags %s\n", + (long)canvas_getcanvas(x), x1, i, arrayname->name, + 42/*sys_hostfontsize(canvas_getfont(x))*/, tag); + } + + /* draw ticks on horizontal borders. If lperb field is zero, this is disabled. */ + #define DRAWTICK(x1,y1,x2,y2) sys_vgui(".x%lx.c create line %d %d %d %d -tags %s\n", \ + (long)c, int(x1),int(y1),int(x2),int(y2),tag) + float f; + if (x->xtick.lperb) { + float upix, lpix; + if (y2xtick.point; f<0.99*x->x2+0.01*x->x1; i++, f+=x->xtick.inc) { + int tickpix = i%x->xtick.lperb?2:4, x0 = canvas_xtopixels(x,f); + DRAWTICK(x0,upix,x0,upix-tickpix); + DRAWTICK(x0,lpix,x0,lpix+tickpix); + } + for (i=1,f=x->xtick.point-x->xtick.inc; f>0.99*x->x1+0.01*x->x2; i++,f-=x->xtick.inc) { + int tickpix = i%x->xtick.lperb?2:4, x0 = canvas_xtopixels(x,f); + DRAWTICK(x0,upix,x0,upix-tickpix); + DRAWTICK(x0,lpix,x0,lpix+tickpix); + } + } + /* draw ticks in vertical borders*/ + if (x->ytick.lperb) { + float ubound, lbound; + if (x->y2y1) {ubound = x->y1; lbound = x->y2;} + else {ubound = x->y2; lbound = x->y1;} + for (i=0,f=x->ytick.point; f<0.99*ubound+0.01*lbound; i++, f += x->ytick.inc) { + int tickpix = i%x->ytick.lperb?2:4, y0 = canvas_ytopixels(x,f); + DRAWTICK(x1,y0,x1+tickpix,y0); + DRAWTICK(x2,y0,x2-tickpix,y0); + } + for (i=1,f=x->ytick.point-x->ytick.inc; f>0.99*lbound+0.01*ubound; i++,f-=x->ytick.inc) { + int tickpix = i%x->ytick.lperb?2:4, y0 = canvas_ytopixels(x,f); + DRAWTICK(x1,y0,x1+tickpix,y0); + DRAWTICK(x2,y0,x2-tickpix,y0); + } + } + /* draw labels */ + #define DRAWLABEL(x1,y1) sys_vgui(".x%lx.c create text %d %d -text {%s} -font "FONT" -tags %s\n", (long)c, \ + int(canvas_xtopixels(x,x1)),int(canvas_ytopixels(x,y1)),s,42,tag); + for (int i=0; i < x->nxlabels; i++) {char *s = x->xlabel[i]->name; DRAWLABEL(atof(s),x->xlabely);} + for (int i=0; i < x->nylabels; i++) {char *s = x->ylabel[i]->name; DRAWLABEL(x->ylabelx,atof(s));} +} +#endif + +static int text_xpix(t_text *x, t_canvas *canvas) { + float width = canvas->x2-canvas->x1; + if (canvas->havewindow || !canvas->gop) return x->x; + if (canvas->goprect) return canvas->x+x->x-canvas->xmargin; + return canvas_xtopixels(canvas, canvas->x1 + width * x->x / (canvas->screenx2-canvas->screenx1)); +} +static int text_ypix(t_text *x, t_canvas *canvas) { + float height = canvas->y2-canvas->y1; + if (canvas->havewindow || !canvas->gop) return x->y; + if (canvas->goprect) return canvas->y+x->y-canvas->ymargin; + return canvas_ytopixels(canvas, canvas->y1 + height* x->y / (canvas->screeny2-canvas->screeny1)); +} +static void graph_graphrect(t_gobj *z, t_canvas *canvas, int *xp1, int *yp1, int *xp2, int *yp2) { + t_canvas *x = (t_canvas *)z; + *xp1 = text_xpix(x,canvas); *xp2 = *xp1+x->pixwidth; + *yp1 = text_ypix(x,canvas); *yp2 = *yp1+x->pixheight; +} + +#if 1 +static float graph_lastxpix, graph_lastypix; +static void graph_motion(void *z, t_floatarg dx, t_floatarg dy) { + t_canvas *x = (t_canvas *)z; + float newxpix = graph_lastxpix + dx, newypix = graph_lastypix + dy; + t_garray *a = (t_garray *)x->boxes->first(); + int oldx = int(0.5 + canvas_pixelstox(x, graph_lastxpix)); + int newx = int(0.5 + canvas_pixelstox(x, newxpix)); + float oldy = canvas_pixelstoy(x, graph_lastypix); + float newy = canvas_pixelstoy(x, newypix); + graph_lastxpix = newxpix; + graph_lastypix = newypix; + // verify that the array is OK + if (!a || a->_class != garray_class) return; + int nelem; + t_float *vec; + if (!garray_getfloatarray(a, &nelem, &vec)) return; + if (oldx < 0) oldx = 0; else if (oldx >= nelem) oldx = nelem - 1; + if (newx < 0) newx = 0; else if (newx >= nelem) newx = nelem - 1; + if (oldx < newx - 1) {for (int i=oldx+1; i<=newx; i++) vec[i] = newy + (oldy-newy) * float(newx-i)/float(newx - oldx);} + else if (oldx > newx + 1) {for (int i=oldx-1; i>=newx; i--) vec[i] = newy + (oldy-newy) * float(newx-i)/float(newx - oldx);} + else vec[newx] = newy; + garray_redraw(a); +} +#endif + +/* functions to read and write canvases to files: canvas_savetofile() writes a root canvas to a "pd" file. + (Reading "pd" files is done simply by passing the contents to the pd message interpreter.) + Alternatively, the glist_read() and glist_write() functions read and write "data" from and to files + (reading reads into an existing canvas), using a file format as in the dialog window for data. */ +static t_class *declare_class; +void canvas_savedeclarationsto(t_canvas *x, t_binbuf *b); + +/* the following functions read "scalars" from a file into a canvas. */ +static int canvas_scanbinbuf(int natoms, t_atom *vec, int *p_indexout, int *p_next) { + int i; + int indexwas = *p_next; + *p_indexout = indexwas; + if (indexwas >= natoms) return 0; + for (i = indexwas; i < natoms && vec[i].a_type != A_SEMI; i++) {} + if (i >= natoms) *p_next = i; else *p_next = i+1; + return i-indexwas; +} +static int canvas_readscalar(t_canvas *x, int natoms, t_atom *vec, int *p_nextmsg, int selectit); +static void canvas_readerror(int natoms, t_atom *vec, int message, int nline, char *s) { + error(s); + startpost("line was:"); + postatom(nline, vec + message); + endpost(); +} + +/* fill in the contents of the scalar into the vector w. */ +static void canvas_readatoms(t_canvas *x, int natoms, t_atom *vec, +int *p_nextmsg, t_symbol *templatesym, t_word *w, int argc, t_atom *argv) { + t_template *t = template_findbyname(templatesym); + if (!t) { + error("%s: no such template", templatesym->name); + *p_nextmsg = natoms; + return; + } + word_restore(w, t, argc, argv); + int n = t->n; + for (int i=0; ivec[i].type == DT_ARRAY) { + t_array *a = w[i].w_array; + int elemsize = a->elemsize, nitems = 0; + t_symbol *arraytemplatesym = t->vec[i].arraytemplate; + t_template *arraytemplate = template_findbyname(arraytemplatesym); + if (!arraytemplate) error("%s: no such template", arraytemplatesym->name); + else while (1) { + int message; + t_word *element; + int nline = canvas_scanbinbuf(natoms, vec, &message, p_nextmsg); + /* empty line terminates array */ + if (!nline) break; + array_resize(a, nitems + 1); + element = (t_word *)&a->vec[nitems*elemsize]; + canvas_readatoms(x, natoms, vec, p_nextmsg, arraytemplatesym, element, nline, vec + message); + nitems++; + } + } else if (t->vec[i].type == DT_CANVAS) { + while (1) { + if (!canvas_readscalar(w->w_canvas, natoms, vec, p_nextmsg, 0)) break; + } + } + } +} + +static int canvas_readscalar(t_canvas *x, int natoms, t_atom *vec, int *p_nextmsg, int selectit) { + int nextmsg = *p_nextmsg; + //int wasvis = canvas_isvisible(x); + if (nextmsg >= natoms || vec[nextmsg].a_type != A_SYMBOL) { + if (nextmsg < natoms) post("stopping early: type %d", vec[nextmsg].a_type); + *p_nextmsg = natoms; return 0; + } + t_symbol *ts = canvas_makebindsym(vec[nextmsg].a_symbol); + *p_nextmsg = nextmsg + 1; + t_template *t = template_findbyname(ts); + if (!t) {error("%s: no such template", ts->name); *p_nextmsg = natoms; return 0;} + t_scalar *sc = scalar_new(x, ts); + if (!sc) {error("couldn't create scalar \"%s\"", ts->name); *p_nextmsg = natoms; return 0;} + //if (wasvis) canvas_getcanvas(x)->mapped = 0; + canvas_add(x,sc); + int message; + int nline = canvas_scanbinbuf(natoms, vec, &message, p_nextmsg); + canvas_readatoms(x, natoms, vec, p_nextmsg, ts, sc->v, nline, vec + message); + //if (wasvis) canvas_getcanvas(x)->mapped = 1; + gobj_changed(sc,0);//is this necessary? + return 1; +} + +static void canvas_readfrombinbuf(t_canvas *x, t_binbuf *b, char *filename, int selectem) { + int message, nextmsg = 0; + int natoms = binbuf_getnatom(b); + t_atom *vec = binbuf_getvec(b); + /* check for file type */ + int nline = canvas_scanbinbuf(natoms, vec, &message, &nextmsg); + if (nline!=1 && vec[message].a_type != A_SYMBOL && strcmp(vec[message].a_symbol->name, "data")) { + error("%s: file apparently of wrong type", filename); + binbuf_free(b); + return; + } + /* read in templates and check for consistency */ + while (1) { + t_template *newtemplate, *existtemplate; + t_atom *templateargs = (t_atom *)getbytes(0); + int ntemplateargs = 0; + nline = canvas_scanbinbuf(natoms, vec, &message, &nextmsg); + if (nline < 2) break; + else if (nline > 2) canvas_readerror(natoms, vec, message, nline, "extra items ignored"); + else if (vec[message].a_type != A_SYMBOL || strcmp(vec[message].a_symbol->name, "template") || + vec[message+1].a_type != A_SYMBOL) { + canvas_readerror(natoms, vec, message, nline, "bad template header"); + continue; + } + t_symbol *templatesym = canvas_makebindsym(vec[message + 1].a_symbol); + while (1) { + nline = canvas_scanbinbuf(natoms, vec, &message, &nextmsg); + if (nline != 2 && nline != 3) break; + int newnargs = ntemplateargs + nline; + templateargs = (t_atom *)realloc(templateargs, sizeof(*templateargs) * newnargs); + templateargs[ntemplateargs] = vec[message]; + templateargs[ntemplateargs + 1] = vec[message + 1]; + if (nline == 3) templateargs[ntemplateargs + 2] = vec[message + 2]; + ntemplateargs = newnargs; + } + newtemplate = template_new(templatesym, ntemplateargs, templateargs); + free(templateargs); + if (!(existtemplate = template_findbyname(templatesym))) { + error("%s: template not found in current patch", templatesym->name); + template_free(newtemplate); + return; + } + if (!template_match(existtemplate, newtemplate)) { + error("%s: template doesn't match current one", templatesym->name); + template_free(newtemplate); + return; + } + template_free(newtemplate); + } + while (nextmsg < natoms) canvas_readscalar(x, natoms, vec, &nextmsg, selectem); +} + +static void canvas_doread(t_canvas *x, t_symbol *filename, t_symbol *format, int clearme) { + t_binbuf *b = binbuf_new(); + t_canvas *canvas = canvas_getcanvas(x); + int wasvis = canvas_isvisible(canvas); + int cr = strcmp(format->name, "cr")==0; + if (!cr && *format->name) error("unknown flag: %s", format->name); + /* flag 2 means eval continuously. this is required to autodetect the syntax */ + if (binbuf_read_via_path(b, filename->name, canvas_getdir(canvas)->name, cr|2)) { + error("read failed"); + binbuf_free(b); + return; + } + if (wasvis) canvas_vis(canvas, 0); + if (clearme) canvas_clear(x); + /* canvas_readfrombinbuf(x, b, filename->name, 0); */ /* what's this for? */ + if (wasvis) canvas_vis(canvas, 1); + binbuf_free(b); +} + +static void canvas_read( t_canvas *x, t_symbol *filename, t_symbol *format) {canvas_doread(x,filename,format,1);} +static void canvas_mergefile(t_canvas *x, t_symbol *filename, t_symbol *format) {canvas_doread(x,filename,format,0);} + +/* read text from a "properties" window, in answer to scalar_properties(). + We try to restore the object; if successful + we delete the scalar and put the new thing in its place on the list. */ +void canvas_dataproperties(t_canvas *x, t_scalar *sc, t_binbuf *b) { +// t_gobj *oldone = 0; +// t_gobj *newone = 0; + x->boxes->remove_by_value(sc); +// if (!newone) {error("couldn't update properties (perhaps a format problem?)"); return;} +// if (!oldone) {bug("data_properties: couldn't find old element"); return;} + canvas_readfrombinbuf(x, b, "properties dialog", 0); +} + +static void canvas_doaddtemplate(t_symbol *templatesym, int *p_ntemplates, t_symbol ***p_templatevec) { + int n = *p_ntemplates; + t_symbol **templatevec = *p_templatevec; + for (int i=0; i < n; i++) if (templatevec[i] == templatesym) return; + templatevec = (t_symbol **)realloc(templatevec, (n+1)*sizeof(*templatevec)); + templatevec[n] = templatesym; + *p_templatevec = templatevec; + *p_ntemplates = n+1; +} + +static void canvas_writelist(t_gobj *y, t_binbuf *b); + +static void canvas_writescalar(t_symbol *templatesym, t_word *w, t_binbuf *b, int amarrayelement) { + t_template *t = template_findbyname(templatesym); + t_atom *a = (t_atom *)getbytes(0); + int n = t->n, natom = 0; + if (!amarrayelement) { + t_atom templatename; + SETSYMBOL(&templatename, gensym(templatesym->name + 3)); + binbuf_add(b, 1, &templatename); + } + if (!t) bug("canvas_writescalar"); + /* write the atoms (floats and symbols) */ + for (int i=0; ivec[i].type; + if (ty==DT_FLOAT || ty==DT_SYMBOL) { + a = (t_atom *)realloc(a, (natom+1)*sizeof(*a)); + if (t->vec[i].type == DT_FLOAT) SETFLOAT( a + natom, w[i].w_float); + else SETSYMBOL(a + natom, w[i].w_symbol); + natom++; + } + } + /* array elements have to have at least something */ + if (natom == 0 && amarrayelement) + SETSYMBOL(a + natom, &s_bang), natom++; + binbuf_add(b, natom, a); + binbuf_addsemi(b); + free(a); + for (int i=0; ivec[i].type == DT_ARRAY) { + t_array *a = w[i].w_array; + int elemsize = a->elemsize, nitems = a->n; + t_symbol *arraytemplatesym = t->vec[i].arraytemplate; + for (int j = 0; j < nitems; j++) + canvas_writescalar(arraytemplatesym, (t_word *)&a->vec[elemsize*j], b, 1); + binbuf_addsemi(b); + } else if (t->vec[i].type == DT_CANVAS) { + canvas_writelist(w->w_canvas->boxes->first(), b); + binbuf_addsemi(b); + } + } +} + +static void canvas_writelist(t_gobj *y, t_binbuf *b) { + for (; y; y = y->next()) if (y->_class==scalar_class) { + t_scalar *z = (t_scalar *)y; + canvas_writescalar(z->t, z->v, b, 0); + } +} + +static void canvas_addtemplatesforlist(t_gobj *y, int *p_ntemplates, t_symbol ***p_templatevec); + +static void canvas_addtemplatesforscalar(t_symbol *templatesym, t_word *w, int *p_ntemplates, t_symbol ***p_templatevec) { + t_template *t = template_findbyname(templatesym); + canvas_doaddtemplate(templatesym, p_ntemplates, p_templatevec); + if (!t) {bug("canvas_addtemplatesforscalar"); return;} + t_dataslot *ds = t->vec; + for (int i=t->n; i--; ds++, w++) { + if (ds->type == DT_ARRAY) { + t_array *a = w->w_array; + int elemsize = a->elemsize, nitems = a->n; + t_symbol *arraytemplatesym = ds->arraytemplate; + canvas_doaddtemplate(arraytemplatesym, p_ntemplates, p_templatevec); + for (int j=0; jvec[elemsize*j], p_ntemplates, p_templatevec); + } else if (ds->type == DT_CANVAS) + canvas_addtemplatesforlist(w->w_canvas->boxes->first(), p_ntemplates, p_templatevec); + } +} + +static void canvas_addtemplatesforlist(t_gobj *y, int *p_ntemplates, t_symbol ***p_templatevec) { + for (; y; y = y->next()) if (y->_class == scalar_class) { + t_scalar *z = (t_scalar *)y; + canvas_addtemplatesforscalar(z->t, z->v, p_ntemplates, p_templatevec); + } +} + +static t_binbuf *canvas_writetobinbuf(t_canvas *x) { + t_symbol **templatevec = (t_symbol **)getbytes(0); + int ntemplates = 0; + t_binbuf *b = binbuf_new(); + canvas_each(y,x) if (y->_class==scalar_class) { + t_scalar *s = (t_scalar *)y; + canvas_addtemplatesforscalar(s->t, s->v, &ntemplates, &templatevec); + } + binbuf_addv(b,"t;","data"); + for (int i=0; in; + /* drop "pd-" prefix from template symbol to print it: */ + binbuf_addv(b,"tt;","template",templatevec[i]->name + 3); + for (int j=0; jvec[j].type) { + case DT_FLOAT: type = &s_float; break; + case DT_SYMBOL: type = &s_symbol; break; + case DT_ARRAY: type = gensym("array"); break; + case DT_CANVAS: type = &s_list; break; + default: type = &s_float; bug("canvas_write"); + } + if (t->vec[j].type == DT_ARRAY) + binbuf_addv(b,"sst;", type, t->vec[j].name, t->vec[j].arraytemplate->name + 3); + else binbuf_addv(b,"ss;", type, t->vec[j].name); + } + binbuf_addsemi(b); + } + binbuf_addsemi(b); + /* now write out the objects themselves */ + canvas_each(y,x) if (y->_class==scalar_class) { + t_scalar *z = (t_scalar *)y; + canvas_writescalar(z->t, z->v, b, 0); + } + return b; +} + +static void canvas_write(t_canvas *x, t_symbol *filename, t_symbol *format) { + t_canvas *canvas = canvas_getcanvas(x); + char *buf = canvas_makefilename(canvas,filename->name,0,0); + int cr = strcmp(format->name, "cr")==0; + if (!cr && *format->name) error("canvas_write: unknown flag: %s", format->name); + t_binbuf *b = canvas_writetobinbuf(x); + if (b) { + if (binbuf_write(b, buf, "", cr)) error("%s: write failed", filename->name); + binbuf_free(b); + } + free(buf); +} + +/* ------ functions to save and restore canvases (patches) recursively. ----*/ + +/* save to a binbuf, called recursively; cf. canvas_savetofile() which saves the document, and is only called on root canvases. */ +void canvas_savecontainerto(t_canvas *x, t_binbuf *b) { + /* have to go to original binbuf to find out how we were named. */ + t_binbuf *bz = binbuf_new(); + t_symbol *patchsym = &s_; + if (x->binbuf) { + binbuf_addbinbuf(bz, x->binbuf); + patchsym = atom_getsymbolarg(1, binbuf_getnatom(bz), binbuf_getvec(bz)); + binbuf_free(bz); + } + int x1=x->screenx1, xs=x->screenx2-x1; + int y1=x->screeny1, ys=x->screeny2-y1; + binbuf_addv(b,"ttiiii","#N","canvas",x1,y1,xs,ys); + if (x->dix->canvas && !x->env) { /* subpatch */ + binbuf_addv(b, "si;", (patchsym != &s_ ? patchsym: gensym("(subpatch)")), x->havewindow); + } else { /* root or abstraction */ + binbuf_addv(b, "i;", (int)x->font); + canvas_savedeclarationsto(x, b); + } +} + +static void canvas_savecoordsto(t_canvas *x, t_binbuf *b) { + /* if everything is the default, skip saving this line */ + if (!x->gop && x->x1==0 && x->y1==0 && x->x2==1 && x->y2==1 && x->pixwidth==0 && x->pixheight==0) return; + /* if we have a graph-on-parent rectangle, we're new style. The format is arranged so + that old versions of Pd can at least do something with it. + otherwise write in 0.38-compatible form. */ + binbuf_addv(b,"ttffffffi","#X","coords", x->x1,x->y1,x->x2,x->y2, (float)x->pixwidth,(float)x->pixheight, x->gop?x->hidetext?2:1:0); + if (x->goprect) binbuf_addv(b, "ff", (float)x->xmargin, (float)x->ymargin); + binbuf_addv(b,";"); +} + +/* get the index of a gobj in a canvas. If y is zero, return the total number of objects. */ +int canvas_oldindex(t_canvas *x, t_gobj *y) { + int i=0; + canvas_each(y2,x) {if (y2==y) break; else i++;} + return i; +} + +static void canvas_saveto(t_canvas *x, t_binbuf *b) { + canvas_savecontainerto(x,b); + canvas_each(y,x) gobj_save(y, b); + canvas_wires_each(oc,t,x) { + int from = canvas_oldindex(x,t.from); + int to = canvas_oldindex(x,t.to); + binbuf_addv(b, "ttiiii;","#X","connect", from, t.outlet, to, t.inlet); + appendix_save(oc,b); + } + canvas_savecoordsto(x,b); +} + +/* call this recursively to collect all the template names for a canvas or for the selection. */ +static void canvas_collecttemplatesfor(t_canvas *x, int *ntemplatesp, t_symbol ***templatevecp) { + canvas_each(y,x) { + if (y->_class==scalar_class) { + t_scalar *z = (t_scalar *)y; + canvas_addtemplatesforscalar(z->t, z->v, ntemplatesp, templatevecp); + } else if (y->_class==canvas_class) { + canvas_collecttemplatesfor((t_canvas *)y, ntemplatesp, templatevecp); + } + } +} + +/* save the templates needed by a canvas to a binbuf. */ +static void canvas_savetemplatesto(t_canvas *x, t_binbuf *b) { + t_symbol **templatevec = (t_symbol **)getbytes(0); + int ntemplates = 0; + canvas_collecttemplatesfor(x, &ntemplates, &templatevec); + for (int i=0; i < ntemplates; i++) { + t_template *t = template_findbyname(templatevec[i]); + if (!t) { + bug("canvas_savetemplatesto"); + continue; + } + /* drop "pd-" prefix from template symbol to print */ + binbuf_addv(b,"ttt","#N","struct",templatevec[i]->name+3); + for (int j=0; jn; j++) { + t_symbol *type; + switch (t->vec[j].type) { + case DT_FLOAT: type = &s_float; break; + case DT_SYMBOL: type = &s_symbol; break; + case DT_ARRAY: type = gensym("array"); break; + case DT_CANVAS: type = &s_list; break; + default: type = &s_float; bug("canvas_write"); + } + binbuf_addv(b,"ss",type,t->vec[j].name); + if (t->vec[j].type == DT_ARRAY) binbuf_addv(b, "t", t->vec[j].arraytemplate->name + 3); + } + binbuf_addsemi(b); + } +} + +/* save a "root" canvas to a file; cf. canvas_saveto() which saves the body (and which is called recursively.) */ +static void canvas_savetofile(t_canvas *x, t_symbol *filename, t_symbol *dir) { + t_binbuf *b = binbuf_new(); + int dsp_status = canvas_suspend_dsp(); + canvas_savetemplatesto(x, b); + canvas_saveto(x, b); + if (!binbuf_write(b, filename->name, dir->name, 0)) { + /* if not an abstraction, reset title bar and directory */ + if (!x->dix->canvas) canvas_rename(x, filename, dir); + post("saved to: %s/%s", dir->name, filename->name); + canvas_reload(filename,dir,x); + } + binbuf_free(b); + canvas_resume_dsp(dsp_status); +} + +/////////////////////////////////////////////////////////////////////////// +// from g_io.c + +/* graphical inlets and outlets, both for control and signals. */ +/* iohannes added multiple samplerates support in vinlet/voutlet */ + +extern "C" void signal_setborrowed(t_signal *sig, t_signal *sig2); +extern "C" void signal_makereusable(t_signal *sig); +extern "C" void inlet_sethelp(t_inlet* i,t_symbol* s); + +/* ------------------------- vinlet -------------------------- */ +t_class *vinlet_class; + +struct t_vinlet : t_object { + t_canvas *canvas; + t_inlet *inlet; + int bufsize; + t_float *buf; /* signal buffer; zero if not a signal */ + t_float *endbuf; + t_float *fill; + t_float *read; + int hop; + /* if not reblocking, the next slot communicates the parent's inlet signal from the prolog to the DSP routine: */ + t_signal *directsignal; + t_resample updown; /* IOhannes */ +}; + +static void *vinlet_new(t_symbol *s) { + t_vinlet *x = (t_vinlet *)pd_new(vinlet_class); + x->canvas = canvas_getcurrent(); + x->inlet = canvas_addinlet(x->canvas,x,0,s); + x->bufsize = 0; + x->buf = 0; + outlet_new(x, 0); + return x; +} + +static void vinlet_bang(t_vinlet *x) {outlet_bang(x->outlet);} +static void vinlet_pointer(t_vinlet *x, t_gpointer *gp) {outlet_pointer(x->outlet, gp);} +static void vinlet_float(t_vinlet *x, t_float f) {outlet_float(x->outlet, f);} +static void vinlet_symbol(t_vinlet *x, t_symbol *s) {outlet_symbol(x->outlet, s);} +static void vinlet_list(t_vinlet *x, t_symbol *s, int argc, t_atom *argv) {outlet_list(x->outlet, s, argc, argv);} +static void vinlet_anything(t_vinlet *x, t_symbol *s, int argc, t_atom *argv) {outlet_anything(x->outlet, s, argc, argv);} + +static void vinlet_free(t_vinlet *x) { + canvas_rminlet(x->canvas, x->inlet); + resample_free(&x->updown); +} + +t_inlet *vinlet_getit(t_pd *x) { + if (pd_class(x) != vinlet_class) bug("vinlet_getit"); + return ((t_vinlet *)x)->inlet; +} + +/* ------------------------- signal inlet -------------------------- */ +int vinlet_issignal(t_vinlet *x) {return x->buf!=0;} + +t_int *vinlet_perform(t_int *w) { + t_vinlet *x = (t_vinlet *)w[1]; + t_float *out = (t_float *)w[2]; + int n = int(w[3]); + t_float *in = x->read; + while (n--) *out++ = *in++; + if (in == x->endbuf) in = x->buf; + x->read = in; + return w+4; +} + +/* tb: vectorized */ +t_int *vinlet_perf8(t_int *w) { + t_vinlet *x = (t_vinlet *)w[1]; + t_float *out = (t_float *)w[2]; + int n = int(w[3]); + t_float *in = x->read; + for (; n; n -= 8, in += 8, out += 8) { + out[0] = in[0]; out[1] = in[1]; out[2] = in[2]; out[3] = in[3]; + out[4] = in[4]; out[5] = in[5]; out[6] = in[6]; out[7] = in[7]; + } + if (in == x->endbuf) in = x->buf; + x->read = in; + return w+4; +} + +/* T.Grill: SIMD version */ +t_int *vinlet_perfsimd(t_int *w) { + t_vinlet *x = (t_vinlet *)(w[1]); + t_float *in = x->read; + copyvec_simd((t_float *)w[2],in,w[3]); + if (in == x->endbuf) in = x->buf; + x->read = in; + return w+4; +} + +static void vinlet_dsp(t_vinlet *x, t_signal **sp) { + if (!x->buf) return; /* no buffer means we're not a signal inlet */ + t_signal *outsig = sp[0]; + if (x->directsignal) signal_setborrowed(sp[0], x->directsignal); + else { + const int vecsize = outsig->vecsize; + /* if the outsig->v is aligned the x->read will also be... */ + if(vecsize&7) dsp_add(vinlet_perform, 3, x, outsig->v,vecsize); + else if(SIMD_CHECK1(outsig->n,outsig->v)) + dsp_add(vinlet_perfsimd, 3, x, outsig->v,vecsize); + else dsp_add(vinlet_perf8, 3, x, outsig->v,vecsize); + x->read = x->buf; + } +} + +/* prolog code: loads buffer from parent patch */ +t_int *vinlet_doprolog(t_int *w) { + t_vinlet *x = (t_vinlet *)w[1]; + t_float *in = (t_float *)w[2]; + int n = int(w[3]); + t_float *out = x->fill; + if (out == x->endbuf) { + t_float *f1 = x->buf, *f2 = x->buf + x->hop; + int nshift = x->bufsize - x->hop; + out -= x->hop; + while (nshift--) *f1++ = *f2++; + } + while (n--) *out++ = *in++; + x->fill = out; + return w+4; +} + +extern "C" int inlet_getsignalindex(t_inlet *x); + +/* set up prolog DSP code */ +extern "C" void vinlet_dspprolog(t_vinlet *x, t_signal **parentsigs, int myvecsize, int calcsize, int phase, int period, +int frequency, int downsample, int upsample, int reblock, int switched) { + t_signal *insig; + x->updown.downsample = downsample; + x->updown.upsample = upsample; + /* if the "reblock" flag is set, arrange to copy data in from the parent. */ + if (reblock) { + int parentvecsize, bufsize, oldbufsize, prologphase; + int re_parentvecsize; /* resampled parentvectorsize: IOhannes */ + /* this should never happen: */ + if (!x->buf) return; + /* the prolog code counts from 0 to period-1; the + phase is backed up by one so that AFTER the prolog code + runs, the "fill" phase is in sync with the "read" phase. */ + prologphase = (phase - 1) & (period - 1); + if (parentsigs) { + insig = parentsigs[inlet_getsignalindex(x->inlet)]; + parentvecsize = insig->vecsize; + re_parentvecsize = parentvecsize * upsample / downsample; + } else { + insig = 0; + parentvecsize = 1; + re_parentvecsize = 1; + } + bufsize = max(re_parentvecsize,myvecsize); + oldbufsize = x->bufsize; + if (bufsize != oldbufsize) { + t_float *buf = x->buf; + buf = (t_float *)resizealignedbytes(buf,oldbufsize * sizeof(*buf), bufsize * sizeof(*buf)); + memset((char *)buf, 0, bufsize * sizeof(*buf)); + x->bufsize = bufsize; + x->endbuf = buf + bufsize; + x->buf = buf; + } + if (parentsigs) { + /* IOhannes { */ + x->hop = period * re_parentvecsize; + x->fill = x->endbuf - (x->hop - prologphase * re_parentvecsize); + if (upsample * downsample == 1) + dsp_add(vinlet_doprolog, 3, x, insig->v, re_parentvecsize); + else { + resamplefrom_dsp(&x->updown, insig->v, parentvecsize, re_parentvecsize, x->updown.method); + dsp_add(vinlet_doprolog, 3, x, x->updown.v, re_parentvecsize); + } + /* } IOhannes */ + /* if the input signal's reference count is zero, we have to free it here because we didn't in ugen_doit(). */ + if (!insig->refcount) signal_makereusable(insig); + } else memset((char *)x->buf, 0, bufsize * sizeof(*x->buf)); + x->directsignal = 0; + } else { + /* no reblocking; in this case our output signal is "borrowed" and merely needs to be pointed to the real one. */ + x->directsignal = parentsigs[inlet_getsignalindex(x->inlet)]; + } +} + +static void *vinlet_newsig(t_symbol *s) { + t_vinlet *x = (t_vinlet *)pd_new(vinlet_class); + x->canvas = canvas_getcurrent(); + x->inlet = canvas_addinlet(x->canvas,x,&s_signal,s); + x->endbuf = x->buf = (t_float *)getalignedbytes(0); + x->bufsize = 0; + x->directsignal = 0; + outlet_new(x, &s_signal); + resample_init(&x->updown); + /* this should be thought over: it might prove hard to provide consistency between labeled up- & downsampling methods + maybe indices would be better... + up till now we provide several upsampling methods and 1 single downsampling method (no filtering !) */ + if (s) { + char c=*s->name; + switch(c) { + case'h':case'H':x->updown.method=RESAMPLE_HOLD; break; /* up: sample and hold */ + case'l':case'L':x->updown.method=RESAMPLE_LINEAR; break; /* up: linear interpolation */ + case'b':case'B':x->updown.method=RESAMPLE_BLOCK; break; /* down: ignore the 2nd half of the block */ + default: x->updown.method=RESAMPLE_ZERO; /* up: zero-padding */ + } + } + return x; +} + +static void vinlet_setup() { + t_class *c = vinlet_class = class_new2("inlet",vinlet_new,vinlet_free,sizeof(t_vinlet),CLASS_NOINLET,"S"); + class_addcreator2("inlet~",vinlet_newsig,"S"); + class_addbang( c, vinlet_bang); + class_addpointer( c, vinlet_pointer); + class_addfloat( c, vinlet_float); + class_addsymbol( c, vinlet_symbol); + class_addlist( c, vinlet_list); + class_addanything(c, vinlet_anything); + class_addmethod2( c, vinlet_dsp,"dsp",""); + class_sethelpsymbol(c, gensym("pd")); +} + +/* ------------------------- voutlet -------------------------- */ + +t_class *voutlet_class; + +struct t_voutlet : t_object { + t_canvas *canvas; + t_outlet *parentoutlet; + int bufsize; + t_float *buf; /* signal buffer; zero if not a signal */ + t_float *endbuf; + t_float *empty; /* next to read out of buffer in epilog code */ + t_float *write; /* next to write in to buffer */ + int hop; /* hopsize */ + /* vice versa from the inlet, if we don't block, this holds the + parent's outlet signal, valid between the prolog and the dsp setup functions. */ + t_signal *directsignal; + /* and here's a flag indicating that we aren't blocked but have to do a copy (because we're switched). */ + char justcopyout; + t_resample updown; /* IOhannes */ +}; + +static void *voutlet_new(t_symbol *s) { + t_voutlet *x = (t_voutlet *)pd_new(voutlet_class); + x->canvas = canvas_getcurrent(); + x->parentoutlet = canvas_addoutlet(x->canvas,x,0); + inlet_new(x,x,0,0); + x->bufsize = 0; + x->buf = 0; + return x; +} + +static void voutlet_bang(t_voutlet *x) +{outlet_bang(x->parentoutlet);} +static void voutlet_pointer(t_voutlet *x, t_gpointer *gp) +{outlet_pointer(x->parentoutlet, gp);} +static void voutlet_float(t_voutlet *x, t_float f) +{outlet_float(x->parentoutlet, f);} +static void voutlet_symbol(t_voutlet *x, t_symbol *s) +{outlet_symbol(x->parentoutlet, s);} +static void voutlet_list(t_voutlet *x, t_symbol *s, int argc, t_atom *argv) +{outlet_list(x->parentoutlet, s, argc, argv);} +static void voutlet_anything(t_voutlet *x, t_symbol *s, int argc, t_atom *argv) +{outlet_anything(x->parentoutlet, s, argc, argv);} + +static void voutlet_free(t_voutlet *x) { + canvas_rmoutlet(x->canvas, x->parentoutlet); + resample_free(&x->updown); +} + +t_outlet *voutlet_getit(t_pd *x) { + if (pd_class(x) != voutlet_class) bug("voutlet_getit"); + return ((t_voutlet *)x)->parentoutlet; +} + +/* ------------------------- signal outlet -------------------------- */ + +int voutlet_issignal(t_voutlet *x) {return x->buf!=0;} + +/* LATER optimize for non-overlapped case where the "+=" isn't needed */ +t_int *voutlet_perform(t_int *w) { + t_voutlet *x = (t_voutlet *)w[1]; + t_float *in = (t_float *)w[2]; + int n = int(w[3]); + t_float *out = x->write, *outwas = out, *end = x->endbuf; + while (n--) { + *out++ += *in++; + if (out == end) out = x->buf; + } + outwas += x->hop; + if (outwas >= end) outwas = x->buf; + x->write = outwas; + return w+4; +} + +/* epilog code for blocking: write buffer to parent patch */ +static t_int *voutlet_doepilog(t_int *w) { + t_voutlet *x = (t_voutlet *)w[1]; + t_float *out = (t_float *)w[2]; /* IOhannes */ + t_float *in = x->empty; + if (x->updown.downsample != x->updown.upsample) out = x->updown.v; /* IOhannes */ + for (int n = (int)(w[3]); n--; in++) *out++ = *in, *in = 0; + if (in == x->endbuf) in = x->buf; + x->empty = in; + return w+4; +} + +/* IOhannes { */ +static t_int *voutlet_doepilog_resampling(t_int *w) { + t_voutlet *x = (t_voutlet *)w[1]; + t_float *in = x->empty; + t_float *out = x->updown.v; /* IOhannes */ + for (int n = (int)(w[2]); n--; in++) *out++ = *in, *in = 0; + if (in == x->endbuf) in = x->buf; + x->empty = in; + return w+3; +} +/* } IOhannes */ +extern "C" int outlet_getsignalindex(t_outlet *x); + +/* prolog for outlets -- store pointer to the outlet on the parent, which, if "reblock" is false, will want to refer + back to whatever we see on our input during the "dsp" method called later. */ +extern "C" void voutlet_dspprolog(t_voutlet *x, t_signal **parentsigs, int myvecsize, int calcsize, int phase, int period, +int frequency, int downsample, int upsample, int reblock, int switched) { + x->updown.downsample=downsample; x->updown.upsample=upsample; /* IOhannes */ + x->justcopyout = (switched && !reblock); + if (reblock) { + x->directsignal = 0; + } else { + if (!parentsigs) bug("voutlet_dspprolog"); + x->directsignal = parentsigs[outlet_getsignalindex(x->parentoutlet)]; + } +} + +static void voutlet_dsp(t_voutlet *x, t_signal **sp) { + if (!x->buf) return; + t_signal *insig = sp[0]; + if (x->justcopyout) dsp_add_copy(insig->v, x->directsignal->v, insig->n); + else if (x->directsignal) { + /* if we're just going to make the signal available on the parent patch, hand it off to the parent signal. */ + /* this is done elsewhere--> sp[0]->refcount++; */ + signal_setborrowed(x->directsignal, sp[0]); + } else dsp_add(voutlet_perform, 3, x, insig->v, insig->n); +} + +/* set up epilog DSP code. If we're reblocking, this is the + time to copy the samples out to the containing object's outlets. + If we aren't reblocking, there's nothing to do here. */ +extern "C" void voutlet_dspepilog(t_voutlet *x, t_signal **parentsigs, +int myvecsize, int calcsize, int phase, int period, int frequency, int downsample, int upsample, int reblock, int switched) { + if (!x->buf) return; /* this shouldn't be necesssary... */ + x->updown.downsample=downsample; + x->updown.upsample=upsample; /* IOhannes */ + if (reblock) { + t_signal *outsig; + int parentvecsize, bufsize, oldbufsize; + int re_parentvecsize; /* IOhannes */ + int bigperiod, epilogphase, blockphase; + if (parentsigs) { + outsig = parentsigs[outlet_getsignalindex(x->parentoutlet)]; + parentvecsize = outsig->vecsize; + re_parentvecsize = parentvecsize * upsample / downsample; + } else { + outsig = 0; + parentvecsize = 1; + re_parentvecsize = 1; + } + bigperiod = myvecsize/re_parentvecsize; /* IOhannes */ + if (!bigperiod) bigperiod = 1; + epilogphase = phase & (bigperiod - 1); + blockphase = (phase + period - 1) & (bigperiod - 1) & (- period); + bufsize = re_parentvecsize; /* IOhannes */ + if (bufsize < myvecsize) bufsize = myvecsize; + if (bufsize != (oldbufsize = x->bufsize)) { + t_float *buf = x->buf; + buf = (t_float *)resizealignedbytes(buf,oldbufsize * sizeof(*buf),bufsize * sizeof(*buf)); + memset((char *)buf, 0, bufsize * sizeof(*buf)); + x->bufsize = bufsize; + x->endbuf = buf + bufsize; + x->buf = buf; + } + /* IOhannes: { */ + if (re_parentvecsize * period > bufsize) bug("voutlet_dspepilog"); + x->write = x->buf + re_parentvecsize * blockphase; + if (x->write == x->endbuf) x->write = x->buf; + if (period == 1 && frequency > 1) x->hop = re_parentvecsize / frequency; + else x->hop = period * re_parentvecsize; + /* } IOhannes */ + if (parentsigs) { + /* set epilog pointer and schedule it */ + /* IOhannes { */ + x->empty = x->buf + re_parentvecsize * epilogphase; + if (upsample*downsample==1) + dsp_add(voutlet_doepilog, 3, x, outsig->v, re_parentvecsize); + else { + dsp_add(voutlet_doepilog_resampling, 2, x, re_parentvecsize); + resampleto_dsp(&x->updown, outsig->v, re_parentvecsize, parentvecsize, x->updown.method); + } + /* } IOhannes */ + } + } + /* if we aren't blocked but we are switched, the epilog code just + copies zeros to the output. In this case the blocking code actually jumps over the epilog if the block is running. */ + else if (switched) { + if (parentsigs) { + t_signal *outsig = parentsigs[outlet_getsignalindex(x->parentoutlet)]; + dsp_add_zero(outsig->v, outsig->n); + } + } +} + +static void *voutlet_newsig(t_symbol *s) { + t_voutlet *x = (t_voutlet *)pd_new(voutlet_class); + x->canvas = canvas_getcurrent(); + x->parentoutlet = canvas_addoutlet(x->canvas,x,&s_signal); + inlet_new(x,x,&s_signal,&s_signal); + x->endbuf = x->buf = (t_float *)getalignedbytes(0); + x->bufsize = 0; + resample_init(&x->updown); + /* this should be though over: + * it might prove hard to provide consistency between labeled up- & downsampling methods + * maybe indeces would be better... + * up till now we provide several upsampling methods and 1 single downsampling method (no filtering !) */ + if (s) { + char c=*s->name; + switch(c) { + case 'h': case 'H': x->updown.method=RESAMPLE_HOLD; break; /* up: sample and hold */ + case 'l': case 'L': x->updown.method=RESAMPLE_LINEAR; break; /* up: linear interpolation */ + case 'b': case 'B': x->updown.method=RESAMPLE_BLOCK; break; /* down: ignore the 2nd half of the block */ + default: x->updown.method=RESAMPLE_ZERO; /* up: zero-padding */ + } + } + return x; +} + +static void voutlet_setup() { + t_class *c = voutlet_class = class_new2("outlet",voutlet_new,voutlet_free,sizeof(t_voutlet),CLASS_NOINLET,"S"); + class_addcreator2("outlet~",voutlet_newsig,"S"); + class_addbang( c, voutlet_bang); + class_addpointer( c, voutlet_pointer); + class_addfloat( c, (t_method)voutlet_float); + class_addsymbol( c, voutlet_symbol); + class_addlist( c, voutlet_list); + class_addanything(c, voutlet_anything); + class_addmethod2( c, voutlet_dsp, "dsp", ""); + class_sethelpsymbol(c, gensym("pd")); +} + +/* This file defines the "scalar" object, which is not a text object, just a + "gobj". Scalars have templates which describe their structures, which can contain numbers, sublists, and arrays. + IOhannes changed the canvas_restore, so that it might accept $args as well (like "pd $0_test") + so you can make multiple & distinguishable templates; added Krzysztof Czajas fix to avoid crashing... */ +t_class *scalar_class; + +void word_init(t_word *wp, t_template *t, t_gpointer *gp) { + t_dataslot *datatypes = t->vec; + for (int i=0; i < t->n; i++, datatypes++, wp++) { + int type = datatypes->type; + if (type == DT_FLOAT) wp->w_float = 0; + else if (type == DT_SYMBOL) wp->w_symbol = &s_symbol; + else if (type == DT_ARRAY) wp->w_array = array_new(datatypes->arraytemplate, gp); + else if (type == DT_CANVAS) { + /* LATER test this and get it to work */ + wp->w_canvas = canvas_new(0,0,0,0); + } + } +} + +void word_restore(t_word *wp, t_template *t, int argc, t_atom *argv) { + t_dataslot *datatypes = t->vec; + for (int i=0; in; i++, datatypes++, wp++) { + int type = datatypes->type; + if (type == DT_FLOAT) { + float f=0; + if (argc) {f = atom_getfloat(argv); argv++; argc--;} + wp->w_float = f; + } else if (type == DT_SYMBOL) { + t_symbol *s=&s_; + if (argc) {s = atom_getsymbol(argv); argv++; argc--;} + wp->w_symbol = s; + } + } + if (argc) post("warning: word_restore: extra arguments"); +} + +void word_free(t_word *wp, t_template *t) { + t_dataslot *dt = t->vec; + for (int i=0; in; i++, dt++) { + if (dt->type == DT_ARRAY) pd_free(wp[i].w_array); + else if (dt->type == DT_CANVAS) pd_free(wp[i].w_canvas); + } +} + +static void gpointer_setcanvas(t_gpointer *gp, t_canvas *canvas, t_scalar *x); +static void gpointer_setarray(t_gpointer *gp, t_array *array, t_word *w); +static t_word *gpointer_word(t_gpointer *gp) {return gp->o->_class == array_class ? gp->w : gp->scalar->v;} +static t_canvas *gpointer_getcanvas(t_gpointer *gp) { + if (gp->o->_class != array_class) return gp->canvas; + return 0; /* FIXME */ +} +static t_scalar *gpointer_getscalar(t_gpointer *gp) { + if (gp->o->_class != array_class) return gp->scalar; + return 0; +} + +/* make a new scalar and add to the canvas. We create a "gp" here which will be used for array items to point back here. + This gp doesn't do reference counting or "validation" updates though; the parent won't go away without the contained + arrays going away too. The "gp" is copied out by value in the word_init() routine so we can throw our copy away. */ +t_scalar *scalar_new(t_canvas *owner, t_symbol *templatesym) { + t_gpointer gp; + gpointer_init(&gp); + t_template *t = template_findbyname(templatesym); + TEMPLATE_CHECK(templatesym,0) + t_scalar *x = (t_scalar *)getbytes(sizeof(t_scalar) + (t->n - 1) * sizeof(*x->v)); + x->_class = scalar_class; + x->t = templatesym; + gpointer_setcanvas(&gp, owner, x); + word_init(x->v, t, &gp); + return x; +} + +/* Pd method to create a new scalar, add it to a canvas, and initialize it from the message arguments. */ +int canvas_readscalar(t_canvas *x, int natoms, t_atom *vec, int *p_nextmsg, int selectit); +static void canvas_scalar(t_canvas *canvas, t_symbol *classname, t_int argc, t_atom *argv) { + t_symbol *templatesym = canvas_makebindsym(atom_getsymbolarg(0, argc, argv)); + if (!template_findbyname(templatesym)) {error("%s: no such template", atom_getsymbolarg(0, argc, argv)->name); return;} + int nextmsg; + t_binbuf *b = binbuf_new(); + binbuf_restore(b, argc, argv); + canvas_readscalar(canvas, binbuf_getnatom(b), binbuf_getvec(b), &nextmsg, 0); + binbuf_free(b); +} + +static void scalar_getbasexy(t_scalar *x, float *basex, float *basey) { + t_template *t = template_findbyname(x->t); + *basex = template_getfloat(t,&s_x,x->v,0); + *basey = template_getfloat(t,&s_y,x->v,0); +} + +/* +static void scalar_displace(t_gobj *z, t_canvas *canvas, int dx, int dy) { + t_scalar *x = (t_scalar *)z; + t_symbol *templatesym = x->t; + t_template *t = template_findbyname(templatesym); + t_symbol *zz; + int xonset, yonset, xtype, ytype, gotx, goty; + TEMPLATE_CHECK(templatesym,) + gotx = template_find_field(t,&s_x,&xonset,&xtype,&zz); + if (gotx && (xtype != DT_FLOAT)) gotx = 0; + goty = template_find_field(t,&s_y,&yonset,&ytype,&zz); + if (goty && (ytype != DT_FLOAT)) goty = 0; + if (gotx) *(t_float *)(((char *)(x->v)) + xonset) += dx * (canvas_pixelstox(canvas, 1) - canvas_pixelstox(canvas, 0)); + if (goty) *(t_float *)(((char *)(x->v)) + yonset) += dy * (canvas_pixelstoy(canvas, 1) - canvas_pixelstoy(canvas, 0)); + scalar_redraw(x, canvas); +}*/ + +static void scalar_vis(t_gobj *z, t_canvas *owner, int vis) { + t_scalar *x = (t_scalar *)z; + t_template *t = template_findbyname(x->t); + t_canvas *templatecanvas = template_findcanvas(t); + float basex, basey; + scalar_getbasexy(x, &basex, &basey); + /* if we don't know how to draw it, make a small rectangle */ + if (!templatecanvas) { + if (vis) { + int x1 = canvas_xtopixels(owner, basex); + int y1 = canvas_ytopixels(owner, basey); + sys_vgui(".x%lx.c create rectangle %d %d %d %d -tags scalar%lx\n", + (long)canvas_getcanvas(owner), x1-1, y1-1, x1+1, y1+1, (long)x); + } else sys_vgui(".x%lx.c delete scalar%lx\n", (long)canvas_getcanvas(owner), (long)x); + return; + } + //canvas_each(y,templatecanvas) pd_getparentwidget(y)->w_parentvisfn(y,owner,x->v,t,basex,basey,vis); + //sys_unqueuegui(x); +} + +static void scalar_doredraw(t_gobj *client, t_canvas *canvas) { + scalar_vis(client, canvas, 0); + scalar_vis(client, canvas, 1); +} + +void scalar_redraw(t_scalar *x, t_canvas *canvas) { + //if (canvas_isvisible(canvas)) sys_queuegui(x, canvas, scalar_doredraw); +} + +#if 0 +int scalar_doclick(t_word *data, t_template *t, t_scalar *sc, t_array *ap, t_canvas *owner, +float xloc, float yloc, int xpix, int ypix, int shift, int alt, int dbl, int doit) { + t_canvas *templatecanvas = template_findcanvas(t); + float basex = template_getfloat(t,&s_x,data,0); + float basey = template_getfloat(t,&s_y,data,0); + canvas_each(y,templatecanvas) { + int hit = pd_getparentwidget(y)->w_parentclickfn(y, owner, data, t, sc, ap, basex+xloc, basey+yloc, + xpix, ypix, shift, alt, dbl, doit); + if (hit) return hit; + }*/ + return 0; +} +#endif + +static int scalar_click(t_gobj *z, t_canvas *owner, int xpix, int ypix, int shift, int alt, int dbl, int doit) { + t_scalar *x = (t_scalar *)z; + t_template *t = template_findbyname(x->t); + return scalar_doclick(x->v, t, x, 0, owner, 0, 0, xpix, ypix, shift, alt, dbl, doit); +} + +void canvas_writescalar(t_symbol *templatesym, t_word *w, t_binbuf *b, int amarrayelement); + +static void scalar_save(t_gobj *z, t_binbuf *b) { + t_scalar *x = (t_scalar *)z; + t_binbuf *b2 = binbuf_new(); + canvas_writescalar(x->t, x->v, b2, 0); + binbuf_addv(b,"tt","#X","scalar"); + binbuf_addbinbuf(b, b2); + binbuf_addsemi(b); + binbuf_free(b2); +} + +/* +static void scalar_properties(t_gobj *z, t_canvas *owner) { + t_scalar *x = (t_scalar *)z; + char *buf, buf2[80]; + int bufsize; + t_binbuf *b; + b = canvas_writetobinbuf(owner, 0); + binbuf_gettext(b, &buf, &bufsize); + binbuf_free(b); + buf = (char *)realloc(buf, bufsize+1); + buf[bufsize] = 0; + sprintf(buf2, "pdtk_data_dialog %%s {"); + sys_gui(buf); + sys_gui("}\n"); + free(buf); +} +*/ + +static void scalar_free(t_scalar *x) { + t_template *t = template_findbyname(x->t); + TEMPLATE_CHECK(x->t,) + word_free(x->v, t); + /* the "size" field in the class is zero, so Pd doesn't try to free us automatically (see pd_free()) */ + free(x); +} + +static void g_scalar_setup() { + scalar_class = class_new2("scalar",0,scalar_free,0,CLASS_GOBJ,""); + class_setsavefn(scalar_class, scalar_save); +} + +void array_redraw(t_array *a, t_canvas *canvas); + +/* +This file contains text objects you would put in a canvas to define a +template. Templates describe objects of type "array" (g_array.c) and "scalar" (g_scalar.c). */ +/* the structure of a "struct" object (also the obsolete "gtemplate" you get when using the name "template" in a box.) */ +struct t_gtemplate : t_object { + t_template *t; + t_canvas *owner; + t_symbol *sym; + t_gtemplate *next; + int argc; + t_atom *argv; +}; + +static void template_conformarray(t_template *tfrom, t_template *tto, int *conformaction, t_array *a); +static void template_conformcanvas(t_template *tfrom, t_template *tto, int *conformaction, t_canvas *canvas); +static t_class *gtemplate_class; +static t_class *template_class; + +/* there's a pre-defined "float" template. LATER should we bind this to a symbol such as "pd-float"??? */ + +/* return true if two dataslot definitions match */ +static int dataslot_matches(t_dataslot *ds1, t_dataslot *ds2, int nametoo) { + return (!nametoo || ds1->name == ds2->name) && ds1->type == ds2->type && + (ds1->type != DT_ARRAY || ds1->arraytemplate == ds2->arraytemplate); +} + +/* -- templates, the active ingredient in gtemplates defined below. ------- */ + +static t_template *template_new(t_symbol *templatesym, int argc, t_atom *argv) { + t_template *x = (t_template *)pd_new(template_class); + x->n = 0; + x->vec = (t_dataslot *)getbytes(0); + while (argc > 0) { + int newtype, oldn, newn; + t_symbol *newname, *newarraytemplate = &s_, *newtypesym; + if (argc < 2 || argv[0].a_type != A_SYMBOL || argv[1].a_type != A_SYMBOL) goto bad; + newtypesym = argv[0].a_symbol; + newname = argv[1].a_symbol; + if (newtypesym == &s_float) newtype = DT_FLOAT; + else if (newtypesym == &s_symbol) newtype = DT_SYMBOL; + else if (newtypesym == &s_list) newtype = DT_CANVAS; + else if (newtypesym == gensym("array")) { + if (argc < 3 || argv[2].a_type != A_SYMBOL) {error("array lacks element template or name"); goto bad;} + newarraytemplate = canvas_makebindsym(argv[2].a_symbol); + newtype = DT_ARRAY; + argc--; + argv++; + } else {error("%s: no such type", newtypesym->name); goto bad;} + newn = (oldn = x->n) + 1; + x->vec = (t_dataslot *)realloc(x->vec, newn*sizeof(*x->vec)); + x->n = newn; + x->vec[oldn].type = newtype; + x->vec[oldn].name = newname; + x->vec[oldn].arraytemplate = newarraytemplate; + bad: + argc -= 2; argv += 2; + } + x->sym = templatesym; + if (templatesym->name) pd_bind(x,x->sym); + return x; +} + +int template_size(t_template *x) {return x->n * sizeof(t_word);} + +int template_find_field(t_template *x, t_symbol *name, int *p_onset, int *p_type, t_symbol **p_arraytype) { + if (!x) {bug("template_find_field"); return 0;} + for (int i = 0; in; i++) if (x->vec[i].name == name) { + *p_onset = i*sizeof(t_word); + *p_type = x->vec[i].type; + *p_arraytype = x->vec[i].arraytemplate; + return 1; + } + return 0; +} + +#define ERR(msg,ret) do {\ + if (loud) error("%s.%s: "msg, x->sym->name, fieldname->name);\ + return ret;} while(0); +static t_float template_getfloat(t_template *x, t_symbol *fieldname, t_word *wp, int loud) { + int onset, type; t_symbol *arraytype; + if (!template_find_field(x, fieldname, &onset, &type, &arraytype)) ERR("no such field",0); + if (type != DT_FLOAT) ERR("not a number",0); + return *(t_float *)(((char *)wp) + onset); +} +static t_symbol *template_getsymbol(t_template *x, t_symbol *fieldname, t_word *wp, int loud) { + int onset, type; t_symbol *arraytype; + if (!template_find_field(x, fieldname, &onset, &type, &arraytype)) ERR("no such field",&s_); + if (type != DT_SYMBOL) ERR("not a symbol",&s_); + return *(t_symbol **)(((char *)wp) + onset); +} +static void template_setfloat(t_template *x, t_symbol *fieldname, t_word *wp, t_float f, int loud) { + int onset, type; t_symbol *arraytype; + if (!template_find_field(x, fieldname, &onset, &type, &arraytype)) ERR("no such field",); + if (type != DT_FLOAT) ERR("not a number",); + *(t_float *)(((char *)wp) + onset) = f; +} +static void template_setsymbol(t_template *x, t_symbol *fieldname, t_word *wp, t_symbol *s, int loud) { + int onset, type; t_symbol *arraytype; + if (!template_find_field(x, fieldname, &onset, &type, &arraytype)) ERR("no such field",); + if (type != DT_SYMBOL) ERR("not a symbol",); + *(t_symbol **)(((char *)wp) + onset) = s; +} +#undef ERR + +/* stringent check to see if a "saved" template, x2, matches the current +one (x1). It's OK if x1 has additional scalar elements but not (yet) +arrays or lists. This is used for reading in "data files". */ +static int template_match(t_template *x1, t_template *x2) { + if (x1->n < x2->n) return 0; + for (int i=x2->n; i < x1->n; i++) + if (x1->vec[i].type == DT_ARRAY || x1->vec[i].type == DT_CANVAS) return 0; + if (x2->n > x1->n) post("add elements..."); + for (int i=0; i < x2->n; i++) if (!dataslot_matches(&x1->vec[i], &x2->vec[i], 1)) return 0; + return 1; +} + +/* --------------- CONFORMING TO CHANGES IN A TEMPLATE ------------ */ + +/* the following functions handle updating scalars to agree with changes +in their template. The old template is assumed to be the "installed" one +so we can delete old items; but making new ones we have to avoid scalar_new +which would make an old one whereas we will want a new one (but whose array +elements might still be old ones.) + LATER deal with graphics updates too... */ + +/* conform the word vector of a scalar to the new template */ +static void template_conformwords(t_template *tfrom, t_template *tto, int *conformaction, t_word *wfrom, t_word *wto) { + for (int i=0; in; i++) { + if (conformaction[i] >= 0) { + /* we swap the two, in case it's an array or list, so that when "wfrom" is deleted the old one gets cleaned up. */ + t_word wwas = wto[i]; + wto[i] = wfrom[conformaction[i]]; + wfrom[conformaction[i]] = wwas; + } + } +} + +/* conform a scalar, recursively conforming sublists and arrays */ +static t_scalar *template_conformscalar(t_template *tfrom, t_template *tto, int *conformaction, t_canvas *canvas, t_scalar *scfrom) { + t_scalar *x; + t_template *scalartemplate; + /* possibly replace the scalar */ + if (scfrom->t == tfrom->sym) { + t_gpointer gp; + /* see scalar_new() for comment about the gpointer. */ + gpointer_init(&gp); + x = (t_scalar *)getbytes(sizeof(t_scalar) + (tto->n - 1) * sizeof(*x->v)); + x->_class = scalar_class; + x->t = tfrom->sym; + gpointer_setcanvas(&gp, canvas, x); + /* Here we initialize to the new template, but array and list elements will still belong to old template. */ + word_init(x->v, tto, &gp); + template_conformwords(tfrom, tto, conformaction, scfrom->v, x->v); + /* replace the old one with the new one in the list */ + canvas->boxes->remove_by_value(scfrom); + canvas->boxes->add(x); + pd_free(scfrom); + scalartemplate = tto; + } else { + x = scfrom; + scalartemplate = template_findbyname(x->t); + } + /* convert all array elements and sublists */ + for (int i=0; i < scalartemplate->n; i++) { + t_dataslot *ds = scalartemplate->vec + i; + if (ds->type == DT_CANVAS) template_conformcanvas(tfrom, tto, conformaction, x->v[i].w_canvas); + if (ds->type == DT_ARRAY) template_conformarray( tfrom, tto, conformaction, x->v[i].w_array); + } + return x; +} + +/* conform an array, recursively conforming sublists and arrays */ +static void template_conformarray(t_template *tfrom, t_template *tto, int *conformaction, t_array *a) { + t_template *scalartemplate = 0; + if (a->templatesym == tfrom->sym) { + /* the array elements must all be conformed */ + int oldelemsize = sizeof(t_word) * tfrom->n; + int newelemsize = sizeof(t_word) * tto->n; + char *newarray = (char *)getbytes(newelemsize * a->n); + char *oldarray = a->vec; + if (a->elemsize != oldelemsize) bug("template_conformarray"); + for (int i=0; in; i++) { + t_word *wp = (t_word *)(newarray + newelemsize*i); + word_init(wp, tto, &a->gp); + template_conformwords(tfrom, tto, conformaction, (t_word *)(oldarray + oldelemsize*i), wp); + word_free((t_word *)(oldarray + oldelemsize*i), tfrom); + } + scalartemplate = tto; + a->vec = newarray; + free(oldarray); + } else scalartemplate = template_findbyname(a->templatesym); + /* convert all arrays and sublist fields in each element of the array */ + for (int i=0; in; i++) { + t_word *wp = (t_word *)(a->vec + sizeof(t_word) * a->n * i); + for (int j=0; j < scalartemplate->n; j++) { + t_dataslot *ds = scalartemplate->vec + j; + if (ds->type == DT_CANVAS) template_conformcanvas(tfrom, tto, conformaction, wp[j].w_canvas); + if (ds->type == DT_ARRAY) template_conformarray( tfrom, tto, conformaction, wp[j].w_array); + } + } +} + +/* this routine searches for every scalar in the canvas that belongs + to the "from" template and makes it belong to the "to" template. Descend canvases recursively. + We don't handle redrawing here; this is to be filled in LATER... */ +t_array *garray_getarray(t_garray *x); +static void template_conformcanvas(t_template *tfrom, t_template *tto, int *conformaction, t_canvas *canvas) { + canvas_each(g,canvas) { + t_class *c = g->_class; + /* what's the purpose of the assignment here?... consult original code */ + if (c==scalar_class) g = template_conformscalar(tfrom, tto, conformaction, canvas, (t_scalar *)g); + else if (c==canvas_class) template_conformcanvas(tfrom, tto, conformaction, (t_canvas *)g); + else if (c==garray_class) template_conformarray(tfrom, tto, conformaction, garray_getarray((t_garray *)g)); + } +} + +/* globally conform all scalars from one template to another */ +void template_conform(t_template *tfrom, t_template *tto) { + int nto = tto->n, nfrom = tfrom->n, doit = 0; + int *conformaction = (int *)getbytes(sizeof(int) * nto); + int *conformedfrom = (int *)getbytes(sizeof(int) * nfrom); + for (int i=0; i< nto; i++) conformaction[i] = -1; + for (int i=0; ivec[i]; + for (int j=0; jvec[j]; + if (dataslot_matches(dataslot, dataslot2, 1)) { + conformaction[i] = j; + conformedfrom[j] = 1; + } + } + } + for (int i=0; ivec[i]; + for (int j=0; jvec[j], 0)) { + conformaction[i] = j; + conformedfrom[j] = 1; + } + } + if (nto != nfrom) doit = 1; + else for (int i=0; isym->name); + for (int i=0; ifirst); + } + free(conformaction); + free(conformedfrom); +} + +t_template *template_findbyname(t_symbol *s) {return (t_template *)pd_findbyclass(s, template_class);} + +t_canvas *template_findcanvas(t_template *t) { + if (!t) bug("template_findcanvas"); + t_gtemplate *gt = t->list; + if (!gt) return 0; + return gt->owner; + /* return ((t_canvas *)pd_findbyclass(t->sym, canvas_class)); */ +} + +void template_notify(t_template *t, t_symbol *s, int argc, t_atom *argv) { + if (t->list) outlet_anything(t->list->outlet, s, argc, argv); +} + +/* bash the first of (argv) with a pointer to a scalar, and send on + to template as a notification message */ +static void template_notifyforscalar(t_template *t, t_canvas *owner, t_scalar *sc, t_symbol *s, int argc, t_atom *argv) { + t_gpointer gp; + gpointer_init(&gp); + gpointer_setcanvas(&gp, owner, sc); + SETPOINTER(argv, &gp); + template_notify(t, s, argc, argv); + gpointer_unset(&gp); +} + +/* call this when reading a patch from a file to declare what templates + we'll need. If there's already a template, check if it matches. + If it doesn't it's still OK as long as there are no "struct" (gtemplate) + objects hanging from it; we just conform everyone to the new template. + If there are still struct objects belonging to the other template, we're + in trouble. LATER we'll figure out how to conform the new patch's objects + to the pre-existing struct. */ +static void *template_usetemplate(void *dummy, t_symbol *s, int argc, t_atom *argv) { + t_symbol *templatesym = canvas_makebindsym(atom_getsymbolarg(0, argc, argv)); + t_template *x = (t_template *)pd_findbyclass(templatesym, template_class); + if (!argc) return 0; + argc--; argv++; + if (x) { + t_template *y = template_new(&s_, argc, argv); + /* If the new template is the same as the old one, there's nothing to do. */ + if (!template_match(x, y)) { /* Are there "struct" objects upholding this template? */ + if (x->list) { + error("%s: template mismatch", templatesym->name); + } else { + template_conform(x, y); + pd_free(x); + t_template *y2 = template_new(templatesym, argc, argv); + y2->list = 0; + } + } + pd_free(y); + } else template_new(templatesym, argc, argv); + return 0; +} + +/* here we assume someone has already cleaned up all instances of this. */ +void template_free(t_template *x) { + if (*x->sym->name) pd_unbind(x,x->sym); + free(x->vec); +} + +/* ---------------- gtemplates. One per canvas. ----------- */ + +/* "Struct": an object that searches for, and if necessary creates, +a template (above). Other objects in the canvas then can give drawing +instructions for the template. The template doesn't go away when the +"struct" is deleted, so that you can replace it with another one to add new fields, for example. */ +static void *gtemplate_donew(t_symbol *sym, int argc, t_atom *argv) { + t_gtemplate *x = (t_gtemplate *)pd_new(gtemplate_class); + t_template *t = template_findbyname(sym); + x->owner = canvas_getcurrent(); + x->next = 0; + x->sym = sym; + x->argc = argc; + x->argv = (t_atom *)getbytes(argc * sizeof(t_atom)); + for (int i=0; iargv[i] = argv[i]; + /* already have a template by this name? */ + if (t) { + x->t = t; + /* if it's already got a "struct" object we + just tack this one to the end of the list and leave it there. */ + if (t->list) { + t_gtemplate *x2, *x3; + for (x2 = x->t->list; (x3 = x2->next); x2 = x3) {} + x2->next = x; + post("template %s: warning: already exists.", sym->name); + } else { + /* if there's none, we just replace the template with our own and conform it. */ + t_template *y = template_new(&s_, argc, argv); + //canvas_redrawallfortemplate(t, 2); + /* Unless the new template is different from the old one, there's nothing to do. */ + if (!template_match(t, y)) { + /* conform everyone to the new template */ + template_conform(t, y); + pd_free(t); + t = template_new(sym, argc, argv); + } + pd_free(y); + t->list = x; + //canvas_redrawallfortemplate(t, 1); + } + } else { + /* otherwise make a new one and we're the only struct on it. */ + x->t = t = template_new(sym, argc, argv); + t->list = x; + } + outlet_new(x,0); + return x; +} + +static void *gtemplate_new(t_symbol *s, int argc, t_atom *argv) { + t_symbol *sym = atom_getsymbolarg(0, argc, argv); + if (argc >= 1) {argc--; argv++;} + return (gtemplate_donew(canvas_makebindsym(sym), argc, argv)); +} + +/* old version (0.34) -- delete 2003 or so */ +static void *gtemplate_new_old(t_symbol *s, int argc, t_atom *argv) { + t_symbol *sym = canvas_makebindsym(canvas_getcurrent()->name); + static int warned; + if (!warned) { + post("warning -- 'template' (%s) is obsolete; replace with 'struct'", sym->name); + warned = 1; + } + return gtemplate_donew(sym, argc, argv); +} + +t_template *gtemplate_get(t_gtemplate *x) {return x->t;} + +static void gtemplate_free(t_gtemplate *x) { + /* get off the template's list */ + t_template *t = x->t; + if (x == t->list) { + //canvas_redrawallfortemplate(t, 2); + if (x->next) { + /* if we were first on the list, and there are others on the list, make a new template corresponding + to the new first-on-list and replace the existing template with it. */ + t_template *z = template_new(&s_, x->next->argc, x->next->argv); + template_conform(t, z); + pd_free(t); + pd_free(z); + z = template_new(x->sym, x->next->argc, x->next->argv); + z->list = x->next; + for (t_gtemplate *y=z->list; y ; y=y->next) y->t=z; + } else t->list = 0; + //canvas_redrawallfortemplate(t, 1); + } else { + t_gtemplate *x2, *x3; + for (x2=t->list; (x3=x2->next); x2=x3) if (x==x3) {x2->next=x3->next; break;} + } + free(x->argv); +} + +/* --------------- FIELD DESCRIPTORS (NOW CALLED SLOT) ---------------------- */ +/* a field descriptor can hold a constant or a variable's name; in the latter case, + it's the name of a field in the template we belong to. LATER, we might want to cache the offset + of the field so we don't have to search for it every single time we draw the object. +*/ +/* note: there is also t_dataslot which plays a similar role. could they be merged someday? */ + +struct _slot { + char type; /* LATER consider removing this? */ + char var; + union { + t_float f; /* the field is a constant float */ + t_symbol *s; /* the field is a constant symbol */ + t_symbol *varsym; /* the field is variable and this is the name */ + }; + float min,max; + float scrmin,scrmax; /* min and max screen values */ + float quantum; /* quantization in value */ +}; + +static void slot_setfloat_const( t_slot *fd, float f) { + fd->type = A_FLOAT; fd->var = 0; fd->f = f; fd->min = fd->max = fd->scrmin = fd->scrmax = fd->quantum = 0;} +static void slot_setsymbol_const(t_slot *fd, t_symbol *s) { + fd->type = A_SYMBOL; fd->var = 0; fd->s = s; fd->min = fd->max = fd->scrmin = fd->scrmax = fd->quantum = 0;} + +static void slot_setfloat_var(t_slot *fd, t_symbol *s) { + char *s1, *s2, *s3; + fd->type = A_FLOAT; + fd->var = 1; + if (!(s1 = strchr(s->name, '(')) || !(s2 = strchr(s->name, ')')) || s1>s2) { + fd->varsym = s; + fd->min = fd->max = fd->scrmin = fd->scrmax = fd->quantum = 0; + } else { + fd->varsym = symprintf("%.*s",s1-s->name,s->name); + t_int got = sscanf(s1, "(%f:%f)(%f:%f)(%f)", &fd->min, &fd->max, &fd->scrmin, &fd->scrmax, &fd->quantum); + if (got < 2) goto fail; + if (got == 3 || (got < 4 && strchr(s2, '('))) goto fail; + if (got < 5 && (s3 = strchr(s2, '(')) && strchr(s3+1, '(')) goto fail; + if (got == 4) fd->quantum = 0; + else if (got == 2) { + fd->quantum = 0; + fd->scrmin = fd->min; + fd->scrmax = fd->max; + } + return; + fail: + error("parse error: %s", s->name); + fd->min = fd->scrmin = fd->max = fd->scrmax = fd->quantum = 0; + } +} + +#define CLOSED 1 +#define BEZ 2 +#define NOMOUSE 4 +#define A_ARRAY 55 /* LATER decide whether to enshrine this in m_pd.h */ + +static void slot_setfloatarg(t_slot *fd, int argc, t_atom *argv) { + if (argc <= 0) slot_setfloat_const(fd, 0); + else if (argv->a_type == A_SYMBOL) slot_setfloat_var( fd, argv->a_symbol); + else slot_setfloat_const(fd, argv->a_float); +} +static void slot_setsymbolarg(t_slot *fd, int argc, t_atom *argv) { + if (argc <= 0) slot_setsymbol_const(fd, &s_); + else if (argv->a_type == A_SYMBOL) { + fd->type = A_SYMBOL; + fd->var = 1; + fd->varsym = argv->a_symbol; + fd->min = fd->max = fd->scrmin = fd->scrmax = fd->quantum = 0; + } else slot_setsymbol_const(fd, &s_); +} +static void slot_setarrayarg(t_slot *fd, int argc, t_atom *argv) { + if (argc <= 0) slot_setfloat_const(fd, 0); + else if (argv->a_type == A_SYMBOL) { + fd->type = A_ARRAY; + fd->var = 1; + fd->varsym = argv->a_symbol; + } else slot_setfloat_const(fd, argv->a_float); +} + +/* getting and setting values via slots -- note confusing names; the above are setting up the slot itself. */ + +/* convert a variable's value to a screen coordinate via its slot */ +static t_float slot_cvttocoord(t_slot *f, float val) { + float coord, extreme, div; + if (f->max == f->min) return val; + div = (f->scrmax - f->scrmin)/(f->max - f->min); + coord = f->scrmin + (val - f->min) * div; + extreme = f->scrminscrmax ? f->scrmin : f->scrmax; if (coordscrmin>f->scrmax ? f->scrmin : f->scrmax; if (coord>extreme) coord = extreme; + return coord; +} + +/* read a variable via slot and convert to screen coordinate */ +static t_float slot_getcoord(t_slot *f, t_template *t, t_word *wp, int loud) { + if (f->type!=A_FLOAT) {if (loud) error("symbolic data field used as number"); return 0;} + if (f->var) return slot_cvttocoord(f, template_getfloat(t, f->varsym, wp, loud)); + return f->f; +} +static t_float slot_getfloat(t_slot *f, t_template *t, t_word *wp, int loud) { + if (f->type!=A_FLOAT) {if (loud) error("symbolic data field used as number"); return 0;} + if (f->var) return template_getfloat(t, f->varsym, wp, loud); + return f->f; +} +static t_symbol *slot_getsymbol(t_slot *f, t_template *t, t_word *wp, int loud) { + if (f->type!=A_SYMBOL) {if (loud) error("numeric data field used as symbol"); return &s_;} + if (f->var) return template_getsymbol(t, f->varsym, wp, loud); + return f->s; +} + +/* convert from a screen coordinate to a variable value */ +static float slot_cvtfromcoord(t_slot *f, float coord) { + if (f->scrmax == f->scrmin) return coord; + else { + float div = (f->max - f->min)/(f->scrmax - f->scrmin); + float extreme; + float val = f->min + (coord - f->scrmin) * div; + if (f->quantum != 0) val = ((int)((val/f->quantum) + 0.5)) * f->quantum; + extreme = f->minmax ? f->min : f->max; if (valmin>f->max ? f->min : f->max; if (val>extreme) val=extreme; + return val; + } + } + +static void slot_setcoord(t_slot *f, t_template *t, t_word *wp, float coord, int loud) { + if (f->type == A_FLOAT && f->var) { + float val = slot_cvtfromcoord(f, coord); + template_setfloat(t, f->varsym, wp, val, loud); + } else { + if (loud) error("attempt to set constant or symbolic data field to a number"); + } +} + +#define FIELDSET(T,F,D) if (argc) slot_set##T##arg(&x->F,argc--,argv++); \ + else slot_setfloat_const(&x->F,D); + +/* curves belong to templates and describe how the data in the template are to be drawn. + The coordinates of the curve (and other display features) can be attached to fields in the template. */ + +t_class *curve_class; + +/* includes polygons too */ +struct t_curve : t_object { + int flags; /* CLOSED and/or BEZ and/or NOMOUSE */ + t_slot fillcolor, outlinecolor, width, vis; + int npoints; + t_slot *vec; + t_canvas *canvas; +}; + +static void *curve_new(t_symbol *classsym, t_int argc, t_atom *argv) { + t_curve *x = (t_curve *)pd_new(curve_class); + char *classname = classsym->name; + int flags = 0; + x->canvas = canvas_getcurrent(); + if (classname[0] == 'f') {classname += 6; flags |= CLOSED;} else classname += 4; + slot_setfloat_const(&x->vis, 1); + if (classname[0] == 'c') flags |= BEZ; + while (1) { + t_symbol *firstarg = atom_getsymbolarg(0, argc, argv); + if (!strcmp(firstarg->name,"-v") && argc > 1) { + slot_setfloatarg(&x->vis, 1, argv+1); + argc -= 2; argv += 2; + } else + if (!strcmp(firstarg->name,"-x")) { + flags |= NOMOUSE; + argc -= 1; argv += 1; + } else break; + } + x->flags = flags; + if (flags&CLOSED&&argc) slot_setfloatarg( &x->fillcolor, argc--,argv++); + else slot_setfloat_const(&x->fillcolor, 0); + FIELDSET(float,outlinecolor,0); + FIELDSET(float,width,1); + if (argc < 0) argc = 0; + int nxy = argc + (argc&1); + x->npoints = nxy>>1; + x->vec = (t_slot *)getbytes(nxy * sizeof(t_slot)); + t_slot *fd = x->vec; + for (int i=0; ivis.type != A_FLOAT || x->vis.var) { + error("global vis/invis for a template with variable visibility"); + return; + } + int viswas = x->vis.f!=0; + if ((f!=0 && viswas) || (f==0 && !viswas)) return; + canvas_redrawallfortemplatecanvas(x->canvas, 2); + slot_setfloat_const(&x->vis, f!=0); + canvas_redrawallfortemplatecanvas(x->canvas, 1); +} + +static int rangecolor(int n) {return n*9/255;} + +static void numbertocolor(int n, char *s) { + n = (int)max(n,0); + sprintf(s, "#%2.2x%2.2x%2.2x", rangecolor(n/100), rangecolor((n/10)%10), rangecolor(n%10)); +} + +static void curve_vis(t_gobj *z, t_canvas *canvas, t_word *data, t_template *t, float basex, float basey, int vis) { + t_curve *x = (t_curve *)z; + int n = x->npoints; + t_slot *f = x->vec; + if (!slot_getfloat(&x->vis, t, data, 0)) return; + if (vis) { + if (n > 1) { + int flags = x->flags; + char outline[20], fill[20]; + int pix[200]; + if (n > 100) n = 100; + /* calculate the pixel values before we start printing out the TK message so that + "error" printout won't be interspersed with it. Only show up to 100 points so we don't + have to allocate memory here. */ + for (int i=0; ioutlinecolor, t, data, 1), outline); + if (flags & CLOSED) { + numbertocolor((int)slot_getfloat(&x->fillcolor, t, data, 1), fill); + //sys_vgui(".x%lx.c create polygon\\\n", (long)canvas_getcanvas(canvas)); + } else sys_vgui(".x%lx.c create line\\\n", (long)canvas_getcanvas(canvas)); + for (int i=0; iwidth, t, data, 1),1.0f)); + if (flags & CLOSED) sys_vgui("-fill %s -outline %s\\\n", fill, outline); + else sys_vgui("-fill %s\\\n", outline); + if (flags & BEZ) sys_vgui("-smooth 1\\\n"); + sys_vgui("-tags curve%lx\n", (long)data); + } else post("warning: curves need at least two points to be graphed"); + } else { + if (n > 1) sys_vgui(".x%lx.c delete curve%lx\n", (long)canvas_getcanvas(canvas), (long)data); + } +} + +static struct { + int field; + float xcumulative, xbase, xper; + float ycumulative, ybase, yper; + t_canvas *canvas; + t_scalar *scalar; + t_array *array; + t_word *wp; + t_template *t; + t_gpointer gpointer; +} cm; + +/* LATER protect against the template changing or the scalar disappearing probably by attaching a gpointer here ... */ +#if 0 +static void curve_motion(void *z, t_floatarg dx, t_floatarg dy) { + t_curve *x = (t_curve *)z; + t_slot *f = x->vec + cm.field; + t_atom at; + if (!gpointer_check(&curve_motion_gpointer, 0)) {post("curve_motion: scalar disappeared"); return;} + cm.xcumulative += dx; + cm.ycumulative += dy; + if (f[0].var && dx!=0) slot_setcoord(f, cm.t, cm.wp, cm.xbase + cm.xcumulative*cm.xper, 1); + if (f[1].var && dy!=0) slot_setcoord(f+1, cm.t, cm.wp, cm.ybase + cm.ycumulative*cm.yper, 1); + /* LATER figure out what to do to notify for an array? */ + if (cm.scalar) template_notifyforscalar(cm.t, cm.canvas, cm.scalar, gensym("change"), 1, &at); + if (cm.scalar) gobj_changed(cm.scalar,0); else gobj_changed(cm.array,0); +} +#endif + +int iabs(int a) {return a<0?-a:a;} + +static int curve_click(t_gobj *z, t_canvas *canvas, t_word *data, t_template *t, t_scalar *sc, +t_array *ap, float basex, float basey, int xpix, int ypix, int shift, int alt, int dbl, int doit) { + t_curve *x = (t_curve *)z; + int bestn = -1; + int besterror = 0x7fffffff; + t_slot *f = x->vec; + if (!slot_getfloat(&x->vis, t, data, 0)) return 0; + for (int i=0; inpoints; i++, f += 2) { + int xval = (int)slot_getcoord(f , t, data, 0), xloc = canvas_xtopixels(canvas, basex + xval); + int yval = (int)slot_getcoord(f+1, t, data, 0), yloc = canvas_ytopixels(canvas, basey + yval); + int xerr = iabs(xloc-xpix); + int yerr = iabs(yloc-ypix); + if (!f->var && !(f+1)->var) continue; + if (yerr > xerr) xerr = yerr; + if (xerr < besterror) { + cm.xbase = xval; + cm.ybase = yval; + besterror = xerr; + bestn = i; + } + } + if (besterror > 10) return 0; + if (doit) { + cm.xper = canvas_pixelstox(canvas, 1) - canvas_pixelstox(canvas, 0); + cm.yper = canvas_pixelstoy(canvas, 1) - canvas_pixelstoy(canvas, 0); + cm.xcumulative = cm.ycumulative = 0; + cm.canvas = canvas; + cm.scalar = sc; + cm.array = ap; + cm.wp = data; + cm.field = 2*bestn; + cm.t = t; + if (cm.scalar) gpointer_setcanvas(&cm.gpointer, cm.canvas, cm.scalar); + else gpointer_setarray(&cm.gpointer, cm.array, cm.wp); + /* canvas_grab(canvas, z, curve_motion, 0, xpix, ypix); */ + } + return 1; +} + +t_class *plot_class; + +struct t_plot : t_object { + t_canvas *canvas; + t_slot outlinecolor, width, xloc, yloc, xinc, style; + t_slot data, xpoints, ypoints, wpoints; + t_slot vis; + t_slot scalarvis; /* true if drawing the scalar at each point */ +}; + +static void *plot_new(t_symbol *classsym, t_int argc, t_atom *argv) { + t_plot *x = (t_plot *)pd_new(plot_class); + int defstyle = PLOTSTYLE_POLY; + x->canvas = canvas_getcurrent(); + slot_setfloat_var(&x->xpoints,&s_x); + slot_setfloat_var(&x->ypoints,&s_y); + slot_setfloat_var(&x->wpoints, gensym("w")); + slot_setfloat_const(&x->vis, 1); + slot_setfloat_const(&x->scalarvis, 1); + while (1) { + const char *f = atom_getsymbolarg(0, argc, argv)->name; + argc--; argv++; + if (!strcmp(f, "curve") || !strcmp(f, "-c")) defstyle = PLOTSTYLE_BEZ; + else if (!strcmp(f,"-v") &&argc>0) {slot_setfloatarg(&x->vis, 1,argv+1);argc--;argv++;} + else if (!strcmp(f,"-vs")&&argc>0) {slot_setfloatarg(&x->scalarvis,1,argv+1);argc--;argv++;} + else if (!strcmp(f,"-x") &&argc>0) {slot_setfloatarg(&x->xpoints, 1,argv+1);argc--;argv++;} + else if (!strcmp(f,"-y") &&argc>0) {slot_setfloatarg(&x->ypoints, 1,argv+1);argc--;argv++;} + else if (!strcmp(f,"-w") &&argc>0) {slot_setfloatarg(&x->wpoints, 1,argv+1);argc--;argv++;} + else break; + } + FIELDSET(array,data,1); + FIELDSET(float,outlinecolor,0); + FIELDSET(float,width,1); + FIELDSET(float,xloc,1); + FIELDSET(float,yloc,1); + FIELDSET(float,xinc,1); + FIELDSET(float,style,defstyle); + return x; +} + +void plot_float(t_plot *x, t_floatarg f) { + int viswas; + if (x->vis.type != A_FLOAT || x->vis.var) {error("global vis/invis for a template with variable visibility"); return;} + viswas = x->vis.f!=0; + if ((f!=0 && viswas) || (f==0 && !viswas)) return; + canvas_redrawallfortemplatecanvas(x->canvas, 2); + slot_setfloat_const(&x->vis, f!=0); + canvas_redrawallfortemplatecanvas(x->canvas, 1); +} + +/* get everything we'll need from the owner template of the array being + plotted. Not used for garrays, but see below */ +static int plot_readownertemplate(t_plot *x, t_word *data, t_template *ownertemplate, +t_symbol **elemtemplatesymp, t_array **arrayp, float *linewidthp, float *xlocp, float *xincp, float *ylocp, +float *stylep, float *visp, float *scalarvisp) { + int arrayonset, type; + t_symbol *elemtemplatesym; + t_array *array; + if (x->data.type != A_ARRAY || !x->data.var) {error("needs an array field"); return -1;} + if (!template_find_field(ownertemplate, x->data.varsym, &arrayonset, &type, &elemtemplatesym)) { + error("%s: no such field", x->data.varsym->name); + return -1; + } + if (type != DT_ARRAY) {error("%s: not an array", x->data.varsym->name); return -1;} + array = *(t_array **)(((char *)data) + arrayonset); + *linewidthp = slot_getfloat(&x->width, ownertemplate, data, 1); + *xlocp = slot_getfloat(&x->xloc, ownertemplate, data, 1); + *xincp = slot_getfloat(&x->xinc, ownertemplate, data, 1); + *ylocp = slot_getfloat(&x->yloc, ownertemplate, data, 1); + *stylep = slot_getfloat(&x->style, ownertemplate, data, 1); + *visp = slot_getfloat(&x->vis, ownertemplate, data, 1); + *scalarvisp = slot_getfloat(&x->scalarvis, ownertemplate, data, 1); + *elemtemplatesymp = elemtemplatesym; + *arrayp = array; + return 0; +} + +/* get everything else you could possibly need about a plot, + either for plot's own purposes or for plotting a "garray" */ +static int array_getfields(t_symbol *elemtemplatesym, t_canvas **elemtemplatecanvasp, +t_template **elemtemplatep, int *elemsizep, t_slot *xslot, t_slot *yslot, t_slot *wslot, +int *xonsetp, int *yonsetp, int *wonsetp) { + int type; + t_symbol *dummy, *varname; + t_canvas *elemtemplatecanvas = 0; + /* the "float" template is special in not having to have a canvas; + template_findbyname is hardwired to return a predefined template. */ + t_template *elemtemplate = template_findbyname(elemtemplatesym); + if (!elemtemplate) {error("%s: no such template", elemtemplatesym->name); return -1;} + if (!(elemtemplatesym==&s_float || (elemtemplatecanvas = template_findcanvas(elemtemplate)))) { + error("%s: no canvas for this template", elemtemplatesym->name); + return -1; + } + *elemtemplatecanvasp = elemtemplatecanvas; + *elemtemplatep = elemtemplate; + *elemsizep = elemtemplate->n * sizeof(t_word); +#define FOO(f,name,onset) \ + varname = f && f->var ? f->varsym : gensym(name); \ + if (!template_find_field(elemtemplate,varname,&onset,&type,&dummy) || type!=DT_FLOAT) onset=-1; + FOO(yslot,"y",*yonsetp) + FOO(xslot,"x",*xonsetp) + FOO(wslot,"w",*wonsetp) +#undef FOO + return 0; +} + +static void plot_vis(t_gobj *z, t_canvas *canvas, t_word *data, t_template *t, float basex, float basey, int tovis) { + t_plot *x = (t_plot *)z; + int elemsize, yonset, wonset, xonset; + t_canvas *elemtemplatecanvas; + t_template *elemtemplate; + t_symbol *elemtemplatesym; + float linewidth, xloc, xinc, yloc, style, xsum, yval, vis, scalarvis; + t_array *array; + if (plot_readownertemplate(x, data, t, &elemtemplatesym, &array, &linewidth, &xloc, &xinc, &yloc, &style, &vis, &scalarvis)) return; + t_slot *xslot = &x->xpoints, *yslot = &x->ypoints, *wslot = &x->wpoints; + if (!vis) return; + if (array_getfields(elemtemplatesym, &elemtemplatecanvas, &elemtemplate, &elemsize, + xslot, yslot, wslot, &xonset, &yonset, &wonset)) return; + int nelem = array->n; + char *elem = (char *)array->vec; + if (tovis) { + if (style == PLOTSTYLE_POINTS) { + float minyval = 1e20, maxyval = -1e20; + int ndrawn = 0; + xsum = basex + xloc; + for (int i=0; i= 0) { + float usexloc = basex + xloc + *(float *)((elem + elemsize*i) + xonset); + ixpix = canvas_xtopixels(canvas, slot_cvttocoord(xslot, usexloc)); + inextx = ixpix + 2; + } else { + ixpix = canvas_xtopixels(canvas, slot_cvttocoord(xslot, xsum)); xsum += xinc; + inextx = canvas_xtopixels(canvas, slot_cvttocoord(xslot, xsum)); + } + yval = yonset>=0 ? yloc + *(float *)((elem + elemsize*i) + yonset) : 0; + if (yval > maxyval) maxyval = yval; + if (yval < minyval) minyval = yval; + if (i == nelem-1 || inextx != ixpix) { + sys_vgui(".x%lx.c create rectangle %d %d %d %d -fill black -width 0 -tags plot%lx\n", + (long)canvas_getcanvas(canvas), ixpix, + (int) canvas_ytopixels(canvas, basey + slot_cvttocoord(yslot, minyval)), inextx, + (int)(canvas_ytopixels(canvas, basey + slot_cvttocoord(yslot, maxyval))+linewidth), + (long)data); + ndrawn++; + minyval = 1e20; + maxyval = -1e20; + } + if (ndrawn > 2000 || ixpix >= 3000) break; + } + } else { + char outline[20]; + int lastpixel = -1, ndrawn = 0; + float yval = 0, wval = 0; + int ixpix = 0; + /* draw the trace */ + numbertocolor((int)slot_getfloat(&x->outlinecolor, t, data, 1), outline); + if (wonset >= 0) { + /* found "w" field which controls linewidth. The trace is a filled polygon with 2n points. */ + //sys_vgui(".x%lx.c create polygon \\\n", (long)canvas_getcanvas(canvas)); + xsum = xloc; + for (int i=0; i=0 ? xloc+*(float *)(elem+elemsize*i+xonset) : (xsum+=xinc); + float yval = yonset>=0 ? *(float *)(elem+elemsize*i+yonset) : 0; + wval = *(float *)(elem+elemsize*i+wonset); + float xpix = canvas_xtopixels(canvas, basex + slot_cvttocoord(xslot, usexloc)); + ixpix = (int)roundf(xpix); + if (xonset >= 0 || ixpix != lastpixel) { + sys_vgui("%d %f \\\n", ixpix, canvas_ytopixels(canvas, + basey + slot_cvttocoord(yslot,yval+yloc) + - slot_cvttocoord(wslot,wval))); + ndrawn++; + } + lastpixel = ixpix; + if (ndrawn >= 1000) goto ouch; + } + lastpixel = -1; + for (int i=nelem-1; i>=0; i--) { + float usexloc = xonset>=0 ? xloc+*(float *)(elem+elemsize*i+xonset) : (xsum-=xinc); + float yval = yonset>=0 ? *(float *)(elem+elemsize*i+yonset) : 0; + wval = *(float *)((elem + elemsize*i) + wonset); + float xpix = canvas_xtopixels(canvas, basex + slot_cvttocoord(xslot, usexloc)); + ixpix = (int)roundf(xpix); + if (xonset >= 0 || ixpix != lastpixel) { + sys_vgui("%d %f \\\n", ixpix, canvas_ytopixels(canvas, + basey + yloc + slot_cvttocoord(yslot, yval) + slot_cvttocoord(wslot, wval))); + ndrawn++; + } + lastpixel = ixpix; + if (ndrawn >= 1000) goto ouch; + } + /* TK will complain if there aren't at least 3 points. There should be at least two already. */ + if (ndrawn < 4) { + int y = int(slot_cvttocoord(yslot, yval)); + int w = int(slot_cvttocoord(wslot, wval)); + sys_vgui("%d %f %d %f\\\n", + ixpix + 10, canvas_ytopixels(canvas, basey + yloc + y + w), + ixpix + 10, canvas_ytopixels(canvas, basey + yloc + y - w)); + } + ouch: + sys_vgui(" -width 1 -fill %s -outline %s\\\n", outline, outline); + if (style == PLOTSTYLE_BEZ) sys_vgui("-smooth 1\\\n"); + sys_vgui("-tags plot%lx\n", (long)data); + } else if (linewidth > 0) { + /* no "w" field. If the linewidth is positive, draw a segmented line with the + requested width; otherwise don't draw the trace at all. */ + sys_vgui(".x%lx.c create line \\\n", (long)canvas_getcanvas(canvas)); + xsum = xloc; + for (int i=0; i=0 ? xloc+*(float *)(elem+elemsize*i+xonset) : xsum; if (xonset>=0) xsum+=(int)xinc; + float yval = yonset>=0 ? *(float *)(elem+elemsize*i+yonset) : 0; + float xpix = canvas_xtopixels(canvas, basex + slot_cvttocoord(xslot, usexloc)); + ixpix = (int)roundf(xpix); + if (xonset >= 0 || ixpix != lastpixel) { + sys_vgui("%d %f \\\n", ixpix, canvas_ytopixels(canvas, basey + yloc + slot_cvttocoord(yslot, yval))); + ndrawn++; + } + lastpixel = ixpix; + if (ndrawn >= 1000) break; + } + /* TK will complain if there aren't at least 2 points... */ + if (ndrawn == 0) sys_vgui("0 0 0 0 \\\n"); + else if (ndrawn == 1) sys_vgui("%d %f \\\n", ixpix + 10, + canvas_ytopixels(canvas, basey + yloc + slot_cvttocoord(yslot, yval))); + sys_vgui("-width %f -fill %s -smooth %d -tags plot%lx",linewidth,outline,style==PLOTSTYLE_BEZ,(long)data); + } + } + /* We're done with the outline; now draw all the points. This code is inefficient since + the template has to be searched for drawing instructions for every last point. */ + if (scalarvis != 0) { + int xsum = (int)xloc; + for (int i=0; i=0 ? basex + xloc + *(float *)(elem+elemsize*i+xonset) : basex+xsum; + if (xonset>=0) xsum+=int(xinc); + yval = yonset>=0 ? *(float *)(elem+elemsize*i+yonset) : 0; + //float useyloc = basey + yloc + slot_cvttocoord(yslot, yval); + /*canvas_each(y,elemtemplatecanvas) pd_getparentwidget(y)->w_parentvisfn(y, canvas, + (t_word *)(elem+elemsize*i), elemtemplate, usexloc, useyloc, tovis);*/ + } + } + } else { + /* un-draw the individual points */ + /* if (scalarvis != 0) + for (int i=0; iw_parentvisfn(y, canvas, (t_word *)(elem+elemsize*i), elemtemplate,0,0,0);*/ + /* and then the trace */ + sys_vgui(".x%lx.c delete plot%lx\n", (long)canvas_getcanvas(canvas), (long)data); + } +} + +static int plot_click(t_gobj *z, t_canvas *canvas, t_word *data, t_template *t, t_scalar *sc, +t_array *ap, float basex, float basey, int xpix, int ypix, int shift, int alt, int dbl, int doit) { + t_plot *x = (t_plot *)z; + t_symbol *elemtemplatesym; + float linewidth, xloc, xinc, yloc, style, vis, scalarvis; + t_array *array; + if (plot_readownertemplate(x, data, t, &elemtemplatesym, &array, &linewidth, &xloc, &xinc, + &yloc, &style, &vis, &scalarvis)) return 0; + if (!vis) return 0; + return array_doclick(array, canvas, sc, ap, elemtemplatesym, linewidth, basex + xloc, xinc, + basey + yloc, scalarvis, &x->xpoints, &x->ypoints, &x->wpoints, xpix, ypix, shift, alt, dbl, doit); +} + +/* ---------------- drawnumber: draw a number (or symbol) ---------------- */ +/* drawnumbers draw numeric fields at controllable locations, with controllable color and label. + invocation: (drawnumber|drawsymbol) [-v ] variable x y color label */ + +t_class *drawnumber_class; + +#define DRAW_SYMBOL 1 + +struct t_drawnumber : t_object { + t_slot value, xloc, yloc, color, vis; + t_symbol *label; + int flags; + t_canvas *canvas; +}; + +static void *drawnumber_new(t_symbol *classsym, t_int argc, t_atom *argv) { + t_drawnumber *x = (t_drawnumber *)pd_new(drawnumber_class); + char *classname = classsym->name; + int flags = 0; + if (classname[4] == 's') flags |= DRAW_SYMBOL; + x->flags = flags; + slot_setfloat_const(&x->vis, 1); + x->canvas = canvas_getcurrent(); + while (1) { + t_symbol *firstarg = atom_getsymbolarg(0, argc, argv); + if (!strcmp(firstarg->name,"-v") && argc > 1) { + slot_setfloatarg(&x->vis, 1, argv+1); + argc -= 2; argv += 2; + } else break; + } + if (flags & DRAW_SYMBOL) { + if (argc) slot_setsymbolarg( &x->value,argc--,argv++); + else slot_setsymbol_const(&x->value,&s_); + } else FIELDSET(float,value, 0); + FIELDSET(float,xloc,0); + FIELDSET(float,yloc,0); + FIELDSET(float,color,1); + if (argc) x->label = atom_getsymbolarg(0, argc, argv); else x->label = &s_; + return x; +} + +void drawnumber_float(t_drawnumber *x, t_floatarg f) { + if (x->vis.type != A_FLOAT || x->vis.var) {error("global vis/invis for a template with variable visibility"); return;} + int viswas = x->vis.f!=0; + if ((f != 0 && viswas) || (f == 0 && !viswas)) return; + canvas_redrawallfortemplatecanvas(x->canvas, 2); + slot_setfloat_const(&x->vis, f!=0); + canvas_redrawallfortemplatecanvas(x->canvas, 1); +} + +static void drawnumber_vis(t_gobj *z, t_canvas *canvas, t_word *data, t_template *t, float basex, float basey, int vis) { + t_drawnumber *x = (t_drawnumber *)z; + if (!slot_getfloat(&x->vis, t, data, 0)) return; + if (vis) { + t_atom at; + int xloc = canvas_xtopixels(canvas, basex + slot_getcoord(&x->xloc, t, data, 0)); + int yloc = canvas_ytopixels(canvas, basey + slot_getcoord(&x->yloc, t, data, 0)); + char colorstring[20]; + numbertocolor((int)slot_getfloat(&x->color, t, data, 1), colorstring); + if (x->flags & DRAW_SYMBOL) SETSYMBOL(&at, slot_getsymbol(&x->value, t, data, 0)); + else SETFLOAT( &at, slot_getfloat( &x->value, t, data, 0)); + std::ostringstream buf; + buf << x->label->name; + atom_ostream(&at,buf); + sys_vgui(".x%lx.c create text %d %d -anchor nw -fill %s -text {%s}", + (long)canvas_getcanvas(canvas), xloc, yloc, colorstring, buf.str().data()); + sys_vgui(" -font {Courier 42} -tags drawnumber%lx\n", (long)data); /*sys_hostfontsize(canvas_getfont(canvas))*/ + } else sys_vgui(".x%lx.c delete drawnumber%lx\n", (long)canvas_getcanvas(canvas), (long)data); +} + +static struct { + float ycumulative; + t_canvas *canvas; + t_scalar *scalar; + t_array *array; + t_word *wp; + t_template *t; + t_gpointer gpointer; + int symbol; + int firstkey; +} dn; + +/* LATER protect against the template changing or the scalar disappearing probably by attaching a gpointer here ... */ +#if 0 +static void drawnumber_motion(void *z, t_floatarg dx, t_floatarg dy) { + t_drawnumber *x = (t_drawnumber *)z; + t_slot *f = &x->value; + t_atom at; + if (!gpointer_check(&dn.gpointer, 0)) {post("drawnumber_motion: scalar disappeared"); return;} + if (dn.symbol) {post("drawnumber_motion: symbol"); return;} + dn.ycumulative -= dy; + template_setfloat(dn.t, f->varsym, dn.wp, dn.ycumulative, 1); + if (dn.scalar) gobj_changed(dn.scalar); else gobj_changed(dn.array); +} +#endif + +static void drawnumber_key(void *z, t_floatarg fkey) { + //t_drawnumber *x = (t_drawnumber *)z; + int key = (int)fkey; + if (!gpointer_check(&dn.gpointer, 0)) { + post("drawnumber_motion: scalar disappeared"); + return; + } + if (key == 0) return; + if (dn.symbol) { + /* key entry for a symbol field... has to be rewritten in Tcl similarly to TextBox for edition of [drawsymbol] */ + // template_getsymbol(dn.t, f->varsym, dn.wp, 1)->name; + } else { + /* key entry for a numeric field... same here... [drawnumber] */ + //t_slot *f = &x->value; + //float newf; + //if (sscanf(sbuf, "%g", &newf) < 1) newf = 0; + //template_setfloat(dn.t, f->varsym, dn.wp, newf, 1); + //t_atom at; + //if (dn.scalar) template_notifyforscalar(dn.t, dn.canvas, dn.scalar, gensym("change"), 1, &at); + //if (dn.scalar) gobj_changed(dn.scalar,0); else gobj_changed(dn.array,0); + } +} + +#if 0 +static int drawnumber_click(t_gobj *z, t_canvas *canvas, t_word *data, t_template *t, t_scalar *sc, t_array *ap, float basex, float basey, int xpix, int ypix, int shift, int alt, int dbl, int doit) { + t_drawnumber *x = (t_drawnumber *)z; + int x1, y1, x2, y2; + drawnumber_getrect(z, canvas, data, t, basex, basey, &x1, &y1, &x2, &y2); + if (xpix >= x1 && xpix <= x2 && ypix >= y1 && ypix <= y2 + && x->value.var && slot_getfloat(&x->vis, t, data, 0)) { + if (doit) { + dn.canvas = canvas; + dn.wp = data; + dn.t = t; + dn.scalar = sc; + dn.array = ap; + dn.firstkey = 1; + dn.ycumulative = slot_getfloat(&x->value, t, data, 0); + dn.symbol = (x->flags & DRAW_SYMBOL)!=0; + if (dn.scalar) + gpointer_setcanvas(&dn.gpointer, dn.canvas, dn.scalar); + else gpointer_setarray(&dn.gpointer, dn.array, dn.wp); + /* canvas_grab(glist, z, drawnumber_motion, drawnumber_key, xpix, ypix); */ + } + return 1; + } else return 0; +} +#endif + +static void drawnumber_free(t_drawnumber *x) {} + +static void g_template_setup() { + template_class = class_new2("template",0,template_free,sizeof(t_template),CLASS_PD,""); + class_addmethod2(pd_canvasmaker._class, template_usetemplate, "struct", "*"); + gtemplate_class = class_new2("struct",gtemplate_new,gtemplate_free,sizeof(t_gtemplate),CLASS_NOINLET,"*"); + class_addcreator2("template",gtemplate_new_old,"*"); + + curve_class = class_new2("drawpolygon",curve_new,0,sizeof(t_curve),0,"*"); + class_setdrawcommand(curve_class); + class_addcreator2("drawcurve", curve_new,"*"); + class_addcreator2("filledpolygon",curve_new,"*"); + class_addcreator2("filledcurve", curve_new,"*"); + class_addfloat(curve_class, curve_float); + plot_class = class_new2("plot",plot_new,0,sizeof(t_plot),0,"*"); + class_setdrawcommand(plot_class); + class_addfloat(plot_class, plot_float); + drawnumber_class = class_new2("drawnumber",drawnumber_new,drawnumber_free,sizeof(t_drawnumber),0,"*"); + class_setdrawcommand(drawnumber_class); + class_addfloat(drawnumber_class, drawnumber_float); + class_addcreator2("drawsymbol",drawnumber_new,"*"); +} + +/* ------------- gpointers - safe pointing --------------- */ + +/* call this to verify that a pointer is fresh, i.e., that it either points to real data or to the head of a list, + and that in either case the object hasn't disappeared since this pointer was generated. Unless "headok" is set, + the routine also fails for the head of a list. */ +int gpointer_check(const t_gpointer *gp, int headok) { + if (!gp->o) return 0; + if (gp->o->_class == array_class) return 1; + return headok || gp->scalar; +} + +/* get the template for the object pointer to. Assumes we've already checked freshness. Returns 0 if head of list. */ +static t_symbol *gpointer_gettemplatesym(const t_gpointer *gp) { + if (gp->o->_class == array_class) return gp->array->templatesym; + t_scalar *sc = gp->scalar; + return sc ? sc->t : 0; +} + +/* copy a pointer to another, assuming the second one hasn't yet been initialized. New gpointers should be initialized + either by this routine or by gpointer_init below. */ +void gpointer_copy(const t_gpointer *gpfrom, t_gpointer *gpto) { + *gpto = *gpfrom; + if (gpto && gpto->o) gpto->o->refcount++; else bug("gpointer_copy"); +} + +void gpointer_unset(t_gpointer *gp) { + if (gp->scalar) { + if (!--gp->o->refcount) pd_free(gp->o); + gp->o=0; + gp->scalar=0; + } +} + +static void gpointer_setcanvas(t_gpointer *gp, t_canvas *o, t_scalar *x) { + gpointer_unset(gp); + gp->o=o; gp->scalar = x; gp->o->refcount++; +} +static void gpointer_setarray(t_gpointer *gp, t_array *o, t_word *w) { + gpointer_unset(gp); + gp->o=o; gp->w = w; gp->o->refcount++; +} + +void gpointer_init(t_gpointer *gp) { + gp->o = 0; + gp->scalar = 0; +} + +/* ---------------------- pointers ----------------------------- */ + +static t_class *ptrobj_class; + +struct t_typedout { + t_symbol *type; + t_outlet *outlet; +}; + +struct t_ptrobj : t_object { + t_gpointer gp; + t_typedout *typedout; + int ntypedout; + t_outlet *otherout; + t_outlet *bangout; +}; + +static void *ptrobj_new(t_symbol *classname, int argc, t_atom *argv) { + t_ptrobj *x = (t_ptrobj *)pd_new(ptrobj_class); + t_typedout *to; + gpointer_init(&x->gp); + x->typedout = to = (t_typedout *)getbytes(argc * sizeof (*to)); + x->ntypedout = argc; + for (int n=argc; n--; to++) { + to->outlet = outlet_new(x,&s_pointer); + to->type = canvas_makebindsym(atom_getsymbol(argv++)); + } + x->otherout = outlet_new(x,&s_pointer); + x->bangout = outlet_new(x,&s_bang); + pointerinlet_new(x,&x->gp); + return x; +} + +static void ptrobj_traverse(t_ptrobj *x, t_symbol *s) { + t_canvas *canvas = (t_canvas *)pd_findbyclass(s, canvas_class); + if (canvas) gpointer_setcanvas(&x->gp, canvas, 0); + else error("list '%s' not found", s->name); +} + +static void ptrobj_vnext(t_ptrobj *x, float f) { + t_gpointer *gp = &x->gp; + int wantselected = f!=0; + if (!gp->o) {error("next: no current pointer"); return;} + if (gp->o->_class == array_class) {error("next: lists only, not arrays"); return;} + t_canvas *canvas = gp->canvas; + if (wantselected) {error("next: next-selected unsupported in desiredata"); return;} + /* if (wantselected && !canvas_isvisible(canvas)) {error("next: next-selected only works for a visible window"); return;} */ + t_gobj *gobj = gp->scalar; + if (!gobj) gobj = canvas->boxes->first(); + else gobj = gobj->next(); + while (gobj && (gobj->_class != scalar_class || wantselected)) gobj = gobj->next(); + if (gobj) { + t_typedout *to = x->typedout; + t_scalar *sc = (t_scalar *)gobj; + gp->scalar = sc; + for (int n = x->ntypedout; n--; to++) + if (to->type == sc->t) {outlet_pointer(to->outlet, &x->gp); return;} + outlet_pointer(x->otherout, &x->gp); + } else { + gpointer_unset(gp); + outlet_bang(x->bangout); + } +} + +static void ptrobj_next(t_ptrobj *x) {ptrobj_vnext(x, 0);} + +static void ptrobj_sendwindow(t_ptrobj *x, t_symbol *s, int argc, t_atom *argv) { + if (!gpointer_check(&x->gp, 1)) {error("bang: empty pointer"); return;} + t_canvas *canvas = gpointer_getcanvas(&x->gp); + if (argc && argv->a_type == A_SYMBOL) pd_typedmess(canvas_getcanvas(canvas), argv->a_symbol, argc-1, argv+1); + else error("send-window: no message?"); +} + +static void ptrobj_bang(t_ptrobj *x) { + t_typedout *to = x->typedout; + if (!gpointer_check(&x->gp, 1)) {error("bang: empty pointer"); return;} + t_symbol *templatesym = gpointer_gettemplatesym(&x->gp); + for (int n=x->ntypedout; n--; to++) + if (to->type == templatesym) {outlet_pointer(to->outlet, &x->gp); return;} + outlet_pointer(x->otherout, &x->gp); +} + +static void ptrobj_pointer(t_ptrobj *x, t_gpointer *gp) { + gpointer_unset(&x->gp); + gpointer_copy(gp, &x->gp); + ptrobj_bang(x); +} + +static void ptrobj_rewind(t_ptrobj *x) { + if (!gpointer_check(&x->gp, 1)) {error("rewind: empty pointer"); return;} + if (x->gp.o->_class == array_class) {error("rewind: sorry, unavailable for arrays"); return;} + gpointer_setcanvas(&x->gp, x->gp.canvas, 0); + ptrobj_bang(x); +} + +static void ptrobj_free(t_ptrobj *x) { + free(x->typedout); + gpointer_unset(&x->gp); +} + +/* ---------------------- get ----------------------------- */ + +static t_class *get_class; +struct t_getvariable { + t_symbol *sym; + t_outlet *outlet; +}; +struct t_get : t_object { + t_symbol *templatesym; + int nout; + t_getvariable *variables; +}; +static void *get_new(t_symbol *why, int argc, t_atom *argv) { + t_get *x = (t_get *)pd_new(get_class); + x->templatesym = canvas_makebindsym(atom_getsymbolarg(0, argc, argv)); + if (argc) argc--, argv++; + t_getvariable *sp = x->variables = (t_getvariable *)getbytes(argc * sizeof (*x->variables)); + x->nout = argc; + for (int i=0; i < argc; i++, sp++) { + sp->sym = atom_getsymbolarg(i, argc, argv); + sp->outlet = outlet_new(x,0); + /* LATER connect with the template and set the outlet's type + correctly. We can't yet guarantee that the template is there + before we hit this routine. */ + } + return x; +} + +static void get_pointer(t_get *x, t_gpointer *gp) { + int nitems = x->nout; + t_template *t = template_findbyname(x->templatesym); + t_getvariable *vp; + TEMPLATE_CHECK(x->templatesym,) + if (!gpointer_check(gp, 0)) {error("stale or empty pointer"); return;} + t_word *vec = gpointer_word(gp); + vp = x->variables + nitems-1; + for (int i=nitems-1; i>=0; i--, vp--) { + int onset, type; + t_symbol *arraytype; + if (template_find_field(t, vp->sym, &onset, &type, &arraytype)) { + if (type == DT_FLOAT) outlet_float(vp->outlet, *(t_float *)(((char *)vec) + onset)); + else if (type == DT_SYMBOL) outlet_symbol(vp->outlet, *(t_symbol **)(((char *)vec) + onset)); + else error("%s.%s is not a number or symbol", t->sym->name, vp->sym->name); + } else error("%s.%s: no such field", t->sym->name, vp->sym->name); + } +} + +static void get_free(t_get *x) {free(x->variables);} + +/* ---------------------- set ----------------------------- */ + +static t_class *set_class; + +struct t_setvariable { + t_symbol *sym; + union word w; +}; + +struct t_set : t_object { + t_gpointer gp; + t_symbol *templatesym; + int nin; + int issymbol; + t_setvariable *variables; +}; + +static void *set_new(t_symbol *why, int argc, t_atom *argv) { + t_set *x = (t_set *)pd_new(set_class); + if (argc && (argv[0].a_type == A_SYMBOL) && !strcmp(argv[0].a_symbol->name,"-symbol")) { + x->issymbol = 1; + argc--; + argv++; + } + else x->issymbol = 0; + x->templatesym = canvas_makebindsym(atom_getsymbolarg(0, argc, argv)); + if (argc) argc--, argv++; + t_setvariable *sp = x->variables = (t_setvariable *)getbytes(argc * sizeof (*x->variables)); + x->nin = argc; + if (argc) { + for (int i=0; isym = atom_getsymbolarg(i, argc, argv); + if (x->issymbol) sp->w.w_symbol = &s_; + else sp->w.w_float = 0; + if (i) { + if (x->issymbol) symbolinlet_new(x,&sp->w.w_symbol); + else floatinlet_new(x, &sp->w.w_float); + } + } + } + pointerinlet_new(x,&x->gp); + gpointer_init(&x->gp); + return x; +} + +static void set_bang(t_set *x) { + int nitems = x->nin; + t_template *t = template_findbyname(x->templatesym); + t_gpointer *gp = &x->gp; + TEMPLATE_CHECK(x->templatesym,) + if (!gpointer_check(gp, 0)) {error("empty pointer"); return;} + if (gpointer_gettemplatesym(gp) != x->templatesym) { + error("%s: got wrong template (%s)",x->templatesym->name,gpointer_gettemplatesym(gp)->name); + return; + } + if (!nitems) return; + t_word *vec = gpointer_word(gp); + t_setvariable *vp=x->variables; + if (x->issymbol) for (int i=0; isym, vec, vp->w.w_symbol, 1); + else for (int i=0; isym, vec, vp->w.w_float, 1); + scalar_redraw(gp->scalar, gpointer_getcanvas(gp)); /* but ought to use owner_array->gp.scalar */ +} + +static void set_float(t_set *x, t_float f) { + if (x->nin && !x->issymbol) {x->variables[0].w.w_float = f; set_bang(x);} + else error("type mismatch or no field specified"); +} +static void set_symbol(t_set *x, t_symbol *s) { + if (x->nin && x->issymbol) {x->variables[0].w.w_symbol = s; set_bang(x);} + else error("type mismatch or no field specified"); +} + +static void set_free(t_set *x) { + free(x->variables); + gpointer_unset(&x->gp); +} + +/* ---------------------- elem ----------------------------- */ + +static t_class *elem_class; + +struct t_elem : t_object { + t_symbol *templatesym; + t_symbol *fieldsym; + t_gpointer gp; + t_gpointer gparent; +}; + +static void *elem_new(t_symbol *templatesym, t_symbol *fieldsym) { + t_elem *x = (t_elem *)pd_new(elem_class); + x->templatesym = canvas_makebindsym(templatesym); + x->fieldsym = fieldsym; + gpointer_init(&x->gp); + gpointer_init(&x->gparent); + pointerinlet_new(x,&x->gparent); + outlet_new(x,&s_pointer); + return x; +} + +static void elem_float(t_elem *x, t_float f) { + int indx = (int)f, nitems, onset; + t_symbol *fieldsym = x->fieldsym, *elemtemplatesym; + t_template *t = template_findbyname(x->templatesym); + t_template *elemtemplate; + t_gpointer *gparent = &x->gparent; + t_array *array; + int elemsize, type; + if (!gpointer_check(gparent, 0)) {error("empty pointer"); return;} + if (gpointer_gettemplatesym(gparent) != x->templatesym) { + error("%s: got wrong template (%s)", x->templatesym->name, gpointer_gettemplatesym(gparent)->name); + return; + } + t_word *w = gpointer_word(gparent); + TEMPLATE_CHECK(x->templatesym,) + if (!template_find_field(t, fieldsym, &onset, &type, &elemtemplatesym)) { + error("couldn't find array field %s", fieldsym->name); + return; + } + if (type != DT_ARRAY) {error("element: field %s not of type array", fieldsym->name); return;} + if (!(elemtemplate = template_findbyname(elemtemplatesym))) { + error("couldn't find field template %s", elemtemplatesym->name); + return; + } + elemsize = elemtemplate->n * sizeof(t_word); + array = *(t_array **)(((char *)w) + onset); + nitems = array->n; + if (indx < 0) indx = 0; + if (indx >= nitems) indx = nitems-1; + gpointer_setarray(&x->gp, array, (t_word *)&array->vec[indx*elemsize]); + outlet_pointer(x->outlet, &x->gp); +} + +static void elem_free(t_elem *x, t_gpointer *gp) { + gpointer_unset(&x->gp); + gpointer_unset(&x->gparent); +} + +/* ---------------------- getsize ----------------------------- */ + +static t_class *getsize_class; + +struct t_getsize : t_object { + t_symbol *templatesym; + t_symbol *fieldsym; +}; + +static void *getsize_new(t_symbol *templatesym, t_symbol *fieldsym) { + t_getsize *x = (t_getsize *)pd_new(getsize_class); + x->templatesym = canvas_makebindsym(templatesym); + x->fieldsym = fieldsym; + outlet_new(x,&s_float); + return x; +} + +static void getsize_pointer(t_getsize *x, t_gpointer *gp) { + int onset, type; + t_symbol *fieldsym = x->fieldsym, *elemtemplatesym; + t_template *t = template_findbyname(x->templatesym); + TEMPLATE_CHECK(x->templatesym,) + if (!template_find_field(t, fieldsym, &onset, &type, &elemtemplatesym)) { + error("couldn't find array field %s", fieldsym->name); + return; + } + if (type != DT_ARRAY) {error("field %s not of type array", fieldsym->name); return;} + if (!gpointer_check(gp, 0)) {error("stale or empty pointer"); return;} + if (gpointer_gettemplatesym(gp) != x->templatesym) { + error("%s: got wrong template (%s)", x->templatesym->name, gpointer_gettemplatesym(gp)->name); + return; + } + t_word *w = gpointer_word(gp); + t_array *array = *(t_array **)(((char *)w) + onset); + outlet_float(x->outlet, (float)(array->n)); +} + +/* ---------------------- setsize ----------------------------- */ + +static t_class *setsize_class; + +struct t_setsize : t_object { + t_symbol *templatesym; + t_symbol *fieldsym; + t_gpointer gp; +}; + +static void *setsize_new(t_symbol *templatesym, t_symbol *fieldsym, t_floatarg newsize) { + t_setsize *x = (t_setsize *)pd_new(setsize_class); + x->templatesym = canvas_makebindsym(templatesym); + x->fieldsym = fieldsym; + gpointer_init(&x->gp); + pointerinlet_new(x,&x->gp); + return x; +} + +static void setsize_float(t_setsize *x, t_float f) { + int onset, type; + t_template *t = template_findbyname(x->templatesym); + int newsize = (int)f; + t_gpointer *gp = &x->gp; + if (!gpointer_check(&x->gp, 0)) {error("empty pointer"); return;} + if (gpointer_gettemplatesym(&x->gp) != x->templatesym) { + error("%s: got wrong template (%s)", x->templatesym->name, gpointer_gettemplatesym(&x->gp)->name); + return; + } + t_word *w = gpointer_word(gp); + TEMPLATE_CHECK(x->templatesym,) + t_symbol *elemtemplatesym; + if (!template_find_field(t, x->fieldsym, &onset, &type, &elemtemplatesym)) { + error("couldn't find array field %s", x->fieldsym->name); + return; + } + if (type != DT_ARRAY) {error("field %s not of type array", x->fieldsym->name); return;} + t_template *elemtemplate = template_findbyname(elemtemplatesym); + if (!elemtemplate) { + error("couldn't find field template %s", elemtemplatesym->name); + return; + } + int elemsize = elemtemplate->n * sizeof(t_word); + t_array *array = *(t_array **)(((char *)w) + onset); + if (elemsize != array->elemsize) bug("setsize_gpointer"); + int nitems = array->n; + if (newsize < 1) newsize = 1; + if (newsize == nitems) return; + + /* here there was something to erase the array before resizing it */ + + /* now do the resizing and, if growing, initialize new scalars */ + array->vec = (char *)resizealignedbytes(array->vec, elemsize * nitems, elemsize * newsize); + array->n = newsize; + if (newsize > nitems) { + char *newelem = &array->vec[nitems*elemsize]; + int nnew = newsize - nitems; + while (nnew--) { + word_init((t_word *)newelem, elemtemplate, gp); + newelem += elemsize; + } + } + gobj_changed(gpointer_getscalar(gp),0); +} + +static void setsize_free(t_setsize *x) {gpointer_unset(&x->gp);} + +/* ---------------------- append ----------------------------- */ + +static t_class *append_class; + +struct t_appendvariable { + t_symbol *sym; + t_float f; +}; + +struct t_append : t_object { + t_gpointer gp; + t_symbol *templatesym; + int nin; + t_appendvariable *variables; +}; + +static void *append_new(t_symbol *why, int argc, t_atom *argv) { + t_append *x = (t_append *)pd_new(append_class); + x->templatesym = canvas_makebindsym(atom_getsymbolarg(0, argc, argv)); + if (argc) argc--, argv++; + x->variables = (t_appendvariable *)getbytes(argc * sizeof (*x->variables)); + x->nin = argc; + if (argc) { + t_appendvariable *sp = x->variables; + for (int i=0; isym = atom_getsymbolarg(i, argc, argv); + sp->f = 0; + if (i) floatinlet_new(x,&sp->f); + } + } + pointerinlet_new(x,&x->gp); + outlet_new(x,&s_pointer); + gpointer_init(&x->gp); + return x; +} + +static void append_float(t_append *x, t_float f) { + int nitems = x->nin; + t_template *t = template_findbyname(x->templatesym); + t_gpointer *gp = &x->gp; + TEMPLATE_CHECK(x->templatesym,) + if (!gp->o) {error("no current pointer"); return;} + if (gp->o->_class == array_class) {error("lists only, not arrays"); return;} + t_canvas *canvas = gp->canvas; + if (!nitems) return; + x->variables[0].f = f; + t_scalar *sc = scalar_new(canvas,x->templatesym); + if (!sc) {error("%s: couldn't create scalar", x->templatesym->name); return;} + canvas->boxes->add(sc); + gobj_changed(sc,0); + gp->scalar = sc; + t_word *vec = sc->v; + t_appendvariable *vp=x->variables; + for (int i=0; isym, vec, vp->f, 1); + scalar_redraw(sc, canvas); + outlet_pointer(x->outlet, gp); +} + +static void append_free(t_append *x) { + free(x->variables); + gpointer_unset(&x->gp); +} + +/* ---------------------- sublist ----------------------------- */ + +static t_class *sublist_class; + +struct t_sublist : t_object { + t_symbol *templatesym; + t_symbol *fieldsym; + t_gpointer gp; +}; + +static void *sublist_new(t_symbol *templatesym, t_symbol *fieldsym) { + t_sublist *x = (t_sublist *)pd_new(sublist_class); + x->templatesym = canvas_makebindsym(templatesym); + x->fieldsym = fieldsym; + gpointer_init(&x->gp); + outlet_new(x,&s_pointer); + return x; +} + +static void sublist_pointer(t_sublist *x, t_gpointer *gp) { + t_symbol *dummy; + t_template *t = template_findbyname(x->templatesym); + int onset, type; + TEMPLATE_CHECK(x->templatesym,) + if (!gpointer_check(gp, 0)) {error("stale or empty pointer"); return;} + if (!template_find_field(t, x->fieldsym, &onset, &type, &dummy)) { + error("couldn't find field %s", x->fieldsym->name); + return; + } + if (type != DT_CANVAS) {error("field %s not of type list", x->fieldsym->name); return;} + t_word *w = gpointer_word(gp); + gpointer_setcanvas(&x->gp, *(t_canvas **)(((char *)w) + onset), 0); + outlet_pointer(x->outlet, &x->gp); +} + +static void sublist_free(t_sublist *x, t_gpointer *gp) {gpointer_unset(&x->gp);} + +static void g_traversal_setup() { + t_class *c = ptrobj_class = class_new2("pointer",ptrobj_new,ptrobj_free,sizeof(t_ptrobj),0,"*"); + class_addmethod2(c, ptrobj_traverse,"traverse", "s"); + class_addmethod2(c, ptrobj_next,"next",""); + class_addmethod2(c, ptrobj_vnext,"vnext","F"); + class_addmethod2(c, ptrobj_sendwindow,"send-window","*"); + class_addmethod2(c, ptrobj_rewind, "rewind",""); + class_addpointer(c, ptrobj_pointer); + class_addbang(c, ptrobj_bang); + get_class = class_new2("get",get_new,get_free,sizeof(t_get),0,"*"); + class_addpointer(get_class, get_pointer); + set_class = class_new2("set",set_new,set_free,sizeof(t_set),0,"*"); + class_addfloat(set_class, set_float); + class_addsymbol(set_class, set_symbol); + class_addbang(set_class, set_bang); + elem_class = class_new2("element",elem_new,elem_free,sizeof(t_elem),0,"SS"); + class_addfloat(elem_class, elem_float); + getsize_class = class_new2("getsize",getsize_new,0,sizeof(t_getsize),0,"SS"); + class_addpointer(getsize_class, getsize_pointer); + setsize_class = class_new2("setsize",setsize_new,setsize_free,sizeof(t_setsize),0,"SSFF"); + class_addfloat(setsize_class, setsize_float); + append_class = class_new2("append",append_new,append_free,sizeof(t_append),0,"*"); + class_addfloat(append_class, append_float); + sublist_class = class_new2("sublist",sublist_new,sublist_free,sizeof(t_sublist),0,"SS"); + class_addpointer(sublist_class, sublist_pointer); +} + +/*EXTERN*/ void canvas_savecontainerto(t_canvas *x, t_binbuf *b); + +struct t_iemgui : t_object { + t_canvas *canvas; + int h,w; + int ldx,ldy; + int isa; /* bit 0: loadinit; bit 20: scale */ + int font_style, fontsize; + int fcol,bcol,lcol; /* foreground, background, label colors */ + t_symbol *snd,*rcv,*lab; /* send, receive, label symbols */ +}; + +struct t_bng : t_iemgui { + int count; + int ftbreak; /* flash time break (ms) */ + int fthold; /* flash time hold (ms) */ +}; + +struct t_slider : t_iemgui { + t_float val; + t_float min,max; + int steady; + int is_log; + int orient; +}; + +struct t_radio : t_iemgui { + int on, on_old; + int change; + int number; + t_atom at[2]; + int orient; + int oldstyle; +}; + +struct t_toggle : t_iemgui { + float on; + float nonzero; +}; + +struct t_cnv : t_iemgui { + t_atom at[3]; + int vis_w, vis_h; +}; + +struct t_dropper : t_iemgui { + t_symbol *s; + t_symbol *ds; +}; + +struct t_vu : t_iemgui { + int led_size; + int peak,rms; + float fp,fr; + int scale; +}; + +struct t_nbx : t_iemgui { + double val; + double min,max; + double k; + char buf[32]; + int log_height; + int change; + int is_log; +}; + +struct t_foo { int argc; t_atom *argv; t_binbuf *b; }; +static int pd_pickle(t_foo *foo, char *fmt, ...); +static int pd_savehead(t_binbuf *b, t_iemgui *x, char *name); + +static t_class *radio_class, *slider_class; +static t_symbol *sym_hdl, *sym_hradio, *sym_vdl, *sym_vradio, *sym_vsl, *sym_vslider; + +t_class *dummy_class; +/*static*/ t_class *text_class; +static t_class *mresp_class; +static t_class *message_class; +static t_class *gatom_class; + +void canvas_text(t_canvas *gl, t_symbol *s, int argc, t_atom *argv) { + t_text *x = (t_text *)pd_new(text_class); + x->binbuf = binbuf_new(); + x->x = atom_getintarg(0, argc, argv); + x->y = atom_getintarg(1, argc, argv); + if (argc > 2) binbuf_restore(x->binbuf, argc-2, argv+2); + canvas_add(gl,x); + pd_set_newest(x); +} + +void canvas_getargs(int *argcp, t_atom **argvp) { + t_canvasenvironment *e = canvas_getenv(canvas_getcurrent()); + *argcp = e->argc; + *argvp = e->argv; +} + +static void canvas_objtext(t_canvas *gl, int xpix, int ypix, t_binbuf *b, int index=-1) { + t_text *x=0; + int argc, n; + t_atom *argv; + char *s; + newest = 0; + binbuf_gettext(b,&s,&n); + pd_pushsym(gl); + canvas_getargs(&argc, &argv); + binbuf_eval(b, &pd_objectmaker, argc, argv); + if (binbuf_getnatom(b)) { + if (!newest) { + char *s = binbuf_gettext2(b); + error("couldn't create %s",s); + free(s); + } else if (!(x = pd_checkobject(newest))) { + char *s = binbuf_gettext2(b); + error("didn't return a patchable object: %s",s); + free(s); + } + } + /* make a "broken object", that is, one that should appear with a dashed contour. */ + if (!x) { + x = (t_text *)pd_new(dummy_class); + pd_set_newest(x); + } + x->binbuf = b; + x->x = xpix; + x->y = ypix; + canvas_add(gl,x,index); + if (x->_class== vinlet_class) canvas_resortinlets(canvas_getcanvas(gl)); + if (x->_class==voutlet_class) canvas_resortoutlets(canvas_getcanvas(gl)); + pd_popsym(gl); +} + +void canvas_obj(t_canvas *gl, t_symbol *s, int argc, t_atom *argv) { + t_binbuf *b = binbuf_new(); + if (argc >= 2) { + binbuf_restore(b, argc-2, argv+2); + canvas_objtext(gl, atom_getintarg(0, argc, argv), atom_getintarg(1, argc, argv), b); + } else canvas_objtext(gl,0,0,b); +} + +void canvas_objfor(t_canvas *gl, t_text *x, int argc, t_atom *argv) { + x->binbuf = binbuf_new(); + x->x = atom_getintarg(0, argc, argv); + x->y = atom_getintarg(1, argc, argv); + if (argc > 2) binbuf_restore(x->binbuf, argc-2, argv+2); + canvas_add(gl,x); +} + +struct t_mresp : t_pd { + t_outlet *outlet; +}; +struct t_message : t_text { + t_mresp mresp; + t_canvas *canvas; +}; + +static void mresp_bang(t_mresp *x) {outlet_bang(x->outlet);} +static void mresp_float(t_mresp *x, t_float f) {outlet_float(x->outlet, f);} +static void mresp_symbol(t_mresp *x, t_symbol *s) {outlet_symbol(x->outlet, s);} +static void mresp_list(t_mresp *x, t_symbol *s, int argc, t_atom *argv) + {outlet_list(x->outlet, s, argc, argv);} +static void mresp_anything(t_mresp *x, t_symbol *s, int argc, t_atom *argv) + {outlet_anything(x->outlet, s, argc, argv);} + +static void message_bang(t_message *x) +{binbuf_eval(x->binbuf,&x->mresp, 0, 0);} +static void message_float(t_message *x, t_float f) +{t_atom at; SETFLOAT(&at, f); binbuf_eval(x->binbuf, &x->mresp, 1, &at);} +static void message_symbol(t_message *x, t_symbol *s) +{t_atom at; SETSYMBOL(&at, s); binbuf_eval(x->binbuf, &x->mresp, 1, &at);} +static void message_list(t_message *x, t_symbol *s, int argc, t_atom *argv) +{binbuf_eval(x->binbuf, &x->mresp, argc, argv);} +static void message_add2(t_message *x, t_symbol *s, int argc, t_atom *argv) +{binbuf_add(x->binbuf, argc, argv); gobj_changed(x,"binbuf");} +static void message_set(t_message *x, t_symbol *s, int argc, t_atom *argv) +{binbuf_clear(x->binbuf); message_add2(x,s,argc,argv);} +static void message_add(t_message *x, t_symbol *s, int argc, t_atom *argv) +{binbuf_add(x->binbuf, argc, argv); binbuf_addsemi(x->binbuf); gobj_changed(x,"binbuf");} +static void message_addcomma(t_message *x) +{t_atom a; SETCOMMA(&a); binbuf_add(x->binbuf, 1, &a); gobj_changed(x,"binbuf");} +static void message_adddollar(t_message *x, t_floatarg f) +{t_atom a; SETDOLLAR(&a, f<0?0:(int)f); binbuf_add(x->binbuf, 1, &a); gobj_changed(x,"binbuf");} +//static void message_adddollsym(t_message *x, t_symbol *s) +//{t_atom a; SETDOLLSYM(&a, s); binbuf_add(x->binbuf, 1, &a); gobj_changed(x,"binbuf");} + +static void message_adddollsym(t_message *x, t_symbol *s) { + t_atom a; + SETDOLLSYM(&a, symprintf("$%s",s->name)); + binbuf_add(x->binbuf, 1, &a); + gobj_changed(x,"binbuf"); +} + +static void message_addsemi(t_message *x) {message_add(x,0,0,0);} + +void canvas_msg(t_canvas *gl, t_symbol *s, int argc, t_atom *argv) { + t_message *x = (t_message *)pd_new(message_class); + x->mresp._class = mresp_class; + x->mresp.outlet = outlet_new(x,&s_float); + x->binbuf = binbuf_new(); + x->canvas = gl; + pd_set_newest(x); + if (argc > 1) { + x->x = atom_getintarg(0, argc, argv); + x->y = atom_getintarg(1, argc, argv); + if (argc > 2) binbuf_restore(x->binbuf, argc-2, argv+2); + } else { + x->x = 0; + x->y = 0; + } + canvas_add(gl,x); +} + +struct t_gatom : t_text { + t_atom atom; /* this holds the value and the type */ + t_canvas *canvas; /* owning canvas */ + t_float max,min; + t_symbol *label; /* symbol to show as label next to box */ + t_symbol *rcv; + t_symbol *snd; + char wherelabel; /* 0-3 for left, right, up, down */ + t_symbol *expanded_to; /* snd after $0, $1, ... expansion */ + short width; +}; + +/* prepend "-" as necessary to avoid empty strings, so we can use them in Pd messages. + A more complete solution would be to introduce some quoting mechanism; + but then we'd be much more complicated. */ +static t_symbol *gatom_escapit(t_symbol *s) { + if (!s || !*s->name) return gensym("-"); + if (*s->name == '-') { + char shmo[1000]; + snprintf(shmo,1000,"-%s",s->name); + shmo[999] = 0; + return gensym(shmo); + } + else return s; +} + +/* undo previous operation: strip leading "-" if found. */ +static t_symbol *gatom_unescapit(t_symbol *s) { + if (*s->name == '-') return gensym(s->name+1); + return s; +} + +static void gatom_set(t_gatom *x, t_symbol *s, int argc, t_atom *argv) { + t_atom oldatom = x->atom; + if (!argc) return; + if (x->atom.a_type == A_FLOAT) { + SETFLOAT( &x->atom,atom_getfloat(argv)); if (x->atom.a_float !=oldatom.a_float ) gobj_changed(x,"atom"); + } else if (x->atom.a_type == A_SYMBOL) { + SETSYMBOL(&x->atom,atom_getsymbol(argv)); if (x->atom.a_symbol!=oldatom.a_symbol) gobj_changed(x,"atom"); + } +} + +static void gatom_bang(t_gatom *x) { + t_symbol *s = x->expanded_to; + t_outlet *o = x->outlet; + if (x->atom.a_type == A_FLOAT) { + if (o) outlet_float(o, x->atom.a_float); + if (*s->name && s->thing) { + if (x->snd == x->rcv) goto err; + pd_float(s->thing, x->atom.a_float); + } + } else if (x->atom.a_type == A_SYMBOL) { + if (o) outlet_symbol(o, x->atom.a_symbol); + if (*s->name && s->thing) { + if (x->snd == x->rcv) goto err; + pd_symbol(s->thing, x->atom.a_symbol); + } + } + return; +err: + error("%s: atom with same send/receive name (infinite loop)", x->snd->name); +} + +static void gatom_float(t_gatom *x, t_float f) +{t_atom at; SETFLOAT(&at, f); gatom_set(x, 0, 1, &at); gatom_bang(x);} +static void gatom_symbol(t_gatom *x, t_symbol *s) +{t_atom at; SETSYMBOL(&at, s); gatom_set(x, 0, 1, &at); gatom_bang(x);} + +static void gatom_reload(t_gatom *x, t_symbol *sel, int argc, t_atom *argv) { + int width; t_float wherelabel; + t_symbol *rcv,*snd; + if (!pd_scanargs(argc,argv,"ifffaaa",&width,&x->min,&x->max,&wherelabel,&x->label,&rcv,&snd)) return; + gobj_changed(x,0); + SET(label,gatom_unescapit(x->label)); + if (x->min>=x->max) {SET(min,0); SET(max,0);} + CLAMP(width,1,80); + SET(width,width); + SET(wherelabel,(int)wherelabel&3); + if (x->rcv) pd_unbind(x, canvas_realizedollar(x->canvas, x->rcv)); + SET(rcv,gatom_unescapit(rcv)); + if (x->rcv) pd_bind( x, canvas_realizedollar(x->canvas, x->rcv)); + SET(snd,gatom_unescapit(snd)); + SET(expanded_to,canvas_realizedollar(x->canvas, x->snd)); +} + +/* We need a list method because, since there's both an "inlet" and a + "nofirstin" flag, the standard list behavior gets confused. */ +static void gatom_list(t_gatom *x, t_symbol *s, int argc, t_atom *argv) { + if (!argc) gatom_bang(x); + else if (argv->a_type == A_FLOAT) gatom_float(x, argv->a_w.w_float); + else if (argv->a_type == A_SYMBOL) gatom_symbol(x, argv->a_w.w_symbol); + else error("gatom_list: need float or symbol"); +} + +void canvas_atom(t_canvas *gl, t_atomtype type, int argc, t_atom *argv) { + t_gatom *x = (t_gatom *)pd_new(gatom_class); + if (type == A_FLOAT) {SET(width, 5); SETFLOAT(&x->atom, 0);} + else {SET(width,10); SETSYMBOL(&x->atom, &s_symbol);} + x->canvas = gl; + SET(min,0); + SET(max,0); + SET(wherelabel,0); + SET(label,0); + SET(rcv,0); + SET(snd,0); + x->expanded_to = &s_; //??? + x->binbuf = binbuf_new(); + binbuf_add(x->binbuf, 1, &x->atom); + SET(x,atom_getintarg(0, argc, argv)); + SET(y,atom_getintarg(1, argc, argv)); + inlet_new(x,x,0,0); + outlet_new(x, type == A_FLOAT ? &s_float: &s_symbol); + if (argc>2) gatom_reload(x,&s_,argc-2,argv+2); + canvas_add(gl,x); + pd_set_newest(x); +} + +void canvas_floatatom( t_canvas *gl, t_symbol *s, int argc, t_atom *argv) {canvas_atom(gl, A_FLOAT, argc, argv);} +void canvas_symbolatom(t_canvas *gl, t_symbol *s, int argc, t_atom *argv) {canvas_atom(gl, A_SYMBOL, argc, argv);} +static void gatom_free(t_gatom *x) {if (x->rcv) pd_unbind(x, canvas_realizedollar(x->canvas, x->rcv));} + +extern "C" void text_save(t_gobj *z, t_binbuf *b) { + t_text *x = (t_text *)z; + t_canvas *c = (t_canvas *)z; /* in case it is */ + if (x->_class == message_class) { + binbuf_addv(b,"ttii","#X","msg", (t_int)x->x, (t_int)x->y); + binbuf_addbinbuf(b, x->binbuf); + } else if (x->_class == gatom_class) { + t_gatom *g = (t_gatom *)x; + t_symbol *sel = g->atom.a_type==A_SYMBOL? gensym("symbolatom") : gensym("floatatom"); + binbuf_addv(b,"tsii","#X", sel, (t_int)x->x, (t_int)x->y); + binbuf_addv(b,"iffi", (t_int)g->width, g->min, g->max, (t_int)g->wherelabel); + binbuf_addv(b,"sss", gatom_escapit(g->label), gatom_escapit(g->rcv), gatom_escapit(g->snd)); + } else if (x->_class == text_class) { + binbuf_addv(b, "ttii","#X","text", (t_int)x->x, (t_int)x->y); + binbuf_addbinbuf(b, x->binbuf); + } else { + if (zgetfn(x,gensym("saveto")) && + !(x->_class==canvas_class && (canvas_isabstraction(c) || canvas_istable(c)))) { + mess1(x,gensym("saveto"),b); + binbuf_addv(b,"ttii","#X","restore", (t_int)x->x, (t_int)x->y); + } else { + binbuf_addv(b,"ttii","#X","obj", (t_int)x->x, (t_int)x->y); + } + if (x->binbuf) { + binbuf_addbinbuf(b, x->binbuf); + } else { + /*bug("binbuf missing at #X restore !!!");*/ + } + } + binbuf_addv(b, ";"); +} + +static t_binbuf *canvas_cut_wires(t_canvas *x, t_gobj *o) { + t_binbuf *buf = binbuf_new(); + canvas_wires_each(oc,t,x) { + if ((o==t.from) != (o==t.to)) + binbuf_addv(buf,"ttiiii;","#X","connect", t.from->dix->index, t.outlet, t.to->dix->index, t.inlet); + } + return buf; +} +static void canvas_paste_wires(t_canvas *x, t_binbuf *buf) { + pd_bind(x,gensym("#X")); + binbuf_eval(buf,0,0,0); + pd_unbind(x,gensym("#X")); +} + +static void text_setto(t_text *x, t_canvas *canvas, char *buf, int bufsize) { + if (x->_class == message_class || x->_class == gatom_class || x->_class == text_class) { + binbuf_text(x->binbuf, buf, bufsize); + gobj_changed(x,"binbuf"); + pd_set_newest(x); + return; + } + t_binbuf *b = binbuf_new(); + binbuf_text(b, buf, bufsize); + int natom1 = binbuf_getnatom(x->binbuf); t_atom *vec1 = binbuf_getvec(x->binbuf); + int natom2 = binbuf_getnatom(b); t_atom *vec2 = binbuf_getvec(b); + /* special case: if pd args change just pass the message on. */ + if (natom1 >= 1 && natom2 >= 1 && + vec1[0].a_type == A_SYMBOL && vec1[0].a_symbol == s_pd && + vec2[0].a_type == A_SYMBOL && vec2[0].a_symbol == s_pd) { + typedmess(x,gensym("rename"),natom2-1,vec2+1); + binbuf_free(x->binbuf); + x->binbuf = b; + pd_set_newest(x); /* fake object creation, for simplicity of client */ + } else { + int xwas=x->x, ywas=x->y; + int backupi = x->dix->index; + t_binbuf *buf = canvas_cut_wires(canvas_getcanvas(canvas),x); + canvas_delete(canvas,x); + canvas_objtext(canvas,xwas,ywas,b,backupi); + t_pd *backup = newest; + post("backupi=%d newest->index=%d",backupi,((t_object *)newest)->dix->index); + if (newest && pd_class(newest) == canvas_class) canvas_loadbang((t_canvas *)newest); + canvas_paste_wires(canvas_getcanvas(canvas), buf); + newest = backup; + } +} + +t_object *symbol2opointer(t_symbol *s) { + t_text *o; + if (sscanf(s->name,"x%lx",(long*)&o)<1) {error("expected object-id"); return 0;} + if (!object_table->exists(o)) {error("%s target is not a currently valid pointer",s->name); return 0;} + if ((object_table->get(o)&1)==0) { + error("%s target is zombie? object_table says '%ld'",s->name,object_table->get(o)); + return 0; + } + if (!o->_class->patchable) {error("%s target not a patchable object"); return 0;} + return o; +} + +t_object *atom2opointer(t_atom *a) {return symbol2opointer(atom_getsymbol(a));} + +static void canvas_text_setto(t_canvas *x, t_symbol *s, int argc, t_atom *argv) { + t_text *o = atom2opointer(&argv[0]); if (!o) return; + char str[4096]; + for (int i=0; ix=(int)px; gobj_changed(o,"x"); + o->y=(int)py; gobj_changed(o,"y"); +} +static void canvas_object_delete(t_canvas *x, t_symbol *name) { + t_text *o = symbol2opointer(name); if (!o) return; + fprintf(stderr,"canvas_object_delete %p\n",o); + canvas_delete(x,o); +} + +static void canvas_object_get_tips(t_canvas *x, t_symbol *name) { + t_text *o = symbol2opointer(name); if (!o) return; + char foo[666]; + if (o->_class->firstin) strcpy(foo,o->_class->firsttip->name); else strcpy(foo,""); + int n = obj_ninlets(x); + //char *foop = foo; + for (int i=!!o->_class->firstin; iinlet,i)); + } + sys_mgui(o,"tips=","S",foo); +} + +extern "C" void open_via_helppath(const char *name, const char *dir); +static void canvas_object_help(t_canvas *x, t_symbol *name) { + t_text *o = symbol2opointer(name); if (!o) return; + const char *hn = class_gethelpname(o->_class); + bool suffixed = strcmp(hn+strlen(hn)-3, ".pd")==0; + char *buf; + asprintf(&buf,"%s%s",hn,suffixed?"":".pd"); + open_via_helppath(buf, o->_class->externdir->name); + free(buf); +} + +static long canvas_children_count(t_canvas *x) { + long n=0; + canvas_each(y,x) n++; + return n; +} + +static void canvas_reorder_last(t_canvas *x, int dest) { + int n = canvas_children_count(x); + t_gobj *foo = canvas_remove_nth(x,n-1); + fprintf(stderr,"canvas_reorder_last(x=%p,dest=%d) n=%d\n",x,dest,n); + fprintf(stderr,"foo=%p\n",foo); + if (!foo) {bug("canvas_remove_nth returned NULL"); return;} + canvas_insert_nth(x,dest,foo); +} + +/* this supposes that $2=#X */ +#if 0 +static void canvas_object_insert(t_canvas *x, t_symbol *s, int argc, t_atom *argv) { + //t_text *o; + //t_binbuf *b; + if (argc<1) {error("not enough args"); return;} + if (argv[0].a_type != A_FLOAT) {error("$1 must be float"); return;} + int i = atom_getint(argv); + if (argv[2].a_type != A_SYMBOL) {error("$2 must be symbol"); return;} + s = argv[2].a_symbol; + x->next_add = i; + if (s == gensym("obj")) { + /* b = binbuf_new(); binbuf_restore(b, argc-5, argv+5); + canvas_objtext(x,atom_getintarg(3,argc,argv),atom_getintarg(4,argc,argv),0,b); */ + canvas_obj(x,s,argc-3,argv+3); + } else if (s == gensym("restore")) { canvas_restore(x,s,argc-3,argv+3); + } else if (s == gensym("floatatom")) { canvas_floatatom(x,s,argc-3,argv+3); + } else if (s == gensym("symbolatom")) { canvas_floatatom(x,s,argc-3,argv+3); + } else if (s == gensym("text")) { canvas_text(x,gensym("text"),argc-3,argv+3); + } else post("UNSUPPORTED object_insert: %s",s->name); + /* canvas_reorder_last(x,i); */ + x->next_add = -1; +/*err: pd_popsym(x);*/ +} +#endif + +static void g_text_setup() { + t_class *c; + text_class = class_new2("text", 0,0,sizeof(t_text),CLASS_NOINLET|CLASS_PATCHABLE,0); + dummy_class = class_new2("dummy",0,0,sizeof(t_text),CLASS_NOINLET|CLASS_PATCHABLE,0); + + c = mresp_class = class_new2("messresponder",0,0,sizeof(t_text),CLASS_PD,""); + class_addbang( c, mresp_bang); + class_addfloat( c, (t_method) mresp_float); + class_addsymbol( c, mresp_symbol); + class_addlist( c, mresp_list); + class_addanything(c, mresp_anything); + + c = message_class = class_new2("message",0,0,sizeof(t_message),CLASS_PATCHABLE,""); + class_addbang(c, message_bang); + class_addfloat(c, message_float); + class_addsymbol(c, message_symbol); + class_addlist(c, message_list); + class_addanything(c, message_list); + class_addmethod2(c, message_set, "set","*"); + class_addmethod2(c, message_add, "add","*"); + class_addmethod2(c, message_add2,"add2","*"); + class_addmethod2(c, message_addcomma, "addcomma", ""); + class_addmethod2(c, message_addsemi, "addsemi", ""); + class_addmethod2(c, message_adddollar, "adddollar", "f"); + class_addmethod2(c, message_adddollsym, "adddollsym","s"); + + c = gatom_class = class_new2("gatom",0,gatom_free,sizeof(t_gatom),CLASS_NOINLET|CLASS_PATCHABLE,""); + class_addbang(c, gatom_bang); + class_addfloat(c, gatom_float); + class_addsymbol(c, gatom_symbol); + class_addlist(c, gatom_list); + class_addmethod2(c, gatom_set, "set","*"); + class_addmethod2(c, gatom_reload, "reload","*"); +} + +static int iemgui_color_hex[]= { + 0xfcfcfc, 0xa0a0a0, 0x404040, 0xfce0e0, 0xfce0c0, + 0xfcfcc8, 0xd8fcd8, 0xd8fcfc, 0xdce4fc, 0xf8d8fc, + 0xe0e0e0, 0x7c7c7c, 0x202020, 0xfc2828, 0xfcac44, + 0xe8e828, 0x14e814, 0x28f4f4, 0x3c50fc, 0xf430f0, + 0xbcbcbc, 0x606060, 0x000000, 0x8c0808, 0x583000, + 0x782814, 0x285014, 0x004450, 0x001488, 0x580050 +}; + +static int iemgui_clip_size(int size) {return max(8,size);} + +int convert_color2(int x) { + return ~ (((0xfc0000&x)>>6) | ((0xfc00&x)>>4) | ((0xfc&x)>>2)); +} + +static int convert_color(int x) { + if (x>=0) return iemgui_color_hex[x%30]; + x=~x; + return ((x&0x3f000)<<6) | ((x&0xfc0)<<4) | ((x&0x3f)<<2); +} + +static void iemgui_send(t_iemgui *x, t_symbol *s) { + SET(snd,canvas_realizedollar(x->canvas, s)); + if (x->snd==s_empty) SET(snd,0); +} + +static void iemgui_receive(t_iemgui *x, t_symbol *s) { + t_symbol *rcv = canvas_realizedollar(x->canvas, s); + if (rcv==s_empty) rcv=0; + if (rcv==x->rcv) return; + if(x->rcv) pd_unbind(x,x->rcv); + SET(rcv,rcv); + if(x->rcv) pd_bind(x,x->rcv); +} + +static void iemgui_label(t_iemgui *x, t_symbol *s) { + SET(lab,s==s_empty?0:s); +} +static void iemgui_label_pos(t_iemgui *x, t_float ldx, t_float ldy) { + SET(ldx,(int)ldx); + SET(ldy,(int)ldy); +} +static void iemgui_label_font(t_iemgui *x, t_symbol *s, int ac, t_atom *av) { + SET(fontsize,max(4,(int)atom_getintarg(1, ac, av))); + SET(font_style,atom_getintarg(0, ac, av)); +} +static void iemgui_delta(t_iemgui *x, t_symbol *s, int ac, t_atom *av) { + SET(x,x->x+(int)atom_getintarg(0, ac, av)); + SET(y,x->y+(int)atom_getintarg(1, ac, av)); +} +static void iemgui_pos(t_iemgui *x, t_symbol *s, int ac, t_atom *av) { + SET(x,x->x+(int)atom_getintarg(0, ac, av)); + SET(y,x->y+(int)atom_getintarg(1, ac, av)); +} + +static int iemgui_compatible_col(int i) {return i>=0 ? iemgui_color_hex[i%30] : (~i)&0xffffff;} + +static void iemgui_color(t_iemgui *x, t_symbol *s, int ac, t_atom *av) { + int i=0; + SET(bcol,iemgui_compatible_col(atom_getintarg(i++, ac, av))); + if(ac > 2) SET(fcol,iemgui_compatible_col(atom_getintarg(i++, ac, av))); + SET(lcol,iemgui_compatible_col(atom_getintarg(i++, ac, av))); +} + +#define NEXT p=va_arg(val,void*); /*printf("p=%p\n",p);*/ +int pd_vscanargs(int argc, t_atom *argv, char *fmt, va_list val) { + int optional=0; + int i,j=0; + for (i=0; fmt[i]; i++) { + switch (fmt[i]) { + case 0: error("too many args"); return 0; + case '*': goto break1; /* rest is any type */ + case 'F': case 'f': case 'd': case 'i': case 'c': case 'b': + if (!IS_A_FLOAT(argv,j)) {error("expected float in $%d",i+1); return 0;} + j++; break; + case 'S': case 's': + if (!IS_A_SYMBOL(argv,j)) {error("expected symbol in $%d",i+1); return 0;} + j++; break; + case '?': break; + case 'a': + if (!IS_A_FLOAT(argv,j) && !IS_A_SYMBOL(argv,j)) {error("expected float or symbol in $%d",i+1); return 0;} + j++; break; + case ';': optional=1; break; + default: error("bad format string"); return 0; + } + } + if (jblah) relative offsets + into each struct... +*/ +int pd_vsaveargs(t_binbuf *b, char *fmt, va_list val) { + t_atom a; + int i; + for (i=0; ; i++) { + switch (fmt[i]) { + case 0: goto break2; + case ';': continue; /* skip */ + case '?': case 'F': case 'S': break; /* skip */ + case 'd': SETFLOAT(&a,*(va_arg(val,double*))); break; + case 'f': SETFLOAT(&a,*(va_arg(val,float *))); break; + case 'i': SETFLOAT(&a,*(va_arg(val, int *))); break; + case 'b': SETFLOAT(&a,!!*(va_arg(val,int *))); break; + case 'c': /* colour, from IEM format to RGB 8:8:8 format */ + SETFLOAT(&a,convert_color2(*(va_arg(val, int *)))); break; + case 'a': + case 's': { t_symbol *s = *(va_arg(val,t_symbol**)); + SETSYMBOL(&a,s?s:s_empty); } break; + default: post("WARNING: bug using pd_saveargs()"); goto err; /* WHAT? */ + } + binbuf_add(b,1,&a); + } +break2: + binbuf_addv(b, ";"); + return 1; +err: + post("WARNING: pd_saveargs failed; fmt=%s, i=%d",fmt,i); + return 0; +} + +int pd_scanargs(int argc, t_atom *argv, char *fmt, ...) { + int i; + va_list val; + va_start(val,fmt); + i=pd_vscanargs(argc,argv,fmt,val); + va_end(val); + return i; +} + +int pd_saveargs(t_binbuf *b, char *fmt, ...) { + int i; + va_list val; + va_start(val,fmt); + i=pd_vsaveargs(b,fmt,val); + va_end(val); + return i; +} + +int pd_pickle(t_foo *foo, char *fmt, ...) { + va_list val; + va_start(val,fmt); + int r = foo->b ? + pd_vsaveargs(foo->b,fmt,val) : + pd_vscanargs(foo->argc,foo->argv,fmt,val); + va_end(val); + return r; +} + +static int pd_savehead(t_binbuf *b, t_iemgui *x, char *name) { + binbuf_addv(b, "ttiit","#X","obj", (t_int)x->x, (t_int)x->y, name); + return 1; +} + +void pd_upload(t_gobj *self) { + long alive = (long)object_table->get(self) & 1; + if (!alive) { + sys_mgui(self,"delete",""); + pd_free_zombie(self); + return; + } + t_binbuf *b = binbuf_new(); + t_class *c = self->_class; + t_text *x = (t_text *)self; + if (c==canvas_class) { + /* just the "#N canvas" line, not the contents */ + canvas_savecontainerto((t_canvas *)self,b); + canvas_savecoordsto((t_canvas *)self,b); /* this may be too early */ + binbuf_addv(b, "ttii", "#X","restore", (t_int)x->x, (t_int)x->y); + if (x->binbuf) { + //pd_print(x,"pd_upload"); + binbuf_addbinbuf(b, x->binbuf); + } else { + /*bug("binbuf missing at #X restore !!!");*/ + } + binbuf_addv(b, ";"); + } else { /* this was outside of the "else" for a while. why? I don't remember */ + c->savefn(self,b); + } + int n; + char *s; + appendix_save(self,b); + binbuf_gettext(b,&s,&n); + if (s[n-1]=='\n') n--; + if (c->patchable) { + sys_vgui("change x%lx x%lx %d {%.*s} %d %d %d\n",(long)self,(long)self->dix->canvas,self->dix->index,n,s, + obj_ninlets((t_text *)self), obj_noutlets((t_text *)self), x->_class!=dummy_class); + } else { + sys_vgui("change x%lx x%lx %d {%.*s}\n",(long)self,(long)self->dix->canvas,self->dix->index,n,s); + } + binbuf_free(b); + free(s); + if (c==canvas_class) { + t_canvas *can = (t_canvas *)self; + sys_mgui(self,"name=","s",can->name); + sys_mgui(self,"folder=","s",canvas_getenv(can)->dir); + sys_mgui(self,"havewindow=","i",can->havewindow); + } + if (c==gatom_class) { + t_gatom *g = (t_gatom *)x; + if (g->atom.a_type==A_SYMBOL) sys_mgui(g,"set","s",g->atom.a_symbol); + else sys_mgui(g,"set","f",g->atom.a_float); + } + if (object_table->exists(self)) { + object_table->set(self,object_table->get(self)|2); /* has been uploaded */ + } else post("object_table is broken"); +} + +void sys_mgui(void *self_, const char *sel, const char *fmt, ...) { + t_gobj *self = (t_gobj *)self_; + char buf[4096]; + int i=0, n=sizeof(buf); + va_list val; + va_start(val,fmt); + i+=snprintf(buf+i,n-i,"x%lx %s", (long)self, sel); + if (i>=n) goto over; + while (*fmt) { + switch (*fmt) { + case 'f': case 'd': i+=snprintf(buf+i,n-i," %f",va_arg(val,double)); break; + case 'i': i+=snprintf(buf+i,n-i," %d",va_arg(val,int)); break; + case 'p': i+=snprintf(buf+i,n-i," x%lx",(long)va_arg(val,void*)); break; + /* + case 's': i+=snprintf(buf+i,n-i," \"%s\"",va_arg(val,t_symbol *)->name); break; + case 'S': i+=snprintf(buf+i,n-i," \"%s\"",va_arg(val,const char *)); break; + */ + case 's': i+=snprintf(buf+i,n-i," {%s}",va_arg(val,t_symbol *)->name); break; + case 'S': i+=snprintf(buf+i,n-i," {%s}",va_arg(val,const char *)); break; + } + if (i>=n) goto over; + fmt++; + } + va_end(val); + i+=snprintf(buf+i,n-i,"\n"); + if (i>=n) goto over; + sys_gui(buf); + return; +over: + post("sys_mgui: can't send: buffer overflow"); + abort(); +} + +static void iemgui_subclass (t_class *c) { + class_addmethod2(c, iemgui_delta, "delta","*"); + class_addmethod2(c, iemgui_pos, "pos","*"); + class_addmethod2(c, iemgui_color, "color","*"); + class_addmethod2(c, iemgui_send, "send","S"); + class_addmethod2(c, iemgui_receive, "receive","S"); + class_addmethod2(c, iemgui_label, "label","S"); + class_addmethod2(c, iemgui_label_pos, "label_pos","ff"); + class_addmethod2(c, iemgui_label_font, "label_font","*"); +} + +t_symbol *pd_makebindsym(t_pd *x) {return symprintf(".x%lx",(long)x);} + +t_iemgui *iemgui_new(t_class *qlass) { + t_iemgui *x = (t_iemgui *)pd_new(qlass); + x->canvas = canvas_getcurrent(); + x->w = x->h = 15; + x->ldx=0; + x->ldy=-6; + x->isa=0; + x->font_style = 0; + x->fontsize = 8; + x->snd = 0; + x->rcv = 0; + x->lab = s_empty; + x->bcol = 0xffffff; + x->fcol = 0x000000; + x->lcol = 0x000000; + pd_bind(x,pd_makebindsym(x)); + return x; +} + +static void iemgui_constrain(t_iemgui *x) { + SET(fontsize,max(x->fontsize,4)); + SET(h,iemgui_clip_size(x->h)); + SET(w,iemgui_clip_size(x->w)); +} + +void iemgui_init(t_iemgui *x, t_floatarg f) {SET(isa,(x->isa&~1)|!!f);} + +void binbuf_update(t_iemgui *x, t_symbol *qlass, int argc, t_atom *argv) { + t_binbuf *buf = x->binbuf; + if (!buf) return; + binbuf_clear(buf); + t_atom foo; + SETSYMBOL(&foo,qlass); + binbuf_add(buf,1,&foo); + binbuf_add(buf,argc,argv); +} + +static /*bool*/ int iemgui_loadbang (t_iemgui *self) { + return !sys_noloadbang && self->isa&1; +} + +static /*bool*/ int iemgui_forward (t_iemgui *self) { + return !self->snd || !self->rcv || self->snd != self->rcv; +} + +static t_class *bng_class; + +static void bng_check_minmax(t_bng *x) { + if(x->ftbreak > x->fthold) { + int h = x->ftbreak; + SET(ftbreak,x->fthold); + SET(fthold,h); + } + SET(ftbreak,max(x->ftbreak,10)); + SET(fthold ,max(x->fthold, 50)); +} + +static void bng_set(t_bng *x) { + SET(count,x->count+1); + sys_mgui(x,"bang","i",x->count); +} + +static void bng_bout2(t_bng *x) { + outlet_bang(x->outlet); + if(x->snd && x->snd->thing) pd_bang(x->snd->thing); +} + +static void bng_bang(t_bng *x) { + bng_set(x); + outlet_bang(x->outlet); + if(x->snd && x->snd->thing && iemgui_forward(x)) pd_bang(x->snd->thing); +} +static void bng_bang2 (t_bng *x) { {bng_set(x); bng_bout2(x);}} +static void bng_loadbang(t_bng *x) {if(iemgui_loadbang(x)) {bng_set(x); bng_bout2(x);}} + +static void bng_size(t_bng *x, t_symbol *s, int ac, t_atom *av) { + SET(w,iemgui_clip_size((int)atom_getintarg(0, ac, av))); + SET(h,x->w); +} + +static void bng_flashtime(t_bng *x, t_symbol *s, int ac, t_atom *av) { + SET(ftbreak,atom_getintarg(0, ac, av)); + SET(fthold ,atom_getintarg(1, ac, av)); + bng_check_minmax(x); +} + +static int bng_pickle(t_bng *x, t_foo *foo) { + return pd_pickle(foo,"iiiiaaaiiiiccc",&x->w,&x->fthold,&x->ftbreak,&x->isa,&x->snd,&x->rcv,&x->lab, + &x->ldx,&x->ldy,&x->font_style,&x->fontsize,&x->bcol,&x->fcol,&x->lcol); +} + +static void bng_savefn(t_bng *x, t_binbuf *b) { + t_foo foo = {0,0,b}; if (!b) return; + pd_savehead(b,x,"bng"); bng_pickle(x,&foo); +} + +static void bng_reload(t_bng *x, t_symbol *s, int argc, t_atom *argv) { + t_foo foo = { argc, argv, 0 }; + binbuf_update(x,gensym("bng"),argc,argv); + if (!bng_pickle(x,&foo)) return; + SET(h,x->w); + bng_check_minmax(x); + iemgui_constrain(x); + if (x->rcv) pd_bind(x,x->rcv); +} + +static void *bng_new(t_symbol *s, int argc, t_atom *argv) { + t_bng *x = (t_bng *)iemgui_new(bng_class); + SET(ftbreak,250); + SET(fthold,50); + SET(count,0); + bng_check_minmax(x); + outlet_new(x, &s_bang); + if (argc) bng_reload(x,0,argc,argv); + return x; +} + +static void iemgui_free(t_iemgui *x) { + if(x->rcv) pd_unbind(x,x->rcv); +} + +static t_class *toggle_class; + +static void toggle_action(t_toggle *x) { + outlet_float(x->outlet, x->on); + if(x->snd && x->snd->thing) pd_float(x->snd->thing, x->on); +} + +static void toggle_bang(t_toggle *x) {SET(on,x->on?0.0:x->nonzero); toggle_action(x);} +static void toggle_set(t_toggle *x, t_floatarg f) {SET(on,f); if(f) SET(nonzero,f);} +static void toggle_float(t_toggle *x, t_floatarg f) {toggle_set(x,f);if(iemgui_forward(x)) toggle_action(x);} +static void toggle_fout (t_toggle *x, t_floatarg f) {toggle_set(x,f); toggle_action(x);} +static void toggle_loadbang(t_toggle *x) {if(iemgui_loadbang(x)) toggle_fout(x, (float)x->on);} +static void toggle_nonzero(t_toggle *x, t_floatarg f) {if (f) SET(nonzero,f);} + +static void toggle_size(t_toggle *x, t_symbol *s, int ac, t_atom *av) { + SET(w,iemgui_clip_size((int)atom_getintarg(0, ac, av))); + SET(h,x->w); +} + +static int toggle_pickle(t_toggle *x, t_foo *foo) { + return pd_pickle(foo,"iiaaaiiiicccf;f",&x->w,&x->isa,&x->snd,&x->rcv,&x->lab, + &x->ldx,&x->ldy,&x->font_style,&x->fontsize,&x->bcol,&x->fcol,&x->lcol,&x->on,&x->nonzero); +} + +static void toggle_savefn(t_toggle *x, t_binbuf *b) { + t_foo foo = {0,0,b}; if (!b) return; + pd_savehead(b,x,"tgl"); toggle_pickle(x,&foo); +} + +static void toggle_reload(t_toggle *x, t_symbol *s, int argc, t_atom *argv) { + t_foo foo = { argc, argv, 0 }; + binbuf_update(x,gensym("tgl"),argc,argv); + if (!toggle_pickle(x,&foo)) return; + SET(h,x->w); + SET(on,x->isa&1 && x->on ? x->nonzero : 0.0); + SET(nonzero,argc==14 && IS_A_FLOAT(argv,13) ? atom_getfloatarg(13, argc, argv) : 1.0); + if (!x->nonzero) SET(nonzero,1.0); + iemgui_constrain(x); + if (x->rcv) pd_bind(x,x->rcv); +} + +static void *toggle_new(t_symbol *s, int argc, t_atom *argv) { + t_toggle *x = (t_toggle *)iemgui_new(toggle_class); + SET(on,0.0); + SET(nonzero,1.0); + outlet_new(x, &s_float); + if (argc) toggle_reload(x,0,argc,argv); + return x; +} + +static void radio_set(t_radio *x, t_floatarg f) { + int i=(int)f; + int old=x->on_old; + CLAMP(i,0,x->number-1); + if(x->on!=old) SET(on_old,x->on); + SET(on,i); + if(x->on!=old) SET(on_old,old); +} + +static void radio_send2(t_radio *x, float a, float b) { + SETFLOAT(x->at,a); + SETFLOAT(x->at+1,b); + outlet_list(x->outlet, &s_list, 2, x->at); + if(x->snd && x->snd->thing) pd_list(x->snd->thing, &s_list, 2, x->at); +} + +static void radio_send(t_radio *x, float a) { + outlet_float(x->outlet,a); + if(x->snd && x->snd->thing) pd_float(x->snd->thing,a); +} + +static void radio_bang(t_radio *x) { + if (x->oldstyle) { + if(x->change && x->on!=x->on_old) radio_send2(x,x->on_old,0.0); + SET(on_old,x->on); + radio_send2(x,x->on,1.0); + } else { + radio_send(x,x->on); + } +} + +static void radio_fout2(t_radio *x, t_floatarg f, int forwardonly) { + int i=(int)f; + CLAMP(i,0,x->number-1); + if (x->oldstyle) { + /* compatibility with earlier "hdial" behavior */ + if(x->change && i!=x->on_old && (!forwardonly || iemgui_forward(x))) radio_send2(x,x->on_old,0.0); + SET(on_old,x->on); + SET(on,i); + SET(on_old,x->on); + radio_send2(x,x->on,1.0); + if (!forwardonly || iemgui_forward(x)) radio_send2(x,x->on,1.0); + } else { + SET(on,i); + if (!forwardonly || iemgui_forward(x)) radio_send(x,x->on); + SET(on_old,x->on); + } +} + +static void radio_fout (t_radio *x, t_floatarg f) {radio_fout2(x,f,0);} +static void radio_float(t_radio *x, t_floatarg f) {radio_fout2(x,f,1);} +static void radio_loadbang(t_radio *x) {if(iemgui_loadbang(x)) radio_bang(x);} +static void radio_orient(t_radio *x,t_floatarg v) {SET(orient,!!v); +post("v=%f, !!v=%d, orient=%d",v,!!v,x->orient);} + +static void radio_number(t_radio *x, t_floatarg num) { + int n=(int)num; + CLAMP(n,1,128); + if (n != x->number) { + SET(number,n); + CLAMP(x->on,0,x->number-1); gobj_changed(x,"on"); + SET(on_old,x->on); + } +} + +static void radio_size(t_radio *x, t_float size) { + SET(w,iemgui_clip_size((int)size)); + SET(h,x->w); +} + +static void radio_double_change(t_radio *x) {SET(change,1);} +static void radio_single_change(t_radio *x) {SET(change,0);} + +static int radio_pickle(t_radio *x, t_foo *foo) { + return pd_pickle(foo, "ibiiaaaiiiiccci",&x->w,&x->change,&x->isa,&x->number,&x->snd,&x->rcv,&x->lab, + &x->ldx,&x->ldy,&x->font_style,&x->fontsize,&x->bcol,&x->fcol,&x->lcol,&x->on); +} + +static t_symbol *radio_flavor(t_radio *x) { + return x->orient?x->oldstyle?sym_vdl:sym_vradio:x->oldstyle?sym_hdl:sym_hradio; +} + +static void radio_savefn(t_radio *x, t_binbuf *b) { + t_foo foo = {0,0,b}; if (!b) return; + pd_savehead(b,x,radio_flavor(x)->name); + radio_pickle(x,&foo); +} + +static void radio_reload(t_radio *x, t_symbol *s, int argc, t_atom *argv) { + t_foo foo = { argc, argv, 0 }; + binbuf_update(x,radio_flavor(x),argc,argv); + if (!radio_pickle(x,&foo)) return; + iemgui_constrain(x); + if (x->rcv) pd_bind(x,x->rcv); + gobj_changed(x,0); +} + +static void *radio_new(t_symbol *s, int argc, t_atom *argv) { + t_radio *x = (t_radio *)iemgui_new(radio_class); + SET(on_old,0); + SET(on,0); + SET(number,8); + SET(change,1); + if (s==sym_hdl) {SET(orient,0); SET(oldstyle,1);} else + if (s==sym_vdl) {SET(orient,1); SET(oldstyle,1);} else + if (s==sym_hradio) {SET(orient,0); SET(oldstyle,0);} else + if (s==sym_vradio) {SET(orient,1); SET(oldstyle,0);} + SET(on,x->isa&1 ? x->on : 0); + SET(on_old,x->on); + outlet_new(x, &s_list); + if (argc) radio_reload(x,0,argc,argv); + return x; +} + +#define IEM_SL_DEFAULTSIZE 128 +#define IEM_SL_MINSIZE 2 + +static void slider_check_width(t_slider *x, int w) { + double l = (double)(x->orient ? x->h : x->w)-1; + int m = (int)(l*100); + if(w < IEM_SL_MINSIZE) w = IEM_SL_MINSIZE; + if (x->orient) SET(h,w); else SET(w,w); + if(x->val > m) SET(val,m); +} + +static void slider_check_minmax(t_slider *x) { + double min=x->min, max=x->max; + if(x->is_log) { + if(min == 0.0 && max == 0.0) max = 1.0; + if(max > 0.0) { if (min<=0.0) min = 0.01*max; } + else { if (min >0.0) max = 0.01*min; } + } + SET(min,min); + SET(max,max); +} + +// the value/centipixel ratio +static double slider_ratio (t_slider *x) { + double diff = x->is_log ? log(x->max/x->min) : (x->max-x->min); + return diff / (double)(x->orient ? (x->h-1) : (x->w-1)); +} + +static void slider_set(t_slider *x, t_floatarg f) { + if(x->min > x->max) CLAMP(f,x->max,x->min); + else CLAMP(f,x->min,x->max); + SET(val,floor(100.0 * (x->is_log ? log(f/x->min) : (f-x->min)) / slider_ratio(x) + 0.5)); +} + +static void slider_bang(t_slider *x) { + double t = (double)x->val * slider_ratio(x) * 0.01; + double out = x->is_log ? x->min*exp(t) : x->min+t; + if (fabs(out) < 1.0e-10) out = 0.0; + outlet_float(x->outlet, out); + if(x->snd && x->snd->thing) pd_float(x->snd->thing, out); +} + +static void slider_size(t_slider *x, t_symbol *s, int ac, t_atom *av) { + int a = atom_getintarg(0,ac,av); + int b = ac>1 ? atom_getintarg(1,ac,av) : 0; + if (x->orient) { + SET(w,iemgui_clip_size(a)); + if(ac>1) slider_check_width(x,b); + } else { + slider_check_width(x,a); + if(ac>1) SET(h,iemgui_clip_size(b)); + } +} + +static void slider_range(t_slider *x, t_float min, t_float max) +{SET(min,min); SET(max,max); slider_check_minmax(x);} +static void slider_lin(t_slider *x) {SET(is_log,0); slider_check_minmax(x);} +static void slider_log(t_slider *x) {SET(is_log,1); slider_check_minmax(x);} +static void slider_steady(t_slider *x, t_floatarg f) {SET(steady,!!f);} +static void slider_float(t_slider *x, t_floatarg f) {slider_set(x,f);if(iemgui_forward(x))slider_bang(x);} +static void slider_loadbang(t_slider *x) {if(iemgui_loadbang(x)) slider_bang(x);} +static void slider_orient(t_slider *x,t_floatarg v) {SET(orient,!!v);} + +static int slider_pickle(t_slider *x, t_foo *foo) { + return pd_pickle(foo, "iiffbiaaaiiiicccf;b", + &x->w,&x->h,&x->min,&x->max,&x->is_log,&x->isa,&x->snd,&x->rcv,&x->lab, + &x->ldx,&x->ldy,&x->font_style,&x->fontsize,&x->bcol,&x->fcol,&x->lcol,&x->val,&x->steady); +} + +static void slider_savefn(t_slider *x, t_binbuf *b) { + t_foo foo = {0,0,b}; if (!b) return; + pd_savehead(b,x,(char *)(x->orient?"vsl":"hsl")); slider_pickle(x,&foo); +} + +static void slider_reload(t_slider *x, t_symbol *s, int argc, t_atom *argv) { + t_foo foo = { argc, argv, 0 }; + binbuf_update(x,gensym((char *)(x->orient?"vsl":"hsl")),argc,argv); + if (!slider_pickle(x,&foo)) return; +//this is wrong because it should happen when loading a file but not when loading from properties: + SET(val,x->isa&1 ? x->val : 0); +//end wrong. + iemgui_constrain(x); + slider_check_minmax(x); + slider_check_width(x, x->orient ? x->h : x->w); + if(x->rcv) pd_bind(x,x->rcv); + gobj_changed(x,0); +} + +static void *slider_new(t_symbol *s, int argc, t_atom *argv) { + t_slider *x = (t_slider *)iemgui_new(slider_class); + SET(orient,s==sym_vslider||s==sym_vsl); + SET(is_log,0); + SET(min,0.0); + SET(steady,1); + SET(max,(double)(IEM_SL_DEFAULTSIZE-1)); + if (x->orient) SET(h,IEM_SL_DEFAULTSIZE); else SET(w,IEM_SL_DEFAULTSIZE); + outlet_new(x, &s_float); + if (argc) slider_reload(x,0,argc,argv); + return x; +} + +static t_class *nbx_class; + +static void nbx_clip(t_nbx *x) {CLAMP(x->val,x->min,x->max);} + +static int nbx_check_minmax(t_nbx *x) { + double min=x->min, max=x->max; + int val=(int)x->val; + if(x->is_log) { + if(min==0.0 && max==0.0) max = 1.0; + if(max>0.0 && min<=0.0) min = 0.01*max; + if(max<=0.0 && min>0.0) max = 0.01*min; + } else { + if(min>max) swap(min,max); + } + SET(min,min); + SET(max,max); + CLAMP(x->val,x->min,x->max); + SET(k,x->is_log ? exp(log(x->max/x->min)/(double)(x->log_height)) : 1.0); + return x->val!=val; +} + +static void nbx_bang(t_nbx *x) { + outlet_float(x->outlet, x->val); + if(x->snd && x->snd->thing) pd_float(x->snd->thing, x->val); +} + +static void nbx_set(t_nbx *x, t_floatarg f) {SET(val,f); nbx_clip(x);} +static void nbx_float(t_nbx *x, t_floatarg f) {nbx_set(x, f); if(iemgui_forward(x)) nbx_bang(x);} + +static void nbx_log_height(t_nbx *x, t_floatarg lh) { + SET(log_height,max(10,(int)lh)); + SET(k,x->is_log ? exp(log(x->max/x->min)/(double)(x->log_height)) : 1.0); +} + +static void nbx_size(t_nbx *x, t_symbol *s, int ac, t_atom *av) { + SET(w,max(1,(int)atom_getintarg(0, ac, av))); + if(ac > 1) SET(h,max(8,(int)atom_getintarg(1, ac, av))); +} + +static void nbx_range(t_nbx *x, t_float min, t_float max) +{SET(min,min); SET(max,max); nbx_check_minmax(x);} + +static void nbx_lin(t_nbx *x) {SET(is_log,0); } +static void nbx_log(t_nbx *x) {SET(is_log,1); nbx_check_minmax(x);} +static void nbx_loadbang(t_nbx *x) {if(iemgui_loadbang(x)) nbx_bang(x);} + +static void nbx_list(t_nbx *x, t_symbol *s, int ac, t_atom *av) { + if (!IS_A_FLOAT(av,0)) return; + nbx_set(x, atom_getfloatarg(0, ac, av)); + nbx_bang(x); +} + +static int nbx_pickle(t_nbx *x, t_foo *foo) { + return pd_pickle(foo,"iiddbiaaaiiiicccd;i", + &x->w,&x->h,&x->min,&x->max,&x->is_log,&x->isa,&x->snd,&x->rcv,&x->lab, + &x->ldx,&x->ldy,&x->font_style,&x->fontsize,&x->bcol,&x->fcol,&x->lcol,&x->val,&x->log_height); +} + +static void nbx_savefn(t_nbx *x, t_binbuf *b) { + t_foo foo = {0,0,b}; + if (!b) return; + pd_savehead(b,x,"nbx"); + nbx_pickle(x,&foo); +} + +static void nbx_reload(t_nbx *x, t_symbol *s, int argc, t_atom *argv) { + t_foo foo = { argc, argv, 0 }; + binbuf_update(x,gensym("nbx"),argc,argv); + if (!nbx_pickle(x,&foo)) return; + if (!x->isa&1) SET(val,0.0); + iemgui_constrain(x); + SET(w,max(x->w,1)); + nbx_check_minmax(x); + SET(w,max(x->w,1)); + if (x->rcv) pd_bind(x,x->rcv); + gobj_changed(x,0); +} + +static void *nbx_new(t_symbol *s, int argc, t_atom *argv) { + t_nbx *x = (t_nbx *)iemgui_new(nbx_class); + SET(log_height,256); + SET(is_log,0); + SET(w,5); + SET(h,14); + SET(min,-1.0e+37); + SET(max,1.0e+37); + x->buf[0]=0; + SET(change,0); + outlet_new(x, &s_float); + if (argc) nbx_reload(x,0,argc,argv); + return x; +} + +#define IEM_VU_STEPS 40 + +static char vu_db2i[]= { + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 9, 9, 9, 9, 9,10,10,10,10,10,11,11,11,11,11,12,12,12,12,12, + 13,13,13,13,14,14,14,14,15,15,15,15,16,16,16,16,17,17,17,18, + 18,18,19,19,19,20,20,20,21,21,22,22,23,23,24,24,25,26,27,28, + 29,30,31,32,33,33,34,34,35,35,36,36,37,37,37,38,38,38,39,39, + 39,39,39,39,40,40 +}; + +static void vu_check_height(t_vu *x, int h) { + int n=max(h/IEM_VU_STEPS,2); + SET(led_size,n-1); + SET(h,IEM_VU_STEPS * n); +} + +static void vu_scale(t_vu *x, t_floatarg fscale) {SET(scale,!!fscale);} + +static void vu_size(t_vu *x, t_symbol *s, int ac, t_atom *av) { + SET(w, iemgui_clip_size((int)atom_getintarg(0, ac, av))); + if(ac>1) vu_check_height(x, (int)atom_getintarg(1, ac, av)); +} + +static int vuify(t_vu *x, float v) { + return v<=-99.9 ? 0 : + v>=12.0 ? IEM_VU_STEPS : + vu_db2i[(int)(2.0*(v+100.0))]; +} + +static float vu_round(float v) {return 0.01*(int)(100.0*v+0.5);} + +static void vu_float0(t_vu *x, t_floatarg v) { + SET(rms, vuify(x,v)); SET(fr,vu_round(v)); outlet_float(x->out(0), x->fr); + sys_mgui(x,"rms=","i",x->rms);} +static void vu_float1(t_vu *x, t_floatarg v) { + SET(peak,vuify(x,v)); SET(fp,vu_round(v)); outlet_float(x->out(1),x->fp); + sys_mgui(x,"peak=","i",x->peak);} + +static void vu_bang(t_vu *x) { + outlet_float(x->out(1), x->fp); + outlet_float(x->out(0), x->fr); +} + +static int vu_pickle(t_vu *x, t_foo *foo) { + return pd_pickle(foo,"iiaaiiiiccb;i",&x->w,&x->h,&x->rcv,&x->lab,&x->ldx,&x->ldy,&x->font_style, + &x->fontsize,&x->bcol,&x->lcol,&x->scale,&x->isa); +} + +static void vu_savefn(t_vu *x, t_binbuf *b) { + t_foo foo = {0,0,b}; if (!b) return; + pd_savehead(b,x,"vu"); vu_pickle(x,&foo); +} + +static void vu_reload(t_vu *x, t_symbol *s, int argc, t_atom *argv) { + t_foo foo = { argc, argv, 0 }; + binbuf_update(x,gensym("vu"),argc,argv); + if (!vu_pickle(x,&foo)) return; + iemgui_constrain(x); + if(x->rcv) pd_bind(x,x->rcv); + gobj_changed(x,0); +} + +static t_class *vu_class; + +static void *vu_new(t_symbol *s, int argc, t_atom *argv) { + t_vu *x = (t_vu *)iemgui_new(vu_class); + outlet_new(x, &s_float); + outlet_new(x, &s_float); + SET(bcol,0x000000); + SET(h,IEM_VU_STEPS*3); + SET(scale,1); + SET(rms,0); /* ??? */ + SET(peak,0); + SET(fp,-101.0); + SET(fr,-101.0); + vu_check_height(x,x->h); + inlet_new(x,x,&s_float,gensym("ft1")); + if (argc) vu_reload(x,0,argc,argv); + return x; +} + +static t_class *cnv_class; + +static void cnv_get_pos(t_cnv *x) { + error("unimplemented (TODO)"); +// if(x->snd && x->snd->thing) {x->at[0].a_float = x; x->at[1].a_float = y; pd_list(x->snd->thing, &s_list, 2, x->at);} +} + +static void cnv_size(t_cnv *x, t_symbol *s, int ac, t_atom *av) { + SET(h,max(1,(int)atom_getintarg(0, ac, av))); + SET(w,x->h); +} + +static void cnv_vis_size(t_cnv *x, t_symbol *s, int ac, t_atom *av) { + SET(vis_w,max(1,(int)atom_getintarg(0, ac, av))); + SET(vis_h,x->w); + if(ac > 1) SET(vis_h,max(1,(int)atom_getintarg(1, ac, av))); + gobj_changed(x,0); +} + +static int cnv_pickle(t_cnv *x, t_foo *foo) { + return pd_pickle(foo,"iiiaaaiiiicc;i",&x->w,&x->vis_w,&x->vis_h, + &x->snd,&x->rcv,&x->lab,&x->ldx,&x->ldy,&x->font_style,&x->fontsize,&x->bcol,&x->lcol,&x->isa); +} + +static void cnv_savefn(t_cnv *x, t_binbuf *b) { + t_foo foo = {0,0,b}; if (!b) return; + pd_savehead(b,x,"cnv"); cnv_pickle(x,&foo); +} + +static void cnv_reload(t_cnv *x, t_symbol *s, int argc, t_atom *argv) { + t_foo foo = { argc, argv, 0 }; + binbuf_update(x,gensym("cnv"),argc,argv); + if (!cnv_pickle(x,&foo)) return; + SET(w,max(x->w,1)); + SET(h,x->w); + SET(vis_w,max(x->vis_w,1)); + SET(vis_h,max(x->vis_h,1)); + x->at[0].a_type = x->at[1].a_type = A_FLOAT; //??? + iemgui_constrain(x); + if (x->rcv) pd_bind(x,x->rcv); + gobj_changed(x,0); +} +#undef FOO + +static void *cnv_new(t_symbol *s, int argc, t_atom *argv) { + t_cnv *x = (t_cnv *) iemgui_new(cnv_class); + SET(bcol,0xe0e0e0); + SET(fcol,0x000000); + SET(lcol,0x404040); + SET(w,15); + SET(vis_w,100); + SET(vis_h,60); + if (argc) cnv_reload(x,0,argc,argv); + return x; +} + +void canvas_notice(t_gobj *x, t_gobj *origin, int argc, t_atom *argv) { + t_canvas *self = (t_canvas *)x; + gobj_changed3(self,origin,argc,argv); +} + +void gobj_onsubscribe(t_gobj *x, t_gobj *observer) {gobj_changed(x,0);} + +void canvas_onsubscribe(t_gobj *x, t_gobj *observer) { + t_canvas *self = (t_canvas *)x; + gobj_onsubscribe(x,observer); + canvas_each( y,self) y->_class->onsubscribe( y,observer); + canvas_wires_each(oc,t,self) oc->_class->onsubscribe(oc,observer); +} + +/* [declare] and canvas_open come from 0.40 */ +/* ------------------------------- declare ------------------------ */ + +/* put "declare" objects in a patch to tell it about the environment in +which objects should be created in this canvas. This includes directories to +search ("-path", "-stdpath") and object libraries to load +("-lib" and "-stdlib"). These must be set before the patch containing +the "declare" object is filled in with its contents; so when the patch is +saved, we throw early messages to the canvas to set the environment +before any objects are created in it. */ + +struct t_declare : t_object { + int useme; +}; + +static void *declare_new(t_symbol *s, int argc, t_atom *argv) { + t_declare *x = (t_declare *)pd_new(declare_class); + x->useme = 1; + /* LATER update environment and/or load libraries */ + return x; +} + +static void declare_free(t_declare *x) { + x->useme = 0; + /* LATER update environment */ +} + +void canvas_savedeclarationsto(t_canvas *x, t_binbuf *b) { + canvas_each(y,x) { + if (pd_class(y) == declare_class) { + binbuf_addv(b,"t","#X"); + binbuf_addbinbuf(b, ((t_declare *)y)->binbuf); + binbuf_addv(b, ";"); + } else if (pd_class(y) == canvas_class) canvas_savedeclarationsto((t_canvas *)y, b); + } +} + +static void canvas_declare(t_canvas *x, t_symbol *s, int argc, t_atom *argv) { + t_canvasenvironment *e = canvas_getenv(x); +#if 0 + startpost("declare:: %s", s->name); + postatom(argc, argv); + endpost(); +#endif + for (int i=0; iname; + if ((argc > i+1) && !strcmp(flag, "-path")) { + e->path = namelist_append(e->path, atom_getsymbolarg(i+1, argc, argv)->name, 0); + i++; + } else if (argc>i+1 && !strcmp(flag, "-stdpath")) { + asprintf(&buf, "%s/%s", sys_libdir->name, atom_getsymbolarg(i+1, argc, argv)->name); + e->path = namelist_append(e->path,buf,0); + i++; + } else if (argc>i+1 && !strcmp(flag, "-lib")) { + sys_load_lib(x, atom_getsymbolarg(i+1, argc, argv)->name); + i++; + } else if (argc>i+1 && !strcmp(flag, "-stdlib")) { + asprintf(&buf, "%s/%s", sys_libdir->name, atom_getsymbolarg(i+1, argc, argv)->name); + sys_load_lib(0,buf); + i++; + } else post("declare: %s: unknown declaration", flag); + } +} + +/* utility function to read a file, looking first down the canvas's search path (set with "declare" + objects in the patch and recursively in calling patches), then down the system one. The filename + is the concatenation of "name" and "ext". "Name" may be absolute, or may be relative with slashes. + If anything can be opened, the true directory is put in the buffer dirresult (provided by caller), + which should be "size" bytes. The "nameresult" pointer will be set somewhere in the interior of + "dirresult" and will give the file basename (with slashes trimmed). If "bin" is set a 'binary' + open is attempted, otherwise ASCII (this only matters on Microsoft.) If "x" is zero, the file is + sought in the directory "." or in the global path.*/ +int canvas_open2(t_canvas *x, const char *name, const char *ext, char **dirresult, char **nameresult, int bin) { + int fd = -1; + /* first check if "name" is absolute (and if so, try to open) */ + if (sys_open_absolute(name, ext, dirresult, nameresult, bin, &fd)) return fd; + /* otherwise "name" is relative; start trying in directories named in this and parent environments */ + for (t_canvas *y=x; y; y = y->dix->canvas) if (y->env) { + t_canvas *x2 = x; + while (x2 && x2->dix->canvas) x2 = x2->dix->canvas; + const char *dir = x2 ? canvas_getdir(x2)->name : "."; + for (t_namelist *nl = y->env->path; nl; nl = nl->nl_next) { + char *realname; + asprintf(&realname, "%s/%s", dir, nl->nl_string); + if ((fd = sys_trytoopenone(realname, name, ext, dirresult, nameresult, bin)) >= 0) return fd; + } + } + return open_via_path2((x ? canvas_getdir(x)->name : "."), name, ext, dirresult, nameresult, bin); +} +/* end miller 0.40 */ + +int canvas_open(t_canvas *x, const char *name, const char *ext, char *dirresult, char **nameresult, unsigned int size, int bin) { + char *dirr; + int r = canvas_open2(x,name,ext,&dirr,nameresult,bin); + if (dirr) {strncpy(dirresult,dirr,size); dirresult[size-1]=0; free(dirr);} + return r; +} + +static void canvas_with_reply (t_pd *x, t_symbol *s, int argc, t_atom *argv) { + if (!( argc>=2 && IS_A_FLOAT(argv,0) && IS_A_SYMBOL(argv,1) )) return; + pd_typedmess(x,atom_getsymbol(&argv[1]),argc-2,argv+2); + queue_put(manager->q,reply_new((short)atom_getfloat(&argv[0]),newest)); +} + +static void canvas_get_elapsed (t_canvas *x) { + canvas_each(y,x) { + sys_mgui(y,"elapsed","f",y->dix->elapsed / 800000000.0); + } +} + +static void g_canvas_setup() { + reply_class = class_new2("reply",0,reply_free,sizeof(t_reply),CLASS_GOBJ,"!"); +// class_setsavefn(reply_class, (t_savefn)reply_savefn); + declare_class = class_new2("declare",declare_new,declare_free,sizeof(t_declare),CLASS_NOINLET,"*"); + t_class *c = canvas_class = class_new2("canvas",0,canvas_free,sizeof(t_canvas),CLASS_NOINLET,""); + /* here is the real creator function, invoked in patch files + by sending the "canvas" message to #N, which is bound to pd_canvasmaker. */ + class_addmethod2(pd_canvasmaker._class,canvas_new,"canvas","*"); + class_addmethod2(c,canvas_restore,"restore","*"); + class_addmethod2(c,canvas_coords,"coords","*"); + class_addmethod2(c,canvas_setbounds,"bounds","ffff"); + class_addmethod2(c,canvas_obj,"obj","*"); + class_addmethod2(c,canvas_msg,"msg","*"); + class_addmethod2(c,canvas_floatatom,"floatatom","*"); + class_addmethod2(c,canvas_symbolatom,"symbolatom","*"); + class_addmethod2(c,canvas_text,"text","*"); + class_addmethod2(c,canvas_canvas,"graph","*"); + class_addmethod2(c,canvas_scalar,"scalar","*"); + class_addmethod2(c,canvas_declare,"declare","*"); + class_addmethod2(c,canvas_push,"push",""); + class_addmethod2(c,canvas_pop,"pop","F"); + class_addmethod2(c,canvas_loadbang,"loadbang",""); + class_addmethod2(c,canvas_relocate,"relocate","ss"); + class_addmethod2(c,canvas_vis,"vis","f"); + class_addmethod2(c,canvas_menu_open,"menu-open",""); + class_addmethod2(c,canvas_clear,"clear",""); + class_addcreator2("pd",subcanvas_new,"S"); + class_addcreator2("page",subcanvas_new,"S"); + class_addmethod2(c,canvas_dsp,"dsp",""); + class_addmethod2(c,canvas_rename_method,"rename","*"); + class_addcreator2("table",table_new,"SF"); + class_addmethod2(c,canvas_close,"close","F"); + class_addmethod2(c,canvas_redraw,"redraw",""); + class_addmethod2(c,canvas_find_parent,"findparent",""); + class_addmethod2(c,canvas_arraydialog,"arraydialog","sfff"); + class_addmethod2(c,canvas_connect,"connect","ffff"); + class_addmethod2(c,canvas_disconnect,"disconnect","ffff"); + class_addmethod2(c,canvas_write,"write","sS"); + class_addmethod2(c,canvas_read, "read","sS"); + class_addmethod2(c,canvas_mergefile, "mergefile","sS"); + class_addmethod2(c,canvas_savetofile,"savetofile","ss"); + class_addmethod2(c,canvas_saveto, "saveto","!"); + class_addmethod2(c,graph_bounds,"bounds","ffff"); + class_addmethod2(c,graph_xticks,"xticks","fff"); + class_addmethod2(c,graph_xlabel,"xlabel","*"); + class_addmethod2(c,graph_yticks,"yticks","fff"); + class_addmethod2(c,graph_ylabel,"ylabel","*"); + class_addmethod2(c,graph_array,"array","sfsF"); + //class_addmethod2(c,canvas_sort,"sort",""); +// dd-specific + class_addmethod2(c,canvas_object_moveto,"object_moveto","sff"); + class_addmethod2(c,canvas_object_delete,"object_delete","s"); + //class_addmethod2(c,canvas_object_insert,"object_insert","*"); + class_addmethod2(c,canvas_object_get_tips,"object_get_tips","s"); + class_addmethod2(c,canvas_object_help,"object_help","s"); + class_addmethod2(c,canvas_text_setto,"text_setto","*"); + class_addmethod2(c,canvas_with_reply,"with_reply","*"); + class_addmethod2(pd_canvasmaker._class,canvas_with_reply,"with_reply","*"); + class_addmethod2(c,canvas_get_elapsed,"get_elapsed",""); + class_setnotice(c, canvas_notice); + class_setonsubscribe(c, canvas_onsubscribe); +} + +t_class *visualloader_class; + +static t_pd *visualloader_new(t_symbol *s, int argc, t_atom *argv) {return pd_new(visualloader_class);} +static void visualloader_free(t_pd *self) {free(self);} +static void copy_atoms(int argc, t_atom *argvdest, t_atom *argvsrc) {memcpy(argvdest,argvsrc,argc*sizeof(t_atom));} +static void visualloader_anything(t_gobj *self, t_symbol *s, int argc, t_atom *argv) { + int i=0,j=0; + //printf("visualloader_anything start newest=%p\n",newest); + while (jc=j-i; + copy_atoms(al->c,al->v,&argv[i]); + //printf("#V reading '%s':\n",s->name); + if (!newest) {error("#V: there is no newest object\n"); return;} + t_visual *h = ((t_gobj *)newest)->dix->visual; + if (h->exists(s)) { + //printf("'%s' exists, deleting\n",s->name); + free(h->get(s)); + } + h->set(s,al); + //fprintf(stderr,"visualloader... %p %d\n",newest,hash_size(h)); + j++; + if (jname); return; + t_binbuf *b = binbuf_new(); + binbuf_addv(b,"s",s); + newest = 0; + binbuf_eval(b,&pd_objectmaker,0,0); + if (!newest) {post("help: no such class '%s'",s->name); return;} + c = newest->_class; + pd_free(newest); + } + const char *hn = class_gethelpname(c); + char *buf; + bool suffixed = strcmp(hn+strlen(hn)-3, ".pd")==0; + asprintf(&buf,"%s%s",hn,suffixed?"":".pd"); + open_via_helppath(buf, c->externdir->name); + free(buf); +} + +extern "C" void glob_update_class_list (t_pd *self, t_symbol *cb_recv, t_symbol *cb_sel) { + t_symbol *k; t_class *v; + sys_gui("global class_list; set class_list {"); + hash_foreach(k,v,class_table) sys_vgui("%s ", ((t_symbol *)k)->name); + sys_gui("}\n"); + sys_vgui("%s %s\n",cb_recv->name, cb_sel->name); +} + +EXTERN t_class *glob_pdobject; + +t_pd *pd_new2(int argc, t_atom *argv) { + if (argv[0].a_type != A_SYMBOL) {error("pd_new2: start with symbol please"); return 0;} + pd_typedmess(&pd_objectmaker,argv[0].a_symbol,argc-1,argv+1); + return newest; +} +t_pd *pd_new3(const char *s) { + t_binbuf *b = binbuf_new(); + binbuf_text(b,(char *)s,strlen(s)); + t_pd *self = pd_new2(binbuf_getnatom(b),binbuf_getvec(b)); + binbuf_free(b); + return self; +} + +extern "C" void boxes_init() { + t_class *c; + c = boxes_class = class_new2("__boxes" ,0/*boxes_new*/ , boxes_free,sizeof(t_boxes),CLASS_GOBJ,""); + class_setnotice(c,t_notice(boxes_notice)); + c = gop_filtre_class = class_new2("__gop_filtre",0/*gop_filtre_new*/,gop_filtre_free,sizeof(t_boxes),CLASS_GOBJ,""); + class_setnotice(c,t_notice(gop_filtre_notice)); +} + +static void desire_setup() { + t_class *c; + s_empty = gensym("empty"); + s_Pd = gensym("Pd"); + s_pd = gensym("pd"); + manager_class = class_new2("__manager",manager_new,manager_free,sizeof(t_manager),0,"*"); + class_addanything(manager_class,manager_anything); + class_setnotice(manager_class,manager_notice); + manager = manager_new(0,0,0); +#define S(x) x##_setup(); + S(vinlet) S(voutlet) S(g_array) S(g_canvas) S(g_scalar) S(g_template) S(g_traversal) S(g_text) +#undef S + + c = bng_class = class_new2("bng",bng_new,iemgui_free,sizeof(t_bng),0,"*"); + iemgui_subclass(c); + class_addbang (c, bng_bang); + class_addfloat (c, bng_bang2); + class_addsymbol (c, bng_bang2); + class_addpointer (c, bng_bang2); + class_addlist (c, bng_bang2); + class_addanything(c, bng_bang2); + class_addmethod2(c,bng_reload,"reload","*"); + class_addmethod2(c,bng_loadbang,"loadbang",""); + class_addmethod2(c,bng_size,"size","*"); + class_addmethod2(c,bng_flashtime,"flashtime","*"); + class_addmethod2(c,iemgui_init,"init","f"); + class_setsavefn(c, (t_savefn)bng_savefn); + class_sethelpsymbol(c, gensym("bng")); + class_setfieldnames(c, "foo bar x1 y1 class w hold break isa snd rcv lab ldx ldy fstyle fs bcol fcol lcol"); + + c = toggle_class = class_new2("tgl",toggle_new,iemgui_free,sizeof(t_toggle),0,"*"); + class_addcreator2("toggle",toggle_new,"*"); + iemgui_subclass(c); + class_addbang(c, toggle_bang); + class_addfloat(c, toggle_float); + class_addmethod2(c,toggle_reload,"reload","*"); + class_addmethod2(c,toggle_loadbang,"loadbang",""); + class_addmethod2(c,toggle_set,"set","f"); + class_addmethod2(c,toggle_size,"size","*"); + class_addmethod2(c,iemgui_init,"init","f"); + class_addmethod2(c,toggle_nonzero,"nonzero","f"); + class_setsavefn(c, (t_savefn)toggle_savefn); + class_sethelpsymbol(c, gensym("toggle")); + + c = radio_class = class_new2("radio",radio_new,iemgui_free,sizeof(t_radio),0,"*"); + iemgui_subclass(c); + class_addbang(c, radio_bang); + class_addfloat(c, radio_float); + class_addmethod2(c,radio_reload, "reload","*"); + class_addmethod2(c,radio_loadbang, "loadbang",""); + class_addmethod2(c,radio_set, "set","f"); + class_addmethod2(c,radio_size, "size","f"); + class_addmethod2(c,iemgui_init, "init","f"); + class_addmethod2(c,radio_fout, "fout","f"); + class_addmethod2(c,radio_number, "number","f"); + class_addmethod2(c,radio_orient,"orient","f"); + class_addmethod2(c,radio_single_change, "single_change",""); + class_addmethod2(c,radio_double_change, "double_change",""); + sym_hdl = gensym("hdl"); sym_hradio = gensym("hradio"); + sym_vdl = gensym("vdl"); sym_vradio = gensym("vradio"); + class_setsavefn(c,(t_savefn)radio_savefn); + class_sethelpsymbol(c, gensym("hradio")); + class_addcreator2("hradio",radio_new,"*"); + class_addcreator2("vradio",radio_new,"*"); + class_addcreator2("hdl",radio_new,"*"); + class_addcreator2("vdl",radio_new,"*"); + class_addcreator2("rdb",radio_new,"*"); + class_addcreator2("radiobut",radio_new,"*"); + class_addcreator2("radiobutton",radio_new,"*"); + + c = slider_class = class_new2("slider",slider_new,iemgui_free,sizeof(t_slider),0,"*"); + class_addcreator2("hslider",slider_new,"*"); + class_addcreator2("vslider",slider_new,"*"); + class_addcreator2("hsl" ,slider_new,"*"); + class_addcreator2("vsl" ,slider_new,"*"); + + iemgui_subclass(c); + class_addbang(c,slider_bang); + class_addfloat(c,slider_float); + class_addmethod2(c,slider_reload,"reload","*"); + class_addmethod2(c,slider_loadbang,"loadbang",""); + class_addmethod2(c,slider_set,"set","f"); + class_addmethod2(c,slider_size,"size","*"); + class_addmethod2(c,slider_range,"range","ff"); + class_addmethod2(c,slider_log,"log",""); + class_addmethod2(c,slider_lin,"lin",""); + class_addmethod2(c,iemgui_init,"init","f"); + class_addmethod2(c,slider_steady,"steady","f"); + class_addmethod2(c,slider_orient,"orient","f"); + sym_vsl = gensym("vsl"); + sym_vslider = gensym("vslider"); + class_setsavefn(c,(t_savefn)slider_savefn); + class_sethelpsymbol(c, gensym("hslider")); + + c = nbx_class = class_new2("nbx",nbx_new,iemgui_free,sizeof(t_nbx),0,"*"); + iemgui_subclass(c); + class_addbang(c,nbx_bang); + class_addfloat(c,nbx_float); + class_addlist(c, nbx_list); + class_addmethod2(c,nbx_reload,"reload","*"); + class_addmethod2(c,nbx_loadbang,"loadbang",""); + class_addmethod2(c,nbx_set,"set","f"); + class_addmethod2(c,nbx_size,"size","*"); + class_addmethod2(c,nbx_range,"range","*"); + class_addmethod2(c,nbx_log,"log",""); + class_addmethod2(c,nbx_lin,"lin",""); + class_addmethod2(c,iemgui_init,"init","f"); + class_addmethod2(c,nbx_log_height,"log_height","f"); + class_setsavefn(c,(t_savefn)nbx_savefn); + class_sethelpsymbol(c, gensym("numbox2")); + + c = cnv_class = class_new2("cnv",cnv_new,iemgui_free,sizeof(t_cnv),CLASS_NOINLET,"*"); + class_addmethod2(c,cnv_reload,"reload","*"); + class_addmethod2(c,cnv_size,"size","*"); + class_addmethod2(c,cnv_vis_size,"vis_size","*"); + class_addmethod2(c,cnv_get_pos,"get_pos",""); + iemgui_subclass(c); + class_setsavefn(c,(t_savefn)cnv_savefn); + class_sethelpsymbol(c, gensym("my_canvas")); + + c = vu_class = class_new2("vu",vu_new,iemgui_free,sizeof(t_vu),0,"*"); + iemgui_subclass(c); + class_addbang(c,vu_bang); + class_addfloat(c,vu_float0); + class_addmethod2(c,vu_float1,"ft1","f"); + class_addmethod2(c,vu_reload,"reload","*"); + class_addmethod2(c,vu_size,"size","*"); + class_addmethod2(c,vu_scale,"scale","F"); + class_setsavefn(c,(t_savefn)vu_savefn); + class_sethelpsymbol(c, gensym("vu")); + + visualloader_class = class_new2("#V",visualloader_new,visualloader_free,sizeof(t_object),CLASS_GOBJ,"*"); + class_addanything(visualloader_class,visualloader_anything); + pd_bind(pd_new(visualloader_class),gensym("#V")); +} + +/* ---------------------------------------------------------------- */ +/* formerly m_glob.c */ + +t_class *glob_pdobject; +static t_class *maxclass; + +#define IGN(sym) if (s==gensym(sym)) return; +void max_default(t_pd *x, t_symbol *s, int argc, t_atom *argv) { + IGN("audioindev"); + IGN("audiooutdev"); + IGN("audioininfo"); + IGN("audiooutinfo"); + IGN("testaudiosettingresult"); + IGN("audiodevice"); + IGN("xrun"); + IGN("audio_started"); + IGN("sys_lock_timeout"); + IGN("midiindev"); + IGN("midioutdev"); + IGN("midicurrentindev"); + IGN("midicurrentoutdev"); + IGN("audiocurrentininfo"); + IGN("audiocurrentoutinfo"); + IGN("asiolatency"); + startpost("%s: unknown message %s ", class_getname(pd_class(x)), s->name); + std::ostringstream buf; + for (int i = 0; i < argc; i++) {buf << " "; atom_ostream(argv+i,buf);} + post("%s",buf.str().data()); + endpost(); +} + +static void openit(const char *dirname, const char *filename) { + char *dirbuf; + char *nameptr; + int fd = open_via_path2(dirname,filename,"",&dirbuf,&nameptr,0); + if (!fd) {error("%s: can't open", filename); return;} + close(fd); + glob_evalfile(0, gensym(nameptr), gensym(dirbuf)); + free(dirbuf); +} + +extern "C" t_socketreceiver *netreceive_newest_receiver(t_text *x); + +/* this should be rethought for multi-client */ +void glob_initfromgui(void *dummy, t_symbol *s) { + char buf[256], buf2[256]; + char cwd[666]; + getcwd(cwd,665); + sys_socketreceiver=netreceive_newest_receiver(sys_netreceive); + sys_vgui("%s",lost_posts.str().data()); + /* load dynamic libraries specified with "-lib" args */ + for (t_namelist *nl=sys_externlist; nl; nl = nl->nl_next) + if (!sys_load_lib(0, nl->nl_string)) + post("%s: can't load library", nl->nl_string); + /* open patches specified with "-open" args */ + for (t_namelist *nl=sys_openlist; nl; nl = nl->nl_next) openit(cwd, nl->nl_string); + namelist_free(sys_openlist); + sys_openlist = 0; + /* send messages specified with "-send" args */ + for (t_namelist *nl=sys_messagelist; nl; nl = nl->nl_next) { + t_binbuf *b = binbuf_new(); + binbuf_text(b, nl->nl_string, strlen(nl->nl_string)); + binbuf_eval(b, 0, 0, 0); + binbuf_free(b); + } + namelist_free(sys_messagelist); + sys_messagelist = 0; + sys_get_audio_apis(buf); + sys_get_midi_apis(buf2); + sys_vgui("pd_startup {%s} %s %s\n", pd_version, buf, buf2); +/* + fprintf(stdout,"This line was printed on stdout\n"); + fprintf(stderr,"This line was printed on stderr\n"); +*/ +} + +void glob_meters(void *dummy, t_floatarg f); +void glob_audiostatus(void *dummy); +void glob_audio_properties(t_pd *dummy, t_floatarg flongform); +void glob_audio_dialog(t_pd *dummy, t_symbol *s, int argc, t_atom *argv); +void glob_audio_setapi(t_pd *dummy, t_floatarg f); +void glob_midi_properties(t_pd *dummy, t_floatarg flongform); +void glob_midi_dialog(t_pd *dummy, t_symbol *s, int argc, t_atom *argv); +void glob_midi_setapi(t_pd *dummy, t_floatarg f); +void glob_start_path_dialog(t_pd *dummy, t_floatarg flongform); +void glob_path_dialog(t_pd *dummy, t_symbol *s, int argc, t_atom *argv); +void glob_start_startup_dialog(t_pd *dummy, t_floatarg flongform); +void glob_startup_dialog(t_pd *dummy, t_symbol *s, int argc, t_atom *argv); +void glob_ping(t_pd *dummy); +extern "C" { +void glob_finderror(t_pd *dummy); +}; +/* tb: message-based audio configuration { */ +void glob_audio_testaudiosetting(t_pd * dummy, t_symbol *s, int ac, t_atom *av); +void glob_audio_getaudioindevices(t_pd * dummy, t_symbol *s, int ac, t_atom *av); +void glob_audio_getaudiooutdevices(t_pd * dummy, t_symbol *s, int ac, t_atom *av); +void glob_audio_getaudioininfo(t_pd * dummy, t_float f); +void glob_audio_getaudiooutinfo(t_pd * dummy, t_float f); +//void glob_audio_samplerate(t_pd * dummy, t_float f); +//void glob_audio_delay(t_pd * dummy, t_float f); +//void glob_audio_dacblocksize(t_pd * dummy, t_float f); +//void glob_audio_scheduler(t_pd * dummy, t_float f); +void glob_audio_device(t_pd * dummy, t_symbol *s, int argc, t_atom *argv); +//void glob_audio_device_in(t_pd * dummy, t_symbol *s, int argc, t_atom *argv); +//void glob_audio_device_out(t_pd * dummy, t_symbol *s, int argc, t_atom *argv); +void glob_audio_getcurrent_devices (); +void glob_audio_asio_latencies(t_pd * dummy, t_float f); +void glob_midi_getindevs( t_pd *dummy, t_symbol *s, int ac, t_atom *av); +void glob_midi_getoutdevs(t_pd *dummy, t_symbol *s, int ac, t_atom *av); +void glob_midi_getcurrentindevs(t_pd *dummy); +void glob_midi_getcurrentoutdevs(t_pd *dummy); +/* tb } */ + +static void glob_object_table() { + t_symbol *s_inlet = gensym("inlet"); + t_symbol *s___list = gensym("__list"); + size_t inlets=0, lists=0, zombies=0; + t_pd *k; long v; + post("object_table = {"); + hash_foreach(k,v,object_table) { + t_pd *x = (t_pd *)k; + if (!(long)v&1) {zombies++; continue;} /* skip zombies */ + //post(" %p %ld %s",k,(long)v,x->_class->name->name); + if (x->_class->name == s_inlet) {inlets++; continue;} + if (x->_class->name == s___list) {lists++; continue;} + int nobs = x->_class->gobj ? ((t_gobj *)x)->dix->nobs : 0; + // this has been duplicated as the ostream operator of t_pd * (see above). + t_binbuf *b; + if (x->_class->patchable && (b = ((t_text *)x)->binbuf)) { + char *buf; int bufn; + binbuf_gettext(b,&buf,&bufn); + post(" %p %ld (%dobs) %s [%.*s]",k,(long)v,nobs,x->_class->name->name,bufn,buf); + } else post(" %p %ld (%dobs) %s",k,(long)v,nobs,x->_class->name->name); + } + post("} (%ld non-omitted objects, plus %ld [inlet], plus %ld [__list], plus %ld zombies)", + object_table->size()-inlets-lists-zombies,inlets,lists,zombies); +} + +extern t_class *glob_pdobject; +extern "C" void glob_init () { + /* can this one really be called "max"? isn't that a name conflict? */ + maxclass = class_new2("max",0,0,sizeof(t_pd),CLASS_DEFAULT,""); + class_addanything(maxclass, max_default); + pd_bind((t_pd *)&maxclass, gensym("max")); + + /* this smells bad... a conflict with [pd] subpatches */ + t_class *c = glob_pdobject = class_new2("pd",0,0,sizeof(t_pd),CLASS_DEFAULT,""); + class_addmethod2(c,glob_initfromgui, "init", "*"); + class_addmethod2(c,glob_setfilename, "filename", "ss"); + class_addmethod2(c,glob_evalfile, "open", "ss"); + class_addmethod2(c,glob_quit, "quit", ""); + class_addmethod2(c,glob_dsp, "dsp", "*"); + class_addmethod2(c,glob_meters, "meters", "f"); + class_addmethod2(c,glob_audiostatus, "audiostatus", ""); + class_addmethod2(c,glob_finderror, "finderror", ""); + class_addmethod2(c,glob_audio_properties, "audio-properties", "F"); + class_addmethod2(c,glob_audio_dialog, "audio-dialog", "*"); + class_addmethod2(c,glob_audio_setapi, "audio-setapi", "f"); + class_addmethod2(c,glob_midi_setapi, "midi-setapi", "f"); + class_addmethod2(c,glob_midi_properties, "midi-properties", "F"); + class_addmethod2(c,glob_midi_dialog, "midi-dialog", "*"); + class_addmethod2(c,glob_ping, "ping",""); + /* tb: message-based audio configuration { */ +// class_addmethod2(c,glob_audio_samplerate, "audio-samplerate", "F"); +// class_addmethod2(c,glob_audio_delay, "audio-delay", "F"); +// class_addmethod2(c,glob_audio_dacblocksize,"audio-dacblocksize", "F"); +// class_addmethod2(c,glob_audio_scheduler, "audio-scheduler", "F"); + class_addmethod2(c,glob_audio_device, "audio-device", "*"); +// class_addmethod2(c,glob_audio_device_in, "audio-device-in", "*"); +// class_addmethod2(c,glob_audio_device_out, "audio-device-out", "*"); + class_addmethod2(c,glob_audio_getaudioindevices, "getaudioindev", "*"); + class_addmethod2(c,glob_audio_getaudiooutdevices,"getaudiooutdev", "*"); + class_addmethod2(c,glob_audio_getaudioininfo, "getaudioininfo", "f"); + class_addmethod2(c,glob_audio_getaudiooutinfo, "getaudiooutinfo", "f"); + class_addmethod2(c,glob_audio_testaudiosetting, "testaudiosetting", "*"); + class_addmethod2(c,glob_audio_getcurrent_devices,"getaudiodevice", ""); + class_addmethod2(c,glob_audio_asio_latencies, "getasiolatencies", "F"); + class_addmethod2(c,glob_midi_getoutdevs,"getmidioutdev", "*"); + class_addmethod2(c,glob_midi_getindevs, "getmidiindev", "*"); + class_addmethod2(c,glob_midi_getcurrentoutdevs, "getmidicurrentoutdev", ""); + class_addmethod2(c,glob_midi_getcurrentindevs, "getmidicurrentindev", ""); + /* tb } */ +#ifdef UNIX + class_addmethod2(c,glob_watchdog, "watchdog", ""); +#endif + class_addmethod2(c,glob_update_class_list, "update-class-list", "ss"); + class_addmethod2(c,glob_update_class_info, "update-class-info", "sss"); + class_addmethod2(c,glob_update_path, "update-path", ""); + class_addmethod2(c,glob_help, "help", "s"); + class_addmethod2(c,glob_object_table,"object_table",""); + class_addanything(c, max_default); + pd_bind((t_pd *)&glob_pdobject, gensym("pd")); +} + +/* ---------------------------------------------------------------- */ +/* formerly s_print.c */ + +t_printhook sys_printhook; +int sys_printtostderr; + +static void dopost(const char *s) { + if (sys_printhook) sys_printhook(s); + else if (sys_printtostderr) fprintf(stderr, "%s", s); + else { + std::ostringstream t; + for(int i=0; s[i]; i++) { + if (strchr("\\\"[]$\n",s[i])) t << '\\'; + t << char(s[i]=='\n' ? 'n' : s[i]); + } + sys_vgui("pdtk_post \"%s\"\n",t.str().data()); + } +} + +void post(const char *fmt, ...) { + char *buf; va_list ap; va_start(ap, fmt); + size_t n = vasprintf(&buf, fmt, ap); va_end(ap); + buf=(char*)realloc(buf,n+2); strcpy(buf+n,"\n"); + dopost(buf); free(buf); +} +void startpost(const char *fmt, ...) { + char *buf; va_list ap; va_start(ap, fmt); + vasprintf(&buf, fmt, ap); va_end(ap); + dopost(buf); free(buf); +} + +void poststring(const char *s) {dopost(" "); dopost(s);} + +void postatom(int argc, t_atom *argv) { + std::ostringstream buf; + for (int i=0; i=0 ? pd_stack[pd_stackn-1].self : 0; +} +void error( const char *fmt, ...) {va_list ap; va_start(ap,fmt); verror(fmt,ap); va_end(ap);} +void pd_error(void *moot, const char *fmt, ...) {va_list ap; va_start(ap,fmt); verror(fmt,ap); va_end(ap);} + +void verbose(int level, const char *fmt, ...) { + char *buf; + va_list ap; + if (level>sys_verbose) return; + dopost("verbose("); + postfloat((float)level); + dopost("):"); + va_start(ap, fmt); + vasprintf(&buf,fmt,ap); + va_end(ap); + dopost(buf); + dopost("\n"); +} + +extern "C" void glob_finderror(t_pd *dummy) { + if (!error_object) {post("no findable error yet."); return;} + post("last trackable error was for object x%lx: %s", error_object, error_string); + sys_mgui(error_object,"show_error","S",error_string); + canvas_finderror(error_object); +} + +void bug(const char *fmt, ...) { + char *buf; + va_list ap; + dopost("bug: "); + va_start(ap, fmt); + vasprintf(&buf,fmt,ap); + va_end(ap); + dopost(buf); + free(buf); + dopost("\n"); +} + +static const char *errobject; +static const char *errstring; + +void sys_logerror (const char *object, const char *s){errobject=object; errstring = s;} +void sys_unixerror(const char *object) {errobject=object; errstring = strerror(errno);} + +void sys_ouch () { + if (*errobject) error("%s: %s", errobject, errstring); else error("%s", errstring); +} + +/* properly close all open root canvases */ +extern "C" void glob_closeall(void *dummy, t_floatarg fforce) { + foreach(x,windowed_canvases) canvas_close(x->first); +} + +/* ---------------------------------------------------------------- */ +/* formerly m_conf.c */ + +void builtins_setup (); +void builtins_dsp_setup (); +void desire_setup (); +void d_soundfile_setup (); +void d_ugen_setup (); + +extern "C" void conf_init () { + builtins_setup(); + builtins_dsp_setup(); + desire_setup(); + d_soundfile_setup(); + d_ugen_setup(); +} + +// and just to make some externs happy: +#define BYE error("%s unimplemented in desiredata!", __PRETTY_FUNCTION__); +extern "C" { + void glist_grab () {BYE} + void glist_xtopixels () {BYE} + void glist_ytopixels () {BYE} + void *glist_findrtext () {BYE return 0;} + void canvas_fixlinesfor () {BYE} + void class_setpropertiesfn () {BYE} + void glist_eraseiofor () {BYE} + void glist_getcanvas () {BYE} + void rtext_gettag () {BYE} + void class_setwidget () {BYE} + void glist_isvisible () {BYE} + void gobj_vis () {BYE} + void gfxstub_deleteforkey () {BYE} + void gfxstub_new () {BYE} + + //redundantwards-compatibility + void canvas_setcurrent (t_canvas *x) {pd_pushsym(x);} + void canvas_unsetcurrent(t_canvas *x) {pd_popsym(x);} +}; -- cgit v1.2.1