~ubuntu-branches/ubuntu/oneiric/inkscape/oneiric-updates

« back to all changes in this revision

Viewing changes to src/connector-context.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Alex Valavanis
  • Date: 2010-09-12 19:44:58 UTC
  • mfrom: (1.1.12 upstream) (45.1.3 maverick)
  • Revision ID: james.westby@ubuntu.com-20100912194458-4sjwmbl7dlsrk5dc
Tags: 0.48.0-1ubuntu1
* Merge with Debian unstable (LP: #628048, LP: #401567, LP: #456248, 
  LP: #463602, LP: #591986)
* debian/control: 
  - Ubuntu maintainers
  - Promote python-lxml, python-numpy, python-uniconvertor to Recommends.
  - Demote pstoedit to Suggests (universe package).
  - Suggests ttf-dejavu instead of ttf-bitstream-vera (LP: #513319)
* debian/rules:
  - Run intltool-update on build (Ubuntu-specific).
  - Add translation domain to .desktop files (Ubuntu-specific).
* debian/dirs:
  - Add usr/share/pixmaps.  Allow inkscape.xpm installation
* drop 50-poppler-API.dpatch (now upstream)
* drop 51-paste-in-unwritable-directory.dpatch (now upstream) 

Show diffs side-by-side

added added

removed removed

Lines of Context:
4
4
 * Authors:
5
5
 *   Michael Wybrow <mjwybrow@users.sourceforge.net>
6
6
 *
7
 
 * Copyright (C) 2005 Michael Wybrow
 
7
 * Copyright (C) 2005-2008  Michael Wybrow
 
8
 * Copyright (C) 2009  Monash University
8
9
 *
9
10
 * Released under GNU GPL, read the file 'COPYING' for more information
10
11
 *
11
12
 * TODO:
12
 
 *  o  Have shapes avoid convex hulls of objects, rather than their
13
 
 *     bounding box.  Possibly implement the unfinished ConvexHull
14
 
 *     class in libnr.
15
 
 *     (HOWEVER, using the convex hull C of a shape S does the wrong thing if a
16
 
 *     connector starts outside of S but inside C, or if the best route around
17
 
 *     an object involves going inside C but without entering S.)
18
 
 *  o  Draw connectors to shape edges rather than bounding box.
19
13
 *  o  Show a visual indicator for objects with the 'avoid' property set.
20
14
 *  o  Allow user to change a object between a path and connector through
21
15
 *     the interface.
22
16
 *  o  Create an interface for setting markers (arrow heads).
23
17
 *  o  Better distinguish between paths and connectors to prevent problems
24
 
 *     in the node tool and paths accidently being turned into connectors
 
18
 *     in the node tool and paths accidentally being turned into connectors
25
19
 *     in the connector tool.  Perhaps have a way to convert between.
26
20
 *  o  Only call libavoid's updateEndPoint as required.  Currently we do it
27
21
 *     for both endpoints, even if only one is moving.
28
22
 *  o  Allow user-placeable connection points.
29
23
 *  o  Deal sanely with connectors with both endpoints attached to the
30
24
 *     same connection point, and drawing of connectors attaching
31
 
 *     overlaping shapes (currently tries to adjust connector to be
 
25
 *     overlapping shapes (currently tries to adjust connector to be
32
26
 *     outside both bounding boxes).
33
27
 *  o  Fix many special cases related to connectors updating,
34
28
 *     e.g., copying a couple of shapes and a connector that are
37
31
 *           one of the other contexts.
38
32
 *  o  Cope with shapes whose ids change when they have attached
39
33
 *     connectors.
40
 
 *  o  gobble_motion_events(GDK_BUTTON1_MASK)?;
 
34
 *  o  During dragging motion, gobble up to and use the final motion event.
 
35
 *     Gobbling away all duplicates after the current can occasionally result
 
36
 *     in the path lagging behind the mouse cursor if it is no longer being
 
37
 *     dragged.
 
38
 *  o  Fix up libavoid's representation after undo actions.  It doesn't see
 
39
 *     any transform signals and hence doesn't know shapes have moved back to
 
40
 *     there earlier positions.
 
41
 *  o  Decide whether drawing/editing mode should be an Inkscape preference
 
42
 *     or the connector tool should always start in drawing mode.
 
43
 *  o  Correct the problem with switching to the select tool when pressing
 
44
 *     space bar (there are moments when it refuses to do so).
 
45
 *
 
46
 * ----------------------------------------------------------------------------
 
47
 *
 
48
 * mjwybrow's observations on acracan's Summer of Code connector work:
 
49
 *
 
50
 *  -  GUI comments:
 
51
 *
 
52
 *      -  Buttons for adding and removing user-specified connection
 
53
 *      points should probably have "+" and "-" symbols on them so they
 
54
 *      are consistent with the similar buttons for the node tool.
 
55
 *      -  Controls on the connector tool be should be reordered logically,
 
56
 *      possibly as follows:
 
57
 *
 
58
 *      *Connector*: [Polyline-radio-button] [Orthgonal-radio-button]
 
59
 *        [Curvature-control] | *Shape*: [Avoid-button] [Dont-avoid-button]
 
60
 *        [Spacing-control] | *Connection pts*: [Edit-mode] [Add-pt] [Rm-pt]
 
61
 *
 
62
 *      I think that the network layout controls be moved to the
 
63
 *      Align and Distribute dialog (there is already the layout button
 
64
 *      there, but no options are exposed).
 
65
 *
 
66
 *      I think that the style change between polyline and orthogonal
 
67
 *      would be much clearer with two buttons (radio behaviour -- just
 
68
 *      one is true).
 
69
 *
 
70
 *      The other tools show a label change from "New:" to "Change:"
 
71
 *      depending on whether an object is selected.  We could consider
 
72
 *      this but there may not be space.
 
73
 *
 
74
 *      The Add-pt and Rm-pt buttons should be greyed out (inactive) if
 
75
 *      we are not in connection point editing mode.  And probably also
 
76
 *      if there is no shape selected, i.e. at the times they have no
 
77
 *      effect when clicked.
 
78
 *
 
79
 *      Likewise for the avoid/ignore shapes buttons.  These should be
 
80
 *      inactive when a shape is not selected in the connector context.
 
81
 *
 
82
 *  -  When creating/editing connection points:
 
83
 *
 
84
 *      -  Strange things can happen if you have connectors selected, or
 
85
 *      try rerouting connectors by dragging their endpoints when in
 
86
 *      connection point editing mode.
 
87
 *
 
88
 *      -  Possibly the selected shape's connection points should always
 
89
 *      be shown (i.e., have knots) when in editing mode.
 
90
 *
 
91
 *      -  It is a little strange to be able to place connection points
 
92
 *      competely outside shapes.  Especially when you later can't draw
 
93
 *      connectors to them since the knots are only visible when you
 
94
 *      are over the shape.  I think that you should only be able to
 
95
 *      place connection points inside or on the boundary of the shape
 
96
 *      itself.
 
97
 *
 
98
 *      -  The intended ability to place a new point at the current cursor
 
99
 *      position by pressing RETURN does not seem to work.
 
100
 *
 
101
 *      -  The Status bar tooltip should change to reflect editing mode
 
102
 *      and tell the user about RETURN and how to use the tool.
 
103
 *
 
104
 *  -  Connection points general:
 
105
 *
 
106
 *      -  Connection points that were inside the shape can end up outside
 
107
 *      after a rotation is applied to the shape in the select tool.
 
108
 *      It doesn't seem like the correct transform is being applied to
 
109
 *      these, or it is being applied at the wrong time.  I'd expect
 
110
 *      connection points to rotate with the shape, and stay at the
 
111
 *      same position "on the shape"
 
112
 *
 
113
 *      -  I was able to make the connectors attached to a shape fall off
 
114
 *      the shape after scaling it.  Not sure the exact cause, but may
 
115
 *      require more investigation/debugging.
 
116
 *
 
117
 *      -  The user-defined connection points should be either absolute
 
118
 *      (as the current ones are) or defined as a percentage of the
 
119
 *      shape.  These would be based on a toggle setting on the
 
120
 *      toolbar, and they would be placed in exactly the same way by
 
121
 *      the user.  The only difference would be that they would be
 
122
 *      store as percentage positions in the SVG connection-points
 
123
 *      property and that they would update/move automatically if the
 
124
 *      object was resized or scaled.
 
125
 *
 
126
 *      -  Thinking more, I think you always want to store and think about
 
127
 *      the positions of connection points to be pre-transform, but
 
128
 *      obviously the shape transform is applied to them.  That way,
 
129
 *      they will rotate and scale automatically with the shape, when
 
130
 *      the shape transform is altered.  The Percentage version would
 
131
 *      compute their position from the pre-transform dimensions and
 
132
 *      then have the transform applied to them, for example.
 
133
 *
 
134
 *      -  The connection points in the test_connection_points.svg file
 
135
 *      seem to follow the shape when it is moved, but connection
 
136
 *      points I add to new shapes, do not follow the shape, either
 
137
 *      when the shape is just moved or transformed.  There is
 
138
 *      something wrong here.  What exactly should the behaviour be
 
139
 *      currently?
 
140
 *
 
141
 *      -  I see that connection points are specified at absolute canvas
 
142
 *      positions.  I really think that they should be specified in
 
143
 *      shape coordinated relative to the shapes.  There may be
 
144
 *      transforms applied to layers and the canvas which would make
 
145
 *      specifying them quite difficult.  I'd expect a position of 0, 0
 
146
 *      to be on the shape in question or very close to it, for example.
41
147
 *
42
148
 */
43
149
 
 
150
 
 
151
 
44
152
#include <gdk/gdkkeysyms.h>
45
153
#include <string>
46
154
#include <cstring>
47
155
 
48
156
#include "connector-context.h"
49
157
#include "pixmaps/cursor-connector.xpm"
 
158
#include "pixmaps/cursor-node.xpm"
 
159
//#include "pixmaps/cursor-node-m.xpm"
 
160
//#include "pixmaps/cursor-node-d.xpm"
50
161
#include "xml/node-event-vector.h"
51
162
#include "xml/repr.h"
52
163
#include "svg/svg.h"
63
174
#include "display/canvas-bpath.h"
64
175
#include "display/sodipodi-ctrl.h"
65
176
#include <glibmm/i18n.h>
 
177
#include <glibmm/stringutils.h>
66
178
#include "snap.h"
67
179
#include "knot.h"
68
180
#include "sp-conn-end.h"
 
181
#include "sp-conn-end-pair.h"
69
182
#include "conn-avoid-ref.h"
70
183
#include "libavoid/vertices.h"
 
184
#include "libavoid/router.h"
71
185
#include "context-fns.h"
72
186
#include "sp-namedview.h"
73
187
#include "sp-text.h"
79
193
static void sp_connector_context_dispose(GObject *object);
80
194
 
81
195
static void sp_connector_context_setup(SPEventContext *ec);
 
196
static void sp_connector_context_set(SPEventContext *ec, Inkscape::Preferences::Entry *val);
82
197
static void sp_connector_context_finish(SPEventContext *ec);
83
198
static gint sp_connector_context_root_handler(SPEventContext *ec, GdkEvent *event);
84
199
static gint sp_connector_context_item_handler(SPEventContext *event_context, SPItem *item, GdkEvent *event);
98
213
static gint connector_handle_button_release(SPConnectorContext *const cc, GdkEventButton const &revent);
99
214
static gint connector_handle_key_press(SPConnectorContext *const cc, guint const keyval);
100
215
 
 
216
static void cc_active_shape_add_knot(SPDesktop* desktop, SPItem* item, ConnectionPointMap &cphandles, ConnectionPoint& cp);
101
217
static void cc_set_active_shape(SPConnectorContext *cc, SPItem *item);
102
218
static void cc_clear_active_shape(SPConnectorContext *cc);
103
219
static void cc_set_active_conn(SPConnectorContext *cc, SPItem *item);
104
220
static void cc_clear_active_conn(SPConnectorContext *cc);
105
 
static gchar *conn_pt_handle_test(SPConnectorContext *cc, Geom::Point& w);
 
221
static bool conn_pt_handle_test(SPConnectorContext *cc, Geom::Point& p, gchar **href, gchar **cpid);
 
222
static void cc_select_handle(SPKnot* knot);
 
223
static void cc_deselect_handle(SPKnot* knot);
106
224
static bool cc_item_is_shape(SPItem *item);
107
225
static void cc_selection_changed(Inkscape::Selection *selection, gpointer data);
108
226
static void cc_connector_rerouting_finish(SPConnectorContext *const cc,
115
233
        gpointer data);
116
234
 
117
235
 
118
 
static Geom::Point connector_drag_origin_w(0, 0);
119
 
static bool connector_within_tolerance = false;
 
236
static char* cc_knot_tips[] = { _("<b>Connection point</b>: click or drag to create a new connector"),
 
237
                           _("<b>Connection point</b>: click to select, drag to move") };
 
238
 
 
239
/*static Geom::Point connector_drag_origin_w(0, 0);
 
240
static bool connector_within_tolerance = false;*/
120
241
static SPEventContextClass *parent_class;
121
242
 
122
243
 
171
292
    object_class->dispose = sp_connector_context_dispose;
172
293
 
173
294
    event_context_class->setup = sp_connector_context_setup;
 
295
    event_context_class->set = sp_connector_context_set;
174
296
    event_context_class->finish = sp_connector_context_finish;
175
297
    event_context_class->root_handler = sp_connector_context_root_handler;
176
298
    event_context_class->item_handler = sp_connector_context_item_handler;
188
310
    ec->xp = 0;
189
311
    ec->yp = 0;
190
312
 
 
313
    cc->mode = SP_CONNECTOR_CONTEXT_DRAWING_MODE;
 
314
    cc->knot_tip = 0;
 
315
 
191
316
    cc->red_color = 0xff00007f;
192
317
 
193
318
    cc->newconn = NULL;
194
319
    cc->newConnRef = NULL;
 
320
    cc->curvature = 0.0;
195
321
 
196
322
    cc->sel_changed_connection = sigc::connection();
197
323
 
204
330
 
205
331
    cc->active_handle = NULL;
206
332
 
 
333
    cc->selected_handle = NULL;
 
334
 
207
335
    cc->clickeditem = NULL;
208
336
    cc->clickedhandle = NULL;
209
337
 
210
 
    cc->connpthandle = NULL;
 
338
    new (&cc->connpthandles) ConnectionPointMap();
 
339
 
211
340
    for (int i = 0; i < 2; ++i) {
212
341
        cc->endpt_handle[i] = NULL;
213
342
        cc->endpt_handler_id[i] = 0;
214
343
    }
215
 
    cc->sid = NULL;
216
 
    cc->eid = NULL;
 
344
    cc->shref = NULL;
 
345
    cc->scpid = NULL;
 
346
    cc->ehref = NULL;
 
347
    cc->ecpid = NULL;
217
348
    cc->npoints = 0;
218
349
    cc->state = SP_CONNECTOR_CONTEXT_IDLE;
219
350
}
226
357
 
227
358
    cc->sel_changed_connection.disconnect();
228
359
 
229
 
    if (cc->connpthandle) {
230
 
        g_object_unref(cc->connpthandle);
231
 
        cc->connpthandle = NULL;
 
360
    if (!cc->connpthandles.empty()) {
 
361
        for (ConnectionPointMap::iterator it = cc->connpthandles.begin();
 
362
                it != cc->connpthandles.end(); ++it) {
 
363
            g_object_unref(it->first);
 
364
        }
 
365
        cc->connpthandles.clear();
232
366
    }
 
367
    cc->connpthandles.~ConnectionPointMap();
233
368
    for (int i = 0; i < 2; ++i) {
234
369
        if (cc->endpt_handle[1]) {
235
370
            g_object_unref(cc->endpt_handle[i]);
236
371
            cc->endpt_handle[i] = NULL;
237
372
        }
238
373
    }
239
 
    if (cc->sid) {
240
 
        g_free(cc->sid);
241
 
        cc->sid = NULL;
242
 
    }
243
 
    if (cc->eid) {
244
 
        g_free(cc->eid);
245
 
        cc->eid = NULL;
 
374
    if (cc->shref) {
 
375
        g_free(cc->shref);
 
376
        cc->shref = NULL;
 
377
    }
 
378
    if (cc->scpid) {
 
379
        g_free(cc->scpid);
 
380
        cc->scpid = NULL;
 
381
    }
 
382
    if (cc->ehref) {
 
383
        g_free(cc->shref);
 
384
        cc->shref = NULL;
 
385
    }
 
386
    if (cc->ecpid) {
 
387
        g_free(cc->scpid);
 
388
        cc->scpid = NULL;
246
389
    }
247
390
    g_assert( cc->newConnRef == NULL );
248
391
 
282
425
    // Notice the initial selection.
283
426
    cc_selection_changed(cc->selection, (gpointer) cc);
284
427
 
 
428
    cc->within_tolerance = false;
 
429
 
 
430
    sp_event_context_read(ec, "curvature");
 
431
    sp_event_context_read(ec, "orthogonal");
 
432
    sp_event_context_read(ec, "mode");
 
433
    cc->knot_tip = cc->mode == SP_CONNECTOR_CONTEXT_DRAWING_MODE ? cc_knot_tips[0] : cc_knot_tips[1];
285
434
    Inkscape::Preferences *prefs = Inkscape::Preferences::get();
286
435
    if (prefs->getBool("/tools/connector/selcue", 0)) {
287
436
        ec->enableSelectionCue();
294
443
 
295
444
 
296
445
static void
 
446
sp_connector_context_set(SPEventContext *ec, Inkscape::Preferences::Entry *val)
 
447
{
 
448
    SPConnectorContext *cc = SP_CONNECTOR_CONTEXT(ec);
 
449
 
 
450
    /* fixme: Proper error handling for non-numeric data.  Use a locale-independent function like
 
451
     * g_ascii_strtod (or a thin wrapper that does the right thing for invalid values inf/nan). */
 
452
    Glib::ustring name = val->getEntryName();
 
453
    if ( name == "curvature" ) {
 
454
        cc->curvature = val->getDoubleLimited(); // prevents NaN and +/-Inf from messing up
 
455
    }
 
456
    else if ( name == "orthogonal" ) {
 
457
        cc->isOrthogonal = val->getBool();
 
458
    }
 
459
    else if ( name == "mode")
 
460
    {
 
461
        sp_connector_context_switch_mode(ec, val->getBool() ? SP_CONNECTOR_CONTEXT_EDITING_MODE : SP_CONNECTOR_CONTEXT_DRAWING_MODE);
 
462
    }
 
463
}
 
464
 
 
465
void sp_connector_context_switch_mode(SPEventContext* ec, unsigned int newMode)
 
466
{
 
467
    SPConnectorContext *cc = SP_CONNECTOR_CONTEXT(ec);
 
468
 
 
469
    cc->mode = newMode;
 
470
    if ( cc->mode == SP_CONNECTOR_CONTEXT_DRAWING_MODE )
 
471
    {
 
472
        ec->cursor_shape = cursor_connector_xpm;
 
473
        cc->knot_tip = cc_knot_tips[0];
 
474
        if (cc->selected_handle)
 
475
            cc_deselect_handle( cc->selected_handle );
 
476
        cc->selected_handle = NULL;
 
477
        // Show all default connection points
 
478
 
 
479
    }
 
480
    else if ( cc->mode == SP_CONNECTOR_CONTEXT_EDITING_MODE )
 
481
    {
 
482
        ec->cursor_shape = cursor_node_xpm;
 
483
        cc->knot_tip = cc_knot_tips[1];
 
484
/*            if (cc->active_shape)
 
485
        {
 
486
            cc->selection->set( SP_OBJECT( cc->active_shape ) );
 
487
        }
 
488
        else
 
489
        {
 
490
            SPItem* item = cc->selection->singleItem();
 
491
            if ( item )
 
492
            {
 
493
                cc_set_active_shape(cc, item);
 
494
                cc->selection->set( SP_OBJECT( item ) );
 
495
            }
 
496
        }*/
 
497
    }
 
498
    sp_event_context_update_cursor(ec);
 
499
 
 
500
}
 
501
 
 
502
 
 
503
static void
297
504
sp_connector_context_finish(SPEventContext *ec)
298
505
{
299
506
    SPConnectorContext *cc = SP_CONNECTOR_CONTEXT(ec);
341
548
        cc->active_shape_layer_repr = NULL;
342
549
    }
343
550
 
344
 
    // Hide the center connection point if it exists.
345
 
    if (cc->connpthandle) {
346
 
        sp_knot_hide(cc->connpthandle);
 
551
    // Hide the connection points if they exist.
 
552
    if (cc->connpthandles.size()) {
 
553
        for (ConnectionPointMap::iterator it = cc->connpthandles.begin();
 
554
                it != cc->connpthandles.end(); ++it) {
 
555
            sp_knot_hide(it->first);
 
556
        }
347
557
    }
348
558
}
349
559
 
373
583
}
374
584
 
375
585
 
376
 
static gchar *
377
 
conn_pt_handle_test(SPConnectorContext *cc, Geom::Point& p)
 
586
static bool
 
587
conn_pt_handle_test(SPConnectorContext *cc, Geom::Point& p, gchar **href, gchar **cpid)
378
588
{
379
589
    // TODO: this will need to change when there are more connection
380
590
    //       points available for each shape.
381
591
 
382
 
    SPKnot *centerpt = cc->connpthandle;
383
 
    if (cc->active_handle && (cc->active_handle == centerpt))
 
592
    if (cc->active_handle && (cc->connpthandles.find(cc->active_handle) != cc->connpthandles.end()))
384
593
    {
385
 
        p = centerpt->pos;
386
 
        return g_strdup_printf("#%s", SP_OBJECT_ID(cc->active_shape));
 
594
        p = cc->active_handle->pos;
 
595
        const ConnectionPoint& cp = cc->connpthandles[cc->active_handle];
 
596
        *href = g_strdup_printf("#%s", cc->active_shape->getId());
 
597
        *cpid = g_strdup_printf("%c%d", cp.type == ConnPointDefault ? 'd' : 'u' , cp.id);
 
598
        return true;
387
599
    }
388
 
    return NULL;
389
 
}
390
 
 
391
 
 
 
600
    *href = NULL;
 
601
    *cpid = NULL;
 
602
    return false;
 
603
}
 
604
 
 
605
static void
 
606
cc_select_handle(SPKnot* knot)
 
607
{
 
608
    knot->setShape(SP_KNOT_SHAPE_SQUARE);
 
609
    knot->setSize(10);
 
610
    knot->setAnchor(GTK_ANCHOR_CENTER);
 
611
    knot->setFill(0x0000ffff, 0x0000ffff, 0x0000ffff);
 
612
    sp_knot_update_ctrl(knot);
 
613
}
 
614
 
 
615
static void
 
616
cc_deselect_handle(SPKnot* knot)
 
617
{
 
618
    knot->setShape(SP_KNOT_SHAPE_SQUARE);
 
619
    knot->setSize(8);
 
620
    knot->setAnchor(GTK_ANCHOR_CENTER);
 
621
    knot->setFill(0xffffff00, 0xff0000ff, 0xff0000ff);
 
622
    sp_knot_update_ctrl(knot);
 
623
}
392
624
 
393
625
static gint
394
626
sp_connector_context_item_handler(SPEventContext *event_context, SPItem *item, GdkEvent *event)
405
637
        case GDK_BUTTON_RELEASE:
406
638
            if (event->button.button == 1 && !event_context->space_panning) {
407
639
                if ((cc->state == SP_CONNECTOR_CONTEXT_DRAGGING) &&
408
 
                        (connector_within_tolerance))
 
640
                        (event_context->within_tolerance))
409
641
                {
410
642
                    spcc_reset_colors(cc);
411
643
                    cc->state = SP_CONNECTOR_CONTEXT_IDLE;
412
 
                    sp_event_context_discard_delayed_snap_event(event_context);
413
644
                }
414
645
                if (cc->state != SP_CONNECTOR_CONTEXT_IDLE) {
415
 
                    // Doing simething else like rerouting.
 
646
                    // Doing something else like rerouting.
416
647
                    break;
417
648
                }
418
 
                // find out clicked item, disregarding groups, honoring Alt
419
 
                SPItem *item_ungrouped = sp_event_context_find_item(desktop,
420
 
                        p, event->button.state & GDK_MOD1_MASK, TRUE);
 
649
                // find out clicked item, honoring Alt
 
650
                SPItem *item = sp_event_context_find_item(desktop,
 
651
                        p, event->button.state & GDK_MOD1_MASK, FALSE);
421
652
 
422
653
                if (event->button.state & GDK_SHIFT_MASK) {
423
 
                    cc->selection->toggle(item_ungrouped);
 
654
                    cc->selection->toggle(item);
424
655
                } else {
425
 
                    cc->selection->set(item_ungrouped);
 
656
                    cc->selection->set(item);
 
657
                    if ( cc->mode == SP_CONNECTOR_CONTEXT_EDITING_MODE && cc->selected_handle )
 
658
                    {
 
659
                        cc_deselect_handle( cc->selected_handle );
 
660
                        cc->selected_handle = NULL;
 
661
                    }
 
662
                    /* When selecting a new item,
 
663
                       do not allow showing connection points
 
664
                       on connectors. (yet?)
 
665
                    */
 
666
                    if ( item != cc->active_shape && !cc_item_is_connector( item ) )
 
667
                        cc_set_active_shape( cc, item );
426
668
                }
427
669
                ret = TRUE;
428
670
 
430
672
            break;
431
673
        case GDK_ENTER_NOTIFY:
432
674
        {
433
 
            if (cc_item_is_shape(item)) {
434
 
                // This is a shape, so show connection point(s).
435
 
                if (!(cc->active_shape) ||
436
 
                        // Don't show handle for another handle.
437
 
                        (item != ((SPItem *) cc->connpthandle))) {
 
675
            if (cc->mode == SP_CONNECTOR_CONTEXT_DRAWING_MODE || (cc->mode == SP_CONNECTOR_CONTEXT_EDITING_MODE && !cc->selected_handle))
 
676
            {
 
677
                if (cc_item_is_shape(item)) {
 
678
 
 
679
                    // I don't really understand what the above does,
 
680
                    // so I commented it.
 
681
                    // This is a shape, so show connection point(s).
 
682
    /*                if (!(cc->active_shape)
 
683
                            // Don't show handle for another handle.
 
684
    //                         || (cc->connpthandles.find((SPKnot*) item) != cc->connpthandles.end())
 
685
                        )
 
686
                    {
 
687
                        cc_set_active_shape(cc, item);
 
688
                    }*/
438
689
                    cc_set_active_shape(cc, item);
439
690
                }
 
691
                ret = TRUE;
440
692
            }
441
 
            ret = TRUE;
442
693
            break;
443
694
        }
444
695
        default:
462
713
            break;
463
714
 
464
715
        case GDK_MOTION_NOTIFY:
465
 
                ret = connector_handle_motion_notify(cc, event->motion);
 
716
            ret = connector_handle_motion_notify(cc, event->motion);
466
717
            break;
467
718
 
468
719
        case GDK_BUTTON_RELEASE:
497
748
    SPEventContext *event_context = SP_EVENT_CONTEXT(cc);
498
749
 
499
750
    gint ret = FALSE;
500
 
    if ( bevent.button == 1 && !event_context->space_panning ) {
501
 
 
502
 
        SPDesktop *desktop = SP_EVENT_CONTEXT_DESKTOP(cc);
503
 
 
504
 
        if (Inkscape::have_viable_layer(desktop, cc->_message_context) == false) {
505
 
            return TRUE;
506
 
        }
507
 
 
508
 
        Geom::Point const event_w(bevent.x,
509
 
                                bevent.y);
510
 
        connector_drag_origin_w = event_w;
511
 
        connector_within_tolerance = true;
512
 
 
513
 
        Geom::Point const event_dt = cc->desktop->w2d(event_w);
514
 
 
515
 
        SnapManager &m = cc->desktop->namedview->snap_manager;
516
 
                m.setup(cc->desktop);
517
 
 
518
 
        switch (cc->state) {
519
 
            case SP_CONNECTOR_CONTEXT_STOP:
520
 
                /* This is allowed, if we just cancelled curve */
521
 
            case SP_CONNECTOR_CONTEXT_IDLE:
522
 
            {
523
 
                if ( cc->npoints == 0 ) {
524
 
                    cc_clear_active_conn(cc);
525
 
 
526
 
                    SP_EVENT_CONTEXT_DESKTOP(cc)->messageStack()->flash(Inkscape::NORMAL_MESSAGE, _("Creating new connector"));
527
 
 
528
 
                    /* Set start anchor */
529
 
                    /* Create green anchor */
530
 
                    Geom::Point p = event_dt;
531
 
 
 
751
    if ( cc->mode == SP_CONNECTOR_CONTEXT_DRAWING_MODE )
 
752
    {
 
753
        if ( bevent.button == 1 && !event_context->space_panning ) {
 
754
 
 
755
            SPDesktop *desktop = SP_EVENT_CONTEXT_DESKTOP(cc);
 
756
 
 
757
            if (Inkscape::have_viable_layer(desktop, cc->_message_context) == false) {
 
758
                return TRUE;
 
759
            }
 
760
 
 
761
            Geom::Point const event_w(bevent.x,
 
762
                                    bevent.y);
 
763
//             connector_drag_origin_w = event_w;
 
764
            cc->xp = bevent.x;
 
765
            cc->yp = bevent.y;
 
766
            cc->within_tolerance = true;
 
767
 
 
768
            Geom::Point const event_dt = cc->desktop->w2d(event_w);
 
769
 
 
770
            SnapManager &m = cc->desktop->namedview->snap_manager;
 
771
            m.setup(cc->desktop);
 
772
 
 
773
            switch (cc->state) {
 
774
                case SP_CONNECTOR_CONTEXT_STOP:
 
775
                    /* This is allowed, if we just canceled curve */
 
776
                case SP_CONNECTOR_CONTEXT_IDLE:
 
777
                {
 
778
                    if ( cc->npoints == 0 ) {
 
779
                        cc_clear_active_conn(cc);
 
780
 
 
781
                        SP_EVENT_CONTEXT_DESKTOP(cc)->messageStack()->flash(Inkscape::NORMAL_MESSAGE, _("Creating new connector"));
 
782
 
 
783
                        /* Set start anchor */
 
784
                        /* Create green anchor */
 
785
                        Geom::Point p = event_dt;
 
786
 
 
787
                        // Test whether we clicked on a connection point
 
788
                        bool found = conn_pt_handle_test(cc, p, &cc->shref, &cc->scpid);
 
789
 
 
790
                        if (!found) {
 
791
                            // This is the first point, so just snap it to the grid
 
792
                            // as there's no other points to go off.
 
793
                            m.freeSnapReturnByRef(p, Inkscape::SNAPSOURCE_OTHER_HANDLE);
 
794
                        }
 
795
                        spcc_connector_set_initial_point(cc, p);
 
796
 
 
797
                    }
 
798
                    cc->state = SP_CONNECTOR_CONTEXT_DRAGGING;
 
799
                    ret = TRUE;
 
800
                    break;
 
801
                }
 
802
                case SP_CONNECTOR_CONTEXT_DRAGGING:
 
803
                {
 
804
                    // This is the second click of a connector creation.
 
805
                    m.freeSnapReturnByRef(p, Inkscape::SNAPSOURCE_OTHER_HANDLE);
 
806
 
 
807
                    spcc_connector_set_subsequent_point(cc, p);
 
808
                    spcc_connector_finish_segment(cc, p);
532
809
                    // Test whether we clicked on a connection point
533
 
                    cc->sid = conn_pt_handle_test(cc, p);
534
 
 
535
 
                    if (!cc->sid) {
536
 
                        // This is the first point, so just snap it to the grid
537
 
                        // as there's no other points to go off.
538
 
                        m.freeSnapReturnByRef(Inkscape::SnapPreferences::SNAPPOINT_NODE, p, Inkscape::SNAPSOURCE_HANDLE);
539
 
                    }
540
 
                    spcc_connector_set_initial_point(cc, p);
541
 
 
542
 
                }
543
 
                cc->state = SP_CONNECTOR_CONTEXT_DRAGGING;
544
 
                ret = TRUE;
545
 
                break;
546
 
            }
547
 
            case SP_CONNECTOR_CONTEXT_DRAGGING:
548
 
            {
549
 
                // This is the second click of a connector creation.
550
 
                m.freeSnapReturnByRef(Inkscape::SnapPreferences::SNAPPOINT_NODE, p, Inkscape::SNAPSOURCE_HANDLE);
551
 
 
552
 
                spcc_connector_set_subsequent_point(cc, p);
553
 
                spcc_connector_finish_segment(cc, p);
554
 
                // Test whether we clicked on a connection point
555
 
                cc->eid = conn_pt_handle_test(cc, p);
556
 
                if (cc->npoints != 0) {
557
 
                    spcc_connector_finish(cc);
558
 
                }
559
 
                cc_set_active_conn(cc, cc->newconn);
560
 
                cc->state = SP_CONNECTOR_CONTEXT_IDLE;
561
 
                sp_event_context_discard_delayed_snap_event(event_context);
562
 
                ret = TRUE;
563
 
                break;
564
 
            }
565
 
            case SP_CONNECTOR_CONTEXT_CLOSE:
566
 
            {
567
 
                g_warning("Button down in CLOSE state");
568
 
                break;
569
 
            }
570
 
            default:
571
 
                break;
572
 
        }
573
 
    } else if (bevent.button == 3) {
574
 
        if (cc->state == SP_CONNECTOR_CONTEXT_REROUTING) {
575
 
            // A context menu is going to be triggered here,
576
 
            // so end the rerouting operation.
577
 
            cc_connector_rerouting_finish(cc, &p);
578
 
 
579
 
            cc->state = SP_CONNECTOR_CONTEXT_IDLE;
580
 
            sp_event_context_discard_delayed_snap_event(event_context);
581
 
 
582
 
            // Don't set ret to TRUE, so we drop through to the
583
 
            // parent handler which will open the context menu.
584
 
        }
585
 
        else if (cc->npoints != 0) {
586
 
            spcc_connector_finish(cc);
587
 
            cc->state = SP_CONNECTOR_CONTEXT_IDLE;
588
 
            sp_event_context_discard_delayed_snap_event(event_context);
589
 
            ret = TRUE;
 
810
                    /*bool found = */conn_pt_handle_test(cc, p, &cc->ehref, &cc->ecpid);
 
811
                    if (cc->npoints != 0) {
 
812
                        spcc_connector_finish(cc);
 
813
                    }
 
814
                    cc_set_active_conn(cc, cc->newconn);
 
815
                    cc->state = SP_CONNECTOR_CONTEXT_IDLE;
 
816
                    ret = TRUE;
 
817
                    break;
 
818
                }
 
819
                case SP_CONNECTOR_CONTEXT_CLOSE:
 
820
                {
 
821
                    g_warning("Button down in CLOSE state");
 
822
                    break;
 
823
                }
 
824
                default:
 
825
                    break;
 
826
            }
 
827
        } else if (bevent.button == 3) {
 
828
            if (cc->state == SP_CONNECTOR_CONTEXT_REROUTING) {
 
829
                // A context menu is going to be triggered here,
 
830
                // so end the rerouting operation.
 
831
                cc_connector_rerouting_finish(cc, &p);
 
832
 
 
833
                cc->state = SP_CONNECTOR_CONTEXT_IDLE;
 
834
 
 
835
                // Don't set ret to TRUE, so we drop through to the
 
836
                // parent handler which will open the context menu.
 
837
            }
 
838
            else if (cc->npoints != 0) {
 
839
                spcc_connector_finish(cc);
 
840
                cc->state = SP_CONNECTOR_CONTEXT_IDLE;
 
841
                ret = TRUE;
 
842
            }
 
843
        }
 
844
    }
 
845
    else if ( cc->mode == SP_CONNECTOR_CONTEXT_EDITING_MODE )
 
846
    {
 
847
        if ( bevent.button == 1 && !event_context->space_panning )
 
848
        {
 
849
            // Initialize variables in case of dragging
 
850
 
 
851
            SPDesktop *desktop = SP_EVENT_CONTEXT_DESKTOP(cc);
 
852
 
 
853
            if (Inkscape::have_viable_layer(desktop, cc->_message_context) == false) {
 
854
                return TRUE;
 
855
            }
 
856
 
 
857
            cc->xp = bevent.x;
 
858
            cc->yp = bevent.y;
 
859
            cc->within_tolerance = true;
 
860
 
 
861
            ConnectionPointMap::iterator const& active_knot_it = cc->connpthandles.find( cc->active_handle );
 
862
 
 
863
            switch (cc->state)
 
864
            {
 
865
                case SP_CONNECTOR_CONTEXT_IDLE:
 
866
                    if ( active_knot_it != cc->connpthandles.end() )
 
867
                    {
 
868
                        // We do not allow selecting and, thereby, moving default knots
 
869
                        if ( active_knot_it->second.type != ConnPointDefault)
 
870
                        {
 
871
                            if (cc->selected_handle != cc->active_handle)
 
872
                            {
 
873
                                if ( cc->selected_handle )
 
874
                                    cc_deselect_handle( cc->selected_handle );
 
875
                                cc->selected_handle = cc->active_handle;
 
876
                                cc_select_handle( cc->selected_handle );
 
877
                            }
 
878
                        }
 
879
                        else
 
880
                            // Just ignore the default connection point
 
881
                            return FALSE;
 
882
                    }
 
883
                    else
 
884
                        if ( cc->selected_handle )
 
885
                        {
 
886
                            cc_deselect_handle( cc->selected_handle );
 
887
                            cc->selected_handle = NULL;
 
888
                        }
 
889
 
 
890
                    if ( cc->selected_handle )
 
891
                    {
 
892
                        cc->state = SP_CONNECTOR_CONTEXT_DRAGGING;
 
893
                        cc->selection->set( SP_OBJECT( cc->active_shape ) );
 
894
                    }
 
895
 
 
896
                    ret = TRUE;
 
897
                    break;
 
898
                // Dragging valid because of the way we create
 
899
                // new connection points.
 
900
                case SP_CONNECTOR_CONTEXT_DRAGGING:
 
901
                    // Do nothing.
 
902
                    ret = TRUE;
 
903
                    break;
 
904
            }
590
905
        }
591
906
    }
592
907
    return ret;
607
922
 
608
923
    Geom::Point const event_w(mevent.x, mevent.y);
609
924
 
610
 
    if (connector_within_tolerance) {
611
 
        gint const tolerance = prefs->getIntLimited("/options/dragtolerance/value", 0, 0, 100);
612
 
        if ( Geom::LInfty( event_w - connector_drag_origin_w ) < tolerance ) {
 
925
    if (cc->within_tolerance) {
 
926
        cc->tolerance = prefs->getIntLimited("/options/dragtolerance/value", 0, 0, 100);
 
927
        if ( ( abs( (gint) mevent.x - cc->xp ) < cc->tolerance ) &&
 
928
             ( abs( (gint) mevent.y - cc->yp ) < cc->tolerance ) ) {
613
929
            return FALSE;   // Do not drag if we're within tolerance from origin.
614
930
        }
615
931
    }
616
932
    // Once the user has moved farther than tolerance from the original location
617
933
    // (indicating they intend to move the object, not click), then always process
618
934
    // the motion notify coordinates as given (no snapping back to origin)
619
 
    connector_within_tolerance = false;
 
935
    cc->within_tolerance = false;
620
936
 
621
937
    SPDesktop *const dt = cc->desktop;
622
938
 
623
939
    /* Find desktop coordinates */
624
940
    Geom::Point p = dt->w2d(event_w);
625
941
 
626
 
    SnapManager &m = dt->namedview->snap_manager;
627
 
        m.setup(dt);
628
 
 
629
 
    switch (cc->state) {
630
 
        case SP_CONNECTOR_CONTEXT_DRAGGING:
631
 
        {
632
 
            // This is movement during a connector creation.
633
 
                if ( cc->npoints > 0 ) {
634
 
                        m.freeSnapReturnByRef(Inkscape::SnapPreferences::SNAPPOINT_NODE, p, Inkscape::SNAPSOURCE_HANDLE);
635
 
                        cc->selection->clear();
636
 
                spcc_connector_set_subsequent_point(cc, p);
637
 
                ret = TRUE;
638
 
            }
639
 
            break;
640
 
        }
641
 
        case SP_CONNECTOR_CONTEXT_REROUTING:
642
 
        {
643
 
            g_assert( SP_IS_PATH(cc->clickeditem));
644
 
 
645
 
            m.freeSnapReturnByRef(Inkscape::SnapPreferences::SNAPPOINT_NODE, p, Inkscape::SNAPSOURCE_HANDLE);
646
 
 
647
 
            // Update the hidden path
648
 
            Geom::Matrix i2d = sp_item_i2d_affine(cc->clickeditem);
649
 
            Geom::Matrix d2i = i2d.inverse();
650
 
            SPPath *path = SP_PATH(cc->clickeditem);
651
 
            SPCurve *curve = (SP_SHAPE(path))->curve;
652
 
            if (cc->clickedhandle == cc->endpt_handle[0]) {
653
 
                Geom::Point o = cc->endpt_handle[1]->pos;
654
 
                curve->stretch_endpoints(p * d2i, o * d2i);
655
 
            }
656
 
            else {
657
 
                Geom::Point o = cc->endpt_handle[0]->pos;
658
 
                curve->stretch_endpoints(o * d2i, p * d2i);
659
 
            }
660
 
            sp_conn_adjust_path(path);
661
 
 
662
 
            // Copy this to the temporary visible path
663
 
            cc->red_curve = SP_SHAPE(path)->curve->copy();
664
 
            cc->red_curve->transform(i2d);
665
 
 
666
 
            sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(cc->red_bpath), cc->red_curve);
667
 
            ret = TRUE;
668
 
            break;
669
 
        }
670
 
        case SP_CONNECTOR_CONTEXT_STOP:
671
 
            /* This is perfectly valid */
672
 
            break;
673
 
        default:
674
 
            break;
 
942
    if ( cc->mode == SP_CONNECTOR_CONTEXT_DRAWING_MODE )
 
943
    {
 
944
        SnapManager &m = dt->namedview->snap_manager;
 
945
        m.setup(dt);
 
946
 
 
947
        switch (cc->state) {
 
948
            case SP_CONNECTOR_CONTEXT_DRAGGING:
 
949
            {
 
950
                gobble_motion_events(mevent.state);
 
951
                // This is movement during a connector creation.
 
952
                if ( cc->npoints > 0 ) {
 
953
                    m.freeSnapReturnByRef(p, Inkscape::SNAPSOURCE_OTHER_HANDLE);
 
954
                    cc->selection->clear();
 
955
                    spcc_connector_set_subsequent_point(cc, p);
 
956
                    ret = TRUE;
 
957
                }
 
958
                break;
 
959
            }
 
960
            case SP_CONNECTOR_CONTEXT_REROUTING:
 
961
            {
 
962
                gobble_motion_events(GDK_BUTTON1_MASK);
 
963
                g_assert( SP_IS_PATH(cc->clickeditem));
 
964
 
 
965
                m.freeSnapReturnByRef(p, Inkscape::SNAPSOURCE_OTHER_HANDLE);
 
966
 
 
967
                // Update the hidden path
 
968
                Geom::Matrix i2d = sp_item_i2d_affine(cc->clickeditem);
 
969
                Geom::Matrix d2i = i2d.inverse();
 
970
                SPPath *path = SP_PATH(cc->clickeditem);
 
971
                SPCurve *curve = path->original_curve ? path->original_curve : path->curve;
 
972
                if (cc->clickedhandle == cc->endpt_handle[0]) {
 
973
                    Geom::Point o = cc->endpt_handle[1]->pos;
 
974
                    curve->stretch_endpoints(p * d2i, o * d2i);
 
975
                }
 
976
                else {
 
977
                    Geom::Point o = cc->endpt_handle[0]->pos;
 
978
                    curve->stretch_endpoints(o * d2i, p * d2i);
 
979
                }
 
980
                sp_conn_reroute_path_immediate(path);
 
981
 
 
982
                // Copy this to the temporary visible path
 
983
                cc->red_curve = path->original_curve ?
 
984
                        path->original_curve->copy() : path->curve->copy();
 
985
                cc->red_curve->transform(i2d);
 
986
 
 
987
                sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(cc->red_bpath), cc->red_curve);
 
988
                ret = TRUE;
 
989
                break;
 
990
            }
 
991
            case SP_CONNECTOR_CONTEXT_STOP:
 
992
                /* This is perfectly valid */
 
993
                break;
 
994
            default:
 
995
                if (!sp_event_context_knot_mouseover(cc)) {
 
996
                    m.preSnap(Inkscape::SnapCandidatePoint(p, Inkscape::SNAPSOURCE_OTHER_HANDLE));
 
997
                }
 
998
                break;
 
999
        }
 
1000
    }
 
1001
    else if ( cc->mode == SP_CONNECTOR_CONTEXT_EDITING_MODE )
 
1002
    {
 
1003
        switch ( cc->state )
 
1004
        {
 
1005
            case SP_CONNECTOR_CONTEXT_DRAGGING:
 
1006
                sp_knot_set_position(cc->selected_handle, p, 0);
 
1007
                ret = TRUE;
 
1008
                break;
 
1009
            case SP_CONNECTOR_CONTEXT_NEWCONNPOINT:
 
1010
                sp_knot_set_position(cc->selected_handle, p, 0);
 
1011
                ret = TRUE;
 
1012
                break;
 
1013
        }
675
1014
    }
676
1015
 
677
1016
    return ret;
685
1024
    SPEventContext *event_context = SP_EVENT_CONTEXT(cc);
686
1025
    if ( revent.button == 1 && !event_context->space_panning ) {
687
1026
 
688
 
        SPDesktop *desktop = SP_EVENT_CONTEXT_DESKTOP(cc);
 
1027
        SPDesktop *desktop = SP_EVENT_CONTEXT_DESKTOP(cc);
689
1028
        SPDocument *doc = sp_desktop_document(desktop);
690
1029
 
691
1030
        SnapManager &m = desktop->namedview->snap_manager;
692
 
                m.setup(desktop);
 
1031
        m.setup(desktop);
693
1032
 
694
1033
        Geom::Point const event_w(revent.x, revent.y);
695
1034
 
696
1035
        /* Find desktop coordinates */
697
1036
        Geom::Point p = cc->desktop->w2d(event_w);
698
 
 
699
 
        switch (cc->state) {
700
 
            //case SP_CONNECTOR_CONTEXT_POINT:
701
 
            case SP_CONNECTOR_CONTEXT_DRAGGING:
702
 
            {
703
 
                m.freeSnapReturnByRef(Inkscape::SnapPreferences::SNAPPOINT_NODE, p, Inkscape::SNAPSOURCE_HANDLE);
704
 
 
705
 
                if (connector_within_tolerance)
 
1037
        if ( cc->mode == SP_CONNECTOR_CONTEXT_DRAWING_MODE )
 
1038
        {
 
1039
            switch (cc->state) {
 
1040
                //case SP_CONNECTOR_CONTEXT_POINT:
 
1041
                case SP_CONNECTOR_CONTEXT_DRAGGING:
706
1042
                {
 
1043
                    m.freeSnapReturnByRef(p, Inkscape::SNAPSOURCE_OTHER_HANDLE);
 
1044
 
 
1045
                    if (cc->within_tolerance)
 
1046
                    {
 
1047
                        spcc_connector_finish_segment(cc, p);
 
1048
                        return TRUE;
 
1049
                    }
 
1050
                    // Connector has been created via a drag, end it now.
 
1051
                    spcc_connector_set_subsequent_point(cc, p);
707
1052
                    spcc_connector_finish_segment(cc, p);
 
1053
                    // Test whether we clicked on a connection point
 
1054
                    /*bool found = */conn_pt_handle_test(cc, p, &cc->ehref, &cc->ecpid);
 
1055
                    if (cc->npoints != 0) {
 
1056
                        spcc_connector_finish(cc);
 
1057
                    }
 
1058
                    cc_set_active_conn(cc, cc->newconn);
 
1059
                    cc->state = SP_CONNECTOR_CONTEXT_IDLE;
 
1060
                    break;
 
1061
                }
 
1062
                case SP_CONNECTOR_CONTEXT_REROUTING:
 
1063
                {
 
1064
                    m.freeSnapReturnByRef(p, Inkscape::SNAPSOURCE_OTHER_HANDLE);
 
1065
                    cc_connector_rerouting_finish(cc, &p);
 
1066
 
 
1067
                    sp_document_ensure_up_to_date(doc);
 
1068
                    cc->state = SP_CONNECTOR_CONTEXT_IDLE;
708
1069
                    return TRUE;
709
 
                }
710
 
                // Connector has been created via a drag, end it now.
711
 
                spcc_connector_set_subsequent_point(cc, p);
712
 
                spcc_connector_finish_segment(cc, p);
713
 
                // Test whether we clicked on a connection point
714
 
                cc->eid = conn_pt_handle_test(cc, p);
715
 
                if (cc->npoints != 0) {
716
 
                    spcc_connector_finish(cc);
717
 
                }
718
 
                cc_set_active_conn(cc, cc->newconn);
719
 
                cc->state = SP_CONNECTOR_CONTEXT_IDLE;
720
 
                sp_event_context_discard_delayed_snap_event(event_context);
721
 
                break;
 
1070
                    break;
 
1071
                }
 
1072
                case SP_CONNECTOR_CONTEXT_STOP:
 
1073
                    /* This is allowed, if we just cancelled curve */
 
1074
                    break;
 
1075
                default:
 
1076
                    break;
722
1077
            }
723
 
            case SP_CONNECTOR_CONTEXT_REROUTING:
 
1078
            ret = TRUE;
 
1079
        }
 
1080
        else if ( cc->mode == SP_CONNECTOR_CONTEXT_EDITING_MODE )
 
1081
        {
 
1082
            switch ( cc->state )
724
1083
            {
725
 
                m.freeSnapReturnByRef(Inkscape::SnapPreferences::SNAPPOINT_NODE, p, Inkscape::SNAPSOURCE_HANDLE);
726
 
                cc_connector_rerouting_finish(cc, &p);
727
 
 
728
 
                sp_document_ensure_up_to_date(doc);
729
 
                cc->state = SP_CONNECTOR_CONTEXT_IDLE;
730
 
                sp_event_context_discard_delayed_snap_event(event_context);
731
 
                return TRUE;
732
 
                break;
 
1084
                case SP_CONNECTOR_CONTEXT_DRAGGING:
 
1085
 
 
1086
                    if (!cc->within_tolerance)
 
1087
                    {
 
1088
                        m.freeSnapReturnByRef(p, Inkscape::SNAPSOURCE_OTHER_HANDLE);
 
1089
                        sp_knot_set_position(cc->selected_handle, p, 0);
 
1090
                        ConnectionPoint& cp = cc->connpthandles[cc->selected_handle];
 
1091
                        cp.pos = p * sp_item_dt2i_affine(cc->active_shape);
 
1092
                        cc->active_shape->avoidRef->updateConnectionPoint(cp);
 
1093
                    }
 
1094
 
 
1095
                    cc->state = SP_CONNECTOR_CONTEXT_IDLE;
 
1096
                    ret = TRUE;
 
1097
                    break;
 
1098
 
 
1099
 
 
1100
                case SP_CONNECTOR_CONTEXT_NEWCONNPOINT:
 
1101
                    m.freeSnapReturnByRef(p, Inkscape::SNAPSOURCE_OTHER_HANDLE);
 
1102
 
 
1103
                    sp_knot_set_position(cc->selected_handle, p, 0);
 
1104
 
 
1105
                    ConnectionPoint cp;
 
1106
                    cp.type = ConnPointUserDefined;
 
1107
                    cp.pos = p * sp_item_dt2i_affine(cc->active_shape);
 
1108
                    cp.dir = Avoid::ConnDirAll;
 
1109
                    g_object_unref(cc->selected_handle);
 
1110
                    cc->active_shape->avoidRef->addConnectionPoint(cp);
 
1111
                    sp_document_ensure_up_to_date(doc);
 
1112
                    for (ConnectionPointMap::iterator it = cc->connpthandles.begin(); it != cc->connpthandles.end(); ++it)
 
1113
                        if (it->second.type == ConnPointUserDefined && it->second.id == cp.id)
 
1114
                        {
 
1115
                            cc->selected_handle = it->first;
 
1116
                            break;
 
1117
                        }
 
1118
                    cc_select_handle( cc->selected_handle );
 
1119
                    cc->state = SP_CONNECTOR_CONTEXT_IDLE;
 
1120
                    ret = TRUE;
 
1121
                    break;
733
1122
            }
734
 
            case SP_CONNECTOR_CONTEXT_STOP:
735
 
                /* This is allowed, if we just cancelled curve */
736
 
                break;
737
 
            default:
738
 
                break;
739
1123
        }
740
 
        ret = TRUE;
741
1124
    }
742
1125
 
 
1126
 
743
1127
    return ret;
744
1128
}
745
1129
 
749
1133
{
750
1134
    gint ret = FALSE;
751
1135
    /* fixme: */
752
 
    switch (keyval) {
753
 
        case GDK_Return:
754
 
        case GDK_KP_Enter:
755
 
            if (cc->npoints != 0) {
756
 
                spcc_connector_finish(cc);
757
 
                cc->state = SP_CONNECTOR_CONTEXT_IDLE;
758
 
                sp_event_context_discard_delayed_snap_event(SP_EVENT_CONTEXT(cc));
759
 
                ret = TRUE;
760
 
            }
761
 
            break;
762
 
        case GDK_Escape:
763
 
            if (cc->state == SP_CONNECTOR_CONTEXT_REROUTING) {
764
 
 
765
 
                SPDesktop *desktop = SP_EVENT_CONTEXT_DESKTOP(cc);
766
 
                SPDocument *doc = sp_desktop_document(desktop);
767
 
 
768
 
                cc_connector_rerouting_finish(cc, NULL);
769
 
 
770
 
                sp_document_undo(doc);
771
 
 
772
 
                cc->state = SP_CONNECTOR_CONTEXT_IDLE;
773
 
                sp_event_context_discard_delayed_snap_event(SP_EVENT_CONTEXT(cc));
774
 
                desktop->messageStack()->flash( Inkscape::NORMAL_MESSAGE,
775
 
                        _("Connector endpoint drag cancelled."));
776
 
                ret = TRUE;
777
 
            }
778
 
            else if (cc->npoints != 0) {
779
 
                // if drawing, cancel, otherwise pass it up for deselecting
780
 
                cc->state = SP_CONNECTOR_CONTEXT_STOP;
781
 
                sp_event_context_discard_delayed_snap_event(SP_EVENT_CONTEXT(cc));
782
 
                spcc_reset_colors(cc);
783
 
                ret = TRUE;
784
 
            }
785
 
            break;
786
 
        default:
787
 
            break;
788
 
    }
 
1136
    if ( cc->mode == SP_CONNECTOR_CONTEXT_DRAWING_MODE )
 
1137
    {
 
1138
        switch (keyval) {
 
1139
            case GDK_Return:
 
1140
            case GDK_KP_Enter:
 
1141
                if (cc->npoints != 0) {
 
1142
                    spcc_connector_finish(cc);
 
1143
                    cc->state = SP_CONNECTOR_CONTEXT_IDLE;
 
1144
                    ret = TRUE;
 
1145
                }
 
1146
                break;
 
1147
            case GDK_Escape:
 
1148
                if (cc->state == SP_CONNECTOR_CONTEXT_REROUTING) {
 
1149
 
 
1150
                    SPDesktop *desktop = SP_EVENT_CONTEXT_DESKTOP(cc);
 
1151
                    SPDocument *doc = sp_desktop_document(desktop);
 
1152
 
 
1153
                    cc_connector_rerouting_finish(cc, NULL);
 
1154
 
 
1155
                    sp_document_undo(doc);
 
1156
 
 
1157
                    cc->state = SP_CONNECTOR_CONTEXT_IDLE;
 
1158
                    desktop->messageStack()->flash( Inkscape::NORMAL_MESSAGE,
 
1159
                            _("Connector endpoint drag cancelled."));
 
1160
                    ret = TRUE;
 
1161
                }
 
1162
                else if (cc->npoints != 0) {
 
1163
                    // if drawing, cancel, otherwise pass it up for deselecting
 
1164
                    cc->state = SP_CONNECTOR_CONTEXT_STOP;
 
1165
                    spcc_reset_colors(cc);
 
1166
                    ret = TRUE;
 
1167
                }
 
1168
                break;
 
1169
            default:
 
1170
                break;
 
1171
        }
 
1172
    }
 
1173
    else if ( cc->mode == SP_CONNECTOR_CONTEXT_EDITING_MODE )
 
1174
    {
 
1175
        switch ( cc->state )
 
1176
        {
 
1177
            case SP_CONNECTOR_CONTEXT_DRAGGING:
 
1178
                if ( keyval == GDK_Escape )
 
1179
                {
 
1180
                    // Cancel connection point dragging
 
1181
 
 
1182
                    // Obtain original position
 
1183
                    ConnectionPoint const& cp = cc->connpthandles[cc->selected_handle];
 
1184
                    SPDesktop *desktop = SP_EVENT_CONTEXT_DESKTOP(cc);
 
1185
                    const Geom::Matrix& i2doc = sp_item_i2doc_affine(cc->active_shape);
 
1186
                    sp_knot_set_position(cc->selected_handle, cp.pos * i2doc * desktop->doc2dt(), 0);
 
1187
                    cc->state = SP_CONNECTOR_CONTEXT_IDLE;
 
1188
                    desktop->messageStack()->flash( Inkscape::NORMAL_MESSAGE,
 
1189
                        _("Connection point drag cancelled."));
 
1190
                    ret = TRUE;
 
1191
                }
 
1192
                else if ( keyval == GDK_Return || keyval == GDK_KP_Enter )
 
1193
                {
 
1194
                    // Put connection point at current position
 
1195
 
 
1196
                    SPDesktop *desktop = SP_EVENT_CONTEXT_DESKTOP(cc);
 
1197
                    SnapManager &m = desktop->namedview->snap_manager;
 
1198
                    m.setup(desktop);
 
1199
                    Geom::Point p = cc->selected_handle->pos;
 
1200
//                     SPEventContext* event_context = SP_EVENT_CONTEXT( cc );
 
1201
 
 
1202
                    if (!cc->within_tolerance)
 
1203
                    {
 
1204
                        m.freeSnapReturnByRef(p, Inkscape::SNAPSOURCE_OTHER_HANDLE);
 
1205
                        sp_knot_set_position(cc->selected_handle, p, 0);
 
1206
                        ConnectionPoint& cp = cc->connpthandles[cc->selected_handle];
 
1207
                        cp.pos = p * sp_item_dt2i_affine(cc->active_shape);
 
1208
                        cc->active_shape->avoidRef->updateConnectionPoint(cp);
 
1209
                    }
 
1210
 
 
1211
                    cc->state = SP_CONNECTOR_CONTEXT_IDLE;
 
1212
                    ret = TRUE;
 
1213
                }
 
1214
                break;
 
1215
            case SP_CONNECTOR_CONTEXT_NEWCONNPOINT:
 
1216
                if ( keyval == GDK_Escape )
 
1217
                {
 
1218
                    // Just destroy the knot
 
1219
                    g_object_unref( cc->selected_handle );
 
1220
                    cc->selected_handle = NULL;
 
1221
                    cc->state = SP_CONNECTOR_CONTEXT_IDLE;
 
1222
                    ret = TRUE;
 
1223
                }
 
1224
                else if ( keyval == GDK_Return || keyval == GDK_KP_Enter )
 
1225
                {
 
1226
                    SPDesktop *desktop = SP_EVENT_CONTEXT_DESKTOP(cc);
 
1227
                    SPDocument *doc = sp_desktop_document(desktop);
 
1228
                    SnapManager &m = desktop->namedview->snap_manager;
 
1229
                    m.setup(desktop);
 
1230
                    Geom::Point p = cc->selected_handle->pos;
 
1231
 
 
1232
                    m.freeSnapReturnByRef(p, Inkscape::SNAPSOURCE_OTHER_HANDLE);
 
1233
 
 
1234
                    sp_knot_set_position(cc->selected_handle, p, 0);
 
1235
 
 
1236
                    ConnectionPoint cp;
 
1237
                    cp.type = ConnPointUserDefined;
 
1238
                    cp.pos = p * sp_item_dt2i_affine(cc->active_shape);
 
1239
                    cp.dir = Avoid::ConnDirAll;
 
1240
                    g_object_unref(cc->selected_handle);
 
1241
                    cc->active_shape->avoidRef->addConnectionPoint(cp);
 
1242
                    sp_document_ensure_up_to_date(doc);
 
1243
                    for (ConnectionPointMap::iterator it = cc->connpthandles.begin(); it != cc->connpthandles.end(); ++it)
 
1244
                        if (it->second.type == ConnPointUserDefined && it->second.id == cp.id)
 
1245
                        {
 
1246
                            cc->selected_handle = it->first;
 
1247
                            break;
 
1248
                        }
 
1249
                    cc_select_handle( cc->selected_handle );
 
1250
                    cc->state = SP_CONNECTOR_CONTEXT_IDLE;
 
1251
                    ret = TRUE;
 
1252
                }
 
1253
 
 
1254
                break;
 
1255
            case SP_CONNECTOR_CONTEXT_IDLE:
 
1256
                if ( keyval == GDK_Delete && cc->selected_handle )
 
1257
                {
 
1258
                    cc->active_shape->avoidRef->deleteConnectionPoint(cc->connpthandles[cc->selected_handle]);
 
1259
                    cc->selected_handle = NULL;
 
1260
                    ret = TRUE;
 
1261
                }
 
1262
 
 
1263
                break;
 
1264
        }
 
1265
    }
 
1266
 
789
1267
    return ret;
790
1268
}
791
1269
 
803
1281
    if (p != NULL)
804
1282
    {
805
1283
        // Test whether we clicked on a connection point
806
 
        gchar *shape_label = conn_pt_handle_test(cc, *p);
 
1284
        gchar *shape_label, *cpid;
 
1285
        bool found = conn_pt_handle_test(cc, *p, &shape_label, &cpid);
807
1286
 
808
 
        if (shape_label) {
 
1287
        if (found) {
809
1288
            if (cc->clickedhandle == cc->endpt_handle[0]) {
810
1289
                sp_object_setAttribute(cc->clickeditem,
811
 
                        "inkscape:connection-start",shape_label, false);
 
1290
                        "inkscape:connection-start", shape_label, false);
 
1291
                sp_object_setAttribute(cc->clickeditem,
 
1292
                        "inkscape:connection-start-point", cpid, false);
812
1293
            }
813
1294
            else {
814
1295
                sp_object_setAttribute(cc->clickeditem,
815
 
                        "inkscape:connection-end",shape_label, false);
 
1296
                        "inkscape:connection-end", shape_label, false);
 
1297
                sp_object_setAttribute(cc->clickeditem,
 
1298
                        "inkscape:connection-end-point", cpid, false);
816
1299
            }
817
1300
            g_free(shape_label);
818
1301
        }
819
1302
    }
820
1303
    cc->clickeditem->setHidden(false);
821
 
    sp_conn_adjust_path(SP_PATH(cc->clickeditem));
 
1304
    sp_conn_reroute_path_immediate(SP_PATH(cc->clickeditem));
822
1305
    cc->clickeditem->updateRepr();
823
1306
    sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
824
1307
                     _("Reroute connector"));
863
1346
 
864
1347
    if (!cc->newConnRef) {
865
1348
        Avoid::Router *router = sp_desktop_document(dt)->router;
866
 
        cc->newConnRef = new Avoid::ConnRef(router, 0, src, dst);
867
 
        cc->newConnRef->updateEndPoint(Avoid::VertID::src, src);
 
1349
        cc->newConnRef = new Avoid::ConnRef(router);
 
1350
        cc->newConnRef->setEndpoint(Avoid::VertID::src, src);
 
1351
        if (cc->isOrthogonal)
 
1352
            cc->newConnRef->setRoutingType(Avoid::ConnType_Orthogonal);
 
1353
        else
 
1354
            cc->newConnRef->setRoutingType(Avoid::ConnType_PolyLine);
868
1355
    }
869
 
    cc->newConnRef->updateEndPoint(Avoid::VertID::tar, dst);
870
 
 
 
1356
    // Set new endpoint.
 
1357
    cc->newConnRef->setEndpoint(Avoid::VertID::tar, dst);
 
1358
    // Immediately generate new routes for connector.
871
1359
    cc->newConnRef->makePathInvalid();
872
 
    cc->newConnRef->generatePath(src, dst);
873
 
 
874
 
    Avoid::PolyLine route = cc->newConnRef->route();
875
 
    cc->newConnRef->calcRouteDist();
876
 
 
877
 
    cc->red_curve->reset();
878
 
    Geom::Point pt(route.ps[0].x, route.ps[0].y);
879
 
    cc->red_curve->moveto(pt);
880
 
 
881
 
    for (int i = 1; i < route.pn; ++i) {
882
 
        Geom::Point p(route.ps[i].x, route.ps[i].y);
883
 
        cc->red_curve->lineto(p);
884
 
    }
 
1360
    cc->newConnRef->router()->processTransaction();
 
1361
    // Recreate curve from libavoid route.
 
1362
    recreateCurve( cc->red_curve, cc->newConnRef, cc->curvature );
885
1363
    cc->red_curve->transform(dt->doc2dt());
886
1364
    sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(cc->red_bpath), cc->red_curve);
887
1365
}
953
1431
 
954
1432
        /* Attach repr */
955
1433
        cc->newconn = SP_ITEM(desktop->currentLayer()->appendChildRepr(repr));
956
 
        cc->selection->set(repr);
957
 
        Inkscape::GC::release(repr);
958
1434
        cc->newconn->transform = sp_item_i2doc_affine(SP_ITEM(desktop->currentLayer())).inverse();
959
 
        cc->newconn->updateRepr();
960
1435
 
961
1436
        bool connection = false;
962
1437
        sp_object_setAttribute(cc->newconn, "inkscape:connector-type",
963
 
                "polyline", false);
964
 
        if (cc->sid)
 
1438
                cc->isOrthogonal ? "orthogonal" : "polyline", false);
 
1439
        sp_object_setAttribute(cc->newconn, "inkscape:connector-curvature",
 
1440
                Glib::Ascii::dtostr(cc->curvature).c_str(), false);
 
1441
        if (cc->shref)
965
1442
        {
966
1443
            sp_object_setAttribute(cc->newconn, "inkscape:connection-start",
967
 
                    cc->sid, false);
 
1444
                    cc->shref, false);
 
1445
            if (cc->scpid)
 
1446
                sp_object_setAttribute(cc->newconn, "inkscape:connection-start-point",
 
1447
                        cc->scpid, false);
968
1448
            connection = true;
969
1449
        }
970
1450
 
971
 
        if (cc->eid)
 
1451
        if (cc->ehref)
972
1452
        {
973
1453
            sp_object_setAttribute(cc->newconn, "inkscape:connection-end",
974
 
                    cc->eid, false);
 
1454
                    cc->ehref, false);
 
1455
            if (cc->ecpid)
 
1456
                sp_object_setAttribute(cc->newconn, "inkscape:connection-end-point",
 
1457
                        cc->ecpid, false);
975
1458
            connection = true;
976
1459
        }
 
1460
        // Process pending updates.
977
1461
        cc->newconn->updateRepr();
 
1462
        sp_document_ensure_up_to_date(doc);
 
1463
 
978
1464
        if (connection) {
979
1465
            // Adjust endpoints to shape edge.
980
 
            sp_conn_adjust_path(SP_PATH(cc->newconn));
 
1466
            sp_conn_reroute_path_immediate(SP_PATH(cc->newconn));
 
1467
            cc->newconn->updateRepr();
981
1468
        }
982
 
        cc->newconn->updateRepr();
 
1469
 
 
1470
        // Only set the selection after we are finished with creating the attributes of
 
1471
        // the connector.  Otherwise, the selection change may alter the defaults for
 
1472
        // values like curvature in the connector context, preventing subsequent lookup
 
1473
        // of their original values.
 
1474
        cc->selection->set(repr);
 
1475
        Inkscape::GC::release(repr);
983
1476
    }
