~ubuntu-branches/ubuntu/utopic/inkscape/utopic-proposed

« back to all changes in this revision

Viewing changes to .pc/0004-Fix_FTBFS_on_gcc-4.8.patch/src/connector-context.cpp

  • Committer: Package Import Robot
  • Author(s): Mattia Rizzolo
  • Date: 2014-01-14 15:44:58 UTC
  • mfrom: (2.5.10 sid)
  • Revision ID: package-import@ubuntu.com-20140114154458-3283675h9vtv5ccc
Tags: 0.48.4-3ubuntu1
* Merge from Debian unstable (LP: #1225013).  Remaining changes:
  - debian/control:
    + Set Ubuntu Developer as maintainer,
    + build-depend on dh-translation to handle Ubuntu translation,
    + build against liblcsm2 instead of liblcsm1,
    + demote pstoedit from Recommends to Suggests (because it's on universe),
    + add a ${python:Depends}.
  - debian/patches/0006_add_unity_quicklist_support.patch: add.
  - debian/patches/series: update.
  - debian/rules:
    + add dh_translation to handle Ubuntu translation,
    + add python2 to dh addon.
* Debian changes:
  - debian/control: make description more user-friendly (LP: #811634)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Connector creation tool
 
3
 *
 
4
 * Authors:
 
5
 *   Michael Wybrow <mjwybrow@users.sourceforge.net>
 
6
 *
 
7
 * Copyright (C) 2005-2008  Michael Wybrow
 
8
 * Copyright (C) 2009  Monash University
 
9
 *
 
10
 * Released under GNU GPL, read the file 'COPYING' for more information
 
11
 *
 
12
 * TODO:
 
13
 *  o  Show a visual indicator for objects with the 'avoid' property set.
 
14
 *  o  Allow user to change a object between a path and connector through
 
15
 *     the interface.
 
16
 *  o  Create an interface for setting markers (arrow heads).
 
17
 *  o  Better distinguish between paths and connectors to prevent problems
 
18
 *     in the node tool and paths accidentally being turned into connectors
 
19
 *     in the connector tool.  Perhaps have a way to convert between.
 
20
 *  o  Only call libavoid's updateEndPoint as required.  Currently we do it
 
21
 *     for both endpoints, even if only one is moving.
 
22
 *  o  Allow user-placeable connection points.
 
23
 *  o  Deal sanely with connectors with both endpoints attached to the
 
24
 *     same connection point, and drawing of connectors attaching
 
25
 *     overlapping shapes (currently tries to adjust connector to be
 
26
 *     outside both bounding boxes).
 
27
 *  o  Fix many special cases related to connectors updating,
 
28
 *     e.g., copying a couple of shapes and a connector that are
 
29
 *           attached to each other.
 
30
 *     e.g., detach connector when it is moved or transformed in
 
31
 *           one of the other contexts.
 
32
 *  o  Cope with shapes whose ids change when they have attached
 
33
 *     connectors.
 
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.
 
147
 *
 
148
 */
 
149
 
 
150
 
 
151
 
 
152
#include <gdk/gdkkeysyms.h>
 
153
#include <string>
 
154
#include <cstring>
 
155
 
 
156
#include "connector-context.h"
 
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"
 
161
#include "xml/node-event-vector.h"
 
162
#include "xml/repr.h"
 
163
#include "svg/svg.h"
 
164
#include "desktop.h"
 
165
#include "desktop-style.h"
 
166
#include "desktop-handles.h"
 
167
#include "document.h"
 
168
#include "message-context.h"
 
169
#include "message-stack.h"
 
170
#include "selection.h"
 
171
#include "inkscape.h"
 
172
#include "preferences.h"
 
173
#include "sp-path.h"
 
174
#include "display/canvas-bpath.h"
 
175
#include "display/sodipodi-ctrl.h"
 
176
#include <glibmm/i18n.h>
 
177
#include <glibmm/stringutils.h>
 
178
#include "snap.h"
 
179
#include "knot.h"
 
180
#include "sp-conn-end.h"
 
181
#include "sp-conn-end-pair.h"
 
182
#include "conn-avoid-ref.h"
 
183
#include "libavoid/vertices.h"
 
184
#include "libavoid/router.h"
 
185
#include "context-fns.h"
 
186
#include "sp-namedview.h"
 
187
#include "sp-text.h"
 
188
#include "sp-flowtext.h"
 
189
#include "display/curve.h"
 
190
 
 
191
static void sp_connector_context_class_init(SPConnectorContextClass *klass);
 
192
static void sp_connector_context_init(SPConnectorContext *conn_context);
 
193
static void sp_connector_context_dispose(GObject *object);
 
194
 
 
195
static void sp_connector_context_setup(SPEventContext *ec);
 
196
static void sp_connector_context_set(SPEventContext *ec, Inkscape::Preferences::Entry *val);
 
197
static void sp_connector_context_finish(SPEventContext *ec);
 
198
static gint sp_connector_context_root_handler(SPEventContext *ec, GdkEvent *event);
 
199
static gint sp_connector_context_item_handler(SPEventContext *event_context, SPItem *item, GdkEvent *event);
 
200
 
 
201
// Stuff borrowed from DrawContext
 
202
static void spcc_connector_set_initial_point(SPConnectorContext *cc, Geom::Point const p);
 
203
static void spcc_connector_set_subsequent_point(SPConnectorContext *cc, Geom::Point const p);
 
204
static void spcc_connector_finish_segment(SPConnectorContext *cc, Geom::Point p);
 
205
static void spcc_reset_colors(SPConnectorContext *cc);
 
206
static void spcc_connector_finish(SPConnectorContext *cc);
 
207
static void spcc_concat_colors_and_flush(SPConnectorContext *cc);
 
208
static void spcc_flush_white(SPConnectorContext *cc, SPCurve *gc);
 
209
 
 
210
// Context event handlers
 
211
static gint connector_handle_button_press(SPConnectorContext *const cc, GdkEventButton const &bevent);
 
212
static gint connector_handle_motion_notify(SPConnectorContext *const cc, GdkEventMotion const &mevent);
 
213
static gint connector_handle_button_release(SPConnectorContext *const cc, GdkEventButton const &revent);
 
214
static gint connector_handle_key_press(SPConnectorContext *const cc, guint const keyval);
 
215
 
 
216
static void cc_active_shape_add_knot(SPDesktop* desktop, SPItem* item, ConnectionPointMap &cphandles, ConnectionPoint& cp);
 
217
static void cc_set_active_shape(SPConnectorContext *cc, SPItem *item);
 
218
static void cc_clear_active_shape(SPConnectorContext *cc);
 
219
static void cc_set_active_conn(SPConnectorContext *cc, SPItem *item);
 
220
static void cc_clear_active_conn(SPConnectorContext *cc);
 
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);
 
224
static bool cc_item_is_shape(SPItem *item);
 
225
static void cc_selection_changed(Inkscape::Selection *selection, gpointer data);
 
226
static void cc_connector_rerouting_finish(SPConnectorContext *const cc,
 
227
        Geom::Point *const p);
 
228
 
 
229
static void shape_event_attr_deleted(Inkscape::XML::Node *repr,
 
230
        Inkscape::XML::Node *child, Inkscape::XML::Node *ref, gpointer data);
 
231
static void shape_event_attr_changed(Inkscape::XML::Node *repr, gchar const *name,
 
232
        gchar const *old_value, gchar const *new_value, bool is_interactive,
 
233
        gpointer data);
 
234
 
 
235
 
 
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;*/
 
241
static SPEventContextClass *parent_class;
 
242
 
 
243
 
 
244
static Inkscape::XML::NodeEventVector shape_repr_events = {
 
245
    NULL, /* child_added */
 
246
    NULL, /* child_added */
 
247
    shape_event_attr_changed,
 
248
    NULL, /* content_changed */
 
249
    NULL  /* order_changed */
 
250
};
 
251
 
 
252
static Inkscape::XML::NodeEventVector layer_repr_events = {
 
253
    NULL, /* child_added */
 
254
    shape_event_attr_deleted,
 
255
    NULL, /* child_added */
 
256
    NULL, /* content_changed */
 
257
    NULL  /* order_changed */
 
258
};
 
259
 
 
260
 
 
261
GType
 
262
sp_connector_context_get_type(void)
 
263
{
 
264
    static GType type = 0;
 
265
    if (!type) {
 
266
        GTypeInfo info = {
 
267
            sizeof(SPConnectorContextClass),
 
268
            NULL, NULL,
 
269
            (GClassInitFunc) sp_connector_context_class_init,
 
270
            NULL, NULL,
 
271
            sizeof(SPConnectorContext),
 
272
            4,
 
273
            (GInstanceInitFunc) sp_connector_context_init,
 
274
            NULL,   /* value_table */
 
275
        };
 
276
        type = g_type_register_static(SP_TYPE_EVENT_CONTEXT, "SPConnectorContext", &info, (GTypeFlags)0);
 
277
    }
 
278
    return type;
 
279
}
 
280
 
 
281
static void
 
282
sp_connector_context_class_init(SPConnectorContextClass *klass)
 
283
{
 
284
    GObjectClass *object_class;
 
285
    SPEventContextClass *event_context_class;
 
286
 
 
287
    object_class = (GObjectClass *) klass;
 
288
    event_context_class = (SPEventContextClass *) klass;
 
289
 
 
290
    parent_class = (SPEventContextClass*)g_type_class_peek_parent(klass);
 
291
 
 
292
    object_class->dispose = sp_connector_context_dispose;
 
293
 
 
294
    event_context_class->setup = sp_connector_context_setup;
 
295
    event_context_class->set = sp_connector_context_set;
 
296
    event_context_class->finish = sp_connector_context_finish;
 
297
    event_context_class->root_handler = sp_connector_context_root_handler;
 
298
    event_context_class->item_handler = sp_connector_context_item_handler;
 
299
}
 
300
 
 
301
 
 
302
static void
 
303
sp_connector_context_init(SPConnectorContext *cc)
 
304
{
 
305
    SPEventContext *ec = SP_EVENT_CONTEXT(cc);
 
306
 
 
307
    ec->cursor_shape = cursor_connector_xpm;
 
308
    ec->hot_x = 1;
 
309
    ec->hot_y = 1;
 
310
    ec->xp = 0;
 
311
    ec->yp = 0;
 
312
 
 
313
    cc->mode = SP_CONNECTOR_CONTEXT_DRAWING_MODE;
 
314
    cc->knot_tip = 0;
 
315
 
 
316
    cc->red_color = 0xff00007f;
 
317
 
 
318
    cc->newconn = NULL;
 
319
    cc->newConnRef = NULL;
 
320
    cc->curvature = 0.0;
 
321
 
 
322
    cc->sel_changed_connection = sigc::connection();
 
323
 
 
324
    cc->active_shape = NULL;
 
325
    cc->active_shape_repr = NULL;
 
326
    cc->active_shape_layer_repr = NULL;
 
327
 
 
328
    cc->active_conn = NULL;
 
329
    cc->active_conn_repr = NULL;
 
330
 
 
331
    cc->active_handle = NULL;
 
332
 
 
333
    cc->selected_handle = NULL;
 
334
 
 
335
    cc->clickeditem = NULL;
 
336
    cc->clickedhandle = NULL;
 
337
 
 
338
    new (&cc->connpthandles) ConnectionPointMap();
 
339
 
 
340
    for (int i = 0; i < 2; ++i) {
 
341
        cc->endpt_handle[i] = NULL;
 
342
        cc->endpt_handler_id[i] = 0;
 
343
    }
 
344
    cc->shref = NULL;
 
345
    cc->scpid = NULL;
 
346
    cc->ehref = NULL;
 
347
    cc->ecpid = NULL;
 
348
    cc->npoints = 0;
 
349
    cc->state = SP_CONNECTOR_CONTEXT_IDLE;
 
350
}
 
351
 
 
352
 
 
353
static void
 
354
sp_connector_context_dispose(GObject *object)
 
355
{
 
356
    SPConnectorContext *cc = SP_CONNECTOR_CONTEXT(object);
 
357
 
 
358
    cc->sel_changed_connection.disconnect();
 
359
 
 
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();
 
366
    }
 
367
    cc->connpthandles.~ConnectionPointMap();
 
368
    for (int i = 0; i < 2; ++i) {
 
369
        if (cc->endpt_handle[1]) {
 
370
            g_object_unref(cc->endpt_handle[i]);
 
371
            cc->endpt_handle[i] = NULL;
 
372
        }
 
373
    }
 
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;
 
389
    }
 
390
    g_assert( cc->newConnRef == NULL );
 
391
 
 
392
    G_OBJECT_CLASS(parent_class)->dispose(object);
 
393
}
 
