~valavanisalex/ubuntu/precise/inkscape/fix-943984

« back to all changes in this revision

Viewing changes to inkscape-0.47pre1/src/connector-context.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Bryce Harrington
  • Date: 2009-07-02 17:09:45 UTC
  • mfrom: (1.1.9 upstream)
  • Revision ID: james.westby@ubuntu.com-20090702170945-nn6d6zswovbwju1t
Tags: 0.47~pre1-0ubuntu1
* New upstream release.
  - Don't constrain maximization on small resolution devices (pre0)
    (LP: #348842)
  - Fixes segfault on startup (pre0)
    (LP: #391149)

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 Michael Wybrow
 
8
 *
 
9
 * Released under GNU GPL, read the file 'COPYING' for more information
 
10
 *
 
11
 * 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
 *  o  Show a visual indicator for objects with the 'avoid' property set.
 
20
 *  o  Allow user to change a object between a path and connector through
 
21
 *     the interface.
 
22
 *  o  Create an interface for setting markers (arrow heads).
 
23
 *  o  Better distinguish between paths and connectors to prevent problems
 
24
 *     in the node tool and paths accidently being turned into connectors
 
25
 *     in the connector tool.  Perhaps have a way to convert between.
 
26
 *  o  Only call libavoid's updateEndPoint as required.  Currently we do it
 
27
 *     for both endpoints, even if only one is moving.
 
28
 *  o  Allow user-placeable connection points.
 
29
 *  o  Deal sanely with connectors with both endpoints attached to the
 
30
 *     same connection point, and drawing of connectors attaching
 
31
 *     overlaping shapes (currently tries to adjust connector to be
 
32
 *     outside both bounding boxes).
 
33
 *  o  Fix many special cases related to connectors updating,
 
34
 *     e.g., copying a couple of shapes and a connector that are
 
35
 *           attached to each other.
 
36
 *     e.g., detach connector when it is moved or transformed in
 
37
 *           one of the other contexts.
 
38
 *  o  Cope with shapes whose ids change when they have attached
 
39
 *     connectors.
 
40
 *  o  gobble_motion_events(GDK_BUTTON1_MASK)?;
 
41
 *
 
42
 */
 
43
 
 
44
#include <gdk/gdkkeysyms.h>
 
45
#include <string>
 
46
#include <cstring>
 
47
 
 
48
#include "connector-context.h"
 
49
#include "pixmaps/cursor-connector.xpm"
 
50
#include "xml/node-event-vector.h"
 
51
#include "xml/repr.h"
 
52
#include "svg/svg.h"
 
53
#include "desktop.h"
 
54
#include "desktop-style.h"
 
55
#include "desktop-handles.h"
 
56
#include "document.h"
 
57
#include "message-context.h"
 
58
#include "message-stack.h"
 
59
#include "selection.h"
 
60
#include "inkscape.h"
 
61
#include "preferences.h"
 
62
#include "sp-path.h"
 
63
#include "display/canvas-bpath.h"
 
64
#include "display/sodipodi-ctrl.h"
 
65
#include <glibmm/i18n.h>
 
66
#include "snap.h"
 
67
#include "knot.h"
 
68
#include "sp-conn-end.h"
 
69
#include "conn-avoid-ref.h"
 
70
#include "libavoid/vertices.h"
 
71
#include "context-fns.h"
 
72
#include "sp-namedview.h"
 
73
#include "sp-text.h"
 
74
#include "sp-flowtext.h"
 
75
#include "display/curve.h"
 
76
 
 
77
static void sp_connector_context_class_init(SPConnectorContextClass *klass);
 
78
static void sp_connector_context_init(SPConnectorContext *conn_context);
 
79
static void sp_connector_context_dispose(GObject *object);
 
80
 
 
81
static void sp_connector_context_setup(SPEventContext *ec);
 
82
static void sp_connector_context_finish(SPEventContext *ec);
 
83
static gint sp_connector_context_root_handler(SPEventContext *ec, GdkEvent *event);
 
84
static gint sp_connector_context_item_handler(SPEventContext *event_context, SPItem *item, GdkEvent *event);
 
85
 
 
86
// Stuff borrowed from DrawContext
 
87
static void spcc_connector_set_initial_point(SPConnectorContext *cc, Geom::Point const p);
 
88
static void spcc_connector_set_subsequent_point(SPConnectorContext *cc, Geom::Point const p);
 
89
static void spcc_connector_finish_segment(SPConnectorContext *cc, Geom::Point p);
 
90
static void spcc_reset_colors(SPConnectorContext *cc);
 
91
static void spcc_connector_finish(SPConnectorContext *cc);
 
92
static void spcc_concat_colors_and_flush(SPConnectorContext *cc);
 
93
static void spcc_flush_white(SPConnectorContext *cc, SPCurve *gc);
 
94
 
 
95
// Context event handlers
 
96
static gint connector_handle_button_press(SPConnectorContext *const cc, GdkEventButton const &bevent);
 
97
static gint connector_handle_motion_notify(SPConnectorContext *const cc, GdkEventMotion const &mevent);
 
98
static gint connector_handle_button_release(SPConnectorContext *const cc, GdkEventButton const &revent);
 
99
static gint connector_handle_key_press(SPConnectorContext *const cc, guint const keyval);
 
100
 
 
101
static void cc_set_active_shape(SPConnectorContext *cc, SPItem *item);
 
102
static void cc_clear_active_shape(SPConnectorContext *cc);
 
103
static void cc_set_active_conn(SPConnectorContext *cc, SPItem *item);
 
104
static void cc_clear_active_conn(SPConnectorContext *cc);
 
105
static gchar *conn_pt_handle_test(SPConnectorContext *cc, Geom::Point& w);
 
106
static bool cc_item_is_shape(SPItem *item);
 
107
static void cc_selection_changed(Inkscape::Selection *selection, gpointer data);
 
108
static void cc_connector_rerouting_finish(SPConnectorContext *const cc,
 
109
        Geom::Point *const p);
 
110
 
 
111
static void shape_event_attr_deleted(Inkscape::XML::Node *repr,
 
112
        Inkscape::XML::Node *child, Inkscape::XML::Node *ref, gpointer data);
 
113
static void shape_event_attr_changed(Inkscape::XML::Node *repr, gchar const *name,
 
114
        gchar const *old_value, gchar const *new_value, bool is_interactive,
 
115
        gpointer data);
 
116
 
 
117
 
 
118
static Geom::Point connector_drag_origin_w(0, 0);
 
119
static bool connector_within_tolerance = false;
 
120
static SPEventContextClass *parent_class;
 
121
 
 
122
 
 
123
static Inkscape::XML::NodeEventVector shape_repr_events = {
 
124
    NULL, /* child_added */
 
125
    NULL, /* child_added */
 
126
    shape_event_attr_changed,
 
127
    NULL, /* content_changed */
 
128
    NULL  /* order_changed */
 
129
};
 
130
 
 
131
static Inkscape::XML::NodeEventVector layer_repr_events = {
 
132
    NULL, /* child_added */
 
133
    shape_event_attr_deleted,
 
134
    NULL, /* child_added */
 
135
    NULL, /* content_changed */
 
136
    NULL  /* order_changed */
 
137
};
 
138
 
 
139
 
 
140
GType
 
141
sp_connector_context_get_type(void)
 
142
{
 
143
    static GType type = 0;
 
144
    if (!type) {
 
145
        GTypeInfo info = {
 
146
            sizeof(SPConnectorContextClass),
 
147
            NULL, NULL,
 
148
            (GClassInitFunc) sp_connector_context_class_init,
 
149
            NULL, NULL,
 
150
            sizeof(SPConnectorContext),
 
151
            4,
 
152
            (GInstanceInitFunc) sp_connector_context_init,
 
153
            NULL,   /* value_table */
 
154
        };
 
155
        type = g_type_register_static(SP_TYPE_EVENT_CONTEXT, "SPConnectorContext", &info, (GTypeFlags)0);
 
156
    }
 
157
    return type;
 
158
}
 
159
 
 
160
static void
 
161
sp_connector_context_class_init(SPConnectorContextClass *klass)
 
162
{
 
163
    GObjectClass *object_class;
 
164
    SPEventContextClass *event_context_class;
 
165
 
 
166
    object_class = (GObjectClass *) klass;
 
167
    event_context_class = (SPEventContextClass *) klass;
 
168
 
 
169
    parent_class = (SPEventContextClass*)g_type_class_peek_parent(klass);
 
170
 
 
171
    object_class->dispose = sp_connector_context_dispose;
 
172
 
 
173
    event_context_class->setup = sp_connector_context_setup;
 
174
    event_context_class->finish = sp_connector_context_finish;
 
175
    event_context_class->root_handler = sp_connector_context_root_handler;
 
176
    event_context_class->item_handler = sp_connector_context_item_handler;
 
177
}
 
178
 
 
179
 
 
180
static void
 
181
sp_connector_context_init(SPConnectorContext *cc)
 
182
{
 
183
    SPEventContext *ec = SP_EVENT_CONTEXT(cc);
 
184
 
 
185
    ec->cursor_shape = cursor_connector_xpm;
 
186
    ec->hot_x = 1;
 
187
    ec->hot_y = 1;
 
188
    ec->xp = 0;
 
189
    ec->yp = 0;
 
190
 
 
191
    cc->red_color = 0xff00007f;
 
192
 
 
193
    cc->newconn = NULL;
 
194
    cc->newConnRef = NULL;
 
195
 
 
196
    cc->sel_changed_connection = sigc::connection();
 
197
 
 
198
    cc->active_shape = NULL;
 
199
    cc->active_shape_repr = NULL;
 
200
    cc->active_shape_layer_repr = NULL;
 
201
 
 
202
    cc->active_conn = NULL;
 
203
    cc->active_conn_repr = NULL;
 
204
 
 
205
    cc->active_handle = NULL;
 
206
 
 
207
    cc->clickeditem = NULL;
 
208
    cc->clickedhandle = NULL;
 
209
 
 
210
    cc->connpthandle = NULL;
 
211
    for (int i = 0; i < 2; ++i) {
 
212
        cc->endpt_handle[i] = NULL;
 
213
        cc->endpt_handler_id[i] = 0;
 
214
    }
 
215
    cc->sid = NULL;
 
216
    cc->eid = NULL;
 
217
    cc->npoints = 0;
 
218
    cc->state = SP_CONNECTOR_CONTEXT_IDLE;
 
219
}
 
220
 
 
221
 
 
222
static void
 
223
sp_connector_context_dispose(GObject *object)
 
224
{
 
225
    SPConnectorContext *cc = SP_CONNECTOR_CONTEXT(object);
 
226
 
 
227
    cc->sel_changed_connection.disconnect();
 
228
 
 
229
    if (cc->connpthandle) {
 
230
        g_object_unref(cc->connpthandle);
 
231
        cc->connpthandle = NULL;
 
232
    }
 
233
    for (int i = 0; i < 2; ++i) {
 
234
        if (cc->endpt_handle[1]) {
 
235
            g_object_unref(cc->endpt_handle[i]);
 
236
            cc->endpt_handle[i] = NULL;
 
237
        }
 
238
    }
 
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;
 
246
    }
 
247
    g_assert( cc->newConnRef == NULL );
 
248
 
 
249
    G_OBJECT_CLASS(parent_class)->dispose(object);
 
250
}
 
251
 
 
252
 
 
253
static void
 
254
sp_connector_context_setup(SPEventContext *ec)
 
255
{
 
256
    SPConnectorContext *cc = SP_CONNECTOR_CONTEXT(ec);
 
257
    SPDesktop *dt = ec->desktop;
 
258
 
 
259
    if (((SPEventContextClass *) parent_class)->setup) {
 
260
        ((SPEventContextClass *) parent_class)->setup(ec);
 
261
    }
 
262
 
 
263
    cc->selection = sp_desktop_selection(dt);
 
264
 
 
265
    cc->sel_changed_connection.disconnect();
 
266
    cc->sel_changed_connection = cc->selection->connectChanged(
 
267
            sigc::bind(sigc::ptr_fun(&cc_selection_changed),
 
268
            (gpointer) cc));
 
269
 
 
270
    /* Create red bpath */
 
271
    cc->red_bpath = sp_canvas_bpath_new(sp_desktop_sketch(ec->desktop), NULL);
 
272
    sp_canvas_bpath_set_stroke(SP_CANVAS_BPATH(cc->red_bpath), cc->red_color,
 
273
            1.0, SP_STROKE_LINEJOIN_MITER, SP_STROKE_LINECAP_BUTT);
 
274
    sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(cc->red_bpath), 0x00000000,
 
275
            SP_WIND_RULE_NONZERO);
 
276
    /* Create red curve */
 
277
    cc->red_curve = new SPCurve();
 
278
 
 
279
    /* Create green curve */
 
280
    cc->green_curve = new SPCurve();
 
281
 
 
282
    // Notice the initial selection.
 
283
    cc_selection_changed(cc->selection, (gpointer) cc);
 
284
 
 
285
    Inkscape::Preferences *prefs = Inkscape::Preferences::get();
 
286
    if (prefs->getBool("/tools/connector/selcue", 0)) {
 
287
        ec->enableSelectionCue();
 
288
    }
 
289
 
 
290
    // Make sure we see all enter events for canvas items,
 
291
    // even if a mouse button is depressed.
 
292
    dt->canvas->gen_all_enter_events = true;
 
293
}
 
