/****************************************************** * * propertybang - implementation file * * copyleft (c) IOhannes m zmölnig * * 2007:forum::für::umläute:2007 * * institute of electronic music and acoustics (iem) * ****************************************************** * * license: GNU General Public License v.2 * ******************************************************/ /* * this object outputs a bang when the savfn of the parent abstraction is called */ /* * LATER sketch: * monkey-patching the parent canvas so that stores a local function table * then modify this function-table so that the savefn points to us * on call: we call the parents savefunction and output bangs */ /* * TODO: how does this behave in sub-patches? * -> BUG: the depth should _really_ refer to the abstraction-depth * else we get weird duplicates (most likely due to the "$0" trick */ #include "m_pd.h" #include "g_canvas.h" /* ------------------------- helper methods for callbacks ---------------------------- */ typedef struct _savebangs_objlist { const t_pd*obj; struct _savebangs_objlist*next; } t_savebangs_objlist; typedef struct _savebangs_canvaslist { const t_pd*parent; t_savebangs_objlist*obj; struct _savebangs_canvaslist*next; } t_savebangs_canvaslist; static t_savebangs_canvaslist*s_canvaslist=0; static t_savebangs_canvaslist*findCanvas(const t_pd*parent) { t_savebangs_canvaslist*list=s_canvaslist; if(0==parent || 0==list) return 0; for(list=s_canvaslist; list; list=list->next) { if(parent == list->parent) { return list; } } return 0; } static t_savebangs_canvaslist*addCanvas(const t_pd*parent) { t_savebangs_canvaslist*list=findCanvas(parent); if(!list) { list=(t_savebangs_canvaslist*)getbytes(sizeof(t_savebangs_canvaslist)); list->parent=parent; list->obj=0; list->next=0; if(0==s_canvaslist) { /* new list */ s_canvaslist=list; } else { /* add to the end of existing list */ t_savebangs_canvaslist*dummy=s_canvaslist; while(dummy->next) dummy=dummy->next; dummy->next = list; } } return list; } static t_savebangs_objlist*objectsInCanvas(const t_pd*parent) { t_savebangs_canvaslist*list=findCanvas(parent); if(list) return list->obj; return 0; } static void addObjectToCanvas(const t_pd*parent, const t_pd*obj) { t_savebangs_canvaslist*p=addCanvas(parent); t_savebangs_objlist*list=0; t_savebangs_objlist*entry=0; if(!p || !obj) return; list=p->obj; if(list&&obj==list->obj) return; while(list && list->next) { if(obj==list->obj) /* obj already in list */ return; list=list->next; } /* we are at the end of the list that does not contain obj yet, so add it */ entry=(t_savebangs_objlist*)getbytes(sizeof(t_savebangs_objlist)); entry->obj=obj; entry->next=0; if(list) { list->next=entry; } else { p->obj=entry; } } static void removeObjectFromCanvas(const t_pd*parent, const t_pd*obj) { t_savebangs_canvaslist*p=findCanvas(parent); t_savebangs_objlist*list=0, *last=0, *next=0; if(!p || !obj)return; list=p->obj; if(!list) return; while(list && obj!=list->obj) { last=list; list=list->next; } if(!list) /* couldn't find this object */ return; next=list->next; if(last) last->next=next; else p->obj=next; freebytes((void*)list, sizeof(t_savebangs_objlist)); list=0; } static void removeObjectFromCanvases(const t_pd*obj) { t_savebangs_canvaslist*parents=s_canvaslist; while(parents) { removeObjectFromCanvas(parents->parent, obj); parents=parents->next; } } /* ------------------------- helper methods for savefunctions ---------------------------- */ typedef struct _savefuns { t_class*class; t_savefn savefn; struct _savefuns *next; } t_savefuns; static t_savefuns*s_savefuns=0; static t_savefn find_savefn(const t_class*class) { t_savefuns*fun=s_savefuns; if(0==s_savefuns || 0==class) return 0; for(fun=s_savefuns; fun; fun=fun->next) { if(class == fun->class) { return fun->savefn; } } return 0; } static void add_savefn(t_class*class) { if(0!=find_savefn(class)) { return; } else { t_savefuns*sfun=(t_savefuns*)getbytes(sizeof(t_savefuns)); sfun->class=class; sfun->savefn=class_getsavefn(class); sfun->next=0; if(0==s_savefuns) { s_savefuns=sfun; } else { t_savefuns*sfp=s_savefuns; while(sfp->next) sfp=sfp->next; sfp->next = sfun; } } } /* ------------------------- savefunctions ---------------------------- */ static void orig_savefn(t_gobj*z, t_binbuf*b) { t_class*class=z->g_pd; t_savefn savefn=find_savefn(class); if(savefn) { savefn(z, b); } } static void savebangs_bangem(t_savebangs_objlist*objs, int pst); static void savebangs_savefn(t_gobj*z, t_binbuf*b) { /* z is the parent abstraction; * we maintain a list of all [savebangs] within such each parent, in order to call all of them */ t_savebangs_objlist*obj=objectsInCanvas((t_pd*)z); savebangs_bangem(obj, 0); orig_savefn(z, b); savebangs_bangem(obj, 1); } /* ------------------------- savebangs ---------------------------- */ static t_class *savebangs_class; typedef struct _savebangs { t_object x_obj; t_outlet *x_pre, *x_post; t_canvas *x_parent; } t_savebangs; static void savebangs_bangs(t_savebangs*x, int pst) { if(!pst) outlet_bang(x->x_pre); else outlet_bang(x->x_post); } static void savebangs_bangem(t_savebangs_objlist*objs, int pst) { while(objs) { t_savebangs*x=(t_savebangs*)objs->obj; savebangs_bangs(x, pst); objs=objs->next; } } static void savebangs_mysavefn(t_gobj*z, t_binbuf*b) { t_savebangs*x=(t_savebangs*)z; int doit=(!x->x_parent); if(doit)savebangs_bangs(x, 0); orig_savefn(z, b); if(doit)savebangs_bangs(x, 1); } static void *savebangs_new(t_floatarg f) { t_savebangs *x = (t_savebangs *)pd_new(savebangs_class); t_glist *glist=(t_glist *)canvas_getcurrent(); t_canvas *canvas=(t_canvas*)glist_getcanvas(glist); t_class *class = 0; int depth=(int)f; if(depth<0)depth=0; if(depth) { depth--; while(depth && canvas) { canvas=canvas->gl_owner; depth--; } if(canvas) { class=((t_gobj*)canvas)->g_pd; add_savefn(class); class_setsavefn(class, savebangs_savefn); x->x_parent=canvas; } else { x->x_parent=0; } addObjectToCanvas((t_pd*)canvas, (t_pd*)x); } x->x_post=outlet_new(&x->x_obj, &s_bang); x->x_pre=outlet_new(&x->x_obj, &s_bang); return (x); } static void savebangs_free(t_savebangs *x) { /* unpatch the parent canvas */ removeObjectFromCanvases((t_pd*)x); } void savebangs_setup(void) { savebangs_class = class_new(gensym("savebangs"), (t_newmethod)savebangs_new, (t_method)savebangs_free, sizeof(t_savebangs), CLASS_NOINLET, A_DEFFLOAT, 0); add_savefn(savebangs_class); class_setsavefn(savebangs_class, savebangs_mysavefn); }