Code

* Implement node snapping.
authorKrzysztof Kosiński <tweenk.pl@gmail.com>
Sun, 10 Jan 2010 00:46:28 +0000 (01:46 +0100)
committerKrzysztof Kosiński <tweenk.pl@gmail.com>
Sun, 10 Jan 2010 00:46:28 +0000 (01:46 +0100)
* Fix minor bug in linear grow.
* Add --fixes.
* Move some node selection-related functions to ControlPointSelection.

21 files changed:
src/2geom/path.cpp
src/event-context.cpp
src/event-context.h
src/node-context.cpp [deleted file]
src/node-context.h [deleted file]
src/snap.cpp
src/snap.h
src/snapper.h
src/ui/tool/control-point-selection.cpp
src/ui/tool/control-point-selection.h
src/ui/tool/control-point.cpp
src/ui/tool/control-point.h
src/ui/tool/multi-path-manipulator.cpp
src/ui/tool/multi-path-manipulator.h
src/ui/tool/node-tool.cpp
src/ui/tool/node.cpp
src/ui/tool/node.h
src/ui/tool/path-manipulator.cpp
src/ui/tool/path-manipulator.h
src/ui/tool/selectable-control-point.cpp
src/verbs.cpp

index 981c9f044717747981ad3c7a85a3d42d5495c7cc..88c7a99b9df9af99c8d1e3112cfb8e03c7825700 100644 (file)
@@ -203,11 +203,10 @@ Path::nearestPointPerCurve(Point const& _point) const
 {
        //return a single nearest point for each curve in this path
        std::vector<double> np;
-       const Path& _path = *this;
-       for (Sequence::const_iterator it = _path.get_curves().begin() ; it != _path.get_curves().end()-1 ; ++it)
+       for (const_iterator it = begin() ; it != end_default(); ++it)
        //for (std::vector<Path>::const_iterator it = _path.begin(); it != _path.end(), ++it){
        {
-           np.push_back((*it)->nearestPoint(_point));
+           np.push_back(it->nearestPoint(_point));
     }
        return np;
 }  
index ec0169573db6a8aa81d13ae9168ae7753d9eade4..13e7e941054f348bcdfdb777fc5e8ee110b45a7a 100644 (file)
@@ -57,6 +57,7 @@
 #include "rubberband.h"
 #include "selcue.h"
 #include "lpe-tool-context.h"
+#include "ui/tool/control-point.h"
 
 static void sp_event_context_class_init(SPEventContextClass *klass);
 static void sp_event_context_init(SPEventContext *event_context);
@@ -1238,6 +1239,7 @@ void sp_event_context_snap_delay_handler(SPEventContext *ec, SPItem* const item,
 
 gboolean sp_event_context_snap_watchdog_callback(gpointer data)
 {
+    if (!data) return FALSE;
        // Snap NOW! For this the "postponed" flag will be reset and the last motion event will be repeated
        DelayedSnapEvent *dse = reinterpret_cast<DelayedSnapEvent*>(data);
 
@@ -1276,6 +1278,11 @@ gboolean sp_event_context_snap_watchdog_callback(gpointer data)
                                }
                        }
                        break;
+            case DelayedSnapEvent::CONTROL_POINT_HANDLER: {
+                using Inkscape::UI::ControlPoint;
+                ControlPoint *point = reinterpret_cast<ControlPoint*>(dse->getKnot());
+                point->_eventHandler(dse->getEvent());
+            } break;
                default:
                        g_warning("Origin of snap-delay event has not been defined!;");
                        break;
index 5285bdb877938362f21e4c628ebd1184fdf5b83a..5be2e19fb4a2163eafb87c51ff51ff66bd85f086 100644 (file)
@@ -49,7 +49,8 @@ public:
                UNDEFINED_HANDLER = 0,
                EVENTCONTEXT_ROOT_HANDLER,
                EVENTCONTEXT_ITEM_HANDLER,
-               KNOT_HANDLER
+               KNOT_HANDLER,
+               CONTROL_POINT_HANDLER
        };
 
        DelayedSnapEvent(SPEventContext *event_context, SPItem* const item, SPKnot* knot, GdkEventMotion const *event, DelayedSnapEvent::DelayedSnapEventOrigin const origin)
