diff --git a/.gitignore b/.gitignore
index 17d938a542fe8a4dfe8f91043ae978a56b65098d..c8c9f229a88943d1b0659dc63cc7cf88e3eb54d6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -32,3 +32,4 @@ packages/linux_make/build/
 pd/src/makefile
 l2ork_addons/cwiid/defs.mak
 CVS
+.DS_Store
diff --git a/README.md b/README.md
index f23d8d0469caeff1ccb27dabb3c98144b3d51305..7cb8d729e295c5b5130e2086501fdce84f1b0cdc 100644
--- a/README.md
+++ b/README.md
@@ -231,7 +231,16 @@ that case, but I haven't tested doing it like that. Sorry. Get a better OS...)
 
 4. Install the dependencies *(5-10 minutes)*  
    Once the shell opens, we need to install the dependencies for building
-   Purr Data. Issue the following command:
+   Purr Data. First we need to update all the packages:
+
+        pacman -Syu
+
+   After closing and reopening the shell as prompted, you may need to do it
+   again:
+   
+        pacman -Syu
+   
+   Now everything should be up-to-date. Issue the following command:
 
         pacman -S autoconf automake git libtool \
           make mingw-w64-i686-dlfcn mingw-w64-i686-fftw \
diff --git a/externals/miXed/cyclone/sickle/Scope.c b/externals/miXed/cyclone/sickle/Scope.c
index b5c6aa5e91ea8db22bd2fa5cb0df4ee54cef621a..33b8b76f2298d7776a021366a11f8a8cebe963e4 100644
--- a/externals/miXed/cyclone/sickle/Scope.c
+++ b/externals/miXed/cyclone/sickle/Scope.c
@@ -106,9 +106,8 @@ typedef struct _scope
     int        x_frozen;
     t_clock   *x_clock;
     t_pd      *x_handle;
-
-	int			scale_offset_x;
-	int			scale_offset_y;
+    int	      scale_offset_x;
+    int       scale_offset_y;
 } t_scope;
 
 typedef struct _scopehandle
@@ -118,6 +117,9 @@ typedef struct _scopehandle
     t_symbol  *h_bindsym;
     char       h_pathname[64];
     char       h_outlinetag[64];
+    int        h_adjust_x;
+    int        h_adjust_y;
+    int        h_constrain;
     int        h_dragon;
     int        h_dragx;
     int        h_dragy;
@@ -992,49 +994,16 @@ static void scope_tick(t_scope *x)
     scope_clear(x, 1);
 }
 
-static void scopehandle__clickhook(t_scopehandle *sh, t_floatarg f, t_floatarg xxx, t_floatarg yyy)
+extern void canvas_apply_setundo(t_canvas *x, t_gobj *y);
+static void scopehandle__clickhook(t_scopehandle *sh, t_floatarg f,
+    t_floatarg xxx, t_floatarg yyy)
 {
-
     t_scope *x = sh->h_master;
-
-    //if (xxx) x->scale_offset_x = xxx;
-    //if (yyy) x->scale_offset_y = yyy;
-
-    //int newstate = (int)f;
-    //if (sh->h_dragon && newstate == 0)
-    //{
-    //    /* done dragging */
-    //    t_canvas *cv;
-    //    if (sh->h_dragx || sh->h_dragy)
-    //    {
-    //        x->x_width = x->x_width + sh->h_dragx - x->scale_offset_x;
-    //        x->x_height = x->x_height + sh->h_dragy - x->scale_offset_y;
-    //    }
-    //    if (cv = scope_isvisible(x))
-    //    {
-    //        sys_vgui(".x%x.c delete %s\n", cv, sh->h_outlinetag);
-    //        scope_revis(x, cv);
-    //        sys_vgui("destroy %s\n", sh->h_pathname);
-    //        scope_select((t_gobj *)x, x->x_glist, 1);
-    //        canvas_fixlinesfor(x->x_glist, (t_text *)x);  /* 2nd inlet */
-    //    }
-    //}
-    //else if (!sh->h_dragon && newstate)
-    //{
-    //    /* dragging */
-    //    t_canvas *cv;
-    //    if (cv = scope_isvisible(x))
-    //    {
-    //        int x1, y1, x2, y2;
-    //        scope_getrect((t_gobj *)x, x->x_glist, &x1, &y1, &x2, &y2);
-    //        sys_vgui("lower %s\n", sh->h_pathname);
-    //        sys_vgui(".x%x.c create rectangle %d %d %d %d\
-    //            -outline $select_color -width %f -tags %s\n",
-    //        cv, x1, y1, x2, y2, SCOPE_SELBDWIDTH, sh->h_outlinetag);
-    //    }
-    //    sh->h_dragx = 0;
-    //    sh->h_dragy = 0;
-    //}
+    /* Use constrained dragging. See g_canvas.c clickhook */
+    sh->h_constrain = (int)f;
+    sh->h_adjust_x = xxx - (((t_object *)x)->te_xpix + x->x_width);
+    sh->h_adjust_y = yyy - (((t_object *)x)->te_ypix + x->x_height);
+    canvas_apply_setundo(x->x_glist, (t_gobj *)x);
     sh->h_dragon = f;
 }
 
@@ -1042,10 +1011,13 @@ static void scopehandle__motionhook(t_scopehandle *sh,
 				    t_floatarg mouse_x, t_floatarg mouse_y)
 {
     t_scope *x = (t_scope *)(sh->h_master);
-    int x1, y1, x2, y2, width, height;
-    scope_getrect((t_gobj *)x, x->x_glist, &x1, &y1, &x2, &y2);
-    width = mouse_x - x1;
-    height = mouse_y - y1;
+    int width = (sh->h_constrain == CURSOR_EDITMODE_RESIZE_Y) ?
+        x->x_width :
+        (int)mouse_x - text_xpix((t_text *)x, x->x_glist) - sh->h_adjust_x;
+    int height = (sh->h_constrain == CURSOR_EDITMODE_RESIZE_X) ?
+        x->x_height :
+        (int)mouse_y - text_ypix((t_text *)x, x->x_glist) - sh->h_adjust_y;
+
     x->x_width =  width < SCOPE_MINWIDTH ? SCOPE_MINWIDTH : width;
     x->x_height = height < SCOPE_MINHEIGHT ? SCOPE_MINHEIGHT : height;
 
@@ -1058,25 +1030,6 @@ static void scopehandle__motionhook(t_scopehandle *sh,
         scope_vis((t_gobj *)x, x->x_glist, 0);
         scope_vis((t_gobj *)x, x->x_glist, 1);
     }
-    //if (sh->h_dragon)
-    //{
-    //    t_scope *x = sh->h_master;
-    //    int dx = (int)f1, dy = (int)f2;
-    //    int x1, y1, x2, y2, newx, newy;
-    //    scope_getrect((t_gobj *)x, x->x_glist, &x1, &y1, &x2, &y2);
-    //    newx = x2 - x->scale_offset_x + dx;
-    //    newy = y2 - x->scale_offset_y + dy;
-
-    //    if (newx > x1 + SCOPE_MINWIDTH && newy > y1 + SCOPE_MINHEIGHT)
-    //    {
-    //        t_canvas *cv;
-    //        if (cv = scope_isvisible(x))
-    //            sys_vgui(".x%x.c coords %s %d %d %d %d\n",
-    //                cv, sh->h_outlinetag, x1, y1, newx, newy);
-    //        sh->h_dragx = dx;
-    //        sh->h_dragy = dy;
-    //    }
-    //}
 }
 
 /* wrapper method for forwarding "scopehandle" data */
@@ -1157,8 +1110,8 @@ static void *scope_new(t_symbol *s, int ac, t_atom *av)
     sprintf(sh->h_outlinetag, "h%x", (int)sh);
     sh->h_dragon = 0;
 
-	x->scale_offset_x = 0;
-	x->scale_offset_y = 0;
+    x->scale_offset_x = 0;
+    x->scale_offset_y = 0;
 
     return (x);
 }
