g_scalar.c 46.7 KB
Newer Older
Miller Puckette's avatar
Miller Puckette committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
/* 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.  */

/* This file defines the "scalar" object, which is not a text object, just a
"gobj".  Scalars have templates which describe their structures, which
can contain numbers, sublists, and arrays.

*/

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

t_class *scalar_class;

19 20
void pd_doloadbang(void);

21 22 23 24
extern t_symbol *canvas_field_templatesym; /* for "canvas" data type */
extern t_word *canvas_field_vec;           /* for "canvas" data type */
extern t_gpointer *canvas_field_gp;        /* parent for "canvas" data type */

25
void word_init(t_word *data, t_template *template, t_gpointer *gp)
Miller Puckette's avatar
Miller Puckette committed
26 27 28
{
    int i, nitems = template->t_n;
    t_dataslot *datatypes = template->t_vec;
29
    t_word *wp = data;
Miller Puckette's avatar
Miller Puckette committed
30 31 32 33 34 35 36 37 38
    for (i = 0; i < nitems; i++, datatypes++, wp++)
    {
        int type = datatypes->ds_type;
        if (type == DT_FLOAT)
            wp->w_float = 0; 
        else if (type == DT_SYMBOL)
            wp->w_symbol = &s_symbol;
        else if (type == DT_ARRAY)
        {
39
            wp->w_array = array_new(datatypes->ds_fieldtemplate, gp);
Miller Puckette's avatar
Miller Puckette committed
40 41 42
        }
        else if (type == DT_LIST)
        {
43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
            /* we feed these values to global vars so that we can 
               read them from inside canvas_new.  This is very hacky, 
               but I couldn't figure out a better way to do it. */
            canvas_field_templatesym = template->t_sym;
            /* this is bad-- we're storing a reference to a position in
               a dynamically allocated byte array when realloc can potentially
               move this data.  Essentially, we're depending on gcc to never
               move it, which is a bad assumption.  Unfortunately gpointers
               do the same thing, and I haven't heard back from Miller yet
               on how he plans to deal with this problem. Hopefully that same
               solution will be usable here. */
            canvas_field_vec = data;
            /* Here too we're being dangerous-- I'm copying the gpointer
               without recounting, and I'm not unsetting the one that's 
               part of the _glist struct (t_gpointer gl_gp). */
            canvas_field_gp = gp;

60 61 62 63 64 65 66
            /* copied from glob_evalfile... */
            t_pd *x = 0;
            /* even though binbuf_evalfile appears to take care of dspstate,
            we have to do it again here, because canvas_startdsp() assumes
            that all toplevel canvases are visible.  LATER check if this
            is still necessary -- probably not. */
            int dspstate = canvas_suspend_dsp();
67 68
            // this needs to be set to sane symbols,
            // possibly stored in the dataslot...
69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85
            glob_setfilename(0, gensym("foo"), gensym("bar"));
            t_pd *boundx = s__X.s_thing;
            s__X.s_thing = 0;       /* don't save #X; we'll need to leave it
                                       bound for the caller to grab it. */

            /* copied from binbuf_evalfile... we need to refactor at some
               point... */
            /* save bindings of symbols #N, #A (and restore afterward) */
            t_pd *bounda = gensym("#A")->s_thing, *boundn = s__N.s_thing;
            gensym("#A")->s_thing = 0;
            s__N.s_thing = &pd_canvasmaker;
            binbuf_eval(datatypes->ds_binbuf, 0, 0, 0);
            gensym("#A")->s_thing = bounda;
            s__N.s_thing = boundn;
            glob_setfilename(0, &s_, &s_);

            wp->w_list = canvas_getcurrent();
86
            wp->w_list->gl_templatesym = template->t_sym;
Jonathan Wilkes's avatar
Jonathan Wilkes committed
87 88
            /* make the parent glist the parent of our canvas field */
            wp->w_list->gl_owner = gp->gp_stub->gs_un.gs_glist;
89 90 91 92

            while ((x != s__X.s_thing) && s__X.s_thing) 
            {
                x = s__X.s_thing;
93
                vmess(x, gensym("pop"), "i", 0);
94 95 96 97 98 99 100 101 102 103 104
            }
            /* oops, can't actually do a loadbang here.
               Why?
               Consider getting the value of a fielddesc that hasn't
               been init'd yet... */
            /* pd_doloadbang(); */
            canvas_resume_dsp(dspstate);

            s__X.s_thing = boundx;
            post("eval'd a canvas with addy x%lx", (long unsigned int)
                wp->w_list);
Miller Puckette's avatar
Miller Puckette committed
105
        }
106 107 108 109 110
        else if (type == DT_TEXT)
        {
            // Miller's [text] object addition
            wp->w_binbuf = binbuf_new();
        }
Miller Puckette's avatar
Miller Puckette committed
111 112 113
    }
}

Jonathan Wilkes's avatar
Jonathan Wilkes committed
114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129
void scalar_doloadbang(t_scalar *x)
{
    t_template *template = template_findbyname(x->sc_template);
    t_dataslot *datatypes = template->t_vec;
    t_word *wp = x->sc_vec;
    int i, nitems = template->t_n;
    for (i = 0; i < nitems; i++, datatypes++, wp++)
    {
        if (datatypes->ds_type == DT_LIST)
        {
            t_canvas *c = wp->w_list;
            pd_vmess((t_pd *)c, gensym("loadbang"), "");
        }
    }
}

Miller Puckette's avatar
Miller Puckette committed
130 131 132 133 134 135 136 137 138 139
void word_restore(t_word *wp, t_template *template,
    int argc, t_atom *argv)
{
    int i, nitems = template->t_n;
    t_dataslot *datatypes = template->t_vec;
    for (i = 0; i < nitems; i++, datatypes++, wp++)
    {
        int type = datatypes->ds_type;
        if (type == DT_FLOAT)
        {
140
            t_float f;
Miller Puckette's avatar
Miller Puckette committed
141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174
            if (argc)
            {
                f =  atom_getfloat(argv);
                argv++, argc--;
            }
            else f = 0;
            wp->w_float = f; 
        }
        else if (type == DT_SYMBOL)
        {
            t_symbol *s;
            if (argc)
            {
                s =  atom_getsymbol(argv);
                argv++, argc--;
            }
            else s = &s_;
            wp->w_symbol = s;
        }
    }
    if (argc)
        post("warning: word_restore: extra arguments");
}