diff --git a/src/node-context.cpp b/src/node-context.cpp
deleted file mode 100644 (file)
index 7efa572..0000000
+++ /dev/null
@@ -1,868 +0,0 @@
-#define __SP_NODE_CONTEXT_C__
-
-/*
- * Node editing context
- *
- * Authors:
- *   Lauris Kaplinski <lauris@kaplinski.com>
- *   bulia byak <buliabyak@users.sf.net>
- *
- * 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 "display/sp-canvas-util.h"
-#include "object-edit.h"
-#include "sp-path.h"
-#include "path-chemistry.h"
-#include "rubberband.h"
-#include "desktop.h"
-#include "desktop-handles.h"
-#include "selection.h"
-#include "pixmaps/cursor-node.xpm"
-#include "message-context.h"
-#include "node-context.h"
-#include "pixmaps/cursor-node-d.xpm"
-#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);
-static void sp_node_context_dispose(GObject *object);
-
-static void sp_node_context_setup(SPEventContext *ec);
-static gint sp_node_context_root_handler(SPEventContext *event_context, GdkEvent *event);
-static gint sp_node_context_item_handler(SPEventContext *event_context,
-                                         SPItem *item, GdkEvent *event);
-
-static SPEventContextClass *parent_class;
-
-GType
-sp_node_context_get_type()
-{
-    static GType type = 0;
-    if (!type) {
-        GTypeInfo info = {
-            sizeof(SPNodeContextClass),
-            NULL, NULL,
-            (GClassInitFunc) sp_node_context_class_init,
-            NULL, NULL,
-            sizeof(SPNodeContext),
-            4,
-            (GInstanceInitFunc) sp_node_context_init,
-            NULL,    /* value_table */
-        };
-        type = g_type_register_static(SP_TYPE_EVENT_CONTEXT, "SPNodeContext", &info, (GTypeFlags)0);
-    }
-    return type;
-}
-
-static void
-sp_node_context_class_init(SPNodeContextClass *klass)
-{
-    GObjectClass *object_class = (GObjectClass *) klass;
-    SPEventContextClass *event_context_class = (SPEventContextClass *) klass;
-
-    parent_class = (SPEventContextClass*)g_type_class_peek_parent(klass);
-
-    object_class->dispose = sp_node_context_dispose;
-
-    event_context_class->setup = sp_node_context_setup;
-    event_context_class->root_handler = sp_node_context_root_handler;
-    event_context_class->item_handler = sp_node_context_item_handler;
-}
-
-static void
-sp_node_context_init(SPNodeContext *node_context)
-{
-    SPEventContext *event_context = SP_EVENT_CONTEXT(node_context);
-
-    event_context->cursor_shape = cursor_node_xpm;
-    event_context->hot_x = 1;
-    event_context->hot_y = 1;
-
-    node_context->leftalt = FALSE;
-    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);
-    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 ec->shape_editor;
-
-    if (nc->_node_message_context) {
-        delete nc->_node_message_context;
-    }
-
-    G_OBJECT_CLASS(parent_class)->dispose(object);
-}
-
-static void
-sp_node_context_setup(SPEventContext *ec)
-{
-    SPNodeContext *nc = SP_NODE_CONTEXT(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 =
-        selection->connectChanged(sigc::bind(sigc::ptr_fun(&sp_node_context_selection_changed), (gpointer)nc));
-
-    SPItem *item = selection->singleItem();
-
-    ec->shape_editor = new ShapeEditor(ec->desktop);
-
-    nc->rb_escaped = false;
-
-    nc->cursor_drag = false;
-
-    nc->added_node = false;
-
-    nc->current_state = SP_NODE_CONTEXT_INACTIVE;
-
-    if (item) {
-        ec->shape_editor->set_item(item, SH_NODEPATH);
-        ec->shape_editor->set_item(item, SH_KNOTHOLDER);
-    }
-
-    Inkscape::Preferences *prefs = Inkscape::Preferences::get();
-    if (prefs->getBool("/tools/nodes/selcue")) {
-        ec->enableSelectionCue();
-    }
-    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());
-
-    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);
-        }
-    }
-}
-
-/**
-\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)
-{
-    SPEventContext *ec = SP_EVENT_CONTEXT(data);
-
-    // 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
-sp_node_context_show_modifier_tip(SPEventContext *event_context, GdkEvent *event)
-{
-    sp_event_show_modifier_tip
-        (event_context->defaultMessageContext(), event,
-         _("<b>Ctrl</b>: toggle node type, snap handle angle, move hor/vert; <b>Ctrl+Alt</b>: move along handles"),
-         _("<b>Shift</b>: toggle node selection, disable snapping, rotate both handles"),
-         _("<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);
-
-    return ret;
-}
-
-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->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 && !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;
-                se->cancel_hit();
-
-                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);
-                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 && !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;
-
-                // 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 (!se->has_nodepath() || selection->singleItem() == NULL) {
-                    break;
-                }
-
-                bool over_stroke = 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;
-                    event_context->hot_x = 1;
-                    event_context->hot_y = 1;
-                    sp_event_context_update_cursor(event_context);
-                    nc->cursor_drag = false;
-                } else if (!nc->cursor_drag && over_stroke) {
-                    event_context->cursor_shape = cursor_node_d_xpm;
-                    event_context->hot_x = 1;
-                    event_context->hot_y = 1;
-                    sp_event_context_update_cursor(event_context);
-                    nc->cursor_drag = true;
-                }
-            }
-            break;
-
-        case GDK_2BUTTON_PRESS:
-        case GDK_BUTTON_RELEASE:
-            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;
-
-                bool over_stroke = 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) {
-                                    //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;
-                    }
-                    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;
-                }
-            }
-            break;
-        case GDK_KEY_PRESS:
-            switch (get_group0_keyval(&event->key)) {
-                case GDK_Insert:
-                case GDK_KP_Insert:
-                    // with any modifiers
-                    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) {
-                        se->delete_nodes();
-                    } else {
-                        se->delete_nodes_preserving_shape();
-                    }
-                    ret = TRUE;
-                    break;
-                case GDK_C:
-                case GDK_c:
-                    if (MOD__SHIFT_ONLY) {
-                        se->set_node_type(Inkscape::NodePath::NODE_CUSP);
-                        ret = TRUE;
-                    }
-                    break;
-                case GDK_S:
-                case GDK_s:
-                    if (MOD__SHIFT_ONLY) {
-                        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) {
-                        se->set_node_type(Inkscape::NodePath::NODE_SYMM);
-                        ret = TRUE;
-                    }
-                    break;
-                case GDK_B:
-                case GDK_b:
-                    if (MOD__SHIFT_ONLY) {
-                        se->break_at_nodes();
-                        ret = TRUE;
-                    }
-                    break;
-                case GDK_J:
-                case GDK_j:
-                    if (MOD__SHIFT_ONLY) {
-                        se->join_nodes();
-                        ret = TRUE;
-                    }
-                    break;
-                case GDK_D:
-                case GDK_d:
-                    if (MOD__SHIFT_ONLY) {
-                        se->duplicate_nodes();
-                        ret = TRUE;
-                    }
-                    break;
-                case GDK_L:
-                case GDK_l:
-                    if (MOD__SHIFT_ONLY) {
-                        se->set_type_of_segments(NR_LINETO);
-                        ret = TRUE;
-                    }
-                    break;
-                case GDK_U:
-                case GDK_u:
-                    if (MOD__SHIFT_ONLY) {
-                        se->set_type_of_segments(NR_CURVETO);
-                        ret = TRUE;
-                    }
-                    break;
-                case GDK_R:
-                case GDK_r:
-                    if (MOD__SHIFT_ONLY) {
-                        // FIXME: add top panel button
-                        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;
-                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) 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) se->move_nodes(mul*-10*nudge, 0); // shift
-                            else se->move_nodes(mul*-nudge, 0); // no shift
-                        }
-                        ret = TRUE;
-                    }
-                    break;
-                case GDK_Up: // move selection up
-                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) 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) se->move_nodes(0, mul*10*nudge); // shift
-                            else se->move_nodes(0, mul*nudge); // no shift
-                        }
-                        ret = TRUE;
-                    }
-                    break;
-                case GDK_Right: // move selection right
-                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) 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) se->move_nodes(mul*10*nudge, 0); // shift
-                            else se->move_nodes(mul*nudge, 0); // no shift
-                        }
-                        ret = TRUE;
-                    }
-                    break;
-                case GDK_Down: // move selection down
-                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) 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) se->move_nodes(0, mul*-10*nudge); // shift
-                            else se->move_nodes(0, mul*-nudge); // no shift
-                        }
-                        ret = TRUE;
-                    }
-                    break;
-                case GDK_Escape:
-                {
-                    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 (se->has_selection()) {
-                            se->deselect();
-                        } else {
-                            sp_desktop_selection(desktop)->clear();
-                        }
-                    }
-                    ret = TRUE;
-                    break;
-                }
-
-                case GDK_bracketleft:
-                    if ( MOD__CTRL && !MOD__ALT && ( snaps != 0 ) ) {
-                        if (nc->leftctrl)
-                            se->rotate_nodes (M_PI/snaps, -1, false);
-                        if (nc->rightctrl)
-                            se->rotate_nodes (M_PI/snaps, 1, false);
-                    } else if ( MOD__ALT && !MOD__CTRL ) {
-                        if (nc->leftalt && nc->rightalt)
-                            se->rotate_nodes (1, 0, true);
-                        else {
-                            if (nc->leftalt)
-                                se->rotate_nodes (1, -1, true);
-                            if (nc->rightalt)
-                                se->rotate_nodes (1, 1, true);
-                        }
-                    } else if ( snaps != 0 ) {
-                        se->rotate_nodes (M_PI/snaps, 0, false);
-                    }
-                    ret = TRUE;
-                    break;
-                case GDK_bracketright:
-                    if ( MOD__CTRL && !MOD__ALT && ( snaps != 0 ) ) {
-                        if (nc->leftctrl)
-                            se->rotate_nodes (-M_PI/snaps, -1, false);
-                        if (nc->rightctrl)
-                            se->rotate_nodes (-M_PI/snaps, 1, false);
-                    } else if ( MOD__ALT && !MOD__CTRL ) {
-                        if (nc->leftalt && nc->rightalt)
-                            se->rotate_nodes (-1, 0, true);
-                        else {
-                            if (nc->leftalt)
-                                se->rotate_nodes (-1, -1, true);
-                            if (nc->rightalt)
-                                se->rotate_nodes (-1, 1, true);
-                        }
-                    } else if ( snaps != 0 ) {
-                        se->rotate_nodes (-M_PI/snaps, 0, false);
-                    }
-                    ret = TRUE;
-                    break;
-                case GDK_less:
-                case GDK_comma:
-                    if (MOD__CTRL) {
-                        if (nc->leftctrl)
-                            se->scale_nodes(-offset, -1);
-                        if (nc->rightctrl)
-                            se->scale_nodes(-offset, 1);
-                    } else if (MOD__ALT) {
-                        if (nc->leftalt && nc->rightalt)
-                            se->scale_nodes_screen (-1, 0);
-                        else {
-                            if (nc->leftalt)
-                                se->scale_nodes_screen (-1, -1);
-                            if (nc->rightalt)
-                                se->scale_nodes_screen (-1, 1);
-                        }
-                    } else {
-                        se->scale_nodes (-offset, 0);
-                    }
-                    ret = TRUE;
-                    break;
-                case GDK_greater:
-                case GDK_period:
-                    if (MOD__CTRL) {
-                        if (nc->leftctrl)
-                            se->scale_nodes (offset, -1);
-                        if (nc->rightctrl)
-                            se->scale_nodes (offset, 1);
-                    } else if (MOD__ALT) {
-                        if (nc->leftalt && nc->rightalt)
-                            se->scale_nodes_screen (1, 0);
-                        else {
-                            if (nc->leftalt)
-                                se->scale_nodes_screen (1, -1);
-                            if (nc->rightalt)
-                                se->scale_nodes_screen (1, 1);
-                        }
-                    } else {
-                        se->scale_nodes (offset, 0);
-                    }
-                    ret = TRUE;
-                    break;
-
-                case GDK_Alt_L:
-                    nc->leftalt = TRUE;
-                    sp_node_context_show_modifier_tip(event_context, event);
-                    break;
-                case GDK_Alt_R:
-                    nc->rightalt = TRUE;
-                    sp_node_context_show_modifier_tip(event_context, event);
-                    break;
-                case GDK_Control_L:
-                    nc->leftctrl = TRUE;
-                    sp_node_context_show_modifier_tip(event_context, event);
-                    break;
-                case GDK_Control_R:
-                    nc->rightctrl = TRUE;
-                    sp_node_context_show_modifier_tip(event_context, event);
-                    break;
-                case GDK_Shift_L:
-                case GDK_Shift_R:
-                case GDK_Meta_L:
-                case GDK_Meta_R:
-                    sp_node_context_show_modifier_tip(event_context, event);
-                    break;
-                default:
-                    ret = node_key(event);
-                    break;
-            }
-            break;
-        case GDK_KEY_RELEASE:
-            switch (get_group0_keyval(&event->key)) {
-                case GDK_Alt_L:
-                    nc->leftalt = FALSE;
-                    event_context->defaultMessageContext()->clear();
-                    break;
-                case GDK_Alt_R:
-                    nc->rightalt = FALSE;
-                    event_context->defaultMessageContext()->clear();
-                    break;
-                case GDK_Control_L:
-                    nc->leftctrl = FALSE;
-                    event_context->defaultMessageContext()->clear();
-                    break;
-                case GDK_Control_R:
-                    nc->rightctrl = FALSE;
-                    event_context->defaultMessageContext()->clear();
-                    break;
-                case GDK_Shift_L:
-                case GDK_Shift_R:
-                case GDK_Meta_L:
-                case GDK_Meta_R:
-                    event_context->defaultMessageContext()->clear();
-                    break;
-            }
-            break;
-        default:
-            break;
-    }
-
-    if (!ret) {
-        if (((SPEventContextClass *) parent_class)->root_handler)
-            ret = ((SPEventContextClass *) parent_class)->root_handler(event_context, event);
-    }
-
-    return ret;
-}
-
-
-/*
-  Local Variables:
-  mode:c++
-  c-file-style:"stroustrup"
-  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
-  indent-tabs-mode:nil
-  fill-column:99
-  End:
-*/
-// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
diff --git a/src/node-context.h b/src/node-context.h
deleted file mode 100644 (file)
index 2345ffc..0000000
+++ /dev/null
@@ -1,87 +0,0 @@
-#ifndef __SP_NODE_CONTEXT_H__
-#define __SP_NODE_CONTEXT_H__
-
-/*
- * Node editing context
- *
- * Authors:
- *   Lauris Kaplinski <lauris@kaplinski.com>
- *   bulia byak <buliabyak@users.sf.net>
- *
- * This code is in public domain
- */
-
-#include <gtk/gtktypeutils.h>
-#include <sigc++/sigc++.h>
-#include "event-context.h"
-#include "forward.h"
-#include "display/display-forward.h"
-#include "nodepath.h"
-namespace Inkscape { class Selection; }
-
-#define SP_TYPE_NODE_CONTEXT            (sp_node_context_get_type ())
-#define SP_NODE_CONTEXT(obj)            (GTK_CHECK_CAST ((obj), SP_TYPE_NODE_CONTEXT, SPNodeContext))
-#define SP_NODE_CONTEXT_CLASS(klass)    (GTK_CHECK_CLASS_CAST ((klass), SP_TYPE_NODE_CONTEXT, SPNodeContextClass))
-#define SP_IS_NODE_CONTEXT(obj)         (GTK_CHECK_TYPE ((obj), SP_TYPE_NODE_CONTEXT))
-#define SP_IS_NODE_CONTEXT_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), SP_TYPE_NODE_CONTEXT))
-
-enum { SP_NODE_CONTEXT_INACTIVE,
-       SP_NODE_CONTEXT_NODE_DRAGGING,
-       SP_NODE_CONTEXT_RUBBERBAND_DRAGGING };
-
-class SPNodeContext;
-class SPNodeContextClass;
-
-struct SPNodeContext {
-        // FIXME: shouldn't this be a pointer???
-       SPEventContext event_context;
-
-       guint drag : 1;
-
-       gboolean leftalt;
-       gboolean rightalt;
-       gboolean leftctrl;
-       gboolean rightctrl;
-
-      /// If true, rubberband was cancelled by esc, so the next button release should not deselect.
-       bool rb_escaped;
-
-       sigc::connection sel_changed_connection;
-
-       Inkscape::MessageContext *_node_message_context;
-
-       bool cursor_drag;
-
-    bool added_node;
-  
-    unsigned int current_state;
-    SPItem * flashed_item;
-       SPCanvasItem *grabbed;
-    Inkscape::Display::TemporaryItem * flash_tempitem;
-    int remove_flash_counter;
-};
-
-struct SPNodeContextClass {
-       SPEventContextClass parent_class;
-};
-
-/* Standard Gtk function */
-
-GtkType sp_node_context_get_type (void);
-
-void sp_node_context_selection_changed (Inkscape::Selection * selection, gpointer data);
-void sp_node_context_selection_modified (Inkscape::Selection * selection, guint flags, gpointer data);
-
-#endif
-
-/*
-  Local Variables:
-  mode:c++
-  c-file-style:"stroustrup"
-  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
-  indent-tabs-mode:nil
-  fill-column:99
-  End:
-*/
-// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
index 545607889c48e13f21da41c2e05150b2028be135..558f61814b169b7fae26a9656bc9cea6366c78d3 100644 (file)
@@ -30,6 +30,7 @@
 
 #include "inkscape.h"
 #include "desktop.h"
