Skip to content
Snippets Groups Projects
g_graph.c 57.5 KiB
Newer Older
Miller Puckette's avatar
Miller Puckette committed
/* 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... */

#include <stdlib.h>
#include "m_pd.h"
#include "m_imp.h"
Miller Puckette's avatar
Miller Puckette committed
#include "t_tk.h"
#include "g_canvas.h"
#include "g_all_guis.h" /* for canvas handle freeing */
Miller Puckette's avatar
Miller Puckette committed
#include "s_stuff.h"    /* for sys_hostfontsize */
#include <stdio.h>
#include <string.h>

extern int array_joc;
int garray_joc(t_garray *x);

Miller Puckette's avatar
Miller Puckette committed
/* ---------------------- forward definitions ----------------- */

static void graph_vis(t_gobj *gr, t_glist *unused_glist, int vis);
void graph_graphrect(t_gobj *z, t_glist *glist,
Miller Puckette's avatar
Miller Puckette committed
    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);
void graph_checkgop_rect(t_gobj *z, t_glist *glist,
    int *xp1, int *yp1, int *xp2, int *yp2);
extern t_template *template_findbydrawcommand(t_gobj *g);

Miller Puckette's avatar
Miller Puckette committed
/* -------------------- maintaining the list -------------------- */

void canvas_drawredrect(t_canvas *x, int doit);

{
/*    t_binbuf *b = x->gl_obj.te_binbuf;
    if (!b)
    {
        bug("canvas_isgroup");
    }
    t_atom *argv = binbuf_getvec(x->gl_obj.te_binbuf);
    if (argv[0].a_type == A_SYMBOL &&
        argv[0].a_w.w_symbol == gensym("group"))
        return 1;
    else
        return 0;
*/
    if (x->gl_svg)
        return 1;
    else
        return 0;
}

extern t_template *canvas_findtemplate(t_canvas *c);
extern t_canvas *canvas_templatecanvas_forgroup(t_canvas *c);

Miller Puckette's avatar
Miller Puckette committed
void glist_add(t_glist *x, t_gobj *y)
{
    //fprintf(stderr,"glist_add %lx %d\n", (t_int)x, (x->gl_editor ? 1 : 0));    
    t_object *ob;
Miller Puckette's avatar
Miller Puckette committed
    y->g_next = 0;
Miller Puckette's avatar
Miller Puckette committed
    if (!x->gl_list) x->gl_list = y;
    else
    {
        t_gobj *y2;
        for (y2 = x->gl_list; y2->g_next; y2 = y2->g_next)
Miller Puckette's avatar
Miller Puckette committed
        y2->g_next = y;
    }
    if (x->gl_editor && (ob = pd_checkobject(&y->g_pd)))
    {
Miller Puckette's avatar
Miller Puckette committed
        rtext_new(x, ob);
        //let's now set up create undo
        //glist_select(x, y);
        //canvas_setundo(x, canvas_undo_create,
        //    canvas_undo_set_create(x, index), "create");
        //glist_noselect(x);
    }
Miller Puckette's avatar
Miller Puckette committed
    if (x->gl_editor && x->gl_isgraph && !x->gl_goprect
        && pd_checkobject(&y->g_pd))
    {
        x->gl_goprect = 1;
        canvas_drawredrect(x, 1);
    }
    if (glist_isvisible(x))
        gobj_vis(y, x, 1);
    if (class_isdrawcommand(y->g_pd)) 
    {
        t_template *tmpl = template_findbydrawcommand(y);
        canvas_redrawallfortemplate(tmpl, 0);
    }
    if (pd_class(&y->g_pd) == canvas_class &&
        canvas_isgroup((t_canvas *)y))
    {
        t_canvas *templatecanvas =
            canvas_templatecanvas_forgroup((t_canvas *)y);
        t_template *tmpl = canvas_findtemplate(templatecanvas);
        canvas_redrawallfortemplate(tmpl, 0);
Miller Puckette's avatar
Miller Puckette committed
}

    /* 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);
}

    /* check if canvas has an array and return 1, otherwise return 0
    this is used to prevent creation of new objects in an array window */
int canvas_hasarray(t_canvas *x)
{
        if (pd_class(&g->g_pd) == garray_class) hasarray = 1;
        g = g->g_next;
    }
    return(hasarray);
}

