Skip to content
Snippets Groups Projects
g_array.c 70.8 KiB
Newer Older
Miller Puckette's avatar
Miller Puckette committed
/* Copyright (c) 1997-1999 Miller Puckette.
* For information on usage and redistribution, and for a DISCLAIMER OF ALL
* WARRANTIES, see the file, "LICENSE.txt," in this distribution.  */

#include <stdlib.h>
#include <string.h>
#include <stdio.h>      /* for read/write to files */
#include "m_pd.h"
#include "g_canvas.h"
#include <math.h>

static void garray_select(t_gobj *z, t_glist *glist, int state);
static void garray_doredraw(t_gobj *client, t_glist *glist);
Miller Puckette's avatar
Miller Puckette committed
/* jsarlo { */
#define ARRAYPAGESIZE 1000  /* this should match the page size in u_main.tk */
/* } jsarlo */

/* see also the "plot" object in g_scalar.c which deals with graphing
arrays which are fields in scalars.  Someday we should unify the
two, but how? */

    /* aux routine to bash leading '#' to '$' for dialogs in u_main.tk
    which can't send symbols starting with '$' (because the Pd message
    interpreter would change them!) */

static t_symbol *sharptodollar(t_symbol *s)
{
    if (*s->s_name == '#')
    {
        char buf[MAXPDSTRING];
        strncpy(buf, s->s_name, MAXPDSTRING);
        buf[MAXPDSTRING-1] = 0;
        buf[0] = '$';
        return (gensym(buf));
    }
    else return (s);
}

/* --------- "pure" arrays with scalars for elements. --------------- */

/* Pure arrays have no a priori graphical capabilities.
They are instantiated by "garrays" below or can be elements of other
scalars (g_scalar.c); their graphical behavior is defined accordingly. */

t_array *array_new(t_symbol *templatesym, t_gpointer *parent)
{
    t_array *x = (t_array *)getbytes(sizeof (*x));
    t_template *template;
    template = template_findbyname(templatesym);
    x->a_templatesym = templatesym;
    x->a_n = 1;
    x->a_elemsize = sizeof(t_word) * template->t_n;
    x->a_vec = (char *)getbytes(x->a_elemsize);
        /* note here we blithely copy a gpointer instead of "setting" a
        new one; this gpointer isn't accounted for and needn't be since
        we'll be deleted before the thing pointed to gets deleted anyway;
        see array_free. */
    x->a_gp = *parent;
    x->a_stub = gstub_new(0, x);
    word_init((t_word *)(x->a_vec), template, parent);
    return (x);
}

/* jsarlo { */
void garray_arrayviewlist_close(t_garray *x);
/* } jsarlo */

void array_resize(t_array *x, int n)
{
    //fprintf(stderr,"array_resize %d\n", n);
Miller Puckette's avatar
Miller Puckette committed
    int elemsize, oldn;
    t_template *template = template_findbyname(x->a_templatesym);
    if (n < 1)
        n = 1;
    oldn = x->a_n;
    elemsize = sizeof(t_word) * template->t_n;

    x->a_vec = (char *)resizebytes(x->a_vec, oldn * elemsize, n * elemsize);
    x->a_n = n;
    if (n > oldn)
    {
        char *cp = x->a_vec + elemsize * oldn;
        int i = n - oldn;
        for (; i--; cp += elemsize)
        {
            t_word *wp = (t_word *)cp;
            word_init(wp, template, &x->a_gp);
        }
    }
    x->a_valid = ++glist_valid;
}

static void array_resize_and_redraw(t_array *array, t_glist *glist, int n)
{
    //fprintf(stderr,"array_resize_and_redraw\n");
Miller Puckette's avatar
Miller Puckette committed
    t_array *a2 = array;
    int vis = glist_isvisible(glist);
    while (a2->a_gp.gp_stub->gs_which == GP_ARRAY)
        a2 = a2->a_gp.gp_stub->gs_un.gs_array;
    if (vis)
        gobj_vis(a2->a_gp.gp_un.gp_gobj, glist, 0);
Miller Puckette's avatar
Miller Puckette committed
    array_resize(array, n);
    if (vis)
        gobj_vis(a2->a_gp.gp_un.gp_gobj, glist, 1);
Miller Puckette's avatar
Miller Puckette committed
}

void word_free(t_word *wp, t_template *template);

void array_free(t_array *x)
{
    int i;
    t_template *scalartemplate = template_findbyname(x->a_templatesym);
    gstub_cutoff(x->a_stub);
    for (i = 0; i < x->a_n; i++)
    {
        t_word *wp = (t_word *)(x->a_vec + x->a_elemsize * i);
        word_free(wp, scalartemplate);
    }
    freebytes(x->a_vec, x->a_elemsize * x->a_n);
    freebytes(x, sizeof *x);
}

/* --------------------- graphical arrays (garrays) ------------------- */

t_class *garray_class;
static int gcount = 0;

