Code

Make curvature work again by fixing a minor omission
[inkscape.git] / src / node-context.cpp
index 56b6dd760ff13ff44159feb5af7b115bca8dc3a0..7efa57290832fb23565c020ddf57e4f448cc8183 100644 (file)
@@ -13,6 +13,8 @@
 #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);
@@ -93,22 +102,31 @@ sp_node_context_init(SPNodeContext *node_context)
     node_context->rightalt = FALSE;
     node_context->leftctrl = FALSE;
     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();
 
-    delete nc->shape_editor;
+    delete ec->shape_editor;
 
     if (nc->_node_message_context) {
         delete nc->_node_message_context;
@@ -125,13 +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_desktop_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_desktop_selection(ec->desktop);
     SPItem *item = selection->singleItem();
 
-    nc->shape_editor = new ShapeEditor(ec->desktop);
+    ec->shape_editor = new ShapeEditor(ec->desktop);
 
     nc->rb_escaped = false;
 
@@ -142,20 +161,45 @@ sp_node_context_setup(SPEventContext *ec)
     nc->current_state = SP_NODE_CONTEXT_INACTIVE;
 
     if (item) {
-        nc->shape_editor->set_item(item);
+        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();
     }
 
+    ec->desktop->emitToolSubselectionChanged(NULL); // sets the coord entry fields to inactive
+
     nc->_node_message_context = new Inkscape::MessageContext((ec->desktop)->messageStack());
 
-    nc->shape_editor->update_statusbar();
+    ec->shape_editor->update_statusbar();
+}
+
+static void
+sp_node_context_flash_path(SPEventContext *event_context, SPItem *item, guint timeout) {
+    SPNodeContext *nc = SP_NODE_CONTEXT(event_context);
+
+    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;
+        }
+
+        SPCanvasItem *canvasitem = sp_nodepath_generate_helperpath(desktop, item);
+
+        if (canvasitem) {
+            nc->flash_tempitem = desktop->add_temporary_canvasitem (canvasitem, timeout);
+        }
+    }
 }
 
 /**
@@ -165,14 +209,15 @@ destroys old and creates new nodepath and reassigns listeners to the new selecte
 void
 sp_node_context_selection_changed(Inkscape::Selection *selection, gpointer data)
 {
-    SPNodeContext *nc = SP_NODE_CONTEXT(data);
+    SPEventContext *ec = SP_EVENT_CONTEXT(data);
 
     // TODO: update ShapeEditorsCollective instead
-    nc->shape_editor->unset_item();
-    SPItem *item = selection->singleItem(); 
-    nc->shape_editor->set_item(item);
-
-    nc->shape_editor->update_statusbar();
+    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
@@ -185,11 +230,52 @@ sp_node_context_show_modifier_tip(SPEventContext *event_context, GdkEvent *event
          _("<b>Alt</b>: lock handle length; <b>Ctrl+Alt</b>: move along handles"));
 }
 
-
 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;
+
+    switch (event->type) {
+        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;
+
+
+            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;
+                    }
+                }
+                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;
+                    }
+                }
+                guint timeout = prefs->getInt("/tools/nodes/pathflash_timeout", 500);
+                sp_node_context_flash_path(event_context, actual_item, timeout);
+            }
+        }
+        break;
+
+        default:
+            break;
+    }
 
     if (((SPEventContextClass *) parent_class)->item_handler)
         ret = ((SPEventContextClass *) parent_class)->item_handler(event_context, item, event);
@@ -201,13 +287,23 @@ static gint
 sp_node_context_root_handler(SPEventContext *event_context, GdkEvent *event)
 {
     SPDesktop *desktop = event_context->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) {
@@ -217,13 +313,13 @@ sp_node_context_root_handler(SPEventContext *event_context, GdkEvent *event)
                 event_context->xp = (gint) event->button.x;
                 event_context->yp = (gint) event->button.y;
                 event_context->within_tolerance = true;
-                nc->shape_editor->cancel_hit();
+                se->cancel_hit();
 
                 if (!(event->button.state & GDK_SHIFT_MASK)) {
                     if (!nc->drag) {
-                        if (nc->shape_editor->has_nodepath() && selection->single() /* && item_over */) {
-                            // save drag origin
-                            bool over_stroke = nc->shape_editor->is_over_stroke(NR::Point(event->button.x, event->button.y), true);
+                        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;
@@ -232,10 +328,21 @@ sp_node_context_root_handler(SPEventContext *event_context, GdkEvent *event)
                         }
                     }
                 }