984
1477
 
985
1478
    c->unref();
986
1479
 
987
 
    /* Flush pending updates */
988
1480
    sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR, _("Create connector"));
989
 
    sp_document_ensure_up_to_date(doc);
990
1481
}
991
1482
 
992
1483
 
1036
1527
 
1037
1528
    gboolean consumed = FALSE;
1038
1529
 
 
1530
    gchar* knot_tip = knot->tip ? knot->tip : cc->knot_tip;
1039
1531
    switch (event->type) {
1040
1532
        case GDK_ENTER_NOTIFY:
1041
1533
            sp_knot_set_flag(knot, SP_KNOT_MOUSEOVER, TRUE);
1042
1534
 
1043
1535
            cc->active_handle = knot;
1044
 
 
1045
 
            if (knot->tip)
 
1536
            if (knot_tip)
1046
1537
            {
1047
1538
                knot->desktop->event_context->defaultMessageContext()->set(
1048
 
                        Inkscape::NORMAL_MESSAGE, knot->tip);
 
1539
                        Inkscape::NORMAL_MESSAGE, knot_tip);
1049
1540
            }
1050
1541
 
1051
1542
            consumed = TRUE;
1055
1546
 
1056
1547
            cc->active_handle = NULL;
1057
1548
 
1058
 
            if (knot->tip) {
 
1549
            if (knot_tip) {
1059
1550
                knot->desktop->event_context->defaultMessageContext()->clear();
1060
1551
            }
1061
1552
 
1101
1592
                }