294
 
 
295
 
 
296
static void
 
297
sp_connector_context_finish(SPEventContext *ec)
 
298
{
 
299
    SPConnectorContext *cc = SP_CONNECTOR_CONTEXT(ec);
 
300
 
 
301
    spcc_connector_finish(cc);
 
302
    cc->state = SP_CONNECTOR_CONTEXT_IDLE;
 
303
 
 
304
    if (((SPEventContextClass *) parent_class)->finish) {
 
305
        ((SPEventContextClass *) parent_class)->finish(ec);
 
306
    }
 
307
 
 
308
    if (cc->selection) {
 
309
        cc->selection = NULL;
 
310
    }
 
311
    cc_clear_active_shape(cc);
 
312
    cc_clear_active_conn(cc);
 
313
 
 
314
    // Restore the default event generating behaviour.
 
315
    SPDesktop *desktop = SP_EVENT_CONTEXT_DESKTOP(ec);
 
316
    desktop->canvas->gen_all_enter_events = false;
 
317
}
 
318
 
 
319
 
 
320
//-----------------------------------------------------------------------------
 
321
 
 
322
 
 
323
static void
 
324
cc_clear_active_shape(SPConnectorContext *cc)
 
325
{
 
326
    if (cc->active_shape == NULL) {
 
327
        return;
 
328
    }
 
329
    g_assert( cc->active_shape_repr );
 
330
    g_assert( cc->active_shape_layer_repr );
 
331
 
 
332
    cc->active_shape = NULL;
 
333
 
 
334
    if (cc->active_shape_repr) {
 
335
        sp_repr_remove_listener_by_data(cc->active_shape_repr, cc);
 
336
        Inkscape::GC::release(cc->active_shape_repr);
 
337
        cc->active_shape_repr = NULL;
 
338
 
 
339
        sp_repr_remove_listener_by_data(cc->active_shape_layer_repr, cc);
 
340
        Inkscape::GC::release(cc->active_shape_layer_repr);
 
341
        cc->active_shape_layer_repr = NULL;
 
342
    }
 
343
 
 
344
    // Hide the center connection point if it exists.
 
345
    if (cc->connpthandle) {
 
346
        sp_knot_hide(cc->connpthandle);
 
347
    }
 
348
}
 