394
 
 
395
 
 
396
static void
 
397
sp_connector_context_setup(SPEventContext *ec)
 
398
{
 
399
    SPConnectorContext *cc = SP_CONNECTOR_CONTEXT(ec);
 
400
    SPDesktop *dt = ec->desktop;
 
401
 
 
402
    if (((SPEventContextClass *) parent_class)->setup) {
 
403
        ((SPEventContextClass *) parent_class)->setup(ec);
 
404
    }
 
405
 
 
406
    cc->selection = sp_desktop_selection(dt);
 
407
 
 
408
    cc->sel_changed_connection.disconnect();
 
409
    cc->sel_changed_connection = cc->selection->connectChanged(
 
410
            sigc::bind(sigc::ptr_fun(&cc_selection_changed),
 
411
            (gpointer) cc));
 
412
 
 
413
    /* Create red bpath */
 
414
    cc->red_bpath = sp_canvas_bpath_new(sp_desktop_sketch(ec->desktop), NULL);
 
415
    sp_canvas_bpath_set_stroke(SP_CANVAS_BPATH(cc->red_bpath), cc->red_color,
 
416
            1.0, SP_STROKE_LINEJOIN_MITER, SP_STROKE_LINECAP_BUTT);
 
417
    sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(cc->red_bpath), 0x00000000,
 
418
            SP_WIND_RULE_NONZERO);
 
