~ubuntu-branches/ubuntu/karmic/mergeant/karmic

« back to all changes in this revision

Viewing changes to libmergeant/graph/mg-canvas-fkconstraint.c

  • Committer: Bazaar Package Importer
  • Author(s): Gustavo R. Montesino
  • Date: 2007-11-29 08:44:48 UTC
  • mfrom: (2.1.4 hardy)
  • Revision ID: james.westby@ubuntu.com-20071129084448-6aon73d22bv6hzfw
Tags: 0.67-3
* Re-enable installation of the mime files in mergeant.install
* mergeant.dirs: create usr/share/mime/packages to make dh_installmime add
  the update-mime-database code snippets

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/* mg-canvas-fkconstraint.c
2
 
 *
3
 
 * Copyright (C) 2004 Vivien Malerba
4
 
 *
5
 
 * This program is free software; you can redistribute it and/or
6
 
 * modify it under the terms of the GNU General Public License as
7
 
 * published by the Free Software Foundation; either version 2 of the
8
 
 * License, or (at your option) any later version.
9
 
 *
10
 
 * This program is distributed in the hope that it will be useful,
11
 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
 
 * GNU General Public License for more details.
14
 
 *
15
 
 * You should have received a copy of the GNU General Public License
16
 
 * along with this program; if not, write to the Free Software
17
 
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
18
 
 * USA
19
 
 */
20
 
 
21
 
#include <math.h>
22
 
#include "mg-canvas.h"
23
 
#include "mg-canvas-fkconstraint.h"
24
 
#include "mg-canvas-entity.h"
25
 
#include "mg-canvas-text.h"
26
 
#include <libmergeant/mg-field.h>
27
 
#include <libmergeant/mg-db-constraint.h>
28
 
 
29
 
static void mg_canvas_fkconstraint_class_init (MgCanvasFkconstraintClass * class);
30
 
static void mg_canvas_fkconstraint_init       (MgCanvasFkconstraint * cc);
31
 
static void mg_canvas_fkconstraint_dispose    (GObject   * object);
32
 
static void mg_canvas_fkconstraint_finalize   (GObject   * object);
33
 
 
34
 
static void mg_canvas_fkconstraint_set_property    (GObject              *object,
35
 
                                                    guint                 param_id,
36
 
                                                    const GValue         *value,
37
 
                                                    GParamSpec           *pspec);
38
 
static void mg_canvas_fkconstraint_get_property    (GObject              *object,
39
 
                                                    guint                 param_id,
40
 
                                                    GValue               *value,
41
 
                                                    GParamSpec           *pspec);
42
 
 
43
 
static void clean_items (MgCanvasFkconstraint *cc);
44
 
static void create_items (MgCanvasFkconstraint *cc);
45
 
 
46
 
enum
47
 
{
48
 
        PROP_0,
49
 
        PROP_FK_CONSTRAINT
50
 
};
51
 
 
52
 
struct _MgCanvasFkconstraintPrivate
53
 
{
54
 
        GSList           *constraints;
55
 
        MgCanvasEntity   *fk_entity_item;
56
 
        MgCanvasEntity   *ref_pk_entity_item;
57
 
};
58
 
 
59
 
/* get a pointer to the parents to be able to call their destructor */
60
 
static GObjectClass *parent_class = NULL;
61
 
 
62
 
guint
63
 
mg_canvas_fkconstraint_get_type (void)
64
 
{
65
 
        static GType type = 0;
66
 
 
67
 
        if (!type) {
68
 
                static const GTypeInfo info = {
69
 
                        sizeof (MgCanvasFkconstraintClass),
70
 
                        (GBaseInitFunc) NULL,
71
 
                        (GBaseFinalizeFunc) NULL,
72
 
                        (GClassInitFunc) mg_canvas_fkconstraint_class_init,
73
 
                        NULL,
74
 
                        NULL,
75
 
                        sizeof (MgCanvasFkconstraint),
76
 
                        0,
77
 
                        (GInstanceInitFunc) mg_canvas_fkconstraint_init
78
 
                };              
79
 
 
80
 
                type = g_type_register_static (MG_CANVAS_ITEM_TYPE, "MgCanvasFkconstraint", &info, 0);
81
 
        }
82
 
 
83
 
        return type;
84
 
}       
85
 
 
86
 
static void
87
 
mg_canvas_fkconstraint_class_init (MgCanvasFkconstraintClass * class)
88
 
{
89
 
        GObjectClass   *object_class = G_OBJECT_CLASS (class);
90
 
 
91
 
        parent_class = g_type_class_peek_parent (class);
92
 
 
93
 
        object_class->dispose = mg_canvas_fkconstraint_dispose;
94
 
        object_class->finalize = mg_canvas_fkconstraint_finalize;
95
 
 
96
 
        /* Properties */
97
 
        object_class->set_property = mg_canvas_fkconstraint_set_property;
98
 
        object_class->get_property = mg_canvas_fkconstraint_get_property;
99
 
        g_object_class_install_property (object_class, PROP_FK_CONSTRAINT,
100
 
                                         g_param_spec_pointer ("fk_constraint", "Latest FK constraint to add", 
101
 
                                                               NULL, G_PARAM_WRITABLE));
102
 
       
103
 
}
104
 
 
105
 