349
 
 
350
 
 
351
static void
 
352
cc_clear_active_conn(SPConnectorContext *cc)
 
353
{
 
354
    if (cc->active_conn == NULL) {
 
355
        return;
 
356
    }
 
357
    g_assert( cc->active_conn_repr );
 
358
 
 
359
    cc->active_conn = NULL;
 
360
 
 
361
    if (cc->active_conn_repr) {
 
362
        sp_repr_remove_listener_by_data(cc->active_conn_repr, cc);
 
363
        Inkscape::GC::release(cc->active_conn_repr);
 
364
        cc->active_conn_repr = NULL;
 
365
    }
 
366
 
 
367
    // Hide the endpoint handles.
 
368
    for (int i = 0; i < 2; ++i) {
 
369
        if (cc->endpt_handle[i]) {
 
370
            sp_knot_hide(cc->endpt_handle[i]);
 
371
        }
 
372
    }
 
373
}
 
374
 
 
375
 
 
376
static gchar *
 
377
conn_pt_handle_test(SPConnectorContext *cc, Geom::Point& p)
 
378
{
 
379
    // TODO: this will need to change when there are more connection
 
380
    //       points available for each shape.
 
381
 
 
382
    SPKnot *centerpt = cc->connpthandle;
 
383
    if (cc->active_handle && (cc->active_handle == centerpt))
 
384
    {
 
385
        p = centerpt->pos;
 
386
        return g_strdup_printf("#%s", SP_OBJECT_ID(cc->active_shape));
 
387
    }
 
388
    return NULL;
 
389
}
 
390
 
 
391
 
 
392
 
 
393
static gint
 
394
sp_connector_context_item_handler(SPEventContext *event_context, SPItem *item, GdkEvent *event)
 
395
{
 
396
    gint ret = FALSE;
 
397
 
 
398
    SPDesktop *desktop = event_context->desktop;
 
399
 
 
400
    SPConnectorContext *cc = SP_CONNECTOR_CONTEXT(event_context);
 
401
 
 
402
    Geom::Point p(event->button.x, event->button.y);
 
403
 
 
404
    switch (event->type) {
 
405
        case GDK_BUTTON_RELEASE:
 
406
            if (event->button.button == 1 && !event_context->space_panning) {
 
407
                if ((cc->state == SP_CONNECTOR_CONTEXT_DRAGGING) &&
 
408
                        (connector_within_tolerance))
 
409
                {
 
410
                    spcc_reset_colors(cc);
 
411
                    cc->state = SP_CONNECTOR_CONTEXT_IDLE;
 
412
                    sp_event_context_snap_window_closed(event_context);
 
413
                }
 
414
                if (cc->state != SP_CONNECTOR_CONTEXT_IDLE) {
 
415
                    // Doing simething else like rerouting.
 
416
                    break;
 
417
                }
 
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);
 
421
 
 
422
                if (event->button.state & GDK_SHIFT_MASK) {
 
423
                    cc->selection->toggle(item_ungrouped);
 
424
                } else {
 
425
                    cc->selection->set(item_ungrouped);
 
426
                }
 
427
                ret = TRUE;
 
428
 
 
429
            }
 
430
            break;
 
431
        case GDK_ENTER_NOTIFY:
 
432
        {
 
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))) {
 
438
                    cc_set_active_shape(cc, item);
 
439
                }
 
440
            }
 
441
            ret = TRUE;
 
442
            break;
 
443
        }
 
444
        default:
 
445
            break;
 
446
    }
 
447
 
 
448
    return ret;
 
449
}
 
450
 
 
451
 
 
452
gint
 
453
sp_connector_context_root_handler(SPEventContext *ec, GdkEvent *event)
 
454
{
 
455
    SPConnectorContext *const cc = SP_CONNECTOR_CONTEXT(ec);
 
456
 
 
457
    gint ret = FALSE;
 
458
 
 
459
    switch (event->type) {
 
460
        case GDK_BUTTON_PRESS:
 
461
            ret = connector_handle_button_press(cc, event->button);
 
462
            break;
 
463
 
 
464
        case GDK_MOTION_NOTIFY:
 
465
                ret = connector_handle_motion_notify(cc, event->motion);
 
466
            break;
 
467
 
 
468
        case GDK_BUTTON_RELEASE:
 
469
            ret = connector_handle_button_release(cc, event->button);
 
470
            break;
 
471
        case GDK_KEY_PRESS:
 
472
            ret = connector_handle_key_press(cc, get_group0_keyval (&event->key));
 
473
            break;
 
474
 
 
475
        default:
 
476
            break;
 
477
    }
 
478
 
 
479
    if (!ret) {
 
480
        gint (*const parent_root_handler)(SPEventContext *, GdkEvent *)
 
481
            = ((SPEventContextClass *) parent_class)->root_handler;
 
482
        if (parent_root_handler) {
 
483
            ret = parent_root_handler(ec, event);
 
484
        }
 
485
    }
 
486
 
 
487
    return ret;
 
488
}
 
489
 
 
490
 
 
491
static gint
 
492
connector_handle_button_press(SPConnectorContext *const cc, GdkEventButton const &bevent)
 
493
{
 
494
    Geom::Point const event_w(bevent.x, bevent.y);
 
495
    /* Find desktop coordinates */
 
496
    Geom::Point p = cc->desktop->w2d(event_w);
 
497
    SPEventContext *event_context = SP_EVENT_CONTEXT(cc);
 
498
 
 
499
    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
 
 
532
                    // Test whether we clicked on a connection point
 
533
                    cc->sid = conn_pt_handle_test(cc, p);
 
534
 
 
535
                    sp_event_context_snap_window_open(event_context);
 
536
 
 
537
                    if (!cc->sid) {
 
538
                        // This is the first point, so just snap it to the grid
 
539
                        // as there's no other points to go off.
 
540
                        m.freeSnapReturnByRef(Inkscape::SnapPreferences::SNAPPOINT_NODE, p, Inkscape::SNAPSOURCE_HANDLE);
 
541
                    }
 
542
                    spcc_connector_set_initial_point(cc, p);
 
543
 
 
544
                }
 
545
                cc->state = SP_CONNECTOR_CONTEXT_DRAGGING;
 
546
                ret = TRUE;
 
547
                break;
 
548
            }
 
549
            case SP_CONNECTOR_CONTEXT_DRAGGING:
 
550
            {
 
551
                // This is the second click of a connector creation.
 
552
                m.freeSnapReturnByRef(Inkscape::SnapPreferences::SNAPPOINT_NODE, p, Inkscape::SNAPSOURCE_HANDLE);
 
553
 
 
554
                spcc_connector_set_subsequent_point(cc, p);
 
555
                spcc_connector_finish_segment(cc, p);
 
556
                // Test whether we clicked on a connection point
 
557
                cc->eid = conn_pt_handle_test(cc, p);
 
558
                if (cc->npoints != 0) {
 
559
                    spcc_connector_finish(cc);
 
560
                }
 
561
                cc_set_active_conn(cc, cc->newconn);
 
562
                cc->state = SP_CONNECTOR_CONTEXT_IDLE;
 
563
                sp_event_context_snap_window_closed(event_context);
 
564
                ret = TRUE;
 
565
                break;
 
566
            }
 