void word_free(t_word *wp, t_template *template)
{
    int i;
    t_dataslot *dt;
    for (dt = template->t_vec, i = 0; i < template->t_n; i++, dt++)
    {
        if (dt->ds_type == DT_ARRAY)
            array_free(wp[i].w_array);
        else if (dt->ds_type == DT_LIST)
            canvas_free(wp[i].w_list);
175 176
        else if (dt->ds_type == DT_TEXT)
            binbuf_free(wp[i].w_binbuf);
Miller Puckette's avatar
Miller Puckette committed
177 178 179
    }
}

180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229
/* some of this code is used in a function in g_canvas.c...
   need to modularize it */
static t_object *template_getstruct(t_template *template)
{
    if (template)
    {
        t_gobj *y;
        t_canvas *c;
        if (c = template_findcanvas(template))
        {
            t_symbol *s1 = gensym("struct");
            for (y = c->gl_list; y; y = y->g_next)
            {
                t_object *ob = pd_checkobject(&y->g_pd);
                t_atom *argv;
                if (!ob || ob->te_type != T_OBJECT ||
                    binbuf_getnatom(ob->te_binbuf) < 2)
                    continue;
                argv = binbuf_getvec(ob->te_binbuf);
                if (argv[0].a_w.w_symbol != s1)
                    continue;
                if (canvas_makebindsym(argv[1].a_w.w_symbol) == template->t_sym)
                    return (ob);
            }
        }
    }
    return (0);
}

int template_hasxy(t_template *template)
{
    t_symbol *zz;
    int xonset, yonset, xtype, ytype, gotx, goty;
    if (!template)
    {
        error("struct: couldn't find template %s", template->t_sym->s_name);
        return 0;
    }
    gotx = template_find_field(template, gensym("x"), &xonset, &xtype, &zz);
    goty = template_find_field(template, gensym("y"), &yonset, &ytype, &zz);
    if ((gotx && (xtype == DT_FLOAT)) &&
        (goty && (ytype == DT_FLOAT)) &&
        (xonset == 0) && (yonset == sizeof(t_word)))
    {
        return 1;
    }
    else
        return 0;
}

230
int template_check_array_fields(t_symbol *structname, t_template *template)
231
{
232
    /* We're calling this from template_cancreate as well as
233 234 235 236 237 238 239 240 241 242 243 244 245
       gtemplate_cancreate. With gtemplate_cancreate, the t_template doesn't
       exist yet. So we send the struct name to see if it matches the name of
       any array field templates that our gtemplate will depend on. If we find
       a match we will refuse to create the gtemplate. However,
       template_cancreate starts from the struct name for a template that
       already exists.  On the first time through this recursive function,
       there isn't a containing structname yet. So we suppress this conditional
       the first time through the recursive loop, and then set the structname
       for all subsequent iterations through the loop.
       
       1 = success
       0 = circular dependency
       -1 = non-existant array elemtemplate found */
246 247
    if (structname && structname == template->t_sym)
    {
248 249 250 251
        t_object *ob = template_getstruct(template);
        pd_error(ob, "%s: circular dependency",
            template->t_sym->s_name);
       return (0);
252
    }
Mathieu L Bouchard's avatar
Mathieu L Bouchard committed
253
    int i, nitems = template->t_n;
254 255 256 257
    t_dataslot *datatypes = template->t_vec;
    t_template *elemtemplate;
    for (i = 0; i < nitems; i++, datatypes++)
    {
258 259
        if (datatypes->ds_type == DT_ARRAY)
        {
260
            elemtemplate = template_findbyname(datatypes->ds_fieldtemplate);
261 262 263 264
            if (!(elemtemplate))
            {
                t_object *ob = template_getstruct(template);
                pd_error(ob, "%s: no such template",
265
                    datatypes->ds_fieldtemplate->s_name);
266
                return (-1);
267 268 269 270 271
            }
            else if (elemtemplate->t_sym == structname)
            {
                t_object *ob = template_getstruct(template);
                pd_error(ob, "%s: circular dependency",
272
                    datatypes->ds_fieldtemplate->s_name);
273 274 275 276 277 278 279 280 281 282 283
                return (0);
            }
            else
            {
                if (!structname)
                {
                    structname = template->t_sym;
                }
                return (template_check_array_fields(structname, elemtemplate));
            }
        }
284
    }
285
    return 1;
286 287
}

288 289 290
int template_cancreate(t_template *template)
{
    /* we send "0" for the structname since there is no container struct */
291
    return (template_check_array_fields(0, template) == 1);
292 293
}

294 295
    /* get the first canvas field for a scalar */
t_canvas *scalar_getcanvasfield(t_scalar *x)
296 297 298 299 300 301 302 303 304
{
    t_template *template = template_findbyname(x->sc_template);
    if (template)
    {
        int i, nitems = template->t_n;
        t_dataslot *datatypes = template->t_vec;
        for (i = 0; i < nitems; i++, datatypes++)
        {
            if (datatypes->ds_type == DT_LIST)
305
                return x->sc_vec[i].w_list;
306 307 308 309 310
        }
    }
    return 0;
}

Miller Puckette's avatar
Miller Puckette committed
311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327
    /* make a new scalar and add to the glist.  We create a "gp" here which
    will be used for array items to point back here.  This gp doesn't do
    reference counting or "validation" updates though; the parent won't go away
    without the contained arrays going away too.  The "gp" is copied out
    by value in the word_init() routine so we can throw our copy away. */
