Commit f4c515a7 authored by Ivica Ico Bukvic's avatar Ivica Ico Bukvic Committed by Hans-Christoph Steiner
Browse files

Fixed random segfaults due to a faulty implementation of the canvas apply undo

parent ea811da0
/* Copyright (c) 1997-1999 Miller Puckette.
* For information on usage and redistribution, and for a DISCLAIMER OF ALL
* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */
/* send~, delread~, throw~, catch~ */
#include "m_pd.h"
extern int ugen_getsortno(void);
#define DEFDELVS 64 /* LATER get this from canvas at DSP time */
static int delread_zero = 0; /* four bytes of zero for delread~, vd~ */
/* ----------------------------- delwrite~ ----------------------------- */
static t_class *sigdelwrite_class;
typedef struct delwritectl
{
int c_n;
t_sample *c_vec;
int c_phase;
} t_delwritectl;
typedef struct _sigdelwrite
{
t_object x_obj;
t_symbol *x_sym;
t_delwritectl x_cspace;
int x_sortno; /* DSP sort number at which this was last put on chain */
int x_rsortno; /* DSP sort # for first delread or write in chain */
int x_vecsize; /* vector size for delread~ to use */
t_float x_f;
} t_sigdelwrite;
#define XTRASAMPS 4
#define SAMPBLK 4
/* routine to check that all delwrites/delreads/vds have same vecsize */
static void sigdelwrite_checkvecsize(t_sigdelwrite *x, int vecsize)
{
if (x->x_rsortno != ugen_getsortno())
{
x->x_vecsize = vecsize;
x->x_rsortno = ugen_getsortno();
}
/*
LATER this should really check sample rate and blocking, once that is
supported. Probably we don't actually care about vecsize.
For now just suppress this check. */
#if 0
else if (vecsize != x->x_vecsize)
pd_error(x, "delread/delwrite/vd vector size mismatch");
#endif
}
#include <stdio.h>
static void *sigdelwrite_new(t_symbol *s, t_floatarg msec)
{
int nsamps;
t_sigdelwrite *x = (t_sigdelwrite *)pd_new(sigdelwrite_class);
if (!*s->s_name) s = gensym("delwrite~");
pd_bind(&x->x_obj.ob_pd, s);
x->x_sym = s;
if (msec == 0) msec = 1000;
nsamps = msec * sys_getsr() * (t_float)(0.001f);
if (nsamps < 1) nsamps = 1;
nsamps += ((- nsamps) & (SAMPBLK - 1));
nsamps += DEFDELVS;
x->x_cspace.c_n = nsamps;
x->x_cspace.c_vec =
(t_sample *)getbytes((nsamps + XTRASAMPS) * sizeof(t_sample));
x->x_cspace.c_phase = XTRASAMPS;
x->x_sortno = 0;
x->x_vecsize = 0;
x->x_f = 0;
return (x);
}
static t_int *sigdelwrite_perform(t_int *w)
{
t_sample *in = (t_sample *)(w[1]);
t_delwritectl *c = (t_delwritectl *)(w[2]);
int n = (int)(w[3]);
int phase = c->c_phase, nsamps = c->c_n;
t_sample *vp = c->c_vec, *bp = vp + phase, *ep = vp + (c->c_n + XTRASAMPS);
phase += n;
while (n--)
{
t_sample f = *in++;
if (PD_BIGORSMALL(f))
f = 0;
*bp++ = f;
if (bp == ep)
{
vp[0] = ep[-4];
vp[1] = ep[-3];
vp[2] = ep[-2];
vp[3] = ep[-1];
bp = vp + XTRASAMPS;
phase -= nsamps;
}
}
bp = vp + c->c_phase;
c->c_phase = phase;
return (w+4);
}
static void sigdelwrite_dsp(t_sigdelwrite *x, t_signal **sp)
{
dsp_add(sigdelwrite_perform, 3, sp[0]->s_vec, &x->x_cspace, sp[0]->s_n);
x->x_sortno = ugen_getsortno();
sigdelwrite_checkvecsize(x, sp[0]->s_n);
}
static void sigdelwrite_free(t_sigdelwrite *x)
{
pd_unbind(&x->x_obj.ob_pd, x->x_sym);
freebytes(x->x_cspace.c_vec,
(x->x_cspace.c_n + XTRASAMPS) * sizeof(t_sample));
}
static void sigdelwrite_setup(void)
{
sigdelwrite_class = class_new(gensym("delwrite~"),
(t_newmethod)sigdelwrite_new, (t_method)sigdelwrite_free,
sizeof(t_sigdelwrite), 0, A_DEFSYM, A_DEFFLOAT, 0);
CLASS_MAINSIGNALIN(sigdelwrite_class, t_sigdelwrite, x_f);
class_addmethod(sigdelwrite_class, (t_method)sigdelwrite_dsp,
gensym("dsp"), 0);
}
/* ----------------------------- delread~ ----------------------------- */
static t_class *sigdelread_class;
typedef struct _sigdelread
{
t_object x_obj;
t_symbol *x_sym;
t_float x_deltime; /* delay in msec */
int x_delsamps; /* delay in samples */
t_float x_sr; /* samples per msec */
t_float x_n; /* vector size */
int x_zerodel; /* 0 or vecsize depending on read/write order */
} t_sigdelread;
static void sigdelread_float(t_sigdelread *x, t_float f);
static void *sigdelread_new(t_symbol *s, t_floatarg f)
{
t_sigdelread *x = (t_sigdelread *)pd_new(sigdelread_class);
x->x_sym = s;
x->x_sr = 1;
x->x_n = 1;
x->x_zerodel = 0;
sigdelread_float(x, f);
outlet_new(&x->x_obj, &s_signal);
return (x);
}
static void sigdelread_float(t_sigdelread *x, t_float f)
{
int samps;
t_sigdelwrite *delwriter =
(t_sigdelwrite *)pd_findbyclass(x->x_sym, sigdelwrite_class);
x->x_deltime = f;
if (delwriter)
{
int delsize = delwriter->x_cspace.c_n;
x->x_delsamps = (int)(0.5 + x->x_sr * x->x_deltime)
+ x->x_n - x->x_zerodel;
if (x->x_delsamps < x->x_n) x->x_delsamps = x->x_n;
else if (x->x_delsamps > delwriter->x_cspace.c_n - DEFDELVS)
x->x_delsamps = delwriter->x_cspace.c_n - DEFDELVS;
}
}
static t_int *sigdelread_perform(t_int *w)
{
t_sample *out = (t_sample *)(w[1]);
t_delwritectl *c = (t_delwritectl *)(w[2]);
int delsamps = *(int *)(w[3]);
int n = (int)(w[4]);
int phase = c->c_phase - delsamps, nsamps = c->c_n;
t_sample *vp = c->c_vec, *bp, *ep = vp + (c->c_n + XTRASAMPS);
if (phase < 0) phase += nsamps;
bp = vp + phase;
while (n--)
{
*out++ = *bp++;
if (bp == ep) bp -= nsamps;
}
return (w+5);
}
static void sigdelread_dsp(t_sigdelread *x, t_signal **sp)
{
t_sigdelwrite *delwriter =
(t_sigdelwrite *)pd_findbyclass(x->x_sym, sigdelwrite_class);
x->x_sr = sp[0]->s_sr * 0.001;
x->x_n = sp[0]->s_n;
if (delwriter)
{
sigdelwrite_checkvecsize(delwriter, sp[0]->s_n);
x->x_zerodel = (delwriter->x_sortno == ugen_getsortno() ?
0 : delwriter->x_vecsize);
sigdelread_float(x, x->x_deltime);
dsp_add(sigdelread_perform, 4,
sp[0]->s_vec, &delwriter->x_cspace, &x->x_delsamps, sp[0]->s_n);
}
else if (*x->x_sym->s_name)
error("delread~: %s: no such delwrite~",x->x_sym->s_name);
}
static void sigdelread_setup(void)
{
sigdelread_class = class_new(gensym("delread~"),
(t_newmethod)sigdelread_new, 0,
sizeof(t_sigdelread), 0, A_DEFSYM, A_DEFFLOAT, 0);
class_addmethod(sigdelread_class, (t_method)sigdelread_dsp,
gensym("dsp"), 0);
class_addfloat(sigdelread_class, (t_method)sigdelread_float);
}
/* ----------------------------- vd~ ----------------------------- */
static t_class *sigvd_class;
typedef struct _sigvd
{
t_object x_obj;
t_symbol *x_sym;
t_float x_sr; /* samples per msec */
int x_zerodel; /* 0 or vecsize depending on read/write order */
t_float x_f;
} t_sigvd;
static void *sigvd_new(t_symbol *s)
{
t_sigvd *x = (t_sigvd *)pd_new(sigvd_class);
if (!*s->s_name) s = gensym("vd~");
x->x_sym = s;
x->x_sr = 1;
x->x_zerodel = 0;
outlet_new(&x->x_obj, &s_signal);
x->x_f = 0;
return (x);
}
static t_int *sigvd_perform(t_int *w)
{
t_sample *in = (t_sample *)(w[1]);
t_sample *out = (t_sample *)(w[2]);
t_delwritectl *ctl = (t_delwritectl *)(w[3]);
t_sigvd *x = (t_sigvd *)(w[4]);
int n = (int)(w[5]);
int nsamps = ctl->c_n;
t_sample limit = nsamps - n - 1;
t_sample fn = n-1;
t_sample *vp = ctl->c_vec, *bp, *wp = vp + ctl->c_phase;
t_sample zerodel = x->x_zerodel;
while (n--)
{
t_sample delsamps = x->x_sr * *in++ - zerodel, frac;
int idelsamps;
t_sample a, b, c, d, cminusb;
if (delsamps < 1.00001f) delsamps = 1.00001f;
if (delsamps > limit) delsamps = limit;
delsamps += fn;
fn = fn - 1.0f;
idelsamps = delsamps;
frac = delsamps - (t_sample)idelsamps;
bp = wp - idelsamps;
if (bp < vp + 4) bp += nsamps;
d = bp[-3];
c = bp[-2];
b = bp[-1];
a = bp[0];
cminusb = c-b;
*out++ = b + frac * (
cminusb - 0.1666667f * (1.-frac) * (
(d - a - 3.0f * cminusb) * frac + (d + 2.0f*a - 3.0f*b)
)
);
}
return (w+6);
}
static void sigvd_dsp(t_sigvd *x, t_signal **sp)
{
t_sigdelwrite *delwriter =
(t_sigdelwrite *)pd_findbyclass(x->x_sym, sigdelwrite_class);
x->x_sr = sp[0]->s_sr * 0.001;
if (delwriter)
{
sigdelwrite_checkvecsize(delwriter, sp[0]->s_n);
x->x_zerodel = (delwriter->x_sortno == ugen_getsortno() ?
0 : delwriter->x_vecsize);
dsp_add(sigvd_perform, 5,
sp[0]->s_vec, sp[1]->s_vec,
&delwriter->x_cspace, x, sp[0]->s_n);
}
else error("vd~: %s: no such delwrite~",x->x_sym->s_name);
}
static void sigvd_setup(void)
{
sigvd_class = class_new(gensym("vd~"), (t_newmethod)sigvd_new, 0,
sizeof(t_sigvd), 0, A_DEFSYM, 0);
class_addmethod(sigvd_class, (t_method)sigvd_dsp, gensym("dsp"), 0);
CLASS_MAINSIGNALIN(sigvd_class, t_sigvd, x_f);
}
/* ----------------------- global setup routine ---------------- */
void d_delay_setup(void)
{
sigdelwrite_setup();
sigdelread_setup();
sigvd_setup();
}
......@@ -709,16 +709,18 @@ void canvas_map(t_canvas *x, t_floatarg f)
t_gobj *y;
if (flag)
{
if (!glist_isvisible(x))
{
//fprintf(stderr,"canvas_map 1\n");
//if (!glist_isvisible(x))
//{
//fprintf(stderr,"canvas_map 1 isvisible\n");
t_selection *sel;
if (!x->gl_havewindow)
{
bug("canvas_map");
canvas_vis(x, 1);
}
else if (x->gl_mapped == 0)
canvas_vis(x, 1);
//else if (x->gl_mapped == 0)
// canvas_vis(x, 1);
/* if parent has editor enabled and we're a sub-patch,
(but not an abstraction) match its edit mode to that
......@@ -741,23 +743,30 @@ void canvas_map(t_canvas *x, t_floatarg f)
//canvas_setcursor(x, CURSOR_EDITMODE_NOTHING);
//}
for (y = x->gl_list; y; y = y->g_next)
if (!x->gl_list) {
//if there are no objects on the canvas
//fprintf(stderr,"window is empty\n");
canvas_create_editor(x);
}
else for (y = x->gl_list; y; y = y->g_next) {
gobj_vis(y, x, 1);
if (x->gl_editor && x->gl_editor->e_selection)
for (sel = x->gl_editor->e_selection; sel; sel = sel->sel_next)
gobj_select(sel->sel_what, x, 1);
if (x->gl_editor && x->gl_editor->e_selection)
for (sel = x->gl_editor->e_selection; sel; sel = sel->sel_next)
gobj_select(sel->sel_what, x, 1);
}
x->gl_mapped = 1;
canvas_drawlines(x);
if (x->gl_isgraph && x->gl_goprect)
canvas_drawredrect(x, 1);
sys_vgui("pdtk_canvas_getscroll .x%lx.c\n", x);
}
//}
}
else
{
//fprintf(stderr,"canvas_map 0\n");
if (glist_isvisible(x))
{
/* just clear out the whole canvas */
/* just clear out the whole canvas */
sys_vgui(".x%lx.c dtag all selected\n", x);
sys_vgui(".x%lx.c delete all\n", x);
x->gl_mapped = 0;
......
This diff is collapsed.
......@@ -349,8 +349,10 @@ void glist_noselect(t_glist *x)
{
if (x->gl_editor)
{
while (x->gl_editor->e_selection)
glist_deselect(x, x->gl_editor->e_selection->sel_what);
if (x->gl_editor->e_selection) {
while (x->gl_editor->e_selection)
glist_deselect(x, x->gl_editor->e_selection->sel_what);
}
if (x->gl_editor->e_selectedline)
glist_deselectline(x);
if (c_selection == x)
......@@ -431,13 +433,14 @@ static const char *canvas_undo_name;
void canvas_setundo(t_canvas *x, t_undofn undofn, void *buf,
const char *name)
{
//fprintf(stderr,"canvas_setundo %lx\n", (t_int)x);
//fprintf(stderr,"canvas_setundo %s\n", name);
int hadone = 0;
/* blow away the old undo information. In one special case the
old undo info is re-used; if so we shouldn't free it here. */
if (canvas_undo_fn && canvas_undo_buf && (buf != canvas_undo_buf))
{
//fprintf(stderr,"hadone canvas_setundo\n");
(*canvas_undo_fn)(canvas_undo_canvas, canvas_undo_buf, UNDO_FREE);
hadone = 1;
}
......@@ -608,22 +611,24 @@ static void *canvas_undo_set_cut(t_canvas *x, int mode)
/* store connections into/out of the selection */
buf->u_reconnectbuf = binbuf_new();
linetraverser_start(&t, x);
while (oc = linetraverser_next(&t))
{
int issel1 = glist_isselected(x, &t.tr_ob->ob_g);
int issel2 = glist_isselected(x, &t.tr_ob2->ob_g);
if (issel1 != issel2)
{
binbuf_addv(buf->u_reconnectbuf, "ssiiii;",
gensym("#X"), gensym("connect"),
(issel1 ? nnotsel : 0)
+ glist_selectionindex(x, &t.tr_ob->ob_g, issel1),
t.tr_outno,
(issel2 ? nnotsel : 0) +
glist_selectionindex(x, &t.tr_ob2->ob_g, issel2),
t.tr_inno);
}
}
if (linetraverser_next(&t)) {
while (oc = linetraverser_next(&t))
{
int issel1 = glist_isselected(x, &t.tr_ob->ob_g);
int issel2 = glist_isselected(x, &t.tr_ob2->ob_g);
if (issel1 != issel2)
{
binbuf_addv(buf->u_reconnectbuf, "ssiiii;",
gensym("#X"), gensym("connect"),
(issel1 ? nnotsel : 0)
+ glist_selectionindex(x, &t.tr_ob->ob_g, issel1),
t.tr_outno,
(issel2 ? nnotsel : 0) +
glist_selectionindex(x, &t.tr_ob2->ob_g, issel2),
t.tr_inno);
}
}
}
if (mode == UCUT_TEXT)
{
buf->u_objectbuf = canvas_docopy(x);
......@@ -706,7 +711,7 @@ static void canvas_undo_cut(t_canvas *x, void *z, int action)
binbuf_free(buf->u_reconnectbuf);
if (buf->u_redotextbuf)
binbuf_free(buf->u_redotextbuf);
t_freebytes(buf, sizeof(*buf));
if (buf != NULL) t_freebytes(buf, sizeof(*buf));
}
}
......@@ -1159,7 +1164,7 @@ typedef struct _undo_canvas_properties
unsigned int gl_hidetext:1; /* hide object-name + args when doing graph on parent */
} t_undo_canvas_properties;
t_undo_canvas_properties *global_buf; /* we need this to avoid redundant undo creation when pressing apply and then ok in the canvas properties menu */
t_undo_canvas_properties global_buf; /* we need this to avoid redundant undo creation when pressing apply and then ok in the canvas properties menu */
static void *canvas_undo_set_canvas(t_canvas *x)
{
......@@ -1169,10 +1174,10 @@ static void *canvas_undo_set_canvas(t_canvas *x)
if (!x->gl_edit)
canvas_editmode(x, 1);
if (global_buf == NULL) {
global_buf = (t_undo_canvas_properties *)getbytes(sizeof(*global_buf));
//if (global_buf == NULL) {
// global_buf = (t_undo_canvas_properties *)getbytes(sizeof(*global_buf));
//fprintf(stderr,"creating a new buffer for canvas properties\n");
}
//}
/*if (
global_buf->gl_pixwidth != x->gl_pixwidth ||
......@@ -1192,24 +1197,24 @@ static void *canvas_undo_set_canvas(t_canvas *x)
global_buf->gl_hidetext != x->gl_hidetext)
{*/
//fprintf(stderr,"changing values\n");
global_buf->gl_pixwidth = x->gl_pixwidth;
global_buf->gl_pixheight = x->gl_pixheight;
global_buf->gl_x1 = x->gl_x1;
global_buf->gl_y1 = x->gl_y1;
global_buf->gl_x2 = x->gl_x2;
global_buf->gl_y2 = x->gl_y2;
global_buf->gl_screenx1 = x->gl_screenx1;
global_buf->gl_screeny1 = x->gl_screeny1;
global_buf->gl_screenx2 = x->gl_screenx2;
global_buf->gl_screeny2 = x->gl_screeny2;
global_buf->gl_xmargin = x->gl_xmargin;
global_buf->gl_ymargin = x->gl_ymargin;
global_buf->gl_goprect = x->gl_goprect;
global_buf->gl_isgraph = x->gl_isgraph;
global_buf->gl_hidetext = x->gl_hidetext;
global_buf.gl_pixwidth = x->gl_pixwidth;
global_buf.gl_pixheight = x->gl_pixheight;
global_buf.gl_x1 = x->gl_x1;
global_buf.gl_y1 = x->gl_y1;
global_buf.gl_x2 = x->gl_x2;
global_buf.gl_y2 = x->gl_y2;
global_buf.gl_screenx1 = x->gl_screenx1;
global_buf.gl_screeny1 = x->gl_screeny1;
global_buf.gl_screenx2 = x->gl_screenx2;
global_buf.gl_screeny2 = x->gl_screeny2;
global_buf.gl_xmargin = x->gl_xmargin;
global_buf.gl_ymargin = x->gl_ymargin;
global_buf.gl_goprect = x->gl_goprect;
global_buf.gl_isgraph = x->gl_isgraph;
global_buf.gl_hidetext = x->gl_hidetext;
//}
return (global_buf);
return (&global_buf);
}
extern int gfxstub_haveproperties(void *key);
......@@ -1217,7 +1222,7 @@ extern int gfxstub_haveproperties(void *key);
static void canvas_undo_canvas_apply(t_canvas *x, void *z, int action)
{
t_undo_canvas_properties *buf = z;
t_undo_canvas_properties *tmp;
t_undo_canvas_properties tmp;
if (!x->gl_edit)
canvas_editmode(x, 1);
......@@ -1232,24 +1237,24 @@ static void canvas_undo_canvas_apply(t_canvas *x, void *z, int action)
}
//create a temporary data holder
tmp = (t_undo_canvas_properties *)getbytes(sizeof(*tmp));
//tmp = (t_undo_canvas_properties *)getbytes(sizeof(*tmp));
//store current canvas values into temporary data holder
tmp->gl_pixwidth = x->gl_pixwidth;
tmp->gl_pixheight = x->gl_pixheight;
tmp->gl_x1 = x->gl_x1;
tmp->gl_y1 = x->gl_y1;
tmp->gl_x2 = x->gl_x2;
tmp->gl_y2 = x->gl_y2;
tmp->gl_screenx1 = x->gl_screenx1;
tmp->gl_screeny1 = x->gl_screeny1;
tmp->gl_screenx2 = x->gl_screenx2;
tmp->gl_screeny2 = x->gl_screeny2;
tmp->gl_xmargin = x->gl_xmargin;
tmp->gl_ymargin = x->gl_ymargin;
tmp->gl_goprect = x->gl_goprect;
tmp->gl_isgraph = x->gl_isgraph;
tmp->gl_hidetext = x->gl_hidetext;
tmp.gl_pixwidth = x->gl_pixwidth;
tmp.gl_pixheight = x->gl_pixheight;
tmp.gl_x1 = x->gl_x1;
tmp.gl_y1 = x->gl_y1;
tmp.gl_x2 = x->gl_x2;
tmp.gl_y2 = x->gl_y2;
tmp.gl_screenx1 = x->gl_screenx1;
tmp.gl_screeny1 = x->gl_screeny1;
tmp.gl_screenx2 = x->gl_screenx2;
tmp.gl_screeny2 = x->gl_screeny2;
tmp.gl_xmargin = x->gl_xmargin;
tmp.gl_ymargin = x->gl_ymargin;
tmp.gl_goprect = x->gl_goprect;
tmp.gl_isgraph = x->gl_isgraph;
tmp.gl_hidetext = x->gl_hidetext;
//change canvas values with the ones from the undo buffer
x->gl_pixwidth = buf->gl_pixwidth;
......@@ -1269,24 +1274,24 @@ static void canvas_undo_canvas_apply(t_canvas *x, void *z, int action)
x->gl_hidetext = buf->gl_hidetext;
//copy data values from the temporary data to the undo buffer
buf->gl_pixwidth = tmp->gl_pixwidth;
buf->gl_pixheight = tmp->gl_pixheight;
buf->gl_x1 = tmp->gl_x1;
buf->gl_y1 = tmp->gl_y1;
buf->gl_x2 = tmp->gl_x2;
buf->gl_y2 = tmp->gl_y2;
buf->gl_screenx1 = tmp->gl_screenx1;
buf->gl_screeny1 = tmp->gl_screeny1;
buf->gl_screenx2 = tmp->gl_screenx2;
buf->gl_screeny2 = tmp->gl_screeny2;
buf->gl_xmargin = tmp->gl_xmargin;
buf->gl_ymargin = tmp->gl_ymargin;
buf->gl_goprect = tmp->gl_goprect;
buf->gl_isgraph = tmp->gl_isgraph;
buf->gl_hidetext = tmp->gl_hidetext;
buf->gl_pixwidth = tmp.gl_pixwidth;
buf->gl_pixheight = tmp.gl_pixheight;
buf->gl_x1 = tmp.gl_x1;
buf->gl_y1 = tmp.gl_y1;
buf->gl_x2 = tmp.gl_x2;
buf->gl_y2 = tmp.gl_y2;
buf->gl_screenx1 = tmp.gl_screenx1;
buf->gl_screeny1 = tmp.gl_screeny1;
buf->gl_screenx2 = tmp.gl_screenx2;
buf->gl_screeny2 = tmp.gl_screeny2;
buf->gl_xmargin = tmp.gl_xmargin;
buf->gl_ymargin = tmp.gl_ymargin;
buf->gl_goprect = tmp.gl_goprect;
buf->gl_isgraph = tmp.gl_isgraph;
buf->gl_hidetext = tmp.gl_hidetext;
//delete temporary data holder
t_freebytes(tmp, sizeof(*tmp));
//t_freebytes(tmp, sizeof(*tmp));
//redraw
canvas_setgraph(x, x->gl_isgraph, 0);
......@@ -1296,7 +1301,7 @@ static void canvas_undo_canvas_apply(t_canvas *x, void *z, int action)
}
if (x->gl_owner && glist_isvisible(x->gl_owner))
{
//fprintf(stderr,"we got gop\n");
fprintf(stderr,"we got gop\n");
glist_noselect(x);
gobj_vis(&x->gl_gobj, x->gl_owner, 0);
gobj_vis(&x->gl_gobj, x->gl_owner, 1);
......@@ -1305,13 +1310,18 @@ static void canvas_undo_canvas_apply(t_canvas *x, void *z, int action)
//update scrollbars when GOP potentially exceeds window size
t_canvas *canvas=(t_canvas *)glist_getcanvas(x);
//if gop is being disabled go one level up
if (!x->gl_isgraph) canvas=canvas->gl_owner;
if (!x->gl_isgraph && x->gl_owner) {
canvas=canvas->gl_owner;