/* JMZ: emit a closebang message */
void canvas_closebang(t_canvas *x);

Miller Puckette's avatar
Miller Puckette committed
    /* delete an object from a glist and free it */
void glist_delete(t_glist *x, t_gobj *y)
{
    //fprintf(stderr,"glist_delete y=%lx x=%lx glist_getcanvas=%lx\n", y, x, glist_getcanvas(x));
    if (x->gl_list)
    {
        //fprintf(stderr,"glist_delete YES\n");
        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;
        t_rtext *rt = NULL;
        int late_rtext_free = 0;

        if (pd_class(&y->g_pd) == canvas_class)
        {
          /* JMZ: send a closebang to the canvas */
          canvas_closebang((t_canvas *)y);
          /* and this little hack so drawing commands can tell
             if a [group] is deleting them (and thus suppress
             their own redraws) */
          ((t_canvas *)y)->gl_unloading = 1;
          /* if we are a group, let's call ourselves a drawcommand */
          if (((t_canvas *)y)->gl_svg)
              drawcommand = 1;
        }
     
        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];
                        //sprintf(tag, "graph%lx", (t_int)gl);
                        //t_glist *yy = (t_glist *)y;
                        sprintf(tag, "%s",
                            rtext_gettag(glist_findrtext(x, &gl->gl_obj)));
                        glist_eraseiofor(x, &gl->gl_obj, tag);
                        text_eraseborder(&gl->gl_obj, x,
                            rtext_gettag(glist_findrtext(x, &gl->gl_obj)));
                    }
                    else
                    {
                        text_eraseborder(&gl->gl_obj, x,
                            rtext_gettag(glist_findrtext(x, &gl->gl_obj)));
                    }
                }
            }
        }
            /* if we're a drawing command, erase all scalars that
                       belong to our template, before deleting
               it; we'll redraw them once it's deleted below. */
        if (drawcommand)
            if (!(canvas_isgroup(canvas) && canvas->gl_unloading))
        if (glist_isvisible(canvas))
        {
            //fprintf(stderr,"...deleting %lx %lx\n", x, glist_getcanvas(x));
            gobj_vis(y, x, 0);
        if (x->gl_editor && (ob = pd_checkobject(&y->g_pd)))
        {
            //rtext_new(x, ob);
            rt = glist_findrtext(x, ob);
            if (rt)
                late_rtext_free = 1;
        }
        if (x->gl_list == y)
        {
            if (y->g_next)
                x->gl_list = y->g_next;
            else
                x->gl_list = NULL;
        }
        else for (g = x->gl_list; g; g = g->g_next)
        {
            if (g->g_next == y)
            {
                if (y->g_next)
                    g->g_next = y->g_next;
                else g->g_next = NULL;
                break;
            }
        }
        gobj_delete(y, x);
        pd_free(&y->g_pd);
        if (chkdsp) canvas_update_dsp();
        if (drawcommand)
            if (tmpl != NULL && !(canvas_isgroup(canvas) && canvas->gl_unloading))
        canvas_setdeleting(canvas, wasdeleting);
        x->gl_valid = ++glist_valid;
        if (late_rtext_free)
        {
            //fprintf(stderr,"glist_delete late_rtext_free\n");
            rtext_free(rt);
        }
    }
Miller Puckette's avatar
Miller Puckette committed
}

    /* remove every object from a glist.  Experimental. */
void glist_clear(t_glist *x)
{
Mathieu L Bouchard's avatar
Mathieu L Bouchard committed
    t_gobj *y;
Miller Puckette's avatar
Miller Puckette committed
    int dspstate = 0, suspended = 0;
    t_symbol *dspsym = gensym("dsp");
    while (y = x->gl_list)
    {
            /* to avoid unnecessary DSP resorting, we suspend DSP
            only if we hit a patchable object. */
        if (!suspended && pd_checkobject(&y->g_pd) && zgetfn(&y->g_pd, dspsym))
        {
            dspstate = canvas_suspend_dsp();
            suspended = 1;
        }
            /* here's the real deletion. */
        glist_delete(x, y);
    }
    if (suspended)
        canvas_resume_dsp(dspstate);
}