diff --git a/externals/unauthorized/grid.c b/externals/unauthorized/grid.c
index da3c56ab1ea165cef34ecd6829ef457ef494156e..557436d7b482190d06ce4b57c5167e09907f2dfb 100644
--- a/externals/unauthorized/grid.c
+++ b/externals/unauthorized/grid.c
@@ -696,39 +696,43 @@ static void grid_bang(t_grid *x) {
 static void grid__clickhook(t_scalehandle *sh, int newstate)
 {
     t_grid *x = (t_grid *)(sh->h_master);
-    if (newstate)
-    {
-        canvas_apply_setundo(x->x_glist, (t_gobj *)x);
-    }
+    /* Use constrained dragging-- see g_canvas.c clickhook */
+    sh->h_constrain = newstate;
+    sh->h_adjust_x = sh->h_offset_x -
+        (((t_object *)x)->te_xpix + x->x_width);
+    sh->h_adjust_y = sh->h_offset_y -
+        (((t_object *)x)->te_ypix + x->x_height);
+    canvas_apply_setundo(x->x_glist, (t_gobj *)x);
     sh->h_dragon = newstate;
 }
 
 static void grid__motionhook(t_scalehandle *sh,
     t_floatarg mouse_x, t_floatarg mouse_y)
 {
-    if (sh->h_scale)
+    t_grid *x = (t_grid *)(sh->h_master);
+    int width = (sh->h_constrain == CURSOR_EDITMODE_RESIZE_Y) ?
+        x->x_width :
+        (int)mouse_x - text_xpix(&x->x_obj, x->x_glist) - sh->h_adjust_x;
+    int height = (sh->h_constrain == CURSOR_EDITMODE_RESIZE_X) ?
+        x->x_height :
+        (int)mouse_y - text_ypix(&x->x_obj, x->x_glist) - sh->h_adjust_y;
+    int minw = MIN_GRID_WIDTH,
+        minh = MIN_GRID_HEIGHT;
+    x->x_width = width < minw ? minw : width;
+    x->x_height = height < minh ? minh : height;
+    if (glist_isvisible(x->x_glist))
     {
-        t_grid *x = (t_grid *)(sh->h_master);
-        int width = mouse_x - text_xpix(&x->x_obj, x->x_glist),
-            height = mouse_y - text_ypix(&x->x_obj, x->x_glist),
-            minw = MIN_GRID_WIDTH,
-            minh = MIN_GRID_HEIGHT;
-        x->x_width = width < minw ? minw : width;
-        x->x_height = height < minh ? minh : height;
-        if (glist_isvisible(x->x_glist))
-        {
-            grid_draw_configure(x, x->x_glist);
-            //scalehandle_unclick_scale(sh);
-        }
+        grid_draw_configure(x, x->x_glist);
+        //scalehandle_unclick_scale(sh);
+    }
 
-        int properties = gfxstub_haveproperties((void *)x);
-        if (properties)
-        {
-            int new_w = x->x_width + sh->h_dragx;
-            int new_h = x->x_height + sh->h_dragy;
-            properties_set_field_int(properties,"width",new_w);
-            properties_set_field_int(properties,"height",new_h);
-        }
+    int properties = gfxstub_haveproperties((void *)x);
+    if (properties)
+    {
+        int new_w = x->x_width + sh->h_dragx;
+        int new_h = x->x_height + sh->h_dragy;
+        properties_set_field_int(properties,"width",new_w);
+        properties_set_field_int(properties,"height",new_h);
     }
 }
 
@@ -737,8 +741,9 @@ static void grid_click_for_resizing(t_grid *x, t_floatarg f,
     t_floatarg xxx, t_floatarg yyy)
 {
     t_scalehandle *sh = (t_scalehandle *)x->x_handle;
+    sh->h_offset_x = (int)xxx;
+    sh->h_offset_y = (int)yyy;
     grid__clickhook(sh, f);
-//    grid__clickhook(sh, f, xxx, yyy);
 }
 
 /* another wrapper for forwarding "scalehandle" motion data */
diff --git a/packages/darwin_app/org.puredata.pd-l2ork.default.plist b/packages/darwin_app/org.puredata.pd-l2ork.default.plist
index d9c49f88ada8e39ebffce67005a819770b808d5e..12451f57f21265f8af7973bd447c20433464ed26 100644
--- a/packages/darwin_app/org.puredata.pd-l2ork.default.plist
+++ b/packages/darwin_app/org.puredata.pd-l2ork.default.plist
@@ -5,7 +5,7 @@
 	<key>defeatrt</key>
 	<string>0</string>
 	<key>flags</key>
-	<string>-helppath ~/Library/Pd-l2ork -helppath /Library/Pd-l2ork</string>
+	<string>-font-size 12 -helppath ~/Library/Pd-l2ork -helppath /Library/Pd-l2ork</string>
 	<key>loadlib1</key>
 	<string>libdir</string>
 	<key>loadlib2</key>
diff --git a/packages/darwin_app/org.puredata.pd-l2ork.light.plist b/packages/darwin_app/org.puredata.pd-l2ork.light.plist
index 3844a60a0d825c980f8e55b6e1a4a1f647d631dc..e56c38d220f3f2931d94d16d3078abbee4d325f7 100644
--- a/packages/darwin_app/org.puredata.pd-l2ork.light.plist
+++ b/packages/darwin_app/org.puredata.pd-l2ork.light.plist
@@ -5,7 +5,7 @@
 	<key>defeatrt</key>
 	<string>0</string>
 	<key>flags</key>
-	<string>-helppath ~/Library/Pd-l2ork -helppath /Library/Pd-l2ork</string>
+	<string>-font-size 12 -helppath ~/Library/Pd-l2ork -helppath /Library/Pd-l2ork</string>
 	<key>loadlib1</key>
 	<string>libdir</string>
 	<key>loadlib2</key>
diff --git a/packages/linux_make/default.settings b/packages/linux_make/default.settings
index f5077065b559e8d2d435b2d8b968cdfa26fc1a8f..772d49e7647855c5164180cd400d741fb6fb19e5 100644
--- a/packages/linux_make/default.settings
+++ b/packages/linux_make/default.settings
@@ -1,6 +1,7 @@
 standardpath: 1
 verbose: 0
 defeatrt: 0
+flags: -font-size 12
 loadlib1: libdir
 loadlib2: Gem
 loadlib3: cyclone
diff --git a/packages/linux_make/light.settings b/packages/linux_make/light.settings
index 26c84749d09b7b4e0382238f5135a055add7ae06..d2b1add6340a7a7d175c40268d483506823e4bce 100644
--- a/packages/linux_make/light.settings
+++ b/packages/linux_make/light.settings
@@ -7,3 +7,4 @@ nloadlib: 2
 path1: ~/pd-l2ork-externals
 path2: /usr/lib/pd-l2ork/extra/pddp
 npath: 2
+flags: -font-size 12
diff --git a/packages/win32_inno/pd-settings-light.reg b/packages/win32_inno/pd-settings-light.reg
index 483722c20e0cb14a89f209360c90294b1ae990c0..b33e4ff2b9395fc4c69fcbc4472983e5dd0e4b46 100644
--- a/packages/win32_inno/pd-settings-light.reg
+++ b/packages/win32_inno/pd-settings-light.reg
@@ -1,7 +1,7 @@
 Windows Registry Editor Version 5.00
 
 [HKEY_LOCAL_MACHINE\SOFTWARE\Pd-extended]
-"flags"=""
+"flags"="-font-size 12"
 "loadlib1"="libdir"
 "loadlib2"="pddp"
 "nloadlib"=2
diff --git a/packages/win32_inno/pd-settings.reg b/packages/win32_inno/pd-settings.reg
index fa5b9a768f23c36187f46a2c3773add5603187de..d4383bf893eab8518123c018c89e06967f01fd45 100755
--- a/packages/win32_inno/pd-settings.reg
+++ b/packages/win32_inno/pd-settings.reg
@@ -1,7 +1,7 @@
 Windows Registry Editor Version 5.00
 
 [HKEY_LOCAL_MACHINE\SOFTWARE\Pd-extended]
-"flags"=""
+"flags"="-font-size 12"
 "loadlib1"="libdir"
 "loadlib2"="Gem"
 "loadlib3"="cyclone"
diff --git a/pd/doc/5.reference/line-help.pd b/pd/doc/5.reference/line-help.pd
index c3e03d1281bf1c42074bf8bff95e7982a01c62dc..55ffc110baf4fa95ca316896473453090c9d0052 100644
--- a/pd/doc/5.reference/line-help.pd
+++ b/pd/doc/5.reference/line-help.pd
@@ -1,9 +1,9 @@
-#N canvas 515 143 555 619 10;
+#N canvas 323 74 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 line 3 12 0 18 -204280
 -1 0;
-#X obj 0 267 cnv 3 550 3 empty \$0-pddp.cnv.inlets inlets 8 12 0 13
+#X obj 0 230 cnv 3 550 3 empty \$0-pddp.cnv.inlets inlets 8 12 0 13
 -228856 -1 0;
 #N canvas 565 371 494 364 META 0;
 #X text 12 125 LIBRARY internal;
@@ -22,11 +22,11 @@ for Pd version 0.42.;
 #X text 12 185 RELEASE_DATE 1997;
 #X text 12 5 KEYWORDS control time;
 #X restore 500 598 pd META;
-#X obj 0 375 cnv 3 550 3 empty \$0-pddp.cnv.outlets outlets 8 12 0
+#X obj 0 418 cnv 3 550 3 empty \$0-pddp.cnv.outlets outlets 8 12 0
 13 -228856 -1 0;
-#X obj 0 439 cnv 3 550 3 empty \$0-pddp.cnv.argument arguments 8 12
+#X obj 0 462 cnv 3 550 3 empty \$0-pddp.cnv.argument arguments 8 12
 0 13 -228856 -1 0;
-#X obj 0 498 cnv 3 550 3 empty \$0-pddp.cnv.more_info more_info 8 12
+#X obj 0 521 cnv 3 550 3 empty \$0-pddp.cnv.more_info more_info 8 12
 0 13 -228856 -1 0;
 #N canvas 297 500 428 230 Related_objects 0;
 #X obj 1 1 cnv 15 425 20 empty \$0-pddp.cnv.subheading empty 3 12 0
@@ -40,28 +40,24 @@ for Pd version 0.42.;
 #X obj 19 182 pddp/helplink iemlib/fade~;
 #X obj 19 122 pddp/helplink maxlib/step;
 #X restore 101 598 pd Related_objects;
-#X obj 78 276 cnv 17 3 63 empty \$0-pddp.cnv.let.0 0 5 9 0 16 -228856
+#X obj 78 239 cnv 17 3 83 empty \$0-pddp.cnv.let.0 0 5 9 0 16 -228856
 -162280 0;
-#X text 98 275 float;
-#X text 98 301 list;
-#X text 98 383 float;
-#X obj 78 384 cnv 17 3 17 empty \$0-pddp.cnv.let.0 0 5 9 0 16 -228856
+#X text 98 238 float;
+#X text 98 269 list;
+#X text 98 426 float;
+#X obj 78 427 cnv 17 3 17 empty \$0-pddp.cnv.let.0 0 5 9 0 16 -228856
 -162280 0;
-#X obj 78 347 cnv 17 3 17 empty \$0-pddp.cnv.let.1 1 5 9 0 16 -228856
+#X obj 78 327 cnv 17 3 17 empty \$0-pddp.cnv.let.1 1 5 9 0 16 -228856
 -162280 0;
-#X text 98 346 float;
+#X text 98 326 float;
 #X obj 509 10 line;
-#X obj 137 180 line;
-#X msg 137 87 1000 1000;
-#X floatatom 137 201 5 0 0 0 - - -, f 5;
-#X text 203 86 ramp up.;
-#X msg 146 110 0 1000;
-#X text 193 110 ramp down.;
-#X msg 158 154 stop;
-#X msg 154 133 42;
-#X text 180 134 jumps to this value immediately.;
-#X text 195 154 stops the current ramp.;
-#N canvas 106 115 428 446 continuous_ramps 0;
+#X obj 137 183 line;
+#X msg 137 50 1000 1000;
+#X floatatom 137 204 5 0 0 0 - - -, f 5;
+#X msg 146 73 0 1000;
+#X msg 158 117 stop;
+#X msg 154 96 42;
+#N canvas 315 171 428 446 continuous_ramps 0;
 #X text 19 98 In other words \, if [line] receives a message specifying
 some new target and time before reaching the previous target \, it
 takes off from its current value.;
@@ -72,181 +68,159 @@ takes off from its current value.;
 #X obj 23 226 line;
 #X msg 23 144 127 5000;
 #X msg 41 165 0 5000;
-#X text 16 271 Due to this unique behavior \, a common construct found
-in Pd patches includes the [pack] object as follows:;
 #X obj 30 392 line;
 #X floatatom 30 413 10 0 0 0 - - -, f 10;
-#X floatatom 30 306 5 0 0 0 - - -, f 5;
+#X floatatom 30 326 5 0 0 0 - - -, f 5;
 #X obj 30 372 pack f 500;
-#X msg 41 325 50;
-#X msg 49 346 2000;
+#X msg 71 325 50;
+#X msg 79 346 2000;
 #X obj 1 1 cnv 15 425 20 empty \$0-pddp.cnv.subheading empty 3 12 0
 14 -204280 -1 0;
 #X text 8 2 [line] Continuous Ramps;
-#X text 21 37 [line] does not schedule its incoming messages. What
-this means is that if you send [line] a new target value mid-way through
+#X text 21 37 If you send [line] a new target value mid-way through
 a ramp \, a new ramp is immediately created to the new target value
 starting from the "current" value.;
+#X text 81 185 <-- will jump immediately to 42;
+#X text 16 271 Due to this unique behavior \, a common construct found
+in Pd patches includes the [pack] object as below:;
 #X connect 2 0 4 0;
 #X connect 3 0 4 0;
 #X connect 4 0 1 0;
 #X connect 5 0 4 0;
 #X connect 6 0 4 0;
-#X connect 8 0 9 0;
-#X connect 10 0 11 0;
-#X connect 11 0 8 0;
-#X connect 12 0 11 0;
-#X connect 13 0 11 0;
-#X restore 101 551 pd continuous_ramps;
-#N canvas 107 25 428 516 grain_rate_and_creation_arguments 0;
-#X text 21 39 The "grain rate" of [line] is the rate at which it will
-output its values. The faster the grain is \, the smoother the ramp
-will appear. While slower grain rates will produce a more disjunct
-ramp. Of course this setting will alter the amount of CPU that the
-[line] object will require. Higher rates require more computation.
-;
-#X text 22 145 This means simply that [line] will output a new value
-between its current value and your new value once every 20 milliseconds.
-;
-#X text 22 126 The default grain rate is 20 milliseconds.;
-#X text 19 193 [line]'s optional creation arguments can be used to
-reset the grain rate. However \, the grain rate cannot be reset dynamically.
-;
-#X text 21 248 First creation argument is the initial value: the starting
-point of the first ramp.;
-#X text 20 287 The second creation argument sets the grain rate. Observe
-the following two examples to see how the grain rate effects the output.
-;
-#X obj 33 403 line 0 5;
-#X obj 36 436 hsl 128 15 0 127 0 0 empty empty empty -2 -6 0 8 -262144
--1 -1 0 1;
-#X msg 136 332 127;
-#X msg 168 340 0;
-#X obj 135 368 pack f 5000;
-#X obj 203 436 hsl 128 15 0 127 0 0 empty empty empty -2 -6 0 8 -262144
--1 -1 0 1;
-#X obj 200 403 line 0 500;
-#X obj 1 1 cnv 15 425 20 empty \$0-pddp.cnv.subheading empty 3 12 0
-14 -204280 -1 0;
-#X text 8 2 [line] Grain Rate;
-#X text 31 469 You can check the default grain rate in the subpatch
-below:;
-#N canvas 398 136 429 326 default_grain_rate 0;
-#X obj 22 87 line;
-#X obj 22 226 timer;
-#X obj 22 111 t b b;
-#X obj 49 137 spigot;
-#X obj 101 169 0;
-#X obj 22 193 spigot;
-#X obj 22 64 t a b;
-#X obj 65 87 1;
-#X obj 71 226 1;
-#X obj 22 251 print Pd's_default_grain_rate;
-#X msg 22 42 0 200;
-#X text 19 274 Here we assume the grain rate is less than 200 ms. For
-a more comprehensive test we could start with a time value of 1 ms
-and step up until the output ceases to increase.;
-#X obj 0 0 cnv 15 552 40 empty \$0-pddp.cnv.header empty 3 12 0 14
--204280 -1 0;
-#X text 7 1 [line] Pd's Default Grain Rate;
-#X text 60 42 Click here to output pd's default grain rate to the console
-;
-#X connect 0 0 2 0;
-#X connect 1 0 9 0;
-#X connect 2 0 5 0;
-#X connect 2 1 3 0;
-#X connect 3 0 4 0;
-#X connect 3 0 1 1;
-#X connect 4 0 3 1;
-#X connect 4 0 5 1;
-#X connect 5 0 1 0;
-#X connect 5 0 8 0;
-#X connect 6 0 0 0;
-#X connect 6 1 7 0;
-#X connect 7 0 5 1;
-#X connect 8 0 3 1;
-#X connect 10 0 6 0;
-#X restore 34 491 pd default_grain_rate;
-#X connect 6 0 7 0;
-#X connect 8 0 10 0;
+#X connect 7 0 8 0;
 #X connect 9 0 10 0;
-#X connect 10 0 6 0;
-#X connect 10 0 12 0;
-#X connect 12 0 11 0;
-#X restore 101 529 pd grain_rate_and_creation_arguments;
+#X connect 10 0 7 0;
+#X connect 11 0 10 0;
+#X connect 12 0 10 0;
+#X restore 341 530 pd continuous_ramps;
 #X text 11 23 ramp generator;
-#X text 98 325 stop;
-#X text 168 325 - stop the current ramp.;
-#X text 167 346 - time to reach the target value (in milliseconds).
+#X text 98 299 stop;
+#X text 168 299 - stop the current ramp.;
+#X text 167 326 - time to reach the target value (in milliseconds).
 ;
-#X text 168 275 - target value. This value is stored and used as the
+#X text 168 238 - target value. This value is stored and used as the
 starting value for the next ramp.;
-#X text 168 301 - a (target \, time) pair is distributed between the
+#X text 168 269 - a (target \, time) pair is distributed between the
 two inlets.;
-#X text 168 474 - grain rate in milliseconds (default: 20 ms).;
-#X text 168 383 - [line] outputs a linear ramp \, reaching the target
-value within the time value specified by the right inlet and at a grain
-rate specified by the second creation argument (default 20 ms).;
-#N canvas 106 32 428 493 tips_on_using_line 0;
-#X text 20 39 [line]'s left inlet defines the "target" value. The right
-inlet defines the "time" value. The "target \, time" pair of numbers
-inform [line] to produce a numeric "ramp" from its current value (whatever
-that might be at any given moment) to the new value within the alloted
-time which is defined at the right inlet.;
-#X obj 25 242 line;
-#X msg 59 218 1000;
-#X msg 25 218 1000;
-#X obj 25 175 bng 15 250 50 0 empty empty empty 0 -6 0 8 -262144 -1
+#N canvas 305 114 428 508 tips_on_using_line 0;
+#X obj 42 269 line;
+#X msg 76 238 1000;
+#X msg 42 238 1000;
+#X obj 42 192 bng 15 250 50 0 empty empty empty 0 -6 0 8 -262144 -1
 -1;
-#X obj 25 195 t b b;
-#X floatatom 25 264 5 0 0 0 - - -, f 5;
-#X obj 150 260 line;
-#X msg 150 236 1000;
-#X obj 150 216 bng 15 250 50 0 empty empty empty 0 -6 0 8 -262144 -1
+#X obj 42 212 t b b;
+#X floatatom 42 291 5 0 0 0 - - -, f 5;
+#X obj 150 270 line;
+#X msg 150 238 1000;
+#X obj 150 192 bng 15 250 50 0 empty empty empty 0 -6 0 8 -262144 -1
 -1;
-#X floatatom 150 282 5 0 0 0 - - -, f 5;
-#X text 20 309 If [line] receives a new target value without an accompanying
-"time" \, it simply jumps to the new value...as in the example above
-on the right. In the example above on the left \, you'll see that [line]
-will create a numeric ramp from 0 to 1000 over 1000 milliseconds. You
-should also note that if you click that [bng] again a second time \,
-nothing happens. This is because [line] is already at 1000 \, so sending
-it new instructions to ramp to 1000 is meaningless and has no effect.
-;
-#X text 20 425 Having said all of that \, let's just reiterate that
+#X floatatom 150 292 5 0 0 0 - - -, f 5;
+#X text 20 437 Having said all of that \, let's just reiterate that
 it's important to send a "time" value to [line] before sending it a
 new "target" value...unless of course you WANT it to jump immediately
 to the new target.;
-#X text 149 182 While this does NOT work unless;
-#X text 149 196 you click "500" first.;
-#X msg 184 236 500;
+#X text 179 197 While this does NOT work unless;
+#X text 179 211 you click "500" first.;
+#X msg 184 238 500;
 #X obj 1 1 cnv 15 425 20 empty \$0-pddp.cnv.subheading empty 3 12 0
 14 -204280 -1 0;
 #X text 8 2 [line] Tips;
-#X text 20 118 It is important to realize that [line] stores only the
+#X text 20 125 It is important to realize that [line] stores only the
 current value...it does not remember the defined "time" (duration of
 the ramp). Hence \, the example to the left works:;
-#X connect 1 0 6 0;
-#X connect 2 0 1 1;
-#X connect 3 0 1 0;
-#X connect 4 0 5 0;
-#X connect 5 0 3 0;
-#X connect 5 1 2 0;
-#X connect 7 0 10 0;
+#X text 20 39 [line]'s left inlet defines the "target" value. The 2nd
+inlet defines the "time" value. The "target \, time" pair of numbers
+inform [line] to produce a numeric "ramp" from its current value (whatever
+that might be at any given moment) to the new value within the alloted
+time which is defined at the right inlet.;
+#X text 20 316 If [line] receives a new target value without an accompanying
+"time" \, it simply jumps to the new value as in the example above
+on the right. In the example above on the left \, you'll see that [line]
+will create a numeric ramp from 0 to 1000 over 1000 milliseconds. You
+should also note that if you click that [bng] again a second time \,
+it doesn't ramp from 0 to 1000 again. This is because [line] is already
+at 1000 Sending it new instructions to ramp to 1000 will output a stream
+of values that are all 1000;
+#X connect 0 0 5 0;
+#X connect 1 0 0 1;
+#X connect 2 0 0 0;
+#X connect 3 0 4 0;
+#X connect 4 0 2 0;
+#X connect 4 1 1 0;
+#X connect 6 0 9 0;
+#X connect 7 0 6 0;
 #X connect 8 0 7 0;
-#X connect 9 0 8 0;
-#X connect 15 0 7 1;
-#X restore 101 507 pd tips_on_using_line;
-#X text 80 459 1) float;
-#X text 80 474 2) float;
-#X text 168 459 - initial value \, i.e. \, the starting point of the
+#X connect 13 0 6 1;
+#X restore 101 530 pd tips_on_using_line;
+#X text 80 482 1) float;
+#X text 80 497 2) float;
+#X text 168 482 - initial value \, i.e. \, the starting point of the
 first ramp.;
 #X obj 3 598 pddp/pddplink all_about_help_patches.pd -text Usage Guide
 ;
-#X obj 98 571 pddp/pddplink ../3.audio.examples/C03.zipper.noise.pd
+#X obj 98 574 pddp/pddplink ../3.audio.examples/C03.zipper.noise.pd
 -text doc/3.audio.examples/C03.zipper.noise.pd;
+#X text 180 97 jump to this value immediately.;
+#X text 203 49 ramp up over 1000 ms;
+#X text 193 73 ramp down over 1000 ms;
+#X text 195 117 stop the current ramp.;
+#X obj 78 357 cnv 17 3 57 empty \$0-pddp.cnv.let.1 2 5 9 0 16 -228856
+-162280 0;
+#X text 98 356 float;
+#X text 167 356 - grain size \, or how often messages get sent to the
+outlet during the ramp (defaults to 20 ms). Note: there may be a shorter
+delay before the final message in order to precisely match the total
+ramp duration given from the 2nd inlet.;
+#X text 168 497 - grain size in milliseconds (default: 20 ms).;
+#X text 168 426 - [line] outputs a linear ramp \, reaching the target
+value within the duration specified at the 2nd inlet.;
+#N canvas 370 126 428 471 grain_size_and_creation_arguments 0;
+#X text 22 125 This means simply that [line] will output a new value
+between its current value and your new value once every 20 milliseconds.
+;
+#X text 22 101 The default grain rate is 20 milliseconds.;
+#X obj 135 413 line 0 5;
+#X obj 150 440 hsl 128 15 0 127 0 0 empty empty empty -2 -6 0 8 -262144
+-1 -1 0 1;
+#X msg 136 272 127;
+#X msg 168 272 0;
+#X obj 135 308 pack f 5000;
+#X obj 150 370 hsl 128 15 0 127 0 0 empty empty empty -2 -6 0 8 -262144
+-1 -1 0 1;
+#X obj 150 343 line 0 500;
+#X obj 1 1 cnv 15 425 20 empty \$0-pddp.cnv.subheading empty 3 12 0
+14 -204280 -1 0;
+#X text 8 2 [line] Grain Rate;
+#X text 22 39 The "grain size" of [line] defines how often it will
+output its values. The smaller the grain is \, the smoother the ramp
+will appear. Of course this setting will alter the amount of CPU that
+the [line] object will require.;
+#X text 19 223 Note that if the chosen grain rate doesn't divide evenly
+into the total ramp duration \, the final "grain" will be smaller to
+achieve the precise total ramp time.;
+#X text 19 173 [line]'s optional 2nd creation arguments can be used
+to reset the grain rate. It can also be set dynamically at the right
+inlet.;
+#X connect 2 0 3 0;
+#X connect 4 0 6 0;
+#X connect 5 0 6 0;
+#X connect 6 0 2 0;
+#X connect 6 0 8 0;
+#X connect 8 0 7 0;
+#X restore 101 552 pd grain_size_and_creation_arguments;
+#N canvas 0 0 450 300 grain_size 0;
+#X obj 83 23 loadbang;
+#X msg 83 45 names 20 50 100 250;
+#X obj 83 67 outlet;
+#X connect 0 0 1 0;
+#X connect 1 0 2 0;
+#X restore 200 141 pd grain_size;
+#X dropdown 200 163 6 1 0 0 - - -, f 6;
 #X connect 16 0 18 0;
 #X connect 17 0 16 0;
+#X connect 19 0 16 0;
 #X connect 20 0 16 0;
-#X connect 22 0 16 0;
-#X connect 23 0 16 0;
+#X connect 21 0 16 0;
+#X connect 45 0 46 0;
+#X connect 46 0 16 2;
diff --git a/pd/doc/about/about.pd b/pd/doc/about/about.pd
index b433ce1d18f0ca69c692ea5eb5f61995f6c71790..12c0727fd861f746980e8c0ed77ff72de4d10836 100644
--- a/pd/doc/about/about.pd
+++ b/pd/doc/about/about.pd
@@ -56,8 +56,8 @@ version of Pd-l2ork. An introduction to Purr Data can be found at:
 #X text 18 360 Reference documentation for Pd lives in:;
 #X obj 18 378 pddp/pddplink @pd_help/../1.manual/index.htm -text doc/1.manual/index.htm
 ;
-#X obj 36 21 cnv 15 432 33 empty \$0-about_text Pd-l2ork 20 12 0 18
--261139 -1 0;
+#X obj 36 21 cnv 15 432 33 empty \$0-about_text Pd-l2ork-2.7.0\ 20181225-rev.8f8acb4
+20 12 0 18 -261139 -1 0;
 #N canvas 430 372 450 323 cat 0;
 #X obj 16 180 draw sprite ./cat;
 #X obj 16 21 struct cat float x float y;
@@ -76,7 +76,7 @@ version of Pd-l2ork. An introduction to Purr Data can be found at:
 #X connect 6 0 0 0;
 #X connect 7 0 4 0;
 #X restore 454 416 pd cat;
-#X scalar cat 383 82 \;;
+#X scalar cat 391 64 \;;
 #X obj 375 47 cnv 15 93 20 empty \$0-vanilla_text Pd-0.48.0 10 6 0
 14 -216373 -1 0;
 #X obj 393 378 cnv 15 31 20 empty \$0-precision_text 32 10 6 0 14 -262144
diff --git a/pd/nw/css/c64.css b/pd/nw/css/c64.css
index eedb26343944fc6b47722de44371be3b5337719f..abb0cc816834f4a6a55b48206d91739753f5db97 100644
--- a/pd/nw/css/c64.css
+++ b/pd/nw/css/c64.css
@@ -619,6 +619,11 @@ div.y2 {
     color: #a49aea;
 }
 
+/* Sample rate input */
+input[name="rate"] {
+    width: 4em;
+}
+
 #prefs_buttons {
     display: table;
     height: 10vh;
diff --git a/pd/nw/css/default.css b/pd/nw/css/default.css
index 5be0c8a93800e9e36b5da8017106ba44954cbdf8..0864425328cea236a724256ab68e61932c1757b5 100644
--- a/pd/nw/css/default.css
+++ b/pd/nw/css/default.css
@@ -193,7 +193,7 @@ mark.console_find_highlighted {
     width: 100%;
     height: 1em;
     padding: 0.2em;
-    padding-left:8px;
+    padding-left: 8px;
     background: silver;
     position: fixed;
     bottom: 0;
@@ -556,11 +556,11 @@ legend {
 /* Iemgui dialog */
 
 input[type="text"]{
-    width:3em;
+    width: 3em;
 }
 
 input[type="number"]{
-    width:3em;
+    width: 3em;
 }
 
 label {
@@ -744,6 +744,11 @@ div.y2 {
     padding-top: 8px;
 }
 
+/* Sample rate input */
+input[name="rate"] {
+    width: 4em;
+}
+
 #prefs_buttons {
     display: table;
     height: 10vh;
diff --git a/pd/nw/css/extended.css b/pd/nw/css/extended.css
index d3b33791e92e6a9689ab50c4f5060b10b4073860..9149fdc54d3ffc338341a1eda3dd9384cc4328ab 100644
--- a/pd/nw/css/extended.css
+++ b/pd/nw/css/extended.css
@@ -593,6 +593,11 @@ div.y2 {
     padding-top: 8px;
 }
 
+/* Sample rate input */
+input[name="rate"] {
+    width: 4em;
+}
+
 #prefs_buttons {
     display: table;
     height: 10vh;
diff --git a/pd/nw/css/footgun.css b/pd/nw/css/footgun.css
new file mode 100644
index 0000000000000000000000000000000000000000..a24f8bfbee89d855d729e5e4d14fe9f30562aca9
--- /dev/null
+++ b/pd/nw/css/footgun.css
@@ -0,0 +1,766 @@
+/* Global CSS */
+
+/* Unfortunately we can't simply include a ttf font of our choice with our
+   project. Chrome has some subtle, nasty rendering bug that ends up making
+   the pd object text invisible. It seems to get triggered when opening a
+   new window-- like a dialog-- if it happens not to use the @font-face font
+   in the body. This ends up somehow breaking the display for the original
+   window. The text will turn invisible _sometimes_ when changing font size,
+   zooming, creating an array (the label might be invisible), or showing
+   the "Save before quitting" dialog. */
+/* 
+@font-face {
+    font-family: "DejaVu Sans Mono";
+    src: url("../DejaVuSansMono.ttf");
+}
+*/
+
+/* This is just a copy of default.css with cords and xlets hidden. */
+
+.cord, .xlet_control, .xlet_signal, .xlet_iemgui {
+    display: none;
+}
+
+body {
+    margin: 0px;
+    font-family: "DejaVu Sans Mono";
+}
+
+.noselect {
+    -webkit-touch-callout: none;
+    -webkit-user-select: none;
+    -khtml-user-select: none;
+    -moz-user-select: none;
+    -ms-user-select: none;
+    user-select: none;
+}
+
+::selection {
+    background: #c3c3c3;
+    color: black;
+}
+
+/* The Pd Console Window */
+
+#console_controls {
+    background-color: LightGray;
+    height: 40px;
+} 
+
+#control_frame {
+    padding: 12px;
+}
+
+/* The DSP toggle */
+
+.dsp_toggle {
+    display: inline-block;
+    position: relative;
+    cursor: pointer;
+    -webkit-user-select: none;
+    -moz-user-select: none;
+    -ms-user-select: none;
+    user-select: none;
+    -webkit-tap-highlight-color: transparent;
+    tap-highlight-color: transparent;
+}
+
+.dsp_toggle input {
+    opacity: 0;
+    position: absolute;
+}
+
+.dsp_toggle input + span {
+    position: relative;
+    display: inline-block;
+    width: 1.65em;
+    height: 1em;
+    background: white;
+    box-shadow: inset 0 0 0 0.0625em #e9e9e9;
+    border-radius: 0.5em;
+    vertical-align: -0.15em;
+    transition: all 0.40s cubic-bezier(.17,.67,.43,.98);
+}
+
+.dsp_toggle:active input + span,
+.dsp_toggle input + span:active {
+    box-shadow: inset 0 0 0 0.73em #e9e9e9;
+}
+
+.dsp_toggle input + span:after {
+    position: absolute;
+    display: block;
+    content: '';
+    width: 0.875em;
+    height: 0.875em;
+    border-radius: 0.4375em;
+    top: 0.0625em;
+    left: 0.0625em;
+    background: white;
+    box-shadow: inset 0 0 0 0.03em rgba(0,0,0,0.1),
+                0 0 0.05em rgba(0,0,0,0.05),
+                0 0.1em 0.2em rgba(0,0,0,0.2);
+    transition: all 0.25s ease-out;
+}
+
+.dsp_toggle:active input + span:after,
+.dsp_toggle input + span:active:after {
+    width: 1.15em;
+}
+
+.dsp_toggle input:checked + span {
+    box-shadow: inset 0 0 0 0.73em #4cd964;
+}
+
+.dsp_toggle input:checked + span:after {
+    left: 0.7125em;
+}
+
+.dsp_toggle:active input:checked + span:after,
+.dsp_toggle input:checked + span:active:after {
+    left: 0.4375em;
+}
+
+/* accessibility styles */
+.dsp_toggle input:focus + span:after {
+    box-shadow: inset 0 0 0 0.03em rgba(0,0,0,0.15),
+                0 0 0.05em rgba(0,0,0,0.08),
+                0 0.1em 0.2em rgba(0,0,0,0.3);
+    background: #fff;
+}
+
+.dsp_toggle input:focus + span {
+    box-shadow: inset 0 0 0 0.0625em #dadada;
+}
+
+.dsp_toggle input:focus:checked + span {
+    box-shadow: inset 0 0 0 0.73em #33be4b;
+}
+
+/* reset accessibility style on hover */
+.dsp_toggle:hover input:focus + span:after {
+    box-shadow: inset 0 0 0 0.03em rgba(0,0,0,0.1),
+                0 0 0.05em rgba(0,0,0,0.05),
+                0 0.1em 0.2em rgba(0,0,0,0.2);
+    background: #fff;
+}
+
+.dsp_toggle:hover input:focus + span {
+    box-shadow: inset 0 0 0 0.0625em #e9e9e9;
+}
+
+.dsp_toggle:hover input:focus:checked + span {
+    box-shadow: inset 0 0 0 0.73em #4cd964;
+}
+
+#printout {
+    margin: 8px;
+}
+
+/* This needs to be renamed, since the "Find" bar is actually at the bottom */
+#console_bottom {
+    position: absolute;
+    top: 40px;
+    left: 0px;
+    right: 0px;
+    bottom: 0px;
+    overflow-y: scroll;    
+}
+
+/* The console API allows classes for different types of messages to print.
+   Currently the only class is "error". More may be added, especially once 
+   we port the "loglevel" functionality that was available in Pd Extended. */
+#console_bottom .error {
+    color: red;
+}
+
+/* Find bar */
+
+#console_find label, #canvas_find label {
+    font-family: "DejaVu Sans", sans-serif;
+    font-size: 10pt;
+}
+
+/* marks for matches to console_find */
+mark {
+    background: white;
+}
+
+mark.console_find_current.console_find_highlighted,
+mark.console_find_current {
+    background: yellow;
+}
+
+mark.console_find_highlighted {
+    background: red;
+}
+
+#console_find {
+    width: 100%;
+    height: 1em;
+    padding: 0.2em;
+    padding-left:8px;
+    background: silver;
+    position: fixed;
+    bottom: 0;
+    left: 0;
+}
+
+/* Pure Data Patch Window (aka canvas) */
+
+/* patch font and background color. (Note: margin needs to stay at zero.) */
+.patch_body {
+    background-color: white;
+}
+
+#selection_rectangle {
+    stroke: #e87216;
+}
+
+/* The outline to show the visible area for a Graph-On-Parent canvas,
+   i.e., the "red rectangle" */
+.gop_rect {
+    fill: none;
+    stroke: red; 
+}
+
+.cord.signal {
+    stroke-width: 2;
+    stroke: #808095;
+}
+
+.cord.control {
+    stroke-width: 1;
+    stroke: #565;
+}
+
+/* selected connection between objects */
+.cord.signal.selected_line,
+.cord.control.selected_line {
+    stroke: #e87216;
+}
+
+#cord_inspector_rect {
+    fill: black;
+    stroke: black;
+}
+
+#cord_inspector_text {
+    fill: white;
+}
+
+#cord_inspector_text.flash {
+    fill: #e87216;
+}
+
+/* text inside boxes: message boxes, object boxes, graphs, comments, etc. */
+.box_text {
+    fill: black;
+}
+
+/* hyperlinks: for now, just pddplink and helplink */
+.pd_link text {
+    fill: blue;
+}
+
+.pd_link text:hover {
+    fill: red;
+}
+
+.pd_link.selected text {
+    fill: #e87216 !important;
+}
+
+/* text inside a box that is being edited */
+#new_object_textentry {
+    /* max-width: 10ch; */
+    min-width: 3ch;
+    position: absolute;
+    display: table-cell;
+    padding: 3px 2px 3px 2px;
+    /* box-shadow: inset 1px 0px 0px 1px #000; */
+    color: black; /* text color */
+    background-color: transparent;
+    white-space: pre-wrap;
+    overflow-wrap: break-word;
+    -webkit-margin-before: 0px;
+}
+
+#new_object_textentry.obj {
+    outline: 1px solid #e87216;
+}
+
+/* We're dynamically creating the svg background data in javascript
+   (in pdgui.js) so that we can change the stroke color of the svg msg box.
+   We store the color as the third argument to "outline" below. Since the
+   outline is 0px it won't show up-- this allows us specify the color here
+   in the style sheet and retrieve it in javascript when we build the svg
+   background. */
+#new_object_textentry.msg {
+    outline: 0px solid #e87216;
+    /* background-image: url(../msg-box.svg); */
+}
+
+p.msg::after {
+    content: "";
+    height: 100%;
+    width: 5px;
+    /* background-image: url(../msg-box-flag.svg); */
+    position: absolute;
+    top: 0%;
+    left: 100%;
+}
+
+/* not sure what this is doing here... */
+text {
+    // fill: red;
+    //cursor: default;
+}
+
+/* not sure if this is still needed */
+.selected_border {
+    stroke: blue;
+    stroke-dasharray: none;
+    stroke-width: 1;
+}
+
+.msg .border {
+    stroke: #ccc;
+    fill: #f8f8f6;
+}
+
+/* state of msg box when clicking it */
+.msg.flashed .border {
+    stroke-width: 4;
+}
+
+/* atom box */
+.atom .border {
+    stroke: #ccc;
+    fill: #eee;
+}
+
+/* for dropdown box we want to visually distinguish boxes that output
+   the index from boxes that output the value. For now we do that by
+   stroking the arrow for boxes that output an index. For boxes that
+   output the value we don't need a CSS rule, as the arrow will be filled
+   black by default */
+.atom .index_arrow {
+    stroke: black;
+    stroke-width: 1;
+    fill: none;
+}
+
+/* gatom "activated" text (i.e., when it has the keyboard focus) */
+.atom.activated text {
+    fill: red;
+}
+
+#dropdown_list {
+    position: absolute;
+    border-width: 1px;
+    border-style: solid;
+    border-color: #c3c3c3;
+    cursor: pointer;
+    box-shadow: 5px 0 5px -5px #aaa, 0 5px 5px -5px #aaa, -5px 0 5px -5px #aaa;
+    overflow-y: auto;
+}
+
+#dropdown_list ol {
+    list-style-position: inside;
+    margin: 0;
+    padding: 0;
+    background: #eee;
+}
+
+#dropdown_list li {
+    list-style-type: none;
+    padding: 5px;
+}
+
+#dropdown_list li.highlighted {
+    background-color: #c3c3c3;
+}
+
+.obj .border {
+    fill: #f6f8f8;
+    stroke: #ccc;
+}
+
+.comment .border {
+    fill: none;
+}
+
+#patchsvg.editmode .comment .border {
+    stroke: #aaa;
+    stroke-dasharray: 8 4;
+}
+
+/* A little hack for special case of [cnv].
+   All other iemguis have a black border, but
+   [cnv] sets its selection rectangle to the
+   user-supplied fill color when the object
+   isn't selected */
+.iemgui .border:not(.mycanvas_border) {
+    stroke: black;
+}
+
+.graph .border {
+    stroke: black;
+    fill: none;
+}
+
+/* Graph (or subpatch) that has been opened to inspect its contents */
+.graph.has_window .border {
+    stroke: black;
+    fill: gray;
+}
+
+/* border color for selected objects
+      * an element with the class "border"
+      * the element is contained within a parent element of class "selected"
+      * that parent element is not in class "gop"
+   in plain English:
+      This lets us highlight an object's border, unless it is inside a gop
+      canvas.
+*/
+:not(.gop).selected .border {
+    stroke: #e87216;
+    display: inline;
+}
+
+/* text inside selected objects */
+:not(.gop).selected text {
+    fill: blue;
+}
+
+/* for an object that didn't create */
+.obj .border.broken_border {
+    fill: #f7f7f7;
+    stroke: #f00;
+    stroke-dasharray: 3 2;
+}
+
+/* control inlet */
+.xlet_control {
+    stroke: #777;
+    fill: white;
+//    stroke-width: 1;
+}
+
+/* signal inlet */
+.xlet_signal {
+    stroke: #777;
+    fill: #808095;
+    stroke-width: 1;
+}
+
+/* iemgui inlet or outlet */
+.xlet_iemgui {
+    stroke: black;
+    fill: black;
+    stroke-width: 1;
+}
+
+/* text label for an iemgui */
+.iemgui_label_selected {
+    fill: blue;
+}
+
+/* test of xlet hover animation... this should 
+   probably use the web animation API instead. That
+   way the animation won't get cut off when you
+   move off the object. We can't do that currently
+   because Pd just floods us with pairs of messages 
+   for every pixel we move inside an xlet. */
+@-webkit-keyframes fizzle {
+    0% {
+        stroke-width: 1;
+        stroke-opacity: 1;
+        rx: 1;
+        ry: 1;
+    }
+    100% {
+        stroke-width: 20;
+        stroke-opacity: 0.2;
+        rx: 50;
+        ry: 50;
+    }
+}
+
+/* can't remember why this was tagged !important */
+.xlet_selected {
+    stroke: orange !important;
+    fill: orange;
+    -webkit-animation: fizzle 0.4s linear 1;
+}
+
+#canvas_find {
+    width: 100%;
+    height: 1em;
+    padding-top: 4px;
+    padding-left: 8px;
+    padding-bottom: 8px;
+    background: silver;
+    position: fixed;
+    bottom: 0;
+    left: 0;
+}
+
+/* Dialog to ask to save the patch before quitting */
+#save_before_quit {
+    background-color: #f3f3f3;
+    border:1px solid #bbb;
+    padding: 12px;
+    margin: 12px;
+    box-shadow: 7px 7px 5px grey;
+}
+
+/* Search dialog */
+
+.search_body {
+    font-family: "DejaVu Sans", sans-serif;
+    font-size: 10pt;
+    padding: 8px;
+}
+
+/* Common to all dialogs */
+
+.dialog_body {
+    font-family: "DejaVu Sans", sans-serif;
+    font-size: 10pt;
+    background-color: #f3f3f3;
+}
+
+.submit_buttons {
+    text-align: center;
+    padding: 8px;
+}
+
+fieldset {
+/*    font-family:Georgia; */
+    background-color:#f3f3f3;
+    border-radius:3px;
+    border:1px solid #ddd;
+    margin-left:auto;
+    margin-right:auto;
+    padding: 8px;
+}
+
+legend {
+    font-size: 1.2em;
+}
+
+.hidden {
+    display: none;
+}
+
+.container{
+    display: none;
+}
+
+/* Iemgui dialog */
+
+input[type="text"]{
+    width:3em;
+}
+
+input[type="number"]{
+    width:3em;
+}
+
+label {
+    text-align: right;
+}
+
+/* Pair of properties that are related */
+.pair {
+    width: 75%;
+    text-align: left;
+    align: left;
+}
+
+.item1 {
+    width: 50%;
+}
+
+.item2 {
+    width: 50%;
+}
+
+input[name="x_offset"] {
+    width: 2em;
+}
+
+input[name="y_offset"] {
+    width: 2em;
+}
+
+input[name="send_symbol"] {
+    width: 8em;
+}
+
+input[name="receive_symbol"] {
+    width: 8em;
+}
+
+input[name="label"] {
+    width: 8em;
+}
+
+input[name="font_size"] {
+    width: 3em;
+}
+
+input[name="startup_flags"] {
+    width: 16em;
+}
+
+/* Canvas dialog */
+
+div.x-scale {
+    padding: 3px;
+    text-align: center;
+}
+
+div.gop-range {
+}
+
+div.y1 {
+    text-align: center;
+    padding: 3px;
+}
+
+div.x1 {
+    text-align: center;
+    padding: 3px;
+    white-space: nowrap;
+}
+
+div.y2 {
+    text-align: center;
+    padding: 3px;
+}
+
+.x-margin {
+    white-space: nowrap;
+}
+
+.array_style {
+    white-space: nowrap;
+}
+
+#array_name_input {
+    width: 6em;
+}
+
+.disabled {
+    color: #aaa;
+}
+
+/* Preferences dialog */
+
+#prefs_html_element {
+/*    height: 100%; */
+    margin: 0px;
+    padding: 0px;
+    height: 100vh;
+}
+
+.prefs_body {
+    padding: 0px;
+}
+
+#prefs_container {
+    display: table;
+}
+
+/* Main tab widget */
+
+/* All the display, width, and height settings below are a house of cards.
+   I don't have the schooling to actually predict how all these CSS elements
+   work together to create the whole. I just fudged around until I found a
+   way to get the buttons anchored at the bottom of the dialog without
+   triggering scrollbars to appear. If someone knows a way to do it "right"
+   without becoming an order of magnitude more complex, do feel free... */
+.prefs_tab_group {
+    display: table;
+    width: 90%;
+}
+
+/* Configure the radio buttons to hide off-screen */
+.prefs_tab {
+    position: absolute;
+    left:-100px;
+    top:-100px;
+}
+
+/* Configure labels to look like tabs */
+.prefs_tab + label {
+    /* inline-block such that the label can be given dimensions */
+    display: inline-block;
+    /* A nice curved border around the tab */
+    border: 1px solid #bbb;
+    border-top-left-radius: 5px;
+    border-top-right-radius: 5px;
+    /* the bottom border is handled by the tab content div */
+    border-bottom: 0;
+    /* Padding around tab text */
+    padding: 5px 10px;
+    /* put a small margin to the left to make the first tab clear */
+    margin-left: 4px;
+    margin-top: 8px;
+    margin-bottom: 0px;
+    /* Set the background color to default gray (non-selected tab) */
+    background-color:#ececec;
+}
+
+/* Focused tabs */
+.prefs_tab:focus + label {
+    border: 1px dashed #bbb;
+}
+
+/* Checked tabs must be white with the bottom border removed */
+.prefs_tab:checked + label {
+    background-color: #f3f3f3;
+    text-shadow: 1px 0px 0px; /* substitute for "bold" to retain div width */
+    border-bottom: 1px solid #f3f3f3;
+    margin-bottom: -1px;
+}
+
+/* The tab content must fill the widgets size and have a nice border */
+.prefs_tab_group > div {
+    display: none;
+    border-top: 1px solid #ddd;
+    padding: 0px;
+    margin: 0px;
+    height: 100%;
+}
+
+/* This matches tabs displaying to their associated radio inputs */
+.tab1:checked ~ .tab1, .tab2:checked ~ .tab2, .tab3:checked ~ .tab3, .tab4:checked ~ .tab4 {
+    display: table;
+    padding: 8px;
+    line-height: 20px;
+    width: 100%;
+    height: 78vh;
+}
+
+.tab_settings {
+    padding-top: 8px;
+}
+
+/* Sample rate input */
+input[name="rate"] {
+    width: 4em;
+}
+
+#prefs_buttons {
+    display: table;
+    height: 10vh;
+    padding: 0px;
+    margin: 0px;
+    margin-top: -10px;
+    margin-bottom: -10px;
+    padding: 30px;
+}
diff --git a/pd/nw/css/inverted.css b/pd/nw/css/inverted.css
index b75f4883f71af9ebb08db4356178e20c8238baeb..cfd662a755dd5ecc0a3c447b074dc08a4a236e0e 100644
--- a/pd/nw/css/inverted.css
+++ b/pd/nw/css/inverted.css
@@ -632,6 +632,11 @@ div.y2 {
     padding-top: 8px;
 }
 