419
    /* Create red curve */
 
420
    cc->red_curve = new SPCurve();
 
421
 
 
422
    /* Create green curve */
 
423
    cc->green_curve = new SPCurve();
 
424
 
 
425
    // Notice the initial selection.
 
426
    cc_selection_changed(cc->selection, (gpointer) cc);
 
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];
 
434
    Inkscape::Preferences *prefs = Inkscape::Preferences::get();
 
435
    if (prefs->getBool("/tools/connector/selcue", 0)) {
 
436
        ec->enableSelectionCue();
 
437
    }
 
438
 
 
439
    // Make sure we see all enter events for canvas items,
 
440
    // even if a mouse button is depressed.
 
441
    dt->canvas->gen_all_enter_events = true;
 
442
}
 
443
 
 
444
 
 
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
 
504
sp_connector_context_finish(SPEventContext *ec)
 
505
{
 
506
    SPConnectorContext *cc = SP_CONNECTOR_CONTEXT(ec);
 
507
 
 
508
    spcc_connector_finish(cc);
 
509
    cc->state = SP_CONNECTOR_CONTEXT_IDLE;
 
510
 
 
511
    if (((SPEventContextClass *) parent_class)->finish) {
 
512
        ((SPEventContextClass *) parent_class)->finish(ec);
 
513
    }
 
514
 
 
515
    if (cc->selection) {
 
516
        cc->selection = NULL;
 
517
    }
 
518
    cc_clear_active_shape(cc);
 
519
    cc_clear_active_conn(cc);
 
520
 
 
521
    // Restore the default event generating behaviour.
 
522
    SPDesktop *desktop = SP_EVENT_CONTEXT_DESKTOP(ec);
 
523
    desktop->canvas->gen_all_enter_events = false;
 
524
}
 
525
 
 
526
 
 
527
//-----------------------------------------------------------------------------
 
528
 
 
529
 
 
530
static void
 
531
cc_clear_active_shape(SPConnectorContext *cc)
 
532
{
 
533
    if (cc->active_shape == NULL) {
 
534
        return;
 
535
    }
 
536
    g_assert( cc->active_shape_repr );
 
537
    g_assert( cc->active_shape_layer_repr );
 
538
 
 
539
    cc->active_shape = NULL;
 
540
 
 
541
    if (cc->active_shape_repr) {
 
542
        sp_repr_remove_listener_by_data(cc->active_shape_repr, cc);
 
543
        Inkscape::GC::release(cc->active_shape_repr);
 
544
        cc->active_shape_repr = NULL;
 
545
 
 
546
        sp_repr_remove_listener_by_data(cc->active_shape_layer_repr, cc);
 
547
        Inkscape::GC::release(cc->active_shape_layer_repr);
 
548
        cc->active_shape_layer_repr = NULL;
 
549
    }
 
550
 
 
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
        }
 
557
    }
 
558
}
 
559
 
 
560
 
 
561
static void
 
562
cc_clear_active_conn(SPConnectorContext *cc)
 
563
{
 
564
    if (cc->active_conn == NULL) {
 
565
        return;
 
566
    }
 
567
    g_assert( cc->active_conn_repr );
 
568
 
 
569
    cc->active_conn = NULL;
 
570
 
 
571
    if (cc->active_conn_repr) {
 
572
        sp_repr_remove_listener_by_data(cc->active_conn_repr, cc);
 
573
        Inkscape::GC::release(cc->active_conn_repr);
 
574
        cc->active_conn_repr = NULL;
 
575
    }
 
576
 
 
577
    // Hide the endpoint handles.
 
578
    for (int i = 0; i < 2; ++i) {
 
579
        if (cc->endpt_handle[i]) {
 
580
            sp_knot_hide(cc->endpt_handle[i]);
 
581
        }
 
582
    }
 
583
}
 
584
 
 
585
 
 
586
static bool
 
587
conn_pt_handle_test(SPConnectorContext *cc, Geom::Point& p, gchar **href, gchar **cpid)
 
588
{
 
589
    // TODO: this will need to change when there are more connection
 
590
    //       points available for each shape.
 
591
 
 
592
    if (cc->active_handle && (cc->connpthandles.find(cc->active_handle) != cc->connpthandles.end()))
 
593
    {
 
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;
 
599
    }
 
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
}
 
624
 
 
625
static gint
 
626
sp_connector_context_item_handler(SPEventContext *event_context, SPItem *item, GdkEvent *event)
 
627
{
 
628
    gint ret = FALSE;
 
629
 
 
630
    SPDesktop *desktop = event_context->desktop;
 
631
 
 
632
    SPConnectorContext *cc = SP_CONNECTOR_CONTEXT(event_context);
 
633
 
 
634
    Geom::Point p(event->button.x, event->button.y);
 
635
 
 
636
    switch (event->type) {
 
637
        case GDK_BUTTON_RELEASE:
 
638
            if (event->button.button == 1 && !event_context->space_panning) {
 
639
                if ((cc->state == SP_CONNECTOR_CONTEXT_DRAGGING) &&
 
640
                        (event_context->within_tolerance))
 
641
                {
 
642
                    spcc_reset_colors(cc);
 
643
                    cc->state = SP_CONNECTOR_CONTEXT_IDLE;
 
644
                }
 
645
                if (cc->state != SP_CONNECTOR_CONTEXT_IDLE) {
 
646
                    // Doing something else like rerouting.
 
647
                    break;
 
648
                }
 
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);
 
652
 
 
653
                if (event->button.state & GDK_SHIFT_MASK) {
 
654
                    cc->selection->toggle(item);
 
655
                } else {
 
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 );
 
668
                }
 
669
                ret = TRUE;
 
670
 
 
671
            }
 
