Code

Make curvature work again by fixing a minor omission
[inkscape.git] / src / node-context.cpp
index 72117c462a295f8607f0fd5d6f53d1fefcea884c..7efa57290832fb23565c020ddf57e4f448cc8183 100644 (file)
@@ -7,13 +7,14 @@
  *   Lauris Kaplinski <lauris@kaplinski.com>
  *   bulia byak <buliabyak@users.sf.net>
  *
- * This code is in public domain, except stamping code,
- * which is Copyright (C) Masatake Yamato 2002
+ * This code is in public domain
  */
 
 #ifdef HAVE_CONFIG_H
 # include "config.h"
 #endif
+#include <cstring>
+#include <string>
 #include <gdk/gdkkeysyms.h>
 #include "macros.h"
 #include <glibmm/i18n.h>
 #include "message-context.h"
 #include "node-context.h"
 #include "pixmaps/cursor-node-d.xpm"
-#include "prefs-utils.h"
+#include "preferences.h"
 #include "xml/node-event-vector.h"
 #include "style.h"
 #include "splivarot.h"
+#include "shape-editor.h"
+#include "live_effects/effect.h"
+
+#include "sp-lpe-item.h"
+
+// needed for flash nodepath upon mouseover:
+#include "display/canvas-bpath.h"
+#include "display/curve.h"
 
 static void sp_node_context_class_init(SPNodeContextClass *klass);
 static void sp_node_context_init(SPNodeContext *node_context);