static void
106
 
mg_canvas_fkconstraint_init (MgCanvasFkconstraint *cc)
107
 
{
108
 
        cc->priv = g_new0 (MgCanvasFkconstraintPrivate, 1);
109
 
        cc->priv->constraints = NULL;
110
 
        cc->priv->fk_entity_item = NULL;
111
 
        cc->priv->ref_pk_entity_item = NULL;
112
 
}
113
 
 
114
 
 
115
 
static void entity_destroy_cb (MgCanvasEntity *entity, MgCanvasFkconstraint *cc);
116
 
static void constraint_nullified_cb (MgDbConstraint *fkcons, MgCanvasFkconstraint *cc);
117
 
 
118
 
static void
119
 
mg_canvas_fkconstraint_dispose (GObject *object)
120
 
{
121
 
        MgCanvasFkconstraint *cc;
122
 
        g_return_if_fail (object != NULL);
123
 
        g_return_if_fail (IS_MG_CANVAS_FKCONSTRAINT (object));
124
 
 
125
 
        cc = MG_CANVAS_FKCONSTRAINT (object);
126
 
 
127
 
        clean_items (cc);
128
 
        if (cc->priv->constraints) {
129
 
                GSList *list = cc->priv->constraints;
130
 
                while (list) {
131
 
                        g_signal_handlers_disconnect_by_func (G_OBJECT (list->data),
132
 
                                                              G_CALLBACK (constraint_nullified_cb), cc);
133
 
                        list = g_slist_next (list);
134
 
                }
135
 
                g_slist_free (cc->priv->constraints);
136
 
                cc->priv->constraints = NULL;
137
 
        }
138
 
 
139
 
        /* for the parent class */
140
 
        parent_class->dispose (object);
141
 
}
142
 
 
143
 
 
144
 
static void
145
 
mg_canvas_fkconstraint_finalize (GObject  *object)
146
 
{
147
 
        MgCanvasFkconstraint *cc;
148
 
        g_return_if_fail (object != NULL);
149
 
        g_return_if_fail (IS_MG_CANVAS_FKCONSTRAINT (object));
150
 
 
151
 
        cc = MG_CANVAS_FKCONSTRAINT (object);
152
 
        if (cc->priv) {
153
 
                g_free (cc->priv);
154
 
                cc->priv = NULL;
155
 
        }
156
 
 
157
 
        /* for the parent class */
158
 
        parent_class->finalize (object);
159
 
}
160
 
 
161
 
static void 
162
 
mg_canvas_fkconstraint_set_property    (GObject              *object,
163
 
                                        guint                 param_id,
164
 
                                        const GValue         *value,
165
 
                                        GParamSpec           *pspec)
166
 
{
167
 
        MgCanvasFkconstraint *cc;
168
 
 
169
 
        cc = MG_CANVAS_FKCONSTRAINT (object);
170
 
 
171
 
        switch (param_id) {
172
 
        case PROP_FK_CONSTRAINT:
173
 
                mg_canvas_fkconstraint_add_constraint (cc, g_value_get_pointer (value));
174
 
                break;
175
 
        }
176
 
}
177
 
 
178
 
static void 
179
 
mg_canvas_fkconstraint_get_property    (GObject              *object,
180
 
                                        guint                 param_id,
181
 
                                        GValue               *value,
182
 
                                        GParamSpec           *pspec)
183
 
{
184
 
        MgCanvasFkconstraint *cc;
185
 
 
186
 
        cc = MG_CANVAS_FKCONSTRAINT (object);
187
 
 
188
 
        switch (param_id) {
189
 
        default:
190
 
                g_warning ("No such property!");
191
 
                break;
192
 
        }
193
 
}
194
 
 
195
 
static void
196
 
constraint_nullified_cb (MgDbConstraint *fkcons, MgCanvasFkconstraint *cc)
197
 
{
198
 
        g_assert (g_slist_find (cc->priv->constraints, fkcons));
199
 
        cc->priv->constraints = g_slist_remove (cc->priv->constraints, fkcons);
200
 
        g_signal_handlers_disconnect_by_func (G_OBJECT (fkcons),
201
 
                                              G_CALLBACK (constraint_nullified_cb), cc);
202
 
        
203
 
        /* destroy itself if there are no more constraint left in the end */
204
 
        if (!cc->priv->constraints)
205
 
                gtk_object_destroy (GTK_OBJECT (cc));
206
 
        else {
207
 
                clean_items (cc);
208
 
                create_items (cc);
209
 
        }
210
 
}
211
 
 
212
 
static void
213
 
entity_destroy_cb (MgCanvasEntity *entity, MgCanvasFkconstraint *cc)
214
 
{
215
 
        gtk_object_destroy (GTK_OBJECT (cc));
216
 
}
217
 
 
218
 
 
219
 
static gboolean single_item_event_cb (GnomeCanvasItem *ci, GdkEvent *event, MgCanvasFkconstraint *cc);
220
 