672
            break;
 
673
        case GDK_ENTER_NOTIFY:
 
674
        {
 
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
                    }*/
 
689
                    cc_set_active_shape(cc, item);
 
690
                }
 
691
                ret = TRUE;
 
692
            }
 
693
            break;
 
694
        }
 
695
        default:
 
696
            break;
 
697
    }
 
698
 
 
699
    return ret;
 
700
}
 
701
 
 
702
 
 
703
gint
 
704
sp_connector_context_root_handler(SPEventContext *ec, GdkEvent *event)
 
705
{
 
706
    SPConnectorContext *const cc = SP_CONNECTOR_CONTEXT(ec);
 
707
 
 
708
    gint ret = FALSE;
 
709
 
 
710
    switch (event->type) {
 
711
        case GDK_BUTTON_PRESS:
 
712
            ret = connector_handle_button_press(cc, event->button);
 
713
            break;
 
714
 
 
715
        case GDK_MOTION_NOTIFY:
 
716
            ret = connector_handle_motion_notify(cc, event->motion);
 
717
            break;
 
718
 
 
719
        case GDK_BUTTON_RELEASE:
 
720
            ret = connector_handle_button_release(cc, event->button);
 
721
            break;
 
722
        case GDK_KEY_PRESS:
 
723
            ret = connector_handle_key_press(cc, get_group0_keyval (&event->key));
 
724
            break;
 
725
 
 
726
        default:
 
727
            break;
 
728
    }
 
729
 
 
730
    if (!ret) {
 
731
        gint (*const parent_root_handler)(SPEventContext *, GdkEvent *)
 
732
            = ((SPEventContextClass *) parent_class)->root_handler;
 
733
        if (parent_root_handler) {
 
734
            ret = parent_root_handler(ec, event);
 
735
        }
 
736
    }
 
737
 
 
738
    return ret;
 
739
}
 
740
 
 
741
 
 
742
static gint
 
743
connector_handle_button_press(SPConnectorContext *const cc, GdkEventButton const &bevent)
 
744
{
 
745
    Geom::Point const event_w(bevent.x, bevent.y);
 
746
    /* Find desktop coordinates */
 
747
    Geom::Point p = cc->desktop->w2d(event_w);
 
748
    SPEventContext *event_context = SP_EVENT_CONTEXT(cc);
 
749
 
 
750
    gint ret = FALSE;
 
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);
 
809
                    // Test whether we clicked on a connection point
 
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
            }
 
905
        }
 
906
    }
 
907
    return ret;
 
908
}
 
909
 
 
910
 
 
911
static gint
 
912
connector_handle_motion_notify(SPConnectorContext *const cc, GdkEventMotion const &mevent)
 
913
{
 
914
    gint ret = FALSE;
 
915
    SPEventContext *event_context = SP_EVENT_CONTEXT(cc);
 
916
    Inkscape::Preferences *prefs = Inkscape::Preferences::get();
 
917
 
 
918
    if (event_context->space_panning || mevent.state & GDK_BUTTON2_MASK || mevent.state & GDK_BUTTON3_MASK) {
 
919
        // allow middle-button scrolling
 
920
        return FALSE;
 
921
    }
 
922
 
 
923
    Geom::Point const event_w(mevent.x, mevent.y);
 
924
 
 
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 ) ) {
 
929
            return FALSE;   // Do not drag if we're within tolerance from origin.
 
930
        }
 
931
    }
 
932
    // Once the user has moved farther than tolerance from the original location
 
933
    // (indicating they intend to move the object, not click), then always process
 
934
    // the motion notify coordinates as given (no snapping back to origin)
 
935
    cc->within_tolerance = false;
 
936
 
 
937
    SPDesktop *const dt = cc->desktop;
 
938
 
 
939
    /* Find desktop coordinates */
 
940
    Geom::Point p = dt->w2d(event_w);
 
941
 
 
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
        }
 
1014
    }
 
1015
 
 
1016
    return ret;
 
1017
}
 
1018
 
 
1019
 
 
1020
static gint
 
1021
connector_handle_button_release(SPConnectorContext *const cc, GdkEventButton const &revent)
 
1022
{
 
1023
    gint ret = FALSE;
 
1024
    SPEventContext *event_context = SP_EVENT_CONTEXT(cc);
 
1025
    if ( revent.button == 1 && !event_context->space_panning ) {
 
1026
 
 
1027
        SPDesktop *desktop = SP_EVENT_CONTEXT_DESKTOP(cc);
 
1028
        SPDocument *doc = sp_desktop_document(desktop);
 
1029
 
 
1030
        SnapManager &m = desktop->namedview->snap_manager;
 
1031
        m.setup(desktop);
 
1032
 
 
1033
        Geom::Point const event_w(revent.x, revent.y);
 
1034
 
 
1035
        /* Find desktop coordinates */
 
1036
        Geom::Point p = cc->desktop->w2d(event_w);
 
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:
 
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);
 
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;
 
1069
                    return TRUE;
 
1070
                    break;
 
1071
                }
 
1072
                case SP_CONNECTOR_CONTEXT_STOP:
 
1073
                    /* This is allowed, if we just cancelled curve */
 
1074
                    break;
 
1075
                default:
 
1076
                    break;
 
1077
            }
 
1078
            ret = TRUE;
 
1079
        }
 
1080
        else if ( cc->mode == SP_CONNECTOR_CONTEXT_EDITING_MODE )
 
1081
        {
 
1082
            switch ( cc->state )
 
1083
            {
 
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;
 
1122
            }
 
1123
        }
 
1124
    }
 
1125
 
 
1126
 
 
1127
    return ret;
 
1128
}
 
1129
 
 
1130
 
 
1131
static gint
 
1132
connector_handle_key_press(SPConnectorContext *const cc, guint const keyval)
 
1133
{
 
1134
    gint ret = FALSE;
 
1135
    /* fixme: */
 
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
 
 
1267
    return ret;
 
1268
}
 
1269
 
 
1270
 
 
1271
static void
 
1272
cc_connector_rerouting_finish(SPConnectorContext *const cc, Geom::Point *const p)
 