@@ -43,18 +52,6 @@ static gint sp_node_context_root_handler(SPEventContext *event_context, GdkEvent
 static gint sp_node_context_item_handler(SPEventContext *event_context,
                                          SPItem *item, GdkEvent *event);
 
-static void nodepath_event_attr_changed(Inkscape::XML::Node *repr, gchar const *name,
-                                        gchar const *old_value, gchar const *new_value,
-                                        bool is_interactive, gpointer data);
-
-static Inkscape::XML::NodeEventVector nodepath_repr_events = {
-    NULL, /* child_added */
-    NULL, /* child_removed */
-    nodepath_event_attr_changed,
-    NULL, /* content_changed */
-    NULL  /* order_changed */
-};
-
 static SPEventContextClass *parent_class;
 
 GType
@@ -107,41 +104,29 @@ sp_node_context_init(SPNodeContext *node_context)
     node_context->rightctrl = FALSE;
 
     new (&node_context->sel_changed_connection) sigc::connection();
+
+    node_context->flash_tempitem = NULL;
+    node_context->flashed_item = NULL;
+    node_context->remove_flash_counter = 0;
 }
 
 static void
 sp_node_context_dispose(GObject *object)
 {
-    SPNodeContext *nc = SP_NODE_CONTEXT(object);
+       SPNodeContext *nc = SP_NODE_CONTEXT(object);
     SPEventContext *ec = SP_EVENT_CONTEXT(object);
 
     ec->enableGrDrag(false);
+       
+    if (nc->grabbed) {
+        sp_canvas_item_ungrab(nc->grabbed, GDK_CURRENT_TIME);
+        nc->grabbed = NULL;
+    }
 
     nc->sel_changed_connection.disconnect();
     nc->sel_changed_connection.~connection();
 
-    Inkscape::XML::Node *repr = NULL;
-    if (nc->nodepath) {
-        repr = nc->nodepath->repr;
-    }
-    if (!repr && ec->shape_knot_holder) {
-        repr = ec->shape_knot_holder->repr;
-    }
-
-    if (repr) {
-        sp_repr_remove_listener_by_data(repr, ec);
-        Inkscape::GC::release(repr);
-    }
-
-    if (nc->nodepath) {
-        sp_nodepath_destroy(nc->nodepath);
-        nc->nodepath = NULL;
-    }
-
-    if (ec->shape_knot_holder) {
-        sp_knot_holder_destroy(ec->shape_knot_holder);
-        ec->shape_knot_holder = NULL;
-    }
+    delete ec->shape_editor;
 
     if (nc->_node_message_context) {
         delete nc->_node_message_context;
@@ -158,14 +143,14 @@ sp_node_context_setup(SPEventContext *ec)
     if (((SPEventContextClass *) parent_class)->setup)
         ((SPEventContextClass *) parent_class)->setup(ec);
 
+    Inkscape::Selection *selection = sp_desktop_selection (ec->desktop);
     nc->sel_changed_connection.disconnect();
-    nc->sel_changed_connection = SP_DT_SELECTION(ec->desktop)->connectChanged(sigc::bind(sigc::ptr_fun(&sp_node_context_selection_changed), (gpointer)nc));
+    nc->sel_changed_connection =
+        selection->connectChanged(sigc::bind(sigc::ptr_fun(&sp_node_context_selection_changed), (gpointer)nc));
 
-    Inkscape::Selection *selection = SP_DT_SELECTION(ec->desktop);
     SPItem *item = selection->singleItem();
 
-    nc->nodepath = NULL;
-    ec->shape_knot_holder = NULL;
+    ec->shape_editor = new ShapeEditor(ec->desktop);
 
     nc->rb_escaped = false;
 
@@ -173,186 +158,66 @@ sp_node_context_setup(SPEventContext *ec)
 
     nc->added_node = false;
 
+    nc->current_state = SP_NODE_CONTEXT_INACTIVE;
+
     if (item) {
-        nc->nodepath = sp_nodepath_new(ec->desktop, item);
-        if ( nc->nodepath) {
-            //point pack to parent in case nodepath is deleted
-            nc->nodepath->nodeContext = nc;
-        }
-        ec->shape_knot_holder = sp_item_knot_holder(item, ec->desktop);
-
-        if (nc->nodepath || ec->shape_knot_holder) {
-            // setting listener
-            Inkscape::XML::Node *repr;
-            if (ec->shape_knot_holder)
-                repr = ec->shape_knot_holder->repr;
-            else
-                repr = SP_OBJECT_REPR(item);
-            if (repr) {
-                Inkscape::GC::anchor(repr);
-                sp_repr_add_listener(repr, &nodepath_repr_events, ec);
-            }
-        }
+        ec->shape_editor->set_item(item, SH_NODEPATH);
+        ec->shape_editor->set_item(item, SH_KNOTHOLDER);
     }
 
-    if (prefs_get_int_attribute("tools.nodes", "selcue", 0) != 0) {
+    Inkscape::Preferences *prefs = Inkscape::Preferences::get();
+    if (prefs->getBool("/tools/nodes/selcue")) {
         ec->enableSelectionCue();
     }
-
-    if (prefs_get_int_attribute("tools.nodes", "gradientdrag", 0) != 0) {
+    if (prefs->getBool("/tools/nodes/gradientdrag")) {
         ec->enableGrDrag();
     }
 
-    nc->_node_message_context = new Inkscape::MessageContext((ec->desktop)->messageStack());
-    sp_nodepath_update_statusbar(nc->nodepath);
-}
-
-/**
-\brief  Callback that processes the "changed" signal on the selection;
-destroys old and creates new nodepath and reassigns listeners to the new selected item's repr
-*/
-void
-sp_node_context_selection_changed(Inkscape::Selection *selection, gpointer data)
-{
-    SPNodeContext *nc = SP_NODE_CONTEXT(data);
-    SPEventContext *ec = SP_EVENT_CONTEXT(nc);
-
-    Inkscape::XML::Node *old_repr = NULL;
-
-    if (nc->nodepath) {
-        old_repr = nc->nodepath->repr;
-        sp_nodepath_destroy(nc->nodepath);
-        nc->nodepath = NULL;
-    }
-
-    if (ec->shape_knot_holder) {
-        old_repr = ec->shape_knot_holder->repr;
-        sp_knot_holder_destroy(ec->shape_knot_holder);
-    }
-
-    if (old_repr) { // remove old listener
-        sp_repr_remove_listener_by_data(old_repr, ec);
-        Inkscape::GC::release(old_repr);
-    }
+    ec->desktop->emitToolSubselectionChanged(NULL); // sets the coord entry fields to inactive
 
-    SPItem *item = selection->singleItem();
+    nc->_node_message_context = new Inkscape::MessageContext((ec->desktop)->messageStack());
 
-    SPDesktop *desktop = selection->desktop();
-    nc->nodepath = NULL;
-    ec->shape_knot_holder = NULL;
-    if (item) {
-        nc->nodepath = sp_nodepath_new(desktop, item);
-        if (nc->nodepath) {
-            nc->nodepath->nodeContext = nc;
-        }
-        ec->shape_knot_holder = sp_item_knot_holder(item, desktop);
-
-        if (nc->nodepath || ec->shape_knot_holder) {
-            // setting new listener
-            Inkscape::XML::Node *repr;
-            if (ec->shape_knot_holder)
-                repr = ec->shape_knot_holder->repr;
-            else
-                repr = SP_OBJECT_REPR(item);
-            if (repr) {
-                Inkscape::GC::anchor(repr);
-                sp_repr_add_listener(repr, &nodepath_repr_events, ec);
-            }
-        }
-    }
-    sp_nodepath_update_statusbar(nc->nodepath);
+    ec->shape_editor->update_statusbar();
 }
 
-/**
-\brief  Regenerates nodepath when the item's repr was change outside of node edit
-(e.g. by undo, or xml editor, or edited in another view). The item is assumed to be the same
-(otherwise sp_node_context_selection_changed() would have been called), so repr and listeners
-are not changed.
-*/
-void
-sp_nodepath_update_from_item(SPNodeContext *nc, SPItem *item)
-{
-    g_assert(nc);
-    SPEventContext *ec = ((SPEventContext *) nc);
-
-    SPDesktop *desktop = SP_EVENT_CONTEXT_DESKTOP(SP_EVENT_CONTEXT(nc));
-    g_assert(desktop);
-
-    if (nc->nodepath) {
-        sp_nodepath_destroy(nc->nodepath);
-        nc->nodepath = NULL;
-    }
+static void
+sp_node_context_flash_path(SPEventContext *event_context, SPItem *item, guint timeout) {
+    SPNodeContext *nc = SP_NODE_CONTEXT(event_context);
 
-    if (ec->shape_knot_holder) {
-        sp_knot_holder_destroy(ec->shape_knot_holder);
-        ec->shape_knot_holder = NULL;
-    }
+    nc->remove_flash_counter = 3; // for some reason root_handler is called twice after each item_handler...
+    if (nc->flashed_item != item) {
+        // we entered a new item
+        nc->flashed_item = item;
+        SPDesktop *desktop = event_context->desktop;
+        if (nc->flash_tempitem) {
+            desktop->remove_temporary_canvasitem(nc->flash_tempitem);
+            nc->flash_tempitem = NULL;
+        }
 
-    Inkscape::Selection *selection = SP_DT_SELECTION(desktop);
-    item = selection->singleItem();
+        SPCanvasItem *canvasitem = sp_nodepath_generate_helperpath(desktop, item);
 
-    if (item) {
-        nc->nodepath = sp_nodepath_new(desktop, item);
-        if (nc->nodepath) {
-            nc->nodepath->nodeContext = nc;
+        if (canvasitem) {
+            nc->flash_tempitem = desktop->add_temporary_canvasitem (canvasitem, timeout);
         }
-        ec->shape_knot_holder = sp_item_knot_holder(item, desktop);
     }
-    sp_nodepath_update_statusbar(nc->nodepath);
 }
 
 /**
-\brief  Callback that is fired whenever an attribute of the selected item (which we have in the nodepath) changes
+\brief  Callback that processes the "changed" signal on the selection;
+destroys old and creates new nodepath and reassigns listeners to the new selected item's repr
 */
-static void
-nodepath_event_attr_changed(Inkscape::XML::Node *repr, gchar const *name,
-                            gchar const *old_value, gchar const *new_value,
-                            bool is_interactive, gpointer data)
+void
+sp_node_context_selection_changed(Inkscape::Selection *selection, gpointer data)
 {
-    SPItem *item = NULL;
-    char const *newd = NULL, *newtypestr = NULL;
-    gboolean changed = FALSE;
-
-    g_assert(data);
-    SPNodeContext *nc = ((SPNodeContext *) data);
-    SPEventContext *ec = ((SPEventContext *) data);
-    g_assert(nc);
-    Inkscape::NodePath::Path *np = nc->nodepath;
-    SPKnotHolder *kh = ec->shape_knot_holder;
-
-    if (np) {
-        item = SP_ITEM(np->path);
-        if (!strcmp(name, "d")) {
-            newd = new_value;
-            changed = nodepath_repr_d_changed(np, new_value);
-        } else if (!strcmp(name, "sodipodi:nodetypes")) {
-            newtypestr = new_value;
-            changed = nodepath_repr_typestr_changed(np, new_value);
-        } else {
-            return;
-            // With paths, we only need to act if one of the path-affecting attributes has changed.
-        }
-    } else if (kh) {
-        item = SP_ITEM(kh->item);
-        changed = !(kh->local_change);
-        kh->local_change = FALSE;
-    }
-    if (np && changed) {
-        GList *saved = NULL;
-        SPDesktop *desktop = np->desktop;
-        g_assert(desktop);
-        Inkscape::Selection *selection = desktop->selection;
-        g_assert(selection);
-
-        saved = save_nodepath_selection(nc->nodepath);
-        sp_nodepath_update_from_item(nc, item);
-        if (nc->nodepath && saved) restore_nodepath_selection(nc->nodepath, saved);
-
-    } else if (kh && changed) {
-        sp_nodepath_update_from_item(nc, item);
-    }
+    SPEventContext *ec = SP_EVENT_CONTEXT(data);
 
-    sp_nodepath_update_statusbar(nc->nodepath);
+    // TODO: update ShapeEditorsCollective instead
+    ec->shape_editor->unset_item(SH_NODEPATH);
+    ec->shape_editor->unset_item(SH_KNOTHOLDER);
+    SPItem *item = selection->singleItem();
+    ec->shape_editor->set_item(item, SH_NODEPATH);
+    ec->shape_editor->set_item(item, SH_KNOTHOLDER);
+    ec->shape_editor->update_statusbar();
 }
 
 void
@@ -365,156 +230,55 @@ sp_node_context_show_modifier_tip(SPEventContext *event_context, GdkEvent *event
          _("<b>Alt</b>: lock handle length; <b>Ctrl+Alt</b>: move along handles"));
 }
 
-bool
-sp_node_context_is_over_stroke (SPNodeContext *nc, SPItem *item, NR::Point event_p, bool remember)
-{
-    SPDesktop *desktop = SP_EVENT_CONTEXT (nc)->desktop;
-
-    //Translate click point into proper coord system
-    nc->curvepoint_doc = desktop->w2d(event_p);
-    nc->curvepoint_doc *= sp_item_dt2i_affine(item);
-    nc->curvepoint_doc *= sp_item_i2doc_affine(item);
-
-    NR::Maybe<Path::cut_position> position = get_nearest_position_on_Path(nc->nodepath->livarot_path, nc->curvepoint_doc);
-    NR::Point nearest = get_point_on_Path(nc->nodepath->livarot_path, position.assume().piece, position.assume().t);
-    NR::Point delta = nearest - nc->curvepoint_doc;
-
-    delta = desktop->d2w(delta);
-
-    double stroke_tolerance =
-        (SP_OBJECT_STYLE (item)->stroke.type != SP_PAINT_TYPE_NONE?
-         desktop->current_zoom() *
-         SP_OBJECT_STYLE (item)->stroke_width.computed *
-         sp_item_i2d_affine (item).expansion() * 0.5
-         : 0.0)
-        + (double) SP_EVENT_CONTEXT(nc)->tolerance;
-
-    bool close = (NR::L2 (delta) < stroke_tolerance);
-
-    if (remember && close) {
-        nc->curvepoint_event[NR::X] = (gint) event_p [NR::X];
-        nc->curvepoint_event[NR::Y] = (gint) event_p [NR::Y];
-        nc->hit = true;
-        nc->grab_t = position.assume().t;
-        nc->grab_node = sp_nodepath_get_node_by_index(position.assume().piece);
-    }
-
-    return close;
-}
-
-
 static gint
 sp_node_context_item_handler(SPEventContext *event_context, SPItem *item, GdkEvent *event)
 {
     gint ret = FALSE;
-
+    Inkscape::Preferences *prefs = Inkscape::Preferences::get();
     SPDesktop *desktop = event_context->desktop;
-    Inkscape::Selection *selection = SP_DT_SELECTION (desktop);
-
-    SPNodeContext *nc = SP_NODE_CONTEXT(event_context);
 
     switch (event->type) {
-        case GDK_2BUTTON_PRESS:
-        case GDK_BUTTON_RELEASE:
-            if (event->button.button == 1) {
-                if (!nc->drag) {
-
-                    // find out clicked item, disregarding groups, honoring Alt
-                    SPItem *item_clicked = sp_event_context_find_item (desktop,
-                            NR::Point(event->button.x, event->button.y),
-                            (event->button.state & GDK_MOD1_MASK) && !(event->button.state & GDK_CONTROL_MASK), TRUE);
-                    // find out if we're over the selected item, disregarding groups
-                    SPItem *item_over = sp_event_context_over_item (desktop, selection->singleItem(),
-                                                                    NR::Point(event->button.x, event->button.y));
-
-                    // check if this is indeed our nodepathed item
-                    if (item_over != nc->nodepath->path)
-                        break;
+        case GDK_MOTION_NOTIFY:
+        {
+            // find out actual item we're over, disregarding groups
+            SPItem *actual_item = sp_event_context_find_item (desktop,
+                                                              Geom::Point(event->button.x, event->button.y), FALSE, TRUE);
+            if (!actual_item)
+                break;
 
-                    bool over_stroke = false;
-                    if (item_over && nc->nodepath) {
-                        over_stroke = sp_node_context_is_over_stroke (nc, item_over, NR::Point(event->button.x, event->button.y), false);
-                    }
 
-                    if (over_stroke || nc->added_node) {
-                        switch (event->type) {
-                            case GDK_BUTTON_RELEASE:
-                                if (event->button.state & GDK_CONTROL_MASK && event->button.state & GDK_MOD1_MASK) {
-                                    //add a node
-                                    sp_nodepath_add_node_near_point(nc->nodepath, nc->curvepoint_doc);
-                                } else {
-                                    if (nc->added_node) { // we just received double click, ignore release
-                                        nc->added_node = false;
-                                        break;
-                                    }
-                                    //select the segment
-                                    if (event->button.state & GDK_SHIFT_MASK) {
-                                        sp_nodepath_select_segment_near_point(nc->nodepath, nc->curvepoint_doc, true);
-                                    } else {
-                                        sp_nodepath_select_segment_near_point(nc->nodepath, nc->curvepoint_doc, false);
-                                    }
-                                }
-                                break;
-                            case GDK_2BUTTON_PRESS:
-                                //add a node
-                                sp_nodepath_add_node_near_point(nc->nodepath, nc->curvepoint_doc);
-                                nc->added_node = true;
-                                break;
-                            default:
-                                break;
-                        }
-                    } else if (event->button.state & GDK_SHIFT_MASK) {
-                        selection->toggle(item_clicked);
-                    } else {
-                        selection->set(item_clicked);
+            if (prefs->getBool("/tools/nodes/pathflash_enabled")) {
+                if (prefs->getBool("/tools/nodes/pathflash_unselected")) {
+                    // do not flash if we have some path selected and a single item in selection (i.e. it
+                    // is the same path that we're editing)
+                    SPDesktop *desktop = event_context->desktop;
+                    ShapeEditor* se = event_context->shape_editor;
+                    Inkscape::Selection *selection = sp_desktop_selection (desktop);
+                    if (se->has_nodepath() && selection->singleItem()) {
+                        break;
                     }
-
-                    ret = TRUE;
                 }
-                break;
-            }
-            break;
-        case GDK_BUTTON_PRESS:
-            if (event->button.button == 1 && !(event->button.state & GDK_SHIFT_MASK)) {
-                // save drag origin
-                event_context->xp = (gint) event->button.x;
-                event_context->yp = (gint) event->button.y;
-                event_context->within_tolerance = true;
-                nc->hit = false;
-
-                if (!nc->drag) {
-                    // find out if we're over the selected item, disregarding groups
-                    SPItem *item_over = sp_event_context_over_item (desktop, selection->singleItem(),
-                                                                    NR::Point(event->button.x, event->button.y));
-
-                        if (nc->nodepath && selection->single() && item_over) {
-
-                            // save drag origin
-                            bool over_stroke = sp_node_context_is_over_stroke (nc, item_over, NR::Point(event->button.x, event->button.y), true);
-                            //only dragging curves
-                            if (over_stroke) {
-                                sp_nodepath_select_segment_near_point(nc->nodepath, nc->curvepoint_doc, false);
-                                ret = TRUE;
-                            } else {
-                                break;
-                            }
-                        } else {
-                            break;
-                        }
-
-                    ret = TRUE;
+                if (SP_IS_LPE_ITEM(actual_item)) {
+                    Inkscape::LivePathEffect::Effect *lpe = sp_lpe_item_get_current_lpe(SP_LPE_ITEM(actual_item));
+                    if (lpe && (lpe->providesOwnFlashPaths() ||
+                                lpe->pathFlashType() == Inkscape::LivePathEffect::SUPPRESS_FLASH)) {
+                        // path should be suppressed or permanent; this is handled in
+                        // sp_node_context_selection_changed()
+                        break;
+                    }
                 }
-                break;
+                guint timeout = prefs->getInt("/tools/nodes/pathflash_timeout", 500);
+                sp_node_context_flash_path(event_context, actual_item, timeout);
             }
-            break;
+        }
+        break;
+
         default:
             break;
     }
 
-    if (!ret) {
-        if (((SPEventContextClass *) parent_class)->item_handler)
-            ret = ((SPEventContextClass *) parent_class)->item_handler(event_context, item, event);
-    }
+    if (((SPEventContextClass *) parent_class)->item_handler)
+        ret = ((SPEventContextClass *) parent_class)->item_handler(event_context, item, event);
 
     return ret;
 }
@@ -523,72 +287,126 @@ static gint
 sp_node_context_root_handler(SPEventContext *event_context, GdkEvent *event)
 {
     SPDesktop *desktop = event_context->desktop;
-    Inkscape::Selection *selection = SP_DT_SELECTION (desktop);
+    ShapeEditor* se = event_context->shape_editor;
+    Inkscape::Selection *selection = sp_desktop_selection (desktop);
+    Inkscape::Preferences *prefs = Inkscape::Preferences::get();
 
     SPNodeContext *nc = SP_NODE_CONTEXT(event_context);
-    double const nudge = prefs_get_double_attribute_limited("options.nudgedistance", "value", 2, 0, 1000); // in px
-    event_context->tolerance = prefs_get_int_attribute_limited("options.dragtolerance", "value", 0, 0, 100); // read every time, to make prefs changes really live
-    int const snaps = prefs_get_int_attribute("options.rotationsnapsperpi", "value", 12);
-    double const offset = prefs_get_double_attribute_limited("options.defaultscale", "value", 2, 0, 1000);
+    double const nudge = prefs->getDoubleLimited("/options/nudgedistance/value", 2, 0, 1000); // in px
+    event_context->tolerance = prefs->getIntLimited("/options/dragtolerance/value", 0, 0, 100); // read every time, to make prefs changes really live
+    int const snaps = prefs->getInt("/options/rotationsnapsperpi/value", 12);
+    double const offset = prefs->getDoubleLimited("/options/defaultscale/value", 2, 0, 1000);
+
+    if ( (nc->flash_tempitem) && (nc->remove_flash_counter <= 0) ) {
+        desktop->remove_temporary_canvasitem(nc->flash_tempitem);
+        nc->flash_tempitem = NULL;
+        nc->flashed_item = NULL; // also reset this one, so the next time the same object is hovered over it shows again the highlight
+    } else {
+        nc->remove_flash_counter--;
+    }
 
     gint ret = FALSE;
-
     switch (event->type) {
         case GDK_BUTTON_PRESS:
-            if (event->button.button == 1) {
+            if (event->button.button == 1 && !event_context->space_panning) {
                 // save drag origin
                 event_context->xp = (gint) event->button.x;
                 event_context->yp = (gint) event->button.y;
                 event_context->within_tolerance = true;
-                nc->hit = false;
+                se->cancel_hit();
 
-                NR::Point const button_w(event->button.x,
+                if (!(event->button.state & GDK_SHIFT_MASK)) {
+                    if (!nc->drag) {
+                        if (se->has_nodepath() && selection->single() /* && item_over */) {
+                               // save drag origin
+                            bool over_stroke = se->is_over_stroke(Geom::Point(event->button.x, event->button.y), true);
+                            //only dragging curves
+                            if (over_stroke) {
+                                ret = TRUE;
+                                break;
+                            }
+                        }
+                    }
+                }
+                Geom::Point const button_w(event->button.x,
                                          event->button.y);
-                NR::Point const button_dt(desktop->w2d(button_w));
-                Inkscape::Rubberband::get()->start(desktop, button_dt);
+                Geom::Point const button_dt(desktop->w2d(button_w));
+                Inkscape::Rubberband::get(desktop)->start(desktop, button_dt);
+
+                if (nc->grabbed) {
+                    sp_canvas_item_ungrab(nc->grabbed, event->button.time);
+                    nc->grabbed = NULL;
+                }
+                               
+                sp_canvas_item_grab(SP_CANVAS_ITEM(desktop->acetate),
+                                    GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK | GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK,
+                                    NULL, event->button.time);
+                               nc->grabbed = SP_CANVAS_ITEM(desktop->acetate);
+                               
+                nc->current_state = SP_NODE_CONTEXT_INACTIVE;
+                desktop->updateNow();
                 ret = TRUE;
             }
             break;
         case GDK_MOTION_NOTIFY:
-            if (event->motion.state & GDK_BUTTON1_MASK) {
+            if (event->motion.state & GDK_BUTTON1_MASK && !event_context->space_panning) {
 
                 if ( event_context->within_tolerance
                      && ( abs( (gint) event->motion.x - event_context->xp ) < event_context->tolerance )
                      && ( abs( (gint) event->motion.y - event_context->yp ) < event_context->tolerance ) ) {
                     break; // do not drag if we're within tolerance from origin
                 }
+
+                // The path went away while dragging; throw away any further motion
+                // events until the mouse pointer is released.
+
+                if (se->hits_curve() && !se->has_nodepath()) {
+                  break;
+                }
+
                 // Once the user has moved farther than tolerance from the original location
                 // (indicating they intend to move the object, not click), then always process the
                 // motion notify coordinates as given (no snapping back to origin)
                 event_context->within_tolerance = false;
 
-                if (nc->nodepath && nc->hit) {
-                    NR::Point const delta_w(event->motion.x - nc->curvepoint_event[NR::X],
-                                         event->motion.y - nc->curvepoint_event[NR::Y]);
-                    NR::Point const delta_dt(desktop->w2d(delta_w));
-                    sp_nodepath_curve_drag (nc->grab_node, nc->grab_t, delta_dt);
-                    nc->curvepoint_event[NR::X] = (gint) event->motion.x;
-                    nc->curvepoint_event[NR::Y] = (gint) event->motion.y;
-                    gobble_motion_events(GDK_BUTTON1_MASK);
-                } else {
-                    NR::Point const motion_w(event->motion.x,
-                                         event->motion.y);
-                    NR::Point const motion_dt(desktop->w2d(motion_w));
-                    Inkscape::Rubberband::get()->move(motion_dt);
+                // Once we determine what the user is doing (dragging either a node or the
+                // selection rubberband), make sure we continue to perform that operation
+                // until the mouse pointer is lifted.
+                if (nc->current_state == SP_NODE_CONTEXT_INACTIVE) {
+                    if (se->hits_curve() && se->has_nodepath()) {
+                        nc->current_state = SP_NODE_CONTEXT_NODE_DRAGGING;
+                    } else {
+                        nc->current_state = SP_NODE_CONTEXT_RUBBERBAND_DRAGGING;
+                    }
+                }
+
+                switch (nc->current_state) {
+                    case SP_NODE_CONTEXT_NODE_DRAGGING:
+                        {
+                            se->curve_drag (event->motion.x, event->motion.y);
+
+                            gobble_motion_events(GDK_BUTTON1_MASK);
+                            break;
+                        }
+                    case SP_NODE_CONTEXT_RUBBERBAND_DRAGGING:
+                        if (Inkscape::Rubberband::get(desktop)->is_started()) {
+                            Geom::Point const motion_w(event->motion.x,
+                                                event->motion.y);
+                            Geom::Point const motion_dt(desktop->w2d(motion_w));
+                            Inkscape::Rubberband::get(desktop)->move(motion_dt);
+                        }
+                        break;
                 }
+
                 nc->drag = TRUE;
                 ret = TRUE;
             } else {
-                if (!nc->nodepath || selection->singleItem() == NULL) {
+                if (!se->has_nodepath() || selection->singleItem() == NULL) {
                     break;
                 }
 
-                SPItem *item_over = sp_event_context_over_item (desktop, selection->singleItem(),
-                                                                NR::Point(event->motion.x, event->motion.y));
                 bool over_stroke = false;
-                if (item_over && nc->nodepath) {
-                    over_stroke = sp_node_context_is_over_stroke (nc, item_over, NR::Point(event->motion.x, event->motion.y), false);
-                }
+                over_stroke = se->is_over_stroke(Geom::Point(event->motion.x, event->motion.y), false);
 
                 if (nc->cursor_drag && !over_stroke) {
                     event_context->cursor_shape = cursor_node_xpm;
@@ -605,32 +423,98 @@ sp_node_context_root_handler(SPEventContext *event_context, GdkEvent *event)
                 }
             }
             break;
+
+        case GDK_2BUTTON_PRESS:
         case GDK_BUTTON_RELEASE:
-            event_context->xp = event_context->yp = 0;
-            if (event->button.button == 1) {
+            if ( (event->button.button == 1) && (!nc->drag) && !event_context->space_panning) {
+                // find out clicked item, disregarding groups, honoring Alt
+                SPItem *item_clicked = sp_event_context_find_item (desktop,
+                        Geom::Point(event->button.x, event->button.y),
+                        (event->button.state & GDK_MOD1_MASK) && !(event->button.state & GDK_CONTROL_MASK), TRUE);
+
+                event_context->xp = event_context->yp = 0;
 
-                NR::Maybe<NR::Rect> b = Inkscape::Rubberband::get()->getRectangle();
+                bool over_stroke = false;
+                if (se->has_nodepath()) {
+                    over_stroke = se->is_over_stroke(Geom::Point(event->button.x, event->button.y), false);
+                }
 
-                if (nc->hit && !event_context->within_tolerance) { //drag curve
-                    sp_nodepath_update_repr (nc->nodepath);
-                } else if (b != NR::Nothing() && !event_context->within_tolerance) { // drag to select
-                    if (nc->nodepath) {
-                        sp_nodepath_select_rect(nc->nodepath, b.assume(), event->button.state & GDK_SHIFT_MASK);
+                if (item_clicked || over_stroke) {
+                    if (over_stroke || nc->added_node) {
+                        switch (event->type) {
+                            case GDK_BUTTON_RELEASE:
+                               if (event->button.state & GDK_CONTROL_MASK && event->button.state & GDK_MOD1_MASK) {
+                                    //add a node
+                                    se->add_node_near_point();
+                                } else {
+                                    if (nc->added_node) { // we just received double click, ignore release
+                                        nc->added_node = false;
+                                        break;
+                                    }
+                                    //select the segment
+                                    if (event->button.state & GDK_SHIFT_MASK) {
+                                        se->select_segment_near_point(true);
+                                    } else {
+                                        se->select_segment_near_point(false);
+                                    }
+                                    desktop->updateNow();
+                                }
+                               break;
+                            case GDK_2BUTTON_PRESS:
+                                //add a node
+                                se->add_node_near_point();
+                                nc->added_node = true;
+                                break;
+                            default:
+                                break;
+                        }
+                    } else if (event->button.state & GDK_SHIFT_MASK) {
+                        selection->toggle(item_clicked);
+                        desktop->updateNow();
+                    } else {
+                        selection->set(item_clicked);
+                        desktop->updateNow();
+                    }
+                    Inkscape::Rubberband::get(desktop)->stop();
+                    if (nc->grabbed) {
+                        sp_canvas_item_ungrab(nc->grabbed, event->button.time);
+                        nc->grabbed = NULL;
                     }
-                } else {
-                    if (!(nc->rb_escaped)) { // unless something was cancelled
-                        if (nc->nodepath && nc->nodepath->selected)
-                            sp_nodepath_deselect(nc->nodepath);
-                        else
-                            SP_DT_SELECTION(desktop)->clear();
+                    ret = TRUE;
+                    break;
+                }
+            }
+            if (event->type == GDK_BUTTON_RELEASE) {
+               event_context->xp = event_context->yp = 0;
+                if (event->button.button == 1) {
+                       Geom::OptRect b = Inkscape::Rubberband::get(desktop)->getRectangle();
+
+                    if (se->hits_curve() && !event_context->within_tolerance) { //drag curve
+                        se->finish_drag();
+                    } else if (b && !event_context->within_tolerance) { // drag to select
+                        se->select_rect(*b, event->button.state & GDK_SHIFT_MASK);
+                    } else {
+                        if (!(nc->rb_escaped)) { // unless something was canceled
+                            if (se->has_selection())
+                                se->deselect();
+                            else
+                                sp_desktop_selection(desktop)->clear();
+                        }
                     }
+                    ret = TRUE;
+                    Inkscape::Rubberband::get(desktop)->stop();
+                                       
+                                       if (nc->grabbed) {
+                                               sp_canvas_item_ungrab(nc->grabbed, event->button.time);
+                                               nc->grabbed = NULL;
+                                       }
+                                       
+                    desktop->updateNow();
+                    nc->rb_escaped = false;
+                    nc->drag = FALSE;
+                    se->cancel_hit();
+                    nc->current_state = SP_NODE_CONTEXT_INACTIVE;
                 }
-                ret = TRUE;
-                Inkscape::Rubberband::get()->stop();
-                nc->rb_escaped = false;
-                nc->drag = FALSE;
-                nc->hit = false;
-                break;
             }
             break;
         case GDK_KEY_PRESS:
@@ -638,69 +522,87 @@ sp_node_context_root_handler(SPEventContext *event_context, GdkEvent *event)
                 case GDK_Insert:
                 case GDK_KP_Insert:
                     // with any modifiers
-                    sp_node_selected_add_node();
+                    se->add_node();
                     ret = TRUE;
                     break;
+                case GDK_I:
+                case GDK_i:
+                    // apple keyboards have no Insert
+                    if (MOD__SHIFT_ONLY) {
+                        se->add_node();
+                        ret = TRUE;
+                    }
+                    break;
                 case GDK_Delete:
                 case GDK_KP_Delete:
                 case GDK_BackSpace:
-                    // with any modifiers
-                    sp_node_selected_delete();
+                    if (MOD__CTRL_ONLY) {
+                        se->delete_nodes();
+                    } else {
+                        se->delete_nodes_preserving_shape();
+                    }
                     ret = TRUE;
                     break;
                 case GDK_C:
                 case GDK_c:
                     if (MOD__SHIFT_ONLY) {
-                        sp_node_selected_set_type(Inkscape::NodePath::NODE_CUSP);
+                        se->set_node_type(Inkscape::NodePath::NODE_CUSP);
                         ret = TRUE;
                     }
                     break;
                 case GDK_S:
                 case GDK_s:
                     if (MOD__SHIFT_ONLY) {
-                        sp_node_selected_set_type(Inkscape::NodePath::NODE_SMOOTH);
+                        se->set_node_type(Inkscape::NodePath::NODE_SMOOTH);
+                        ret = TRUE;
+                    }
+                    break;
+                case GDK_A:
+                case GDK_a:
+                    if (MOD__SHIFT_ONLY) {
+                        se->set_node_type(Inkscape::NodePath::NODE_AUTO);
                         ret = TRUE;
                     }
                     break;
                 case GDK_Y:
                 case GDK_y:
                     if (MOD__SHIFT_ONLY) {
-                        sp_node_selected_set_type(Inkscape::NodePath::NODE_SYMM);
+                        se->set_node_type(Inkscape::NodePath::NODE_SYMM);
                         ret = TRUE;
                     }
                     break;
                 case GDK_B:
                 case GDK_b:
                     if (MOD__SHIFT_ONLY) {
-                        sp_node_selected_break();
+                        se->break_at_nodes();
                         ret = TRUE;
                     }
                     break;
                 case GDK_J:
                 case GDK_j:
                     if (MOD__SHIFT_ONLY) {
-                        sp_node_selected_join();
+                        se->join_nodes();
                         ret = TRUE;
                     }
                     break;
                 case GDK_D:
                 case GDK_d:
                     if (MOD__SHIFT_ONLY) {
-                        sp_node_selected_duplicate();
+                        se->duplicate_nodes();
                         ret = TRUE;
                     }
                     break;
                 case GDK_L:
                 case GDK_l:
                     if (MOD__SHIFT_ONLY) {
-                        sp_node_selected_set_line_type(NR_LINETO);
+                        se->set_type_of_segments(NR_LINETO);
                         ret = TRUE;
                     }
                     break;
                 case GDK_U:
                 case GDK_u:
                     if (MOD__SHIFT_ONLY) {
-                        sp_node_selected_set_line_type(NR_CURVETO);
+                        se->set_type_of_segments(NR_CURVETO);
                         ret = TRUE;
                     }
                     break;
@@ -708,7 +610,14 @@ sp_node_context_root_handler(SPEventContext *event_context, GdkEvent *event)
                 case GDK_r:
                     if (MOD__SHIFT_ONLY) {
                         // FIXME: add top panel button
-                        sp_selected_path_reverse();
+                        sp_selected_path_reverse(desktop);
+                        ret = TRUE;
+                    }
+                    break;
+                case GDK_x:
+                case GDK_X:
+                    if (MOD__ALT_ONLY) {
+                        desktop->setToolboxFocusTo ("altx-nodes");
                         ret = TRUE;
                     }
                     break;
@@ -716,13 +625,15 @@ sp_node_context_root_handler(SPEventContext *event_context, GdkEvent *event)
                 case GDK_KP_Left:
                 case GDK_KP_4:
                     if (!MOD__CTRL) { // not ctrl
+                        gint mul = 1 + gobble_key_events(
+                            get_group0_keyval(&event->key), 0); // with any mask
                         if (MOD__ALT) { // alt
-                            if (MOD__SHIFT) sp_node_selected_move_screen(-10, 0); // shift
-                            else sp_node_selected_move_screen(-1, 0); // no shift
+                            if (MOD__SHIFT) se->move_nodes_screen(desktop, mul*-10, 0); // shift
+                            else se->move_nodes_screen(desktop, mul*-1, 0); // no shift
                         }
                         else { // no alt
-                            if (MOD__SHIFT) sp_node_selected_move(-10*nudge, 0); // shift
-                            else sp_node_selected_move(-nudge, 0); // no shift
+                            if (MOD__SHIFT) se->move_nodes(mul*-10*nudge, 0); // shift
+                            else se->move_nodes(mul*-nudge, 0); // no shift
                         }
                         ret = TRUE;
                     }
@@ -731,13 +642,15 @@ sp_node_context_root_handler(SPEventContext *event_context, GdkEvent *event)
                 case GDK_KP_Up:
                 case GDK_KP_8:
                     if (!MOD__CTRL) { // not ctrl
+                        gint mul = 1 + gobble_key_events(
+                            get_group0_keyval(&event->key), 0); // with any mask
                         if (MOD__ALT) { // alt
-                            if (MOD__SHIFT) sp_node_selected_move_screen(0, 10); // shift
-                            else sp_node_selected_move_screen(0, 1); // no shift
+                            if (MOD__SHIFT) se->move_nodes_screen(desktop, 0, mul*10); // shift
+                            else se->move_nodes_screen(desktop, 0, mul*1); // no shift
                         }
                         else { // no alt
-                            if (MOD__SHIFT) sp_node_selected_move(0, 10*nudge); // shift
-                            else sp_node_selected_move(0, nudge); // no shift
+                            if (MOD__SHIFT) se->move_nodes(0, mul*10*nudge); // shift
+                            else se->move_nodes(0, mul*nudge); // no shift
                         }
                         ret = TRUE;
                     }
@@ -746,13 +659,15 @@ sp_node_context_root_handler(SPEventContext *event_context, GdkEvent *event)
                 case GDK_KP_Right:
                 case GDK_KP_6:
                     if (!MOD__CTRL) { // not ctrl
+                        gint mul = 1 + gobble_key_events(
+                            get_group0_keyval(&event->key), 0); // with any mask
                         if (MOD__ALT) { // alt
-                            if (MOD__SHIFT) sp_node_selected_move_screen(10, 0); // shift
-                            else sp_node_selected_move_screen(1, 0); // no shift
+                            if (MOD__SHIFT) se->move_nodes_screen(desktop, mul*10, 0); // shift
+                            else se->move_nodes_screen(desktop, mul*1, 0); // no shift
                         }
                         else { // no alt
-                            if (MOD__SHIFT) sp_node_selected_move(10*nudge, 0); // shift
-                            else sp_node_selected_move(nudge, 0); // no shift
+                            if (MOD__SHIFT) se->move_nodes(mul*10*nudge, 0); // shift
+                            else se->move_nodes(mul*nudge, 0); // no shift
                         }
                         ret = TRUE;
                     }
@@ -761,40 +676,31 @@ sp_node_context_root_handler(SPEventContext *event_context, GdkEvent *event)
                 case GDK_KP_Down:
                 case GDK_KP_2:
                     if (!MOD__CTRL) { // not ctrl
+                        gint mul = 1 + gobble_key_events(
+                            get_group0_keyval(&event->key), 0); // with any mask
                         if (MOD__ALT) { // alt
-                            if (MOD__SHIFT) sp_node_selected_move_screen(0, -10); // shift
-                            else sp_node_selected_move_screen(0, -1); // no shift
+                            if (MOD__SHIFT) se->move_nodes_screen(desktop, 0, mul*-10); // shift
+                            else se->move_nodes_screen(desktop, 0, mul*-1); // no shift
                         }
                         else { // no alt
-                            if (MOD__SHIFT) sp_node_selected_move(0, -10*nudge); // shift
-                            else sp_node_selected_move(0, -nudge); // no shift
+                            if (MOD__SHIFT) se->move_nodes(0, mul*-10*nudge); // shift
+                            else se->move_nodes(0, mul*-nudge); // no shift
                         }
                         ret = TRUE;
                     }
                     break;
-                case GDK_Tab: // Tab - cycle selection forward
-                    if (!(MOD__CTRL_ONLY || (MOD__CTRL && MOD__SHIFT))) {
-                        sp_nodepath_select_next(nc->nodepath);
-                        ret = TRUE;
-                    }
-                    break;
-                case GDK_ISO_Left_Tab:  // Shift Tab - cycle selection backward
-                    if (!(MOD__CTRL_ONLY || (MOD__CTRL && MOD__SHIFT))) {
-                        sp_nodepath_select_prev(nc->nodepath);
-                        ret = TRUE;
-                    }
-                    break;
                 case GDK_Escape:
                 {
-                    NR::Maybe<NR::Rect> const b = Inkscape::Rubberband::get()->getRectangle();
-                    if (b != NR::Nothing()) {
-                        Inkscape::Rubberband::get()->stop();
+                    Geom::OptRect const b = Inkscape::Rubberband::get(desktop)->getRectangle();
+                    if (b) {
+                        Inkscape::Rubberband::get(desktop)->stop();
+                        nc->current_state = SP_NODE_CONTEXT_INACTIVE;
                         nc->rb_escaped = true;
                     } else {
-                        if (nc->nodepath && nc->nodepath->selected) {
-                            sp_nodepath_deselect(nc->nodepath);
+                        if (se->has_selection()) {
+                            se->deselect();
                         } else {
-                            SP_DT_SELECTION(desktop)->clear();
+                            sp_desktop_selection(desktop)->clear();
                         }
                     }
                     ret = TRUE;
@@ -804,40 +710,40 @@ sp_node_context_root_handler(SPEventContext *event_context, GdkEvent *event)
                 case GDK_bracketleft:
                     if ( MOD__CTRL && !MOD__ALT && ( snaps != 0 ) ) {
                         if (nc->leftctrl)
-                            sp_nodepath_selected_nodes_rotate (nc->nodepath, M_PI/snaps, -1, false);
+                            se->rotate_nodes (M_PI/snaps, -1, false);
                         if (nc->rightctrl)
-                            sp_nodepath_selected_nodes_rotate (nc->nodepath, M_PI/snaps, 1, false);
+                            se->rotate_nodes (M_PI/snaps, 1, false);
                     } else if ( MOD__ALT && !MOD__CTRL ) {
                         if (nc->leftalt && nc->rightalt)
-                            sp_nodepath_selected_nodes_rotate (nc->nodepath, 1, 0, true);
+                            se->rotate_nodes (1, 0, true);
                         else {
                             if (nc->leftalt)
-                                sp_nodepath_selected_nodes_rotate (nc->nodepath, 1, -1, true);
+                                se->rotate_nodes (1, -1, true);
                             if (nc->rightalt)
-                                sp_nodepath_selected_nodes_rotate (nc->nodepath, 1, 1, true);
+                                se->rotate_nodes (1, 1, true);
                         }
                     } else if ( snaps != 0 ) {
-                        sp_nodepath_selected_nodes_rotate (nc->nodepath, M_PI/snaps, 0, false);
+                        se->rotate_nodes (M_PI/snaps, 0, false);
                     }
                     ret = TRUE;
                     break;
                 case GDK_bracketright:
                     if ( MOD__CTRL && !MOD__ALT && ( snaps != 0 ) ) {
                         if (nc->leftctrl)
-                            sp_nodepath_selected_nodes_rotate (nc->nodepath, -M_PI/snaps, -1, false);
+                            se->rotate_nodes (-M_PI/snaps, -1, false);
                         if (nc->rightctrl)
-                            sp_nodepath_selected_nodes_rotate (nc->nodepath, -M_PI/snaps, 1, false);
+                            se->rotate_nodes (-M_PI/snaps, 1, false);
                     } else if ( MOD__ALT && !MOD__CTRL ) {
                         if (nc->leftalt && nc->rightalt)
-                            sp_nodepath_selected_nodes_rotate (nc->nodepath, -1, 0, true);
+                            se->rotate_nodes (-1, 0, true);
                         else {
                             if (nc->leftalt)
-                                sp_nodepath_selected_nodes_rotate (nc->nodepath, -1, -1, true);
+                                se->rotate_nodes (-1, -1, true);
                             if (nc->rightalt)
-                                sp_nodepath_selected_nodes_rotate (nc->nodepath, -1, 1, true);
+                                se->rotate_nodes (-1, 1, true);
                         }
                     } else if ( snaps != 0 ) {
-                        sp_nodepath_selected_nodes_rotate (nc->nodepath, -M_PI/snaps, 0, false);
+                        se->rotate_nodes (-M_PI/snaps, 0, false);
                     }
                     ret = TRUE;
                     break;
@@ -845,20 +751,20 @@ sp_node_context_root_handler(SPEventContext *event_context, GdkEvent *event)
                 case GDK_comma:
                     if (MOD__CTRL) {
                         if (nc->leftctrl)
-                            sp_nodepath_selected_nodes_scale(nc->nodepath, -offset, -1);
+                            se->scale_nodes(-offset, -1);
                         if (nc->rightctrl)
-                            sp_nodepath_selected_nodes_scale(nc->nodepath, -offset, 1);
+                            se->scale_nodes(-offset, 1);
                     } else if (MOD__ALT) {
                         if (nc->leftalt && nc->rightalt)
-                            sp_nodepath_selected_nodes_scale_screen(nc->nodepath, -1, 0);
+                            se->scale_nodes_screen (-1, 0);
                         else {
                             if (nc->leftalt)
-                                sp_nodepath_selected_nodes_scale_screen(nc->nodepath, -1, -1);
+                                se->scale_nodes_screen (-1, -1);
                             if (nc->rightalt)
-                                sp_nodepath_selected_nodes_scale_screen(nc->nodepath, -1, 1);
+                                se->scale_nodes_screen (-1, 1);
                         }
                     } else {
-                        sp_nodepath_selected_nodes_scale(nc->nodepath, -offset, 0);
+                        se->scale_nodes (-offset, 0);
                     }
                     ret = TRUE;
                     break;
@@ -866,20 +772,20 @@ sp_node_context_root_handler(SPEventContext *event_context, GdkEvent *event)
                 case GDK_period:
                     if (MOD__CTRL) {
                         if (nc->leftctrl)
-                            sp_nodepath_selected_nodes_scale(nc->nodepath, offset, -1);
+                            se->scale_nodes (offset, -1);
                         if (nc->rightctrl)
-                            sp_nodepath_selected_nodes_scale(nc->nodepath, offset, 1);
+                            se->scale_nodes (offset, 1);
                     } else if (MOD__ALT) {
                         if (nc->leftalt && nc->rightalt)
-                            sp_nodepath_selected_nodes_scale_screen(nc->nodepath, 1, 0);
+                            se->scale_nodes_screen (1, 0);
                         else {
                             if (nc->leftalt)
-                                sp_nodepath_selected_nodes_scale_screen(nc->nodepath, 1, -1);
+                                se->scale_nodes_screen (1, -1);
                             if (nc->rightalt)
-                                sp_nodepath_selected_nodes_scale_screen(nc->nodepath, 1, 1);
+                                se->scale_nodes_screen (1, 1);
                         }
                     } else {
-                        sp_nodepath_selected_nodes_scale(nc->nodepath, offset, 0);
+                        se->scale_nodes (offset, 0);
                     }
                     ret = TRUE;
                     break;