diff --git a/externals/pd-lua b/externals/pd-lua index b767c65b6607a1b2b80798a5ff4fb3e3c1d752a5..8eaaaa3c39af8968248fab5659d4a4d2da4f1ed7 160000 --- a/externals/pd-lua +++ b/externals/pd-lua @@ -1 +1 @@ -Subproject commit b767c65b6607a1b2b80798a5ff4fb3e3c1d752a5 +Subproject commit 8eaaaa3c39af8968248fab5659d4a4d2da4f1ed7 diff --git a/pd/nw/dialog_abstractions.html b/pd/nw/dialog_abstractions.html new file mode 100644 index 0000000000000000000000000000000000000000..78b1e594cc5fe5136dea665758f69ab962e9965f --- /dev/null +++ b/pd/nw/dialog_abstractions.html @@ -0,0 +1,198 @@ +<!DOCTYPE html> +<html> + <head> + <link id="page_style" rel="stylesheet" type="text/css" href="css/default.css"> + </head> + <body class="dialog_body"> + <div class="container"> + <form> + <fieldset style="width: 90%; border: 2px solid grey"> + <legend data-i18n="abstractions.private"></legend> + <strong data-i18n="abstractions.global"></strong> + <hr> + <table style="width: 90%; table-layout: fixed;"> + </table> + <br> + <strong data-i18n="abstractions.local"></strong> + <hr> + <table style="width: 90%; table-layout: fixed;"> + </table> + <br> + <button id="selectall_button" type="button" onClick="selectall()" data-i18n="[title]abstractions.selectall_tt" style="float: right;"> + <span data-i18n="abstractions.selectall"></span> + </button> + </fieldset> + <div class="submit_buttons"> + <button id="delete_button" type="button" onClick="deleteselected()" data-i18n="[title]abstractions.delete_tt"> + <span data-i18n="abstractions.delete"></span> + </button> + </div> + <hr> + <fieldset style="width: 90%; border: 2px solid grey"> + <legend data-i18n="abstractions.filebased"></legend> + <i>NOT IMPLEMENTED</i> + <table style="width: 90%; table-layout: fixed;"> + </table> + </fieldset> + <hr> + <div class="submit_buttons"> + <button type="button" onClick="cancel()" data-i18n="[title]abstractions.close_tt"> + <span data-i18n="abstractions.close"></span> + </button> + </div> + </form> + </div> + <script> +"use strict"; +var gui = require("nw.gui"); +var pdgui = require("./pdgui.js"); + +// For translations +var l = pdgui.get_local_string; + +pdgui.skin.apply(window); + +var pd_object_callback; +var canvas; +var deletequeue; + +function selectall() { + var checkboxes = document.querySelectorAll('input[type="checkbox"]:not(:checked)'); + for(var i = 0, n = checkboxes.length; i < n; i++) { + if(!checkboxes[i].checked) + { + checkboxes[i].click(); + } + } +} + +function deleteselected() { + pdgui.pdsend(pd_object_callback, "delabstractions", Array.from(deletequeue).join(" ")); + cancel(); +} + +function cancel() { + pdgui.pdsend(pd_object_callback, "cancel"); +} + +function populate_form(attrs) { + var filebased_abs = attrs.filebased_abs, + private_abs = attrs.private_abs, + i, + zero = 0; + canvas = attrs.canvas; + deletequeue = new Set(); + var tables = document.querySelectorAll("table"), + filebased_table = tables[2], + privateglobal_table = tables[0], + privatelocal_table = tables[1]; + + for(i = 0; i < filebased_abs.length; i += 2) + { + var row = document.createElement("tr"); + cell1 = document.createElement("td"), + cell2 = document.createElement("td"), + cell3 = document.createElement("td"); + + cell1.textContent = filebased_abs[i]; + cell1.style.setProperty("width", "70%"); + cell2.textContent = filebased_abs[i+1]; + cell2.style.setProperty("width", "20%"); + row.appendChild(cell1); + row.appendChild(cell2); + row.appendChild(cell3); + filebased_table.appendChild(row); + } + + for(i = 0; i < private_abs.length; i += 2) + { + var row = document.createElement("tr"), + cell1 = document.createElement("td"), + cell2 = document.createElement("td"), + cell3 = document.createElement("td"); + + cell1.textContent = private_abs[i]; + cell1.style.setProperty("width", "70%"); + cell2.textContent = private_abs[i+1]; + cell2.style.setProperty("width", "20%"); + + if(private_abs[i+1] === 0) + { + let input_elem = document.createElement("input"), + j = i; + input_elem.type = "checkbox"; + input_elem.onchange = function() { + if(input_elem.checked) { + deletequeue.add(private_abs[j]); + } else { + deletequeue.delete(private_abs[j]); + } + get_elem("delete_button").disabled = (deletequeue.size === 0); + console.log(deletequeue.size); + }; + cell3.appendChild(input_elem); + zero++; + } + + row.appendChild(cell1); + row.appendChild(cell2); + row.appendChild(cell3); + + if(private_abs[i][0] === '@') { + privatelocal_table.appendChild(row); + } else { + privateglobal_table.appendChild(row); + } + } + + get_elem("selectall_button").disabled = (zero === 0); + get_elem("delete_button").disabled = true; +} + +// This gets called from the nw_create_window function in index.html +// It provides us with our window id from the C side. Once we have it +// we can create the menu and register event callbacks +function register_window_id(gfxstub, attrs) { + pd_object_callback = gfxstub; + add_events(gfxstub); + // not sure that we need this for properties windows + //pdgui.canvas_map(gfxstub); + translate_form(); + populate_form(attrs); + // We don't turn on rendering of the "container" div until + // We've finished displaying all the spans and populating the + // labels and form elements. That makes it more efficient and + // snappier, at least on older machines. + document.getElementsByClassName("container")[0].style.setProperty("display", "inline"); + + gui.Window.get().setResizable(false); +} + +function get_elem(name) { + return document.getElementById(name); +} + +// Stop-gap translator +function translate_form() { + var elements = document.querySelectorAll("[data-i18n]"), + data, + i; + for (i = 0; i < elements.length; i++) { + data = elements[i].dataset.i18n; + if (data.slice(0, 7) === "[title]") { + elements[i].title = l(data.slice(7)); + } else { + elements[i].textContent = l(data); + } + } +} + +function add_events(name) { + gui.Window.get().on("close", function () { + cancel(); + }); + pdgui.dialog_bindings(name); +} + </script> + </body> +</html> diff --git a/pd/nw/dialog_external.html b/pd/nw/dialog_external.html index 28e4d3f9e155b7f688c0191183be1b623207633c..a0305f715ce6de93a52f5336dd25fe47b4b3aa84 100644 --- a/pd/nw/dialog_external.html +++ b/pd/nw/dialog_external.html @@ -149,7 +149,8 @@ function parse_attrs(attrs) { if (token.length > 1) { elem.type = token[token.length - 1]; if (elem.type !== "symbol" && - elem.type !== "toggle") { + elem.type !== "toggle" && + elem.type !== "hidden") { // no suffix defaults to "number" elem.type = "number"; } else { @@ -218,6 +219,7 @@ function get_input_type(t) { return t === "symbol" ? "text" : t === "number" ? "text" : t === "toggle" ? "checkbox": + t === "hidden" ? "hidden": "text"; } diff --git a/pd/nw/locales/en/translation.json b/pd/nw/locales/en/translation.json index b54d90c993b803af05c4e412ae7f566a661a9a12..18fae5ef392d4165ef78e983a85c1a2ff4fdf3b2 100644 --- a/pd/nw/locales/en/translation.json +++ b/pd/nw/locales/en/translation.json @@ -237,6 +237,8 @@ "visible_ancestor_tt": "give focus to the closest ancestor of this window that is currently visible", "pdwin": "Pd Window", "pdwin_tt": "Give focus to the main Pd window", + "abstractions": "Abstractions", + "abstractions_tt": "Consult and handle abstractions", "media": "Media", @@ -504,5 +506,17 @@ "building_index": "Building index...", "no_results": "No results found.", "search_placeholder": "Search Pd Docs" + }, + "abstractions": { + "filebased": "File-based abstractions", + "private": "Private abstractions", + "global": "Global scope", + "local": "Local scope", + "selectall": "Select all", + "selectall_tt": "Select all definitions with zero instances", + "delete": "Delete", + "delete_tt": "Delete all selected definitions", + "close": "Close", + "close_tt": "Close the dialog window" } } diff --git a/pd/nw/pd_canvas.js b/pd/nw/pd_canvas.js index a0bfb1a2e49a248c2d19ed3884943002f3f463dd..afdff9414b4139e36355cb4eabb95809f1613e9e 100644 --- a/pd/nw/pd_canvas.js +++ b/pd/nw/pd_canvas.js @@ -1417,6 +1417,41 @@ function nw_undo_menu(undo_text, redo_text) { } } +function ab_callback(cid, name) { + return function() { + pdgui.pdsend(cid, "delab", name); + } +} + +function nw_ab_menu(cid, definitions) { + while(canvas_menu.ab.this.items.length > 2) + canvas_menu.ab.this.removeAt(2); + if(definitions.length === 0) + { + var item = new nw.MenuItem({ + label: "Ø", + enabled: false + }); + canvas_menu.ab.this.append(item); + canvas_menu.ab.clean.enabled = 0; + } + else + { + var i, del = 0; + for(i = 0; i < definitions.length; i += 2) + { + var item = new nw.MenuItem({ + label: definitions[i] + " (" + definitions[i+1] + ")", + click: ab_callback(cid, definitions[i]), + enabled: (definitions[i+1] === 0) + }); + canvas_menu.ab.this.append(item); + if(definitions[i+1] === 0) del++; + } + canvas_menu.ab.clean.enabled = (del > 0); + } +} + function have_live_box() { var state = canvas_events.get_state(); if (state === "text" || state === "floating_text") { @@ -2008,6 +2043,12 @@ function nw_create_patch_window_menus(gui, w, name) { pdgui.raise_pd_window(); } }); + minit(m.win.abstractions, { + enabled: true, + click: function () { + pdgui.pdsend(name, "getabstractions"); + } + }); // Media menu minit(m.media.audio_on, { diff --git a/pd/nw/pd_menus.js b/pd/nw/pd_menus.js index a9dd9e69d189c98ec4f18a408af1a1c9f66b4489..0e3646546aef68d406085f90b7e36a24411df77a 100644 --- a/pd/nw/pd_menus.js +++ b/pd/nw/pd_menus.js @@ -579,6 +579,11 @@ function create_menu(gui, type) { key: shortcuts.menu.pdwin.key, modifiers: shortcuts.menu.pdwin.modifiers })); + winman_menu.append(new gui.MenuItem({ type: "separator" })); + winman_menu.append(m.win.abstractions = new gui.MenuItem({ + label: l("menu.abstractions"), + tooltip: l("menu.abstractions_tt") + })); } // Media menu diff --git a/pd/nw/pdgui.js b/pd/nw/pdgui.js index 7e90a25941a92a63259b6fee7f4e5af48794c3ab..0908f5160973321af3b9cd90105a76759744571d 100644 --- a/pd/nw/pdgui.js +++ b/pd/nw/pdgui.js @@ -6015,6 +6015,13 @@ function gui_external_dialog(did, external_name, attr_array) { }); } +function gui_abstractions_dialog(cid, gfxstub, filebased_abs, private_abs) { + var attrs = { canvas: cid, filebased_abs: filebased_abs, + private_abs: private_abs }; + dialogwin[gfxstub] = create_window(gfxstub, "abstractions", 300, 600, + 0, 0, attrs); +} + // Global settings function gui_pd_dsp(state) { diff --git a/pd/src/g_canvas.c b/pd/src/g_canvas.c index 8cabb3506b34b0b5175ddeca4ed106f7df352003..9883e7a7a92989870498fd0812280de507089373 100644 --- a/pd/src/g_canvas.c +++ b/pd/src/g_canvas.c @@ -82,6 +82,8 @@ static t_symbol *canvas_newdirectory = &s_; static int canvas_newargc; static t_atom *canvas_newargv; +static t_ab_definition *canvas_newabsource = 0; + /* maintain the list of visible toplevels for the GUI's "windows" menu */ void canvas_updatewindowlist( void) { @@ -144,6 +146,12 @@ void glob_setfilename(void *dummy, t_symbol *filesym, t_symbol *dirsym) canvas_newdirectory = dirsym; } +/* set the source for the next canvas, it will be an ab instance */ +void canvas_setabsource(t_ab_definition *abdef) +{ + canvas_newabsource = abdef; +} + t_canvas *canvas_getcurrent(void) { return ((t_canvas *)pd_findbyclass(&s__X, canvas_class)); @@ -218,12 +226,16 @@ t_symbol *canvas_realizedollar(t_canvas *x, t_symbol *s) t_symbol *canvas_getcurrentdir(void) { - t_canvasenvironment *e = canvas_getenv(canvas_getcurrent()); - return (e->ce_dir); + return (canvas_getdir(canvas_getcurrent())); } +/* ** refactored function, check for errors */ t_symbol *canvas_getdir(t_canvas *x) { + x = canvas_getrootfor(x); + /* in the case the root is an ab instance, we borrow the + dir from the main root canvas (where the definition is stored) */ + if(x->gl_isab) x = x->gl_absource->ad_owner; t_canvasenvironment *e = canvas_getenv(x); return (e->ce_dir); } @@ -397,6 +409,7 @@ static int calculate_zoom(t_float zoom_hack) } int canvas_dirty_broadcast_all(t_symbol *name, t_symbol *dir, int mess); +int canvas_dirty_broadcast_ab_all(t_ab_definition *abdef, int mess); /* make a new glist. It will either be a "root" canvas or else it appears as a "text" object in another window (canvas_getcurrent() @@ -473,6 +486,17 @@ t_canvas *canvas_new(void *dummy, t_symbol *sel, int argc, t_atom *argv) } else x->gl_env = 0; + x->gl_abdefs = 0; + /* if canvas_newabsource is set means that + this canvas is going to be an ab instance */ + if(canvas_newabsource) + { + x->gl_isab = 1; + x->gl_absource = canvas_newabsource; + canvas_newabsource = 0; + } + else x->gl_isab = 0; + x->gl_subdirties = 0; x->gl_dirties = 0; @@ -529,8 +553,11 @@ t_canvas *canvas_new(void *dummy, t_symbol *sel, int argc, t_atom *argv) get the number of dirty instances of this same abstraction */ if(x->gl_env) { - x->gl_dirties = canvas_dirty_broadcast_all(x->gl_name, + if(!x->gl_isab) + x->gl_dirties = canvas_dirty_broadcast_all(x->gl_name, canvas_getdir(x), 0); + else + x->gl_dirties = canvas_dirty_broadcast_ab_all(x->gl_absource, 0); } return(x); @@ -728,6 +755,14 @@ t_symbol *canvas_makebindsym(t_symbol *s) return (gensym(buf)); } +t_symbol *canvas_makebindsym_ab(t_symbol *s) +{ + char buf[MAXPDSTRING]; + snprintf(buf, MAXPDSTRING-1, "ab-%s", s->s_name); + buf[MAXPDSTRING-1] = 0; + return (gensym(buf)); +} + int garray_getname(t_garray *x, t_symbol **namep); void canvas_args_to_string(char *namebuf, t_canvas *x) @@ -809,6 +844,8 @@ void canvas_dirtyclimb(t_canvas *x, int n) void clone_iterate(t_pd *z, t_canvas_iterator it, void* data); int clone_match(t_pd *z, t_symbol *name, t_symbol *dir); +int clone_isab(t_pd *z); +int clone_matchab(t_pd *z, t_ab_definition *source); static void canvas_dirty_common(t_canvas *x, int mess) { @@ -855,7 +892,7 @@ static int canvas_dirty_broadcast(t_canvas *x, t_symbol *name, t_symbol *dir, in { if(pd_class(&g->g_pd) == canvas_class) { - if(canvas_isabstraction((t_canvas *)g) + if(canvas_isabstraction((t_canvas *)g) && !((t_canvas *)g)->gl_isab && ((t_canvas *)g)->gl_name == name && canvas_getdir((t_canvas *)g) == dir) { @@ -897,6 +934,74 @@ int canvas_dirty_broadcast_all(t_symbol *name, t_symbol *dir, int mess) res += canvas_dirty_broadcast(x, name, dir, mess); return (res); } + +/* same but for ab */ + +typedef struct _dirty_broadcast_ab_data +{ + t_ab_definition *abdef; + int mess; + int *res; +} t_dirty_broadcast_ab_data; + +static void canvas_dirty_deliver_ab_packed(t_canvas *x, t_dirty_broadcast_ab_data *data) +{ + *data->res += (x->gl_dirty > 0); + canvas_dirty_common(x, data->mess); +} + +static int canvas_dirty_broadcast_ab_packed(t_canvas *x, t_dirty_broadcast_ab_data *data); + +static int canvas_dirty_broadcast_ab(t_canvas *x, t_ab_definition *abdef, int mess) +{ + int res = 0; + t_gobj *g; + for (g = x->gl_list; g; g = g->g_next) + { + if(pd_class(&g->g_pd) == canvas_class) + { + if(canvas_isabstraction((t_canvas *)g) && ((t_canvas *)g)->gl_isab + && ((t_canvas *)g)->gl_absource == abdef) + { + res += (((t_canvas *)g)->gl_dirty > 0); + canvas_dirty_common((t_canvas *)g, mess); + } + else + res += canvas_dirty_broadcast_ab((t_canvas *)g, abdef, mess); + } + else if(pd_class(&g->g_pd) == clone_class) + { + int cres = 0; + t_dirty_broadcast_ab_data data; + data.abdef = abdef; data.mess = mess; data.res = &cres; + if(clone_matchab(&g->g_pd, abdef)) + { + clone_iterate(&g->g_pd, canvas_dirty_deliver_ab_packed, &data); + } + else if(clone_isab(&g->g_pd)) + { + clone_iterate(&g->g_pd, canvas_dirty_broadcast_ab_packed, &data); + } + res += cres; + } + } + return (res); +} + +static int canvas_dirty_broadcast_ab_packed(t_canvas *x, t_dirty_broadcast_ab_data *data) +{ + *data->res = canvas_dirty_broadcast_ab(x, data->abdef, data->mess); +} + +int canvas_dirty_broadcast_ab_all(t_ab_definition *abdef, int mess) +{ + int res = 0; + t_canvas *x; + for (x = pd_this->pd_canvaslist; x; x = x->gl_next) + res += canvas_dirty_broadcast_ab(x, abdef, mess); + return (res); +} + /* mark a glist dirty or clean */ void canvas_dirty(t_canvas *x, t_floatarg n) { @@ -911,14 +1016,19 @@ void canvas_dirty(t_canvas *x, t_floatarg n) /* set dirtiness visual markings */ canvas_dirtyclimb(x2, (unsigned)n); + /* in the case it is an abstraction, we tell all other instances that there is eiher one more dirty instance or one less dirty instance */ if(canvas_isabstraction(x2) && (x2->gl_owner || x2->gl_isclone)) { - canvas_dirty_broadcast_all(x2->gl_name, canvas_getdir(x2), - (x2->gl_dirty ? 1 : -1)); + if(!x2->gl_isab) + canvas_dirty_broadcast_all(x2->gl_name, canvas_getdir(x2), + (x2->gl_dirty ? 1 : -1)); + else + canvas_dirty_broadcast_ab_all(x2->gl_absource, + (x2->gl_dirty ? 1 : -1)); } } } @@ -1144,17 +1254,28 @@ int glist_getfont(t_glist *x) } extern void canvas_group_free(t_pd *x); +static void canvas_deregister_ab(t_canvas *x, t_ab_definition *a); void canvas_free(t_canvas *x) { //fprintf(stderr,"canvas_free %zx\n", (t_uint)x); + /* crude hack. in the case it was a clone instance, it shouldn't have an owner. + For ab instances, we have set the owner inside clone_free because we need it + in order to deregister the dependencies. + here we set it to NULL again to prevent any error in the functions called bellow */ + t_canvas *aux = x->gl_owner; + if(x->gl_isclone) x->gl_owner = 0; + /* in the case it is a dirty abstraction, we tell all other instances that there is one less dirty instance */ if(canvas_isabstraction(x) && x->gl_dirty && (x->gl_owner || x->gl_isclone)) { - canvas_dirty_broadcast_all(x->gl_name, canvas_getdir(x), -1); + if(!x->gl_isab) + canvas_dirty_broadcast_all(x->gl_name, canvas_getdir(x), -1); + else + canvas_dirty_broadcast_ab_all(x->gl_absource, -1); } t_gobj *y; @@ -1194,6 +1315,26 @@ void canvas_free(t_canvas *x) canvas_takeofflist(x); if (x->gl_svg) /* for groups, free the data */ canvas_group_free(x->gl_svg); + + /* freeing an ab instance */ + if(x->gl_isab) + { + x->gl_absource->ad_numinstances--; + canvas_deregister_ab((x->gl_isclone ? aux : x->gl_owner), + x->gl_absource); + } + + /* free stored ab definitions */ + t_ab_definition *d = x->gl_abdefs, *daux; + while(d) + { + daux = d->ad_next; + binbuf_free(d->ad_source); + freebytes(d->ad_dep, sizeof(t_ab_definition*)*d->ad_numdep); + freebytes(d->ad_deprefs, sizeof(int)*d->ad_numdep); + freebytes(d, sizeof(t_ab_definition)); + d = daux; + } } /* ----------------- lines ---------- */ @@ -1727,13 +1868,17 @@ static int canvas_should_bind(t_canvas *x) static void canvas_bind(t_canvas *x) { - if (canvas_should_bind(x)) + if (x->gl_isab) /* if it is an ab instance, we bind it to symbol 'ab-<name>' */ + pd_bind(&x->gl_pd, canvas_makebindsym_ab(x->gl_name)); + else if (canvas_should_bind(x)) pd_bind(&x->gl_pd, canvas_makebindsym(x->gl_name)); } static void canvas_unbind(t_canvas *x) { - if (canvas_should_bind(x)) + if (x->gl_isab) + pd_unbind(&x->gl_pd, canvas_makebindsym_ab(x->gl_name)); + else if (canvas_should_bind(x)) pd_unbind(&x->gl_pd, canvas_makebindsym(x->gl_name)); } @@ -2016,11 +2161,530 @@ void canvas_redrawallfortemplatecanvas(t_canvas *x, int action) canvas_redrawallfortemplate(0, action); } +/* ------------------------------- ab ------------------------ */ + +static char ab_templatecanvas[] = "#N canvas;\n"; + +/* create an ab instance from its source */ +static t_pd *do_create_ab(t_ab_definition *abdef, int argc, t_atom *argv) +{ + canvas_setargs(argc, argv); + int dspstate = canvas_suspend_dsp(); + glob_setfilename(0, abdef->ad_name, gensym("[ab]")); + + /* set ab source, next canvas is going to be a private abstraction */ + canvas_setabsource(abdef); + binbuf_eval(abdef->ad_source, 0, 0, 0); + canvas_initbang((t_canvas *)(s__X.s_thing)); + + glob_setfilename(0, &s_, &s_); + canvas_resume_dsp(dspstate); + canvas_popabstraction((t_canvas *)(s__X.s_thing)); + canvas_setargs(0, 0); + + /* open the canvas if we are creating it for the first time */ + canvas_vis((t_canvas *)newest, !glist_amreloadingabstractions + && !abdef->ad_numinstances + && binbuf_getnatom(abdef->ad_source) == 3); + + return(newest); +} + +/* get root canvas crossing ab boundaries, where ab definitions are stored */ +t_canvas *canvas_getrootfor_ab(t_canvas *x) +{ + if ((!x->gl_owner && !x->gl_isclone) || (canvas_isabstraction(x) && !x->gl_isab)) + return (x); + else if (x->gl_isab) /* shortcut + workaround for clones (since they haven't owner)*/ + return (x->gl_absource->ad_owner); + else + return (canvas_getrootfor_ab(x->gl_owner)); +} + +/* check if the dependency graph has a cycle, assuming an new edge between parent and + current nodes if there is a cycle, a visual scheme of the cycle is stored in 'res' */ +static int ab_check_cycle(t_ab_definition *current, t_ab_definition *parent, int pathlen, + char *path, char *res) +{ + if(current == parent) + { + sprintf(path+pathlen, "[ab %s]", current->ad_name->s_name); + strcpy(res, path); + return (1); + } + else + { + /* if it is a local private abstraction, get rid of classmember-like names (only used internally) */ + char *hash = strrchr(current->ad_name->s_name, '#'); + if(!hash) hash = current->ad_name->s_name; + else hash += 1; + int len = strlen(hash); + sprintf(path+pathlen, "[ab %s]<-", hash); + pathlen += (len+7); + int i, cycle = 0; + for(i = 0; !cycle && i < current->ad_numdep; i++) + { + cycle = ab_check_cycle(current->ad_dep[i], parent, pathlen, path, res); + } + pathlen -= (len+7); + return (cycle); + } +} + +/* try to register a new dependency into the dependency graph, + returns 0 and the scheme in 'res' if a dependency issue is found */ +static int canvas_register_ab(t_canvas *x, t_ab_definition *a, char *res) +{ + /* climb to closest ab */ + while(x && !x->gl_isab) + x = x->gl_owner; + + if(x && x->gl_isab) + { + t_ab_definition *f = x->gl_absource; + + int i, found = 0; + for(i = 0; !found && i < a->ad_numdep; i++) + found = (a->ad_dep[i] == f); + + if(!found) + { + char path[MAXPDSTRING]; + sprintf(path, "[ab %s]<-", a->ad_name->s_name); + if(!ab_check_cycle(f, a, strlen(path), path, res)) + { + /* no dependency issues found so we add the new dependency */ + a->ad_dep = + (t_ab_definition **)resizebytes(a->ad_dep, sizeof(t_ab_definition *)*a->ad_numdep, + sizeof(t_ab_definition *)*(a->ad_numdep+1)); + a->ad_deprefs = + (int *)resizebytes(a->ad_deprefs, sizeof(int)*a->ad_numdep, + sizeof(int)*(a->ad_numdep+1)); + a->ad_dep[a->ad_numdep] = f; + a->ad_deprefs[a->ad_numdep] = 1; + a->ad_numdep++; + } + else return (0); + } + else + { + a->ad_deprefs[i-1]++; + } + } + return (1); +} + +static void canvas_deregister_ab(t_canvas *x, t_ab_definition *a) +{ + /* climb to closest ab */ + while(x && !x->gl_isab) + x = x->gl_owner; + + if(x && x->gl_isab) + { + t_ab_definition *f = x->gl_absource; + + int i, found = 0; + for(i = 0; !found && i < a->ad_numdep; i++) + found = (a->ad_dep[i] == f); + + if(found) + { + a->ad_deprefs[i-1]--; + + if(!a->ad_deprefs[i-1]) + { + /* we can delete the dependency since there are no instances left */ + t_ab_definition **ad = + (t_ab_definition **)getbytes(sizeof(t_ab_definition *) * (a->ad_numdep - 1)); + int *adr = (int *)getbytes(sizeof(int) * (a->ad_numdep - 1)); + memcpy(ad, a->ad_dep, sizeof(t_ab_definition *) * (i-1)); + memcpy(ad+(i-1), a->ad_dep+i, sizeof(t_ab_definition *) * (a->ad_numdep - i)); + memcpy(adr, a->ad_deprefs, sizeof(int) * (i-1)); + memcpy(adr+(i-1), a->ad_deprefs+i, sizeof(int) * (a->ad_numdep - i)); + freebytes(a->ad_dep, sizeof(t_ab_definition *) * a->ad_numdep); + freebytes(a->ad_deprefs, sizeof(int) * a->ad_numdep); + a->ad_numdep--; + a->ad_dep = ad; + a->ad_deprefs = adr; + } + } + else bug("canvas_deregister_ab"); + } +} + +/* tries to find an ab definition given its name */ +static t_ab_definition *canvas_find_ab(t_canvas *x, t_symbol *name) +{ + t_canvas *c = canvas_getrootfor_ab(x); + t_ab_definition* d; + for (d = c->gl_abdefs; d; d = d->ad_next) + { + if (d->ad_name == name) + return d; + } + return 0; +} + +/* tries to add a new ab definition. returns the definition if it has been added, 0 otherwise */ +static t_ab_definition *canvas_add_ab(t_canvas *x, t_symbol *name, t_binbuf *source) +{ + if(!canvas_find_ab(x, name)) + { + t_canvas *c = canvas_getrootfor_ab(x); + t_ab_definition *abdef = (t_ab_definition *)getbytes(sizeof(t_ab_definition)); + + abdef->ad_name = name; + abdef->ad_source = source; + abdef->ad_numinstances = 0; + abdef->ad_owner = c; + abdef->ad_numdep = 0; + abdef->ad_dep = (t_ab_definition **)getbytes(0); + abdef->ad_deprefs = (int *)getbytes(0); + abdef->ad_visflag = 0; + + abdef->ad_next = c->gl_abdefs; + c->gl_abdefs = abdef; + return (abdef); + } + return (0); +} + +static int canvas_del_ab(t_canvas *x, t_symbol *name) +{ + t_canvas *c = canvas_getrootfor_ab(x); + t_ab_definition *abdef, *abdefpre; + for(abdef = c->gl_abdefs, abdefpre = 0; abdef; abdefpre = abdef, abdef = abdef->ad_next) + { + if(abdef->ad_name == name && !abdef->ad_numinstances) + { + if(abdefpre) abdefpre->ad_next = abdef->ad_next; + else c->gl_abdefs = abdef->ad_next; + binbuf_free(abdef->ad_source); + freebytes(abdef->ad_dep, sizeof(t_ab_definition*)*abdef->ad_numdep); + freebytes(abdef->ad_deprefs, sizeof(int)*abdef->ad_numdep); + freebytes(abdef, sizeof(t_ab_definition)); + return (1); + } + } + return (0); +} + +/* given the ab definitions list, returns its topological ordering */ + +static int currvisflag = 0; + +static void ab_topological_sort_rec(t_ab_definition *a, t_ab_definition **stack, int *head) +{ + a->ad_visflag = currvisflag; + + int i; + for(i = 0; i < a->ad_numdep; i++) + { + if(a->ad_dep[i]->ad_visflag != currvisflag) + ab_topological_sort_rec(a->ad_dep[i], stack, head); + } + + stack[*head] = a; + (*head)++; +} + +static void ab_topological_sort(t_ab_definition *abdefs, t_ab_definition **stack, int *head) +{ + currvisflag++; + t_ab_definition *abdef; + for(abdef = abdefs; abdef; abdef = abdef->ad_next) + { + if(abdef->ad_visflag != currvisflag) + ab_topological_sort_rec(abdef, stack, head); + } +} + +/* saves all ab definition within the scope into the b binbuf, + they are sorted topollogially before saving in order to get exactly the + same state (ab objects that can't be instantiated due dependencies) when reloading the file */ +void canvas_saveabdefinitionsto(t_canvas *x, t_binbuf *b) +{ + if(!x->gl_abdefs) + return; + + int numabdefs = 0; + t_ab_definition *abdef; + for(abdef = x->gl_abdefs; abdef; abdef = abdef->ad_next) + numabdefs++; + + t_ab_definition **stack = + (t_ab_definition **)getbytes(sizeof(t_ab_definition *) * numabdefs); + int head = 0; + ab_topological_sort(x->gl_abdefs, stack, &head); + + int i, fra = 0; + for(i = 0; i < head; i++) + { + if(stack[i]->ad_numinstances) + { + if(!fra) + { + binbuf_addv(b, "ssi;", gensym("#X"), gensym("abframe"), 1); + fra = 1; + } + + binbuf_add(b, binbuf_getnatom(stack[i]->ad_source), binbuf_getvec(stack[i]->ad_source)); + binbuf_addv(b, "sss", gensym("#X"), gensym("abpush"), stack[i]->ad_name); + + int j; + for(j = 0; j < stack[i]->ad_numdep; j++) + binbuf_addv(b, "s", stack[i]->ad_dep[j]->ad_name); + binbuf_addsemi(b); + } + } + if(fra) binbuf_addv(b, "ssi;", gensym("#X"), gensym("abframe"), 0); + + freebytes(stack, sizeof(t_ab_definition *) * numabdefs); +} + +/* saves last canvas as an ab definition */ +static void canvas_abpush(t_canvas *x, t_symbol *s, int argc, t_atom *argv) +{ + canvas_pop(x, 0); + + t_canvas *c = canvas_getcurrent(); + t_symbol *name = argv[0].a_w.w_symbol; + t_binbuf *source = binbuf_new(); + x->gl_env = 0xF1A6; //to save it as a root canvas + mess1(&((t_text *)x)->te_pd, gensym("saveto"), source); + x->gl_env = 0; + + t_ab_definition *n; + if(!(n = canvas_add_ab(c, name, source))) + { + error("canvas_abpush: ab definition for '%s' already exists, skipping", + name->s_name); + } + else + { + if(argc > 1) + { + /* restore all dependencies, to get exactly the + same state (ab objects that can't be instantiated due dependencies) as before */ + n->ad_numdep = argc-1; + n->ad_dep = + (t_ab_definition **)resizebytes(n->ad_dep, 0, sizeof(t_ab_definition *)*n->ad_numdep); + n->ad_deprefs = + (int *)resizebytes(n->ad_deprefs, 0, sizeof(int)*n->ad_numdep); + + int i; + for(i = 1; i < argc; i++) + { + t_symbol *abname = argv[i].a_w.w_symbol; + t_ab_definition *absource = canvas_find_ab(c, abname); + if(!absource) { bug("canvas_abpush"); return; } + n->ad_dep[i-1] = absource; + n->ad_deprefs[i-1] = 0; + } + } + } + + pd_free(&x->gl_pd); +} + +/* extends the name for a local ab, using a classmember-like format */ +static t_symbol *ab_extend_name(t_canvas *x, t_symbol *s) +{ + char res[MAXPDSTRING]; + t_canvas *next = canvas_getrootfor(x); + if(next->gl_isab) + sprintf(res, "%s#%s", next->gl_absource->ad_name->s_name, s->s_name); + else + strcpy(res, s->s_name); + return gensym(res); +} + +static int abframe = 0; +static void canvas_abframe(t_canvas *x, t_float val) +{ + abframe = val; +} + +extern t_class *text_class; + +/* creator for "ab" objects */ +static void *ab_new(t_symbol *s, int argc, t_atom *argv) +{ + if(abframe) + /* return dummy text object so that creator + does not throw an error */ + return pd_new(text_class); + + t_canvas *c = canvas_getcurrent(); + + if (argc && argv[0].a_type != A_SYMBOL) + { + error("ab_new: ab name must be a symbol"); + newest = 0; + } + else + { + t_symbol *name = (argc ? argv[0].a_w.w_symbol : gensym("(ab)")); + t_ab_definition *source; + + if(name->s_name[0] == '@') /* is local ab */ + name = ab_extend_name(c, name); + + if(!(source = canvas_find_ab(c, name))) + { + t_binbuf *b = binbuf_new(); + binbuf_text(b, ab_templatecanvas, strlen(ab_templatecanvas)); + source = canvas_add_ab(c, name, b); + } + + char res[MAXPDSTRING]; + if(canvas_register_ab(c, source, res)) + { + newest = do_create_ab(source, (argc ? argc-1 : 0), (argc ? argv+1 : 0)); + source->ad_numinstances++; + } + else + { + error("ab_new: can't insantiate ab within itself\n cycle: %s", res); + newest = 0; + } + } + return (newest); +} + +static void canvas_getabstractions(t_canvas *x) +{ + t_canvas *c = canvas_getrootfor_ab(x), + *r = canvas_getrootfor(x); + gfxstub_deleteforkey(x); + char *gfxstub = gfxstub_new2(&x->gl_pd, &x->gl_pd); + t_ab_definition *abdef; + gui_start_vmess("gui_abstractions_dialog", "xs", x, gfxstub); + gui_start_array(); + gui_end_array(); + gui_start_array(); + for(abdef = c->gl_abdefs; abdef; abdef = abdef->ad_next) + { + char *hash = strrchr(abdef->ad_name->s_name, '#'); + if(!hash) + { + if(abdef->ad_name->s_name[0] != '@' || !r->gl_isab) + { + gui_s(abdef->ad_name->s_name); + gui_i(abdef->ad_numinstances); + } + } + else + { + *hash = '\0'; + if(r->gl_isab && + gensym(abdef->ad_name->s_name) == r->gl_absource->ad_name) + { + gui_s(hash+1); + gui_i(abdef->ad_numinstances); + } + *hash = '#'; + } + } + gui_end_array(); + gui_end_vmess(); +} + +static void canvas_delabstractions(t_canvas *x, t_symbol *s, int argc, t_atom *argv) +{ + t_symbol *name; + int i; + for(i = 0; i < argc; i++) + { + name = atom_getsymbol(argv++); + if(name->s_name[0] == '@') + name = ab_extend_name(x, name); + if(!canvas_del_ab(x, name)) + bug("canvas_delabstractions"); + } + startpost("info: a total of [%d] ab definitions have been deleted\n > ", argc); + postatom(argc, argv-argc); + endpost(); +} + +/* --------- */ + +static t_class *abdefs_class; + +typedef struct _abdefs +{ + t_object x_obj; + t_canvas *x_canvas; +} t_abdefs; + +static void *abdefs_new(void) +{ + t_abdefs *x = (t_abdefs *)pd_new(abdefs_class); + x->x_canvas = canvas_getcurrent(); + outlet_new(&x->x_obj, &s_list); + return (x); +} + +static void abdefs_get(t_abdefs *x); +static void abdefs_bang(t_abdefs *x) +{ + abdefs_get(x); +} + +static void abdefs_get(t_abdefs *x) +{ + t_canvas *c = canvas_getrootfor_ab(x->x_canvas), + *r = canvas_getrootfor(x->x_canvas); + t_binbuf *out = binbuf_new(); + t_ab_definition *abdef; + for(abdef = c->gl_abdefs; abdef; abdef = abdef->ad_next) + { + char *hash = strrchr(abdef->ad_name->s_name, '#'); + if(!hash) + { + if(abdef->ad_name->s_name[0] != '@' || !r->gl_isab) + { + binbuf_addv(out, "si", abdef->ad_name, abdef->ad_numinstances); + } + } + else + { + *hash = '\0'; + if(r->gl_isab && + gensym(abdef->ad_name->s_name) == r->gl_absource->ad_name) + { + binbuf_addv(out, "si", gensym(hash+1), abdef->ad_numinstances); + } + *hash = '#'; + } + } + outlet_list(x->x_obj.ob_outlet, &s_list, + binbuf_getnatom(out), binbuf_getvec(out)); + binbuf_free(out); +} + +static void abdefs_instances(t_abdefs *x, t_symbol *s) +{ + t_ab_definition *abdef; + if((abdef = canvas_find_ab(x->x_canvas, s))) + { + t_atom at[1]; + SETFLOAT(at, abdef->ad_numinstances); + outlet_list(x->x_obj.ob_outlet, &s_list, 1, at); + } + else + error("abdefs: couldn't find definition for '%s'", s->s_name); +} + /* --------- */ static void canvas_showdirty(t_canvas *x) { - canvas_dirty_broadcast_all(x->gl_name, canvas_getdir(x), 2); + if(!x->gl_isab) + canvas_dirty_broadcast_all(x->gl_name, canvas_getdir(x), 2); + else + canvas_dirty_broadcast_ab_all(x->gl_absource, 2); } /* ------------------------------- declare ------------------------ */ @@ -2895,8 +3559,29 @@ void g_canvas_setup(void) class_addcreator((t_newmethod)table_new, gensym("table"), A_DEFSYM, A_DEFFLOAT, 0); +/* ------------------- */ + class_addmethod(canvas_class, (t_method)canvas_showdirty, gensym("showdirty"), 0); + +/*---------------------------- ab ------------------- */ + + class_addcreator((t_newmethod)ab_new, gensym("ab"), A_GIMME, 0); + class_addmethod(canvas_class, (t_method)canvas_abpush, + gensym("abpush"), A_GIMME, 0); + class_addmethod(canvas_class, (t_method)canvas_abframe, + gensym("abframe"), A_FLOAT, 0); + + abdefs_class = class_new(gensym("abdefs"), (t_newmethod)abdefs_new, + 0, sizeof(t_abdefs), CLASS_DEFAULT, A_NULL); + class_addbang(abdefs_class, (t_method)abdefs_bang); + class_addmethod(abdefs_class, (t_method)abdefs_get, gensym("get"), 0); + class_addmethod(abdefs_class, (t_method)abdefs_instances, gensym("instances"), A_SYMBOL, 0); + + class_addmethod(canvas_class, (t_method)canvas_getabstractions, + gensym("getabstractions"), 0); + class_addmethod(canvas_class, (t_method)canvas_delabstractions, + gensym("delabstractions"), A_GIMME, 0); /*---------------------------- declare ------------------- */ declare_class = class_new(gensym("declare"), (t_newmethod)declare_new, (t_method)declare_free, sizeof(t_declare), CLASS_NOINLET, A_GIMME, 0); diff --git a/pd/src/g_canvas.h b/pd/src/g_canvas.h index 44ee4e4ab581c2ae9ea79e8ef07292400260780a..23c088b5739c6ec9ffbc9f00e36d842adee696f1 100644 --- a/pd/src/g_canvas.h +++ b/pd/src/g_canvas.h @@ -164,6 +164,24 @@ typedef struct _tick /* where to put ticks on x or y axes */ int k_lperb; /* little ticks per big; 0 if no ticks to draw */ } t_tick; +/* the t_ab_definition structure holds an ab definiton and all the attributes we need + to handle it */ +typedef struct _ab_definition +{ + t_symbol *ad_name; /* id for the ab definition */ + t_binbuf *ad_source; /* binbuf where the source is stored */ + int ad_numinstances; /* number of instances */ + struct _ab_definition *ad_next; /* next ab definition */ + t_canvas *ad_owner; /* canvas that stores this definition */ + + /* dependency graph stuff */ + int ad_numdep; /* number of other ab definitions that it depends on */ + struct _ab_definition **ad_dep; /* the actual ab defintitions */ + int *ad_deprefs; /* number of instances that define the dependency */ + int ad_visflag; /* visited flag for topological sort algorithm */ +} t_ab_definition; + + /* the t_glist structure, which describes a list of elements that live on an area of a window. @@ -236,6 +254,11 @@ struct _glist int gl_subdirties; /* number of descending dirty abstractions */ int gl_dirties; /* number of diry instances, for multiple dirty warning */ + + unsigned int gl_isab:1; /* is an ab instance */ + t_ab_definition *gl_absource; /* ab definition pointer, + in the case it is an ab instance */ + t_ab_definition *gl_abdefs; /* stored ab definitions */ }; #define gl_gobj gl_obj.te_g @@ -554,6 +577,7 @@ EXTERN void canvas_setcurrent(t_canvas *x); EXTERN void canvas_unsetcurrent(t_canvas *x); EXTERN t_symbol *canvas_realizedollar(t_canvas *x, t_symbol *s); EXTERN t_canvas *canvas_getrootfor(t_canvas *x); +EXTERN t_canvas *canvas_getrootfor_ab(t_canvas *x); EXTERN void canvas_dirty(t_canvas *x, t_floatarg n); EXTERN int canvas_getfont(t_canvas *x); typedef int (*t_canvasapply)(t_canvas *x, t_int x1, t_int x2, t_int x3); diff --git a/pd/src/g_clone.c b/pd/src/g_clone.c index c06f8dba7fb293b955382fa085feb4137933ec64..d5ba76414de9c785cf1e71efc617c821f3da4c27 100644 --- a/pd/src/g_clone.c +++ b/pd/src/g_clone.c @@ -63,6 +63,7 @@ typedef struct _clone int x_phase; int x_startvoice; /* number of first voice, 0 by default */ int x_suppressvoice; /* suppress voice number as $1 arg */ + t_canvas *x_owner; /* clone owner */ } t_clone; /* the given 'it' function is executed over each of the underlying canvases @@ -80,8 +81,23 @@ int clone_match(t_pd *z, t_symbol *name, t_symbol *dir) t_clone *x = (t_clone *)z; if (!x->x_n) return (0); - return (x->x_vec[0].c_gl->gl_name == name && - canvas_getdir(x->x_vec[0].c_gl) == dir); + return (!x->x_vec[0].c_gl->gl_isab + && x->x_vec[0].c_gl->gl_name == name + && canvas_getdir(x->x_vec[0].c_gl) == dir); +} + +int clone_isab(t_pd *z) +{ + t_clone *x = (t_clone *)z; + if (!x->x_n) + return (0); + return (x->x_vec[0].c_gl->gl_isab); +} + +int clone_matchab(t_pd *z, t_ab_definition *source) +{ + t_clone *x = (t_clone *)z; + return (clone_isab(z) && x->x_vec[0].c_gl->gl_absource == source); } void obj_sendinlet(t_object *x, int n, t_symbol *s, int argc, t_atom *argv); @@ -185,6 +201,13 @@ static void clone_free(t_clone *x) for (i = 0; i < x->x_n; i++) { canvas_closebang(x->x_vec[i].c_gl); + if(x->x_vec[i].c_gl->gl_isab) + { + /* crude hack. since clones don't have owner, + we set it manually to allow the clone to + deregister the dependencies */ + x->x_vec[i].c_gl->gl_owner = x->x_owner; + } pd_free(&x->x_vec[i].c_gl->gl_pd); t_freebytes(x->x_outvec[i], x->x_nout * sizeof(*x->x_outvec[i])); @@ -238,7 +261,8 @@ void clone_setn(t_clone *x, t_floatarg f) { t_canvas *c; t_out *outvec; - SETFLOAT(x->x_argv, x->x_startvoice + i); + /* in the case they are [ab]s, the instance number is one atom beyond */ + SETFLOAT((x->x_vec[0].c_gl->gl_isab ? x->x_argv+1 : x->x_argv), x->x_startvoice + i); if (!(c = clone_makeone(x->x_s, x->x_argc - x->x_suppressvoice, x->x_argv + x->x_suppressvoice))) { @@ -364,6 +388,7 @@ static void clone_dsp(t_clone *x, t_signal **sp) } } +static int clone_newabclone = 0; static void *clone_new(t_symbol *s, int argc, t_atom *argv) { t_clone *x = (t_clone *)pd_new(clone_class); @@ -403,10 +428,27 @@ static void *clone_new(t_symbol *s, int argc, t_atom *argv) else goto usage; /* store a copy of the argmuents with an extra space (argc+1) for supplying an instance number, which we'll bash as we go. */ - x->x_argc = argc - 1; - x->x_argv = getbytes(x->x_argc * sizeof(*x->x_argv)); - memcpy(x->x_argv, argv+1, x->x_argc * sizeof(*x->x_argv)); - SETFLOAT(x->x_argv, x->x_startvoice); + if(clone_newabclone) + /* we are creating a clone from an [ab] definition, we use the same creation + method as for normal clones but the name we pass to objectmaker is + 'ab <name>' instead of just '<name>' */ + { + x->x_argc = argc; + x->x_argv = getbytes(x->x_argc * sizeof(*x->x_argv)); + memcpy(x->x_argv, argv, x->x_argc * sizeof(*x->x_argv)); + SETSYMBOL(x->x_argv, x->x_s); + SETFLOAT(x->x_argv+1, x->x_startvoice); + x->x_s = gensym("ab"); + x->x_owner = canvas_getcurrent(); + clone_newabclone = 0; + } + else + { + x->x_argc = argc - 1; + x->x_argv = getbytes(x->x_argc * sizeof(*x->x_argv)); + memcpy(x->x_argv, argv+1, x->x_argc * sizeof(*x->x_argv)); + SETFLOAT(x->x_argv, x->x_startvoice); + } if (!(c = clone_makeone(x->x_s, x->x_argc - x->x_suppressvoice, x->x_argv + x->x_suppressvoice))) goto fail; @@ -454,10 +496,20 @@ fail: return (0); } +/* creator for [ab]-based clones */ +static void *abclone_new(t_symbol *s, int argc, t_atom *argv) +{ + clone_newabclone = 1; + return clone_new(s, argc, argv); +} + void clone_setup(void) { clone_class = class_new(gensym("clone"), (t_newmethod)clone_new, (t_method)clone_free, sizeof(t_clone), CLASS_NOINLET, A_GIMME, 0); + + class_addcreator((t_newmethod)abclone_new, gensym("abclone"), A_GIMME, 0); + class_addmethod(clone_class, (t_method)clone_click, gensym("click"), A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, 0); class_addmethod(clone_class, (t_method)clone_loadbang, gensym("loadbang"), diff --git a/pd/src/g_editor.c b/pd/src/g_editor.c index 50f8d035ca87cdd2b14f98c56e1721c33f68764b..54ee20261858970a305c5ad6fc222dc05b33f601 100644 --- a/pd/src/g_editor.c +++ b/pd/src/g_editor.c @@ -1288,7 +1288,21 @@ void canvas_undo_paste(t_canvas *x, void *z, int action) } } +void canvas_dirtyclimb(t_canvas *x, int n); +void clone_iterate(t_pd *z, t_canvas_iterator it, void* data); int clone_match(t_pd *z, t_symbol *name, t_symbol *dir); +int clone_isab(t_pd *z); +int clone_matchab(t_pd *z, t_ab_definition *source); + +/* packed data passing structure for glist_doreload */ +typedef struct _reload_data +{ + t_symbol *n; + t_symbol *d; + t_gobj *e; +} t_reload_data; + +static void glist_doreload_packed(t_canvas *x, t_reload_data *data); /* recursively check for abstractions to reload as result of a save. Don't reload the one we just saved ("except") though. */ @@ -1310,7 +1324,7 @@ static void glist_doreload(t_glist *gl, t_symbol *name, t_symbol *dir, /* remake the object if it's an abstraction that appears to have been loaded from the file we just saved */ remakeit = (g != except && pd_class(&g->g_pd) == canvas_class && - canvas_isabstraction((t_canvas *)g) && + canvas_isabstraction((t_canvas *)g) && !((t_canvas *)g)->gl_isab && ((t_canvas *)g)->gl_name == name && canvas_getdir((t_canvas *)g) == dir); @@ -1375,16 +1389,32 @@ static void glist_doreload(t_glist *gl, t_symbol *name, t_symbol *dir, if (g != except && pd_class(&g->g_pd) == canvas_class && (!canvas_isabstraction((t_canvas *)g) || + ((t_canvas *)g)->gl_isab || ((t_canvas *)g)->gl_name != name || canvas_getdir((t_canvas *)g) != dir) ) glist_doreload((t_canvas *)g, name, dir, except); + + /* also reload the instances within ab-based clone objects + *COMMENT: missing recursive case for abstraction-based clone + objects that don't match with the one we are reloading? */ + if(pd_class(&g->g_pd) == clone_class && clone_isab(&g->g_pd)) + { + t_reload_data d; + d.n = name; d.d = dir; d.e = except; + clone_iterate(&g->g_pd, glist_doreload_packed, &d); + } g = g->g_next; } if (!hadwindow && gl->gl_havewindow) canvas_vis(glist_getcanvas(gl), 0); } +static void glist_doreload_packed(t_canvas *x, t_reload_data *data) +{ + glist_doreload(x, data->n, data->d, data->e); +} + /* this flag stops canvases from being marked "dirty" if we have to touch them to reload an abstraction; also suppress window list update */ int glist_amreloadingabstractions = 0; @@ -1402,6 +1432,93 @@ void canvas_reload(t_symbol *name, t_symbol *dir, t_gobj *except) canvas_resume_dsp(dspwas); } +/* packed data passing structure for glist_doreload_ab */ +typedef struct _reload_ab_data +{ + t_ab_definition *a; + t_gobj *e; +} t_reload_ab_data; + +static void glist_doreload_ab_packed(t_canvas *x, t_reload_ab_data *data); + +/* recursive ab reload method */ +static void glist_doreload_ab(t_canvas *x, t_ab_definition *a, t_gobj *e) +{ + t_gobj *g; + int i, nobj = glist_getindex(x, 0); + int found = 0, remakeit = 0; + + for (g = x->gl_list, i = 0; g && i < nobj; i++) + { + remakeit = (g != e && pd_class(&g->g_pd) == canvas_class && canvas_isabstraction((t_canvas *)g) + && ((t_canvas *)g)->gl_isab && ((t_canvas *)g)->gl_absource == a); + + /* remove dirtiness visual markings */ + if(remakeit && ((t_canvas *)g)->gl_dirty) + canvas_dirtyclimb((t_canvas *)g, 0); + + remakeit = remakeit || (pd_class(&g->g_pd) == clone_class && clone_matchab(&g->g_pd, a)); + + if(remakeit) + { + canvas_create_editor(x); + if (!found) + { + glist_noselect(x); + found = 1; + } + glist_select(x, g); + } + /* since this one won't be reloaded, we need to trigger initbang manually */ + else if(g == e) + canvas_initbang((t_canvas *)g); + g = g->g_next; + } + if (found) + { + canvas_cut(x); + canvas_undo_undo(x); + glist_noselect(x); + } + + for (g = x->gl_list, i = 0; g && i < nobj; i++) + { + if(pd_class(&g->g_pd) == canvas_class && (!canvas_isabstraction((t_canvas *)g) + || (((t_canvas *)g)->gl_isab && (((t_canvas *)g)->gl_absource != a)))) + glist_doreload_ab((t_canvas *)g, a, e); + + /* also reload the instances within ab-based clone objects */ + if(pd_class(&g->g_pd) == clone_class + && clone_isab(&g->g_pd) && !clone_matchab(&g->g_pd, a)) + { + t_reload_ab_data d; + d.a = a; d.e = e; + clone_iterate(&g->g_pd, glist_doreload_ab_packed, &d); + } + g = g->g_next; + } +} + +static void glist_doreload_ab_packed(t_canvas *x, t_reload_ab_data *data) +{ + glist_doreload_ab(x, data->a, data->e); +} + +/* reload ab instances */ +void canvas_reload_ab(t_canvas *x) +{ + t_canvas *c = canvas_getrootfor_ab(x); + int dspwas = canvas_suspend_dsp(); + glist_amreloadingabstractions = 1; + glist_doreload_ab(c, x->gl_absource, &x->gl_gobj); + glist_amreloadingabstractions = 0; + canvas_resume_dsp(dspwas); + /* we set the dirty flag of the root canvas (where the ab definitions + are stored) because its file contents (in this case, the definition + for an specific ab) has been edited */ + canvas_dirty(c, 1); +} + /* --------- 6. apply ----------- */ typedef struct _undo_apply @@ -6174,6 +6291,10 @@ void gobj_dirty(t_gobj *x, t_glist *g, int state) t_rtext *y = glist_findrtext(g, (t_text *)x); gui_vmess("gui_gobj_dirty", "xsi", g, rtext_gettag(y), state); } +<<<<<<< HEAD +======= + +>>>>>>> feature_privateabstractions2 /* tell the gui to display a specific message in the top right corner */ void canvas_warning(t_canvas *x, int warid) diff --git a/pd/src/g_readwrite.c b/pd/src/g_readwrite.c index 672d536bb12c9ba32c39d515fedfafc944271bf3..007dcc0ee3f156ce1a686233496b4a1154912ac8 100644 --- a/pd/src/g_readwrite.c +++ b/pd/src/g_readwrite.c @@ -103,6 +103,7 @@ void canvas_saved(t_glist *x, t_symbol *s, int argc, t_atom *argv) } void canvas_savedeclarationsto(t_canvas *x, t_binbuf *b); +void canvas_saveabdefinitionsto(t_canvas *x, t_binbuf *b); /* the following routines read "scalars" from a file into a canvas. */ @@ -830,6 +831,9 @@ static void canvas_saveto(t_canvas *x, t_binbuf *b) } canvas_savedeclarationsto(x, b); } + + canvas_saveabdefinitionsto(x, b); + for (y = x->gl_list; y; y = y->g_next) gobj_save(y, b); @@ -976,26 +980,55 @@ static void canvas_savetofile(t_canvas *x, t_symbol *filename, t_symbol *dir, binbuf_free(b); } +void canvas_reload_ab(t_canvas *x); + +/* updates the shared ab definition and reloads all instances */ +static void canvas_save_ab(t_canvas *x, t_floatarg fdestroy) +{ + if(!x->gl_absource) bug("canvas_save_ab"); + + t_binbuf *b = binbuf_new(); + canvas_savetemplatesto(x, b, 1); + canvas_saveto(x, b); + + binbuf_free(x->gl_absource->ad_source); + x->gl_absource->ad_source = b; + + canvas_dirty(x, 0); + canvas_reload_ab(x); + + if (fdestroy != 0) //necessary? + vmess(&x->gl_pd, gensym("menuclose"), "f", 1.); +} + static void canvas_menusaveas(t_canvas *x, t_floatarg fdestroy) { t_canvas *x2 = canvas_getrootfor(x); - gui_vmess("gui_canvas_saveas", "xssi", - x2, - (strncmp(x2->gl_name->s_name, "Untitled", 8) ? - x2->gl_name->s_name : "title"), - canvas_getdir(x2)->s_name, - fdestroy != 0); + if(!x->gl_isab) + gui_vmess("gui_canvas_saveas", "xssi", + x2, + (strncmp(x2->gl_name->s_name, "Untitled", 8) ? + x2->gl_name->s_name : "title"), + canvas_getdir(x2)->s_name, + fdestroy != 0); + else if(x->gl_dirty) + canvas_save_ab(x2, fdestroy); } static void canvas_menusave(t_canvas *x, t_floatarg fdestroy) { t_canvas *x2 = canvas_getrootfor(x); char *name = x2->gl_name->s_name; - if (*name && strncmp(name, "Untitled", 8) - && (strlen(name) < 4 || strcmp(name + strlen(name)-4, ".pat") - || strcmp(name + strlen(name)-4, ".mxt"))) - canvas_savetofile(x2, x2->gl_name, canvas_getdir(x2), fdestroy); - else canvas_menusaveas(x2, fdestroy); + if(!x->gl_isab) + { + if (*name && strncmp(name, "Untitled", 8) + && (strlen(name) < 4 || strcmp(name + strlen(name)-4, ".pat") + || strcmp(name + strlen(name)-4, ".mxt"))) + canvas_savetofile(x2, x2->gl_name, canvas_getdir(x2), fdestroy); + else canvas_menusaveas(x2, fdestroy); + } + else if(x->gl_dirty) + canvas_save_ab(x2, fdestroy); } static void canvas_menuprint(t_canvas *x) diff --git a/pd/src/x_interface.c b/pd/src/x_interface.c index fe05f3c52b87b5b3387cbb4cc6f54965918d4fb6..80a8c85ad6ffa00528c0f7ab018ee1c1c6b0cb2b 100644 --- a/pd/src/x_interface.c +++ b/pd/src/x_interface.c @@ -331,6 +331,14 @@ typedef struct _debuginfo { t_object x_obj; } t_debuginfo; +static t_class *abinfo_class; + +typedef struct _abinfo +{ + t_object x_obj; + t_ab_definition *abdef; +} t_abinfo; + /* used by all the *info objects */ static int info_to_console = 0; @@ -1629,6 +1637,69 @@ void objectinfo_setup(void) post("stable objectinfo methods: class"); } +/* -------------------------- abinfo ------------------------------ */ + +void *abinfo_new(void) +{ + t_abinfo *x; + t_canvas *c = canvas_getcurrent(); + while(c->gl_owner && !c->gl_isab) c = c->gl_owner; + if(c->gl_isab) + { + x = (t_abinfo *)pd_new(abinfo_class); + x->abdef = c->gl_absource; + outlet_new(&x->x_obj, &s_list); + } + else + { + error("abinfo: only instantiable inside an ab object"); + x = 0; + } + return (x); +} + +void abinfo_name(t_abinfo *x, t_symbol *s, int argc, t_atom *argv) +{ + t_atom at[1]; + SETSYMBOL(at, x->abdef->ad_name); + info_out((t_text *)x, s, 1, at); +} + +void abinfo_instances(t_abinfo *x, t_symbol *s, int argc, t_atom *argv) +{ + t_atom at[1]; + SETFLOAT(at, x->abdef->ad_numinstances); + info_out((t_text *)x, s, 1, at); +} + +void abinfo_within(t_abinfo *x, t_symbol *s, int argc, t_atom *argv) +{ + t_binbuf *buf = binbuf_new(); + int i; + for(i = 0; i < x->abdef->ad_numdep; i++) + { + binbuf_addv(buf, "s", x->abdef->ad_dep[i]->ad_name); + } + info_out((t_text *)x, s, binbuf_getnatom(buf), binbuf_getvec(buf)); + binbuf_free(buf); +} + +//ADD MORE METHODS + +void abinfo_setup(void) +{ + abinfo_class = class_new(gensym("abinfo"), (t_newmethod)abinfo_new, 0, + sizeof(t_abinfo), CLASS_DEFAULT, 0); + + class_addmethod(abinfo_class, (t_method)abinfo_name, + gensym("name"), A_GIMME, 0); + class_addmethod(abinfo_class, (t_method)abinfo_instances, + gensym("instances"), A_GIMME, 0); + class_addmethod(abinfo_class, (t_method)abinfo_within, + gensym("within"), A_GIMME, 0); +} + + void debuginfo_print(t_debuginfo *x) { info_print((t_text *)x); @@ -1665,6 +1736,7 @@ void x_interface_setup(void) classinfo_setup(); debuginfo_setup(); objectinfo_setup(); + abinfo_setup(); pdinfo_setup(); print_setup(); unpost_setup();