static void entity_item_moved_cb (GnomeCanvasItem *entity, MgCanvasFkconstraint *cc);
221
 
 
222
 
/* 
223
 
 * destroy any existing GnomeCanvasItem objects 
224
 
 */
225
 
static void 
226
 
clean_items (MgCanvasFkconstraint *cc)
227
 
{
228
 
        if (cc->priv->fk_entity_item) {
229
 
                g_signal_handlers_disconnect_by_func (G_OBJECT (cc->priv->fk_entity_item),
230
 
                                                      G_CALLBACK (entity_item_moved_cb), cc);
231
 
                g_signal_handlers_disconnect_by_func (G_OBJECT (cc->priv->fk_entity_item),
232
 
                                                      G_CALLBACK (entity_destroy_cb), cc);
233
 
                cc->priv->fk_entity_item = NULL;
234
 
        }
235
 
 
236
 
        if (cc->priv->ref_pk_entity_item) {
237
 
                g_signal_handlers_disconnect_by_func (G_OBJECT (cc->priv->ref_pk_entity_item),
238
 
                                                      G_CALLBACK (entity_item_moved_cb), cc);
239
 
                g_signal_handlers_disconnect_by_func (G_OBJECT (cc->priv->ref_pk_entity_item),
240
 
                                                      G_CALLBACK (entity_destroy_cb), cc);
241
 
                cc->priv->ref_pk_entity_item = NULL;
242
 
        }
243
 
 
244
 
        /* remove all the GnomeCanvasItem objects */
245
 
        while (GNOME_CANVAS_GROUP (cc)->item_list)
246
 
                gtk_object_destroy (GTK_OBJECT (GNOME_CANVAS_GROUP (cc)->item_list->data));
247
 
        
248
 
}
249
 
 
250
 
/* structure to return shapes, being either points or a path definition */
251
 
typedef struct {
252
 
        GnomeCanvasPoints  *points;
253
 
        GnomeCanvasPathDef *path_def;
254
 
} AnchorShape;
255
 
#define ANCHOR_SHAPE(x) ((AnchorShape*)(x))
256
 
 
257
 
static GSList *compute_anchor_shapes (MgCanvasEntity *fk_ent, MgCanvasEntity *ref_pk_ent, guint nb_anchors);
258
 
static void    free_anchor_shapes (GSList *anchor_shapes);
259
 
/*
260
 
 * create new GnomeCanvasItem objects
261
 
 *
262
 
 * It is assumed that the list of FK constraints to render is coherent (that all the constraints are
263
 
 * FK constraints, for the same table, referencing the same FK table).
264
 
 */
265
 
static void 
266
 
create_items (MgCanvasFkconstraint *cc)
267
 
