A modern Music Player Daemon based on Rockbox open source high quality audio player
libadwaita audio rust zig deno mpris rockbox mpd
at master 1191 lines 34 kB view raw
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