From 710d3e637d0c55277e620d6dccc34fca0283566a Mon Sep 17 00:00:00 2001
From: Jonathan Wilkes <jon.w.wilkes@gmail.com>
Date: Fri, 21 Aug 2015 21:57:38 -0400
Subject: [PATCH] more work on canvas field for data structures

---
 pd/src/g_canvas.h    |   3 +-
 pd/src/g_editor.c    |  12 ++++-
 pd/src/g_scalar.c    |  41 ++++++++++++++--
 pd/src/g_traversal.c | 108 +++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 159 insertions(+), 5 deletions(-)

diff --git a/pd/src/g_canvas.h b/pd/src/g_canvas.h
index 703b4c18e..1d32a0981 100644
--- a/pd/src/g_canvas.h
+++ b/pd/src/g_canvas.h
@@ -223,8 +223,9 @@ struct _glist
     struct _scalehandle *x_handle;
     struct _scalehandle *x_mhandle;
     t_pd *gl_svg;
-    t_symbol *gl_templatename; /* for "canvas" data type */
+    t_symbol *gl_templatesym; /* for "canvas" data type */
     t_word *gl_vec;            /* for "canvas" data type */
+    t_gpointer gl_gp;            /* parent for "canvas" data type */
 };
 
 #define gl_gobj gl_obj.te_g
diff --git a/pd/src/g_editor.c b/pd/src/g_editor.c
index 2f620ffba..306376237 100644
--- a/pd/src/g_editor.c
+++ b/pd/src/g_editor.c
@@ -2307,6 +2307,8 @@ t_gobj *canvas_findhitbox(t_canvas *x, int xpos, int ypos,
     return (rval);
 }
 
