From 97e29688842a959105a13c06c3c3f655179dde55 Mon Sep 17 00:00:00 2001
From: Albert Graef <aggraef@gmail.com>
Date: Tue, 15 Nov 2016 14:29:24 +0100
Subject: [PATCH] Add an "Optimal Zoom" option to the canvas view menu. This
 automatically picks the right zoom level to make the patch fill the window as
 much as possible. Also fixes a missing call to pdgui.gui_canvas_get_scroll in
 the m.view.zoomreset menu command which caused the viewport not to be updated
 properly after resetting the zoom level in canvas windows.

---
 pd/nw/locales/de/translation.json |  2 ++
 pd/nw/locales/en/translation.json |  2 ++
 pd/nw/pd_canvas.js                |  8 +++++
 pd/nw/pd_menus.js                 |  8 +++++
 pd/nw/pdgui.js                    | 56 +++++++++++++++++++++++++++++++
 5 files changed, 76 insertions(+)

diff --git a/pd/nw/locales/de/translation.json b/pd/nw/locales/de/translation.json
index f275b2538..5b3c1c5ac 100644
--- a/pd/nw/locales/de/translation.json
+++ b/pd/nw/locales/de/translation.json
@@ -165,6 +165,8 @@
     "zoomout_tt": "Verkleinere die Anzeige des Patches",
     "zoomreset": "Originalgröße",
     "zoomreset_tt": "Setze die Anzeige des Patches auf die Originalgröße zurück",
+    "zoomoptimal": "Optimale Größe",
+    "zoomoptimal_tt": "Wahlt die optimale Größe, mit der der gesamte Patch angezeigt werden kann (soweit möglich)",
     "fullscreen": "Vollbild",
 
     "put": "Hinzufügen",
diff --git a/pd/nw/locales/en/translation.json b/pd/nw/locales/en/translation.json
index e5cfb44ec..7f4cf14fa 100644
--- a/pd/nw/locales/en/translation.json
+++ b/pd/nw/locales/en/translation.json
@@ -165,6 +165,8 @@
     "zoomout_tt": "Make the patch visually smaller",
     "zoomreset": "Reset Zoom",
     "zoomreset_tt": "Reset the zoom to the original level",
+    "zoomoptimal": "Optimal Zoom",
+    "zoomoptimal_tt": "Change the zoom to the optimal level which makes the entire patch visible (as far as possible)",
     "fullscreen": "Fullscreen",
 
     "put": "Put",
diff --git a/pd/nw/pd_canvas.js b/pd/nw/pd_canvas.js
index 2f0b2b170..2f697bebe 100644
--- a/pd/nw/pd_canvas.js
+++ b/pd/nw/pd_canvas.js
@@ -1352,10 +1352,18 @@ function nw_create_patch_window_menus(gui, w, name) {
             pdgui.gui_canvas_get_scroll(name);
         }
     });
+    minit(m.view.optimalzoom, {
+        enabled: true,
+        click: function () {
+            pdgui.gui_canvas_optimal_zoom(name);
+            pdgui.gui_canvas_get_scroll(name);
+        }
+    });
     minit(m.view.zoomreset, {
         enabled: true,
         click: function () {
             gui.Window.get().zoomLevel = 0;
+            pdgui.gui_canvas_get_scroll(name);
         }
     });
     minit(m.view.fullscreen, {
diff --git a/pd/nw/pd_menus.js b/pd/nw/pd_menus.js
index 2121a94d5..d32c46ef7 100644
--- a/pd/nw/pd_menus.js
+++ b/pd/nw/pd_menus.js
@@ -325,6 +325,14 @@ function create_menu(gui, type) {
         modifiers: cmd_or_ctrl,
         tooltip: l("menu.zoomreset_tt")
     }));
