X-Git-Url: https://git.tokkee.org/?a=blobdiff_plain;f=src%2Fconnector-context.cpp;h=6da465f2a48691557a97d0ffb71f5bbd95bc4030;hb=66632b492f9cd54e5667fd4e1fca8e457f59b282;hp=d8e7b879710430e9cf72a80dd36b7335ed9fbecb;hpb=6129af7cc5b723223e9617614c931936e5190421;p=inkscape.git diff --git a/src/connector-context.cpp b/src/connector-context.cpp index d8e7b8797..6da465f2a 100644 --- a/src/connector-context.cpp +++ b/src/connector-context.cpp @@ -9,7 +9,7 @@ * Released under GNU GPL, read the file 'COPYING' for more information * * TODO: - * o Have shapes avoid coonvex hulls of objects, rather than their + * o Have shapes avoid convex hulls of objects, rather than their * bounding box. Possibly implement the unfinished ConvexHull * class in libnr. * (HOWEVER, using the convex hull C of a shape S does the wrong thing if a @@ -17,13 +17,14 @@ * an object involves going inside C but without entering S.) * o Draw connectors to shape edges rather than bounding box. * o Show a visual indicator for objects with the 'avoid' property set. + * o Allow user to change a object between a path and connector through + * the interface. * o Create an interface for setting markers (arrow heads). * o Better distinguish between paths and connectors to prevent problems * in the node tool and paths accidently being turned into connectors * in the connector tool. Perhaps have a way to convert between. * o Only call libavoid's updateEndPoint as required. Currently we do it * for both endpoints, even if only one is moving. - * o Cleanup to remove unecessary borrowed DrawContext code. * o Allow user-placeable connection points. * o Deal sanely with connectors with both endpoints attached to the * same connection point, and drawing of connectors attaching @@ -41,6 +42,8 @@ */ #include +#include +#include #include "connector-context.h" #include "pixmaps/cursor-connector.xpm" @@ -67,7 +70,9 @@ #include "conn-avoid-ref.h" #include "libavoid/vertices.h" #include "context-fns.h" - +#include "sp-namedview.h" +#include "sp-text.h" +#include "sp-flowtext.h" static void sp_connector_context_class_init(SPConnectorContextClass *klass); @@ -101,6 +106,8 @@ static void cc_clear_active_conn(SPConnectorContext *cc); static gchar *conn_pt_handle_test(SPConnectorContext *cc, NR::Point& w); static bool cc_item_is_shape(SPItem *item); static void cc_selection_changed(Inkscape::Selection *selection, gpointer data); +static void cc_connector_rerouting_finish(SPConnectorContext *const cc, + NR::Point *const p); static void shape_event_attr_deleted(Inkscape::XML::Node *repr, Inkscape::XML::Node *child, Inkscape::XML::Node *ref, gpointer data); @@ -383,19 +390,19 @@ conn_pt_handle_test(SPConnectorContext *cc, NR::Point& p) static gint -sp_connector_context_item_handler(SPEventContext *ec, SPItem *item, GdkEvent *event) +sp_connector_context_item_handler(SPEventContext *event_context, SPItem *item, GdkEvent *event) { gint ret = FALSE; - SPDesktop *desktop = ec->desktop; + SPDesktop *desktop = event_context->desktop; - SPConnectorContext *cc = SP_CONNECTOR_CONTEXT(ec); + SPConnectorContext *cc = SP_CONNECTOR_CONTEXT(event_context); NR::Point p(event->button.x, event->button.y); switch (event->type) { case GDK_BUTTON_RELEASE: - if (event->button.button == 1) { + if (event->button.button == 1 && !event_context->space_panning) { if ((cc->state == SP_CONNECTOR_CONTEXT_DRAGGING) && (connector_within_tolerance)) { @@ -484,9 +491,10 @@ connector_handle_button_press(SPConnectorContext *const cc, GdkEventButton const NR::Point const event_w(bevent.x, bevent.y); /* Find desktop coordinates */ NR::Point p = cc->desktop->w2d(event_w); + SPEventContext *event_context = SP_EVENT_CONTEXT(cc); gint ret = FALSE; - if ( bevent.button == 1 ) { + if ( bevent.button == 1 && !event_context->space_panning ) { SPDesktop *desktop = SP_EVENT_CONTEXT_DESKTOP(cc); @@ -522,8 +530,8 @@ connector_handle_button_press(SPConnectorContext *const cc, GdkEventButton const if (!cc->sid) { // This is the first point, so just snap it to the grid // as there's no other points to go off. - SnapManager const m(cc->desktop->namedview); - p = m.freeSnap(Inkscape::Snapper::SNAP_POINT | Inkscape::Snapper::BBOX_POINT, + SnapManager const &m = cc->desktop->namedview->snap_manager; + p = m.freeSnap(Inkscape::Snapper::SNAPPOINT_NODE, p, NULL).getPoint(); } spcc_connector_set_initial_point(cc, p); @@ -558,7 +566,17 @@ connector_handle_button_press(SPConnectorContext *const cc, GdkEventButton const break; } } else if (bevent.button == 3) { - if (cc->npoints != 0) { + if (cc->state == SP_CONNECTOR_CONTEXT_REROUTING) { + // A context menu is going to be triggered here, + // so end the rerouting operation. + cc_connector_rerouting_finish(cc, &p); + + cc->state = SP_CONNECTOR_CONTEXT_IDLE; + + // Don't set ret to TRUE, so we drop through to the + // parent handler which will open the context menu. + } + else if (cc->npoints != 0) { spcc_connector_finish(cc); ret = TRUE; } @@ -571,8 +589,9 @@ static gint connector_handle_motion_notify(SPConnectorContext *const cc, GdkEventMotion const &mevent) { gint ret = FALSE; + SPEventContext *event_context = SP_EVENT_CONTEXT(cc); - if (mevent.state & GDK_BUTTON2_MASK || mevent.state & GDK_BUTTON3_MASK) { + if (event_context->space_panning || mevent.state & GDK_BUTTON2_MASK || mevent.state & GDK_BUTTON3_MASK) { // allow middle-button scrolling return FALSE; } @@ -650,7 +669,8 @@ static gint connector_handle_button_release(SPConnectorContext *const cc, GdkEventButton const &revent) { gint ret = FALSE; - if ( revent.button == 1 ) { + SPEventContext *event_context = SP_EVENT_CONTEXT(cc); + if ( revent.button == 1 && !event_context->space_panning ) { SPDesktop *desktop = SP_EVENT_CONTEXT_DESKTOP(cc); SPDocument *doc = sp_desktop_document(desktop); @@ -683,29 +703,8 @@ connector_handle_button_release(SPConnectorContext *const cc, GdkEventButton con } case SP_CONNECTOR_CONTEXT_REROUTING: { - // Clear the temporary path: - sp_curve_reset(cc->red_curve); - sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(cc->red_bpath), NULL); - - // Test whether we clicked on a connection point - gchar *shape_label = conn_pt_handle_test(cc, p); - - if (shape_label) { - if (cc->clickedhandle == cc->endpt_handle[0]) { - sp_object_setAttribute(cc->clickeditem, - "inkscape:connection-start",shape_label, false); - } - else { - sp_object_setAttribute(cc->clickeditem, - "inkscape:connection-end",shape_label, false); - } - g_free(shape_label); - } - cc->clickeditem->setHidden(false); - sp_conn_adjust_path(SP_PATH(cc->clickeditem)); - cc->clickeditem->updateRepr(); - sp_document_done(doc); - cc_set_active_conn(cc, cc->clickeditem); + cc_connector_rerouting_finish(cc, &p); + sp_document_ensure_up_to_date(doc); cc->state = SP_CONNECTOR_CONTEXT_IDLE; return TRUE; @@ -738,7 +737,21 @@ connector_handle_key_press(SPConnectorContext *const cc, guint const keyval) } break; case GDK_Escape: - if (cc->npoints != 0) { + if (cc->state == SP_CONNECTOR_CONTEXT_REROUTING) { + + SPDesktop *desktop = SP_EVENT_CONTEXT_DESKTOP(cc); + SPDocument *doc = sp_desktop_document(desktop); + + cc_connector_rerouting_finish(cc, NULL); + + sp_document_undo(doc); + + cc->state = SP_CONNECTOR_CONTEXT_IDLE; + desktop->messageStack()->flash( Inkscape::NORMAL_MESSAGE, + _("Connector endpoint drag cancelled.")); + ret = TRUE; + } + else if (cc->npoints != 0) { // if drawing, cancel, otherwise pass it up for deselecting cc->state = SP_CONNECTOR_CONTEXT_STOP; spcc_reset_colors(cc); @@ -752,6 +765,42 @@ connector_handle_key_press(SPConnectorContext *const cc, guint const keyval) } +static void +cc_connector_rerouting_finish(SPConnectorContext *const cc, NR::Point *const p) +{ + SPDesktop *desktop = SP_EVENT_CONTEXT_DESKTOP(cc); + SPDocument *doc = sp_desktop_document(desktop); + + // Clear the temporary path: + sp_curve_reset(cc->red_curve); + sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(cc->red_bpath), NULL); + + if (p != NULL) + { + // Test whether we clicked on a connection point + gchar *shape_label = conn_pt_handle_test(cc, *p); + + if (shape_label) { + if (cc->clickedhandle == cc->endpt_handle[0]) { + sp_object_setAttribute(cc->clickeditem, + "inkscape:connection-start",shape_label, false); + } + else { + sp_object_setAttribute(cc->clickeditem, + "inkscape:connection-end",shape_label, false); + } + g_free(shape_label); + } + } + cc->clickeditem->setHidden(false); + sp_conn_adjust_path(SP_PATH(cc->clickeditem)); + cc->clickeditem->updateRepr(); + sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR, + _("Reroute connector")); + cc_set_active_conn(cc, cc->clickeditem); +} + + static void spcc_reset_colors(SPConnectorContext *cc) { @@ -784,8 +833,8 @@ spcc_connector_set_subsequent_point(SPConnectorContext *const cc, NR::Point cons SPDesktop *dt = cc->desktop; NR::Point o = dt->dt2doc(cc->p[0]); NR::Point d = dt->dt2doc(p); - Avoid::Point src = { o[NR::X], o[NR::Y] }; - Avoid::Point dst = { d[NR::X], d[NR::Y] }; + Avoid::Point src(o[NR::X], o[NR::Y]); + Avoid::Point dst(d[NR::X], d[NR::Y]); if (!cc->newConnRef) { Avoid::Router *router = sp_desktop_document(dt)->router; @@ -864,11 +913,12 @@ 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); if ( c && !sp_curve_empty(c) ) { /* We actually have something to write */ - Inkscape::XML::Node *repr = sp_repr_new("svg:path"); + Inkscape::XML::Node *repr = xml_doc->createElement("svg:path"); /* Set style */ sp_desktop_apply_style_tool(desktop, repr, "tools.connector", false); @@ -911,13 +961,13 @@ spcc_flush_white(SPConnectorContext *cc, SPCurve *gc) sp_curve_unref(c); /* Flush pending updates */ - sp_document_done(doc); + sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR, _("Create connector")); sp_document_ensure_up_to_date(doc); } static void -spcc_connector_finish_segment(SPConnectorContext *const cc, NR::Point const p) +spcc_connector_finish_segment(SPConnectorContext *const cc, NR::Point const /*p*/) { if (!sp_curve_empty(cc->red_curve)) { sp_curve_append_continuous(cc->green_curve, cc->red_curve, 0.0625); @@ -955,6 +1005,8 @@ static gboolean cc_generic_knot_handler(SPCanvasItem *, GdkEvent *event, SPKnot *knot) { g_assert (knot != NULL); + + g_object_ref(knot); SPConnectorContext *cc = SP_CONNECTOR_CONTEXT( knot->desktop->event_context); @@ -963,11 +1015,8 @@ cc_generic_knot_handler(SPCanvasItem *, GdkEvent *event, SPKnot *knot) switch (event->type) { case GDK_ENTER_NOTIFY: - gtk_object_set (GTK_OBJECT (knot->item), "fill_color", - knot->fill [SP_KNOT_STATE_MOUSEOVER], NULL); - gtk_object_set (GTK_OBJECT (knot->item), "stroke_color", - knot->stroke [SP_KNOT_STATE_MOUSEOVER], NULL); - + sp_knot_set_flag(knot, SP_KNOT_MOUSEOVER, TRUE); + cc->active_handle = knot; if (knot->tip) @@ -975,33 +1024,32 @@ cc_generic_knot_handler(SPCanvasItem *, GdkEvent *event, SPKnot *knot) knot->desktop->event_context->defaultMessageContext()->set( Inkscape::NORMAL_MESSAGE, knot->tip); } - + consumed = TRUE; break; case GDK_LEAVE_NOTIFY: - gtk_object_set (GTK_OBJECT (knot->item), "fill_color", - knot->fill [SP_KNOT_STATE_NORMAL], NULL); - gtk_object_set (GTK_OBJECT (knot->item), "stroke_color", - knot->stroke [SP_KNOT_STATE_NORMAL], NULL); + sp_knot_set_flag(knot, SP_KNOT_MOUSEOVER, FALSE); cc->active_handle = NULL; - + if (knot->tip) { knot->desktop->event_context->defaultMessageContext()->clear(); } - + consumed = TRUE; break; default: break; } + + g_object_unref(knot); return consumed; } static gboolean -endpt_handler(SPKnot *knot, GdkEvent *event, SPConnectorContext *cc) +endpt_handler(SPKnot */*knot*/, GdkEvent *event, SPConnectorContext *cc) { g_assert( SP_IS_CONNECTOR_CONTEXT(cc) ); @@ -1080,43 +1128,36 @@ static void cc_set_active_shape(SPConnectorContext *cc, SPItem *item) // Set center connection point. if ( cc->connpthandle == NULL ) { - SPKnot * knot = (SPKnot*)g_object_new (SP_TYPE_KNOT, 0); - - knot->desktop = cc->desktop; - knot->flags = SP_KNOT_VISIBLE; + SPKnot *knot = sp_knot_new(cc->desktop, + _("Connection point: click or drag to create a new connector")); - knot->item = sp_canvas_item_new (sp_desktop_controls(cc->desktop), - SP_TYPE_CTRL, - "anchor", GTK_ANCHOR_CENTER, - "filled", TRUE, - "stroked", TRUE, - "mode", SP_KNOT_MODE_XOR, - NULL); + knot->setShape(SP_KNOT_SHAPE_SQUARE); + knot->setSize(8); + knot->setAnchor(GTK_ANCHOR_CENTER); + knot->setFill(0xffffff00, 0xff0000ff, 0xff0000ff); + sp_knot_update_ctrl(knot); - gtk_signal_connect (GTK_OBJECT (knot->item), "event", - GTK_SIGNAL_FUNC (cc_generic_knot_handler), knot); + // We don't want to use the standard knot handler, + //since we don't want this knot to be draggable. + g_signal_handler_disconnect(G_OBJECT(knot->item), + knot->_event_handler_id); + knot->_event_handler_id = 0; - knot->fill [SP_KNOT_STATE_NORMAL] = 0xffffff00; - knot->fill [SP_KNOT_STATE_MOUSEOVER] = 0xff0000ff; - knot->stroke [SP_KNOT_STATE_NORMAL] = 0x01000000; - - g_object_set(G_OBJECT(knot), - "shape", SP_KNOT_SHAPE_SQUARE, - "size", 8, - "anchor", GTK_ANCHOR_CENTER, - "tip", _("Connection point: click or drag to create a new connector"), - NULL); + gtk_signal_connect(GTK_OBJECT(knot->item), "event", + GTK_SIGNAL_FUNC(cc_generic_knot_handler), knot); cc->connpthandle = knot; } - NR::Rect bbox = sp_item_bbox_desktop(cc->active_shape); - NR::Point center = bbox.midpoint(); - sp_knot_set_position(cc->connpthandle, ¢er, 0); - - sp_knot_show(cc->connpthandle); - + NR::Maybe bbox = sp_item_bbox_desktop(cc->active_shape); + if (bbox) { + NR::Point center = bbox->midpoint(); + sp_knot_set_position(cc->connpthandle, ¢er, 0); + sp_knot_show(cc->connpthandle); + } else { + sp_knot_hide(cc->connpthandle); + } } @@ -1160,32 +1201,24 @@ cc_set_active_conn(SPConnectorContext *cc, SPItem *item) // Create the handle if it doesn't exist if ( cc->endpt_handle[i] == NULL ) { - SPKnot * knot = (SPKnot*) g_object_new (SP_TYPE_KNOT, 0); + SPKnot *knot = sp_knot_new(cc->desktop, + _("Connector endpoint: drag to reroute or connect to new shapes")); - knot->desktop = cc->desktop; - knot->flags = SP_KNOT_VISIBLE; + knot->setShape(SP_KNOT_SHAPE_SQUARE); + knot->setSize(7); + knot->setAnchor(GTK_ANCHOR_CENTER); + knot->setFill(0xffffff00, 0xff0000ff, 0xff0000ff); + knot->setStroke(0x000000ff, 0x000000ff, 0x000000ff); + sp_knot_update_ctrl(knot); - knot->item = sp_canvas_item_new (sp_desktop_controls (cc->desktop), - SP_TYPE_CTRL, - "anchor", GTK_ANCHOR_CENTER, - "filled", TRUE, - "stroked", TRUE, - "mode", SP_KNOT_MODE_XOR, - NULL); + // We don't want to use the standard knot handler, + //since we don't want this knot to be draggable. + g_signal_handler_disconnect(G_OBJECT(knot->item), + knot->_event_handler_id); + knot->_event_handler_id = 0; - knot->fill [SP_KNOT_STATE_NORMAL] = 0xffffff00; - knot->stroke [SP_KNOT_STATE_NORMAL] = 0x000000ff; - knot->stroke [SP_KNOT_STATE_DRAGGING] = 0x000000ff; - knot->stroke [SP_KNOT_STATE_MOUSEOVER] = 0x000000ff; - - g_object_set(G_OBJECT(knot), - "shape", SP_KNOT_SHAPE_DIAMOND, - "size", 10, - "tip", _("Connector endpoint: drag to reroute or connect to new shapes"), - NULL); - - gtk_signal_connect (GTK_OBJECT (knot->item), "event", - GTK_SIGNAL_FUNC (cc_generic_knot_handler), knot); + gtk_signal_connect(GTK_OBJECT(knot->item), "event", + GTK_SIGNAL_FUNC(cc_generic_knot_handler), knot); cc->endpt_handle[i] = knot; } @@ -1226,6 +1259,12 @@ static bool cc_item_is_shape(SPItem *item) return false; } } + else if (SP_IS_TEXT(item) || SP_IS_FLOWTEXT(item)) { + if (prefs_get_int_attribute("tools.connector", "ignoretext", 1) == 1) { + // Don't count text as a shape we can connect connector to. + return false; + } + } return true; } @@ -1278,7 +1317,10 @@ void cc_selection_set_avoid(bool const set_avoid) return; } - sp_document_done(document); + 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); } @@ -1308,8 +1350,8 @@ cc_selection_changed(Inkscape::Selection *selection, gpointer data) static void -shape_event_attr_deleted(Inkscape::XML::Node *repr, Inkscape::XML::Node *child, - Inkscape::XML::Node *ref, gpointer data) +shape_event_attr_deleted(Inkscape::XML::Node */*repr*/, Inkscape::XML::Node *child, + Inkscape::XML::Node */*ref*/, gpointer data) { g_assert(data); SPConnectorContext *cc = SP_CONNECTOR_CONTEXT(data); @@ -1323,8 +1365,8 @@ shape_event_attr_deleted(Inkscape::XML::Node *repr, Inkscape::XML::Node *child, static void shape_event_attr_changed(Inkscape::XML::Node *repr, gchar const *name, - gchar const *old_value, gchar const *new_value, - bool is_interactive, gpointer data) + gchar const */*old_value*/, gchar const */*new_value*/, + bool /*is_interactive*/, gpointer data) { g_assert(data); SPConnectorContext *cc = SP_CONNECTOR_CONTEXT(data);