+extern int scalar_hascanvasfield(t_scalar *x);
+
     /* right-clicking on a canvas object pops up a menu. */
 static void canvas_rightclick(t_canvas *x, int xpos, int ypos, t_gobj *y_sel)
 {
@@ -2314,7 +2316,7 @@ static void canvas_rightclick(t_canvas *x, int xpos, int ypos, t_gobj *y_sel)
     if (x->gl_editor->e_onmotion != MA_NONE) return;
     int canprop, canopen, isobject;
     t_gobj *y = NULL;
-    int x1, y1, x2, y2;
+    int x1, y1, x2, y2, scalar_has_canvas = 0;
     if (x->gl_editor->e_selection)
     {
         glist_noselect(x);
@@ -2344,6 +2346,14 @@ static void canvas_rightclick(t_canvas *x, int xpos, int ypos, t_gobj *y_sel)
     canprop = (!y || (y && class_getpropertiesfn(pd_class(&y->g_pd)))
                /*&& !canvas_isabstraction( ((t_glist*)y) )*/ );
     canopen = (y && zgetfn(&y->g_pd, gensym("menu-open")));
+    /* we add an extra check for scalars to enable the "Open" button
+       if they happen to have a canvas field inside them. */
+    if (pd_class(&y->g_pd) == scalar_class)
+    {
+        if (scalar_hascanvasfield((t_scalar *)y))
+            scalar_has_canvas = 1;
+        canopen = scalar_has_canvas;
+    }
     if (y || x->gl_editor->e_selection)
     {
         isobject = 1;
diff --git a/pd/src/g_scalar.c b/pd/src/g_scalar.c
index 13767fb46..cbb4355f8 100644
--- a/pd/src/g_scalar.c
+++ b/pd/src/g_scalar.c
@@ -18,10 +18,11 @@ t_class *scalar_class;
 
 void pd_doloadbang(void);
 
-void word_init(t_word *wp, t_template *template, t_gpointer *gp)
+void word_init(t_word *data, t_template *template, t_gpointer *gp)
 {
     int i, nitems = template->t_n;
     t_dataslot *datatypes = template->t_vec;
+    t_word *wp = data;
     for (i = 0; i < nitems; i++, datatypes++, wp++)
     {
         int type = datatypes->ds_type;
@@ -60,11 +61,23 @@ void word_init(t_word *wp, t_template *template, t_gpointer *gp)
             glob_setfilename(0, &s_, &s_);
 
             wp->w_list = canvas_getcurrent();
+            wp->w_list->gl_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. */
+            wp->w_list->gl_vec = data;
+            /* Here too we're being dangerous-- I'm not unsetting this
+               gpointer yet. */
+            gpointer_copy(gp, &wp->w_list->gl_gp);
 
             while ((x != s__X.s_thing) && s__X.s_thing) 
             {
                 x = s__X.s_thing;
-                vmess(x, gensym("pop"), "i", 1);
+                vmess(x, gensym("pop"), "i", 0);
             }
             /* oops, can't actually do a loadbang here.
                Why?
@@ -242,12 +255,27 @@ int template_cancreate(t_template *template)
     return (template_check_array_fields(0, template) == 1);
 }
 
+int scalar_hascanvasfield(t_scalar *x)
+{
+    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)
+                return 1;
+        }
+    }
+    return 0;
+}
+
     /* 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;
@@ -1197,6 +1225,11 @@ static void scalar_save(t_gobj *z, t_binbuf *b)
     binbuf_free(b2);
 }
 
+static void scalar_menuopen(t_scalar *x)
+{
+    post("tried to open a thing");
+}
+
 static void scalar_properties(t_gobj *z, struct _glist *owner)
 {
     t_scalar *x = (t_scalar *)z;
@@ -1256,6 +1289,8 @@ void g_scalar_setup(void)
         CLASS_GOBJ, 0);
     class_addmethod(scalar_class, (t_method)scalar_mouseover,
         gensym("mouseover"), A_FLOAT, A_NULL);
+    class_addmethod(scalar_class, (t_method)scalar_menuopen,
+        gensym("menu-open"), 0);
     class_setwidget(scalar_class, &scalar_widgetbehavior);
     class_setsavefn(scalar_class, scalar_save);
     class_setpropertiesfn(scalar_class, scalar_properties);
diff --git a/pd/src/g_traversal.c b/pd/src/g_traversal.c
index ce4cae884..10c1709a5 100644
--- a/pd/src/g_traversal.c
+++ b/pd/src/g_traversal.c
@@ -13,6 +13,7 @@ getsize - get the size of an array
 setsize - change the size of an array
 append -  add an element to a list
 sublist - get a pointer into a list which is an element of another scalar
+field -   get numeric/symbolic field while within a canvas field
 
 */
 
@@ -1305,6 +1306,112 @@ static void sublist_setup(void)
     class_addpointer(sublist_class, sublist_pointer); 
 }
 
+/* ---------------------- field ----------------------------- */
+
+static t_class *field_class;
+
+typedef struct _field
+{
+    t_object x_obj;
+    t_symbol *x_s;
+    t_template *x_template;
+    t_canvas *x_canvas;
+} t_field;
+
+static void *field_new(t_symbol *s)
+{
+    t_field *x = (t_field *)pd_new(field_class);
+    x->x_s = s;
+    x->x_canvas = canvas_getcurrent();
+    // do some error checking here
+    x->x_template = template_findbyname(x->x_canvas->gl_templatesym);
+    outlet_new(&x->x_obj, &s_list);
+    return (x);
+}
+
+static void field_set(t_field *x, t_symbol *s)
+{
+    x->x_s = s;
+}
+
+static void field_bang(t_field *x)
+{
+    post("sanity is %d", x->x_canvas->sanity);
+    t_word *vec = x->x_canvas->gl_vec;
+    t_template *template = x->x_template;
+    t_symbol *fieldsym = x->x_s;
+    int onset, type;
+    t_symbol *arraytype;
+    if (template_find_field(template, fieldsym, &onset, &type, &arraytype))
+    {
+        if (type == DT_FLOAT)
+            outlet_float(x->x_obj.ob_outlet,
+                *(t_float *)(((char *)vec) + onset));
+        else if (type == DT_SYMBOL)
+            outlet_symbol(x->x_obj.ob_outlet,
+                *(t_symbol **)(((char *)vec)+ onset));
+        else pd_error(x, "field: %s.%s is not a number or symbol",
+                template->t_sym->s_name, fieldsym->s_name);
+    }
+    else pd_error(x, "field: %s.%s: no such field",
+        template->t_sym->s_name, fieldsym->s_name);
+}
+
+static void field_setvalue(t_field *x, t_symbol *s, int argc, t_atom *argv)
+{
+    t_template *template = x->x_template;
+    t_word *vec = x->x_canvas->gl_vec;
+    t_gpointer *gp = &x->x_canvas->gl_gp;
+    t_gstub *gs = gp->gp_stub;
+    int onset, type;
+    t_symbol *arraytype, *fieldsym = x->x_s;
+    if (argc)
+    {
+        if (argv->a_type == A_FLOAT)
+            template_setfloat(template, fieldsym, vec,
+                atom_getfloatarg(0, argc, argv), 1);
+        else if (argv->a_type == A_SYMBOL)
+            template_setsymbol(template, fieldsym, vec,
+                atom_getsymbolarg(0, argc, argv), 1);
+        else pd_error(x, "field: incoming value must be float or symbol");
+    }
+    if (gp->gp_stub->gs_which == GP_GLIST)
+        scalar_configure((t_scalar *)(gp->gp_un.gp_gobj), gs->gs_un.gs_glist);  
+    else
+    {
+        t_array *owner_array = gs->gs_un.gs_array;
+        while (owner_array->a_gp.gp_stub->gs_which == GP_ARRAY)
+            owner_array = owner_array->a_gp.gp_stub->gs_un.gs_array;
+        scalar_redraw((t_scalar *)(owner_array->a_gp.gp_un.gp_gobj),
+            owner_array->a_gp.gp_stub->gs_un.gs_glist);  
+    }
+}
+
+static void field_symbol(t_field *x, t_symbol *s)
+{
+    t_atom at[1];
+    SETSYMBOL(at, s);
+    field_setvalue(x, gensym("symbol"), 1, at);
+}
+
+static void field_float(t_field *x, t_floatarg f)
+{
+    t_atom at[1];
+    SETFLOAT(at, f);
+    field_setvalue(x, gensym("float"), 1, at);
+}
+
+static void field_setup(void)
+{
+    field_class = class_new(gensym("field"), (t_newmethod)field_new,
+        0, sizeof(t_field), 0, A_DEFSYM, 0);
+    class_addfloat(field_class, field_float); 
+    class_addsymbol(field_class, field_symbol); 
+    class_addbang(field_class, field_bang); 
+    class_addmethod(field_class, (t_method)field_set, gensym("set"),
+        A_SYMBOL, 0); 
+}
+
 /* ----------------- setup function ------------------- */
 
 void g_traversal_setup(void)
@@ -1317,4 +1424,5 @@ void g_traversal_setup(void)
     setsize_setup();
     append_setup();
     sublist_setup();
+    field_setup();
 }
-- 
GitLab