diff --git a/pd/doc/5.reference/objectinfo-help.pd b/pd/doc/5.reference/objectinfo-help.pd
new file mode 100644
index 0000000000000000000000000000000000000000..ad0ff73c964e19d5a0bb22d67d905660e5185271
--- /dev/null
+++ b/pd/doc/5.reference/objectinfo-help.pd
@@ -0,0 +1,89 @@
+#N canvas 0 19 555 619 10;
+#X obj 0 595 cnv 15 552 21 empty \$0-pddp.cnv.footer empty 20 12 0
+14 -228856 -66577 0;
+#X obj 0 0 cnv 15 552 40 empty \$0-pddp.cnv.header objectinfo 3 12
+0 18 -204280 -1 0;
+#X obj 0 403 cnv 3 550 3 empty \$0-pddp.cnv.inlets inlets 8 12 0 13
+-228856 -1 0;
+#N canvas 456 104 482 506 META 0;
+#X text 12 175 LIBRARY internal;
+#X text 12 25 LICENSE SIBSD;
+#X text 12 235 HELP_PATCH_AUTHORS Jonathan Wilkes revised the patch
+to conform to the PDDP template for Pd version 0.42.;
+#X text 12 135 OUTLET_0 list;
+#X text 12 195 AUTHOR Jonathan Wilkes;
+#X text 12 215 RELEASE_DATE 2013;
+#X text 12 5 KEYWORDS canvas_op;
+#X text 12 45 DESCRIPTION get info on a Pd object on a canvas;
+#X text 12 75 INLET_0 print bbox boxtext class inlets outlets version
+;
+#X text 12 95 INLET_1 float;
+#X text 12 115 INLET_2 float;
+#X text 12 155 OUTLET_1 bang;
+#X restore 500 597 pd META;
+#X obj 0 478 cnv 3 550 3 empty \$0-pddp.cnv.outlets outlets 8 12 0
+13 -228856 -1 0;
+#X obj 0 515 cnv 3 550 3 empty \$0-pddp.cnv.argument arguments 8 12
+0 13 -228856 -1 0;
+#X obj 0 555 cnv 3 550 3 empty \$0-pddp.cnv.more_info more_info 8 12
+0 13 -228856 -1 0;
+#X obj 78 411 cnv 17 3 30 empty \$0-pddp.cnv.let.0 0 5 9 0 16 -228856
+-162280 0;
+#N canvas 102 481 428 108 Related_objects 0;
+#X obj 1 1 cnv 15 425 20 empty \$0-pddp.cnv.subheading empty 3 12 0
+14 -204280 -1 0;
+#X text 7 1 [objectinfo] Related Objects;
+#X obj 22 36 pdinfo;
+#X obj 72 36 classinfo;
+#X obj 142 36 canvasinfo;
+#X restore 101 597 pd Related_objects;
+#X obj 78 487 cnv 17 3 17 empty \$0-pddp.cnv.let.0 0 5 9 0 16 -228856
+-162280 0;
+#X obj 4 597 pddp/pddplink all_about_help_patches.pd -text Usage Guide
+;
+#X msg 39 62 print;
+#X text 80 61 print all attributes to the console;
+#X msg 101 232 version;
+#X text 98 410 print;
+#X text 168 410 - print out each available message (other than "print")
+followed by a semicolon and the output for that method.;
+#X text 99 487 list;
+#X text 169 487 - output varies depending on the message sent to [pdinfo]
+;
+#X text 11 20 get info about a Pd object;
+#X obj 473 7 objectinfo;
+#X obj 39 323 objectinfo;
+#X msg 72 92 bbox;
+#X text 114 91 list of coordinates-- x1 y1 x2 y2-- for the bounding
+rectangle of the object on the canvas.;
+#X msg 78 122 boxtext;
+#X text 137 121 list of atoms that appear in the object box;
+#X msg 89 152 class;
+#X text 136 152 class of the object;
+#X msg 92 182 inlets;
+#X text 141 181 number of inlets the object has;
+#X msg 95 209 outlets;
+#X text 151 208 number of outlets the object has;
+#X floatatom 98 269 5 0 0 0 - - -, f 5;
+#X text 141 269 index number of the object you want to inspect;
+#X floatatom 104 299 5 0 0 0 - - -, f 5;
+#X text 141 299 canvas level: '0' for this canvas \, '1' for the parent
+canvas \, etc.;
+#X text 201 347 outputs a bang if there isn't an object at this index
+;
+#X obj 39 372 print object-info;
+#X obj 96 347 print no-object;
+#X text 168 450 objectinfo's available methods are shown above.;
+#X text 81 532 1) float;
+#X text 169 532 canvas level;
+#X connect 11 0 20 0;
+#X connect 13 0 20 0;
+#X connect 20 0 36 0;
+#X connect 20 1 37 0;
+#X connect 21 0 20 0;
+#X connect 23 0 20 0;
+#X connect 25 0 20 0;
+#X connect 27 0 20 0;
+#X connect 29 0 20 0;
+#X connect 31 0 20 1;
+#X connect 33 0 20 2;
diff --git a/pd/doc/5.reference/pdinfo-help.pd b/pd/doc/5.reference/pdinfo-help.pd
index 0f18107a59d1709590e9d14f671985af043f68ba..848c778275781bc601a4d842fcb4320c57b09fbe 100644
--- a/pd/doc/5.reference/pdinfo-help.pd
+++ b/pd/doc/5.reference/pdinfo-help.pd
@@ -1,9 +1,9 @@
-#N canvas 308 59 555 619 10;
+#N canvas -9 19 555 619 10;
 #X obj 0 595 cnv 15 552 21 empty \$0-pddp.cnv.footer empty 20 12 0
 14 -228856 -66577 0;
 #X obj 0 0 cnv 15 552 40 empty \$0-pddp.cnv.header pdinfo 3 12 0 18
 -204280 -1 0;
-#X obj 0 389 cnv 3 550 3 empty \$0-pddp.cnv.inlets inlets 8 12 0 13
+#X obj 0 353 cnv 3 550 3 empty \$0-pddp.cnv.inlets inlets 8 12 0 13
 -228856 -1 0;
 #N canvas 34 81 482 506 META 0;
 #X text 12 175 LIBRARY internal;
