summaryrefslogtreecommitdiff
path: root/apps/plugins/pdbox/PDa/src/g_canvas.c
diff options
context:
space:
mode:
Diffstat (limited to 'apps/plugins/pdbox/PDa/src/g_canvas.c')
-rw-r--r--apps/plugins/pdbox/PDa/src/g_canvas.c2952
1 files changed, 2952 insertions, 0 deletions
diff --git a/apps/plugins/pdbox/PDa/src/g_canvas.c b/apps/plugins/pdbox/PDa/src/g_canvas.c
new file mode 100644
index 0000000000..f4ef8b14aa
--- /dev/null
+++ b/apps/plugins/pdbox/PDa/src/g_canvas.c
@@ -0,0 +1,2952 @@
1/* Copyright (c) 1997-2001 Miller Puckette and others.
2* For information on usage and redistribution, and for a DISCLAIMER OF ALL
3* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */
4
5/* this file defines the "glist" class, also known as "canvas" (the two used
6to be different but are now unified except for some fossilized names.) */
7
8/* changes by Thomas Musil IEM KUG Graz Austria 2001 */
9
10/* bug-fix: canvas_menuclose(): by Krzysztof Czaja */
11/* bug-fix: table_new(): I reversed the y-bounds */
12
13/* IOhannes :
14 * changed the canvas_restore, so that it might accept $args as well
15 * (like "pd $0_test")
16 * so you can make multiple & distinguishable templates
17 * 1511:forum::für::umläute:2001
18 * changes marked with IOhannes
19 */
20
21#include <stdlib.h>
22#include <stdio.h>
23#include "m_pd.h"
24#include "m_imp.h"
25#include "s_stuff.h"
26#include "g_canvas.h"
27#include <string.h>
28#include "g_all_guis.h"
29
30struct _canvasenvironment
31{
32 t_symbol *ce_dir; /* directory patch lives in */
33 int ce_argc; /* number of "$" arguments */
34 t_atom *ce_argv; /* array of "$" arguments */
35 int ce_dollarzero; /* value of "$0" */
36};
37
38#define GLIST_DEFCANVASWIDTH 240
39#define GLIST_DEFCANVASHEIGHT 300
40
41#ifdef MACOSX
42#define GLIST_DEFCANVASYLOC 22
43#else
44#define GLIST_DEFCANVASYLOC 0
45#endif
46
47/* ---------------------- variables --------------------------- */
48
49extern t_pd *newest;
50t_class *canvas_class;
51static int canvas_dspstate; /* whether DSP is on or off */
52t_canvas *canvas_editing; /* last canvas to start text edting */
53t_canvas *canvas_whichfind; /* last canvas we did a find in */
54t_canvas *canvas_list; /* list of all root canvases */
55
56/* ------------------ forward function declarations --------------- */
57static void canvas_start_dsp(void);
58static void canvas_stop_dsp(void);
59static void canvas_drawlines(t_canvas *x);
60static void canvas_setbounds(t_canvas *x, int x1, int y1, int x2, int y2);
61static void canvas_reflecttitle(t_canvas *x);
62static void canvas_addtolist(t_canvas *x);
63static void canvas_takeofflist(t_canvas *x);
64static void canvas_pop(t_canvas *x, t_floatarg fvis);
65void canvas_create_editor(t_glist *x, int createit);
66
67/* --------- functions to handle the canvas environment ----------- */
68
69static t_symbol *canvas_newfilename = &s_;
70static t_symbol *canvas_newdirectory = &s_;
71static int canvas_newargc;
72static t_atom *canvas_newargv;
73
74static void glist_doupdatewindowlist(t_glist *gl, char *sbuf)
75{
76 t_gobj *g;
77 if (!gl->gl_owner)
78 {
79 /* this is a canvas; if we have a window, put on "windows" list */
80 t_canvas *canvas = (t_canvas *)gl;
81 if (canvas->gl_havewindow)
82 {
83 if (strlen(sbuf) + strlen(gl->gl_name->s_name) + 100 <= 1024)
84 {
85 char tbuf[1024];
86 sprintf(tbuf, "{%s .x%x} ", gl->gl_name->s_name, (t_int)canvas);
87 strcat(sbuf, tbuf);
88 }
89 }
90 }
91 for (g = gl->gl_list; g; g = g->g_next)
92 {
93 if (pd_class(&g->g_pd) == canvas_class)
94 glist_doupdatewindowlist((t_glist *)g, sbuf);
95 }
96 return;
97}
98
99 /* maintain the list of visible toplevels for the GUI's "windows" menu */
100void canvas_updatewindowlist( void)
101{
102 t_canvas *x;
103 char sbuf[1024];
104 strcpy(sbuf, "set menu_windowlist {");
105 /* find all root canvases */
106 for (x = canvas_list; x; x = x->gl_next)
107 glist_doupdatewindowlist(x, sbuf);
108 /* next line updates the window menu state before -postcommand tries it */
109 strcat(sbuf, "}\npdtk_fixwindowmenu\n");
110 sys_gui(sbuf);
111}
112
113 /* add a glist the list of "root" canvases (toplevels without parents.) */
114static void canvas_addtolist(t_canvas *x)
115{
116 x->gl_next = canvas_list;
117 canvas_list = x;
118}
119
120static void canvas_takeofflist(t_canvas *x)
121{
122 /* take it off the window list */
123 if (x == canvas_list) canvas_list = x->gl_next;
124 else
125 {
126 t_canvas *z;
127 for (z = canvas_list; z->gl_next != x; z = z->gl_next)
128 ;
129 z->gl_next = x->gl_next;
130 }
131}
132
133
134void canvas_setargs(int argc, t_atom *argv)
135{
136 /* if there's an old one lying around free it here. This
137 happens if an abstraction is loaded but never gets as far
138 as calling canvas_new(). */
139 if (canvas_newargv)
140 freebytes(canvas_newargv, canvas_newargc * sizeof(t_atom));
141 canvas_newargc = argc;
142 canvas_newargv = copybytes(argv, argc * sizeof(t_atom));
143}
144
145void glob_setfilename(void *dummy, t_symbol *filesym, t_symbol *dirsym)
146{
147 canvas_newfilename = filesym;
148 canvas_newdirectory = dirsym;
149}
150
151t_canvas *canvas_getcurrent(void)
152{
153 return ((t_canvas *)pd_findbyclass(&s__X, canvas_class));
154}
155
156void canvas_setcurrent(t_canvas *x)
157{
158 pd_pushsym(&x->gl_pd);
159}
160
161void canvas_unsetcurrent(t_canvas *x)
162{
163 pd_popsym(&x->gl_pd);
164}
165
166t_canvasenvironment *canvas_getenv(t_canvas *x)
167{
168 if (!x) bug("canvas_getenv");
169 while (!x->gl_env)
170 if (!(x = x->gl_owner))
171 bug("t_canvasenvironment", x);
172 return (x->gl_env);
173}
174
175int canvas_getdollarzero( void)
176{
177 t_canvas *x = canvas_getcurrent();
178 t_canvasenvironment *env = (x ? canvas_getenv(x) : 0);
179 if (env)
180 return (env->ce_dollarzero);
181 else return (0);
182}
183
184void canvas_getargs(int *argcp, t_atom **argvp)
185{
186 t_canvasenvironment *e = canvas_getenv(canvas_getcurrent());
187 *argcp = e->ce_argc;
188 *argvp = e->ce_argv;
189}
190
191t_symbol *canvas_realizedollar(t_canvas *x, t_symbol *s)
192{
193 t_symbol *ret;
194 char *name = s->s_name;
195 if (*name == '$' && name[1] >= '0' && name[1] <= '9')
196 {
197 t_canvasenvironment *env = canvas_getenv(x);
198 canvas_setcurrent(x);
199 ret = binbuf_realizedollsym(gensym(name+1),
200 env->ce_argc, env->ce_argv, 1);
201 canvas_unsetcurrent(x);
202 }
203 else ret = s;
204 return (ret);
205}
206
207t_symbol *canvas_getcurrentdir(void)
208{
209 t_canvasenvironment *e = canvas_getenv(canvas_getcurrent());
210 return (e->ce_dir);
211}
212
213t_symbol *canvas_getdir(t_canvas *x)
214{
215 t_canvasenvironment *e = canvas_getenv(x);
216 return (e->ce_dir);
217}
218
219void canvas_makefilename(t_canvas *x, char *file, char *result, int resultsize)
220{
221 char *dir = canvas_getenv(x)->ce_dir->s_name;
222 if (file[0] == '/' || (file[0] && file[1] == ':') || !*dir)
223 {
224 strncpy(result, file, resultsize);
225 result[resultsize-1] = 0;
226 }
227 else
228 {
229 int nleft;
230 strncpy(result, dir, resultsize);
231 result[resultsize-1] = 0;
232 nleft = resultsize - strlen(result) - 1;
233 if (nleft <= 0) return;
234 strcat(result, "/");
235 strncat(result, file, nleft);
236 result[resultsize-1] = 0;
237 }
238}
239
240void canvas_rename(t_canvas *x, t_symbol *s, t_symbol *dir)
241{
242 if (strcmp(x->gl_name->s_name, "Pd"))
243 pd_unbind(&x->gl_pd, canvas_makebindsym(x->gl_name));
244 x->gl_name = s;
245 if (strcmp(x->gl_name->s_name, "Pd"))
246 pd_bind(&x->gl_pd, canvas_makebindsym(x->gl_name));
247 if (glist_isvisible(x))
248 canvas_reflecttitle(x);
249 if (dir && dir != &s_)
250 {
251 t_canvasenvironment *e = canvas_getenv(x);
252 e->ce_dir = dir;
253 }
254}
255
256/* --------------- traversing the set of lines in a canvas ----------- */
257
258int canvas_getindex(t_canvas *x, t_gobj *y)
259{
260 t_gobj *y2;
261 int indexno;
262 for (indexno = 0, y2 = x->gl_list; y2 && y2 != y; y2 = y2->g_next)
263 indexno++;
264 return (indexno);
265}
266
267void linetraverser_start(t_linetraverser *t, t_canvas *x)
268{
269 t->tr_ob = 0;
270 t->tr_x = x;
271 t->tr_nextoc = 0;
272 t->tr_nextoutno = t->tr_nout = 0;
273}
274
275t_outconnect *linetraverser_next(t_linetraverser *t)
276{
277 t_outconnect *rval = t->tr_nextoc;
278 int outno;
279 while (!rval)
280 {
281 outno = t->tr_nextoutno;
282 while (outno == t->tr_nout)
283 {
284 t_gobj *y;
285 t_object *ob = 0;
286 if (!t->tr_ob) y = t->tr_x->gl_list;
287 else y = t->tr_ob->ob_g.g_next;
288 for (; y; y = y->g_next)
289 if (ob = pd_checkobject(&y->g_pd)) break;
290 if (!ob) return (0);
291 t->tr_ob = ob;
292 t->tr_nout = obj_noutlets(ob);
293 outno = 0;
294 if (glist_isvisible(t->tr_x))
295 gobj_getrect(y, t->tr_x,
296 &t->tr_x11, &t->tr_y11, &t->tr_x12, &t->tr_y12);
297 else t->tr_x11 = t->tr_y11 = t->tr_x12 = t->tr_y12 = 0;
298 }
299 t->tr_nextoutno = outno + 1;
300 rval = obj_starttraverseoutlet(t->tr_ob, &t->tr_outlet, outno);
301 t->tr_outno = outno;
302 }
303 t->tr_nextoc = obj_nexttraverseoutlet(rval, &t->tr_ob2,
304 &t->tr_inlet, &t->tr_inno);
305 t->tr_nin = obj_ninlets(t->tr_ob2);
306 if (!t->tr_nin) bug("drawline");
307 if (glist_isvisible(t->tr_x))
308 {
309 int inplus = (t->tr_nin == 1 ? 1 : t->tr_nin - 1);
310 int outplus = (t->tr_nout == 1 ? 1 : t->tr_nout - 1);
311 gobj_getrect(&t->tr_ob2->ob_g, t->tr_x,
312 &t->tr_x21, &t->tr_y21, &t->tr_x22, &t->tr_y22);
313 t->tr_lx1 = t->tr_x11 +
314 ((t->tr_x12 - t->tr_x11 - IOWIDTH) * t->tr_outno) /
315 outplus + IOMIDDLE;
316 t->tr_ly1 = t->tr_y12;
317 t->tr_lx2 = t->tr_x21 +
318 ((t->tr_x22 - t->tr_x21 - IOWIDTH) * t->tr_inno)/inplus +
319 IOMIDDLE;
320 t->tr_ly2 = t->tr_y21;
321 }
322 else
323 {
324 t->tr_x21 = t->tr_y21 = t->tr_x22 = t->tr_y22 = 0;
325 t->tr_lx1 = t->tr_ly1 = t->tr_lx2 = t->tr_ly2 = 0;
326 }
327
328 return (rval);
329}
330
331void linetraverser_skipobject(t_linetraverser *t)
332{
333 t->tr_nextoc = 0;
334 t->tr_nextoutno = t->tr_nout;
335}
336
337/* -------------------- the canvas object -------------------------- */
338int glist_valid = 10000;
339
340void glist_init(t_glist *x)
341{
342 /* zero out everyone except "pd" field */
343 memset(((char *)x) + sizeof(x->gl_pd), 0, sizeof(*x) - sizeof(x->gl_pd));
344 x->gl_stub = gstub_new(x, 0);
345 x->gl_valid = ++glist_valid;
346 x->gl_xlabel = (t_symbol **)t_getbytes(0);
347 x->gl_ylabel = (t_symbol **)t_getbytes(0);
348}
349
350 /* make a new glist. It will either be a "root" canvas or else
351 its parent will be a "text" object in another window... we don't
352 know which yet. */
353t_canvas *canvas_new(void *dummy, t_symbol *sel, int argc, t_atom *argv)
354{
355 t_canvas *x = (t_canvas *)pd_new(canvas_class);
356 t_canvas *owner = canvas_getcurrent();
357 t_symbol *s = &s_;
358 int vis = 0, width = GLIST_DEFCANVASWIDTH, height = GLIST_DEFCANVASHEIGHT;
359 int xloc = 0, yloc = GLIST_DEFCANVASYLOC;
360 int font = (owner ? owner->gl_font : sys_defaultfont);
361 glist_init(x);
362 x->gl_obj.te_type = T_OBJECT;
363 if (!owner)
364 canvas_addtolist(x);
365 /* post("canvas %x, owner %x", x, owner); */
366
367 if (argc == 5) /* toplevel: x, y, w, h, font */
368 {
369 xloc = atom_getintarg(0, argc, argv);
370 yloc = atom_getintarg(1, argc, argv);
371 width = atom_getintarg(2, argc, argv);
372 height = atom_getintarg(3, argc, argv);
373 font = atom_getintarg(4, argc, argv);
374 }
375 else if (argc == 6) /* subwindow: x, y, w, h, name, vis */
376 {
377 xloc = atom_getintarg(0, argc, argv);
378 yloc = atom_getintarg(1, argc, argv);
379 width = atom_getintarg(2, argc, argv);
380 height = atom_getintarg(3, argc, argv);
381 s = atom_getsymbolarg(4, argc, argv);
382 vis = atom_getintarg(5, argc, argv);
383 }
384 /* (otherwise assume we're being created from the menu.) */
385
386 if (canvas_newdirectory->s_name[0])
387 {
388 static int dollarzero = 1000;
389 t_canvasenvironment *env = x->gl_env =
390 (t_canvasenvironment *)getbytes(sizeof(*x->gl_env));
391 env->ce_dir = canvas_newdirectory;
392 env->ce_argc = canvas_newargc;
393 env->ce_argv = canvas_newargv;
394 env->ce_dollarzero = dollarzero++;
395 canvas_newdirectory = &s_;
396 canvas_newargc = 0;
397 canvas_newargv = 0;
398 }
399 else x->gl_env = 0;
400
401 if (yloc < GLIST_DEFCANVASYLOC)
402 yloc = GLIST_DEFCANVASYLOC;
403 if (xloc < 0)
404 xloc = 0;
405 x->gl_x1 = 0;
406 x->gl_y1 = 0;
407 x->gl_x2 = 1;
408 x->gl_y2 = 1;
409 canvas_setbounds(x, xloc, yloc, xloc + width, yloc + height);
410 x->gl_owner = owner;
411 x->gl_name = (*s->s_name ? s :
412 (canvas_newfilename ? canvas_newfilename : gensym("Pd")));
413 if (strcmp(x->gl_name->s_name, "Pd"))
414 pd_bind(&x->gl_pd, canvas_makebindsym(x->gl_name));
415 x->gl_loading = 1;
416 x->gl_willvis = vis;
417 x->gl_edit = !strncmp(x->gl_name->s_name, "Untitled", 8);
418 x->gl_font = sys_nearestfontsize(font);
419 pd_pushsym(&x->gl_pd);
420 return(x);
421}
422
423void canvas_setgraph(t_glist *x, int flag);
424
425static void canvas_coords(t_glist *x, t_symbol *s, int argc, t_atom *argv)
426{
427 x->gl_x1 = atom_getfloatarg(0, argc, argv);
428 x->gl_y1 = atom_getfloatarg(1, argc, argv);
429 x->gl_x2 = atom_getfloatarg(2, argc, argv);
430 x->gl_y2 = atom_getfloatarg(3, argc, argv);
431 x->gl_pixwidth = atom_getintarg(4, argc, argv);
432 x->gl_pixheight = atom_getintarg(5, argc, argv);
433 canvas_setgraph(x, atom_getintarg(6, argc, argv));
434}
435
436 /* make a new glist and add it to this glist. It will appear as
437 a "graph", not a text object. */
438t_glist *glist_addglist(t_glist *g, t_symbol *sym,
439 float x1, float y1, float x2, float y2,
440 float px1, float py1, float px2, float py2)
441{
442 static int gcount = 0;
443 int zz;
444 int menu = 0;
445 char *str;
446 t_glist *x = (t_glist *)pd_new(canvas_class);
447 glist_init(x);
448 x->gl_obj.te_type = T_OBJECT;
449 if (!*sym->s_name)
450 {
451 char buf[40];
452 sprintf(buf, "graph%d", ++gcount);
453 sym = gensym(buf);
454 menu = 1;
455 }
456 else if (!strncmp((str = sym->s_name), "graph", 5)
457 && (zz = atoi(str + 5)) > gcount)
458 gcount = zz;
459 /* in 0.34 and earlier, the pixel rectangle and the y bounds were
460 reversed; this would behave the same, except that the dialog window
461 would be confusing. The "correct" way is to have "py1" be the value
462 that is higher on the screen. */
463 if (py2 < py1)
464 {
465 float zz;
466 zz = y2;
467 y2 = y1;
468 y1 = zz;
469 zz = py2;
470 py2 = py1;
471 py1 = zz;
472 }
473 if (x1 == x2 || y1 == y2)
474 x1 = 0, x2 = 100, y1 = 1, y2 = -1;
475 if (px1 >= px2 || py1 >= py2)
476 px1 = 100, py1 = 20, px2 = 100 + GLIST_DEFGRAPHWIDTH,
477 py2 = 20 + GLIST_DEFGRAPHHEIGHT;
478 x->gl_name = sym;
479 x->gl_x1 = x1;
480 x->gl_x2 = x2;
481 x->gl_y1 = y1;
482 x->gl_y2 = y2;
483 x->gl_obj.te_xpix = px1;
484 x->gl_obj.te_ypix = py1;
485 x->gl_pixwidth = px2 - px1;
486 x->gl_pixheight = py2 - py1;
487 x->gl_font = (canvas_getcurrent() ?
488 canvas_getcurrent()->gl_font : sys_defaultfont);
489 x->gl_screenx1 = x->gl_screeny1 = 0;
490 x->gl_screenx2 = 240;
491 x->gl_screeny2 = 300;
492 if (strcmp(x->gl_name->s_name, "Pd"))
493 pd_bind(&x->gl_pd, canvas_makebindsym(x->gl_name));
494 x->gl_owner = g;
495 x->gl_stretch = 1;
496 x->gl_isgraph = 1;
497 x->gl_obj.te_binbuf = binbuf_new();
498 binbuf_addv(x->gl_obj.te_binbuf, "s", gensym("graph"));
499 if (!menu)
500 pd_pushsym(&x->gl_pd);
501 glist_add(g, &x->gl_gobj);
502 if (glist_isvisible(g))
503 canvas_create_editor(x, 1);
504 return (x);
505}
506
507 /* call glist_addglist from a Pd message */
508void glist_glist(t_glist *g, t_symbol *s, int argc, t_atom *argv)
509{
510 t_symbol *sym = atom_getsymbolarg(0, argc, argv);
511 float x1 = atom_getfloatarg(1, argc, argv);
512 float y1 = atom_getfloatarg(2, argc, argv);
513 float x2 = atom_getfloatarg(3, argc, argv);
514 float y2 = atom_getfloatarg(4, argc, argv);
515 float px1 = atom_getfloatarg(5, argc, argv);
516 float py1 = atom_getfloatarg(6, argc, argv);
517 float px2 = atom_getfloatarg(7, argc, argv);
518 float py2 = atom_getfloatarg(8, argc, argv);
519 glist_addglist(g, sym, x1, y1, x2, y2, px1, py1, px2, py2);
520}
521
522 /* return true if the glist should appear as a graph on parent;
523 otherwise it appears as a text box. */
524int glist_isgraph(t_glist *x)
525{
526 return (x->gl_isgraph);
527}
528
529 /* This is sent from the GUI to inform a toplevel that its window has been
530 moved or resized. */
531static void canvas_setbounds(t_canvas *x, int x1, int y1, int x2, int y2)
532{
533 int heightwas = y2 - y1;
534 int heightchange = y2 - y1 - (x->gl_screeny2 - x->gl_screeny1);
535 x->gl_screenx1 = x1;
536 x->gl_screeny1 = y1;
537 x->gl_screenx2 = x2;
538 x->gl_screeny2 = y2;
539 /* post("set bounds %d %d %d %d", x1, y1, x2, y2); */
540 if (!glist_isgraph(x) && (x->gl_y2 < x->gl_y1))
541 {
542 /* if it's flipped so that y grows upward,
543 fix so that zero is bottom edge and redraw. This is
544 only appropriate if we're a regular "text" object on the
545 parent. */
546 float diff = x->gl_y1 - x->gl_y2;
547 t_gobj *y;
548 x->gl_y1 = heightwas * diff;
549 x->gl_y2 = x->gl_y1 - diff;
550 /* and move text objects accordingly; they should stick
551 to the bottom, not the top. */
552 for (y = x->gl_list; y; y = y->g_next)
553 if (pd_checkobject(&y->g_pd))
554 gobj_displace(y, x, 0, heightchange);
555 canvas_redraw(x);
556 }
557}
558
559t_symbol *canvas_makebindsym(t_symbol *s)
560{
561 char buf[MAXPDSTRING];
562 strcpy(buf, "pd-");
563 strcat(buf, s->s_name);
564 return (gensym(buf));
565}
566
567void canvas_reflecttitle(t_canvas *x)
568{
569 char namebuf[MAXPDSTRING];
570 t_canvasenvironment *env = canvas_getenv(x);
571 if (env->ce_argc)
572 {
573 int i;
574 strcpy(namebuf, " (");
575 for (i = 0; i < env->ce_argc; i++)
576 {
577 if (strlen(namebuf) > MAXPDSTRING/2 - 5)
578 break;
579 if (i != 0)
580 strcat(namebuf, " ");
581 atom_string(&env->ce_argv[i], namebuf + strlen(namebuf),
582 MAXPDSTRING/2);
583 }
584 strcat(namebuf, ")");
585 }
586 else namebuf[0] = 0;
587 sys_vgui("wm title .x%x {%s%c%s - %s}\n",
588 x, x->gl_name->s_name, (x->gl_dirty? '*' : ' '), namebuf,
589 canvas_getdir(x)->s_name);
590}
591
592void canvas_dirty(t_canvas *x, t_int n)
593{
594 t_canvas *x2 = canvas_getrootfor(x);
595 if ((unsigned)n != x2->gl_dirty)
596 {
597 x2->gl_dirty = n;
598 canvas_reflecttitle(x2);
599 }
600}
601
602 /* the window becomes "mapped" (visible and not miniaturized) or
603 "unmapped" (either miniaturized or just plain gone.) This should be
604 called from the GUI after the fact to "notify" us that we're mapped. */
605void canvas_map(t_canvas *x, t_floatarg f)
606{
607 int flag = (f != 0);
608 t_gobj *y;
609 if (flag)
610 {
611 if (!glist_isvisible(x))
612 {
613 t_selection *sel;
614 if (!x->gl_havewindow)
615 {
616 bug("canvas_map");
617 canvas_vis(x, 1);
618 }
619 for (y = x->gl_list; y; y = y->g_next)
620 gobj_vis(y, x, 1);
621 for (sel = x->gl_editor->e_selection; sel; sel = sel->sel_next)
622 gobj_select(sel->sel_what, x, 1);
623 x->gl_mapped = 1;
624 canvas_drawlines(x);
625 /* simulate a mouse up so u_main will calculate scrollbars...
626 ugly! */
627 sys_vgui("pdtk_canvas_mouseup .x%x.c 0 0 0\n", x);
628 }
629 }
630 else
631 {
632 if (glist_isvisible(x))
633 {
634 /* just clear out the whole canvas... */
635 sys_vgui(".x%x.c delete all\n", x);
636 /* alternatively, we could have erased them one by one...
637 for (y = x->gl_list; y; y = y->g_next)
638 gobj_vis(y, x, 0);
639 ... but we should go through and erase the lines as well
640 if we do it that way. */
641 x->gl_mapped = 0;
642 }
643 }
644}
645
646void canvas_redraw(t_canvas *x)
647{
648 if (glist_isvisible(x))
649 {
650 canvas_map(x, 0);
651 canvas_map(x, 1);
652 }
653}
654
655/* ---- editors -- perhaps this and "vis" should go to g_editor.c ------- */
656
657static t_editor *editor_new(t_glist *owner)
658{
659 char buf[40];
660 t_editor *x = (t_editor *)getbytes(sizeof(*x));
661 x->e_connectbuf = binbuf_new();
662 x->e_deleted = binbuf_new();
663 x->e_glist = owner;
664 sprintf(buf, ".x%x", (t_int)owner);
665 x->e_guiconnect = guiconnect_new(&owner->gl_pd, gensym(buf));
666 return (x);
667}
668
669static void editor_free(t_editor *x, t_glist *y)
670{
671 glist_noselect(y);
672 guiconnect_notarget(x->e_guiconnect, 1000);
673 binbuf_free(x->e_connectbuf);
674 binbuf_free(x->e_deleted);
675 freebytes((void *)x, sizeof(*x));
676}
677
678 /* recursively create or destroy all editors of a glist and its
679 sub-glists, as long as they aren't toplevels. */
680void canvas_create_editor(t_glist *x, int createit)
681{
682 t_gobj *y;
683 t_object *ob;
684 if (createit)
685 {
686 if (x->gl_editor)
687 bug("canvas_create_editor");
688 else
689 {
690 x->gl_editor = editor_new(x);
691 for (y = x->gl_list; y; y = y->g_next)
692 if (ob = pd_checkobject(&y->g_pd))
693 rtext_new(x, ob);
694 }
695 }
696 else
697 {
698 if (!x->gl_editor)
699 bug("canvas_create_editor");
700 else
701 {
702 for (y = x->gl_list; y; y = y->g_next)
703 if (ob = pd_checkobject(&y->g_pd))
704 rtext_free(glist_findrtext(x, ob));
705 editor_free(x->gl_editor, x);
706 x->gl_editor = 0;
707 }
708 }
709 for (y = x->gl_list; y; y = y->g_next)
710 if (pd_class(&y->g_pd) == canvas_class &&
711 ((t_canvas *)y)->gl_isgraph)
712 canvas_create_editor((t_canvas *)y, createit);
713}
714
715 /* we call this when we want the window to become visible, mapped, and
716 in front of all windows; or with "f" zero, when we want to get rid of
717 the window. */
718void canvas_vis(t_canvas *x, t_floatarg f)
719{
720 char buf[30];
721 int flag = (f != 0);
722 if (flag)
723 {
724 /* test if we're already visible and toplevel */
725 if (glist_isvisible(x) && !x->gl_isgraph)
726 { /* just put us in front */
727#ifdef MSW
728 canvas_vis(x, 0);
729 canvas_vis(x, 1);
730#else
731 sys_vgui("raise .x%x\n", x);
732 sys_vgui("focus .x%x.c\n", x);
733 sys_vgui("wm deiconify .x%x\n", x);
734#endif
735 }
736 else
737 {
738 canvas_create_editor(x, 1);
739 sys_vgui("pdtk_canvas_new .x%x %d %d +%d+%d %d\n", x,
740 (int)(x->gl_screenx2 - x->gl_screenx1),
741 (int)(x->gl_screeny2 - x->gl_screeny1),
742 (int)(x->gl_screenx1), (int)(x->gl_screeny1),
743 x->gl_edit);
744 canvas_reflecttitle(x);
745 x->gl_havewindow = 1;
746 canvas_updatewindowlist();
747 }
748 }
749 else /* make invisible */
750 {
751 int i;
752 t_canvas *x2;
753 if (!x->gl_havewindow)
754 {
755 /* bug workaround -- a graph in a visible patch gets "invised"
756 when the patch is closed, and must lose the editor here. It's
757 probably not the natural place to do this. Other cases like
758 subpatches fall here too but don'd need the editor freed, so
759 we check if it exists. */
760 if (x->gl_editor)
761 canvas_create_editor(x, 0);
762 return;
763 }
764 glist_noselect(x);
765 if (glist_isvisible(x))
766 canvas_map(x, 0);
767 canvas_create_editor(x, 0);
768 sys_vgui("destroy .x%x\n", x);
769 for (i = 1, x2 = x; x2; x2 = x2->gl_next, i++)
770 ;
771 sys_vgui(".mbar.find delete %d\n", i);
772 /* if we're a graph on our parent, and if the parent exists
773 and is visible, show ourselves on parent. */
774 if (glist_isgraph(x) && x->gl_owner)
775 {
776 t_glist *gl2 = x->gl_owner;
777 canvas_create_editor(x, 1);
778 if (glist_isvisible(gl2))
779 gobj_vis(&x->gl_gobj, gl2, 0);
780 x->gl_havewindow = 0;
781 if (glist_isvisible(gl2))
782 gobj_vis(&x->gl_gobj, gl2, 1);
783 }
784 else x->gl_havewindow = 0;
785 canvas_updatewindowlist();
786 }
787}
788
789 /* we call this on a non-toplevel glist to "open" it into its
790 own window. */
791void glist_menu_open(t_glist *x)
792{
793 if (glist_isvisible(x) && !glist_istoplevel(x))
794 {
795 t_glist *gl2 = x->gl_owner;
796 if (!gl2)
797 bug("canvas_vis"); /* shouldn't happen but don't get too upset. */
798 else
799 {
800 /* erase ourself in parent window */
801 gobj_vis(&x->gl_gobj, gl2, 0);
802 /* get rid of our editor (and subeditors) */
803 canvas_create_editor(x, 0);
804 x->gl_havewindow = 1;
805 /* redraw ourself in parent window (blanked out this time) */
806 gobj_vis(&x->gl_gobj, gl2, 1);
807 }
808 }
809 canvas_vis(x, 1);
810}
811
812int glist_isvisible(t_glist *x)
813{
814 return ((!x->gl_loading) && glist_getcanvas(x)->gl_mapped);
815}
816
817int glist_istoplevel(t_glist *x)
818{
819 /* we consider a graph "toplevel" if it has its own window
820 or if it appears as a box in its parent window so that we
821 don't draw the actual contents there. */
822 return (x->gl_havewindow || !x->gl_isgraph);
823}
824
825int glist_getfont(t_glist *x)
826{
827 return (glist_getcanvas(x)->gl_font);
828}
829
830void canvas_free(t_canvas *x)
831{
832 t_gobj *y;
833 int dspstate = canvas_suspend_dsp();
834 canvas_noundo(x);
835 if (canvas_editing == x)
836 canvas_editing = 0;
837 if (canvas_whichfind == x)
838 canvas_whichfind = 0;
839 glist_noselect(x);
840 while (y = x->gl_list)
841 glist_delete(x, y);
842 canvas_vis(x, 0);
843
844 if (strcmp(x->gl_name->s_name, "Pd"))
845 pd_unbind(&x->gl_pd, canvas_makebindsym(x->gl_name));
846 if (x->gl_env)
847 {
848 freebytes(x->gl_env->ce_argv, x->gl_env->ce_argc * sizeof(t_atom));
849 freebytes(x->gl_env, sizeof(*x->gl_env));
850 }
851 canvas_resume_dsp(dspstate);
852 glist_cleanup(x);
853 gfxstub_deleteforkey(x); /* probably unnecessary */
854 if (!x->gl_owner)
855 canvas_takeofflist(x);
856}
857
858/* ----------------- lines ---------- */
859
860static void canvas_drawlines(t_canvas *x)
861{
862 t_linetraverser t;
863 t_outconnect *oc;
864 {
865 linetraverser_start(&t, x);
866 while (oc = linetraverser_next(&t))
867 sys_vgui(".x%x.c create line %d %d %d %d -width %d -tags l%x\n",
868 glist_getcanvas(x),
869 t.tr_lx1, t.tr_ly1, t.tr_lx2, t.tr_ly2,
870 (outlet_getsymbol(t.tr_outlet) == &s_signal ? 2:1),
871 oc);
872 }
873}
874
875void canvas_fixlinesfor(t_canvas *x, t_text *text)
876{
877 t_linetraverser t;
878 t_outconnect *oc;
879
880 linetraverser_start(&t, x);
881 while (oc = linetraverser_next(&t))
882 {
883 if (t.tr_ob == text || t.tr_ob2 == text)
884 {
885 sys_vgui(".x%x.c coords l%x %d %d %d %d\n",
886 glist_getcanvas(x), oc,
887 t.tr_lx1, t.tr_ly1, t.tr_lx2, t.tr_ly2);
888 }
889 }
890}
891
892 /* kill all lines for the object */
893void canvas_deletelinesfor(t_canvas *x, t_text *text)
894{
895 t_linetraverser t;
896 t_outconnect *oc;
897 linetraverser_start(&t, x);
898 while (oc = linetraverser_next(&t))
899 {
900 if (t.tr_ob == text || t.tr_ob2 == text)
901 {
902 if (x->gl_editor)
903 {
904 sys_vgui(".x%x.c delete l%x\n",
905 glist_getcanvas(x), oc);
906 }
907 obj_disconnect(t.tr_ob, t.tr_outno, t.tr_ob2, t.tr_inno);
908 }
909 }
910}
911
912 /* kill all lines for one inlet or outlet */
913void canvas_deletelinesforio(t_canvas *x, t_text *text,
914 t_inlet *inp, t_outlet *outp)
915{
916 t_linetraverser t;
917 t_outconnect *oc;
918 linetraverser_start(&t, x);
919 while (oc = linetraverser_next(&t))
920 {
921 if ((t.tr_ob == text && t.tr_outlet == outp) ||
922 (t.tr_ob2 == text && t.tr_inlet == inp))
923 {
924 if (x->gl_editor)
925 {
926 sys_vgui(".x%x.c delete l%x\n",
927 glist_getcanvas(x), oc);
928 }
929 obj_disconnect(t.tr_ob, t.tr_outno, t.tr_ob2, t.tr_inno);
930 }
931 }
932}
933
934static void canvas_pop(t_canvas *x, t_floatarg fvis)
935{
936 if (fvis != 0)
937 canvas_vis(x, 1);
938 pd_popsym(&x->gl_pd);
939 canvas_resortinlets(x);
940 canvas_resortoutlets(x);
941 x->gl_loading = 0;
942}
943
944void canvas_objfor(t_glist *gl, t_text *x, int argc, t_atom *argv);
945
946
947void canvas_restore(t_canvas *x, t_symbol *s, int argc, t_atom *argv)
948{ /* IOhannes */
949 t_pd *z;
950 /* this should be unnecessary, but sometimes the canvas's name gets
951 out of sync with the owning box's argument; this fixes that */
952 if (argc > 3)
953 {
954 t_atom *ap=argv+3;
955 if (ap->a_type == A_SYMBOL)
956 {
957 char *buf=ap->a_w.w_symbol->s_name, *bufp;
958 if (*buf == '$' && buf[1] >= '0' && buf[1] <= '9')
959 {
960 for (bufp = buf+2; *bufp; bufp++)
961 if (*bufp < '0' || *bufp > '9')
962 {
963 SETDOLLSYM(ap, gensym(buf+1));
964 goto didit;
965 }
966 SETDOLLAR(ap, atoi(buf+1));
967 didit: ;
968 }
969 }
970
971 if (ap->a_type == A_DOLLSYM)
972 {
973 t_canvasenvironment *e = canvas_getenv(canvas_getcurrent());
974 canvas_rename(x, binbuf_realizedollsym(ap->a_w.w_symbol,
975 e->ce_argc, e->ce_argv, 1), 0);
976 }
977 else if (ap->a_type == A_SYMBOL)
978 canvas_rename(x, argv[3].a_w.w_symbol, 0);
979 }
980 canvas_pop(x, x->gl_willvis);
981
982 if (!(z = gensym("#X")->s_thing)) error("canvas_restore: out of context");
983 else if (*z != canvas_class) error("canvas_restore: wasn't a canvas");
984 else
985 {
986 t_canvas *x2 = (t_canvas *)z;
987 x->gl_owner = x2;
988 canvas_objfor(x2, &x->gl_obj, argc, argv);
989 }
990}
991
992static void canvas_loadbangabstractions(t_canvas *x)
993{
994 t_gobj *y;
995 t_symbol *s = gensym("loadbang");
996 for (y = x->gl_list; y; y = y->g_next)
997 if (pd_class(&y->g_pd) == canvas_class)
998 {
999 if (canvas_isabstraction((t_canvas *)y))
1000 canvas_loadbang((t_canvas *)y);
1001 else
1002 canvas_loadbangabstractions((t_canvas *)y);
1003 }
1004}
1005
1006void canvas_loadbangsubpatches(t_canvas *x)
1007{
1008 t_gobj *y;
1009 t_symbol *s = gensym("loadbang");
1010 for (y = x->gl_list; y; y = y->g_next)
1011 if (pd_class(&y->g_pd) == canvas_class)
1012 {
1013 if (!canvas_isabstraction((t_canvas *)y))
1014 canvas_loadbangsubpatches((t_canvas *)y);
1015 }
1016 for (y = x->gl_list; y; y = y->g_next)
1017 if ((pd_class(&y->g_pd) != canvas_class) &&
1018 zgetfn(&y->g_pd, s))
1019 pd_vmess(&y->g_pd, s, "");
1020}
1021
1022void canvas_loadbang(t_canvas *x)
1023{
1024 t_gobj *y;
1025 canvas_loadbangabstractions(x);
1026 canvas_loadbangsubpatches(x);
1027}
1028
1029 /* When you ask a canvas its size the result is 2 pixels more than what
1030 you gave it to open it; perhaps there's a 1-pixel border all around it
1031 or something. Anyway, we just add the 2 pixels back here; seems we
1032 have to do this for linux but not MSW; not sure about MacOS. */
1033
1034#ifdef MSW
1035#define HORIZBORDER 0
1036#define VERTBORDER 0
1037#else
1038#define HORIZBORDER 2
1039#define VERTBORDER 2
1040#endif
1041
1042static void canvas_relocate(t_canvas *x, t_symbol *canvasgeom,
1043 t_symbol *topgeom)
1044{
1045 int cxpix, cypix, cw, ch, txpix, typix, tw, th;
1046 if (sscanf(canvasgeom->s_name, "%dx%d+%d+%d", &cw, &ch, &cxpix, &cypix)
1047 < 4 ||
1048 sscanf(topgeom->s_name, "%dx%d+%d+%d", &tw, &th, &txpix, &typix) < 4)
1049 bug("canvas_relocate");
1050 /* for some reason this is initially called with cw=ch=1 so
1051 we just suppress that here. */
1052 if (cw > 5 && ch > 5)
1053 canvas_setbounds(x, txpix, typix,
1054 txpix + cw - HORIZBORDER, typix + ch - VERTBORDER);
1055}
1056
1057void canvas_popabstraction(t_canvas *x)
1058{
1059 newest = &x->gl_pd;
1060 pd_popsym(&x->gl_pd);
1061 x->gl_loading = 0;
1062 canvas_resortinlets(x);
1063 canvas_resortoutlets(x);
1064}
1065
1066void canvas_logerror(t_object *y)
1067{
1068#ifdef LATER
1069 canvas_vis(x, 1);
1070 if (!glist_isselected(x, &y->ob_g))
1071 glist_select(x, &y->ob_g);
1072#endif
1073}
1074
1075/* -------------------------- subcanvases ---------------------- */
1076
1077static void *subcanvas_new(t_symbol *s)
1078{
1079 t_atom a[6];
1080 t_canvas *x, *z = canvas_getcurrent();
1081 if (!*s->s_name) s = gensym("/SUBPATCH/");
1082 SETFLOAT(a, 0);
1083 SETFLOAT(a+1, GLIST_DEFCANVASYLOC);
1084 SETFLOAT(a+2, GLIST_DEFCANVASWIDTH);
1085 SETFLOAT(a+3, GLIST_DEFCANVASHEIGHT);
1086 SETSYMBOL(a+4, s);
1087 SETFLOAT(a+5, 1);
1088 x = canvas_new(0, 0, 6, a);
1089 x->gl_owner = z;
1090 canvas_pop(x, 1);
1091 return (x);
1092}
1093
1094static void canvas_click(t_canvas *x,
1095 t_floatarg xpos, t_floatarg ypos,
1096 t_floatarg shift, t_floatarg ctrl, t_floatarg alt)
1097{
1098 canvas_vis(x, 1);
1099}
1100
1101
1102 /* find out from subcanvas contents how much to fatten the box */
1103void canvas_fattensub(t_canvas *x,
1104 int *xp1, int *yp1, int *xp2, int *yp2)
1105{
1106 t_gobj *y;
1107 *xp2 += 50; /* fake for now */
1108 *yp2 += 50;
1109}
1110
1111static void canvas_rename_method(t_canvas *x, t_symbol *s, int ac, t_atom *av)
1112{
1113 if (ac && av->a_type == A_SYMBOL)
1114 canvas_rename(x, av->a_w.w_symbol, 0);
1115 else canvas_rename(x, gensym("Pd"), 0);
1116}
1117
1118/* ------------------ table ---------------------------*/
1119
1120static int tabcount = 0;
1121
1122static void *table_new(t_symbol *s, t_floatarg f)
1123{
1124 t_atom a[9];
1125 t_glist *gl;
1126 t_canvas *x, *z = canvas_getcurrent();
1127 if (s == &s_)
1128 {
1129 char tabname[255];
1130 t_symbol *t = gensym("table");
1131 sprintf(tabname, "%s%d", t->s_name, tabcount++);
1132 s = gensym(tabname);
1133 }
1134 if (f <= 1)
1135 f = 100;
1136 SETFLOAT(a, 0);
1137 SETFLOAT(a+1, GLIST_DEFCANVASYLOC);
1138 SETFLOAT(a+2, 600);
1139 SETFLOAT(a+3, 400);
1140 SETSYMBOL(a+4, s);
1141 SETFLOAT(a+5, 0);
1142 x = canvas_new(0, 0, 6, a);
1143
1144 x->gl_owner = z;
1145
1146 /* create a graph for the table */
1147 gl = glist_addglist((t_glist*)x, &s_, 0, -1, (f > 1 ? f-1 : 1), 1,
1148 50, 350, 550, 50);
1149
1150 graph_array(gl, s, &s_float, f, 0);
1151
1152 canvas_pop(x, 0);
1153
1154 return (x);
1155}
1156
1157 /* return true if the "canvas" object is an abstraction (so we don't
1158 save its contents, fogr example.) */
1159int canvas_isabstraction(t_canvas *x)
1160{
1161 return (x->gl_env != 0);
1162}
1163
1164 /* return true if the "canvas" object is a "table". */
1165int canvas_istable(t_canvas *x)
1166{
1167 t_atom *argv = (x->gl_obj.te_binbuf? binbuf_getvec(x->gl_obj.te_binbuf):0);
1168 int argc = (x->gl_obj.te_binbuf? binbuf_getnatom(x->gl_obj.te_binbuf) : 0);
1169 int istable = (argc && argv[0].a_type == A_SYMBOL &&
1170 argv[0].a_w.w_symbol == gensym("table"));
1171 return (istable);
1172}
1173
1174 /* return true if the "canvas" object should be treated as a text
1175 object. This is true for abstractions but also for "table"s... */
1176int canvas_showtext(t_canvas *x)
1177{
1178 t_atom *argv = (x->gl_obj.te_binbuf? binbuf_getvec(x->gl_obj.te_binbuf):0);
1179 int argc = (x->gl_obj.te_binbuf? binbuf_getnatom(x->gl_obj.te_binbuf) : 0);
1180 int isarray = (argc && argv[0].a_type == A_SYMBOL &&
1181 argv[0].a_w.w_symbol == gensym("graph"));
1182 return (!isarray);
1183}
1184
1185static void canvas_dodsp(t_canvas *x, int toplevel, t_signal **sp);
1186static void canvas_dsp(t_canvas *x, t_signal **sp)
1187{
1188 canvas_dodsp(x, 0, sp);
1189}
1190
1191 /* get the document containing this canvas */
1192t_canvas *canvas_getrootfor(t_canvas *x)
1193{
1194 if ((!x->gl_owner) || canvas_isabstraction(x))
1195 return (x);
1196 else return (canvas_getrootfor(x->gl_owner));
1197}
1198
1199/* ------------------------- DSP chain handling ------------------------- */
1200
1201EXTERN_STRUCT _dspcontext;
1202#define t_dspcontext struct _dspcontext
1203
1204void ugen_start(void);
1205void ugen_stop(void);
1206
1207t_dspcontext *ugen_start_graph(int toplevel, t_signal **sp,
1208 int ninlets, int noutlets);
1209void ugen_add(t_dspcontext *dc, t_object *x);
1210void ugen_connect(t_dspcontext *dc, t_object *x1, int outno,
1211 t_object *x2, int inno);
1212void ugen_done_graph(t_dspcontext *dc);
1213
1214 /* schedule one canvas for DSP. This is called below for all "root"
1215 canvases, but is also called from the "dsp" method for sub-
1216 canvases, which are treated almost like any other tilde object. */
1217
1218static void canvas_dodsp(t_canvas *x, int toplevel, t_signal **sp)
1219{
1220 t_linetraverser t;
1221 t_outconnect *oc;
1222 t_gobj *y;
1223 t_object *ob;
1224 t_symbol *dspsym = gensym("dsp");
1225 t_dspcontext *dc;
1226
1227 /* create a new "DSP graph" object to use in sorting this canvas.
1228 If we aren't toplevel, there are already other dspcontexts around. */
1229
1230 dc = ugen_start_graph(toplevel, sp,
1231 obj_nsiginlets(&x->gl_obj),
1232 obj_nsigoutlets(&x->gl_obj));
1233
1234 /* find all the "dsp" boxes and add them to the graph */
1235
1236 for (y = x->gl_list; y; y = y->g_next)
1237 if ((ob = pd_checkobject(&y->g_pd)) && zgetfn(&y->g_pd, dspsym))
1238 ugen_add(dc, ob);
1239
1240 /* ... and all dsp interconnections */
1241 linetraverser_start(&t, x);
1242 while (oc = linetraverser_next(&t))
1243 if (obj_issignaloutlet(t.tr_ob, t.tr_outno))
1244 ugen_connect(dc, t.tr_ob, t.tr_outno, t.tr_ob2, t.tr_inno);
1245
1246 /* finally, sort them and add them to the DSP chain */
1247 ugen_done_graph(dc);
1248}
1249
1250 /* this routine starts DSP for all root canvases. */
1251static void canvas_start_dsp(void)
1252{
1253 t_canvas *x;
1254 if (canvas_dspstate) ugen_stop();
1255 else sys_gui("pdtk_pd_dsp ON\n");
1256 ugen_start();
1257
1258 for (x = canvas_list; x; x = x->gl_next)
1259 canvas_dodsp(x, 1, 0);
1260
1261 canvas_dspstate = 1;
1262}
1263
1264static void canvas_stop_dsp(void)
1265{
1266 if (canvas_dspstate)
1267 {
1268 ugen_stop();
1269 sys_gui("pdtk_pd_dsp OFF\n");
1270 canvas_dspstate = 0;
1271 }
1272}
1273
1274 /* DSP can be suspended before, and resumed after, operations which
1275 might affect the DSP chain. For example, we suspend before loading and
1276 resume afterward, so that DSP doesn't get resorted for every DSP object
1277 int the patch. */
1278
1279int canvas_suspend_dsp(void)
1280{
1281 int rval = canvas_dspstate;
1282 if (rval) canvas_stop_dsp();
1283 return (rval);
1284}
1285
1286void canvas_resume_dsp(int oldstate)
1287{
1288 if (oldstate) canvas_start_dsp();
1289}
1290
1291 /* this is equivalent to suspending and resuming in one step. */
1292void canvas_update_dsp(void)
1293{
1294 if (canvas_dspstate) canvas_start_dsp();
1295}
1296
1297void glob_dsp(void *dummy, t_symbol *s, int argc, t_atom *argv)
1298{
1299 int newstate;
1300 if (argc)
1301 {
1302 newstate = atom_getintarg(0, argc, argv);
1303 if (newstate && !canvas_dspstate)
1304 {
1305 sys_set_audio_state(1);
1306 canvas_start_dsp();
1307 }
1308 else if (!newstate && canvas_dspstate)
1309 {
1310 canvas_stop_dsp();
1311 sys_set_audio_state(0);
1312 }
1313 }
1314 else post("dsp state %d", canvas_dspstate);
1315}
1316
1317 /* LATER replace this with a queueing scheme */
1318void glist_redrawitem(t_glist *owner, t_gobj *gobj)
1319{
1320 if (glist_isvisible(owner))
1321 {
1322 gobj_vis(gobj, owner, 0);
1323 gobj_vis(gobj, owner, 1);
1324 }
1325}
1326
1327 /* redraw all "scalars" (do this if a drawing command is changed.)
1328 LATER we'll use the "template" information to select which ones we
1329 redraw. */
1330static void glist_redrawall(t_glist *gl)
1331{
1332 t_gobj *g;
1333 int vis = glist_isvisible(gl);
1334 for (g = gl->gl_list; g; g = g->g_next)
1335 {
1336 t_class *cl;
1337 if (vis && g->g_pd == scalar_class)
1338 glist_redrawitem(gl, g);
1339 else if (g->g_pd == canvas_class)
1340 glist_redrawall((t_glist *)g);
1341 }
1342}
1343
1344 /* public interface for above */
1345void canvas_redrawallfortemplate(t_canvas *templatecanvas)
1346{
1347 t_canvas *x;
1348 /* find all root canvases */
1349 for (x = canvas_list; x; x = x->gl_next)
1350 glist_redrawall(x);
1351}
1352
1353/* ------------------------------- setup routine ------------------------ */
1354
1355 /* why are some of these "glist" and others "canvas"? */
1356extern void glist_text(t_glist *x, t_symbol *s, int argc, t_atom *argv);
1357extern void canvas_obj(t_glist *gl, t_symbol *s, int argc, t_atom *argv);
1358extern void canvas_bng(t_glist *gl, t_symbol *s, int argc, t_atom *argv);
1359extern void canvas_toggle(t_glist *gl, t_symbol *s, int argc, t_atom *argv);
1360extern void canvas_vslider(t_glist *gl, t_symbol *s, int argc, t_atom *argv);
1361extern void canvas_hslider(t_glist *gl, t_symbol *s, int argc, t_atom *argv);
1362extern void canvas_vdial(t_glist *gl, t_symbol *s, int argc, t_atom *argv);
1363 /* old version... */
1364extern void canvas_hdial(t_glist *gl, t_symbol *s, int argc, t_atom *argv);
1365extern void canvas_hdial(t_glist *gl, t_symbol *s, int argc, t_atom *argv);
1366 /* new version: */
1367extern void canvas_hradio(t_glist *gl, t_symbol *s, int argc, t_atom *argv);
1368extern void canvas_vradio(t_glist *gl, t_symbol *s, int argc, t_atom *argv);
1369extern void canvas_vumeter(t_glist *gl, t_symbol *s, int argc, t_atom *argv);
1370extern void canvas_mycnv(t_glist *gl, t_symbol *s, int argc, t_atom *argv);
1371extern void canvas_numbox(t_glist *gl, t_symbol *s, int argc, t_atom *argv);
1372extern void canvas_msg(t_glist *gl, t_symbol *s, int argc, t_atom *argv);
1373extern void canvas_floatatom(t_glist *gl, t_symbol *s, int argc, t_atom *argv);
1374extern void canvas_symbolatom(t_glist *gl, t_symbol *s, int argc, t_atom *argv);
1375extern void glist_scalar(t_glist *canvas, t_symbol *s, int argc, t_atom *argv);
1376
1377void g_graph_setup(void);
1378void g_editor_setup(void);
1379void g_readwrite_setup(void);
1380extern void graph_properties(t_gobj *z, t_glist *owner);
1381
1382void g_canvas_setup(void)
1383{
1384 /* we prevent the user from typing "canvas" in an object box
1385 by sending 0 for a creator function. */
1386 canvas_class = class_new(gensym("canvas"), 0,
1387 (t_method)canvas_free, sizeof(t_canvas), CLASS_NOINLET, 0);
1388 /* here is the real creator function, invoked in patch files
1389 by sending the "canvas" message to #N, which is bound
1390 to pd_camvasmaker. */
1391 class_addmethod(pd_canvasmaker, (t_method)canvas_new, gensym("canvas"),
1392 A_GIMME, 0);
1393 class_addmethod(canvas_class, (t_method)canvas_restore,
1394 gensym("restore"), A_GIMME, 0);
1395 class_addmethod(canvas_class, (t_method)canvas_coords,
1396 gensym("coords"), A_GIMME, 0);
1397
1398/* -------------------------- objects ----------------------------- */
1399 class_addmethod(canvas_class, (t_method)canvas_obj,
1400 gensym("obj"), A_GIMME, A_NULL);
1401 class_addmethod(canvas_class, (t_method)canvas_msg,
1402 gensym("msg"), A_GIMME, A_NULL);
1403 class_addmethod(canvas_class, (t_method)canvas_floatatom,
1404 gensym("floatatom"), A_GIMME, A_NULL);
1405 class_addmethod(canvas_class, (t_method)canvas_symbolatom,
1406 gensym("symbolatom"), A_GIMME, A_NULL);
1407 class_addmethod(canvas_class, (t_method)glist_text,
1408 gensym("text"), A_GIMME, A_NULL);
1409 class_addmethod(canvas_class, (t_method)glist_glist, gensym("graph"),
1410 A_GIMME, A_NULL);
1411 class_addmethod(canvas_class, (t_method)glist_scalar,
1412 gensym("scalar"), A_GIMME, A_NULL);
1413
1414 /* -------------- Thomas Musil's GUI objects ------------ */
1415 class_addmethod(canvas_class, (t_method)canvas_bng, gensym("bng"),
1416 A_GIMME, A_NULL);
1417 class_addmethod(canvas_class, (t_method)canvas_toggle, gensym("toggle"),
1418 A_GIMME, A_NULL);
1419 class_addmethod(canvas_class, (t_method)canvas_vslider, gensym("vslider"),
1420 A_GIMME, A_NULL);
1421 class_addmethod(canvas_class, (t_method)canvas_hslider, gensym("hslider"),
1422 A_GIMME, A_NULL);
1423 class_addmethod(canvas_class, (t_method)canvas_hdial, gensym("hdial"),
1424 A_GIMME, A_NULL);
1425 class_addmethod(canvas_class, (t_method)canvas_vdial, gensym("vdial"),
1426 A_GIMME, A_NULL);
1427 class_addmethod(canvas_class, (t_method)canvas_hradio, gensym("hradio"),
1428 A_GIMME, A_NULL);
1429 class_addmethod(canvas_class, (t_method)canvas_vradio, gensym("vradio"),
1430 A_GIMME, A_NULL);
1431 class_addmethod(canvas_class, (t_method)canvas_vumeter, gensym("vumeter"),
1432 A_GIMME, A_NULL);
1433 class_addmethod(canvas_class, (t_method)canvas_mycnv, gensym("mycnv"),
1434 A_GIMME, A_NULL);
1435 class_addmethod(canvas_class, (t_method)canvas_numbox, gensym("numbox"),
1436 A_GIMME, A_NULL);
1437
1438/* ------------------------ gui stuff --------------------------- */
1439 class_addmethod(canvas_class, (t_method)canvas_pop, gensym("pop"),
1440 A_DEFFLOAT, A_NULL);
1441 class_addmethod(canvas_class, (t_method)canvas_loadbang,
1442 gensym("loadbang"), A_NULL);
1443 class_addmethod(canvas_class, (t_method)canvas_relocate,
1444 gensym("relocate"), A_SYMBOL, A_SYMBOL, A_NULL);
1445 class_addmethod(canvas_class, (t_method)canvas_vis,
1446 gensym("vis"), A_FLOAT, A_NULL);
1447 class_addmethod(canvas_class, (t_method)glist_menu_open,
1448 gensym("menu-open"), A_NULL);
1449 class_addmethod(canvas_class, (t_method)canvas_map,
1450 gensym("map"), A_FLOAT, A_NULL);
1451 class_setpropertiesfn(canvas_class, graph_properties);
1452
1453/* ---------------------- list handling ------------------------ */
1454 class_addmethod(canvas_class, (t_method)glist_clear, gensym("clear"),
1455 A_NULL);
1456
1457/* ----- subcanvases, which you get by typing "pd" in a box ---- */
1458 class_addcreator((t_newmethod)subcanvas_new, gensym("pd"), A_DEFSYMBOL, 0);
1459 class_addcreator((t_newmethod)subcanvas_new, gensym("page"), A_DEFSYMBOL, 0);
1460
1461 class_addmethod(canvas_class, (t_method)canvas_click,
1462 gensym("click"), A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, 0);
1463 class_addmethod(canvas_class, (t_method)canvas_dsp, gensym("dsp"), 0);
1464 class_addmethod(canvas_class, (t_method)canvas_rename_method,
1465 gensym("rename"), A_GIMME, 0);
1466
1467/*---------------------------- tables -- GG ------------------- */
1468
1469 class_addcreator((t_newmethod)table_new, gensym("table"),
1470 A_DEFSYM, A_DEFFLOAT, 0);
1471
1472/* -------------- setups from other files for canvas_class ---------------- */
1473 g_graph_setup();
1474 g_editor_setup();
1475 g_readwrite_setup();
1476}
1477/* Copyright (c) 1997-2001 Miller Puckette and others.
1478* For information on usage and redistribution, and for a DISCLAIMER OF ALL
1479* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */
1480
1481/* this file defines the "glist" class, also known as "canvas" (the two used
1482to be different but are now unified except for some fossilized names.) */
1483
1484/* changes by Thomas Musil IEM KUG Graz Austria 2001 */
1485
1486/* bug-fix: canvas_menuclose(): by Krzysztof Czaja */
1487/* bug-fix: table_new(): I reversed the y-bounds */
1488
1489/* IOhannes :
1490 * changed the canvas_restore, so that it might accept $args as well
1491 * (like "pd $0_test")
1492 * so you can make multiple & distinguishable templates
1493 * 1511:forum::für::umläute:2001
1494 * changes marked with IOhannes
1495 */
1496
1497#include <stdlib.h>
1498#include <stdio.h>
1499#include "m_pd.h"
1500#include "m_imp.h"
1501#include "s_stuff.h"
1502#include "g_canvas.h"
1503#include <string.h>
1504#include "g_all_guis.h"
1505
1506struct _canvasenvironment
1507{
1508 t_symbol *ce_dir; /* directory patch lives in */
1509 int ce_argc; /* number of "$" arguments */
1510 t_atom *ce_argv; /* array of "$" arguments */
1511 int ce_dollarzero; /* value of "$0" */
1512};
1513
1514#define GLIST_DEFCANVASWIDTH 240
1515#define GLIST_DEFCANVASHEIGHT 300
1516
1517#ifdef MACOSX
1518#define GLIST_DEFCANVASYLOC 22
1519#else
1520#define GLIST_DEFCANVASYLOC 0
1521#endif
1522
1523/* ---------------------- variables --------------------------- */
1524
1525extern t_pd *newest;
1526t_class *canvas_class;
1527static int canvas_dspstate; /* whether DSP is on or off */
1528t_canvas *canvas_editing; /* last canvas to start text edting */
1529t_canvas *canvas_whichfind; /* last canvas we did a find in */
1530t_canvas *canvas_list; /* list of all root canvases */
1531
1532/* ------------------ forward function declarations --------------- */
1533static void canvas_start_dsp(void);
1534static void canvas_stop_dsp(void);
1535static void canvas_drawlines(t_canvas *x);
1536static void canvas_setbounds(t_canvas *x, int x1, int y1, int x2, int y2);
1537static void canvas_reflecttitle(t_canvas *x);
1538static void canvas_addtolist(t_canvas *x);
1539static void canvas_takeofflist(t_canvas *x);
1540static void canvas_pop(t_canvas *x, t_floatarg fvis);
1541void canvas_create_editor(t_glist *x, int createit);
1542
1543/* --------- functions to handle the canvas environment ----------- */
1544
1545static t_symbol *canvas_newfilename = &s_;
1546static t_symbol *canvas_newdirectory = &s_;
1547static int canvas_newargc;
1548static t_atom *canvas_newargv;
1549
1550static void glist_doupdatewindowlist(t_glist *gl, char *sbuf)
1551{
1552 t_gobj *g;
1553 if (!gl->gl_owner)
1554 {
1555 /* this is a canvas; if we have a window, put on "windows" list */
1556 t_canvas *canvas = (t_canvas *)gl;
1557 if (canvas->gl_havewindow)
1558 {
1559 if (strlen(sbuf) + strlen(gl->gl_name->s_name) + 100 <= 1024)
1560 {
1561 char tbuf[1024];
1562 sprintf(tbuf, "{%s .x%x} ", gl->gl_name->s_name, (t_int)canvas);
1563 strcat(sbuf, tbuf);
1564 }
1565 }
1566 }
1567 for (g = gl->gl_list; g; g = g->g_next)
1568 {
1569 if (pd_class(&g->g_pd) == canvas_class)
1570 glist_doupdatewindowlist((t_glist *)g, sbuf);
1571 }
1572 return;
1573}
1574
1575 /* maintain the list of visible toplevels for the GUI's "windows" menu */
1576void canvas_updatewindowlist( void)
1577{
1578 t_canvas *x;
1579 char sbuf[1024];
1580 strcpy(sbuf, "set menu_windowlist {");
1581 /* find all root canvases */
1582 for (x = canvas_list; x; x = x->gl_next)
1583 glist_doupdatewindowlist(x, sbuf);
1584 /* next line updates the window menu state before -postcommand tries it */
1585 strcat(sbuf, "}\npdtk_fixwindowmenu\n");
1586 sys_gui(sbuf);
1587}
1588
1589 /* add a glist the list of "root" canvases (toplevels without parents.) */
1590static void canvas_addtolist(t_canvas *x)
1591{
1592 x->gl_next = canvas_list;
1593 canvas_list = x;
1594}
1595
1596static void canvas_takeofflist(t_canvas *x)
1597{
1598 /* take it off the window list */
1599 if (x == canvas_list) canvas_list = x->gl_next;
1600 else
1601 {
1602 t_canvas *z;
1603 for (z = canvas_list; z->gl_next != x; z = z->gl_next)
1604 ;
1605 z->gl_next = x->gl_next;
1606 }
1607}
1608
1609
1610void canvas_setargs(int argc, t_atom *argv)
1611{
1612 /* if there's an old one lying around free it here. This
1613 happens if an abstraction is loaded but never gets as far
1614 as calling canvas_new(). */
1615 if (canvas_newargv)
1616 freebytes(canvas_newargv, canvas_newargc * sizeof(t_atom));
1617 canvas_newargc = argc;
1618 canvas_newargv = copybytes(argv, argc * sizeof(t_atom));
1619}
1620
1621void glob_setfilename(void *dummy, t_symbol *filesym, t_symbol *dirsym)
1622{
1623 canvas_newfilename = filesym;
1624 canvas_newdirectory = dirsym;
1625}
1626
1627t_canvas *canvas_getcurrent(void)
1628{
1629 return ((t_canvas *)pd_findbyclass(&s__X, canvas_class));
1630}
1631
1632void canvas_setcurrent(t_canvas *x)
1633{
1634 pd_pushsym(&x->gl_pd);
1635}
1636
1637void canvas_unsetcurrent(t_canvas *x)
1638{
1639 pd_popsym(&x->gl_pd);
1640}
1641
1642t_canvasenvironment *canvas_getenv(t_canvas *x)
1643{
1644 if (!x) bug("canvas_getenv");
1645 while (!x->gl_env)
1646 if (!(x = x->gl_owner))
1647 bug("t_canvasenvironment", x);
1648 return (x->gl_env);
1649}
1650
1651int canvas_getdollarzero( void)
1652{
1653 t_canvas *x = canvas_getcurrent();
1654 t_canvasenvironment *env = (x ? canvas_getenv(x) : 0);
1655 if (env)
1656 return (env->ce_dollarzero);
1657 else return (0);
1658}
1659
1660void canvas_getargs(int *argcp, t_atom **argvp)
1661{
1662 t_canvasenvironment *e = canvas_getenv(canvas_getcurrent());
1663 *argcp = e->ce_argc;
1664 *argvp = e->ce_argv;
1665}
1666
1667t_symbol *canvas_realizedollar(t_canvas *x, t_symbol *s)
1668{
1669 t_symbol *ret;
1670 char *name = s->s_name;
1671 if (*name == '$' && name[1] >= '0' && name[1] <= '9')
1672 {
1673 t_canvasenvironment *env = canvas_getenv(x);
1674 canvas_setcurrent(x);
1675 ret = binbuf_realizedollsym(gensym(name+1),
1676 env->ce_argc, env->ce_argv, 1);
1677 canvas_unsetcurrent(x);
1678 }
1679 else ret = s;
1680 return (ret);
1681}
1682
1683t_symbol *canvas_getcurrentdir(void)
1684{
1685 t_canvasenvironment *e = canvas_getenv(canvas_getcurrent());
1686 return (e->ce_dir);
1687}
1688
1689t_symbol *canvas_getdir(t_canvas *x)
1690{
1691 t_canvasenvironment *e = canvas_getenv(x);
1692 return (e->ce_dir);
1693}
1694
1695void canvas_makefilename(t_canvas *x, char *file, char *result, int resultsize)
1696{
1697 char *dir = canvas_getenv(x)->ce_dir->s_name;
1698 if (file[0] == '/' || (file[0] && file[1] == ':') || !*dir)
1699 {
1700 strncpy(result, file, resultsize);
1701 result[resultsize-1] = 0;
1702 }
1703 else
1704 {
1705 int nleft;
1706 strncpy(result, dir, resultsize);
1707 result[resultsize-1] = 0;
1708 nleft = resultsize - strlen(result) - 1;
1709 if (nleft <= 0) return;
1710 strcat(result, "/");
1711 strncat(result, file, nleft);
1712 result[resultsize-1] = 0;
1713 }
1714}
1715
1716void canvas_rename(t_canvas *x, t_symbol *s, t_symbol *dir)
1717{
1718 if (strcmp(x->gl_name->s_name, "Pd"))
1719 pd_unbind(&x->gl_pd, canvas_makebindsym(x->gl_name));
1720 x->gl_name = s;
1721 if (strcmp(x->gl_name->s_name, "Pd"))
1722 pd_bind(&x->gl_pd, canvas_makebindsym(x->gl_name));
1723 if (glist_isvisible(x))
1724 canvas_reflecttitle(x);
1725 if (dir && dir != &s_)
1726 {
1727 t_canvasenvironment *e = canvas_getenv(x);
1728 e->ce_dir = dir;
1729 }
1730}
1731
1732/* --------------- traversing the set of lines in a canvas ----------- */
1733
1734int canvas_getindex(t_canvas *x, t_gobj *y)
1735{
1736 t_gobj *y2;
1737 int indexno;
1738 for (indexno = 0, y2 = x->gl_list; y2 && y2 != y; y2 = y2->g_next)
1739 indexno++;
1740 return (indexno);
1741}
1742
1743void linetraverser_start(t_linetraverser *t, t_canvas *x)
1744{
1745 t->tr_ob = 0;
1746 t->tr_x = x;
1747 t->tr_nextoc = 0;
1748 t->tr_nextoutno = t->tr_nout = 0;
1749}
1750
1751t_outconnect *linetraverser_next(t_linetraverser *t)
1752{
1753 t_outconnect *rval = t->tr_nextoc;
1754 int outno;
1755 while (!rval)
1756 {
1757 outno = t->tr_nextoutno;
1758 while (outno == t->tr_nout)
1759 {
1760 t_gobj *y;
1761 t_object *ob = 0;
1762 if (!t->tr_ob) y = t->tr_x->gl_list;
1763 else y = t->tr_ob->ob_g.g_next;
1764 for (; y; y = y->g_next)
1765 if (ob = pd_checkobject(&y->g_pd)) break;
1766 if (!ob) return (0);
1767 t->tr_ob = ob;
1768 t->tr_nout = obj_noutlets(ob);
1769 outno = 0;
1770 if (glist_isvisible(t->tr_x))
1771 gobj_getrect(y, t->tr_x,
1772 &t->tr_x11, &t->tr_y11, &t->tr_x12, &t->tr_y12);
1773 else t->tr_x11 = t->tr_y11 = t->tr_x12 = t->tr_y12 = 0;
1774 }
1775 t->tr_nextoutno = outno + 1;
1776 rval = obj_starttraverseoutlet(t->tr_ob, &t->tr_outlet, outno);
1777 t->tr_outno = outno;
1778 }
1779 t->tr_nextoc = obj_nexttraverseoutlet(rval, &t->tr_ob2,
1780 &t->tr_inlet, &t->tr_inno);
1781 t->tr_nin = obj_ninlets(t->tr_ob2);
1782 if (!t->tr_nin) bug("drawline");
1783 if (glist_isvisible(t->tr_x))
1784 {
1785 int inplus = (t->tr_nin == 1 ? 1 : t->tr_nin - 1);
1786 int outplus = (t->tr_nout == 1 ? 1 : t->tr_nout - 1);
1787 gobj_getrect(&t->tr_ob2->ob_g, t->tr_x,
1788 &t->tr_x21, &t->tr_y21, &t->tr_x22, &t->tr_y22);
1789 t->tr_lx1 = t->tr_x11 +
1790 ((t->tr_x12 - t->tr_x11 - IOWIDTH) * t->tr_outno) /
1791 outplus + IOMIDDLE;
1792 t->tr_ly1 = t->tr_y12;
1793 t->tr_lx2 = t->tr_x21 +
1794 ((t->tr_x22 - t->tr_x21 - IOWIDTH) * t->tr_inno)/inplus +
1795 IOMIDDLE;
1796 t->tr_ly2 = t->tr_y21;
1797 }
1798 else
1799 {
1800 t->tr_x21 = t->tr_y21 = t->tr_x22 = t->tr_y22 = 0;
1801 t->tr_lx1 = t->tr_ly1 = t->tr_lx2 = t->tr_ly2 = 0;
1802 }
1803
1804 return (rval);
1805}
1806
1807void linetraverser_skipobject(t_linetraverser *t)
1808{
1809 t->tr_nextoc = 0;
1810 t->tr_nextoutno = t->tr_nout;
1811}
1812
1813/* -------------------- the canvas object -------------------------- */
1814int glist_valid = 10000;
1815
1816void glist_init(t_glist *x)
1817{
1818 /* zero out everyone except "pd" field */
1819 memset(((char *)x) + sizeof(x->gl_pd), 0, sizeof(*x) - sizeof(x->gl_pd));
1820 x->gl_stub = gstub_new(x, 0);
1821 x->gl_valid = ++glist_valid;
1822 x->gl_xlabel = (t_symbol **)t_getbytes(0);
1823 x->gl_ylabel = (t_symbol **)t_getbytes(0);
1824}
1825
1826 /* make a new glist. It will either be a "root" canvas or else
1827 its parent will be a "text" object in another window... we don't
1828 know which yet. */
1829t_canvas *canvas_new(void *dummy, t_symbol *sel, int argc, t_atom *argv)
1830{
1831 t_canvas *x = (t_canvas *)pd_new(canvas_class);
1832 t_canvas *owner = canvas_getcurrent();
1833 t_symbol *s = &s_;
1834 int vis = 0, width = GLIST_DEFCANVASWIDTH, height = GLIST_DEFCANVASHEIGHT;
1835 int xloc = 0, yloc = GLIST_DEFCANVASYLOC;
1836 int font = (owner ? owner->gl_font : sys_defaultfont);
1837 glist_init(x);
1838 x->gl_obj.te_type = T_OBJECT;
1839 if (!owner)
1840 canvas_addtolist(x);
1841 /* post("canvas %x, owner %x", x, owner); */
1842
1843 if (argc == 5) /* toplevel: x, y, w, h, font */
1844 {
1845 xloc = atom_getintarg(0, argc, argv);
1846 yloc = atom_getintarg(1, argc, argv);
1847 width = atom_getintarg(2, argc, argv);
1848 height = atom_getintarg(3, argc, argv);
1849 font = atom_getintarg(4, argc, argv);
1850 }
1851 else if (argc == 6) /* subwindow: x, y, w, h, name, vis */
1852 {
1853 xloc = atom_getintarg(0, argc, argv);
1854 yloc = atom_getintarg(1, argc, argv);
1855 width = atom_getintarg(2, argc, argv);
1856 height = atom_getintarg(3, argc, argv);
1857 s = atom_getsymbolarg(4, argc, argv);
1858 vis = atom_getintarg(5, argc, argv);
1859 }
1860 /* (otherwise assume we're being created from the menu.) */
1861
1862 if (canvas_newdirectory->s_name[0])
1863 {
1864 static int dollarzero = 1000;
1865 t_canvasenvironment *env = x->gl_env =
1866 (t_canvasenvironment *)getbytes(sizeof(*x->gl_env));
1867 env->ce_dir = canvas_newdirectory;
1868 env->ce_argc = canvas_newargc;
1869 env->ce_argv = canvas_newargv;
1870 env->ce_dollarzero = dollarzero++;
1871 canvas_newdirectory = &s_;
1872 canvas_newargc = 0;
1873 canvas_newargv = 0;
1874 }
1875 else x->gl_env = 0;
1876
1877 if (yloc < GLIST_DEFCANVASYLOC)
1878 yloc = GLIST_DEFCANVASYLOC;
1879 if (xloc < 0)
1880 xloc = 0;
1881 x->gl_x1 = 0;
1882 x->gl_y1 = 0;
1883 x->gl_x2 = 1;
1884 x->gl_y2 = 1;
1885 canvas_setbounds(x, xloc, yloc, xloc + width, yloc + height);
1886 x->gl_owner = owner;
1887 x->gl_name = (*s->s_name ? s :
1888 (canvas_newfilename ? canvas_newfilename : gensym("Pd")));
1889 if (strcmp(x->gl_name->s_name, "Pd"))
1890 pd_bind(&x->gl_pd, canvas_makebindsym(x->gl_name));
1891 x->gl_loading = 1;
1892 x->gl_willvis = vis;
1893 x->gl_edit = !strncmp(x->gl_name->s_name, "Untitled", 8);
1894 x->gl_font = sys_nearestfontsize(font);
1895 pd_pushsym(&x->gl_pd);
1896 return(x);
1897}
1898
1899void canvas_setgraph(t_glist *x, int flag);
1900
1901static void canvas_coords(t_glist *x, t_symbol *s, int argc, t_atom *argv)
1902{
1903 x->gl_x1 = atom_getfloatarg(0, argc, argv);
1904 x->gl_y1 = atom_getfloatarg(1, argc, argv);
1905 x->gl_x2 = atom_getfloatarg(2, argc, argv);
1906 x->gl_y2 = atom_getfloatarg(3, argc, argv);
1907 x->gl_pixwidth = atom_getintarg(4, argc, argv);
1908 x->gl_pixheight = atom_getintarg(5, argc, argv);
1909 canvas_setgraph(x, atom_getintarg(6, argc, argv));
1910}
1911
1912 /* make a new glist and add it to this glist. It will appear as
1913 a "graph", not a text object. */
1914t_glist *glist_addglist(t_glist *g, t_symbol *sym,
1915 float x1, float y1, float x2, float y2,
1916 float px1, float py1, float px2, float py2)
1917{
1918 static int gcount = 0;
1919 int zz;
1920 int menu = 0;
1921 char *str;
1922 t_glist *x = (t_glist *)pd_new(canvas_class);
1923 glist_init(x);
1924 x->gl_obj.te_type = T_OBJECT;
1925 if (!*sym->s_name)
1926 {
1927 char buf[40];
1928 sprintf(buf, "graph%d", ++gcount);
1929 sym = gensym(buf);
1930 menu = 1;
1931 }
1932 else if (!strncmp((str = sym->s_name), "graph", 5)
1933 && (zz = atoi(str + 5)) > gcount)
1934 gcount = zz;
1935 /* in 0.34 and earlier, the pixel rectangle and the y bounds were
1936 reversed; this would behave the same, except that the dialog window
1937 would be confusing. The "correct" way is to have "py1" be the value
1938 that is higher on the screen. */
1939 if (py2 < py1)
1940 {
1941 float zz;
1942 zz = y2;
1943 y2 = y1;
1944 y1 = zz;
1945 zz = py2;
1946 py2 = py1;
1947 py1 = zz;
1948 }
1949 if (x1 == x2 || y1 == y2)
1950 x1 = 0, x2 = 100, y1 = 1, y2 = -1;
1951 if (px1 >= px2 || py1 >= py2)
1952 px1 = 100, py1 = 20, px2 = 100 + GLIST_DEFGRAPHWIDTH,
1953 py2 = 20 + GLIST_DEFGRAPHHEIGHT;
1954 x->gl_name = sym;
1955 x->gl_x1 = x1;
1956 x->gl_x2 = x2;
1957 x->gl_y1 = y1;
1958 x->gl_y2 = y2;
1959 x->gl_obj.te_xpix = px1;
1960 x->gl_obj.te_ypix = py1;
1961 x->gl_pixwidth = px2 - px1;
1962 x->gl_pixheight = py2 - py1;
1963 x->gl_font = (canvas_getcurrent() ?
1964 canvas_getcurrent()->gl_font : sys_defaultfont);
1965 x->gl_screenx1 = x->gl_screeny1 = 0;
1966 x->gl_screenx2 = 240;
1967 x->gl_screeny2 = 300;
1968 if (strcmp(x->gl_name->s_name, "Pd"))
1969 pd_bind(&x->gl_pd, canvas_makebindsym(x->gl_name));
1970 x->gl_owner = g;
1971 x->gl_stretch = 1;
1972 x->gl_isgraph = 1;
1973 x->gl_obj.te_binbuf = binbuf_new();
1974 binbuf_addv(x->gl_obj.te_binbuf, "s", gensym("graph"));
1975 if (!menu)
1976 pd_pushsym(&x->gl_pd);
1977 glist_add(g, &x->gl_gobj);
1978 if (glist_isvisible(g))
1979 canvas_create_editor(x, 1);
1980 return (x);
1981}
1982
1983 /* call glist_addglist from a Pd message */
1984void glist_glist(t_glist *g, t_symbol *s, int argc, t_atom *argv)
1985{
1986 t_symbol *sym = atom_getsymbolarg(0, argc, argv);
1987 float x1 = atom_getfloatarg(1, argc, argv);
1988 float y1 = atom_getfloatarg(2, argc, argv);
1989 float x2 = atom_getfloatarg(3, argc, argv);
1990 float y2 = atom_getfloatarg(4, argc, argv);
1991 float px1 = atom_getfloatarg(5, argc, argv);
1992 float py1 = atom_getfloatarg(6, argc, argv);
1993 float px2 = atom_getfloatarg(7, argc, argv);
1994 float py2 = atom_getfloatarg(8, argc, argv);
1995 glist_addglist(g, sym, x1, y1, x2, y2, px1, py1, px2, py2);
1996}
1997
1998 /* return true if the glist should appear as a graph on parent;
1999 otherwise it appears as a text box. */
2000int glist_isgraph(t_glist *x)
2001{
2002 return (x->gl_isgraph);
2003}
2004
2005 /* This is sent from the GUI to inform a toplevel that its window has been
2006 moved or resized. */
2007static void canvas_setbounds(t_canvas *x, int x1, int y1, int x2, int y2)
2008{
2009 int heightwas = y2 - y1;
2010 int heightchange = y2 - y1 - (x->gl_screeny2 - x->gl_screeny1);
2011 x->gl_screenx1 = x1;
2012 x->gl_screeny1 = y1;
2013 x->gl_screenx2 = x2;
2014 x->gl_screeny2 = y2;
2015 /* post("set bounds %d %d %d %d", x1, y1, x2, y2); */
2016 if (!glist_isgraph(x) && (x->gl_y2 < x->gl_y1))
2017 {
2018 /* if it's flipped so that y grows upward,
2019 fix so that zero is bottom edge and redraw. This is
2020 only appropriate if we're a regular "text" object on the
2021 parent. */
2022 float diff = x->gl_y1 - x->gl_y2;
2023 t_gobj *y;
2024 x->gl_y1 = heightwas * diff;
2025 x->gl_y2 = x->gl_y1 - diff;
2026 /* and move text objects accordingly; they should stick
2027 to the bottom, not the top. */
2028 for (y = x->gl_list; y; y = y->g_next)
2029 if (pd_checkobject(&y->g_pd))
2030 gobj_displace(y, x, 0, heightchange);
2031 canvas_redraw(x);
2032 }
2033}
2034
2035t_symbol *canvas_makebindsym(t_symbol *s)
2036{
2037 char buf[MAXPDSTRING];
2038 strcpy(buf, "pd-");
2039 strcat(buf, s->s_name);
2040 return (gensym(buf));
2041}
2042
2043void canvas_reflecttitle(t_canvas *x)
2044{
2045 char namebuf[MAXPDSTRING];
2046 t_canvasenvironment *env = canvas_getenv(x);
2047 if (env->ce_argc)
2048 {
2049 int i;
2050 strcpy(namebuf, " (");
2051 for (i = 0; i < env->ce_argc; i++)
2052 {
2053 if (strlen(namebuf) > MAXPDSTRING/2 - 5)
2054 break;
2055 if (i != 0)
2056 strcat(namebuf, " ");
2057 atom_string(&env->ce_argv[i], namebuf + strlen(namebuf),
2058 MAXPDSTRING/2);
2059 }
2060 strcat(namebuf, ")");
2061 }
2062 else namebuf[0] = 0;
2063 sys_vgui("wm title .x%x {%s%c%s - %s}\n",
2064 x, x->gl_name->s_name, (x->gl_dirty? '*' : ' '), namebuf,
2065 canvas_getdir(x)->s_name);
2066}
2067
2068void canvas_dirty(t_canvas *x, t_int n)
2069{
2070 t_canvas *x2 = canvas_getrootfor(x);
2071 if ((unsigned)n != x2->gl_dirty)
2072 {
2073 x2->gl_dirty = n;
2074 canvas_reflecttitle(x2);
2075 }
2076}
2077
2078 /* the window becomes "mapped" (visible and not miniaturized) or
2079 "unmapped" (either miniaturized or just plain gone.) This should be
2080 called from the GUI after the fact to "notify" us that we're mapped. */
2081void canvas_map(t_canvas *x, t_floatarg f)
2082{
2083 int flag = (f != 0);
2084 t_gobj *y;
2085 if (flag)
2086 {
2087 if (!glist_isvisible(x))
2088 {
2089 t_selection *sel;
2090 if (!x->gl_havewindow)
2091 {
2092 bug("canvas_map");
2093 canvas_vis(x, 1);
2094 }
2095 for (y = x->gl_list; y; y = y->g_next)
2096 gobj_vis(y, x, 1);
2097 for (sel = x->gl_editor->e_selection; sel; sel = sel->sel_next)
2098 gobj_select(sel->sel_what, x, 1);
2099 x->gl_mapped = 1;
2100 canvas_drawlines(x);
2101 /* simulate a mouse up so u_main will calculate scrollbars...
2102 ugly! */
2103 sys_vgui("pdtk_canvas_mouseup .x%x.c 0 0 0\n", x);
2104 }
2105 }
2106 else
2107 {
2108 if (glist_isvisible(x))
2109 {
2110 /* just clear out the whole canvas... */
2111 sys_vgui(".x%x.c delete all\n", x);
2112 /* alternatively, we could have erased them one by one...
2113 for (y = x->gl_list; y; y = y->g_next)
2114 gobj_vis(y, x, 0);
2115 ... but we should go through and erase the lines as well
2116 if we do it that way. */
2117 x->gl_mapped = 0;
2118 }
2119 }
2120}
2121
2122void canvas_redraw(t_canvas *x)
2123{
2124 if (glist_isvisible(x))
2125 {
2126 canvas_map(x, 0);
2127 canvas_map(x, 1);
2128 }
2129}
2130
2131/* ---- editors -- perhaps this and "vis" should go to g_editor.c ------- */
2132
2133static t_editor *editor_new(t_glist *owner)
2134{
2135 char buf[40];
2136 t_editor *x = (t_editor *)getbytes(sizeof(*x));
2137 x->e_connectbuf = binbuf_new();
2138 x->e_deleted = binbuf_new();
2139 x->e_glist = owner;
2140 sprintf(buf, ".x%x", (t_int)owner);
2141 x->e_guiconnect = guiconnect_new(&owner->gl_pd, gensym(buf));
2142 return (x);
2143}
2144
2145static void editor_free(t_editor *x, t_glist *y)
2146{
2147 glist_noselect(y);
2148 guiconnect_notarget(x->e_guiconnect, 1000);
2149 binbuf_free(x->e_connectbuf);
2150 binbuf_free(x->e_deleted);
2151 freebytes((void *)x, sizeof(*x));
2152}
2153
2154 /* recursively create or destroy all editors of a glist and its
2155 sub-glists, as long as they aren't toplevels. */
2156void canvas_create_editor(t_glist *x, int createit)
2157{
2158 t_gobj *y;
2159 t_object *ob;
2160 if (createit)
2161 {
2162 if (x->gl_editor)
2163 bug("canvas_create_editor");
2164 else
2165 {
2166 x->gl_editor = editor_new(x);
2167 for (y = x->gl_list; y; y = y->g_next)
2168 if (ob = pd_checkobject(&y->g_pd))
2169 rtext_new(x, ob);
2170 }
2171 }
2172 else
2173 {
2174 if (!x->gl_editor)
2175 bug("canvas_create_editor");
2176 else
2177 {
2178 for (y = x->gl_list; y; y = y->g_next)
2179 if (ob = pd_checkobject(&y->g_pd))
2180 rtext_free(glist_findrtext(x, ob));
2181 editor_free(x->gl_editor, x);
2182 x->gl_editor = 0;
2183 }
2184 }
2185 for (y = x->gl_list; y; y = y->g_next)
2186 if (pd_class(&y->g_pd) == canvas_class &&
2187 ((t_canvas *)y)->gl_isgraph)
2188 canvas_create_editor((t_canvas *)y, createit);
2189}
2190
2191 /* we call this when we want the window to become visible, mapped, and
2192 in front of all windows; or with "f" zero, when we want to get rid of
2193 the window. */
2194void canvas_vis(t_canvas *x, t_floatarg f)
2195{
2196 char buf[30];
2197 int flag = (f != 0);
2198 if (flag)
2199 {
2200 /* test if we're already visible and toplevel */
2201 if (glist_isvisible(x) && !x->gl_isgraph)
2202 { /* just put us in front */
2203#ifdef MSW
2204 canvas_vis(x, 0);
2205 canvas_vis(x, 1);
2206#else
2207 sys_vgui("raise .x%x\n", x);
2208 sys_vgui("focus .x%x.c\n", x);
2209 sys_vgui("wm deiconify .x%x\n", x);
2210#endif
2211 }
2212 else
2213 {
2214 canvas_create_editor(x, 1);
2215 sys_vgui("pdtk_canvas_new .x%x %d %d +%d+%d %d\n", x,
2216 (int)(x->gl_screenx2 - x->gl_screenx1),
2217 (int)(x->gl_screeny2 - x->gl_screeny1),
2218 (int)(x->gl_screenx1), (int)(x->gl_screeny1),
2219 x->gl_edit);
2220 canvas_reflecttitle(x);
2221 x->gl_havewindow = 1;
2222 canvas_updatewindowlist();
2223 }
2224 }
2225 else /* make invisible */
2226 {
2227 int i;
2228 t_canvas *x2;
2229 if (!x->gl_havewindow)
2230 {
2231 /* bug workaround -- a graph in a visible patch gets "invised"
2232 when the patch is closed, and must lose the editor here. It's
2233 probably not the natural place to do this. Other cases like
2234 subpatches fall here too but don'd need the editor freed, so
2235 we check if it exists. */
2236 if (x->gl_editor)
2237 canvas_create_editor(x, 0);
2238 return;
2239 }
2240 glist_noselect(x);
2241 if (glist_isvisible(x))
2242 canvas_map(x, 0);
2243 canvas_create_editor(x, 0);
2244 sys_vgui("destroy .x%x\n", x);
2245 for (i = 1, x2 = x; x2; x2 = x2->gl_next, i++)
2246 ;
2247 sys_vgui(".mbar.find delete %d\n", i);
2248 /* if we're a graph on our parent, and if the parent exists
2249 and is visible, show ourselves on parent. */
2250 if (glist_isgraph(x) && x->gl_owner)
2251 {
2252 t_glist *gl2 = x->gl_owner;
2253 canvas_create_editor(x, 1);
2254 if (glist_isvisible(gl2))
2255 gobj_vis(&x->gl_gobj, gl2, 0);
2256 x->gl_havewindow = 0;
2257 if (glist_isvisible(gl2))
2258 gobj_vis(&x->gl_gobj, gl2, 1);
2259 }
2260 else x->gl_havewindow = 0;
2261 canvas_updatewindowlist();
2262 }
2263}
2264
2265 /* we call this on a non-toplevel glist to "open" it into its
2266 own window. */
2267void glist_menu_open(t_glist *x)
2268{
2269 if (glist_isvisible(x) && !glist_istoplevel(x))
2270 {
2271 t_glist *gl2 = x->gl_owner;
2272 if (!gl2)
2273 bug("canvas_vis"); /* shouldn't happen but don't get too upset. */
2274 else
2275 {
2276 /* erase ourself in parent window */
2277 gobj_vis(&x->gl_gobj, gl2, 0);
2278 /* get rid of our editor (and subeditors) */
2279 canvas_create_editor(x, 0);
2280 x->gl_havewindow = 1;
2281 /* redraw ourself in parent window (blanked out this time) */
2282 gobj_vis(&x->gl_gobj, gl2, 1);
2283 }
2284 }
2285 canvas_vis(x, 1);
2286}
2287
2288int glist_isvisible(t_glist *x)
2289{
2290 return ((!x->gl_loading) && glist_getcanvas(x)->gl_mapped);
2291}
2292
2293int glist_istoplevel(t_glist *x)
2294{
2295 /* we consider a graph "toplevel" if it has its own window
2296 or if it appears as a box in its parent window so that we
2297 don't draw the actual contents there. */
2298 return (x->gl_havewindow || !x->gl_isgraph);
2299}
2300
2301int glist_getfont(t_glist *x)
2302{
2303 return (glist_getcanvas(x)->gl_font);
2304}
2305
2306void canvas_free(t_canvas *x)
2307{
2308 t_gobj *y;
2309 int dspstate = canvas_suspend_dsp();
2310 canvas_noundo(x);
2311 if (canvas_editing == x)
2312 canvas_editing = 0;
2313 if (canvas_whichfind == x)
2314 canvas_whichfind = 0;
2315 glist_noselect(x);
2316 while (y = x->gl_list)
2317 glist_delete(x, y);
2318 canvas_vis(x, 0);
2319
2320 if (strcmp(x->gl_name->s_name, "Pd"))
2321 pd_unbind(&x->gl_pd, canvas_makebindsym(x->gl_name));
2322 if (x->gl_env)
2323 {
2324 freebytes(x->gl_env->ce_argv, x->gl_env->ce_argc * sizeof(t_atom));
2325 freebytes(x->gl_env, sizeof(*x->gl_env));
2326 }
2327 canvas_resume_dsp(dspstate);
2328 glist_cleanup(x);
2329 gfxstub_deleteforkey(x); /* probably unnecessary */
2330 if (!x->gl_owner)
2331 canvas_takeofflist(x);
2332}
2333
2334/* ----------------- lines ---------- */
2335
2336static void canvas_drawlines(t_canvas *x)
2337{
2338 t_linetraverser t;
2339 t_outconnect *oc;
2340 {
2341 linetraverser_start(&t, x);
2342 while (oc = linetraverser_next(&t))
2343 sys_vgui(".x%x.c create line %d %d %d %d -width %d -tags l%x\n",
2344 glist_getcanvas(x),
2345 t.tr_lx1, t.tr_ly1, t.tr_lx2, t.tr_ly2,
2346 (outlet_getsymbol(t.tr_outlet) == &s_signal ? 2:1),
2347 oc);
2348 }
2349}
2350
2351void canvas_fixlinesfor(t_canvas *x, t_text *text)
2352{
2353 t_linetraverser t;
2354 t_outconnect *oc;
2355
2356 linetraverser_start(&t, x);
2357 while (oc = linetraverser_next(&t))
2358 {
2359 if (t.tr_ob == text || t.tr_ob2 == text)
2360 {
2361 sys_vgui(".x%x.c coords l%x %d %d %d %d\n",
2362 glist_getcanvas(x), oc,
2363 t.tr_lx1, t.tr_ly1, t.tr_lx2, t.tr_ly2);
2364 }
2365 }
2366}
2367
2368 /* kill all lines for the object */
2369void canvas_deletelinesfor(t_canvas *x, t_text *text)
2370{
2371 t_linetraverser t;
2372 t_outconnect *oc;
2373 linetraverser_start(&t, x);
2374 while (oc = linetraverser_next(&t))
2375 {
2376 if (t.tr_ob == text || t.tr_ob2 == text)
2377 {
2378 if (x->gl_editor)
2379 {
2380 sys_vgui(".x%x.c delete l%x\n",
2381 glist_getcanvas(x), oc);
2382 }
2383 obj_disconnect(t.tr_ob, t.tr_outno, t.tr_ob2, t.tr_inno);
2384 }
2385 }
2386}
2387
2388 /* kill all lines for one inlet or outlet */
2389void canvas_deletelinesforio(t_canvas *x, t_text *text,
2390 t_inlet *inp, t_outlet *outp)
2391{
2392 t_linetraverser t;
2393 t_outconnect *oc;
2394 linetraverser_start(&t, x);
2395 while (oc = linetraverser_next(&t))
2396 {
2397 if ((t.tr_ob == text && t.tr_outlet == outp) ||
2398 (t.tr_ob2 == text && t.tr_inlet == inp))
2399 {
2400 if (x->gl_editor)
2401 {
2402 sys_vgui(".x%x.c delete l%x\n",
2403 glist_getcanvas(x), oc);
2404 }
2405 obj_disconnect(t.tr_ob, t.tr_outno, t.tr_ob2, t.tr_inno);
2406 }
2407 }
2408}
2409
2410static void canvas_pop(t_canvas *x, t_floatarg fvis)
2411{
2412 if (fvis != 0)
2413 canvas_vis(x, 1);
2414 pd_popsym(&x->gl_pd);
2415 canvas_resortinlets(x);
2416 canvas_resortoutlets(x);
2417 x->gl_loading = 0;
2418}
2419
2420void canvas_objfor(t_glist *gl, t_text *x, int argc, t_atom *argv);
2421
2422
2423void canvas_restore(t_canvas *x, t_symbol *s, int argc, t_atom *argv)
2424{ /* IOhannes */
2425 t_pd *z;
2426 /* this should be unnecessary, but sometimes the canvas's name gets
2427 out of sync with the owning box's argument; this fixes that */
2428 if (argc > 3)
2429 {
2430 t_atom *ap=argv+3;
2431 if (ap->a_type == A_SYMBOL)
2432 {
2433 char *buf=ap->a_w.w_symbol->s_name, *bufp;
2434 if (*buf == '$' && buf[1] >= '0' && buf[1] <= '9')
2435 {
2436 for (bufp = buf+2; *bufp; bufp++)
2437 if (*bufp < '0' || *bufp > '9')
2438 {
2439 SETDOLLSYM(ap, gensym(buf+1));
2440 goto didit;
2441 }
2442 SETDOLLAR(ap, atoi(buf+1));
2443 didit: ;
2444 }
2445 }
2446
2447 if (ap->a_type == A_DOLLSYM)
2448 {
2449 t_canvasenvironment *e = canvas_getenv(canvas_getcurrent());
2450 canvas_rename(x, binbuf_realizedollsym(ap->a_w.w_symbol,
2451 e->ce_argc, e->ce_argv, 1), 0);
2452 }
2453 else if (ap->a_type == A_SYMBOL)
2454 canvas_rename(x, argv[3].a_w.w_symbol, 0);
2455 }
2456 canvas_pop(x, x->gl_willvis);
2457
2458 if (!(z = gensym("#X")->s_thing)) error("canvas_restore: out of context");
2459 else if (*z != canvas_class) error("canvas_restore: wasn't a canvas");
2460 else
2461 {
2462 t_canvas *x2 = (t_canvas *)z;
2463 x->gl_owner = x2;
2464 canvas_objfor(x2, &x->gl_obj, argc, argv);
2465 }
2466}
2467
2468static void canvas_loadbangabstractions(t_canvas *x)
2469{
2470 t_gobj *y;
2471 t_symbol *s = gensym("loadbang");
2472 for (y = x->gl_list; y; y = y->g_next)
2473 if (pd_class(&y->g_pd) == canvas_class)
2474 {
2475 if (canvas_isabstraction((t_canvas *)y))
2476 canvas_loadbang((t_canvas *)y);
2477 else
2478 canvas_loadbangabstractions((t_canvas *)y);
2479 }
2480}
2481
2482void canvas_loadbangsubpatches(t_canvas *x)
2483{
2484 t_gobj *y;
2485 t_symbol *s = gensym("loadbang");
2486 for (y = x->gl_list; y; y = y->g_next)
2487 if (pd_class(&y->g_pd) == canvas_class)
2488 {
2489 if (!canvas_isabstraction((t_canvas *)y))
2490 canvas_loadbangsubpatches((t_canvas *)y);
2491 }
2492 for (y = x->gl_list; y; y = y->g_next)
2493 if ((pd_class(&y->g_pd) != canvas_class) &&
2494 zgetfn(&y->g_pd, s))
2495 pd_vmess(&y->g_pd, s, "");
2496}
2497
2498void canvas_loadbang(t_canvas *x)
2499{
2500 t_gobj *y;
2501 canvas_loadbangabstractions(x);
2502 canvas_loadbangsubpatches(x);
2503}
2504
2505 /* When you ask a canvas its size the result is 2 pixels more than what
2506 you gave it to open it; perhaps there's a 1-pixel border all around it
2507 or something. Anyway, we just add the 2 pixels back here; seems we
2508 have to do this for linux but not MSW; not sure about MacOS. */
2509
2510#ifdef MSW
2511#define HORIZBORDER 0
2512#define VERTBORDER 0
2513#else
2514#define HORIZBORDER 2
2515#define VERTBORDER 2
2516#endif
2517
2518static void canvas_relocate(t_canvas *x, t_symbol *canvasgeom,
2519 t_symbol *topgeom)
2520{
2521 int cxpix, cypix, cw, ch, txpix, typix, tw, th;
2522 if (sscanf(canvasgeom->s_name, "%dx%d+%d+%d", &cw, &ch, &cxpix, &cypix)
2523 < 4 ||
2524 sscanf(topgeom->s_name, "%dx%d+%d+%d", &tw, &th, &txpix, &typix) < 4)
2525 bug("canvas_relocate");
2526 /* for some reason this is initially called with cw=ch=1 so
2527 we just suppress that here. */
2528 if (cw > 5 && ch > 5)
2529 canvas_setbounds(x, txpix, typix,
2530 txpix + cw - HORIZBORDER, typix + ch - VERTBORDER);
2531}
2532
2533void canvas_popabstraction(t_canvas *x)
2534{
2535 newest = &x->gl_pd;
2536 pd_popsym(&x->gl_pd);
2537 x->gl_loading = 0;
2538 canvas_resortinlets(x);
2539 canvas_resortoutlets(x);
2540}
2541
2542void canvas_logerror(t_object *y)
2543{
2544#ifdef LATER
2545 canvas_vis(x, 1);
2546 if (!glist_isselected(x, &y->ob_g))
2547 glist_select(x, &y->ob_g);
2548#endif
2549}
2550
2551/* -------------------------- subcanvases ---------------------- */
2552
2553static void *subcanvas_new(t_symbol *s)
2554{
2555 t_atom a[6];
2556 t_canvas *x, *z = canvas_getcurrent();
2557 if (!*s->s_name) s = gensym("/SUBPATCH/");
2558 SETFLOAT(a, 0);
2559 SETFLOAT(a+1, GLIST_DEFCANVASYLOC);
2560 SETFLOAT(a+2, GLIST_DEFCANVASWIDTH);
2561 SETFLOAT(a+3, GLIST_DEFCANVASHEIGHT);
2562 SETSYMBOL(a+4, s);
2563 SETFLOAT(a+5, 1);
2564 x = canvas_new(0, 0, 6, a);
2565 x->gl_owner = z;
2566 canvas_pop(x, 1);
2567 return (x);
2568}
2569
2570static void canvas_click(t_canvas *x,
2571 t_floatarg xpos, t_floatarg ypos,
2572 t_floatarg shift, t_floatarg ctrl, t_floatarg alt)
2573{
2574 canvas_vis(x, 1);
2575}
2576
2577
2578 /* find out from subcanvas contents how much to fatten the box */
2579void canvas_fattensub(t_canvas *x,
2580 int *xp1, int *yp1, int *xp2, int *yp2)
2581{
2582 t_gobj *y;
2583 *xp2 += 50; /* fake for now */
2584 *yp2 += 50;
2585}
2586
2587static void canvas_rename_method(t_canvas *x, t_symbol *s, int ac, t_atom *av)
2588{
2589 if (ac && av->a_type == A_SYMBOL)
2590 canvas_rename(x, av->a_w.w_symbol, 0);
2591 else canvas_rename(x, gensym("Pd"), 0);
2592}
2593
2594/* ------------------ table ---------------------------*/
2595
2596static int tabcount = 0;
2597
2598static void *table_new(t_symbol *s, t_floatarg f)
2599{
2600 t_atom a[9];
2601 t_glist *gl;
2602 t_canvas *x, *z = canvas_getcurrent();
2603 if (s == &s_)
2604 {
2605 char tabname[255];
2606 t_symbol *t = gensym("table");
2607 sprintf(tabname, "%s%d", t->s_name, tabcount++);
2608 s = gensym(tabname);
2609 }
2610 if (f <= 1)
2611 f = 100;
2612 SETFLOAT(a, 0);
2613 SETFLOAT(a+1, GLIST_DEFCANVASYLOC);
2614 SETFLOAT(a+2, 600);
2615 SETFLOAT(a+3, 400);
2616 SETSYMBOL(a+4, s);
2617 SETFLOAT(a+5, 0);
2618 x = canvas_new(0, 0, 6, a);
2619
2620 x->gl_owner = z;
2621
2622 /* create a graph for the table */
2623 gl = glist_addglist((t_glist*)x, &s_, 0, -1, (f > 1 ? f-1 : 1), 1,
2624 50, 350, 550, 50);
2625
2626 graph_array(gl, s, &s_float, f, 0);
2627
2628 canvas_pop(x, 0);
2629
2630 return (x);
2631}
2632
2633 /* return true if the "canvas" object is an abstraction (so we don't
2634 save its contents, fogr example.) */
2635int canvas_isabstraction(t_canvas *x)
2636{
2637 return (x->gl_env != 0);
2638}
2639
2640 /* return true if the "canvas" object is a "table". */
2641int canvas_istable(t_canvas *x)
2642{
2643 t_atom *argv = (x->gl_obj.te_binbuf? binbuf_getvec(x->gl_obj.te_binbuf):0);
2644 int argc = (x->gl_obj.te_binbuf? binbuf_getnatom(x->gl_obj.te_binbuf) : 0);
2645 int istable = (argc && argv[0].a_type == A_SYMBOL &&
2646 argv[0].a_w.w_symbol == gensym("table"));
2647 return (istable);
2648}
2649
2650 /* return true if the "canvas" object should be treated as a text
2651 object. This is true for abstractions but also for "table"s... */
2652int canvas_showtext(t_canvas *x)
2653{
2654 t_atom *argv = (x->gl_obj.te_binbuf? binbuf_getvec(x->gl_obj.te_binbuf):0);
2655 int argc = (x->gl_obj.te_binbuf? binbuf_getnatom(x->gl_obj.te_binbuf) : 0);
2656 int isarray = (argc && argv[0].a_type == A_SYMBOL &&
2657 argv[0].a_w.w_symbol == gensym("graph"));
2658 return (!isarray);
2659}
2660
2661static void canvas_dodsp(t_canvas *x, int toplevel, t_signal **sp);
2662static void canvas_dsp(t_canvas *x, t_signal **sp)
2663{
2664 canvas_dodsp(x, 0, sp);
2665}
2666
2667 /* get the document containing this canvas */
2668t_canvas *canvas_getrootfor(t_canvas *x)
2669{
2670 if ((!x->gl_owner) || canvas_isabstraction(x))
2671 return (x);
2672 else return (canvas_getrootfor(x->gl_owner));
2673}
2674
2675/* ------------------------- DSP chain handling ------------------------- */
2676
2677EXTERN_STRUCT _dspcontext;
2678#define t_dspcontext struct _dspcontext
2679
2680void ugen_start(void);
2681void ugen_stop(void);
2682
2683t_dspcontext *ugen_start_graph(int toplevel, t_signal **sp,
2684 int ninlets, int noutlets);
2685void ugen_add(t_dspcontext *dc, t_object *x);
2686void ugen_connect(t_dspcontext *dc, t_object *x1, int outno,
2687 t_object *x2, int inno);
2688void ugen_done_graph(t_dspcontext *dc);
2689
2690 /* schedule one canvas for DSP. This is called below for all "root"
2691 canvases, but is also called from the "dsp" method for sub-
2692 canvases, which are treated almost like any other tilde object. */
2693
2694static void canvas_dodsp(t_canvas *x, int toplevel, t_signal **sp)
2695{
2696 t_linetraverser t;
2697 t_outconnect *oc;
2698 t_gobj *y;
2699 t_object *ob;
2700 t_symbol *dspsym = gensym("dsp");
2701 t_dspcontext *dc;
2702
2703 /* create a new "DSP graph" object to use in sorting this canvas.
2704 If we aren't toplevel, there are already other dspcontexts around. */
2705
2706 dc = ugen_start_graph(toplevel, sp,
2707 obj_nsiginlets(&x->gl_obj),
2708 obj_nsigoutlets(&x->gl_obj));
2709
2710 /* find all the "dsp" boxes and add them to the graph */
2711
2712 for (y = x->gl_list; y; y = y->g_next)
2713 if ((ob = pd_checkobject(&y->g_pd)) && zgetfn(&y->g_pd, dspsym))
2714 ugen_add(dc, ob);
2715
2716 /* ... and all dsp interconnections */
2717 linetraverser_start(&t, x);
2718 while (oc = linetraverser_next(&t))
2719 if (obj_issignaloutlet(t.tr_ob, t.tr_outno))
2720 ugen_connect(dc, t.tr_ob, t.tr_outno, t.tr_ob2, t.tr_inno);
2721
2722 /* finally, sort them and add them to the DSP chain */
2723 ugen_done_graph(dc);
2724}
2725
2726 /* this routine starts DSP for all root canvases. */
2727static void canvas_start_dsp(void)
2728{
2729 t_canvas *x;
2730 if (canvas_dspstate) ugen_stop();
2731 else sys_gui("pdtk_pd_dsp ON\n");
2732 ugen_start();
2733
2734 for (x = canvas_list; x; x = x->gl_next)
2735 canvas_dodsp(x, 1, 0);
2736
2737 canvas_dspstate = 1;
2738}
2739
2740static void canvas_stop_dsp(void)
2741{
2742 if (canvas_dspstate)
2743 {
2744 ugen_stop();
2745 sys_gui("pdtk_pd_dsp OFF\n");
2746 canvas_dspstate = 0;
2747 }
2748}
2749
2750 /* DSP can be suspended before, and resumed after, operations which
2751 might affect the DSP chain. For example, we suspend before loading and
2752 resume afterward, so that DSP doesn't get resorted for every DSP object
2753 int the patch. */
2754
2755int canvas_suspend_dsp(void)
2756{
2757 int rval = canvas_dspstate;
2758 if (rval) canvas_stop_dsp();
2759 return (rval);
2760}
2761
2762void canvas_resume_dsp(int oldstate)
2763{
2764 if (oldstate) canvas_start_dsp();
2765}
2766
2767 /* this is equivalent to suspending and resuming in one step. */
2768void canvas_update_dsp(void)
2769{
2770 if (canvas_dspstate) canvas_start_dsp();
2771}
2772
2773void glob_dsp(void *dummy, t_symbol *s, int argc, t_atom *argv)
2774{
2775 int newstate;
2776 if (argc)
2777 {
2778 newstate = atom_getintarg(0, argc, argv);
2779 if (newstate && !canvas_dspstate)
2780 {
2781 sys_set_audio_state(1);
2782 canvas_start_dsp();
2783 }
2784 else if (!newstate && canvas_dspstate)
2785 {
2786 canvas_stop_dsp();
2787 sys_set_audio_state(0);
2788 }
2789 }
2790 else post("dsp state %d", canvas_dspstate);
2791}
2792
2793 /* LATER replace this with a queueing scheme */
2794void glist_redrawitem(t_glist *owner, t_gobj *gobj)
2795{
2796 if (glist_isvisible(owner))
2797 {
2798 gobj_vis(gobj, owner, 0);
2799 gobj_vis(gobj, owner, 1);
2800 }
2801}
2802
2803 /* redraw all "scalars" (do this if a drawing command is changed.)
2804 LATER we'll use the "template" information to select which ones we
2805 redraw. */
2806static void glist_redrawall(t_glist *gl)
2807{
2808 t_gobj *g;
2809 int vis = glist_isvisible(gl);
2810 for (g = gl->gl_list; g; g = g->g_next)
2811 {
2812 t_class *cl;
2813 if (vis && g->g_pd == scalar_class)
2814 glist_redrawitem(gl, g);
2815 else if (g->g_pd == canvas_class)
2816 glist_redrawall((t_glist *)g);
2817 }
2818}
2819
2820 /* public interface for above */
2821void canvas_redrawallfortemplate(t_canvas *templatecanvas)
2822{
2823 t_canvas *x;
2824 /* find all root canvases */
2825 for (x = canvas_list; x; x = x->gl_next)
2826 glist_redrawall(x);
2827}
2828
2829/* ------------------------------- setup routine ------------------------ */
2830
2831 /* why are some of these "glist" and others "canvas"? */
2832extern void glist_text(t_glist *x, t_symbol *s, int argc, t_atom *argv);
2833extern void canvas_obj(t_glist *gl, t_symbol *s, int argc, t_atom *argv);
2834extern void canvas_bng(t_glist *gl, t_symbol *s, int argc, t_atom *argv);
2835extern void canvas_toggle(t_glist *gl, t_symbol *s, int argc, t_atom *argv);
2836extern void canvas_vslider(t_glist *gl, t_symbol *s, int argc, t_atom *argv);
2837extern void canvas_hslider(t_glist *gl, t_symbol *s, int argc, t_atom *argv);
2838extern void canvas_vdial(t_glist *gl, t_symbol *s, int argc, t_atom *argv);
2839 /* old version... */
2840extern void canvas_hdial(t_glist *gl, t_symbol *s, int argc, t_atom *argv);
2841extern void canvas_hdial(t_glist *gl, t_symbol *s, int argc, t_atom *argv);
2842 /* new version: */
2843extern void canvas_hradio(t_glist *gl, t_symbol *s, int argc, t_atom *argv);
2844extern void canvas_vradio(t_glist *gl, t_symbol *s, int argc, t_atom *argv);
2845extern void canvas_vumeter(t_glist *gl, t_symbol *s, int argc, t_atom *argv);
2846extern void canvas_mycnv(t_glist *gl, t_symbol *s, int argc, t_atom *argv);
2847extern void canvas_numbox(t_glist *gl, t_symbol *s, int argc, t_atom *argv);
2848extern void canvas_msg(t_glist *gl, t_symbol *s, int argc, t_atom *argv);
2849extern void canvas_floatatom(t_glist *gl, t_symbol *s, int argc, t_atom *argv);
2850extern void canvas_symbolatom(t_glist *gl, t_symbol *s, int argc, t_atom *argv);
2851extern void glist_scalar(t_glist *canvas, t_symbol *s, int argc, t_atom *argv);
2852
2853void g_graph_setup(void);
2854void g_editor_setup(void);
2855void g_readwrite_setup(void);
2856extern void graph_properties(t_gobj *z, t_glist *owner);
2857
2858void g_canvas_setup(void)
2859{
2860 /* we prevent the user from typing "canvas" in an object box
2861 by sending 0 for a creator function. */
2862 canvas_class = class_new(gensym("canvas"), 0,
2863 (t_method)canvas_free, sizeof(t_canvas), CLASS_NOINLET, 0);
2864 /* here is the real creator function, invoked in patch files
2865 by sending the "canvas" message to #N, which is bound
2866 to pd_camvasmaker. */
2867 class_addmethod(pd_canvasmaker, (t_method)canvas_new, gensym("canvas"),
2868 A_GIMME, 0);
2869 class_addmethod(canvas_class, (t_method)canvas_restore,
2870 gensym("restore"), A_GIMME, 0);
2871 class_addmethod(canvas_class, (t_method)canvas_coords,
2872 gensym("coords"), A_GIMME, 0);
2873
2874/* -------------------------- objects ----------------------------- */
2875 class_addmethod(canvas_class, (t_method)canvas_obj,
2876 gensym("obj"), A_GIMME, A_NULL);
2877 class_addmethod(canvas_class, (t_method)canvas_msg,
2878 gensym("msg"), A_GIMME, A_NULL);
2879 class_addmethod(canvas_class, (t_method)canvas_floatatom,
2880 gensym("floatatom"), A_GIMME, A_NULL);
2881 class_addmethod(canvas_class, (t_method)canvas_symbolatom,
2882 gensym("symbolatom"), A_GIMME, A_NULL);
2883 class_addmethod(canvas_class, (t_method)glist_text,
2884 gensym("text"), A_GIMME, A_NULL);
2885 class_addmethod(canvas_class, (t_method)glist_glist, gensym("graph"),
2886 A_GIMME, A_NULL);
2887 class_addmethod(canvas_class, (t_method)glist_scalar,
2888 gensym("scalar"), A_GIMME, A_NULL);
2889
2890 /* -------------- Thomas Musil's GUI objects ------------ */
2891 class_addmethod(canvas_class, (t_method)canvas_bng, gensym("bng"),
2892 A_GIMME, A_NULL);
2893 class_addmethod(canvas_class, (t_method)canvas_toggle, gensym("toggle"),
2894 A_GIMME, A_NULL);
2895 class_addmethod(canvas_class, (t_method)canvas_vslider, gensym("vslider"),
2896 A_GIMME, A_NULL);
2897 class_addmethod(canvas_class, (t_method)canvas_hslider, gensym("hslider"),
2898 A_GIMME, A_NULL);
2899 class_addmethod(canvas_class, (t_method)canvas_hdial, gensym("hdial"),
2900 A_GIMME, A_NULL);
2901 class_addmethod(canvas_class, (t_method)canvas_vdial, gensym("vdial"),
2902 A_GIMME, A_NULL);
2903 class_addmethod(canvas_class, (t_method)canvas_hradio, gensym("hradio"),
2904 A_GIMME, A_NULL);
2905 class_addmethod(canvas_class, (t_method)canvas_vradio, gensym("vradio"),
2906 A_GIMME, A_NULL);
2907 class_addmethod(canvas_class, (t_method)canvas_vumeter, gensym("vumeter"),
2908 A_GIMME, A_NULL);
2909 class_addmethod(canvas_class, (t_method)canvas_mycnv, gensym("mycnv"),
2910 A_GIMME, A_NULL);
2911 class_addmethod(canvas_class, (t_method)canvas_numbox, gensym("numbox"),
2912 A_GIMME, A_NULL);
2913
2914/* ------------------------ gui stuff --------------------------- */
2915 class_addmethod(canvas_class, (t_method)canvas_pop, gensym("pop"),
2916 A_DEFFLOAT, A_NULL);
2917 class_addmethod(canvas_class, (t_method)canvas_loadbang,
2918 gensym("loadbang"), A_NULL);
2919 class_addmethod(canvas_class, (t_method)canvas_relocate,
2920 gensym("relocate"), A_SYMBOL, A_SYMBOL, A_NULL);
2921 class_addmethod(canvas_class, (t_method)canvas_vis,
2922 gensym("vis"), A_FLOAT, A_NULL);
2923 class_addmethod(canvas_class, (t_method)glist_menu_open,
2924 gensym("menu-open"), A_NULL);
2925 class_addmethod(canvas_class, (t_method)canvas_map,
2926 gensym("map"), A_FLOAT, A_NULL);
2927 class_setpropertiesfn(canvas_class, graph_properties);
2928
2929/* ---------------------- list handling ------------------------ */
2930 class_addmethod(canvas_class, (t_method)glist_clear, gensym("clear"),
2931 A_NULL);
2932
2933/* ----- subcanvases, which you get by typing "pd" in a box ---- */
2934 class_addcreator((t_newmethod)subcanvas_new, gensym("pd"), A_DEFSYMBOL, 0);
2935 class_addcreator((t_newmethod)subcanvas_new, gensym("page"), A_DEFSYMBOL, 0);
2936
2937 class_addmethod(canvas_class, (t_method)canvas_click,
2938 gensym("click"), A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, 0);
2939 class_addmethod(canvas_class, (t_method)canvas_dsp, gensym("dsp"), 0);
2940 class_addmethod(canvas_class, (t_method)canvas_rename_method,
2941 gensym("rename"), A_GIMME, 0);
2942
2943/*---------------------------- tables -- GG ------------------- */
2944
2945 class_addcreator((t_newmethod)table_new, gensym("table"),
2946 A_DEFSYM, A_DEFFLOAT, 0);
2947
2948/* -------------- setups from other files for canvas_class ---------------- */
2949 g_graph_setup();
2950 g_editor_setup();
2951 g_readwrite_setup();
2952}