1273
{
 
1274
    SPDesktop *desktop = SP_EVENT_CONTEXT_DESKTOP(cc);
 
1275
    SPDocument *doc = sp_desktop_document(desktop);
 
1276
 
 
1277
    // Clear the temporary path:
 
1278
    cc->red_curve->reset();
 
1279
    sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(cc->red_bpath), NULL);
 
1280
 
 
1281
    if (p != NULL)
 
1282
    {
 
1283
        // Test whether we clicked on a connection point
 
1284
        gchar *shape_label, *cpid;
 
1285
        bool found = conn_pt_handle_test(cc, *p, &shape_label, &cpid);
 
1286
 
 
1287
        if (found) {
 
1288
            if (cc->clickedhandle == cc->endpt_handle[0]) {
 
1289
                sp_object_setAttribute(cc->clickeditem,
 
1290
                        "inkscape:connection-start", shape_label, false);
 
1291
                sp_object_setAttribute(cc->clickeditem,
 
1292
                        "inkscape:connection-start-point", cpid, false);
 
1293
            }
 
1294
            else {
 
1295
                sp_object_setAttribute(cc->clickeditem,
 
1296
                        "inkscape:connection-end", shape_label, false);
 
1297
                sp_object_setAttribute(cc->clickeditem,
 
1298
                        "inkscape:connection-end-point", cpid, false);
 
1299
            }
 
1300
            g_free(shape_label);
 
1301
        }
 
1302
    }
 
1303
    cc->clickeditem->setHidden(false);
 
1304
    sp_conn_reroute_path_immediate(SP_PATH(cc->clickeditem));
 
1305
    cc->clickeditem->updateRepr();
 
1306
    sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
 
1307
                     _("Reroute connector"));
 
1308
    cc_set_active_conn(cc, cc->clickeditem);
 
1309
}
 
1310
 
 
1311
 
 
1312
static void
 
1313
spcc_reset_colors(SPConnectorContext *cc)
 
1314
{
 
1315
    /* Red */
 
1316
    cc->red_curve->reset();
 
1317
    sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(cc->red_bpath), NULL);
 
1318
 
 
1319
    cc->green_curve->reset();
 
1320
    cc->npoints = 0;
 
1321
}
 
1322
 
 
1323
 
 
1324
static void
 
1325
spcc_connector_set_initial_point(SPConnectorContext *const cc, Geom::Point const p)
 
1326
{
 
1327
    g_assert( cc->npoints == 0 );
 
1328
 
 
1329
    cc->p[0] = p;
 
1330
    cc->p[1] = p;
 
1331
    cc->npoints = 2;
 
1332
    sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(cc->red_bpath), NULL);
 
1333
}
 
1334
 
 
1335
 
 
1336
static void
 
1337
spcc_connector_set_subsequent_point(SPConnectorContext *const cc, Geom::Point const p)
 
1338
{
 
1339
    g_assert( cc->npoints != 0 );
 
1340
 
 
1341
    SPDesktop *dt = cc->desktop;
 
1342
    Geom::Point o = dt->dt2doc(cc->p[0]);
 
1343
    Geom::Point d = dt->dt2doc(p);
 
1344
    Avoid::Point src(o[Geom::X], o[Geom::Y]);
 
1345
    Avoid::Point dst(d[Geom::X], d[Geom::Y]);
 
1346
 
 
1347
    if (!cc->newConnRef) {
 
1348
        Avoid::Router *router = sp_desktop_document(dt)->router;
 
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);
 
1355
    }
 
1356
    // Set new endpoint.
 
1357
    cc->newConnRef->setEndpoint(Avoid::VertID::tar, dst);
 
1358
    // Immediately generate new routes for connector.
 
1359
    cc->newConnRef->makePathInvalid();
 
1360
    cc->newConnRef->router()->processTransaction();
 
1361
    // Recreate curve from libavoid route.
 
1362
    recreateCurve( cc->red_curve, cc->newConnRef, cc->curvature );
 
1363
    cc->red_curve->transform(dt->doc2dt());
 
1364
    sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(cc->red_bpath), cc->red_curve);
 
1365
}
 
1366
 
 
1367
 
 
1368
/**
 
1369
 * Concats red, blue and green.
 
1370
 * If any anchors are defined, process these, optionally removing curves from white list
 
1371
 * Invoke _flush_white to write result back to object.
 
1372
 */
 
1373
static void
 
1374
spcc_concat_colors_and_flush(SPConnectorContext *cc)
 
1375
{
 
1376
    SPCurve *c = cc->green_curve;
 
1377
    cc->green_curve = new SPCurve();
 
1378
 
 
1379
    cc->red_curve->reset();
 
1380
    sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(cc->red_bpath), NULL);
 
1381
 
 
1382
    if (c->is_empty()) {
 
1383
        c->unref();
 
1384
        return;
 
1385
    }
 
1386
 
 
1387
    spcc_flush_white(cc, c);
 
1388
 
 
1389
    c->unref();
 
1390
}
 
1391
 
 
1392
 
 
1393
/*
 
1394
 * Flushes white curve(s) and additional curve into object
 
1395
 *
 
1396
 * No cleaning of colored curves - this has to be done by caller
 
1397
 * No rereading of white data, so if you cannot rely on ::modified, do it in caller
 
1398
 *
 
1399
 */
 
1400
 
 
1401
static void
 
1402
spcc_flush_white(SPConnectorContext *cc, SPCurve *gc)
 