t_scalar *scalar_new(t_glist *owner, t_symbol *templatesym)
{
    t_scalar *x;
    t_template *template;
    t_gpointer gp;
    gpointer_init(&gp);
    template = template_findbyname(templatesym);
    if (!template)
    {
        error("scalar: couldn't find template %s", templatesym->s_name);
        return (0);
    }
328 329
    if (!template_cancreate(template))
        return (0);
Miller Puckette's avatar
Miller Puckette committed
330 331 332 333
    x = (t_scalar *)getbytes(sizeof(t_scalar) +
        (template->t_n - 1) * sizeof(*x->sc_vec));
    x->sc_gobj.g_pd = scalar_class;
    x->sc_template = templatesym;
334
    gpointer_setglist(&gp, owner, &x->sc_gobj);
Miller Puckette's avatar
Miller Puckette committed
335 336 337 338 339 340 341
    word_init(x->sc_vec, template, &gp);
    return (x);
}

    /* Pd method to create a new scalar, add it to a glist, and initialize
    it from the message arguments. */

342
int canvas_readscalar(t_glist *x, int natoms, t_atom *vec,
Miller Puckette's avatar
Miller Puckette committed
343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364
    int *p_nextmsg, int selectit);

void glist_scalar(t_glist *glist,
    t_symbol *classname, t_int argc, t_atom *argv)
{
    t_symbol *templatesym =
        canvas_makebindsym(atom_getsymbolarg(0, argc, argv));
    t_binbuf *b;
    int natoms, nextmsg = 0;
    t_atom *vec;
    if (!template_findbyname(templatesym))
    {
        pd_error(glist, "%s: no such template",
            atom_getsymbolarg(0, argc, argv)->s_name);
        return;
    }

    b = binbuf_new();
    binbuf_restore(b, argc, argv);
    natoms = binbuf_getnatom(b);
    vec = binbuf_getvec(b);
    
365
    canvas_readscalar(glist, natoms, vec, &nextmsg, 0);
Miller Puckette's avatar
Miller Puckette committed
366 367 368
    binbuf_free(b);
}

369 370 371 372 373 374 375 376
    /* search template fields recursively to see if the template
       depends on elemtemplate */
int template_has_elemtemplate(t_template *t, t_template *elemtemplate)
{
    int returnval = 0;
    if (t && elemtemplate)
    {
        int i;
377
        t_dataslot *d = t->t_vec;
378 379 380 381
        for (i = 0; i < t->t_n; i++, d++)
        {
            if (d->ds_type == DT_ARRAY)
            {
382
                if (d->ds_fieldtemplate == elemtemplate->t_sym)
383 384 385 386 387 388 389
                {
                    returnval = 1;
                    break;
                }
                else
                {
                    returnval = template_has_elemtemplate(
390
                        template_findbyname(d->ds_fieldtemplate),
391 392 393 394 395 396 397 398
                        elemtemplate);
                }
            }
        }
    }
    return (returnval);
}

Miller Puckette's avatar
Miller Puckette committed
399
/* -------------------- widget behavior for scalar ------------ */
400
void scalar_getbasexy(t_scalar *x, t_float *basex, t_float *basey)
Miller Puckette's avatar
Miller Puckette committed
401 402 403 404 405 406
{
    t_template *template = template_findbyname(x->sc_template);
    *basex = template_getfloat(template, gensym("x"), x->sc_vec, 0);
    *basey = template_getfloat(template, gensym("y"), x->sc_vec, 0);
}

407 408
extern int array_joc;

409 410 411
extern void template_notifyforscalar(t_template *template, t_glist *owner,
    t_scalar *sc, t_symbol *s, int argc, t_atom *argv);

412 413 414 415 416
extern void scalar_getinnersvgrect(t_gobj *z, t_glist *owner, t_word *data,
    t_template *template, t_float basex, t_float basey,
    int *xp1, int *yp1, int *xp2, int *yp2);

extern t_symbol *group_gettype(t_glist *glist);
417 418 419 420 421 422 423 424
static void scalar_getgrouprect(t_glist *owner, t_glist *groupcanvas,
    t_word *data, t_template *template, int basex, int basey,
    int *x1, int *x2, int *y1, int *y2)
{
    t_gobj *y;
    for (y = groupcanvas->gl_list; y; y = y->g_next)
    {
        if (pd_class(&y->g_pd) == canvas_class &&
425
            ((t_canvas *)y)->gl_svg)
426 427
        {
            /* todo: accumulate basex and basey for correct offset */
428 429 430 431 432 433
            if (group_gettype((t_canvas *)y) == gensym("g"))
                scalar_getgrouprect(owner, (t_glist *)y, data, template,
                    basex, basey, x1, x2, y1, y2);
            else /* inner svg */
                scalar_getinnersvgrect(y, owner, data, template, basex, basey,
                    x1, y1, x2, y2);
434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452
        }
        else
        {
            t_parentwidgetbehavior *wb = pd_getparentwidget(&y->g_pd);
            int nx1, ny1, nx2, ny2;
            if (!wb) continue;
            (*wb->w_parentgetrectfn)(y, owner,
                data, template, basex, basey,
                &nx1, &ny1, &nx2, &ny2);
            if (nx1 < *x1) *x1 = nx1;
            if (ny1 < *y1) *y1 = ny1;
            if (nx2 > *x2) *x2 = nx2;
            if (ny2 > *y2) *y2 = ny2;
            //fprintf(stderr,"====scalar_getrect x1 %d y1 %d x2 %d y2 %d\n",
            //    x1, y1, x2, y2);
        }
    }
}
 