567
            case SP_CONNECTOR_CONTEXT_CLOSE:
 
568
            {
 
569
                g_warning("Button down in CLOSE state");
 
570
                break;
 
571
            }
 
572
            default:
 
573
                break;
 
574
        }
 
575
    } else if (bevent.button == 3) {
 
576
        if (cc->state == SP_CONNECTOR_CONTEXT_REROUTING) {
 
577
            // A context menu is going to be triggered here,
 
578
            // so end the rerouting operation.
 
579
            cc_connector_rerouting_finish(cc, &p);
 
580
 
 
581
            cc->state = SP_CONNECTOR_CONTEXT_IDLE;
 
582
            sp_event_context_snap_window_closed(event_context);
 
583
 
 
584
            // Don't set ret to TRUE, so we drop through to the
 
585
            // parent handler which will open the context menu.
 
586
        }
 
587
        else if (cc->npoints != 0) {
 
588
            spcc_connector_finish(cc);
 
589
            cc->state = SP_CONNECTOR_CONTEXT_IDLE;
 
590
            sp_event_context_snap_window_closed(event_context);
 
591
            ret = TRUE;
 
592
        }
 
593
    }
 
594
    return ret;
 
595
}
 
596
 
 
597
 
 
598
static gint
 
599
connector_handle_motion_notify(SPConnectorContext *const cc, GdkEventMotion const &mevent)
 
600
{
 
601
    gint ret = FALSE;
 
602
    SPEventContext *event_context = SP_EVENT_CONTEXT(cc);
 
603
    Inkscape::Preferences *prefs = Inkscape::Preferences::get();
 
604
 
 
605
    if (event_context->space_panning || mevent.state & GDK_BUTTON2_MASK || mevent.state & GDK_BUTTON3_MASK) {
 
606
        // allow middle-button scrolling
 
607
        return FALSE;
 
608
    }
 
609
 
 
610
    Geom::Point const event_w(mevent.x, mevent.y);
 
611
 
 
612
    if (connector_within_tolerance) {
 
613
        gint const tolerance = prefs->getIntLimited("/options/dragtolerance/value", 0, 0, 100);
 
614
        if ( Geom::LInfty( event_w - connector_drag_origin_w ) < tolerance ) {
 
615
            return FALSE;   // Do not drag if we're within tolerance from origin.
 
616
        }
 
617
    }
 
618
    // Once the user has moved farther than tolerance from the original location
 
619
    // (indicating they intend to move the object, not click), then always process
 
620
    // the motion notify coordinates as given (no snapping back to origin)
 
621
    connector_within_tolerance = false;
 
622
 
 
623
    SPDesktop *const dt = cc->desktop;
 
624
 
 
625
    /* Find desktop coordinates */
 
626
    Geom::Point p = dt->w2d(event_w);
 
627
 
 
628
    SnapManager &m = dt->namedview->snap_manager;
 
629
        m.setup(dt);
 
630
 
 
631
    switch (cc->state) {
 
632
        case SP_CONNECTOR_CONTEXT_DRAGGING:
 
633
        {
 
634
            // This is movement during a connector creation.
 
635
                if ( cc->npoints > 0 ) {
 
636
                        m.freeSnapReturnByRef(Inkscape::SnapPreferences::SNAPPOINT_NODE, p, Inkscape::SNAPSOURCE_HANDLE);
 
637
                        cc->selection->clear();
 
638
                spcc_connector_set_subsequent_point(cc, p);
 
639
                ret = TRUE;
 
640
            }
 
641
            break;
 
642
        }
 
643
        case SP_CONNECTOR_CONTEXT_REROUTING:
 
644
        {
 
645
            g_assert( SP_IS_PATH(cc->clickeditem));
 
646
 
 
647
            m.freeSnapReturnByRef(Inkscape::SnapPreferences::SNAPPOINT_NODE, p, Inkscape::SNAPSOURCE_HANDLE);
 
648
 
 
649
            // Update the hidden path
 
650
            Geom::Matrix i2d = sp_item_i2d_affine(cc->clickeditem);
 
651
            Geom::Matrix d2i = i2d.inverse();
 
652
            SPPath *path = SP_PATH(cc->clickeditem);
 
653
            SPCurve *curve = (SP_SHAPE(path))->curve;
 
654
            if (cc->clickedhandle == cc->endpt_handle[0]) {
 
655
                Geom::Point o = cc->endpt_handle[1]->pos;
 
656
                curve->stretch_endpoints(p * d2i, o * d2i);
 
657
            }
 
658
            else {
 
659
                Geom::Point o = cc->endpt_handle[0]->pos;
 
660
                curve->stretch_endpoints(o * d2i, p * d2i);
 
661
            }
 
662
            sp_conn_adjust_path(path);
 
663
 
 
664
            // Copy this to the temporary visible path
 
665
            cc->red_curve = SP_SHAPE(path)->curve->copy();
 
666
            cc->red_curve->transform(i2d);
 
667
 
 
668
            sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(cc->red_bpath), cc->red_curve);
 
669
            ret = TRUE;
 
670
            break;
 
671
        }
 
672
        case SP_CONNECTOR_CONTEXT_STOP:
 
673
            /* This is perfectly valid */
 
674
            break;
 
675
        default:
 
676
            break;
 
677
    }
 
678
 
 
679
    return ret;
 
680
}
 
681
 
 
682
 
 
683
static gint
 
684
connector_handle_button_release(SPConnectorContext *const cc, GdkEventButton const &revent)
 
685
{
 
686
    gint ret = FALSE;
 
687
    SPEventContext *event_context = SP_EVENT_CONTEXT(cc);
 
688
    if ( revent.button == 1 && !event_context->space_panning ) {
 
689
 
 
690
        SPDesktop *desktop = SP_EVENT_CONTEXT_DESKTOP(cc);
 
691
        SPDocument *doc = sp_desktop_document(desktop);
 
692
 
 
693
        SnapManager &m = desktop->namedview->snap_manager;
 
694
                m.setup(desktop);
 
695
 
 
696
        Geom::Point const event_w(revent.x, revent.y);
 
697
 
 
698
        /* Find desktop coordinates */
 
699
        Geom::Point p = cc->desktop->w2d(event_w);
 
700
 
 
701
        switch (cc->state) {
 
702
            //case SP_CONNECTOR_CONTEXT_POINT:
 
703
            case SP_CONNECTOR_CONTEXT_DRAGGING:
 
704
            {
 
705
                m.freeSnapReturnByRef(Inkscape::SnapPreferences::SNAPPOINT_NODE, p, Inkscape::SNAPSOURCE_HANDLE);
 
706
 
 
707
                if (connector_within_tolerance)
 
708
                {
 
709
                    spcc_connector_finish_segment(cc, p);
 
710
                    return TRUE;
 
711
                }
 
712
                // Connector has been created via a drag, end it now.
 
713
                spcc_connector_set_subsequent_point(cc, p);
 
714
                spcc_connector_finish_segment(cc, p);
 
715
                // Test whether we clicked on a connection point
 
716
                cc->eid = conn_pt_handle_test(cc, p);
 
717
                if (cc->npoints != 0) {
 
718
                    spcc_connector_finish(cc);
 
719
                }
 
720
                cc_set_active_conn(cc, cc->newconn);
 
721
                cc->state = SP_CONNECTOR_CONTEXT_IDLE;
 
722
                sp_event_context_snap_window_closed(event_context);
 
723
                break;
 
724
            }
 
725
            case SP_CONNECTOR_CONTEXT_REROUTING:
 
726
            {
 
727
                m.freeSnapReturnByRef(Inkscape::SnapPreferences::SNAPPOINT_NODE, p, Inkscape::SNAPSOURCE_HANDLE);
 
728
                cc_connector_rerouting_finish(cc, &p);
 
729
 
 
730
                sp_document_ensure_up_to_date(doc);
 
731
                cc->state = SP_CONNECTOR_CONTEXT_IDLE;
 
732
                sp_event_context_snap_window_closed(event_context);
 
733
                return TRUE;
 
734
                break;
 
735
            }
 
736
            case SP_CONNECTOR_CONTEXT_STOP:
 
737
                /* This is allowed, if we just cancelled curve */
 
738
                break;
 
739
            default:
 
740
                break;
 
741
        }
 
742
        ret = TRUE;
 
743
    }
 
744
 
 
745
    return ret;
 
746
}
 