+#include "selection.h"
 #include "sp-guide.h"
 #include "preferences.h"
 #include "event-context.h"
@@ -206,29 +207,15 @@ Inkscape::SnappedPoint SnapManager::freeSnap(Inkscape::SnapPreferences::PointTyp
                                              bool first_point,
                                              Geom::OptRect const &bbox_to_snap) const
 {
-       if (!someSnapperMightSnap()) {
+    if (!someSnapperMightSnap()) {
         return Inkscape::SnappedPoint(p, source_type, Inkscape::SNAPTARGET_UNDEFINED, NR_HUGE, 0, false, false);
     }
 
-    std::vector<SPItem const *> *items_to_ignore;
-    if (_item_to_ignore) { // If we have only a single item to ignore
-        // then build a list containing this single item;
-        // This single-item list will prevail over any other _items_to_ignore list, should that exist
-        items_to_ignore = new std::vector<SPItem const *>;
-        items_to_ignore->push_back(_item_to_ignore);
-    } else {
-        items_to_ignore = _items_to_ignore;
-    }
-
     SnappedConstraints sc;
     SnapperList const snappers = getSnappers();
 
     for (SnapperList::const_iterator i = snappers.begin(); i != snappers.end(); i++) {
-        (*i)->freeSnap(sc, point_type, p, source_type, first_point, bbox_to_snap, items_to_ignore, _unselected_nodes);
-    }
-
-    if (_item_to_ignore) {
-        delete items_to_ignore;
+        (*i)->freeSnap(sc, point_type, p, source_type, first_point, bbox_to_snap, &_items_to_ignore, _unselected_nodes);
     }
 
     return findBestSnap(p, source_type, sc, false);
@@ -368,17 +355,6 @@ Inkscape::SnappedPoint SnapManager::constrainedSnap(Inkscape::SnapPreferences::P
         return Inkscape::SnappedPoint(p, source_type, Inkscape::SNAPTARGET_UNDEFINED, NR_HUGE, 0, false, false);
     }
 
-    std::vector<SPItem const *> *items_to_ignore;
-    if (_item_to_ignore) { // If we have only a single item to ignore
-        // then build a list containing this single item;
-        // This single-item list will prevail over any other _items_to_ignore list, should that exist
-        items_to_ignore = new std::vector<SPItem const *>;
-        items_to_ignore->push_back(_item_to_ignore);
-    } else {
-        items_to_ignore = _items_to_ignore;
-    }
-
-
     // First project the mouse pointer onto the constraint
     Geom::Point pp = constraint.projection(p);
     // Then try to snap the projected point
@@ -386,11 +362,7 @@ Inkscape::SnappedPoint SnapManager::constrainedSnap(Inkscape::SnapPreferences::P
     SnappedConstraints sc;
     SnapperList const snappers = getSnappers();
     for (SnapperList::const_iterator i = snappers.begin(); i != snappers.end(); i++) {
-        (*i)->constrainedSnap(sc, point_type, pp, source_type, first_point, bbox_to_snap, constraint, items_to_ignore);
-    }
-
-    if (_item_to_ignore) {
-        delete items_to_ignore;
+        (*i)->constrainedSnap(sc, point_type, pp, source_type, first_point, bbox_to_snap, constraint, &_items_to_ignore);
     }
 
     return findBestSnap(pp, source_type, sc, true);
@@ -1033,22 +1005,7 @@ Inkscape::SnappedPoint SnapManager::findBestSnap(Geom::Point const &p,
     return bestSnappedPoint;
 }
 
-/**
- * \brief Prepare the snap manager for the actual snapping, which includes building a list of snap targets
- * to ignore and toggling the snap indicator
- *
- * There are two overloaded setup() methods, of which this one only allows for a single item to be ignored
- * whereas the other one will take a list of items to ignore
- *
- * \param desktop Reference to the desktop to which this snap manager is attached
- * \param snapindicator If true then a snap indicator will be displayed automatically (when enabled in the preferences)
- * \param item_to_ignore This item will not be snapped to, e.g. the item that is currently being dragged. This avoids "self-snapping"
- * \param unselected_nodes Stationary nodes of the path that is currently being edited in the node tool and
- * that can be snapped too. Nodes not in this list will not be snapped to, to avoid "self-snapping". Of each
- * unselected node both the position (Geom::Point) and the type (Inkscape::SnapTargetType) will be stored
- * \param guide_to_ignore Guide that is currently being dragged and should not be snapped to
- */
-
+/// Convenience shortcut when there is only one item to ignore
 void SnapManager::setup(SPDesktop const *desktop,
                         bool snapindicator,
                         SPItem const *item_to_ignore,
@@ -1056,8 +1013,8 @@ void SnapManager::setup(SPDesktop const *desktop,
                         SPGuide *guide_to_ignore)
 {
     g_assert(desktop != NULL);
-    _item_to_ignore = item_to_ignore;
-    _items_to_ignore = NULL;
+    _items_to_ignore.clear();
+    _items_to_ignore.push_back(item_to_ignore);
     _desktop = desktop;
     _snapindicator = snapindicator;
     _unselected_nodes = unselected_nodes;
@@ -1082,17 +1039,35 @@ void SnapManager::setup(SPDesktop const *desktop,
 
 void SnapManager::setup(SPDesktop const *desktop,
                         bool snapindicator,
-                        std::vector<SPItem const *> &items_to_ignore,
+                        std::vector<SPItem const *> const &items_to_ignore,
                         std::vector<std::pair<Geom::Point, int> > *unselected_nodes,
                         SPGuide *guide_to_ignore)
 {
     g_assert(desktop != NULL);
-    _item_to_ignore = NULL;
-    _items_to_ignore = &items_to_ignore;
+    _items_to_ignore = items_to_ignore;
+    _desktop = desktop;
+    _snapindicator = snapindicator;
+    _unselected_nodes = unselected_nodes;
+    _guide_to_ignore = guide_to_ignore;
+}
+
+/// Setup, taking the list of items to ignore from the desktop's selection.
+void SnapManager::setupIgnoreSelection(SPDesktop const *desktop,
+                                      bool snapindicator,
+                                      std::vector<std::pair<Geom::Point, int> > *unselected_nodes,
+                                      SPGuide *guide_to_ignore)
+{
     _desktop = desktop;
     _snapindicator = snapindicator;
     _unselected_nodes = unselected_nodes;
     _guide_to_ignore = guide_to_ignore;
+    _items_to_ignore.clear();
+
+    Inkscape::Selection *sel = _desktop->selection;
+    GSList const *items = sel->itemList();
+    for (GSList *i = const_cast<GSList*>(items); i; i = i->next) {
+        _items_to_ignore.push_back(static_cast<SPItem const *>(i->data));
+    }
 }
 
 SPDocument *SnapManager::getDocument() const
index e621bdb605334cb1b1e0fa4619368d2c09ca69f4..5696dcd536a3fe3cac2441c04bcd92ec42174c11 100644 (file)
@@ -1,17 +1,7 @@
-#ifndef SEEN_SNAP_H
-#define SEEN_SNAP_H
-
 /**
  * \file snap.h
- * \brief SnapManager class.
- *
- * The SnapManager class handles most (if not all) of the interfacing of the snapping mechanisms with the
- * other parts of the code base. It stores the references to the various types of snappers for grid, guides
- * and objects, and it stores most of the snapping preferences. Besides that it provides methods to setup
- * the snapping environment (e.g. keeps a list of the items to ignore when looking for snap target candidates,
- * and toggling of the snap indicator), and it provides many different methods for the snapping itself (free
- * snapping vs. constrained snapping, returning the result by reference or through a return statement, etc.)
- *
+ * \brief Per-desktop object that handles snapping queries
+ *//*
  * Authors:
  *   Lauris Kaplinski <lauris@kaplinski.com>
  *   Frank Felfe <innerspace@iname.com>
  * Released under GNU GPL, read the file 'COPYING' for more information
  */
 
-#include <vector>
+#ifndef SEEN_SNAP_H
+#define SEEN_SNAP_H
 
+#include <vector>
 #include "guide-snapper.h"
 #include "object-snapper.h"
 #include "snap-preferences.h"
@@ -42,11 +34,33 @@ enum SPGuideDragType { // used both here and in desktop-events.cpp
 class SPNamedView;
 
 /// Class to coordinate snapping operations
-
 /**
- *  Each SPNamedView has one of these.  It offers methods to snap points to whatever
- *  snappers are defined (e.g. grid, guides etc.).  It also allows callers to snap
- *  points which have undergone some transformation (e.g. translation, scaling etc.)
+ * The SnapManager class handles most (if not all) of the interfacing of the snapping mechanisms
+ * with the other parts of the code base. It stores the references to the various types of snappers
+ * for grid, guides and objects, and it stores most of the snapping preferences. Besides that
+ * it provides methods to setup the snapping environment (e.g. keeps a list of the items to ignore
+ * when looking for snap target candidates, and toggling of the snap indicator), and it provides
+ * many different methods for snapping queries (free snapping vs. constrained snapping,
+ * returning the result by reference or through a return statement, etc.)
+ * 
+ * Each SPNamedView has one of these.  It offers methods to snap points to whatever
+ * snappers are defined (e.g. grid, guides etc.).  It also allows callers to snap
+ * points which have undergone some transformation (e.g. translation, scaling etc.)
+ *
+ * \par How snapping is implemented in Inkscape
+ * \par
+ * The snapping system consists of two key elements. The first one is the snap manager
+ * (this class), which keeps some data about objects in the document and answers queries
+ * of the type "given this point and type of transformation, what is the best place
+ * to snap to?".
+ * 
+ * The second is in event-context.cpp and implements the snapping timeout. Whenever a motion
+ * events happens over the canvas, it stores it for later use and initiates a timeout.
+ * This timeout is discarded whenever a new motion event occurs. When the timeout expires,
+ * a global flag in SnapManager, accessed via getSnapPostponedGlobally(), is set to true
+ * and the stored event is replayed, but this time with snapping enabled. This way you can
+ * write snapping code directly in your control point's dragged handler as if there was
+ * no timeout.
  */
 
 class SnapManager
@@ -74,9 +88,13 @@ public:
 
     void setup(SPDesktop const *desktop,
                bool snapindicator,
-               std::vector<SPItem const *> &items_to_ignore,
+               std::vector<SPItem const *> const &items_to_ignore,
                std::vector<std::pair<Geom::Point, int> > *unselected_nodes = NULL,
                SPGuide *guide_to_ignore = NULL);
+    void setupIgnoreSelection(SPDesktop const *desktop,
+                              bool snapindicator = true,
+                              std::vector<std::pair<Geom::Point, int> > *unselected_nodes = NULL,
+                              SPGuide *guide_to_ignore = NULL);
 
     // freeSnapReturnByRef() is preferred over freeSnap(), because it only returns a
     // point if snapping has occurred (by overwriting p); otherwise p is untouched
@@ -171,8 +189,7 @@ protected:
     SPNamedView const *_named_view;
 
 private:
-    std::vector<SPItem const *> *_items_to_ignore; ///< Items that should not be snapped to, for example the items that are currently being dragged. Set using the setup() method
-    SPItem const *_item_to_ignore; ///< Single item that should not be snapped to. If not NULL then this takes precedence over _items_to_ignore. Set using the setup() method
+    std::vector<SPItem const *> _items_to_ignore; ///< Items that should not be snapped to, for example the items that are currently being dragged. Set using the setup() method
     SPGuide *_guide_to_ignore; ///< A guide that should not be snapped to, e.g. the guide that is currently being dragged
     SPDesktop const *_desktop;
     bool _snapindicator; ///< When true, an indicator will be drawn at the position that was being snapped to
index 110b3d36ad5292fa721e1db7e55b76af01c5ce19..1801f309c807790fc701a77f55925080ff90489f 100644 (file)
@@ -71,6 +71,7 @@ public:
     public:
         ConstraintLine(Geom::Point const &d) : _has_point(false), _direction(d) {}
         ConstraintLine(Geom::Point const &p, Geom::Point const &d) : _has_point(true), _point(p), _direction(d) {}
+        ConstraintLine(Geom::Line const &l) : _has_point(true), _point(l.origin()), _direction(l.versor()) {}
 
         bool hasPoint() const {
             return _has_point;
index d10045c623598dffffaf1ae545837c2aeed5fc0f..5a84592b69113dbf0a47ea055fa206de877996dc 100644 (file)
@@ -140,7 +140,60 @@ void ControlPointSelection::clear()
         erase(i++);
 }
 
-/** Transform all selected control points by the supplied affine transformation. */
+/** Select all points that this selection can contain. */
+void ControlPointSelection::selectAll()
+{
+    for (set_type::iterator i = _all_points.begin(); i != _all_points.end(); ++i) {
+        insert(*i);
+    }
+}
+/** Select all points inside the given rectangle (in desktop coordinates). */
+void ControlPointSelection::selectArea(Geom::Rect const &r)
+{
+    for (set_type::iterator i = _all_points.begin(); i != _all_points.end(); ++i) {
+        if (r.contains(**i))
+            insert(*i);
+    }
+}
+/** Unselect all selected points and select all unselected points. */
+void ControlPointSelection::invertSelection()
+{
+    for (set_type::iterator i = _all_points.begin(); i != _all_points.end(); ++i) {
+        if ((*i)->selected()) erase(*i);
+        else insert(*i);
+    }
+}
+void ControlPointSelection::spatialGrow(SelectableControlPoint *origin, int dir)
+{
+    bool grow = (dir > 0);
+    Geom::Point p = origin->position();
+    double best_dist = grow ? HUGE_VAL : 0;
+    SelectableControlPoint *match = NULL;
+    for (set_type::iterator i = _all_points.begin(); i != _all_points.end(); ++i) {
+        bool selected = (*i)->selected();
+        if (grow && !selected) {
+            double dist = Geom::distance((*i)->position(), p);
+            if (dist < best_dist) {
+                best_dist = dist;
+                match = *i;
+            }
+        }
+        if (!grow && selected) {
+            double dist = Geom::distance((*i)->position(), p);
+            // use >= to also deselect the origin node when it's the last one selected
+            if (dist >= best_dist) {
+                best_dist = dist;
+                match = *i;
+            }
+        }
+    }
+    if (match) {
+        if (grow) insert(match);
+        else erase(match);
+    }
+}
+
+/** Transform all selected control points by the given affine transformation. */
 void ControlPointSelection::transform(Geom::Matrix const &m)
 {
     for (iterator i = _points.begin(); i != _points.end(); ++i) {
index 0f0daffaa4cdb0143d8e546d03c80a9c86cd7209..38df5c7e5f76802cdda10fe75173c7a34f74c1a2 100644 (file)
@@ -14,6 +14,7 @@
 
 #include <memory>
 #include <tr1/unordered_map>
+#include <tr1/unordered_set>
 #include <boost/shared_ptr.hpp>
 #include <boost/weak_ptr.hpp>
 #include <boost/optional.hpp>
@@ -43,12 +44,12 @@ public:
     typedef std::list<sigc::connection> connlist_type;
     typedef std::unordered_map< SelectableControlPoint *,
         boost::shared_ptr<connlist_type> > map_type;
+    typedef std::unordered_set< SelectableControlPoint * > set_type;
+    typedef set_type Set; // convenience alias
 
-    // boilerplate typedefs
     typedef map_type::iterator iterator;
     typedef map_type::const_iterator const_iterator;
     typedef map_type::size_type size_type;
-
     typedef SelectableControlPoint *value_type;
     typedef SelectableControlPoint *key_type;
 
@@ -80,6 +81,15 @@ public:
     // find
     iterator find(const key_type &k) { return _points.find(k); }
 
+    // Sometimes it is very useful to keep a list of all selectable points.
+    set_type const &allPoints() const { return _all_points; }
+    set_type &allPoints() { return _all_points; }
+    // ...for example in these methods. Another useful case is snapping.
+    void selectAll();
+    void selectArea(Geom::Rect const &);
+    void invertSelection();
+    void spatialGrow(SelectableControlPoint *origin, int dir);
+
     virtual bool event(GdkEvent *);
 
     void transform(Geom::Matrix const &m);
@@ -113,6 +123,7 @@ private:
     void _keyboardTransform(Geom::Matrix const &);
     void _commitTransform(CommitEvent ce);
     map_type _points;
+    set_type _all_points;
     boost::optional<double> _rot_radius;
     TransformHandleSet *_handles;
     SelectableControlPoint *_grabbed_point;
index 74dd6e31c52acb063def3b1213aead0a7189a906..0d076a5ab2ce3fcc0bd6621128a0a71e6590c77d 100644 (file)
 #include <gdkmm.h>
 #include <gtkmm.h>
 #include <2geom/point.h>
-#include "ui/tool/control-point.h"
-#include "ui/tool/event-utils.h"
-#include "preferences.h"
 #include "desktop.h"
 #include "desktop-handles.h"
+#include "display/snap-indicator.h"
 #include "event-context.h"
 #include "message-context.h"
+#include "preferences.h"
+#include "ui/tool/control-point.h"
+#include "ui/tool/event-utils.h"
 
 namespace Inkscape {
 namespace UI {
@@ -397,6 +398,7 @@ bool ControlPoint::_eventHandler(GdkEvent *event)
         
     case GDK_MOTION_NOTIFY:
         if (held_button<1>(event->motion) && !_desktop->event_context->space_panning) {
+            _desktop->snapindicator->remove_snaptarget(); 
             bool transferred = false;
             if (!_drag_initiated) {
                 bool t = fabs(event->motion.x - _drag_event_origin[Geom::X]) <= drag_tolerance &&
@@ -414,48 +416,57 @@ bool ControlPoint::_eventHandler(GdkEvent *event)
                     _drag_initiated = true;
                 }
             }
-            if (transferred) return true;
-            // the point was moved beyond the drag tolerance
-            Geom::Point new_pos = _desktop->w2d(event_point(event->motion)) + pointer_offset;
-            
-            // the new position is passed by reference and can be changed in the handlers.
-            signal_dragged.emit(_position, new_pos, &event->motion);
-            move(new_pos);
-            _updateDragTip(&event->motion); // update dragging tip after moving to new position
-            
-            _desktop->scroll_to_point(new_pos);
-            _desktop->set_coordinate_status(_position);
+            if (!transferred) {
+                // dragging in progress
+                Geom::Point new_pos = _desktop->w2d(event_point(event->motion)) + pointer_offset;
+                
+                // the new position is passed by reference and can be changed in the handlers.
+                signal_dragged.emit(_position, new_pos, &event->motion);
+                move(new_pos);
+                _updateDragTip(&event->motion); // update dragging tip after moving to new position
+                
+                _desktop->scroll_to_point(new_pos);
+                _desktop->set_coordinate_status(_position);
+                sp_event_context_snap_delay_handler(_desktop->event_context, NULL,
+                    reinterpret_cast<SPKnot*>(this), &event->motion,
+                    DelayedSnapEvent::CONTROL_POINT_HANDLER);
+            }
             return true;
         }
         break;
         
     case GDK_BUTTON_RELEASE:
-        if (_event_grab) {
-            sp_canvas_item_ungrab(_canvas_item, event->button.time);
-            _setMouseover(this, event->button.state);
-            _event_grab = false;
+        if (!_event_grab) break;
 
-            if (_drag_initiated) {
-                sp_canvas_end_forced_full_redraws(_desktop->canvas);
-            }
+        // TODO I think this "feature" is wrong.
+        // sp_event_context_snap_watchdog_callback(_desktop->event_context->_delayed_snap_event);
+        sp_event_context_discard_delayed_snap_event(_desktop->event_context);
+        _desktop->snapindicator->remove_snaptarget();
+
+        sp_canvas_item_ungrab(_canvas_item, event->button.time);
+        _setMouseover(this, event->button.state);
+        _event_grab = false;
 
-            if (event->button.button == next_release_doubleclick) {
+        if (_drag_initiated) {
+            sp_canvas_end_forced_full_redraws(_desktop->canvas);
+        }
+
+        if (event->button.button == next_release_doubleclick) {
+            _drag_initiated = false;
+            return signal_doubleclicked.emit(&event->button);
+        }
+        if (event->button.button == 1) {
+            if (_drag_initiated) {
+                // it is the end of a drag
+                signal_ungrabbed.emit(&event->button);
                 _drag_initiated = false;
-                return signal_doubleclicked.emit(&event->button);
+                return true;
+            } else {
+                // it is the end of a click
+                return signal_clicked.emit(&event->button);
             }
-            if (event->button.button == 1) {
-                if (_drag_initiated) {
-                    // it is the end of a drag
-                    signal_ungrabbed.emit(&event->button);
-                    _drag_initiated = false;
-                    return true;
-                } else {
-                    // it is the end of a click
-                    return signal_clicked.emit(&event->button);
-                }
-            }
-            _drag_initiated = false;
         }
+        _drag_initiated = false;
         break;
 
     case GDK_ENTER_NOTIFY:
index c4b0a42be18ff54bc43000a50636c36e93c4e2ee..4997c5ef4d07a797cef09d377e93818bfbc094fa 100644 (file)
@@ -90,6 +90,9 @@ public:
     static sigc::signal<void, ControlPoint*> signal_mouseover_change;
     static Glib::ustring format_tip(char const *format, ...) G_GNUC_PRINTF(1,2);
 
+    // temporarily public, until snapping is refactored a little
+    virtual bool _eventHandler(GdkEvent *event);
+
 protected:
     ControlPoint(SPDesktop *d, Geom::Point const &initial_pos, Gtk::AnchorType anchor,
         SPCtrlShapeType shape, unsigned int size, ColorSet *cset = 0, SPCanvasGroup *group = 0);
@@ -112,14 +115,13 @@ protected:
     void _setPixbuf(Glib::RefPtr<Gdk::Pixbuf>);
     /// @}
 
-    virtual bool _eventHandler(GdkEvent *event);
     virtual Glib::ustring _getTip(unsigned state) { return ""; }
     virtual Glib::ustring _getDragTip(GdkEventMotion *event) { return ""; }
     virtual bool _hasDragTips() { return false; }
 
     SPDesktop *const _desktop; ///< The desktop this control point resides on.
     SPCanvasItem * _canvas_item; ///< Visual representation of the control point.
-    ColorSet *_cset; ///< Describes the colors used to represent the point
+    ColorSet *_cset; ///< Colors used to represent the point
     State _state;
 
     static int const _grab_event_mask;
index ac0165e1a1a62882dc12059f6eed8e610def6e7d..33d96c7064967149b11b69ccbb7d3159e10832b4 100644 (file)
@@ -181,71 +181,21 @@ void MultiPathManipulator::setItems(std::set<ShapeRecord> const &s)
 void MultiPathManipulator::selectSubpaths()
 {
     if (_selection.empty()) {
-        invokeForAll(&PathManipulator::selectAll);
+        _selection.selectAll();
     } else {
         invokeForAll(&PathManipulator::selectSubpaths);
     }
 }
-void MultiPathManipulator::selectAll()
-{
-    invokeForAll(&PathManipulator::selectAll);
-}
-
-void MultiPathManipulator::selectArea(Geom::Rect const &area, bool take)
-{
-    if (take) _selection.clear();
-    invokeForAll(&PathManipulator::selectArea, area);
-}
 
 void MultiPathManipulator::shiftSelection(int dir)
 {
     invokeForAll(&PathManipulator::shiftSelection, dir);
 }
-void MultiPathManipulator::spatialGrow(NodeList::iterator origin, int dir)
-{
-    double extr_dist = dir > 0 ? HUGE_VAL : -HUGE_VAL;
-    NodeList::iterator target;
 
-    do { // this substitutes for goto
-        if ((dir > 0 && !origin->selected())) {
-            target = origin;
-            break;
-        }
-
-        bool closest = dir > 0; // when growing, find closest node
-        bool selected = dir < 0; // when growing, consider only unselected nodes
-
-        for (MapType::iterator i = _mmap.begin(); i != _mmap.end(); ++i) {
-            NodeList::iterator t = i->second->extremeNode(origin, selected, !selected, closest);
-            if (!t) continue;
-            double dist = Geom::distance(*t, *origin);
-            bool cond = closest ? (dist < extr_dist) : (dist > extr_dist);
-            if (cond) {
-                extr_dist = dist;
-                target = t;
-            }
-        }
-    } while (0);
-
-    if (!target) return;
-    if (dir > 0) {
-        _selection.insert(target.ptr());
-    } else {
-        _selection.erase(target.ptr());
-    }
-}
-void MultiPathManipulator::invertSelection()
-{
-    invokeForAll(&PathManipulator::invertSelection);
-}
 void MultiPathManipulator::invertSelectionInSubpaths()
 {
     invokeForAll(&PathManipulator::invertSelectionInSubpaths);
 }
-void MultiPathManipulator::deselect()
-{
-    _selection.clear();
-}
 
 void MultiPathManipulator::setNodeType(NodeType type)
 {
index 4fbbf1b054415dd798f60c2616e843285b7e5c2a..46ad3a8d22f054b81863c9a90f9347c70340714d 100644 (file)
@@ -45,13 +45,8 @@ public:
     void cleanup();
 
     void selectSubpaths();
-    void selectAll();
-    void selectArea(Geom::Rect const &area, bool take);
     void shiftSelection(int dir);
-    void spatialGrow(NodeList::iterator center, int dir);
-    void invertSelection();
     void invertSelectionInSubpaths();
-    void deselect();
 
     void setNodeType(NodeType t);
     void setSegmentType(SegmentType t);
index 735ddf87ef61c97a8f2f4a3ca7d227ea7e59a59c..c1ba3394e53cdd0b1fc335cdd95dca8fcd0bcbe6 100644 (file)
  *   it might handle all shapes. Handles XML commit of actions that affect all paths or
  *   the node selection and removes PathManipulators that have no nodes left after e.g. node
  *   deletes.
- * - ControlPointSelection: keeps track of node selection. Performs actions that require no
+ * - ControlPointSelection: keeps track of node selection and a set of nodes that can potentially
+ *   be selected. There can be more than one selection. Performs actions that require no
  *   knowledge about the path, only about the nodes, like dragging and transforms. It is not
  *   specific to nodes and can accomodate any control point derived from SelectableControlPoint.
  *   Transforms nodes in response to transform handle events.
  * - TransformHandleSet: displays nodeset transform handles and emits transform events. The aim
  *   is to eventually use a common class for object and control point transforms.
+ * - SelectableControlPoint: base for any type of selectable point. It can belong to only one
+ *   selection.
  * 
  * @par Plans for the future
  * @par
@@ -384,9 +387,6 @@ void ink_node_tool_selection_changed(InkNodeTool *nt, Inkscape::Selection *sel)
 
     std::set<ShapeRecord> shapes;
 
-    // TODO this is ugly!!!
-    //typedef std::map<SPItem*, std::pair<Geom::Matrix, guint32> > TransMap;
-    //typedef std::map<SPPath*, std::pair<Geom::Matrix, guint32> > PathMap;
     GSList const *ilist = sel->itemList();
 
     for (GSList *i = const_cast<GSList*>(ilist); i; i = i->next) {
@@ -477,7 +477,7 @@ gint ink_node_tool_root_handler(SPEventContext *event_context, GdkEvent *event)
         case GDK_a:
             if (held_control(event->key)) {
                 if (held_alt(event->key)) {
-                    nt->_multipath->selectAll();
+                    nt->_selected_nodes->selectAll();
                 } else {
                     // select all nodes in subpaths that have something selected
                     // if nothing is selected, select everything
@@ -554,7 +554,7 @@ void ink_node_tool_select_area(InkNodeTool *nt, Geom::Rect const &sel, GdkEventB
         selection->setList(items);
         g_slist_free(items);
     } else {
-        nt->_multipath->selectArea(sel, !held_shift(*event));
+        nt->_selected_nodes->selectArea(sel);
     }
 }
 void ink_node_tool_select_point(InkNodeTool *nt, Geom::Point const &sel, GdkEventButton *event)
index 22d4ddc47f1ef2b6ed47cb8ba90eb0e0b2743f0b..adef8e5a7e93b3fc7f7f9489427ba9828e7caef7 100644 (file)
@@ -22,6 +22,8 @@
 #include "desktop.h"
 #include "desktop-handles.h"
 #include "preferences.h"
+#include "snap.h"
+#include "snap-preferences.h"
 #include "sp-metrics.h"
 #include "sp-namedview.h"
 #include "ui/tool/control-point-selection.h"
@@ -638,10 +640,8 @@ bool Node::_eventHandler(GdkEvent *event)
         } else if (event->scroll.direction == GDK_SCROLL_DOWN) {
             dir = -1;
         } else break;
-        origin = NodeList::get_iterator(this);
-
         if (held_control(event->scroll)) {
-            list()->_list._path_manipulator._multi_path_manipulator.spatialGrow(origin, dir);
+            _selection.spatialGrow(this, dir);
         } else {
             _linearGrow(dir);
         }
@@ -658,7 +658,6 @@ static double bezier_length (Geom::Point a0, Geom::Point a1, Geom::Point a2, Geo
     double lower = Geom::distance(a0, a3);
     double upper = Geom::distance(a0, a1) + Geom::distance(a1, a2) + Geom::distance(a2, a3);
 
-    // TODO maybe EPSILON is this is too big in this case?
     if (upper - lower < Geom::EPSILON) return (lower + upper)/2;
 
     Geom::Point // Casteljau subdivision
@@ -732,7 +731,7 @@ void Node::_linearGrow(int dir)
     } else {
         // both iterators that store last selected nodes are initially empty
         NodeList::iterator last_fwd, last_rev;
-        double last_distance_back, last_distance_front;
+        double last_distance_back = 0, last_distance_front = 0;
 
         while (rev || fwd) {
             if (fwd && (!rev || distance_front <= distance_back)) {
@@ -741,7 +740,7 @@ void Node::_linearGrow(int dir)
                     last_distance_front = distance_front;
                 }
                 NodeList::iterator n = fwd.next();
-                distance_front += bezier_length(*fwd, fwd->_front, n->_back, *n);
+                if (n) distance_front += bezier_length(*fwd, fwd->_front, n->_back, *n);
                 fwd = n;
             } else if (rev && (!fwd || distance_front > distance_back)) {
                 if (rev->selected()) {
@@ -749,13 +748,27 @@ void Node::_linearGrow(int dir)
                     last_distance_back = distance_back;
                 }
                 NodeList::iterator p = rev.prev();
-                distance_back += bezier_length(*rev, rev->_back, p->_front, *p);
+                if (p) distance_back += bezier_length(*rev, rev->_back, p->_front, *p);
                 rev = p;
             }
             // Check whether we walked the entire cyclic subpath.
             // This is initially true because both iterators start from this node,
             // so this check cannot go in the while condition.
-            if (fwd == rev) break;
+            // When this happens, we need to check the last node, pointed to by the iterators.
+            if (fwd && fwd == rev) {
+                if (!fwd->selected()) break;
+                NodeList::iterator fwdp = fwd.prev(), revn = rev.next();
+                double df = distance_front + bezier_length(*fwdp, fwdp->_front, fwd->_back, *fwd);
+                double db = distance_back + bezier_length(*revn, revn->_back, rev->_front, *rev);
+                if (df > db) {
+                    last_fwd = fwd;
+                    last_distance_front = df;
+                } else {
+                    last_rev = rev;
+                    last_distance_back = db;
+                }
+                break;
+            }
         }
 
         NodeList::iterator t;
@@ -825,34 +838,97 @@ bool Node::_grabbedHandler(GdkEventMotion *event)
 
 void Node::_draggedHandler(Geom::Point &new_pos, GdkEventMotion *event)
 {
+    // For a note on how snapping is implemented in Inkscape, see snap.h.
+    SnapManager &sm = _desktop->namedview->snap_manager;
+    Inkscape::SnapPreferences::PointType t = Inkscape::SnapPreferences::SNAPPOINT_NODE;
+    bool snap = sm.someSnapperMightSnap();
+    std::vector< std::pair<Geom::Point, int> > unselected;
+    if (snap) {
+        // setup
+        // TODO we are doing this every time a snap happens. It should once be done only once
+        //      per drag - maybe in the grabbed handler?
+        // TODO "unselected" must be valid during the snap run, because it is not copied.
+        //      Fix this in snap.h and snap.cpp, then the above.
+
+        // Build the list of unselected nodes.
+        typedef ControlPointSelection::Set Set;
+        Set nodes = _selection.allPoints();
+        for (Set::iterator i = nodes.begin(); i != nodes.end(); ++i) {
+            if (!(*i)->selected()) {
+                Node *n = static_cast<Node*>(*i);
+                unselected.push_back(std::make_pair((*i)->position(), (int) n->_snapTargetType()));
+            }
+        }
+        sm.setupIgnoreSelection(_desktop, true, &unselected);
+    }
+
     if (held_control(*event)) {
+        Geom::Point origin = _last_drag_origin();
         if (held_alt(*event)) {
             // with Ctrl+Alt, constrain to handle lines
             // project the new position onto a handle line that is closer
-            Geom::Point origin = _last_drag_origin();
-            Geom::Line line_front(origin, origin + _front.relativePos());
-            Geom::Line line_back(origin, origin + _back.relativePos());
-            double dist_front, dist_back;
-            dist_front = Geom::distance(new_pos, line_front);
-            dist_back = Geom::distance(new_pos, line_back);
-            if (dist_front < dist_back) {
-                new_pos = Geom::projection(new_pos, line_front);
+            Inkscape::Snapper::ConstraintLine line_front(origin, _front.relativePos());
+            Inkscape::Snapper::ConstraintLine line_back(origin, _back.relativePos());
+
+            // TODO: combine these two branches by modifying snap.h / snap.cpp
+            if (snap) {
+                Inkscape::SnappedPoint fp, bp;
+                fp = sm.constrainedSnap(t, position(), _snapSourceType(), line_front);
+                bp = sm.constrainedSnap(t, position(), _snapSourceType(), line_back);
+
+                if (fp.isOtherSnapBetter(bp, false)) {
+                    bp.getPoint(new_pos);
+                } else {
+                    fp.getPoint(new_pos);
+                }
             } else {
-                new_pos = Geom::projection(new_pos, line_back);
+                Geom::Point p_front = line_front.projection(new_pos);
+                Geom::Point p_back = line_back.projection(new_pos);
+                if (Geom::distance(new_pos, p_front) < Geom::distance(new_pos, p_back)) {
+                    new_pos = p_front;
+                } else {
+                    new_pos = p_back;
+                }
             }
         } else {
             // with Ctrl, constrain to axes
-            // TODO maybe add diagonals when the distance from origin is large enough?
-            Geom::Point origin = _last_drag_origin();
-            Geom::Point delta = new_pos - origin;
-            Geom::Dim2 d = (fabs(delta[Geom::X]) < fabs(delta[Geom::Y])) ? Geom::X : Geom::Y;
-            new_pos[d] = origin[d];
+            // TODO combine the two branches
+            if (snap) {
+                Inkscape::SnappedPoint fp, bp;
+                Inkscape::Snapper::ConstraintLine line_x(origin, Geom::Point(1, 0));
+                Inkscape::Snapper::ConstraintLine line_y(origin, Geom::Point(0, 1));
+                fp = sm.constrainedSnap(t, position(), _snapSourceType(), line_x);
+                bp = sm.constrainedSnap(t, position(), _snapSourceType(), line_y);
+
+                if (fp.isOtherSnapBetter(bp, false)) {
+                    fp = bp;
+                }
+                fp.getPoint(new_pos);
+            } else {
+                Geom::Point origin = _last_drag_origin();
+                Geom::Point delta = new_pos - origin;
+                Geom::Dim2 d = (fabs(delta[Geom::X]) < fabs(delta[Geom::Y])) ? Geom::X : Geom::Y;
+                new_pos[d] = origin[d];
+            }
         }
-    } else {
-        // TODO snapping?
+    } else if (snap) {
+        sm.freeSnapReturnByRef(Inkscape::SnapPreferences::SNAPPOINT_NODE, new_pos, _snapSourceType());
     }
 }
 
+Inkscape::SnapSourceType Node::_snapSourceType()
+{
+    if (_type == NODE_SMOOTH || _type == NODE_AUTO)
+        return SNAPSOURCE_NODE_SMOOTH;
+    return SNAPSOURCE_NODE_CUSP;
+}
+Inkscape::SnapTargetType Node::_snapTargetType()
+{
+    if (_type == NODE_SMOOTH || _type == NODE_AUTO)
+        return SNAPTARGET_NODE_SMOOTH;
+    return SNAPTARGET_NODE_CUSP;
+}
+
 Glib::ustring Node::_getTip(unsigned state)
 {
     if (state_held_shift(state)) {
index 167cf90b871e63c20816a050c481ec70f1004ba7..a85877d5ca996345e572b41de689f88a8de04480 100644 (file)
@@ -19,6 +19,7 @@
 #include <boost/shared_ptr.hpp>
 #include <boost/optional.hpp>
 #include <boost/operators.hpp>
+#include "snapped-point.h"
 #include "ui/tool/selectable-control-point.h"
 #include "ui/tool/node-types.h"
 
@@ -136,8 +137,9 @@ public:
     void sink();
 
     static char const *node_type_to_localized_string(NodeType type);
-protected:
+    // temporarily public
     virtual bool _eventHandler(GdkEvent *event);
+protected:
     virtual void _setState(State state);
     virtual Glib::ustring _getTip(unsigned state);
     virtual Glib::ustring _getDragTip(GdkEventMotion *event);
@@ -151,6 +153,8 @@ private:
     void _linearGrow(int dir);
     Node *_next();
     Node *_prev();
+    Inkscape::SnapSourceType _snapSourceType();
+    Inkscape::SnapTargetType _snapTargetType();
     static SPCtrlShapeType _node_type_to_shape(NodeType type);
     static bool _is_line_segment(Node *first, Node *second);
 
index 2755d6fb35c94cb4bd8d0bde7258d862b706e6e1..cfa3846f86495a5b37aa2206db5e369817bbde9b 100644 (file)
@@ -214,42 +214,6 @@ void PathManipulator::selectSubpaths()
     }
 }
 
-/** Select all nodes in the path. */
-void PathManipulator::selectAll()
-{
-    for (SubpathList::iterator i = _subpaths.begin(); i != _subpaths.end(); ++i) {
-        for (NodeList::iterator j = (*i)->begin(); j != (*i)->end(); ++j) {
-            _selection.insert(j.ptr());
-        }
-    }
-}
-
-/** Select points inside the given rectangle. If all points inside it are already selected,
- * they will be deselected.
- * @param area Area to select
- */
-void PathManipulator::selectArea(Geom::Rect const &area)
-{
-    bool nothing_selected = true;
-    std::vector<Node*> in_area;
-    for (SubpathList::iterator i = _subpaths.begin(); i != _subpaths.end(); ++i) {
-        for (NodeList::iterator j = (*i)->begin(); j != (*i)->end(); ++j) {
-            if (area.contains(j->position())) {
-                in_area.push_back(j.ptr());
-                if (!j->selected()) {
-                    _selection.insert(j.ptr());
-                    nothing_selected = false;
-                }
-            }
-        }
-    }
-    if (nothing_selected) {
-        for (std::vector<Node*>::iterator i = in_area.begin(); i != in_area.end(); ++i) {
-            _selection.erase(*i);
-        }
-    }
-}
-
 /** Move the selection forward or backward by one node in each subpath, based on the sign
  * of the parameter. */
 void PathManipulator::shiftSelection(int dir)
@@ -298,17 +262,6 @@ void PathManipulator::shiftSelection(int dir)
     }
 }
 
-/** Invert selection in the entire path. */
-void PathManipulator::invertSelection()
-{
-    for (SubpathList::iterator i = _subpaths.begin(); i != _subpaths.end(); ++i) {
-        for (NodeList::iterator j = (*i)->begin(); j != (*i)->end(); ++j) {
-            if (j->selected()) _selection.erase(j.ptr());
-            else _selection.insert(j.ptr());
-        }
-    }
-}
-
 /** Invert selection in the selected subpaths. */
 void PathManipulator::invertSelectionInSubpaths()
 {
@@ -724,6 +677,7 @@ void PathManipulator::setControlsTransform(Geom::Matrix const &tnew)
     _createGeometryFromControlPoints();
 }
 
+/** Hide the curve drag point until the next motion event. */
 void PathManipulator::hideDragPoint()
 {
     _dragpoint->setVisible(false);
index 38f66dee0a0f62d5a0e4c381c087c64facb90ab6..99e183b4530c656ecca0c4ab9ec929394ab974e6 100644 (file)
@@ -64,10 +64,7 @@ public:
     SPPath *item() { return _path; }
 
     void selectSubpaths();
-    void selectAll();
-    void selectArea(Geom::Rect const &);
     void shiftSelection(int dir);
-    void invertSelection();
     void invertSelectionInSubpaths();
 
     void insertNodes();
index b189a713f845716c515c0524315b64aee68be5c8..5b9aa4fc838207a1b42753503b4f5c3abdeae4d4 100644 (file)
@@ -50,10 +50,12 @@ SelectableControlPoint::SelectableControlPoint(SPDesktop *d, Geom::Point const &
 SelectableControlPoint::~SelectableControlPoint()
 {
     _selection.erase(this);
+    _selection.allPoints().erase(this);
 }
 
 void SelectableControlPoint::_connectHandlers()
 {
+    _selection.allPoints().insert(this);
     signal_clicked.connect(
         sigc::mem_fun(*this, &SelectableControlPoint::_clickedHandler));
     signal_grabbed.connect(
index d26a4e6d5cf87c018e7c57f152c2986edef1a747..f03be681a631d68ae11ffb88df3ae7f27a3a2bbc 100644 (file)
@@ -79,6 +79,7 @@
 #include "ui/dialog/layers.h"
 #include "ui/dialog/swatches.h"
 #include "ui/icon-names.h"
+#include "ui/tool/control-point-selection.h"
 #include "ui/tool/multi-path-manipulator.h"
 #include "ui/tool/node-tool.h"
 
@@ -937,7 +938,7 @@ EditVerb::perform(SPAction *action, void *data, void */*pdata*/)
         case SP_VERB_EDIT_SELECT_ALL_IN_ALL_LAYERS:
             if (tools_isactive(dt, TOOLS_NODES)) {
                 InkNodeTool *nt = static_cast<InkNodeTool*>(dt->event_context);
-                nt->_multipath->selectAll();
+                nt->_selected_nodes->selectAll();
             } else {
                 sp_edit_select_all_in_all_layers(dt);
             }
@@ -945,7 +946,7 @@ EditVerb::perform(SPAction *action, void *data, void */*pdata*/)
         case SP_VERB_EDIT_INVERT_IN_ALL_LAYERS:
             if (tools_isactive(dt, TOOLS_NODES)) {
                 InkNodeTool *nt = static_cast<InkNodeTool*>(dt->event_context);
-                nt->_multipath->invertSelection();
+                nt->_selected_nodes->invertSelection();
             } else {
                 sp_edit_invert_in_all_layers(dt);
             }
@@ -977,7 +978,7 @@ EditVerb::perform(SPAction *action, void *data, void */*pdata*/)
         case SP_VERB_EDIT_DESELECT:
             if (tools_isactive(dt, TOOLS_NODES)) {
                 InkNodeTool *nt = static_cast<InkNodeTool*>(dt->event_context);
-                nt->_multipath->deselect();
+                nt->_selected_nodes->clear();
             } else {
                 sp_desktop_selection(dt)->clear();
             }