Miller Puckette's avatar
Miller Puckette committed
453 454 455
static void scalar_getrect(t_gobj *z, t_glist *owner,
    int *xp1, int *yp1, int *xp2, int *yp2)
{
456
    //fprintf(stderr,"scalar_getrect %d\n", array_joc);
Miller Puckette's avatar
Miller Puckette committed
457
    t_scalar *x = (t_scalar *)z;
458

Miller Puckette's avatar
Miller Puckette committed
459 460 461
    t_template *template = template_findbyname(x->sc_template);
    t_canvas *templatecanvas = template_findcanvas(template);
    int x1 = 0x7fffffff, x2 = -0x7fffffff, y1 = 0x7fffffff, y2 = -0x7fffffff;
462
    t_float basex, basey;
463
    t_float screenx1, screeny1, screenx2, screeny2;
464

465 466 467
    // EXPERIMENTAL: we assume that entire canvas is within
    // the rectangle--this is for arrays
    // with "jump on click" enabled TODO: test for other regressions
468
    // (there should not be any
469
    // provided the global variable array_joc is properly maintained)
470 471
    if (glist_istoplevel(owner) && array_joc)
    {
472
        x1 = -0x7fffffff, y1 = -0x7fffffff, x2 = 0x7fffffff, y2 = 0x7fffffff;
Miller Puckette's avatar
Miller Puckette committed
473
    }
474 475
    else
    {
476 477 478
        scalar_getbasexy(x, &basex, &basey);
            /* if someone deleted the template canvas, we're just a point */
        if (!templatecanvas)
Miller Puckette's avatar
Miller Puckette committed
479
        {
480
            //fprintf(stderr,"...point\n");
481 482 483 484 485
            x1 = x2 = glist_xtopixels(owner, basex);
            y1 = y2 = glist_ytopixels(owner, basey);
        }
        else
        {
486
            /* todo: bad flow with internal return here. make it cleaner */
487
            if (x->sc_bboxcache && 0)
488
            {
489 490 491 492 493 494 495 496 497 498 499 500
                screenx1 = glist_xtopixels(owner, x->sc_x1);
                screeny1 = glist_ytopixels(owner, x->sc_y1);
                screenx2 = glist_xtopixels(owner, x->sc_x2);
                screeny2 = glist_ytopixels(owner, x->sc_y2);

                *xp1 = (int)(screenx1 < screenx2 ? screenx1 : screenx2);
                *yp1 = (int)(screeny1 < screeny2 ? screeny1 : screeny2);
                *xp2 = (int)(screenx2 > screenx1 ? screenx2 : screenx1);
                *yp2 = (int)(screeny2 > screeny1 ? screeny2 : screeny1);

                //fprintf(stderr,"CACHED FINAL scalar_getrect "
                //               "x1 %g y1 %g x2 %g y2 %g\n",
501 502 503 504
                //    screenx1,
                //    screeny1,
                //    screenx2,
                //    screeny2);
505

506 507
                return;
            }
508 509
            x1 = y1 = 0x7fffffff;
            x2 = y2 = -0x7fffffff;
510
            scalar_getgrouprect(owner, templatecanvas, x->sc_vec, template,
511 512
                basex, basey, &x1, &x2, &y1, &y2);
            if (x2 < x1 || y2 < y1)
513
                x1 = y1 = x2 = y2 = 0;
Miller Puckette's avatar
Miller Puckette committed
514 515
        }
    }
516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540
    screenx1 = glist_xtopixels(owner, x1);
    screeny1 = glist_ytopixels(owner, y1);
    screenx2 = glist_xtopixels(owner, x2);
    screeny2 = glist_ytopixels(owner, y2);

    // Values for screen bounding box
    *xp1 = (int)(screenx1 < screenx2 ? screenx1 : screenx2);
    *xp2 = (int)(screenx2 > screenx1 ? screenx2 : screenx1);
    *yp1 = (int)(screeny1 < screeny2 ? screeny1 : screeny2);
    *yp2 = (int)(screeny2 > screeny1 ? screeny2 : screeny1);

    // Cache without glist_topixel (in case a gop is moved, for example)
    x->sc_x1 = x1;
    x->sc_x2 = x2;
    x->sc_y1 = y1;
    x->sc_y2 = y2;

    //fprintf(stderr,"COMPUTED FINAL scalar_getrect "
    //    "x1 %g y1 %g x2 %g y2 %g\n",
    //    screenx1,
    //    screeny1,
    //    screenx2,
    //    screeny2);

    x->sc_bboxcache = 1; // We now have cached values for the next call
Miller Puckette's avatar
Miller Puckette committed
541 542
}

543
void scalar_drawselectrect(t_scalar *x, t_glist *glist, int state)
Miller Puckette's avatar
Miller Puckette committed
544
{
Jonathan Wilkes's avatar
Jonathan Wilkes committed
545 546 547
    char tagbuf[MAXPDSTRING];
    sprintf(tagbuf, "scalar%lx", (long unsigned int)x->sc_vec);

548
    //fprintf(stderr,"scalar_drawselecterect%d\n", state);
Miller Puckette's avatar
Miller Puckette committed
549 550 551
    if (state)
    {
        int x1, y1, x2, y2;
Jonathan Wilkes's avatar
Jonathan Wilkes committed
552 553 554
        t_float basex, basey;
        scalar_getbasexy(x, &basex, &basey);

Miller Puckette's avatar
Miller Puckette committed
555
        scalar_getrect(&x->sc_gobj, glist, &x1, &y1, &x2, &y2);
Albert Gräf's avatar
Albert Gräf committed
556
        x1--; x2++; y1--; y2++;
557
        if (glist_istoplevel(glist))
Jonathan Wilkes's avatar
Jonathan Wilkes committed
558
        {
559 560 561 562
            t_float xorig = glist_xtopixels(glist, 0);
            t_float yorig = glist_ytopixels(glist, 0);
            t_float xscale = glist_xtopixels(glist, 1) - xorig;
            t_float yscale = glist_ytopixels(glist, 1) - yorig;
Albert Gräf's avatar
Albert Gräf committed
563
            // unscaled x/y coordinates
564 565 566 567
            t_float u1 = (x1 - xorig) / xscale;
            t_float v1 = (y1 - yorig) / yscale;
            t_float u2 = (x2 - xorig) / xscale;
            t_float v2 = (y2 - yorig) / yscale;
Albert Gräf's avatar
Albert Gräf committed
568 569
            // make sure that these are in the right order,
            // gui_scalar_draw_select_rect expects them that way
570 571 572 573 574 575 576 577 578
            if (u2 < u1) {
                t_float u = u2;
                u2 = u1; u1 = u;
            }
            if (v2 < v1) {
                t_float v = v2;
                v2 = v1; v1 = v;
            }
            gui_vmess("gui_scalar_draw_select_rect", "xsiffffff",
579
                glist_getcanvas(glist), tagbuf,
Jonathan Wilkes's avatar
Jonathan Wilkes committed
580
                state,
Albert Gräf's avatar
Albert Gräf committed
581
                u1, v1, u2, v2,
582 583
                basex,
                basey);
Jonathan Wilkes's avatar
Jonathan Wilkes committed
584
        }
Miller Puckette's avatar
Miller Puckette committed
585 586 587
    }
    else
    {
588
        if (glist_istoplevel(glist))
589
        {
590 591
            gui_vmess("gui_scalar_draw_select_rect", "xsiiiiiii",
                glist_getcanvas(glist), tagbuf,
592 593 594
                state,
                0, 0, 0, 0, 0, 0);
        }
Miller Puckette's avatar
Miller Puckette committed
595 596 597
    }
}