+/* Sample rate input */
+input[name="rate"] {
+    width: 4em;
+}
+
 #prefs_buttons {
     display: table;
     height: 10vh;
diff --git a/pd/nw/css/solarized.css b/pd/nw/css/solarized.css
index 44ddaf2388adb09001443e80cf317b2c78428652..3b2e4b836c902baad81e6f0f690e86fc39a398ab 100644
--- a/pd/nw/css/solarized.css
+++ b/pd/nw/css/solarized.css
@@ -623,6 +623,11 @@ div.y2 {
     padding-top: 8px;
 }
 
+/* Sample rate input */
+input[name="rate"] {
+    width: 4em;
+}
+
 #prefs_buttons {
     display: table;
     height: 10vh;
diff --git a/pd/nw/css/solarized_inverted.css b/pd/nw/css/solarized_inverted.css
index 90a58cfd7b212eef38141a20207c32dac9cd7ba5..63a64fb063cbf54d561c878c4981fced8e903c5b 100644
--- a/pd/nw/css/solarized_inverted.css
+++ b/pd/nw/css/solarized_inverted.css
@@ -624,6 +624,11 @@ div.y2 {
     padding-top: 8px;
 }
 
+/* Sample rate input */
+input[name="rate"] {
+    width: 4em;
+}
+
 #prefs_buttons {
     display: table;
     height: 10vh;
diff --git a/pd/nw/css/strongbad.css b/pd/nw/css/strongbad.css
index 45b9708c512eb72c47330c319899e76b76d202d4..dbc601349ce4641c889a8cb717c7ba5b1d487111 100644
--- a/pd/nw/css/strongbad.css
+++ b/pd/nw/css/strongbad.css
@@ -605,6 +605,11 @@ div.y2 {
     padding-top: 8px;
 }
 
+/* Sample rate input */
+input[name="rate"] {
+    width: 4em;
+}
+
 #prefs_buttons {
     display: table;
     height: 10vh;
diff --git a/pd/nw/css/subdued.css b/pd/nw/css/subdued.css
index 78af8f5c99c8b67b34435e93d6abd1b86256da87..5a364bbc17c075e3cf2836d6780ea6644663113d 100644
--- a/pd/nw/css/subdued.css
+++ b/pd/nw/css/subdued.css
@@ -608,6 +608,11 @@ div.y2 {
     padding-top: 8px;
 }
 
+/* Sample rate input */
+input[name="rate"] {
+    width: 4em;
+}
+
 #prefs_buttons {
     display: table;
     height: 10vh;
diff --git a/pd/nw/css/vanilla.css b/pd/nw/css/vanilla.css
index 66a4323d18a8d578e9c36370cc2124e5e3a4c0c8..e8bf4f7a3b23368130cefda1da01b85df7e0accb 100644
--- a/pd/nw/css/vanilla.css
+++ b/pd/nw/css/vanilla.css
@@ -593,6 +593,11 @@ div.y2 {
     padding-top: 8px;
 }
 
+/* Sample rate input */
+input[name="rate"] {
+    width: 4em;
+}
+
 #prefs_buttons {
     display: table;
     height: 10vh;
diff --git a/pd/nw/css/vanilla_inverted.css b/pd/nw/css/vanilla_inverted.css
index 710255f072af5a931665154b07f9e3591f61edf8..ee126862338670878fa7d88574a5d076f3c93106 100644
--- a/pd/nw/css/vanilla_inverted.css
+++ b/pd/nw/css/vanilla_inverted.css
@@ -611,6 +611,11 @@ div.y2 {
     padding-top: 8px;
 }
 
+/* Sample rate input */
+input[name="rate"] {
+    width: 4em;
+}
+
 #prefs_buttons {
     display: table;
     height: 10vh;
diff --git a/pd/nw/dialog_prefs.html b/pd/nw/dialog_prefs.html
index 44b95fce4dbb9779d14673b487aa98159335cf7a..2453761175069e176516521084cd9b61478809dd 100644
--- a/pd/nw/dialog_prefs.html
+++ b/pd/nw/dialog_prefs.html
@@ -40,57 +40,72 @@
         </label>
 
         <div class="tab1">