@@ -27,13 +27,13 @@ version;
 #X text 12 195 AUTHOR Jonathan Wilkes;
 #X text 12 215 RELEASE_DATE 2013;
 #X restore 500 597 pd META;
-#X obj 0 486 cnv 3 550 3 empty \$0-pddp.cnv.outlets outlets 8 12 0
+#X obj 0 450 cnv 3 550 3 empty \$0-pddp.cnv.outlets outlets 8 12 0
 13 -228856 -1 0;
-#X obj 0 523 cnv 3 550 3 empty \$0-pddp.cnv.argument arguments 8 12
+#X obj 0 487 cnv 3 550 3 empty \$0-pddp.cnv.argument arguments 8 12
 0 13 -228856 -1 0;
-#X obj 0 563 cnv 3 550 3 empty \$0-pddp.cnv.more_info more_info 8 12
+#X obj 0 527 cnv 3 550 3 empty \$0-pddp.cnv.more_info more_info 8 12
 0 13 -228856 -1 0;
-#X obj 78 397 cnv 17 3 30 empty \$0-pddp.cnv.let.0 0 5 9 0 16 -228856
+#X obj 78 361 cnv 17 3 30 empty \$0-pddp.cnv.let.0 0 5 9 0 16 -228856
 -162280 0;
 #N canvas 102 481 428 108 Related_objects 0;
 #X obj 1 1 cnv 15 425 20 empty \$0-pddp.cnv.subheading empty 3 12 0
@@ -41,28 +41,28 @@ version;
 #X obj 22 36 samplerate~;
 #X text 7 1 [pdinfo] Related Objects;
 #X restore 101 597 pd Related_objects;
-#X obj 78 495 cnv 17 3 17 empty \$0-pddp.cnv.let.0 0 5 9 0 16 -228856
+#X obj 78 459 cnv 17 3 17 empty \$0-pddp.cnv.let.0 0 5 9 0 16 -228856
 -162280 0;
 #X obj 4 597 pddp/pddplink all_about_help_patches.pd -text Usage Guide
 ;
 #X text 11 20 get info from the global Pd instance that's running;
 #X obj 503 7 pdinfo;
-#X obj 39 286 pdinfo;
-#X msg 39 80 print;
-#X text 80 79 print all attributes to the console;
-#X msg 101 255 version;
-#X obj 39 317 print pd-version;
-#X text 155 254 version of Pd that's running (MAJOR MINOR TEST);
-#X msg 95 232 samplerate;
-#X text 169 231 global samplerate;
-#X msg 78 145 dir;
-#X text 107 144 directory of the Pd executable;
-#X msg 72 115 blocksize;
-#X text 144 114 global blocksize;
-#X msg 92 205 pi;
-#X text 121 204 value of Pi;
-#X msg 89 175 dsp-status;
-#X text 164 175 whether dsp is turned on (0 = off \, 1 = on);
+#X obj 39 291 pdinfo;
+#X msg 39 60 print;
+#X text 80 59 print all attributes to the console;
+#X msg 101 230 version;
+#X obj 39 322 print pd-version;
+#X text 155 229 version of Pd that's running (MAJOR MINOR TEST);
+#X msg 95 207 samplerate;
+#X text 169 206 global samplerate;
+#X msg 78 120 dir;
+#X text 107 119 directory of the Pd executable;
+#X msg 72 90 blocksize;
+#X text 144 89 global blocksize;
+#X msg 92 180 pi;
+#X text 121 179 value of Pi;
+#X msg 89 150 dsp-status;
+#X text 164 150 whether dsp is turned on (0 = off \, 1 = on);
 #N canvas 100 44 428 514 audio-attributes 0;
 #X obj 1 1 cnv 15 425 20 empty \$0-pddp.cnv.subheading empty 3 12 0
 14 -204280 -1 0;
@@ -107,7 +107,7 @@ version;
 #X connect 22 0 2 0;
 #X connect 24 0 22 0;
 #X connect 25 0 2 0;
-#X restore 206 317 pd audio-attributes;
+#X restore 206 322 pd audio-attributes;
 #N canvas 125 83 428 395 midi-attributes 0;
 #X obj 1 1 cnv 15 425 20 empty \$0-pddp.cnv.subheading empty 3 12 0
 14 -204280 -1 0;
@@ -139,16 +139,23 @@ version;
 #X connect 15 0 2 0;
 #X connect 16 0 2 0;
 #X connect 17 0 2 0;
-#X restore 346 317 pd midi-attributes;
-#X text 98 396 print;
-#X text 168 396 - print out each available message (other than "print")
+#X restore 346 322 pd midi-attributes;
+#X text 98 360 print;
+#X text 168 360 - print out each available message (other than "print")
 followed by a semicolon and the output for that method.;
-#X text 99 495 list;
-#X text 81 540 none;
-#X text 168 436 pdinfo's available methods are shown above \, with
+#X text 99 459 list;
+#X text 81 504 none;
+#X text 168 400 pdinfo's available methods are shown above \, with
 audio-device and midi-device message in subpatches.;
-#X text 169 495 - output varies depending on the message sent to [pdinfo]
+#X text 169 459 - output varies depending on the message sent to [pdinfo]
 ;
+#X msg 101 260 classtable;
+#X text 81 547 When using the "classtable" message \, note that some
+external Pd classes don't have a name. These are currently listed simply
+as "anonymous-class" in the output.;
+#X text 175 259 (long) list of all classes that have been loaded in
+the running instance of Pd. (Note: not all classes can be created in
+an object box.);
 #X connect 13 0 17 0;
 #X connect 14 0 13 0;
 #X connect 16 0 13 0;
@@ -157,3 +164,4 @@ audio-device and midi-device message in subpatches.;
 #X connect 23 0 13 0;
 #X connect 25 0 13 0;
 #X connect 27 0 13 0;