void glist_retext(t_glist *glist, t_text *y)
{
        /* 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)
{
    //fprintf(stderr,"glist_grab\n");
Miller Puckette's avatar
Miller Puckette committed
    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)
{
    //fprintf(stderr,"glist_getcanvas\n");
    while (x->gl_owner && !x->gl_havewindow && x->gl_isgraph &&
        gobj_shouldvis(&x->gl_gobj, x->gl_owner))
    {
            //fprintf(stderr,"x=%lx x->gl_owner=%d x->gl_havewindow=%d x->gl_isgraph=%d gobj_shouldvis=%d\n", 
            //    x, (x->gl_owner ? 1:0), x->gl_havewindow, x->gl_isgraph, 
            //    gobj_shouldvis(&x->gl_gobj, x->gl_owner));
            //fprintf(stderr,"+\n");
    }
Miller Puckette's avatar
Miller Puckette committed
    return((t_canvas *)x);
}

static t_float gobj_getxforsort(t_gobj *g)
Miller Puckette's avatar
Miller Puckette committed
{
    if (pd_class(&g->g_pd) == scalar_class)
    {
Miller Puckette's avatar
Miller Puckette committed
        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;
    t_float f1 = 0, f2 = 0;
Miller Puckette's avatar
Miller Puckette committed
    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;
    t_float lastx = -1e37;
Miller Puckette's avatar
Miller Puckette committed
    t_gobj *g;
    for (g = x->gl_list; g; g = g->g_next)
    {
        t_float x1 = gobj_getxforsort(g);
Miller Puckette's avatar
Miller Puckette committed
        if (x1 < lastx)
            foo = 1;
        lastx = x1;
        nitems++;
    }
    if (foo)
        x->gl_list = glist_dosort(x, x->gl_list, nitems);
}

/* --------------- inlets and outlets  ----------- */


t_inlet *canvas_addinlet(t_canvas *x, t_pd *who, t_symbol *s)
{
    //fprintf(stderr,"canvas_addinlet %d %lx %d\n", x->gl_loading, x->gl_owner, glist_isvisible(x->gl_owner));
Miller Puckette's avatar
Miller Puckette committed
    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  && !(canvas_isgroup(x))) return;
Miller Puckette's avatar
Miller Puckette committed
    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) && glist_isvisible(x) &&
        !x->gl_owner->gl_loading && !x->gl_loading)
    {
Miller Puckette's avatar
Miller Puckette committed
        canvas_fixlinesfor(x->gl_owner, &x->gl_obj);
        //fprintf(stderr,"good place to fix redrawing of inlets "
        //               ".x%lx owner=.x%lx %d (parent)%d\n",
        //    x, x->gl_owner, x->gl_loading, x->gl_owner->gl_loading);

        /*
        t_object *ob = pd_checkobject(&y->g_pd);
        t_rtext *rt = glist_findrtext(x->gl_owner, (t_text *)&ob->ob_g);
        for (i = 0; i < ninlets; i++)
        {
            //sys_vgui(".x%x.c itemconfigure %si%d -fill %s -width 1\n",
            //    x, rtext_gettag(rt), i, 
            //    (obj_issignalinlet(ob, i) ?
            //        "$signal_nlet" : "$pd_colors_control_nlet)"));
            sprintf(xlet_tag, "%si%d", rtext_gettag(rt), i);
            char xlet_tag[MAXPDSTRING];
            gui_vmess("gui_configure_io", "xsiii",
                x,
                xlet_tag,
                0,
                obj_issignalinlet(ob, i),
                1);
        }
        */

        //glist_redraw(x);
        graph_vis(&x->gl_gobj, x->gl_owner, 0); 
        graph_vis(&x->gl_gobj, x->gl_owner, 1);
    }