747
 
 
748
 
 
749
static gint
 
750
connector_handle_key_press(SPConnectorContext *const cc, guint const keyval)
 
751
{
 
752
    gint ret = FALSE;
 
753
    /* fixme: */
 
754
    switch (keyval) {
 
755
        case GDK_Return:
 
756
        case GDK_KP_Enter:
 
757
            if (cc->npoints != 0) {
 
758
                spcc_connector_finish(cc);
 
759
                cc->state = SP_CONNECTOR_CONTEXT_IDLE;
 
760
                sp_event_context_snap_window_closed(SP_EVENT_CONTEXT(cc));
 
761
                ret = TRUE;
 
762
            }
 
763
            break;
 
764
        case GDK_Escape:
 
765
            if (cc->state == SP_CONNECTOR_CONTEXT_REROUTING) {
 
766
 
 
767
                SPDesktop *desktop = SP_EVENT_CONTEXT_DESKTOP(cc);
 
768
                SPDocument *doc = sp_desktop_document(desktop);
 
769
 
 
770
                cc_connector_rerouting_finish(cc, NULL);
 
771
 
 
772
                sp_document_undo(doc);
 
773
 
 
774
                cc->state = SP_CONNECTOR_CONTEXT_IDLE;
 
775
                sp_event_context_snap_window_closed(SP_EVENT_CONTEXT(cc));
 
776
                desktop->messageStack()->flash( Inkscape::NORMAL_MESSAGE,
 
777
                        _("Connector endpoint drag cancelled."));
 
778
                ret = TRUE;
 
779
            }
 
780
            else if (cc->npoints != 0) {
 
781
                // if drawing, cancel, otherwise pass it up for deselecting
 
782
                cc->state = SP_CONNECTOR_CONTEXT_STOP;
 
783
                sp_event_context_snap_window_closed(SP_EVENT_CONTEXT(cc));
 
784
                spcc_reset_colors(cc);
 
785
                ret = TRUE;
 
786
            }
 
787
            break;
 
788
        default:
 
789
            break;
 
790
    }
 
791
    return ret;
 
792
}
 
793
 
 
794
 
 
795
static void
 
796
cc_connector_rerouting_finish(SPConnectorContext *const cc, Geom::Point *const p)
 
797
{
 
798
    SPDesktop *desktop = SP_EVENT_CONTEXT_DESKTOP(cc);
 
799
    SPDocument *doc = sp_desktop_document(desktop);
 
800
 
 
801
    // Clear the temporary path:
 
802
    cc->red_curve->reset();
 
803
    sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(cc->red_bpath), NULL);
 
804
 
 
805
    if (p != NULL)
 
806
    {
 
807
        // Test whether we clicked on a connection point
 
808
        gchar *shape_label = conn_pt_handle_test(cc, *p);
 
809
 
 
810
        if (shape_label) {
 
811
            if (cc->clickedhandle == cc->endpt_handle[0]) {
 
812
                sp_object_setAttribute(cc->clickeditem,
 
813
                        "inkscape:connection-start",shape_label, false);
 
814
            }
 
815
            else {
 
816
                sp_object_setAttribute(cc->clickeditem,
 
817
                        "inkscape:connection-end",shape_label, false);
 
818
            }
 
819
            g_free(shape_label);
 
820
        }
 
821
    }
 
822
    cc->clickeditem->setHidden(false);
 
823
    sp_conn_adjust_path(SP_PATH(cc->clickeditem));
 
824
    cc->clickeditem->updateRepr();
 
825
    sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
 
826
                     _("Reroute connector"));
 
827
    cc_set_active_conn(cc, cc->clickeditem);
 
828
}
 
829
 
 
830
 
 
831
static void
 
832
spcc_reset_colors(SPConnectorContext *cc)
 
833
{
 
834
    /* Red */
 
835
    cc->red_curve->reset();
 
836
    sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(cc->red_bpath), NULL);
 
837
 
 
838
    cc->green_curve->reset();
 
839
    cc->npoints = 0;
 
840
}
 
841
 
 
842
 
 
843
static void
 
844
spcc_connector_set_initial_point(SPConnectorContext *const cc, Geom::Point const p)
 
845
{
 
846
    g_assert( cc->npoints == 0 );
 
847
 
 
848
    cc->p[0] = p;
 
849
    cc->p[1] = p;
 
850
    cc->npoints = 2;
 
851
    sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(cc->red_bpath), NULL);
 
852
}
 
853
 
 
854
 
 
855
static void
 
856
spcc_connector_set_subsequent_point(SPConnectorContext *const cc, Geom::Point const p)
 
857
{
 
858
    g_assert( cc->npoints != 0 );
 
859
 
 
860
    SPDesktop *dt = cc->desktop;
 
861
    Geom::Point o = dt->dt2doc(cc->p[0]);
 
862
    Geom::Point d = dt->dt2doc(p);
 
863
    Avoid::Point src(o[Geom::X], o[Geom::Y]);
 
864
    Avoid::Point dst(d[Geom::X], d[Geom::Y]);
 
865
 
 
866
    if (!cc->newConnRef) {
 
867
        Avoid::Router *router = sp_desktop_document(dt)->router;
 
868
        cc->newConnRef = new Avoid::ConnRef(router, 0, src, dst);
 
869
        cc->newConnRef->updateEndPoint(Avoid::VertID::src, src);
 
870
    }
 
871
    cc->newConnRef->updateEndPoint(Avoid::VertID::tar, dst);
 
872
 
 
873
    cc->newConnRef->makePathInvalid();
 
874
    cc->newConnRef->generatePath(src, dst);
 
875
 
 
876
    Avoid::PolyLine route = cc->newConnRef->route();
 
877
    cc->newConnRef->calcRouteDist();
 
878
 
 
879
    cc->red_curve->reset();
 
880
    Geom::Point pt(route.ps[0].x, route.ps[0].y);
 
881
    cc->red_curve->moveto(pt);
 
882
 
 
883
    for (int i = 1; i < route.pn; ++i) {
 
884
        Geom::Point p(route.ps[i].x, route.ps[i].y);
 
885
        cc->red_curve->lineto(p);
 
886
    }
 
887
    cc->red_curve->transform(dt->doc2dt());
 
888
    sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(cc->red_bpath), cc->red_curve);
 
889
}
 
890
 
 
891
 
 
892
/**
 
893
 * Concats red, blue and green.
 
894
 * If any anchors are defined, process these, optionally removing curves from white list
 
895
 * Invoke _flush_white to write result back to object.
 
896
 */
 
897
static void
 
898
spcc_concat_colors_and_flush(SPConnectorContext *cc)
 
899
{
 
900
    SPCurve *c = cc->green_curve;
 
901
    cc->green_curve = new SPCurve();
 
902
 
 
903
    cc->red_curve->reset();
 
904
    sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(cc->red_bpath), NULL);
 
905
 
 
906
    if (c->is_empty()) {
 
907
        c->unref();
 
908
        return;
 
909
    }
 
