summaryrefslogtreecommitdiff
path: root/apps/plugins/pdbox/PDa/src/g_graph.c
diff options
context:
space:
mode:
Diffstat (limited to 'apps/plugins/pdbox/PDa/src/g_graph.c')
-rw-r--r--apps/plugins/pdbox/PDa/src/g_graph.c2224
1 files changed, 2224 insertions, 0 deletions
diff --git a/apps/plugins/pdbox/PDa/src/g_graph.c b/apps/plugins/pdbox/PDa/src/g_graph.c
new file mode 100644
index 0000000000..6a64900213
--- /dev/null
+++ b/apps/plugins/pdbox/PDa/src/g_graph.c
@@ -0,0 +1,2224 @@
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 deals with the behavior of glists as either "text objects" or
6"graphs" inside another glist. LATER move the inlet/outlet code of g_canvas.c
7to this file... */
8
9#include <stdlib.h>
10#include "m_pd.h"
11#include "t_tk.h"
12#include "g_canvas.h"
13#include <stdio.h>
14#include <string.h>
15
16/* ---------------------- forward definitions ----------------- */
17
18static void graph_vis(t_gobj *gr, t_glist *unused_glist, int vis);
19static void graph_graphrect(t_gobj *z, t_glist *glist,
20 int *xp1, int *yp1, int *xp2, int *yp2);
21static void graph_getrect(t_gobj *z, t_glist *glist,
22 int *xp1, int *yp1, int *xp2, int *yp2);
23
24/* -------------------- maintaining the list -------------------- */
25
26void glist_add(t_glist *x, t_gobj *y)
27{
28 t_object *ob;
29 y->g_next = 0;
30 if (!x->gl_list) x->gl_list = y;
31 else
32 {
33 t_gobj *y2;
34 for (y2 = x->gl_list; y2->g_next; y2 = y2->g_next);
35 y2->g_next = y;
36 }
37 if (x->gl_editor && (ob = pd_checkobject(&y->g_pd)))
38 rtext_new(x, ob);
39 if (glist_isvisible(x))
40 gobj_vis(y, x, 1);
41 if (class_isdrawcommand(y->g_pd))
42 canvas_redrawallfortemplate(glist_getcanvas(x));
43}
44
45 /* this is to protect against a hairy problem in which deleting
46 a sub-canvas might delete an inlet on a box, after the box had
47 been invisible-ized, so that we have to protect against redrawing it! */
48int canvas_setdeleting(t_canvas *x, int flag)
49{
50 int ret = x->gl_isdeleting;
51 x->gl_isdeleting = flag;
52 return (ret);
53}
54
55 /* delete an object from a glist and free it */
56void glist_delete(t_glist *x, t_gobj *y)
57{
58 t_gobj *g;
59 t_object *ob;
60 t_gotfn chkdsp = zgetfn(&y->g_pd, gensym("dsp"));
61 t_canvas *canvas = glist_getcanvas(x);
62 int drawcommand = class_isdrawcommand(y->g_pd);
63 int wasdeleting;
64
65 wasdeleting = canvas_setdeleting(canvas, 1);
66 if (x->gl_editor)
67 {
68 if (x->gl_editor->e_grab == y) x->gl_editor->e_grab = 0;
69 if (glist_isselected(x, y)) glist_deselect(x, y);
70
71 /* HACK -- we had phantom outlets not getting erased on the
72 screen because the canvas_setdeleting() mechanism is too
73 crude. LATER carefully set up rules for when the rtexts
74 should exist, so that they stay around until all the
75 steps of becoming invisible are done. In the meantime, just
76 zap the inlets and outlets here... */
77 if (pd_class(&y->g_pd) == canvas_class)
78 {
79 t_glist *gl = (t_glist *)y;
80 if (gl->gl_isgraph)
81 {
82 char tag[80];
83 sprintf(tag, "graph%x", (int)gl);
84 glist_eraseiofor(x, &gl->gl_obj, tag);
85 }
86 else
87 {
88 text_eraseborder(&gl->gl_obj, x,
89 rtext_gettag(glist_findrtext(x, &gl->gl_obj)));
90 }
91 }
92 }
93 gobj_delete(y, x);
94 if (glist_isvisible(canvas))
95 gobj_vis(y, x, 0);
96 if (x->gl_editor && (ob = pd_checkobject(&y->g_pd)))
97 rtext_new(x, ob);
98 if (x->gl_list == y) x->gl_list = y->g_next;
99 else for (g = x->gl_list; g; g = g->g_next)
100 if (g->g_next == y)
101 {
102 g->g_next = y->g_next;
103 break;
104 }
105 pd_free(&y->g_pd);
106 if (chkdsp) canvas_update_dsp();
107 if (drawcommand) canvas_redrawallfortemplate(canvas);
108 canvas_setdeleting(canvas, wasdeleting);
109 x->gl_valid = ++glist_valid;
110}
111
112 /* remove every object from a glist. Experimental. */
113void glist_clear(t_glist *x)
114{
115 t_gobj *y, *y2;
116 int dspstate = canvas_suspend_dsp();
117 while (y = x->gl_list)
118 glist_delete(x, y);
119 canvas_resume_dsp(dspstate);
120}
121
122void glist_retext(t_glist *glist, t_text *y)
123{
124 t_canvas *c = glist_getcanvas(glist);
125 /* check that we have built rtexts yet. LATER need a better test. */
126 if (glist->gl_editor && glist->gl_editor->e_rtext)
127 {
128 t_rtext *rt = glist_findrtext(glist, y);
129 if (rt)
130 rtext_retext(rt);
131 }
132}
133
134void glist_grab(t_glist *x, t_gobj *y, t_glistmotionfn motionfn,
135 t_glistkeyfn keyfn, int xpos, int ypos)
136{
137 t_glist *x2 = glist_getcanvas(x);
138 if (motionfn)
139 x2->gl_editor->e_onmotion = MA_PASSOUT;
140 else x2->gl_editor->e_onmotion = 0;
141 x2->gl_editor->e_grab = y;
142 x2->gl_editor->e_motionfn = motionfn;
143 x2->gl_editor->e_keyfn = keyfn;
144 x2->gl_editor->e_xwas = xpos;
145 x2->gl_editor->e_ywas = ypos;
146}
147
148t_canvas *glist_getcanvas(t_glist *x)
149{
150 while (x->gl_owner && !x->gl_havewindow && x->gl_isgraph)
151 x = x->gl_owner;
152 return((t_canvas *)x);
153}
154
155static float gobj_getxforsort(t_gobj *g)
156{
157 if (pd_class(&g->g_pd) == scalar_class)
158 {
159 float x1, y1;
160 scalar_getbasexy((t_scalar *)g, &x1, &y1);
161 return(x1);
162 }
163 else return (0);
164}
165
166static t_gobj *glist_merge(t_glist *x, t_gobj *g1, t_gobj *g2)
167{
168 t_gobj *g = 0, *g9 = 0;
169 float f1 = 0, f2 = 0;
170 if (g1)
171 f1 = gobj_getxforsort(g1);
172 if (g2)
173 f2 = gobj_getxforsort(g2);
174 while (1)
175 {
176 if (g1)
177 {
178 if (g2)
179 {
180 if (f1 <= f2)
181 goto put1;
182 else goto put2;
183 }
184 else goto put1;
185 }
186 else if (g2)
187 goto put2;
188 else break;
189 put1:
190 if (g9)
191 g9->g_next = g1, g9 = g1;
192 else g9 = g = g1;
193 if (g1 = g1->g_next)
194 f1 = gobj_getxforsort(g1);
195 g9->g_next = 0;
196 continue;
197 put2:
198 if (g9)
199 g9->g_next = g2, g9 = g2;
200 else g9 = g = g2;
201 if (g2 = g2->g_next)
202 f2 = gobj_getxforsort(g2);
203 g9->g_next = 0;
204 continue;
205 }
206 return (g);
207}
208
209static t_gobj *glist_dosort(t_glist *x,
210 t_gobj *g, int nitems)
211{
212 if (nitems < 2)
213 return (g);
214 else
215 {
216 int n1 = nitems/2, n2 = nitems - n1, i;
217 t_gobj *g2, *g3;
218 for (g2 = g, i = n1-1; i--; g2 = g2->g_next)
219 ;
220 g3 = g2->g_next;
221 g2->g_next = 0;
222 g = glist_dosort(x, g, n1);
223 g3 = glist_dosort(x, g3, n2);
224 return (glist_merge(x, g, g3));
225 }
226}
227
228void glist_sort(t_glist *x)
229{
230 int nitems = 0, foo = 0;
231 float lastx = -1e37;
232 t_gobj *g;
233 for (g = x->gl_list; g; g = g->g_next)
234 {
235 float x1 = gobj_getxforsort(g);
236 if (x1 < lastx)
237 foo = 1;
238 lastx = x1;
239 nitems++;
240 }
241 if (foo)
242 x->gl_list = glist_dosort(x, x->gl_list, nitems);
243}
244
245void glist_cleanup(t_glist *x)
246{
247 freebytes(x->gl_xlabel, x->gl_nxlabels * sizeof(*(x->gl_xlabel)));
248 freebytes(x->gl_ylabel, x->gl_nylabels * sizeof(*(x->gl_ylabel)));
249 gstub_cutoff(x->gl_stub);
250}
251
252void glist_free(t_glist *x)
253{
254 glist_cleanup(x);
255 freebytes(x, sizeof(*x));
256}
257
258/* --------------- inlets and outlets ----------- */
259
260
261t_inlet *canvas_addinlet(t_canvas *x, t_pd *who, t_symbol *s)
262{
263 t_inlet *ip = inlet_new(&x->gl_obj, who, s, 0);
264 if (!x->gl_loading && x->gl_owner && glist_isvisible(x->gl_owner))
265 {
266 gobj_vis(&x->gl_gobj, x->gl_owner, 0);
267 gobj_vis(&x->gl_gobj, x->gl_owner, 1);
268 canvas_fixlinesfor(x->gl_owner, &x->gl_obj);
269 }
270 if (!x->gl_loading) canvas_resortinlets(x);
271 return (ip);
272}
273
274void canvas_rminlet(t_canvas *x, t_inlet *ip)
275{
276 t_canvas *owner = x->gl_owner;
277 int redraw = (owner && glist_isvisible(owner) && (!owner->gl_isdeleting)
278 && glist_istoplevel(owner));
279
280 if (owner) canvas_deletelinesforio(owner, &x->gl_obj, ip, 0);
281 if (redraw)
282 gobj_vis(&x->gl_gobj, x->gl_owner, 0);
283 inlet_free(ip);
284 if (redraw)
285 {
286 gobj_vis(&x->gl_gobj, x->gl_owner, 1);
287 canvas_fixlinesfor(x->gl_owner, &x->gl_obj);
288 }
289}
290
291extern t_inlet *vinlet_getit(t_pd *x);
292extern void obj_moveinletfirst(t_object *x, t_inlet *i);
293
294void canvas_resortinlets(t_canvas *x)
295{
296 int ninlets = 0, i, j, xmax;
297 t_gobj *y, **vec, **vp, **maxp;
298
299 for (ninlets = 0, y = x->gl_list; y; y = y->g_next)
300 if (pd_class(&y->g_pd) == vinlet_class) ninlets++;
301
302 if (ninlets < 2) return;
303
304 vec = (t_gobj **)getbytes(ninlets * sizeof(*vec));
305
306 for (y = x->gl_list, vp = vec; y; y = y->g_next)
307 if (pd_class(&y->g_pd) == vinlet_class) *vp++ = y;
308
309 for (i = ninlets; i--;)
310 {
311 t_inlet *ip;
312 for (vp = vec, xmax = -0x7fffffff, maxp = 0, j = ninlets;
313 j--; vp++)
314 {
315 int x1, y1, x2, y2;
316 t_gobj *g = *vp;
317 if (!g) continue;
318 gobj_getrect(g, x, &x1, &y1, &x2, &y2);
319 if (x1 > xmax) xmax = x1, maxp = vp;
320 }
321 if (!maxp) break;
322 y = *maxp;
323 *maxp = 0;
324 ip = vinlet_getit(&y->g_pd);
325
326 obj_moveinletfirst(&x->gl_obj, ip);
327 }
328 freebytes(vec, ninlets * sizeof(*vec));
329 if (x->gl_owner && glist_isvisible(x->gl_owner))
330 canvas_fixlinesfor(x->gl_owner, &x->gl_obj);
331}
332
333t_outlet *canvas_addoutlet(t_canvas *x, t_pd *who, t_symbol *s)
334{
335 t_outlet *op = outlet_new(&x->gl_obj, s);
336 if (!x->gl_loading && x->gl_owner && glist_isvisible(x->gl_owner))
337 {
338 gobj_vis(&x->gl_gobj, x->gl_owner, 0);
339 gobj_vis(&x->gl_gobj, x->gl_owner, 1);
340 canvas_fixlinesfor(x->gl_owner, &x->gl_obj);
341 }
342 if (!x->gl_loading) canvas_resortoutlets(x);
343 return (op);
344}
345
346void canvas_rmoutlet(t_canvas *x, t_outlet *op)
347{
348 t_canvas *owner = x->gl_owner;
349 int redraw = (owner && glist_isvisible(owner) && (!owner->gl_isdeleting)
350 && glist_istoplevel(owner));
351
352 if (owner) canvas_deletelinesforio(owner, &x->gl_obj, 0, op);
353 if (redraw)
354 gobj_vis(&x->gl_gobj, x->gl_owner, 0);
355
356 outlet_free(op);
357 if (redraw)
358 {
359 gobj_vis(&x->gl_gobj, x->gl_owner, 1);
360 canvas_fixlinesfor(x->gl_owner, &x->gl_obj);
361 }
362}
363
364extern t_outlet *voutlet_getit(t_pd *x);
365extern void obj_moveoutletfirst(t_object *x, t_outlet *i);
366
367void canvas_resortoutlets(t_canvas *x)
368{
369 int noutlets = 0, i, j, xmax;
370 t_gobj *y, **vec, **vp, **maxp;
371
372 for (noutlets = 0, y = x->gl_list; y; y = y->g_next)
373 if (pd_class(&y->g_pd) == voutlet_class) noutlets++;
374
375 if (noutlets < 2) return;
376
377 vec = (t_gobj **)getbytes(noutlets * sizeof(*vec));
378
379 for (y = x->gl_list, vp = vec; y; y = y->g_next)
380 if (pd_class(&y->g_pd) == voutlet_class) *vp++ = y;
381
382 for (i = noutlets; i--;)
383 {
384 t_outlet *ip;
385 for (vp = vec, xmax = -0x7fffffff, maxp = 0, j = noutlets;
386 j--; vp++)
387 {
388 int x1, y1, x2, y2;
389 t_gobj *g = *vp;
390 if (!g) continue;
391 gobj_getrect(g, x, &x1, &y1, &x2, &y2);
392 if (x1 > xmax) xmax = x1, maxp = vp;
393 }
394 if (!maxp) break;
395 y = *maxp;
396 *maxp = 0;
397 ip = voutlet_getit(&y->g_pd);
398
399 obj_moveoutletfirst(&x->gl_obj, ip);
400 }
401 freebytes(vec, noutlets * sizeof(*vec));
402 if (x->gl_owner && glist_isvisible(x->gl_owner))
403 canvas_fixlinesfor(x->gl_owner, &x->gl_obj);
404}
405
406/* ----------calculating coordinates and controlling appearance --------- */
407
408
409static void graph_bounds(t_glist *x, t_floatarg x1, t_floatarg y1,
410 t_floatarg x2, t_floatarg y2)
411{
412 x->gl_x1 = x1;
413 x->gl_x2 = x2;
414 x->gl_y1 = y1;
415 x->gl_y2 = y2;
416 if (x->gl_x2 == x->gl_x1 ||
417 x->gl_y2 == x->gl_y1)
418 {
419 error("graph: empty bounds rectangle");
420 x1 = y1 = 0;
421 x2 = y2 = 1;
422 }
423 glist_redraw(x);
424}
425
426static void graph_xticks(t_glist *x,
427 t_floatarg point, t_floatarg inc, t_floatarg f)
428{
429 x->gl_xtick.k_point = point;
430 x->gl_xtick.k_inc = inc;
431 x->gl_xtick.k_lperb = f;
432 glist_redraw(x);
433}
434
435static void graph_yticks(t_glist *x,
436 t_floatarg point, t_floatarg inc, t_floatarg f)
437{
438 x->gl_ytick.k_point = point;
439 x->gl_ytick.k_inc = inc;
440 x->gl_ytick.k_lperb = f;
441 glist_redraw(x);
442}
443
444static void graph_xlabel(t_glist *x, t_symbol *s, int argc, t_atom *argv)
445{
446 int i;
447 if (argc < 1) error("graph_xlabel: no y value given");
448 else
449 {
450 x->gl_xlabely = atom_getfloat(argv);
451 argv++; argc--;
452 x->gl_xlabel = (t_symbol **)t_resizebytes(x->gl_xlabel,
453 x->gl_nxlabels * sizeof (t_symbol *), argc * sizeof (t_symbol *));
454 x->gl_nxlabels = argc;
455 for (i = 0; i < argc; i++) x->gl_xlabel[i] = atom_gensym(&argv[i]);
456 }
457 glist_redraw(x);
458}
459
460static void graph_ylabel(t_glist *x, t_symbol *s, int argc, t_atom *argv)
461{
462 int i;
463 if (argc < 1) error("graph_ylabel: no x value given");
464 else
465 {
466 x->gl_ylabelx = atom_getfloat(argv);
467 argv++; argc--;
468 x->gl_ylabel = (t_symbol **)t_resizebytes(x->gl_ylabel,
469 x->gl_nylabels * sizeof (t_symbol *), argc * sizeof (t_symbol *));
470 x->gl_nylabels = argc;
471 for (i = 0; i < argc; i++) x->gl_ylabel[i] = atom_gensym(&argv[i]);
472 }
473 glist_redraw(x);
474}
475
476/****** routines to convert pixels to X or Y value and vice versa ******/
477
478 /* convert an x pixel value to an x coordinate value */
479float glist_pixelstox(t_glist *x, float xpix)
480{
481 /* if we appear as a text box on parent, our range in our
482 coordinates (x1, etc.) specifies the coordinate range
483 of a one-pixel square at top left of the window. */
484 if (!x->gl_isgraph)
485 return (x->gl_x1 + (x->gl_x2 - x->gl_x1) * xpix);
486
487 /* if we're a graph when shown on parent, but own our own
488 window right now, our range in our coordinates (x1, etc.) is spread
489 over the visible window size, given by screenx1, etc. */
490 else if (x->gl_isgraph && x->gl_havewindow)
491 return (x->gl_x1 + (x->gl_x2 - x->gl_x1) *
492 (xpix) / (x->gl_screenx2 - x->gl_screenx1));
493
494 /* otherwise, we appear in a graph within a parent glist,
495 so get our screen rectangle on parent and transform. */
496 else
497 {
498 int x1, y1, x2, y2;
499 if (!x->gl_owner)
500 bug("glist_pixelstox");
501 graph_graphrect(&x->gl_gobj, x->gl_owner, &x1, &y1, &x2, &y2);
502 return (x->gl_x1 + (x->gl_x2 - x->gl_x1) *
503 (xpix - x1) / (x2 - x1));
504 }
505}
506
507float glist_pixelstoy(t_glist *x, float ypix)
508{
509 if (!x->gl_isgraph)
510 return (x->gl_y1 + (x->gl_y2 - x->gl_y1) * ypix);
511 else if (x->gl_isgraph && x->gl_havewindow)
512 return (x->gl_y1 + (x->gl_y2 - x->gl_y1) *
513 (ypix) / (x->gl_screeny2 - x->gl_screeny1));
514 else
515 {
516 int x1, y1, x2, y2;
517 if (!x->gl_owner)
518 bug("glist_pixelstox");
519 graph_graphrect(&x->gl_gobj, x->gl_owner, &x1, &y1, &x2, &y2);
520 return (x->gl_y1 + (x->gl_y2 - x->gl_y1) *
521 (ypix - y1) / (y2 - y1));
522 }
523}
524
525 /* convert an x coordinate value to an x pixel location in window */
526float glist_xtopixels(t_glist *x, float xval)
527{
528 if (!x->gl_isgraph)
529 return ((xval - x->gl_x1) / (x->gl_x2 - x->gl_x1));
530 else if (x->gl_isgraph && x->gl_havewindow)
531 return (x->gl_screenx2 - x->gl_screenx1) *
532 (xval - x->gl_x1) / (x->gl_x2 - x->gl_x1);
533 else
534 {
535 int x1, y1, x2, y2;
536 if (!x->gl_owner)
537 bug("glist_pixelstox");
538 graph_graphrect(&x->gl_gobj, x->gl_owner, &x1, &y1, &x2, &y2);
539 return (x1 + (x2 - x1) * (xval - x->gl_x1) / (x->gl_x2 - x->gl_x1));
540 }
541}
542
543float glist_ytopixels(t_glist *x, float yval)
544{
545 if (!x->gl_isgraph)
546 return ((yval - x->gl_y1) / (x->gl_y2 - x->gl_y1));
547 else if (x->gl_isgraph && x->gl_havewindow)
548 return (x->gl_screeny2 - x->gl_screeny1) *
549 (yval - x->gl_y1) / (x->gl_y2 - x->gl_y1);
550 else
551 {
552 int x1, y1, x2, y2;
553 if (!x->gl_owner)
554 bug("glist_pixelstox");
555 graph_graphrect(&x->gl_gobj, x->gl_owner, &x1, &y1, &x2, &y2);
556 return (y1 + (y2 - y1) * (yval - x->gl_y1) / (x->gl_y2 - x->gl_y1));
557 }
558}
559
560 /* convert an X screen distance to an X coordinate increment.
561 This is terribly inefficient;
562 but probably not a big enough CPU hog to warrant optimizing. */
563float glist_dpixtodx(t_glist *x, float dxpix)
564{
565 return (dxpix * (glist_pixelstox(x, 1) - glist_pixelstox(x, 0)));
566}
567
568float glist_dpixtody(t_glist *x, float dypix)
569{
570 return (dypix * (glist_pixelstoy(x, 1) - glist_pixelstoy(x, 0)));
571}
572
573 /* get the window location in pixels of a "text" object. The
574 object's x and y positions are in pixels when the glist they're
575 in is toplevel. If it's not, we convert to pixels on the parent
576 window. */
577int text_xpix(t_text *x, t_glist *glist)
578{
579 if (glist->gl_havewindow || !glist->gl_isgraph)
580 return (x->te_xpix);
581 else return (glist_xtopixels(glist,
582 glist->gl_x1 + (glist->gl_x2 - glist->gl_x1) *
583 x->te_xpix / (glist->gl_screenx2 - glist->gl_screenx1)));
584}
585
586int text_ypix(t_text *x, t_glist *glist)
587{
588 if (glist->gl_havewindow || !glist->gl_isgraph)
589 return (x->te_ypix);
590 else return (glist_ytopixels(glist,
591 glist->gl_y1 + (glist->gl_y2 - glist->gl_y1) *
592 x->te_ypix / (glist->gl_screeny2 - glist->gl_screeny1)));
593}
594
595 /* redraw all the items in a glist. We construe this to mean
596 redrawing in its own window and on parent, as needed in each case.
597 This is too conservative -- for instance, when you draw an "open"
598 rectangle on the parent, you shouldn't have to redraw the window! */
599void glist_redraw(t_glist *x)
600{
601 if (glist_isvisible(x))
602 {
603 /* LATER fix the graph_vis() code to handle both cases */
604 if (glist_istoplevel(x))
605 {
606 t_gobj *g;
607 t_linetraverser t;
608 t_outconnect *oc;
609 for (g = x->gl_list; g; g = g->g_next)
610 {
611 gobj_vis(g, x, 0);
612 gobj_vis(g, x, 1);
613 }
614 /* redraw all the lines */
615 linetraverser_start(&t, x);
616 while (oc = linetraverser_next(&t))
617 sys_vgui(".x%x.c coords l%x %d %d %d %d\n",
618 glist_getcanvas(x), oc,
619 t.tr_lx1, t.tr_ly1, t.tr_lx2, t.tr_ly2);
620 }
621 if (x->gl_owner)
622 {
623 graph_vis(&x->gl_gobj, x->gl_owner, 0);
624 graph_vis(&x->gl_gobj, x->gl_owner, 1);
625 }
626 }
627}
628
629/* --------------------------- widget behavior ------------------- */
630
631extern t_widgetbehavior text_widgetbehavior;
632
633 /* Note that some code in here would also be useful for drawing
634 graph decorations in toplevels... */
635static void graph_vis(t_gobj *gr, t_glist *parent_glist, int vis)
636{
637 t_glist *x = (t_glist *)gr;
638 char tag[50];
639 t_gobj *g;
640 int x1, y1, x2, y2;
641 /* ordinary subpatches: just act like a text object */
642 if (!x->gl_isgraph)
643 {
644 text_widgetbehavior.w_visfn(gr, parent_glist, vis);
645 return;
646 }
647
648 if (vis && canvas_showtext(x))
649 rtext_draw(glist_findrtext(parent_glist, &x->gl_obj));
650 graph_getrect(gr, parent_glist, &x1, &y1, &x2, &y2);
651 if (!vis)
652 rtext_erase(glist_findrtext(parent_glist, &x->gl_obj));
653
654 sprintf(tag, "graph%x", (int)x);
655 if (vis)
656 glist_drawiofor(parent_glist, &x->gl_obj, 1,
657 tag, x1, y1, x2, y2);
658 else glist_eraseiofor(parent_glist, &x->gl_obj, tag);
659 /* if we look like a graph but have been moved to a toplevel,
660 just show the bounding rectangle */
661 if (x->gl_havewindow)
662 {
663 if (vis)
664 {
665 sys_vgui(".x%x.c create polygon\
666 %d %d %d %d %d %d %d %d %d %d -tags %s -fill #c0c0c0\n",
667 glist_getcanvas(x->gl_owner),
668 x1, y1, x1, y2, x2, y2, x2, y1, x1, y1, tag);
669 }
670 else
671 {
672 sys_vgui(".x%x.c delete %s\n",
673 glist_getcanvas(x->gl_owner), tag);
674 }
675 return;
676 }
677 /* otherwise draw (or erase) us as a graph inside another glist. */
678 if (vis)
679 {
680 int i;
681 float f;
682
683 /* draw a rectangle around the graph */
684 sys_vgui(".x%x.c create line\
685 %d %d %d %d %d %d %d %d %d %d -tags %s\n",
686 glist_getcanvas(x->gl_owner),
687 x1, y1, x1, y2, x2, y2, x2, y1, x1, y1, tag);
688
689 /* draw ticks on horizontal borders. If lperb field is
690 zero, this is disabled. */
691 if (x->gl_xtick.k_lperb)
692 {
693 float upix, lpix;
694 if (y2 < y1)
695 upix = y1, lpix = y2;
696 else upix = y2, lpix = y1;
697 for (i = 0, f = x->gl_xtick.k_point;
698 f < 0.99 * x->gl_x2 + 0.01*x->gl_x1; i++,
699 f += x->gl_xtick.k_inc)
700 {
701 int tickpix = (i % x->gl_xtick.k_lperb ? 2 : 4);
702 sys_vgui(".x%x.c create line %d %d %d %d -tags %s\n",
703 glist_getcanvas(x->gl_owner),
704 (int)glist_xtopixels(x, f), (int)upix,
705 (int)glist_xtopixels(x, f), (int)upix - tickpix, tag);
706 sys_vgui(".x%x.c create line %d %d %d %d -tags %s\n",
707 glist_getcanvas(x->gl_owner),
708 (int)glist_xtopixels(x, f), (int)lpix,
709 (int)glist_xtopixels(x, f), (int)lpix + tickpix, tag);
710 }
711 for (i = 1, f = x->gl_xtick.k_point - x->gl_xtick.k_inc;
712 f > 0.99 * x->gl_x1 + 0.01*x->gl_x2;
713 i++, f -= x->gl_xtick.k_inc)
714 {
715 int tickpix = (i % x->gl_xtick.k_lperb ? 2 : 4);
716 sys_vgui(".x%x.c create line %d %d %d %d -tags %s\n",
717 glist_getcanvas(x->gl_owner),
718 (int)glist_xtopixels(x, f), (int)upix,
719 (int)glist_xtopixels(x, f), (int)upix - tickpix, tag);
720 sys_vgui(".x%x.c create line %d %d %d %d -tags %s\n",
721 glist_getcanvas(x->gl_owner),
722 (int)glist_xtopixels(x, f), (int)lpix,
723 (int)glist_xtopixels(x, f), (int)lpix + tickpix, tag);
724 }
725 }
726
727 /* draw ticks in vertical borders*/
728 if (x->gl_ytick.k_lperb)
729 {
730 float ubound, lbound;
731 if (x->gl_y2 < x->gl_y1)
732 ubound = x->gl_y1, lbound = x->gl_y2;
733 else ubound = x->gl_y2, lbound = x->gl_y1;
734 for (i = 0, f = x->gl_ytick.k_point;
735 f < 0.99 * ubound + 0.01 * lbound;
736 i++, f += x->gl_ytick.k_inc)
737 {
738 int tickpix = (i % x->gl_ytick.k_lperb ? 2 : 4);
739 sys_vgui(".x%x.c create line %d %d %d %d -tags %s\n",
740 glist_getcanvas(x->gl_owner),
741 x1, (int)glist_ytopixels(x, f),
742 x1 + tickpix, (int)glist_ytopixels(x, f), tag);
743 sys_vgui(".x%x.c create line %d %d %d %d -tags %s\n",
744 glist_getcanvas(x->gl_owner),
745 x2, (int)glist_ytopixels(x, f),
746 x2 - tickpix, (int)glist_ytopixels(x, f), tag);
747 }
748 for (i = 1, f = x->gl_ytick.k_point - x->gl_ytick.k_inc;
749 f > 0.99 * lbound + 0.01 * ubound;
750 i++, f -= x->gl_ytick.k_inc)
751 {
752 int tickpix = (i % x->gl_ytick.k_lperb ? 2 : 4);
753 sys_vgui(".x%x.c create line %d %d %d %d -tags %s\n",
754 glist_getcanvas(x->gl_owner),
755 x1, (int)glist_ytopixels(x, f),
756 x1 + tickpix, (int)glist_ytopixels(x, f), tag);
757 sys_vgui(".x%x.c create line %d %d %d %d -tags %s\n",
758 glist_getcanvas(x->gl_owner),
759 x2, (int)glist_ytopixels(x, f),
760 x2 - tickpix, (int)glist_ytopixels(x, f), tag);
761 }
762 }
763 /* draw x labels */
764 for (i = 0; i < x->gl_nxlabels; i++)
765 sys_vgui(".x%x.c create text\
766 %d %d -text {%s} -font -*-courier-bold--normal--%d-* -tags %s\n",
767 glist_getcanvas(x),
768 (int)glist_xtopixels(x, atof(x->gl_xlabel[i]->s_name)),
769 (int)glist_ytopixels(x, x->gl_xlabely), x->gl_xlabel[i]->s_name,
770 glist_getfont(x), tag);
771
772 /* draw y labels */
773 for (i = 0; i < x->gl_nylabels; i++)
774 sys_vgui(".x%x.c create text\
775 %d %d -text {%s} -font -*-courier-bold--normal--%d-* -tags %s\n",
776 glist_getcanvas(x),
777 (int)glist_xtopixels(x, x->gl_ylabelx),
778 (int)glist_ytopixels(x, atof(x->gl_ylabel[i]->s_name)),
779 x->gl_ylabel[i]->s_name,
780 glist_getfont(x), tag);
781
782 /* draw contents of graph as glist */
783 for (g = x->gl_list; g; g = g->g_next)
784 gobj_vis(g, x, 1);
785 }
786 else
787 {
788 sys_vgui(".x%x.c delete %s\n",
789 glist_getcanvas(x->gl_owner), tag);
790 for (g = x->gl_list; g; g = g->g_next)
791 gobj_vis(g, x, 0);
792 }
793}
794
795 /* get the graph's rectangle, not counting extra swelling for controls
796 to keep them inside the graph. This is the "logical" pixel size. */
797
798static void graph_graphrect(t_gobj *z, t_glist *glist,
799 int *xp1, int *yp1, int *xp2, int *yp2)
800{
801 t_glist *x = (t_glist *)z;
802 int x1 = text_xpix(&x->gl_obj, glist);
803 int y1 = text_ypix(&x->gl_obj, glist);
804 int x2, y2;
805#if 0 /* this used to adjust graph size when it was in another graph;
806 now we just preserve the size. */
807 /* same logic here as in text_xpix(): */
808 if (glist->gl_havewindow)
809 {
810 x2 = x1 + x->gl_pixwidth;
811 y2 = y1 + x->gl_pixheight;
812 }
813 else
814 {
815 x2 = glist_xtopixels(glist,
816 glist->gl_x1 + (glist->gl_x2 - glist->gl_x1) *
817 (x->gl_obj.te_xpix + x->gl_pixwidth) /
818 (glist->gl_screenx2 - glist->gl_screenx1));
819 y2 = glist_ytopixels(glist,
820 glist->gl_y1 + (glist->gl_y2 - glist->gl_y1) *
821 (x->gl_obj.te_ypix + x->gl_pixheight) /
822 (glist->gl_screeny2 - glist->gl_screeny1));
823 }
824#endif
825 x2 = x1 + x->gl_pixwidth;
826 y2 = y1 + x->gl_pixheight;
827
828 *xp1 = x1;
829 *yp1 = y1;
830 *xp2 = x2;
831 *yp2 = y2;
832}
833
834 /* get the rectangle, enlarged to contain all the "contents" --
835 meaning their formal bounds rectangles. */
836static void graph_getrect(t_gobj *z, t_glist *glist,
837 int *xp1, int *yp1, int *xp2, int *yp2)
838{
839 int x1 = 0x7fffffff, y1 = 0x7fffffff, x2 = -0x7fffffff, y2 = -0x7fffffff;
840 t_glist *x = (t_glist *)z;
841 if (x->gl_isgraph)
842 {
843 int hadwindow;
844 t_gobj *g;
845 t_text *ob;
846 int x21, y21, x22, y22;
847
848 graph_graphrect(z, glist, &x1, &y1, &x2, &y2);
849 if (canvas_showtext(x))
850 {
851 text_widgetbehavior.w_getrectfn(z, glist, &x21, &y21, &x22, &y22);
852 if (x22 > x2)
853 x2 = x22;
854 if (y22 > y2)
855 y2 = y22;
856 }
857 /* lie about whether we have our own window to affect gobj_getrect
858 calls below. (LATER add argument to gobj_getrect()?) */
859 hadwindow = x->gl_havewindow;
860 x->gl_havewindow = 0;
861 for (g = x->gl_list; g; g = g->g_next)
862 if ((!(ob = pd_checkobject(&g->g_pd))) || text_shouldvis(ob, x))
863 {
864 /* don't do this for arrays, just let them hang outsize the
865 box. */
866 if (pd_class(&g->g_pd) == garray_class)
867 continue;
868 gobj_getrect(g, x, &x21, &y21, &x22, &y22);
869 if (x22 > x2)
870 x2 = x22;
871 if (y22 > y2)
872 y2 = y22;
873 }
874 x->gl_havewindow = hadwindow;
875 }
876 else text_widgetbehavior.w_getrectfn(z, glist, &x1, &y1, &x2, &y2);
877 *xp1 = x1;
878 *yp1 = y1;
879 *xp2 = x2;
880 *yp2 = y2;
881}
882
883static void graph_displace(t_gobj *z, t_glist *glist, int dx, int dy)
884{
885 t_glist *x = (t_glist *)z;
886 if (!x->gl_isgraph)
887 text_widgetbehavior.w_displacefn(z, glist, dx, dy);
888 else
889 {
890 x->gl_obj.te_xpix += dx;
891 x->gl_obj.te_ypix += dy;
892 glist_redraw(x);
893 canvas_fixlinesfor(glist_getcanvas(glist), &x->gl_obj);
894 }
895}
896
897static void graph_select(t_gobj *z, t_glist *glist, int state)
898{
899 t_glist *x = (t_glist *)z;
900 if (!x->gl_isgraph)
901 text_widgetbehavior.w_selectfn(z, glist, state);
902 else
903 {
904 t_rtext *y = glist_findrtext(glist, &x->gl_obj);
905 if (canvas_showtext(x))
906 rtext_select(y, state);
907 sys_vgui(".x%x.c itemconfigure %sR -fill %s\n", glist,
908 rtext_gettag(y), (state? "blue" : "black"));
909 sys_vgui(".x%x.c itemconfigure graph%x -fill %s\n",
910 glist_getcanvas(glist), z, (state? "blue" : "black"));
911 }
912}
913
914static void graph_activate(t_gobj *z, t_glist *glist, int state)
915{
916 t_glist *x = (t_glist *)z;
917 if (canvas_showtext(x))
918 text_widgetbehavior.w_activatefn(z, glist, state);
919}
920
921#if 0
922static void graph_delete(t_gobj *z, t_glist *glist)
923{
924 t_glist *x = (t_glist *)z;
925 if (!x->gl_isgraph)
926 text_widgetbehavior.w_deletefn(z, glist);
927 else
928 {
929 t_gobj *y;
930 while (y = x->gl_list) glist_delete(x, y);
931#if 0 /* I think this was just wrong. */
932 if (glist_isvisible(x))
933 sys_vgui(".x%x.c delete graph%x\n", glist_getcanvas(glist), x);
934#endif
935 }
936}
937#endif
938
939static void graph_delete(t_gobj *z, t_glist *glist)
940{
941 t_glist *x = (t_glist *)z;
942 t_gobj *y;
943 text_widgetbehavior.w_deletefn(z, glist);
944 while (y = x->gl_list)
945 glist_delete(x, y);
946}
947
948static float graph_lastxpix, graph_lastypix;
949
950static void graph_motion(void *z, t_floatarg dx, t_floatarg dy)
951{
952 t_glist *x = (t_glist *)z;
953 float newxpix = graph_lastxpix + dx, newypix = graph_lastypix + dy;
954 t_garray *a = (t_garray *)(x->gl_list);
955 int oldx = 0.5 + glist_pixelstox(x, graph_lastxpix);
956 int newx = 0.5 + glist_pixelstox(x, newxpix);
957 t_sample *vec;
958 int nelem, i;
959 float oldy = glist_pixelstoy(x, graph_lastypix);
960 float newy = glist_pixelstoy(x, newypix);
961 graph_lastxpix = newxpix;
962 graph_lastypix = newypix;
963 /* verify that the array is OK */
964 if (!a || pd_class((t_pd *)a) != garray_class)
965 return;
966 if (!garray_getfloatarray(a, &nelem, &vec))
967 return;
968 if (oldx < 0) oldx = 0;
969 if (oldx >= nelem)
970 oldx = nelem - 1;
971 if (newx < 0) newx = 0;
972 if (newx >= nelem)
973 newx = nelem - 1;
974 if (oldx < newx - 1)
975 {
976 for (i = oldx + 1; i <= newx; i++)
977 vec[i] = newy + (oldy - newy) *
978 ((float)(newx - i))/(float)(newx - oldx);
979 }
980 else if (oldx > newx + 1)
981 {
982 for (i = oldx - 1; i >= newx; i--)
983 vec[i] = newy + (oldy - newy) *
984 ((float)(newx - i))/(float)(newx - oldx);
985 }
986 else vec[newx] = newy;
987 garray_redraw(a);
988}
989
990static int graph_click(t_gobj *z, struct _glist *glist,
991 int xpix, int ypix, int shift, int alt, int dbl, int doit)
992{
993 t_glist *x = (t_glist *)z;
994 t_gobj *y;
995 int clickreturned = 0;
996 if (!x->gl_isgraph)
997 return (text_widgetbehavior.w_clickfn(z, glist,
998 xpix, ypix, shift, alt, dbl, doit));
999 else if (x->gl_havewindow)
1000 return (0);
1001 else
1002 {
1003 for (y = x->gl_list; y; y = y->g_next)
1004 {
1005 int x1, y1, x2, y2;
1006 /* check if the object wants to be clicked */
1007 if (canvas_hitbox(x, y, xpix, ypix, &x1, &y1, &x2, &y2)
1008 && (clickreturned = gobj_click(y, x, xpix, ypix,
1009 shift, alt, 0, doit)))
1010 break;
1011 }
1012 if (!doit)
1013 {
1014 if (y)
1015 canvas_setcursor(glist_getcanvas(x), clickreturned);
1016 else canvas_setcursor(glist_getcanvas(x), CURSOR_RUNMODE_NOTHING);
1017 }
1018 return (clickreturned);
1019 }
1020}
1021
1022void garray_properties(t_garray *x);
1023
1024t_widgetbehavior graph_widgetbehavior =
1025{
1026 graph_getrect,
1027 graph_displace,
1028 graph_select,
1029 graph_activate,
1030 graph_delete,
1031 graph_vis,
1032 graph_click,
1033};
1034
1035void graph_properties(t_gobj *z, t_glist *owner)
1036{
1037 t_glist *x = (t_glist *)z;
1038 {
1039 t_gobj *y;
1040 char graphbuf[200];
1041 sprintf(graphbuf, "pdtk_graph_dialog %%s %g %g %g %g %d %d\n",
1042 x->gl_x1, x->gl_y1, x->gl_x2, x->gl_y2,
1043 x->gl_pixwidth, x->gl_pixheight);
1044 gfxstub_new(&x->gl_pd, x, graphbuf);
1045
1046 for (y = x->gl_list; y; y = y->g_next)
1047 if (pd_class(&y->g_pd) == garray_class)
1048 garray_properties((t_garray *)y);
1049 }
1050}
1051
1052 /* find the graph most recently added to this glist;
1053 if none exists, return 0. */
1054
1055t_glist *glist_findgraph(t_glist *x)
1056{
1057 t_gobj *y = 0, *z;
1058 for (z = x->gl_list; z; z = z->g_next)
1059 if (pd_class(&z->g_pd) == canvas_class && ((t_glist *)z)->gl_isgraph)
1060 y = z;
1061 return ((t_glist *)y);
1062}
1063
1064 /* message back from dialog GUI to set parameters. Args are:
1065 1-4: bounds in our coordinates; 5-6: size in parent */
1066static void graph_dialog(t_glist *x, t_symbol *s, int argc, t_atom *argv)
1067{
1068 t_float x1 = atom_getfloatarg(0, argc, argv);
1069 t_float y1 = atom_getfloatarg(1, argc, argv);
1070 t_float x2 = atom_getfloatarg(2, argc, argv);
1071 t_float y2 = atom_getfloatarg(3, argc, argv);
1072 t_float xpix = atom_getfloatarg(4, argc, argv);
1073 t_float ypix = atom_getfloatarg(5, argc, argv);
1074 if (x1 != x->gl_x1 || x2 != x->gl_x2 ||
1075 y1 != x->gl_y1 || y2 != x->gl_y2)
1076 graph_bounds(x, x1, y1, x2, y2);
1077 if (xpix != x->gl_pixwidth || ypix != x->gl_pixheight)
1078 {
1079 x->gl_pixwidth = xpix;
1080 x->gl_pixheight = ypix;
1081 glist_redraw(x);
1082 if (x->gl_owner)
1083 canvas_fixlinesfor(x->gl_owner, &x->gl_obj);
1084 }
1085}
1086
1087extern void canvas_menuarray(t_glist *canvas);
1088
1089void g_graph_setup(void)
1090{
1091 class_setwidget(canvas_class, &graph_widgetbehavior);
1092 class_addmethod(canvas_class, (t_method)graph_bounds, gensym("bounds"),
1093 A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, 0);
1094 class_addmethod(canvas_class, (t_method)graph_xticks, gensym("xticks"),
1095 A_FLOAT, A_FLOAT, A_FLOAT, 0);
1096 class_addmethod(canvas_class, (t_method)graph_xlabel, gensym("xlabel"),
1097 A_GIMME, 0);
1098 class_addmethod(canvas_class, (t_method)graph_yticks, gensym("yticks"),
1099 A_FLOAT, A_FLOAT, A_FLOAT, 0);
1100 class_addmethod(canvas_class, (t_method)graph_ylabel, gensym("ylabel"),
1101 A_GIMME, 0);
1102 class_addmethod(canvas_class, (t_method)graph_array, gensym("array"),
1103 A_SYMBOL, A_FLOAT, A_SYMBOL, A_DEFFLOAT, A_NULL);
1104 class_addmethod(canvas_class, (t_method)canvas_menuarray,
1105 gensym("menuarray"), A_NULL);
1106 class_addmethod(canvas_class, (t_method)graph_dialog, gensym("dialog"),
1107 A_GIMME, 0);
1108 class_addmethod(canvas_class, (t_method)glist_arraydialog,
1109 gensym("arraydialog"), A_SYMBOL, A_FLOAT, A_FLOAT, A_FLOAT, A_NULL);
1110 class_addmethod(canvas_class, (t_method)glist_sort,
1111 gensym("sort"), A_NULL);
1112}
1113/* Copyright (c) 1997-2001 Miller Puckette and others.
1114* For information on usage and redistribution, and for a DISCLAIMER OF ALL
1115* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */
1116
1117/* This file deals with the behavior of glists as either "text objects" or
1118"graphs" inside another glist. LATER move the inlet/outlet code of g_canvas.c
1119to this file... */
1120
1121#include <stdlib.h>
1122#include "m_pd.h"
1123#include "t_tk.h"
1124#include "g_canvas.h"
1125#include <stdio.h>
1126#include <string.h>
1127
1128/* ---------------------- forward definitions ----------------- */
1129
1130static void graph_vis(t_gobj *gr, t_glist *unused_glist, int vis);
1131static void graph_graphrect(t_gobj *z, t_glist *glist,
1132 int *xp1, int *yp1, int *xp2, int *yp2);
1133static void graph_getrect(t_gobj *z, t_glist *glist,
1134 int *xp1, int *yp1, int *xp2, int *yp2);
1135
1136/* -------------------- maintaining the list -------------------- */
1137
1138void glist_add(t_glist *x, t_gobj *y)
1139{
1140 t_object *ob;
1141 y->g_next = 0;
1142 if (!x->gl_list) x->gl_list = y;
1143 else
1144 {
1145 t_gobj *y2;
1146 for (y2 = x->gl_list; y2->g_next; y2 = y2->g_next);
1147 y2->g_next = y;
1148 }
1149 if (x->gl_editor && (ob = pd_checkobject(&y->g_pd)))
1150 rtext_new(x, ob);
1151 if (glist_isvisible(x))
1152 gobj_vis(y, x, 1);
1153 if (class_isdrawcommand(y->g_pd))
1154 canvas_redrawallfortemplate(glist_getcanvas(x));
1155}
1156
1157 /* this is to protect against a hairy problem in which deleting
1158 a sub-canvas might delete an inlet on a box, after the box had
1159 been invisible-ized, so that we have to protect against redrawing it! */
1160int canvas_setdeleting(t_canvas *x, int flag)
1161{
1162 int ret = x->gl_isdeleting;
1163 x->gl_isdeleting = flag;
1164 return (ret);
1165}
1166
1167 /* delete an object from a glist and free it */
1168void glist_delete(t_glist *x, t_gobj *y)
1169{
1170 t_gobj *g;
1171 t_object *ob;
1172 t_gotfn chkdsp = zgetfn(&y->g_pd, gensym("dsp"));
1173 t_canvas *canvas = glist_getcanvas(x);
1174 int drawcommand = class_isdrawcommand(y->g_pd);
1175 int wasdeleting;
1176
1177 wasdeleting = canvas_setdeleting(canvas, 1);
1178 if (x->gl_editor)
1179 {
1180 if (x->gl_editor->e_grab == y) x->gl_editor->e_grab = 0;
1181 if (glist_isselected(x, y)) glist_deselect(x, y);
1182
1183 /* HACK -- we had phantom outlets not getting erased on the
1184 screen because the canvas_setdeleting() mechanism is too
1185 crude. LATER carefully set up rules for when the rtexts
1186 should exist, so that they stay around until all the
1187 steps of becoming invisible are done. In the meantime, just
1188 zap the inlets and outlets here... */
1189 if (pd_class(&y->g_pd) == canvas_class)
1190 {
1191 t_glist *gl = (t_glist *)y;
1192 if (gl->gl_isgraph)
1193 {
1194 char tag[80];
1195 sprintf(tag, "graph%x", (int)gl);
1196 glist_eraseiofor(x, &gl->gl_obj, tag);
1197 }
1198 else
1199 {
1200 text_eraseborder(&gl->gl_obj, x,
1201 rtext_gettag(glist_findrtext(x, &gl->gl_obj)));
1202 }
1203 }
1204 }
1205 gobj_delete(y, x);
1206 if (glist_isvisible(canvas))
1207 gobj_vis(y, x, 0);
1208 if (x->gl_editor && (ob = pd_checkobject(&y->g_pd)))
1209 rtext_new(x, ob);
1210 if (x->gl_list == y) x->gl_list = y->g_next;
1211 else for (g = x->gl_list; g; g = g->g_next)
1212 if (g->g_next == y)
1213 {
1214 g->g_next = y->g_next;
1215 break;
1216 }
1217 pd_free(&y->g_pd);
1218 if (chkdsp) canvas_update_dsp();
1219 if (drawcommand) canvas_redrawallfortemplate(canvas);
1220 canvas_setdeleting(canvas, wasdeleting);
1221 x->gl_valid = ++glist_valid;
1222}
1223
1224 /* remove every object from a glist. Experimental. */
1225void glist_clear(t_glist *x)
1226{
1227 t_gobj *y, *y2;
1228 int dspstate = canvas_suspend_dsp();
1229 while (y = x->gl_list)
1230 glist_delete(x, y);
1231 canvas_resume_dsp(dspstate);
1232}
1233
1234void glist_retext(t_glist *glist, t_text *y)
1235{
1236 t_canvas *c = glist_getcanvas(glist);
1237 /* check that we have built rtexts yet. LATER need a better test. */
1238 if (glist->gl_editor && glist->gl_editor->e_rtext)
1239 {
1240 t_rtext *rt = glist_findrtext(glist, y);
1241 if (rt)
1242 rtext_retext(rt);
1243 }
1244}
1245
1246void glist_grab(t_glist *x, t_gobj *y, t_glistmotionfn motionfn,
1247 t_glistkeyfn keyfn, int xpos, int ypos)
1248{
1249 t_glist *x2 = glist_getcanvas(x);
1250 if (motionfn)
1251 x2->gl_editor->e_onmotion = MA_PASSOUT;
1252 else x2->gl_editor->e_onmotion = 0;
1253 x2->gl_editor->e_grab = y;
1254 x2->gl_editor->e_motionfn = motionfn;
1255 x2->gl_editor->e_keyfn = keyfn;
1256 x2->gl_editor->e_xwas = xpos;
1257 x2->gl_editor->e_ywas = ypos;
1258}
1259
1260t_canvas *glist_getcanvas(t_glist *x)
1261{
1262 while (x->gl_owner && !x->gl_havewindow && x->gl_isgraph)
1263 x = x->gl_owner;
1264 return((t_canvas *)x);
1265}
1266
1267static float gobj_getxforsort(t_gobj *g)
1268{
1269 if (pd_class(&g->g_pd) == scalar_class)
1270 {
1271 float x1, y1;
1272 scalar_getbasexy((t_scalar *)g, &x1, &y1);
1273 return(x1);
1274 }
1275 else return (0);
1276}
1277
1278static t_gobj *glist_merge(t_glist *x, t_gobj *g1, t_gobj *g2)
1279{
1280 t_gobj *g = 0, *g9 = 0;
1281 float f1 = 0, f2 = 0;
1282 if (g1)
1283 f1 = gobj_getxforsort(g1);
1284 if (g2)
1285 f2 = gobj_getxforsort(g2);
1286 while (1)
1287 {
1288 if (g1)
1289 {
1290 if (g2)
1291 {
1292 if (f1 <= f2)
1293 goto put1;
1294 else goto put2;
1295 }
1296 else goto put1;
1297 }
1298 else if (g2)
1299 goto put2;
1300 else break;
1301 put1:
1302 if (g9)
1303 g9->g_next = g1, g9 = g1;
1304 else g9 = g = g1;
1305 if (g1 = g1->g_next)
1306 f1 = gobj_getxforsort(g1);
1307 g9->g_next = 0;
1308 continue;
1309 put2:
1310 if (g9)
1311 g9->g_next = g2, g9 = g2;
1312 else g9 = g = g2;
1313 if (g2 = g2->g_next)
1314 f2 = gobj_getxforsort(g2);
1315 g9->g_next = 0;
1316 continue;
1317 }
1318 return (g);
1319}
1320
1321static t_gobj *glist_dosort(t_glist *x,
1322 t_gobj *g, int nitems)
1323{
1324 if (nitems < 2)
1325 return (g);
1326 else
1327 {
1328 int n1 = nitems/2, n2 = nitems - n1, i;
1329 t_gobj *g2, *g3;
1330 for (g2 = g, i = n1-1; i--; g2 = g2->g_next)
1331 ;
1332 g3 = g2->g_next;
1333 g2->g_next = 0;
1334 g = glist_dosort(x, g, n1);
1335 g3 = glist_dosort(x, g3, n2);
1336 return (glist_merge(x, g, g3));
1337 }
1338}
1339
1340void glist_sort(t_glist *x)
1341{
1342 int nitems = 0, foo = 0;
1343 float lastx = -1e37;
1344 t_gobj *g;
1345 for (g = x->gl_list; g; g = g->g_next)
1346 {
1347 float x1 = gobj_getxforsort(g);
1348 if (x1 < lastx)
1349 foo = 1;
1350 lastx = x1;
1351 nitems++;
1352 }
1353 if (foo)
1354 x->gl_list = glist_dosort(x, x->gl_list, nitems);
1355}
1356
1357void glist_cleanup(t_glist *x)
1358{
1359 freebytes(x->gl_xlabel, x->gl_nxlabels * sizeof(*(x->gl_xlabel)));
1360 freebytes(x->gl_ylabel, x->gl_nylabels * sizeof(*(x->gl_ylabel)));
1361 gstub_cutoff(x->gl_stub);
1362}
1363
1364void glist_free(t_glist *x)
1365{
1366 glist_cleanup(x);
1367 freebytes(x, sizeof(*x));
1368}
1369
1370/* --------------- inlets and outlets ----------- */
1371
1372
1373t_inlet *canvas_addinlet(t_canvas *x, t_pd *who, t_symbol *s)
1374{
1375 t_inlet *ip = inlet_new(&x->gl_obj, who, s, 0);
1376 if (!x->gl_loading && x->gl_owner && glist_isvisible(x->gl_owner))
1377 {
1378 gobj_vis(&x->gl_gobj, x->gl_owner, 0);
1379 gobj_vis(&x->gl_gobj, x->gl_owner, 1);
1380 canvas_fixlinesfor(x->gl_owner, &x->gl_obj);
1381 }
1382 if (!x->gl_loading) canvas_resortinlets(x);
1383 return (ip);
1384}
1385
1386void canvas_rminlet(t_canvas *x, t_inlet *ip)
1387{
1388 t_canvas *owner = x->gl_owner;
1389 int redraw = (owner && glist_isvisible(owner) && (!owner->gl_isdeleting)
1390 && glist_istoplevel(owner));
1391
1392 if (owner) canvas_deletelinesforio(owner, &x->gl_obj, ip, 0);
1393 if (redraw)
1394 gobj_vis(&x->gl_gobj, x->gl_owner, 0);
1395 inlet_free(ip);
1396 if (redraw)
1397 {
1398 gobj_vis(&x->gl_gobj, x->gl_owner, 1);
1399 canvas_fixlinesfor(x->gl_owner, &x->gl_obj);
1400 }
1401}
1402
1403extern t_inlet *vinlet_getit(t_pd *x);
1404extern void obj_moveinletfirst(t_object *x, t_inlet *i);
1405
1406void canvas_resortinlets(t_canvas *x)
1407{
1408 int ninlets = 0, i, j, xmax;
1409 t_gobj *y, **vec, **vp, **maxp;
1410
1411 for (ninlets = 0, y = x->gl_list; y; y = y->g_next)
1412 if (pd_class(&y->g_pd) == vinlet_class) ninlets++;
1413
1414 if (ninlets < 2) return;
1415
1416 vec = (t_gobj **)getbytes(ninlets * sizeof(*vec));
1417
1418 for (y = x->gl_list, vp = vec; y; y = y->g_next)
1419 if (pd_class(&y->g_pd) == vinlet_class) *vp++ = y;
1420
1421 for (i = ninlets; i--;)
1422 {
1423 t_inlet *ip;
1424 for (vp = vec, xmax = -0x7fffffff, maxp = 0, j = ninlets;
1425 j--; vp++)
1426 {
1427 int x1, y1, x2, y2;
1428 t_gobj *g = *vp;
1429 if (!g) continue;
1430 gobj_getrect(g, x, &x1, &y1, &x2, &y2);
1431 if (x1 > xmax) xmax = x1, maxp = vp;
1432 }
1433 if (!maxp) break;
1434 y = *maxp;
1435 *maxp = 0;
1436 ip = vinlet_getit(&y->g_pd);
1437
1438 obj_moveinletfirst(&x->gl_obj, ip);
1439 }
1440 freebytes(vec, ninlets * sizeof(*vec));
1441 if (x->gl_owner && glist_isvisible(x->gl_owner))
1442 canvas_fixlinesfor(x->gl_owner, &x->gl_obj);
1443}
1444
1445t_outlet *canvas_addoutlet(t_canvas *x, t_pd *who, t_symbol *s)
1446{
1447 t_outlet *op = outlet_new(&x->gl_obj, s);
1448 if (!x->gl_loading && x->gl_owner && glist_isvisible(x->gl_owner))
1449 {
1450 gobj_vis(&x->gl_gobj, x->gl_owner, 0);
1451 gobj_vis(&x->gl_gobj, x->gl_owner, 1);
1452 canvas_fixlinesfor(x->gl_owner, &x->gl_obj);
1453 }
1454 if (!x->gl_loading) canvas_resortoutlets(x);
1455 return (op);
1456}
1457
1458void canvas_rmoutlet(t_canvas *x, t_outlet *op)
1459{
1460 t_canvas *owner = x->gl_owner;
1461 int redraw = (owner && glist_isvisible(owner) && (!owner->gl_isdeleting)
1462 && glist_istoplevel(owner));
1463
1464 if (owner) canvas_deletelinesforio(owner, &x->gl_obj, 0, op);
1465 if (redraw)
1466 gobj_vis(&x->gl_gobj, x->gl_owner, 0);
1467
1468 outlet_free(op);
1469 if (redraw)
1470 {
1471 gobj_vis(&x->gl_gobj, x->gl_owner, 1);
1472 canvas_fixlinesfor(x->gl_owner, &x->gl_obj);
1473 }
1474}
1475
1476extern t_outlet *voutlet_getit(t_pd *x);
1477extern void obj_moveoutletfirst(t_object *x, t_outlet *i);
1478
1479void canvas_resortoutlets(t_canvas *x)
1480{
1481 int noutlets = 0, i, j, xmax;
1482 t_gobj *y, **vec, **vp, **maxp;
1483
1484 for (noutlets = 0, y = x->gl_list; y; y = y->g_next)
1485 if (pd_class(&y->g_pd) == voutlet_class) noutlets++;
1486
1487 if (noutlets < 2) return;
1488
1489 vec = (t_gobj **)getbytes(noutlets * sizeof(*vec));
1490
1491 for (y = x->gl_list, vp = vec; y; y = y->g_next)
1492 if (pd_class(&y->g_pd) == voutlet_class) *vp++ = y;
1493
1494 for (i = noutlets; i--;)
1495 {
1496 t_outlet *ip;
1497 for (vp = vec, xmax = -0x7fffffff, maxp = 0, j = noutlets;
1498 j--; vp++)
1499 {
1500 int x1, y1, x2, y2;
1501 t_gobj *g = *vp;
1502 if (!g) continue;
1503 gobj_getrect(g, x, &x1, &y1, &x2, &y2);
1504 if (x1 > xmax) xmax = x1, maxp = vp;
1505 }
1506 if (!maxp) break;
1507 y = *maxp;
1508 *maxp = 0;
1509 ip = voutlet_getit(&y->g_pd);
1510
1511 obj_moveoutletfirst(&x->gl_obj, ip);
1512 }
1513 freebytes(vec, noutlets * sizeof(*vec));
1514 if (x->gl_owner && glist_isvisible(x->gl_owner))
1515 canvas_fixlinesfor(x->gl_owner, &x->gl_obj);
1516}
1517
1518/* ----------calculating coordinates and controlling appearance --------- */
1519
1520
1521static void graph_bounds(t_glist *x, t_floatarg x1, t_floatarg y1,
1522 t_floatarg x2, t_floatarg y2)
1523{
1524 x->gl_x1 = x1;
1525 x->gl_x2 = x2;
1526 x->gl_y1 = y1;
1527 x->gl_y2 = y2;
1528 if (x->gl_x2 == x->gl_x1 ||
1529 x->gl_y2 == x->gl_y1)
1530 {
1531 error("graph: empty bounds rectangle");
1532 x1 = y1 = 0;
1533 x2 = y2 = 1;
1534 }
1535 glist_redraw(x);
1536}
1537
1538static void graph_xticks(t_glist *x,
1539 t_floatarg point, t_floatarg inc, t_floatarg f)
1540{
1541 x->gl_xtick.k_point = point;
1542 x->gl_xtick.k_inc = inc;
1543 x->gl_xtick.k_lperb = f;
1544 glist_redraw(x);
1545}
1546
1547static void graph_yticks(t_glist *x,
1548 t_floatarg point, t_floatarg inc, t_floatarg f)
1549{
1550 x->gl_ytick.k_point = point;
1551 x->gl_ytick.k_inc = inc;
1552 x->gl_ytick.k_lperb = f;
1553 glist_redraw(x);
1554}
1555
1556static void graph_xlabel(t_glist *x, t_symbol *s, int argc, t_atom *argv)
1557{
1558 int i;
1559 if (argc < 1) error("graph_xlabel: no y value given");
1560 else
1561 {
1562 x->gl_xlabely = atom_getfloat(argv);
1563 argv++; argc--;
1564 x->gl_xlabel = (t_symbol **)t_resizebytes(x->gl_xlabel,
1565 x->gl_nxlabels * sizeof (t_symbol *), argc * sizeof (t_symbol *));
1566 x->gl_nxlabels = argc;
1567 for (i = 0; i < argc; i++) x->gl_xlabel[i] = atom_gensym(&argv[i]);
1568 }
1569 glist_redraw(x);
1570}
1571
1572static void graph_ylabel(t_glist *x, t_symbol *s, int argc, t_atom *argv)
1573{
1574 int i;
1575 if (argc < 1) error("graph_ylabel: no x value given");
1576 else
1577 {
1578 x->gl_ylabelx = atom_getfloat(argv);
1579 argv++; argc--;
1580 x->gl_ylabel = (t_symbol **)t_resizebytes(x->gl_ylabel,
1581 x->gl_nylabels * sizeof (t_symbol *), argc * sizeof (t_symbol *));
1582 x->gl_nylabels = argc;
1583 for (i = 0; i < argc; i++) x->gl_ylabel[i] = atom_gensym(&argv[i]);
1584 }
1585 glist_redraw(x);
1586}
1587
1588/****** routines to convert pixels to X or Y value and vice versa ******/
1589
1590 /* convert an x pixel value to an x coordinate value */
1591float glist_pixelstox(t_glist *x, float xpix)
1592{
1593 /* if we appear as a text box on parent, our range in our
1594 coordinates (x1, etc.) specifies the coordinate range
1595 of a one-pixel square at top left of the window. */
1596 if (!x->gl_isgraph)
1597 return (x->gl_x1 + (x->gl_x2 - x->gl_x1) * xpix);
1598
1599 /* if we're a graph when shown on parent, but own our own
1600 window right now, our range in our coordinates (x1, etc.) is spread
1601 over the visible window size, given by screenx1, etc. */
1602 else if (x->gl_isgraph && x->gl_havewindow)
1603 return (x->gl_x1 + (x->gl_x2 - x->gl_x1) *
1604 (xpix) / (x->gl_screenx2 - x->gl_screenx1));
1605
1606 /* otherwise, we appear in a graph within a parent glist,
1607 so get our screen rectangle on parent and transform. */
1608 else
1609 {
1610 int x1, y1, x2, y2;
1611 if (!x->gl_owner)
1612 bug("glist_pixelstox");
1613 graph_graphrect(&x->gl_gobj, x->gl_owner, &x1, &y1, &x2, &y2);
1614 return (x->gl_x1 + (x->gl_x2 - x->gl_x1) *
1615 (xpix - x1) / (x2 - x1));
1616 }
1617}
1618
1619float glist_pixelstoy(t_glist *x, float ypix)
1620{
1621 if (!x->gl_isgraph)
1622 return (x->gl_y1 + (x->gl_y2 - x->gl_y1) * ypix);
1623 else if (x->gl_isgraph && x->gl_havewindow)
1624 return (x->gl_y1 + (x->gl_y2 - x->gl_y1) *
1625 (ypix) / (x->gl_screeny2 - x->gl_screeny1));
1626 else
1627 {
1628 int x1, y1, x2, y2;
1629 if (!x->gl_owner)
1630 bug("glist_pixelstox");
1631 graph_graphrect(&x->gl_gobj, x->gl_owner, &x1, &y1, &x2, &y2);
1632 return (x->gl_y1 + (x->gl_y2 - x->gl_y1) *
1633 (ypix - y1) / (y2 - y1));
1634 }
1635}
1636
1637 /* convert an x coordinate value to an x pixel location in window */
1638float glist_xtopixels(t_glist *x, float xval)
1639{
1640 if (!x->gl_isgraph)
1641 return ((xval - x->gl_x1) / (x->gl_x2 - x->gl_x1));
1642 else if (x->gl_isgraph && x->gl_havewindow)
1643 return (x->gl_screenx2 - x->gl_screenx1) *
1644 (xval - x->gl_x1) / (x->gl_x2 - x->gl_x1);
1645 else
1646 {
1647 int x1, y1, x2, y2;
1648 if (!x->gl_owner)
1649 bug("glist_pixelstox");
1650 graph_graphrect(&x->gl_gobj, x->gl_owner, &x1, &y1, &x2, &y2);
1651 return (x1 + (x2 - x1) * (xval - x->gl_x1) / (x->gl_x2 - x->gl_x1));
1652 }
1653}
1654
1655float glist_ytopixels(t_glist *x, float yval)
1656{
1657 if (!x->gl_isgraph)
1658 return ((yval - x->gl_y1) / (x->gl_y2 - x->gl_y1));
1659 else if (x->gl_isgraph && x->gl_havewindow)
1660 return (x->gl_screeny2 - x->gl_screeny1) *
1661 (yval - x->gl_y1) / (x->gl_y2 - x->gl_y1);
1662 else
1663 {
1664 int x1, y1, x2, y2;
1665 if (!x->gl_owner)
1666 bug("glist_pixelstox");
1667 graph_graphrect(&x->gl_gobj, x->gl_owner, &x1, &y1, &x2, &y2);
1668 return (y1 + (y2 - y1) * (yval - x->gl_y1) / (x->gl_y2 - x->gl_y1));
1669 }
1670}
1671
1672 /* convert an X screen distance to an X coordinate increment.
1673 This is terribly inefficient;
1674 but probably not a big enough CPU hog to warrant optimizing. */
1675float glist_dpixtodx(t_glist *x, float dxpix)
1676{
1677 return (dxpix * (glist_pixelstox(x, 1) - glist_pixelstox(x, 0)));
1678}
1679
1680float glist_dpixtody(t_glist *x, float dypix)
1681{
1682 return (dypix * (glist_pixelstoy(x, 1) - glist_pixelstoy(x, 0)));
1683}
1684
1685 /* get the window location in pixels of a "text" object. The
1686 object's x and y positions are in pixels when the glist they're
1687 in is toplevel. If it's not, we convert to pixels on the parent
1688 window. */
1689int text_xpix(t_text *x, t_glist *glist)
1690{
1691 if (glist->gl_havewindow || !glist->gl_isgraph)
1692 return (x->te_xpix);
1693 else return (glist_xtopixels(glist,
1694 glist->gl_x1 + (glist->gl_x2 - glist->gl_x1) *
1695 x->te_xpix / (glist->gl_screenx2 - glist->gl_screenx1)));
1696}
1697
1698int text_ypix(t_text *x, t_glist *glist)
1699{
1700 if (glist->gl_havewindow || !glist->gl_isgraph)
1701 return (x->te_ypix);
1702 else return (glist_ytopixels(glist,
1703 glist->gl_y1 + (glist->gl_y2 - glist->gl_y1) *
1704 x->te_ypix / (glist->gl_screeny2 - glist->gl_screeny1)));
1705}
1706
1707 /* redraw all the items in a glist. We construe this to mean
1708 redrawing in its own window and on parent, as needed in each case.
1709 This is too conservative -- for instance, when you draw an "open"
1710 rectangle on the parent, you shouldn't have to redraw the window! */
1711void glist_redraw(t_glist *x)
1712{
1713 if (glist_isvisible(x))
1714 {
1715 /* LATER fix the graph_vis() code to handle both cases */
1716 if (glist_istoplevel(x))
1717 {
1718 t_gobj *g;
1719 t_linetraverser t;
1720 t_outconnect *oc;
1721 for (g = x->gl_list; g; g = g->g_next)
1722 {
1723 gobj_vis(g, x, 0);
1724 gobj_vis(g, x, 1);
1725 }
1726 /* redraw all the lines */
1727 linetraverser_start(&t, x);
1728 while (oc = linetraverser_next(&t))
1729 sys_vgui(".x%x.c coords l%x %d %d %d %d\n",
1730 glist_getcanvas(x), oc,
1731 t.tr_lx1, t.tr_ly1, t.tr_lx2, t.tr_ly2);
1732 }
1733 if (x->gl_owner)
1734 {
1735 graph_vis(&x->gl_gobj, x->gl_owner, 0);
1736 graph_vis(&x->gl_gobj, x->gl_owner, 1);
1737 }
1738 }
1739}
1740
1741/* --------------------------- widget behavior ------------------- */
1742
1743extern t_widgetbehavior text_widgetbehavior;
1744
1745 /* Note that some code in here would also be useful for drawing
1746 graph decorations in toplevels... */
1747static void graph_vis(t_gobj *gr, t_glist *parent_glist, int vis)
1748{
1749 t_glist *x = (t_glist *)gr;
1750 char tag[50];
1751 t_gobj *g;
1752 int x1, y1, x2, y2;
1753 /* ordinary subpatches: just act like a text object */
1754 if (!x->gl_isgraph)
1755 {
1756 text_widgetbehavior.w_visfn(gr, parent_glist, vis);
1757 return;
1758 }
1759
1760 if (vis && canvas_showtext(x))
1761 rtext_draw(glist_findrtext(parent_glist, &x->gl_obj));
1762 graph_getrect(gr, parent_glist, &x1, &y1, &x2, &y2);
1763 if (!vis)
1764 rtext_erase(glist_findrtext(parent_glist, &x->gl_obj));
1765
1766 sprintf(tag, "graph%x", (int)x);
1767 if (vis)
1768 glist_drawiofor(parent_glist, &x->gl_obj, 1,
1769 tag, x1, y1, x2, y2);
1770 else glist_eraseiofor(parent_glist, &x->gl_obj, tag);
1771 /* if we look like a graph but have been moved to a toplevel,
1772 just show the bounding rectangle */
1773 if (x->gl_havewindow)
1774 {
1775 if (vis)
1776 {
1777 sys_vgui(".x%x.c create polygon\
1778 %d %d %d %d %d %d %d %d %d %d -tags %s -fill #c0c0c0\n",
1779 glist_getcanvas(x->gl_owner),
1780 x1, y1, x1, y2, x2, y2, x2, y1, x1, y1, tag);
1781 }
1782 else
1783 {
1784 sys_vgui(".x%x.c delete %s\n",
1785 glist_getcanvas(x->gl_owner), tag);
1786 }
1787 return;
1788 }
1789 /* otherwise draw (or erase) us as a graph inside another glist. */
1790 if (vis)
1791 {
1792 int i;
1793 float f;
1794
1795 /* draw a rectangle around the graph */
1796 sys_vgui(".x%x.c create line\
1797 %d %d %d %d %d %d %d %d %d %d -tags %s\n",
1798 glist_getcanvas(x->gl_owner),
1799 x1, y1, x1, y2, x2, y2, x2, y1, x1, y1, tag);
1800
1801 /* draw ticks on horizontal borders. If lperb field is
1802 zero, this is disabled. */
1803 if (x->gl_xtick.k_lperb)
1804 {
1805 float upix, lpix;
1806 if (y2 < y1)
1807 upix = y1, lpix = y2;
1808 else upix = y2, lpix = y1;
1809 for (i = 0, f = x->gl_xtick.k_point;
1810 f < 0.99 * x->gl_x2 + 0.01*x->gl_x1; i++,
1811 f += x->gl_xtick.k_inc)
1812 {
1813 int tickpix = (i % x->gl_xtick.k_lperb ? 2 : 4);
1814 sys_vgui(".x%x.c create line %d %d %d %d -tags %s\n",
1815 glist_getcanvas(x->gl_owner),
1816 (int)glist_xtopixels(x, f), (int)upix,
1817 (int)glist_xtopixels(x, f), (int)upix - tickpix, tag);
1818 sys_vgui(".x%x.c create line %d %d %d %d -tags %s\n",
1819 glist_getcanvas(x->gl_owner),
1820 (int)glist_xtopixels(x, f), (int)lpix,
1821 (int)glist_xtopixels(x, f), (int)lpix + tickpix, tag);
1822 }
1823 for (i = 1, f = x->gl_xtick.k_point - x->gl_xtick.k_inc;
1824 f > 0.99 * x->gl_x1 + 0.01*x->gl_x2;
1825 i++, f -= x->gl_xtick.k_inc)
1826 {
1827 int tickpix = (i % x->gl_xtick.k_lperb ? 2 : 4);
1828 sys_vgui(".x%x.c create line %d %d %d %d -tags %s\n",
1829 glist_getcanvas(x->gl_owner),
1830 (int)glist_xtopixels(x, f), (int)upix,
1831 (int)glist_xtopixels(x, f), (int)upix - tickpix, tag);
1832 sys_vgui(".x%x.c create line %d %d %d %d -tags %s\n",
1833 glist_getcanvas(x->gl_owner),
1834 (int)glist_xtopixels(x, f), (int)lpix,
1835 (int)glist_xtopixels(x, f), (int)lpix + tickpix, tag);
1836 }
1837 }
1838
1839 /* draw ticks in vertical borders*/
1840 if (x->gl_ytick.k_lperb)
1841 {
1842 float ubound, lbound;
1843 if (x->gl_y2 < x->gl_y1)
1844 ubound = x->gl_y1, lbound = x->gl_y2;
1845 else ubound = x->gl_y2, lbound = x->gl_y1;
1846 for (i = 0, f = x->gl_ytick.k_point;
1847 f < 0.99 * ubound + 0.01 * lbound;
1848 i++, f += x->gl_ytick.k_inc)
1849 {
1850 int tickpix = (i % x->gl_ytick.k_lperb ? 2 : 4);
1851 sys_vgui(".x%x.c create line %d %d %d %d -tags %s\n",
1852 glist_getcanvas(x->gl_owner),
1853 x1, (int)glist_ytopixels(x, f),
1854 x1 + tickpix, (int)glist_ytopixels(x, f), tag);
1855 sys_vgui(".x%x.c create line %d %d %d %d -tags %s\n",
1856 glist_getcanvas(x->gl_owner),
1857 x2, (int)glist_ytopixels(x, f),
1858 x2 - tickpix, (int)glist_ytopixels(x, f), tag);
1859 }
1860 for (i = 1, f = x->gl_ytick.k_point - x->gl_ytick.k_inc;
1861 f > 0.99 * lbound + 0.01 * ubound;
1862 i++, f -= x->gl_ytick.k_inc)
1863 {
1864 int tickpix = (i % x->gl_ytick.k_lperb ? 2 : 4);
1865 sys_vgui(".x%x.c create line %d %d %d %d -tags %s\n",
1866 glist_getcanvas(x->gl_owner),
1867 x1, (int)glist_ytopixels(x, f),
1868 x1 + tickpix, (int)glist_ytopixels(x, f), tag);
1869 sys_vgui(".x%x.c create line %d %d %d %d -tags %s\n",
1870 glist_getcanvas(x->gl_owner),
1871 x2, (int)glist_ytopixels(x, f),
1872 x2 - tickpix, (int)glist_ytopixels(x, f), tag);
1873 }
1874 }
1875 /* draw x labels */
1876 for (i = 0; i < x->gl_nxlabels; i++)
1877 sys_vgui(".x%x.c create text\
1878 %d %d -text {%s} -font -*-courier-bold--normal--%d-* -tags %s\n",
1879 glist_getcanvas(x),
1880 (int)glist_xtopixels(x, atof(x->gl_xlabel[i]->s_name)),
1881 (int)glist_ytopixels(x, x->gl_xlabely), x->gl_xlabel[i]->s_name,
1882 glist_getfont(x), tag);
1883
1884 /* draw y labels */
1885 for (i = 0; i < x->gl_nylabels; i++)
1886 sys_vgui(".x%x.c create text\
1887 %d %d -text {%s} -font -*-courier-bold--normal--%d-* -tags %s\n",
1888 glist_getcanvas(x),
1889 (int)glist_xtopixels(x, x->gl_ylabelx),
1890 (int)glist_ytopixels(x, atof(x->gl_ylabel[i]->s_name)),
1891 x->gl_ylabel[i]->s_name,
1892 glist_getfont(x), tag);
1893
1894 /* draw contents of graph as glist */
1895 for (g = x->gl_list; g; g = g->g_next)
1896 gobj_vis(g, x, 1);
1897 }
1898 else
1899 {
1900 sys_vgui(".x%x.c delete %s\n",
1901 glist_getcanvas(x->gl_owner), tag);
1902 for (g = x->gl_list; g; g = g->g_next)
1903 gobj_vis(g, x, 0);
1904 }
1905}
1906
1907 /* get the graph's rectangle, not counting extra swelling for controls
1908 to keep them inside the graph. This is the "logical" pixel size. */
1909
1910static void graph_graphrect(t_gobj *z, t_glist *glist,
1911 int *xp1, int *yp1, int *xp2, int *yp2)
1912{
1913 t_glist *x = (t_glist *)z;
1914 int x1 = text_xpix(&x->gl_obj, glist);
1915 int y1 = text_ypix(&x->gl_obj, glist);
1916 int x2, y2;
1917#if 0 /* this used to adjust graph size when it was in another graph;
1918 now we just preserve the size. */
1919 /* same logic here as in text_xpix(): */
1920 if (glist->gl_havewindow)
1921 {
1922 x2 = x1 + x->gl_pixwidth;
1923 y2 = y1 + x->gl_pixheight;
1924 }
1925 else
1926 {
1927 x2 = glist_xtopixels(glist,
1928 glist->gl_x1 + (glist->gl_x2 - glist->gl_x1) *
1929 (x->gl_obj.te_xpix + x->gl_pixwidth) /
1930 (glist->gl_screenx2 - glist->gl_screenx1));
1931 y2 = glist_ytopixels(glist,
1932 glist->gl_y1 + (glist->gl_y2 - glist->gl_y1) *
1933 (x->gl_obj.te_ypix + x->gl_pixheight) /
1934 (glist->gl_screeny2 - glist->gl_screeny1));
1935 }
1936#endif
1937 x2 = x1 + x->gl_pixwidth;
1938 y2 = y1 + x->gl_pixheight;
1939
1940 *xp1 = x1;
1941 *yp1 = y1;
1942 *xp2 = x2;
1943 *yp2 = y2;
1944}
1945
1946 /* get the rectangle, enlarged to contain all the "contents" --
1947 meaning their formal bounds rectangles. */
1948static void graph_getrect(t_gobj *z, t_glist *glist,
1949 int *xp1, int *yp1, int *xp2, int *yp2)
1950{
1951 int x1 = 0x7fffffff, y1 = 0x7fffffff, x2 = -0x7fffffff, y2 = -0x7fffffff;
1952 t_glist *x = (t_glist *)z;
1953 if (x->gl_isgraph)
1954 {
1955 int hadwindow;
1956 t_gobj *g;
1957 t_text *ob;
1958 int x21, y21, x22, y22;
1959
1960 graph_graphrect(z, glist, &x1, &y1, &x2, &y2);
1961 if (canvas_showtext(x))
1962 {
1963 text_widgetbehavior.w_getrectfn(z, glist, &x21, &y21, &x22, &y22);
1964 if (x22 > x2)
1965 x2 = x22;
1966 if (y22 > y2)
1967 y2 = y22;
1968 }
1969 /* lie about whether we have our own window to affect gobj_getrect
1970 calls below. (LATER add argument to gobj_getrect()?) */
1971 hadwindow = x->gl_havewindow;
1972 x->gl_havewindow = 0;
1973 for (g = x->gl_list; g; g = g->g_next)
1974 if ((!(ob = pd_checkobject(&g->g_pd))) || text_shouldvis(ob, x))
1975 {
1976 /* don't do this for arrays, just let them hang outsize the
1977 box. */
1978 if (pd_class(&g->g_pd) == garray_class)
1979 continue;
1980 gobj_getrect(g, x, &x21, &y21, &x22, &y22);
1981 if (x22 > x2)
1982 x2 = x22;
1983 if (y22 > y2)
1984 y2 = y22;
1985 }
1986 x->gl_havewindow = hadwindow;
1987 }
1988 else text_widgetbehavior.w_getrectfn(z, glist, &x1, &y1, &x2, &y2);
1989 *xp1 = x1;
1990 *yp1 = y1;
1991 *xp2 = x2;
1992 *yp2 = y2;
1993}
1994
1995static void graph_displace(t_gobj *z, t_glist *glist, int dx, int dy)
1996{
1997 t_glist *x = (t_glist *)z;
1998 if (!x->gl_isgraph)
1999 text_widgetbehavior.w_displacefn(z, glist, dx, dy);
2000 else
2001 {
2002 x->gl_obj.te_xpix += dx;
2003 x->gl_obj.te_ypix += dy;
2004 glist_redraw(x);
2005 canvas_fixlinesfor(glist_getcanvas(glist), &x->gl_obj);
2006 }
2007}
2008
2009static void graph_select(t_gobj *z, t_glist *glist, int state)
2010{
2011 t_glist *x = (t_glist *)z;
2012 if (!x->gl_isgraph)
2013 text_widgetbehavior.w_selectfn(z, glist, state);
2014 else
2015 {
2016 t_rtext *y = glist_findrtext(glist, &x->gl_obj);
2017 if (canvas_showtext(x))
2018 rtext_select(y, state);
2019 sys_vgui(".x%x.c itemconfigure %sR -fill %s\n", glist,
2020 rtext_gettag(y), (state? "blue" : "black"));
2021 sys_vgui(".x%x.c itemconfigure graph%x -fill %s\n",
2022 glist_getcanvas(glist), z, (state? "blue" : "black"));
2023 }
2024}
2025
2026static void graph_activate(t_gobj *z, t_glist *glist, int state)
2027{
2028 t_glist *x = (t_glist *)z;
2029 if (canvas_showtext(x))
2030 text_widgetbehavior.w_activatefn(z, glist, state);
2031}
2032
2033#if 0
2034static void graph_delete(t_gobj *z, t_glist *glist)
2035{
2036 t_glist *x = (t_glist *)z;
2037 if (!x->gl_isgraph)
2038 text_widgetbehavior.w_deletefn(z, glist);
2039 else
2040 {
2041 t_gobj *y;
2042 while (y = x->gl_list) glist_delete(x, y);
2043#if 0 /* I think this was just wrong. */
2044 if (glist_isvisible(x))
2045 sys_vgui(".x%x.c delete graph%x\n", glist_getcanvas(glist), x);
2046#endif
2047 }
2048}
2049#endif
2050
2051static void graph_delete(t_gobj *z, t_glist *glist)
2052{
2053 t_glist *x = (t_glist *)z;
2054 t_gobj *y;
2055 text_widgetbehavior.w_deletefn(z, glist);
2056 while (y = x->gl_list)
2057 glist_delete(x, y);
2058}
2059
2060static float graph_lastxpix, graph_lastypix;
2061
2062static void graph_motion(void *z, t_floatarg dx, t_floatarg dy)
2063{
2064 t_glist *x = (t_glist *)z;
2065 float newxpix = graph_lastxpix + dx, newypix = graph_lastypix + dy;
2066 t_garray *a = (t_garray *)(x->gl_list);
2067 int oldx = 0.5 + glist_pixelstox(x, graph_lastxpix);
2068 int newx = 0.5 + glist_pixelstox(x, newxpix);
2069 t_sample *vec;
2070 int nelem, i;
2071 float oldy = glist_pixelstoy(x, graph_lastypix);
2072 float newy = glist_pixelstoy(x, newypix);
2073 graph_lastxpix = newxpix;
2074 graph_lastypix = newypix;
2075 /* verify that the array is OK */
2076 if (!a || pd_class((t_pd *)a) != garray_class)
2077 return;
2078 if (!garray_getfloatarray(a, &nelem, &vec))
2079 return;
2080 if (oldx < 0) oldx = 0;
2081 if (oldx >= nelem)
2082 oldx = nelem - 1;
2083 if (newx < 0) newx = 0;
2084 if (newx >= nelem)
2085 newx = nelem - 1;
2086 if (oldx < newx - 1)
2087 {
2088 for (i = oldx + 1; i <= newx; i++)
2089 vec[i] = newy + (oldy - newy) *
2090 ((float)(newx - i))/(float)(newx - oldx);
2091 }
2092 else if (oldx > newx + 1)
2093 {
2094 for (i = oldx - 1; i >= newx; i--)
2095 vec[i] = newy + (oldy - newy) *
2096 ((float)(newx - i))/(float)(newx - oldx);
2097 }
2098 else vec[newx] = newy;
2099 garray_redraw(a);
2100}
2101
2102static int graph_click(t_gobj *z, struct _glist *glist,
2103 int xpix, int ypix, int shift, int alt, int dbl, int doit)
2104{
2105 t_glist *x = (t_glist *)z;
2106 t_gobj *y;
2107 int clickreturned = 0;
2108 if (!x->gl_isgraph)
2109 return (text_widgetbehavior.w_clickfn(z, glist,
2110 xpix, ypix, shift, alt, dbl, doit));
2111 else if (x->gl_havewindow)
2112 return (0);
2113 else
2114 {
2115 for (y = x->gl_list; y; y = y->g_next)
2116 {
2117 int x1, y1, x2, y2;
2118 /* check if the object wants to be clicked */
2119 if (canvas_hitbox(x, y, xpix, ypix, &x1, &y1, &x2, &y2)
2120 && (clickreturned = gobj_click(y, x, xpix, ypix,
2121 shift, alt, 0, doit)))
2122 break;
2123 }
2124 if (!doit)
2125 {
2126 if (y)
2127 canvas_setcursor(glist_getcanvas(x), clickreturned);
2128 else canvas_setcursor(glist_getcanvas(x), CURSOR_RUNMODE_NOTHING);
2129 }
2130 return (clickreturned);
2131 }
2132}
2133
2134void garray_properties(t_garray *x);
2135
2136t_widgetbehavior graph_widgetbehavior =
2137{
2138 graph_getrect,
2139 graph_displace,
2140 graph_select,
2141 graph_activate,
2142 graph_delete,
2143 graph_vis,
2144 graph_click,
2145};
2146
2147void graph_properties(t_gobj *z, t_glist *owner)
2148{
2149 t_glist *x = (t_glist *)z;
2150 {
2151 t_gobj *y;
2152 char graphbuf[200];
2153 sprintf(graphbuf, "pdtk_graph_dialog %%s %g %g %g %g %d %d\n",
2154 x->gl_x1, x->gl_y1, x->gl_x2, x->gl_y2,
2155 x->gl_pixwidth, x->gl_pixheight);
2156 gfxstub_new(&x->gl_pd, x, graphbuf);
2157
2158 for (y = x->gl_list; y; y = y->g_next)
2159 if (pd_class(&y->g_pd) == garray_class)
2160 garray_properties((t_garray *)y);
2161 }
2162}
2163
2164 /* find the graph most recently added to this glist;
2165 if none exists, return 0. */
2166
2167t_glist *glist_findgraph(t_glist *x)
2168{
2169 t_gobj *y = 0, *z;
2170 for (z = x->gl_list; z; z = z->g_next)
2171 if (pd_class(&z->g_pd) == canvas_class && ((t_glist *)z)->gl_isgraph)
2172 y = z;
2173 return ((t_glist *)y);
2174}
2175
2176 /* message back from dialog GUI to set parameters. Args are:
2177 1-4: bounds in our coordinates; 5-6: size in parent */
2178static void graph_dialog(t_glist *x, t_symbol *s, int argc, t_atom *argv)
2179{
2180 t_float x1 = atom_getfloatarg(0, argc, argv);
2181 t_float y1 = atom_getfloatarg(1, argc, argv);
2182 t_float x2 = atom_getfloatarg(2, argc, argv);
2183 t_float y2 = atom_getfloatarg(3, argc, argv);
2184 t_float xpix = atom_getfloatarg(4, argc, argv);
2185 t_float ypix = atom_getfloatarg(5, argc, argv);
2186 if (x1 != x->gl_x1 || x2 != x->gl_x2 ||
2187 y1 != x->gl_y1 || y2 != x->gl_y2)
2188 graph_bounds(x, x1, y1, x2, y2);
2189 if (xpix != x->gl_pixwidth || ypix != x->gl_pixheight)
2190 {
2191 x->gl_pixwidth = xpix;
2192 x->gl_pixheight = ypix;
2193 glist_redraw(x);
2194 if (x->gl_owner)
2195 canvas_fixlinesfor(x->gl_owner, &x->gl_obj);
2196 }
2197}
2198
2199extern void canvas_menuarray(t_glist *canvas);
2200
2201void g_graph_setup(void)
2202{
2203 class_setwidget(canvas_class, &graph_widgetbehavior);
2204 class_addmethod(canvas_class, (t_method)graph_bounds, gensym("bounds"),
2205 A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, 0);
2206 class_addmethod(canvas_class, (t_method)graph_xticks, gensym("xticks"),
2207 A_FLOAT, A_FLOAT, A_FLOAT, 0);
2208 class_addmethod(canvas_class, (t_method)graph_xlabel, gensym("xlabel"),
2209 A_GIMME, 0);
2210 class_addmethod(canvas_class, (t_method)graph_yticks, gensym("yticks"),
2211 A_FLOAT, A_FLOAT, A_FLOAT, 0);
2212 class_addmethod(canvas_class, (t_method)graph_ylabel, gensym("ylabel"),
2213 A_GIMME, 0);
2214 class_addmethod(canvas_class, (t_method)graph_array, gensym("array"),
2215 A_SYMBOL, A_FLOAT, A_SYMBOL, A_DEFFLOAT, A_NULL);
2216 class_addmethod(canvas_class, (t_method)canvas_menuarray,
2217 gensym("menuarray"), A_NULL);
2218 class_addmethod(canvas_class, (t_method)graph_dialog, gensym("dialog"),
2219 A_GIMME, 0);
2220 class_addmethod(canvas_class, (t_method)glist_arraydialog,
2221 gensym("arraydialog"), A_SYMBOL, A_FLOAT, A_FLOAT, A_FLOAT, A_NULL);
2222 class_addmethod(canvas_class, (t_method)glist_sort,
2223 gensym("sort"), A_NULL);
2224}