Jonathan Wilkes's avatar
Jonathan Wilkes committed
598 599 600 601 602
/* This is greatly simplified with Node-Webkit-- we just need to get the
   basex/basey for the scalar, in addition to the bbox of the scalar.

   This can be simplified further by using a single function on the GUI
   side, and sending it the "state" parameter.
603 604
*/
void scalar_select(t_gobj *z, t_glist *owner, int state)
Miller Puckette's avatar
Miller Puckette committed
605
{
606
    //fprintf(stderr,"scalar_select %d\n", state);
Miller Puckette's avatar
Miller Puckette committed
607
    t_scalar *x = (t_scalar *)z;
Jonathan Wilkes's avatar
Jonathan Wilkes committed
608 609 610 611

    char tagbuf[MAXPDSTRING];
    sprintf(tagbuf, "scalar%lx", (long unsigned int)x->sc_vec);

Miller Puckette's avatar
Miller Puckette committed
612 613 614
    t_template *tmpl;
    t_symbol *templatesym = x->sc_template;
    t_atom at;
615
    //t_canvas *templatecanvas = NULL;
Miller Puckette's avatar
Miller Puckette committed
616 617
    t_gpointer gp;
    gpointer_init(&gp);
618
    gpointer_setglist(&gp, owner, &x->sc_gobj);
Miller Puckette's avatar
Miller Puckette committed
619
    SETPOINTER(&at, &gp);
620 621
    if (tmpl = template_findbyname(templatesym))
    {
Miller Puckette's avatar
Miller Puckette committed
622 623
        template_notify(tmpl, (state ? gensym("select") : gensym("deselect")),
            1, &at);
624
        //templatecanvas = template_findcanvas(tmpl);
625
    }
Miller Puckette's avatar
Miller Puckette committed
626
    gpointer_unset(&gp);
627 628
    if (state)
    {
629
        x->sc_selected = owner;
630 631 632
        if (glist_isvisible(owner))
            gui_vmess("gui_gobj_select", "xs",
                glist_getcanvas(owner), tagbuf);
633 634 635
    }
    else
    {
636
        x->sc_selected = 0;
637 638 639
        if (glist_isvisible(owner))
            gui_vmess("gui_gobj_deselect", "xs",
                glist_getcanvas(owner), tagbuf);
640 641 642
    }
    //sys_vgui("pdtk_select_all_gop_widgets .x%lx %lx %d\n",
    //    glist_getcanvas(owner), owner, state);
Miller Puckette's avatar
Miller Puckette committed
643 644 645 646 647
    scalar_drawselectrect(x, owner, state);
}

static void scalar_displace(t_gobj *z, t_glist *glist, int dx, int dy)
{
648
    //fprintf(stderr,"scalar_displace\n");
Miller Puckette's avatar
Miller Puckette committed
649 650 651 652 653 654 655 656 657 658 659 660 661
    t_scalar *x = (t_scalar *)z;
    t_symbol *templatesym = x->sc_template;
    t_template *template = template_findbyname(templatesym);
    t_symbol *zz;
    t_atom at[3];
    t_gpointer gp;
    int xonset, yonset, xtype, ytype, gotx, goty;
    if (!template)
    {
        error("scalar: couldn't find template %s", templatesym->s_name);
        return;
    }
    gotx = template_find_field(template, gensym("x"), &xonset, &xtype, &zz);
662
    if ((gotx && (xtype != DT_FLOAT)) || x->sc_selected != glist)
Miller Puckette's avatar
Miller Puckette committed
663 664
        gotx = 0;
    goty = template_find_field(template, gensym("y"), &yonset, &ytype, &zz);
665
    if ((goty && (ytype != DT_FLOAT)) || x->sc_selected != glist)
Miller Puckette's avatar
Miller Puckette committed
666 667
        goty = 0;
    if (gotx)
668
    {
Miller Puckette's avatar
Miller Puckette committed
669 670
        *(t_float *)(((char *)(x->sc_vec)) + xonset) +=
            dx * (glist_pixelstox(glist, 1) - glist_pixelstox(glist, 0));
671 672 673
        x->sc_x1 += dx;
        x->sc_x2 += dx;
    }
Miller Puckette's avatar
Miller Puckette committed
674
    if (goty)
675
    {
Miller Puckette's avatar
Miller Puckette committed
676 677
        *(t_float *)(((char *)(x->sc_vec)) + yonset) +=
            dy * (glist_pixelstoy(glist, 1) - glist_pixelstoy(glist, 0));
678 679 680
        x->sc_y1 += dy;
        x->sc_y2 += dy;
    }
Miller Puckette's avatar
Miller Puckette committed
681
    gpointer_init(&gp);
682
    gpointer_setglist(&gp, glist, &x->sc_gobj);
Miller Puckette's avatar
Miller Puckette committed
683
    SETPOINTER(&at[0], &gp);
684 685
    SETFLOAT(&at[1], (t_float)dx);
    SETFLOAT(&at[2], (t_float)dy);
Miller Puckette's avatar
Miller Puckette committed
686 687 688 689
    template_notify(template, gensym("displace"), 2, at);
    scalar_redraw(x, glist);
}

