Code

Split SPCanvasItem and SPCanvasGroup to individual .h files. Removed forward header.
[inkscape.git] / src / connector-context.cpp
index 228c81d29e0bdc26365ebe694bf4600778f901f3..f4b202451156cc5e83133d55d3fe5dfda76075b1 100644 (file)
@@ -1,11 +1,14 @@
-/*
+/**
  * Connector creation tool
  *
  * Authors:
  *   Michael Wybrow <mjwybrow@users.sourceforge.net>
+ *   Abhishek Sharma
+ *   Jon A. Cruz <jon@joncruz.org>
  *
  * Copyright (C) 2005-2008  Michael Wybrow
  * Copyright (C) 2009  Monash University
+ * Copyright (C) 2010  authors
  *
  * Released under GNU GPL, read the file 'COPYING' for more information
  *
@@ -35,7 +38,7 @@
  *     Gobbling away all duplicates after the current can occasionally result
  *     in the path lagging behind the mouse cursor if it is no longer being
  *     dragged.
- *  o  Fix up libavoid's representation after undo actions.  It doesn't see 
+ *  o  Fix up libavoid's representation after undo actions.  It doesn't see
  *     any transform signals and hence doesn't know shapes have moved back to
  *     there earlier positions.
  *  o  Decide whether drawing/editing mode should be an Inkscape preference
  * ----------------------------------------------------------------------------
  *
  * mjwybrow's observations on acracan's Summer of Code connector work:
- * 
+ *
  *  -  GUI comments:
- * 
+ *
  *      -  Buttons for adding and removing user-specified connection
  *     points should probably have "+" and "-" symbols on them so they
  *     are consistent with the similar buttons for the node tool.
- *      -  Controls on the connector tool be should be reordered logically, 
+ *      -  Controls on the connector tool be should be reordered logically,
  *     possibly as follows:
- * 
- *     *Connector*: [Polyline-radio-button] [Orthgonal-radio-button] 
+ *
+ *     *Connector*: [Polyline-radio-button] [Orthgonal-radio-button]
  *       [Curvature-control] | *Shape*: [Avoid-button] [Dont-avoid-button]
  *       [Spacing-control] | *Connection pts*: [Edit-mode] [Add-pt] [Rm-pt]
- * 
- *     I think that the network layout controls be moved to the 
- *     Align and Distribute dialog (there is already the layout button 
+ *
+ *     I think that the network layout controls be moved to the
+ *     Align and Distribute dialog (there is already the layout button
  *     there, but no options are exposed).
- * 
+ *
  *     I think that the style change between polyline and orthogonal
  *     would be much clearer with two buttons (radio behaviour -- just
  *     one is true).
- * 
- *     The other tools show a label change from "New:" to "Change:" 
+ *
+ *     The other tools show a label change from "New:" to "Change:"
  *     depending on whether an object is selected.  We could consider
  *     this but there may not be space.
- * 
+ *
  *     The Add-pt and Rm-pt buttons should be greyed out (inactive) if
  *     we are not in connection point editing mode.  And probably also
  *     if there is no shape selected, i.e. at the times they have no
  *     effect when clicked.
- * 
- *     Likewise for the avoid/ignore shapes buttons.  These should be 
+ *
+ *     Likewise for the avoid/ignore shapes buttons.  These should be
  *     inactive when a shape is not selected in the connector context.
- * 
+ *
  *  -  When creating/editing connection points:
- * 
+ *
  *      -  Strange things can happen if you have connectors selected, or
- *     try rerouting connectors by dragging their endpoints when in 
+ *     try rerouting connectors by dragging their endpoints when in
  *     connection point editing mode.
- * 
+ *
  *      -  Possibly the selected shape's connection points should always
  *     be shown (i.e., have knots) when in editing mode.
- * 
+ *
  *      -  It is a little strange to be able to place connection points
  *     competely outside shapes.  Especially when you later can't draw
  *     connectors to them since the knots are only visible when you
  *     are over the shape.  I think that you should only be able to
- *     place connection points inside or on the boundary of the shape 
+ *     place connection points inside or on the boundary of the shape
  *     itself.
- *  
- *      -  The intended ability to place a new point at the current cursor 
+ *
+ *      -  The intended ability to place a new point at the current cursor
  *     position by pressing RETURN does not seem to work.
- * 
+ *
  *      -  The Status bar tooltip should change to reflect editing mode
  *     and tell the user about RETURN and how to use the tool.
- * 
+ *
  *  -  Connection points general:
- * 
- *      -  Connection points that were inside the shape can end up outside 
+ *
+ *      -  Connection points that were inside the shape can end up outside
  *     after a rotation is applied to the shape in the select tool.
  *     It doesn't seem like the correct transform is being applied to
  *     these, or it is being applied at the wrong time.  I'd expect
  *     connection points to rotate with the shape, and stay at the
  *     same position "on the shape"
- * 
+ *
  *      -  I was able to make the connectors attached to a shape fall off
  *     the shape after scaling it.  Not sure the exact cause, but may
  *     require more investigation/debugging.
- * 
+ *
  *      -  The user-defined connection points should be either absolute
  *     (as the current ones are) or defined as a percentage of the
  *     shape.  These would be based on a toggle setting on the
  *     toolbar, and they would be placed in exactly the same way by
  *     the user.  The only difference would be that they would be
  *     store as percentage positions in the SVG connection-points
- *     property and that they would update/move automatically if the 
+ *     property and that they would update/move automatically if the
  *     object was resized or scaled.
- * 
+ *
  *      -  Thinking more, I think you always want to store and think about
  *     the positions of connection points to be pre-transform, but
  *     obviously the shape transform is applied to them.  That way,
  *     the shape transform is altered.  The Percentage version would
  *     compute their position from the pre-transform dimensions and
  *     then have the transform applied to them, for example.
- * 
+ *
  *      -  The connection points in the test_connection_points.svg file
  *     seem to follow the shape when it is moved, but connection
  *     points I add to new shapes, do not follow the shape, either
  *     when the shape is just moved or transformed.  There is
- *     something wrong here.  What exactly should the behaviour be 
+ *     something wrong here.  What exactly should the behaviour be
  *     currently?
- * 
+ *
  *      -  I see that connection points are specified at absolute canvas
  *     positions.  I really think that they should be specified in
  *     shape coordinated relative to the shapes.  There may be
 #include "connector-context.h"
 #include "pixmaps/cursor-connector.xpm"
 #include "pixmaps/cursor-node.xpm"
-#include "pixmaps/cursor-node-m.xpm"
-#include "pixmaps/cursor-node-d.xpm"
+//#include "pixmaps/cursor-node-m.xpm"
+//#include "pixmaps/cursor-node-d.xpm"
 #include "xml/node-event-vector.h"
 #include "xml/repr.h"
 #include "svg/svg.h"
 #include "inkscape.h"
 #include "preferences.h"
 #include "sp-path.h"
+#include "display/sp-canvas.h"
 #include "display/canvas-bpath.h"
 #include "display/sodipodi-ctrl.h"
 #include <glibmm/i18n.h>
 #include "sp-flowtext.h"
 #include "display/curve.h"
 
+using Inkscape::DocumentUndo;
+
 static void sp_connector_context_class_init(SPConnectorContextClass *klass);
 static void sp_connector_context_init(SPConnectorContext *conn_context);
 static void sp_connector_context_dispose(GObject *object);
@@ -218,7 +224,7 @@ static void cc_set_active_shape(SPConnectorContext *cc, SPItem *item);
 static void cc_clear_active_shape(SPConnectorContext *cc);
 static void cc_set_active_conn(SPConnectorContext *cc, SPItem *item);
 static void cc_clear_active_conn(SPConnectorContext *cc);
-static gchar *conn_pt_handle_test(SPConnectorContext *cc, Geom::Point& w);
+static bool conn_pt_handle_test(SPConnectorContext *cc, Geom::Point& p, gchar **href, gchar **cpid);
 static void cc_select_handle(SPKnot* knot);
 static void cc_deselect_handle(SPKnot* knot);
 static bool cc_item_is_shape(SPItem *item);
@@ -233,6 +239,9 @@ static void shape_event_attr_changed(Inkscape::XML::Node *repr, gchar const *nam
         gpointer data);
 
 
+static char* cc_knot_tips[] = { _("<b>Connection point</b>: click or drag to create a new connector"),
+                           _("<b>Connection point</b>: click to select, drag to move") };
+
 /*static Geom::Point connector_drag_origin_w(0, 0);
 static bool connector_within_tolerance = false;*/
 static SPEventContextClass *parent_class;