{
268
 
        GnomeCanvasItem *item;
269
 
        GSList *fklist = cc->priv->constraints, *list, *anchor_shapes, *list2;
270
 
        MgDbConstraint *fkcons;
271
 
        MgDbTable *table;
272
 
        MgCanvasItem *entity_item;
273
 
        MgCanvas *canvas = mg_canvas_item_get_canvas (MG_CANVAS_ITEM (cc));
274
 
        GdkLineStyle style;
275
 
        gboolean user_constraint;
276
 
 
277
 
        if (!fklist)
278
 
                return;
279
 
 
280
 
        /* Analyse FK constraint */
281
 
        fkcons = MG_DB_CONSTRAINT (fklist->data);
282
 
        item = GNOME_CANVAS_ITEM (cc);
283
 
        table = mg_db_constraint_get_table (fkcons);
284
 
        entity_item = mg_canvas_get_item_for_object (canvas, MG_BASE (table));
285
 
        g_return_if_fail (entity_item);
286
 
        cc->priv->fk_entity_item = MG_CANVAS_ENTITY (entity_item);
287
 
        g_signal_connect (G_OBJECT (entity_item), "moving",
288
 
                          G_CALLBACK (entity_item_moved_cb), cc);
289
 
        g_signal_connect (G_OBJECT (entity_item), "moved",
290
 
                          G_CALLBACK (entity_item_moved_cb), cc);
291
 
        g_signal_connect (G_OBJECT (entity_item), "shifted",
292
 
                          G_CALLBACK (entity_item_moved_cb), cc);
293
 
        g_signal_connect (G_OBJECT (entity_item), "destroy",
294
 
                          G_CALLBACK (entity_destroy_cb), cc);
295
 
 
296
 
        table = mg_db_constraint_fkey_get_ref_table (fkcons);
297
 
        entity_item = mg_canvas_get_item_for_object (canvas, MG_BASE (table));
298
 
        g_return_if_fail (entity_item);
299
 
        cc->priv->ref_pk_entity_item = MG_CANVAS_ENTITY (entity_item);
300
 
        g_signal_connect (G_OBJECT (entity_item), "moving",
301
 
                          G_CALLBACK (entity_item_moved_cb), cc);
302
 
        g_signal_connect (G_OBJECT (entity_item), "moved",
303
 
                          G_CALLBACK (entity_item_moved_cb), cc);
304
 
        g_signal_connect (G_OBJECT (entity_item), "shifted",
305
 
                          G_CALLBACK (entity_item_moved_cb), cc);
306
 
        g_signal_connect (G_OBJECT (entity_item), "destroy",
307
 
                          G_CALLBACK (entity_destroy_cb), cc);
308
 
 
309
 
        /* actual line(s) */
310
 
        anchor_shapes = compute_anchor_shapes (cc->priv->fk_entity_item, cc->priv->ref_pk_entity_item, 
311
 
                                               g_slist_length (fklist));
312
 
        list2 = anchor_shapes;
313
 
        list = fklist;
314
 
        while (list && list2) {
315
 
                AnchorShape *shape = ANCHOR_SHAPE (list2->data);
316
 
 
317
 
                fkcons = MG_DB_CONSTRAINT (list->data);
318
 
                g_object_get (G_OBJECT (fkcons), "user_constraint", &user_constraint, NULL);
319
 
                if (user_constraint)
320
 
                        style = GDK_LINE_ON_OFF_DASH;
321
 
                else
322
 
                        style = GDK_LINE_SOLID;
323
 
                
324
 
                if (shape->points) {
325
 
                        item = gnome_canvas_item_new (GNOME_CANVAS_GROUP (cc),
326
 
                                                      GNOME_TYPE_CANVAS_LINE,
327
 
                                                      "points", shape->points,
328
 
                                                      "fill_color", "black",
329
 
                                                      "width_units", 2.,
330
 
                                                      "cap_style", GDK_CAP_ROUND,
331
 
                                                      "line_style", style,
332
 
                                                      "first_arrowhead", TRUE,
333
 
                                                      "arrow-shape-a", 8.,
334
 
                                                      "arrow-shape-b", 12.,
335
 
                                                      "arrow-shape-c", 5.,
336
 
                                                      "smooth", TRUE,
337
 
                                                      NULL);
338
 
                        g_object_set_data (G_OBJECT (item), "fkcons", fkcons);
339
 
                        g_signal_connect (G_OBJECT (item), "event", 
340
 
                                          G_CALLBACK (single_item_event_cb), cc);
341
 
                }
342
 
                if (shape->path_def) {
343
 
                        item = gnome_canvas_item_new (GNOME_CANVAS_GROUP (cc),
344
 
                                                      gnome_canvas_bpath_get_type(),
345
 
                                                      "bpath", shape->path_def,
346
 
                                                      "fill_color", "black",
347
 
                                                      "width_units", 2.,
348
 
                                                      "cap_style", GDK_CAP_ROUND,
349
 
                                                      NULL);
350
 
                        g_object_set_data (G_OBJECT (item), "fkcons", fkcons);
351
 
                        g_signal_connect (G_OBJECT (item), "event", 
352
 
                                          G_CALLBACK (single_item_event_cb), cc);
353
 
                }
354
 
 
355
 
                list = g_slist_next (list);
356
 
                list2 = g_slist_next (list2);
357
 
        }
358
 
        free_anchor_shapes (anchor_shapes);
359
 
}
360
 
 
361
 
static void popup_delete_cb (GtkMenuItem *mitem, MgCanvasFkconstraint *cc);
362
 
 
363
 
 
364
 
/*
365
 
 * item is for a single FK constraint
366
 
 */
367
 
static gboolean
368
 
single_item_event_cb (GnomeCanvasItem *ci, GdkEvent *event, MgCanvasFkconstraint *cc)
369
 
{
370
 
        MgDbConstraint *fkcons = g_object_get_data (G_OBJECT (ci), "fkcons");
371
 
        gboolean highlight = FALSE, is_user_constraint = FALSE;
372
 
        GtkWidget *menu, *entry;
373
 
        gboolean done = FALSE;
374
 
        GSList *list, *pairs;
375
 
 
376
 
        switch (event->type) {
377
 
        case GDK_ENTER_NOTIFY:
378
 
                highlight = TRUE;
379
 
        case GDK_LEAVE_NOTIFY:
380
 
                pairs = mg_db_constraint_fkey_get_fields (fkcons);
381
 
                list = pairs;
382
 
                while (list) {
383
 
                        MgDbConstraintFkeyPair *pair = MG_DB_CONSTRAINT_FK_PAIR (list->data);
384
 
                        MgCanvasField *field;
385
 
                        
386
 
                        field = mg_canvas_entity_get_field_item (cc->priv->fk_entity_item, 
387
 
                                                                 MG_FIELD (pair->fkey));
388
 
                        mg_canvas_text_set_highlight (MG_CANVAS_TEXT (field), highlight);
389
 
                        field = mg_canvas_entity_get_field_item (cc->priv->ref_pk_entity_item, 
390
 
                                                                 MG_FIELD (pair->ref_pkey));
391
 
                        mg_canvas_text_set_highlight (MG_CANVAS_TEXT (field), highlight);
392
 
                        
393
 
                        g_free (pair);
394
 
                        list = g_slist_next (list);
395
 
                }
396
 
                g_slist_free (pairs);
397
 
                break;
398
 
        case GDK_BUTTON_PRESS:
399
 
                menu = gtk_menu_new ();
400
 
                entry = gtk_menu_item_new_with_label (_("Remove"));
401
 
                g_object_get (G_OBJECT (fkcons), "user_constraint", &is_user_constraint, NULL);
402
 
                gtk_widget_set_sensitive (entry, is_user_constraint);
403
 
                g_object_set_data (G_OBJECT (entry), "fkcons", fkcons);
404
 
                g_signal_connect (G_OBJECT (entry), "activate", G_CALLBACK (popup_delete_cb), cc);
405
 
                gtk_menu_append (GTK_MENU (menu), entry);
406
 
                gtk_widget_show (entry);
407
 
                gtk_menu_popup (GTK_MENU (menu), NULL, NULL,
408
 
                                NULL, NULL, ((GdkEventButton *)event)->button,
409
 
                                ((GdkEventButton *)event)->time);
410
 
                done = TRUE;
411
 
                break;
412
 
        default:
413
 
                break;
414
 
        }
415
 
 
416
 
        return done;
417
 
}
418
 
 
419
 
 
420
 
