diff --git a/src/x_preset.c b/src/x_preset.c new file mode 100644 index 0000000000000000000000000000000000000000..2054d7d5d1179a2d702c326918d3193308db9a8d --- /dev/null +++ b/src/x_preset.c @@ -0,0 +1,1044 @@ +/* preset_node and preset_hub implementation by Ivica Ico Bukvic <ico@vt.edu> (c) 2012 + distributed under GPL v3 license or newer +*/ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include "m_pd.h" +#include "g_canvas.h" +#include "x_preset.h" +#include "s_stuff.h" + +#define DEBUG(x) + +//TODO: changes in order happen when cutting, undo cut, undo paste, +// delete, undo delete, to front, and to back +// this should trigger cascading check for all preset_nodes +// is there a way to limit it to ones that are affected? +// potentially... by taking all the ordinal values above the one moved +// e.g. if object 1,5 was moved, everything above it +// (1,5,1 or 1,6 etc.) needs to be reevaluated BEFORE +// reevaluating moved object (if applicable) +// this does not affect objects like 2 or 2,7 +// since it is a different branch altogether + +/* gl_loc int array explanation: + + Each number corresponds to object or parent's location starting with root window. + This ensures that each object has a unique ID within the preset even when using + multiple instances of the same abstraction within the same patch. + This also means that if the object has changed its location within the gl_list + (e.g. via to_front/to_back), all corresponding IDs have to be updated in the parent. + + EXAMPLE: + 1,4,7 means the preset_node is 8th object in the subpatch or abstraction that is a + child of 5th object in a parent patch, that is a child of 2nd object of the root + patch. +*/ + +//==================== forward declarations ========================// + +void preset_hub_add_a_node(t_preset_hub *h, t_preset_node *x); +void preset_hub_recall(t_preset_hub *h, t_float f); +void preset_hub_store(t_preset_hub *h, t_float f); +void preset_hub_add_a_node(t_preset_hub *h, t_preset_node *x); +void preset_hub_delete_a_node(t_preset_hub *h, t_preset_node *x); +void preset_node_seek_hub(t_preset_node *x); + +//======================== global vars =============================// + +/* following contains a linear list of all hubs and nodes which ensures easy access, + pairing, deletion, etc. of all nodes and hubs regardless whether they are a part of + subpatches, root canvases, or abstractions. +*/ + +t_glob_preset_node_list *gpnl; +t_glob_preset_hub_list *gphl; + +int glob_preset_node_list_add(t_preset_node *x) +{ + fprintf(stderr,"glob_preset_node_list_add\n"); + t_glob_preset_node_list *n1, *n2; + + if (!gpnl) { + gpnl = (t_glob_preset_node_list *)t_getbytes(sizeof(*gpnl)); + n2 = gpnl; + } else { + n1 = gpnl; + while(n1->gpnl_next) + n1 = n1->gpnl_next; + n2 = (t_glob_preset_node_list *)t_getbytes(sizeof(*n2)); + n1->gpnl_next = n2; + } + n2->gpnl_paired = 0; + n2->gpnl_node = x; + return(0); +} + +int glob_preset_node_list_delete(t_preset_node *x) +{ + fprintf(stderr,"glob_preset_node_list_delete\n"); + t_glob_preset_node_list *n1, *n2; + int found; + + if (!gpnl) + return(1); + + n1 = gpnl; + n2 = gpnl; + + // first check if we are the first gpnl + if (n2->gpnl_node == x) { + gpnl = n2->gpnl_next; + freebytes(n2, sizeof(*n2)); + } + else { + while (n2->gpnl_node != x && n2->gpnl_next) { + n1 = n2; + n2 = n2->gpnl_next; + } + if (n2->gpnl_node == x) { + n1->gpnl_next = n2->gpnl_next; + freebytes(n2, sizeof(*n2)); + } else { + // we should never get here + fprintf(stderr,"error, could not find appropriate gpnl to delete\n"); + return(1); + } + } + + return(0); +} + +int glob_preset_node_list_update_paired(t_preset_node *x, int paired) +{ + fprintf(stderr,"glob_preset_node_list_update_paired %d\n", paired); + t_glob_preset_node_list *n; + + if (!gpnl) + return(1); + + n = gpnl; + + while (n->gpnl_node != x && n->gpnl_next) { + n = n->gpnl_next; + } + if (n->gpnl_node == x) { + n->gpnl_paired = 1; + } + else { + // we should never get here + fprintf(stderr,"error, could not find appropriate gpnl to pair\n"); + return(1); + } + + return(0); +} + +void glob_preset_node_list_seek_hub(void) +{ + fprintf(stderr,"glob_preset_node_list_seek_hub\n"); + t_glob_preset_node_list *nl; + + if (gpnl) { + nl = gpnl; + while(nl) { + fprintf(stderr," seeking\n"); + if (!nl->gpnl_paired) { + preset_node_seek_hub(nl->gpnl_node); + } + nl = nl->gpnl_next; + } + } +} + +int glob_preset_hub_list_add(t_preset_hub *x) +{ + fprintf(stderr,"glob_preset_hub_list_add\n"); + t_glob_preset_hub_list *h1, *h2; + + if (!gphl) { + gphl = (t_glob_preset_hub_list *)t_getbytes(sizeof(*gphl)); + h2 = gphl; + } else { + h1 = gphl; + while(h1->gphl_next) + h1 = h1->gphl_next; + h2 = (t_glob_preset_hub_list *)t_getbytes(sizeof(*h2)); + h1->gphl_next = h2; + } + h2->gphl_hub = x; + return(0); +} + +int glob_preset_hub_list_delete(t_preset_hub *x) +{ + fprintf(stderr,"glob_preset_hub_list_delete\n"); + t_glob_preset_hub_list *h1, *h2; + int found; + + if (!gphl) + return(1); + + h1 = gphl; + h2 = gphl; + + // first check if we are the first gpnl + if (h2->gphl_hub == x) { + gphl = h2->gphl_next; + freebytes(h2, sizeof(*h2)); + } + else { + while (h2->gphl_hub != x && h2->gphl_next) { + h1 = h2; + h2 = h2->gphl_next; + } + if (h2->gphl_hub == x) { + h1->gphl_next = h2->gphl_next; + freebytes(h2, sizeof(*h2)); + } + else { + // we should never get here + fprintf(stderr,"error, could not find appropriate gphl to delete\n"); + return(1); + } + } + + return(0); +} + +//====================== end global vars ===========================// + +//======================== preset_node =============================// + +// we declare this class as part of the g_canvas.h (to expose it to the rest of the code) + +static void preset_node_update_my_glist_location(t_preset_node *x) +{ + fprintf(stderr,"node_update_glist_location\n"); + // location is calculated in respect to the hub (if any), otherwise it is 0 + t_preset_hub *h; + t_canvas *c = x->pn_canvas; + int found = 0; + + // do all this only if we are already paired with a hub + if (x->pn_hub) { + + if (x->pn_old_gl_loc) free(x->pn_old_gl_loc); + x->pn_old_gl_loc = x->pn_gl_loc; + x->pn_old_gl_loc_length = x->pn_gl_loc_length; + + x->pn_gl_loc = NULL; + x->pn_gl_loc_length = 0; + + // let's try to find our hub (if any) + int depth = 0; + while (!found && c) { + if (c->gl_phub) { + h = c->gl_phub; + while (h) { + if (!strcmp(c->gl_phub->ph_name->s_name, x->pn_hub_name->s_name)) { + found = 1; + break; + } + h = h->ph_next; + } + } + c = c->gl_owner; + if (c) + depth++; + } + + if (found) { + + fprintf(stderr," depth = %d\n", depth); + + // allocate depth array + x->pn_gl_loc = (int*)calloc(depth+1, sizeof(int)); + + // now let's figure out where each of the child gobj's location is + // starting from the back + int i = 0; // iteration within each gl_list + int j = 0; // number of gl_list to traverse + c = x->pn_canvas; + t_gobj *g = c->gl_list; + t_gobj *target = (t_gobj *)x; + + for (j = depth; j >= 0; j--) { + + while (g && g != target) // have to do sanity check for g in the event this is run at patch creation time + { + g = g->g_next; + i++; + fprintf(stderr," searching... %d\n", i); + } + // even if the g fails sanity check due to creation time, it will still land on the last created element whose + // pointer at this point is still null since this means this is being called at the end of the preset_node_new call + fprintf(stderr," location = %d %lx %lx\n", i, (t_int)g, (t_int)target); + x->pn_gl_loc[j] = i; + + // now readjust the target, c, g, and i variables for the next level up + if (c->gl_owner) { + target = (t_gobj *)c; + c = c->gl_owner; + g = c->gl_list; + i = 0; + } + } + x->pn_gl_loc_length = depth+1; + + fprintf(stderr," final structure:\n"); + for (j = 0; j < x->pn_gl_loc_length; j++) { + fprintf(stderr," %d: %d\n", j, x->pn_gl_loc[j]); + } + } else { + fprintf(stderr," preset_node: no matching hub %s found\n", x->pn_hub_name->s_name); + } + + // finally if this is the first time we are creating the object, old_location should be the same as the current location + if (x->pn_old_gl_loc_length == 0) { + x->pn_old_gl_loc = x->pn_gl_loc; + x->pn_old_gl_loc_length = x->pn_gl_loc_length; + } + } +} + +// this is called during the canvas creation (e.g. at load time) as part of pre-loadbang event, +// during object creation (in case it is manually created), as well as when hubs try to query +// nodes that have not been paired yet. +void preset_node_seek_hub(t_preset_node *x) +{ + fprintf(stderr,"preset_node_seek_hub\n"); + t_canvas *y = x->pn_canvas; + t_preset_hub *h; + + if (!x->pn_hub) { + fprintf(stderr," have to seek\n"); + while (!x->pn_hub && y) { + h = y->gl_phub; + while (h) { + if (h->ph_name->s_name == x->pn_hub_name->s_name) { + x->pn_hub = h; + fprintf(stderr," node found hub\n"); + // update our location in respect to the newfound hub + preset_node_update_my_glist_location(x); + // add a node on the hub's list of nodes and copy location to its struct + preset_hub_add_a_node(x->pn_hub, x); + // reflect that this node is paired in the global list of all existing nodes + glob_preset_node_list_update_paired(x, 1); + break; + } + h = h->ph_next; + } + y = y->gl_owner; + } + } +} + +static int preset_node_location_changed(t_preset_node *x) +{ + int i; + if (x->pn_old_gl_loc_length != x->pn_gl_loc_length) + return(1); + for (i = 0; i < x->pn_gl_loc_length; i++) { + if (x->pn_gl_loc[i] != x->pn_old_gl_loc[i]) + return(1); + } + return(0); +} + +static void preset_node_bang(t_preset_node *x) +{ + SETSYMBOL(&x->pn_val, gensym("bang")); +} + +static void preset_node_float(t_preset_node *x, t_float f) +{ + SETFLOAT(&x->pn_val, f); +} + +static void preset_node_symbol(t_preset_node *x, t_symbol *s) +{ + SETSYMBOL(&x->pn_val, s); +} + + //=============== following functions are for interaction with the hub/pd =================// + +int preset_node_check_location(t_preset_node *x) +{ + int result; + if (x->pn_hub) { + preset_node_update_my_glist_location(x); + return(preset_node_location_changed(x)); + } + return(0); +} + +void preset_node_request_hub_recall(t_preset_node *x, t_float f) +{ + if (x->pn_hub) + preset_hub_recall(x->pn_hub, f); +} + +void preset_node_request_hub_store(t_preset_node *x, t_float f) +{ + if (x->pn_hub) + preset_hub_store(x->pn_hub, f); +} + +t_atom preset_node_request_value(t_preset_node *x) +{ + return(x->pn_val); +} + +void preset_node_set_and_output_value(t_preset_node *x, t_atom *val) +{ + if (val->a_type == A_FLOAT) { + preset_node_float(x, val->a_w.w_float); + outlet_float(x->pn_outlet, x->pn_val.a_w.w_float); + } else if (val->a_type == A_SYMBOL) { + preset_node_symbol(x, val->a_w.w_symbol); + outlet_symbol(x->pn_outlet, x->pn_val.a_w.w_symbol); + } else + pd_error(x, "error: preset_node currently supports only floats and symbols"); +} + + //==================== end functions are for interaction with the hub =====================// + +static void *preset_node_new(t_symbol *s, int argc, t_atom *argv) +{ + fprintf(stderr,"===preset_node_new===\n"); + t_glist *glist=(t_glist *)canvas_getcurrent(); + t_canvas *canvas = (t_canvas *)glist_getcanvas(glist); + + // first check if the mandatory argument is sane, otherwise bail out + if (!(argc > 0 && argv[0].a_type == A_SYMBOL)) { + pd_error(canvas, "preset_node requires alphanumeric name as its argument"); + return(NULL); + } + + t_preset_node *x = (t_preset_node *)pd_new(preset_node_class); + + // read basic creation arguments + x->pn_hub_name = (t_symbol *)atom_getsymbol(&argv[0]); + //fprintf(stderr,"hub name %s\n", x->pn_hub_name->s_name); + + x->pn_canvas = canvas; + t_canvas *y = x->pn_canvas; + t_preset_hub *h; + + x->pn_hub = NULL; + x->pn_gl_loc_length = 0; + x->pn_old_gl_loc_length = 0; + x->pn_outlet = outlet_new(&x->pn_obj, 0); + + glob_preset_node_list_add(x); + + // now we seek our potential hub (if it exists), this is also called as part of + // pre-loadbang if the patch is being loaded (this one is for manually created objects) + // do this only if we are not undoing, otherwise, we'll have the undo do it for us + // once it has repositioned objects to their original locations + if (!we_are_undoing) + preset_node_seek_hub(x); + + return(x); +} + +static void preset_node_free(t_preset_node* x) +{ + // deactivate a node on the hub's list of nodes + if (x->pn_hub) + preset_hub_delete_a_node(x->pn_hub, x); + glob_preset_node_list_delete(x); + + // the two arrays can point to same locations so here we prevent double free + // (this is only possible initially when the object is first instantiated) + if (x->pn_gl_loc != x->pn_old_gl_loc) { + free(x->pn_gl_loc); + free(x->pn_old_gl_loc); + } else { + free(x->pn_gl_loc); + } +} + +void preset_node_setup(void) +{ + preset_node_class = class_new(gensym("preset_node"), + (t_newmethod)preset_node_new, (t_method)preset_node_free, + sizeof(t_preset_node), 0, A_GIMME, 0); + + // have to call this after everything has loaded, otherwise, the hub may not yet exist + // we do this using pre-loadbang call that is issued before other loadbangs, otherwise + // out-of-order loadbangs can issue preset recall before the preset has been properly + // configured + class_addmethod(preset_node_class, (t_method)preset_node_seek_hub, + gensym("pre-loadbang"), 0); + + class_addmethod(preset_node_class, (t_method)preset_node_request_hub_recall, + gensym("recall"), A_DEFFLOAT, 0); + class_addmethod(preset_node_class, (t_method)preset_node_request_hub_store, + gensym("store"), A_DEFFLOAT, 0); + + class_addbang(preset_node_class, preset_node_bang); + class_addfloat(preset_node_class, preset_node_float); + class_addsymbol(preset_node_class, preset_node_symbol); + + // TODO: we may add this later if necessary + //class_addlist(preset_node_class, preset_node_list); +} + +//====================== end preset_node ===========================// + +//======================== preset_hub ==============================// + +// we declare this class as part of the g_canvas.h (to expose it to the rest of the code) + +typedef enum +{ + H_NONE, + H_NODE, + H_PRESET, + H_PRESET_DATA +} t_hub_parser; + +/* syntax for saving a preset hub (all in a single line, here it is + separated for legibility sakes): + #X obj X Y preset_hub NAME %hidden% + %node% LOCATION_ARRAY_(INT) 1 2 3 etc. + %preset% 1 %float%/%symbol% (e.g. number or symbol, later also possibly a list) + %preset% 2 4 + etc + %node% 2 0 + %preset% 1 5.561 + %preset% 3 7.00001 + %preset% 5 some_text + ; + + NB: %hidden% is used to hide optional arguments following that argument + it can be used by any other object as well +*/ + +void preset_hub_save(t_gobj *z, t_binbuf *b) +{ + fprintf(stderr,"preset_hub_save\n"); + int i; + t_preset_hub_data *phd; + t_node_preset *np; + + t_preset_hub *x = (t_preset_hub *)z; + + binbuf_addv(b, "ssiiss", gensym("#X"), gensym("obj"), (int)x->ph_obj.te_xpix, + (int)x->ph_obj.te_ypix, gensym("preset_hub"), x->ph_name); + + if (x->ph_invis) + binbuf_addv(b, "i", (int)x->ph_invis); + + binbuf_addv(b, "s", gensym("%hidden%")); + + phd = x->ph_data; + while (phd) { + fprintf(stderr," saving phd\n"); + // designate a node and state whether it is active or disabled + // (disabled nodes are ones that have presets saved but have been deleted since-- + // we keep these in the case of undo actions during the session that may go beyond + // saving something into a file) + binbuf_addv(b, "s", gensym("%node%")); + + // gather info about the length of the node's location and store it + for (i = 0; i < phd->phd_pn_gl_loc_length; i++) { + binbuf_addv(b,"i", (int)phd->phd_pn_gl_loc[i]); + } + + // save preset data + np = phd->phd_npreset; + while (np) { + binbuf_addv(b, "si", gensym("%preset%"), (int)np->np_preset); + if (np->np_val.a_type == A_FLOAT) + binbuf_addv(b, "f", np->np_val.a_w.w_float); + else if (np->np_val.a_type == A_SYMBOL) + binbuf_addv(b, "s", np->np_val.a_w.w_symbol); + np = np->np_next; + } + + phd = phd->phd_next; + } + fprintf(stderr," done\n"); + binbuf_addv(b, ";"); +} + +void preset_hub_bang(t_preset_hub *x) +{ + t_atom ap[1]; + SETFLOAT(ap+0, (t_float)x->ph_preset); + outlet_anything(x->ph_outlet, gensym("current"), 1, ap); +} + +void preset_hub_recall(t_preset_hub *x, t_float f) +{ + fprintf(stderr,"hub_recall\n"); + t_atom ap[2]; + t_preset_hub_data *hd; + t_node_preset *np; + t_float valid = 0; + + if (f>=0) { + // check if preset exists and apply it + if (x->ph_data) { + hd = x->ph_data; + while (hd) { + fprintf(stderr," searching\n"); + // now check if the object is active (node pointer is not NULL) + if (hd->phd_node) { + fprintf(stderr," object active\n"); + if (hd->phd_npreset) { + fprintf(stderr," object has presets\n"); + np = hd->phd_npreset; + while (np) { + fprintf(stderr," searching presets\n"); + if (np->np_preset == (int)f) { + valid = 1; + fprintf(stderr," valid %d %g\n", (hd->phd_node ? 1:0), np->np_val.a_w.w_float); + preset_node_set_and_output_value(hd->phd_node, &np->np_val); + break; + } + np = np->np_next; + } + } + } + hd = hd->phd_next; + } + } + if (valid) + x->ph_preset = f; + SETFLOAT(ap+0, f); + SETFLOAT(ap+1, (t_float)valid); + outlet_anything(x->ph_outlet, gensym("recall"), 2, ap); + } +} + +void preset_hub_store(t_preset_hub *x, t_float f) +{ + fprintf(stderr,"preset_hub_store\n"); + t_atom ap[1]; + t_preset_hub_data *hd1; + t_node_preset *np1, *np2; + int overwrite; + t_atom val; + + np1 = NULL; + np2 = NULL; + + if (f>=0) { + //check if there are any existing nodes + if (x->ph_data) { + hd1 = x->ph_data; + while (hd1) { + fprintf(stderr," analyzing phd\n"); + if (hd1->phd_node) { + fprintf(stderr," node is active\n"); + // only if the node is active (not NULL/disabled) + overwrite = 0; + if (hd1->phd_npreset) { + // if this node has already pre-existing presets + // first check if there is already one we need to overwrite + np2 = hd1->phd_npreset; + while (np2) { + if (np2->np_preset == (int)f) { + overwrite = 1; + break; + } + np1 = np2; + np2 = np2->np_next; + } + } + + if (!overwrite) { + // we need to create a new preset (this is also true if hd1->phd_npreset is NULL) + np2 = (t_node_preset *)t_getbytes(sizeof(*np2)); + if (np1) + np1->np_next = np2; + else hd1->phd_npreset = np2; + np2->np_preset = (int)f; + } + + val = preset_node_request_value(hd1->phd_node); + if (val.a_type == A_FLOAT) + SETFLOAT(&np2->np_val, atom_getfloat(&val)); + else if (val.a_type == A_SYMBOL) + SETSYMBOL(&np2->np_val, atom_getsymbol(&val)); + + // finally if this is the first preset, hd1->phd_npreset will be NULL so, + // let's have it point to the newly created n2 + if (!hd1->phd_npreset) + hd1->phd_npreset = np2; + } + hd1 = hd1->phd_next; + } + } + SETFLOAT(ap+0, f); + outlet_anything(x->ph_outlet, gensym("store"), 1, ap); + } +} + +int preset_hub_compare_loc(int *h_loc, int h_loc_length, int *n_loc, int n_loc_length) +{ + int i; + if (h_loc_length != n_loc_length) + return(1); + for (i = 0; i < h_loc_length; i++) { + if (h_loc[i] != n_loc[i]) + return(1); + } + return(0); +} + +void preset_hub_add_a_node(t_preset_hub *h, t_preset_node *x) +{ + t_preset_hub_data *hd1, *hd2; + int found = 0; + int i; + + hd1 = NULL; + hd2 = NULL; + + fprintf(stderr,"preset_hub_add_a_node\n"); + if (h->ph_data) { + // first check for disabled nodes and reenable them if they match location + hd1 = h->ph_data; + while (hd1) { + if (!hd1->phd_node) { + // only if the node is disabled (NULL) + if (!preset_hub_compare_loc(hd1->phd_pn_gl_loc, hd1->phd_pn_gl_loc_length, x->pn_gl_loc, x->pn_gl_loc_length)) + { + // if this hub node data's location matches that of the node + fprintf(stderr," found disabled -> enabling\n"); + found = 1; + hd1->phd_node = x; + break; + } + } + hd1 = hd1->phd_next; + } + } + + if (!found) { + // we have no stored node data (or none that match node's location) so let's create a new one + fprintf(stderr," creating a new\n"); + // create a new data struct + hd2 = (t_preset_hub_data *)t_getbytes(sizeof(*hd2)); + // reconstruct the dynamic location array + hd2->phd_pn_gl_loc_length = x->pn_gl_loc_length; + hd2->phd_pn_gl_loc = (int*)calloc(hd2->phd_pn_gl_loc_length, sizeof(int)); + for (i=0; i < hd2->phd_pn_gl_loc_length; i++) { + fprintf(stderr," loc %d\n", x->pn_gl_loc[i]); + hd2->phd_pn_gl_loc[i] = x->pn_gl_loc[i]; + } + // assign node value + hd2->phd_node = x; + + // adjust pointers + if (!h->ph_data) { + h->ph_data = hd2; + } else { + hd1 = h->ph_data; + while (hd1->phd_next) { + hd1 = hd1->phd_next; + } + hd1->phd_next = hd2; + } + } +} + +void preset_hub_delete_a_node(t_preset_hub *h, t_preset_node *x) +{ + t_preset_hub_data *hd1; + hd1 = NULL; + + fprintf(stderr,"preset_hub_delete_a_node\n"); + if (h->ph_data) { + // check for enabled nodes only + hd1 = h->ph_data; + while (hd1) { + if (hd1->phd_node && hd1->phd_node == x) { + // only if the node is enabled and matches ours + fprintf(stderr," found enabled -> disabling\n"); + hd1->phd_node = NULL; + break; + } + hd1 = hd1->phd_next; + } + } +} + +static void *preset_hub_new(t_symbol *s, int argc, t_atom *argv) +{ + fprintf(stderr,"===preset_hub_new===\n"); + t_glob_preset_node_list *nl; + t_glob_preset_hub_list *hl; + t_preset_hub_data *hd1, *hd2; + t_node_preset *np1, *np2; + t_hub_parser h_cur = H_NONE; + + t_preset_hub *x; + t_symbol *name; + int i, pos; + t_glist *glist=(t_glist *)canvas_getcurrent(); + t_canvas *canvas = (t_canvas *)glist_getcanvas(glist); + + pos = 0; // position within argc + + // first check if the mandatory argument is sane, otherwise bail out + if (!(argc > 0 && argv[0].a_type == A_SYMBOL)) { + pd_error(canvas, "preset_hub requires alphanumeric name as its argument"); + return(NULL); + } + + /* + NO! WE CAN HAVE MULTIPLE HUBS WITH SAME NAMES, BUT IT IS USER'S RESPONSIBILITY TO KEEP THEM SANE + THIS IS BECAUSE MULTIPLE INSTANCES OF ABSTRACTIONS WILL HAVE IDENTICAL NAMES!!! + // now let's check if there is already a pre-existing hub with the same name + // if so, fail at creating the object and report an error to the user + if (gphl) { + hl = gphl; + while(hl) { + if (!strcmp(hl->gphl_hub->ph_name->s_name, atom_getsymbol(&argv[0])->s_name)) { + pd_error(canvas, "preset_hub with the name %s already exists... hub creation failed", atom_getsymbol(&argv[0])->s_name); + return(NULL); + } + hl = hl->gphl_next; + } + } + */ + +/*#ifdef PDL2Ork + if (sys_k12_mode && !strcmp(atom_getsymbol(&argv[0])->s_name,"k12")) { + // exception, we are the root preset for k12 that is pre-made for us + } +#endif*/ + x = (t_preset_hub *)pd_new(preset_hub_class); + x->ph_invis = 0; + + // read basic creation arguments + x->ph_name = (t_symbol *)atom_getsymbol(&argv[0]); + pos++; + if (argc > 1 && argv[1].a_type == A_FLOAT) { + x->ph_invis = (int)atom_getfloat(&argv[1]); + if (x->ph_invis < 0) x->ph_invis = 0; + /*level = x->ph_level; + while (level > 0 && canvas->gl_owner) { + if (canvas->gl_owner) canvas = canvas->gl_owner; + level--; + }*/ + pos++; + } + fprintf(stderr,"hub name %s invis %d\n", x->ph_name->s_name, (int)x->ph_invis); + + pos++; // one more time to move ahead of the %hidden% tag + + // assign default x and y position + x->ph_obj.te_xpix = 0; + x->ph_obj.te_ypix = 0; + + x->ph_preset = -1; + + x->ph_canvas = canvas; + + // add us to the global hub list + glob_preset_hub_list_add(x); + + // add hub to the canvas pointer + if (!canvas->gl_phub) + canvas->gl_phub = x; + else { + t_preset_hub *gl_phub = canvas->gl_phub; + while (gl_phub->ph_next) + gl_phub = gl_phub->ph_next; + gl_phub->ph_next = x; + } + x->ph_next = NULL; + + hd1 = NULL; + hd2 = NULL; + np1 = NULL; + np2 = NULL; + + // load the data from the buffer and build the preset db + if (pos < argc) { + for (i=pos; i < argc; i++) { + fprintf(stderr," position: %d\n", i); + // SYMBOL ANALYSIS + if (argv[i].a_type == A_SYMBOL) { + fprintf(stderr," data = %s\n", atom_getsymbol(&argv[i])->s_name); + if (!strcmp(atom_getsymbol(&argv[i])->s_name, "%node%")) { + // beginning of a new node + fprintf(stderr," new node\n"); + hd2 = (t_preset_hub_data *)t_getbytes(sizeof(*hd2)); + hd2->phd_pn_gl_loc_length = 0; + if (hd1) { + hd1->phd_next = hd2; + fprintf(stderr," new node\n"); + } + else { + x->ph_data = hd2; + fprintf(stderr," first node\n"); + } + hd1 = hd2; + np1 = NULL; // have to reset it so that new presets are not erroneously appended to previous node + h_cur = H_NODE; + } + else if (!strcmp(atom_getsymbol(&argv[i])->s_name, "%preset%")) { + // beginning of a new preset + fprintf(stderr," new preset\n"); + np2 = (t_node_preset *)t_getbytes(sizeof(*np2)); + if (np1) { + np1->np_next = np2; + fprintf(stderr," new preset\n"); + } + else { + hd2->phd_npreset = np2; + fprintf(stderr," first preset\n"); + } + np1 = np2; + h_cur = H_PRESET; + } + // SYMBOL DATA + else if (h_cur == H_PRESET_DATA) { + fprintf(stderr," sym data %s\n", atom_getsymbol(&argv[i])->s_name); + SETSYMBOL(&np2->np_val, atom_getsymbol(&argv[i])); + } + } + // FLOAT ANALYSIS + else if (argv[i].a_type == A_FLOAT) { + fprintf(stderr," data = %g\n", atom_getfloat(&argv[i])); + if (h_cur == H_NODE) { + // node location + hd2->phd_pn_gl_loc_length++; + // reconstruct the dynamic location array + if (!hd2->phd_pn_gl_loc) + hd2->phd_pn_gl_loc = (int*)calloc(1, sizeof(int)); + else hd2->phd_pn_gl_loc = (int*)realloc(hd2->phd_pn_gl_loc, hd2->phd_pn_gl_loc_length*sizeof(int)); + hd2->phd_pn_gl_loc[hd2->phd_pn_gl_loc_length-1] = (int)atom_getfloat(&argv[i]); + fprintf(stderr," loc = %d\n", hd2->phd_pn_gl_loc[hd2->phd_pn_gl_loc_length-1]); + } + else if (h_cur == H_PRESET) { + // preset number + fprintf(stderr," preset %g\n", atom_getfloat(&argv[i])); + np2->np_preset = (int)atom_getfloat(&argv[i]); + h_cur = H_PRESET_DATA; + } + else if (h_cur == H_PRESET_DATA) { + fprintf(stderr," preset data %g\n", atom_getfloat(&argv[i])); + SETFLOAT(&np2->np_val, atom_getfloat(&argv[i])); + } + } + } + } + x->ph_outlet = outlet_new(&x->ph_obj, 0); + + // let us pair with our nodes (if any) + // this is done node-side (node has to traverse canvases upwards to make sure + // multiple abstractions with same hubs/nodes do not trip over each other) + // do this only if we are not undoing, otherwise, we'll have the undo do it for us + // once it has repositioned objects to their original locations + if (!we_are_undoing) + glob_preset_node_list_seek_hub(); + + return(x); +} + +static void preset_hub_free(t_preset_hub* x) +{ + fprintf(stderr,"preset_hub_free\n"); + t_glob_preset_node_list *nl; + t_preset_hub_data *hd1, *hd2; + t_node_preset *np1, *np2; + t_preset_hub *h1, *h2; + + // inform all nodes that the hub is going bye-bye + if (gpnl) { + fprintf(stderr," we got gpnl\n"); + nl = gpnl; + while(nl) { + fprintf(stderr," analyzing gpnl entry %d\n", nl->gpnl_paired); + if (nl->gpnl_paired && nl->gpnl_node->pn_hub == x) { + // we only make the hub pointer null and leave location for undo/redo/save purposes + nl->gpnl_paired = 0; + nl->gpnl_node->pn_hub = NULL; + fprintf(stderr," removed gpnl reference\n"); + } + nl = nl->gpnl_next; + } + } + glob_preset_hub_list_delete(x); + + // remove the hub from the canvas pointer list + h2 = x->ph_canvas->gl_phub; + h1 = h2; + if (!h2->ph_next) { + // if there is only one hub on this canvas + x->ph_canvas->gl_phub = NULL; + } else { + while (h2 && h2 != x) { + h1 = h2; + h2 = h2->ph_next; + } + if (h2 != x) { + // this should never happen + pd_error(x, "preset_hub %s destructor was unable to find its canvas pointer", x->ph_name->s_name); + } else { + h1->ph_next = h2->ph_next; + } + } + + // deallocate all the dynamically-allocated memory + if (x->ph_data) { + hd1 = x->ph_data; + while (hd1) { + if (hd1->phd_npreset) { + np1 = hd1->phd_npreset; + while (np1) { + np2 = np1->np_next; + freebytes(np1, sizeof(*np1)); + np1 = np2; + } + } + hd2 = hd1->phd_next; + freebytes(hd1, sizeof(*hd1)); + hd1 = hd2; + } + } +} + +void preset_hub_setup(void) +{ + preset_hub_class = class_new(gensym("preset_hub"), + (t_newmethod)preset_hub_new, (t_method)preset_hub_free, + sizeof(t_preset_hub), 0, A_GIMME, 0); + + // have to call this after everything has loaded, otherwise, the hub may not yet exist + // we do this using pre-loadbang call that is issued before other loadbangs, otherwise + // out-of-order loadbangs can issue preset recall before the preset has been properly + // configured + class_addmethod(preset_hub_class, (t_method)glob_preset_node_list_seek_hub, + gensym("pre-loadbang"), 0); + + class_addmethod(preset_hub_class, (t_method)preset_hub_recall, + gensym("recall"), A_DEFFLOAT, 0); + class_addmethod(preset_hub_class, (t_method)preset_hub_store, + gensym("store"), A_DEFFLOAT, 0); + + class_addbang(preset_hub_class, preset_hub_bang); // we'll use this to output current preset + class_setsavefn(preset_hub_class, preset_hub_save); +} + +//====================== end preset_hub ============================// + +void x_preset_setup(void) +{ + preset_node_setup(); + preset_hub_setup(); +} + +//TODO: autocreate k12 hub for the root canvas (we will do this in pd.tk's pdtk_canvas_new) + diff --git a/src/x_preset.h b/src/x_preset.h new file mode 100644 index 0000000000000000000000000000000000000000..fbade30f26d24a0ece2652283339cc8cdf345258 --- /dev/null +++ b/src/x_preset.h @@ -0,0 +1,84 @@ + +#ifndef __x_preset_h_ +#define __x_preset_h_ + +/* +Global preset system by Ivica Ico Bukvic <ico@vt.edu> July, 2012 +*/ + +// each node in the patch +typedef struct _preset_node +{ + t_object pn_obj; + t_atom pn_val; // last known value (null if not initialized) + + t_symbol *pn_hub_name; // hub name this node is associated with + t_preset_hub *pn_hub; // pointer to the hub (null if none, equiv. to active/disabled) + + t_canvas *pn_canvas; + int *pn_gl_loc; + int pn_gl_loc_length; + + // we use this to compare with the old position, + // so that hub can be notified of node's new position + int *pn_old_gl_loc; + int pn_old_gl_loc_length; + + t_outlet *pn_outlet; +} t_preset_node; + +// stores each of the presets per each preset_node +typedef struct _node_preset +{ + int np_preset; + t_atom np_val; + struct _node_preset *np_next; +} t_node_preset; + +// stores each of the nodes within a hub +typedef struct _preset_hub_data +{ + t_preset_node *phd_node; // if the node is erased, this will be NULL (inactive) + int *phd_pn_gl_loc; // last known location of the node (according to the hub) + int phd_pn_gl_loc_length; // location array's length (see x_preset.c for explanation) + struct _preset_hub_data *phd_next; + t_node_preset *phd_npreset; +} t_preset_hub_data; + +// preset hub (nexus for data saving/recalling) +struct _preset_hub +{ + t_object ph_obj; + t_symbol *ph_name; + int ph_invis; // make it invisible (only for the k12 mode) + int ph_preset; // last enabled preset (-1 at init time) + + t_canvas *ph_canvas; + + struct _preset_hub *ph_next; // next hub on the same canvas + t_preset_hub_data *ph_data; // pointer to data (single-linked list) + + t_outlet *ph_outlet; +}; + +typedef struct _glob_preset_hub_list +{ + t_preset_hub *gphl_hub; + struct _glob_preset_hub_list *gphl_next; +} t_glob_preset_hub_list; + +typedef struct _glob_preset_node_list +{ + t_preset_node *gpnl_node; + int gpnl_paired; // whether the node is paired with a hub (otherwise don't bother updating its location) + struct _glob_preset_node_list *gpnl_next; +} t_glob_preset_node_list; + +#ifndef t_preset_hub +#define t_preset_hub struct _preset_hub +#endif + +EXTERN int we_are_undoing; + +#endif /* __x_preset_h_ */ +