-          <div class="tab_settings">
-            <label data-i18n="[title]prefs.audio.api_tt">
-              <span data-i18n="prefs.audio.api"></span>
-              <select id="audio_api" onchange="change_api(this);">
-              </select>
-            </label>
-          </div>
-          <div class="tab_settings">
-            <label data-i18n="[title]prefs.audio.sr_tt">
-              <span data-i18n="prefs.audio.sr"></span>
-              <input type="text"
-                     id="rate"
-                     name="rate"
-                     onchange="attr_change(this);">
-            </label>
-          </div>
-          <div class="tab_settings">
-            <label data-i18n="[title]prefs.audio.blocksize_tt">
-              <span data-i18n="prefs.audio.blocksize"></span>
-              <select id="blocksize"
-                      onchange="attr_change(this);">
-                <option value="64">64</option>
-                <option value="128">128</option>
-                <option value="256">256</option>
-                <option value="512">512</option>
-                <option value="1024">1024</option>
-                <option value="2048">2048</option>
-              </select>
-            </label>
-            <label data-i18n="[title]prefs.audio.advance_tt">
-              <span data-i18n="prefs.audio.advance"></span>
-              <input type="text"
-                     id="advance"
-                     name="advance"
-                     onchange="attr_change(this);">
-            </label>
-          </div>
-          <div class="tab_settings">
-            <span id="callback_container" class="hidden">
-              <label data-i18n="[title]prefs.audio.callback_tt">
+          <table class="tab_settings">
+            <tr data-i18n="[title]prefs.audio.api_tt">
+              <td>
+                <span data-i18n="prefs.audio.api"></span>
+              </td>
+              <td>
+                <select id="audio_api" onchange="change_api(this);">
+                </select>
+              </td>
+            </tr>
+            <tr data-i18n="[title]prefs.audio.sr_tt">
+              <td>
+                <span data-i18n="prefs.audio.sr"></span>
+              </td>
+              <td>
+                <input type="text"
+                       id="rate"
+                       name="rate"
+                       onchange="attr_change(this);">
+              </td>
+            </tr>
+            <tr data-i18n="[title]prefs.audio.blocksize_tt">
+              <td>
+                <span data-i18n="prefs.audio.blocksize"></span>
+              </td>
+              <td>
+                <select id="blocksize"
+                        onchange="attr_change(this);">
+                  <option value="64">64</option>
+                  <option value="128">128</option>
+                  <option value="256">256</option>
+                  <option value="512">512</option>
+                  <option value="1024">1024</option>
+                  <option value="2048">2048</option>
+                </select>
+              </td>
+            <tr data-i18n="[title]prefs.audio.advance_tt">
+              <td>
+                <span data-i18n="prefs.audio.advance"></span>
+              </td>
+              <td>
+                <input type="text"
+                       id="advance"
+                       name="advance"
+                       onchange="attr_change(this);">
+              </td>
+            </tr>
+            <tr id="callback_container" class="hidden"
+                data-i18n="[title]prefs.audio.callback_tt">
+              <td>
                 <span data-i18n="prefs.audio.callback"></span>
+              </td>
+              <td>
                 <input type="checkbox" id="callback" name="callback">
-              </label>
-            </span>
-            <span id="multi_dev_container" class="hidden">
-              <label data-i18n="[title]prefs.audio.callback_tt">
+              </td>
+            </tr>
+            <tr id="multi_dev_container" class="hidden"
+                data-i18n="[title]prefs.audio.callback_tt">
+              <td>
                 <span data-i18n="prefs.audio.callback"></span>
+              </td>
+              <td>
                 <input type="checkbox" id="multi-dev" name="multi-dev">
-              </label>
-            </span>
-          </div>
+              </td>
+            </tr>
+          </table>
           <table class="tab_settings">
             <tr>
               <td data-i18n="[title]prefs.audio.input_title_tt">
@@ -362,6 +377,8 @@
               <option data-i18n="prefs.gui.presets.solarized_inverted"
                       value="solarized_inverted">
               </option>
+              <option data-i18n="prefs.gui.presets.footgun" value="footgun">
+              </option>
             </select>
             <br/>
             <label data-i18n="[title]prefs.gui.zoom.save_zoom_tt">
diff --git a/pd/nw/locales/de/translation.json b/pd/nw/locales/de/translation.json
index f382fa17c9889c466db4412024d44700bf72664e..9eb94414ee09df0fc9c37ff09bdfbd1a05001a01 100644
--- a/pd/nw/locales/de/translation.json
+++ b/pd/nw/locales/de/translation.json
@@ -402,7 +402,8 @@
         "solarized": "Solarized",
         "solarized_inverted": "Solarized (Inverted)",
         "extended": "Extended",