Miller Puckette's avatar
Miller Puckette committed
}

t_outlet *canvas_addoutlet(t_canvas *x, t_pd *who, t_symbol *s)
{
    t_outlet *op = outlet_new(&x->gl_obj, s);
    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 && !(canvas_isgroup(x))) return;
Miller Puckette's avatar
Miller Puckette committed
    
    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) && glist_isvisible(x) &&
        !x->gl_owner->gl_loading && !x->gl_loading)
    {
Miller Puckette's avatar
Miller Puckette committed
        canvas_fixlinesfor(x->gl_owner, &x->gl_obj);
        //fprintf(stderr,"good place to fix redrawing of outlets\n");
        //fprintf(stderr,"found it\n");
        //glist_redraw(x);
        graph_vis(&x->gl_gobj, x->gl_owner, 0); 
        graph_vis(&x->gl_gobj, x->gl_owner, 1);
Miller Puckette's avatar
Miller Puckette committed
}

/* ----------calculating coordinates and controlling appearance --------- */


static void graph_bounds(t_glist *x, t_floatarg x1, t_floatarg y1,
    t_floatarg x2, t_floatarg y2)
{
Miller Puckette's avatar
Miller Puckette committed
        error("graph: empty bounds rectangle");
        x1 = y1 = 0;
        x2 = y2 = 1;
    }
    if (x->gl_x1!=x1 || x->gl_y1!=y1 || x->gl_x2!=x2 || x->gl_y2!=y2) {
        //printf("%f %f %f %f %f %f %f %f\n",x->gl_x1,x1,x->gl_y1,y1,x->gl_x2,x2,x->gl_y2,y2);
        if (!do_not_redraw)
            glist_redraw(x);
Miller Puckette's avatar
Miller Puckette committed
}

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;
    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;
    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 */
t_float glist_pixelstox(t_glist *x, t_float xpix)
Miller Puckette's avatar
Miller Puckette committed
{
        /* 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));
    }
}

t_float glist_pixelstoy(t_glist *x, t_float ypix)
Miller Puckette's avatar
Miller Puckette committed
{
    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_pixelstoy");
Miller Puckette's avatar
Miller Puckette committed
        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 */
t_float glist_xtopixels(t_glist *x, t_float xval)
Miller Puckette's avatar
Miller Puckette committed
{
    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));
    }
}

t_float glist_ytopixels(t_glist *x, t_float yval)
Miller Puckette's avatar
Miller Puckette committed
{
    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_pixelstoy");
Miller Puckette's avatar
Miller Puckette committed
        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. */
t_float glist_dpixtodx(t_glist *x, t_float dxpix)
Miller Puckette's avatar
Miller Puckette committed
{ 
    return (dxpix * (glist_pixelstox(x, 1) - glist_pixelstox(x, 0)));
}

t_float glist_dpixtody(t_glist *x, t_float dypix)
Miller Puckette's avatar
Miller Puckette committed
{
    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.  Otherwise, if it's a new-style graph-on-parent
    (so gl_goprect is set) we use the offset into the framing subrectangle
    as an offset into the parent rectangle.  Finally, it might be an old,
    proportional-style GOP.  In this case we do a coordinate transformation. */
int text_xpix(t_text *x, t_glist *glist)
{
    if (glist->gl_havewindow || !glist->gl_isgraph)
        return (x->te_xpix);
    else if (glist->gl_goprect)
        return (glist_xtopixels(glist, glist->gl_x1) +
            x->te_xpix - glist->gl_xmargin);
    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 if (glist->gl_goprect)
        return (glist_ytopixels(glist, glist->gl_y1) +
            x->te_ypix - glist->gl_ymargin);
    else return (glist_ytopixels(glist, 
            glist->gl_y1 + (glist->gl_y2 - glist->gl_y1) * 
                x->te_ypix / (glist->gl_screeny2 - glist->gl_screeny1)));
}

extern void canvas_updateconnection(t_canvas *x, int lx1, int ly1, int lx2, int ly2, t_int tag);

Miller Puckette's avatar
Miller Puckette committed
    /* 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)
Miller Puckette's avatar
Miller Puckette committed
    if (glist_isvisible(x))
    {
            /* LATER fix the graph_vis() code to handle both cases */
Miller Puckette's avatar
Miller Puckette committed
        {
            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))
                canvas_updateconnection(glist_getcanvas(x), t.tr_lx1, t.tr_ly1, t.tr_lx2, t.tr_ly2, (t_int)oc);
                //sys_vgui(".x%lx.c coords l%lx %d %d %d %d\n",
                //    glist_getcanvas(x), oc,
                //        t.tr_lx1, t.tr_ly1, t.tr_lx2, t.tr_ly2);
Miller Puckette's avatar
Miller Puckette committed
            canvas_drawredrect(x, 0);
            if (x->gl_goprect)
            {
                //post("draw it");
                /* update gop rect size on toplevel in case font has
                changed and we are showing text */
                /*if (!x->gl_hidetext) {
                    int x1, y1, x2, y2;
                    graph_getrect((t_gobj *)x, x, &x1, &y1, &x2, &y2);
                    if (x2-x1 > x->gl_pixwidth) x->gl_pixwidth = x2-x1;
                    if (y2-y1 > x->gl_pixheight) x->gl_pixheight = y2-y1;
                }*/
Miller Puckette's avatar
Miller Puckette committed
                canvas_drawredrect(x, 1);
            }
        }
    }
    if (x->gl_owner && glist_isvisible(x->gl_owner))
    {
        graph_vis(&x->gl_gobj, x->gl_owner, 0); 
        graph_vis(&x->gl_gobj, x->gl_owner, 1);
Miller Puckette's avatar
Miller Puckette committed
    }
}