910
 
 
911
    spcc_flush_white(cc, c);
 
912
 
 
913
    c->unref();
 
914
}
 
915
 
 
916
 
 
917
/*
 
918
 * Flushes white curve(s) and additional curve into object
 
919
 *
 
920
 * No cleaning of colored curves - this has to be done by caller
 
921
 * No rereading of white data, so if you cannot rely on ::modified, do it in caller
 
922
 *
 
923
 */
 
924
 
 
925
static void
 
926
spcc_flush_white(SPConnectorContext *cc, SPCurve *gc)
 
927
{
 
928
    SPCurve *c;
 
929
 
 
930
    if (gc) {
 
931
        c = gc;
 
932
        c->ref();
 
933
    } else {
 
934
        return;
 
935
    }
 
936
 
 
937
    /* Now we have to go back to item coordinates at last */
 
938
    c->transform(SP_EVENT_CONTEXT_DESKTOP(cc)->dt2doc());
 
939
 
 
940
    SPDesktop *desktop = SP_EVENT_CONTEXT_DESKTOP(cc);
 
941
    SPDocument *doc = sp_desktop_document(desktop);
 
942
    Inkscape::XML::Document *xml_doc = sp_document_repr_doc(doc);
 
943
 
 
944
    if ( c && !c->is_empty() ) {
 
945
        /* We actually have something to write */
 
946
 
 
947
        Inkscape::XML::Node *repr = xml_doc->createElement("svg:path");
 
948
        /* Set style */
 
949
        sp_desktop_apply_style_tool(desktop, repr, "/tools/connector", false);
 
950
 
 
951
        gchar *str = sp_svg_write_path( c->get_pathvector() );
 
952
        g_assert( str != NULL );
 
953
        repr->setAttribute("d", str);
 
954
        g_free(str);
 
955
 
 
956
        /* Attach repr */
 
957
        cc->newconn = SP_ITEM(desktop->currentLayer()->appendChildRepr(repr));
 
958
        cc->selection->set(repr);
 
959
        Inkscape::GC::release(repr);
 
960
        cc->newconn->transform = sp_item_i2doc_affine(SP_ITEM(desktop->currentLayer())).inverse();
 
961
        cc->newconn->updateRepr();
 
962
 
 
963
        bool connection = false;
 
964
        sp_object_setAttribute(cc->newconn, "inkscape:connector-type",
 
965
                "polyline", false);
 
966
        if (cc->sid)
 
967
        {
 
968
            sp_object_setAttribute(cc->newconn, "inkscape:connection-start",
 
969
                    cc->sid, false);
 
970
            connection = true;
 
971
        }
 
972
 
 
973
        if (cc->eid)
 
974
        {
 
975
            sp_object_setAttribute(cc->newconn, "inkscape:connection-end",
 
976
                    cc->eid, false);
 
977
            connection = true;
 
978
        }
 
979
        cc->newconn->updateRepr();
 
980
        if (connection) {
 
981
            // Adjust endpoints to shape edge.
 
982
            sp_conn_adjust_path(SP_PATH(cc->newconn));
 
983
        }
 
984
        cc->newconn->updateRepr();
 
985
    }
 
986
 
 
987
    c->unref();
 
988
 
 
989
    /* Flush pending updates */
 
990
    sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR, _("Create connector"));
 
991
    sp_document_ensure_up_to_date(doc);
 
992
}
 
993
 
 
994
 
 
995
static void
 
996
spcc_connector_finish_segment(SPConnectorContext *const cc, Geom::Point const /*p*/)
 
997
{
 
998
    if (!cc->red_curve->is_empty()) {
 
999
        cc->green_curve->append_continuous(cc->red_curve, 0.0625);
 
1000
 
 
1001
        cc->p[0] = cc->p[3];
 
1002
        cc->p[1] = cc->p[4];
 
1003
        cc->npoints = 2;
 
1004
 
 
1005
        cc->red_curve->reset();
 
1006
    }
 
1007
}
 
1008
 
 
1009
 
 
1010
static void
 
1011
spcc_connector_finish(SPConnectorContext *const cc)
 
1012
{
 
1013
    SPDesktop *const desktop = cc->desktop;
 
1014
    desktop->messageStack()->flash(Inkscape::NORMAL_MESSAGE, _("Finishing connector"));
 
1015
 
 
1016
    cc->red_curve->reset();
 
1017
    spcc_concat_colors_and_flush(cc);
 
1018
 
 
1019
    cc->npoints = 0;
 
1020
 
 
1021
    if (cc->newConnRef) {
 
1022
        cc->newConnRef->removeFromGraph();
 
1023
        delete cc->newConnRef;
 
1024
        cc->newConnRef = NULL;
 
1025
    }
 
1026
}
 
1027
 
 
1028
 
 
1029
static gboolean
 
1030
cc_generic_knot_handler(SPCanvasItem *, GdkEvent *event, SPKnot *knot)
 
1031
{
 
1032
    g_assert (knot != NULL);
 
1033
 
 
1034
    g_object_ref(knot);
 
1035
 
 
1036
    SPConnectorContext *cc = SP_CONNECTOR_CONTEXT(
 
1037
            knot->desktop->event_context);
 
1038
 
 
1039
    gboolean consumed = FALSE;
 
1040
 
 
1041
    switch (event->type) {
 
1042
        case GDK_ENTER_NOTIFY:
 
1043
            sp_knot_set_flag(knot, SP_KNOT_MOUSEOVER, TRUE);
 
1044
 
 
1045
            cc->active_handle = knot;
 
1046
 
 
1047
            if (knot->tip)
 
1048
            {
 
1049
                knot->desktop->event_context->defaultMessageContext()->set(
 
1050
                        Inkscape::NORMAL_MESSAGE, knot->tip);
 
1051
            }
 
1052
 
 
1053
            consumed = TRUE;
 
1054
            break;
 
1055
        case GDK_LEAVE_NOTIFY:
 
1056
            sp_knot_set_flag(knot, SP_KNOT_MOUSEOVER, FALSE);
 
1057
 
 
1058
            cc->active_handle = NULL;
 
1059
 
 
1060
            if (knot->tip) {
 
1061
                knot->desktop->event_context->defaultMessageContext()->clear();
 
1062
            }
 
1063
 
 
1064
            consumed = TRUE;
 
1065
            break;
 
1066
        default:
 
1067
            break;
 
1068
    }
 
1069
 
 
1070
    g_object_unref(knot);
 
1071
 
 
1072
    return consumed;
 
1073
}
 
1074
 
 
1075
 
 
1076
static gboolean
 
1077
endpt_handler(SPKnot */*knot*/, GdkEvent *event, SPConnectorContext *cc)
 