-                NR::Point const button_w(event->button.x,
+                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;
@@ -252,8 +359,8 @@ sp_node_context_root_handler(SPEventContext *event_context, GdkEvent *event)
 
                 // The path went away while dragging; throw away any further motion
                 // events until the mouse pointer is released.
-                
-                if (nc->shape_editor->hits_curve() && !nc->shape_editor->has_nodepath()) {
+
+                if (se->hits_curve() && !se->has_nodepath()) {
                   break;
                 }
 
@@ -266,7 +373,7 @@ sp_node_context_root_handler(SPEventContext *event_context, GdkEvent *event)
                 // 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 (nc->shape_editor->hits_curve() && nc->shape_editor->has_nodepath()) {
+                    if (se->hits_curve() && se->has_nodepath()) {
                         nc->current_state = SP_NODE_CONTEXT_NODE_DRAGGING;
                     } else {
                         nc->current_state = SP_NODE_CONTEXT_RUBBERBAND_DRAGGING;
@@ -276,17 +383,17 @@ sp_node_context_root_handler(SPEventContext *event_context, GdkEvent *event)
                 switch (nc->current_state) {
                     case SP_NODE_CONTEXT_NODE_DRAGGING:
                         {
-                            nc->shape_editor->curve_drag (event->motion.x, event->motion.y);
+                            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()->is_started()) {
-                            NR::Point const motion_w(event->motion.x,
+                        if (Inkscape::Rubberband::get(desktop)->is_started()) {
+                            Geom::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);
+                            Geom::Point const motion_dt(desktop->w2d(motion_w));
+                            Inkscape::Rubberband::get(desktop)->move(motion_dt);
                         }
                         break;
                 }
@@ -294,12 +401,12 @@ sp_node_context_root_handler(SPEventContext *event_context, GdkEvent *event)
                 nc->drag = TRUE;
                 ret = TRUE;
             } else {
-                if (!nc->shape_editor->has_nodepath() || selection->singleItem() == NULL) {
+                if (!se->has_nodepath() || selection->singleItem() == NULL) {
                     break;
                 }
 
                 bool over_stroke = false;
-                over_stroke = nc->shape_editor->is_over_stroke(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;
@@ -322,23 +429,23 @@ sp_node_context_root_handler(SPEventContext *event_context, GdkEvent *event)
             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,
-                        NR::Point(event->button.x, event->button.y),
+                        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;
 
                 bool over_stroke = false;
-                if (nc->shape_editor->has_nodepath()) {
-                    over_stroke = nc->shape_editor->is_over_stroke(NR::Point(event->button.x, event->button.y), false);
+                if (se->has_nodepath()) {
+                    over_stroke = se->is_over_stroke(Geom::Point(event->button.x, event->button.y), false);
                 }
 
                 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) {
+                               if (event->button.state & GDK_CONTROL_MASK && event->button.state & GDK_MOD1_MASK) {
                                     //add a node
-                                    nc->shape_editor->add_node_near_point();
+                                    se->add_node_near_point();
                                 } else {
                                     if (nc->added_node) { // we just received double click, ignore release
                                         nc->added_node = false;
@@ -346,16 +453,16 @@ sp_node_context_root_handler(SPEventContext *event_context, GdkEvent *event)
                                     }
                                     //select the segment
                                     if (event->button.state & GDK_SHIFT_MASK) {
-                                        nc->shape_editor->select_segment_near_point(true);
+                                        se->select_segment_near_point(true);
                                     } else {
-                                        nc->shape_editor->select_segment_near_point(false);
+                                        se->select_segment_near_point(false);
                                     }
                                     desktop->updateNow();
                                 }
-                                break;
+                               break;
                             case GDK_2BUTTON_PRESS:
                                 //add a node
-                                nc->shape_editor->add_node_near_point();
+                                se->add_node_near_point();
                                 nc->added_node = true;
                                 break;
                             default:
@@ -368,34 +475,44 @@ sp_node_context_root_handler(SPEventContext *event_context, GdkEvent *event)
                         selection->set(item_clicked);
                         desktop->updateNow();
                     }
-                    Inkscape::Rubberband::get()->stop();
+                    Inkscape::Rubberband::get(desktop)->stop();
+                    if (nc->grabbed) {
+                        sp_canvas_item_ungrab(nc->grabbed, event->button.time);
+                        nc->grabbed = NULL;
+                    }
                     ret = TRUE;
                     break;
                 }
-            } 
+            }
             if (event->type == GDK_BUTTON_RELEASE) {
-                event_context->xp = event_context->yp = 0;
+               event_context->xp = event_context->yp = 0;
                 if (event->button.button == 1) {
-                    NR::Maybe<NR::Rect> b = Inkscape::Rubberband::get()->getRectangle();
+                       Geom::OptRect b = Inkscape::Rubberband::get(desktop)->getRectangle();
 
-                    if (nc->shape_editor->hits_curve() && !event_context->within_tolerance) { //drag curve
-                        nc->shape_editor->finish_drag();
+                    if (se->hits_curve() && !event_context->within_tolerance) { //drag curve
+                        se->finish_drag();
                     } else if (b && !event_context->within_tolerance) { // drag to select
-                        nc->shape_editor->select_rect(*b, event->button.state & GDK_SHIFT_MASK);
+                        se->select_rect(*b, event->button.state & GDK_SHIFT_MASK);
                     } else {
-                        if (!(nc->rb_escaped)) { // unless something was cancelled
-                            if (nc->shape_editor->has_selection())
-                                nc->shape_editor->deselect();
+                        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()->stop();
+                    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;
-                    nc->shape_editor->cancel_hit();
+                    se->cancel_hit();
                     nc->current_state = SP_NODE_CONTEXT_INACTIVE;
                 }
             }
@@ -405,72 +522,87 @@ sp_node_context_root_handler(SPEventContext *event_context, GdkEvent *event)
                 case GDK_Insert:
                 case GDK_KP_Insert:
                     // with any modifiers
-                    nc->shape_editor->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:
                     if (MOD__CTRL_ONLY) {
-                        nc->shape_editor->delete_nodes();
+                        se->delete_nodes();
                     } else {
-                        nc->shape_editor->delete_nodes_preserving_shape();
+                        se->delete_nodes_preserving_shape();
                     }
                     ret = TRUE;
                     break;
                 case GDK_C:
                 case GDK_c:
                     if (MOD__SHIFT_ONLY) {
-                        nc->shape_editor->set_node_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) {
-                        nc->shape_editor->set_node_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) {
-                        nc->shape_editor->set_node_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) {
-                        nc->shape_editor->break_at_nodes();
+                        se->break_at_nodes();
                         ret = TRUE;
                     }
                     break;
                 case GDK_J:
                 case GDK_j:
                     if (MOD__SHIFT_ONLY) {
-                        nc->shape_editor->join_nodes();
+                        se->join_nodes();
                         ret = TRUE;
                     }
                     break;
                 case GDK_D:
                 case GDK_d:
                     if (MOD__SHIFT_ONLY) {
-                        nc->shape_editor->duplicate_nodes();
+                        se->duplicate_nodes();
                         ret = TRUE;
                     }
                     break;
                 case GDK_L:
                 case GDK_l:
                     if (MOD__SHIFT_ONLY) {
-                        nc->shape_editor->set_type_of_segments(NR_LINETO);
+                        se->set_type_of_segments(NR_LINETO);
                         ret = TRUE;
                     }
                     break;
                 case GDK_U:
                 case GDK_u:
                     if (MOD__SHIFT_ONLY) {
-                        nc->shape_editor->set_type_of_segments(NR_CURVETO);
+                        se->set_type_of_segments(NR_CURVETO);
                         ret = TRUE;
                     }
                     break;
@@ -478,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;
@@ -486,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) nc->shape_editor->move_nodes_screen(-10, 0); // shift
-                            else nc->shape_editor->move_nodes_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) nc->shape_editor->move_nodes(-10*nudge, 0); // shift
-                            else nc->shape_editor->move_nodes(-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;
                     }
@@ -501,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) nc->shape_editor->move_nodes_screen(0, 10); // shift
-                            else nc->shape_editor->move_nodes_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) nc->shape_editor->move_nodes(0, 10*nudge); // shift
-                            else nc->shape_editor->move_nodes(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;
                     }
@@ -516,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) nc->shape_editor->move_nodes_screen(10, 0); // shift
-                            else nc->shape_editor->move_nodes_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) nc->shape_editor->move_nodes(10*nudge, 0); // shift
-                            else nc->shape_editor->move_nodes(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;
                     }