static void
421
 
popup_delete_cb (GtkMenuItem *mitem, MgCanvasFkconstraint *cc)
422
 
{
423
 
        MgDbConstraint *fkcons = g_object_get_data (G_OBJECT (mitem), "fkcons");
424
 
        gboolean is_user_constraint;
425
 
 
426
 
        g_object_get (G_OBJECT (fkcons), "user_constraint", &is_user_constraint, NULL);
427
 
        if (is_user_constraint)
428
 
                mg_base_nullify (MG_BASE (fkcons));
429
 
}
430
 
 
431
 
static void
432
 
entity_item_moved_cb (GnomeCanvasItem *entity, MgCanvasFkconstraint *cc)
433
 
{
434
 
        clean_items (cc);
435
 
        create_items (cc);
436
 
}
437
 
 
438
 
 
439
 
static gboolean
440
 
compute_intersect_rect_line (gdouble rectx1, gdouble recty1, gdouble rectx2, gdouble recty2,
441
 
                             gdouble P1x, gdouble P1y, gdouble P2x, gdouble P2y,
442
 
                             gdouble *R1x, gdouble *R1y, gdouble *R2x, gdouble *R2y);
443
 
 
444
 
/**
445
 
 * mg_canvas_fkconstraint_add_constraint
446
 
 * @cc:
447
 
 * @fkcons:
448
 
 * 
449
 
 * Add @fkcons to the list of displayed constraints
450
 
 */
451
 
void
452
 
mg_canvas_fkconstraint_add_constraint (MgCanvasFkconstraint *cc, MgDbConstraint *fkcons)
453
 
{
454
 
        g_return_if_fail (cc && IS_MG_CANVAS_FKCONSTRAINT (cc));
455
 
        g_return_if_fail (cc->priv);
456
 
        g_return_if_fail (fkcons && IS_MG_DB_CONSTRAINT (fkcons));
457
 
        g_return_if_fail (mg_db_constraint_get_constraint_type (MG_DB_CONSTRAINT (fkcons)) == 
458
 
                          CONSTRAINT_FOREIGN_KEY);
459
 
 
460
 
        if (g_slist_find (cc->priv->constraints, fkcons))
461
 
                return;
462
 
 
463
 
        if (cc->priv->constraints) {
464
 
                /* there are already some FK constraints there, so test that the new one is
465
 
                 * compatible with the existing ones*/
466
 
                
467
 
        }
468
 
 
469
 
        cc->priv->constraints = g_slist_append (cc->priv->constraints, fkcons);
470
 
        g_signal_connect (G_OBJECT (fkcons), "nullified",
471
 
                          G_CALLBACK (constraint_nullified_cb), cc);
472
 
 
473
 
        clean_items (cc);
474
 
        create_items (cc);
475
 
}
476
 
 
477
 
/*
478
 
 * Computes the points' coordinates of the line going from
479
 
 * @ref_pk_ent to @fk_ent (which are themselves rectangles)
480
 
 *
481
 
 * Returns a list of AnchorShape structures, use free_anchor_shapes() to free it.
482
 
 */
483
 
static GSList *
484
 
compute_anchor_shapes (MgCanvasEntity *fk_ent, MgCanvasEntity *ref_pk_ent, guint nb_anchors)
485
 