@@ -333,13 +342,15 @@ sp_connector_context_init(SPConnectorContext *cc)
     cc->clickedhandle = NULL;
 
     new (&cc->connpthandles) ConnectionPointMap();
-    
+
     for (int i = 0; i < 2; ++i) {
         cc->endpt_handle[i] = NULL;
         cc->endpt_handler_id[i] = 0;
     }
-    cc->sid = NULL;
-    cc->eid = NULL;
+    cc->shref = NULL;
+    cc->scpid = NULL;
+    cc->ehref = NULL;
+    cc->ecpid = NULL;
     cc->npoints = 0;
     cc->state = SP_CONNECTOR_CONTEXT_IDLE;
 }
@@ -353,7 +364,7 @@ sp_connector_context_dispose(GObject *object)
     cc->sel_changed_connection.disconnect();
 
     if (!cc->connpthandles.empty()) {
-        for (ConnectionPointMap::iterator it = cc->connpthandles.begin(); 
+        for (ConnectionPointMap::iterator it = cc->connpthandles.begin();
                 it != cc->connpthandles.end(); ++it) {
             g_object_unref(it->first);
         }
@@ -366,13 +377,21 @@ sp_connector_context_dispose(GObject *object)
             cc->endpt_handle[i] = NULL;
         }
     }