struct _garray
{
    t_gobj x_gobj;
    t_scalar *x_scalar;     /* scalar "containing" the array */
    t_glist *x_glist;       /* containing glist */
    t_symbol *x_name;       /* unexpanded name (possibly with leading '$') */
    t_symbol *x_realname;   /* expanded name (symbol we're bound to) */
    char x_usedindsp;       /* true if some DSP routine is using this */
    char x_saveit;          /* true if we should save this with parent */
    char x_joc;             /* true if we should "jump on click" in a graph */
Miller Puckette's avatar
Miller Puckette committed
    char x_listviewing;     /* true if list view window is open */
    char x_hidename;        /* don't print name above graph */
    int x_style;            /* so much simpler to keep it here */
    t_symbol *x_send;       /* send_changed hook */
    t_symbol *x_fillcolor;       /* color for filled area of the are */
    t_symbol *x_outlinecolor;    /* color of the outline around the element */
t_pd *garray_arraytemplatecanvas;
t_pd *garray_floattemplatecanvas;
Miller Puckette's avatar
Miller Puckette committed
static char garray_arraytemplatefile[] = "\
#N canvas 0 0 458 153 10;\n\
#X obj 43 31 struct _float_array array z float float style\n\
float linewidth float color symbol fillcolor symbol outlinecolor;\n\
#X obj 43 70 plot z color linewidth 0 0 1 style fillcolor outlinecolor;\n\
Miller Puckette's avatar
Miller Puckette committed
";
static char garray_floattemplatefile[] = "\
#N canvas 0 0 458 153 10;\n\
#X obj 39 26 struct float float y;\n\
";

/* create invisible, built-in canvases to determine the templates for floats
and float-arrays. */

void garray_init( void)
{
    t_binbuf *b;
    if (garray_arraytemplatecanvas)
        return;
    b = binbuf_new();
    
    glob_setfilename(0, gensym("_float"), gensym("."));
    binbuf_text(b, garray_floattemplatefile, strlen(garray_floattemplatefile));
    binbuf_eval(b, 0, 0, 0);
    garray_floattemplatecanvas = s__X.s_thing;
Miller Puckette's avatar
Miller Puckette committed
    vmess(s__X.s_thing, gensym("pop"), "i", 0);
    
    glob_setfilename(0, gensym("_float_array"), gensym("."));
    binbuf_text(b, garray_arraytemplatefile, strlen(garray_arraytemplatefile));
    binbuf_eval(b, 0, 0, 0);
    garray_arraytemplatecanvas = s__X.s_thing;
    vmess(s__X.s_thing, gensym("pop"), "i", 0);

    glob_setfilename(0, &s_, &s_);
Miller Puckette's avatar
Miller Puckette committed
}

/* create a new scalar attached to a symbol.  Used to make floating-point
arrays (the scalar will be of type "_float_array").  Currently this is
always called by graph_array() below; but when we make a more general way
to save and create arrays this might get called more directly. */

static t_garray *graph_scalar(t_glist *gl, t_symbol *s, t_symbol *templatesym,
    t_symbol *fill, t_symbol *outline, int saveit)
Miller Puckette's avatar
Miller Puckette committed
{
    t_garray *x;
    t_template *template;
    if (!(template = template_findbyname(templatesym)))
        return (0);
Miller Puckette's avatar
Miller Puckette committed
    x = (t_garray *)pd_new(garray_class);
    x->x_scalar = scalar_new(gl, templatesym);
    x->x_name = s;
    x->x_realname = canvas_realizedollar(gl, s);
    pd_bind(&x->x_gobj.g_pd, x->x_realname);
    x->x_usedindsp = 0;
    x->x_saveit = saveit;
    x->x_listviewing = 0;
    template_setsymbol(template, gensym("fillcolor"), x->x_scalar->sc_vec,
        fill, 1);
    template_setsymbol(template, gensym("outlinecolor"), x->x_scalar->sc_vec,
        outline, 1);
Miller Puckette's avatar
Miller Puckette committed
    glist_add(gl, &x->x_gobj);
    x->x_glist = gl;
    char buf[MAXPDSTRING];
    sprintf(buf, "%s_changed", x->x_realname->s_name);
    x->x_send = gensym(buf);
Miller Puckette's avatar
Miller Puckette committed
    return (x);
}

    /* get a garray's "array" structure. */
t_array *garray_getarray(t_garray *x)
{
Mathieu L Bouchard's avatar
Mathieu L Bouchard committed
    int zonset, ztype;
Miller Puckette's avatar
Miller Puckette committed
    t_symbol *zarraytype;
    t_scalar *sc = x->x_scalar;
    t_symbol *templatesym = sc->sc_template;
    t_template *template = template_findbyname(templatesym);
    if (!template)
    {
        error("array: couldn't find template %s", templatesym->s_name);
        return (0);
    }
    if (!template_find_field(template, gensym("z"), 
        &zonset, &ztype, &zarraytype))
    {
        error("array: template %s has no 'z' field", templatesym->s_name);
        return (0);
    }
    if (ztype != DT_ARRAY)
    {
        error("array: template %s, 'z' field is not an array",
            templatesym->s_name);
        return (0);
    }
    return (sc->sc_vec[zonset].w_array);
}