/* --------------------------- widget behavior  ------------------- */

int garray_getname(t_garray *x, t_symbol **namep);


    /* 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;
    //fprintf(stderr,"graph vis canvas=%lx gobj=%lx %d\n",
    //    (t_int)parent_glist, (t_int)gr, vis);
    //fprintf(stderr, "graph_vis gr=.x%lx parent_glist=.x%lx "
    //                "glist_getcanvas(x->gl_owner)=.x%lx vis=%d\n",
    //    (t_int)gr, (t_int)parent_glist,
    //    (t_int)glist_getcanvas(x->gl_owner), vis);  
    char tag[50];
Miller Puckette's avatar
Miller Puckette committed
    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;
    }

    // weird exception
    //int exception = 0;
    //t_canvas* tgt = glist_getcanvas(x->gl_owner);
    //if (parent_glist->gl_owner && !parent_glist->gl_mapped &&
    //        parent_glist->gl_owner->gl_mapped) {
    //    tgt = parent_glist;
    //    exception = 1;
    //}
    //fprintf(stderr,"tgt=.x%lx %d\n", (t_int)tgt, exception);

    sprintf(tag, "%s", rtext_gettag(glist_findrtext(parent_glist, &x->gl_obj)));
//post("before rtext_draw: %s", tag);

    if (vis & gobj_shouldvis(gr, parent_glist))
    {
        int xpix, ypix;
        xpix = text_xpix(&x->gl_obj, parent_glist);
        ypix = text_ypix(&x->gl_obj, parent_glist);
        gui_vmess("gui_text_create_gobj", "xssiii",
            glist_getcanvas(x->gl_owner),
        if (canvas_showtext(x))
            rtext_draw(glist_findrtext(parent_glist, &x->gl_obj));
    }

//    sprintf(tag, "%s", rtext_gettag(glist_findrtext(parent_glist, &x->gl_obj)));
//post("after rtext_draw: %s", tag);

    // need the rect to create the gobj, so this should perhaps be above the
    // conditional
Miller Puckette's avatar
Miller Puckette committed
    graph_getrect(gr, parent_glist, &x1, &y1, &x2, &y2);
    //fprintf(stderr,"%d %d %d %d\n", x1, y1, x2, y2);
Miller Puckette's avatar
Miller Puckette committed
    if (!vis)
        rtext_erase(glist_findrtext(parent_glist, &x->gl_obj));

    //sprintf(tag, "graph%lx", (t_int)x);
    //fprintf(stderr, "gettag=%s, tag=graph%lx\n",
    //    rtext_gettag(glist_findrtext(parent_glist, &x->gl_obj)),(t_int)x);
    /* if we look like a graph but have been moved to a toplevel,
       just show the bounding rectangle */