690
/* Kind of complicated at the moment. If a scalar is in a gop canvas, then
691
   we don't need to update its x/y fields (if it even has them) when displacing
692 693 694
   it.  Otherwise we do.  The member sc_selected is used to store the canvas
   where the selection exists-- if it matches the glist parameter below then we
   know the scalar is directly selected.  If not it's in a gop canvas.
695
*/
696 697
static void scalar_displace_withtag(t_gobj *z, t_glist *glist, int dx, int dy)
{
698
    //fprintf(stderr,"scalar_displace_withtag %lx %d %d\n", (t_int)z, dx, dy);
699 700 701 702 703 704 705
    t_scalar *x = (t_scalar *)z;
    t_symbol *templatesym = x->sc_template;
    t_template *template = template_findbyname(templatesym);
    t_symbol *zz;
    t_atom at[3];
    t_gpointer gp;
    int xonset, yonset, xtype, ytype, gotx, goty;
706
    t_float basex = 0, basey = 0;
707 708 709 710 711 712
    if (!template)
    {
        error("scalar: couldn't find template %s", templatesym->s_name);
        return;
    }
    gotx = template_find_field(template, gensym("x"), &xonset, &xtype, &zz);
713
    if ((gotx && (xtype != DT_FLOAT)) || x->sc_selected != glist)
714 715
        gotx = 0;
    goty = template_find_field(template, gensym("y"), &yonset, &ytype, &zz);
716
    if ((goty && (ytype != DT_FLOAT)) || x->sc_selected != glist)
717 718
        goty = 0;
    if (gotx)
719
    {
720 721
        *(t_float *)(((char *)(x->sc_vec)) + xonset) +=
            dx * (glist_pixelstox(glist, 1) - glist_pixelstox(glist, 0));
722 723 724
        x->sc_x1 += dx;
        x->sc_x2 += dx;
    }
725
    if (goty)
726
    {
727 728
        *(t_float *)(((char *)(x->sc_vec)) + yonset) +=
            dy * (glist_pixelstoy(glist, 1) - glist_pixelstoy(glist, 0));
729 730 731
        x->sc_y1 += dy;
        x->sc_y2 += dy;
    }
Ivica Bukvic's avatar
Ivica Bukvic committed
732
    //fprintf(stderr,"gotx=%d goty=%d\n", gotx, goty);
733
    scalar_getbasexy(x, &basex, &basey);
734
    gpointer_init(&gp);
735
    gpointer_setglist(&gp, glist, &x->sc_gobj);
736 737 738 739
    SETPOINTER(&at[0], &gp);
    SETFLOAT(&at[1], (t_float)dx);
    SETFLOAT(&at[2], (t_float)dy);
    template_notify(template, gensym("displace"), 2, at);
740

741
    canvas_getscroll(glist);
742
     
743 744
    /* Apparently this is no longer needed, so it is commented out.  But if
       we merge garrays back into this code we may need it... */
745
    /*
746 747 748 749 750 751 752 753 754 755 756 757
    if (template->t_sym != gensym("_float_array"))
    {
        t_gobj *y;
        t_canvas *templatecanvas = template_findcanvas(template);
        for (y = templatecanvas->gl_list; y; y = y->g_next)
            {
                t_parentwidgetbehavior *wb = pd_getparentwidget(&y->g_pd);
                if (!wb) continue;
                (*wb->w_parentdisplacefn)(y, glist, x->sc_vec, template,
                    basex, basey, dx, dy);
            }
    }
758
    */
759

760 761 762
    //scalar_redraw(x, glist);
}

Miller Puckette's avatar
Miller Puckette committed
763 764 765 766 767 768 769 770 771 772 773
static void scalar_activate(t_gobj *z, t_glist *owner, int state)
{
    /* post("scalar_activate %d", state); */
    /* later */
}

static void scalar_delete(t_gobj *z, t_glist *glist)
{
    /* nothing to do */
}

774 775
extern void svg_grouptogui(t_glist *g, t_template *template, t_word *data);

776 777
extern void svg_parentwidgettogui(t_gobj *z, t_scalar *sc, t_glist *owner,
    t_word *data, t_template *template);
Jonathan Wilkes's avatar
Jonathan Wilkes committed
778

779
extern void svg_register_events(t_gobj *z, t_canvas *c, t_scalar *sc,
780
    t_template *template, t_word *data, t_array *parentarray);
781

Jonathan Wilkes's avatar
Jonathan Wilkes committed
782
static void scalar_group_configure(t_scalar *x, t_glist *owner,
783 784
    t_template *template, t_word *data, t_glist *gl, t_glist *parent,
    t_array *parentarray)
Jonathan Wilkes's avatar
Jonathan Wilkes committed
785 786 787
{
    t_gobj *y;
    char tagbuf[MAXPDSTRING];
788
    sprintf(tagbuf, "draw%lx.%lx", (long unsigned int)gl,
789
        (long unsigned int)data);
Jonathan Wilkes's avatar
Jonathan Wilkes committed
790
    char parentbuf[MAXPDSTRING];
791 792
    /* check if we're in an array-- really need to see if we can just
       get rid of the different tag names for arrays... */
793
    sprintf(parentbuf, "draw%lx.%lx",
794 795
        (long unsigned int)parent,
        (long unsigned int)data);
796 797
    gui_start_vmess("gui_draw_configure_all", "xs",
        glist_getcanvas(owner), tagbuf);
798
    svg_grouptogui(gl, template, data);
Jonathan Wilkes's avatar
Jonathan Wilkes committed
799
    gui_end_vmess();
800
    svg_register_events((t_gobj *)gl, owner, x, template, data, parentarray);
Jonathan Wilkes's avatar
Jonathan Wilkes committed
801 802 803 804 805
    for (y = gl->gl_list; y; y = y->g_next)
    {
        if (pd_class(&y->g_pd) == canvas_class &&
            ((t_glist *)y)->gl_svg)
        {
806 807
            scalar_group_configure(x, owner, template, data, (t_glist *)y, gl,
                0);
Jonathan Wilkes's avatar
Jonathan Wilkes committed
808 809 810
        }
        t_parentwidgetbehavior *wb = pd_getparentwidget(&y->g_pd);
        if (!wb) continue;
811
        //(*wb->w_parentvisfn)(y, owner, gl, x, data, template,
812
        //   0, 0, 0, vis);
813
        svg_parentwidgettogui(y, x, owner, data, template);
814
        svg_register_events(y, owner, x, template, data, parentarray);
Jonathan Wilkes's avatar
Jonathan Wilkes committed
815 816 817
    }
}