-    if (cc->sid) {
-        g_free(cc->sid);
-        cc->sid = NULL;
+    if (cc->shref) {
+        g_free(cc->shref);
+        cc->shref = NULL;
+    }
+    if (cc->scpid) {
+        g_free(cc->scpid);
+        cc->scpid = NULL;
+    }
+    if (cc->ehref) {
+        g_free(cc->shref);
+        cc->shref = NULL;
     }
-    if (cc->eid) {
-        g_free(cc->eid);
-        cc->eid = NULL;
+    if (cc->ecpid) {
+        g_free(cc->scpid);
+        cc->scpid = NULL;
     }
     g_assert( cc->newConnRef == NULL );
 
@@ -413,7 +432,7 @@ sp_connector_context_setup(SPEventContext *ec)
     cc_selection_changed(cc->selection, (gpointer) cc);
 
     cc->within_tolerance = false;
-    
+
     sp_event_context_read(ec, "curvature");
     sp_event_context_read(ec, "orthogonal");
     sp_event_context_read(ec, "mode");
@@ -472,7 +491,7 @@ void sp_connector_context_switch_mode(SPEventContext* ec, unsigned int newMode)
         {
             cc->selection->set( SP_OBJECT( cc->active_shape ) );
         }
-        else 
+        else
         {
             SPItem* item = cc->selection->singleItem();
             if ( item )
@@ -537,7 +556,7 @@ cc_clear_active_shape(SPConnectorContext *cc)
 
     // Hide the connection points if they exist.
     if (cc->connpthandles.size()) {
-        for (ConnectionPointMap::iterator it = cc->connpthandles.begin(); 
+        for (ConnectionPointMap::iterator it = cc->connpthandles.begin();
                 it != cc->connpthandles.end(); ++it) {
             sp_knot_hide(it->first);
         }
@@ -570,8 +589,8 @@ cc_clear_active_conn(SPConnectorContext *cc)
 }
 
 
-static gchar *
-conn_pt_handle_test(SPConnectorContext *cc, Geom::Point& p)
+static bool
+conn_pt_handle_test(SPConnectorContext *cc, Geom::Point& p, gchar **href, gchar **cpid)
 {
     // TODO: this will need to change when there are more connection
     //       points available for each shape.
@@ -580,10 +599,13 @@ conn_pt_handle_test(SPConnectorContext *cc, Geom::Point& p)
     {
         p = cc->active_handle->pos;
         const ConnectionPoint& cp = cc->connpthandles[cc->active_handle];
-        return g_strdup_printf("#%s_%c_%d", SP_OBJECT_ID(cc->active_shape),
-                               cp.type == ConnPointDefault ? 'd' : 'u' , cp.id);
+        *href = g_strdup_printf("#%s", cc->active_shape->getId());
+        *cpid = g_strdup_printf("%c%d", cp.type == ConnPointDefault ? 'd' : 'u' , cp.id);
+        return true;
     }
-    return NULL;
+    *href = NULL;
+    *cpid = NULL;
+    return false;
 }
 
 static void
@@ -616,7 +638,7 @@ sp_connector_context_item_handler(SPEventContext *event_context, SPItem *item, G
     SPConnectorContext *cc = SP_CONNECTOR_CONTEXT(event_context);
 
     Geom::Point p(event->button.x, event->button.y);
-    
+
     switch (event->type) {
         case GDK_BUTTON_RELEASE:
             if (event->button.button == 1 && !event_context->space_panning) {
@@ -625,7 +647,6 @@ sp_connector_context_item_handler(SPEventContext *event_context, SPItem *item, G
                 {
                     spcc_reset_colors(cc);
                     cc->state = SP_CONNECTOR_CONTEXT_IDLE;
-//                    sp_event_context_snap_window_closed(event_context);
                 }
                 if (cc->state != SP_CONNECTOR_CONTEXT_IDLE) {
                     // Doing something else like rerouting.
@@ -660,11 +681,11 @@ sp_connector_context_item_handler(SPEventContext *event_context, SPItem *item, G
             if (cc->mode == SP_CONNECTOR_CONTEXT_DRAWING_MODE || (cc->mode == SP_CONNECTOR_CONTEXT_EDITING_MODE && !cc->selected_handle))
             {
                 if (cc_item_is_shape(item)) {
-                    
+
                     // I don't really understand what the above does,
                     // so I commented it.
                     // This is a shape, so show connection point(s).
-    /*                if (!(cc->active_shape) 
+    /*                if (!(cc->active_shape)
                             // Don't show handle for another handle.
     //                         || (cc->connpthandles.find((SPKnot*) item) != cc->connpthandles.end())
                         )
@@ -753,11 +774,10 @@ connector_handle_button_press(SPConnectorContext *const cc, GdkEventButton const
             Geom::Point const event_dt = cc->desktop->w2d(event_w);
 
             SnapManager &m = cc->desktop->namedview->snap_manager;
-            m.setup(cc->desktop);
 
             switch (cc->state) {
                 case SP_CONNECTOR_CONTEXT_STOP:
-                    /* This is allowed, if we just cancelled curve */
+                    /* This is allowed, if we just canceled curve */
                 case SP_CONNECTOR_CONTEXT_IDLE:
                 {
                     if ( cc->npoints == 0 ) {
@@ -770,14 +790,14 @@ connector_handle_button_press(SPConnectorContext *const cc, GdkEventButton const
                         Geom::Point p = event_dt;
 
                         // Test whether we clicked on a connection point
-                        cc->sid = conn_pt_handle_test(cc, p);
+                        bool found = conn_pt_handle_test(cc, p, &cc->shref, &cc->scpid);
 
-//                        sp_event_context_snap_window_open(event_context);
-
-                        if (!cc->sid) {
+                        if (!found) {
                             // This is the first point, so just snap it to the grid
                             // as there's no other points to go off.
-                            m.freeSnapReturnByRef(Inkscape::SnapPreferences::SNAPPOINT_NODE, p, Inkscape::SNAPSOURCE_HANDLE);
+                            m.setup(cc->desktop);
+                            m.freeSnapReturnByRef(p, Inkscape::SNAPSOURCE_OTHER_HANDLE);
+                            m.unSetup();
                         }
                         spcc_connector_set_initial_point(cc, p);
 
@@ -789,18 +809,19 @@ connector_handle_button_press(SPConnectorContext *const cc, GdkEventButton const
                 case SP_CONNECTOR_CONTEXT_DRAGGING:
                 {
                     // This is the second click of a connector creation.
-                    m.freeSnapReturnByRef(Inkscape::SnapPreferences::SNAPPOINT_NODE, p, Inkscape::SNAPSOURCE_HANDLE);
+                    m.setup(cc->desktop);
+                    m.freeSnapReturnByRef(p, Inkscape::SNAPSOURCE_OTHER_HANDLE);
+                    m.unSetup();
 
                     spcc_connector_set_subsequent_point(cc, p);
                     spcc_connector_finish_segment(cc, p);
                     // Test whether we clicked on a connection point
-                    cc->eid = conn_pt_handle_test(cc, p);
+                    /*bool found = */conn_pt_handle_test(cc, p, &cc->ehref, &cc->ecpid);
                     if (cc->npoints != 0) {
                         spcc_connector_finish(cc);
                     }
                     cc_set_active_conn(cc, cc->newconn);
                     cc->state = SP_CONNECTOR_CONTEXT_IDLE;
-//                    sp_event_context_snap_window_closed(event_context);
                     ret = TRUE;
                     break;
                 }
@@ -819,7 +840,6 @@ connector_handle_button_press(SPConnectorContext *const cc, GdkEventButton const
                 cc_connector_rerouting_finish(cc, &p);
 
                 cc->state = SP_CONNECTOR_CONTEXT_IDLE;
-//                sp_event_context_snap_window_closed(event_context);
 
                 // Don't set ret to TRUE, so we drop through to the
                 // parent handler which will open the context menu.
@@ -827,7 +847,6 @@ connector_handle_button_press(SPConnectorContext *const cc, GdkEventButton const
             else if (cc->npoints != 0) {
                 spcc_connector_finish(cc);
                 cc->state = SP_CONNECTOR_CONTEXT_IDLE;
-//                sp_event_context_snap_window_closed(event_context);
                 ret = TRUE;
             }
         }
@@ -847,7 +866,7 @@ connector_handle_button_press(SPConnectorContext *const cc, GdkEventButton const
             cc->xp = bevent.x;
             cc->yp = bevent.y;
             cc->within_tolerance = true;
-            
+
             ConnectionPointMap::iterator const& active_knot_it = cc->connpthandles.find( cc->active_handle );
 
             switch (cc->state)
@@ -879,7 +898,6 @@ connector_handle_button_press(SPConnectorContext *const cc, GdkEventButton const
 
                     if ( cc->selected_handle )
                     {
-//                         sp_event_context_snap_window_open(event_context);
                         cc->state = SP_CONNECTOR_CONTEXT_DRAGGING;
                         cc->selection->set( SP_OBJECT( cc->active_shape ) );
                     }
@@ -933,16 +951,16 @@ connector_handle_motion_notify(SPConnectorContext *const cc, GdkEventMotion cons
     if ( cc->mode == SP_CONNECTOR_CONTEXT_DRAWING_MODE )
     {
         SnapManager &m = dt->namedview->snap_manager;
-        m.setup(dt);
-        
+
         switch (cc->state) {
             case SP_CONNECTOR_CONTEXT_DRAGGING:
             {
                 gobble_motion_events(mevent.state);
                 // This is movement during a connector creation.
                 if ( cc->npoints > 0 ) {
-                    m.freeSnapReturnByRef(Inkscape::SnapPreferences::SNAPPOINT_NODE, p,
-                            Inkscape::SNAPSOURCE_HANDLE);
+                    m.setup(dt);
+                    m.freeSnapReturnByRef(p, Inkscape::SNAPSOURCE_OTHER_HANDLE);
+                    m.unSetup();
                     cc->selection->clear();
                     spcc_connector_set_subsequent_point(cc, p);
                     ret = TRUE;
@@ -954,11 +972,12 @@ connector_handle_motion_notify(SPConnectorContext *const cc, GdkEventMotion cons
                 gobble_motion_events(GDK_BUTTON1_MASK);
                 g_assert( SP_IS_PATH(cc->clickeditem));
 
-                m.freeSnapReturnByRef(Inkscape::SnapPreferences::SNAPPOINT_NODE, p,
-                        Inkscape::SNAPSOURCE_HANDLE);
+                m.setup(dt);
+                m.freeSnapReturnByRef(p, Inkscape::SNAPSOURCE_OTHER_HANDLE);
+                m.unSetup();
 
                 // Update the hidden path
-                Geom::Matrix i2d = sp_item_i2d_affine(cc->clickeditem);
+                Geom::Matrix i2d = (cc->clickeditem)->i2d_affine();
                 Geom::Matrix d2i = i2d.inverse();
                 SPPath *path = SP_PATH(cc->clickeditem);
                 SPCurve *curve = path->original_curve ? path->original_curve : path->curve;
@@ -985,6 +1004,11 @@ connector_handle_motion_notify(SPConnectorContext *const cc, GdkEventMotion cons
                 /* This is perfectly valid */
                 break;
             default:
+                if (!sp_event_context_knot_mouseover(cc)) {
+                    m.setup(dt);
+                    m.preSnap(Inkscape::SnapCandidatePoint(p, Inkscape::SNAPSOURCE_OTHER_HANDLE));
+                    m.unSetup();
+                }
                 break;
         }
     }
@@ -1018,7 +1042,6 @@ connector_handle_button_release(SPConnectorContext *const cc, GdkEventButton con
         SPDocument *doc = sp_desktop_document(desktop);
 
         SnapManager &m = desktop->namedview->snap_manager;
-        m.setup(desktop);
 
         Geom::Point const event_w(revent.x, revent.y);
 
@@ -1030,7 +1053,9 @@ connector_handle_button_release(SPConnectorContext *const cc, GdkEventButton con
                 //case SP_CONNECTOR_CONTEXT_POINT:
                 case SP_CONNECTOR_CONTEXT_DRAGGING:
                 {
-                    m.freeSnapReturnByRef(Inkscape::SnapPreferences::SNAPPOINT_NODE, p, Inkscape::SNAPSOURCE_HANDLE);
+                    m.setup(desktop);
+                    m.freeSnapReturnByRef(p, Inkscape::SNAPSOURCE_OTHER_HANDLE);
+                    m.unSetup();
 
                     if (cc->within_tolerance)
                     {
@@ -1041,23 +1066,23 @@ connector_handle_button_release(SPConnectorContext *const cc, GdkEventButton con
                     spcc_connector_set_subsequent_point(cc, p);
                     spcc_connector_finish_segment(cc, p);
                     // Test whether we clicked on a connection point
-                    cc->eid = conn_pt_handle_test(cc, p);
+                    /*bool found = */conn_pt_handle_test(cc, p, &cc->ehref, &cc->ecpid);
                     if (cc->npoints != 0) {
                         spcc_connector_finish(cc);
                     }
                     cc_set_active_conn(cc, cc->newconn);
                     cc->state = SP_CONNECTOR_CONTEXT_IDLE;
-//                    sp_event_context_snap_window_closed(event_context);
                     break;
                 }
                 case SP_CONNECTOR_CONTEXT_REROUTING:
                 {
-                    m.freeSnapReturnByRef(Inkscape::SnapPreferences::SNAPPOINT_NODE, p, Inkscape::SNAPSOURCE_HANDLE);
+                    m.setup(desktop);
+                    m.freeSnapReturnByRef(p, Inkscape::SNAPSOURCE_OTHER_HANDLE);
+                    m.unSetup();
                     cc_connector_rerouting_finish(cc, &p);
 
-                    sp_document_ensure_up_to_date(doc);
+                    doc->ensureUpToDate();
                     cc->state = SP_CONNECTOR_CONTEXT_IDLE;
-//                    sp_event_context_snap_window_closed(event_context);
                     return TRUE;
                     break;
                 }
@@ -1074,15 +1099,15 @@ connector_handle_button_release(SPConnectorContext *const cc, GdkEventButton con
             switch ( cc->state )
             {
                 case SP_CONNECTOR_CONTEXT_DRAGGING:
-                    
+
                     if (!cc->within_tolerance)
                     {
-//                        sp_event_context_snap_window_open(event_context);
-                        m.freeSnapReturnByRef(Inkscape::SnapPreferences::SNAPPOINT_NODE, p, Inkscape::SNAPSOURCE_HANDLE);
+                        m.setup(desktop);
+                        m.freeSnapReturnByRef(p, Inkscape::SNAPSOURCE_OTHER_HANDLE);
+                        m.unSetup();
                         sp_knot_set_position(cc->selected_handle, p, 0);
-//                        sp_event_context_snap_window_closed(event_context);
                         ConnectionPoint& cp = cc->connpthandles[cc->selected_handle];
-                        cp.pos = p * sp_item_dt2i_affine(cc->active_shape);
+                        cp.pos = p * (cc->active_shape)->dt2i_affine();
                         cc->active_shape->avoidRef->updateConnectionPoint(cp);
                     }
 
@@ -1092,19 +1117,19 @@ connector_handle_button_release(SPConnectorContext *const cc, GdkEventButton con
 
 
                 case SP_CONNECTOR_CONTEXT_NEWCONNPOINT:
-//                    sp_event_context_snap_window_open( event_context );
-                    m.freeSnapReturnByRef(Inkscape::SnapPreferences::SNAPPOINT_NODE, p, Inkscape::SNAPSOURCE_HANDLE);
-                    
+                    m.setup(desktop);
+                    m.freeSnapReturnByRef(p, Inkscape::SNAPSOURCE_OTHER_HANDLE);
+                    m.unSetup();
+
                     sp_knot_set_position(cc->selected_handle, p, 0);
-//                    sp_event_context_snap_window_closed(event_context);
 
                     ConnectionPoint cp;
                     cp.type = ConnPointUserDefined;
-                    cp.pos = p * sp_item_dt2i_affine(cc->active_shape);
+                    cp.pos = p * (cc->active_shape)->dt2i_affine();
                     cp.dir = Avoid::ConnDirAll;
                     g_object_unref(cc->selected_handle);
                     cc->active_shape->avoidRef->addConnectionPoint(cp);
-                    sp_document_ensure_up_to_date(doc);
+                    doc->ensureUpToDate();
                     for (ConnectionPointMap::iterator it = cc->connpthandles.begin(); it != cc->connpthandles.end(); ++it)
                         if (it->second.type == ConnPointUserDefined && it->second.id == cp.id)
                         {
@@ -1118,7 +1143,7 @@ connector_handle_button_release(SPConnectorContext *const cc, GdkEventButton con
             }
         }
     }
-    
+
 
     return ret;
 }
@@ -1137,7 +1162,6 @@ connector_handle_key_press(SPConnectorContext *const cc, guint const keyval)
                 if (cc->npoints != 0) {
                     spcc_connector_finish(cc);
                     cc->state = SP_CONNECTOR_CONTEXT_IDLE;
-//                    sp_event_context_snap_window_closed(SP_EVENT_CONTEXT(cc));
                     ret = TRUE;
                 }
                 break;
@@ -1149,10 +1173,9 @@ connector_handle_key_press(SPConnectorContext *const cc, guint const keyval)
 
                     cc_connector_rerouting_finish(cc, NULL);
 
-                    sp_document_undo(doc);
+                    DocumentUndo::undo(doc);
 
                     cc->state = SP_CONNECTOR_CONTEXT_IDLE;
-//                    sp_event_context_snap_window_closed(SP_EVENT_CONTEXT(cc));
                     desktop->messageStack()->flash( Inkscape::NORMAL_MESSAGE,
                             _("Connector endpoint drag cancelled."));
                     ret = TRUE;
@@ -1160,7 +1183,6 @@ connector_handle_key_press(SPConnectorContext *const cc, guint const keyval)
                 else if (cc->npoints != 0) {
                     // if drawing, cancel, otherwise pass it up for deselecting
                     cc->state = SP_CONNECTOR_CONTEXT_STOP;
-//                    sp_event_context_snap_window_closed(SP_EVENT_CONTEXT(cc));
                     spcc_reset_colors(cc);
                     ret = TRUE;
                 }
@@ -1181,7 +1203,7 @@ connector_handle_key_press(SPConnectorContext *const cc, guint const keyval)
                     // Obtain original position
                     ConnectionPoint const& cp = cc->connpthandles[cc->selected_handle];
                     SPDesktop *desktop = SP_EVENT_CONTEXT_DESKTOP(cc);
-                    const Geom::Matrix& i2doc = sp_item_i2doc_affine(cc->active_shape);
+                    const Geom::Matrix& i2doc = (cc->active_shape)->i2doc_affine();
                     sp_knot_set_position(cc->selected_handle, cp.pos * i2doc * desktop->doc2dt(), 0);
                     cc->state = SP_CONNECTOR_CONTEXT_IDLE;
                     desktop->messageStack()->flash( Inkscape::NORMAL_MESSAGE,
@@ -1192,20 +1214,18 @@ connector_handle_key_press(SPConnectorContext *const cc, guint const keyval)
                 {
                     // Put connection point at current position
 
-                    SPDesktop *desktop = SP_EVENT_CONTEXT_DESKTOP(cc);
-                    SnapManager &m = desktop->namedview->snap_manager;
-                    m.setup(desktop);
                     Geom::Point p = cc->selected_handle->pos;
-                    SPEventContext* event_context = SP_EVENT_CONTEXT( cc );
-                    
+
                     if (!cc->within_tolerance)
                     {
-//                        sp_event_context_snap_window_open(event_context);
-                        m.freeSnapReturnByRef(Inkscape::SnapPreferences::SNAPPOINT_NODE, p, Inkscape::SNAPSOURCE_HANDLE);
+                        SPDesktop *desktop = SP_EVENT_CONTEXT_DESKTOP(cc);
+                        SnapManager &m = desktop->namedview->snap_manager;
+                        m.setup(desktop);
+                        m.freeSnapReturnByRef(p, Inkscape::SNAPSOURCE_OTHER_HANDLE);
+                        m.unSetup();
                         sp_knot_set_position(cc->selected_handle, p, 0);
-//                        sp_event_context_snap_window_closed(event_context);
                         ConnectionPoint& cp = cc->connpthandles[cc->selected_handle];
-                        cp.pos = p * sp_item_dt2i_affine(cc->active_shape);
+                        cp.pos = p * (cc->active_shape)->dt2i_affine();
                         cc->active_shape->avoidRef->updateConnectionPoint(cp);
                     }
 
@@ -1229,21 +1249,17 @@ connector_handle_key_press(SPConnectorContext *const cc, guint const keyval)
                     SnapManager &m = desktop->namedview->snap_manager;
                     m.setup(desktop);
                     Geom::Point p = cc->selected_handle->pos;
-                    SPEventContext* event_context = SP_EVENT_CONTEXT( cc );
-                    
-//                    sp_event_context_snap_window_open( event_context );
-                    m.freeSnapReturnByRef(Inkscape::SnapPreferences::SNAPPOINT_NODE, p, Inkscape::SNAPSOURCE_HANDLE);
-                    
+                    m.freeSnapReturnByRef(p, Inkscape::SNAPSOURCE_OTHER_HANDLE);
+                    m.unSetup();
                     sp_knot_set_position(cc->selected_handle, p, 0);
-//                    sp_event_context_snap_window_closed(event_context);
 
                     ConnectionPoint cp;
                     cp.type = ConnPointUserDefined;
-                    cp.pos = p * sp_item_dt2i_affine(cc->active_shape);
+                    cp.pos = p * (cc->active_shape)->dt2i_affine();
                     cp.dir = Avoid::ConnDirAll;
                     g_object_unref(cc->selected_handle);
                     cc->active_shape->avoidRef->addConnectionPoint(cp);
-                    sp_document_ensure_up_to_date(doc);
+                    doc->ensureUpToDate();
                     for (ConnectionPointMap::iterator it = cc->connpthandles.begin(); it != cc->connpthandles.end(); ++it)
                         if (it->second.type == ConnPointUserDefined && it->second.id == cp.id)
                         {
@@ -1263,7 +1279,7 @@ connector_handle_key_press(SPConnectorContext *const cc, guint const keyval)
                     cc->selected_handle = NULL;
                     ret = TRUE;
                 }
-                
+
                 break;
         }
     }
@@ -1285,16 +1301,17 @@ cc_connector_rerouting_finish(SPConnectorContext *const cc, Geom::Point *const p
     if (p != NULL)
     {
         // Test whether we clicked on a connection point
-        gchar *shape_label = conn_pt_handle_test(cc, *p);
+        gchar *shape_label, *cpid;
+        bool found = conn_pt_handle_test(cc, *p, &shape_label, &cpid);
 
-        if (shape_label) {
+        if (found) {
             if (cc->clickedhandle == cc->endpt_handle[0]) {
-                sp_object_setAttribute(cc->clickeditem,
-                        "inkscape:connection-start",shape_label, false);
+                cc->clickeditem->setAttribute("inkscape:connection-start", shape_label, false);
+                cc->clickeditem->setAttribute("inkscape:connection-start-point", cpid, false);
             }
             else {
-                sp_object_setAttribute(cc->clickeditem,
-                        "inkscape:connection-end",shape_label, false);
+                cc->clickeditem->setAttribute("inkscape:connection-end", shape_label, false);
+                cc->clickeditem->setAttribute("inkscape:connection-end-point", cpid, false);
             }
             g_free(shape_label);
         }
@@ -1302,7 +1319,7 @@ cc_connector_rerouting_finish(SPConnectorContext *const cc, Geom::Point *const p
     cc->clickeditem->setHidden(false);
     sp_conn_reroute_path_immediate(SP_PATH(cc->clickeditem));
     cc->clickeditem->updateRepr();
-    sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
+    DocumentUndo::done(doc, SP_VERB_CONTEXT_CONNECTOR,
                      _("Reroute connector"));
     cc_set_active_conn(cc, cc->clickeditem);
 }
@@ -1414,7 +1431,7 @@ spcc_flush_white(SPConnectorContext *cc, SPCurve *gc)
 
     SPDesktop *desktop = SP_EVENT_CONTEXT_DESKTOP(cc);
     SPDocument *doc = sp_desktop_document(desktop);
-    Inkscape::XML::Document *xml_doc = sp_document_repr_doc(doc);
+    Inkscape::XML::Document *xml_doc = doc->getReprDoc();
 
     if ( c && !c->is_empty() ) {
         /* We actually have something to write */
@@ -1430,30 +1447,34 @@ spcc_flush_white(SPConnectorContext *cc, SPCurve *gc)
 
         /* Attach repr */
         cc->newconn = SP_ITEM(desktop->currentLayer()->appendChildRepr(repr));
-        cc->newconn->transform = sp_item_i2doc_affine(SP_ITEM(desktop->currentLayer())).inverse();
+        cc->newconn->transform = SP_ITEM(desktop->currentLayer())->i2doc_affine().inverse();
 
         bool connection = false;
-        sp_object_setAttribute(cc->newconn, "inkscape:connector-type",
-                cc->isOrthogonal ? "orthogonal" : "polyline", false);
-        sp_object_setAttribute(cc->newconn, "inkscape:connector-curvature",
-                Glib::Ascii::dtostr(cc->curvature).c_str(), false);
-        if (cc->sid)
+        cc->newconn->setAttribute( "inkscape:connector-type",
+                                   cc->isOrthogonal ? "orthogonal" : "polyline", false );
+        cc->newconn->setAttribute( "inkscape:connector-curvature",
+                                   Glib::Ascii::dtostr(cc->curvature).c_str(), false );
+        if (cc->shref)
         {
-            sp_object_setAttribute(cc->newconn, "inkscape:connection-start",
-                    cc->sid, false);
+            cc->newconn->setAttribute( "inkscape:connection-start", cc->shref, false);
+            if (cc->scpid) {
+                cc->newconn->setAttribute( "inkscape:connection-start-point", cc->scpid, false);
+            }
             connection = true;
         }
 
-        if (cc->eid)
+        if (cc->ehref)
         {
-            sp_object_setAttribute(cc->newconn, "inkscape:connection-end",
-                    cc->eid, false);
+            cc->newconn->setAttribute( "inkscape:connection-end", cc->ehref, false);
+            if (cc->ecpid) {
+                cc->newconn->setAttribute( "inkscape:connection-end-point", cc->ecpid, false);
+            }
             connection = true;
         }
         // Process pending updates.
         cc->newconn->updateRepr();
-        sp_document_ensure_up_to_date(doc);
-        
+        doc->ensureUpToDate();
+
         if (connection) {
             // Adjust endpoints to shape edge.
             sp_conn_reroute_path_immediate(SP_PATH(cc->newconn));
@@ -1461,7 +1482,7 @@ spcc_flush_white(SPConnectorContext *cc, SPCurve *gc)
         }
 
         // Only set the selection after we are finished with creating the attributes of
-        // the connector.  Otherwise, the selection change may alter the defaults for 
+        // the connector.  Otherwise, the selection change may alter the defaults for
         // values like curvature in the connector context, preventing subsequent lookup
         // of their original values.
         cc->selection->set(repr);
@@ -1470,7 +1491,7 @@ spcc_flush_white(SPConnectorContext *cc, SPCurve *gc)
 
     c->unref();
 
-    sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR, _("Create connector"));
+    DocumentUndo::done(doc, SP_VERB_CONTEXT_CONNECTOR, _("Create connector"));
 }
 
 
@@ -1571,7 +1592,6 @@ endpt_handler(SPKnot */*knot*/, GdkEvent *event, SPConnectorContext *cc)
                 cc->clickedhandle = cc->active_handle;
                 cc_clear_active_conn(cc);
                 cc->state = SP_CONNECTOR_CONTEXT_REROUTING;
-//                sp_event_context_snap_window_open(SP_EVENT_CONTEXT(cc));
 
                 // Disconnect from attached shape
                 unsigned ind = (cc->active_handle == cc->endpt_handle[0]) ? 0 : 1;
@@ -1587,7 +1607,7 @@ endpt_handler(SPKnot */*knot*/, GdkEvent *event, SPConnectorContext *cc)
 
                 // Show the red path for dragging.
                 cc->red_curve = SP_PATH(cc->clickeditem)->original_curve ? SP_PATH(cc->clickeditem)->original_curve->copy() : SP_PATH(cc->clickeditem)->curve->copy();
-                Geom::Matrix i2d = sp_item_i2d_affine(cc->clickeditem);
+                Geom::Matrix i2d = (cc->clickeditem)->i2d_affine();
                 cc->red_curve->transform(i2d);
                 sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(cc->red_bpath), cc->red_curve);
 
@@ -1630,7 +1650,7 @@ static void cc_active_shape_add_knot(SPDesktop* desktop, SPItem* item, Connectio
 static void cc_set_active_shape(SPConnectorContext *cc, SPItem *item)
 {
     g_assert(item != NULL );
-    
+
     std::map<int, ConnectionPoint>* connpts = &item->avoidRef->connection_points;
 
     if (cc->active_shape != item)
@@ -1671,7 +1691,7 @@ static void cc_set_active_shape(SPConnectorContext *cc, SPItem *item)
         if ( connpts->size() )
         for (std::map<int, ConnectionPoint>::iterator it = connpts->begin(); it != connpts->end(); ++it)
             cc_active_shape_add_knot(cc->desktop, item, cc->connpthandles, it->second);
-        
+
         // Also add default connection points
         // For now, only centre default connection point will
         // be available
@@ -1687,7 +1707,7 @@ static void cc_set_active_shape(SPConnectorContext *cc, SPItem *item)
 
         // Ensure the item's connection_points map
         // has been updated
-        sp_document_ensure_up_to_date(SP_OBJECT_DOCUMENT(item));
+        item->document->ensureUpToDate();
 
         std::set<int> seen;
         for  ( ConnectionPointMap::iterator it = cc->connpthandles.begin(); it != cc->connpthandles.end() ;)
@@ -1746,16 +1766,26 @@ cc_set_active_conn(SPConnectorContext *cc, SPItem *item)
     g_assert( SP_IS_PATH(item) );
 
     SPCurve *curve = SP_PATH(item)->original_curve ? SP_PATH(item)->original_curve : SP_PATH(item)->curve;
-    Geom::Matrix i2d = sp_item_i2d_affine(item);
+    Geom::Matrix i2d = item->i2d_affine();
 
     if (cc->active_conn == item)
     {
-        // Just adjust handle positions.
-        Geom::Point startpt = *(curve->first_point()) * i2d;
-        sp_knot_set_position(cc->endpt_handle[0], startpt, 0);
+        if (curve->is_empty())
+        {
+            // Connector is invisible because it is clipped to the boundary of
+            // two overlpapping shapes.
+            sp_knot_hide(cc->endpt_handle[0]);
+            sp_knot_hide(cc->endpt_handle[1]);
+        }
+        else
+        {
+            // Just adjust handle positions.
+            Geom::Point startpt = *(curve->first_point()) * i2d;
+            sp_knot_set_position(cc->endpt_handle[0], startpt, 0);
 
-        Geom::Point endpt = *(curve->last_point()) * i2d;
-        sp_knot_set_position(cc->endpt_handle[1], endpt, 0);
+            Geom::Point endpt = *(curve->last_point()) * i2d;
+            sp_knot_set_position(cc->endpt_handle[1], endpt, 0);
+        }
 
         return;
     }
@@ -1818,6 +1848,13 @@ cc_set_active_conn(SPConnectorContext *cc, SPItem *item)
                 G_CALLBACK(endpt_handler), cc);
     }
 
+    if (curve->is_empty())
+    {
+        // Connector is invisible because it is clipped to the boundary 
+        // of two overlpapping shapes.  So, it doesn't need endpoints.
+        return;
+    }
+
     Geom::Point startpt = *(curve->first_point()) * i2d;
     sp_knot_set_position(cc->endpt_handle[0], startpt, 0);
 
@@ -1881,8 +1918,10 @@ static bool cc_item_is_shape(SPItem *item)
 bool cc_item_is_connector(SPItem *item)
 {
     if (SP_IS_PATH(item)) {
-        if (SP_PATH(item)->connEndPair.isAutoRoutingConn()) {
-            g_assert( SP_PATH(item)->original_curve ? !(SP_PATH(item)->original_curve->is_closed()) : !(SP_PATH(item)->curve->is_closed()) );
+        bool closed = SP_PATH(item)->original_curve ? SP_PATH(item)->original_curve->is_closed() : SP_PATH(item)->curve->is_closed();
+        if (SP_PATH(item)->connEndPair.isAutoRoutingConn() && !closed) {
+            // To be considered a connector, an object must be a non-closed 
+            // path that is marked with a "inkscape:connector-type" attribute.
             return true;
         }
     }
@@ -1911,8 +1950,7 @@ void cc_selection_set_avoid(bool const set_avoid)
         char const *value = (set_avoid) ? "true" : NULL;
 
         if (cc_item_is_shape(item)) {
-            sp_object_setAttribute(item, "inkscape:connector-avoid",
-                    value, false);
+            item->setAttribute("inkscape:connector-avoid", value, false);
             item->avoidRef->handleSettingChange();
             changes++;
         }
@@ -1929,7 +1967,7 @@ void cc_selection_set_avoid(bool const set_avoid)
     char *event_desc = (set_avoid) ?
             _("Make connectors avoid selected objects") :
             _("Make connectors ignore selected objects");
-    sp_document_done(document, SP_VERB_CONTEXT_CONNECTOR, event_desc);
+    DocumentUndo::done(document, SP_VERB_CONTEXT_CONNECTOR, event_desc);
 }
 
 
@@ -2013,4 +2051,4 @@ shape_event_attr_changed(Inkscape::XML::Node *repr, gchar const *name,
   fill-column:99
   End:
 */
-// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :