Commit 84c03a78 authored by Guillem Bartrina's avatar Guillem Bartrina
Browse files

Merge branch 'feature_privateabstractions2' into feature_privateabstractions_test

parents edb80827 da0b4fc7
Subproject commit b767c65b6607a1b2b80798a5ff4fb3e3c1d752a5
Subproject commit 8eaaaa3c39af8968248fab5659d4a4d2da4f1ed7
<!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>
......@@ -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";
}
......
......@@ -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"
}
}
......@@ -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, {
......
......@@ -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
......
......@@ -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) {
......
This diff is collapsed.
......@@ -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);
......
......@@ -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"),
......
......@@ -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);