From 84c016f61c7c9344c0fa14b452a4af82880d485f Mon Sep 17 00:00:00 2001
From: Jonathan Wilkes <jon.w.wilkes@gmail.com>
Date: Mon, 4 Jan 2016 17:19:29 -0500
Subject: [PATCH] use <dialog> modal instead of javascript prompt. Also, port
 some changes from Vanilla to allow closing window immediately after saving

---
 pd/nw/locales/en/translation.json |  8 ++++++
 pd/nw/pd_canvas.html              | 25 ++++++++++++++++++
 pd/nw/pd_canvas.js                | 31 ++++++++++++++++++++--
 pd/nw/pdgui.js                    | 44 +++++++++++++++++++++++--------
 pd/src/g_readwrite.c              | 21 ++++++++-------
 5 files changed, 107 insertions(+), 22 deletions(-)

diff --git a/pd/nw/locales/en/translation.json b/pd/nw/locales/en/translation.json
index 8677abdb3..d842be70b 100644
--- a/pd/nw/locales/en/translation.json
+++ b/pd/nw/locales/en/translation.json
@@ -243,6 +243,14 @@
     "devtools_tt": "Show the DevTools window (for debugging)"
   },
   "canvas": {
+    "save_dialog": {
+      "yes": "Yes",
+      "yes_tt": "Write the changes to file before closing the patch",
+      "no": "No",
+      "no_tt": "Don't save any changes, just close the patch",
+      "cancel": "Cancel",
+      "cancel_tt": "Don't save any changes, and don't close the patch"
+    },
     "prop": {
       "heading": {
         "gop": "appearance on parent",
diff --git a/pd/nw/pd_canvas.html b/pd/nw/pd_canvas.html
index feed1c5c5..4e5b2931e 100644
--- a/pd/nw/pd_canvas.html
+++ b/pd/nw/pd_canvas.html
@@ -39,6 +39,31 @@
              value="Search"/>
       </input>
     </div>
+    <dialog id="save_before_quit">
+      <h4>Do you want to save the changes you made in
+        <span id="save_before_quit_filename"></span>?
+      </h4>
+      <div id="save_before_quit_filename"></div>
+      <div class="submit_buttons">
+        <button type="button"
+                id="yes_button"
+                onClick="canvas_events.save_and_close();"
+                data-i18n="[title]canvas.save_dialog.yes_tt">
+          <span data-i18n="canvas.save_dialog.yes"></span>
+        </button>
+        <button type="button"
+                id="no_button"
+                data-i18n="[title]canvas.save_dialog.no_tt">
+          <span data-i18n="canvas.save_dialog.no"></span>
+        </button>
+        <button type="button"
+                id="cancel_button"
+                onClick="close_save_dialog()"
+                data-i18n="[title]canvas.save_dialog.cancel_tt">
+          <span data-i18n="canvas.save_dialog.cancel"></span>
+        </button>
+      </div>
+    </dialog>
     <script type="text/javascript" src="./pd_canvas.js"></script>
   </body>
 </html>
diff --git a/pd/nw/pd_canvas.js b/pd/nw/pd_canvas.js
index 97f646e0d..3071beecf 100644
--- a/pd/nw/pd_canvas.js
+++ b/pd/nw/pd_canvas.js
@@ -28,6 +28,10 @@ function add_keymods(key, evt) {
     return shift + ctrl + key;
 }
 
+function close_save_dialog() {
+    document.getElementById("save_before_quit").close();
+}
+
 function text_to_fudi(text) {
     text = text.trim();
     text = text.replace(/(\$[0-9]+)/g, "\\$1");    // escape dollar signs
@@ -444,7 +448,7 @@ var canvas_events = (function() {
     // with nwworkingdir
     document.querySelector("#saveDialog").addEventListener("change",
         function(evt) {
-            pdgui.saveas_callback(name, this.value);
+            pdgui.saveas_callback(name, this.value, 0);
             // reset value so that we can open the same file twice
             this.value = null;
             console.log("tried to save something");
@@ -645,10 +649,32 @@ var canvas_events = (function() {
                 scalar_draggables[id] = null;
             }
         },
-        clickable_resize_handle: false // this can be removed...
+        clickable_resize_handle: false, // this can be removed...
+        save_and_close: function() {
+            pdgui.pdsend(name, "menusave", 1);
+        },
+        close_without_saving: function(cid, force) {
+            pdgui.pdsend(name, "dirty 0");
+            pdgui.pdsend(cid, "menuclose", force);
+        }
     }
 }());
 
+// Stop-gap translator. We copy/pasted this in each dialog, too. It
+// should be moved to pdgui.js
+function translate_form() {
+    var i
+    var elements = document.querySelectorAll("[data-i18n]");
+    for (i = 0; i < elements.length; i++) {
+        var data = elements[i].dataset.i18n;
+        if (data.slice(0,7) === "[title]") {
+            elements[i].title = l(data.slice(7));
+        } else {
+            elements[i].textContent = l(data);
+        }
+    }
+}
+
 // This gets called from the nw_create_window function in index.html
 // It provides us with our canvas id from the C side.  Once we have it
 // we can create the menu and register event callbacks
@@ -667,6 +693,7 @@ function register_window_id(cid, attr_array) {
     }
     create_popup_menu(cid);
     canvas_events.register(cid);
+    translate_form();
     // Trigger a "focus" event so that OSX updates the menu for this window
     nw_window_focus_callback();
     canvas_events.normal();
diff --git a/pd/nw/pdgui.js b/pd/nw/pdgui.js
index 9384c046c..fe6e0552d 100644
--- a/pd/nw/pdgui.js
+++ b/pd/nw/pdgui.js
@@ -388,7 +388,7 @@ function build_file_dialog_string(obj) {
 
 exports.build_file_dialog_string = build_file_dialog_string;
 
-function gui_canvas_saveas (name, initfile, initdir) {
+function gui_canvas_saveas (name, initfile, initdir, close_flag) {
     var input, chooser,
         span = patchwin[name].window.document.querySelector("#saveDialogSpan");
     if (!fs.existsSync(initdir)) {
@@ -411,7 +411,7 @@ function gui_canvas_saveas (name, initfile, initdir) {
     span.innerHTML = input;
     chooser = patchwin[name].window.document.querySelector("#saveDialog"); 
     chooser.onchange = function() {
-        saveas_callback(name, this.value);
+        saveas_callback(name, this.value, close_flag);
         // reset value so that we can open the same file twice
         this.value = null;
         console.log("tried to save something");
@@ -419,7 +419,7 @@ function gui_canvas_saveas (name, initfile, initdir) {
     chooser.click();
 }
 
-function saveas_callback(cid, file) {
+function saveas_callback(cid, file, close_flag) {
     var filename = file,
         directory = path.dirname(filename),
         basename = path.basename(filename);
@@ -428,7 +428,8 @@ function saveas_callback(cid, file) {
     if (filename === null) {
         return;
     }
-    pdsend(cid, "savetofile", enquote(basename), enquote(directory));
+    pdsend(cid, "savetofile", enquote(basename), enquote(directory),
+        close_flag);
 }
 
 exports.saveas_callback = saveas_callback;
@@ -498,15 +499,36 @@ function canvas_menuclose_callback(cid_for_dialog, cid, force) {
     // filename/args/dir which is ugly. Also, this should use the
     // html5 dialog-- or some CSS equivalent-- instead of the
     // confusing OK/Cancel javascript prompt.
-    var title = patchwin[cid_for_dialog].window.document.title,
-        reply = patchwin[cid_for_dialog].window
-                .confirm("Save changes to " + title + "?");
-    if (reply) {
-        pdsend(cid_for_dialog + " menusave");
+    var dialog = patchwin[cid_for_dialog].window.document.getElementById("save_before_quit"),
+        // hm... we messed up somewhere. It'd be better to set the document
+        // title so that we don't have to mess with nw.js-specific properties.
+        // Also, it's pretty shoddy to have to split on " * ", and to include
+        // the creation arguments in this dialog. We'd be better off storing
+        // the actual path and filename somewhere, then just fetching it here.
+        title = patchwin[cid_for_dialog].title.split(" * "),
+        dialog_file_slot = patchwin[cid_for_dialog].window.document.getElementById("save_before_quit_filename"),
+        no_button = patchwin[cid_for_dialog].window.document.getElementById("no_button"),
+        filename = title[0],
+        dir = title[1];
+    if (dir.charAt(0) === "(") {
+        dir = dir.slice(dir.indexOf(")")+4); // slice off ") - "
     } else {
-        pdsend(cid_for_dialog + " dirty 0");        
-        pdsend(cid + " menuclose " + force);
+        dir = dir.slice(2); // slice off "- "
     }
+    dialog_file_slot.textContent = filename;
+    dialog_file_slot.title = dir;
+    no_button.onclick = function() {
+        patchwin[cid_for_dialog].window.canvas_events.close_without_saving(cid, force);
+    };
+    dialog.showModal();
+//        reply = patchwin[cid_for_dialog].window
+//                .confirm("Save changes to " + title + "?");
+//    if (reply) {
+//        pdsend(cid_for_dialog + " menusave");
+//    } else {
+//        pdsend(cid_for_dialog + " dirty 0");
+//        pdsend(cid + " menuclose " + force);
+//    }
 }
 
 function gui_canvas_menuclose(cid_for_dialog, cid, force) {
diff --git a/pd/src/g_readwrite.c b/pd/src/g_readwrite.c
index d46b25671..4d56bc97e 100644
--- a/pd/src/g_readwrite.c
+++ b/pd/src/g_readwrite.c
@@ -763,7 +763,8 @@ extern void canvasgop_checksize(t_canvas *x);
 
     /* save a "root" canvas to a file; cf. canvas_saveto() which saves the
     body (and which is called recursively.) */
-static void canvas_savetofile(t_canvas *x, t_symbol *filename, t_symbol *dir)
+static void canvas_savetofile(t_canvas *x, t_symbol *filename, t_symbol *dir,
+    t_floatarg fdestroy)
 {
     t_binbuf *b = binbuf_new();
     canvas_savetemplatesto(x, b, 1);
@@ -783,27 +784,29 @@ static void canvas_savetofile(t_canvas *x, t_symbol *filename, t_symbol *dir)
         if (x->gl_isgraph)
             canvasgop_checksize(x);
         canvas_reload(filename, dir, &x->gl_gobj);
+        if (fdestroy != 0)
+            vmess(&x->gl_pd, gensym("menuclose"), "f", 1.);
     }
     binbuf_free(b);
 }
 
-static void canvas_menusaveas(t_canvas *x)
+static void canvas_menusaveas(t_canvas *x, t_floatarg fdestroy)
 {
     t_canvas *x2 = canvas_getrootfor(x);
-    gui_vmess("gui_canvas_saveas", "xss", x2, x2->gl_name->s_name, canvas_getdir(x2)->s_name);
+    gui_vmess("gui_canvas_saveas", "xssi", x2, x2->gl_name->s_name, canvas_getdir(x2)->s_name, fdestroy != 0);
 //    sys_vgui("pdtk_canvas_saveas .x%lx \"%s\" \"%s\"\n", x2,
 //        x2->gl_name->s_name, canvas_getdir(x2)->s_name);
 }
 
-static void canvas_menusave(t_canvas *x)
+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));
-    else canvas_menusaveas(x2);
+            canvas_savetofile(x2, x2->gl_name, canvas_getdir(x2), fdestroy);
+    else canvas_menusaveas(x2, fdestroy);
 }
 
 void g_readwrite_setup(void)
@@ -815,12 +818,12 @@ void g_readwrite_setup(void)
     class_addmethod(canvas_class, (t_method)glist_mergefile,
         gensym("mergefile"), A_SYMBOL, A_DEFSYM, A_NULL);
     class_addmethod(canvas_class, (t_method)canvas_savetofile,
-        gensym("savetofile"), A_SYMBOL, A_SYMBOL, 0);
+        gensym("savetofile"), A_SYMBOL, A_SYMBOL, A_DEFFLOAT, 0);
     class_addmethod(canvas_class, (t_method)canvas_saveto,
         gensym("saveto"), A_CANT, 0);
 /* ------------------ from the menu ------------------------- */
     class_addmethod(canvas_class, (t_method)canvas_menusave,
-        gensym("menusave"), 0);
+        gensym("menusave"), A_DEFFLOAT, 0);
     class_addmethod(canvas_class, (t_method)canvas_menusaveas,
-        gensym("menusaveas"), 0);
+        gensym("menusaveas"), A_DEFFLOAT, 0);
 }
-- 
GitLab