1078
{
 
1079
    g_assert( SP_IS_CONNECTOR_CONTEXT(cc) );
 
1080
 
 
1081
    gboolean consumed = FALSE;
 
1082
 
 
1083
    switch (event->type) {
 
1084
        case GDK_BUTTON_PRESS:
 
1085
            g_assert( (cc->active_handle == cc->endpt_handle[0]) ||
 
1086
                      (cc->active_handle == cc->endpt_handle[1]) );
 
1087
            if (cc->state == SP_CONNECTOR_CONTEXT_IDLE) {
 
1088
                cc->clickeditem = cc->active_conn;
 
1089
                cc->clickedhandle = cc->active_handle;
 
1090
                cc_clear_active_conn(cc);
 
1091
                cc->state = SP_CONNECTOR_CONTEXT_REROUTING;
 
1092
                sp_event_context_snap_window_open(SP_EVENT_CONTEXT(cc));
 
1093
 
 
1094
                // Disconnect from attached shape
 
1095
                unsigned ind = (cc->active_handle == cc->endpt_handle[0]) ? 0 : 1;
 
1096
                sp_conn_end_detach(cc->clickeditem, ind);
 
1097
 
 
1098
                Geom::Point origin;
 
1099
                if (cc->clickedhandle == cc->endpt_handle[0]) {
 
1100
                    origin = cc->endpt_handle[1]->pos;
 
1101
                }
 
1102
                else {
 
1103
                    origin = cc->endpt_handle[0]->pos;
 
1104
                }
 
1105
 
 
1106
                // Show the red path for dragging.
 
1107
                cc->red_curve = SP_PATH(cc->clickeditem)->curve->copy();
 
1108
                Geom::Matrix i2d = sp_item_i2d_affine(cc->clickeditem);
 
1109
                cc->red_curve->transform(i2d);
 
1110
                sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(cc->red_bpath), cc->red_curve);
 
1111
 
 
1112
                cc->clickeditem->setHidden(true);
 
1113
 
 
1114
                // The rest of the interaction rerouting the connector is
 
1115
                // handled by the context root handler.
 
1116
                consumed = TRUE;
 
1117
            }
 
1118
            break;
 
1119
        default:
 
1120
            break;
 
1121
    }
 
1122
 
 
1123
    return consumed;
 
1124
}
 
1125
 
 
1126
 
 
1127
static void cc_set_active_shape(SPConnectorContext *cc, SPItem *item)
 
1128
{
 
1129
    g_assert(item != NULL );
 
1130
 
 
1131
    cc->active_shape = item;
 
1132
 
 
1133
    // Remove existing active shape listeners
 
1134
    if (cc->active_shape_repr) {
 
1135
        sp_repr_remove_listener_by_data(cc->active_shape_repr, cc);
 
1136
        Inkscape::GC::release(cc->active_shape_repr);
 
1137
 
 
1138
        sp_repr_remove_listener_by_data(cc->active_shape_layer_repr, cc);
 
1139
        Inkscape::GC::release(cc->active_shape_layer_repr);
 
1140
    }
 
1141
 
 
1142
    // Listen in case the active shape changes
 
1143
    cc->active_shape_repr = SP_OBJECT_REPR(item);
 
1144
    if (cc->active_shape_repr) {
 
1145
        Inkscape::GC::anchor(cc->active_shape_repr);
 
1146
        sp_repr_add_listener(cc->active_shape_repr, &shape_repr_events, cc);
 
1147
 
 
1148
        cc->active_shape_layer_repr = cc->active_shape_repr->parent();
 
1149
        Inkscape::GC::anchor(cc->active_shape_layer_repr);
 
1150
        sp_repr_add_listener(cc->active_shape_layer_repr, &layer_repr_events, cc);
 
1151
    }
 
1152
 
 
1153
 
 
1154
    // Set center connection point.
 
1155
    if ( cc->connpthandle == NULL ) {
 
1156
        SPKnot *knot = sp_knot_new(cc->desktop,
 
1157
                _("<b>Connection point</b>: click or drag to create a new connector"));
 
1158
 
 
1159
        knot->setShape(SP_KNOT_SHAPE_SQUARE);
 
1160
        knot->setSize(8);
 
1161
        knot->setAnchor(GTK_ANCHOR_CENTER);
 
1162
        knot->setFill(0xffffff00, 0xff0000ff, 0xff0000ff);
 
1163
        sp_knot_update_ctrl(knot);
 
1164
 
 
1165
        // We don't want to use the standard knot handler,
 
1166
        //since we don't want this knot to be draggable.
 
1167
        g_signal_handler_disconnect(G_OBJECT(knot->item),
 
1168
                knot->_event_handler_id);
 
1169
        knot->_event_handler_id = 0;
 
1170
 
 
1171
        gtk_signal_connect(GTK_OBJECT(knot->item), "event",
 
1172
                GTK_SIGNAL_FUNC(cc_generic_knot_handler), knot);
 
1173
 
 
1174
        cc->connpthandle = knot;
 
1175
    }
 
1176
 
 
1177
 
 
1178
    Geom::OptRect bbox = sp_item_bbox_desktop(cc->active_shape);
 
1179
    if (bbox) {
 
1180
        Geom::Point center = bbox->midpoint();
 
1181
        sp_knot_set_position(cc->connpthandle, center, 0);
 
1182
        sp_knot_show(cc->connpthandle);
 
1183
    } else {
 
1184
        sp_knot_hide(cc->connpthandle);
 
1185
    }
 
1186
}
 
1187
 
 
1188
 
 
1189
static void
 
1190
cc_set_active_conn(SPConnectorContext *cc, SPItem *item)
 
1191
{
 
1192
    g_assert( SP_IS_PATH(item) );
 
1193
 
 
1194
    SPCurve *curve = SP_SHAPE(SP_PATH(item))->curve;
 
1195
    Geom::Matrix i2d = sp_item_i2d_affine(item);
 
1196
 
 
1197
    if (cc->active_conn == item)
 
1198
    {
 
1199
        // Just adjust handle positions.
 
1200
        Geom::Point startpt = *(curve->first_point()) * i2d;
 
1201
        sp_knot_set_position(cc->endpt_handle[0], startpt, 0);
 
1202
 
 
1203
        Geom::Point endpt = *(curve->last_point()) * i2d;
 
1204
        sp_knot_set_position(cc->endpt_handle[1], endpt, 0);
 
1205
 
 
1206
        return;
 
1207
    }
 
1208
 
 
1209
    cc->active_conn = item;
 
1210
 
 
1211
    // Remove existing active conn listeners
 
1212
    if (cc->active_conn_repr) {
 
1213
        sp_repr_remove_listener_by_data(cc->active_conn_repr, cc);
 
1214
        Inkscape::GC::release(cc->active_conn_repr);
 
1215
        cc->active_conn_repr = NULL;
 
1216
    }
 
1217
 
 
1218
    // Listen in case the active conn changes
 
1219
    cc->active_conn_repr = SP_OBJECT_REPR(item);
 
1220
    if (cc->active_conn_repr) {
 
1221
        Inkscape::GC::anchor(cc->active_conn_repr);
 
1222
        sp_repr_add_listener(cc->active_conn_repr, &shape_repr_events, cc);
 
1223
    }
 
1224
 
 
1225
    for (int i = 0; i < 2; ++i) {
 
1226
 
 
1227
        // Create the handle if it doesn't exist
 
1228
        if ( cc->endpt_handle[i] == NULL ) {
 
1229
            SPKnot *knot = sp_knot_new(cc->desktop,
 
1230
                    _("<b>Connector endpoint</b>: drag to reroute or connect to new shapes"));
 
1231
 
 
1232
            knot->setShape(SP_KNOT_SHAPE_SQUARE);
 
1233
            knot->setSize(7);
 
1234
            knot->setAnchor(GTK_ANCHOR_CENTER);
 
1235
            knot->setFill(0xffffff00, 0xff0000ff, 0xff0000ff);
 
1236
            knot->setStroke(0x000000ff, 0x000000ff, 0x000000ff);
 
1237
            sp_knot_update_ctrl(knot);
 
1238
 
 
1239
            // We don't want to use the standard knot handler,
 
1240
            //since we don't want this knot to be draggable.
 
1241
            g_signal_handler_disconnect(G_OBJECT(knot->item),
 
1242
                    knot->_event_handler_id);
 
1243
            knot->_event_handler_id = 0;
 
1244
 
 
1245
            gtk_signal_connect(GTK_OBJECT(knot->item), "event",
 
1246
                    GTK_SIGNAL_FUNC(cc_generic_knot_handler), knot);
 
1247
 
 
1248
            cc->endpt_handle[i] = knot;
 
1249
        }
 
1250
 
 
1251
        // Remove any existing handlers
 
1252
        if (cc->endpt_handler_id[i]) {
 
1253
            g_signal_handlers_disconnect_by_func(
 
1254
                    G_OBJECT(cc->endpt_handle[i]->item),
 
1255
                    (void*)G_CALLBACK(endpt_handler), (gpointer) cc );
 
1256
            cc->endpt_handler_id[i] = 0;
 
1257
        }
 
1258
 
 
1259
        // Setup handlers for connector endpoints, this is
 
1260
        // is as 'after' so that cc_generic_knot_handler is
 
1261
        // triggered first for any endpoint.
 
1262
        cc->endpt_handler_id[i] = g_signal_connect_after(
 
1263
                G_OBJECT(cc->endpt_handle[i]->item), "event",
 
1264
                G_CALLBACK(endpt_handler), cc);
 
1265
    }
 
1266
 
 
1267
    Geom::Point startpt = *(curve->first_point()) * i2d;
 
1268
    sp_knot_set_position(cc->endpt_handle[0], startpt, 0);
 
1269
 
 
1270
    Geom::Point endpt = *(curve->last_point()) * i2d;
 
1271
    sp_knot_set_position(cc->endpt_handle[1], endpt, 0);
 
1272
 
 
1273
    sp_knot_show(cc->endpt_handle[0]);
 
1274
    sp_knot_show(cc->endpt_handle[1]);
 
1275
}
 