1102
1593
 
1103
1594
                // Show the red path for dragging.
1104
 
                cc->red_curve = SP_PATH(cc->clickeditem)->curve->copy();
 
1595
                cc->red_curve = SP_PATH(cc->clickeditem)->original_curve ? SP_PATH(cc->clickeditem)->original_curve->copy() : SP_PATH(cc->clickeditem)->curve->copy();
1105
1596
                Geom::Matrix i2d = sp_item_i2d_affine(cc->clickeditem);
1106
1597
                cc->red_curve->transform(i2d);
1107
1598
                sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(cc->red_bpath), cc->red_curve);
1120
1611
    return consumed;
1121
1612
}
1122
1613
 
1123
 
 
1124
 
static void cc_set_active_shape(SPConnectorContext *cc, SPItem *item)
 
1614
static void cc_active_shape_add_knot(SPDesktop* desktop, SPItem* item, ConnectionPointMap &cphandles, ConnectionPoint& cp)
1125
1615
{
1126
 
    g_assert(item != NULL );
1127
 
 
1128
 
    cc->active_shape = item;
1129
 
 
1130
 
    // Remove existing active shape listeners
1131
 
    if (cc->active_shape_repr) {
1132
 
        sp_repr_remove_listener_by_data(cc->active_shape_repr, cc);
1133
 
        Inkscape::GC::release(cc->active_shape_repr);
1134
 
 
1135
 
        sp_repr_remove_listener_by_data(cc->active_shape_layer_repr, cc);
1136
 
        Inkscape::GC::release(cc->active_shape_layer_repr);
1137
 
    }
1138
 
 
1139
 
    // Listen in case the active shape changes
1140
 
    cc->active_shape_repr = SP_OBJECT_REPR(item);
1141
 
    if (cc->active_shape_repr) {
1142
 
        Inkscape::GC::anchor(cc->active_shape_repr);
1143
 
        sp_repr_add_listener(cc->active_shape_repr, &shape_repr_events, cc);
1144
 
 
1145
 
        cc->active_shape_layer_repr = cc->active_shape_repr->parent();
1146
 
        Inkscape::GC::anchor(cc->active_shape_layer_repr);
1147
 
        sp_repr_add_listener(cc->active_shape_layer_repr, &layer_repr_events, cc);
1148
 
    }
1149
 
 
1150
 
 
1151
 
    // Set center connection point.
1152
 
    if ( cc->connpthandle == NULL ) {
1153
 
        SPKnot *knot = sp_knot_new(cc->desktop,
1154
 
                _("<b>Connection point</b>: click or drag to create a new connector"));
 
1616
        SPKnot *knot = sp_knot_new(desktop, 0);
1155
1617
 
1156
1618
        knot->setShape(SP_KNOT_SHAPE_SQUARE);
1157
1619
        knot->setSize(8);
1159
1621
        knot->setFill(0xffffff00, 0xff0000ff, 0xff0000ff);
1160
1622
        sp_knot_update_ctrl(knot);
1161
1623
 
1162
 
        // We don't want to use the standard knot handler,
1163
 
        //since we don't want this knot to be draggable.
 
1624
        // We don't want to use the standard knot handler.
1164
1625
        g_signal_handler_disconnect(G_OBJECT(knot->item),
1165
1626
                knot->_event_handler_id);
1166
1627
        knot->_event_handler_id = 0;
1167
1628
 
1168
1629
        gtk_signal_connect(GTK_OBJECT(knot->item), "event",
1169
1630
                GTK_SIGNAL_FUNC(cc_generic_knot_handler), knot);
1170
 
 
1171
 
        cc->connpthandle = knot;
 
1631
        sp_knot_set_position(knot, item->avoidRef->getConnectionPointPos(cp.type, cp.id) * desktop->doc2dt(), 0);
 
1632
        sp_knot_show(knot);
 
1633
        cphandles[knot] = cp;
 
1634
}
 
1635
 
 
1636
static void cc_set_active_shape(SPConnectorContext *cc, SPItem *item)
 
1637
{
 
1638
    g_assert(item != NULL );
 
1639
 
 
1640
    std::map<int, ConnectionPoint>* connpts = &item->avoidRef->connection_points;
 
1641
 
 
1642
    if (cc->active_shape != item)
 
1643
    {
 
1644
        // The active shape has changed
 
1645
        // Rebuild everything
 
1646
        cc->active_shape = item;
 
1647
        // Remove existing active shape listeners
 
1648
        if (cc->active_shape_repr) {
 
1649
            sp_repr_remove_listener_by_data(cc->active_shape_repr, cc);
 
1650
            Inkscape::GC::release(cc->active_shape_repr);
 
1651
 
 
1652
            sp_repr_remove_listener_by_data(cc->active_shape_layer_repr, cc);
 
1653
            Inkscape::GC::release(cc->active_shape_layer_repr);
 
1654
        }
 
1655
 
 
1656
        // Listen in case the active shape changes
 
1657
        cc->active_shape_repr = SP_OBJECT_REPR(item);
 
1658
        if (cc->active_shape_repr) {
 
1659
            Inkscape::GC::anchor(cc->active_shape_repr);
 
1660
            sp_repr_add_listener(cc->active_shape_repr, &shape_repr_events, cc);
 
1661
 
 
1662
            cc->active_shape_layer_repr = cc->active_shape_repr->parent();
 
1663
            Inkscape::GC::anchor(cc->active_shape_layer_repr);
 
1664
            sp_repr_add_listener(cc->active_shape_layer_repr, &layer_repr_events, cc);
 
1665
        }
 
1666
 
 
1667
 
 
1668
        // Set the connection points.
 
1669
        if ( cc->connpthandles.size() )
 
1670
            // destroy the old list
 
1671
            while (! cc->connpthandles.empty() )
 
1672
            {
 
1673
                g_object_unref(cc->connpthandles.begin()->first);
 
1674
                cc->connpthandles.erase(cc->connpthandles.begin());
 
1675
            }
 
1676
        // build the new one
 
1677
        if ( connpts->size() )
 
1678
        for (std::map<int, ConnectionPoint>::iterator it = connpts->begin(); it != connpts->end(); ++it)
 
1679
            cc_active_shape_add_knot(cc->desktop, item, cc->connpthandles, it->second);
 
1680
 
 
1681
        // Also add default connection points
 
1682
        // For now, only centre default connection point will
 
1683
        // be available
 
1684
        ConnectionPoint centre;
 
1685
        centre.type = ConnPointDefault;
 
1686
        centre.id = ConnPointPosCC;
 
1687
        cc_active_shape_add_knot(cc->desktop, item, cc->connpthandles, centre);
1172
1688
    }
1173
 
 
1174
 
 
1175
 
    Geom::OptRect bbox = sp_item_bbox_desktop(cc->active_shape);
1176
 
    if (bbox) {
1177
 
        Geom::Point center = bbox->midpoint();
1178
 
        sp_knot_set_position(cc->connpthandle, center, 0);
1179
 
        sp_knot_show(cc->connpthandle);
1180
 
    } else {
1181
 
        sp_knot_hide(cc->connpthandle);
 
1689
    else
 
1690
    {
 
1691
        // The active shape didn't change
 
1692
        // Update only the connection point knots
 
1693
 
 
1694
        // Ensure the item's connection_points map
 
1695
        // has been updated
 
1696
        sp_document_ensure_up_to_date(SP_OBJECT_DOCUMENT(item));
 
1697
 
 
1698
        std::set<int> seen;
 
1699
        for  ( ConnectionPointMap::iterator it = cc->connpthandles.begin(); it != cc->connpthandles.end() ;)
 
1700
        {
 
1701
            bool removed = false;
 
1702
            if ( it->second.type == ConnPointUserDefined )
 
1703
            {
 
1704
                std::map<int, ConnectionPoint>::iterator p = connpts->find(it->second.id);
 
1705
                if (p != connpts->end())
 
1706
                {
 
1707
                    if ( it->second != p->second )
 
1708
                        // Connection point position has changed
 
1709
                        // Update knot position
 
1710
                        sp_knot_set_position(it->first,
 
1711
                                             item->avoidRef->getConnectionPointPos(it->second.type, it->second.id) * cc->desktop->doc2dt(), 0);
 
1712
                    seen.insert(it->second.id);
 
1713
                    sp_knot_show(it->first);
 
1714
                }
 
1715
                else
 
1716
                {
 
1717
                    // This connection point does no longer exist,
 
1718
                    // remove the knot
 
1719
                    ConnectionPointMap::iterator curr = it;
 
1720
                    ++it;
 
1721
                    g_object_unref( curr->first );
 
1722
                    cc->connpthandles.erase(curr);
 
1723
                    removed = true;
 
1724
                }
 
1725
            }
 
1726
            else
 
1727
            {
 
1728
                // It's a default connection point
 
1729
                // Just make sure it's position is correct
 
1730
                sp_knot_set_position(it->first,
 
1731
                                     item->avoidRef->getConnectionPointPos(it->second.type, it->second.id) * cc->desktop->doc2dt(), 0);
 
1732
                sp_knot_show(it->first);
 
1733
 
 
1734
            }
 
1735
            if ( !removed )
 
1736
                ++it;
 
1737
        }
 
1738
        // Add knots for new connection points.
 
1739
        if (connpts->size())
 
1740
            for ( std::map<int, ConnectionPoint>::iterator it = connpts->begin(); it != connpts->end(); ++it )
 
1741
                if ( seen.find(it->first) == seen.end() )
 
1742
                    // A new connection point has been added
 
1743
                    // to the shape. Add a knot for it.
 
1744
                    cc_active_shape_add_knot(cc->desktop, item, cc->connpthandles, it->second);
1182
1745
    }
1183
1746
}
1184
1747
 
1188
1751
{
1189
1752
    g_assert( SP_IS_PATH(item) );
1190
1753
 
1191
 
    SPCurve *curve = SP_SHAPE(SP_PATH(item))->curve;
 
1754
    SPCurve *curve = SP_PATH(item)->original_curve ? SP_PATH(item)->original_curve : SP_PATH(item)->curve;
1192
1755
    Geom::Matrix i2d = sp_item_i2d_affine(item);
1193
1756
 
1194
1757
    if (cc->active_conn == item)
1195
1758
    {
1196
 
        // Just adjust handle positions.
1197
 
        Geom::Point startpt = *(curve->first_point()) * i2d;
1198
 
        sp_knot_set_position(cc->endpt_handle[0], startpt, 0);
 
1759
        if (curve->is_empty())
 
1760
        {
 
1761
            // Connector is invisible because it is clipped to the boundary of
 
1762
            // two overlpapping shapes.
 
1763
            sp_knot_hide(cc->endpt_handle[0]);
 
1764
            sp_knot_hide(cc->endpt_handle[1]);
 
1765
        }
 
1766
        else
 
1767
        {
 
1768
            // Just adjust handle positions.
 
1769
            Geom::Point startpt = *(curve->first_point()) * i2d;
 
1770
            sp_knot_set_position(cc->endpt_handle[0], startpt, 0);
1199
1771
 
1200
 
        Geom::Point endpt = *(curve->last_point()) * i2d;
1201
 
        sp_knot_set_position(cc->endpt_handle[1], endpt, 0);
 
1772
            Geom::Point endpt = *(curve->last_point()) * i2d;
 
1773
            sp_knot_set_position(cc->endpt_handle[1], endpt, 0);
 
1774
        }
1202
1775
 
1203
1776
        return;
1204
1777
    }
1234
1807
            sp_knot_update_ctrl(knot);
1235
1808
 
1236
1809
            // We don't want to use the standard knot handler,
1237
 
            //since we don't want this knot to be draggable.
 
1810
            // since we don't want this knot to be draggable.
1238
1811
            g_signal_handler_disconnect(G_OBJECT(knot->item),
1239
1812
                    knot->_event_handler_id);
1240
1813
            knot->_event_handler_id = 0;
1261
1834
                G_CALLBACK(endpt_handler), cc);
1262
1835
    }
1263
1836
 
 
1837
    if (curve->is_empty())
 
1838
    {
 
1839
        // Connector is invisible because it is clipped to the boundary 
 
1840
        // of two overlpapping shapes.  So, it doesn't need endpoints.
 
1841
        return;
 
1842
    }
 
1843
 
1264
1844
    Geom::Point startpt = *(curve->first_point()) * i2d;
1265
1845
    sp_knot_set_position(cc->endpt_handle[0], startpt, 0);
1266
1846
 
1271
1851
    sp_knot_show(cc->endpt_handle[1]);
1272
1852
}
1273
1853
 
 
1854
void cc_create_connection_point(SPConnectorContext* cc)
 