1403
{
 
1404
    SPCurve *c;
 
1405
 
 
1406
    if (gc) {
 
1407
        c = gc;
 
1408
        c->ref();
 
1409
    } else {
 
1410
        return;
 
1411
    }
 
1412
 
 
1413
    /* Now we have to go back to item coordinates at last */
 
1414
    c->transform(SP_EVENT_CONTEXT_DESKTOP(cc)->dt2doc());
 
1415
 
 
1416
    SPDesktop *desktop = SP_EVENT_CONTEXT_DESKTOP(cc);
 
1417
    SPDocument *doc = sp_desktop_document(desktop);
 
1418
    Inkscape::XML::Document *xml_doc = sp_document_repr_doc(doc);
 
1419
 
 
1420
    if ( c && !c->is_empty() ) {
 
1421
        /* We actually have something to write */
 
1422
 
 
1423
        Inkscape::XML::Node *repr = xml_doc->createElement("svg:path");
 
1424
        /* Set style */
 
1425
        sp_desktop_apply_style_tool(desktop, repr, "/tools/connector", false);
 
1426
 
 
1427
        gchar *str = sp_svg_write_path( c->get_pathvector() );
 
1428
        g_assert( str != NULL );
 
1429
        repr->setAttribute("d", str);
 
1430
        g_free(str);
 
1431
 
 
1432
        /* Attach repr */
 
1433
        cc->newconn = SP_ITEM(desktop->currentLayer()->appendChildRepr(repr));
 
1434
        cc->newconn->transform = sp_item_i2doc_affine(SP_ITEM(desktop->currentLayer())).inverse();
 
1435
 
 
1436
        bool connection = false;
 
1437
        sp_object_setAttribute(cc->newconn, "inkscape:connector-type",
 
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)
 
1442
        {
 
1443
            sp_object_setAttribute(cc->newconn, "inkscape:connection-start",
 
1444
                    cc->shref, false);
 
1445
            if (cc->scpid)
 
1446
                sp_object_setAttribute(cc->newconn, "inkscape:connection-start-point",
 
1447
                        cc->scpid, false);
 
1448
            connection = true;
 
1449
        }
 
1450
 
 
1451
        if (cc->ehref)
 
1452
        {
 
1453
            sp_object_setAttribute(cc->newconn, "inkscape:connection-end",
 
1454
                    cc->ehref, false);
 
1455
            if (cc->ecpid)
 
1456
                sp_object_setAttribute(cc->newconn, "inkscape:connection-end-point",
 
1457
                        cc->ecpid, false);
 
1458
            connection = true;
 
1459
        }
 
1460
        // Process pending updates.
 
1461
        cc->newconn->updateRepr();
 
1462
        sp_document_ensure_up_to_date(doc);
 
1463
 
 
1464
        if (connection) {
 
1465
            // Adjust endpoints to shape edge.
 
1466
            sp_conn_reroute_path_immediate(SP_PATH(cc->newconn));
 
1467
            cc->newconn->updateRepr();
 
1468
        }
 
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);
 
1476
    }
 
1477
 
 
1478
    c->unref();
 
1479
 
 
1480
    sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR, _("Create connector"));
 
1481
}
 
1482
 
 
1483
 
 
1484
static void
 
1485
spcc_connector_finish_segment(SPConnectorContext *const cc, Geom::Point const /*p*/)
 
1486
{
 
1487
    if (!cc->red_curve->is_empty()) {
 
1488
        cc->green_curve->append_continuous(cc->red_curve, 0.0625);
 
1489
 
 
1490
        cc->p[0] = cc->p[3];
 
1491
        cc->p[1] = cc->p[4];
 
1492
        cc->npoints = 2;
 
1493
 
 
1494
        cc->red_curve->reset();
 
1495
    }
 
1496
}
 
1497
 
 
1498
 
 
1499
static void
 
1500
spcc_connector_finish(SPConnectorContext *const cc)
 
1501
{
 
1502
    SPDesktop *const desktop = cc->desktop;
 
1503
    desktop->messageStack()->flash(Inkscape::NORMAL_MESSAGE, _("Finishing connector"));
 
1504
 
 
1505
    cc->red_curve->reset();
 
1506
    spcc_concat_colors_and_flush(cc);
 
1507
 
 
1508
    cc->npoints = 0;
 
1509
 
 
1510
    if (cc->newConnRef) {
 
1511
        cc->newConnRef->removeFromGraph();
 
1512
        delete cc->newConnRef;
 
1513
        cc->newConnRef = NULL;
 
1514
    }
 
1515
}
 
1516
 
 
1517
 
 
1518
static gboolean
 
1519
cc_generic_knot_handler(SPCanvasItem *, GdkEvent *event, SPKnot *knot)
 
1520
{
 
1521
    g_assert (knot != NULL);
 
1522
 
 
1523
    g_object_ref(knot);
 
1524
 
 
1525
    SPConnectorContext *cc = SP_CONNECTOR_CONTEXT(
 
1526
            knot->desktop->event_context);
 
1527
 
 
1528
    gboolean consumed = FALSE;
 
1529
 
 
1530
    gchar* knot_tip = knot->tip ? knot->tip : cc->knot_tip;
 
1531
    switch (event->type) {
 
1532
        case GDK_ENTER_NOTIFY:
 
1533
            sp_knot_set_flag(knot, SP_KNOT_MOUSEOVER, TRUE);
 
1534
 
 
1535
            cc->active_handle = knot;
 
1536
            if (knot_tip)
 
1537
            {
 
1538
                knot->desktop->event_context->defaultMessageContext()->set(
 
1539
                        Inkscape::NORMAL_MESSAGE, knot_tip);
 
1540
            }
 
1541
 
 
1542
            consumed = TRUE;
 
1543
            break;
 
1544
        case GDK_LEAVE_NOTIFY:
 
1545
            sp_knot_set_flag(knot, SP_KNOT_MOUSEOVER, FALSE);
 
1546
 
 
1547
            cc->active_handle = NULL;
 
1548
 
 
1549
            if (knot_tip) {
 
1550
                knot->desktop->event_context->defaultMessageContext()->clear();
 
1551
            }
 
1552
 
 
1553
            consumed = TRUE;
 
1554
            break;
 
1555
        default:
 
1556
            break;
 
1557
    }
 
1558
 
 
1559
    g_object_unref(knot);
 
1560
 
 
1561
    return consumed;
 
1562
}
 
1563
 
 
1564
 
 
1565
static gboolean
 
1566
endpt_handler(SPKnot */*knot*/, GdkEvent *event, SPConnectorContext *cc)
 
