Commit 8e727b94 authored by Albert Gräf's avatar Albert Gräf
Browse files

Save zoom level in patch file, so that it can be restored when the patch gets reloaded. Fixes #145.

parent f116b8d1
......@@ -292,6 +292,11 @@
<option data-i18n="prefs.gui.presets.strongbad" value="strongbad">
</option>
</select>
<br/>
<label data-i18n="[title]prefs.gui.zoom.save_zoom_tt">
<input type="checkbox" id="save_zoom" name="save_zoom">
<span data-i18n="prefs.gui.zoom.save_zoom"></span>
</label>
</div>
</div>
......@@ -386,6 +391,10 @@ function get_gui_preset() {
return document.getElementById("gui_preset").selectedOptions[0].value;
}
function get_save_zoom() {
return +document.getElementById("save_zoom").checked;
}
// startup config data
// XXXTODO: We should maybe include these in the dialog, but the startup tab
......@@ -592,8 +601,9 @@ function apply(save_prefs) {
midi_use_alsa ? get_attr("midi-outdev-names", attrs).length : 0
);
// Send the name of the gui preset to Pd
pdgui.pdsend("pd gui-preset", get_gui_preset());
// Send the gui prefs (currently just the name of the gui preset and the
// status of the save-zoom toggle) to Pd
pdgui.pdsend("pd gui-prefs", get_gui_preset(), get_save_zoom());
// Send the startup config data to Pd
pdgui.pdsend.apply(null, ["pd path-dialog", startup_use_stdpath, startup_verbose].concat(get_path_array()));
......@@ -832,7 +842,7 @@ function midi_prefs_callback(attrs) {
pdgui.resize_window(pd_object_callback);
}
function gui_prefs_callback(name) {
function gui_prefs_callback(name, save_zoom) {
var s = document.getElementById("gui_preset"),
i;
for (i = 0; i < s.options.length; i++) {
......@@ -841,6 +851,7 @@ function gui_prefs_callback(name) {
break;
}
}
document.getElementById("save_zoom").checked = !!save_zoom;
}
// startup settings
......
......@@ -392,6 +392,10 @@
"strongbad": "Strongbad",
"extended": "Extended",
"c64": "C64"
},
"zoom": {
"save_zoom": "Speichern/Laden der Vergrößerung im Patch",
"save_zoom_tt": "Speichere die aktuelle Vergrößerung mit dem Patch und stelle diese beim Laden des Patches wieder her"
}
},
"audio": {
......
......@@ -392,6 +392,10 @@
"strongbad": "Strongbad",
"extended": "Extended",
"c64": "C64"
},
"zoom": {
"save_zoom": "save/load zoom level with patch",
"save_zoom_tt": "Save the current zoom level with the patch and restore it when reloading the patch"
}
},
"audio": {
......
......@@ -788,6 +788,8 @@ function register_window_id(cid, attr_array) {
// Trigger a "focus" event so that OSX updates the menu for this window
nw_window_focus_callback(cid);
canvas_events.normal();
// Initialize the zoom level to the value retrieved from the patch, if any.
nw.Window.get().zoomLevel = attr_array.zoom;
pdgui.canvas_map(cid); // side-effect: triggers gui_canvas_get_scroll
set_editmode_checkbox(attr_array.editmode !== 0 ? true : false);
// For now, there is no way for the cord inspector to be turned on by
......@@ -1219,6 +1221,7 @@ function nw_create_patch_window_menus(gui, w, name) {
var z = gui.Window.get().zoomLevel;
if (z < 8) { z++; }
gui.Window.get().zoomLevel = z;
pdgui.pdsend(name, "zoom", z);
pdgui.gui_canvas_get_scroll(name);
}
});
......@@ -1228,6 +1231,7 @@ function nw_create_patch_window_menus(gui, w, name) {
var z = gui.Window.get().zoomLevel;
if (z > -7) { z--; }
gui.Window.get().zoomLevel = z;
pdgui.pdsend(name, "zoom", z);
pdgui.gui_canvas_get_scroll(name);
}
});
......@@ -1256,6 +1260,7 @@ function nw_create_patch_window_menus(gui, w, name) {
enabled: true,
click: function () {
gui.Window.get().zoomLevel = 0;
pdgui.pdsend(name, "zoom", 0);
pdgui.gui_canvas_get_scroll(name);
}
});
......
......@@ -1303,7 +1303,7 @@ function create_window(cid, type, width, height, xpos, ypos, attr_array) {
}
// create a new canvas
function gui_canvas_new(cid, width, height, geometry, editmode, name, dir, dirty_flag, cargs) {
function gui_canvas_new(cid, width, height, geometry, zoom, editmode, name, dir, dirty_flag, cargs) {
// hack for buggy tcl popups... should go away for node-webkit
//reset_ctrl_on_popup_window
......@@ -1353,6 +1353,7 @@ function gui_canvas_new(cid, width, height, geometry, editmode, name, dir, dirty
dir: dir,
dirty: dirty_flag,
args: cargs,
zoom: zoom,
editmode: editmode
});
}
......@@ -4567,9 +4568,9 @@ function gui_midi_properties(gfxstub, sys_indevs, sys_outdevs,
}
}
function gui_gui_properties(dummy, name) {
function gui_gui_properties(dummy, name, save_zoom) {
if (dialogwin["prefs"] !== null) {
dialogwin["prefs"].window.gui_prefs_callback(name);
dialogwin["prefs"].window.gui_prefs_callback(name, save_zoom);
}
}
......@@ -4920,6 +4921,7 @@ function do_optimalzoom(cid, hflag, vflag) {
//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;
pdsend(cid, "zoom", z);
}
}
......
......@@ -14,6 +14,7 @@ to be different but are now unified except for some fossilized names.) */
#include "g_canvas.h"
#include "g_all_guis.h"
#include <string.h>
#include <math.h>
extern int do_not_redraw;
extern void canvas_drawconnection(t_canvas *x, int lx1, int ly1, int lx2, int ly2, t_int tag, int issignal);
......@@ -350,6 +351,22 @@ t_symbol *canvas_field_templatesym; /* for "canvas" data type */
t_word *canvas_field_vec; /* for "canvas" data type */
t_gpointer *canvas_field_gp; /* parent for "canvas" data type */
static int calculate_zoom(t_float zoom_hack)
{
// This gives back the zoom level stored in the patch (cf. zoom_hack
// in g_readwrite.c). Make sure to round this value to handle any rounding
// errors due to the limited float precision in Pd's binbuf writer.
int zoom = round(zoom_hack*32);
// This is a 5 bit number in 2's complement, so we need to extend the sign.
zoom = zoom << 27 >> 27;
// To be on the safe side, we finally clamp the result to the range -7..8
// which are the zoom levels supported by Purr Data right now.
if (zoom < -7) zoom = -7;
if (zoom > 8) zoom = 8;
//post("read zoom level: %d", zoom);
return zoom;
}
/* 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.) */
......@@ -370,6 +387,8 @@ t_canvas *canvas_new(void *dummy, t_symbol *sel, int argc, t_atom *argv)
int vis = 0, width = GLIST_DEFCANVASWIDTH, height = GLIST_DEFCANVASHEIGHT;
int xloc = 0, yloc = GLIST_DEFCANVASYLOC;
int font = (owner ? owner->gl_font : sys_defaultfont);
int zoom = 0;
extern int sys_zoom;
glist_init(x);
//x->gl_magic_glass = magicGlass_new(x);
......@@ -380,20 +399,28 @@ t_canvas *canvas_new(void *dummy, t_symbol *sel, int argc, t_atom *argv)
if (argc == 5) /* toplevel: x, y, w, h, font */
{
t_float zoom_hack = atom_getfloatarg(3, argc, argv);
xloc = atom_getintarg(0, argc, argv);
yloc = atom_getintarg(1, argc, argv);
width = atom_getintarg(2, argc, argv);
height = atom_getintarg(3, argc, argv);
font = atom_getintarg(4, argc, argv);
zoom_hack -= height;
if (sys_zoom && zoom_hack > 0)
zoom = calculate_zoom(zoom_hack);
}
else if (argc == 6) /* subwindow: x, y, w, h, name, vis */
{
t_float zoom_hack = atom_getfloatarg(3, argc, argv);
xloc = atom_getintarg(0, argc, argv);
yloc = atom_getintarg(1, argc, argv);
width = atom_getintarg(2, argc, argv);
height = atom_getintarg(3, argc, argv);
s = atom_getsymbolarg(4, argc, argv);
vis = atom_getintarg(5, argc, argv);
zoom_hack -= height;
if (sys_zoom && zoom_hack > 0)
zoom = calculate_zoom(zoom_hack);
}
/* (otherwise assume we're being created from the menu.) */
......@@ -448,6 +475,7 @@ t_canvas *canvas_new(void *dummy, t_symbol *sel, int argc, t_atom *argv)
x->gl_willvis = vis;
x->gl_edit = !strncmp(x->gl_name->s_name, "Untitled", 8);
x->gl_font = sys_nearestfontsize(font);
x->gl_zoom = zoom;
pd_pushsym(&x->gl_pd);
//dpsaha@vt.edu gop resize (refactored by mathieu)
......@@ -548,6 +576,7 @@ t_glist *glist_addglist(t_glist *g, t_symbol *sym,
x->gl_pixheight = py2 - py1;
x->gl_font = (canvas_getcurrent() ?
canvas_getcurrent()->gl_font : sys_defaultfont);
x->gl_zoom = 0;
x->gl_screenx1 = x->gl_screeny1 = 0;
x->gl_screenx2 = 450;
x->gl_screeny2 = 300;
......
......@@ -200,6 +200,7 @@ struct _glist
t_editor *gl_editor; /* editor structure when visible */
t_symbol *gl_name; /* symbol bound here */
int gl_font; /* nominal font size in points, e.g., 10 */
int gl_zoom; /* current zoom level (-7..8) */
struct _glist *gl_next; /* link in list of toplevels */
t_canvasenvironment *gl_env; /* root canvases and abstractions only */
unsigned int gl_havewindow:1; /* true if we own a window */
......
......@@ -2471,11 +2471,12 @@ void canvas_vis(t_canvas *x, t_floatarg f)
//fprintf(stderr,"new\n");
canvas_create_editor(x);
canvas_args_to_string(argsbuf, x);
gui_vmess("gui_canvas_new", "xiisissis",
gui_vmess("gui_canvas_new", "xiisiissis",
x,
(int)(x->gl_screenx2 - x->gl_screenx1),
(int)(x->gl_screeny2 - x->gl_screeny1),
geobuf,
x->gl_zoom,
x->gl_edit,
x->gl_name->s_name,
canvas_getdir(x)->s_name,
......@@ -7394,6 +7395,12 @@ static void canvas_font(t_canvas *x, t_floatarg font, t_floatarg oldfont,
canvas_dofont(x2, font, realresize, realresize);
}
static void canvas_zoom(t_canvas *x, t_floatarg zoom)
{
x->gl_zoom = zoom;
//post("store zoom level: %d", x->gl_zoom);
}
void glist_getnextxy(t_glist *gl, int *xpix, int *ypix)
{
if (canvas_last_glist == gl)
......@@ -7592,6 +7599,8 @@ void g_editor_setup(void)
gensym("menufont"), A_NULL);
class_addmethod(canvas_class, (t_method)canvas_font,
gensym("font"), A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, A_NULL);
class_addmethod(canvas_class, (t_method)canvas_zoom,
gensym("zoom"), A_FLOAT, A_NULL);
class_addmethod(canvas_class, (t_method)canvas_find,
gensym("find"), A_SYMBOL, A_FLOAT, A_NULL);
class_addmethod(canvas_class, (t_method)canvas_find_again,
......
......@@ -646,6 +646,28 @@ static void glist_write(t_glist *x, t_symbol *filename, t_symbol *format)
/* ------ routines to save and restore canvases (patches) recursively. ----*/
static double zoom_hack(int x, int zoom)
{
// AG: This employs an interesting little hack made possible by a loophole
// in Pd's patch parser (binbuf_eval), which will happily read a float value
// where an int is expected, and cast it to an int anyway. Applied to the #N
// header of a canvas, this lets us store the zoom level of a patch in the
// fractional part of a window geometry argument x, so that we can restore
// it when the patch is reloaded. Since vanilla and other Pd flavors won't
// even notice the extra data, the same patch can still be loaded by any
// other Pd version without any problems.
// Encoding of the zoom level: Purr Data's zoom levels go from -7 to 8 so
// that we can encode them as 4 bit numbers in two's complement. However, it
// makes sense to leave 1 extra bit for future extensions, so our encoding
// actually uses nonnegative 5 bit integer multiples of 2^-5 = 0.03125 in a
// way that leaves the integer part of the height parameter intact. Note
// that using 2's complement here has the advantage that a zero zoom level
// maps to a zero fraction so that we don't need an extra bit to detect
// whether there's a zoom level when reading the patch.
return x + (zoom & 31) * 0.03125;
}
/* save to a binbuf, called recursively; cf. canvas_savetofile() which
saves the document, and is only called on root canvases. */
static void canvas_saveto(t_canvas *x, t_binbuf *b)
......@@ -653,6 +675,7 @@ static void canvas_saveto(t_canvas *x, t_binbuf *b)
t_gobj *y;
t_linetraverser t;
t_outconnect *oc;
extern int sys_zoom;
/* subpatch */
if (x->gl_owner && !x->gl_env)
{
......@@ -671,25 +694,53 @@ static void canvas_saveto(t_canvas *x, t_binbuf *b)
patchsym = atom_getsymbolarg(name_index,
binbuf_getnatom(bz), binbuf_getvec(bz));
binbuf_free(bz);
binbuf_addv(b, "ssiiiisi;", gensym("#N"), gensym("canvas"),
(int)(x->gl_screenx1),
(int)(x->gl_screeny1),
(int)(x->gl_screenx2 - x->gl_screenx1),
(int)(x->gl_screeny2 - x->gl_screeny1),
(patchsym != &s_ ? patchsym: gensym("(subpatch)")),
x->gl_mapped);
if (sys_zoom && x->gl_zoom != 0) {
// This uses the hack described above to store the zoom factor in
// the fractional part of the windows height parameter. Note that
// any of the other canvas geometry parameters would do just as
// well, as they are all measured in pixels and thus unlikely to
// become fractional in the future.
binbuf_addv(b, "ssiiifsi;", gensym("#N"), gensym("canvas"),
(int)(x->gl_screenx1),
(int)(x->gl_screeny1),
(int)(x->gl_screenx2 - x->gl_screenx1),
zoom_hack(x->gl_screeny2 - x->gl_screeny1, x->gl_zoom),
(patchsym != &s_ ? patchsym: gensym("(subpatch)")),
x->gl_mapped);
} else {
// This is the standard (vanilla) code. This is also used in the
// case of a zero zoom level, so unzoomed patches will always
// produce a standard vanilla patch file when saved.
binbuf_addv(b, "ssiiiisi;", gensym("#N"), gensym("canvas"),
(int)(x->gl_screenx1),
(int)(x->gl_screeny1),
(int)(x->gl_screenx2 - x->gl_screenx1),
(int)(x->gl_screeny2 - x->gl_screeny1),
(patchsym != &s_ ? patchsym: gensym("(subpatch)")),
x->gl_mapped);
}
/* Not sure what the following commented line was doing... */
//t_text *xt = (t_text *)x;
}
/* root or abstraction */
else
{
binbuf_addv(b, "ssiiiii;", gensym("#N"), gensym("canvas"),
(int)(x->gl_screenx1),
(int)(x->gl_screeny1),
(int)(x->gl_screenx2 - x->gl_screenx1),
(int)(x->gl_screeny2 - x->gl_screeny1),
(int)x->gl_font);
// See above.
if (sys_zoom && x->gl_zoom != 0) {
binbuf_addv(b, "ssiiifi;", gensym("#N"), gensym("canvas"),
(int)(x->gl_screenx1),
(int)(x->gl_screeny1),
(int)(x->gl_screenx2 - x->gl_screenx1),
zoom_hack(x->gl_screeny2 - x->gl_screeny1, x->gl_zoom),
(int)x->gl_font);
} else {
binbuf_addv(b, "ssiiiii;", gensym("#N"), gensym("canvas"),
(int)(x->gl_screenx1),
(int)(x->gl_screeny1),
(int)(x->gl_screenx2 - x->gl_screenx1),
(int)(x->gl_screeny2 - x->gl_screeny1),
(int)x->gl_font);
}
canvas_savedeclarationsto(x, b);
}
for (y = x->gl_list; y; y = y->g_next)
......
......@@ -75,16 +75,18 @@ static void glob_perf(t_pd *dummy, float f)
sys_perf = (f != 0);
}
extern int sys_zoom;
extern t_symbol *sys_gui_preset;
static void glob_gui_preset(t_pd *dummy, t_symbol *s)
static void glob_gui_prefs(t_pd *dummy, t_symbol *s, float f)
{
sys_gui_preset = s;
sys_zoom = !!(int)f;
}
/* just the gui-preset for now */
/* just the gui-preset and the save-zoom toggle for now */
static void glob_gui_properties(t_pd *dummy)
{
gui_vmess("gui_gui_properties", "xs", 0, sys_gui_preset->s_name);
gui_vmess("gui_gui_properties", "xsi", 0, sys_gui_preset->s_name, sys_zoom);
}
// ths one lives inside g_editor so that it can access the clipboard
......@@ -167,8 +169,8 @@ void glob_init(void)
gensym("perf"), A_FLOAT, 0);
class_addmethod(glob_pdobject, (t_method)glob_clipboard_text,
gensym("clipboardtext"), A_FLOAT, 0);
class_addmethod(glob_pdobject, (t_method)glob_gui_preset,
gensym("gui-preset"), A_SYMBOL, 0);
class_addmethod(glob_pdobject, (t_method)glob_gui_prefs,
gensym("gui-prefs"), A_SYMBOL, A_FLOAT, 0);
class_addmethod(glob_pdobject, (t_method)glob_gui_properties,
gensym("gui-properties"), 0);
class_addmethod(glob_pdobject, (t_method)glob_recent_files,
......
......@@ -40,7 +40,7 @@
#define snprintf sprintf_s
#endif
int sys_defeatrt;
int sys_defeatrt, sys_zoom;
t_symbol *sys_flags = &s_;
void sys_doflags( void);
......@@ -618,6 +618,8 @@ void sys_loadpreferences( void)
}
if (sys_getpreference("defeatrt", prefbuf, MAXPDSTRING))
sscanf(prefbuf, "%d", &sys_defeatrt);
if (sys_getpreference("savezoom", prefbuf, MAXPDSTRING))
sscanf(prefbuf, "%d", &sys_zoom);
if (sys_getpreference("guipreset", prefbuf, MAXPDSTRING))
{
char preset_buf[MAXPDSTRING];
......@@ -755,6 +757,8 @@ void glob_savepreferences(t_pd *dummy)
sys_putpreference("nloadlib", buf1);
sprintf(buf1, "%d", sys_defeatrt);
sys_putpreference("defeatrt", buf1);
sprintf(buf1, "%d", sys_zoom);
sys_putpreference("savezoom", buf1);
sys_putpreference("guipreset", sys_gui_preset->s_name);
sys_putpreference("flags",
(sys_flags ? sys_flags->s_name : ""));
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment