diff --git a/pd/nw/dialog_abstractions.html b/pd/nw/dialog_abstractions.html new file mode 100644 index 0000000000000000000000000000000000000000..0525ed1b665640ff50e3524426fe6d026408bacf --- /dev/null +++ b/pd/nw/dialog_abstractions.html @@ -0,0 +1,197 @@ +<!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" disabled> + <legend data-i18n="abstractions.filebased"></legend> + <table style="width: 90%; table-layout: fixed;"> + </table> + </fieldset> + <hr> + <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> + <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[0], + privateglobal_table = tables[1], + privatelocal_table = tables[2]; + + 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/locales/en/translation.json b/pd/nw/locales/en/translation.json index 6a0e72bae8428c1382893e88af7c75b79a5f123d..0f902cff6b735aab4c69c06e34ab4851c5ff84fe 100644 --- a/pd/nw/locales/en/translation.json +++ b/pd/nw/locales/en/translation.json @@ -235,6 +235,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", @@ -485,5 +487,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 0bbcf24e9f6fa8a71771c37dcb657283c6edf11f..0cac8ec5ba9f8216b685882ff354d76f690208bb 100644 --- a/pd/nw/pd_canvas.js +++ b/pd/nw/pd_canvas.js @@ -1403,6 +1403,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") { @@ -1910,6 +1945,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 cfbc48f25c561eb3a760906db1ce9a31eb4e7700..add45392f076b5f696c0c515b721ab238a872b0c 100644 --- a/pd/nw/pd_menus.js +++ b/pd/nw/pd_menus.js @@ -533,6 +533,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 951cede88a9f8744d919fdcd1711ca38dfc8edf5..fab5208c98b7a73eeb414489561f4895c07c015a 100644 --- a/pd/nw/pdgui.js +++ b/pd/nw/pdgui.js @@ -5601,6 +5601,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 d4961a0a13585c7487d8047835fc42dd22f6c572..04c2e6c7cf4189826514c8e1e898039097f8359b 100644 --- a/pd/src/g_canvas.c +++ b/pd/src/g_canvas.c @@ -2543,6 +2543,61 @@ static void *ab_new(t_symbol *s, int argc, t_atom *argv) 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; @@ -2594,53 +2649,6 @@ static void abdefs_instances(t_abdefs *x, t_symbol *s) error("abdefs: couldn't find definition for '%s'", s->s_name); } -static void abdefs_del(t_abdefs *x, t_symbol *s) -{ - t_ab_definition *abdef; - if((abdef = canvas_find_ab(x->x_canvas, s)) && !abdef->ad_numinstances) - { - canvas_del_ab(x->x_canvas, s); - post("abdefs: definition for '%s' has been deleted", s->s_name); - } - else if(abdef) - error("abdefs: couldn't delete '%s', it has at least one instance", s->s_name); - else - error("abdefs: couldn't find definition for '%s'", s->s_name); -} - -static void abdefs_clean(t_abdefs *x) -{ - t_canvas *c = canvas_getrootfor_ab(x->x_canvas); - t_ab_definition *abdef, *abdefpre; - t_binbuf *buf = binbuf_new(); - int tot = 0; - for(abdef = c->gl_abdefs, abdefpre = 0; abdef; ) - { - if(!abdef->ad_numinstances) - { - binbuf_addv(buf, "s", abdef->ad_name); - tot++; - 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)); - if(abdefpre) abdef = abdefpre->ad_next; - else abdef = c->gl_abdefs; - } - else - { - abdefpre = abdef; - abdef = abdef->ad_next; - } - } - startpost("abdefs: a total of [%d] ab definitions have been deleted\n> ", tot); - postatom(binbuf_getnatom(buf), binbuf_getvec(buf)); - endpost(); - binbuf_free(buf); -} - static void abdefs_menuopen(t_abdefs *x) { char buf[MAXPDSTRING]; @@ -2650,18 +2658,13 @@ static void abdefs_menuopen(t_abdefs *x) gui_start_vmess("gui_external_dialog", "s", gfx_tag); gui_s("[ab] definitions"); gui_start_array(); - gui_s("name | #instances [delete?]_hidden"); gui_i(0); - gui_s("----------------------------------------_hidden"); gui_i(0); if(!c->gl_abdefs) { - gui_s("*no definitions*_hidden"); gui_i(0); + gui_s("Ø_hidden"); gui_i(0); } for(abdef = c->gl_abdefs; abdef; abdef = abdef->ad_next) { - if(abdef->ad_numinstances) - sprintf(buf, "%s | %d _hidden", abdef->ad_name->s_name, abdef->ad_numinstances); - else - sprintf(buf, "%s | %d _toggle", abdef->ad_name->s_name, 0); + sprintf(buf, "%s (%d)_hidden", abdef->ad_name->s_name, abdef->ad_numinstances); gui_s(buf); gui_i(0); } gui_end_array(); @@ -2670,47 +2673,6 @@ static void abdefs_menuopen(t_abdefs *x) static void abdefs_dialog(t_abdefs *x, t_symbol *s, int argc, t_atom *argv) { - argc -= 2; - argv += 2; - - t_canvas *c = canvas_getrootfor_ab(x->x_canvas); - t_ab_definition *abdef, *abdefpre; - t_binbuf *buf = binbuf_new(); - int tot = 0; - for(abdef = c->gl_abdefs, abdefpre = 0; abdef; ) - { - if(atom_getfloat(argv)) - { - if(abdef->ad_numinstances) bug("abdefs_dialog"); - - binbuf_addv(buf, "s", abdef->ad_name); - tot++; - - 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)); - if(abdefpre) abdef = abdefpre->ad_next; - else abdef = c->gl_abdefs; - } - else - { - abdefpre = abdef; - abdef = abdef->ad_next; - } - argv++; - } - - if(tot) - { - startpost("abdefs: a total of [%d] ab definitions have been deleted\n> ", tot); - postatom(binbuf_getnatom(buf), binbuf_getvec(buf)); - endpost(); - } - binbuf_free(buf); - gfxstub_deleteforkey(x); } @@ -3577,13 +3539,15 @@ void g_canvas_setup(void) 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(abdefs_class, (t_method)abdefs_del, gensym("del"), A_SYMBOL, 0); - class_addmethod(abdefs_class, (t_method)abdefs_clean, gensym("clean"), 0); class_addmethod(abdefs_class, (t_method)abdefs_menuopen, gensym("menu-open"), 0); class_addmethod(abdefs_class, (t_method)abdefs_dialog, gensym("dialog"), A_GIMME, 0); class_addmethod(canvas_class, (t_method)canvas_showdirty, gensym("showdirty"), 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);