1855
{
 
1856
    if (cc->active_shape && cc->state == SP_CONNECTOR_CONTEXT_IDLE)
 
1857
    {
 
1858
        if (cc->selected_handle)
 
1859
        {
 
1860
            cc_deselect_handle( cc->selected_handle );
 
1861
        }
 
1862
        SPKnot *knot = sp_knot_new(cc->desktop, 0);
 
1863
        // We do not process events on this knot.
 
1864
        g_signal_handler_disconnect(G_OBJECT(knot->item),
 
1865
                                    knot->_event_handler_id);
 
1866
        knot->_event_handler_id = 0;
 
1867
 
 
1868
        cc_select_handle( knot );
 
1869
        cc->selected_handle = knot;
 
1870
        sp_knot_show(cc->selected_handle);
 
1871
        cc->state = SP_CONNECTOR_CONTEXT_NEWCONNPOINT;
 
1872
    }
 
1873
}
 
1874
 
 
1875
void cc_remove_connection_point(SPConnectorContext* cc)
 
1876
{
 
1877
    if (cc->selected_handle && cc->state == SP_CONNECTOR_CONTEXT_IDLE )
 
1878
    {
 
1879
        cc->active_shape->avoidRef->deleteConnectionPoint(cc->connpthandles[cc->selected_handle]);
 
1880
        cc->selected_handle = NULL;
 
1881
    }
 
1882
}
1274
1883
 
