aboutsummaryrefslogtreecommitdiff
path: root/pd/src/g_canvas.c
diff options
context:
space:
mode:
authorGuenter Geiger <ggeiger@users.sourceforge.net>2002-07-29 17:06:19 +0000
committerGuenter Geiger <ggeiger@users.sourceforge.net>2002-07-29 17:06:19 +0000
commit57045df5fe3ec557e57dc7434ac1a07b5521bffc (patch)
tree7174058b41b73c808107c7090d9a4e93ee202341 /pd/src/g_canvas.c
parentda38b3424229e59f956252c3d89895e43e84e278 (diff)
This commit was generated by cvs2svn to compensate for changes in r58,
which included commits to RCS files with non-trunk default branches. svn path=/trunk/; revision=59
Diffstat (limited to 'pd/src/g_canvas.c')
-rw-r--r--pd/src/g_canvas.c1482
1 files changed, 1482 insertions, 0 deletions
diff --git a/pd/src/g_canvas.c b/pd/src/g_canvas.c
new file mode 100644
index 00000000..c63b2c0b
--- /dev/null
+++ b/pd/src/g_canvas.c
@@ -0,0 +1,1482 @@
+/* Copyright (c) 1997-2001 Miller Puckette and others.
+* For information on usage and redistribution, and for a DISCLAIMER OF ALL
+* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */
+
+/* this file defines the "glist" class, also known as "canvas" (the two used
+to be different but are now unified except for some fossilized names.) */
+
+/* changes by Thomas Musil IEM KUG Graz Austria 2001 */
+
+/* improvement: line-delete-protection, look for "protect" */
+/* bug-fix: canvas_menuclose(): by Krzysztof Czaja */
+/* bug-fix: table_new(): I reversed the y-bounds */
+
+/* IOhannes :
+ * changed the canvas_restore, so that it might accept $args as well
+ * (like "pd $0_test")
+ * so you can make multiple & distinguishable templates
+ * 1511:forum::für::umläute:2001
+ * changes marked with IOhannes
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include "m_imp.h"
+#include "g_canvas.h"
+#include <string.h>
+#include "g_all_guis.h"
+
+struct _canvasenvironment
+{
+ t_symbol *ce_dir; /* directory patch lives in */
+ int ce_argc; /* number of "$" arguments */
+ t_atom *ce_argv; /* array of "$" arguments */
+ int ce_dollarzero; /* value of "$0" */
+};
+
+#define GLIST_DEFCANVASWIDTH 450
+#define GLIST_DEFCANVASHEIGHT 300
+
+#ifdef MACOSX
+#define GLIST_DEFCANVASYLOC 20
+#else
+#define GLIST_DEFCANVASYLOC 0
+#endif
+
+/* ---------------------- variables --------------------------- */
+
+extern t_pd *newest;
+t_class *canvas_class;
+static int canvas_dspstate; /* whether DSP is on or off */
+t_canvas *canvas_editing; /* last canvas to start text edting */
+t_canvas *canvas_whichfind; /* last canvas we did a find in */
+t_canvas *canvas_list; /* list of all root canvases */
+
+/* ------------------ forward function declarations --------------- */
+static void canvas_start_dsp(void);
+static void canvas_stop_dsp(void);
+static void canvas_drawlines(t_canvas *x);
+static void canvas_setbounds(t_canvas *x, int x1, int y1, int x2, int y2);
+static void canvas_reflecttitle(t_canvas *x);
+static void canvas_addtolist(t_canvas *x);
+static void canvas_takeofflist(t_canvas *x);
+static void canvas_pop(t_canvas *x, t_floatarg fvis);
+void canvas_create_editor(t_glist *x, int createit);
+
+/* --------- functions to handle the canvas environment ----------- */
+
+static t_symbol *canvas_newfilename = &s_;
+static t_symbol *canvas_newdirectory = &s_;
+static int canvas_newargc;
+static t_atom *canvas_newargv;
+
+static void glist_doupdatewindowlist(t_glist *gl, char *sbuf)
+{
+ t_gobj *g;
+ if (!gl->gl_owner)
+ {
+ /* this is a canvas; if we have a window, put on "windows" list */
+ t_canvas *canvas = (t_canvas *)gl;
+ if (canvas->gl_havewindow)
+ {
+ if (strlen(sbuf) + strlen(gl->gl_name->s_name) + 100 <= 1024)
+ {
+ char tbuf[1024];
+ sprintf(tbuf, "{%s .x%x} ", gl->gl_name->s_name, (t_int)canvas);
+ strcat(sbuf, tbuf);
+ }
+ }
+ }
+ for (g = gl->gl_list; g; g = g->g_next)
+ {
+ if (pd_class(&g->g_pd) == canvas_class)
+ glist_doupdatewindowlist((t_glist *)g, sbuf);
+ }
+ return;
+}
+
+ /* maintain the list of visible toplevels for the GUI's "windows" menu */
+void canvas_updatewindowlist( void)
+{
+ t_canvas *x;
+ char sbuf[1024];
+ strcpy(sbuf, "set menu_windowlist {");
+ /* find all root canvases */
+ for (x = canvas_list; x; x = x->gl_next)
+ glist_doupdatewindowlist(x, sbuf);
+ strcat(sbuf, "}\n");
+ sys_gui(sbuf);
+}
+
+ /* add a glist the list of "root" canvases (toplevels without parents.) */
+static void canvas_addtolist(t_canvas *x)
+{
+ x->gl_next = canvas_list;
+ canvas_list = x;
+}
+
+static void canvas_takeofflist(t_canvas *x)
+{
+ /* take it off the window list */
+ if (x == canvas_list) canvas_list = x->gl_next;
+ else
+ {
+ t_canvas *z;
+ for (z = canvas_list; z->gl_next != x; z = z->gl_next)
+ ;
+ z->gl_next = x->gl_next;
+ }
+}
+
+
+void canvas_setargs(int argc, t_atom *argv)
+{
+ /* if there's an old one lying around free it here. This
+ happens if an abstraction is loaded but never gets as far
+ as calling canvas_new(). */
+ if (canvas_newargv)
+ freebytes(canvas_newargv, canvas_newargc * sizeof(t_atom));
+ canvas_newargc = argc;
+ canvas_newargv = copybytes(argv, argc * sizeof(t_atom));
+}
+
+void glob_setfilename(void *dummy, t_symbol *filesym, t_symbol *dirsym)
+{
+ canvas_newfilename = filesym;
+ canvas_newdirectory = dirsym;
+}
+
+t_canvas *canvas_getcurrent(void)
+{
+ return ((t_canvas *)pd_findbyclass(&s__X, canvas_class));
+}
+
+void canvas_setcurrent(t_canvas *x)
+{
+ pd_pushsym(&x->gl_pd);
+}
+
+void canvas_unsetcurrent(t_canvas *x)
+{
+ pd_popsym(&x->gl_pd);
+}
+
+t_canvasenvironment *canvas_getenv(t_canvas *x)
+{
+ if (!x) bug("canvas_getenv");
+ while (!x->gl_env)
+ if (!(x = x->gl_owner))
+ bug("t_canvasenvironment", x);
+ return (x->gl_env);
+}
+
+int canvas_getdollarzero( void)
+{
+ t_canvas *x = canvas_getcurrent();
+ t_canvasenvironment *env = (x ? canvas_getenv(x) : 0);
+ if (env)
+ return (env->ce_dollarzero);
+ else return (0);
+}
+
+void canvas_getargs(int *argcp, t_atom **argvp)
+{
+ t_canvasenvironment *e = canvas_getenv(canvas_getcurrent());
+ *argcp = e->ce_argc;
+ *argvp = e->ce_argv;
+}
+
+t_symbol *realizedollsym(t_symbol *s, int ac, t_atom *av, int tonew);
+
+t_symbol *canvas_realizedollar(t_canvas *x, t_symbol *s)
+{
+ t_symbol *ret;
+ char *name = s->s_name;
+ if (*name == '$' && name[1] >= '0' && name[1] <= '9')
+ {
+ t_canvasenvironment *env = canvas_getenv(x);
+ canvas_setcurrent(x);
+ ret = realizedollsym(gensym(name+1), env->ce_argc, env->ce_argv, 1);
+ canvas_unsetcurrent(x);
+ }
+ else ret = s;
+ return (ret);
+}
+
+t_symbol *canvas_getcurrentdir(void)
+{
+ t_canvasenvironment *e = canvas_getenv(canvas_getcurrent());
+ return (e->ce_dir);
+}
+
+t_symbol *canvas_getdir(t_canvas *x)
+{
+ t_canvasenvironment *e = canvas_getenv(x);
+ return (e->ce_dir);
+}
+
+void canvas_makefilename(t_canvas *x, char *file, char *result, int resultsize)
+{
+ char *dir = canvas_getenv(x)->ce_dir->s_name;
+ if (file[0] == '/' || (file[0] && file[1] == ':') || !*dir)
+ {
+ strncpy(result, file, resultsize);
+ result[resultsize-1] = 0;
+ }
+ else
+ {
+ int nleft;
+ strncpy(result, dir, resultsize);
+ result[resultsize-1] = 0;
+ nleft = resultsize - strlen(result) - 1;
+ if (nleft <= 0) return;
+ strcat(result, "/");
+ strncat(result, file, nleft);
+ result[resultsize-1] = 0;
+ }
+}
+
+void canvas_rename(t_canvas *x, t_symbol *s, t_symbol *dir)
+{
+ if (strcmp(x->gl_name->s_name, "Pd"))
+ pd_unbind(&x->gl_pd, canvas_makebindsym(x->gl_name));
+ x->gl_name = s;
+ if (strcmp(x->gl_name->s_name, "Pd"))
+ pd_bind(&x->gl_pd, canvas_makebindsym(x->gl_name));
+ if (glist_isvisible(x))
+ canvas_reflecttitle(x);
+ if (dir && dir != &s_)
+ {
+ t_canvasenvironment *e = canvas_getenv(x);
+ e->ce_dir = dir;
+ }
+}
+
+/* --------------- traversing the set of lines in a canvas ----------- */
+
+void linetraverser_start(t_linetraverser *t, t_canvas *x)
+{
+ t->tr_ob = 0;
+ t->tr_x = x;
+ t->tr_nextoc = 0;
+ t->tr_nextoutno = t->tr_nout = 0;
+}
+
+t_outconnect *linetraverser_next(t_linetraverser *t)
+{
+ t_outconnect *rval = t->tr_nextoc;
+ int outno;
+ while (!rval)
+ {
+ outno = t->tr_nextoutno;
+ while (outno == t->tr_nout)
+ {
+ t_gobj *y;
+ t_object *ob = 0;
+ if (!t->tr_ob) y = t->tr_x->gl_list;
+ else y = t->tr_ob->ob_g.g_next;
+ for (; y; y = y->g_next)
+ if (ob = pd_checkobject(&y->g_pd)) break;
+ if (!ob) return (0);
+ t->tr_ob = ob;
+ t->tr_nout = obj_noutlets(ob);
+ outno = 0;
+ if (glist_isvisible(t->tr_x))
+ gobj_getrect(y, t->tr_x,
+ &t->tr_x11, &t->tr_y11, &t->tr_x12, &t->tr_y12);
+ else t->tr_x11 = t->tr_y11 = t->tr_x12 = t->tr_y12 = 0;
+ }
+ t->tr_nextoutno = outno + 1;
+ rval = obj_starttraverseoutlet(t->tr_ob, &t->tr_outlet, outno);
+ t->tr_outno = outno;
+ }
+ t->tr_nextoc = obj_nexttraverseoutlet(rval, &t->tr_ob2,
+ &t->tr_inlet, &t->tr_inno);
+ t->tr_nin = obj_ninlets(t->tr_ob2);
+ if (!t->tr_nin) bug("drawline");
+ if (glist_isvisible(t->tr_x))
+ {
+ int inplus = (t->tr_nin == 1 ? 1 : t->tr_nin - 1);
+ int outplus = (t->tr_nout == 1 ? 1 : t->tr_nout - 1);
+ gobj_getrect(&t->tr_ob2->ob_g, t->tr_x,
+ &t->tr_x21, &t->tr_y21, &t->tr_x22, &t->tr_y22);
+ t->tr_lx1 = t->tr_x11 +
+ ((t->tr_x12 - t->tr_x11 - IOWIDTH) * t->tr_outno) /
+ outplus + IOMIDDLE;
+ t->tr_ly1 = t->tr_y12;
+ t->tr_lx2 = t->tr_x21 +
+ ((t->tr_x22 - t->tr_x21 - IOWIDTH) * t->tr_inno)/inplus +
+ IOMIDDLE;
+ t->tr_ly2 = t->tr_y21;
+ }
+ else
+ {
+ t->tr_x21 = t->tr_y21 = t->tr_x22 = t->tr_y22 = 0;
+ t->tr_lx1 = t->tr_ly1 = t->tr_lx2 = t->tr_ly2 = 0;
+ }
+
+ return (rval);
+}
+
+void linetraverser_skipobject(t_linetraverser *t)
+{
+ t->tr_nextoc = 0;
+ t->tr_nextoutno = t->tr_nout;
+}
+
+/* -------------------- the canvas object -------------------------- */
+int glist_valid = 10000;
+
+void glist_init(t_glist *x)
+{
+ /* zero out everyone except "pd" field */
+ memset(((char *)x) + sizeof(x->gl_pd), 0, sizeof(*x) - sizeof(x->gl_pd));
+ x->gl_stub = gstub_new(x, 0);
+ x->gl_valid = ++glist_valid;
+ x->gl_xlabel = (t_symbol **)t_getbytes(0);
+ x->gl_ylabel = (t_symbol **)t_getbytes(0);
+}
+
+ /* make a new glist. It will either be a "root" canvas or else
+ its parent will be a "text" object in another window... we don't
+ know which yet. */
+t_canvas *canvas_new(void *dummy, t_symbol *sel, int argc, t_atom *argv)
+{
+ t_canvas *x = (t_canvas *)pd_new(canvas_class);
+ t_canvas *owner = canvas_getcurrent();
+ t_symbol *s = &s_;
+ int vis = 0, width = GLIST_DEFCANVASWIDTH, height = GLIST_DEFCANVASHEIGHT;
+ int xloc = 0, yloc = GLIST_DEFCANVASYLOC;
+ int font = (owner ? owner->gl_font : sys_defaultfont);
+ glist_init(x);
+ x->gl_obj.te_type = T_OBJECT;
+ if (!owner)
+ canvas_addtolist(x);
+ /* post("canvas %x, owner %x", x, owner); */
+
+ if (argc == 5) /* toplevel: x, y, w, h, font */
+ {
+ 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);
+ }
+ else if (argc == 6) /* subwindow: x, y, w, h, name, vis */
+ {
+ 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);
+ }
+ /* (otherwise assume we're being created from the menu.) */
+
+ if (canvas_newdirectory->s_name[0])
+ {
+ static int dollarzero = 1000;
+ t_canvasenvironment *env = x->gl_env =
+ (t_canvasenvironment *)getbytes(sizeof(*x->gl_env));
+ env->ce_dir = canvas_newdirectory;
+ env->ce_argc = canvas_newargc;
+ env->ce_argv = canvas_newargv;
+ env->ce_dollarzero = dollarzero++;
+ canvas_newdirectory = &s_;
+ canvas_newargc = 0;
+ canvas_newargv = 0;
+ }
+ else x->gl_env = 0;
+
+ x->gl_x1 = 0;
+ x->gl_y1 = 0;
+ x->gl_x2 = 1;
+ x->gl_y2 = 1;
+ canvas_setbounds(x, xloc, yloc, xloc + width, yloc + height);
+ x->gl_owner = owner;
+ x->gl_name = (*s->s_name ? s :
+ (canvas_newfilename ? canvas_newfilename : gensym("Pd")));
+ if (strcmp(x->gl_name->s_name, "Pd"))
+ pd_bind(&x->gl_pd, canvas_makebindsym(x->gl_name));
+ x->gl_loading = 1;
+ x->gl_willvis = vis;
+ x->gl_edit = !strncmp(x->gl_name->s_name, "Untitled", 8);
+ x->gl_font = sys_nearestfontsize(font);
+ pd_pushsym(&x->gl_pd);
+ return(x);
+}
+
+void canvas_setgraph(t_glist *x, int flag);
+
+static void canvas_coords(t_glist *x, t_symbol *s, int argc, t_atom *argv)
+{
+ x->gl_x1 = atom_getfloatarg(0, argc, argv);
+ x->gl_y1 = atom_getfloatarg(1, argc, argv);
+ x->gl_x2 = atom_getfloatarg(2, argc, argv);
+ x->gl_y2 = atom_getfloatarg(3, argc, argv);
+ x->gl_pixwidth = atom_getintarg(4, argc, argv);
+ x->gl_pixheight = atom_getintarg(5, argc, argv);
+ canvas_setgraph(x, atom_getintarg(6, argc, argv));
+}
+
+ /* make a new glist and add it to this glist. It will appear as
+ a "graph", not a text object. */
+t_glist *glist_addglist(t_glist *g, t_symbol *sym,
+ float x1, float y1, float x2, float y2,
+ float px1, float py1, float px2, float py2)
+{
+ static int gcount = 0;
+ int zz;
+ int menu = 0;
+ char *str;
+ t_glist *x = (t_glist *)pd_new(canvas_class);
+ glist_init(x);
+ x->gl_obj.te_type = T_OBJECT;
+ if (!*sym->s_name)
+ {
+ char buf[40];
+ sprintf(buf, "graph%d", ++gcount);
+ sym = gensym(buf);
+ menu = 1;
+ }
+ else if (!strncmp((str = sym->s_name), "graph", 5)
+ && (zz = atoi(str + 5)) > gcount)
+ gcount = zz;
+ /* in 0.34 and earlier, the pixel rectangle and the y bounds were
+ reversed; this would behave the same, except that the dialog window
+ would be confusing. The "correct" way is to have "py1" be the value
+ that is higher on the screen. */
+ if (py2 < py1)
+ {
+ float zz;
+ zz = y2;
+ y2 = y1;
+ y1 = zz;
+ zz = py2;
+ py2 = py1;
+ py1 = zz;
+ }
+ if (x1 == x2 || y1 == y2)
+ x1 = 0, x2 = 100, y1 = 1, y2 = -1;
+ if (px1 >= px2 || py1 >= py2)
+ px1 = 100, py1 = 20, px2 = 100 + GLIST_DEFGRAPHWIDTH,
+ py2 = 20 + GLIST_DEFGRAPHHEIGHT;
+ x->gl_name = sym;
+ x->gl_x1 = x1;
+ x->gl_x2 = x2;
+ x->gl_y1 = y1;
+ x->gl_y2 = y2;
+ x->gl_obj.te_xpix = px1;
+ x->gl_obj.te_ypix = py1;
+ x->gl_pixwidth = px2 - px1;
+ x->gl_pixheight = py2 - py1;
+ x->gl_font = (canvas_getcurrent() ?
+ canvas_getcurrent()->gl_font : sys_defaultfont);
+ x->gl_screenx1 = x->gl_screeny1 = 0;
+ x->gl_screenx2 = 450;
+ x->gl_screeny2 = 300;
+ if (strcmp(x->gl_name->s_name, "Pd"))
+ pd_bind(&x->gl_pd, canvas_makebindsym(x->gl_name));
+ x->gl_owner = g;
+ x->gl_stretch = 1;
+ x->gl_isgraph = 1;
+ x->gl_obj.te_binbuf = binbuf_new();
+ binbuf_addv(x->gl_obj.te_binbuf, "s", gensym("graph"));
+ if (!menu)
+ pd_pushsym(&x->gl_pd);
+ glist_add(g, &x->gl_gobj);
+ if (glist_isvisible(g))
+ canvas_create_editor(x, 1);
+ return (x);
+}
+
+ /* call glist_addglist from a Pd message */
+void glist_glist(t_glist *g, t_symbol *s, int argc, t_atom *argv)
+{
+ t_symbol *sym = atom_getsymbolarg(0, argc, argv);
+ float x1 = atom_getfloatarg(1, argc, argv);
+ float y1 = atom_getfloatarg(2, argc, argv);
+ float x2 = atom_getfloatarg(3, argc, argv);
+ float y2 = atom_getfloatarg(4, argc, argv);
+ float px1 = atom_getfloatarg(5, argc, argv);
+ float py1 = atom_getfloatarg(6, argc, argv);
+ float px2 = atom_getfloatarg(7, argc, argv);
+ float py2 = atom_getfloatarg(8, argc, argv);
+ glist_addglist(g, sym, x1, y1, x2, y2, px1, py1, px2, py2);
+}
+
+ /* return true if the glist should appear as a graph on parent;
+ otherwise it appears as a text box. */
+int glist_isgraph(t_glist *x)
+{
+ return (x->gl_isgraph);
+}
+
+ /* This is sent from the GUI to inform a toplevel that its window has been
+ moved or resized. */
+static void canvas_setbounds(t_canvas *x, int x1, int y1, int x2, int y2)
+{
+ x->gl_screenx1 = x1;
+ x->gl_screeny1 = y1;
+ x->gl_screenx2 = x2;
+ x->gl_screeny2 = y2;
+ if (!glist_isgraph(x) && (x->gl_y2 < x->gl_y1))
+ {
+ /* if it's flipped so that y grows upward,
+ fix so that zero is bottom edge and redraw. This is
+ only appropriate if we're a regular "text" object on the
+ parent. */
+ float diff = x->gl_y1 - x->gl_y2;
+ x->gl_y1 = x->gl_screeny2 * diff;
+ x->gl_y2 = x->gl_y1 - diff;
+ canvas_redraw(x);
+ }
+}
+
+t_symbol *canvas_makebindsym(t_symbol *s)
+{
+ char buf[MAXPDSTRING];
+ strcpy(buf, "pd-");
+ strcat(buf, s->s_name);
+ return (gensym(buf));
+}
+
+void canvas_reflecttitle(t_canvas *x)
+{
+ char namebuf[MAXPDSTRING];
+ t_canvasenvironment *env = canvas_getenv(x);
+ if (env->ce_argc)
+ {
+ int i;
+ strcpy(namebuf, " (");
+ for (i = 0; i < env->ce_argc; i++)
+ {
+ if (strlen(namebuf) > MAXPDSTRING/2 - 5)
+ break;
+ if (i != 0)
+ strcat(namebuf, " ");
+ atom_string(&env->ce_argv[i], namebuf + strlen(namebuf),
+ MAXPDSTRING/2);
+ }
+ strcat(namebuf, ")");
+ }
+ else namebuf[0] = 0;
+ sys_vgui("wm title .x%x {%s%c%s - %s}\n",
+ x, x->gl_name->s_name, (x->gl_dirty? '*' : ' '), namebuf,
+ canvas_getdir(x)->s_name);
+}
+
+void canvas_dirty(t_canvas *x, t_int n)
+{
+ t_canvas *x2 = canvas_getrootfor(x);
+ if ((unsigned)n != x2->gl_dirty)
+ {
+ x2->gl_dirty = n;
+ canvas_reflecttitle(x2);
+ }
+}
+
+extern t_gobj *canvas_selectme; /* HACK */
+
+ /* the window becomes "mapped" (visible and not miniaturized) or
+ "unmapped" (either miniaturized or just plain gone.) This should be
+ called from the GUI after the fact to "notify" us that we're mapped. */
+void canvas_map(t_canvas *x, t_floatarg f)
+{
+ int flag = (f != 0);
+ t_gobj *y;
+ if (flag)
+ {
+ if (!glist_isvisible(x))
+ {
+ t_selection *sel;
+ if (!x->gl_havewindow)
+ {
+ bug("canvas_map");
+ canvas_vis(x, 1);
+ }
+ for (y = x->gl_list; y; y = y->g_next)
+ gobj_vis(y, x, 1);
+ for (sel = x->gl_editor->e_selection; sel; sel = sel->sel_next)
+ gobj_select(sel->sel_what, x, 1);
+ x->gl_mapped = 1;
+ if (canvas_selectme)
+ {
+ glist_noselect(x);
+ glist_select(x, canvas_selectme);
+ canvas_selectme = 0;
+ }
+ canvas_drawlines(x);
+ }
+ }
+ else
+ {
+ if (glist_isvisible(x))
+ {
+ for (y = x->gl_list; y; y = y->g_next)
+ gobj_vis(y, x, 0);
+ x->gl_mapped = 0;
+ }
+ }
+}
+
+void canvas_redraw(t_canvas *x)
+{
+ if (glist_isvisible(x))
+ {
+ canvas_map(x, 0);
+ canvas_map(x, 1);
+ }
+}
+
+/* ---- editors -- perhaps this and "vis" should go to g_editor.c ------- */
+
+static t_editor *editor_new(t_glist *owner)
+{
+ char buf[40];
+ t_editor *x = (t_editor *)getbytes(sizeof(*x));
+ x->e_connectbuf = binbuf_new();
+ x->e_deleted = binbuf_new();
+ x->e_glist = owner;
+ sprintf(buf, ".x%x", (t_int)owner);
+ x->e_guiconnect = guiconnect_new(&owner->gl_pd, gensym(buf));
+ return (x);
+}
+
+static void editor_free(t_editor *x, t_glist *y)
+{
+ glist_noselect(y);
+ guiconnect_notarget(x->e_guiconnect, 1000);
+ binbuf_free(x->e_connectbuf);
+ binbuf_free(x->e_deleted);
+ freebytes((void *)x, sizeof(*x));
+}
+
+ /* recursively create or destroy all editors of a glist and its
+ sub-glists, as long as they aren't toplevels. */
+void canvas_create_editor(t_glist *x, int createit)
+{
+ t_gobj *y;
+ if (createit)
+ {
+ if (x->gl_editor)
+ bug("canvas_create_editor");
+ else x->gl_editor = editor_new(x);
+ }
+ else
+ {
+ if (!x->gl_editor)
+ bug("canvas_create_editor");
+ else editor_free(x->gl_editor, x);
+ x->gl_editor = 0;
+ }
+ for (y = x->gl_list; y; y = y->g_next)
+ if (pd_class(&y->g_pd) == canvas_class &&
+ ((t_canvas *)y)->gl_isgraph)
+ canvas_create_editor((t_canvas *)y, createit);
+}
+
+ /* we call this when we want the window to become visible, mapped, and
+ in front of all windows; or with "f" zero, when we want to get rid of
+ the window. */
+void canvas_vis(t_canvas *x, t_floatarg f)
+{
+ char buf[30];
+ int flag = (f != 0);
+ if (flag)
+ {
+ /* test if we're already visible and toplevel */
+ if (glist_isvisible(x) && !x->gl_isgraph)
+ { /* just put us in front */
+#ifdef NT
+ canvas_vis(x, 0);
+ canvas_vis(x, 1);
+#else
+ sys_vgui("raise .x%x\n", x);
+ sys_vgui("focus .x%x.c\n", x);
+ sys_vgui("wm deiconify .x%x\n", x);
+#endif
+ }
+ else
+ {
+ canvas_create_editor(x, 1);
+ sys_vgui("pdtk_canvas_new .x%x %d %d +%d+%d\n", x,
+ (int)(x->gl_screenx2 - x->gl_screenx1),
+ (int)(x->gl_screeny2 - x->gl_screeny1),
+ (int)(x->gl_screenx1), (int)(x->gl_screeny1)
+ );
+ canvas_reflecttitle(x);
+ /* simulate a mouse up so u_main will calculate scrollbars...
+ ugly! */
+ sys_vgui("pdtk_canvas_mouseup .x%x.c 0 0 0\n", x);
+ x->gl_havewindow = 1;
+ canvas_updatewindowlist();
+ }
+ }
+ else /* make invisible */
+ {
+ int i;
+ t_canvas *x2;
+ if (!x->gl_havewindow)
+ {
+ /* bug workaround -- a graph in a visible patch gets "invised"
+ when the patch is closed, and must lost the editor here. It's
+ probably not the natural place to do this. Other cases like
+ subpatches fall here too but don'd need the editor freed, so
+ we check if it exists. */
+ if (x->gl_editor)
+ canvas_create_editor(x, 0);
+ return;
+ }
+ glist_noselect(x);
+ if (glist_isvisible(x))
+ canvas_map(x, 0);
+ canvas_create_editor(x, 0);
+ sys_vgui("destroy .x%x\n", x);
+ for (i = 1, x2 = x; x2; x2 = x2->gl_next, i++)
+ ;
+ sys_vgui(".mbar.find.menu delete %d\n", i);
+ /* if we're a graph on our parent, and if the parent exists
+ and is visible, show ourselves on parent. */
+ if (glist_isgraph(x) && x->gl_owner)
+ {
+ t_glist *gl2 = x->gl_owner;
+ canvas_create_editor(x, 1);
+ if (glist_isvisible(gl2))
+ gobj_vis(&x->gl_gobj, gl2, 0);
+ x->gl_havewindow = 0;
+ if (glist_isvisible(gl2))
+ gobj_vis(&x->gl_gobj, gl2, 1);
+ }
+ else x->gl_havewindow = 0;
+ canvas_updatewindowlist();
+ }
+}
+
+ /* we call this on a non-toplevel glist to "open" it into its
+ own window. */
+void glist_menu_open(t_glist *x)
+{
+ if (glist_isvisible(x) && !glist_istoplevel(x))
+ {
+ t_glist *gl2 = x->gl_owner;
+ if (!gl2)
+ bug("canvas_vis"); /* shouldn't happen but don't get too upset. */
+ else
+ {
+ /* erase ourself in parent window */
+ gobj_vis(&x->gl_gobj, gl2, 0);
+ /* get rid of our editor (and subeditors) */
+ canvas_create_editor(x, 0);
+ x->gl_havewindow = 1;
+ /* redraw ourself in parent window (blanked out this time) */
+ gobj_vis(&x->gl_gobj, gl2, 1);
+ }
+ }
+ canvas_vis(x, 1);
+}
+
+int glist_isvisible(t_glist *x)
+{
+ return ((!x->gl_loading) && glist_getcanvas(x)->gl_mapped);
+}
+
+int glist_istoplevel(t_glist *x)
+{
+ /* we consider a graph "toplevel" if it has its own window
+ or if it appears as a box in its parent window so that we
+ don't draw the actual contents there. */
+ return (x->gl_havewindow || !x->gl_isgraph);
+}
+
+int glist_getfont(t_glist *x)
+{
+ return (glist_getcanvas(x)->gl_font);
+}
+
+void canvas_free(t_canvas *x)
+{
+ t_gobj *y;
+ int dspstate = canvas_suspend_dsp();
+
+ if (canvas_editing == x)
+ canvas_editing = 0;
+ if (canvas_whichfind == x)
+ canvas_whichfind = 0;
+ glist_noselect(x);
+ while (y = x->gl_list)
+ glist_delete(x, y);
+ canvas_vis(x, 0);
+
+ if (strcmp(x->gl_name->s_name, "Pd"))
+ pd_unbind(&x->gl_pd, canvas_makebindsym(x->gl_name));
+ if (x->gl_env)
+ {
+ freebytes(x->gl_env->ce_argv, x->gl_env->ce_argc * sizeof(t_atom));
+ freebytes(x->gl_env, sizeof(*x->gl_env));
+ }
+ canvas_resume_dsp(dspstate);
+ glist_cleanup(x);
+ gfxstub_deleteforkey(x); /* probably unnecessary */
+ if (!x->gl_owner)
+ canvas_takeofflist(x);
+}
+
+/* ----------------- lines ---------- */
+
+static void canvas_drawlines(t_canvas *x)
+{
+ t_linetraverser t;
+ t_outconnect *oc;
+ {
+ linetraverser_start(&t, x);
+ while (oc = linetraverser_next(&t))
+ sys_vgui(".x%x.c create line %d %d %d %d -tags l%x\n",
+ glist_getcanvas(x),
+ t.tr_lx1, t.tr_ly1, t.tr_lx2, t.tr_ly2, oc);
+ }
+}
+
+void canvas_fixlinesfor(t_canvas *x, t_text *text)
+{
+ t_linetraverser t;
+ t_outconnect *oc;
+
+ linetraverser_start(&t, x);
+ while (oc = linetraverser_next(&t))
+ {
+ if (t.tr_ob == text || t.tr_ob2 == text)
+ {
+ sys_vgui(".x%x.c coords l%x %d %d %d %d\n",
+ glist_getcanvas(x), oc,
+ t.tr_lx1, t.tr_ly1, t.tr_lx2, t.tr_ly2);
+ }
+ }
+}
+
+ /* kill all lines for the object */
+void canvas_deletelinesfor(t_canvas *x, t_text *text)
+{
+ t_linetraverser t;
+ t_outconnect *oc;
+ linetraverser_start(&t, x);
+ while (oc = linetraverser_next(&t))
+ {
+ if (t.tr_ob == text || t.tr_ob2 == text)
+ {
+ if (x->gl_editor)
+ {
+ sys_vgui(".x%x.c delete l%x\n",
+ glist_getcanvas(x), oc);
+ }
+ obj_disconnect(t.tr_ob, t.tr_outno, t.tr_ob2, t.tr_inno);
+ }
+ }
+}
+
+ /* kill all lines for one inlet or outlet */
+void canvas_deletelinesforio(t_canvas *x, t_text *text,
+ t_inlet *inp, t_outlet *outp)
+{
+ t_linetraverser t;
+ t_outconnect *oc;
+ linetraverser_start(&t, x);
+ while (oc = linetraverser_next(&t))
+ {
+ if ((t.tr_ob == text && t.tr_outlet == outp) ||
+ (t.tr_ob2 == text && t.tr_inlet == inp))
+ {
+ if (x->gl_editor)
+ {
+ sys_vgui(".x%x.c delete l%x\n",
+ glist_getcanvas(x), oc);
+ }
+ obj_disconnect(t.tr_ob, t.tr_outno, t.tr_ob2, t.tr_inno);
+ }
+ }
+}
+
+static void canvas_pop(t_canvas *x, t_floatarg fvis)
+{
+ if (fvis != 0)
+ canvas_vis(x, 1);
+ pd_popsym(&x->gl_pd);
+ canvas_resortinlets(x);
+ canvas_resortoutlets(x);
+ x->gl_loading = 0;
+}
+
+void canvas_objfor(t_glist *gl, t_text *x, int argc, t_atom *argv);
+
+
+void canvas_restore(t_canvas *x, t_symbol *s, int argc, t_atom *argv)
+{ /* IOhannes */
+ t_pd *z;
+ /* this should be unnecessary, but sometimes the canvas's name gets
+ out of sync with the owning box's argument; this fixes that */
+ if (argc > 3)
+ {
+ t_atom *ap=argv+3;
+ if (ap->a_type == A_SYMBOL)
+ {
+ char *buf=ap->a_w.w_symbol->s_name, *bufp;
+ if (*buf == '$' && buf[1] >= '0' && buf[1] <= '9')
+ {
+ for (bufp = buf+2; *bufp; bufp++)
+ if (*bufp < '0' || *bufp > '9')
+ {
+ SETDOLLSYM(ap, gensym(buf+1));
+ goto didit;
+ }
+ SETDOLLAR(ap, atoi(buf+1));
+ didit: ;
+ }
+ }
+
+ if (ap->a_type == A_DOLLSYM)
+ {
+ t_canvasenvironment *e = canvas_getenv(canvas_getcurrent());
+ canvas_rename(x, realizedollsym(ap->a_w.w_symbol,
+ e->ce_argc, e->ce_argv, 1), 0);
+ }
+ else if (ap->a_type == A_SYMBOL)
+ canvas_rename(x, argv[3].a_w.w_symbol, 0);
+ }
+ canvas_pop(x, x->gl_willvis);
+
+ if (!(z = gensym("#X")->s_thing)) error("canvas_restore: out of context");
+ else if (*z != canvas_class) error("canvas_restore: wasn't a canvas");
+ else
+ {
+ t_canvas *x2 = (t_canvas *)z;
+ x->gl_owner = x2;
+ canvas_objfor(x2, &x->gl_obj, argc, argv);
+ }
+}
+
+static void canvas_loadbangabstractions(t_canvas *x)
+{
+ t_gobj *y;
+ t_symbol *s = gensym("loadbang");
+ for (y = x->gl_list; y; y = y->g_next)
+ if (pd_class(&y->g_pd) == canvas_class)
+ {
+ if (canvas_isabstraction((t_canvas *)y))
+ canvas_loadbang((t_canvas *)y);
+ else
+ canvas_loadbangabstractions((t_canvas *)y);
+ }
+}
+
+void canvas_loadbangsubpatches(t_canvas *x)
+{
+ t_gobj *y;
+ t_symbol *s = gensym("loadbang");
+ for (y = x->gl_list; y; y = y->g_next)
+ if (pd_class(&y->g_pd) == canvas_class)
+ {
+ if (!canvas_isabstraction((t_canvas *)y))
+ canvas_loadbangsubpatches((t_canvas *)y);
+ }
+ for (y = x->gl_list; y; y = y->g_next)
+ if ((pd_class(&y->g_pd) != canvas_class) &&
+ zgetfn(&y->g_pd, s))
+ pd_vmess(&y->g_pd, s, "");
+}
+
+void canvas_loadbang(t_canvas *x)
+{
+ t_gobj *y;
+ canvas_loadbangabstractions(x);
+ canvas_loadbangsubpatches(x);
+}
+
+ /* When you ask a canvas its size the result is 2 pixels more than what
+ you gave it to open it; perhaps there's a 1-pixel border all around it
+ or something. Anyway, we just add the 2 pixels back here: */
+
+#ifdef NT
+#define HORIZBORDER 2
+#define VERTBORDER 2
+#else
+#define HORIZBORDER 2
+#define VERTBORDER 2
+#endif
+
+static void canvas_relocate(t_canvas *x, t_symbol *canvasgeom,
+ t_symbol *topgeom)
+{
+ int cxpix, cypix, cw, ch, txpix, typix, tw, th;
+ if (sscanf(canvasgeom->s_name, "%dx%d+%d+%d", &cw, &ch, &cxpix, &cypix)
+ < 4 ||
+ sscanf(topgeom->s_name, "%dx%d+%d+%d", &tw, &th, &txpix, &typix) < 4)
+ bug("canvas_relocate");
+ /* for some reason this is initially called with cw=ch=1 so
+ we just suppress that here. */
+ if (cw > 5 && ch > 5)
+ canvas_setbounds(x, txpix, typix,
+ txpix + cw - HORIZBORDER, typix + ch - VERTBORDER);
+}
+
+void canvas_popabstraction(t_canvas *x)
+{
+ newest = &x->gl_pd;
+ pd_popsym(&x->gl_pd);
+ x->gl_loading = 0;
+ canvas_resortinlets(x);
+ canvas_resortoutlets(x);
+}
+
+void canvas_logerror(t_object *y)
+{
+#ifdef LATER
+ canvas_vis(x, 1);
+ if (!glist_isselected(x, &y->ob_g))
+ glist_select(x, &y->ob_g);
+#endif
+}
+
+/* -------------------------- subcanvases ---------------------- */
+
+static void *subcanvas_new(t_symbol *s)
+{
+ t_atom a[6];
+ t_canvas *x, *z = canvas_getcurrent();
+ if (!*s->s_name) s = gensym("/SUBPATCH/");
+ SETFLOAT(a, 0);
+ SETFLOAT(a+1, GLIST_DEFCANVASYLOC);
+ SETFLOAT(a+2, GLIST_DEFCANVASWIDTH);
+ SETFLOAT(a+3, GLIST_DEFCANVASHEIGHT);
+ SETSYMBOL(a+4, s);
+ SETFLOAT(a+5, 1);
+ x = canvas_new(0, 0, 6, a);
+ x->gl_owner = z;
+ canvas_pop(x, 1);
+ return (x);
+}
+
+static void canvas_click(t_canvas *x,
+ t_floatarg xpos, t_floatarg ypos,
+ t_floatarg shift, t_floatarg ctrl, t_floatarg alt)
+{
+ canvas_vis(x, 1);
+}
+
+
+ /* find out from subcanvas contents how much to fatten the box */
+void canvas_fattensub(t_canvas *x,
+ int *xp1, int *yp1, int *xp2, int *yp2)
+{
+ t_gobj *y;
+ *xp2 += 50; /* fake for now */
+ *yp2 += 50;
+}
+
+static void canvas_rename_method(t_canvas *x, t_symbol *s, int ac, t_atom *av)
+{
+ if (ac && av->a_type == A_SYMBOL)
+ canvas_rename(x, av->a_w.w_symbol, 0);
+ else canvas_rename(x, gensym("Pd"), 0);
+}
+
+/* ------------------ table ---------------------------*/
+
+static int tabcount = 0;
+
+static void *table_new(t_symbol *s, t_floatarg f)
+{
+ t_atom a[9];
+ t_glist *gl;
+ t_canvas *x, *z = canvas_getcurrent();
+ if (s == &s_)
+ {
+ char tabname[255];
+ t_symbol *t = gensym("table");
+ sprintf(tabname, "%s%d", t->s_name, tabcount++);
+ s = gensym(tabname);
+ }
+ if (f <= 1)
+ f = 100;
+ SETFLOAT(a, 0);
+ SETFLOAT(a+1, GLIST_DEFCANVASYLOC);
+ SETFLOAT(a+2, 600);
+ SETFLOAT(a+3, 400);
+ SETSYMBOL(a+4, s);
+ SETFLOAT(a+5, 0);
+ x = canvas_new(0, 0, 6, a);
+
+ x->gl_owner = z;
+
+ /* create a graph for the table */
+ gl = glist_addglist((t_glist*)x, &s_, 0, -1, (f > 1 ? f-1 : 1), 1,
+ 50, 350, 550, 50);
+
+ graph_array(gl, s, &s_float, f, 0);
+
+ canvas_pop(x, 0);
+
+ return (x);
+}
+
+ /* return true if the "canvas" object is an abstraction (so we don't
+ save its contents, fogr example.) */
+int canvas_isabstraction(t_canvas *x)
+{
+ return (x->gl_env != 0);
+}
+
+ /* return true if the "canvas" object is a "table". */
+int canvas_istable(t_canvas *x)
+{
+ t_atom *argv = (x->gl_obj.te_binbuf? binbuf_getvec(x->gl_obj.te_binbuf):0);
+ int argc = (x->gl_obj.te_binbuf? binbuf_getnatom(x->gl_obj.te_binbuf) : 0);
+ int istable = (argc && argv[0].a_type == A_SYMBOL &&
+ argv[0].a_w.w_symbol == gensym("table"));
+ return (istable);
+}
+
+ /* return true if the "canvas" object should be treated as a text
+ object. This is true for abstractions but also for "table"s... */
+int canvas_showtext(t_canvas *x)
+{
+ t_atom *argv = (x->gl_obj.te_binbuf? binbuf_getvec(x->gl_obj.te_binbuf):0);
+ int argc = (x->gl_obj.te_binbuf? binbuf_getnatom(x->gl_obj.te_binbuf) : 0);
+ int isarray = (argc && argv[0].a_type == A_SYMBOL &&
+ argv[0].a_w.w_symbol == gensym("graph"));
+ return (!isarray);
+}
+
+static void canvas_dodsp(t_canvas *x, int toplevel, t_signal **sp);
+static void canvas_dsp(t_canvas *x, t_signal **sp)
+{
+ canvas_dodsp(x, 0, sp);
+}
+
+ /* get the document containing this canvas */
+t_canvas *canvas_getrootfor(t_canvas *x)
+{
+ if ((!x->gl_owner) || canvas_isabstraction(x))
+ return (x);
+ else return (canvas_getrootfor(x->gl_owner));
+}
+
+/* ------------------------- DSP chain handling ------------------------- */
+
+EXTERN_STRUCT _dspcontext;
+#define t_dspcontext struct _dspcontext
+
+void ugen_start(void);
+void ugen_stop(void);
+
+t_dspcontext *ugen_start_graph(int toplevel, t_signal **sp,
+ int ninlets, int noutlets);
+void ugen_add(t_dspcontext *dc, t_object *x);
+void ugen_connect(t_dspcontext *dc, t_object *x1, int outno,
+ t_object *x2, int inno);
+void ugen_done_graph(t_dspcontext *dc);
+
+int obj_issignaloutlet(t_object *x, int outno);
+int obj_nsiginlets(t_object *x);
+int obj_nsigoutlets(t_object *x);
+
+ /* schedule one canvas for DSP. This is called below for all "root"
+ canvases, but is also called from the "dsp" method for sub-
+ canvases, which are treated almost like any other tilde object. */
+
+static void canvas_dodsp(t_canvas *x, int toplevel, t_signal **sp)
+{
+ t_linetraverser t;
+ t_outconnect *oc;
+ t_gobj *y;
+ t_object *ob;
+ t_symbol *dspsym = gensym("dsp");
+ t_dspcontext *dc;
+
+ /* create a new "DSP graph" object to use in sorting this canvas.
+ If we aren't toplevel, there are already other dspcontexts around. */
+
+ dc = ugen_start_graph(toplevel, sp,
+ obj_nsiginlets(&x->gl_obj),
+ obj_nsigoutlets(&x->gl_obj));
+
+ /* find all the "dsp" boxes and add them to the graph */
+
+ for (y = x->gl_list; y; y = y->g_next)
+ if ((ob = pd_checkobject(&y->g_pd)) && zgetfn(&y->g_pd, dspsym))
+ ugen_add(dc, ob);
+
+ /* ... and all dsp interconnections */
+ linetraverser_start(&t, x);
+ while (oc = linetraverser_next(&t))
+ if (obj_issignaloutlet(t.tr_ob, t.tr_outno))
+ ugen_connect(dc, t.tr_ob, t.tr_outno, t.tr_ob2, t.tr_inno);
+
+ /* finally, sort them and add them to the DSP chain */
+ ugen_done_graph(dc);
+}
+
+ /* this routine starts DSP for all root canvases. */
+static void canvas_start_dsp(void)
+{
+ t_canvas *x;
+ if (canvas_dspstate) ugen_stop();
+ else sys_gui("pdtk_pd_dsp ON\n");
+ ugen_start();
+
+ for (x = canvas_list; x; x = x->gl_next)
+ canvas_dodsp(x, 1, 0);
+
+ canvas_dspstate = 1;
+}
+
+static void canvas_stop_dsp(void)
+{
+ if (canvas_dspstate)
+ {
+ ugen_stop();
+ sys_gui("pdtk_pd_dsp OFF\n");
+ canvas_dspstate = 0;
+ }
+}
+
+ /* DSP can be suspended before, and resumed after, operations which
+ might affect the DSP chain. For example, we suspend before loading and
+ resume afterward, so that DSP doesn't get resorted for every DSP object
+ int the patch. */
+
+int canvas_suspend_dsp(void)
+{
+ int rval = canvas_dspstate;
+ if (rval) canvas_stop_dsp();
+ return (rval);
+}
+
+void canvas_resume_dsp(int oldstate)
+{
+ if (oldstate) canvas_start_dsp();
+}
+
+ /* this is equivalent to suspending and resuming in one step. */
+void canvas_update_dsp(void)
+{
+ if (canvas_dspstate) canvas_start_dsp();
+}
+
+void glob_dsp(void *dummy, t_symbol *s, int argc, t_atom *argv)
+{
+ int newstate;
+ if (argc)
+ {
+ newstate = atom_getintarg(0, argc, argv);
+ if (newstate && !canvas_dspstate)
+ canvas_start_dsp();
+ else if (!newstate && canvas_dspstate)
+ canvas_stop_dsp();
+ }
+ else post("dsp state %d", canvas_dspstate);
+}
+
+ /* LATER replace this with a queueing scheme */
+void glist_redrawitem(t_glist *owner, t_gobj *gobj)
+{
+ if (glist_isvisible(owner))
+ {
+ gobj_vis(gobj, owner, 0);
+ gobj_vis(gobj, owner, 1);
+ }
+}
+
+ /* redraw all "scalars" (do this if a drawing command is changed.)
+ LATER we'll use the "template" information to select which ones we
+ redraw. */
+static void glist_redrawall(t_glist *gl)
+{
+ t_gobj *g;
+ int vis = glist_isvisible(gl);
+ for (g = gl->gl_list; g; g = g->g_next)
+ {
+ t_class *cl;
+ if (vis && g->g_pd == scalar_class)
+ glist_redrawitem(gl, g);
+ else if (g->g_pd == canvas_class)
+ glist_redrawall((t_glist *)g);
+ }
+}
+
+ /* public interface for above */
+void canvas_redrawallfortemplate(t_canvas *template)
+{
+ t_canvas *x;
+ if (!template->gl_imatemplate) return;
+ /* find all root canvases */
+ for (x = canvas_list; x; x = x->gl_next)
+ glist_redrawall(x);
+}
+
+ /* Same as above but just zap them. Call this if a template
+ is changed by adding or removing a field. LATER we'll just
+ modify all the items appropriately. */
+static void glist_zapall(t_glist *gl)
+{
+ t_gobj *g;
+ for (g = gl->gl_list; g; g = g->g_next)
+ {
+ t_class *cl;
+ if (g->g_pd == canvas_class)
+ glist_zapall((t_glist *)g);
+ }
+ /* do we have any scalars? */
+ for (g = gl->gl_list; g; g = g->g_next)
+ {
+ if (g->g_pd == scalar_class)
+ break;
+ }
+ if (!g) return;
+ /* delete all the scalars. This is inefficient if for some reason
+ you've mixed scalars with other items in a single glist. */
+ while (1)
+ {
+ for (g = gl->gl_list; g; g = g->g_next)
+ {
+ if (g->g_pd == scalar_class)
+ {
+ glist_delete(gl, g);
+ break;
+ }
+ }
+ if (!g) break;
+ }
+}
+
+ /* public interface for above */
+void canvas_zapallfortemplate(t_canvas *template)
+{
+ t_canvas *x;
+ if (!template->gl_imatemplate) return;
+ /* find all root canvases */
+ for (x = canvas_list; x; x = x->gl_next)
+ glist_zapall(x);
+}
+
+ /* warn a canvas that some datum has used it as a template. If a
+ canvas has no data associated with it (at load time, for instance)
+ we don't have to search through the world for instances as it changes. */
+void canvas_setusedastemplate(t_canvas *x)
+{
+ x->gl_imatemplate = 1;
+}
+
+/* ------------------------------- setup routine ------------------------ */
+
+ /* why are some of these "glist" and others "canvas"? */
+extern void glist_text(t_glist *x, t_symbol *s, int argc, t_atom *argv);
+extern void canvas_obj(t_glist *gl, t_symbol *s, int argc, t_atom *argv);
+extern void canvas_bng(t_glist *gl, t_symbol *s, int argc, t_atom *argv);
+extern void canvas_toggle(t_glist *gl, t_symbol *s, int argc, t_atom *argv);
+extern void canvas_vslider(t_glist *gl, t_symbol *s, int argc, t_atom *argv);
+extern void canvas_hslider(t_glist *gl, t_symbol *s, int argc, t_atom *argv);
+extern void canvas_vdial(t_glist *gl, t_symbol *s, int argc, t_atom *argv);
+extern void canvas_hdial(t_glist *gl, t_symbol *s, int argc, t_atom *argv);
+extern void canvas_vumeter(t_glist *gl, t_symbol *s, int argc, t_atom *argv);
+extern void canvas_mycnv(t_glist *gl, t_symbol *s, int argc, t_atom *argv);
+extern void canvas_numbox(t_glist *gl, t_symbol *s, int argc, t_atom *argv);
+extern void canvas_msg(t_glist *gl, t_symbol *s, int argc, t_atom *argv);
+extern void canvas_floatatom(t_glist *gl, t_symbol *s, int argc, t_atom *argv);
+extern void canvas_symbolatom(t_glist *gl, t_symbol *s, int argc, t_atom *argv);
+extern void glist_scalar(t_glist *canvas, t_symbol *s, int argc, t_atom *argv);
+
+void g_graph_setup(void);
+void g_editor_setup(void);
+void g_readwrite_setup(void);
+
+void g_canvas_setup(void)
+{
+ /* we prevent the user from typing "canvas" in an object box
+ by sending 0 for a creator function. */
+ canvas_class = class_new(gensym("canvas"), 0,
+ (t_method)canvas_free, sizeof(t_canvas), CLASS_NOINLET, 0);
+ /* here is the real creator function, invoked in patch files
+ by sending the "canvas" message to #N, which is bound
+ to pd_camvasmaker. */
+ class_addmethod(pd_canvasmaker, (t_method)canvas_new, gensym("canvas"),
+ A_GIMME, 0);
+ class_addmethod(canvas_class, (t_method)canvas_restore,
+ gensym("restore"), A_GIMME, 0);
+ class_addmethod(canvas_class, (t_method)canvas_coords,
+ gensym("coords"), A_GIMME, 0);
+
+/* -------------------------- objects ----------------------------- */
+ class_addmethod(canvas_class, (t_method)canvas_obj,
+ gensym("obj"), A_GIMME, A_NULL);
+ class_addmethod(canvas_class, (t_method)canvas_msg,
+ gensym("msg"), A_GIMME, A_NULL);
+ class_addmethod(canvas_class, (t_method)canvas_floatatom,
+ gensym("floatatom"), A_GIMME, A_NULL);
+ class_addmethod(canvas_class, (t_method)canvas_symbolatom,
+ gensym("symbolatom"), A_GIMME, A_NULL);
+ class_addmethod(canvas_class, (t_method)glist_text,
+ gensym("text"), A_GIMME, A_NULL);
+ class_addmethod(canvas_class, (t_method)glist_glist, gensym("graph"),
+ A_GIMME, A_NULL);
+ class_addmethod(canvas_class, (t_method)glist_scalar,
+ gensym("scalar"), A_GIMME, A_NULL);
+
+ /* -------------- Thomas Musil's GUI objects ------------ */
+ class_addmethod(canvas_class, (t_method)canvas_bng, gensym("bng"),
+ A_GIMME, A_NULL);
+ class_addmethod(canvas_class, (t_method)canvas_toggle, gensym("toggle"),
+ A_GIMME, A_NULL);
+ class_addmethod(canvas_class, (t_method)canvas_vslider, gensym("vslider"),
+ A_GIMME, A_NULL);
+ class_addmethod(canvas_class, (t_method)canvas_hslider, gensym("hslider"),
+ A_GIMME, A_NULL);
+ class_addmethod(canvas_class, (t_method)canvas_hdial, gensym("hdial"),
+ A_GIMME, A_NULL);
+ class_addmethod(canvas_class, (t_method)canvas_vdial, gensym("vdial"),
+ A_GIMME, A_NULL);
+ class_addmethod(canvas_class, (t_method)canvas_vumeter, gensym("vumeter"),
+ A_GIMME, A_NULL);
+ class_addmethod(canvas_class, (t_method)canvas_mycnv, gensym("mycnv"),
+ A_GIMME, A_NULL);
+ class_addmethod(canvas_class, (t_method)canvas_numbox, gensym("numbox"),
+ A_GIMME, A_NULL);
+
+/* ------------------------ gui stuff --------------------------- */
+ class_addmethod(canvas_class, (t_method)canvas_pop, gensym("pop"),
+ A_DEFFLOAT, A_NULL);
+ class_addmethod(canvas_class, (t_method)canvas_loadbang,
+ gensym("loadbang"), A_NULL);
+ class_addmethod(canvas_class, (t_method)canvas_relocate,
+ gensym("relocate"), A_SYMBOL, A_SYMBOL, A_NULL);
+ class_addmethod(canvas_class, (t_method)canvas_vis,
+ gensym("vis"), A_FLOAT, A_NULL);
+ class_addmethod(canvas_class, (t_method)glist_menu_open,
+ gensym("menu-open"), A_NULL);
+ class_addmethod(canvas_class, (t_method)canvas_map,
+ gensym("map"), A_FLOAT, A_NULL);
+
+/* ---------------------- list handling ------------------------ */
+ class_addmethod(canvas_class, (t_method)glist_clear, gensym("clear"),
+ A_NULL);
+
+/* ----- subcanvases, which you get by typing "pd" in a box ---- */
+ class_addcreator((t_newmethod)subcanvas_new, gensym("pd"), A_DEFSYMBOL, 0);
+ class_addcreator((t_newmethod)subcanvas_new, gensym("page"), A_DEFSYMBOL, 0);
+
+ class_addmethod(canvas_class, (t_method)canvas_click,
+ gensym("click"), A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, 0);
+ class_addmethod(canvas_class, (t_method)canvas_dsp, gensym("dsp"), 0);
+ class_addmethod(canvas_class, (t_method)canvas_rename_method,
+ gensym("rename"), A_GIMME, 0);
+
+/*---------------------------- tables -- GG ------------------- */
+
+ class_addcreator((t_newmethod)table_new, gensym("table"),
+ A_DEFSYM, A_DEFFLOAT, 0);
+
+/* -------------- setups from other files for canvas_class ---------------- */
+ g_graph_setup();
+ g_editor_setup();
+ g_readwrite_setup();
+
+}