1567
{
 
1568
    g_assert( SP_IS_CONNECTOR_CONTEXT(cc) );
 
1569
 
 
1570
    gboolean consumed = FALSE;
 
1571
 
 
1572
    switch (event->type) {
 
1573
        case GDK_BUTTON_PRESS:
 
1574
            g_assert( (cc->active_handle == cc->endpt_handle[0]) ||
 
1575
                      (cc->active_handle == cc->endpt_handle[1]) );
 
1576
            if (cc->state == SP_CONNECTOR_CONTEXT_IDLE) {
 
1577
                cc->clickeditem = cc->active_conn;
 
1578
                cc->clickedhandle = cc->active_handle;
 
1579
                cc_clear_active_conn(cc);
 
1580
                cc->state = SP_CONNECTOR_CONTEXT_REROUTING;
 
1581
 
 
1582
                // Disconnect from attached shape
 
1583
                unsigned ind = (cc->active_handle == cc->endpt_handle[0]) ? 0 : 1;
 
1584
                sp_conn_end_detach(cc->clickeditem, ind);
 
1585
 
 
1586
                Geom::Point origin;
 
1587
                if (cc->clickedhandle == cc->endpt_handle[0]) {
 
1588
                    origin = cc->endpt_handle[1]->pos;
 
1589
                }
 
1590
                else {
 
1591
                    origin = cc->endpt_handle[0]->pos;
 
1592
                }
 
1593
 
 
1594
                // Show the red path for dragging.
 
1595
                cc->red_curve = SP_PATH(cc->clickeditem)->original_curve ? SP_PATH(cc->clickeditem)->original_curve->copy() : SP_PATH(cc->clickeditem)->curve->copy();
 
1596
                Geom::Matrix i2d = sp_item_i2d_affine(cc->clickeditem);
 
1597
                cc->red_curve->transform(i2d);
 
1598
                sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(cc->red_bpath), cc->red_curve);
 
1599
 
 
1600
                cc->clickeditem->setHidden(true);
 
1601
 
 
1602
                // The rest of the interaction rerouting the connector is
 
1603
                // handled by the context root handler.
 
1604
                consumed = TRUE;
 
1605
            }
 
1606
            break;
 
1607
        default:
 
1608
            break;
 
1609
    }
 
1610
 
 
1611
    return consumed;
 
1612
}
 
1613
 
 
1614
static void cc_active_shape_add_knot(SPDesktop* desktop, SPItem* item, ConnectionPointMap &cphandles, ConnectionPoint& cp)
 
1615
{
 
1616
        SPKnot *knot = sp_knot_new(desktop, 0);
 
1617
 
 
1618
        knot->setShape(SP_KNOT_SHAPE_SQUARE);
 
1619
        knot->setSize(8);
 
1620
        knot->setAnchor(GTK_ANCHOR_CENTER);
 
1621
        knot->setFill(0xffffff00, 0xff0000ff, 0xff0000ff);
 
1622
        sp_knot_update_ctrl(knot);
 
1623
 
 
1624
        // We don't want to use the standard knot handler.
 
1625
        g_signal_handler_disconnect(G_OBJECT(knot->item),
 
1626
                knot->_event_handler_id);
 
1627
        knot->_event_handler_id = 0;
 
1628
 
 
1629
        gtk_signal_connect(GTK_OBJECT(knot->item), "event",
 
1630
                GTK_SIGNAL_FUNC(cc_generic_knot_handler), 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);
 
1688
    }
 
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);
 
1745
    }
 
1746
}
 
1747
 
 
1748
 
 
1749
static void
 
1750
cc_set_active_conn(SPConnectorContext *cc, SPItem *item)
 
1751
{
 
1752
    g_assert( SP_IS_PATH(item) );
 
1753
 
 
1754
    SPCurve *curve = SP_PATH(item)->original_curve ? SP_PATH(item)->original_curve : SP_PATH(item)->curve;
 
1755
    Geom::Matrix i2d = sp_item_i2d_affine(item);
 
1756
 
 
1757
    if (cc->active_conn == item)
 
1758
    {
 
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);
 
1771
 
 
1772
            Geom::Point endpt = *(curve->last_point()) * i2d;
 
1773
            sp_knot_set_position(cc->endpt_handle[1], endpt, 0);
 
1774
        }
 
1775
 
 
1776
        return;
 
1777
    }
 
1778
 
 
1779
    cc->active_conn = item;
 
1780
 
 
1781
    // Remove existing active conn listeners
 
1782
    if (cc->active_conn_repr) {
 
1783
        sp_repr_remove_listener_by_data(cc->active_conn_repr, cc);
 
1784
        Inkscape::GC::release(cc->active_conn_repr);
 
1785
        cc->active_conn_repr = NULL;
 
1786
    }
 
1787
 
 
1788
    // Listen in case the active conn changes
 
1789
    cc->active_conn_repr = SP_OBJECT_REPR(item);
 
1790
    if (cc->active_conn_repr) {
 
1791
        Inkscape::GC::anchor(cc->active_conn_repr);
 
1792
        sp_repr_add_listener(cc->active_conn_repr, &shape_repr_events, cc);
 
1793
    }
 
1794
 
 
1795
    for (int i = 0; i < 2; ++i) {
 
1796
 
 
1797
        // Create the handle if it doesn't exist
 
1798
        if ( cc->endpt_handle[i] == NULL ) {
 
1799
            SPKnot *knot = sp_knot_new(cc->desktop,
 
1800
                    _("<b>Connector endpoint</b>: drag to reroute or connect to new shapes"));
 
1801
 
 
1802
            knot->setShape(SP_KNOT_SHAPE_SQUARE);
 
1803
            knot->setSize(7);
 
1804
            knot->setAnchor(GTK_ANCHOR_CENTER);
 
1805
            knot->setFill(0xffffff00, 0xff0000ff, 0xff0000ff);
 
1806
            knot->setStroke(0x000000ff, 0x000000ff, 0x000000ff);
 
1807
            sp_knot_update_ctrl(knot);
 
1808
 
 
1809
            // We don't want to use the standard knot handler,
 
1810
            // since we don't want this knot to be draggable.
 
1811
            g_signal_handler_disconnect(G_OBJECT(knot->item),
 
1812
                    knot->_event_handler_id);
 
1813
            knot->_event_handler_id = 0;
 
1814
 
 
1815
            gtk_signal_connect(GTK_OBJECT(knot->item), "event",
 
1816
                    GTK_SIGNAL_FUNC(cc_generic_knot_handler), knot);
 
1817
 
 
1818
            cc->endpt_handle[i] = knot;
 
1819
        }
 
1820
 
 
1821
        // Remove any existing handlers
 
1822
        if (cc->endpt_handler_id[i]) {
 
1823
            g_signal_handlers_disconnect_by_func(
 
1824
                    G_OBJECT(cc->endpt_handle[i]->item),
 
1825
                    (void*)G_CALLBACK(endpt_handler), (gpointer) cc );
 
1826
            cc->endpt_handler_id[i] = 0;
 
1827
        }
 
1828
 
 
1829
        // Setup handlers for connector endpoints, this is
 
1830
        // is as 'after' so that cc_generic_knot_handler is
 
1831
        // triggered first for any endpoint.
 
1832
        cc->endpt_handler_id[i] = g_signal_connect_after(
 
1833
                G_OBJECT(cc->endpt_handle[i]->item), "event",
 
1834
                G_CALLBACK(endpt_handler), cc);
 
1835
    }
 
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
 
 
1844
    Geom::Point startpt = *(curve->first_point()) * i2d;
 