818
void scalar_doconfigure(t_gobj *xgobj, t_glist *owner)
Jonathan Wilkes's avatar
Jonathan Wilkes committed
819
{
820
    t_scalar *x = (t_scalar *)xgobj;
Jonathan Wilkes's avatar
Jonathan Wilkes committed
821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838
    int vis = glist_isvisible(owner);
    if (vis)
    {
        //fprintf(stderr,"scalar_vis %d %lx\n", vis, (t_int)z);
        x->sc_bboxcache = 0;

        t_template *template = template_findbyname(x->sc_template);
        t_canvas *templatecanvas = template_findcanvas(template);
        t_gobj *y;
        t_float basex, basey;
        scalar_getbasexy(x, &basex, &basey);
            /* if we don't know how to draw it, make a small rectangle */

        t_float xscale = glist_xtopixels(owner, 1) - glist_xtopixels(owner, 0);
        t_float yscale = glist_ytopixels(owner, 1) - glist_ytopixels(owner, 0);

        char tagbuf[MAXPDSTRING];
        sprintf(tagbuf, "scalar%lx", (long unsigned int)x->sc_vec);
839 840
        gui_vmess("gui_scalar_configure_gobj", "xsiffffii",
            glist_getcanvas(owner), 
Jonathan Wilkes's avatar
Jonathan Wilkes committed
841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856
            tagbuf,
            glist_isselected(owner, &x->sc_gobj),
            xscale, 0.0, 0.0, yscale,
            (int)glist_xtopixels(owner, basex),
            (int)glist_ytopixels(owner, basey));

        for (y = templatecanvas->gl_list; y; y = y->g_next)
        {
            t_parentwidgetbehavior *wb = pd_getparentwidget(&y->g_pd);
            if (!wb)
            {
                /* check subpatches for more drawing commands.
                   (Optimized to only search [group] subpatches) */
                if (pd_class(&y->g_pd) == canvas_class &&
                    ((t_glist *)y)->gl_svg)
                {
857
                    scalar_group_configure(x, owner, template, x->sc_vec,
858
                        (t_glist *)y, templatecanvas, 0);
Jonathan Wilkes's avatar
Jonathan Wilkes committed
859 860 861 862
                }
                continue;
            }
            //(*wb->w_parentvisfn)(y, owner, 0, x, x->sc_vec, template,
863
            //    basex, basey, 0, vis);
864
            svg_parentwidgettogui(y, x, owner, x->sc_vec, template);
865
            svg_register_events(y, owner, x, template, x->sc_vec, 0);
Jonathan Wilkes's avatar
Jonathan Wilkes committed
866 867 868 869 870 871 872 873 874 875 876 877
        }
        if (glist_isselected(owner, &x->sc_gobj))
        {
            // we removed this because it caused infinite recursion
            // in the scalar-help.pd example
            //scalar_select(z, owner, 1);
            scalar_drawselectrect(x, owner, 0);
            scalar_drawselectrect(x, owner, 1);
        }
    }
}

878 879 880 881 882
void scalar_configure(t_scalar *x, t_glist *owner)
{
    sys_queuegui(x, owner, scalar_doconfigure);
}

883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913
extern int is_plot_class(t_gobj *y);
void array_configure(t_scalar *x, t_glist *owner, t_array *a, t_word *data)
{
    t_template *template = template_findbyname(x->sc_template);
    t_template *elemtemplate = template_findbyname(a->a_templatesym);
    t_canvas *templatecanvas = template_findcanvas(template);
    t_canvas *elemtemplatecanvas = template_findcanvas(elemtemplate);
    t_gobj *y;

    for (y = templatecanvas->gl_list; y; y = y->g_next)
    {
        t_parentwidgetbehavior *wb = pd_getparentwidget(&y->g_pd);
        if (wb && is_plot_class(y))
        {
            scalar_redraw(x, owner);
            return;
        }
    }
        /* If no plot widgets, it is now safe to just configure the individual
           array elements. */
    for (y = elemtemplatecanvas->gl_list; y; y = y->g_next)
    {
        t_parentwidgetbehavior *wb = pd_getparentwidget(&y->g_pd);
        if (!wb)
        {
            /* check subpatches for more drawing commands.
               (Optimized to only search [group] subpatches) */
            if (pd_class(&y->g_pd) == canvas_class &&
                ((t_glist *)y)->gl_svg)
            {
                scalar_group_configure(x, owner, template, data,
914
                    (t_glist *)y, elemtemplatecanvas, a);
915 916 917 918
            }
            continue;
        }
        svg_parentwidgettogui(y, x, owner, data, elemtemplate);
919
        svg_register_events(y, owner, x, elemtemplate, data, a);
920 921 922 923
    }

}

924 925 926 927 928 929
static void scalar_groupvis(t_scalar *x, t_glist *owner, t_template *template,
    t_glist *gl, t_glist *parent, int vis)
{
    t_gobj *y;
    if (vis)
    {
Jonathan Wilkes's avatar
Jonathan Wilkes committed
930 931 932 933 934 935
        char tagbuf[MAXPDSTRING];
        sprintf(tagbuf, "dgroup%lx.%lx", (long unsigned int)gl,
            (long unsigned int)x->sc_vec);
        char parentbuf[MAXPDSTRING];
        sprintf(parentbuf, "dgroup%lx.%lx", (long unsigned int)parent,
            (long unsigned int)x->sc_vec);
936 937 938
        gui_start_vmess("gui_scalar_draw_group", "xsss",
            glist_getcanvas(owner), tagbuf, parentbuf,
            group_gettype(gl)->s_name);
939
        svg_grouptogui(gl, template, x->sc_vec);
Jonathan Wilkes's avatar
Jonathan Wilkes committed
940
        gui_end_vmess();
941 942

        /* register events */
943
        svg_register_events((t_gobj *)gl, owner, x, template, x->sc_vec, 0);
944 945 946 947
    }
    for (y = gl->gl_list; y; y = y->g_next)
    {
        if (pd_class(&y->g_pd) == canvas_class &&
948
            ((t_glist *)y)->gl_svg)
949
        {
950
            scalar_groupvis(x, owner, template, (t_glist *)y, gl, vis);
951 952 953 954
        }
        t_parentwidgetbehavior *wb = pd_getparentwidget(&y->g_pd);
        if (!wb) continue;
        (*wb->w_parentvisfn)(y, owner, gl, x, x->sc_vec, template,
955
            0, 0, 0, vis);
956 957 958
    }
}