{
486
 
        GSList *retval = NULL;
487
 
        guint i;
488
 
        gdouble fx1, fy1, fx2, fy2; /* FK entity item (bounds) */
489
 
        gdouble rx1, ry1, rx2, ry2; /* REF PK entity item  (bounds) */
490
 
 
491
 
        gdouble rcx, rcy; /* center of ref_pk entity item */
492
 
        gdouble cx, cy;
493
 
 
494
 
        gdouble rux, ruy; /* current ref_pk point for the arrow line */
495
 
        gdouble dx, dy; /* increments to compute the new ref_pk point for the arrow line */
496
 
 
497
 
        g_return_val_if_fail (nb_anchors > 0, NULL);
498
 
 
499
 
        gnome_canvas_item_get_bounds (GNOME_CANVAS_ITEM (fk_ent), &fx1, &fy1, &fx2, &fy2);
500
 
        gnome_canvas_item_get_bounds (GNOME_CANVAS_ITEM (ref_pk_ent), &rx1, &ry1, &rx2, &ry2);
501
 
 
502
 
        /* compute the cx, cy, dx and dy values */
503
 
        rcx = (rx1 + rx2) / 2.;
504
 
        rcy = (ry1 + ry2) / 2.;
505
 
        cx = (fx1 + fx2) / 2.;
506
 
        cy = (fy1 + fy2) / 2.;
507
 
        rux = rcx;
508
 
        ruy = rcy;
509
 
        dx = 0;
510
 
        dy = 0;
511
 
 
512
 
        for (i = 0; i < nb_anchors; i++) {
513
 
                AnchorShape *shape = g_new0 (AnchorShape, 1);
514
 
 
515
 
                /* TODO:
516
 
                   - detect tables overlapping
517
 
                */              
518
 
                if ((rcx == cx) && (rcy == cy)) {
519
 
                        /* tables have the same center (includes when they are equal) */
520
 
                        gdouble Dy, Dx;
521
 
                        GnomeCanvasPoints *ap, *points;
522
 
                        
523
 
                        points = gnome_canvas_points_new (4);
524
 
                        ap = gnome_canvas_points_new (4);
525
 
 
526
 
                        Dy = (ry2 - ry1) / 2. / (gdouble ) (nb_anchors + 1) * (gdouble) (i + 1);
527
 
                        Dx = (rx2 - rx1) * (0.8 + 0.1 * i);
528
 
                        g_assert (compute_intersect_rect_line (rx1, ry1, rx2, ry2,
529
 
                                                               cx, cy, cx + Dx, cy - Dy,
530
 
                                                               &(ap->coords[0]), &(ap->coords[1]),
531
 
                                                               &(ap->coords[2]), &(ap->coords[3])));
532
 
                        
533
 
                        if (ap->coords[0] > ap->coords[2]) {
534
 
                                points->coords[0] = ap->coords[0];
535
 
                                points->coords[1] = ap->coords[1];
536
 
                        }
537
 
                        else {
538
 
                                points->coords[0] = ap->coords[2];
539
 
                                points->coords[1] = ap->coords[3];
540
 
                        }
541
 
 
542
 
                        points->coords[2] = cx + Dx;
543
 
                        points->coords[3] = cy - Dy;
544
 
 
545
 
                        Dy = (fy2 - fy1) / 2. / (gdouble ) (nb_anchors + 1) * (gdouble) (i + 1);
546
 
                        Dx = (fx2 - fx1) * (0.8 + 0.1 * i);
547
 
                        points->coords[4] = cx + Dx;
548
 
                        points->coords[5] = cy + Dy;
549
 
 
550
 
                        g_assert (compute_intersect_rect_line (fx1, fy1, fx2, fy2,
551
 
                                                               cx, cy, cx + Dx, cy + Dy,
552
 
                                                               &(ap->coords[0]), &(ap->coords[1]),
553
 
                                                               &(ap->coords[2]), &(ap->coords[3])));
554
 
                        
555
 
                        if (ap->coords[0] > ap->coords[2]) {
556
 
                                points->coords[6] = ap->coords[0];
557
 
                                points->coords[7] = ap->coords[1];
558
 
                        }
559
 
                        else {
560
 
                                points->coords[6] = ap->coords[2];
561
 
                                points->coords[7] = ap->coords[3];
562
 
                        }
563
 
                        
564
 
                        shape->points = points;
565
 
                        gnome_canvas_points_free (ap);
566
 
 
567
 
                        /* GnomeCanvasPathDef *path_def; */
568
 
                        
569
 
                        /* path_def = gnome_canvas_path_def_new(); */
570
 
                        
571
 
                        /* gnome_canvas_path_def_moveto(path_def, 500.0, 175.0); */
572
 
                        /* gnome_canvas_path_def_curveto(path_def, 550.0, 175.0, 550.0, 275.0, 500.0, 275.0); */
573
 
                        /* shape->path_def = path_def; */
574
 
                        
575
 
                }
576
 
                else {
577
 
                        GnomeCanvasPoints *ap, *points;
578
 
                        
579
 
                        points = gnome_canvas_points_new (2);
580
 
                        ap = gnome_canvas_points_new (4);
581
 
 
582
 
                        if (nb_anchors > 1) {
583
 
                                if ((dx == 0) && (dy == 0)) {
584
 
                                        /* compute perpendicular to D={(rcx, rcy), (cx, cy)} */
585
 
                                        gdouble vx = (rcx - cx), vy = (rcy - cy);
586
 
                                        gdouble tmp;
587
 
                                        
588
 
                                        tmp = vx;
589
 
                                        vx = vy;
590
 
                                        vy = - tmp;
591
 
                                        
592
 
                                        /* compute intersect of ref_pkey rectangle and D=[vx, vy] passing at (rcx, rcy) */
593
 
                                        g_assert (compute_intersect_rect_line (rx1, ry1, rx2, ry2,
594
 
                                                                               rcx, rcy, rcx + vx, rcy + vy,
595
 
                                                                               &(ap->coords[0]), &(ap->coords[1]),
596
 
                                                                               &(ap->coords[2]), &(ap->coords[3])));
597
 
                                        dx = (ap->coords[2] - ap->coords[0]) / (gdouble) (nb_anchors  + 1);
598
 
                                        dy = (ap->coords[3] - ap->coords[1]) / (gdouble) (nb_anchors  + 1);
599
 
                                        rux = ap->coords[0];
600
 
                                        ruy = ap->coords[1];
601
 
                                }
602
 
 
603
 
                                rux += dx;
604
 
                                ruy += dy;
605
 
                        }
606
 
                        
607
 
                        /* compute the 4 intersection points */
608
 
                        g_assert (compute_intersect_rect_line (rx1, ry1, rx2, ry2,
609
 
                                                               rux, ruy, cx, cy,
610
 
                                                               &(ap->coords[0]), &(ap->coords[1]),
611
 
                                                               &(ap->coords[2]), &(ap->coords[3])));
612
 
                        g_assert (compute_intersect_rect_line (fx1, fy1, fx2, fy2,
613
 
                                                               rux, ruy, cx, cy,
614
 
                                                               &(ap->coords[4]), &(ap->coords[5]),
615
 
                                                               &(ap->coords[6]), &(ap->coords[7])));
616
 
                        
617
 
                        /* choosing between point coords(0,1) and coords(2,3) */
618
 
                        if (((ap->coords[0] - ap->coords[4]) * (ap->coords[0] - ap->coords[4]) + 
619
 
                             (ap->coords[1] - ap->coords[5]) * (ap->coords[1] - ap->coords[5])) <
620
 
                            ((ap->coords[2] - ap->coords[4]) * (ap->coords[2] - ap->coords[4]) + 
621
 
                             (ap->coords[3] - ap->coords[5]) * (ap->coords[3] - ap->coords[5]))) {
622
 
                                points->coords[0] = ap->coords[0];
623
 
                                points->coords[1] = ap->coords[1];
624
 
                        }
625
 
                        else {
626
 
                                points->coords[0] = ap->coords[2];
627
 
                                points->coords[1] = ap->coords[3];
628
 
                        }
629
 
                        
630
 
                        /* choosing between point coords(4,5) and coords(6,7) */
631
 
                        if (((points->coords[0] - ap->coords[4]) * (points->coords[0] - ap->coords[4]) +
632
 
                             (points->coords[1] - ap->coords[5]) * (points->coords[1] - ap->coords[5])) <
633
 
                            ((points->coords[0] - ap->coords[6]) * (points->coords[0] - ap->coords[6]) +
634
 
                             (points->coords[1] - ap->coords[7]) * (points->coords[1] - ap->coords[7]))) {
635
 
                                points->coords[2] = ap->coords[4];
636
 
                                points->coords[3] = ap->coords[5];
637
 
                        }
638
 
                        else {
639
 
                                points->coords[2] = ap->coords[6];
640
 
                                points->coords[3] = ap->coords[7];
641
 
                        }
642
 
                        
643
 
                        shape->points = points;
644
 
                        gnome_canvas_points_free (ap);
645
 
                }
646
 
 
647
 
                retval = g_slist_append (retval, shape);
648
 
        }
649
 
 
650
 
        return retval;
651
 
}
652
 
 
653
 