+    if (canvas_menu) {
+	view_menu.append(m.view.optimalzoom = new gui.MenuItem({
+            label: l("menu.zoomoptimal"),
+            key: "0",
+            modifiers: cmd_or_ctrl + "+alt",
+            tooltip: l("menu.zoomoptimal_tt")
+	}));
+    }
     view_menu.append(new gui.MenuItem({ type: "separator" }));
     view_menu.append(m.view.fullscreen = new gui.MenuItem({
         label: l("menu.fullscreen"),
diff --git a/pd/nw/pdgui.js b/pd/nw/pdgui.js
index 09244de1f..c4c7df9c2 100644
--- a/pd/nw/pdgui.js
+++ b/pd/nw/pdgui.js
@@ -4458,6 +4458,62 @@ function gui_canvas_get_scroll(cid) {
 
 exports.gui_canvas_get_scroll = gui_canvas_get_scroll;
 
+function do_optimalzoom(cid) {
+    // determine an optimal zoom level; this uses pretty much the same
+    // logic as do_getscroll above to determine bbox and actual window
+    // size, and then determines the maximal zoom level that makes the
+    // entire patch fit within the window
+    var bbox, width, height, min_width, min_height, x, y,
+        svg;
+    if (!patchwin[cid]) { return; }
+    svg = get_item(cid, "patchsvg");
+    bbox = svg.getBBox();
+    width = bbox.x > 0 ? bbox.x + bbox.width : bbox.width;
+    height = bbox.y > 0 ? bbox.y + bbox.height : bbox.height;
+    x = bbox.x > 0 ? 0 : bbox.x,
+    y = bbox.y > 0 ? 0 : bbox.y;
+    min_width = patchwin[cid].window.innerWidth - 4;
+    min_height = patchwin[cid].window.innerHeight - 4;
+    width |= 0; // drop everything to the right of the decimal point
+    height |= 0;
+    min_width |= 0;
+    min_height |= 0;
+    x |= 0;
+    y |= 0;
+    // Calculate the optimal horizontal and vertical zoom values,
+    // using floor to always round down to the nearest integer. Note
+    // that these may well be negative, if the viewport is too small
+    // for the patch at the current zoom level. XXXREVIEW: We assume a
+    // zoom factor of 1.2 here; this works for me on Linux, but I'm
+    // not sure how portable it is. -ag
+    var zx = 0, zy = 0;
+    if (width>0) zx = Math.floor(Math.log(min_width/width)/Math.log(1.2));
+    if (height>0) zy = Math.floor(Math.log(min_height/height)/Math.log(1.2));
+    // Optimal zoom is the minimum of the horizontal and vertical zoom
+    // values. This gives us the offset to the current zoom level. We
+    // then need to clamp the resulting new zoom level to the valid
+    // zoom level range of -8..+7.
+    var actz = patchwin[cid].zoomLevel, z = actz+Math.min(zx, zy);
+    if (z < -8) z = -8; if (z > 7) z = 7;
+    //post("bbox: "+width+"x"+height+"+"+x+"+"+y+" window size: "+min_width+"x"+min_height+" current zoom level: "+actz+" optimal zoom level: "+z);
+    if (z != actz) {
+	patchwin[cid].zoomLevel = z;
+    }
+}
+
+var optimalzoom_var = {};
+
+// We use a setTimeout here as with do_getscroll above, but we have to
+// use a smaller value here, so that we're done before a subsequent
+// call to do_getscroll updates the viewport. XXXREVIEW: Hopefully
+// 100 msec are enough for do_optimalzoom to finish.
+function gui_canvas_optimal_zoom(cid) {
+    clearTimeout(optimalzoom_var[cid]);
+    optimalzoom_var[cid] = setTimeout(do_optimalzoom, 150, cid);
+}
+
+exports.gui_canvas_optimal_zoom = gui_canvas_optimal_zoom;
+
 // handling the selection
 function gui_lower(cid, tag) {
     var svg = patchwin[cid].window.document.getElementById("patchsvg"),
-- 
GitLab