Code

NR::Maybe => boost::optional
[inkscape.git] / src / node-context.cpp
index 0008181613a98343c2081641f889ac557bbd3ade..d8a1ec1d7deb953f20517561dfd35d78ff8b5d2b 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 "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);
@@ -95,6 +104,10 @@ 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
@@ -125,10 +138,11 @@ 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);
@@ -142,7 +156,8 @@ sp_node_context_setup(SPEventContext *ec)
     nc->current_state = SP_NODE_CONTEXT_INACTIVE;
 
     if (item) {
-        nc->shape_editor->set_item(item);
+        nc->shape_editor->set_item(item, SH_NODEPATH);
+        nc->shape_editor->set_item(item, SH_KNOTHOLDER);
     }
 
     if (prefs_get_int_attribute("tools.nodes", "selcue", 0) != 0) {
@@ -153,11 +168,35 @@ sp_node_context_setup(SPEventContext *ec)
         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();
 }
 
+static void
+sp_node_context_flash_path(SPEventContext *event_context, SPItem *item, guint timeout) {
+    g_print ("-----> sp_node_context_flash_path(): TODO: generate the helper path!!\n");
+    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;
+        }
+
+        if (SP_IS_PATH(item)) {
+            SPCanvasItem *canvasitem = sp_nodepath_helperpath_from_path(desktop, SP_PATH(item));
+            nc->flash_tempitem = desktop->add_temporary_canvasitem (canvasitem, timeout);
+        }
+    }
+}
+
 /**
 \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
@@ -168,10 +207,11 @@ sp_node_context_selection_changed(Inkscape::Selection *selection, gpointer data)
     SPNodeContext *nc = SP_NODE_CONTEXT(data);
 
     // TODO: update ShapeEditorsCollective instead
-    nc->shape_editor->unset_item();
+    nc->shape_editor->unset_item(SH_NODEPATH);
+    nc->shape_editor->unset_item(SH_KNOTHOLDER);
     SPItem *item = selection->singleItem(); 
-    nc->shape_editor->set_item(item);
-
+    nc->shape_editor->set_item(item, SH_NODEPATH);
+    nc->shape_editor->set_item(item, SH_KNOTHOLDER);
     nc->shape_editor->update_statusbar();
 }
 
@@ -185,118 +225,27 @@ 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;
 
-    SPDesktop *desktop = event_context->desktop;
-    Inkscape::Selection *selection = sp_desktop_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));
-
-                    bool over_stroke = false;
-                    if (item_over && nc->shape_editor->has_nodepath()) {
-                        over_stroke = nc->shape_editor->is_over_stroke(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
-                                    nc->shape_editor->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) {
-                                        nc->shape_editor->select_segment_near_point(true);
-                                    } else {
-                                        nc->shape_editor->select_segment_near_point(false);
-                                    }
-                                    desktop->updateNow();
-                                }
-                                break;
-                            case GDK_2BUTTON_PRESS:
-                                //add a node
-                                nc->shape_editor->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();
-                    }
-
-                    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->shape_editor->cancel_hit();
-
-                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->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);
-                            //only dragging curves
-                            if (over_stroke) {
-                                nc->shape_editor->select_segment_near_point(false);
-                                ret = TRUE;
-                            } else {
-                                break;
-                            }
-                        } else {
-                            break;
-                        }
-
-                    ret = TRUE;
-                }
-                break;
+    if (prefs_get_int_attribute ("tools.nodes", "pathflash_enabled", 0) == 1) {
+        guint timeout = prefs_get_int_attribute("tools.nodes", "pathflash_timeout", 500);
+        if (SP_IS_LPE_ITEM(item)) {
+            Inkscape::LivePathEffect::Effect *lpe = sp_lpe_item_get_current_lpe(SP_LPE_ITEM(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()
+                return ret;
             }
-            break;
-        default:
-            break;
+        }
+        sp_node_context_flash_path(event_context, item, timeout);
     }
 
-    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;
 }
@@ -313,17 +262,37 @@ sp_node_context_root_handler(SPEventContext *event_context, GdkEvent *event)
     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);
 
-    gint ret = FALSE;
+    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->shape_editor->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);
+                            //only dragging curves
+                            if (over_stroke) {
+                                ret = TRUE;
+                                break;
+                            }
+                        }
+                    }
+                }
                 NR::Point const button_w(event->button.x,
                                          event->button.y);
                 NR::Point const button_dt(desktop->w2d(button_w));
@@ -334,7 +303,7 @@ sp_node_context_root_handler(SPEventContext *event_context, GdkEvent *event)
             }
             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 )
@@ -390,12 +359,8 @@ sp_node_context_root_handler(SPEventContext *event_context, GdkEvent *event)
                     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->shape_editor->has_nodepath()) {
-                    over_stroke = nc->shape_editor->is_over_stroke(NR::Point(event->motion.x, event->motion.y), false);
-                }
+                over_stroke = nc->shape_editor->is_over_stroke(NR::Point(event->motion.x, event->motion.y), false);
 
                 if (nc->cursor_drag && !over_stroke) {
                     event_context->cursor_shape = cursor_node_xpm;
@@ -412,32 +377,88 @@ 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) {
-
-                NR::Maybe<NR::Rect> b = Inkscape::Rubberband::get()->getRectangle();
-
-                if (nc->shape_editor->hits_curve() && !event_context->within_tolerance) { //drag curve
-                    nc->shape_editor->finish_drag();
-                } else if (b != NR::Nothing() && !event_context->within_tolerance) { // drag to select
-                    nc->shape_editor->select_rect(b.assume(), 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();
-                        else
-                            sp_desktop_selection(desktop)->clear();
+            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),
+                        (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 (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
+                                    nc->shape_editor->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) {
+                                        nc->shape_editor->select_segment_near_point(true);
+                                    } else {
+                                        nc->shape_editor->select_segment_near_point(false);
+                                    }
+                                    desktop->updateNow();
+                                }
+                                break;
+                            case GDK_2BUTTON_PRESS:
+                                //add a node
+                                nc->shape_editor->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()->stop();
+                    ret = TRUE;
+                    break;
+                }
+            } 
+            if (event->type == GDK_BUTTON_RELEASE) {
+                event_context->xp = event_context->yp = 0;
+                if (event->button.button == 1) {
+                    boost::optional<NR::Rect> b = Inkscape::Rubberband::get()->getRectangle();
+
+                    if (nc->shape_editor->hits_curve() && !event_context->within_tolerance) { //drag curve
+                        nc->shape_editor->finish_drag();
+                    } else if (b && !event_context->within_tolerance) { // drag to select
+                        nc->shape_editor->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();
+                            else
+                                sp_desktop_selection(desktop)->clear();
+                        }
+                    }
+                    ret = TRUE;
+                    Inkscape::Rubberband::get()->stop();
+                    desktop->updateNow();
+                    nc->rb_escaped = false;
+                    nc->drag = FALSE;
+                    nc->shape_editor->cancel_hit();
+                    nc->current_state = SP_NODE_CONTEXT_INACTIVE;
                 }
-                ret = TRUE;
-                Inkscape::Rubberband::get()->stop();
-                desktop->updateNow();
-                nc->rb_escaped = false;
-                nc->drag = FALSE;
-                nc->shape_editor->cancel_hit();
-                nc->current_state = SP_NODE_CONTEXT_INACTIVE;
-                break;
             }
             break;
         case GDK_KEY_PRESS:
@@ -522,17 +543,26 @@ sp_node_context_root_handler(SPEventContext *event_context, GdkEvent *event)
                         ret = TRUE;
                     }
                     break;
+                case GDK_x:
+                case GDK_X:
+                    if (MOD__ALT_ONLY) {
+                        desktop->setToolboxFocusTo ("altx-nodes");
+                        ret = TRUE;
+                    }
+                    break;
                 case GDK_Left: // move selection left
                 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) nc->shape_editor->move_nodes_screen(mul*-10, 0); // shift
+                            else nc->shape_editor->move_nodes_screen(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) nc->shape_editor->move_nodes(mul*-10*nudge, 0); // shift
+                            else nc->shape_editor->move_nodes(mul*-nudge, 0); // no shift
                         }
                         ret = TRUE;
                     }
@@ -541,13 +571,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) nc->shape_editor->move_nodes_screen(0, mul*10); // shift
+                            else nc->shape_editor->move_nodes_screen(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) nc->shape_editor->move_nodes(0, mul*10*nudge); // shift
+                            else nc->shape_editor->move_nodes(0, mul*nudge); // no shift
                         }
                         ret = TRUE;
                     }
@@ -556,13 +588,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) nc->shape_editor->move_nodes_screen(mul*10, 0); // shift
+                            else nc->shape_editor->move_nodes_screen(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) nc->shape_editor->move_nodes(mul*10*nudge, 0); // shift
+                            else nc->shape_editor->move_nodes(mul*nudge, 0); // no shift
                         }
                         ret = TRUE;
                     }
@@ -571,21 +605,23 @@ 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) nc->shape_editor->move_nodes_screen(0, mul*-10); // shift
+                            else nc->shape_editor->move_nodes_screen(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) nc->shape_editor->move_nodes(0, mul*-10*nudge); // shift
+                            else nc->shape_editor->move_nodes(0, mul*-nudge); // no shift
                         }
                         ret = TRUE;
                     }
                     break;
                 case GDK_Escape:
                 {
-                    NR::Maybe<NR::Rect> const b = Inkscape::Rubberband::get()->getRectangle();
-                    if (b != NR::Nothing()) {
+                    boost::optional<NR::Rect> const b = Inkscape::Rubberband::get()->getRectangle();
+                    if (b) {
                         Inkscape::Rubberband::get()->stop();
                         nc->current_state = SP_NODE_CONTEXT_INACTIVE;
                         nc->rb_escaped = true;