1845
    sp_knot_set_position(cc->endpt_handle[0], startpt, 0);
 
1846
 
 
1847
    Geom::Point endpt = *(curve->last_point()) * i2d;
 
1848
    sp_knot_set_position(cc->endpt_handle[1], endpt, 0);
 
1849
 
 
1850
    sp_knot_show(cc->endpt_handle[0]);
 
1851
    sp_knot_show(cc->endpt_handle[1]);
 
1852
}
 
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
}
 
1883
 
 
1884
static bool cc_item_is_shape(SPItem *item)
 
1885
{
 
1886
    if (SP_IS_PATH(item)) {
 
1887
        SPCurve *curve = (SP_SHAPE(item))->curve;
 
1888
        if ( curve && !(curve->is_closed()) ) {
 
1889
            // Open paths are connectors.
 
1890
            return false;
 
1891
        }
 
1892
    }
 
1893
    else if (SP_IS_TEXT(item) || SP_IS_FLOWTEXT(item)) {
 
1894
        Inkscape::Preferences *prefs = Inkscape::Preferences::get();
 
1895
        if (prefs->getBool("/tools/connector/ignoretext", true)) {
 
1896
            // Don't count text as a shape we can connect connector to.
 
1897
            return false;
 
1898
        }
 
1899
    }
 
1900
    return true;
 
1901
}
 
1902
 
 
1903
 
 
1904
bool cc_item_is_connector(SPItem *item)
 
1905
{
 
1906
    if (SP_IS_PATH(item)) {
 
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.
 
1911
            return true;
 
1912
        }
 
1913
    }
 
1914
    return false;
 
1915
}
 
1916
 
 
1917
 
 
1918
void cc_selection_set_avoid(bool const set_avoid)
 
1919
{
 
1920
    SPDesktop *desktop = inkscape_active_desktop();
 
1921
    if (desktop == NULL) {
 
1922
        return;
 
1923
    }
 
1924
 
 
1925
    SPDocument *document = sp_desktop_document(desktop);
 
1926
 
 
1927
    Inkscape::Selection *selection = sp_desktop_selection(desktop);
 
1928
 
 
1929
    GSList *l = (GSList *) selection->itemList();
 
1930
 
 
1931
    int changes = 0;
 
1932
 
 
1933
    while (l) {
 
1934
        SPItem *item = (SPItem *) l->data;
 
1935
 
 
1936
        char const *value = (set_avoid) ? "true" : NULL;
 
1937
 
 
1938
        if (cc_item_is_shape(item)) {
 
1939
            sp_object_setAttribute(item, "inkscape:connector-avoid",
 
1940
                    value, false);
 
1941
            item->avoidRef->handleSettingChange();
 
1942
            changes++;
 
1943
        }
 
1944
 
 
1945
        l = l->next;
 
1946
    }
 
1947
 
 
1948
    if (changes == 0) {
 
1949
        desktop->messageStack()->flash(Inkscape::WARNING_MESSAGE,
 
1950
                _("Select <b>at least one non-connector object</b>."));
 
1951
        return;
 
1952
    }
 
1953
 
 
1954
    char *event_desc = (set_avoid) ?
 
1955
            _("Make connectors avoid selected objects") :
 
1956
            _("Make connectors ignore selected objects");
 
1957
    sp_document_done(document, SP_VERB_CONTEXT_CONNECTOR, event_desc);
 
1958
}
 
1959
 
 
1960
 
 
1961
static void
 
1962
cc_selection_changed(Inkscape::Selection *selection, gpointer data)
 
1963
{
 
1964
    SPConnectorContext *cc = SP_CONNECTOR_CONTEXT(data);
 
1965
    //SPEventContext *ec = SP_EVENT_CONTEXT(cc);
 
1966
 
 
1967
    SPItem *item = selection->singleItem();
 
1968
 
 
1969
    if (cc->active_conn == item)
 
1970
    {
 
1971
        // Nothing to change.
 
1972
        return;
 
1973
    }
 
1974
    if (item == NULL)
 
1975
    {
 
1976
        cc_clear_active_conn(cc);
 
1977
        return;
 
1978
    }
 
1979
 
 
1980
    if (cc_item_is_connector(item)) {
 
1981
        cc_set_active_conn(cc, item);
 
1982
    }
 
1983
}
 
1984
 
 
1985
 
 
1986
static void
 
1987
shape_event_attr_deleted(Inkscape::XML::Node */*repr*/, Inkscape::XML::Node *child,
 
1988
                         Inkscape::XML::Node */*ref*/, gpointer data)
 
1989
{
 
1990
    g_assert(data);
 
1991
    SPConnectorContext *cc = SP_CONNECTOR_CONTEXT(data);
 
1992
 
 
1993
    if (child == cc->active_shape_repr) {
 
1994
        // The active shape has been deleted.  Clear active shape.
 
1995
        cc_clear_active_shape(cc);
 
1996
    }
 
1997
}
 
1998
 
 
1999
 
 
2000
static void
 
2001
shape_event_attr_changed(Inkscape::XML::Node *repr, gchar const *name,
 
2002
                         gchar const */*old_value*/, gchar const */*new_value*/,
 
2003
                         bool /*is_interactive*/, gpointer data)
 
2004
{
 
2005
    g_assert(data);
 
2006
    SPConnectorContext *cc = SP_CONNECTOR_CONTEXT(data);
 
2007
 
 
2008
    // Look for changes that result in onscreen movement.
 
2009
    if (!strcmp(name, "d") || !strcmp(name, "x") || !strcmp(name, "y") ||
 
2010
            !strcmp(name, "width") || !strcmp(name, "height") ||
 
2011
            !strcmp(name, "transform"))
 
2012
    {
 
2013
        if (repr == cc->active_shape_repr) {
 
2014
            // Active shape has moved. Clear active shape.
 
2015
            cc_clear_active_shape(cc);
 
2016
        }
 
2017
        else if (repr == cc->active_conn_repr) {
 
2018
            // The active conn has been moved.
 
2019
            // Set it again, which just sets new handle positions.
 
2020
            cc_set_active_conn(cc, cc->active_conn);
 
2021
        }
 
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);
 
2029
}
 
2030
 
 
2031
 
 
2032
/*
 
2033
  Local Variables:
 
2034
  mode:c++
 
2035
  c-file-style:"stroustrup"
 
2036
  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
 
2037
  indent-tabs-mode:nil
 
2038
  fill-column:99
 
2039
  End:
 
2040
*/
 
2041
// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :