diff --git a/pd/nw/index.js b/pd/nw/index.js
index 95836df6384ce2ef5d301f8764a20964e39d32ef..9c84762a7c92a3f8e33ebee8f9ffcc4c26dd9e31 100644
--- a/pd/nw/index.js
+++ b/pd/nw/index.js
@@ -456,6 +456,7 @@ function nw_create_pd_window_menus(gui, w) {
     });
     if (osx) {
         minit(m.edit.paste, { enabled: false });
+        minit(m.edit.paste_clipboard, { enabled: false });
         minit(m.edit.duplicate, { enabled: false });
     }
     minit(m.edit.selectall, {
diff --git a/pd/nw/locales/de/translation.json b/pd/nw/locales/de/translation.json
index 6a199fbdc9a3be829f02a101dc0e0d7356032e5b..f275b2538bafc1a5086283792b40279d3c5ad527 100644
--- a/pd/nw/locales/de/translation.json
+++ b/pd/nw/locales/de/translation.json
@@ -122,6 +122,8 @@
     "copy_tt": "Kopiere die selektierten Objekte in die Ablage",
     "paste": "Einfügen",
     "paste_tt": "Füge Objekte aus der Ablage in den Patch ein",
+    "paste_clipboard": "Einfügen aus Zwischenablage",
+    "paste_clipboard_tt": "Füge Pd code aus der Zwischenablage in den Patch ein (mit Vorsicht verwenden!)",
     "duplicate": "Duplizieren",
     "duplicate_tt": "Füge eine Kopie der aktuell selektierten Objekte in den Patch ein (verwendet nicht die Zwischenablage)",
     "selectall": "Alles auswählen",
diff --git a/pd/nw/locales/en/translation.json b/pd/nw/locales/en/translation.json
index 2b98e8d02807275d68896ffc345d57f21effe19f..e5cfb44ec8a4c4794de0fd58dacc3c5e06651ac4 100644
--- a/pd/nw/locales/en/translation.json
+++ b/pd/nw/locales/en/translation.json
@@ -122,6 +122,8 @@
     "copy_tt": "Copy selected objects to the clipboard",
     "paste": "Paste",
     "paste_tt": "Add any objects to the canvas which were previously cut or copied",
+    "paste_clipboard": "Paste from Clipboard",
+    "paste_clipboard_tt": "Paste Pd code from the clipboard to the canvas (use with caution!)",
     "duplicate": "Duplicate",
     "duplicate_tt": "Paste a copy of the current selection on the canvas (doesn't use clipboard)",
     "selectall": "Select All",
diff --git a/pd/nw/pd_canvas.js b/pd/nw/pd_canvas.js
index b600fb045db845f3adb8e9b3fdaab6ca2b22c2c2..894a094bd6da336ca8e01074bfea18a4a3621c52 100644
--- a/pd/nw/pd_canvas.js
+++ b/pd/nw/pd_canvas.js
@@ -363,12 +363,6 @@ var canvas_events = (function() {
                         break;
                     case 86:
                         if (cmd_or_ctrl_key(evt)) { // ctrl-v
-                            // Instead of sending the "paste" message to Pd
-                            // here, we wait for the "paste" DOM listener to
-                            // pick it up. That way it can check to see if
-                            // there's anything in the paste buffer, and if so
-                            // forward it to Pd.
-
                             // We also use "cut" and "copy" DOM event handlers
                             // and leave this code in case we need to change
                             // tactics for some reason.
@@ -684,81 +678,14 @@ var canvas_events = (function() {
         }
     });
 
-    // Listen to paste event using the half-baked Clipboard API from HTML5
+    // Listen to paste event
+    // XXXTODO: Not sure whether this is even needed any more, as the
+    // paste-from-clipboard functionality has been moved to its own menu
+    // option. So this code may possibly be removed in the future. -ag
     document.addEventListener("paste", function(evt) {
-        var clipboard_data = evt.clipboardData.getData("text"),
-            line,
-            lines,
-            i,
-            pd_message;
-        // Precarious, overly complicated and prone to bugs...
-        // Basically, if a Pd user copies some Pd source file from another
-        // application, we give them a single paste operation to paste the
-        // code directly into a window (empty or otherwise). We supply a
-        // warning prompt to let the user know this is what's happening, so
-        // they could cancel if that's not what they wanted.
-
-        // After the prompt, the user can no longer paste that particular
-        // string from the OS clipboard buffer. All paste actions
-        // will instead apply to whatever has been copied or cut from within
-        // a Pd patch. To paste from the OS clipboard again, the user
-        // must cut/copy a _different_ snippet of Pd source file than the
-        // one they previously tried to paste.
-
-        // A temporary workaround to this confusing behavior would be to give
-        // external code-pasting its own menu button. Another possibility is
-        // to let copy/cut actions within the patch actually get written to
-        // the OS clipboard. The latter would involve a lot more work (e.g.,
-        // sending FUDI messages from Pd to set the OS clipboard, etc.)
-
-        // Also, we check below to make sure the OS clipboard is holding
-        // text that could conceivably be Pd source code. If not then the
-        // user won't get bothered with a prompt at all, and normal Pd
-        // paste behavior will follow.
-
-        // From a usability standpoint the main drawback is that
-        // you can't try to paste the same Pd source code more than once.
-        // For users who want to pasting lots of source code this could be
-        // a frustration, but Pd's normal copy/paste behavior remains
-        // intuitive and in line with the way other apps tend to work.
-
-        // Yet another caveat: we only want to check the external buffer
-        // and/or send a "paste" event to Pd if the canvas is in a "normal"
-        // state.
         if (canvas_events.get_state() !== "normal") {
             return;
         }
-
-        if (might_be_a_pd_file(clipboard_data) &&
-            clipboard_data !== pdgui.get_last_clipboard_data()) {
-            if (permission_to_paste_from_external_clipboard()) {
-                // clear the buffer
-                pdgui.pdsend(name, "copyfromexternalbuffer");
-                pd_message = "";
-                lines = clipboard_data.split("\n");
-                for (i = 0; i < lines.length; i++) {
-                    line = lines[i];
-                    // process pd_message if it ends with a semicolon that
-                    // isn't preceded by a backslash
-                    if (line.slice(-1) === ";" &&
-                         (line.length < 2 || line.slice(-2, -1) !== "\\")) {
-                        if (pd_message === "") {
-                            pd_message = line;
-                        } else {
-                            pd_message = pd_message + " " + line;
-                        }
-                        pdgui.pdsend(name, "copyfromexternalbuffer", pd_message);
-                        pd_message = "";
-                    } else {
-                        pd_message = pd_message + " " + line;
-                        pd_message = pd_message.replace(/\n/g, "");
-                    }
-                }
-                // This isn't needed, but pd-l2ork did it for some reason...
-                pdgui.pdsend(name, "copyfromexternalbuffer");
-            }
-            pdgui.set_last_clipboard_data(clipboard_data);
-        }
         // Send a canvas "paste" message to Pd
         pdgui.pdsend(name, "paste");
     });
@@ -1096,6 +1023,65 @@ function instantiate_live_box() {
     }
 }
 
+function canvas_paste_from_clipboard(name, clipboard_data)
+{
+    var line, lines, i, pd_message;
+
+    // This lets the user copy some Pd source file from another application
+    // and paste the code directly into a canvas window (empty or otherwise).
+    // It does a quick check to make sure the OS clipboard is holding text
+    // that could conceivably be Pd source code. But that's not 100% foolproof
+    // and if it's not then the engine might crash and burn, so be careful! :)
+
+    // We only want to check the external buffer and/or send a "paste" event
+    // to Pd if the canvas is in a "normal" state.
+    if (canvas_events.get_state() !== "normal") {
+        return;
+    }
+
+    // Maybe we want a warning prompt here? Then uncomment the line below. I
+    // disabled this for now, as the paste-from-clipboard command now has its
+    // own menu option, so presumably the user knows what he's doing. -ag
+    if (!might_be_a_pd_file(clipboard_data)
+	//|| !permission_to_paste_from_external_clipboard()
+       ) {
+        return;
+    }
+
+    // clear the buffer
+    pdgui.pdsend(name, "copyfromexternalbuffer");
+    pd_message = "";
+    lines = clipboard_data.split("\n");
+    for (i = 0; i < lines.length; i++) {
+        line = lines[i];
+        // process pd_message if it ends with a semicolon that
+        // isn't preceded by a backslash
+        if (line.slice(-1) === ";" &&
+            (line.length < 2 || line.slice(-2, -1) !== "\\")) {
+            if (pd_message === "") {
+                pd_message = line;
+            } else {
+                pd_message = pd_message + " " + line;
+            }
+            pdgui.pdsend(name, "copyfromexternalbuffer", pd_message);
+            pd_message = "";
+        } else {
+            pd_message = pd_message + " " + line;
+            pd_message = pd_message.replace(/\n/g, "");
+        }
+    }
+    // This signals to the engine that we're done filling the external buffer,
+    // so this might conceivably call some internal cleanup code. At present
+    // it doesn't do anything, though.
+    pdgui.pdsend(name, "copyfromexternalbuffer");
+    // Send a canvas "paste" message to Pd
+    pdgui.pdsend(name, "paste");
+    // Finally, make sure to reset the buffer so that next time around we
+    // start from a clean slate. (Otherwise, the engine will just add stuff to
+    // the existing buffer contents.)
+    pdgui.pdsend(name, "reset_copyfromexternalbuffer");
+}
+
 // Menus for the Patch window
 
 var canvas_menu = {};
@@ -1242,6 +1228,15 @@ function nw_create_patch_window_menus(gui, w, name) {
         enabled: true,
         click: function () { pdgui.pdsend(name, "paste"); }
     });
+    minit(m.edit.paste_clipboard, {
+        enabled: true,
+        click: function () {
+	    var clipboard = nw.Clipboard.get();
+	    var text = clipboard.get('text');
+	    //pdgui.post("** paste from clipboard: "+text);
+	    canvas_paste_from_clipboard(name, text);
+	}
+    });
     minit(m.edit.duplicate, {
         enabled: true,
         click: function () { pdgui.pdsend(name, "duplicate"); }
diff --git a/pd/nw/pd_menus.js b/pd/nw/pd_menus.js
index dead27ceb16d8e995715070fcccd341cbb0a8bef..d1fcf91188e485e951c8d035ac07999a946101c2 100644
--- a/pd/nw/pd_menus.js
+++ b/pd/nw/pd_menus.js
@@ -184,12 +184,6 @@ function create_menu(gui, type) {
             tooltip: l("menu.copy_tt")
         }));
         if (canvas_menu) {
-            // The nwjs menubar keybindings don't propagate down
-            // to the DOM. Here, we need the DOM to receive the
-            // "paste" event in case we want to paste a Pd file
-            // from an external buffer. So unfortunately we can't
-            // do the keybindings here. The side-effect is that
-            // "Ctrl-V" isn't displayed in the menu item.
             edit_menu.append(m.edit.paste = new gui.MenuItem({
                 label: l("menu.paste"),
                 key: "v",
@@ -203,6 +197,10 @@ function create_menu(gui, type) {
     // part of the builtin Edit menu...
 
     if (canvas_menu) {
+        edit_menu.append(m.edit.paste_clipboard = new gui.MenuItem({
+            label: l("menu.paste_clipboard"),
+            tooltip: l("menu.paste_clipboard_tt")
+        }));
         edit_menu.append(m.edit.duplicate = new gui.MenuItem({
             label: l("menu.duplicate"),
             key: "d",
diff --git a/pd/nw/pdgui.js b/pd/nw/pdgui.js
index ef3c25aa76efe3c2e4235fb1de8a116b19a05d62..09244de1fc77db09573b31da760769c28d572ce3 100644
--- a/pd/nw/pdgui.js
+++ b/pd/nw/pdgui.js
@@ -3,7 +3,6 @@
 var pwd;
 var gui_dir;
 var lib_dir;
-var last_clipboard_data;
 var pd_engine_id;
 
 exports.set_pwd = function(pwd_string) {
@@ -67,14 +66,6 @@ function gui_set_gui_preset(name) {
     skin.set(name);
 }
 
-exports.set_last_clipboard_data = function(data) {
-    last_clipboard_data = data;
-}
-
-exports.get_last_clipboard_data = function() {
-    return last_clipboard_data;
-}
-
 // Modules
 
 var fs = require("fs");     // for fs.existsSync