Commit 8269de18 authored by Albert Gräf's avatar Albert Gräf
Browse files

Merge branch 'feature_privateabstractions_test' into release_2.15.0

parents 967e6a05 f31452b5
#N canvas 717 240 480 339 12;
#X abframe 1;
#N canvas 870 153 488 334 12;
#X obj 27 32 inlet;
#X obj 27 61 t f b b;
#X obj 27 140 expr 2*$f1*$f2+$f3;
#X obj 27 169 outlet;
#X obj 88 104 f \$1;
#X obj 150 104 f \$2;
#X obj 252 225 abinfo;
#X obj 252 254 print abinfo;
#X msg 219 188 name;
#X msg 264 188 instances;
#X msg 344 188 within;
#X obj 27 233 bng 15 250 50 0 empty empty empty 17 7 0 10 #fcfcfc #000000
#000000;
#X obj 27 257 f \$0;
#X obj 27 286 print;
#X text 94 61 private abstractions can have arguments...;
#X text 26 202 ... and have their own \$0;
#X obj 115 233 bng 15 250 50 0 empty empty empty 17 7 0 10 #fcfcfc
#000000 #000000;
#X obj 115 257 ab bar;
#X floatatom 115 286 5 0 0 0 - - -, f 5;
#X text 218 98 abinfo yields the name of the abstraction \, the number
of its instances \, and the names of other private abstractions they
are contained in (if any), f 35;
#X connect 0 0 1 0;
#X connect 1 0 2 0;
#X connect 1 1 4 0;
#X connect 1 2 5 0;
#X connect 2 0 3 0;
#X connect 4 0 2 1;
#X connect 5 0 2 2;
#X connect 6 0 7 0;
#X connect 8 0 6 0;
#X connect 9 0 6 0;
#X connect 10 0 6 0;
#X connect 11 0 12 0;
#X connect 12 0 13 0;
#X connect 16 0 17 0;
#X connect 17 0 18 0;
#X abpush foo;
#N canvas 869 280 453 305 12;
#X obj 23 21 bng 15 250 50 0 empty empty empty 17 7 0 10 #fcfcfc #000000
#000000;
#X obj 23 45 ab bar;
#X floatatom 23 74 5 0 0 0 - - -, f 5;
#X text 81 45 <-- open "bar" and try abinfo "within"!;
#X obj 23 203 ab other_foobar;
#X text 20 233 Normally \, private abstractions such as [ab bar] above
have global scope \, so they are visible through the entire root patch
\, including all its subpatches and private abstractions., f 57;
#X text 20 109 Note that @foobar has local scope (indicated by the
"@" prefix) \, which includes all subpatches \, but doesn't cross (private)
abstraction boundaries. Thus the @foobar inside the other_foobar abstraction
below is a different @foobar than this one.;
#X connect 0 0 1 0;
#X connect 1 0 2 0;
#X abpush @foobar;
#N canvas 1023 266 454 304 12;
#X obj 44 54 f \$0;
#X obj 44 83 outlet;
#X obj 44 25 inlet;
#X obj 147 63 abinfo;
#X obj 147 92 print abinfo;
#X msg 114 26 name;
#X msg 159 26 instances;
#X msg 239 26 within;
#X connect 0 0 1 0;
#X connect 2 0 0 0;
#X connect 3 0 4 0;
#X connect 5 0 3 0;
#X connect 6 0 3 0;
#X connect 7 0 3 0;
#X abpush bar foo @foobar;
#N canvas 788 330 452 303 12;
#X obj 22 24 ab @foobar;
#X text 112 24 <-- click here;
#X abpush other_foobar @foobar;
#N canvas 950 315 452 304 12;
#X text 16 31 Note that this instance of @foobar is different from
the one in the main patch!;
#X abpush other_foobar#@foobar other_foobar;
#X abframe 0;
#X floatatom 31 12 5 0 0 0 - - -, f 5;
#X floatatom 31 70 5 0 0 0 - - -, f 5;
#X obj 31 41 ab foo 3 2;
#X obj 267 89 bng 15 250 50 0 empty empty empty 17 7 0 10 #fcfcfc #000000
#000000;
#X obj 267 113 abdefs;
#X obj 267 142 print defs;
#X floatatom 30 159 5 0 0 0 - - -, f 5;
#X floatatom 30 217 5 0 0 0 - - -, f 5;
#X floatatom 128 71 5 0 0 0 - - -, f 5;
#X text 127 28 ... and cloned, f 10;
#X obj 128 99 pack f f;
#X msg 128 128 \$2 \$1;
#X obj 128 187 unpack f f;
#X floatatom 128 216 5 0 0 0 - - -, f 5;
#X floatatom 195 216 5 0 0 0 - - -, f 5;
#X text 29 117 ab's can be copied..., f 11;
#X obj 30 188 ab foo 3 5;
#X text 265 13 abdefs prints the list of all private abstractions contained
in a patch \, along with their instance counts, f 27;
#X text 22 266 CAVEAT: Private abstractions are identified by their
name. In contrast to one-offs \, editing their name may create a new
(and empty) abstraction!;
#X obj 128 158 abclone foo 4 5;
#X obj 181 73 hradio 15 0 0 4 empty empty empty 0 -8 0 10 #fcfcfc #000000
#000000 0;
#X obj 267 230 ab @foobar;
#N canvas 432 306 417 220 META 0;
#X text 13 47 LIBRARY internal;
#X text 13 1 KEYWORDS abstraction private;
#X text 13 24 DESCRIPTION creates a private abstraction;
#X text 13 70 AUTHOR Guillem Bartrina;
#X text 13 116 RELEASE_DATE 2020;
#X text 13 139 HELP_PATCH_AUTHORS Albert Gräf;
#X text 13 93 WEBSITE https://agraef.github.io/purr-data/;
#X restore 408 306 pd META;
#X text 265 174 a "local" abstraction which illustrates local scope
as well as abinfo "within"., f 27;
#X connect 0 0 2 0;
#X connect 2 0 1 0;
#X connect 3 0 4 0;
#X connect 4 0 5 0;
#X connect 6 0 16 0;
#X connect 8 0 10 0;
#X connect 10 0 11 0;
#X connect 11 0 19 0;
#X connect 12 0 13 0;
#X connect 12 1 14 0;
#X connect 16 0 7 0;
#X connect 19 0 12 0;
#X connect 20 0 10 1;
<!DOCTYPE html>
<html>
<head>
<link id="page_style" rel="stylesheet" type="text/css" href="css/default.css">
</head>
<body class="dialog_body prefs_body" style="overflow: hidden;">
<div class="container noselect prefs_container">
<table id="titlebar">
<tr>
<td style="width: 100%;">
<div id="titlebar_title">Abstractions</div>
</td>
<td id="titlebar_buttons_td">
<div class="titlebar_buttons">
<div id="titlebar_close_button" onclick="cancel();">&#215</div>
</div>
</td>
</tr>
</table>
<form>
<fieldset style="width: 90%">
<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>
<!--
<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 id="delete_button" type="button" onClick="deleteselected()" data-i18n="[title]abstractions.delete_tt">
<span data-i18n="abstractions.delete"></span>
</button>
<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"),
privateglobal_table = tables[1],
privatelocal_table = tables[2];
//filebased_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.style.setProperty("max-height", "10px");
input_elem.style.setProperty("top", "0px");
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);
};
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";
}
......
......@@ -239,6 +239,8 @@
"visible_ancestor_tt": "Zeige den nächsten aktuell sichtbaren Vorgänger des aktuellen Fensters",
"pdwin": "Pd-Fenster",
"pdwin_tt": "Zeige das Pd-Hauptfenster",
"abstractions": "Abstraktionen",
"abstractions_tt": "Abstraktionen abfragen und verwalten",
"media": "Medien",
......@@ -506,5 +508,17 @@
"building_index": "Erstelle Index...",
"no_results": "Keine Resultate gefunden.",
"search_placeholder": "Suche Pd-Doku"
},
"abstractions": {
"filebased": "Datei-basierte Abstraktionen",
"private": "Private Abstraktionen",
"global": "Globaler Geltungsbereich",
"local": "Lokaler Geltungsbereich",
"selectall": "Alles auswählen",
"selectall_tt": "Wähle alle uninstanziierten Definitionen aus",
"delete": "Löschen",
"delete_tt": "Löscht alle ausgewählten Definitionen",
"close": "Schließen",
"close_tt": "Schließt das Dialog-Fenster"
}
}
......@@ -239,6 +239,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": "Query and manage abstractions",
"media": "Media",
......@@ -506,5 +508,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"
}
}
......@@ -239,6 +239,8 @@
"visible_ancestor_tt": "Donner le focus à l'ancêtre le plus proche de cette fenêtre qui est actuellement visible",
"pdwin": "Fenêtre Console Pd",
"pdwin_tt": "Donner le focus à la fenêtre principale de Pd",
"abstractions": "Abstractions",
"abstractions_tt": "Interroger et gérer les abstractions",
"media": "Média",
......@@ -281,7 +283,7 @@
"canvas": {
"paste_clipboard_prompt": "Attention: vous êtes sur le point de coller du 'code Pd' provenant de l'extérieur de Pd. Voulez-vous continuer ?",
"save_dialog": {
"prompt": "Voulez-vous enregistrer les modifications apportées à ce patch ?",
"prompt": "Voulez-vous enregistrer les modifications apportées à",
"yes": "Oui",
"yes_tt": "Enregistrer les modifications dans le fichier avant de fermer le patch ?",
"no": "Non",
......@@ -506,5 +508,17 @@
"building_index": "Construction de l'index...",
"no_results": "Aucun résultat trouvé !",
"search_placeholder": "Chercher dans les Docs Pd"
},
"abstractions": {
"filebased": "Abstractions basées sur des fichiers",
"private": "Abstractions privées",
"global": "Portée globale",
"local": "Portée locale",
"selectall": "Tout sélectionner",
"selectall_tt": "Sélectionner toutes les définitions avec zéro instance",
"delete": "Supprimer",
"delete_tt": "Supprimer toutes les définitions sélectionnées",
"close": "Fermer",
"close_tt": "Fermer la fenêtre de dialogue"
}
}
......@@ -2029,6 +2029,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
......
......@@ -6026,6 +6026,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,
Math.min(600, (private_abs.length*10 + 190+(nw_os_is_osx?20:0))), 0, 0, attrs);
}
// Global settings
function gui_pd_dsp(state) {
......
This diff is collapsed.
......@@ -169,6 +169,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.
......@@ -241,6 +259,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
......@@ -560,6 +583,8 @@ 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 int abframe;
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. */