From fdf163ad38abf7beca1df0aaff394b7a7ffa9c5e Mon Sep 17 00:00:00 2001 From: Guillem <guillembartrina@gmail.com> Date: Wed, 9 Sep 2020 12:59:29 +0200 Subject: [PATCH] add an alert system that notifies the user of the existence of unsaved edited instances of the abstraction he is editing or about to edit --- pd/nw/locales/de/translation.json | 4 + pd/nw/locales/en/translation.json | 4 + pd/nw/locales/fr/translation.json | 4 + pd/nw/pd_canvas.html | 3 + pd/nw/pd_canvas.js | 3 + pd/nw/pdgui.js | 32 +++++++- pd/src/g_canvas.c | 132 ++++++++++++++++++++++++++++++ pd/src/g_canvas.h | 2 + pd/src/g_editor.c | 11 ++- 9 files changed, 193 insertions(+), 2 deletions(-) diff --git a/pd/nw/locales/de/translation.json b/pd/nw/locales/de/translation.json index 69265bfb6..37fa1f64e 100644 --- a/pd/nw/locales/de/translation.json +++ b/pd/nw/locales/de/translation.json @@ -297,6 +297,10 @@ "none": "Keinen", "none_tt": "Ersetze keinen der Subpatches" }, + "warning": { + "unsaved_tt": "There is an unsaved edited instance of this abstraction", + "multipleunsaved_tt": "There is another unsaved edited instance of this abstraction" + }, "find": { "placeholder": "Suche im Patch", "search": "Suche", diff --git a/pd/nw/locales/en/translation.json b/pd/nw/locales/en/translation.json index 5a161dce5..2a24c5494 100644 --- a/pd/nw/locales/en/translation.json +++ b/pd/nw/locales/en/translation.json @@ -297,6 +297,10 @@ "none": "None", "none_tt": "Do not replace any subpatch" }, + "warning": { + "unsaved_tt": "There is an unsaved edited instance of this abstraction", + "multipleunsaved_tt": "There is another unsaved edited instance of this abstraction" + }, "find": { "placeholder": "Search in Canvas", "search": "Search", diff --git a/pd/nw/locales/fr/translation.json b/pd/nw/locales/fr/translation.json index 8a3408787..04da6b2c3 100644 --- a/pd/nw/locales/fr/translation.json +++ b/pd/nw/locales/fr/translation.json @@ -297,6 +297,10 @@ "none": "Aucun", "none_tt": "Ne remplacer aucun sous-patch" }, + "warning": { + "unsaved_tt": "There is an unsaved edited instance of this abstraction", + "multipleunsaved_tt": "There is another unsaved edited instance of this abstraction" + }, "find": { "placeholder": "Chercher dans le Canevas", "search": "Chercher", diff --git a/pd/nw/pd_canvas.html b/pd/nw/pd_canvas.html index 7ba063a27..d61d4ae94 100644 --- a/pd/nw/pd_canvas.html +++ b/pd/nw/pd_canvas.html @@ -102,6 +102,9 @@ </button> </div> </dialog> + <div style="position: fixed; right: 2%; top: 2%; "> + <strong id="canvas_warning" style="display: none; text-align: right; -webkit-user-select: none;">!</strong> + </div> <div id="hscroll" style="position: fixed; left: 0px; bottom: 0px; border-radius: 0px; width: 10px; height: 5px; visibility: hidden;"></div> <div id="vscroll" style="position: fixed; right: 0px; top: 0px; border-radius: 0px; width: 5px; height: 10px; visibility: hidden;"></div> <script type="text/javascript" src="./pd_canvas.js"></script> diff --git a/pd/nw/pd_canvas.js b/pd/nw/pd_canvas.js index 60278d79f..a0bfb1a2e 100644 --- a/pd/nw/pd_canvas.js +++ b/pd/nw/pd_canvas.js @@ -1336,6 +1336,9 @@ function register_window_id(cid, attr_array) { // we check the title_queue to see if our title now contains an asterisk // (which is the visual cue for "dirty") + // Enable/disable the warning for multiple dirty instances + pdgui.gui_canvas_warning(cid, attr_array.warid); + // Two possibilities for handling this better: // have a representation of canvas attys in pdgui.js (editmode, dirty, etc.) // or diff --git a/pd/nw/pdgui.js b/pd/nw/pdgui.js index a23dc55d0..5ef0918dd 100644 --- a/pd/nw/pdgui.js +++ b/pd/nw/pdgui.js @@ -1760,7 +1760,7 @@ function create_window(cid, type, width, height, xpos, ypos, attr_array) { } // create a new canvas -function gui_canvas_new(cid, width, height, geometry, grid, zoom, editmode, name, dir, dirty_flag, hide_scroll, hide_menu, has_toplevel_scalars, cargs) { +function gui_canvas_new(cid, width, height, geometry, grid, zoom, editmode, name, dir, dirty_flag, warid, hide_scroll, hide_menu, has_toplevel_scalars, cargs) { // hack for buggy tcl popups... should go away for node-webkit //reset_ctrl_on_popup_window @@ -1817,6 +1817,7 @@ function gui_canvas_new(cid, width, height, geometry, grid, zoom, editmode, name name: name, dir: dir, dirty: dirty_flag, + warid: warid, args: cargs, zoom: zoom, editmode: editmode, @@ -2860,6 +2861,35 @@ function gui_gobj_dirty(cid, tag, state) { }); } +function gui_canvas_warning(cid, warid) { + var warning = get_item(cid, "canvas_warning"); + switch(warid) + { + case 0: + warning.style.setProperty("display", "none"); + break; + case 1: + warning.title = lang.get_local_string("canvas.warning.unsaved_tt"); + warning.onclick = function(){ pdsend(cid, "showdirty"); } + warning.style.setProperty("color", "coral"); + warning.style.setProperty("font-size", "x-large"); + warning.style.setProperty("display", "inline"); + break; + case 2: + warning.title = lang.get_local_string("canvas.warning.multipleunsaved_tt"); + warning.onclick = function(){ pdsend(cid, "showdirty"); } + warning.style.setProperty("color", "red"); + warning.style.setProperty("font-size", "xx-large"); + warning.style.setProperty("display", "inline"); + break; + + default: + break; + } +} + +exports.gui_canvas_warning = gui_canvas_warning; + function gui_canvas_emphasize(cid) { gui(cid).get_elem("patchsvg", function(e) { // raise the window diff --git a/pd/src/g_canvas.c b/pd/src/g_canvas.c index c88379b5c..8cabb3506 100644 --- a/pd/src/g_canvas.c +++ b/pd/src/g_canvas.c @@ -396,6 +396,8 @@ static int calculate_zoom(t_float zoom_hack) return zoom; } +int canvas_dirty_broadcast_all(t_symbol *name, t_symbol *dir, 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() tells us which.) */ @@ -472,6 +474,7 @@ t_canvas *canvas_new(void *dummy, t_symbol *sel, int argc, t_atom *argv) else x->gl_env = 0; x->gl_subdirties = 0; + x->gl_dirties = 0; if (yloc < GLIST_DEFCANVASYLOC) yloc = GLIST_DEFCANVASYLOC; @@ -522,6 +525,14 @@ t_canvas *canvas_new(void *dummy, t_symbol *sel, int argc, t_atom *argv) canvas_field_vec = NULL; canvas_field_gp = NULL; + /* in the case it is an abstraction (gl_env is not null) + get the number of dirty instances of this same abstraction */ + if(x->gl_env) + { + x->gl_dirties = canvas_dirty_broadcast_all(x->gl_name, + canvas_getdir(x), 0); + } + return(x); } @@ -792,6 +803,100 @@ void canvas_dirtyclimb(t_canvas *x, int n) } } +/* the following functions are used to broadcast messages to all instances of + a specific abstraction (either file-based or ab). + the state of these instances change accoring to the message sent. */ + +void clone_iterate(t_pd *z, t_canvas_iterator it, void* data); +int clone_match(t_pd *z, t_symbol *name, t_symbol *dir); + +static void canvas_dirty_common(t_canvas *x, int mess) +{ + if(mess == 2) + { + if(x->gl_dirty) + { + if(!x->gl_havewindow) canvas_vis(x, 1); + gui_vmess("gui_canvas_emphasize", "x", x); + } + } + else + { + x->gl_dirties += mess; + if(x->gl_havewindow) + canvas_warning(x, (x->gl_dirties > 1 ? + (x->gl_dirty ? 2 : 1) + : (x->gl_dirties ? !x->gl_dirty : 0))); + } +} + +/* packed data passing structure for canvas_dirty_broadcast */ +typedef struct _dirty_broadcast_data +{ + t_symbol *name; + t_symbol *dir; + int mess; + int *res; /* return value */ +} t_dirty_broadcast_data; + +static void canvas_dirty_deliver_packed(t_canvas *x, t_dirty_broadcast_data *data) +{ + *data->res += (x->gl_dirty > 0); + canvas_dirty_common(x, data->mess); +} + +static int canvas_dirty_broadcast_packed(t_canvas *x, t_dirty_broadcast_data *data); + +static int canvas_dirty_broadcast(t_canvas *x, t_symbol *name, t_symbol *dir, 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_name == name + && canvas_getdir((t_canvas *)g) == dir) + { + res += (((t_canvas *)g)->gl_dirty > 0); + canvas_dirty_common((t_canvas *)g, mess); + } + else + res += canvas_dirty_broadcast((t_canvas *)g, name, dir, mess); + } + else if(pd_class(&g->g_pd) == clone_class) + { + int cres = 0; + t_dirty_broadcast_data data; + data.name = name; data.dir = dir; data.mess = mess; data.res = &cres; + if(clone_match(&g->g_pd, name, dir)) + { + clone_iterate(&g->g_pd, canvas_dirty_deliver_packed, &data); + } + else + { + clone_iterate(&g->g_pd, canvas_dirty_broadcast_packed, &data); + } + res += cres; + } + } + return (res); +} + +static int canvas_dirty_broadcast_packed(t_canvas *x, t_dirty_broadcast_data *data) +{ + *data->res = canvas_dirty_broadcast(x, data->name, data->dir, data->mess); +} + +int canvas_dirty_broadcast_all(t_symbol *name, t_symbol *dir, int mess) +{ + int res = 0; + t_canvas *x; + for (x = pd_this->pd_canvaslist; x; x = x->gl_next) + res += canvas_dirty_broadcast(x, name, dir, mess); + return (res); +} /* mark a glist dirty or clean */ void canvas_dirty(t_canvas *x, t_floatarg n) { @@ -806,6 +911,15 @@ 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)); + } } } @@ -1034,6 +1148,15 @@ extern void canvas_group_free(t_pd *x); void canvas_free(t_canvas *x) { //fprintf(stderr,"canvas_free %zx\n", (t_uint)x); + + /* 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); + } + t_gobj *y; int dspstate = canvas_suspend_dsp(); @@ -1893,6 +2016,13 @@ void canvas_redrawallfortemplatecanvas(t_canvas *x, int action) canvas_redrawallfortemplate(0, action); } +/* --------- */ + +static void canvas_showdirty(t_canvas *x) +{ + canvas_dirty_broadcast_all(x->gl_name, canvas_getdir(x), 2); +} + /* ------------------------------- declare ------------------------ */ /* put "declare" objects in a patch to tell it about the environment in @@ -2765,6 +2895,8 @@ 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); /*---------------------------- 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 a5d7ed116..44ee4e4ab 100644 --- a/pd/src/g_canvas.h +++ b/pd/src/g_canvas.h @@ -235,6 +235,7 @@ struct _glist t_gpointer gl_gp; /* parent for "canvas" data type */ int gl_subdirties; /* number of descending dirty abstractions */ + int gl_dirties; /* number of diry instances, for multiple dirty warning */ }; #define gl_gobj gl_obj.te_g @@ -581,6 +582,7 @@ EXTERN int canvas_setdeleting(t_canvas *x, int flag); EXTERN int canvas_hasarray(t_canvas *x); EXTERN int canvas_has_scalars_only(t_canvas *x); +EXTERN void canvas_warning(t_canvas *x, int warid); #define LB_LOAD 0 /* "loadbang" actions - 0 for original meaning */ #define LB_INIT 1 /* loaded but not yet connected to parent patch */ diff --git a/pd/src/g_editor.c b/pd/src/g_editor.c index 295f13bb2..50f8d035c 100644 --- a/pd/src/g_editor.c +++ b/pd/src/g_editor.c @@ -2752,7 +2752,7 @@ void canvas_vis(t_canvas *x, t_floatarg f) We may need to expand this to include scalars, as well. */ canvas_create_editor(x); canvas_args_to_string(argsbuf, x); - gui_vmess("gui_canvas_new", "xiisiiissiiiis", + gui_vmess("gui_canvas_new", "xiisiiissiiiiis", x, (int)(x->gl_screenx2 - x->gl_screenx1), (int)(x->gl_screeny2 - x->gl_screeny1), @@ -2763,6 +2763,9 @@ void canvas_vis(t_canvas *x, t_floatarg f) x->gl_name->s_name, canvas_getdir(x)->s_name, x->gl_dirty, + (x->gl_dirties > 1 ? + (x->gl_dirty ? 2 : 1) + : (x->gl_dirties ? !x->gl_dirty : 0)), x->gl_noscroll, x->gl_nomenu, canvas_hasarray(x), @@ -6171,6 +6174,12 @@ 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); } + /* tell the gui to display a specific message in the + top right corner */ +void canvas_warning(t_canvas *x, int warid) +{ + gui_vmess("gui_canvas_warning", "xi", x, warid); +} static int glist_dofinderror(t_glist *gl, void *error_object) { -- GitLab