Miller Puckette's avatar
Miller Puckette committed
    if (x->gl_havewindow)
    {
        if (vis && gobj_shouldvis(gr, parent_glist))
        {
            //sys_vgui(".x%lx.c create ppolygon %d %d %d %d %d %d %d %d %d %d "
            //         "-tags {%sfill graph} -fill $pd_colors(graph_border) "
            //         "-stroke $pd_colors(graph_border)\n",
            //    glist_getcanvas(x->gl_owner),
                ////parent_glist,
            //    x1, y1, x1, y2, x2, y2, x2, y1, x1, y1, tag);
            gui_vmess("gui_text_drawborder", "xssiiiii",
                glist_getcanvas(x->gl_owner),
                0, x1, y1, x2, y2);
            glist_noselect(x->gl_owner);
            gui_vmess("gui_graph_fill_border", "xsi",
                glist_getcanvas(x->gl_owner),
        }
        else if (gobj_shouldvis(gr, parent_glist))
        {
            //sys_vgui(".x%lx.c delete %sfill\n",
            //    glist_getcanvas(x->gl_owner), tag);
               ////parent_glist, tag);
            gui_vmess("gui_gobj_erase", "xs",
                glist_getcanvas(x->gl_owner),
Miller Puckette's avatar
Miller Puckette committed
        return;
    }
        /* otherwise draw (or erase) us as a graph inside another glist. */
    if (vis)
    {
        int i;
Miller Puckette's avatar
Miller Puckette committed
        t_gobj *g;
        t_symbol *arrayname;
            /* draw a rectangle around the graph */
        /*sys_vgui(".x%lx.c create polyline %d %d %d %d %d %d %d %d %d %d "
                   "-stroke $pd_colors(graph_border) -tags {%sR %s graph}\n",
Miller Puckette's avatar
Miller Puckette committed
            glist_getcanvas(x->gl_owner),
            x1, y1, x1, y2, x2, y2, x2, y1, x1, y1, tag, tag);*/
        //sys_vgui(".x%lx.c create prect %d %d %d %d "
        //         "-stroke $pd_colors(graph_border) -tags {%sR graph}\n",
        //         //REMOVED: -fill $pd_colors(graph) 
        //    glist_getcanvas(x->gl_owner),
        //    x1, y1, x2, y2, tag); // -fill $pd_colors(graph)
        char tagbuf[MAXPDSTRING];
        sprintf(tagbuf, "%sR", tag);
        gui_vmess("gui_text_drawborder", "xssiiiii",
            glist_getcanvas(x->gl_owner),
            0, x1, y1, x2, y2);
            /* write garrays' names along the top */
Miller Puckette's avatar
Miller Puckette committed
        for (i = (y1 < y2 ? y1 : y2)-1, g = x->gl_list; g; g = g->g_next)
        {
            //fprintf(stderr,".\n");
            //if (g->g_pd == garray_class)
            //    fprintf(stderr,"garray_getname=%d\n",garray_getname((t_garray *)g, &arrayname));
Miller Puckette's avatar
Miller Puckette committed
            if (g->g_pd == garray_class &&
                !garray_getname((t_garray *)g, &arrayname))
                //sys_vgui(".x%lx.c create text %d %d -text {%s} -anchor nw "
                //         "-font {{%s} -%d %s} -tags {%s label graph} -fill %s\n",
                //    (long)glist_getcanvas(x),  x1+2, i, arrayname->s_name,
                //    sys_font, sys_hostfontsize(glist_getfont(x)), sys_fontweight,
                //    tag,
                //    (glist_isselected(x, gr) ?
                //        "$pd_colors(selection)" : "$pd_colors(graph_border)"));
                gui_vmess("gui_graph_label", "xsissisi",
                    glist_getcanvas(x),