/*
654
 
 * Free the list of points
655
 
 */
656
 
static void
657
 
free_anchor_shapes (GSList *anchor_shapes)
658
 
{
659
 
        GSList *list = anchor_shapes;
660
 
 
661
 
        if (!list)
662
 
                return;
663
 
 
664
 
        while (list) {
665
 
                AnchorShape *shape = ANCHOR_SHAPE (list->data);
666
 
                if (shape->points)
667
 
                        gnome_canvas_points_free (shape->points);
668
 
                if (shape->path_def)
669
 
                        gnome_canvas_path_def_unref (shape->path_def);
670
 
                g_free (shape);
671
 
                list = g_slist_next (list);
672
 
        }
673
 
        g_slist_free (anchor_shapes);
674
 
}
675
 
 
676
 
/*
677
 
 * Computes the points of intersection between a rectangle (defined by the first 2 points)
678
 
 * and a line (defined by the next 2 points).
679
 
 *
680
 
 * The result is returned in place of the line's point definition
681
 
 *
682
 
 *             --------- -----   D1
683
 
 *             |       |
684
 
 *             |       |
685
 
 *             |       |
686
 
 *             |       |
687
 
 *             |       |
688
 
 *             |       |
689
 
 *             |       |
690
 
 *             --------- ----   D2
691
 
 *             
692
 
 *             |       |
693
 
 *             |       |
694
 
 *             D3      D4
695
 
 *
696
 
 * Returns: TRUE if the line crosses the rectangle, and FALSE if it misses it.
697
 
 */
698
 
static gboolean
699
 