    /* get the "array" structure and furthermore check it's float */
static t_array *garray_getarray_floatonly(t_garray *x,
    int *yonsetp, int *elemsizep)
{
    t_array *a = garray_getarray(x);
    int yonset, type;
    t_symbol *arraytype;
    t_template *template = template_findbyname(a->a_templatesym);
    if (!template_find_field(template, gensym("y"), &yonset,
        &type, &arraytype) || type != DT_FLOAT)
            return (0);
    *yonsetp = yonset;
    *elemsizep = a->a_elemsize;
    return (a);
}

    /* get the array's name.  Return nonzero if it should be hidden */
int garray_getname(t_garray *x, t_symbol **namep)
{
    *namep = x->x_name;
    return (x->x_hidename);
}

    /* find out if array elements should "jump on click" in a graph */
int garray_joc(t_garray *x)
{
    return (x->x_joc);
}

    /* helper function for fittograph to see if the same GOP has multiple
        arrays in which case take length of the largest one */
static int garray_get_largest_array(t_garray *x)
{
    t_gobj *g;
    t_array *a;
    int an = 0;

    // checks if there is a PLOTSTYLE_POLY vs others
    // longest array uses has_*, total_* gives total number
    // n_array gives you the number of arrays
    int has_poly = 0;
    int total_poly = 0;
    int has_other = 0;
    int total_other = 0;
    int n_array = 0;

    int length = 0;
    for (g = x->x_glist->gl_list; g; g = g->g_next)
    {
        if (pd_class(&g->g_pd) == garray_class)
        {
            a = garray_getarray((t_garray *)g);
            //if ((t_garray *)g != x)
            //{
            //    an = (x->x_style == PLOTSTYLE_POINTS
            //        || x->x_style == PLOTSTYLE_BARS
            //        || a->a_n == 1 ? a->a_n : a->a_n-1);
            //    if (an > length)
            //        length = an;               
            //}
            //else
            //{
                a = garray_getarray((t_garray *)g);
                tmp = (t_garray *)g;

                // if we have new longest plot reset the
                // count for the longest plot style
                if (a->a_n > length)
                {
                    has_poly = 0;
                    has_other = 0;
                }

                // get the longest (or matching) plots style
                if (a->a_n >= length)
                {
                    if (tmp->x_style == PLOTSTYLE_POLY)
                    {
                        has_poly++;                      
                    }
                    else
                    {
                        has_other++;
                    }
                    length = a->a_n;
                }

                // adjust total number of plot styles
                if (tmp->x_style == PLOTSTYLE_POLY)
                {
                    total_poly++;                        
                }
                else
                {
                    total_other++;
                }
    //fprintf(stderr, "has_poly=%d has_other=%d | total_poly=%d total_other=%d | n_array=%d | pre-length=%d ",
    //        has_poly, has_other, total_poly, total_other, n_array, length);
    if (has_poly && !has_other)
    {
        if (total_other)
            length--;
    }
    if (!has_poly && has_other)
    {
        if (total_poly)
            length++;
    }
    //fprintf(stderr, "post-lenght=%d\n", length);
    return(length);
}

    /* reset the graph's coordinates
        to fit a new size and style for the garray */
    /* in pd-l2ork we use this for all situations, both one or more
        arrays to ensure that they are always drawn within boundaries. */
    /* flag options:
        -1 = don't resize or redraw, just send bounds message to scalars
         0 = bounds, and also resize
         1 = bounds, resize, and redraw */
void garray_fittograph(t_garray *x, int n, int flag)
    //fprintf(stderr,"garray_fittograph\n");
    // here we check for x->x_glist validity because when creating
    // a new array from the menu gl is null at the first garray_vis call
    if (!x->x_glist)
        return;
    int max_length = garray_get_largest_array(x);
    //fprintf(stderr,"garray_fittograph n=%d flag=%d | max_length=%d\n", n, flag, max_length);
Miller Puckette's avatar
Miller Puckette committed
    t_array *array = garray_getarray(x);
    t_garray *tmp;
Miller Puckette's avatar
Miller Puckette committed
    t_glist *gl = x->x_glist;
    //if (gl->gl_list == &x->x_gobj && !x->x_gobj.g_next)

    t_gobj *g;
    for (g = gl->gl_list; g; g = g->g_next)
        if (pd_class(&g->g_pd) == garray_class)
        {
            //fprintf(stderr,"found array\n");
            tmp = (t_garray *)g;
            vmess(&gl->gl_pd, gensym("bounds"), "ffff",
                0., gl->gl_y1, (double)
                    (tmp->x_style == PLOTSTYLE_POINTS || tmp->x_style == PLOTSTYLE_BARS
                        || max_length == 1 ? max_length : max_length-1),
                        gl->gl_y2);
        }
        /* close any dialogs that might have the wrong info now... 
        TODO: make changes dynamic to avoid this as it causes Apply to
        close the properties which is annoying */
        //gfxstub_deleteforkey(gl);
    if (flag >= 0)
    {
        if (flag == 1)
            array_resize_and_redraw(array, x->x_glist, n);
        else
            array_resize(array, n);
    }
Miller Puckette's avatar
Miller Puckette committed
}

/* handle "array" message to glists; call graph_scalar above with
an appropriate template; then set size and flags.  This is called
from the menu and in the file format for patches.  LATER replace this
by a more coherent (and general) invocation. */

t_garray *graph_array(t_glist *gl, t_symbol *s, int argc, t_atom *argv)
    t_symbol *fill;
    t_symbol *outline;
    int fflags;

    if (argc < 3) {pd_error(gl, "garray: not enough args"); return 0;}
    t_symbol *name = atom_getsymbolarg(0, argc--, argv++);
    int fsize = (int)atom_getfloatarg(0, argc--, argv++);
    t_symbol *templateargsym = atom_getsymbolarg(0, argc--, argv++);
    if (argc) fflags = (int)atom_getfloatarg(0, argc--, argv++);
    else fflags = 0;
    if (argc) fill = atom_getsymbolarg(0, argc--, argv++);
    else fill = gensym("black");
    if (argc) outline = atom_getsymbolarg(0, argc--, argv++);
    else outline = gensym("black");
Mathieu L Bouchard's avatar
Mathieu L Bouchard committed
    int n = fsize, zonset, ztype, saveit;
Miller Puckette's avatar
Miller Puckette committed
    t_symbol *zarraytype;
    t_garray *x;
    t_pd *x2;
    t_template *template, *ztemplate;
    t_symbol *templatesym;
    int flags = fflags;
    int filestyle = ((flags & 6) >> 1);
    //fprintf(stderr,"filestyle = %d\n", filestyle);
    int style = (filestyle == 0 ? PLOTSTYLE_POLY :
        (filestyle == 1 ? PLOTSTYLE_POINTS : filestyle));
Miller Puckette's avatar
Miller Puckette committed
    if (templateargsym != &s_float)
    {
        error("array %s: only 'float' type understood", templateargsym->s_name);
        return (0);
    }
    templatesym = gensym("pd-_float_array");
    template = template_findbyname(templatesym);
    if (!template)
    {
        error("array: couldn't find template %s", templatesym->s_name);
        return (0);
    }
    if (!template_find_field(template, gensym("z"), 
        &zonset, &ztype, &zarraytype))
    {
        error("array: template %s has no 'z' field", templatesym->s_name);
        return (0);
    }
    if (ztype != DT_ARRAY)
    {
        error("array: template %s, 'z' field is not an array",
            templatesym->s_name);
        return (0);
    }
    if (!(ztemplate = template_findbyname(zarraytype)))
    {
        error("array: no template of type %s", zarraytype->s_name);
        return (0);
    }
    saveit = ((flags & 1) != 0);
    x = graph_scalar(gl, name, templatesym, fill, outline, saveit);
Miller Puckette's avatar
Miller Puckette committed
    x->x_hidename = ((flags & 8) >> 3);
    x->x_joc = ((flags & 16) >> 4);
    x->x_fillcolor = fill;
    x->x_outlinecolor = outline;
    x->x_style = style;
Miller Puckette's avatar
Miller Puckette committed

    if (n <= 0)
        n = 100;
    array_resize(x->x_scalar->sc_vec[zonset].w_array, n);

    // let's make sure that the GOP glist has sane x values to ensure that
    // the entire array fits inside the box (this gets properly initialized
    // when creating from the menu but not when loading from the file)
    int gop_w = (x->x_style == PLOTSTYLE_POINTS || x->x_style == PLOTSTYLE_BARS
                    || n == 1 ? n : n-1);
    //fprintf(stderr,"graph_array glist->gl_x2=%g gop_w=%d n=%d\n", gl->gl_x2, gop_w, n);
    if (gl->gl_x2 != gop_w) gl->gl_x2 = gop_w;
Miller Puckette's avatar
Miller Puckette committed
    template_setfloat(template, gensym("style"), x->x_scalar->sc_vec,
Miller Puckette's avatar
Miller Puckette committed
    template_setfloat(template, gensym("linewidth"), x->x_scalar->sc_vec, 
        ((x->x_style == PLOTSTYLE_POINTS) ? 2 : 1), 1);
    template_setsymbol(template, gensym("fillcolor"), x->x_scalar->sc_vec,
        fill, 1);
    template_setsymbol(template, gensym("outlinecolor"), x->x_scalar->sc_vec,
        outline, 1);
Miller Puckette's avatar
Miller Puckette committed
    if (x2 = pd_findbyclass(gensym("#A"), garray_class))
        pd_unbind(x2, gensym("#A"));
    pd_bind(&x->x_gobj.g_pd, gensym("#A"));
    garray_redraw(x);

/* todo: need to test to see if this is necessary
   doesn't seem like it is...
Miller Puckette's avatar
Miller Puckette committed
    return (x);
}

    /* called from array menu item to create a new one */
void canvas_menuarray(t_glist *canvas)
{
Miller Puckette's avatar
Miller Puckette committed
    t_glist *x = (t_glist *)canvas;
    pd_vmess(&x->gl_pd, gensym("editmode"), "i", 1);
    //char cmdbuf[200];
    gcount++;
    gui_vmess("gui_create_array", "si",
        gfxstub_new2(&x->gl_pd, x),
        gcount);
    //sprintf(cmdbuf, "pdtk_array_dialog %%s array%d 100 3 1 .x%lx black black\n",
    //    ++gcount, (long unsigned int)canvas);
    //gfxstub_new(&x->gl_pd, x, cmdbuf);
    /* called from canvas_dialog to return array properties for the gui */
int garray_properties(t_garray *x, t_symbol **gfxstubp, t_symbol **namep,
    int *sizep, int *flagsp, t_symbol **fillp, t_symbol **outlinep)
Miller Puckette's avatar
Miller Puckette committed
    t_array *a = garray_getarray(x);
    if (!a)
        return 0;
    //gfxstub_deleteforkey(x);
        /* LATER fix this to escape '$'
Miller Puckette's avatar
Miller Puckette committed
        properly; right now we just detect a leading '$' and escape
        it.  There should be a systematic way of doing this. */
    /* still don't understand this filestyle business... */
    int filestyle = (x->x_style == 0 ? PLOTSTYLE_POLY :
        (x->x_style == 1 ? PLOTSTYLE_POINTS : x->x_style));
    int flags = x->x_saveit + 2 * filestyle + 8 * x->x_hidename + 16 * x->x_joc;

    if (x->x_name->s_name[0] == '$')
    {
        sprintf(namebuf, "\\%s", x->x_name->s_name);
    }
    else
    {
        sprintf(namebuf, "%s", x->x_name->s_name);
    }

    *gfxstubp = gensym(gfxstub_new2(&x->x_gobj.g_pd, x));
    *namep = gensym(namebuf);
    *sizep = a->a_n;
    *flagsp = flags;
    *fillp = x->x_fillcolor;
    *outlinep = x->x_outlinecolor;
    //sprintf(cmdbuf, ((x->x_name->s_name[0] == '$') ?
    //    "pdtk_array_dialog %%s \\%s %d %d 0 .x%lx %s %s\n" :
    //    "pdtk_array_dialog %%s %s %d %d 0 .x%lx %s %s\n"),
    //        x->x_name->s_name,
    //        a->a_n,
    //        x->x_saveit +  2 * filestyle + 8 * x->x_hidename + 16 * x->x_joc,
    //        (long unsigned int)glist_getcanvas(canvas),
    //        x->x_fillcolor->s_name,
    //        x->x_outlinecolor->s_name);
    //gfxstub_new(&x->x_gobj.g_pd, x, cmdbuf);
    return 1;
Miller Puckette's avatar
Miller Puckette committed
}

    /* this is called back from the dialog window to create a garray. 
    The otherflag requests that we find an existing graph to put it in. */
void glist_arraydialog(t_glist *parent, t_symbol *s, int argc, t_atom *argv)
//t_floatarg size, t_floatarg fflags, t_floatarg otherflag, float xdraw, float ydraw)
    t_atom at[6];
    if (argc !=8) {pd_error(parent,
        "arraydialog: wrong number of args"); return;}
    t_float size, fflags, otherflag, xdraw, ydraw;
    t_symbol *name = atom_getsymbolarg(0, argc, argv);
    size = atom_getfloatarg(1, argc, argv);
    fflags = atom_getfloatarg(2, argc, argv);
    otherflag = atom_getfloatarg(3, argc, argv);
    xdraw = atom_getfloatarg(4, argc, argv);
    ydraw = atom_getfloatarg(5, argc, argv);
    t_symbol *fillcolor = atom_getsymbolarg(6, argc, argv);
    t_symbol *outlinecolor = atom_getsymbolarg(7, argc, argv);
Miller Puckette's avatar
Miller Puckette committed
    t_glist *gl;
    t_garray *a;
    int flags = fflags;
    if (size < 1)
        size = 1;
    if (otherflag == 0 || (!(gl = glist_findgraph(parent))))
    {
Miller Puckette's avatar
Miller Puckette committed
        gl = glist_addglist(parent, &s_, 0, 1,
            (size > 1 ? size-1 : size), -1, xdraw+30, ydraw+30,
            xdraw+30+GLIST_DEFGRAPHWIDTH, ydraw+30+GLIST_DEFGRAPHHEIGHT);
    //a = graph_array(gl, sharptodollar(name), &s_float, size, flags);
    SETSYMBOL(at, sharptodollar(name));
    SETFLOAT(at+1, size);
    SETSYMBOL(at+2, &s_float);
    SETFLOAT(at+3, flags);

    /* no idea what xdraw and ydraw are used for...
       graph_array doesn't seem to use them 
    SETFLOAT(at+4, xdraw);
    SETFLOAT(at+5, ydraw);
    */

    SETSYMBOL(at+4, fillcolor);
    SETSYMBOL(at+5, outlinecolor);
    a = graph_array(gl, gensym("array"), 8, at);
    
    //canvas_redraw(glist_getcanvas(parent));
    garray_fittograph(a, (int)size, -1);
    // we queue gui here because otherwise garray is not drawn properly
    // since things are still being created and the object has not yet
    // been associated with glist
    // sys_queuegui((t_gobj *)gl, glist_getcanvas(gl), graph_redraw);
    //fprintf(stderr,"glist_arraydialog done\n");
    glist_redraw(gl);
    canvas_dirty(parent, 1);
extern void canvas_apply_setundo(t_canvas *x, t_gobj *y);

Miller Puckette's avatar
Miller Puckette committed
    /* this is called from the properties dialog window for an existing array */
void garray_arraydialog(t_garray *x, t_symbol *s, int argc, t_atom *argv)
    //fprintf(stderr,"================garray_arraydialog\n");

    int deleteit = atom_getfloatarg(3, argc, argv);
Miller Puckette's avatar
Miller Puckette committed
    if (deleteit != 0)
    {
        //fprintf(stderr,"deleteit\n");
        //glist_select(x->x_glist, &x->x_gobj);
        //canvas_undo_add(x->x_glist, 3, "delete", canvas_undo_set_cut(x->x_glist, 2)); // 2 = UCUT_CLEAR (from g_editor.c)
//currently cannot be undo'd until we do a new kind of undo
        int dspwas = canvas_suspend_dsp();
Miller Puckette's avatar
Miller Puckette committed
        glist_delete(x->x_glist, &x->x_gobj);
        canvas_resume_dsp(dspwas);
        canvas_redraw(glist_getcanvas(x->x_glist));
    else 
    {
        //need a new kind of undo
        //canvas_apply_setundo(glist_getcanvas(x->x_glist), (t_gobj *)x);

        if (argc !=8)
        {
            pd_error(x, "arraydialog: wrong number of args"); return;
        }
        t_symbol *name = atom_getsymbolarg(0, argc, argv);
        int fsize = atom_getfloatarg(1, argc, argv);
        int flags = atom_getfloatarg(2, argc, argv);
        t_symbol *fill = atom_getsymbolarg(6, argc, argv);
        t_symbol *outline = atom_getsymbolarg(7, argc, argv);
        int saveit = ((flags & 1) != 0);
        int filestyle = ((flags & 6) >> 1);
        int style = (filestyle == 0 ? PLOTSTYLE_POLY :
            (filestyle == 1 ? PLOTSTYLE_POINTS : filestyle));
        /* todo: revisit this filestyle business
        if (style < 2) style = !style;
        */
        int hidename = ((flags & 8) >> 3);
        int joc = ((flags & 16) >> 4);

        /*t_float stylewas = template_getfloat(
        template_findbyname(x->x_scalar->sc_template),
        gensym("style"), x->x_scalar->sc_vec, 1);*/

Miller Puckette's avatar
Miller Puckette committed
        int size;
        t_symbol *argname = sharptodollar(name);
        t_array *a = garray_getarray(x);
        t_template *scalartemplate;
        if (!a)
        {
            pd_error(x, "can't find array\n");
            return;
        }
        if (!(scalartemplate = template_findbyname(x->x_scalar->sc_template)))
        {
            error("array: no template of type %s",
                x->x_scalar->sc_template->s_name);
            return;
        }
        if (argname != x->x_name)
        {
            /* jsarlo { */
            if (x->x_listviewing)
            {
              garray_arrayviewlist_close(x);
            }
            /* } jsarlo */
            x->x_name = argname;
            pd_unbind(&x->x_gobj.g_pd, x->x_realname);
            x->x_realname = canvas_realizedollar(x->x_glist, argname);
            pd_bind(&x->x_gobj.g_pd, x->x_realname);
        }
            /* redraw the whole glist, just so the name change shows up
                there has to be a better way */
        /*if (x->x_glist->gl_havewindow)
            canvas_redraw(glist_getcanvas(x->x_glist));
            //fprintf(stderr,"================REDRAW\n");
Miller Puckette's avatar
Miller Puckette committed
        size = fsize;
        if (size < 1)
            size = 1;
        if (style != x->x_style)
        {
            x->x_style = style;
            //garray_fittograph(x, size, 1);
        }
        //fprintf(stderr,"style=%d %f\n", style, (t_float)x->x_style);
Miller Puckette's avatar
Miller Puckette committed
        template_setfloat(scalartemplate, gensym("style"),
            x->x_scalar->sc_vec, (t_float)x->x_style, 0);
        template_setfloat(scalartemplate, gensym("linewidth"),
            x->x_scalar->sc_vec, ((x->x_style == PLOTSTYLE_POINTS) ? 2 : 1), 1);
        template_setsymbol(scalartemplate, gensym("fillcolor"),
            x->x_scalar->sc_vec, fill, 0);
        template_setsymbol(scalartemplate, gensym("outlinecolor"),
            x->x_scalar->sc_vec, outline, 0);
        char buf[MAXPDSTRING];
        sprintf(buf, "%s_changed", x->x_realname->s_name);
        x->x_send = gensym(buf);
Miller Puckette's avatar
Miller Puckette committed

        garray_setsaveit(x, (saveit != 0));
        x->x_joc = joc;
        x->x_hidename = hidename;
        x->x_fillcolor = fill;
        x->x_outlinecolor = outline;
        x->x_style = style;
        if (size != a->a_n)
            garray_resize(x, size);
        else
            garray_redraw(x);
        //fprintf(stderr,"garray_arraydialog garray_redraw done\n");
        garray_select((t_gobj *)x,glist_getcanvas(x->x_glist),1);
        canvas_dirty(x->x_glist, 1);
Miller Puckette's avatar
Miller Puckette committed
    }
}

/* jsarlo { */
void garray_arrayviewlist_new(t_garray *x)
{
Miller Puckette's avatar
Miller Puckette committed
    char cmdbuf[200];
    t_array *a = garray_getarray_floatonly(x, &yonset, &elemsize);

    if (!a)
    {
        /* FIXME */
        error("error in garray_arrayviewlist_new()");
    }
    x->x_listviewing = 1;
    sprintf(cmdbuf,
            "pdtk_array_listview_new %%s %s %d\n",
            x->x_realname->s_name,
            0);
    gfxstub_new(&x->x_gobj.g_pd, x, cmdbuf);
    for (i = 0; i < ARRAYPAGESIZE && i < a->a_n; i++)
    {
        yval = *(t_float *)(a->a_vec +
Miller Puckette's avatar
Miller Puckette committed
               elemsize * i + yonset);
        sys_vgui(".%sArrayWindow.lb insert %d {%d) %g}\n",
                 x->x_realname->s_name,
                 i,
                 i,
                 yval);
    }
}

void garray_arrayviewlist_fillpage(t_garray *x,
                                   t_float page,
                                   t_float fTopItem)
{
    //fprintf(stderr,"garray_fillpage %g %g\n", page, fTopItem);
Mathieu L Bouchard's avatar
Mathieu L Bouchard committed
    int i, yonset=0, elemsize=0, topItem;
Miller Puckette's avatar
Miller Puckette committed
    t_array *a = garray_getarray_floatonly(x, &yonset, &elemsize);
    
    topItem = (int)fTopItem;
    if (!a)
    {
        /* FIXME */
        error("error in garray_arrayviewlist_new()");
    }

Miller Puckette's avatar
Miller Puckette committed
      page = 0;
      sys_vgui("pdtk_array_listview_setpage %s %d\n",
               x->x_realname->s_name,
               (int)page);
    }
    else if ((page * ARRAYPAGESIZE) >= a->a_n)
    {
Miller Puckette's avatar
Miller Puckette committed
      page = (int)(((int)a->a_n - 1)/ (int)ARRAYPAGESIZE);
      sys_vgui("pdtk_array_listview_setpage %s %d\n",
               x->x_realname->s_name,
               (int)page);
    }
    sys_vgui(".%sArrayWindow.lb delete 0 %d\n",
             x->x_realname->s_name,
             ARRAYPAGESIZE - 1);
    for (i = page * ARRAYPAGESIZE;
         (i < (page + 1) * ARRAYPAGESIZE && i < a->a_n);
         i++)
    {
        yval = *(t_float *)(a->a_vec + \
Miller Puckette's avatar
Miller Puckette committed
               elemsize * i + yonset);
        sys_vgui(".%sArrayWindow.lb insert %d {%d) %g}\n",
                 x->x_realname->s_name,
                 i % ARRAYPAGESIZE,
                 i,
                 yval);
    }
    sys_vgui(".%sArrayWindow.lb yview %d\n",
             x->x_realname->s_name,
             topItem);
}

void garray_arrayviewlist_close(t_garray *x)
{
    x->x_listviewing = 0;
    sys_vgui("pdtk_array_listview_closeWindow %s\n",
             x->x_realname->s_name);
}
/* } jsarlo */

static void garray_free(t_garray *x)
{
    t_pd *x2;
        sys_unqueuegui(&x->x_gobj);
    /* jsarlo { */
        if (x->x_listviewing)
    {
        garray_arrayviewlist_close(x);
    }
    /* } jsarlo */
    gfxstub_deleteforkey(x);
    pd_unbind(&x->x_gobj.g_pd, x->x_realname);
        /* LATER find a way to get #A unbound earlier (at end of load?) */
    while (x2 = pd_findbyclass(gensym("#A"), garray_class))
        pd_unbind(x2, gensym("#A"));
    pd_free(&x->x_scalar->sc_gobj.g_pd);
}

/* ------------- code used by both array and plot widget functions ---- */

void array_redraw(t_array *a, t_glist *glist)
{
    while (a->a_gp.gp_stub->gs_which == GP_ARRAY)
        a = a->a_gp.gp_stub->gs_un.gs_array;
    t_scalar *sc = (t_scalar *)(a->a_gp.gp_un.gp_gobj);
    scalar_redraw(sc, glist);
Miller Puckette's avatar
Miller Puckette committed
}

    /* routine to get screen coordinates of a point in an array */
    /* glist_topixels parameter was added because of t_scalar
       and t_draw caching mechanism. For scalers, we're only
       converting to pixels in scalar_getrect. So in plot_getrect
       we need to get the coordinate without regard to the x/y
       offset (and scaling factor) of a gop window */
Miller Puckette's avatar
Miller Puckette committed
void array_getcoordinate(t_glist *glist,
    char *elem, int xonset, int yonset, int wonset, int indx,
    t_float basex, t_float basey, t_float xinc,
Miller Puckette's avatar
Miller Puckette committed
    t_fielddesc *xfielddesc, t_fielddesc *yfielddesc, t_fielddesc *wfielddesc,
    t_float *xp1, t_float *xp2, t_float *yp, t_float *wp,
    int glist_topixels)
    t_float xval, yval, ypix, wpix;
Miller Puckette's avatar
Miller Puckette committed
    if (xonset >= 0)
        xval = *(t_float *)(elem + xonset);
Miller Puckette's avatar
Miller Puckette committed
    else xval = indx * xinc;
    if (yonset >= 0)
        yval = *(t_float *)(elem + yonset);
Miller Puckette's avatar
Miller Puckette committed
    else yval = 0;
//    ypix = glist_ytopixels(glist, basey +
//        fielddesc_cvttocoord(yfielddesc, yval));
    ypix = basey + fielddesc_cvttocoord(yfielddesc, yval);
    if (glist_topixels)
        ypix = glist_ytopixels(glist, ypix);
Miller Puckette's avatar
Miller Puckette committed
    if (wonset >= 0)
    {
            /* found "w" field which controls linewidth. */
        t_float wval = *(t_float *)(elem + wonset);
//        wpix = glist_ytopixels(glist, basey + 
//            fielddesc_cvttocoord(yfielddesc, yval) +
//                fielddesc_cvttocoord(wfielddesc, wval)) - ypix;
        wpix = basey + fielddesc_cvttocoord(yfielddesc, yval) +
            fielddesc_cvttocoord(wfielddesc, wval);
        if (glist_topixels)
            wpix = glist_ytopixels(glist, wpix);
        wpix -= ypix;
Miller Puckette's avatar
Miller Puckette committed
        if (wpix < 0)
            wpix = -wpix;
    }
    else wpix = 1;
    if (glist_topixels)
    {
        *xp1 = glist_xtopixels(glist, basex +
            fielddesc_cvttocoord(xfielddesc, xval));
        *xp2 = glist_xtopixels(glist, basex +
            fielddesc_cvttocoord(xfielddesc, xval+1))-1;
    }
    else
    {
        *xp1 = basex + fielddesc_cvttocoord(xfielddesc, xval);
        *xp2 = basex + fielddesc_cvttocoord(xfielddesc, xval+1)-1;
    }
Miller Puckette's avatar
Miller Puckette committed
    *yp = ypix;
    // increased following on 20140830 to 8 (+4 and -4) and 
    // so that the smallest hitbox is always at least 8x8
    // check with all_about_arrays.pd inside custom scalars
    // in an array
    if (*xp1 == *xp2)
    {
        *xp1 = *xp1 - 4;
        *xp2 = *xp2 + 4;
        wpix = 8;
    }
Miller Puckette's avatar
Miller Puckette committed
    *wp = wpix;
}

extern int array_joc; /* from g_canvas.h */
static t_float array_motion_xcumulative;
static t_float array_motion_ycumulative;
Miller Puckette's avatar
Miller Puckette committed
static t_fielddesc *array_motion_xfield;
static t_fielddesc *array_motion_yfield;
static t_glist *array_motion_glist;
static t_scalar *array_motion_scalar;
static t_array *array_motion_array;
static t_word *array_motion_wp;
static t_template *array_motion_template;
static int array_motion_npoints;
static int array_motion_elemsize;
Mathieu L Bouchard's avatar
Mathieu L Bouchard committed
//static int array_motion_altkey;
static t_float array_motion_initx;
static t_float array_motion_xperpix;
static t_float array_motion_yperpix;
Miller Puckette's avatar
Miller Puckette committed
static int array_motion_lastx;
static int array_motion_fatten;

    /* LATER protect against the template changing or the scalar disappearing
    probably by attaching a gpointer here ... */

    /* this is called when a mouse drag happens inside an array (either
       scalar or the whole array--this needs to be tested) */
Miller Puckette's avatar
Miller Puckette committed
static void array_motion(void *z, t_floatarg dx, t_floatarg dy)
{
    array_motion_xcumulative += dx * array_motion_xperpix;
    array_motion_ycumulative += dy * array_motion_yperpix;
    //fprintf(stderr,"array_motion %f %f %f %f\n", array_motion_xcumulative, array_motion_ycumulative, dx, dy);
    // used to set up boundaries and update sends accordingly
    t_glist *graph = NULL;
    if (array_garray != NULL) graph = array_garray->x_glist;
Miller Puckette's avatar
Miller Puckette committed
    if (array_motion_xfield)
    {
            /* it's an x, y plot */
        int i;
        for (i = 0; i < array_motion_npoints; i++)
        {
            t_word *thisword = (t_word *)(((char *)array_motion_wp) +
                i * array_motion_elemsize);
            t_float xwas = fielddesc_getcoord(array_motion_xfield, 
Miller Puckette's avatar
Miller Puckette committed
                array_motion_template, thisword, 1);
            t_float ywas = (array_motion_yfield ?
Miller Puckette's avatar
Miller Puckette committed
                fielddesc_getcoord(array_motion_yfield, 
                    array_motion_template, thisword, 1) : 0);
            fielddesc_setcoord(array_motion_xfield,
                array_motion_template, thisword, xwas + dx, 1);
            if (array_motion_yfield)
            {
                if (array_motion_fatten)
                {
                    if (i == 0)
                    {
                        t_float newy = ywas + dy * array_motion_yperpix;
Miller Puckette's avatar
Miller Puckette committed
                        if (newy < 0)
                            newy = 0;
                        fielddesc_setcoord(array_motion_yfield,
                            array_motion_template, thisword, newy, 1);