-        "c64": "C64"
+        "c64": "C64",
+        "footgun": "Fusspistole"
       },
       "zoom": {
         "save_zoom": "Speichern/Laden der Vergrößerung im Patch",
diff --git a/pd/nw/locales/en/translation.json b/pd/nw/locales/en/translation.json
index c779742a70f17f466865b89090416169b593fd64..57f658c2214feb87f7533ce404b43e83dcec6e6d 100644
--- a/pd/nw/locales/en/translation.json
+++ b/pd/nw/locales/en/translation.json
@@ -403,7 +403,8 @@
         "solarized": "Solarized",
         "solarized_inverted": "Solarized (Inverted)",
         "extended": "Extended",
-        "c64": "C64"
+        "c64": "C64",
+        "footgun": "Footgun"
       },
       "zoom": {
         "save_zoom": "save/load zoom level with patch",
diff --git a/pd/nw/pd_canvas.js b/pd/nw/pd_canvas.js
index db54bda82cc9a561b3f0fe2187b9920661862f20..1e9c770a326da00b48b7bd5e1f77a5c35706dc0d 100644
--- a/pd/nw/pd_canvas.js
+++ b/pd/nw/pd_canvas.js
@@ -233,25 +233,50 @@ var canvas_events = (function() {
                 return false;
             },
             mousedown: function(evt) {
-                var target_id;
+                var target_id, resize_type;
                 if (target_is_scrollbar(evt)) {
                     return;
-                } else if (evt.target.classList.contains("clickable_resize_handle")) {
+                } else if (evt.target.parentNode &&
+                    evt.target.parentNode.classList
+                        .contains("clickable_resize_handle")) {
                     draggable_label =
-                        evt.target.classList.contains("move_handle");
+                        evt.target.parentNode.classList.contains("move_handle");
 
                     // get id ("x123456etcgobj" without the "x" or "gobj")
                     target_id = (draggable_label ? "_l" : "_s") +
-                        evt.target.parentNode.id.slice(0,-4).slice(1);
+                        evt.target.parentNode.parentNode.id.slice(0,-4).slice(1);
                     last_draggable_x = evt.pageX + svg_view.x;
                     last_draggable_y = evt.pageY + svg_view.y;
-                    pdgui.pdsend(target_id, "_click", 1,
+
+                    // Nasty-- we have to forward magic values from g_canvas.h
+                    // defines in order to get the correct constrain behavior.
+                    if (evt.target.classList.contains("constrain_top_right")) {
+                        resize_type = 7; // CURSOR_EDITMODE_RESIZE_X
+                    } else if (evt.target.classList
+                        .contains("constrain_bottom_right")) {
+                        resize_type = 10; // CURSOR_EDITMODE_RESIZE_Y
+                    } else if (draggable_label) {
+                        resize_type = 11; // CURSOR_EDITMODE_MOVE
+                    } else {
+                        resize_type = 8; // CURSOR_EDITMODE_RESIZE
+                    }
+
+                    // Even nastier-- we now must turn off the cursor styles
+                    // so that moving the pointer outside the hotspot doesn't
+                    // cause the cursor to change. This happens for the
+                    // drag handle to move the gop red rectangle. Unlike
+                    // the label handles, it doesn't get immediately
+                    // destroyed upon receiving its callback below.
+                    pdgui.toggle_drag_handle_cursors(evt.target.parentNode,
+                        !!draggable_label, false);
+
+                    pdgui.pdsend(target_id, "_click", resize_type,
                         (evt.pageX + svg_view.x),
                         (evt.pageY + svg_view.y));
                     canvas_events.iemgui_label_drag();
                     return;
                 }
-                // tk events (and, therefore, Pd evnets) are one greater
+                // tk events (and, therefore, Pd events) are one greater
                 // than html5...
                 var b = evt.button + 1;
                 var mod, match_elem;
@@ -472,16 +497,6 @@ var canvas_events = (function() {
                 last_draggable_x = evt.pageX + svg_view.x;
                 last_draggable_y = evt.pageY + svg_view.y;
 
-                if (!is_canvas_gop_rect) {
-                    // This is bad-- we should be translating
-                    // here so that the logic doesn't depend on the shape
-                    // type we chose in pdgui (here, it's "line").
-                    handle_elem.x1.baseVal.value += dx;
-                    handle_elem.y1.baseVal.value += dy;
-                    handle_elem.x2.baseVal.value += dx;
-                    handle_elem.y2.baseVal.value += dy;
-                }
-
                 pdgui.pdsend(target_id, "_motion",
                     (evt.pageX + svg_view.x),
                     (evt.pageY + svg_view.y));
@@ -491,6 +506,19 @@ var canvas_events = (function() {
                 // Set last state (none doesn't count as a state)
                 //pdgui.post("previous state is "
                 //    + canvas_events.get_previous_state());
+                var label_handle = document.querySelector(".move_handle");
+                var cnv_resize_handle =
+                    document.querySelector(".cnv_resize_handle");
+                // Restore our cursor bindings for any drag handles that
+                // happen to exist
+                if (label_handle) {
+                    pdgui.toggle_drag_handle_cursors(label_handle,
+                        true, true);
+                }
+                if (cnv_resize_handle) {
+                    pdgui.toggle_drag_handle_cursors(cnv_resize_handle,
+                        false, true);
+                }
                 canvas_events[canvas_events.get_previous_state()]();
             },
             dropdown_menu_keydown: function(evt) {
diff --git a/pd/nw/pdgui.js b/pd/nw/pdgui.js
index 97d0fa3a013323cee5624eadf55911722285334c..e4ae68a1132bc99be814d736a584acf0d36509db 100644
--- a/pd/nw/pdgui.js
+++ b/pd/nw/pdgui.js
@@ -1058,7 +1058,7 @@ function canvas_menuclose_callback(cid_for_dialog, cid, force) {
         w.canvas_events.close_without_saving(cid, force);
     };
     cancel_button.onclick = function() {
-        w.close_save_dialog();
+        w.canvas_events.close_save_dialog();
         w.canvas_events[w.canvas_events.get_previous_state()]();
     }
 
@@ -1472,11 +1472,18 @@ function gui_canvas_cursor(cid, pd_event_type) {
             case "cursor_editmode_resize":
                 c = "ew-resize";
                 break;
-            case "cursor_editmode_resize_bottom_right": c = "se-resize";
+            case "cursor_editmode_resize_bottom_right":
+                c = "se-resize";
                 break;
             case "cursor_scroll":
                 c = "all-scroll";
                 break;
+            case "cursor_editmode_resize_vert":
+                c = "ns-resize";
+                break;
+            case "cursor_editmode_move":
+                c = "move";
+                break;
         }
         patch.style.cursor = c;
     });
@@ -1757,6 +1764,14 @@ function connect_as_client() {
     client.setNoDelay(true);
     // uncomment the next line to use fast_parser (then set its callback below)
     //client.setEncoding("utf8");
+    client.on("error", function(e) {
+        var eString = "";
+        Object.keys(e).forEach(function(k) {
+            eString += " " + k + ": " + e[k];
+        });
+        pd_window.prompt("Error:" + eString);
+    });
+
     client.connect(PORT, HOST, function() {
         console.log("CONNECTED TO: " + HOST + ":" + PORT);
     });
@@ -2021,7 +2036,7 @@ var gui = (function() {
         return {
             append: !w ? null_fn: function(cb) {
                 var frag = w.window.document.createDocumentFragment();
-                frag = cb(frag, w.window);
+                frag = cb(frag, w.window, c[cid]);
                 last_thing.appendChild(frag);
                 return c[cid];
             },
@@ -3239,12 +3254,29 @@ function gui_iemgui_label_font(cid, tag, fontname, fontweight, fontsize) {
     });
 }
 
+function toggle_drag_handle_cursors(e, is_label, state) {
+    e.querySelector(".constrain_top_right").style.cursor =
+        state ? "ew-resize" : "";
+    e.querySelector(".constrain_bottom_right").style.cursor =
+        state ? "ns-resize" : "";
+    e.querySelector(".unconstrained").style.cursor =
+        state ? (is_label ? "move" : "se-resize") : "";
+}
+
+exports.toggle_drag_handle_cursors = toggle_drag_handle_cursors;
+
 // Show or hide little handle for dragging around iemgui labels
 function gui_iemgui_label_show_drag_handle(cid, tag, state, x, y, cnv_resize) {
     if (state !== 0) {
         gui(cid).get_gobj(tag)
-        .append(function(frag) {
-            var rect;
+        .append(function(frag, w) {
+            var g, rect, top_right, bottom_right;
+            g = create_item(cid, "g", {
+                class: (cid === tag) ? "gop_drag_handle move_handle border" :
+                    cnv_resize !== 0 ? "cnv_resize_handle border" :
+                    "label_drag_handle move_handle border",
+                transform: "matrix(1, 0, 0, 1, 0, 0)"
+            });
             // Here we use a "line" shape so that we can control its color
             // using the "border" class (for iemguis) or the "gop_rect" class
             // for the graph-on-parent rectangle anchor. In both cases the
@@ -3252,28 +3284,59 @@ function gui_iemgui_label_show_drag_handle(cid, tag, state, x, y, cnv_resize) {
             // to define than a "rect" for that case.
             rect = create_item(cid, "line", {
                 x1: x,
-                y1: y + 3,
+                y1: y,
                 x2: x,
-                y2: y + 10,
-                "stroke-width": 7,
-                class: (cid === tag) ? "gop_drag_handle move_handle gop_rect" :
-                    cnv_resize !== 0 ? "cnv_resize_handle border" :
-                    "label_drag_handle move_handle border"
+                y2: y + 14,
+                "stroke-width": 14,
+                class: "unconstrained"
+            });
+            g.classList.add("clickable_resize_handle");
+            top_right = create_item(cid, "rect", {
+                x: x + 1.5,
+                y: y + 0.5,
+                width: 5,
+                height: 7,
+                fill: "black",
+                "fill-opacity": "0",
+                class: "constrain_top_right"
             });
-            rect.classList.add("clickable_resize_handle");
-            frag.appendChild(rect);
+            bottom_right = create_item(cid, "rect", {
+                x: x - 6.5,
+                y: y + 8.5,
+                width: 7,
+                height: 5,
+                fill: "black",
+                "fill-opacity": "0",
+                class: "constrain_bottom_right"
+            });
+            g.appendChild(rect);
+            g.appendChild(top_right);
+            g.appendChild(bottom_right);
+
+            // Quick hack for cursors on mouse-over. We only add them if
+            // we're not already dragging a label or resizing an iemgui.
+            // Apparently I didn't register all these edge-case event states
+            // in canvas_events. States like "iemgui_label_drag" actually
+            // just get registered as state "none". So we just check for "none"
+            // here and assume it means we're in the middle of dragging.
+            // If not we go ahead and set our cursor styles.
+            if (w.canvas_events.get_state() != "none") {
+                toggle_drag_handle_cursors(g, cnv_resize === 0, true);
+            }
+
+            frag.appendChild(g);
             return frag;
         });
     } else {
         gui(cid).get_gobj(tag, function(e) {
-            var rect =
+            var g =
                 e.getElementsByClassName((cid === tag) ? "gop_drag_handle" :
                     cnv_resize !== 0 ? "cnv_resize_handle" :
                         "label_drag_handle")[0];
             //rect = get_item(cid, "clickable_resize_handle");
             // Need to check for null here...
-            if (rect) {
-                rect.parentNode.removeChild(rect);
+            if (g) {
+                g.parentNode.removeChild(g);
             } else {
                 post("error: couldn't delete the iemgui drag handle!");
             }
@@ -3281,6 +3344,15 @@ function gui_iemgui_label_show_drag_handle(cid, tag, state, x, y, cnv_resize) {
     }
 }
 
+function gui_iemgui_label_displace_drag_handle(cid, tag, dx, dy) {
+    gui(cid).get_gobj(tag)
+    .q(".label_drag_handle", function(e) {
+        var t = e.transform.baseVal.getItem(0);
+        t.matrix.e += dx;
+        t.matrix.f += dy;
+    });
+}
+
 function gui_mycanvas_new(cid,tag,color,x1,y1,x2_vis,y2_vis,x2,y2) {
     gui(cid).get_gobj(tag)
     .append(function(frag) {
@@ -5168,9 +5240,13 @@ function gui_text_dialog(did, width, height, font_size) {
         font_size);
 }
 
+function dialog_raise(did) {
+    dialogwin[did].focus();
+}
+
 function gui_text_dialog_raise(did) {
     if (dialogwin[did]) {
-        dialogwin[did].focus();
+        dialog_raise(did);
     }
 }
 
@@ -5213,6 +5289,8 @@ function gui_pd_dsp(state) {
 function open_prefs() {
     if (!dialogwin["prefs"]) {
         create_window("prefs", "prefs", 370, 470, 0, 0, null);
+    } else {
+        dialog_raise("prefs");
     }
 }
 
@@ -5221,6 +5299,8 @@ exports.open_prefs = open_prefs;
 function open_search() {
     if (!dialogwin["search"]) {
         create_window("search", "search", 300, 400, 20, 20, null);
+    } else {
+        dialog_raise("search");
     }
 }
 
diff --git a/pd/src/g_all_guis.c b/pd/src/g_all_guis.c
index c2381e0f12f3d6d283868cf82e2c936143a31ef7..f5c4e9dcd3b59f7a6ebb40b24a7a27f86ff3aaa7 100644
--- a/pd/src/g_all_guis.c
+++ b/pd/src/g_all_guis.c
@@ -246,7 +246,7 @@ void iemgui_label_pos(t_iemgui *x, t_symbol *s, int ac, t_atom *av)
 {
     x->x_ldx = atom_getintarg(0, ac, av);
     x->x_ldy = atom_getintarg(1, ac, av);
-    if(glist_isvisible(x->x_glist))
+    if (glist_isvisible(x->x_glist))
     {
         int x1 = x->x_ldx;
         int y1 = x->x_ldy;
@@ -474,7 +474,7 @@ void iemgui_pos(t_iemgui *x, t_symbol *s, int ac, t_atom *av)
 {
     x->x_obj.te_xpix = atom_getintarg(0, ac, av);
     x->x_obj.te_ypix = atom_getintarg(1, ac, av);
-    if(glist_isvisible(x->x_glist))
+    if (glist_isvisible(x->x_glist))
         iemgui_shouldvis(x, IEM_GUI_DRAW_MODE_MOVE);
 }
 
@@ -488,7 +488,7 @@ void iemgui_color(t_iemgui *x, t_symbol *s, int ac, t_atom *av)
     }
     else
         x->x_lcol = iemgui_compatible_col(atom_getintarg(1, ac, av));
-    if(glist_isvisible(x->x_glist))
+    if (glist_isvisible(x->x_glist))
     {
         x->x_draw(x, x->x_glist, IEM_GUI_DRAW_MODE_CONFIG);
         iemgui_label_draw_config(x);
@@ -692,7 +692,6 @@ extern t_class *my_canvas_class;
 // but in the case of canvas, the "iemgui" tag is added (it wasn't the case originally)
 void scalehandle_draw_select(t_scalehandle *h, int px, int py)
 {
-    char tagbuf[MAXPDSTRING];
     t_object *x = h->h_master;
     t_canvas *canvas=glist_getcanvas(h->h_glist);
 
@@ -702,9 +701,8 @@ void scalehandle_draw_select(t_scalehandle *h, int px, int py)
     scalehandle_draw_erase(h);
 
     if (!h->h_vis) {
-        sprintf(tagbuf, "x%lx", (long unsigned int)x);
-        gui_vmess("gui_iemgui_label_show_drag_handle", "xsiiii",
-            canvas, tagbuf, 1, px - sx, py - sy, h->h_scale);
+        gui_vmess("gui_iemgui_label_show_drag_handle", "xxiiii",
+            canvas, x, 1, px - sx, py - sy, h->h_scale);
         h->h_vis = 1;
     }
 }
@@ -713,7 +711,7 @@ void scalehandle_draw_select2(t_iemgui *x)
 {
     t_canvas *canvas=glist_getcanvas(x->x_glist);
     t_class *c = pd_class((t_pd *)x);
-    int sx,sy;
+    int sx, sy;
     if (c == my_canvas_class)
     {
         t_my_canvas *y = (t_my_canvas *)x;
@@ -722,17 +720,20 @@ void scalehandle_draw_select2(t_iemgui *x)
     }
     else
     {
-        int x1,y1,x2,y2;
-        c->c_wb->w_getrectfn((t_gobj *)x,canvas,&x1,&y1,&x2,&y2);
+        int x1, y1, x2, y2;
+        c->c_wb->w_getrectfn((t_gobj *)x,canvas, &x1, &y1, &x2, &y2);
         //iemgui_getrect_draw(x, &x1, &y1, &x2, &y2);
-        sx=x2-x1; sy=y2-y1;
+        sx = x2 - x1; sy = y2 - y1;
     }
     /* we're not drawing the scalehandle for the actual iemgui-- just
-       the one for the label. */
+       the one for the label. Special case for [cnv] which for some reason
+       allows a smaller selection area than its painted rectangle. */
     if (c == my_canvas_class)
-        scalehandle_draw_select(x->x_handle,sx+8,sy+3);
+        scalehandle_draw_select(x->x_handle,
+            (int)(sx + SCALEHANDLE_WIDTH * 1.5) + 1,
+            sy + SCALEHANDLE_HEIGHT);
     if (x->x_lab != s_empty)
-        scalehandle_draw_select(x->x_lhandle,x->x_ldx,x->x_ldy);
+        scalehandle_draw_select(x->x_lhandle, x->x_ldx + 5, x->x_ldy + 10);
 }
 
 void scalehandle_draw_erase(t_scalehandle *h)
@@ -740,7 +741,12 @@ void scalehandle_draw_erase(t_scalehandle *h)
     //t_canvas *canvas = glist_getcanvas(h->h_glist);
     if (!h->h_vis) return;
     gui_vmess("gui_iemgui_label_show_drag_handle", "xxiiii",
-        h->h_glist, h->h_master, 0, 0, 0, h->h_scale);
+        h->h_glist,
+        h->h_master,
+        0,
+        0,
+        0,
+        h->h_scale);
     h->h_vis = 0;
 }
 
@@ -783,7 +789,10 @@ t_scalehandle *scalehandle_new(t_object *x, t_glist *glist, int scale,
     h->h_scale = scale;
     h->h_offset_x = 0;
     h->h_offset_y = 0;
+    h->h_adjust_x = 0;
+    h->h_adjust_y = 0;
     h->h_vis = 0;
+    h->h_constrain = 0;
     sprintf(h->h_pathname, ".x%lx.h%lx", (t_int)h->h_glist, (t_int)h);
     h->h_clickfn = chf;
     h->h_motionfn = mhf;
@@ -814,8 +823,10 @@ void scalehandle_dragon_label(t_scalehandle *h, float mouse_x, float mouse_y)
     if (h->h_dragon && !h->h_scale)
     {
         t_iemgui *x = (t_iemgui *)(h->h_master);
-        int dx = (int)mouse_x - (int)h->h_offset_x,
-            dy = (int)mouse_y - (int)h->h_offset_y;
+        int dx = (h->h_constrain == CURSOR_EDITMODE_RESIZE_Y) ? 0 :
+            (int)mouse_x - (int)h->h_offset_x,
+        dy = (h->h_constrain == CURSOR_EDITMODE_RESIZE_X) ? 0 :
+            (int)mouse_y - (int)h->h_offset_y;
         h->h_dragx = dx;
         h->h_dragy = dy;
 
@@ -841,6 +852,11 @@ void scalehandle_dragon_label(t_scalehandle *h, float mouse_x, float mouse_y)
                 x,
                 x->x_ldx,
                 x->x_ldy);
+            gui_vmess("gui_iemgui_label_displace_drag_handle", "xxii",
+                canvas,
+                x,
+                dx,
+                dy);
         }
     }
 }
@@ -914,15 +930,25 @@ void scalehandle_drag_scale(t_scalehandle *h) {
 static void scalehandle_clickhook(t_scalehandle *h, t_floatarg f,
     t_floatarg xxx, t_floatarg yyy)
 {
-    h->h_offset_x=xxx;
-    h->h_offset_y=yyy;
-    h->h_clickfn(h,f);
+    /* The label scalehandles do an end run around canvas_doclick,
+       which is where the cursor gets set. So we go ahead and set
+       the cursor here, too. "f" is our cursor number as defined
+       by the CURSOR_* macros in canvas.h */
+    canvas_setcursor(h->h_glist, (int)f);
+    /* We also go ahead and set h_constrain here so we don't have
+       to do it separately for each widget. Any widget that wants
+       to constrain movement along an axis can just check that
+       field against the CURSOR_* values in the motionhook callback. */
+    h->h_constrain = f;
+    h->h_offset_x = xxx;
+    h->h_offset_y = yyy;
+    h->h_clickfn(h, f);
 }
 
 static void scalehandle_motionhook(t_scalehandle *h,
     t_floatarg f1, t_floatarg f2)
 {
-    h->h_motionfn(h,f1,f2);
+    h->h_motionfn(h, f1, f2);
     // Now set the offset to the new mouse position
     h->h_offset_x = f1;
     h->h_offset_y = f2;
@@ -948,7 +974,8 @@ static void scalehandle_check_and_redraw(t_iemgui *x)
 //----------------------------------------------------------------
 // IEMGUI refactor (by Mathieu)
 
-void iemgui_tag_selected(t_iemgui *x) {
+void iemgui_tag_selected(t_iemgui *x)
+{
     t_canvas *canvas=glist_getcanvas(x->x_glist);
     if (x->x_selected)
         gui_vmess("gui_gobj_select", "xx", canvas, x);
@@ -956,7 +983,8 @@ void iemgui_tag_selected(t_iemgui *x) {
         gui_vmess("gui_gobj_deselect", "xx", canvas, x);
 }
 
-void iemgui_label_draw_new(t_iemgui *x) {
+void iemgui_label_draw_new(t_iemgui *x)
+{
     char col[8];
     t_canvas *canvas=glist_getcanvas(x->x_glist);
     int x1=text_xpix(&x->x_obj, x->x_glist)+x->legacy_x;
@@ -994,7 +1022,8 @@ void iemgui_label_draw_move(t_iemgui *x)
         x->x_ldy + x->legacy_y);
 }
 
-void iemgui_label_draw_config(t_iemgui *x) {
+void iemgui_label_draw_config(t_iemgui *x)
+{
     char col[8];
     t_canvas *canvas=glist_getcanvas(x->x_glist);
     if (x->x_selected == canvas && x->x_glist == canvas)
@@ -1036,15 +1065,16 @@ void iemgui_label_draw_config(t_iemgui *x) {
     if (x->x_selected == canvas && x->x_glist == canvas)
     {
         t_scalehandle *lh = (t_scalehandle *)(x->x_lhandle);
-        if (x->x_lab==s_empty)    
+        if (x->x_lab == s_empty)
             scalehandle_draw_erase(x->x_lhandle);
         else if (lh->h_vis == 0)
             scalehandle_draw_select(lh,x->x_ldx,x->x_ldy);
     }
 }
 
-void iemgui_label_draw_select(t_iemgui *x) {
-    t_canvas *canvas=glist_getcanvas(x->x_glist);
+void iemgui_label_draw_select(t_iemgui *x)
+{
+    t_canvas *canvas = glist_getcanvas(x->x_glist);
     if (x->x_selected == canvas && x->x_glist == canvas)
     {
         gui_vmess("gui_iemgui_label_select", "xxi",
@@ -1070,9 +1100,10 @@ void iemgui_draw_io(t_iemgui *x, int old_sr_flags)
     t_canvas *canvas=glist_getcanvas(x->x_glist);
     if (x->x_glist != canvas) return; // is gop
     t_class *c = pd_class((t_pd *)x);
-    if (c==my_numbox_class && ((t_my_numbox *)x)->x_hide_frame > 1) return; //sigh
-
-    if (!(old_sr_flags&4) && !glist_isvisible(canvas)) {
+    if (c == my_numbox_class && ((t_my_numbox *)x)->x_hide_frame > 1)
+        return; //sigh
+    if (!(old_sr_flags&4) && !glist_isvisible(canvas))
+    {
         return;
     }
     if (c==my_canvas_class) return;
@@ -1087,7 +1118,9 @@ void iemgui_draw_io(t_iemgui *x, int old_sr_flags)
         n = 0;
     int a=old_sr_flags&IEM_GUI_OLD_SND_FLAG;
     int b=x->x_snd!=s_empty;
-    //fprintf(stderr,"%lx SND: old_sr_flags=%d SND_FLAG=%d || OUTCOME: OLD_SND_FLAG=%d not_empty=%d\n", (t_int)x, old_sr_flags, IEM_GUI_OLD_SND_FLAG, a, b);
+    //fprintf(stderr, "%lx SND: old_sr_flags=%d SND_FLAG=%d || "
+    //                "OUTCOME: OLD_SND_FLAG=%d not_empty=%d\n",
+    //  (t_int)x, old_sr_flags, IEM_GUI_OLD_SND_FLAG, a, b);
     
     if (a && !b)
     {
@@ -1136,7 +1169,8 @@ void iemgui_draw_io(t_iemgui *x, int old_sr_flags)
     }
 }
 
-void iemgui_io_draw_move(t_iemgui *x) {
+void iemgui_io_draw_move(t_iemgui *x)
+{
     char tagbuf[MAXPDSTRING];
     t_canvas *canvas=glist_getcanvas(x->x_glist);
     t_class *c = pd_class((t_pd *)x);
@@ -1180,13 +1214,15 @@ void iemgui_io_draw_move(t_iemgui *x) {
     }
 }
 
-void iemgui_base_draw_new(t_iemgui *x) {
+void iemgui_base_draw_new(t_iemgui *x)
+{
     t_canvas *canvas=glist_getcanvas(x->x_glist);
     t_class *c = pd_class((t_pd *)x);
-    int x1,y1,x2,y2,gr=gop_redraw; gop_redraw=0;
-    c->c_wb->w_getrectfn((t_gobj *)x,x->x_glist,&x1,&y1,&x2,&y2);
+    int x1, y1, x2, y2, gr = gop_redraw;
+    gop_redraw = 0;
+    c->c_wb->w_getrectfn((t_gobj *)x, x->x_glist, &x1, &y1, &x2, &y2);
     //iemgui_getrect_draw(x, &x1, &y1, &x2, &y2); 
-    gop_redraw=gr;
+    gop_redraw = gr;
     char colorbuf[MAXPDSTRING];
     sprintf(colorbuf, "#%6.6x", x->x_bcol);
     gui_vmess("gui_gobj_new", "xxsiii", canvas, x,
@@ -1203,7 +1239,8 @@ void iemgui_base_draw_new(t_iemgui *x) {
         canvas, x, colorbuf);
 }
 
-void iemgui_base_draw_move(t_iemgui *x) {
+void iemgui_base_draw_move(t_iemgui *x)
+{
     t_canvas *canvas=glist_getcanvas(x->x_glist);
     t_class *c = pd_class((t_pd *)x);
     int x1,y1,x2,y2,gr=gop_redraw; gop_redraw=0;
@@ -1214,8 +1251,8 @@ void iemgui_base_draw_move(t_iemgui *x) {
         canvas, x, x1, y1, x2, y2);
 }
 
-void iemgui_base_draw_config(t_iemgui *x) {
-    
+void iemgui_base_draw_config(t_iemgui *x)
+{
     t_canvas *canvas=glist_getcanvas(x->x_glist);
     char fcol[8]; sprintf(fcol,"#%6.6x", x->x_fcol);
     char tagbuf[MAXPDSTRING];
@@ -1225,44 +1262,52 @@ void iemgui_base_draw_config(t_iemgui *x) {
         canvas, x, bcol); 
 }
 
-void iemgui_draw_update(t_iemgui *x, t_glist *glist) {
+void iemgui_draw_update(t_iemgui *x, t_glist *glist)
+{
     x->x_draw(x, x->x_glist, IEM_GUI_DRAW_MODE_UPDATE);
 }
 
-void iemgui_draw_new(t_iemgui *x) {
+void iemgui_draw_new(t_iemgui *x)
+{
     x->x_draw(x, x->x_glist, IEM_GUI_DRAW_MODE_NEW);
     iemgui_label_draw_new(x);
     iemgui_draw_io(x,7);
-    canvas_raise_all_cords(glist_getcanvas(x->x_glist)); // used to be inside x_draw
+    // used to be inside x_draw...
+    canvas_raise_all_cords(glist_getcanvas(x->x_glist));
 }
 
-void iemgui_draw_config(t_iemgui *x) {
+void iemgui_draw_config(t_iemgui *x)
+{
     x->x_draw(x, x->x_glist, IEM_GUI_DRAW_MODE_CONFIG);
     iemgui_label_draw_config(x);
     //iemgui_base_draw_config(x); // can't
 }
 
-void iemgui_draw_move(t_iemgui *x) {
+void iemgui_draw_move(t_iemgui *x)
+{
     x->x_draw(x, x->x_glist, IEM_GUI_DRAW_MODE_MOVE);
     iemgui_label_draw_move(x);
     //iemgui_base_draw_move(x); // can't
     iemgui_io_draw_move(x);
 }
 
-void iemgui_draw_erase(t_iemgui *x) {
+void iemgui_draw_erase(t_iemgui *x)
+{
     t_canvas *canvas=glist_getcanvas(x->x_glist);
     gui_vmess("gui_gobj_erase", "xx", canvas, x);
     scalehandle_draw_erase2(x);
 }
 
-void scrollbar_update(t_glist *glist) {
+void scrollbar_update(t_glist *glist)
+{
     //ico@bukvic.net 100518 update scrollbars when object potentially
     //exceeds window size
     t_canvas *canvas=(t_canvas *)glist_getcanvas(glist);
     canvas_getscroll(canvas);
 }
 
-void wb_init(t_widgetbehavior *wb, t_getrectfn gr, t_clickfn cl) {
+void wb_init(t_widgetbehavior *wb, t_getrectfn gr, t_clickfn cl)
+{
     wb->w_getrectfn = gr;
     wb->w_displacefn = iemgui_displace;
     wb->w_selectfn = iemgui_select;
@@ -1273,13 +1318,15 @@ void wb_init(t_widgetbehavior *wb, t_getrectfn gr, t_clickfn cl) {
     wb->w_displacefnwtag = iemgui_displace_withtag;
 }
 
-const char *iemgui_typeface(t_iemgui *x) {
+const char *iemgui_typeface(t_iemgui *x)
+{
     int f = x->x_font_style;
     if(f == 0) return sys_font;
     if(f == 1) return "helvetica";
     if(f == 2) return "times";
     return "invalid-font";
 }
+
 // this uses a static buffer, so don't use it twice in the same sys_vgui.
 // the static buffer could be replaced by a malloc when sys_vgui is replaced
 // by something that frees that memory.
@@ -1287,7 +1334,9 @@ const char *iemgui_typeface(t_iemgui *x) {
    separate arg so we don't have to parse on the gui side.
    Once we check to make sure all iemguis work without it we can safely
    remove it */
-const char *iemgui_font(t_iemgui *x) {
+
+const char *iemgui_font(t_iemgui *x)
+{
     static char buf[64];
     sprintf(buf, "{{%s} -%d %s}", iemgui_typeface(x), x->x_fontsize, sys_fontweight);
     return buf;
@@ -1295,7 +1344,8 @@ const char *iemgui_font(t_iemgui *x) {
 
 void iemgui_init(t_iemgui *x, t_floatarg f) {x->x_loadinit = f!=0.0;}
 
-void iemgui_class_addmethods(t_class *c) {
+void iemgui_class_addmethods(t_class *c)
+{
     class_addmethod(c, (t_method)iemgui_delta,
         gensym("delta"), A_GIMME, 0);
     class_addmethod(c, (t_method)iemgui_pos,
@@ -1314,7 +1364,8 @@ void iemgui_class_addmethods(t_class *c) {
         gensym("label_font"), A_GIMME, 0);
 }
 
-void g_iemgui_setup (void) {
+void g_iemgui_setup (void)
+{
     s_empty = gensym("empty");
     scalehandle_class = class_new(gensym("_scalehandle"), 0, 0,
                   sizeof(t_scalehandle), CLASS_PD, 0);
@@ -1330,16 +1381,21 @@ const char *border_color = "$pd_colors(iemgui_border)";
 #define GET_OUTLET t_outlet *out = x->x_obj.ob_outlet; /* can't use int o because there's not obj_nth_outlet function */
 #define SEND_BY_SYMBOL (iemgui_has_snd(x) && x->x_snd->s_thing && (!chk_putin || x->x_put_in2out))
 
-void iemgui_out_bang(t_iemgui *x, int o, int chk_putin) {
+void iemgui_out_bang(t_iemgui *x, int o, int chk_putin)
+{
     GET_OUTLET outlet_bang(out);
     if(SEND_BY_SYMBOL) pd_bang(x->x_snd->s_thing);
 }
-void iemgui_out_float(t_iemgui *x, int o, int chk_putin, t_float f) {
+
+void iemgui_out_float(t_iemgui *x, int o, int chk_putin, t_float f)
+{
     GET_OUTLET outlet_float(out,f);
     if(SEND_BY_SYMBOL) pd_float(x->x_snd->s_thing,f);
 }
-void iemgui_out_list(t_iemgui *x, int o, int chk_putin, t_symbol *s, int argc, t_atom *argv) {
-    GET_OUTLET outlet_list(out,s,argc,argv);
-    if(SEND_BY_SYMBOL) pd_list(x->x_snd->s_thing,s,argc,argv);
-}
 
+void iemgui_out_list(t_iemgui *x, int o, int chk_putin, t_symbol *s,
+    int argc, t_atom *argv)
+{
+    GET_OUTLET outlet_list(out, s, argc, argv);
+    if (SEND_BY_SYMBOL) pd_list(x->x_snd->s_thing, s, argc, argv);
+}
diff --git a/pd/src/g_all_guis.h b/pd/src/g_all_guis.h
index 81549d3e194f97f19ae2c3c3e0fc3ffd134f3dfc..a9d6768a01fd1c9b5504d2ffa4fd38840f53097a 100644
--- a/pd/src/g_all_guis.h
+++ b/pd/src/g_all_guis.h
@@ -27,10 +27,10 @@
 #define SCALE_NUM_MINHEIGHT 8
 #define SCALE_GOP_MINWIDTH 12
 #define SCALE_GOP_MINHEIGHT 12
-#define SCALEHANDLE_WIDTH   5
-#define SCALEHANDLE_HEIGHT  5
-#define LABELHANDLE_WIDTH   5
-#define LABELHANDLE_HEIGHT  5
+#define SCALEHANDLE_WIDTH   14
+#define SCALEHANDLE_HEIGHT  14
+#define LABELHANDLE_WIDTH   14
+#define LABELHANDLE_HEIGHT  14
 
 typedef void (*t_iemfunptr)(void *x, t_glist *glist, int mode);
 
@@ -46,7 +46,7 @@ typedef struct _scalehandle
     t_object  *h_master;
     t_glist   *h_glist; // this is the canvas to draw on. Note that when objects are edited, "glist" and "canvas" mean the same.
     t_symbol  *h_bindsym;
-    int        h_scale; // bool
+    int        h_scale;
     char       h_pathname[37]; // max size for ".x%lx.h%lx" = 5+4*sizeof(long)
     char       h_outlinetag[18]; // max size for "h%lx" = 2+2*sizeof(long)
     int        h_dragon; // bool
@@ -54,6 +54,9 @@ typedef struct _scalehandle
     int        h_dragy;
     int        h_offset_x;
     int        h_offset_y;
+    int        h_adjust_x;
+    int        h_adjust_y;
+    int        h_constrain;
     int        h_vis; // bool
     t_clickhandlefn h_clickfn;
     t_motionhandlefn h_motionfn;
diff --git a/pd/src/g_canvas.c b/pd/src/g_canvas.c
index 47b5ebfcd446dca1ebdeea6dff22508db1ee8c5b..787f14c9f3cca588dbfb85e952f3ae74c19f96dc 100644
--- a/pd/src/g_canvas.c
+++ b/pd/src/g_canvas.c
@@ -340,6 +340,10 @@ void linetraverser_skipobject(t_linetraverser *t)
 /* -------------------- the canvas object -------------------------- */
 int glist_valid = 10000;
 
+void canvasgop__clickhook(t_scalehandle *sh, int newstate);
+void canvasgop__motionhook(t_scalehandle *sh,t_floatarg f1, t_floatarg f2);
+extern void glist_setlastxy(t_glist *gl, int xval, int yval);
+
 void glist_init(t_glist *x)
 {
         /* zero out everyone except "pd" field */
@@ -348,11 +352,13 @@ void glist_init(t_glist *x)
     x->gl_valid = ++glist_valid;
     x->gl_xlabel = (t_symbol **)t_getbytes(0);
     x->gl_ylabel = (t_symbol **)t_getbytes(0);
-}
 
-void canvasgop__clickhook(t_scalehandle *sh, int newstate);
-void canvasgop__motionhook(t_scalehandle *sh,t_floatarg f1, t_floatarg f2);
-extern void glist_setlastxy(t_glist *gl, int xval, int yval);
+    //dpsaha@vt.edu gop resize (refactored by mathieu)
+    x->x_handle = scalehandle_new((t_object *)x, x, 1,
+        canvasgop__clickhook, canvasgop__motionhook);
+    x->x_mhandle = scalehandle_new((t_object *)x, x, 0,
+        canvasgop__clickhook, canvasgop__motionhook);
+}
 
 /* These globals are used to set state for the "canvas" field in a
    struct. We try below to make sure they only get set for the toplevel
@@ -488,10 +494,6 @@ t_canvas *canvas_new(void *dummy, t_symbol *sel, int argc, t_atom *argv)
     x->gl_zoom = zoom;
     pd_pushsym(&x->gl_pd);
 
-    //dpsaha@vt.edu gop resize (refactored by mathieu)
-    x-> x_handle = scalehandle_new((t_object *)x,x,1,canvasgop__clickhook,canvasgop__motionhook);
-    x->x_mhandle = scalehandle_new((t_object *)x,x,0,canvasgop__clickhook,canvasgop__motionhook);
-
     x->u_queue = canvas_undo_init(x);
     //glist_setlastxy(x, 20, 20);
 
@@ -985,7 +987,7 @@ void canvas_free(t_canvas *x)
     if (x->gl_editor)
         canvas_destroy_editor(x);   /* bug workaround; should already be gone*/
 
-    if (x-> x_handle) scalehandle_free( x->x_handle);
+    if (x->x_handle) scalehandle_free(x->x_handle);
     if (x->x_mhandle) scalehandle_free(x->x_mhandle);
     
     canvas_unbind(x);
@@ -2318,108 +2320,50 @@ void canvasgop_checksize(t_canvas *x)
 void canvasgop__clickhook(t_scalehandle *sh, int newstate)
 {
     t_canvas *x = (t_canvas *)(sh->h_master);
-    if (sh->h_dragon && newstate == 0)
-    {
-        /* done dragging */
-        if(sh->h_scale) //enter if resize_gop hook
-        {
-            canvas_undo_add(x, 8, "apply", canvas_undo_set_canvas(x));
 
-            if (sh->h_dragx || sh->h_dragy) 
-            {
-                x->gl_pixwidth += sh->h_dragx;
-                x->gl_pixheight += sh->h_dragy;
-                // check if the text is not hidden. if so, make minimum
-                // width and height based retrieved from getrect.
-                if (!x->gl_hidetext)
-                {
-                    int x1=0, y1=0, x2=0, y2=0;
-                    if (x->gl_owner)
-                    {
-                        gobj_getrect((t_gobj*)x, x->gl_owner,
-                            &x1, &y1, &x2, &y2);
-                    }
-                    else
-                    {
-                        graph_checkgop_rect((t_gobj*)x, x, &x1, &y1, &x2, &y2);
-                    }
-                    if (x2-x1 > x->gl_pixwidth) x->gl_pixwidth = x2-x1;
-                    if (y2-y1 > x->gl_pixheight) x->gl_pixheight = y2-y1;
-                }
-                canvas_dirty(x, 1);
-            }
-
-            // can't remove this update because the text size check above is not in motionhook
-            int properties = gfxstub_haveproperties((void *)x);
-            if (properties)
-            {
-                properties_set_field_int(properties,"x_pix",x->gl_pixwidth);
-                properties_set_field_int(properties,"y_pix",x->gl_pixheight);
-            }
+    /* So ugly: if the user is dragging the bottom right-hand corner of
+       a gop subcanvas on the parent, we already set an undo event for it.
+       So we only add one here for resizing the gop red rectangle, or for
+       moving it with the red scalehandle. */
+    if (sh->h_scale != 1)
+        canvas_undo_add(x, 8, "apply", canvas_undo_set_canvas(x));
 
-            if (glist_isvisible(x))
-            {
-                /* Still not sure what this is doing... */
-                //sys_vgui(".x%x.c delete %s\n", x, sh->h_outlinetag);
-                canvasgop_draw_move(x,1);
-                canvas_fixlinesfor(x, (t_text *)x);
-                scrollbar_update(x);
-            }
-        }
-        else //enter if move_gop hook
-        {
-            // this block is similar to scalehandle_unclick_label but not enough
-            // We've actually removed scalehandle_unclick_label everywhere, so
-            // check to see whether this can be removed as well...
-            canvas_undo_add(x, 8, "apply", canvas_undo_set_canvas(x));
-            if (sh->h_dragx || sh->h_dragy) 
-            {
-                x->gl_xmargin += sh->h_dragx;
-                x->gl_ymargin += sh->h_dragy;
-                canvas_dirty(x, 1);
-            }
-            if (glist_isvisible(x))
-            {
-                canvasgop_draw_move(x,1);
-                canvas_fixlinesfor(x, (t_text *)x);
-                scrollbar_update(x);
-            }
-        }
+    /* We're abusing h_scale to differentiate between clicking gop red
+       rectangle and clicking the corner of a subcanvas on the parent */
+    if (sh->h_scale == 1) /* clicking corner of gop subcanvas on parent */
+    {
+        /* This is for clicking on the bottom right-hand corner of the
+           gop canvas when it's displayed on the parent canvas. */
+        sh->h_adjust_x = sh->h_offset_x -
+            (((t_object *)x)->te_xpix + x->gl_pixwidth);
+        sh->h_adjust_y = sh->h_offset_y -
+            (((t_object *)x)->te_ypix + x->gl_pixheight);
     }
-    else if (!sh->h_dragon && newstate)
+    else if (sh->h_scale == 2) /* resize gop hook for (red) gop rect */
     {
-        /* set undo */
-        canvas_undo_add(x, 8, "apply", canvas_undo_set_canvas(x));
+        /* Store an adjustment for difference between the initial
+           pointer position-- which is within five pixels or so-- and the
+           bottom right-hand corner. Otherwise we'd get a "jump" from the
+           the current dimensions to the pointer offset. Such a jump would
+           become noticeable with constrained dragging, as the constrained
+           dimension would initially jump to the pointer position.
 
-        if(sh->h_scale) //enter if resize_gop hook
-        {
-            /* We could port this, but it might be better to wait until we
-               just move the scalehandle stuff directly to the GUI... */
-            //sys_vgui("lower %s\n", sh->h_pathname);
-
-            //delete GOP rect where it started from
-            /* Doesn't look like we're using this anymore, so no need to
-               port it. */
-            //sys_vgui(".x%lx.c delete GOP\n", x);
-            //sys_vgui(".x%x.c create rectangle %d %d %d %d "
-            //     "-outline $pd_colors(selection) -width 1 -tags %s\n",
-            //     x, x->gl_xmargin, x->gl_ymargin,
-            //        x->gl_xmargin + x->gl_pixwidth,
-            //        x->gl_ymargin + x->gl_pixheight, sh->h_outlinetag);
-        }
-        else //enter if move_gop hook
-        {
-            //scalehandle_draw_erase(sh,x);
-            /* Same as above... */
-            //sys_vgui("lower %s\n", sh->h_pathname);
+           We could alternatively use dx/dy, but then the pointer position
+           would stray when the user attempts to drag past the minimum
+           width/height of the rectangle. */
 
-            //delete GOP_resblob when moving the whole GOP
-            //sys_vgui(".x%lx.c delete %lxSCALE\n", x, x);
-        }
-        sh->h_dragx = 0;
-        sh->h_dragy = 0;
+        sh->h_adjust_x = sh->h_offset_x - (x->gl_xmargin + x->gl_pixwidth);
+        sh->h_adjust_y = sh->h_offset_y - (x->gl_ymargin + x->gl_pixheight);
+
+        /* We could port this, but it might be better to wait until we
+           just move the scalehandle stuff directly to the GUI... */
+        //sys_vgui("lower %s\n", sh->h_pathname);
+    }
+    else /* move_gop hook */
+    {
+        /* Same as above... */
+        //sys_vgui("lower %s\n", sh->h_pathname);
     }
-    sh->h_dragon = newstate;
 }
 
 void canvasgop__motionhook(t_scalehandle *sh, t_floatarg mouse_x,
@@ -2428,51 +2372,111 @@ void canvasgop__motionhook(t_scalehandle *sh, t_floatarg mouse_x,
     t_canvas *x = (t_canvas *)(sh->h_master);
     int dx = (int)mouse_x - sh->h_offset_x,
         dy = (int)mouse_y - sh->h_offset_y;
-    
-    if (sh->h_dragon)
-    {
-        if (sh->h_scale) //enter if resize_gop hook
+
+    if (sh->h_scale == 1) /* resize the gop on the parent */
+    {
+        int tmpx1 = 0, tmpy1 = 0, tmpx2 = 0, tmpy2 = 0;
+        int tmp_x_final = 0, tmp_y_final = 0;
+        /* The member h_glist currently points our current glist x. I think
+           that is being used to draw the gop red rect move anchor atm. So
+           rather than muck around with that code, we just set a pointer to
+           whatever our toplevel is here: */
+        t_glist *owner = canvas_getrootfor(x);
+        /* Just unvis the object, then vis it once we've done our
+           mutation and checks */
+        gobj_vis((t_gobj *)x, owner, 0);
+        /* struct _glist has its own member e_xnew for storing our offset.
+           At some point we need to refactor since our t_scalehandle has
+           members for storing offsets already. */
+        x->gl_pixwidth = (sh->h_constrain == CURSOR_EDITMODE_RESIZE_Y) ?
+            x->gl_pixwidth :
+            (int)mouse_x - ((t_object *)x)->te_xpix - sh->h_adjust_x;
+        x->gl_pixheight = (sh->h_constrain == CURSOR_EDITMODE_RESIZE_X) ?
+            x->gl_pixheight :
+            (int)mouse_y - ((t_object *)x)->te_ypix - sh->h_adjust_y;
+        /* If the box text is not hidden then it sets a lower boundary for
+           box size... */
+        graph_checkgop_rect((t_gobj *)x, owner, &tmpx1, &tmpy1, &tmpx2,
+            &tmpy2);
+        tmpx1 = ((t_object *)x)->te_xpix;
+        tmpy1 = ((t_object *)x)->te_ypix;
+        //fprintf(stderr,"%d %d %d %d\n", tmpx1, tmpy1, tmpx2, tmpy2);
+        if (!x->gl_hidetext)
         {
-            int width = mouse_x - x->gl_xmargin,
-                height = mouse_y - x->gl_ymargin;
-            x->gl_pixwidth = width = maxi(SCALE_GOP_MINWIDTH, width);
-            x->gl_pixheight = height = maxi(SCALE_GOP_MINHEIGHT, height);
-            gui_vmess("gui_canvas_redrect_coords", "xiiii",
-                x,
-                x->gl_xmargin,
-                x->gl_ymargin,
-                x->gl_xmargin + width,
-                x->gl_ymargin + height);
-            int properties = gfxstub_haveproperties((void *)x);
-            if (properties)
-            {
-                properties_set_field_int(properties,
-                    "x_pix",x->gl_pixwidth + sh->h_dragx);
-                properties_set_field_int(properties,
-                    "y_pix",x->gl_pixheight + sh->h_dragy);
-            }
+            tmp_x_final = tmpx2 - tmpx1;
+            tmp_y_final = tmpy2 - tmpy1;
         }
-        else //enter if move_gop hook
+        else
         {
-            int properties = gfxstub_haveproperties((void *)x);
-            if (properties)
-            {
-                properties_set_field_int(properties,
-                    "x_margin",x->gl_xmargin + dx);
+            tmp_x_final = tmpx2;
+            tmp_y_final = tmpy2;
+        }
+        if (tmp_x_final > x->gl_pixwidth)
+            x->gl_pixwidth = tmp_x_final;
+        if (tmp_y_final > x->gl_pixheight)
+            x->gl_pixheight = tmp_y_final;
+        owner->gl_editor->e_xnew = mouse_x;
+        owner->gl_editor->e_ynew = mouse_y;
+        canvas_fixlinesfor(owner, (t_text *)x);
+        gobj_vis((t_gobj *)x, owner, 1);
+        canvas_dirty(owner, 1);
+
+        int properties = gfxstub_haveproperties((void *)x);
+        if (properties)
+        {
+            properties_set_field_int(properties,
+                "x_pix",x->gl_pixwidth + sh->h_dragx);
+            properties_set_field_int(properties,
+                "y_pix",x->gl_pixheight + sh->h_dragy);
+        }
+    }
+    else if (sh->h_scale == 2) /* resize_gop red rect hook */
+    {
+        int width = (sh->h_constrain == CURSOR_EDITMODE_RESIZE_Y) ?
+            x->gl_pixwidth :
+            (int)mouse_x - x->gl_xmargin - sh->h_adjust_x;
+        int height = (sh->h_constrain == CURSOR_EDITMODE_RESIZE_X) ?
+            x->gl_pixheight :
+            (int)mouse_y - x->gl_ymargin - sh->h_adjust_y;
+        x->gl_pixwidth = width = maxi(SCALE_GOP_MINWIDTH, width);
+        x->gl_pixheight = height = maxi(SCALE_GOP_MINHEIGHT, height);
+        gui_vmess("gui_canvas_redrect_coords", "xiiii",
+            x,
+            x->gl_xmargin,
+            x->gl_ymargin,
+            x->gl_xmargin + width,
+            x->gl_ymargin + height);
+        int properties = gfxstub_haveproperties((void *)x);
+        if (properties)
+        {
+            properties_set_field_int(properties,
+                "x_pix",x->gl_pixwidth + sh->h_dragx);
+            properties_set_field_int(properties,
+                "y_pix",x->gl_pixheight + sh->h_dragy);
+        }
+    }
+    else /* move_gop hook */
+    {
+        int properties = gfxstub_haveproperties((void *)x);
+        if (properties)
+        {
+            properties_set_field_int(properties,
+                "x_margin",x->gl_xmargin + dx);
                 properties_set_field_int(properties,
-                    "y_margin",x->gl_ymargin + dy);
-            }
+                "y_margin",x->gl_ymargin + dy);
+        }
+        if (sh->h_constrain != CURSOR_EDITMODE_RESIZE_Y)
             x->gl_xmargin += dx;
+        if (sh->h_constrain != CURSOR_EDITMODE_RESIZE_X)
             x->gl_ymargin += dy;
 
-            int x1 = x->gl_xmargin, x2 = x1+x->gl_pixwidth;
-            int y1 = x->gl_ymargin, y2 = y1+x->gl_pixheight;
+        int x1 = x->gl_xmargin, x2 = x1 + x->gl_pixwidth;
+        int y1 = x->gl_ymargin, y2 = y1 + x->gl_pixheight;
 
-            gui_vmess("gui_canvas_redrect_coords", "xiiii",
-                x, x1, y1, x2, y2);
-            sh->h_dragx = dx;
-            sh->h_dragy = dy;            
-        }
+        gui_vmess("gui_canvas_redrect_coords", "xiiii",
+            x, x1, y1, x2, y2);
+        sh->h_dragx = dx;
+        sh->h_dragy = dy;
     }
 }
 
@@ -2516,7 +2520,7 @@ extern void glist_scalar(t_glist *canvas, t_symbol *s, int argc, t_atom *argv);
 void g_graph_setup(void);
 void g_editor_setup(void);
 void g_readwrite_setup(void);
-extern void canvas_properties(t_gobj *z);
+extern void canvas_properties(t_gobj *z, t_glist *dummy);
 
 void g_canvas_setup(void)
 {
diff --git a/pd/src/g_canvas.h b/pd/src/g_canvas.h
index a08ef13d6376186e2b7338957fa1219b352b35f2..a3b5f7cd5ea4fb2abbaedb51ffd0585e45714320 100644
--- a/pd/src/g_canvas.h
+++ b/pd/src/g_canvas.h
@@ -125,8 +125,8 @@ typedef struct _editor
     unsigned int e_textdirty: 1;    /* one if e_textedfor has changed */
     unsigned int e_selectedline: 1; /* one if a line is selected */
     t_magicGlass *gl_magic_glass;   /* magic glass object */
-	char canvas_cnct_inlet_tag[4096]; /* tags for currently highlighted nlets */
-	char canvas_cnct_outlet_tag[4096];
+    char canvas_cnct_inlet_tag[4096]; /* tags for currently highlighted nlets */
+    char canvas_cnct_outlet_tag[4096];
     t_clock *e_clock;               /* clock to filter GUI move messages */
     int e_xnew;                     /* xpos for next move event */
     int e_ynew;                     /* ypos, similarly */
@@ -137,7 +137,8 @@ typedef struct _editor
 #define MA_CONNECT 2    /* make a connection */
 #define MA_REGION  3    /* selection region */
 #define MA_PASSOUT 4    /* send on to e_grab */
-#define MA_DRAGTEXT 5   /* drag in text editor to alter selection */
+/* Text edition handled completely in the GUI now */
+//#define MA_DRAGTEXT 5   /* drag in text editor to alter selection */
 #define MA_RESIZE  6    /* drag to resize */
 #define MA_SCROLL  7    /* scroll with middle click onto empty canvas */
 
@@ -151,8 +152,8 @@ typedef struct _arrayvis
     t_garray *av_garray;            /* owning structure */    
 } t_arrayvis;
 
-t_garray* array_garray;				/* used for sending bangs when
-									   array is changed  via gui */
+t_garray* array_garray;    /* used for sending bangs when
+                              array is changed  via gui */
 
 /* the t_tick structure describes where to draw x and y "ticks" for a glist */
 
@@ -171,7 +172,7 @@ area of a window.
 //#include "g_undo.h"
 
 struct _glist
-{  
+{
     t_object gl_obj;            /* header in case we're a glist */
     t_gobj *gl_list;            /* the actual data */
     struct _gstub *gl_stub;     /* safe pointer handler */
@@ -403,9 +404,11 @@ struct _parentwidgetbehavior
 #define CURSOR_EDITMODE_NOTHING 4
 #define CURSOR_EDITMODE_CONNECT 5
 #define CURSOR_EDITMODE_DISCONNECT 6
-#define CURSOR_EDITMODE_RESIZE 7
-#define CURSOR_EDITMODE_RESIZE_BOTTOM_RIGHT 8
+#define CURSOR_EDITMODE_RESIZE_X 7
+#define CURSOR_EDITMODE_RESIZE 8
 #define CURSOR_SCROLL 9
+#define CURSOR_EDITMODE_RESIZE_Y 10
+#define CURSOR_EDITMODE_MOVE 11
 EXTERN void canvas_setcursor(t_glist *x, unsigned int cursornum);
 
 extern t_canvas *canvas_editing;    /* last canvas to start text edting */ 
@@ -496,10 +499,6 @@ EXTERN int text_xpix(t_text *x, t_glist *glist);
 EXTERN int text_ypix(t_text *x, t_glist *glist);
 
 /* -------------------- functions on rtexts ------------------------- */
-#define RTEXT_DOWN 1
-#define RTEXT_DRAG 2
-#define RTEXT_DBL 3
-#define RTEXT_SHIFT 4
 
 // number in comment is the number in grep -w|wc
 EXTERN t_rtext *rtext_new(t_glist *glist, t_text *who); //5
@@ -736,7 +735,7 @@ EXTERN t_symbol *iemgui_dollar2raute(t_symbol *s);
 
 EXTERN t_undo_action *canvas_undo_init(t_canvas *x);
 EXTERN t_undo_action *canvas_undo_add(t_canvas *x,
-	int type, const char *name, void *data);
+    int type, const char *name, void *data);
 EXTERN void canvas_undo_undo(t_canvas *x);
 EXTERN void canvas_undo_redo(t_canvas *x);
 EXTERN void canvas_undo_rebranch(t_canvas *x);
diff --git a/pd/src/g_editor.c b/pd/src/g_editor.c
index 1dbcad0b6468e50b2e63b971aff515a98fedf5af..92f03ded89ea76c2a5548cf642d390e52e35b28d 100644
--- a/pd/src/g_editor.c
+++ b/pd/src/g_editor.c
@@ -2190,7 +2190,9 @@ static char *cursorlist[] = {
     "cursor_editmode_disconnect",
     "cursor_editmode_resize",
     "cursor_editmode_resize_bottom_right",
-    "cursor_scroll"
+    "cursor_scroll",
+    "cursor_editmode_resize_vert",
+    "cursor_editmode_move"
 };
 
 void canvas_setcursor(t_canvas *x, unsigned int cursornum)
@@ -2650,8 +2652,9 @@ int garray_properties(t_garray *x, t_symbol **gfxstubp, t_symbol **namep,
     /* tell GUI to create a properties dialog on the canvas.  We tell
     the user the negative of the "pixel" y scale to make it appear to grow
     naturally upward, whereas pixels grow downward. */
-void canvas_properties(t_glist *x)
+void canvas_properties(t_gobj *z, t_glist *dummy)
 {
+    t_glist *x = (t_glist *)z;
     t_gobj *y;
     //char graphbuf[200];
     char *gfx_tag;
@@ -2949,6 +2952,23 @@ static void canvas_doarrange(t_canvas *x, t_float which, t_gobj *oldy,
     glob_preset_node_list_check_loc_and_update();
 }
 
+static char *canvas_gethelpname(t_object *ob)
+{
+    if (ob->te_binbuf &&
+        binbuf_getnatom(ob->te_binbuf) &&
+        binbuf_getvec(ob->te_binbuf)->a_type == A_SYMBOL)
+    {
+        t_atom *a = binbuf_getvec(ob->te_binbuf);
+        if (a->a_w.w_symbol == gensym("draw"))
+            return "draw";
+        else if (a->a_w.w_symbol == gensym("table"))
+            return "table";
+        else return (class_gethelpname(pd_class(&ob->te_pd)));
+    }
+    else
+        return (class_gethelpname(pd_class(&ob->te_pd)));
+}
+
     /* called from the gui when a popup menu comes back with "properties,"
         "open," or "help." */
     /* Ivica Ico Bukvic <ico@bukvic.net> 2010-11-17
@@ -3077,7 +3097,10 @@ void canvas_done_popup(t_canvas *x, t_float which, t_float xpos,
                     }
                     else
                     {
-                        strncpy(namebuf, class_gethelpname(pd_class(&y->g_pd)),
+                        char *obname = (pd_class(&y->g_pd) == canvas_class) ?
+                            canvas_gethelpname((t_object *)y) :
+                            class_gethelpname(pd_class(&y->g_pd));
+                        strncpy(namebuf, obname,
                             FILENAME_MAX-1);
                         namebuf[FILENAME_MAX-1] = 0;
                         dir = class_gethelpdir(pd_class(&y->g_pd));
@@ -3122,12 +3145,75 @@ void canvas_done_popup(t_canvas *x, t_float which, t_float xpos,
     {
         if (!x->gl_edit)
             canvas_editmode(x, 1);
-        canvas_properties(x);
+        canvas_properties((t_gobj *)x, x);
     }
     else if (which == 2)
         open_via_helppath("intro.pd", canvas_getdir((t_canvas *)x)->s_name);
 }
 
+extern t_class *my_canvas_class; // for ignoring runtime clicks and resizing
+
+/* For triggering text_widgetbehavior objects, graphs, atom/dropdown, iemgui and
+   comment resizing */
+static int text_resizing_hotspot(t_canvas *x, t_object *ob, int xpos, int ypos,
+    int x1, int y1, int x2, int y2, int *typep)
+{
+    /* No resizing anchor in k12 mode, plus sanity checks */
+    if (sys_k12_mode || !ob || x->gl_editor->e_textedfor || xpos > x2 ||
+        ypos > y2)
+        return 0;
+    *typep = 0;
+        /* objects that can only be resized along x-axis: regular text
+           objects, atom boxes, and canvas without gop checked */
+    if (ob->te_pd->c_wb == &text_widgetbehavior ||
+        ob->te_type == T_ATOM ||
+        (ob->ob_pd == canvas_class && !((t_canvas *)ob)->gl_isgraph))
+    {
+        if (xpos >= x2 - 4 && ypos < y2 - 4 && ypos > y1 + 4)
+        {
+            *typep = 1;
+            return CURSOR_EDITMODE_RESIZE_X;
+        }
+    }
+
+        /* gop canvases, gop red rectangle, scope, grid, iemguis except [cnv] */
+    if ((ob->te_iemgui && ob->ob_pd != my_canvas_class) ||
+        ob->ob_pd == canvas_class ||
+        ob->ob_pd->c_name == gensym("Scope~") ||
+        ob->ob_pd->c_name == gensym("grid"))
+    {
+            /* stay out of the way of outlet in the bottom right-hand corner */
+        int offset = (obj_noutlets(ob) > 1) ? -4 : 0;
+            /* We want to disable horiz and vert anchors for [bng], [tgl],
+               [hradio] and [vradio]. These widgets only have one
+               dimension that can be resized-- the other is handled
+               automatically. */
+        int can_resize_x = (ob->ob_pd->c_name != gensym("bng") &&
+            ob->ob_pd->c_name != gensym("tgl") &&
+            ob->ob_pd->c_name != gensym("hradio") &&
+            ob->ob_pd->c_name != gensym("vradio"));
+
+        int can_resize_y = can_resize_x &&
+            (ob->ob_pd->c_name != gensym("vsl") || x2 - x1 > 15) &&
+            (ob->ob_pd->c_name != gensym("hsl") || x2 - x1 > 15);
+
+        if (can_resize_x &&
+            xpos >= x2 - 4 && ypos <= y2 + offset - 10 && ypos > y2 + offset - 24)
+            return CURSOR_EDITMODE_RESIZE_X;
+        else if (xpos >= x2 - 4 && ypos <= y2 + offset && ypos > y2 + offset - 10)
+            return CURSOR_EDITMODE_RESIZE;
+        else if (can_resize_y &&
+                 xpos >= x2 - 12 &&
+                 ypos >= y2 + offset - 5 &&
+                 ypos <= y2 + offset)
+            return CURSOR_EDITMODE_RESIZE_Y;
+        else
+            return 0;
+    }
+    else
+        return 0;
+}
+
 #define NOMOD 0
 #define SHIFTMOD 1
 #define CTRLMOD 2
@@ -3138,8 +3224,6 @@ static double canvas_upclicktime;
 static int canvas_upx, canvas_upy;
 #define DCLICKINTERVAL 0.25
 
-extern t_class *my_canvas_class; // for ignoring runtime clicks
-
     /* mouse click */
 void canvas_doclick(t_canvas *x, int xpos, int ypos, int which,
     int mod, int doit)
@@ -3151,7 +3235,8 @@ void canvas_doclick(t_canvas *x, int xpos, int ypos, int which,
     array_garray = NULL;
 
     t_gobj *y;
-    int shiftmod, runmode, altmod, doublemod = 0, rightclick;
+    int shiftmod, runmode, altmod, doublemod = 0, rightclick,
+        in_text_resizing_hotspot, default_type;
     int x1=0, y1=0, x2=0, y2=0, clickreturned = 0;
     t_gobj *yclick = NULL;
     t_object *ob;
@@ -3282,40 +3367,41 @@ void canvas_doclick(t_canvas *x, int xpos, int ypos, int which,
         }
         return;
     }
+
         /* if in editmode click, fall here. */
-    if (y = canvas_findhitbox(x, xpos, ypos, &x1, &y1, &x2, &y2))
+        /* check you're in the rectangle */
+    y = canvas_findhitbox(x, xpos, ypos, &x1, &y1, &x2, &y2);
+        /* We've got a special case for handling the gop red rectangle. In that
+           case we want all other click actions to take precedence, so we run
+           the checks here first so we can keep the same conditional ordering
+           we've had for ages. */
+    if (y)
     {
-            /* check you're in the rectangle */
         ob = pd_checkobject(&y->g_pd);
+        in_text_resizing_hotspot = text_resizing_hotspot(x, ob, xpos, ypos,
+            x1, y1, x2, y2, &default_type);
+    }
+    else
+    {
+        in_text_resizing_hotspot = text_resizing_hotspot(x, &x->gl_obj,
+            xpos, ypos, x->gl_xmargin, x->gl_ymargin,
+            x->gl_xmargin + x->gl_pixwidth,
+            x->gl_ymargin + x->gl_pixheight, &default_type);
+    }
+
+    if (y)
+    {
         if (rightclick)
             canvas_rightclick(x, xpos, ypos, y);
-        else if (shiftmod && x->gl_editor->canvas_cnct_outlet_tag[0] == 0)
+        else if (shiftmod &&
+            x->gl_editor->canvas_cnct_outlet_tag[0] == 0)
         {
             //selection (only if we are not hovering above an outlet)
             if (doit)
             {
-                t_rtext *rt;
-                if (ob && (rt = x->gl_editor->e_textedfor) &&
-                    rt == glist_findrtext(x, ob))
-                {
-                    rtext_mouse(rt, xpos - x1, ypos - y1, RTEXT_SHIFT);
-                    x->gl_editor->e_onmotion = MA_DRAGTEXT;
-                    x->gl_editor->e_xwas = x1;
-                    x->gl_editor->e_ywas = y1;
-                }
-                else
-                {
-                    //t_undo_redo_sel *buf =
-                    //    (t_undo_redo_sel *)getbytes(sizeof(*buf));
-                    //buf->u_undo = (t_undo_sel *)canvas_undo_set_selection(x);
-
-                    if (glist_isselected(x, y))
-                        glist_deselect(x, y);
-                    else glist_select(x, y);
-
-                    //buf->u_redo = (t_undo_sel *)canvas_undo_set_selection(x);
-                    //canvas_undo_add(x, 11, "selection", buf);
-                }
+                if (glist_isselected(x, y))
+                    glist_deselect(x, y);
+                else glist_select(x, y);
             }
         }
         else
@@ -3332,74 +3418,64 @@ void canvas_doclick(t_canvas *x, int xpos, int ypos, int which,
                    extends well past the bounds of the bbox. For that reason
                    we have a virtual waterfall of conditionals flowing all
                    the way to the GUI just handle resizing a stupid rectangle.
-
-                   Also, the following conditional is way too complex.
                 */
-            if (ob && (ob->te_iemgui
-                 && pd_class((t_pd *)ob) != my_canvas_class
-                 || pd_class(&ob->te_pd)->c_name == gensym("Scope~")
-                 || pd_class(&ob->te_pd)->c_name == gensym("grid"))
-                && xpos >= x2-4 && ypos > y2-6)
-            {
-                if (doit)
-                {
-                    x->gl_editor->e_onmotion = MA_RESIZE;
-                    x->gl_editor->e_xwas = x1;
-                    x->gl_editor->e_ywas = y1;
-                    x->gl_editor->e_xnew = xpos;
-                    x->gl_editor->e_ynew = ypos;
-                    if (ob->te_iemgui)
-                    {
-                        t_pd *sh = (t_pd *)((t_iemgui *)ob)->x_handle;
-                        pd_vmess(sh, gensym("_click"), "fff",
-                            (t_float)1, (t_float)xpos, (t_float)ypos);
-                    }
-                    else
-                    {
-                        pd_vmess((t_pd *)ob, gensym("_click_for_resizing"),
-                           "fff", (t_float)1, (t_float)xpos, (t_float)ypos);
-                    }
-                }
-                else
-                {
-                    canvas_setcursor(x,
-                        CURSOR_EDITMODE_RESIZE_BOTTOM_RIGHT);
-                }
-                canvas_check_nlet_highlights(x);
-            }
-            else if (!sys_k12_mode && ob && !x->gl_editor->e_textedfor &&
-                (ob->te_pd->c_wb == &text_widgetbehavior ||
-                 ob->te_type == T_ATOM ||
-                 ob->ob_pd == canvas_class) &&
-                 xpos >= x2-4 && ypos < y2-4 && ypos > y1+4)
+            if (in_text_resizing_hotspot)
             {
                 if (doit)
                 {
-                    if (!glist_isselected(x, y) || x->gl_editor->e_selection->sel_next)
+                    if (!glist_isselected(x, y) ||
+                        x->gl_editor->e_selection->sel_next)
                     {
                         glist_noselect(x);
                         glist_select(x, y);
                     }
+
                     x->gl_editor->e_onmotion = MA_RESIZE;
                     x->gl_editor->e_xwas = x1;
                     x->gl_editor->e_ywas = y1;
                     x->gl_editor->e_xnew = xpos;
                     x->gl_editor->e_ynew = ypos;
-                    canvas_undo_add(x, 6, "resize",
-                        canvas_undo_set_apply(x, glist_getindex(x, y)));
-                }                                   
-                else
-                {
-                    if (ob->ob_pd != canvas_class ||
-                        !((t_canvas *)ob)->gl_isgraph)
+                    /* For normal text objects/atom boxes, subpatches and
+                       graphs we just go ahead and set an undo point here.
+                       GUI objects have their own click callback where they
+                       do this. */
+                    int isgraph = (ob->ob_pd == canvas_class &&
+                        ((t_canvas *)ob)->gl_isgraph);
+
+                    if (default_type || isgraph)
+                        canvas_undo_add(x, 6, "resize",
+                            canvas_undo_set_apply(x, glist_getindex(x, y)));
+
+                    /* Scalehandle callbacks */
+                    if (isgraph)
                     {
-                        canvas_setcursor(x, CURSOR_EDITMODE_RESIZE);
+                        t_scalehandle *sh = ((t_canvas *)ob)->x_handle;
+                        /* Special case: we're abusing the value of h_scale
+                           to differentiate between this case and the case
+                           of clicking the red gop rectangle. */
+                        sh->h_scale = 1;
+                        pd_vmess(&sh->h_pd, gensym("_click"), "fff",
+                            (t_float)in_text_resizing_hotspot,
+                            (t_float)xpos, (t_float)ypos);
                     }
-                    else
+                    else if (ob->te_iemgui)
+                    {
+                        t_scalehandle *sh = ((t_iemgui *)ob)->x_handle;
+                        pd_vmess(&sh->h_pd, gensym("_click"), "fff",
+                            (t_float)in_text_resizing_hotspot,
+                            (t_float)xpos, (t_float)ypos);
+                    }
+                    else if (!default_type)
                     {
-                        canvas_setcursor(x,
-                            CURSOR_EDITMODE_RESIZE_BOTTOM_RIGHT);
+                        /* Scope~ and grid */
+                        pd_vmess(&ob->ob_pd, gensym("_click_for_resizing"),
+                           "fff", (t_float)in_text_resizing_hotspot,
+                           (t_float)xpos, (t_float)ypos);
                     }
+                }
+                else
+                {
+                    canvas_setcursor(x, in_text_resizing_hotspot);
                     canvas_check_nlet_highlights(x);
                 }
             }
@@ -3541,45 +3617,33 @@ void canvas_doclick(t_canvas *x, int xpos, int ypos, int which,
                 t_rtext *rt;
                     /* check if the box is being text edited */
                 nooutletafterall:
-                if (ob && (rt = x->gl_editor->e_textedfor) &&
-                    rt == glist_findrtext(x, ob))
+                    /* otherwise select and drag to displace */
+                if (!glist_isselected(x, y))
                 {
-                    rtext_mouse(rt, xpos - x1, ypos - y1,
-                        (doublemod ? RTEXT_DBL : RTEXT_DOWN));
-                    x->gl_editor->e_onmotion = MA_DRAGTEXT;
-                    x->gl_editor->e_xwas = x1;
-                    x->gl_editor->e_ywas = y1;
+                    //t_undo_redo_sel *buf =
+                    //    (t_undo_redo_sel *)getbytes(sizeof(*buf));
+                    //buf->u_undo =
+                    //    (t_undo_sel *)canvas_undo_set_selection(x);
+
+                    glist_noselect(x);
+                    glist_select(x, y);
+                    //buf->u_redo =
+                    //    (t_undo_sel *)canvas_undo_set_selection(x);
+                    //canvas_undo_add(x, 11, "selection", buf);
                 }
                 else
                 {
-                        /* otherwise select and drag to displace */
-                    if (!glist_isselected(x, y))
-                    {
-                        //t_undo_redo_sel *buf =
-                        //    (t_undo_redo_sel *)getbytes(sizeof(*buf));
-                        //buf->u_undo =
-                        //    (t_undo_sel *)canvas_undo_set_selection(x);
-
-                        glist_noselect(x);
-                        glist_select(x, y);
-                        //buf->u_redo =
-                        //    (t_undo_sel *)canvas_undo_set_selection(x);
-                        //canvas_undo_add(x, 11, "selection", buf);
-                    }
-                    else
-                    {
-                        canvas_check_nlet_highlights(x);
-                    }
-                    //toggle_moving = 1;
-                    //sys_vgui("pdtk_update_xy_tooltip .x%lx %d %d\n",
-                    //    x, (int)xpos, (int)ypos);
-                    //sys_vgui("pdtk_toggle_xy_tooltip .x%lx %d\n", x, 1);
-                    x->gl_editor->e_onmotion = MA_MOVE;
-                    /* once the code for creating a new object looks sane
-                       we'll leave rendering the tooltips to the GUI. */
-                    //if (tooltips)
-                    //    sys_vgui("pdtk_tip .x%x.c 0 0\n", x);
+                    canvas_check_nlet_highlights(x);
                 }
+                //toggle_moving = 1;
+                //sys_vgui("pdtk_update_xy_tooltip .x%lx %d %d\n",
+                //    x, (int)xpos, (int)ypos);
+                //sys_vgui("pdtk_toggle_xy_tooltip .x%lx %d\n", x, 1);
+                x->gl_editor->e_onmotion = MA_MOVE;
+                /* once the code for creating a new object looks sane
+                   we'll leave rendering the tooltips to the GUI. */
+                //if (tooltips)
+                //    sys_vgui("pdtk_tip .x%x.c 0 0\n", x);
             }
             else
             // jsarlo 
@@ -3616,13 +3680,9 @@ void canvas_doclick(t_canvas *x, int xpos, int ypos, int which,
             // end jsarlo
         }
         return;
-    } else if (x->gl_isgraph && x->gl_goprect &&
-               xpos <= x->gl_xmargin + x->gl_pixwidth + 4 &&
-               xpos >= x->gl_xmargin + x->gl_pixwidth - 2 &&
-               ypos <= x->gl_ymargin + x->gl_pixheight + 4 &&
-               ypos > x->gl_ymargin + x->gl_pixheight - 2)
+    }
+    else if (in_text_resizing_hotspot) /* red gop rectangle */
     {
-        // refactor the if into a function call...
         if (doit)
         {
             x->gl_editor->e_onmotion = MA_RESIZE;
@@ -3630,14 +3690,18 @@ void canvas_doclick(t_canvas *x, int xpos, int ypos, int which,
             x->gl_editor->e_ywas = y1;
             x->gl_editor->e_xnew = xpos;
             x->gl_editor->e_ynew = ypos;
-            t_pd *sh = (t_pd *)x->x_handle; // scale handle
+            t_pd *sh = (t_pd *)((t_canvas *)x)->x_handle; // scale handle
+            /* Special case-- we abuse the value of h_scale to differentiate
+               between clicking the corner of a subcanvas and resizing a
+               red gop rectangle. */
+            ((t_scalehandle *)sh)->h_scale = 2;
             pd_vmess(sh, gensym("_click"), "fff",
-                (t_float)1, (t_float)xpos, (t_float)ypos);
+                (t_float)in_text_resizing_hotspot, (t_float)xpos,
+                (t_float)ypos);
         }
         else
         {
-            canvas_setcursor(x,
-                CURSOR_EDITMODE_RESIZE_BOTTOM_RIGHT);
+            canvas_setcursor(x, in_text_resizing_hotspot);
         }
         canvas_check_nlet_highlights(x);
         return;
@@ -5192,7 +5256,7 @@ void canvas_key(t_canvas *x, t_symbol *s, int ac, t_atom *av)
     pd_vmess(&x->gl_pd, gensym("motion"), "fff",
         (double)canvas_last_glist_x,
         (double)canvas_last_glist_y,
-        (double)(glob_shift+glob_ctrl*2+glob_alt*4));
+        (double)(glob_shift + glob_ctrl * 2 + glob_alt * 4));
 }
 
 extern void graph_checkgop_rect(t_gobj *z, t_glist *glist,
@@ -5256,13 +5320,6 @@ void canvas_motion(t_canvas *x, t_floatarg xpos, t_floatarg ypos,
         x->gl_editor->e_xwas = xpos;
         x->gl_editor->e_ywas = ypos;
     }
-    else if (x->gl_editor->e_onmotion == MA_DRAGTEXT)
-    {
-        t_rtext *rt = x->gl_editor->e_textedfor;
-        if (rt)
-            rtext_mouse(rt, xpos - x->gl_editor->e_xwas,
-                ypos - x->gl_editor->e_ywas, RTEXT_DRAG);
-    }
     else if (x->gl_editor->e_onmotion == MA_RESIZE)
     {
         int x11=0, y11=0, x12=0, y12=0; 
@@ -5293,35 +5350,9 @@ void canvas_motion(t_canvas *x, t_floatarg xpos, t_floatarg ypos,
             }
             else if (ob && ob->ob_pd == canvas_class)
             {
-                int tmpx1 = 0, tmpy1 = 0, tmpx2 = 0, tmpy2 = 0;
-                int tmp_x_final = 0, tmp_y_final = 0;
-                gobj_vis(y1, x, 0);
-                ((t_canvas *)ob)->gl_pixwidth += xpos - x->gl_editor->e_xnew;
-                ((t_canvas *)ob)->gl_pixheight += ypos - x->gl_editor->e_ynew;
-                graph_checkgop_rect((t_gobj *)ob, x, &tmpx1, &tmpy1, &tmpx2,
-                    &tmpy2);
-                tmpx1 = ob->te_xpix;
-                tmpy1 = ob->te_ypix;
-                //fprintf(stderr,"%d %d %d %d\n", tmpx1, tmpy1, tmpx2, tmpy2);
-                if (!((t_canvas *)ob)->gl_hidetext)
-                {
-                    tmp_x_final = tmpx2 - tmpx1;
-                    tmp_y_final    = tmpy2 - tmpy1;
-                }
-                else
-                {
-                    tmp_x_final = tmpx2;
-                    tmp_y_final = tmpy2;
-                }
-                if (tmp_x_final > ((t_canvas *)ob)->gl_pixwidth)
-                    ((t_canvas *)ob)->gl_pixwidth = tmp_x_final;
-                if (tmp_y_final > ((t_canvas *)ob)->gl_pixheight)
-                    ((t_canvas *)ob)->gl_pixheight = tmp_y_final;
-                x->gl_editor->e_xnew = xpos;
-                x->gl_editor->e_ynew = ypos;
-                canvas_fixlinesfor(x, ob);
-                gobj_vis(y1, x, 1);
-                canvas_dirty(x, 1);
+                t_pd *sh = (t_pd *)((t_canvas *)ob)->x_handle;
+                pd_vmess(sh, gensym("_motion"), "ff", (t_float)xpos,
+                    (t_float)ypos);
             }
             else if (ob && ob->te_iemgui)
             {
diff --git a/pd/src/g_graph.c b/pd/src/g_graph.c
index 94b129609da2c65de2d1f23f36a6286cc49920ed..d2b58b94d5c790531eb36762e78aa8144bbbc20f 100644
--- a/pd/src/g_graph.c
+++ b/pd/src/g_graph.c
@@ -948,6 +948,17 @@ static void graph_vis(t_gobj *gr, t_glist *parent_glist, int vis)
         }
         else if (gobj_shouldvis(gr, parent_glist))
         {
+            /* erase contents of glist. We need to do this because
+               scalar_vis is currently using pd_bind/unbind to handle
+               scalar events. */
+            for (g = x->gl_list; g; g = g->g_next)
+            {
+                gop_redraw = 1;
+                //fprintf(stderr,"drawing gop objects\n");
+                gobj_vis(g, x, 0);
+                //fprintf(stderr,"done\n");
+                gop_redraw = 0;
+            }
             gui_vmess("gui_gobj_erase", "xs",
                 glist_getcanvas(x->gl_owner),
                 tag);
diff --git a/pd/src/g_mycanvas.c b/pd/src/g_mycanvas.c
index d33cb46edb3a060e810644b868d4e71d685cc435..638ef318d84a3e594b745ea57cb570b7686748f0 100644
--- a/pd/src/g_mycanvas.c
+++ b/pd/src/g_mycanvas.c
@@ -22,8 +22,8 @@ t_widgetbehavior my_canvas_widgetbehavior;
 void my_canvas_draw_new(t_my_canvas *x, t_glist *glist)
 {
     t_canvas *canvas=glist_getcanvas(glist);
-    int x1=text_xpix(&x->x_gui.x_obj, glist);
-    int y1=text_ypix(&x->x_gui.x_obj, glist);
+    int x1 = text_xpix(&x->x_gui.x_obj, glist);
+    int y1 = text_ypix(&x->x_gui.x_obj, glist);
     char cbuf[8];
     sprintf(cbuf, "#%6.6x", x->x_gui.x_bcol);
 
@@ -69,6 +69,15 @@ static void my_canvas__clickhook(t_scalehandle *sh, int newstate)
         if (!sh->h_scale)
             scalehandle_click_label(sh);
     }
+    if (sh->h_scale)
+    {
+        sh->h_adjust_x = sh->h_offset_x -
+            (((t_object *)x)->te_xpix + x->x_vis_w);
+        sh->h_adjust_y = sh->h_offset_y -
+            (((t_object *)x)->te_ypix + x->x_vis_h);
+        /* Hack to set the cursor since we're doing and end-run
+           around canas_doclick here */
+    }
     sh->h_dragon = newstate;
 }
 
@@ -77,16 +86,19 @@ static void my_canvas__motionhook(t_scalehandle *sh, t_floatarg mouse_x, t_float
     if (sh->h_scale)
     {
         t_my_canvas *x = (t_my_canvas *)(sh->h_master);
-        int dx = (int)(mouse_x - sh->h_offset_x),
-            dy = (int)(mouse_y - sh->h_offset_y);
-        dx = maxi(dx,1-x->x_vis_w);
-        dy = maxi(dy,1-x->x_vis_h);        
-        sh->h_dragx = dx;
-        sh->h_dragy = dy;
-        scalehandle_drag_scale(sh);
 
-        x->x_vis_w += dx;
-        x->x_vis_h += dy;
+        int width = (sh->h_constrain == CURSOR_EDITMODE_RESIZE_Y) ?
+            x->x_vis_w :
+            (int)mouse_x - text_xpix(&x->x_gui.x_obj, x->x_gui.x_glist) -
+                sh->h_adjust_x;
+        int height = (sh->h_constrain == CURSOR_EDITMODE_RESIZE_X) ?
+            x->x_vis_h :
+            (int)mouse_y - text_ypix(&x->x_gui.x_obj, x->x_gui.x_glist) -
+                sh->h_adjust_y;
+        x->x_vis_w = maxi(width, IEM_GUI_MINSIZE);
+        x->x_vis_h = maxi(height, IEM_GUI_MINSIZE);
+
+        scalehandle_drag_scale(sh);
 
         if (glist_isvisible(x->x_gui.x_glist))
         {
@@ -97,19 +109,11 @@ static void my_canvas__motionhook(t_scalehandle *sh, t_floatarg mouse_x, t_float
         int properties = gfxstub_haveproperties((void *)x);
         if (properties)
         {
-            int new_w = x->x_vis_w + sh->h_dragx;
-            int new_h = x->x_vis_h + sh->h_dragy;
-            properties_set_field_int(properties,"rng.min_ent",new_w);
-            properties_set_field_int(properties,"rng.max_ent",new_h);
-
-            int min = (new_w < new_h ? new_w : new_h);
-            if (min <= x->x_gui.x_w)
-            {
-                properties_set_field_int(properties,"dim.w_ent",min);
-            }
+            properties_set_field_int(properties,"rng.min_ent",width);
+            properties_set_field_int(properties,"rng.max_ent",height);
         }
     }
-    scalehandle_dragon_label(sh,mouse_x, mouse_y);
+    scalehandle_dragon_label(sh, mouse_x, mouse_y);
 }
 
 void my_canvas_draw(t_my_canvas *x, t_glist *glist, int mode)
diff --git a/pd/src/g_rtext.c b/pd/src/g_rtext.c
index 348bad8dddc358c91e84c2a55790f7e4e00de8ba..b82f34701c7dc14e0206fdb1a7f91b4af8ce0f86 100644
--- a/pd/src/g_rtext.c
+++ b/pd/src/g_rtext.c
@@ -42,7 +42,6 @@ struct _rtext
     int x_selstart; /*-- byte offset --*/
     int x_selend;   /*-- byte offset --*/
     int x_active;
-    int x_dragfrom;
     int x_drawnwidth;
     int x_drawnheight;
     t_text *x_text;
@@ -575,7 +574,7 @@ void rtext_activate(t_rtext *x, int state)
         //sys_vgui(".x%lx.c focus %s\n", canvas, x->x_tag);
         glist->gl_editor->e_textedfor = x;
         glist->gl_editor->e_textdirty = 0;
-        x->x_dragfrom = x->x_selstart = 0;
+        x->x_selstart = 0;
         x->x_selend = x->x_bufsize;
         x->x_active = 1;
     }
@@ -1045,62 +1044,3 @@ be printable in whatever 8-bit character set we find ourselves. */
     }
     rtext_senditup(x, SEND_UPDATE, &w, &h, &indx);
 }
-
-void rtext_mouse(t_rtext *x, int xval, int yval, int flag)
-{
-    int w = xval, h = yval, indx;
-    rtext_senditup(x, SEND_CHECK, &w, &h, &indx);
-    if (flag == RTEXT_DOWN)
-    {
-        x->x_dragfrom = x->x_selstart = x->x_selend = indx;
-    }
-    else if (flag == RTEXT_DBL)
-    {
-        int whereseparator, newseparator;
-        x->x_dragfrom = -1;
-        whereseparator = 0;
-        if ((newseparator = lastone(x->x_buf, ' ', indx)) > whereseparator)
-            whereseparator = newseparator+1;
-        if ((newseparator = lastone(x->x_buf, '\n', indx)) > whereseparator)
-            whereseparator = newseparator+1;
-        if ((newseparator = lastone(x->x_buf, ';', indx)) > whereseparator)
-            whereseparator = newseparator+1;
-        if ((newseparator = lastone(x->x_buf, ',', indx)) > whereseparator)
-            whereseparator = newseparator+1;
-        x->x_selstart = whereseparator;
-        
-        whereseparator = x->x_bufsize - indx;
-        if ((newseparator =
-            firstone(x->x_buf+indx, ' ', x->x_bufsize - indx)) >= 0 &&
-                newseparator < whereseparator)
-                    whereseparator = newseparator;
-        if ((newseparator =
-            firstone(x->x_buf+indx, '\n', x->x_bufsize - indx)) >= 0 &&
-                newseparator < whereseparator)
-                    whereseparator = newseparator;
-        if ((newseparator =
-            firstone(x->x_buf+indx, ';', x->x_bufsize - indx)) >= 0 &&
-                newseparator < whereseparator)
-                    whereseparator = newseparator;
-        if ((newseparator =
-            firstone(x->x_buf+indx, ',', x->x_bufsize - indx)) >= 0 &&
-                newseparator < whereseparator)
-                    whereseparator = newseparator;
-        x->x_selend = indx + whereseparator;
-    }
-    else if (flag == RTEXT_SHIFT)
-    {
-        if (indx * 2 > x->x_selstart + x->x_selend)
-            x->x_dragfrom = x->x_selstart, x->x_selend = indx;
-        else
-            x->x_dragfrom = x->x_selend, x->x_selstart = indx;
-    }
-    else if (flag == RTEXT_DRAG)
-    {
-        if (x->x_dragfrom < 0)
-            return;
-        x->x_selstart = (x->x_dragfrom < indx ? x->x_dragfrom : indx);
-        x->x_selend = (x->x_dragfrom > indx ? x->x_dragfrom : indx);
-    }
-    rtext_senditup(x, SEND_UPDATE, &w, &h, &indx);
-}
diff --git a/pd/src/g_slider.c b/pd/src/g_slider.c
index 5421df81fdaaff18954b9f6e56194c9b144c4ad4..320a45a99320d997f135484076d29538380e6a34 100644
--- a/pd/src/g_slider.c
+++ b/pd/src/g_slider.c
@@ -113,25 +113,36 @@ static void slider_draw_config(t_slider *x, t_glist *glist)
 static void slider__clickhook(t_scalehandle *sh, int newstate)
 {
     t_slider *x = (t_slider *)(sh->h_master);
-    if (newstate)
+    if (sh->h_scale)
     {
-        canvas_apply_setundo(x->x_gui.x_glist, (t_gobj *)x);
-        if (!sh->h_scale)
-            scalehandle_click_label(sh);
+        sh->h_adjust_x = sh->h_offset_x -
+            (((t_object *)x)->te_xpix + x->x_gui.x_w);
+        sh->h_adjust_y = sh->h_offset_y -
+            (((t_object *)x)->te_ypix + x->x_gui.x_h);
     }
+    else
+        scalehandle_click_label(sh);
+    canvas_apply_setundo(x->x_gui.x_glist, (t_gobj *)x);
     sh->h_dragon = newstate;
 }
 
 void slider_check_length(t_slider *x, int w);
 
-static void slider__motionhook(t_scalehandle *sh, t_floatarg mouse_x, t_floatarg mouse_y)
+static void slider__motionhook(t_scalehandle *sh, t_floatarg mouse_x,
+    t_floatarg mouse_y)
 {
     if (sh->h_scale)
     {
         t_slider *x = (t_slider *)(sh->h_master);
-        int width = mouse_x - text_xpix(&x->x_gui.x_obj, x->x_gui.x_glist),
-            height = mouse_y - text_ypix(&x->x_gui.x_obj, x->x_gui.x_glist),
-            minx = x->x_orient ? IEM_GUI_MINSIZE : IEM_SL_MINSIZE,
+        int width = (sh->h_constrain == CURSOR_EDITMODE_RESIZE_Y) ?
+            x->x_gui.x_w :
+            (int)mouse_x - text_xpix(&x->x_gui.x_obj, x->x_gui.x_glist) -
+                sh->h_adjust_x;
+        int height = (sh->h_constrain == CURSOR_EDITMODE_RESIZE_X) ?
+            x->x_gui.x_h :
+            (int)mouse_y - text_ypix(&x->x_gui.x_obj, x->x_gui.x_glist) -
+                sh->h_adjust_y;
+        int minx = x->x_orient ? IEM_GUI_MINSIZE : IEM_SL_MINSIZE,
             miny = x->x_orient ? IEM_SL_MINSIZE : IEM_GUI_MINSIZE;
         x->x_gui.x_w = maxi(width, minx);
         x->x_gui.x_h = maxi(height, miny);
diff --git a/pd/src/g_text.c b/pd/src/g_text.c
index 2d41440d3e21fcef2ea0a3b50c85fd4e1cd4ce38..30affd0b9e94957f69f3805a9697ae225166c569 100644
--- a/pd/src/g_text.c
+++ b/pd/src/g_text.c
@@ -296,6 +296,7 @@ void canvas_howputnew(t_canvas *x, int *connectp, int *xpixp, int *ypixp,
             selected = x->gl_editor->e_selection->sel_what;
         }
         for (g = x->gl_list, nobj = 0; g; g = g->g_next, nobj++)
+        {
             if (g == selected)
             {
                 gobj_getrect(g, x, &x1, &y1, &x2, &y2);
@@ -303,6 +304,7 @@ void canvas_howputnew(t_canvas *x, int *connectp, int *xpixp, int *ypixp,
                 *xpixp = x1;
                 *ypixp = y2 + 5;
             }
+        }
         glist_noselect(x);
             /* search back for 'selected' and if it isn't on the list, 
                 plan just to connect from the last item on the list. */
@@ -442,7 +444,7 @@ void canvas_iemguis(t_glist *gl, t_symbol *guiobjname)
     t_binbuf *b = binbuf_new();
     //int xpix, ypix;
 
-    if(!strcmp(guiobjname->s_name, "cnv"))
+    if (!strcmp(guiobjname->s_name, "cnv"))
         glist_noselect(gl);
 
     int connectme, xpix, ypix, indx, nobj;
@@ -453,11 +455,11 @@ void canvas_iemguis(t_glist *gl, t_symbol *guiobjname)
        in case of autopatch
     if (connectme)
     {
-        if(!strcmp(guiobjname->s_name, "hsl"))
+        if (!strcmp(guiobjname->s_name, "hsl"))
             xpix = xpix + 3;
-        else if(!strcmp(guiobjname->s_name, "vsl"))
+        else if (!strcmp(guiobjname->s_name, "vsl"))
             ypix = ypix + 2;
-        else if(!strcmp(guiobjname->s_name, "vu"))
+        else if (!strcmp(guiobjname->s_name, "vu"))
         {
             xpix = xpix + 1;
             ypix = ypix + 2;
@@ -2231,7 +2233,7 @@ void text_save(t_gobj *z, t_binbuf *b)
         for (i = 0; i < natom; i++)
         {
             t_symbol *s;
-            if(a[i].a_type == A_SYMBOL)
+            if (a[i].a_type == A_SYMBOL)
             {
                 //fprintf(stderr,"%d is a symbol\n", i);
                 s = a[i].a_w.w_symbol;
@@ -2241,7 +2243,7 @@ void text_save(t_gobj *z, t_binbuf *b)
                     char *c;
                     for(c = s->s_name; c != NULL && *c != '\0'; c++)
                     {
-                        if(*c == '\n')
+                        if (*c == '\n')
                         {
                             *c = '\v';
                             //fprintf(stderr,"n->v\n");
diff --git a/pd/src/g_traversal.c b/pd/src/g_traversal.c
index fdb88748b2eedc93b1b89d2320604c768228d071..57ee39044c9d3ab734ec1ede72ef2d558fc70985 100644
--- a/pd/src/g_traversal.c
+++ b/pd/src/g_traversal.c
@@ -68,7 +68,6 @@ void gstub_cutoff(t_gstub *gs)
     if (!gs->gs_refcount) t_freebytes(gs, sizeof (*gs));
 }
 
-
 int gpointer_docheck(const t_gpointer *gp, int headok, int gobjok)
 {
     t_gstub *gs = gp->gp_stub;
@@ -1104,7 +1103,6 @@ static void setsize_float(t_setsize *x, t_float f)
     }
 }
 
-
 static void setsize_free(t_setsize *x)
 {
     gpointer_unset(&x->x_gp);
@@ -1118,7 +1116,6 @@ static void setsize_setup(void)
     class_addfloat(setsize_class, setsize_float);
     class_addmethod(setsize_class, (t_method)setsize_set, gensym("set"),
         A_SYMBOL, A_SYMBOL, 0); 
-
 }
 
 /* ---------------------- append ----------------------------- */
diff --git a/pd/src/g_vumeter.c b/pd/src/g_vumeter.c
index 0d6e7f5dcd5a52e26682820dabc9f2d8219da49e..d1693a08a7301963780c8fd23a32e8b75d395206 100644
--- a/pd/src/g_vumeter.c
+++ b/pd/src/g_vumeter.c
@@ -265,12 +265,16 @@ static void vu_draw_select(t_vu* x,t_glist* glist)
 static void vu__clickhook(t_scalehandle *sh, int newstate)
 {
     t_vu *x = (t_vu *)(sh->h_master);
-    if (newstate)
+    if (sh->h_scale)
     {
-        canvas_apply_setundo(x->x_gui.x_glist, (t_gobj *)x);
-        if (!sh->h_scale)
-            scalehandle_click_label(sh);
+        sh->h_adjust_x = sh->h_offset_x -
+            (((t_object *)x)->te_xpix + x->x_gui.x_w);
+        sh->h_adjust_y = sh->h_offset_y -
+            (((t_object *)x)->te_ypix + x->x_gui.x_h);
     }
+    else
+        scalehandle_click_label(sh);
+    canvas_apply_setundo(x->x_gui.x_glist, (t_gobj *)x);
     sh->h_dragon = newstate;
 }
 
@@ -279,10 +283,14 @@ static void vu__motionhook(t_scalehandle *sh, t_floatarg mouse_x, t_floatarg mou
     if (sh->h_scale)
     {
         t_vu *x = (t_vu *)(sh->h_master);
-        int width = ((int)mouse_x) -
-                text_xpix(&x->x_gui.x_obj, x->x_gui.x_glist),
-            height = ((int)mouse_y) -
-                text_ypix(&x->x_gui.x_obj, x->x_gui.x_glist);
+        int width = (sh->h_constrain == CURSOR_EDITMODE_RESIZE_Y) ?
+            x->x_gui.x_w :
+            (int)mouse_x - text_xpix(&x->x_gui.x_obj, x->x_gui.x_glist) -
+                sh->h_adjust_x;
+        int height = (sh->h_constrain == CURSOR_EDITMODE_RESIZE_X) ?
+            x->x_gui.x_h :
+            (int)mouse_y - text_ypix(&x->x_gui.x_obj, x->x_gui.x_glist) -
+                sh->h_adjust_y;
 
         width = maxi(width, 8);
 
diff --git a/pd/src/m_pd.c b/pd/src/m_pd.c
index 04fed7e385918646cc911d1edd74e79e67344f91..7e2f9826933a85f455bb9167741d3ae59a546925 100644
--- a/pd/src/m_pd.c
+++ b/pd/src/m_pd.c
@@ -439,6 +439,11 @@ void pd_list(t_pd *x, t_symbol *s, int argc, t_atom *argv)
     (*(*x)->c_listmethod)(x, &s_list, argc, argv);
 }
 
+void pd_anything(t_pd *x, t_symbol *s, int argc, t_atom *argv)
+{
+    (*(*x)->c_anymethod)(x, s, argc, argv);
+}
+
 void mess_init(void);
 void obj_init(void);
 void conf_init(void);
diff --git a/pd/src/m_pd.h b/pd/src/m_pd.h
index 65ef7162073e753f368eb2383088096db3280b0d..2f42b760360cb27acb978758ec8d5ef16c35034e 100644
--- a/pd/src/m_pd.h
+++ b/pd/src/m_pd.h
@@ -14,7 +14,7 @@ extern "C" {
 #define PD_MINOR_VERSION 48
 #define PD_BUGFIX_VERSION 0
 #define PD_TEST_VERSION ""
-#define PD_L2ORK_VERSION "2.7.0"
+#define PD_L2ORK_VERSION "2.8.1"
 #define PDL2ORK
 extern int pd_compatibilitylevel;   /* e.g., 43 for pd 0.43 compatibility */
 
diff --git a/pd/src/s_inter.c b/pd/src/s_inter.c
index 29acc4de21ccd0d6cd1e4eef540c9acde2847e55..d6f9f3b1ac135343b52fae61a571b39de1636262 100644
--- a/pd/src/s_inter.c
+++ b/pd/src/s_inter.c
@@ -717,8 +717,8 @@ void blargh(void) {
 
 static int lastend = -1;
 void sys_vvgui(const char *fmt, va_list ap) {
-	va_list aq;
-	va_copy(aq,ap);
+    va_list aq;
+    va_copy(aq, ap);
     int msglen;
 
     if (sys_nogui)
@@ -738,7 +738,7 @@ void sys_vvgui(const char *fmt, va_list ap) {
     msglen = vsnprintf(sys_guibuf + sys_guibufhead,
         sys_guibufsize - sys_guibufhead, fmt, ap);
     va_end(ap);
-    if(msglen < 0) 
+    if (msglen < 0)
     {
         fprintf(stderr, "Pd: buffer space wasn't sufficient for long GUI string\n");
         return;
@@ -749,7 +749,7 @@ void sys_vvgui(const char *fmt, va_list ap) {
             (msglen > GUI_ALLOCCHUNK ? msglen : GUI_ALLOCCHUNK);
         sys_trytogetmoreguibuf(newsize);
 
-        va_copy(ap,aq);
+        va_copy(ap, aq);
         msglen2 = vsnprintf(sys_guibuf + sys_guibufhead,
             sys_guibufsize - sys_guibufhead, fmt, ap);
         va_end(ap);