@@ -531,27 +676,29 @@ 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) nc->shape_editor->move_nodes_screen(0, -10); // shift
-                            else nc->shape_editor->move_nodes_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) nc->shape_editor->move_nodes(0, -10*nudge); // shift
-                            else nc->shape_editor->move_nodes(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_Escape:
                 {
-                    NR::Maybe<NR::Rect> const b = Inkscape::Rubberband::get()->getRectangle();
+                    Geom::OptRect const b = Inkscape::Rubberband::get(desktop)->getRectangle();
                     if (b) {
-                        Inkscape::Rubberband::get()->stop();
+                        Inkscape::Rubberband::get(desktop)->stop();
                         nc->current_state = SP_NODE_CONTEXT_INACTIVE;
                         nc->rb_escaped = true;
                     } else {
-                        if (nc->shape_editor->has_selection()) {
-                            nc->shape_editor->deselect();
+                        if (se->has_selection()) {
+                            se->deselect();
                         } else {
                             sp_desktop_selection(desktop)->clear();
                         }
@@ -563,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)
-                            nc->shape_editor->rotate_nodes (M_PI/snaps, -1, false);
+                            se->rotate_nodes (M_PI/snaps, -1, false);
                         if (nc->rightctrl)
-                            nc->shape_editor->rotate_nodes (M_PI/snaps, 1, false);
+                            se->rotate_nodes (M_PI/snaps, 1, false);
                     } else if ( MOD__ALT && !MOD__CTRL ) {
                         if (nc->leftalt && nc->rightalt)
-                            nc->shape_editor->rotate_nodes (1, 0, true);
+                            se->rotate_nodes (1, 0, true);
                         else {
                             if (nc->leftalt)
-                                nc->shape_editor->rotate_nodes (1, -1, true);
+                                se->rotate_nodes (1, -1, true);
                             if (nc->rightalt)
-                                nc->shape_editor->rotate_nodes (1, 1, true);
+                                se->rotate_nodes (1, 1, true);
                         }
                     } else if ( snaps != 0 ) {
-                        nc->shape_editor->rotate_nodes (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)
-                            nc->shape_editor->rotate_nodes (-M_PI/snaps, -1, false);
+                            se->rotate_nodes (-M_PI/snaps, -1, false);
                         if (nc->rightctrl)
-                            nc->shape_editor->rotate_nodes (-M_PI/snaps, 1, false);
+                            se->rotate_nodes (-M_PI/snaps, 1, false);
                     } else if ( MOD__ALT && !MOD__CTRL ) {
                         if (nc->leftalt && nc->rightalt)
-                            nc->shape_editor->rotate_nodes (-1, 0, true);
+                            se->rotate_nodes (-1, 0, true);
                         else {
                             if (nc->leftalt)
-                                nc->shape_editor->rotate_nodes (-1, -1, true);
+                                se->rotate_nodes (-1, -1, true);
                             if (nc->rightalt)
-                                nc->shape_editor->rotate_nodes (-1, 1, true);
+                                se->rotate_nodes (-1, 1, true);
                         }
                     } else if ( snaps != 0 ) {
-                        nc->shape_editor->rotate_nodes (-M_PI/snaps, 0, false);
+                        se->rotate_nodes (-M_PI/snaps, 0, false);
                     }
                     ret = TRUE;
                     break;
@@ -604,20 +751,20 @@ sp_node_context_root_handler(SPEventContext *event_context, GdkEvent *event)
                 case GDK_comma:
                     if (MOD__CTRL) {
                         if (nc->leftctrl)
-                            nc->shape_editor->scale_nodes(-offset, -1);
+                            se->scale_nodes(-offset, -1);
                         if (nc->rightctrl)
-                            nc->shape_editor->scale_nodes(-offset, 1);
+                            se->scale_nodes(-offset, 1);
                     } else if (MOD__ALT) {
                         if (nc->leftalt && nc->rightalt)
-                            nc->shape_editor->scale_nodes_screen (-1, 0);
+                            se->scale_nodes_screen (-1, 0);
                         else {
                             if (nc->leftalt)
-                                nc->shape_editor->scale_nodes_screen (-1, -1);
+                                se->scale_nodes_screen (-1, -1);
                             if (nc->rightalt)
-                                nc->shape_editor->scale_nodes_screen (-1, 1);
+                                se->scale_nodes_screen (-1, 1);
                         }
                     } else {
-                        nc->shape_editor->scale_nodes (-offset, 0);
+                        se->scale_nodes (-offset, 0);
                     }
                     ret = TRUE;
                     break;
@@ -625,20 +772,20 @@ sp_node_context_root_handler(SPEventContext *event_context, GdkEvent *event)
                 case GDK_period:
                     if (MOD__CTRL) {
                         if (nc->leftctrl)
-                            nc->shape_editor->scale_nodes (offset, -1);
+                            se->scale_nodes (offset, -1);
                         if (nc->rightctrl)
-                            nc->shape_editor->scale_nodes (offset, 1);
+                            se->scale_nodes (offset, 1);
                     } else if (MOD__ALT) {
                         if (nc->leftalt && nc->rightalt)
-                            nc->shape_editor->scale_nodes_screen (1, 0);
+                            se->scale_nodes_screen (1, 0);
                         else {
                             if (nc->leftalt)
-                                nc->shape_editor->scale_nodes_screen (1, -1);
+                                se->scale_nodes_screen (1, -1);
                             if (nc->rightalt)
-                                nc->shape_editor->scale_nodes_screen (1, 1);
+                                se->scale_nodes_screen (1, 1);
                         }
                     } else {
-                        nc->shape_editor->scale_nodes (offset, 0);
+                        se->scale_nodes (offset, 0);
                     }
                     ret = TRUE;
                     break;