A modern Music Player Daemon based on Rockbox open source high quality audio player
libadwaita
audio
rust
zig
deno
mpris
rockbox
mpd
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#ifdef ROCKBOX
10#include "plugin.h"
11#include "../../pdbox.h"
12#include "m_pd.h"
13#include "g_canvas.h"
14#define snprintf rb->snprintf
15#define atof rb_atof
16#else /* ROCKBOX */
17#include <stdlib.h>
18#include "m_pd.h"
19#include "t_tk.h"
20#include "g_canvas.h"
21#include <stdio.h>
22#include <string.h>
23#endif /* ROCKBOX */
24
25/* ---------------------- forward definitions ----------------- */
26
27static void graph_vis(t_gobj *gr, t_glist *unused_glist, int vis);
28static void graph_graphrect(t_gobj *z, t_glist *glist,
29 int *xp1, int *yp1, int *xp2, int *yp2);
30static void graph_getrect(t_gobj *z, t_glist *glist,
31 int *xp1, int *yp1, int *xp2, int *yp2);
32
33/* -------------------- maintaining the list -------------------- */
34
35void glist_add(t_glist *x, t_gobj *y)
36{
37 t_object *ob;
38 y->g_next = 0;
39 if (!x->gl_list) x->gl_list = y;
40 else
41 {
42 t_gobj *y2;
43 for (y2 = x->gl_list; y2->g_next; y2 = y2->g_next);
44 y2->g_next = y;
45 }
46 if (x->gl_editor && (ob = pd_checkobject(&y->g_pd)))
47 rtext_new(x, ob);
48 if (glist_isvisible(x))
49 gobj_vis(y, x, 1);
50 if (class_isdrawcommand(y->g_pd))
51 canvas_redrawallfortemplate(glist_getcanvas(x));
52}
53
54 /* this is to protect against a hairy problem in which deleting
55 a sub-canvas might delete an inlet on a box, after the box had
56 been invisible-ized, so that we have to protect against redrawing it! */
57int canvas_setdeleting(t_canvas *x, int flag)
58{
59 int ret = x->gl_isdeleting;
60 x->gl_isdeleting = flag;
61 return (ret);
62}
63
64 /* delete an object from a glist and free it */
65void glist_delete(t_glist *x, t_gobj *y)
66{
67 t_gobj *g;
68 t_object *ob;
69 t_gotfn chkdsp = zgetfn(&y->g_pd, gensym("dsp"));
70 t_canvas *canvas = glist_getcanvas(x);
71 int drawcommand = class_isdrawcommand(y->g_pd);
72 int wasdeleting;
73
74 wasdeleting = canvas_setdeleting(canvas, 1);
75 if (x->gl_editor)
76 {
77 if (x->gl_editor->e_grab == y) x->gl_editor->e_grab = 0;
78 if (glist_isselected(x, y)) glist_deselect(x, y);
79
80 /* HACK -- we had phantom outlets not getting erased on the
81 screen because the canvas_setdeleting() mechanism is too
82 crude. LATER carefully set up rules for when the rtexts
83 should exist, so that they stay around until all the
84 steps of becoming invisible are done. In the meantime, just
85 zap the inlets and outlets here... */
86 if (pd_class(&y->g_pd) == canvas_class)
87 {
88 t_glist *gl = (t_glist *)y;
89 if (gl->gl_isgraph)
90 {
91 char tag[80];
92#ifdef ROCKBOX
93 snprintf(tag, sizeof(tag), "graph%lx",
94 (unsigned long) (intptr_t) gl);
95#else /* ROCKBOX */
96 sprintf(tag, "graph%x", (int)gl);
97#endif /* ROCKBOX */
98 glist_eraseiofor(x, &gl->gl_obj, tag);
99 }
100 else
101 {
102 text_eraseborder(&gl->gl_obj, x,
103 rtext_gettag(glist_findrtext(x, &gl->gl_obj)));
104 }
105 }
106 }
107 gobj_delete(y, x);
108 if (glist_isvisible(canvas))
109 gobj_vis(y, x, 0);
110 if (x->gl_editor && (ob = pd_checkobject(&y->g_pd)))
111 rtext_new(x, ob);
112 if (x->gl_list == y) x->gl_list = y->g_next;
113 else for (g = x->gl_list; g; g = g->g_next)
114 if (g->g_next == y)
115 {
116 g->g_next = y->g_next;
117 break;
118 }
119 pd_free(&y->g_pd);
120 if (chkdsp) canvas_update_dsp();
121 if (drawcommand) canvas_redrawallfortemplate(canvas);
122 canvas_setdeleting(canvas, wasdeleting);
123 x->gl_valid = ++glist_valid;
124}
125
126 /* remove every object from a glist. Experimental. */
127void glist_clear(t_glist *x)
128{
129#ifdef ROCKBOX
130 t_gobj *y;
131#else
132 t_gobj *y, *y2;
133#endif
134 int dspstate = canvas_suspend_dsp();
135 while((y = x->gl_list))
136 glist_delete(x, y);
137 canvas_resume_dsp(dspstate);
138}
139
140void glist_retext(t_glist *glist, t_text *y)
141{
142#ifdef ROCKBOX
143 glist_getcanvas(glist);
144#else
145 t_canvas *c = glist_getcanvas(glist);
146#endif
147 /* check that we have built rtexts yet. LATER need a better test. */
148 if (glist->gl_editor && glist->gl_editor->e_rtext)
149 {
150 t_rtext *rt = glist_findrtext(glist, y);
151 if (rt)
152 rtext_retext(rt);
153 }
154}
155
156void glist_grab(t_glist *x, t_gobj *y, t_glistmotionfn motionfn,
157 t_glistkeyfn keyfn, int xpos, int ypos)
158{
159 t_glist *x2 = glist_getcanvas(x);
160 if (motionfn)
161 x2->gl_editor->e_onmotion = MA_PASSOUT;
162 else x2->gl_editor->e_onmotion = 0;
163 x2->gl_editor->e_grab = y;
164 x2->gl_editor->e_motionfn = motionfn;
165 x2->gl_editor->e_keyfn = keyfn;
166 x2->gl_editor->e_xwas = xpos;
167 x2->gl_editor->e_ywas = ypos;
168}
169
170t_canvas *glist_getcanvas(t_glist *x)
171{
172 while (x->gl_owner && !x->gl_havewindow && x->gl_isgraph)
173 x = x->gl_owner;
174 return((t_canvas *)x);
175}
176
177static float gobj_getxforsort(t_gobj *g)
178{
179 if (pd_class(&g->g_pd) == scalar_class)
180 {
181 float x1, y1;
182 scalar_getbasexy((t_scalar *)g, &x1, &y1);
183 return(x1);
184 }
185 else return (0);
186}
187
188static t_gobj *glist_merge(t_glist *x, t_gobj *g1, t_gobj *g2)
189{
190 t_gobj *g = 0, *g9 = 0;
191 float f1 = 0, f2 = 0;
192#ifdef ROCKBOX
193 (void) x;
194#endif
195 if (g1)
196 f1 = gobj_getxforsort(g1);
197 if (g2)
198 f2 = gobj_getxforsort(g2);
199 while (1)
200 {
201 if (g1)
202 {
203 if (g2)
204 {
205 if (f1 <= f2)
206 goto put1;
207 else goto put2;
208 }
209 else goto put1;
210 }
211 else if (g2)
212 goto put2;
213 else break;
214 put1:
215 if (g9)
216 g9->g_next = g1, g9 = g1;
217 else g9 = g = g1;
218 if((g1 = g1->g_next))
219 f1 = gobj_getxforsort(g1);
220 g9->g_next = 0;
221 continue;
222 put2:
223 if (g9)
224 g9->g_next = g2, g9 = g2;
225 else g9 = g = g2;
226 if((g2 = g2->g_next))
227 f2 = gobj_getxforsort(g2);
228 g9->g_next = 0;
229 continue;
230 }
231 return (g);
232}
233
234static t_gobj *glist_dosort(t_glist *x,
235 t_gobj *g, int nitems)
236{
237 if (nitems < 2)
238 return (g);
239 else
240 {
241 int n1 = nitems/2, n2 = nitems - n1, i;
242 t_gobj *g2, *g3;
243 for (g2 = g, i = n1-1; i--; g2 = g2->g_next)
244 ;
245 g3 = g2->g_next;
246 g2->g_next = 0;
247 g = glist_dosort(x, g, n1);
248 g3 = glist_dosort(x, g3, n2);
249 return (glist_merge(x, g, g3));
250 }
251}
252
253void glist_sort(t_glist *x)
254{
255 int nitems = 0, foo = 0;
256 float lastx = -1e37;
257 t_gobj *g;
258 for (g = x->gl_list; g; g = g->g_next)
259 {
260 float x1 = gobj_getxforsort(g);
261 if (x1 < lastx)
262 foo = 1;
263 lastx = x1;
264 nitems++;
265 }
266 if (foo)
267 x->gl_list = glist_dosort(x, x->gl_list, nitems);
268}
269
270void glist_cleanup(t_glist *x)
271{
272 freebytes(x->gl_xlabel, x->gl_nxlabels * sizeof(*(x->gl_xlabel)));
273 freebytes(x->gl_ylabel, x->gl_nylabels * sizeof(*(x->gl_ylabel)));
274 gstub_cutoff(x->gl_stub);
275}
276
277void glist_free(t_glist *x)
278{
279 glist_cleanup(x);
280 freebytes(x, sizeof(*x));
281}
282
283/* --------------- inlets and outlets ----------- */
284
285
286t_inlet *canvas_addinlet(t_canvas *x, t_pd *who, t_symbol *s)
287{
288 t_inlet *ip = inlet_new(&x->gl_obj, who, s, 0);
289 if (!x->gl_loading && x->gl_owner && glist_isvisible(x->gl_owner))
290 {
291 gobj_vis(&x->gl_gobj, x->gl_owner, 0);
292 gobj_vis(&x->gl_gobj, x->gl_owner, 1);
293 canvas_fixlinesfor(x->gl_owner, &x->gl_obj);
294 }
295 if (!x->gl_loading) canvas_resortinlets(x);
296 return (ip);
297}
298
299void canvas_rminlet(t_canvas *x, t_inlet *ip)
300{
301 t_canvas *owner = x->gl_owner;
302 int redraw = (owner && glist_isvisible(owner) && (!owner->gl_isdeleting)
303 && glist_istoplevel(owner));
304
305 if (owner) canvas_deletelinesforio(owner, &x->gl_obj, ip, 0);
306 if (redraw)
307 gobj_vis(&x->gl_gobj, x->gl_owner, 0);
308 inlet_free(ip);
309 if (redraw)
310 {
311 gobj_vis(&x->gl_gobj, x->gl_owner, 1);
312 canvas_fixlinesfor(x->gl_owner, &x->gl_obj);
313 }
314}
315
316extern t_inlet *vinlet_getit(t_pd *x);
317extern void obj_moveinletfirst(t_object *x, t_inlet *i);
318
319void canvas_resortinlets(t_canvas *x)
320{
321 int ninlets = 0, i, j, xmax;
322 t_gobj *y, **vec, **vp, **maxp;
323
324 for (ninlets = 0, y = x->gl_list; y; y = y->g_next)
325 if (pd_class(&y->g_pd) == vinlet_class) ninlets++;
326
327 if (ninlets < 2) return;
328
329 vec = (t_gobj **)getbytes(ninlets * sizeof(*vec));
330
331 for (y = x->gl_list, vp = vec; y; y = y->g_next)
332 if (pd_class(&y->g_pd) == vinlet_class) *vp++ = y;
333
334 for (i = ninlets; i--;)
335 {
336 t_inlet *ip;
337 for (vp = vec, xmax = -0x7fffffff, maxp = 0, j = ninlets;
338 j--; vp++)
339 {
340 int x1, y1, x2, y2;
341 t_gobj *g = *vp;
342 if (!g) continue;
343 gobj_getrect(g, x, &x1, &y1, &x2, &y2);
344 if (x1 > xmax) xmax = x1, maxp = vp;
345 }
346 if (!maxp) break;
347 y = *maxp;
348 *maxp = 0;
349 ip = vinlet_getit(&y->g_pd);
350
351 obj_moveinletfirst(&x->gl_obj, ip);
352 }
353 freebytes(vec, ninlets * sizeof(*vec));
354 if (x->gl_owner && glist_isvisible(x->gl_owner))
355 canvas_fixlinesfor(x->gl_owner, &x->gl_obj);
356}
357
358t_outlet *canvas_addoutlet(t_canvas *x, t_pd *who, t_symbol *s)
359{
360 t_outlet *op = outlet_new(&x->gl_obj, s);
361#ifdef ROCKBOX
362 (void) who;
363#endif
364 if (!x->gl_loading && x->gl_owner && glist_isvisible(x->gl_owner))
365 {
366 gobj_vis(&x->gl_gobj, x->gl_owner, 0);
367 gobj_vis(&x->gl_gobj, x->gl_owner, 1);
368 canvas_fixlinesfor(x->gl_owner, &x->gl_obj);
369 }
370 if (!x->gl_loading) canvas_resortoutlets(x);
371 return (op);
372}
373
374void canvas_rmoutlet(t_canvas *x, t_outlet *op)
375{
376 t_canvas *owner = x->gl_owner;
377 int redraw = (owner && glist_isvisible(owner) && (!owner->gl_isdeleting)
378 && glist_istoplevel(owner));
379
380 if (owner) canvas_deletelinesforio(owner, &x->gl_obj, 0, op);
381 if (redraw)
382 gobj_vis(&x->gl_gobj, x->gl_owner, 0);
383
384 outlet_free(op);
385 if (redraw)
386 {
387 gobj_vis(&x->gl_gobj, x->gl_owner, 1);
388 canvas_fixlinesfor(x->gl_owner, &x->gl_obj);
389 }
390}
391
392extern t_outlet *voutlet_getit(t_pd *x);
393extern void obj_moveoutletfirst(t_object *x, t_outlet *i);
394
395void canvas_resortoutlets(t_canvas *x)
396{
397 int noutlets = 0, i, j, xmax;
398 t_gobj *y, **vec, **vp, **maxp;
399
400 for (noutlets = 0, y = x->gl_list; y; y = y->g_next)
401 if (pd_class(&y->g_pd) == voutlet_class) noutlets++;
402
403 if (noutlets < 2) return;
404
405 vec = (t_gobj **)getbytes(noutlets * sizeof(*vec));
406
407 for (y = x->gl_list, vp = vec; y; y = y->g_next)
408 if (pd_class(&y->g_pd) == voutlet_class) *vp++ = y;
409
410 for (i = noutlets; i--;)
411 {
412 t_outlet *ip;
413 for (vp = vec, xmax = -0x7fffffff, maxp = 0, j = noutlets;
414 j--; vp++)
415 {
416 int x1, y1, x2, y2;
417 t_gobj *g = *vp;
418 if (!g) continue;
419 gobj_getrect(g, x, &x1, &y1, &x2, &y2);
420 if (x1 > xmax) xmax = x1, maxp = vp;
421 }
422 if (!maxp) break;
423 y = *maxp;
424 *maxp = 0;
425 ip = voutlet_getit(&y->g_pd);
426
427 obj_moveoutletfirst(&x->gl_obj, ip);
428 }
429 freebytes(vec, noutlets * sizeof(*vec));
430 if (x->gl_owner && glist_isvisible(x->gl_owner))
431 canvas_fixlinesfor(x->gl_owner, &x->gl_obj);
432}
433
434/* ----------calculating coordinates and controlling appearance --------- */
435
436
437static void graph_bounds(t_glist *x, t_floatarg x1, t_floatarg y1,
438 t_floatarg x2, t_floatarg y2)
439{
440 x->gl_x1 = x1;
441 x->gl_x2 = x2;
442 x->gl_y1 = y1;
443 x->gl_y2 = y2;
444 if (x->gl_x2 == x->gl_x1 ||
445 x->gl_y2 == x->gl_y1)
446 {
447 error("graph: empty bounds rectangle");
448 x1 = y1 = 0;
449 x2 = y2 = 1;
450 }
451 glist_redraw(x);
452}
453
454static void graph_xticks(t_glist *x,
455 t_floatarg point, t_floatarg inc, t_floatarg f)
456{
457 x->gl_xtick.k_point = point;
458 x->gl_xtick.k_inc = inc;
459 x->gl_xtick.k_lperb = f;
460 glist_redraw(x);
461}
462
463static void graph_yticks(t_glist *x,
464 t_floatarg point, t_floatarg inc, t_floatarg f)
465{
466 x->gl_ytick.k_point = point;
467 x->gl_ytick.k_inc = inc;
468 x->gl_ytick.k_lperb = f;
469 glist_redraw(x);
470}
471
472static void graph_xlabel(t_glist *x, t_symbol *s, int argc, t_atom *argv)
473{
474 int i;
475#ifdef ROCKBOX
476 (void) s;
477#endif
478 if (argc < 1) error("graph_xlabel: no y value given");
479 else
480 {
481 x->gl_xlabely = atom_getfloat(argv);
482 argv++; argc--;
483 x->gl_xlabel = (t_symbol **)t_resizebytes(x->gl_xlabel,
484 x->gl_nxlabels * sizeof (t_symbol *), argc * sizeof (t_symbol *));
485 x->gl_nxlabels = argc;
486 for (i = 0; i < argc; i++) x->gl_xlabel[i] = atom_gensym(&argv[i]);
487 }
488 glist_redraw(x);
489}
490
491static void graph_ylabel(t_glist *x, t_symbol *s, int argc, t_atom *argv)
492{
493 int i;
494#ifdef ROCKBOX
495 (void) s;
496#endif
497 if (argc < 1) error("graph_ylabel: no x value given");
498 else
499 {
500 x->gl_ylabelx = atom_getfloat(argv);
501 argv++; argc--;
502 x->gl_ylabel = (t_symbol **)t_resizebytes(x->gl_ylabel,
503 x->gl_nylabels * sizeof (t_symbol *), argc * sizeof (t_symbol *));
504 x->gl_nylabels = argc;
505 for (i = 0; i < argc; i++) x->gl_ylabel[i] = atom_gensym(&argv[i]);
506 }
507 glist_redraw(x);
508}
509
510/****** routines to convert pixels to X or Y value and vice versa ******/
511
512 /* convert an x pixel value to an x coordinate value */
513float glist_pixelstox(t_glist *x, float xpix)
514{
515 /* if we appear as a text box on parent, our range in our
516 coordinates (x1, etc.) specifies the coordinate range
517 of a one-pixel square at top left of the window. */
518 if (!x->gl_isgraph)
519 return (x->gl_x1 + (x->gl_x2 - x->gl_x1) * xpix);
520
521 /* if we're a graph when shown on parent, but own our own
522 window right now, our range in our coordinates (x1, etc.) is spread
523 over the visible window size, given by screenx1, etc. */
524 else if (x->gl_isgraph && x->gl_havewindow)
525 return (x->gl_x1 + (x->gl_x2 - x->gl_x1) *
526 (xpix) / (x->gl_screenx2 - x->gl_screenx1));
527
528 /* otherwise, we appear in a graph within a parent glist,
529 so get our screen rectangle on parent and transform. */
530 else
531 {
532 int x1, y1, x2, y2;
533 if (!x->gl_owner)
534 bug("glist_pixelstox");
535 graph_graphrect(&x->gl_gobj, x->gl_owner, &x1, &y1, &x2, &y2);
536 return (x->gl_x1 + (x->gl_x2 - x->gl_x1) *
537 (xpix - x1) / (x2 - x1));
538 }
539}
540
541float glist_pixelstoy(t_glist *x, float ypix)
542{
543 if (!x->gl_isgraph)
544 return (x->gl_y1 + (x->gl_y2 - x->gl_y1) * ypix);
545 else if (x->gl_isgraph && x->gl_havewindow)
546 return (x->gl_y1 + (x->gl_y2 - x->gl_y1) *
547 (ypix) / (x->gl_screeny2 - x->gl_screeny1));
548 else
549 {
550 int x1, y1, x2, y2;
551 if (!x->gl_owner)
552 bug("glist_pixelstox");
553 graph_graphrect(&x->gl_gobj, x->gl_owner, &x1, &y1, &x2, &y2);
554 return (x->gl_y1 + (x->gl_y2 - x->gl_y1) *
555 (ypix - y1) / (y2 - y1));
556 }
557}
558
559 /* convert an x coordinate value to an x pixel location in window */
560float glist_xtopixels(t_glist *x, float xval)
561{
562 if (!x->gl_isgraph)
563 return ((xval - x->gl_x1) / (x->gl_x2 - x->gl_x1));
564 else if (x->gl_isgraph && x->gl_havewindow)
565 return (x->gl_screenx2 - x->gl_screenx1) *
566 (xval - x->gl_x1) / (x->gl_x2 - x->gl_x1);
567 else
568 {
569 int x1, y1, x2, y2;
570 if (!x->gl_owner)
571 bug("glist_pixelstox");
572 graph_graphrect(&x->gl_gobj, x->gl_owner, &x1, &y1, &x2, &y2);
573 return (x1 + (x2 - x1) * (xval - x->gl_x1) / (x->gl_x2 - x->gl_x1));
574 }
575}
576
577float glist_ytopixels(t_glist *x, float yval)
578{
579 if (!x->gl_isgraph)
580 return ((yval - x->gl_y1) / (x->gl_y2 - x->gl_y1));
581 else if (x->gl_isgraph && x->gl_havewindow)
582 return (x->gl_screeny2 - x->gl_screeny1) *
583 (yval - x->gl_y1) / (x->gl_y2 - x->gl_y1);
584 else
585 {
586 int x1, y1, x2, y2;
587 if (!x->gl_owner)
588 bug("glist_pixelstox");
589 graph_graphrect(&x->gl_gobj, x->gl_owner, &x1, &y1, &x2, &y2);
590 return (y1 + (y2 - y1) * (yval - x->gl_y1) / (x->gl_y2 - x->gl_y1));
591 }
592}
593
594 /* convert an X screen distance to an X coordinate increment.
595 This is terribly inefficient;
596 but probably not a big enough CPU hog to warrant optimizing. */
597float glist_dpixtodx(t_glist *x, float dxpix)
598{
599 return (dxpix * (glist_pixelstox(x, 1) - glist_pixelstox(x, 0)));
600}
601
602float glist_dpixtody(t_glist *x, float dypix)
603{
604 return (dypix * (glist_pixelstoy(x, 1) - glist_pixelstoy(x, 0)));
605}
606
607 /* get the window location in pixels of a "text" object. The
608 object's x and y positions are in pixels when the glist they're
609 in is toplevel. If it's not, we convert to pixels on the parent
610 window. */
611int text_xpix(t_text *x, t_glist *glist)
612{
613 if (glist->gl_havewindow || !glist->gl_isgraph)
614 return (x->te_xpix);
615 else return (glist_xtopixels(glist,
616 glist->gl_x1 + (glist->gl_x2 - glist->gl_x1) *
617 x->te_xpix / (glist->gl_screenx2 - glist->gl_screenx1)));
618}
619
620int text_ypix(t_text *x, t_glist *glist)
621{
622 if (glist->gl_havewindow || !glist->gl_isgraph)
623 return (x->te_ypix);
624 else return (glist_ytopixels(glist,
625 glist->gl_y1 + (glist->gl_y2 - glist->gl_y1) *
626 x->te_ypix / (glist->gl_screeny2 - glist->gl_screeny1)));
627}
628
629 /* redraw all the items in a glist. We construe this to mean
630 redrawing in its own window and on parent, as needed in each case.
631 This is too conservative -- for instance, when you draw an "open"
632 rectangle on the parent, you shouldn't have to redraw the window! */
633void glist_redraw(t_glist *x)
634{
635 if (glist_isvisible(x))
636 {
637 /* LATER fix the graph_vis() code to handle both cases */
638 if (glist_istoplevel(x))
639 {
640 t_gobj *g;
641 t_linetraverser t;
642 t_outconnect *oc;
643 for (g = x->gl_list; g; g = g->g_next)
644 {
645 gobj_vis(g, x, 0);
646 gobj_vis(g, x, 1);
647 }
648 /* redraw all the lines */
649 linetraverser_start(&t, x);
650 while((oc = linetraverser_next(&t)))
651#ifdef ROCKBOX
652 ;
653#else /* ROCKBOX */
654 sys_vgui(".x%x.c coords l%x %d %d %d %d\n",
655 glist_getcanvas(x), oc,
656 t.tr_lx1, t.tr_ly1, t.tr_lx2, t.tr_ly2);
657#endif /* ROCKBOX */
658 }
659 if (x->gl_owner)
660 {
661 graph_vis(&x->gl_gobj, x->gl_owner, 0);
662 graph_vis(&x->gl_gobj, x->gl_owner, 1);
663 }
664 }
665}
666
667/* --------------------------- widget behavior ------------------- */
668
669extern t_widgetbehavior text_widgetbehavior;
670
671 /* Note that some code in here would also be useful for drawing
672 graph decorations in toplevels... */
673static void graph_vis(t_gobj *gr, t_glist *parent_glist, int vis)
674{
675 t_glist *x = (t_glist *)gr;
676 char tag[50];
677 t_gobj *g;
678 int x1, y1, x2, y2;
679 /* ordinary subpatches: just act like a text object */
680 if (!x->gl_isgraph)
681 {
682 text_widgetbehavior.w_visfn(gr, parent_glist, vis);
683 return;
684 }
685
686 if (vis && canvas_showtext(x))
687 rtext_draw(glist_findrtext(parent_glist, &x->gl_obj));
688 graph_getrect(gr, parent_glist, &x1, &y1, &x2, &y2);
689 if (!vis)
690 rtext_erase(glist_findrtext(parent_glist, &x->gl_obj));
691
692#ifdef ROCKBOX
693 snprintf(tag, sizeof(tag), "graph%lx", (unsigned long) (intptr_t) x);
694#else
695 sprintf(tag, "graph%x", (int)x);
696#endif
697 if (vis)
698 glist_drawiofor(parent_glist, &x->gl_obj, 1,
699 tag, x1, y1, x2, y2);
700 else glist_eraseiofor(parent_glist, &x->gl_obj, tag);
701 /* if we look like a graph but have been moved to a toplevel,
702 just show the bounding rectangle */
703 if (x->gl_havewindow)
704 {
705#ifndef ROCKBOX
706 if (vis)
707 {
708 sys_vgui(".x%x.c create polygon\
709 %d %d %d %d %d %d %d %d %d %d -tags %s -fill #c0c0c0\n",
710 glist_getcanvas(x->gl_owner),
711 x1, y1, x1, y2, x2, y2, x2, y1, x1, y1, tag);
712 }
713 else
714 {
715 sys_vgui(".x%x.c delete %s\n",
716 glist_getcanvas(x->gl_owner), tag);
717 }
718#endif
719 return;
720 }
721 /* otherwise draw (or erase) us as a graph inside another glist. */
722 if (vis)
723 {
724 int i;
725 float f;
726
727 /* draw a rectangle around the graph */
728#ifndef ROCKBOX
729 sys_vgui(".x%x.c create line\
730 %d %d %d %d %d %d %d %d %d %d -tags %s\n",
731 glist_getcanvas(x->gl_owner),
732 x1, y1, x1, y2, x2, y2, x2, y1, x1, y1, tag);
733#endif
734
735 /* draw ticks on horizontal borders. If lperb field is
736 zero, this is disabled. */
737 if (x->gl_xtick.k_lperb)
738 {
739#ifndef ROCKBOX
740 float upix, lpix;
741 if (y2 < y1)
742 upix = y1, lpix = y2;
743 else upix = y2, lpix = y1;
744#endif
745 for (i = 0, f = x->gl_xtick.k_point;
746 f < 0.99 * x->gl_x2 + 0.01*x->gl_x1; i++,
747 f += x->gl_xtick.k_inc)
748 {
749#ifndef ROCKBOX
750 int tickpix = (i % x->gl_xtick.k_lperb ? 2 : 4);
751 sys_vgui(".x%x.c create line %d %d %d %d -tags %s\n",
752 glist_getcanvas(x->gl_owner),
753 (int)glist_xtopixels(x, f), (int)upix,
754 (int)glist_xtopixels(x, f), (int)upix - tickpix, tag);
755 sys_vgui(".x%x.c create line %d %d %d %d -tags %s\n",
756 glist_getcanvas(x->gl_owner),
757 (int)glist_xtopixels(x, f), (int)lpix,
758 (int)glist_xtopixels(x, f), (int)lpix + tickpix, tag);
759#endif
760 }
761 for (i = 1, f = x->gl_xtick.k_point - x->gl_xtick.k_inc;
762 f > 0.99 * x->gl_x1 + 0.01*x->gl_x2;
763 i++, f -= x->gl_xtick.k_inc)
764 {
765#ifndef ROCKBOX
766 int tickpix = (i % x->gl_xtick.k_lperb ? 2 : 4);
767 sys_vgui(".x%x.c create line %d %d %d %d -tags %s\n",
768 glist_getcanvas(x->gl_owner),
769 (int)glist_xtopixels(x, f), (int)upix,
770 (int)glist_xtopixels(x, f), (int)upix - tickpix, tag);
771 sys_vgui(".x%x.c create line %d %d %d %d -tags %s\n",
772 glist_getcanvas(x->gl_owner),
773 (int)glist_xtopixels(x, f), (int)lpix,
774 (int)glist_xtopixels(x, f), (int)lpix + tickpix, tag);
775#endif
776 }
777 }
778
779 /* draw ticks in vertical borders*/
780 if (x->gl_ytick.k_lperb)
781 {
782 float ubound, lbound;
783 if (x->gl_y2 < x->gl_y1)
784 ubound = x->gl_y1, lbound = x->gl_y2;
785 else ubound = x->gl_y2, lbound = x->gl_y1;
786 for (i = 0, f = x->gl_ytick.k_point;
787 f < 0.99 * ubound + 0.01 * lbound;
788 i++, f += x->gl_ytick.k_inc)
789 {
790#ifndef ROCKBOX
791 int tickpix = (i % x->gl_ytick.k_lperb ? 2 : 4);
792 sys_vgui(".x%x.c create line %d %d %d %d -tags %s\n",
793 glist_getcanvas(x->gl_owner),
794 x1, (int)glist_ytopixels(x, f),
795 x1 + tickpix, (int)glist_ytopixels(x, f), tag);
796 sys_vgui(".x%x.c create line %d %d %d %d -tags %s\n",
797 glist_getcanvas(x->gl_owner),
798 x2, (int)glist_ytopixels(x, f),
799 x2 - tickpix, (int)glist_ytopixels(x, f), tag);
800#endif
801 }
802 for (i = 1, f = x->gl_ytick.k_point - x->gl_ytick.k_inc;
803 f > 0.99 * lbound + 0.01 * ubound;
804 i++, f -= x->gl_ytick.k_inc)
805 {
806#ifndef ROCKBOX
807 int tickpix = (i % x->gl_ytick.k_lperb ? 2 : 4);
808 sys_vgui(".x%x.c create line %d %d %d %d -tags %s\n",
809 glist_getcanvas(x->gl_owner),
810 x1, (int)glist_ytopixels(x, f),
811 x1 + tickpix, (int)glist_ytopixels(x, f), tag);
812 sys_vgui(".x%x.c create line %d %d %d %d -tags %s\n",
813 glist_getcanvas(x->gl_owner),
814 x2, (int)glist_ytopixels(x, f),
815 x2 - tickpix, (int)glist_ytopixels(x, f), tag);
816#endif
817 }
818 }
819 /* draw x labels */
820 for (i = 0; i < x->gl_nxlabels; i++)
821#ifdef ROCKBOX
822 ;
823#else /* ROCKBOX */
824 sys_vgui(".x%x.c create text\
825 %d %d -text {%s} -font -*-courier-bold--normal--%d-* -tags %s\n",
826 glist_getcanvas(x),
827 (int)glist_xtopixels(x, atof(x->gl_xlabel[i]->s_name)),
828 (int)glist_ytopixels(x, x->gl_xlabely), x->gl_xlabel[i]->s_name,
829 glist_getfont(x), tag);
830#endif /* ROCKBOX */
831
832 /* draw y labels */
833 for (i = 0; i < x->gl_nylabels; i++)
834#ifdef ROCKBOX
835 ;
836#else /* ROCKBOX */
837 sys_vgui(".x%x.c create text\
838 %d %d -text {%s} -font -*-courier-bold--normal--%d-* -tags %s\n",
839 glist_getcanvas(x),
840 (int)glist_xtopixels(x, x->gl_ylabelx),
841 (int)glist_ytopixels(x, atof(x->gl_ylabel[i]->s_name)),
842 x->gl_ylabel[i]->s_name,
843 glist_getfont(x), tag);
844#endif /* ROCKBOX */
845
846 /* draw contents of graph as glist */
847 for (g = x->gl_list; g; g = g->g_next)
848 gobj_vis(g, x, 1);
849 }
850 else
851 {
852#ifndef ROCKBOX
853 sys_vgui(".x%x.c delete %s\n",
854 glist_getcanvas(x->gl_owner), tag);
855#endif
856 for (g = x->gl_list; g; g = g->g_next)
857 gobj_vis(g, x, 0);
858 }
859}
860
861 /* get the graph's rectangle, not counting extra swelling for controls
862 to keep them inside the graph. This is the "logical" pixel size. */
863
864static void graph_graphrect(t_gobj *z, t_glist *glist,
865 int *xp1, int *yp1, int *xp2, int *yp2)
866{
867 t_glist *x = (t_glist *)z;
868 int x1 = text_xpix(&x->gl_obj, glist);
869 int y1 = text_ypix(&x->gl_obj, glist);
870 int x2, y2;
871#if 0 /* this used to adjust graph size when it was in another graph;
872 now we just preserve the size. */
873 /* same logic here as in text_xpix(): */
874 if (glist->gl_havewindow)
875 {
876 x2 = x1 + x->gl_pixwidth;
877 y2 = y1 + x->gl_pixheight;
878 }
879 else
880 {
881 x2 = glist_xtopixels(glist,
882 glist->gl_x1 + (glist->gl_x2 - glist->gl_x1) *
883 (x->gl_obj.te_xpix + x->gl_pixwidth) /
884 (glist->gl_screenx2 - glist->gl_screenx1));
885 y2 = glist_ytopixels(glist,
886 glist->gl_y1 + (glist->gl_y2 - glist->gl_y1) *
887 (x->gl_obj.te_ypix + x->gl_pixheight) /
888 (glist->gl_screeny2 - glist->gl_screeny1));
889 }
890#endif
891 x2 = x1 + x->gl_pixwidth;
892 y2 = y1 + x->gl_pixheight;
893
894 *xp1 = x1;
895 *yp1 = y1;
896 *xp2 = x2;
897 *yp2 = y2;
898}
899
900 /* get the rectangle, enlarged to contain all the "contents" --
901 meaning their formal bounds rectangles. */
902static void graph_getrect(t_gobj *z, t_glist *glist,
903 int *xp1, int *yp1, int *xp2, int *yp2)
904{
905 int x1 = 0x7fffffff, y1 = 0x7fffffff, x2 = -0x7fffffff, y2 = -0x7fffffff;
906 t_glist *x = (t_glist *)z;
907 if (x->gl_isgraph)
908 {
909 int hadwindow;
910 t_gobj *g;
911 t_text *ob;
912 int x21, y21, x22, y22;
913
914 graph_graphrect(z, glist, &x1, &y1, &x2, &y2);
915 if (canvas_showtext(x))
916 {
917 text_widgetbehavior.w_getrectfn(z, glist, &x21, &y21, &x22, &y22);
918 if (x22 > x2)
919 x2 = x22;
920 if (y22 > y2)
921 y2 = y22;
922 }
923 /* lie about whether we have our own window to affect gobj_getrect
924 calls below. (LATER add argument to gobj_getrect()?) */
925 hadwindow = x->gl_havewindow;
926 x->gl_havewindow = 0;
927 for (g = x->gl_list; g; g = g->g_next)
928 if ((!(ob = pd_checkobject(&g->g_pd))) || text_shouldvis(ob, x))
929 {
930 /* don't do this for arrays, just let them hang outsize the
931 box. */
932 if (pd_class(&g->g_pd) == garray_class)
933 continue;
934 gobj_getrect(g, x, &x21, &y21, &x22, &y22);
935 if (x22 > x2)
936 x2 = x22;
937 if (y22 > y2)
938 y2 = y22;
939 }
940 x->gl_havewindow = hadwindow;
941 }
942 else text_widgetbehavior.w_getrectfn(z, glist, &x1, &y1, &x2, &y2);
943 *xp1 = x1;
944 *yp1 = y1;
945 *xp2 = x2;
946 *yp2 = y2;
947}
948
949static void graph_displace(t_gobj *z, t_glist *glist, int dx, int dy)
950{
951 t_glist *x = (t_glist *)z;
952 if (!x->gl_isgraph)
953 text_widgetbehavior.w_displacefn(z, glist, dx, dy);
954 else
955 {
956 x->gl_obj.te_xpix += dx;
957 x->gl_obj.te_ypix += dy;
958 glist_redraw(x);
959 canvas_fixlinesfor(glist_getcanvas(glist), &x->gl_obj);
960 }
961}
962
963static void graph_select(t_gobj *z, t_glist *glist, int state)
964{
965 t_glist *x = (t_glist *)z;
966 if (!x->gl_isgraph)
967 text_widgetbehavior.w_selectfn(z, glist, state);
968 else
969 {
970 t_rtext *y = glist_findrtext(glist, &x->gl_obj);
971 if (canvas_showtext(x))
972 rtext_select(y, state);
973#ifndef ROCKBOX
974 sys_vgui(".x%x.c itemconfigure %sR -fill %s\n", glist,
975 rtext_gettag(y), (state? "blue" : "black"));
976 sys_vgui(".x%x.c itemconfigure graph%x -fill %s\n",
977 glist_getcanvas(glist), z, (state? "blue" : "black"));
978#endif
979 }
980}
981
982static void graph_activate(t_gobj *z, t_glist *glist, int state)
983{
984 t_glist *x = (t_glist *)z;
985 if (canvas_showtext(x))
986 text_widgetbehavior.w_activatefn(z, glist, state);
987}
988
989#if 0
990static void graph_delete(t_gobj *z, t_glist *glist)
991{
992 t_glist *x = (t_glist *)z;
993 if (!x->gl_isgraph)
994 text_widgetbehavior.w_deletefn(z, glist);
995 else
996 {
997 t_gobj *y;
998 while (y = x->gl_list) glist_delete(x, y);
999#if 0 /* I think this was just wrong. */
1000 if (glist_isvisible(x))
1001 sys_vgui(".x%x.c delete graph%x\n", glist_getcanvas(glist), x);
1002#endif
1003 }
1004}
1005#endif
1006
1007static void graph_delete(t_gobj *z, t_glist *glist)
1008{
1009 t_glist *x = (t_glist *)z;
1010 t_gobj *y;
1011 text_widgetbehavior.w_deletefn(z, glist);
1012 while((y = x->gl_list))
1013 glist_delete(x, y);
1014}
1015
1016#ifndef ROCKBOX
1017static float graph_lastxpix, graph_lastypix;
1018
1019static void graph_motion(void *z, t_floatarg dx, t_floatarg dy)
1020{
1021 t_glist *x = (t_glist *)z;
1022 float newxpix = graph_lastxpix + dx, newypix = graph_lastypix + dy;
1023 t_garray *a = (t_garray *)(x->gl_list);
1024 int oldx = 0.5 + glist_pixelstox(x, graph_lastxpix);
1025 int newx = 0.5 + glist_pixelstox(x, newxpix);
1026 t_sample *vec;
1027 int nelem, i;
1028 float oldy = glist_pixelstoy(x, graph_lastypix);
1029 float newy = glist_pixelstoy(x, newypix);
1030 graph_lastxpix = newxpix;
1031 graph_lastypix = newypix;
1032 /* verify that the array is OK */
1033 if (!a || pd_class((t_pd *)a) != garray_class)
1034 return;
1035 if (!garray_getfloatarray(a, &nelem, &vec))
1036 return;
1037 if (oldx < 0) oldx = 0;
1038 if (oldx >= nelem)
1039 oldx = nelem - 1;
1040 if (newx < 0) newx = 0;
1041 if (newx >= nelem)
1042 newx = nelem - 1;
1043 if (oldx < newx - 1)
1044 {
1045 for (i = oldx + 1; i <= newx; i++)
1046 vec[i] = newy + (oldy - newy) *
1047 ((float)(newx - i))/(float)(newx - oldx);
1048 }
1049 else if (oldx > newx + 1)
1050 {
1051 for (i = oldx - 1; i >= newx; i--)
1052 vec[i] = newy + (oldy - newy) *
1053 ((float)(newx - i))/(float)(newx - oldx);
1054 }
1055 else vec[newx] = newy;
1056 garray_redraw(a);
1057}
1058#endif
1059
1060static int graph_click(t_gobj *z, struct _glist *glist,
1061 int xpix, int ypix, int shift, int alt, int dbl, int doit)
1062{
1063 t_glist *x = (t_glist *)z;
1064 t_gobj *y;
1065 int clickreturned = 0;
1066 if (!x->gl_isgraph)
1067 return (text_widgetbehavior.w_clickfn(z, glist,
1068 xpix, ypix, shift, alt, dbl, doit));
1069 else if (x->gl_havewindow)
1070 return (0);
1071 else
1072 {
1073 for (y = x->gl_list; y; y = y->g_next)
1074 {
1075 int x1, y1, x2, y2;
1076 /* check if the object wants to be clicked */
1077 if (canvas_hitbox(x, y, xpix, ypix, &x1, &y1, &x2, &y2)
1078 && (clickreturned = gobj_click(y, x, xpix, ypix,
1079 shift, alt, 0, doit)))
1080 break;
1081 }
1082 if (!doit)
1083 {
1084 if (y)
1085 canvas_setcursor(glist_getcanvas(x), clickreturned);
1086 else canvas_setcursor(glist_getcanvas(x), CURSOR_RUNMODE_NOTHING);
1087 }
1088 return (clickreturned);
1089 }
1090}
1091
1092void garray_properties(t_garray *x);
1093
1094t_widgetbehavior graph_widgetbehavior =
1095{
1096 graph_getrect,
1097 graph_displace,
1098 graph_select,
1099 graph_activate,
1100 graph_delete,
1101 graph_vis,
1102 graph_click,
1103};
1104
1105void graph_properties(t_gobj *z, t_glist *owner)
1106{
1107 t_glist *x = (t_glist *)z;
1108 {
1109 t_gobj *y;
1110#ifdef ROCKBOX
1111 (void) owner;
1112#else /* ROCKBOX */
1113 char graphbuf[200];
1114
1115 sprintf(graphbuf, "pdtk_graph_dialog %%s %g %g %g %g %d %d\n",
1116 x->gl_x1, x->gl_y1, x->gl_x2, x->gl_y2,
1117 x->gl_pixwidth, x->gl_pixheight);
1118 gfxstub_new(&x->gl_pd, x, graphbuf);
1119#endif /* ROCKBOX */
1120
1121 for (y = x->gl_list; y; y = y->g_next)
1122 if (pd_class(&y->g_pd) == garray_class)
1123 garray_properties((t_garray *)y);
1124 }
1125}
1126
1127 /* find the graph most recently added to this glist;
1128 if none exists, return 0. */
1129
1130t_glist *glist_findgraph(t_glist *x)
1131{
1132 t_gobj *y = 0, *z;
1133 for (z = x->gl_list; z; z = z->g_next)
1134 if (pd_class(&z->g_pd) == canvas_class && ((t_glist *)z)->gl_isgraph)
1135 y = z;
1136 return ((t_glist *)y);
1137}
1138
1139 /* message back from dialog GUI to set parameters. Args are:
1140 1-4: bounds in our coordinates; 5-6: size in parent */
1141static void graph_dialog(t_glist *x, t_symbol *s, int argc, t_atom *argv)
1142{
1143 t_float x1 = atom_getfloatarg(0, argc, argv);
1144 t_float y1 = atom_getfloatarg(1, argc, argv);
1145 t_float x2 = atom_getfloatarg(2, argc, argv);
1146 t_float y2 = atom_getfloatarg(3, argc, argv);
1147 t_float xpix = atom_getfloatarg(4, argc, argv);
1148 t_float ypix = atom_getfloatarg(5, argc, argv);
1149#ifdef ROCKBOX
1150 (void) s;
1151#endif
1152 if (x1 != x->gl_x1 || x2 != x->gl_x2 ||
1153 y1 != x->gl_y1 || y2 != x->gl_y2)
1154 graph_bounds(x, x1, y1, x2, y2);
1155 if (xpix != x->gl_pixwidth || ypix != x->gl_pixheight)
1156 {
1157 x->gl_pixwidth = xpix;
1158 x->gl_pixheight = ypix;
1159 glist_redraw(x);
1160 if (x->gl_owner)
1161 canvas_fixlinesfor(x->gl_owner, &x->gl_obj);
1162 }
1163}
1164
1165extern void canvas_menuarray(t_glist *canvas);
1166
1167void g_graph_setup(void)
1168{
1169 class_setwidget(canvas_class, &graph_widgetbehavior);
1170 class_addmethod(canvas_class, (t_method)graph_bounds, gensym("bounds"),
1171 A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, 0);
1172 class_addmethod(canvas_class, (t_method)graph_xticks, gensym("xticks"),
1173 A_FLOAT, A_FLOAT, A_FLOAT, 0);
1174 class_addmethod(canvas_class, (t_method)graph_xlabel, gensym("xlabel"),
1175 A_GIMME, 0);
1176 class_addmethod(canvas_class, (t_method)graph_yticks, gensym("yticks"),
1177 A_FLOAT, A_FLOAT, A_FLOAT, 0);
1178 class_addmethod(canvas_class, (t_method)graph_ylabel, gensym("ylabel"),
1179 A_GIMME, 0);
1180 class_addmethod(canvas_class, (t_method)graph_array, gensym("array"),
1181 A_SYMBOL, A_FLOAT, A_SYMBOL, A_DEFFLOAT, A_NULL);
1182 class_addmethod(canvas_class, (t_method)canvas_menuarray,
1183 gensym("menuarray"), A_NULL);
1184 class_addmethod(canvas_class, (t_method)graph_dialog, gensym("dialog"),
1185 A_GIMME, 0);
1186 class_addmethod(canvas_class, (t_method)glist_arraydialog,
1187 gensym("arraydialog"), A_SYMBOL, A_FLOAT, A_FLOAT, A_FLOAT, A_NULL);
1188 class_addmethod(canvas_class, (t_method)glist_sort,
1189 gensym("sort"), A_NULL);
1190}
1191