1275
1884
static bool cc_item_is_shape(SPItem *item)
1276
1885
{
1295
1904
bool cc_item_is_connector(SPItem *item)
1296
1905
{
1297
1906
    if (SP_IS_PATH(item)) {
1298
 
        if (SP_PATH(item)->connEndPair.isAutoRoutingConn()) {
1299
 
            g_assert( !(SP_SHAPE(item)->curve->is_closed()) );
 
1907
        bool closed = SP_PATH(item)->original_curve ? SP_PATH(item)->original_curve->is_closed() : SP_PATH(item)->curve->is_closed();
 
1908
        if (SP_PATH(item)->connEndPair.isAutoRoutingConn() && !closed) {
 
1909
            // To be considered a connector, an object must be a non-closed 
 
1910
            // path that is marked with a "inkscape:connector-type" attribute.
1300
1911
            return true;
1301
1912
        }
1302
1913
    }
1394
2005
    g_assert(data);
1395
2006
    SPConnectorContext *cc = SP_CONNECTOR_CONTEXT(data);
1396
2007
 
1397
 
    // Look for changes than result in onscreen movement.
 
2008
    // Look for changes that result in onscreen movement.
1398
2009
    if (!strcmp(name, "d") || !strcmp(name, "x") || !strcmp(name, "y") ||
1399
2010
            !strcmp(name, "width") || !strcmp(name, "height") ||
1400
2011
            !strcmp(name, "transform"))
1409
2020
            cc_set_active_conn(cc, cc->active_conn);
1410
2021
        }
1411
2022
    }
 
2023
    else
 
2024
        if ( !strcmp(name, "inkscape:connection-points") )
 
2025
            if (repr == cc->active_shape_repr)
 
2026
                // The connection points of the active shape
 
2027
                // have changed. Update them.
 
2028
                cc_set_active_shape(cc, cc->active_shape);
1412
2029
}
1413
2030
 
1414
2031