compute_intersect_rect_line (gdouble rectx1, gdouble recty1, gdouble rectx2, gdouble recty2,
700
 
                             gdouble P1x, gdouble P1y, gdouble P2x, gdouble P2y,
701
 
                             gdouble *R1x, gdouble *R1y, gdouble *R2x, gdouble *R2y)
702
 
{
703
 
        gboolean retval = FALSE;
704
 
        gboolean rotated = FALSE;
705
 
        gdouble a=0.; /* line slope   y = a x + b */
706
 
        gdouble b;    /* line offset  y = a x + b */
707
 
        gdouble offset = 2.;
708
 
                
709
 
        gdouble ptsx[4]; /* points' X coordinate: 0 for intersect with D1, 1 for D2,... */
710
 
        gdouble ptsy[4]; /* points' Y coordinate */
711
 
 
712
 
        g_return_val_if_fail ((rectx1 != rectx2) || (recty1 != recty2), FALSE);
713
 
        g_return_val_if_fail ((rectx1 < rectx2) && (recty1 < recty2), FALSE);
714
 
        g_return_val_if_fail ((P1x != P2x) || (P1y != P2y), FALSE);
715
 
 
716
 
        /* rotate the coordinates to invert X and Y to avoid rounding problems ? */
717
 
        if (P1x != P2x)
718
 
                a = (P1y - P2y) / (P1x - P2x);
719
 
        if ((P1x == P2x) || (fabs (a) > 1)) {
720
 
                gdouble tmp;
721
 
                rotated = TRUE;
722
 
                tmp = rectx1; rectx1 = recty1; recty1 = tmp;
723
 
                tmp = rectx2; rectx2 = recty2; recty2 = tmp;
724
 
                tmp = P1x; P1x = P1y; P1y = tmp;
725
 
                tmp = P2x; P2x = P2y; P2y = tmp;
726
 
                a = (P1y - P2y) / (P1x - P2x);
727
 
        }
728
 
 
729
 
        /* here we have (P1x != P2x), non vertical line */
730
 
        b = P1y - a * P1x;
731
 
 
732
 
        if (a == 0) {
733
 
                /* horizontal line */
734
 
 
735
 
                if ((b <= recty2) && (b >= recty1)) {
736
 
                        retval = TRUE;
737
 
                        *R1x = rectx1 - offset; *R1y = b;
738
 
                        *R2x = rectx2 + offset; *R2y = b;
739
 
                }
740
 
        }
741
 
        else {
742
 
                gdouble retx[2] = {0., 0.};
743
 
                gdouble rety[2] = {0., 0.};
744
 
                gint i = 0;
745
 
 
746
 
                /* non horizontal and non vertical line */
747
 
                /* D1 */
748
 
                ptsy[0] = recty1 - offset;
749
 
                ptsx[0] = (recty1 - b) / a;
750
 
 
751
 
                /* D2 */
752
 
                ptsy[1] = recty2 + offset;
753
 
                ptsx[1] = (recty2 - b) / a;
754
 
 
755
 
                /* D3 */
756
 
                ptsx[2] = rectx1 - offset;
757
 
                ptsy[2] = a * rectx1 + b;
758
 
 
759
 
                /* D4 */
760
 
                ptsx[3] = rectx2 + offset;
761
 
                ptsy[3] = a * rectx2 + b;
762
 
                
763
 
                if ((ptsx[0] >= rectx1) && (ptsx[0] <= rectx2)) {
764
 
                        retval = TRUE;
765
 
                        retx[i] = ptsx[0]; rety[i] = ptsy[0];
766
 
                        i ++;
767
 
                }
768
 
                if ((ptsx[1] >= rectx1) && (ptsx[1] <= rectx2)) {
769
 
                        retval = TRUE;
770
 
                        retx[i] = ptsx[1]; rety[i] = ptsy[1];
771
 
                        i ++;
772
 
                }
773
 
                if ((i<2) && (ptsy[2] >= recty1) && (ptsy[2] <= recty2)) {
774
 
                        retval = TRUE;
775
 
                        retx[i] = ptsx[2]; rety[i] = ptsy[2];
776
 
                        i ++;
777
 
                }
778
 
                if ((i<2) && (ptsy[3] >= recty1) && (ptsy[3] <= recty2)) {
779
 
                        retval = TRUE;
780
 
                        retx[i] = ptsx[3]; rety[i] = ptsy[3];
781
 
                        i++;
782
 
                }
783
 
 
784
 
                if (retval) {
785
 
                        g_assert (i == 2); /* wee need 2 points! */
786
 
                        *R1x = retx[0]; *R1y = rety[0];
787
 
                        *R2x = retx[1]; *R2y = rety[1];
788
 
                }
789
 
        }
790
 
 
791
 
        if (retval && rotated) {
792
 
                /* rotate it back */
793
 
                gdouble tmp;
794
 
 
795
 
                tmp = *R1x; *R1x = *R1y; *R1y = tmp;
796
 
                tmp = *R2x; *R2x = *R2y; *R2y = tmp;
797
 
        }
798
 
 
799
 
        return retval;
800
 
}