1
0
Fork 0
forked from len0rd/rockbox
foxbox/apps/plugins/pdbox/PDa/src/g_graph.c
Andree Buschmann f7808c1fe5 Fix several 'variable set but not used' warnings reported by GCC 6.4.1.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@29871 a1c6a512-1295-4272-9138-f99709370657
2011-05-13 18:47:42 +00:00

1191 lines
33 KiB
C

/* Copyright (c) 1997-2001 Miller Puckette and others.
* For information on usage and redistribution, and for a DISCLAIMER OF ALL
* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */
/* This file deals with the behavior of glists as either "text objects" or
"graphs" inside another glist. LATER move the inlet/outlet code of g_canvas.c
to this file... */
#ifdef ROCKBOX
#include "plugin.h"
#include "../../pdbox.h"
#include "m_pd.h"
#include "g_canvas.h"
#define snprintf rb->snprintf
#define atof rb_atof
#else /* ROCKBOX */
#include <stdlib.h>
#include "m_pd.h"
#include "t_tk.h"
#include "g_canvas.h"
#include <stdio.h>
#include <string.h>
#endif /* ROCKBOX */
/* ---------------------- forward definitions ----------------- */
static void graph_vis(t_gobj *gr, t_glist *unused_glist, int vis);
static void graph_graphrect(t_gobj *z, t_glist *glist,
int *xp1, int *yp1, int *xp2, int *yp2);
static void graph_getrect(t_gobj *z, t_glist *glist,
int *xp1, int *yp1, int *xp2, int *yp2);
/* -------------------- maintaining the list -------------------- */
void glist_add(t_glist *x, t_gobj *y)
{
t_object *ob;
y->g_next = 0;
if (!x->gl_list) x->gl_list = y;
else
{
t_gobj *y2;
for (y2 = x->gl_list; y2->g_next; y2 = y2->g_next);
y2->g_next = y;
}
if (x->gl_editor && (ob = pd_checkobject(&y->g_pd)))
rtext_new(x, ob);
if (glist_isvisible(x))
gobj_vis(y, x, 1);
if (class_isdrawcommand(y->g_pd))
canvas_redrawallfortemplate(glist_getcanvas(x));
}
/* this is to protect against a hairy problem in which deleting
a sub-canvas might delete an inlet on a box, after the box had
been invisible-ized, so that we have to protect against redrawing it! */
int canvas_setdeleting(t_canvas *x, int flag)
{
int ret = x->gl_isdeleting;
x->gl_isdeleting = flag;
return (ret);
}
/* delete an object from a glist and free it */
void glist_delete(t_glist *x, t_gobj *y)
{
t_gobj *g;
t_object *ob;
t_gotfn chkdsp = zgetfn(&y->g_pd, gensym("dsp"));
t_canvas *canvas = glist_getcanvas(x);
int drawcommand = class_isdrawcommand(y->g_pd);
int wasdeleting;
wasdeleting = canvas_setdeleting(canvas, 1);
if (x->gl_editor)
{
if (x->gl_editor->e_grab == y) x->gl_editor->e_grab = 0;
if (glist_isselected(x, y)) glist_deselect(x, y);
/* HACK -- we had phantom outlets not getting erased on the
screen because the canvas_setdeleting() mechanism is too
crude. LATER carefully set up rules for when the rtexts
should exist, so that they stay around until all the
steps of becoming invisible are done. In the meantime, just
zap the inlets and outlets here... */
if (pd_class(&y->g_pd) == canvas_class)
{
t_glist *gl = (t_glist *)y;
if (gl->gl_isgraph)
{
char tag[80];
#ifdef ROCKBOX
snprintf(tag, sizeof(tag), "graph%lx",
(unsigned long) (intptr_t) gl);
#else /* ROCKBOX */
sprintf(tag, "graph%x", (int)gl);
#endif /* ROCKBOX */
glist_eraseiofor(x, &gl->gl_obj, tag);
}
else
{
text_eraseborder(&gl->gl_obj, x,
rtext_gettag(glist_findrtext(x, &gl->gl_obj)));
}
}
}
gobj_delete(y, x);
if (glist_isvisible(canvas))
gobj_vis(y, x, 0);
if (x->gl_editor && (ob = pd_checkobject(&y->g_pd)))
rtext_new(x, ob);
if (x->gl_list == y) x->gl_list = y->g_next;
else for (g = x->gl_list; g; g = g->g_next)
if (g->g_next == y)
{
g->g_next = y->g_next;
break;
}
pd_free(&y->g_pd);
if (chkdsp) canvas_update_dsp();
if (drawcommand) canvas_redrawallfortemplate(canvas);
canvas_setdeleting(canvas, wasdeleting);
x->gl_valid = ++glist_valid;
}
/* remove every object from a glist. Experimental. */
void glist_clear(t_glist *x)
{
#ifdef ROCKBOX
t_gobj *y;
#else
t_gobj *y, *y2;
#endif
int dspstate = canvas_suspend_dsp();
while((y = x->gl_list))
glist_delete(x, y);
canvas_resume_dsp(dspstate);
}
void glist_retext(t_glist *glist, t_text *y)
{
#ifdef ROCKBOX
glist_getcanvas(glist);
#else
t_canvas *c = glist_getcanvas(glist);
#endif
/* check that we have built rtexts yet. LATER need a better test. */
if (glist->gl_editor && glist->gl_editor->e_rtext)
{
t_rtext *rt = glist_findrtext(glist, y);
if (rt)
rtext_retext(rt);
}
}
void glist_grab(t_glist *x, t_gobj *y, t_glistmotionfn motionfn,
t_glistkeyfn keyfn, int xpos, int ypos)
{
t_glist *x2 = glist_getcanvas(x);
if (motionfn)
x2->gl_editor->e_onmotion = MA_PASSOUT;
else x2->gl_editor->e_onmotion = 0;
x2->gl_editor->e_grab = y;
x2->gl_editor->e_motionfn = motionfn;
x2->gl_editor->e_keyfn = keyfn;
x2->gl_editor->e_xwas = xpos;
x2->gl_editor->e_ywas = ypos;
}
t_canvas *glist_getcanvas(t_glist *x)
{
while (x->gl_owner && !x->gl_havewindow && x->gl_isgraph)
x = x->gl_owner;
return((t_canvas *)x);
}
static float gobj_getxforsort(t_gobj *g)
{
if (pd_class(&g->g_pd) == scalar_class)
{
float x1, y1;
scalar_getbasexy((t_scalar *)g, &x1, &y1);
return(x1);
}
else return (0);
}
static t_gobj *glist_merge(t_glist *x, t_gobj *g1, t_gobj *g2)
{
t_gobj *g = 0, *g9 = 0;
float f1 = 0, f2 = 0;
#ifdef ROCKBOX
(void) x;
#endif
if (g1)
f1 = gobj_getxforsort(g1);
if (g2)
f2 = gobj_getxforsort(g2);
while (1)
{
if (g1)
{
if (g2)
{
if (f1 <= f2)
goto put1;
else goto put2;
}
else goto put1;
}
else if (g2)
goto put2;
else break;
put1:
if (g9)
g9->g_next = g1, g9 = g1;
else g9 = g = g1;
if((g1 = g1->g_next))
f1 = gobj_getxforsort(g1);
g9->g_next = 0;
continue;
put2:
if (g9)
g9->g_next = g2, g9 = g2;
else g9 = g = g2;
if((g2 = g2->g_next))
f2 = gobj_getxforsort(g2);
g9->g_next = 0;
continue;
}
return (g);
}
static t_gobj *glist_dosort(t_glist *x,
t_gobj *g, int nitems)
{
if (nitems < 2)
return (g);
else
{
int n1 = nitems/2, n2 = nitems - n1, i;
t_gobj *g2, *g3;
for (g2 = g, i = n1-1; i--; g2 = g2->g_next)
;
g3 = g2->g_next;
g2->g_next = 0;
g = glist_dosort(x, g, n1);
g3 = glist_dosort(x, g3, n2);
return (glist_merge(x, g, g3));
}
}
void glist_sort(t_glist *x)
{
int nitems = 0, foo = 0;
float lastx = -1e37;
t_gobj *g;
for (g = x->gl_list; g; g = g->g_next)
{
float x1 = gobj_getxforsort(g);
if (x1 < lastx)
foo = 1;
lastx = x1;
nitems++;
}
if (foo)
x->gl_list = glist_dosort(x, x->gl_list, nitems);
}
void glist_cleanup(t_glist *x)
{
freebytes(x->gl_xlabel, x->gl_nxlabels * sizeof(*(x->gl_xlabel)));
freebytes(x->gl_ylabel, x->gl_nylabels * sizeof(*(x->gl_ylabel)));
gstub_cutoff(x->gl_stub);
}
void glist_free(t_glist *x)
{
glist_cleanup(x);
freebytes(x, sizeof(*x));
}
/* --------------- inlets and outlets ----------- */
t_inlet *canvas_addinlet(t_canvas *x, t_pd *who, t_symbol *s)
{
t_inlet *ip = inlet_new(&x->gl_obj, who, s, 0);
if (!x->gl_loading && x->gl_owner && glist_isvisible(x->gl_owner))
{
gobj_vis(&x->gl_gobj, x->gl_owner, 0);
gobj_vis(&x->gl_gobj, x->gl_owner, 1);
canvas_fixlinesfor(x->gl_owner, &x->gl_obj);
}
if (!x->gl_loading) canvas_resortinlets(x);
return (ip);
}
void canvas_rminlet(t_canvas *x, t_inlet *ip)
{
t_canvas *owner = x->gl_owner;
int redraw = (owner && glist_isvisible(owner) && (!owner->gl_isdeleting)
&& glist_istoplevel(owner));
if (owner) canvas_deletelinesforio(owner, &x->gl_obj, ip, 0);
if (redraw)
gobj_vis(&x->gl_gobj, x->gl_owner, 0);
inlet_free(ip);
if (redraw)
{
gobj_vis(&x->gl_gobj, x->gl_owner, 1);
canvas_fixlinesfor(x->gl_owner, &x->gl_obj);
}
}
extern t_inlet *vinlet_getit(t_pd *x);
extern void obj_moveinletfirst(t_object *x, t_inlet *i);
void canvas_resortinlets(t_canvas *x)
{
int ninlets = 0, i, j, xmax;
t_gobj *y, **vec, **vp, **maxp;
for (ninlets = 0, y = x->gl_list; y; y = y->g_next)
if (pd_class(&y->g_pd) == vinlet_class) ninlets++;
if (ninlets < 2) return;
vec = (t_gobj **)getbytes(ninlets * sizeof(*vec));
for (y = x->gl_list, vp = vec; y; y = y->g_next)
if (pd_class(&y->g_pd) == vinlet_class) *vp++ = y;
for (i = ninlets; i--;)
{
t_inlet *ip;
for (vp = vec, xmax = -0x7fffffff, maxp = 0, j = ninlets;
j--; vp++)
{
int x1, y1, x2, y2;
t_gobj *g = *vp;
if (!g) continue;
gobj_getrect(g, x, &x1, &y1, &x2, &y2);
if (x1 > xmax) xmax = x1, maxp = vp;
}
if (!maxp) break;
y = *maxp;
*maxp = 0;
ip = vinlet_getit(&y->g_pd);
obj_moveinletfirst(&x->gl_obj, ip);
}
freebytes(vec, ninlets * sizeof(*vec));
if (x->gl_owner && glist_isvisible(x->gl_owner))
canvas_fixlinesfor(x->gl_owner, &x->gl_obj);
}
t_outlet *canvas_addoutlet(t_canvas *x, t_pd *who, t_symbol *s)
{
t_outlet *op = outlet_new(&x->gl_obj, s);
#ifdef ROCKBOX
(void) who;
#endif
if (!x->gl_loading && x->gl_owner && glist_isvisible(x->gl_owner))
{
gobj_vis(&x->gl_gobj, x->gl_owner, 0);
gobj_vis(&x->gl_gobj, x->gl_owner, 1);
canvas_fixlinesfor(x->gl_owner, &x->gl_obj);
}
if (!x->gl_loading) canvas_resortoutlets(x);
return (op);
}
void canvas_rmoutlet(t_canvas *x, t_outlet *op)
{
t_canvas *owner = x->gl_owner;
int redraw = (owner && glist_isvisible(owner) && (!owner->gl_isdeleting)
&& glist_istoplevel(owner));
if (owner) canvas_deletelinesforio(owner, &x->gl_obj, 0, op);
if (redraw)
gobj_vis(&x->gl_gobj, x->gl_owner, 0);
outlet_free(op);
if (redraw)
{
gobj_vis(&x->gl_gobj, x->gl_owner, 1);
canvas_fixlinesfor(x->gl_owner, &x->gl_obj);
}
}
extern t_outlet *voutlet_getit(t_pd *x);
extern void obj_moveoutletfirst(t_object *x, t_outlet *i);
void canvas_resortoutlets(t_canvas *x)
{
int noutlets = 0, i, j, xmax;
t_gobj *y, **vec, **vp, **maxp;
for (noutlets = 0, y = x->gl_list; y; y = y->g_next)
if (pd_class(&y->g_pd) == voutlet_class) noutlets++;
if (noutlets < 2) return;
vec = (t_gobj **)getbytes(noutlets * sizeof(*vec));
for (y = x->gl_list, vp = vec; y; y = y->g_next)
if (pd_class(&y->g_pd) == voutlet_class) *vp++ = y;
for (i = noutlets; i--;)
{
t_outlet *ip;
for (vp = vec, xmax = -0x7fffffff, maxp = 0, j = noutlets;
j--; vp++)
{
int x1, y1, x2, y2;
t_gobj *g = *vp;
if (!g) continue;
gobj_getrect(g, x, &x1, &y1, &x2, &y2);
if (x1 > xmax) xmax = x1, maxp = vp;
}
if (!maxp) break;
y = *maxp;
*maxp = 0;
ip = voutlet_getit(&y->g_pd);
obj_moveoutletfirst(&x->gl_obj, ip);
}
freebytes(vec, noutlets * sizeof(*vec));
if (x->gl_owner && glist_isvisible(x->gl_owner))
canvas_fixlinesfor(x->gl_owner, &x->gl_obj);
}
/* ----------calculating coordinates and controlling appearance --------- */
static void graph_bounds(t_glist *x, t_floatarg x1, t_floatarg y1,
t_floatarg x2, t_floatarg y2)
{
x->gl_x1 = x1;
x->gl_x2 = x2;
x->gl_y1 = y1;
x->gl_y2 = y2;
if (x->gl_x2 == x->gl_x1 ||
x->gl_y2 == x->gl_y1)
{
error("graph: empty bounds rectangle");
x1 = y1 = 0;
x2 = y2 = 1;
}
glist_redraw(x);
}
static void graph_xticks(t_glist *x,
t_floatarg point, t_floatarg inc, t_floatarg f)
{
x->gl_xtick.k_point = point;
x->gl_xtick.k_inc = inc;
x->gl_xtick.k_lperb = f;
glist_redraw(x);
}
static void graph_yticks(t_glist *x,
t_floatarg point, t_floatarg inc, t_floatarg f)
{
x->gl_ytick.k_point = point;
x->gl_ytick.k_inc = inc;
x->gl_ytick.k_lperb = f;
glist_redraw(x);
}
static void graph_xlabel(t_glist *x, t_symbol *s, int argc, t_atom *argv)
{
int i;
#ifdef ROCKBOX
(void) s;
#endif
if (argc < 1) error("graph_xlabel: no y value given");
else
{
x->gl_xlabely = atom_getfloat(argv);
argv++; argc--;
x->gl_xlabel = (t_symbol **)t_resizebytes(x->gl_xlabel,
x->gl_nxlabels * sizeof (t_symbol *), argc * sizeof (t_symbol *));
x->gl_nxlabels = argc;
for (i = 0; i < argc; i++) x->gl_xlabel[i] = atom_gensym(&argv[i]);
}
glist_redraw(x);
}
static void graph_ylabel(t_glist *x, t_symbol *s, int argc, t_atom *argv)
{
int i;
#ifdef ROCKBOX
(void) s;
#endif
if (argc < 1) error("graph_ylabel: no x value given");
else
{
x->gl_ylabelx = atom_getfloat(argv);
argv++; argc--;
x->gl_ylabel = (t_symbol **)t_resizebytes(x->gl_ylabel,
x->gl_nylabels * sizeof (t_symbol *), argc * sizeof (t_symbol *));
x->gl_nylabels = argc;
for (i = 0; i < argc; i++) x->gl_ylabel[i] = atom_gensym(&argv[i]);
}
glist_redraw(x);
}
/****** routines to convert pixels to X or Y value and vice versa ******/
/* convert an x pixel value to an x coordinate value */
float glist_pixelstox(t_glist *x, float xpix)
{
/* if we appear as a text box on parent, our range in our
coordinates (x1, etc.) specifies the coordinate range
of a one-pixel square at top left of the window. */
if (!x->gl_isgraph)
return (x->gl_x1 + (x->gl_x2 - x->gl_x1) * xpix);
/* if we're a graph when shown on parent, but own our own
window right now, our range in our coordinates (x1, etc.) is spread
over the visible window size, given by screenx1, etc. */
else if (x->gl_isgraph && x->gl_havewindow)
return (x->gl_x1 + (x->gl_x2 - x->gl_x1) *
(xpix) / (x->gl_screenx2 - x->gl_screenx1));
/* otherwise, we appear in a graph within a parent glist,
so get our screen rectangle on parent and transform. */
else
{
int x1, y1, x2, y2;
if (!x->gl_owner)
bug("glist_pixelstox");
graph_graphrect(&x->gl_gobj, x->gl_owner, &x1, &y1, &x2, &y2);
return (x->gl_x1 + (x->gl_x2 - x->gl_x1) *
(xpix - x1) / (x2 - x1));
}
}
float glist_pixelstoy(t_glist *x, float ypix)
{
if (!x->gl_isgraph)
return (x->gl_y1 + (x->gl_y2 - x->gl_y1) * ypix);
else if (x->gl_isgraph && x->gl_havewindow)
return (x->gl_y1 + (x->gl_y2 - x->gl_y1) *
(ypix) / (x->gl_screeny2 - x->gl_screeny1));
else
{
int x1, y1, x2, y2;
if (!x->gl_owner)
bug("glist_pixelstox");
graph_graphrect(&x->gl_gobj, x->gl_owner, &x1, &y1, &x2, &y2);
return (x->gl_y1 + (x->gl_y2 - x->gl_y1) *
(ypix - y1) / (y2 - y1));
}
}
/* convert an x coordinate value to an x pixel location in window */
float glist_xtopixels(t_glist *x, float xval)
{
if (!x->gl_isgraph)
return ((xval - x->gl_x1) / (x->gl_x2 - x->gl_x1));
else if (x->gl_isgraph && x->gl_havewindow)
return (x->gl_screenx2 - x->gl_screenx1) *
(xval - x->gl_x1) / (x->gl_x2 - x->gl_x1);
else
{
int x1, y1, x2, y2;
if (!x->gl_owner)
bug("glist_pixelstox");
graph_graphrect(&x->gl_gobj, x->gl_owner, &x1, &y1, &x2, &y2);
return (x1 + (x2 - x1) * (xval - x->gl_x1) / (x->gl_x2 - x->gl_x1));
}
}
float glist_ytopixels(t_glist *x, float yval)
{
if (!x->gl_isgraph)
return ((yval - x->gl_y1) / (x->gl_y2 - x->gl_y1));
else if (x->gl_isgraph && x->gl_havewindow)
return (x->gl_screeny2 - x->gl_screeny1) *
(yval - x->gl_y1) / (x->gl_y2 - x->gl_y1);
else
{
int x1, y1, x2, y2;
if (!x->gl_owner)
bug("glist_pixelstox");
graph_graphrect(&x->gl_gobj, x->gl_owner, &x1, &y1, &x2, &y2);
return (y1 + (y2 - y1) * (yval - x->gl_y1) / (x->gl_y2 - x->gl_y1));
}
}
/* convert an X screen distance to an X coordinate increment.
This is terribly inefficient;
but probably not a big enough CPU hog to warrant optimizing. */
float glist_dpixtodx(t_glist *x, float dxpix)
{
return (dxpix * (glist_pixelstox(x, 1) - glist_pixelstox(x, 0)));
}
float glist_dpixtody(t_glist *x, float dypix)
{
return (dypix * (glist_pixelstoy(x, 1) - glist_pixelstoy(x, 0)));
}
/* get the window location in pixels of a "text" object. The
object's x and y positions are in pixels when the glist they're
in is toplevel. If it's not, we convert to pixels on the parent
window. */
int text_xpix(t_text *x, t_glist *glist)
{
if (glist->gl_havewindow || !glist->gl_isgraph)
return (x->te_xpix);
else return (glist_xtopixels(glist,
glist->gl_x1 + (glist->gl_x2 - glist->gl_x1) *
x->te_xpix / (glist->gl_screenx2 - glist->gl_screenx1)));
}
int text_ypix(t_text *x, t_glist *glist)
{
if (glist->gl_havewindow || !glist->gl_isgraph)
return (x->te_ypix);
else return (glist_ytopixels(glist,
glist->gl_y1 + (glist->gl_y2 - glist->gl_y1) *
x->te_ypix / (glist->gl_screeny2 - glist->gl_screeny1)));
}
/* redraw all the items in a glist. We construe this to mean
redrawing in its own window and on parent, as needed in each case.
This is too conservative -- for instance, when you draw an "open"
rectangle on the parent, you shouldn't have to redraw the window! */
void glist_redraw(t_glist *x)
{
if (glist_isvisible(x))
{
/* LATER fix the graph_vis() code to handle both cases */
if (glist_istoplevel(x))
{
t_gobj *g;
t_linetraverser t;
t_outconnect *oc;
for (g = x->gl_list; g; g = g->g_next)
{
gobj_vis(g, x, 0);
gobj_vis(g, x, 1);
}
/* redraw all the lines */
linetraverser_start(&t, x);
while((oc = linetraverser_next(&t)))
#ifdef ROCKBOX
;
#else /* ROCKBOX */
sys_vgui(".x%x.c coords l%x %d %d %d %d\n",
glist_getcanvas(x), oc,
t.tr_lx1, t.tr_ly1, t.tr_lx2, t.tr_ly2);
#endif /* ROCKBOX */
}
if (x->gl_owner)
{
graph_vis(&x->gl_gobj, x->gl_owner, 0);
graph_vis(&x->gl_gobj, x->gl_owner, 1);
}
}
}
/* --------------------------- widget behavior ------------------- */
extern t_widgetbehavior text_widgetbehavior;
/* Note that some code in here would also be useful for drawing
graph decorations in toplevels... */
static void graph_vis(t_gobj *gr, t_glist *parent_glist, int vis)
{
t_glist *x = (t_glist *)gr;
char tag[50];
t_gobj *g;
int x1, y1, x2, y2;
/* ordinary subpatches: just act like a text object */
if (!x->gl_isgraph)
{
text_widgetbehavior.w_visfn(gr, parent_glist, vis);
return;
}
if (vis && canvas_showtext(x))
rtext_draw(glist_findrtext(parent_glist, &x->gl_obj));
graph_getrect(gr, parent_glist, &x1, &y1, &x2, &y2);
if (!vis)
rtext_erase(glist_findrtext(parent_glist, &x->gl_obj));
#ifdef ROCKBOX
snprintf(tag, sizeof(tag), "graph%lx", (unsigned long) (intptr_t) x);
#else
sprintf(tag, "graph%x", (int)x);
#endif
if (vis)
glist_drawiofor(parent_glist, &x->gl_obj, 1,
tag, x1, y1, x2, y2);
else glist_eraseiofor(parent_glist, &x->gl_obj, tag);
/* if we look like a graph but have been moved to a toplevel,
just show the bounding rectangle */
if (x->gl_havewindow)
{
#ifndef ROCKBOX
if (vis)
{
sys_vgui(".x%x.c create polygon\
%d %d %d %d %d %d %d %d %d %d -tags %s -fill #c0c0c0\n",
glist_getcanvas(x->gl_owner),
x1, y1, x1, y2, x2, y2, x2, y1, x1, y1, tag);
}
else
{
sys_vgui(".x%x.c delete %s\n",
glist_getcanvas(x->gl_owner), tag);
}
#endif
return;
}
/* otherwise draw (or erase) us as a graph inside another glist. */
if (vis)
{
int i;
float f;
/* draw a rectangle around the graph */
#ifndef ROCKBOX
sys_vgui(".x%x.c create line\
%d %d %d %d %d %d %d %d %d %d -tags %s\n",
glist_getcanvas(x->gl_owner),
x1, y1, x1, y2, x2, y2, x2, y1, x1, y1, tag);
#endif
/* draw ticks on horizontal borders. If lperb field is
zero, this is disabled. */
if (x->gl_xtick.k_lperb)
{
#ifndef ROCKBOX
float upix, lpix;
if (y2 < y1)
upix = y1, lpix = y2;
else upix = y2, lpix = y1;
#endif
for (i = 0, f = x->gl_xtick.k_point;
f < 0.99 * x->gl_x2 + 0.01*x->gl_x1; i++,
f += x->gl_xtick.k_inc)
{
#ifndef ROCKBOX
int tickpix = (i % x->gl_xtick.k_lperb ? 2 : 4);
sys_vgui(".x%x.c create line %d %d %d %d -tags %s\n",
glist_getcanvas(x->gl_owner),
(int)glist_xtopixels(x, f), (int)upix,
(int)glist_xtopixels(x, f), (int)upix - tickpix, tag);
sys_vgui(".x%x.c create line %d %d %d %d -tags %s\n",
glist_getcanvas(x->gl_owner),
(int)glist_xtopixels(x, f), (int)lpix,
(int)glist_xtopixels(x, f), (int)lpix + tickpix, tag);
#endif
}
for (i = 1, f = x->gl_xtick.k_point - x->gl_xtick.k_inc;
f > 0.99 * x->gl_x1 + 0.01*x->gl_x2;
i++, f -= x->gl_xtick.k_inc)
{
#ifndef ROCKBOX
int tickpix = (i % x->gl_xtick.k_lperb ? 2 : 4);
sys_vgui(".x%x.c create line %d %d %d %d -tags %s\n",
glist_getcanvas(x->gl_owner),
(int)glist_xtopixels(x, f), (int)upix,
(int)glist_xtopixels(x, f), (int)upix - tickpix, tag);
sys_vgui(".x%x.c create line %d %d %d %d -tags %s\n",
glist_getcanvas(x->gl_owner),
(int)glist_xtopixels(x, f), (int)lpix,
(int)glist_xtopixels(x, f), (int)lpix + tickpix, tag);
#endif
}
}
/* draw ticks in vertical borders*/
if (x->gl_ytick.k_lperb)
{
float ubound, lbound;
if (x->gl_y2 < x->gl_y1)
ubound = x->gl_y1, lbound = x->gl_y2;
else ubound = x->gl_y2, lbound = x->gl_y1;
for (i = 0, f = x->gl_ytick.k_point;
f < 0.99 * ubound + 0.01 * lbound;
i++, f += x->gl_ytick.k_inc)
{
#ifndef ROCKBOX
int tickpix = (i % x->gl_ytick.k_lperb ? 2 : 4);
sys_vgui(".x%x.c create line %d %d %d %d -tags %s\n",
glist_getcanvas(x->gl_owner),
x1, (int)glist_ytopixels(x, f),
x1 + tickpix, (int)glist_ytopixels(x, f), tag);
sys_vgui(".x%x.c create line %d %d %d %d -tags %s\n",
glist_getcanvas(x->gl_owner),
x2, (int)glist_ytopixels(x, f),
x2 - tickpix, (int)glist_ytopixels(x, f), tag);
#endif
}
for (i = 1, f = x->gl_ytick.k_point - x->gl_ytick.k_inc;
f > 0.99 * lbound + 0.01 * ubound;
i++, f -= x->gl_ytick.k_inc)
{
#ifndef ROCKBOX
int tickpix = (i % x->gl_ytick.k_lperb ? 2 : 4);
sys_vgui(".x%x.c create line %d %d %d %d -tags %s\n",
glist_getcanvas(x->gl_owner),
x1, (int)glist_ytopixels(x, f),
x1 + tickpix, (int)glist_ytopixels(x, f), tag);
sys_vgui(".x%x.c create line %d %d %d %d -tags %s\n",
glist_getcanvas(x->gl_owner),
x2, (int)glist_ytopixels(x, f),
x2 - tickpix, (int)glist_ytopixels(x, f), tag);
#endif
}
}
/* draw x labels */
for (i = 0; i < x->gl_nxlabels; i++)
#ifdef ROCKBOX
;
#else /* ROCKBOX */
sys_vgui(".x%x.c create text\
%d %d -text {%s} -font -*-courier-bold--normal--%d-* -tags %s\n",
glist_getcanvas(x),
(int)glist_xtopixels(x, atof(x->gl_xlabel[i]->s_name)),
(int)glist_ytopixels(x, x->gl_xlabely), x->gl_xlabel[i]->s_name,
glist_getfont(x), tag);
#endif /* ROCKBOX */
/* draw y labels */
for (i = 0; i < x->gl_nylabels; i++)
#ifdef ROCKBOX
;
#else /* ROCKBOX */
sys_vgui(".x%x.c create text\
%d %d -text {%s} -font -*-courier-bold--normal--%d-* -tags %s\n",
glist_getcanvas(x),
(int)glist_xtopixels(x, x->gl_ylabelx),
(int)glist_ytopixels(x, atof(x->gl_ylabel[i]->s_name)),
x->gl_ylabel[i]->s_name,
glist_getfont(x), tag);
#endif /* ROCKBOX */
/* draw contents of graph as glist */
for (g = x->gl_list; g; g = g->g_next)
gobj_vis(g, x, 1);
}
else
{
#ifndef ROCKBOX
sys_vgui(".x%x.c delete %s\n",
glist_getcanvas(x->gl_owner), tag);
#endif
for (g = x->gl_list; g; g = g->g_next)
gobj_vis(g, x, 0);
}
}
/* get the graph's rectangle, not counting extra swelling for controls
to keep them inside the graph. This is the "logical" pixel size. */
static void graph_graphrect(t_gobj *z, t_glist *glist,
int *xp1, int *yp1, int *xp2, int *yp2)
{
t_glist *x = (t_glist *)z;
int x1 = text_xpix(&x->gl_obj, glist);
int y1 = text_ypix(&x->gl_obj, glist);
int x2, y2;
#if 0 /* this used to adjust graph size when it was in another graph;
now we just preserve the size. */
/* same logic here as in text_xpix(): */
if (glist->gl_havewindow)
{
x2 = x1 + x->gl_pixwidth;
y2 = y1 + x->gl_pixheight;
}
else
{
x2 = glist_xtopixels(glist,
glist->gl_x1 + (glist->gl_x2 - glist->gl_x1) *
(x->gl_obj.te_xpix + x->gl_pixwidth) /
(glist->gl_screenx2 - glist->gl_screenx1));
y2 = glist_ytopixels(glist,
glist->gl_y1 + (glist->gl_y2 - glist->gl_y1) *
(x->gl_obj.te_ypix + x->gl_pixheight) /
(glist->gl_screeny2 - glist->gl_screeny1));
}
#endif
x2 = x1 + x->gl_pixwidth;
y2 = y1 + x->gl_pixheight;
*xp1 = x1;
*yp1 = y1;
*xp2 = x2;
*yp2 = y2;
}
/* get the rectangle, enlarged to contain all the "contents" --
meaning their formal bounds rectangles. */
static void graph_getrect(t_gobj *z, t_glist *glist,
int *xp1, int *yp1, int *xp2, int *yp2)
{
int x1 = 0x7fffffff, y1 = 0x7fffffff, x2 = -0x7fffffff, y2 = -0x7fffffff;
t_glist *x = (t_glist *)z;
if (x->gl_isgraph)
{
int hadwindow;
t_gobj *g;
t_text *ob;
int x21, y21, x22, y22;
graph_graphrect(z, glist, &x1, &y1, &x2, &y2);
if (canvas_showtext(x))
{
text_widgetbehavior.w_getrectfn(z, glist, &x21, &y21, &x22, &y22);
if (x22 > x2)
x2 = x22;
if (y22 > y2)
y2 = y22;
}
/* lie about whether we have our own window to affect gobj_getrect
calls below. (LATER add argument to gobj_getrect()?) */
hadwindow = x->gl_havewindow;
x->gl_havewindow = 0;
for (g = x->gl_list; g; g = g->g_next)
if ((!(ob = pd_checkobject(&g->g_pd))) || text_shouldvis(ob, x))
{
/* don't do this for arrays, just let them hang outsize the
box. */
if (pd_class(&g->g_pd) == garray_class)
continue;
gobj_getrect(g, x, &x21, &y21, &x22, &y22);
if (x22 > x2)
x2 = x22;
if (y22 > y2)
y2 = y22;
}
x->gl_havewindow = hadwindow;
}
else text_widgetbehavior.w_getrectfn(z, glist, &x1, &y1, &x2, &y2);
*xp1 = x1;
*yp1 = y1;
*xp2 = x2;
*yp2 = y2;
}
static void graph_displace(t_gobj *z, t_glist *glist, int dx, int dy)
{
t_glist *x = (t_glist *)z;
if (!x->gl_isgraph)
text_widgetbehavior.w_displacefn(z, glist, dx, dy);
else
{
x->gl_obj.te_xpix += dx;
x->gl_obj.te_ypix += dy;
glist_redraw(x);
canvas_fixlinesfor(glist_getcanvas(glist), &x->gl_obj);
}
}
static void graph_select(t_gobj *z, t_glist *glist, int state)
{
t_glist *x = (t_glist *)z;
if (!x->gl_isgraph)
text_widgetbehavior.w_selectfn(z, glist, state);
else
{
t_rtext *y = glist_findrtext(glist, &x->gl_obj);
if (canvas_showtext(x))
rtext_select(y, state);
#ifndef ROCKBOX
sys_vgui(".x%x.c itemconfigure %sR -fill %s\n", glist,
rtext_gettag(y), (state? "blue" : "black"));
sys_vgui(".x%x.c itemconfigure graph%x -fill %s\n",
glist_getcanvas(glist), z, (state? "blue" : "black"));
#endif
}
}
static void graph_activate(t_gobj *z, t_glist *glist, int state)
{
t_glist *x = (t_glist *)z;
if (canvas_showtext(x))
text_widgetbehavior.w_activatefn(z, glist, state);
}
#if 0
static void graph_delete(t_gobj *z, t_glist *glist)
{
t_glist *x = (t_glist *)z;
if (!x->gl_isgraph)
text_widgetbehavior.w_deletefn(z, glist);
else
{
t_gobj *y;
while (y = x->gl_list) glist_delete(x, y);
#if 0 /* I think this was just wrong. */
if (glist_isvisible(x))
sys_vgui(".x%x.c delete graph%x\n", glist_getcanvas(glist), x);
#endif
}
}
#endif
static void graph_delete(t_gobj *z, t_glist *glist)
{
t_glist *x = (t_glist *)z;
t_gobj *y;
text_widgetbehavior.w_deletefn(z, glist);
while((y = x->gl_list))
glist_delete(x, y);
}
#ifndef ROCKBOX
static float graph_lastxpix, graph_lastypix;
static void graph_motion(void *z, t_floatarg dx, t_floatarg dy)
{
t_glist *x = (t_glist *)z;
float newxpix = graph_lastxpix + dx, newypix = graph_lastypix + dy;
t_garray *a = (t_garray *)(x->gl_list);
int oldx = 0.5 + glist_pixelstox(x, graph_lastxpix);
int newx = 0.5 + glist_pixelstox(x, newxpix);
t_sample *vec;
int nelem, i;
float oldy = glist_pixelstoy(x, graph_lastypix);
float newy = glist_pixelstoy(x, newypix);
graph_lastxpix = newxpix;
graph_lastypix = newypix;
/* verify that the array is OK */
if (!a || pd_class((t_pd *)a) != garray_class)
return;
if (!garray_getfloatarray(a, &nelem, &vec))
return;
if (oldx < 0) oldx = 0;
if (oldx >= nelem)
oldx = nelem - 1;
if (newx < 0) newx = 0;
if (newx >= nelem)
newx = nelem - 1;
if (oldx < newx - 1)
{
for (i = oldx + 1; i <= newx; i++)
vec[i] = newy + (oldy - newy) *
((float)(newx - i))/(float)(newx - oldx);
}
else if (oldx > newx + 1)
{
for (i = oldx - 1; i >= newx; i--)
vec[i] = newy + (oldy - newy) *
((float)(newx - i))/(float)(newx - oldx);
}
else vec[newx] = newy;
garray_redraw(a);
}
#endif
static int graph_click(t_gobj *z, struct _glist *glist,
int xpix, int ypix, int shift, int alt, int dbl, int doit)
{
t_glist *x = (t_glist *)z;
t_gobj *y;
int clickreturned = 0;
if (!x->gl_isgraph)
return (text_widgetbehavior.w_clickfn(z, glist,
xpix, ypix, shift, alt, dbl, doit));
else if (x->gl_havewindow)
return (0);
else
{
for (y = x->gl_list; y; y = y->g_next)
{
int x1, y1, x2, y2;
/* check if the object wants to be clicked */
if (canvas_hitbox(x, y, xpix, ypix, &x1, &y1, &x2, &y2)
&& (clickreturned = gobj_click(y, x, xpix, ypix,
shift, alt, 0, doit)))
break;
}
if (!doit)
{
if (y)
canvas_setcursor(glist_getcanvas(x), clickreturned);
else canvas_setcursor(glist_getcanvas(x), CURSOR_RUNMODE_NOTHING);
}
return (clickreturned);
}
}
void garray_properties(t_garray *x);
t_widgetbehavior graph_widgetbehavior =
{
graph_getrect,
graph_displace,
graph_select,
graph_activate,
graph_delete,
graph_vis,
graph_click,
};
void graph_properties(t_gobj *z, t_glist *owner)
{
t_glist *x = (t_glist *)z;
{
t_gobj *y;
#ifdef ROCKBOX
(void) owner;
#else /* ROCKBOX */
char graphbuf[200];
sprintf(graphbuf, "pdtk_graph_dialog %%s %g %g %g %g %d %d\n",
x->gl_x1, x->gl_y1, x->gl_x2, x->gl_y2,
x->gl_pixwidth, x->gl_pixheight);
gfxstub_new(&x->gl_pd, x, graphbuf);
#endif /* ROCKBOX */
for (y = x->gl_list; y; y = y->g_next)
if (pd_class(&y->g_pd) == garray_class)
garray_properties((t_garray *)y);
}
}
/* find the graph most recently added to this glist;
if none exists, return 0. */
t_glist *glist_findgraph(t_glist *x)
{
t_gobj *y = 0, *z;
for (z = x->gl_list; z; z = z->g_next)
if (pd_class(&z->g_pd) == canvas_class && ((t_glist *)z)->gl_isgraph)
y = z;
return ((t_glist *)y);
}
/* message back from dialog GUI to set parameters. Args are:
1-4: bounds in our coordinates; 5-6: size in parent */
static void graph_dialog(t_glist *x, t_symbol *s, int argc, t_atom *argv)
{
t_float x1 = atom_getfloatarg(0, argc, argv);
t_float y1 = atom_getfloatarg(1, argc, argv);
t_float x2 = atom_getfloatarg(2, argc, argv);
t_float y2 = atom_getfloatarg(3, argc, argv);
t_float xpix = atom_getfloatarg(4, argc, argv);
t_float ypix = atom_getfloatarg(5, argc, argv);
#ifdef ROCKBOX
(void) s;
#endif
if (x1 != x->gl_x1 || x2 != x->gl_x2 ||
y1 != x->gl_y1 || y2 != x->gl_y2)
graph_bounds(x, x1, y1, x2, y2);
if (xpix != x->gl_pixwidth || ypix != x->gl_pixheight)
{
x->gl_pixwidth = xpix;
x->gl_pixheight = ypix;
glist_redraw(x);
if (x->gl_owner)
canvas_fixlinesfor(x->gl_owner, &x->gl_obj);
}
}
extern void canvas_menuarray(t_glist *canvas);
void g_graph_setup(void)
{
class_setwidget(canvas_class, &graph_widgetbehavior);
class_addmethod(canvas_class, (t_method)graph_bounds, gensym("bounds"),
A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, 0);
class_addmethod(canvas_class, (t_method)graph_xticks, gensym("xticks"),
A_FLOAT, A_FLOAT, A_FLOAT, 0);
class_addmethod(canvas_class, (t_method)graph_xlabel, gensym("xlabel"),
A_GIMME, 0);
class_addmethod(canvas_class, (t_method)graph_yticks, gensym("yticks"),
A_FLOAT, A_FLOAT, A_FLOAT, 0);
class_addmethod(canvas_class, (t_method)graph_ylabel, gensym("ylabel"),
A_GIMME, 0);
class_addmethod(canvas_class, (t_method)graph_array, gensym("array"),
A_SYMBOL, A_FLOAT, A_SYMBOL, A_DEFFLOAT, A_NULL);
class_addmethod(canvas_class, (t_method)canvas_menuarray,
gensym("menuarray"), A_NULL);
class_addmethod(canvas_class, (t_method)graph_dialog, gensym("dialog"),
A_GIMME, 0);
class_addmethod(canvas_class, (t_method)glist_arraydialog,
gensym("arraydialog"), A_SYMBOL, A_FLOAT, A_FLOAT, A_FLOAT, A_NULL);
class_addmethod(canvas_class, (t_method)glist_sort,
gensym("sort"), A_NULL);
}