959
/* At present, scalars have a three-level hierarchy in the gui,
960
   with two levels accessible by the user from within Pd:
961
   scalar - ".scalar%lx", x->sc_vec
962
     |      <g> with matrix derived from x/y fields,
963 964
     |      gop basexy, and gop scaling values. This group is
     |      not configurable by the user. This means that the
965
     |      a [draw g] below can ignore basexy and gop junk
966
     |      when computing the transform matrix.
967
     v
968
   dgroup - ".dgroup%lx.%lx", templatecanvas, x->sc_vec
969 970 971
     |      group used as parent for all the toplevel drawing
     |      commands of the scalar (i.e., the ones located on
     |      the same canvas as the [struct]).  Its matrix and
972
     |      options aren't accessible by the user.
973
     v
974
   (draw  - ".draw%lx.%lx", (t_draw *ptr), x->sc_vec
975
     |      the actual drawing command: rectangle, path, g, etc. 
976
     or     Each has its own matrix and options which can set
977 978 979 980
   dgroup   with messages to the corresponding [draw] object.
     |      Also, ds arrays have an additional group for the sake of
     |      convenience.
     |      Anything with "dgroup" is either [draw g] or [draw svg]
981 982
     v
    etc.
983 984 985 986

   The tag "blankscalar" is for scalars that don't have a visual
   representation, but maybe this can just be merged with "scalar"
*/
Miller Puckette's avatar
Miller Puckette committed
987 988
static void scalar_vis(t_gobj *z, t_glist *owner, int vis)
{
989
    //fprintf(stderr,"scalar_vis %d %lx\n", vis, (t_int)z);
Miller Puckette's avatar
Miller Puckette committed
990
    t_scalar *x = (t_scalar *)z;
Jonathan Wilkes's avatar
Jonathan Wilkes committed
991 992
    char buf[50];
    sprintf(buf, "x%lx", (long unsigned int)x);
993 994 995

    x->sc_bboxcache = 0;

Miller Puckette's avatar
Miller Puckette committed
996 997 998
    t_template *template = template_findbyname(x->sc_template);
    t_canvas *templatecanvas = template_findcanvas(template);
    t_gobj *y;
999
    t_float basex, basey;
Miller Puckette's avatar
Miller Puckette committed
1000 1001 1002 1003 1004 1005
    scalar_getbasexy(x, &basex, &basey);
        /* if we don't know how to draw it, make a small rectangle */
    if (!templatecanvas)
    {
        if (vis)
        {
1006 1007
            //int x1 = glist_xtopixels(owner, basex);
            //int y1 = glist_ytopixels(owner, basey);
1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022
            /* Let's just not create anything to visualize scalars that
               don't have a template. Pd Vanilla draws a single pixel to 
               represent them, so later we might want to do a simple
               shape for them... */
            //sys_vgui(".x%lx.c create prect %d %d %d %d "
            //         "-tags {blankscalar%lx %s}\n",
            //    glist_getcanvas(owner), x1-1, y1-1, x1+1, y1+1, x,
            //    (glist_isselected(owner, &x->sc_gobj) ?
            //        "scalar_selected" : ""));
        }
        else
        {
            /* No need to delete if we don't draw anything... */
            //sys_vgui(".x%lx.c delete blankscalar%lx\n",
            //    glist_getcanvas(owner), x);
Miller Puckette's avatar
Miller Puckette committed
1023 1024 1025
        }
        return;
    }
1026 1027
    //else sys_vgui(".x%lx.c delete blankscalar%lx\n",
    //    glist_getcanvas(owner), x);
1028 1029 1030 1031 1032

    if (vis)
    {
        t_float xscale = glist_xtopixels(owner, 1) - glist_xtopixels(owner, 0);
        t_float yscale = glist_ytopixels(owner, 1) - glist_ytopixels(owner, 0);
1033 1034 1035
        /* we translate the .scalar%lx group to displace it on the tk side.
           This is the outermost group for the scalar, something like a
           poor man's viewport.
1036 1037 1038 1039 1040 1041 1042
           Also:
             * the default stroke is supposed to be "none"
             * default fill is supposed to be black.
             * stroke-linejoin should be "miter", not "round"  
           To fix these, we set the correct fill/stroke/strokelinjoin options
           here on the .scalar%lx group. (Notice also that tkpath doesn't
           understand "None"-- instead we must send an empty symbol.) */
Jonathan Wilkes's avatar
Jonathan Wilkes committed
1043 1044
        char tagbuf[MAXPDSTRING];
        sprintf(tagbuf, "scalar%lx", (long unsigned int)x->sc_vec);
1045
        gui_vmess("gui_scalar_new", "xsiffffiii",
1046
            glist_getcanvas(owner), 
Jonathan Wilkes's avatar
Jonathan Wilkes committed
1047 1048 1049 1050
            tagbuf,
            glist_isselected(owner, &x->sc_gobj),
            xscale, 0.0, 0.0, yscale,
            (int)glist_xtopixels(owner, basex),
1051 1052
            (int)glist_ytopixels(owner, basey),
            glist_istoplevel(owner));
Jonathan Wilkes's avatar
Jonathan Wilkes committed
1053
        char groupbuf[MAXPDSTRING];