+#X connect 37 0 13 0;
diff --git a/pd/src/g_editor.c b/pd/src/g_editor.c
index 0d40081f75d24258612dde2333d7b1fa6a8dc3d5..c957e4fa766d08b7672339ad5d7c3f9e954f8500 100644
--- a/pd/src/g_editor.c
+++ b/pd/src/g_editor.c
@@ -4923,7 +4923,7 @@ static void canvas_displaceselection(t_canvas *x, int dx, int dy)
     }
     if (dx || dy)
     {
-        sys_vgui(".x%lx.c move selected %d %d\n", x, dx, dy);
+        sys_vgui("pdtk_canvas_displace_withtag .x%lx.c %d %d\n", x, dx, dy);
         if (resortin) canvas_resortinlets(x);
         if (resortout) canvas_resortoutlets(x);
         //sys_vgui("pdtk_canvas_getscroll .x%lx.c\n", x);
@@ -5184,7 +5184,7 @@ extern void graph_checkgop_rect(t_gobj *z, t_glist *glist,
 
 void canvas_motion(t_canvas *x, t_floatarg xpos, t_floatarg ypos,
     t_floatarg fmod)
-{ 
+{
     //fprintf(stderr,"motion %d %d %d %d\n",
     //    (int)xpos, (int)ypos, (int)fmod, canvas_last_glist_mod);
     //fprintf(stderr,"canvas_motion=%d\n",x->gl_editor->e_onmotion);
diff --git a/pd/src/g_graph.c b/pd/src/g_graph.c
index 05eccc20f26d1217a626f5545c8939907d90ff43..1f1d2b0e8c2779f2df02215d0edf2861ef26bd3e 100644
--- a/pd/src/g_graph.c
+++ b/pd/src/g_graph.c
@@ -803,7 +803,7 @@ int text_ypix(t_text *x, t_glist *glist)
     This is too conservative -- for instance, when you draw an "open"
     rectangle on the parent, you shouldn't have to redraw the window!  */
 void glist_redraw(t_glist *x)
-{  
+{
     if (glist_isvisible(x))
     {
             /* LATER fix the graph_vis() code to handle both cases */
diff --git a/pd/src/g_scalar.c b/pd/src/g_scalar.c
index 444e7b602422af784e7084c6a951c4df85877e2e..c49a8d29c884147bcd58d0e67d354599d6e60518 100644
--- a/pd/src/g_scalar.c
+++ b/pd/src/g_scalar.c
@@ -374,18 +374,10 @@ void scalar_drawselectrect(t_scalar *x, t_glist *glist, int state)
        
         scalar_getrect(&x->sc_gobj, glist, &x1, &y1, &x2, &y2);
         x1--; x2++; y1--; y2++;
-                /* we're not giving the rectangle the "select" tag
-                   because we have to manually displace the scalar
-                   in scalar_displace_withtag. The reason for that
-                   is the "displace" message may trigger a redraw
-                   of the bbox at the new position, and Pd-l2ork's
-                   general "move selected" subcommand will end up
-                   offsetting such a rect by dx dy.
-                */
         if (glist_istoplevel(glist))
             sys_vgui(".x%lx.c create prect %d %d %d %d "
                      "-strokewidth 1 -stroke $pd_colors(selection) "
-                     "-tags {select%lx}\n",
+                     "-tags {select%lx selected}\n",
                     glist_getcanvas(glist), x1, y1, x2, y2,
                     x);
     }
@@ -397,19 +389,18 @@ void scalar_drawselectrect(t_scalar *x, t_glist *glist, int state)
 }
 
 /* This is a workaround.  Since scalars are contained within a tkpath
-   group, and since tkpath groups don't have coords, we have to fudge
-   things with regard to Pd-l2ork's normal *_displace_withtag functionality.
-   (Additionally, we can't just fall back to the old displace method
-   because it too assumes the canvas item has an xy coord.)
-
-   We draw the selection rect but don't add the scalar to the
-   "selected" tag.  This means when Pd-l2ork issues the canvas "move"
-   command, our scalar doesn't go anywhere.  Instead we get the callback
-   to scalar_displace_withtag and do another workaround to get the
-   new position and feed it to the scalar's matrix.
-
-   This creates a problem with gop canvases, and yet _another_ partial
-   workaround which I apologize for inside t_scalar def in m_pd.h.
+   group, and since tkpath groups don't have coords, we can't just use
+   the same "selected" tag that is used to move all other Pd objects. That
+   would move scalars in their local coordinate system, which is wrong
+   for transformed objects. For example, if a rectangle is rotated 45 and
+   we try to do a [canvas move 10 0] command on it, it would get moved to
+   the northeast instead of to the right!
+
+   Instead, we tag selected scalars with the "scalar_selected" tag. Then in
+   the GUI we use that tag to loop through and change each scalar's group
+   matrix, and add (dx,dy) to its current translation values. The scalar
+   group matrix .scalar%lx isn't accessible by the user, so it will only
+   ever contain these translation values.
 */
 void scalar_select(t_gobj *z, t_glist *owner, int state)
 {
@@ -435,57 +426,16 @@ void scalar_select(t_gobj *z, t_glist *owner, int state)
         x->sc_selected = owner;
         sys_vgui(".x%lx.c addtag selected withtag blankscalar%lx\n",
             glist_getcanvas(owner), x);
-        /* how do we navigate through a t_word list?
-        if (x->sc_vec)
-        {
-            t_word *v = x->sc_vec;
-            while(v)
-            {
-                sys_vgui(".x%lx.c "
-                         "addtag selected withtag .x%lx.x%lx.template%lx\n",
-                    glist_getcanvas(owner), glist_getcanvas(owner), owner, v);
-            }
-        }*/
-        /*if (templatecanvas) {
-            // get the universal tag for all nested objects
-            t_canvas *tag = owner;
-            while (tag->gl_owner)
-            {
-                tag = tag->gl_owner;
-            }
-            sys_vgui(".x%lx.c addtag selected withtag %lx\n",
-                glist_getcanvas(owner), (t_int)tag);
-        }*/
+        sys_vgui(".x%lx.c addtag scalar_selected withtag {.scalar%lx}\n",
+            glist_getcanvas(owner), x->sc_vec);
     }
     else
     {
         x->sc_selected = 0;
         sys_vgui(".x%lx.c dtag blankscalar%lx selected\n",
             glist_getcanvas(owner), x);
-        sys_vgui(".x%lx.c dtag .x%lx.x%lx.template%lx selected\n",
-            glist_getcanvas(owner), glist_getcanvas(owner),
-            owner, x->sc_vec);
-        /* how do we navigate through a t_word list?
-        if (x->sc_vec)
-        {
-            t_word *v = x->sc_vec;
-            while (v)
-            {
-                sys_vgui(".x%lx.c dtag .x%lx.x%lx.template%lx selected\n",
-                    glist_getcanvas(owner), glist_getcanvas(owner),
-                    owner, x->sc_vec);
-            }
-        }*/
-        /*if (templatecanvas) {
-            // get the universal tag for all nested objects
-            t_canvas *tag = owner;
-            while (tag->gl_owner)
-            {
-                tag = tag->gl_owner;
-            }
-            sys_vgui(".x%lx.c dtag %lx selected\n",
-                glist_getcanvas(owner), (t_int)tag);
-        }*/
+        sys_vgui(".x%lx.c dtag .scalar%lx scalar_selected\n",
+            glist_getcanvas(owner), x->sc_vec);
     }
     //sys_vgui("pdtk_select_all_gop_widgets .x%lx %lx %d\n",
     //    glist_getcanvas(owner), owner, state);
@@ -537,12 +487,11 @@ static void scalar_displace(t_gobj *z, t_glist *glist, int dx, int dy)
     scalar_redraw(x, glist);
 }
 
-/* Very complicated at the moment. If a scalar is in a gop canvas, then
+/* Kind of complicated at the moment. If a scalar is in a gop canvas, then
    we don't need to update its x/y fields (if it even has them) when displacing
-   it.  Otherwise we do.  The global selected_owner variable is used to store
-   the "owner" canvas-- if it matches the glist parameter below then we know
-   the scalar is directly selected.  If not it's in a gop canvas.  (This doesn't
-   yet handle nested GOPs, unfortunately.)
+   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.
 */
 static void scalar_displace_withtag(t_gobj *z, t_glist *glist, int dx, int dy)
 {
@@ -589,48 +538,15 @@ static void scalar_displace_withtag(t_gobj *z, t_glist *glist, int dx, int dy)
     SETFLOAT(&at[2], (t_float)dy);
     template_notify(template, gensym("displace"), 2, at);
 
-    /* this is a hack to make sure the bbox gets drawn in
-       the right location.  If the scalar is selected
-       then it's possible that a "displace" message
-       from the [struct] will trigger a redraw of the
-       bbox. So we don't update the cached bbox until
-       after that redraw, so we can move the bbox below.
-    */
-    sys_vgui(".x%lx.c coords {select%lx} %d %d %d %d\n", glist, x,
-        x->sc_x1 - 1, x->sc_y1 - 1, x->sc_x2 + 1, x->sc_y2 + 1);
-
     t_float xscale = glist_xtopixels(x->sc_selected, 1) -
         glist_xtopixels(x->sc_selected, 0);
     t_float yscale = glist_ytopixels(x->sc_selected, 1) -
         glist_ytopixels(x->sc_selected, 0);
 
-    sys_vgui(".x%lx.c itemconfigure {.scalar%lx} "
-             "-matrix { {%g %g} {%g %g} {%d %d} }\n",
-        glist_getcanvas(glist), x->sc_vec, xscale, 0.0, 0.0, yscale,
-        (int)glist_xtopixels(x->sc_selected, basex) +
-            (x->sc_selected == glist ? 0 : dx),
-        (int)glist_ytopixels(x->sc_selected, basey) +
-            (x->sc_selected == glist ? 0 : dy));
-
     sys_vgui("pdtk_canvas_getscroll .x%lx.c\n", glist);
      
-    /* This awful hack is here because plot_vis is used by both ds arrays
-       and garrays.  Unlike the other drawing commands, plot_vis still does
-       all the gop scaling and basex/basey calculations manually.  So
-       currently the trace does not use the scalar group as its "-parent".
-
-       Once all the calculations inside plot_vis, array_doclick, and
-       array_motion remove the glist_[xy]topixels and basex/y stuff, plot_vis
-       can use the scalar "-parent" and this awful hack can be removed.
-
-       Garrays follow their Pd-l2ork's standard select and displace_withtag
-       behavior, so we don't use the hack for them.  (Non-garray scalars
-       should follow that behavior too, but cannot atm for the reason given
-       in the comment above scalar_select...)
-
-       Apparently this is no longer needed, so it is commented out.  Once
-       we test it we should be able to delete it for good... */
-
+    /* Apparently this is no longer needed, so it is commented out.  But if
+       we merge garrays back into this code we may need it... */
     /*
     if (template->t_sym != gensym("_float_array"))
     {
@@ -735,8 +651,10 @@ static void scalar_vis(t_gobj *z, t_glist *owner, int vis)
             int x1 = glist_xtopixels(owner, basex);
             int y1 = glist_ytopixels(owner, basey);
             sys_vgui(".x%lx.c create prect %d %d %d %d "
-                     "-tags {blankscalar%lx}\n",
-                glist_getcanvas(owner), x1-1, y1-1, x1+1, y1+1, x);
+                     "-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 sys_vgui(".x%lx.c delete blankscalar%lx\n",
             glist_getcanvas(owner), x);
@@ -751,9 +669,10 @@ static void scalar_vis(t_gobj *z, t_glist *owner, int vis)
         t_float yscale = glist_ytopixels(owner, 1) - glist_ytopixels(owner, 0);
         /* we could use the tag .template%lx for easy access from
            the draw_class, but that's not necessary at this point */
-        sys_vgui(".x%lx.c create group -tags {.scalar%lx} "
+        sys_vgui(".x%lx.c create group -tags {.scalar%lx %s} "
             "-matrix { {%g %g} {%g %g} {%d %d} }\n",
             glist_getcanvas(owner), x->sc_vec,
+            (glist_isselected(owner, &x->sc_gobj) ? "scalar_selected" : ""),
             xscale, 0.0, 0.0, yscale, (int)glist_xtopixels(owner, basex),
             (int)glist_ytopixels(owner, basey)
             );
@@ -833,7 +752,6 @@ int scalar_groupclick(struct _glist *groupcanvas,
         if (pd_class(&y->g_pd) == canvas_class &&
             ((t_glist *)y)->gl_svg)
         {
-//            fprintf(stderr, "fuck dolphins");
             if (hit = scalar_groupclick((t_glist *)y, data, template, sc, ap,
                 owner, xloc, yloc, xpix, ypix,
                 shift, alt, dbl, doit, basex, basey))
@@ -889,25 +807,11 @@ int scalar_doclick(t_word *data, t_template *template, t_scalar *sc,
                 owner, xloc, yloc, xpix, ypix,
                 shift, alt, dbl, doit, basex, basey);
     return hit;
-
-//    for (y = templatecanvas->gl_list; y; y = y->g_next)
-//    {
-        //fprintf(stderr,"looking for template... %f %f %f %f %lx %lx\n",
-        //    basex, basey, xloc, yloc, (t_int)owner, (t_int)data);
-//        t_parentwidgetbehavior *wb = pd_getparentwidget(&y->g_pd);
-//        if (!wb) continue;
-//        if (hit = (*wb->w_parentclickfn)(y, owner,
-//            data, template, sc, ap, basex + xloc, basey + yloc,
-//            xpix, ypix, shift, alt, dbl, doit))
-//        {
-                //fprintf(stderr,"    ...got it %f %f\n",
-                //    basex + xloc, basey + yloc);
-//                return (hit);
-//        }
-//    }
-//    return (0);
 }
 
+/* Unfortunately, nested gops don't yet handle scalar clicks correctly. The
+   nested scalar seems not to receive the click.  However, the enter/leave
+   messages happen just fine since most of their logic is in tcl/tk. */
 static int scalar_click(t_gobj *z, struct _glist *owner,
     int xpix, int ypix, int shift, int alt, int dbl, int doit)
 {
diff --git a/pd/src/g_template.c b/pd/src/g_template.c
index 186509bb07bac0ac2c031e27e1b04a53c1662465..275e151a4aa25f472f85fd1bcc1cfda2a4d990fb 100644
--- a/pd/src/g_template.c
+++ b/pd/src/g_template.c
@@ -2414,10 +2414,9 @@ static void svg_curvedim(t_float p1x, t_float p1y,
     t_float mtx2[3][3];
     int i;
     t_float a = (c2x - 2 * c1x + p1x) - (p2x - 2 * c2x + c1x);
-    t_float        b = 2 * (c1x - p1x) - 2 * (c2x - c1x),
+    t_float b = 2 * (c1x - p1x) - 2 * (c2x - c1x),
             c = p1x - c1x;
-    t_float        syntax_sanity_check = 42,
-            t1 = (a ? ((-b + sqrt(abs(b * b - 4 * a * c))) / 2.0 / a) : 0),
+    t_float t1 = (a ? ((-b + sqrt(abs(b * b - 4 * a * c))) / 2.0 / a) : 0),
             t2 = (a ? ((-b - sqrt(abs(b * b - 4 * a * c))) / 2.0 / a) : 0);
     t_float xy[12];
     xy[0] = p1x; xy[1] = p1y; xy[2] = p2x; xy[3] = p2y;
@@ -2808,8 +2807,8 @@ static void svg_getpathrect(t_svg *x, t_glist *glist,
             yy = *ia;
             break;
         case 'M':
-            mx = *(ia+(x->x_nargs_per_cmd[i] - 2));
-            my = *(ia+(x->x_nargs_per_cmd[i] - 1));
+            mx = *(ia);
+            my = *(ia+1);
         default:
             xx = *(ia+(x->x_nargs_per_cmd[i] - 2));
             yy = *(ia+(x->x_nargs_per_cmd[i] - 1));
@@ -2867,9 +2866,9 @@ static void svg_getpathrect(t_svg *x, t_glist *glist,
         case 'S':
             for (j = 0; j < x->x_nargs_per_cmd[i]; j += 4)
             {
-                    /* hack */
-                    if ((j + 3) >= x->x_nargs_per_cmd[i])
-                        break;
+                /* hack */
+                if ((j + 3) >= x->x_nargs_per_cmd[i])
+                    break;
                 if (pcmd == 'C' || pcmd == 'S')
                 {
                     /* this is wrong... we need a
@@ -2884,12 +2883,12 @@ static void svg_getpathrect(t_svg *x, t_glist *glist,
                     ny = yprev;
                 }
                 svg_curvedim(xprev, yprev,
-                    nx, ny, *ia, *(ia+1), *(ia+2), *(ia+3),
+                    nx, ny, *ia+j, *(ia+j+1), *(ia+j+2), *(ia+j+3),
                     &x1, &y1, &x2, &y2, mtx1);
-                xprev = *(ia+2);
-                yprev = *(ia+3);
-                bx = *ia;
-                by = *(ia + 1);
+                xprev = *(ia+j+2);
+                yprev = *(ia+j+3);
+                bx = *ia+j;
+                by = *(ia + j + 1);
 
                 if (x1 == 0x7fffffff && y1 == 0x7fffffff &&
                 x2 == -0x7fffffff && y2 == -0x7fffffff)
@@ -2917,13 +2916,13 @@ static void svg_getpathrect(t_svg *x, t_glist *glist,
                     qxprev = xprev;
                     qyprev = yprev;
                 }
-                t_float cx1 = qxprev, cy1 = qyprev, cx2 = *ia, cy2 = *(ia+1),
-                    cx, cy;
+                t_float cx1 = qxprev, cy1 = qyprev, cx2 = *ia+j,
+                    cy2 = *(ia+j+1), cx, cy;
                 svg_q2c(xprev, yprev, &cx1, &cy1, &cx2, &cy2, &cx, &cy);
                 svg_curvedim(xprev, yprev, cx1, cy1, cx2, cy2, cx, cy,
                     &x1, &y1, &x2, &y2, mtx1);
-                xprev = *ia;
-                yprev = *(ia+1);
+                xprev = *ia+j;
+                yprev = *(ia+j+1);
 
                 if (x1 == 0x7fffffff && y1 == 0x7fffffff &&
                 x2 == -0x7fffffff && y2 == -0x7fffffff)
@@ -2941,13 +2940,13 @@ static void svg_getpathrect(t_svg *x, t_glist *glist,
                     /* hack */
                     if ((j + 3) >= x->x_nargs_per_cmd[i])
                         break;
-                t_float cx1 = *ia, cy1 = *(ia+1),
-                    cx2 = *(ia+2), cy2 = *(ia+3), cx, cy;
+                t_float cx1 = *ia+j, cy1 = *(ia+j+1),
+                    cx2 = *(ia+j+2), cy2 = *(ia+j+3), cx, cy;
                 svg_q2c(xprev, yprev, &cx1, &cy1, &cx2, &cy2, &cx, &cy);
                 svg_curvedim(xprev, yprev, cx1, cy1, cx2, cy2, cx, cy,
                     &x1, &y1, &x2, &y2, mtx1);
-                xprev = *(ia+2);
-                yprev = *(ia+3);
+                xprev = *(ia+j+2);
+                yprev = *(ia+j+3);
 
                 if (x1 == 0x7fffffff && y1 == 0x7fffffff &&
                     x2 == -0x7fffffff && y2 == -0x7fffffff)
@@ -2964,12 +2963,12 @@ static void svg_getpathrect(t_svg *x, t_glist *glist,
                 /* hack */
                 if ((j + 5) >= x->x_nargs_per_cmd[i])
                     break;
-                svg_curvedim(xprev, yprev, *ia, *(ia+1), *(ia+2), *(ia+3),
-                    *(ia+4), *(ia+5), &x1, &y1, &x2, &y2, mtx1);
-                xprev = *(ia+4);
-                yprev = *(ia+5);
-                bx = *(ia+2);
-                by = *(ia+3);
+                svg_curvedim(xprev, yprev, *(ia+j), *(ia+j+1), *(ia+j+2),
+                    *(ia+j+3), *(ia+j+4), *(ia+j+5), &x1, &y1, &x2, &y2, mtx1);
+                xprev = *(ia+j+4);
+                yprev = *(ia+j+5);
+                bx = *(ia+j+2);
+                by = *(ia+j+3);
 
                 if (x1 == 0x7fffffff && y1 == 0x7fffffff &&
                     x2 == -0x7fffffff && y2 == -0x7fffffff)
@@ -2983,25 +2982,25 @@ static void svg_getpathrect(t_svg *x, t_glist *glist,
         case 'V':
             for (j = 0; j < x->x_nargs_per_cmd[i]; j++)
             {
-                mset(mtx2, xprev, *ia, 0, 0, 0, 0);
+                mset(mtx2, xprev, *ia+j, 0, 0, 0, 0);
                 mtx2[2][0] = 1;
                 mmult(mtx1, mtx2, mtx2);
                 tmpy = mtx2[1][0];
                 finaly1 = tmpy < finaly1 ? tmpy : finaly1;
                 finaly2 = tmpy > finaly2 ? tmpy : finaly2;
-                yprev = *ia;
+                yprev = *ia+j;
             }
             break;
         case 'H':
             for (j = 0; j < x->x_nargs_per_cmd[i]; j++)
             {
-                mset(mtx2, *ia, yprev, 0, 0, 0, 0);
+                mset(mtx2, *ia+j, yprev, 0, 0, 0, 0);
                 mtx2[2][0] = 1;
                 mmult(mtx1, mtx2, mtx2);
                 tmpx = mtx2[0][0];  
                 finalx1 = tmpx < finalx1 ? tmpx : finalx1;
                 finalx2 = tmpx > finalx2 ? tmpx : finalx2;
-                xprev = *ia;
+                xprev = *ia+j;
             }
             break;
         default:
@@ -5122,14 +5121,16 @@ static void plot_vis(t_gobj *z, t_glist *glist, t_glist *parentglist,
                     fielddesc_cvttocoord(yfielddesc, yval);
                 for (y = elemtemplatecanvas->gl_list; y; y = y->g_next)
                 {
+                    /* We're setting up a special group that will get set as
+                       the parent by array elements */
+                    sys_vgui(".x%lx.c create group -tags {.scelem%lx.%lx} "
+                             "-parent {.dgroup%lx.%lx}\n",
+                        glist_getcanvas(glist), elemtemplatecanvas,
+                        (t_word *)(elem + elemsize * i),
+                        x->x_canvas, data);
                     if (pd_class(&y->g_pd) == canvas_class &&
                         ((t_glist *)y)->gl_svg)
                     {
-                        sys_vgui(".x%lx.c create group -tags {.scelem%lx.%lx} "
-                                 "-parent {.dgroup%lx.%lx}\n",
-                            glist_getcanvas(glist), elemtemplatecanvas,
-                            (t_word *)(elem + elemsize * i),
-                            x->x_canvas, data);
                         plot_groupvis(sc, glist,
                             (t_word *)(elem + elemsize * i),
                         template, (t_glist *)y, 
diff --git a/pd/src/m_class.c b/pd/src/m_class.c
index cb66d85f6f303620d952767724f30e38cbf64f39..6d4a8f9c04f2790de8091e182d80d3644549e3ea 100644
--- a/pd/src/m_class.c
+++ b/pd/src/m_class.c
@@ -184,6 +184,27 @@ void classtable_register(t_class *c)
     ct = t;
 }
 
+int classtable_size(void) {
+    t_classtable *t;
+    int i;
+    for(t = ct, i = 0; t; t = t->ct_next)
+        i++;
+    return i;
+}
+
+void classtable_tovec(int size, t_atom *vec)
+{
+    t_classtable *t;
+    int i;
+    for(t = ct, i = 0; t && i < size; t = t->ct_next, i++)
+        if (!t->ct_class->c_name)
+        {
+            SETSYMBOL(vec+i, gensym("anonymous-class"));
+        }
+        else
+            SETSYMBOL(vec+i, t->ct_class->c_name);
+}
+
 // todo-- make accessors so m_imp.h isn't needed by x_interface.c
 
 t_class *classtable_findbyname(t_symbol *s)
diff --git a/pd/src/m_pd.h b/pd/src/m_pd.h
index 369a12a5b68892e7161f8c384c2c00663ded8174..2311a58a9e4ea6c2057cdcd268a0d935199e7622 100644
--- a/pd/src/m_pd.h
+++ b/pd/src/m_pd.h
@@ -466,6 +466,8 @@ EXTERN void class_set_extern_dir(t_symbol *s);
 
          /* classtable functions */
 EXTERN t_class *classtable_findbyname(t_symbol *s);
+EXTERN int classtable_size(void);
+EXTERN void classtable_tovec(int size, t_atom *vec);
 
          /* prototype for functions to save Pd's to a binbuf */
 typedef void (*t_savefn)(t_gobj *x, t_binbuf *b);
diff --git a/pd/src/pd.tk b/pd/src/pd.tk
index 83272d2a74c44c2bd9ea84648a9b719da6f7d14f..95535c28b3cb43dd58e2fe886ceeddb5791a8578 100644
--- a/pd/src/pd.tk
+++ b/pd/src/pd.tk
@@ -9057,6 +9057,17 @@ proc pdtk_canvas_update_sticky_tip {w} {
 		}
 	#}
 }
+    # move normal selected items and add (dx, dy) to selected scalars' matrices
+proc pdtk_canvas_displace_withtag {w dx dy} {
+    $w move selected $dx $dy
+    foreach item [$w find withtag scalar_selected] {
+        set matrix [lindex [$w itemconfigure $item -matrix] 4]
+        set newx [expr {[lindex $matrix 2 0] + $dx}]
+        set newy [expr {[lindex $matrix 2 1] + $dy}]
+        set matrix [lreplace $matrix 2 2 [list $newx $newy]]
+        $w itemconfigure $item -matrix $matrix
+    }
+}
 
 # move activewidth to toggle on editmode?
 proc pdtk_canvas_leaveitem {w} {
diff --git a/pd/src/x_interface.c b/pd/src/x_interface.c
index d1411f4094a20a934cd4ca7d413f3aacec1366b6..85a4fd954becb35fe79b396cf861dbe27c7f5a0d 100644
--- a/pd/src/x_interface.c
+++ b/pd/src/x_interface.c
@@ -456,14 +456,14 @@ void pdinfo_dir(t_pdinfo *x, t_symbol *s, int argc, t_atom *argv)
     info_out((t_text *)x, s, 1, at);
 }
 
-void pdinfo_dsp(t_pdinfo *x, t_symbol *s, int argc, t_atom *arg)
+void pdinfo_dsp(t_pdinfo *x, t_symbol *s, int argc, t_atom *argv)
 {
     t_atom at[1];
     SETFLOAT(at, (t_float)canvas_dspstate);
     info_out((t_text *)x, s, 1, at);
 }
 
-void pdinfo_audio_api(t_pdinfo *x, t_symbol *s, int argc, t_atom *arg)
+void pdinfo_audio_api(t_pdinfo *x, t_symbol *s, int argc, t_atom *argv)
 {
     t_atom at[1];
     t_symbol *api = getapiname(sys_audioapi);
@@ -471,12 +471,20 @@ void pdinfo_audio_api(t_pdinfo *x, t_symbol *s, int argc, t_atom *arg)
     info_out((t_text *)x, s, 1, at);
 }
 
+void pdinfo_classtable(t_pdinfo *x, t_symbol *s, int argc, t_atom *argv)
+{
+    int size = classtable_size();
+    t_atom at[size];
+    classtable_tovec(size, at);
+    info_out((t_text *)x, s, size, at);
+}
+
 void pdinfo_audioin(t_pdinfo *x, t_symbol *s, int argc, t_atom *arg)
 {
 //        char i
 }
 
-void pdinfo_audio_api_list_raw(t_pdinfo *x, t_symbol *s, int argc, t_atom *arg)
+void pdinfo_audio_api_list_raw(t_pdinfo *x, t_symbol *s, int argc, t_atom *argv)
 {
     t_atom at[7];
     int i;
@@ -485,7 +493,7 @@ void pdinfo_audio_api_list_raw(t_pdinfo *x, t_symbol *s, int argc, t_atom *arg)
     info_out((t_text *)x, s, i, at);
 }
 
-void pdinfo_audio_apilist(t_pdinfo *x, t_symbol *s, int argc, t_atom *arg)
+void pdinfo_audio_apilist(t_pdinfo *x, t_symbol *s, int argc, t_atom *argv)
 {
     t_atom at[8];
     int n = 0;
@@ -553,10 +561,10 @@ void pdinfo_audio_listdevs(t_pdinfo *x, t_symbol *s, int argc, t_atom *argv)
     }
 }
 
-void pdinfo_audio_dev(t_pdinfo *x, t_symbol *s, int argc, t_atom *arg)
+void pdinfo_audio_dev(t_pdinfo *x, t_symbol *s, int argc, t_atom *argv)
 {
     int devno;
-    if (argc) devno = (int)atom_getfloatarg(0, argc, arg);
+    if (argc) devno = (int)atom_getfloatarg(0, argc, argv);
     else devno = 0;
     int naudioindev, audioindev[MAXAUDIOINDEV], chindev[MAXAUDIOINDEV];
     int naudiooutdev, audiooutdev[MAXAUDIOOUTDEV], choutdev[MAXAUDIOOUTDEV];
@@ -579,7 +587,7 @@ void pdinfo_audio_dev(t_pdinfo *x, t_symbol *s, int argc, t_atom *arg)
         info_out((t_text *)x, s, 0, 0);
 }
 
-void pdinfo_midi_api(t_pdinfo *x, t_symbol *s, int argc, t_atom *arg)
+void pdinfo_midi_api(t_pdinfo *x, t_symbol *s, int argc, t_atom *argv)
 {
     t_atom at[1];
     t_symbol *api, *def = gensym("DEFAULT");
@@ -591,7 +599,7 @@ void pdinfo_midi_api(t_pdinfo *x, t_symbol *s, int argc, t_atom *arg)
     info_out((t_text *)x, s, 1, at);
 }
 
-void pdinfo_midi_apilist(t_pdinfo *x, t_symbol *s, int argc, t_atom *arg)
+void pdinfo_midi_apilist(t_pdinfo *x, t_symbol *s, int argc, t_atom *argv)
 {
     t_atom at[8];
     int n = 0;
@@ -629,13 +637,13 @@ void pdinfo_midi_listdevs(t_pdinfo *x, t_symbol *s, int argc, t_atom *argv)
     }
 }
 
-void pdinfo_midi_dev(t_pdinfo *x, t_symbol *s, int argc, t_atom *arg)
+void pdinfo_midi_dev(t_pdinfo *x, t_symbol *s, int argc, t_atom *argv)
 {
     int devno, nmidiindev, midiindev[MAXMIDIINDEV],
         nmidioutdev, midioutdev[MAXMIDIOUTDEV];
     int *dev, *chan, ndev;
     t_atom at[4];
-    if (argc) devno = (int)atom_getfloatarg(0, argc, arg);
+    if (argc) devno = (int)atom_getfloatarg(0, argc, argv);
     else devno = 0;
     sys_get_midi_params(&nmidiindev, midiindev, &nmidioutdev, midioutdev);
     if (s == gensym("midi-indev"))
@@ -652,10 +660,10 @@ void pdinfo_midi_dev(t_pdinfo *x, t_symbol *s, int argc, t_atom *arg)
         info_out((t_text *)x, s, 0, 0);
 }
 
-void pdinfo_audio_outdev(t_pdinfo *x, t_symbol *s, int argc, t_atom *arg)
+void pdinfo_audio_outdev(t_pdinfo *x, t_symbol *s, int argc, t_atom *argv)
 {
     int devno;
-    if (argc) devno = (int)atom_getfloatarg(0, argc, arg);
+    if (argc) devno = (int)atom_getfloatarg(0, argc, argv);
     else devno = 0;
     int naudioindev, audioindev[MAXAUDIOINDEV], chindev[MAXAUDIOINDEV];
     int naudiooutdev, audiooutdev[MAXAUDIOOUTDEV], choutdev[MAXAUDIOOUTDEV];
@@ -673,14 +681,14 @@ void pdinfo_audio_outdev(t_pdinfo *x, t_symbol *s, int argc, t_atom *arg)
         info_out((t_text *)x, s, 0, 0);
 }
 
-void pdinfo_audio_inchannels(t_pdinfo *x, t_symbol *s, int argc, t_atom *arg)
+void pdinfo_audio_inchannels(t_pdinfo *x, t_symbol *s, int argc, t_atom *argv)
 {
     t_atom at[1];
     SETFLOAT(at, (t_float)sys_get_inchannels());
     info_out((t_text *)x, s, 1, at);
 }
 
-void pdinfo_audio_outchannels(t_pdinfo *x, t_symbol *s, int argc, t_atom *arg)
+void pdinfo_audio_outchannels(t_pdinfo *x, t_symbol *s, int argc, t_atom *argv)
 {
     t_atom at[1];
     SETFLOAT(at, (t_float)sys_get_outchannels());
@@ -688,21 +696,21 @@ void pdinfo_audio_outchannels(t_pdinfo *x, t_symbol *s, int argc, t_atom *arg)
 }
 
 
-void pdinfo_audio_samplerate(t_pdinfo *x, t_symbol *s, int argc, t_atom *arg)
+void pdinfo_audio_samplerate(t_pdinfo *x, t_symbol *s, int argc, t_atom *argv)
 {
     t_atom at[1];
     SETFLOAT(at, (t_float)sys_getsr());
     info_out((t_text *)x, s, 1, at);
 }
 
-void pdinfo_audio_blocksize(t_pdinfo *x, t_symbol *s, int argc, t_atom *arg)
+void pdinfo_audio_blocksize(t_pdinfo *x, t_symbol *s, int argc, t_atom *argv)
 {
     t_atom at[1];
     SETFLOAT(at, (t_float)sys_getblksize());
     info_out((t_text *)x, s, 1, at);
 }
 
-void pdinfo_version(t_pdinfo *x, t_symbol *s, int argc, t_atom *arg)
+void pdinfo_version(t_pdinfo *x, t_symbol *s, int argc, t_atom *argv)
 {
     int major=0, minor=0, bugfix=0;
     sys_getversion(&major, &minor, &bugfix);
@@ -713,7 +721,7 @@ void pdinfo_version(t_pdinfo *x, t_symbol *s, int argc, t_atom *arg)
     info_out((t_text *)x, s, 3, at);
 }
 
-void pdinfo_pi(t_pdinfo *x, t_symbol *s, int argc, t_atom *arg)
+void pdinfo_pi(t_pdinfo *x, t_symbol *s, int argc, t_atom *argv)
 {
     t_atom at[1];
     const t_float Pi = 3.141592653589793;
@@ -762,6 +770,10 @@ void pdinfo_setup(void)
         gensym("audio-outdevlist"), A_GIMME, 0);
     class_addmethod(pdinfo_class, (t_method)pdinfo_audio_blocksize,
         gensym("blocksize"), A_GIMME, 0);
+    /* this needs a better name-- the user doesn't have to know the
+       name used in the implementation */
+    class_addmethod(pdinfo_class, (t_method)pdinfo_classtable,
+        gensym("classtable"), A_GIMME, 0);
     class_addmethod(pdinfo_class, (t_method)pdinfo_dir,
         gensym("dir"), A_GIMME, 0);
     class_addmethod(pdinfo_class, (t_method)pdinfo_dsp,
@@ -1121,7 +1133,7 @@ void *objectinfo_new(t_floatarg f)
     t_objectinfo *x = (t_objectinfo *)pd_new(objectinfo_class);
     t_glist *glist = (t_glist *)canvas_getcurrent();
     x->x_canvas = (t_canvas*)glist_getcanvas(glist);
-    x->x_index = f;
+    x->x_depth = f;
     floatinlet_new(&x->x_obj, &x->x_index);
     floatinlet_new(&x->x_obj, &x->x_depth);
     outlet_new(&x->x_obj, &s_anything);