1 /*
2  * Copyright (C) 2009 - 2011 Vivien Malerba <malerba@gnome-db.org>
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
17  */
18 
19 #include "browser-canvas-utility.h"
20 #include <math.h>
21 #include <string.h>
22 
23 static gboolean compute_intersect_rect_line (gdouble rectx1, gdouble recty1, gdouble rectx2, gdouble recty2,
24 					     gdouble P1x, gdouble P1y, gdouble P2x, gdouble P2y,
25 					     gdouble *R1x, gdouble *R1y, gdouble *R2x, gdouble *R2y);
26 
27 static void     compute_text_marks_offsets (gdouble x1, gdouble y1, gdouble x2, gdouble y2,
28 					    gdouble *xoff, gdouble *yoff, GooCanvasAnchorType *anchor_type);
29 
30 static GSList *browser_canvas_canvas_shape_add_to_list (GSList *list, gchar *swallow_id, GooCanvasItem *item);
31 static BrowserCanvasCanvasShape *browser_canvas_canvas_shape_find (GSList *list, const gchar *id);
32 
33 
34 /*
35  * Computes the points' coordinates of the line going from
36  * @ref_pk_ent to @fk_ent (which are themselves rectangles)
37  *
38  * if @shapes is not NULL, then the shapes in the list are reused, and the ones which don't need
39  * to exist anymore are removed
40  *
41  * Returns a list of BrowserCanvasCanvasShapes structures
42  */
43 GSList *
browser_canvas_util_compute_anchor_shapes(GooCanvasItem * parent,GSList * shapes,BrowserCanvasTable * fk_ent,BrowserCanvasTable * ref_pk_ent,guint nb_anchors,guint ext,gboolean with_handle)44 browser_canvas_util_compute_anchor_shapes (GooCanvasItem *parent, GSList *shapes,
45 					   BrowserCanvasTable *fk_ent, BrowserCanvasTable *ref_pk_ent,
46 					   guint nb_anchors, guint ext, gboolean with_handle)
47 {
48 	GSList *retval = shapes;
49 	guint i;
50 	gdouble fx1, fy1, fx2, fy2; /* FK entity item (bounds) */
51 	gdouble rx1, ry1, rx2, ry2; /* REF PK entity item  (bounds) */
52 
53 	BrowserCanvasCanvasShape *shape;
54 	gchar *id;
55 
56 	gdouble rcx, rcy; /* center of ref_pk entity item */
57 	gdouble cx, cy;
58 
59 	gdouble rux, ruy; /* current ref_pk point for the arrow line */
60 	gdouble dx, dy; /* increments to compute the new ref_pk point for the arrow line */
61 	GooCanvasBounds bounds;
62 
63 	g_return_val_if_fail (nb_anchors > 0, NULL);
64 
65 	browser_canvas_table_get_anchor_bounds (fk_ent, &bounds);
66 	fx1 = bounds.x1;
67 	fy1 = bounds.y1;
68 	fx2 = bounds.x2;
69 	fy2 = bounds.y2;
70 	browser_canvas_table_get_anchor_bounds (ref_pk_ent, &bounds);
71 	rx1 = bounds.x1;
72 	ry1 = bounds.y1;
73 	rx2 = bounds.x2;
74 	ry2 = bounds.y2;
75 
76 	/* compute the cx, cy, dx and dy values */
77 	rcx = (rx1 + rx2) / 2.;
78 	rcy = (ry1 + ry2) / 2.;
79 	cx = (fx1 + fx2) / 2.;
80 	cy = (fy1 + fy2) / 2.;
81 	rux = rcx;
82 	ruy = rcy;
83 	dx = 0;
84 	dy = 0;
85 
86 	for (i = 0; i < nb_anchors; i++) {
87 		/* TODO:
88 		   - detect tables overlapping
89 		*/
90 		if ((rcx == cx) && (rcy == cy)) {
91 			/* tables have the same center (includes case when they are equal) */
92 			gdouble Dy, Dx;
93 			GooCanvasPoints *ap, *points;
94 			GooCanvasItem *item;
95 
96 			points = goo_canvas_points_new (4);
97 			ap = goo_canvas_points_new (4);
98 
99 			Dy = (ry2 - ry1) / 2. / (gdouble ) (nb_anchors + 1) * (gdouble) (i + 1);
100 			Dx = (rx2 - rx1) * (0.8 + 0.1 * i);
101 			if (! compute_intersect_rect_line (rx1, ry1, rx2, ry2,
102 							   cx, cy, cx + Dx, cy - Dy,
103 							   &(ap->coords[0]), &(ap->coords[1]),
104 							   &(ap->coords[2]), &(ap->coords[3])))
105 				return retval;
106 
107 			if (ap->coords[0] > ap->coords[2]) {
108 				points->coords[0] = ap->coords[0];
109 				points->coords[1] = ap->coords[1];
110 			}
111 			else {
112 				points->coords[0] = ap->coords[2];
113 				points->coords[1] = ap->coords[3];
114 			}
115 
116 			points->coords[2] = cx + Dx;
117 			points->coords[3] = cy - Dy;
118 
119 			Dy = (fy2 - fy1) / 2. / (gdouble ) (nb_anchors + 1) * (gdouble) (i + 1);
120 			Dx = (fx2 - fx1) * (0.8 + 0.1 * i);
121 			points->coords[4] = cx + Dx;
122 			points->coords[5] = cy + Dy;
123 
124 			if (! compute_intersect_rect_line (fx1, fy1, fx2, fy2,
125 							   cx, cy, cx + Dx, cy + Dy,
126 							   &(ap->coords[0]), &(ap->coords[1]),
127 							   &(ap->coords[2]), &(ap->coords[3])))
128 				return retval;
129 
130 			if (ap->coords[0] > ap->coords[2]) {
131 				points->coords[6] = ap->coords[0];
132 				points->coords[7] = ap->coords[1];
133 			}
134 			else {
135 				points->coords[6] = ap->coords[2];
136 				points->coords[7] = ap->coords[3];
137 			}
138 
139 			id = g_strdup_printf ("a%d", i);
140 			shape = browser_canvas_canvas_shape_find (retval, id);
141 			if (shape) {
142 				g_object_set (shape->item, "points", points, NULL);
143 				shape->_used = TRUE;
144 				g_free (id);
145 			}
146 			else {
147 				item = goo_canvas_polyline_new_line (parent,
148 								     points->coords[0], points->coords [1],
149 								     points->coords[2], points->coords [3],
150 								     "close-path", FALSE,
151 								     "points", points, NULL);
152 				retval = browser_canvas_canvas_shape_add_to_list (retval, id, item);
153 			}
154 			goo_canvas_points_unref (ap);
155 
156 			/* extension marks as text */
157 			if (ext & CANVAS_SHAPE_EXT_JOIN_OUTER_1) {
158 				id = g_strdup_printf ("a%de1", i);
159 				shape = browser_canvas_canvas_shape_find (retval, id);
160 				if (shape) {
161 					g_object_set (shape->item,
162 						      "x", points->coords[2] + 5.,
163 						      "y", points->coords[3] - 5., NULL);
164 					shape->_used = TRUE;
165 					g_free (id);
166 				}
167 				else {
168 					item = goo_canvas_text_new (parent, "*",
169 								    points->coords[2] + 5.,
170 								    points->coords[3] - 5., -1,
171 								    GOO_CANVAS_ANCHOR_SOUTH, NULL);
172 					retval = browser_canvas_canvas_shape_add_to_list (retval, id, item);
173 				}
174 			}
175 
176 			if (ext & CANVAS_SHAPE_EXT_JOIN_OUTER_2) {
177 				id = g_strdup_printf ("a%de2", i);
178 				if (shape) {
179 					g_object_set (shape->item,
180 						      "x", points->coords[4] + 5.,
181 						      "y", points->coords[5] + 5., NULL);
182 					shape->_used = TRUE;
183 					g_free (id);
184 				}
185 				else {
186 					item = goo_canvas_text_new (parent, "*",
187 								    points->coords[4] + 5.,
188 								    points->coords[5] + 5., -1,
189 								    GOO_CANVAS_ANCHOR_NORTH, NULL);
190 					retval = browser_canvas_canvas_shape_add_to_list (retval, id, item);
191 				}
192 			}
193 
194 			goo_canvas_points_unref (points);
195 		}
196 		else {
197 			GooCanvasPoints *ap, *points;
198 			GooCanvasItem *item;
199 
200 			points = goo_canvas_points_new (2);
201 			ap = goo_canvas_points_new (4);
202 
203 			if (nb_anchors > 1) {
204 				if ((dx == 0) && (dy == 0)) {
205 					/* compute perpendicular to D={(rcx, rcy), (cx, cy)} */
206 					gdouble vx = (rcx - cx), vy = (rcy - cy);
207 					gdouble tmp;
208 
209 					tmp = vx;
210 					vx = vy;
211 					vy = - tmp;
212 
213 					/* compute intersect of ref_pkey rectangle and D=[vx, vy] passing at (rcx, rcy) */
214 					if (! compute_intersect_rect_line (rx1, ry1, rx2, ry2,
215 									   rcx, rcy, rcx + vx, rcy + vy,
216 									   &(ap->coords[0]), &(ap->coords[1]),
217 									   &(ap->coords[2]), &(ap->coords[3])))
218 						return retval;
219 					dx = (ap->coords[2] - ap->coords[0]) / (gdouble) (nb_anchors  + 1);
220 					dy = (ap->coords[3] - ap->coords[1]) / (gdouble) (nb_anchors  + 1);
221 					rux = ap->coords[0];
222 					ruy = ap->coords[1];
223 				}
224 
225 				rux += dx;
226 				ruy += dy;
227 			}
228 
229 			/* compute the 4 intersection points */
230 			if (! compute_intersect_rect_line (rx1, ry1, rx2, ry2,
231 							   rux, ruy, cx, cy,
232 							   &(ap->coords[0]), &(ap->coords[1]),
233 							   &(ap->coords[2]), &(ap->coords[3])))
234 				return retval;
235 			if (! compute_intersect_rect_line (fx1, fy1, fx2, fy2,
236 							   rux, ruy, cx, cy,
237 							   &(ap->coords[4]), &(ap->coords[5]),
238 							   &(ap->coords[6]), &(ap->coords[7])))
239 				return retval;
240 
241 			/* choosing between point coords(0,1) and coords(2,3) */
242 			if (((ap->coords[0] - ap->coords[4]) * (ap->coords[0] - ap->coords[4]) +
243 			     (ap->coords[1] - ap->coords[5]) * (ap->coords[1] - ap->coords[5])) <
244 			    ((ap->coords[2] - ap->coords[4]) * (ap->coords[2] - ap->coords[4]) +
245 			     (ap->coords[3] - ap->coords[5]) * (ap->coords[3] - ap->coords[5]))) {
246 				points->coords[0] = ap->coords[0];
247 				points->coords[1] = ap->coords[1];
248 			}
249 			else {
250 				points->coords[0] = ap->coords[2];
251 				points->coords[1] = ap->coords[3];
252 			}
253 
254 			/* choosing between point coords(4,5) and coords(6,7) */
255 			if (((points->coords[0] - ap->coords[4]) * (points->coords[0] - ap->coords[4]) +
256 			     (points->coords[1] - ap->coords[5]) * (points->coords[1] - ap->coords[5])) <
257 			    ((points->coords[0] - ap->coords[6]) * (points->coords[0] - ap->coords[6]) +
258 			     (points->coords[1] - ap->coords[7]) * (points->coords[1] - ap->coords[7]))) {
259 				points->coords[2] = ap->coords[4];
260 				points->coords[3] = ap->coords[5];
261 			}
262 			else {
263 				points->coords[2] = ap->coords[6];
264 				points->coords[3] = ap->coords[7];
265 			}
266 
267 			id = g_strdup_printf ("a%d", i);
268 			shape = browser_canvas_canvas_shape_find (retval, id);
269 			if (shape) {
270 				g_object_set (shape->item, "points", points, NULL);
271 				shape->_used = TRUE;
272 				g_free (id);
273 			}
274 			else {
275 				item = goo_canvas_polyline_new_line (parent,
276 								     points->coords[0], points->coords [1],
277 								     points->coords[2], points->coords [3],
278 								     "close-path", FALSE,
279 								     "points", points, NULL);
280 				retval = browser_canvas_canvas_shape_add_to_list (retval, id, item);
281 			}
282 			goo_canvas_points_unref (ap);
283 
284 			/* extension marks as text */
285 			if (ext & CANVAS_SHAPE_EXT_JOIN_OUTER_1) {
286 				gdouble mxoff = 0., myoff = 0.;
287 				GooCanvasAnchorType atype;
288 
289 				compute_text_marks_offsets (points->coords[0], points->coords[1],
290 							    points->coords[2], points->coords[3],
291 							    &mxoff, &myoff, &atype);
292 				id = g_strdup_printf ("a%de1", i);
293 				shape = browser_canvas_canvas_shape_find (retval, id);
294 				if (shape) {
295 					g_object_set (shape->item,
296 						      "x", points->coords[2] + mxoff,
297 						      "y", points->coords[3] + myoff,
298 						      "anchor", atype, NULL);
299 					shape->_used = TRUE;
300 					g_free (id);
301 				}
302 				else {
303 					item = goo_canvas_text_new (parent, "*",
304 								    points->coords[2] + mxoff,
305 								    points->coords[3] + myoff, -1,
306 								    atype, NULL);
307 					retval = browser_canvas_canvas_shape_add_to_list (retval, id, item);
308 				}
309 			}
310 
311 			if (ext & CANVAS_SHAPE_EXT_JOIN_OUTER_2) {
312 				gdouble mxoff, myoff;
313 				GooCanvasAnchorType atype;
314 
315 				compute_text_marks_offsets (points->coords[2], points->coords[3],
316 							    points->coords[0], points->coords[1],
317 							    &mxoff, &myoff, &atype);
318 
319 				id = g_strdup_printf ("a%de2", i);
320 				if (shape) {
321 					g_object_set (shape->item,
322 						      "x", points->coords[0] + mxoff,
323 						      "y", points->coords[1] + myoff,
324 						      "anchor", atype, NULL);
325 					shape->_used = TRUE;
326 					g_free (id);
327 				}
328 				else {
329 					item = goo_canvas_text_new (parent, "*",
330 								    points->coords[0] + mxoff,
331 								    points->coords[1] + myoff, -1,
332 								    atype, NULL);
333 					retval = browser_canvas_canvas_shape_add_to_list (retval, id, item);
334 				}
335 			}
336 
337 			goo_canvas_points_unref (points);
338 		}
339 	}
340 
341 	return retval;
342 }
343 
344 /*
345  * Computes the position offsets, relative to X2=(x2, y2) of a text to be written
346  * close to the X2 point, also computes the anchor type
347  */
348 static void
compute_text_marks_offsets(gdouble x1,gdouble y1,gdouble x2,gdouble y2,gdouble * xoff,gdouble * yoff,GooCanvasAnchorType * anchor_type)349 compute_text_marks_offsets (gdouble x1, gdouble y1, gdouble x2, gdouble y2,
350 			    gdouble *xoff, gdouble *yoff, GooCanvasAnchorType *anchor_type)
351 {
352 	gdouble mxoff, myoff;
353 	GooCanvasAnchorType atype = GOO_CANVAS_ANCHOR_CENTER; /* FIXME */
354 	gdouble sint, cost;
355 	gdouble sina = 0.5;
356 	gdouble cosa = 0.866025; /* sqrt(3)/2 */
357 	gdouble hyp;
358 	gdouble d = 15.;
359 
360 	hyp = sqrt ((y2 - y1) * (y2 - y1) + (x2 - x1) * (x2 - x1));
361 	sint = - (y2 - y1) / hyp;
362 	cost = (x2 - x1) / hyp;
363 
364 	mxoff = -d * (sina * sint + cosa * cost);
365 	myoff = -d * (sina * cost - cosa * sint);
366 
367 	if (xoff)
368 		*xoff = mxoff;
369 	if (yoff)
370 		*yoff = myoff;
371 	if (anchor_type)
372 		*anchor_type = atype;
373 }
374 
375 /*
376  * Computes the points of intersection between a rectangle (defined by the first 2 points)
377  * and a line (defined by the next 2 points).
378  *
379  * The result is returned in place of the line's point definition
380  *
381  *             --------- -----   D1
382  *             |       |
383  *             |       |
384  *             |       |
385  *             |       |
386  *             |       |
387  *             |       |
388  *             |       |
389  *             --------- ----   D2
390  *
391  *             |       |
392  *             |       |
393  *             D3      D4
394  *
395  * Returns: TRUE if the line crosses the rectangle, and FALSE if it misses it.
396  */
397 static gboolean
compute_intersect_rect_line(gdouble rectx1,gdouble recty1,gdouble rectx2,gdouble recty2,gdouble P1x,gdouble P1y,gdouble P2x,gdouble P2y,gdouble * R1x,gdouble * R1y,gdouble * R2x,gdouble * R2y)398 compute_intersect_rect_line (gdouble rectx1, gdouble recty1, gdouble rectx2, gdouble recty2,
399 			     gdouble P1x, gdouble P1y, gdouble P2x, gdouble P2y,
400 			     gdouble *R1x, gdouble *R1y, gdouble *R2x, gdouble *R2y)
401 {
402 	gboolean retval = FALSE;
403 	gboolean rotated = FALSE;
404 	gdouble a=0.; /* line slope   y = a x + b */
405 	gdouble b;    /* line offset  y = a x + b */
406 	gdouble offset = 2.;
407 
408 	gdouble ptsx[4]; /* points' X coordinate: 0 for intersect with D1, 1 for D2,... */
409 	gdouble ptsy[4]; /* points' Y coordinate */
410 
411 	if ((rectx1 == rectx2) && (recty1 == recty2))
412 		return FALSE;
413 	if ((rectx1 >= rectx2) || (recty1 >= recty2))
414 		return FALSE;
415 	if ((P1x == P2x) && (P1y == P2y))
416 		return FALSE;
417 
418 	/* rotate the coordinates to invert X and Y to avoid rounding problems ? */
419 	if (P1x != P2x)
420 		a = (P1y - P2y) / (P1x - P2x);
421 	if ((P1x == P2x) || (fabs (a) > 1)) {
422 		gdouble tmp;
423 		rotated = TRUE;
424 		tmp = rectx1; rectx1 = recty1; recty1 = tmp;
425 		tmp = rectx2; rectx2 = recty2; recty2 = tmp;
426 		tmp = P1x; P1x = P1y; P1y = tmp;
427 		tmp = P2x; P2x = P2y; P2y = tmp;
428 		a = (P1y - P2y) / (P1x - P2x);
429 	}
430 
431 	/* here we have (P1x != P2x), non vertical line */
432 	b = P1y - a * P1x;
433 
434 	if (a == 0) {
435 		/* horizontal line */
436 
437 		if ((b <= recty2) && (b >= recty1)) {
438 			retval = TRUE;
439 			*R1x = rectx1 - offset; *R1y = b;
440 			*R2x = rectx2 + offset; *R2y = b;
441 		}
442 	}
443 	else {
444 		gdouble retx[2] = {0., 0.};
445 		gdouble rety[2] = {0., 0.};
446 		gint i = 0;
447 
448 		/* non horizontal and non vertical line */
449 		/* D1 */
450 		ptsy[0] = recty1 - offset;
451 		ptsx[0] = (recty1 - b) / a;
452 
453 		/* D2 */
454 		ptsy[1] = recty2 + offset;
455 		ptsx[1] = (recty2 - b) / a;
456 
457 		/* D3 */
458 		ptsx[2] = rectx1 - offset;
459 		ptsy[2] = a * rectx1 + b;
460 
461 		/* D4 */
462 		ptsx[3] = rectx2 + offset;
463 		ptsy[3] = a * rectx2 + b;
464 
465 		if ((ptsx[0] >= rectx1) && (ptsx[0] <= rectx2)) {
466 			retval = TRUE;
467 			retx[i] = ptsx[0]; rety[i] = ptsy[0];
468 			i ++;
469 		}
470 		if ((ptsx[1] >= rectx1) && (ptsx[1] <= rectx2)) {
471 			retval = TRUE;
472 			retx[i] = ptsx[1]; rety[i] = ptsy[1];
473 			i ++;
474 		}
475 		if ((i<2) && (ptsy[2] >= recty1) && (ptsy[2] <= recty2)) {
476 			retval = TRUE;
477 			retx[i] = ptsx[2]; rety[i] = ptsy[2];
478 			i ++;
479 		}
480 		if ((i<2) && (ptsy[3] >= recty1) && (ptsy[3] <= recty2)) {
481 			retval = TRUE;
482 			retx[i] = ptsx[3]; rety[i] = ptsy[3];
483 			i++;
484 		}
485 
486 		if (retval) {
487 			g_assert (i == 2); /* wee need 2 points! */
488 			*R1x = retx[0]; *R1y = rety[0];
489 			*R2x = retx[1]; *R2y = rety[1];
490 		}
491 	}
492 
493 	if (retval && rotated) {
494 		/* rotate it back */
495 		gdouble tmp;
496 
497 		tmp = *R1x; *R1x = *R1y; *R1y = tmp;
498 		tmp = *R2x; *R2x = *R2y; *R2y = tmp;
499 	}
500 
501 	return retval;
502 }
503 
504 /*
505  * Compute the anchor shapes to link field1 to field2 from ent1 to ent2
506  *
507  * if @shapes is not NULL, then the shapes in the list are reused, and the ones which don't need
508  * to exist anymore are removed
509  *
510  * Returns a list of BrowserCanvasCanvasShapes structures
511  */
512 GSList *
browser_canvas_util_compute_connect_shapes(GooCanvasItem * parent,GSList * shapes,BrowserCanvasTable * ent1,GdaMetaTableColumn * field1,BrowserCanvasTable * ent2,GdaMetaTableColumn * field2,guint nb_connect,guint ext)513 browser_canvas_util_compute_connect_shapes (GooCanvasItem *parent, GSList *shapes,
514 					  BrowserCanvasTable *ent1, GdaMetaTableColumn *field1,
515 					  BrowserCanvasTable *ent2, GdaMetaTableColumn *field2,
516 					  guint nb_connect, guint ext)
517 {
518 	GSList *retval = shapes;
519 	GooCanvasItem *item;
520 	GooCanvasPoints *points;
521 	gdouble xl1, xr1, xl2, xr2, yt1, yt2; /* X boundings and Y top of ent1 and ent2 */
522 	gdouble x1, x2; /* X positions of the lines extremities close to ent1 and ent2 */
523 	gdouble x1offset, x2offset; /* offsets for the horizontal part of the lines */
524 	double sq = 5.;
525 	double eps = 0.5;
526 	GooCanvasBounds bounds;
527 
528 	BrowserCanvasCanvasShape *shape;
529 	gchar *id;
530 
531 	if (!field1 || !field2)
532 		return browser_canvas_util_compute_anchor_shapes (parent, shapes, ent1, ent2, 1, ext, FALSE);
533 
534 	/* line made of 4 points */
535 	points = goo_canvas_points_new (4);
536 	browser_canvas_table_get_anchor_bounds (ent1, &bounds);
537 	xl1 = bounds.x1;
538 	yt1 = bounds.y1;
539 	xr1 = bounds.x2;
540 	browser_canvas_table_get_anchor_bounds (ent2, &bounds);
541 	xl2 = bounds.x1;
542 	yt2 = bounds.y1;
543 	xr2 = bounds.x2;
544 
545 	if (xl2 > xr1) {
546 		x1 = xr1 + eps;
547 		x2 = xl2 - eps;
548 		x1offset = 2 * sq;
549 		x2offset = -2 * sq;
550 	}
551 	else {
552 		if (xl1 >= xr2) {
553 			x1 = xl1 - eps;
554 			x2 = xr2 + eps;
555 			x1offset = - 2 * sq;
556 			x2offset = 2 * sq;
557 		}
558 		else {
559 			if ((xl1 + xr1) < (xl2 + xr2)) {
560 				x1 = xl1 - eps;
561 				x2 = xl2 - eps;
562 				x1offset = -2 * sq;
563 				x2offset = -2 * sq;
564 			}
565 			else {
566 				x1 = xr1 + eps;
567 				x2 = xr2 + eps;
568 				x1offset = 2 * sq;
569 				x2offset = 2 * sq;
570 			}
571 		}
572 	}
573 
574 	points->coords[0] = x1;
575 	points->coords[1] = browser_canvas_table_get_column_ypos (ent1, field1) + yt1;
576 
577 	points->coords[2] = x1 + x1offset;
578 	points->coords[3] = points->coords[1];
579 
580 	points->coords[4] = x2 + x2offset;
581 	points->coords[5] = browser_canvas_table_get_column_ypos (ent2, field2) + yt2;
582 
583 	points->coords[6] = x2;
584 	points->coords[7] = points->coords[5];
585 
586 	id = g_strdup_printf ("c%d", nb_connect);
587 	shape = browser_canvas_canvas_shape_find (retval, id);
588 	if (shape) {
589 		g_object_set (shape->item, "points", points, NULL);
590 		shape->_used = TRUE;
591 		g_free (id);
592 	}
593 	else {
594 		item = goo_canvas_polyline_new_line (parent,
595 						     points->coords[0], points->coords [1],
596 						     points->coords[2], points->coords [3],
597 						     "close-path", FALSE,
598 						     "points", points, NULL);
599 		retval = browser_canvas_canvas_shape_add_to_list (retval, id, item);
600 	}
601 
602 	/* extension marks as text */
603 	if (ext & CANVAS_SHAPE_EXT_JOIN_OUTER_1) {
604 		gdouble mxoff = 0., myoff = 0.;
605 		GooCanvasAnchorType atype;
606 
607 		compute_text_marks_offsets (points->coords[4], points->coords[5],
608 					    points->coords[2], points->coords[3],
609 					    &mxoff, &myoff, &atype);
610 
611 		id = g_strdup_printf ("ce%d1", nb_connect);
612 		shape = browser_canvas_canvas_shape_find (retval, id);
613 		if (shape) {
614 			g_object_set (shape->item,
615 				      "x", points->coords[2] + mxoff,
616 				      "y", points->coords[3] + myoff,
617 				      "anchor", atype, NULL);
618 			shape->_used = TRUE;
619 			g_free (id);
620 		}
621 		else {
622 			item = goo_canvas_text_new (parent, "*",
623 						    points->coords[2] + mxoff,
624 						    points->coords[3] + myoff, -1,
625 						    atype, NULL);
626 			retval = browser_canvas_canvas_shape_add_to_list (retval, id, item);
627 		}
628 	}
629 
630 	if (ext & CANVAS_SHAPE_EXT_JOIN_OUTER_2) {
631 		gdouble mxoff, myoff;
632 		GooCanvasAnchorType atype;
633 
634 		compute_text_marks_offsets (points->coords[2], points->coords[3],
635 					    points->coords[4], points->coords[5],
636 					    &mxoff, &myoff, &atype);
637 
638 		id = g_strdup_printf ("ce%d2", nb_connect);
639 		shape = browser_canvas_canvas_shape_find (retval, id);
640 		if (shape) {
641 			g_object_set (shape->item,
642 				      "x", points->coords[2] + mxoff,
643 				      "y", points->coords[3] + myoff,
644 				      "anchor", atype, NULL);
645 			shape->_used = TRUE;
646 			g_free (id);
647 		}
648 		else {
649 			item = goo_canvas_text_new (parent, "*",
650 						    points->coords[2] + mxoff,
651 						    points->coords[3] + myoff, -1,
652 						    atype, NULL);
653 			retval = browser_canvas_canvas_shape_add_to_list (retval, id, item);
654 		}
655 	}
656 
657 	goo_canvas_points_unref (points);
658 
659 	return retval;
660 }
661 
662 static GSList *
browser_canvas_canvas_shape_add_to_list(GSList * list,gchar * swallow_id,GooCanvasItem * item)663 browser_canvas_canvas_shape_add_to_list (GSList *list, gchar *swallow_id, GooCanvasItem *item)
664 {
665 	BrowserCanvasCanvasShape *shape = g_new (BrowserCanvasCanvasShape, 1);
666 
667 	g_assert (swallow_id);
668 	g_assert (item);
669 	shape->id = swallow_id;
670 	shape->item = item;
671 	shape->_used = TRUE;
672 	shape->is_new = TRUE;
673 
674 	/*g_print ("Shape %p (%s: %s) added\n", item, swallow_id, G_OBJECT_TYPE_NAME (item));*/
675 
676 	return g_slist_append (list, shape);
677 }
678 
679 static BrowserCanvasCanvasShape *
browser_canvas_canvas_shape_find(GSList * list,const gchar * id)680 browser_canvas_canvas_shape_find (GSList *list, const gchar *id)
681 {
682 	BrowserCanvasCanvasShape *shape = NULL;
683 	GSList *l;
684 
685 	for (l = list; l && !shape; l = l->next)
686 		if (!strcmp (((BrowserCanvasCanvasShape*) l->data)->id, id))
687 			shape = (BrowserCanvasCanvasShape*) l->data;
688 
689 	/*g_print ("Looking for shape %s: %s\n", id, shape ? "Found" : "Not found");*/
690 	return shape;
691 }
692 
693 GSList *
browser_canvas_canvas_shapes_remove_obsolete_shapes(GSList * list)694 browser_canvas_canvas_shapes_remove_obsolete_shapes (GSList *list)
695 {
696 	GSList *l, *ret = list;
697 
698 	for (l = list; l; ) {
699 		if (((BrowserCanvasCanvasShape*)(l->data))->_used) {
700 			((BrowserCanvasCanvasShape*)(l->data))->_used = FALSE;
701 			l=l->next;
702 		}
703 		else {
704 			GSList *tmp;
705 			BrowserCanvasCanvasShape *shape = (BrowserCanvasCanvasShape*) l->data;
706 
707 			g_free (shape->id);
708 			goo_canvas_item_remove (shape->item);
709 			g_free (shape);
710 
711 			tmp = l->next;
712 			ret = g_slist_delete_link (ret, l);
713 			l = tmp;
714 		}
715 	}
716 
717 	return ret;
718 }
719 
720 void
browser_canvas_canvas_shapes_remove_all(GSList * list)721 browser_canvas_canvas_shapes_remove_all (GSList *list)
722 {
723 	GSList *l;
724 
725 	for (l = list; l; l = l->next) {
726 		BrowserCanvasCanvasShape *shape = (BrowserCanvasCanvasShape*) l->data;
727 
728 		g_free (shape->id);
729 		goo_canvas_item_remove (shape->item);
730 		g_free (shape);
731 	}
732 
733 	g_slist_free (list);
734 }
735 
736 void
browser_canvas_canvas_shapes_dump(GSList * list)737 browser_canvas_canvas_shapes_dump (GSList *list)
738 {
739 	GSList *l;
740 	g_print ("Canvas shapes...\n");
741 	for (l = list; l; l = l->next)
742 		g_print ("\tShape %s @%p (%s: %p) %s\n", BROWSER_CANVAS_CANVAS_SHAPE (l->data)->id,
743 			 BROWSER_CANVAS_CANVAS_SHAPE (l->data),
744 			 G_OBJECT_TYPE_NAME (BROWSER_CANVAS_CANVAS_SHAPE (l->data)->item),
745 			 BROWSER_CANVAS_CANVAS_SHAPE (l->data)->item,
746 			 BROWSER_CANVAS_CANVAS_SHAPE (l->data)->_used ? "Used": "Not used");
747 }
748