1276
 
 
1277
 
 
1278
static bool cc_item_is_shape(SPItem *item)
 
1279
{
 
1280
    if (SP_IS_PATH(item)) {
 
1281
        SPCurve *curve = (SP_SHAPE(item))->curve;
 
1282
        if ( curve && !(curve->is_closed()) ) {
 
1283
            // Open paths are connectors.
 
1284
            return false;
 
1285
        }
 
1286
    }
 
1287
    else if (SP_IS_TEXT(item) || SP_IS_FLOWTEXT(item)) {
 
1288
        Inkscape::Preferences *prefs = Inkscape::Preferences::get();
 
1289
        if (prefs->getBool("/tools/connector/ignoretext", true)) {
 
1290
            // Don't count text as a shape we can connect connector to.
 
1291
            return false;
 
1292
        }
 
1293
    }
 
1294
    return true;
 
1295
}
 
1296
 
 
1297
 
 
1298
bool cc_item_is_connector(SPItem *item)
 
1299
{
 
1300
    if (SP_IS_PATH(item)) {
 
1301
        if (SP_PATH(item)->connEndPair.isAutoRoutingConn()) {
 
1302
            g_assert( !(SP_SHAPE(item)->curve->is_closed()) );
 
1303
            return true;
 
1304
        }
 
1305
    }
 
1306
    return false;
 
1307
}
 
1308
 
 
1309
 
 
1310
void cc_selection_set_avoid(bool const set_avoid)
 
1311
{
 
1312
    SPDesktop *desktop = inkscape_active_desktop();
 
1313
    if (desktop == NULL) {
 
1314
        return;
 
1315
    }
 
1316
 
 
1317
    SPDocument *document = sp_desktop_document(desktop);
 
1318
 
 
1319
    Inkscape::Selection *selection = sp_desktop_selection(desktop);
 
1320
 
 
1321
    GSList *l = (GSList *) selection->itemList();
 
1322
 
 
1323
    int changes = 0;
 
1324
 
 
1325
    while (l) {
 
1326
        SPItem *item = (SPItem *) l->data;
 
1327
 
 
1328
        char const *value = (set_avoid) ? "true" : NULL;
 
1329
 
 
1330
        if (cc_item_is_shape(item)) {
 
1331
            sp_object_setAttribute(item, "inkscape:connector-avoid",
 
1332
                    value, false);
 
1333
            item->avoidRef->handleSettingChange();
 
1334
            changes++;
 
1335
        }
 
1336
 
 
1337
        l = l->next;
 
1338
    }
 
1339
 
 
1340
    if (changes == 0) {
 
1341
        desktop->messageStack()->flash(Inkscape::WARNING_MESSAGE,
 
1342
                _("Select <b>at least one non-connector object</b>."));
 
1343
        return;
 
1344
    }
 
1345
 
 
1346
    char *event_desc = (set_avoid) ?
 
1347
            _("Make connectors avoid selected objects") :
 
1348
            _("Make connectors ignore selected objects");
 
1349
    sp_document_done(document, SP_VERB_CONTEXT_CONNECTOR, event_desc);
 
1350
}
 
1351
 
 
1352
 
 
1353
static void
 
1354
cc_selection_changed(Inkscape::Selection *selection, gpointer data)
 
1355
{
 
1356
    SPConnectorContext *cc = SP_CONNECTOR_CONTEXT(data);
 
1357
    //SPEventContext *ec = SP_EVENT_CONTEXT(cc);
 
1358
 
 
1359
    SPItem *item = selection->singleItem();
 
1360
 
 
1361
    if (cc->active_conn == item)
 
1362
    {
 
1363
        // Nothing to change.
 
1364
        return;
 
1365
    }
 
1366
    if (item == NULL)
 
1367
    {
 
1368
        cc_clear_active_conn(cc);
 
1369
        return;
 
1370
    }
 
1371
 
 
1372
    if (cc_item_is_connector(item)) {
 
1373
        cc_set_active_conn(cc, item);
 
1374
    }
 
1375
}
 
1376
 
 
1377
 
 
1378
static void
 
1379
shape_event_attr_deleted(Inkscape::XML::Node */*repr*/, Inkscape::XML::Node *child,
 
1380
                         Inkscape::XML::Node */*ref*/, gpointer data)
 
1381
{
 
1382
    g_assert(data);
 
1383
    SPConnectorContext *cc = SP_CONNECTOR_CONTEXT(data);
 
1384
 
 
1385
    if (child == cc->active_shape_repr) {
 
1386
        // The active shape has been deleted.  Clear active shape.
 
1387
        cc_clear_active_shape(cc);
 
1388
    }
 
1389
}
 
1390
 
 
1391
 
 
1392
static void
 
1393
shape_event_attr_changed(Inkscape::XML::Node *repr, gchar const *name,
 
1394
                         gchar const */*old_value*/, gchar const */*new_value*/,
 
1395
                         bool /*is_interactive*/, gpointer data)
 
1396
{
 
1397
    g_assert(data);
 
1398
    SPConnectorContext *cc = SP_CONNECTOR_CONTEXT(data);
 
1399
 
 
1400
    // Look for changes than result in onscreen movement.
 
1401
    if (!strcmp(name, "d") || !strcmp(name, "x") || !strcmp(name, "y") ||
 
1402
            !strcmp(name, "width") || !strcmp(name, "height") ||
 
1403
            !strcmp(name, "transform"))
 
1404
    {
 
1405
        if (repr == cc->active_shape_repr) {
 
1406
            // Active shape has moved. Clear active shape.
 
1407
            cc_clear_active_shape(cc);
 
1408
        }
 
1409
        else if (repr == cc->active_conn_repr) {
 
1410
            // The active conn has been moved.
 
1411
            // Set it again, which just sets new handle positions.
 
1412
            cc_set_active_conn(cc, cc->active_conn);
 
1413
        }
 
1414
    }
 
1415
}
 
1416
 
 
1417
 
 
1418
/*
 
1419
  Local Variables:
 
1420
  mode:c++
 
1421
  c-file-style:"stroustrup"
 
1422
  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
 
1423
  indent-tabs-mode:nil
 
1424
  fill-column:99
 
1425
  End:
 
1426
*/
 
1427
// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :