fixes for 98% of all the warnings/errors reported by OS X gcc on tiger
[ardour.git] / libs / gnomecanvas / libgnomecanvas / gnome-canvas-line.c
1 /*
2  * Copyright (C) 1997, 1998, 1999, 2000 Free Software Foundation
3  * All rights reserved.
4  *
5  * This file is part of the Gnome Library.
6  *
7  * The Gnome Library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Library General Public License as
9  * published by the Free Software Foundation; either version 2 of the
10  * License, or (at your option) any later version.
11  *
12  * The Gnome Library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Library General Public License for more details.
16  *
17  * You should have received a copy of the GNU Library General Public
18  * License along with the Gnome Library; see the file COPYING.LIB.  If not,
19  * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20  * Boston, MA 02111-1307, USA.
21  */
22 /*
23   @NOTATION@
24  */
25
26 /* Line/curve item type for GnomeCanvas widget
27  *
28  * GnomeCanvas is basically a port of the Tk toolkit's most excellent canvas widget.  Tk is
29  * copyrighted by the Regents of the University of California, Sun Microsystems, and other parties.
30  *
31  *
32  * Author: Federico Mena <federico@nuclecu.unam.mx>
33  */
34
35 #include <config.h>
36 #include <math.h>
37 #include <string.h>
38 #include "libart_lgpl/art_vpath.h"
39 #include "libart_lgpl/art_svp.h"
40 #include "libart_lgpl/art_svp_vpath.h"
41 #include "libart_lgpl/art_svp_vpath_stroke.h"
42 #include "libgnomecanvas.h"
43
44 #define noVERBOSE
45
46 #define DEFAULT_SPLINE_STEPS 12         /* this is what Tk uses */
47 #define NUM_ARROW_POINTS     6          /* number of points in an arrowhead */
48 #define NUM_STATIC_POINTS    256        /* number of static points to use to avoid allocating arrays */
49
50
51 #define GROW_BOUNDS(bx1, by1, bx2, by2, x, y) { \
52         if (x < bx1)                            \
53                 bx1 = x;                        \
54                                                 \
55         if (x > bx2)                            \
56                 bx2 = x;                        \
57                                                 \
58         if (y < by1)                            \
59                 by1 = y;                        \
60                                                 \
61         if (y > by2)                            \
62                 by2 = y;                        \
63 }
64
65
66 enum {
67         PROP_0,
68         PROP_POINTS,
69         PROP_FILL_COLOR,
70         PROP_FILL_COLOR_GDK,
71         PROP_FILL_COLOR_RGBA,
72         PROP_FILL_STIPPLE,
73         PROP_WIDTH_PIXELS,
74         PROP_WIDTH_UNITS,
75         PROP_CAP_STYLE,
76         PROP_JOIN_STYLE,
77         PROP_LINE_STYLE,
78         PROP_FIRST_ARROWHEAD,
79         PROP_LAST_ARROWHEAD,
80         PROP_SMOOTH,
81         PROP_SPLINE_STEPS,
82         PROP_ARROW_SHAPE_A,
83         PROP_ARROW_SHAPE_B,
84         PROP_ARROW_SHAPE_C
85 };
86
87
88 static void gnome_canvas_line_class_init   (GnomeCanvasLineClass *class);
89 static void gnome_canvas_line_init         (GnomeCanvasLine      *line);
90 static void gnome_canvas_line_destroy      (GtkObject            *object);
91 static void gnome_canvas_line_set_property (GObject              *object,
92                                             guint                 param_id,
93                                             const GValue         *value,
94                                             GParamSpec           *pspec);
95 static void gnome_canvas_line_get_property (GObject              *object,
96                                             guint                 param_id,
97                                             GValue               *value,
98                                             GParamSpec           *pspec);
99
100 static void   gnome_canvas_line_update      (GnomeCanvasItem *item, double *affine, ArtSVP *clip_path, int flags);
101 static void   gnome_canvas_line_realize     (GnomeCanvasItem *item);
102 static void   gnome_canvas_line_unrealize   (GnomeCanvasItem *item);
103 static void   gnome_canvas_line_draw        (GnomeCanvasItem *item, GdkDrawable *drawable,
104                                              int x, int y, int width, int height);
105 static double gnome_canvas_line_point       (GnomeCanvasItem *item, double x, double y,
106                                              int cx, int cy, GnomeCanvasItem **actual_item);
107 static void   gnome_canvas_line_bounds      (GnomeCanvasItem *item, double *x1, double *y1, double *x2, double *y2);
108 static void   gnome_canvas_line_render      (GnomeCanvasItem *item, GnomeCanvasBuf *buf);
109
110
111 static GnomeCanvasItemClass *parent_class;
112
113
114 GType
115 gnome_canvas_line_get_type (void)
116 {
117         static GType line_type;
118
119         if (!line_type) {
120                 const GTypeInfo object_info = {
121                         sizeof (GnomeCanvasLineClass),
122                         (GBaseInitFunc) NULL,
123                         (GBaseFinalizeFunc) NULL,
124                         (GClassInitFunc) gnome_canvas_line_class_init,
125                         (GClassFinalizeFunc) NULL,
126                         NULL,                   /* class_data */
127                         sizeof (GnomeCanvasLine),
128                         0,                      /* n_preallocs */
129                         (GInstanceInitFunc) gnome_canvas_line_init,
130                         NULL                    /* value_table */
131                 };
132
133                 line_type = g_type_register_static (GNOME_TYPE_CANVAS_ITEM, "GnomeCanvasLine",
134                                                     &object_info, 0);
135         }
136
137         return line_type;
138 }
139
140 static void
141 gnome_canvas_line_class_init (GnomeCanvasLineClass *class)
142 {
143         GObjectClass *gobject_class;
144         GtkObjectClass *object_class;
145         GnomeCanvasItemClass *item_class;
146
147         gobject_class = (GObjectClass *) class;
148         object_class = (GtkObjectClass *) class;
149         item_class = (GnomeCanvasItemClass *) class;
150
151         parent_class = g_type_class_peek_parent (class);
152
153         gobject_class->set_property = gnome_canvas_line_set_property;
154         gobject_class->get_property = gnome_canvas_line_get_property;
155
156         g_object_class_install_property
157                 (gobject_class,
158                  PROP_POINTS,
159                  g_param_spec_boxed ("points", NULL, NULL,
160                                      GNOME_TYPE_CANVAS_POINTS,
161                                      (G_PARAM_READABLE | G_PARAM_WRITABLE)));
162         g_object_class_install_property
163                 (gobject_class,
164                  PROP_FILL_COLOR,
165                  g_param_spec_string ("fill_color", NULL, NULL,
166                                       NULL,
167                                       (G_PARAM_READABLE | G_PARAM_WRITABLE)));
168         g_object_class_install_property
169                 (gobject_class,
170                  PROP_FILL_COLOR_GDK,
171                  g_param_spec_boxed ("fill_color_gdk", NULL, NULL,
172                                      GDK_TYPE_COLOR,
173                                      (G_PARAM_READABLE | G_PARAM_WRITABLE)));
174         g_object_class_install_property
175                 (gobject_class,
176                  PROP_FILL_COLOR_RGBA,
177                  g_param_spec_uint ("fill_color_rgba", NULL, NULL,
178                                     0, G_MAXUINT, 0,
179                                     (G_PARAM_READABLE | G_PARAM_WRITABLE)));
180         g_object_class_install_property
181                 (gobject_class,
182                  PROP_FILL_STIPPLE,
183                  g_param_spec_object ("fill_stipple", NULL, NULL,
184                                       GDK_TYPE_DRAWABLE,
185                                       (G_PARAM_READABLE | G_PARAM_WRITABLE)));
186         g_object_class_install_property
187                 (gobject_class,
188                  PROP_WIDTH_PIXELS,
189                  g_param_spec_uint ("width_pixels", NULL, NULL,
190                                     0, G_MAXUINT, 0,
191                                     (G_PARAM_READABLE | G_PARAM_WRITABLE)));
192         g_object_class_install_property
193                 (gobject_class,
194                  PROP_WIDTH_UNITS,
195                  g_param_spec_double ("width_units", NULL, NULL,
196                                       0.0, G_MAXDOUBLE, 0.0,
197                                       (G_PARAM_READABLE | G_PARAM_WRITABLE)));
198         g_object_class_install_property
199                 (gobject_class,
200                  PROP_CAP_STYLE,
201                  g_param_spec_enum ("cap_style", NULL, NULL,
202                                     GDK_TYPE_CAP_STYLE,
203                                     GDK_CAP_BUTT,
204                                     (G_PARAM_READABLE | G_PARAM_WRITABLE)));
205         g_object_class_install_property
206                 (gobject_class,
207                  PROP_JOIN_STYLE,
208                  g_param_spec_enum ("join_style", NULL, NULL,
209                                     GDK_TYPE_JOIN_STYLE,
210                                     GDK_JOIN_MITER,
211                                     (G_PARAM_READABLE | G_PARAM_WRITABLE)));
212         g_object_class_install_property
213                 (gobject_class,
214                  PROP_LINE_STYLE,
215                  g_param_spec_enum ("line_style", NULL, NULL,
216                                     GDK_TYPE_LINE_STYLE,
217                                     GDK_LINE_SOLID,
218                                     (G_PARAM_READABLE | G_PARAM_WRITABLE)));
219         g_object_class_install_property
220                 (gobject_class,
221                  PROP_FIRST_ARROWHEAD,
222                  g_param_spec_boolean ("first_arrowhead", NULL, NULL,
223                                        FALSE,
224                                        (G_PARAM_READABLE | G_PARAM_WRITABLE)));
225         g_object_class_install_property
226                 (gobject_class,
227                  PROP_LAST_ARROWHEAD,
228                  g_param_spec_boolean ("last_arrowhead", NULL, NULL,
229                                        FALSE,
230                                        (G_PARAM_READABLE | G_PARAM_WRITABLE)));
231         g_object_class_install_property
232                 (gobject_class,
233                  PROP_SMOOTH,
234                  g_param_spec_boolean ("smooth", NULL, NULL,
235                                        FALSE,
236                                        (G_PARAM_READABLE | G_PARAM_WRITABLE)));
237         g_object_class_install_property
238                 (gobject_class,
239                  PROP_SPLINE_STEPS,
240                  g_param_spec_uint ("spline_steps", NULL, NULL,
241                                     0, G_MAXUINT, DEFAULT_SPLINE_STEPS,
242                                     (G_PARAM_READABLE | G_PARAM_WRITABLE)));
243         g_object_class_install_property
244                 (gobject_class,
245                  PROP_ARROW_SHAPE_A,
246                  g_param_spec_double ("arrow_shape_a", NULL, NULL,
247                                       -G_MAXDOUBLE, G_MAXDOUBLE, 0,
248                                       (G_PARAM_READABLE | G_PARAM_WRITABLE)));
249         g_object_class_install_property
250                 (gobject_class,
251                  PROP_ARROW_SHAPE_B,
252                  g_param_spec_double ("arrow_shape_b", NULL, NULL,
253                                       -G_MAXDOUBLE, G_MAXDOUBLE, 0,
254                                       (G_PARAM_READABLE | G_PARAM_WRITABLE)));
255         g_object_class_install_property
256                 (gobject_class,
257                  PROP_ARROW_SHAPE_C,
258                  g_param_spec_double ("arrow_shape_c", NULL, NULL,
259                                       -G_MAXDOUBLE, G_MAXDOUBLE, 0,
260                                       (G_PARAM_READABLE | G_PARAM_WRITABLE)));
261
262         object_class->destroy = gnome_canvas_line_destroy;
263
264         item_class->update = gnome_canvas_line_update;
265         item_class->realize = gnome_canvas_line_realize;
266         item_class->unrealize = gnome_canvas_line_unrealize;
267         item_class->draw = gnome_canvas_line_draw;
268         item_class->point = gnome_canvas_line_point;
269         item_class->bounds = gnome_canvas_line_bounds;
270
271         item_class->render = gnome_canvas_line_render;
272 }
273
274 static void
275 gnome_canvas_line_init (GnomeCanvasLine *line)
276 {
277         line->width = 0.0;
278         line->cap = GDK_CAP_BUTT;
279         line->join = GDK_JOIN_MITER;
280         line->line_style = GDK_LINE_SOLID;
281         line->shape_a = 0.0;
282         line->shape_b = 0.0;
283         line->shape_c = 0.0;
284         line->spline_steps = DEFAULT_SPLINE_STEPS;
285 }
286
287 static void
288 gnome_canvas_line_destroy (GtkObject *object)
289 {
290         GnomeCanvasLine *line;
291
292         g_return_if_fail (object != NULL);
293         g_return_if_fail (GNOME_IS_CANVAS_LINE (object));
294
295         line = GNOME_CANVAS_LINE (object);
296
297         /* remember, destroy can be run multiple times! */
298
299         if (line->coords)
300                 g_free (line->coords);
301         line->coords = NULL;
302
303         if (line->first_coords)
304                 g_free (line->first_coords);
305         line->first_coords = NULL;
306
307         if (line->last_coords)
308                 g_free (line->last_coords);
309         line->last_coords = NULL;
310
311         if (line->stipple)
312                 g_object_unref (line->stipple);
313         line->stipple = NULL;
314
315         if (line->fill_svp)
316                 art_svp_free (line->fill_svp);
317         line->fill_svp = NULL;
318
319         if (line->first_svp)
320                 art_svp_free (line->first_svp);
321         line->first_svp = NULL;
322
323         if (line->last_svp)
324                 art_svp_free (line->last_svp);
325         line->last_svp = NULL;
326
327         if (GTK_OBJECT_CLASS (parent_class)->destroy)
328                 (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
329 }
330
331 /* Computes the bounding box of the line, including its arrow points.  Assumes that the number of
332  * points in the line is not zero.
333  */
334 static void
335 get_bounds (GnomeCanvasLine *line, double *bx1, double *by1, double *bx2, double *by2)
336 {
337         double *coords;
338         double x1, y1, x2, y2;
339         double width;
340         int i;
341
342         if (!line->coords) {
343             *bx1 = *by1 = *bx2 = *by2 = 0.0;
344             return;
345         }
346         
347         /* Find bounding box of line's points */
348
349         x1 = x2 = line->coords[0];
350         y1 = y2 = line->coords[1];
351
352         for (i = 1, coords = line->coords + 2; i < line->num_points; i++, coords += 2)
353                 GROW_BOUNDS (x1, y1, x2, y2, coords[0], coords[1]);
354
355         /* Add possible over-estimate for wide lines */
356
357         if (line->width_pixels)
358                 width = line->width / line->item.canvas->pixels_per_unit;
359         else
360                 width = line->width;
361
362         x1 -= width;
363         y1 -= width;
364         x2 += width;
365         y2 += width;
366
367         /* For mitered lines, make a second pass through all the points.  Compute the location of
368          * the two miter vertex points and add them to the bounding box.
369          */
370
371         if (line->join == GDK_JOIN_MITER)
372                 for (i = line->num_points, coords = line->coords; i >= 3; i--, coords += 2) {
373                         double mx1, my1, mx2, my2;
374
375                         if (gnome_canvas_get_miter_points (coords[0], coords[1],
376                                                            coords[2], coords[3],
377                                                            coords[4], coords[5],
378                                                            width,
379                                                            &mx1, &my1, &mx2, &my2)) {
380                                 GROW_BOUNDS (x1, y1, x2, y2, mx1, my1);
381                                 GROW_BOUNDS (x1, y1, x2, y2, mx2, my2);
382                         }
383                 }
384
385         /* Add the arrow points, if any */
386
387         if (line->first_arrow && line->first_coords)
388                 for (i = 0, coords = line->first_coords; i < NUM_ARROW_POINTS; i++, coords += 2)
389                         GROW_BOUNDS (x1, y1, x2, y2, coords[0], coords[1]);
390
391         if (line->last_arrow && line->last_coords)
392                 for (i = 0, coords = line->last_coords; i < NUM_ARROW_POINTS; i++, coords += 2)
393                         GROW_BOUNDS (x1, y1, x2, y2, coords[0], coords[1]);
394
395         /* Done */
396
397         *bx1 = x1;
398         *by1 = y1;
399         *bx2 = x2;
400         *by2 = y2;
401 }
402
403 /* Computes the bounding box of the line, in canvas coordinates.  Assumes that the number of points in the polygon is
404  * not zero. Affine is the i2c transformation.
405  */
406 static void
407 get_bounds_canvas (GnomeCanvasLine *line, double *bx1, double *by1, double *bx2, double *by2, double affine[6])
408 {
409         /* It would be possible to tighten the bounds somewhat by transforming the individual points before
410            aggregating them into the bbox. But it hardly seems worth it. */
411         ArtDRect bbox_world;
412         ArtDRect bbox_canvas;
413
414         get_bounds (line, &bbox_world.x0, &bbox_world.y0, &bbox_world.x1, &bbox_world.y1);
415
416         art_drect_affine_transform (&bbox_canvas, &bbox_world, affine);
417         /* include 1 pixel of fudge */
418         *bx1 = bbox_canvas.x0 - 1;
419         *by1 = bbox_canvas.y0 - 1;
420         *bx2 = bbox_canvas.x1 + 1;
421         *by2 = bbox_canvas.y1 + 1;
422 }
423
424 /* Recalculates the arrow polygons for the line */
425 static void
426 reconfigure_arrows (GnomeCanvasLine *line)
427 {
428         double *poly, *coords;
429         double dx, dy, length;
430         double sin_theta, cos_theta, tmp;
431         double frac_height;     /* Line width as fraction of arrowhead width */
432         double backup;          /* Distance to backup end points so the line ends in the middle of the arrowhead */
433         double vx, vy;          /* Position of arrowhead vertex */
434         double shape_a, shape_b, shape_c;
435         double width;
436         int i;
437
438         if (line->num_points == 0)
439                 return;
440
441         /* Set up things */
442
443         if (line->first_arrow) {
444                 if (line->first_coords) {
445                         line->coords[0] = line->first_coords[0];
446                         line->coords[1] = line->first_coords[1];
447                 } else
448                         line->first_coords = g_new (double, 2 * NUM_ARROW_POINTS);
449         } else if (line->first_coords) {
450                 line->coords[0] = line->first_coords[0];
451                 line->coords[1] = line->first_coords[1];
452
453                 g_free (line->first_coords);
454                 line->first_coords = NULL;
455         }
456
457         i = 2 * (line->num_points - 1);
458
459         if (line->last_arrow) {
460                 if (line->last_coords) {
461                         line->coords[i] = line->last_coords[0];
462                         line->coords[i + 1] = line->last_coords[1];
463                 } else
464                         line->last_coords = g_new (double, 2 * NUM_ARROW_POINTS);
465         } else if (line->last_coords) {
466                 line->coords[i] = line->last_coords[0];
467                 line->coords[i + 1] = line->last_coords[1];
468
469                 g_free (line->last_coords);
470                 line->last_coords = NULL;
471         }
472
473         if (!line->first_arrow && !line->last_arrow)
474                 return;
475
476         if (line->width_pixels)
477                 width = line->width / line->item.canvas->pixels_per_unit;
478         else
479                 width = line->width;
480
481         /* Add fudge value for better-looking results */
482
483         shape_a = line->shape_a;
484         shape_b = line->shape_b;
485         shape_c = line->shape_c + width / 2.0;
486
487         if (line->width_pixels) {
488                 shape_a /= line->item.canvas->pixels_per_unit;
489                 shape_b /= line->item.canvas->pixels_per_unit;
490                 shape_c /= line->item.canvas->pixels_per_unit;
491         }
492
493         shape_a += 0.001;
494         shape_b += 0.001;
495         shape_c += 0.001;
496
497         /* Compute the polygon for the first arrowhead and adjust the first point in the line so
498          * that the line does not stick out past the leading edge of the arrowhead.
499          */
500
501         frac_height = (line->width / 2.0) / shape_c;
502         backup = frac_height * shape_b + shape_a * (1.0 - frac_height) / 2.0;
503
504         if (line->first_arrow) {
505                 poly = line->first_coords;
506                 poly[0] = poly[10] = line->coords[0];
507                 poly[1] = poly[11] = line->coords[1];
508
509                 dx = poly[0] - line->coords[2];
510                 dy = poly[1] - line->coords[3];
511                 length = sqrt (dx * dx + dy * dy);
512                 if (length < GNOME_CANVAS_EPSILON)
513                         sin_theta = cos_theta = 0.0;
514                 else {
515                         sin_theta = dy / length;
516                         cos_theta = dx / length;
517                 }
518
519                 vx = poly[0] - shape_a * cos_theta;
520                 vy = poly[1] - shape_a * sin_theta;
521
522                 tmp = shape_c * sin_theta;
523
524                 poly[2] = poly[0] - shape_b * cos_theta + tmp;
525                 poly[8] = poly[2] - 2.0 * tmp;
526
527                 tmp = shape_c * cos_theta;
528
529                 poly[3] = poly[1] - shape_b * sin_theta - tmp;
530                 poly[9] = poly[3] + 2.0 * tmp;
531
532                 poly[4] = poly[2] * frac_height + vx * (1.0 - frac_height);
533                 poly[5] = poly[3] * frac_height + vy * (1.0 - frac_height);
534                 poly[6] = poly[8] * frac_height + vx * (1.0 - frac_height);
535                 poly[7] = poly[9] * frac_height + vy * (1.0 - frac_height);
536
537                 /* Move the first point towards the second so that the corners at the end of the
538                  * line are inside the arrowhead.
539                  */
540
541                 line->coords[0] = poly[0] - backup * cos_theta;
542                 line->coords[1] = poly[1] - backup * sin_theta;
543         }
544
545         /* Same process for last arrowhead */
546
547         if (line->last_arrow) {
548                 coords = line->coords + 2 * (line->num_points - 2);
549                 poly = line->last_coords;
550                 poly[0] = poly[10] = coords[2];
551                 poly[1] = poly[11] = coords[3];
552
553                 dx = poly[0] - coords[0];
554                 dy = poly[1] - coords[1];
555                 length = sqrt (dx * dx + dy * dy);
556                 if (length < GNOME_CANVAS_EPSILON)
557                         sin_theta = cos_theta = 0.0;
558                 else {
559                         sin_theta = dy / length;
560                         cos_theta = dx / length;
561                 }
562
563                 vx = poly[0] - shape_a * cos_theta;
564                 vy = poly[1] - shape_a * sin_theta;
565
566                 tmp = shape_c * sin_theta;
567
568                 poly[2] = poly[0] - shape_b * cos_theta + tmp;
569                 poly[8] = poly[2] - 2.0 * tmp;
570
571                 tmp = shape_c * cos_theta;
572
573                 poly[3] = poly[1] - shape_b * sin_theta - tmp;
574                 poly[9] = poly[3] + 2.0 * tmp;
575
576                 poly[4] = poly[2] * frac_height + vx * (1.0 - frac_height);
577                 poly[5] = poly[3] * frac_height + vy * (1.0 - frac_height);
578                 poly[6] = poly[8] * frac_height + vx * (1.0 - frac_height);
579                 poly[7] = poly[9] * frac_height + vy * (1.0 - frac_height);
580
581                 coords[2] = poly[0] - backup * cos_theta;
582                 coords[3] = poly[1] - backup * sin_theta;
583         }
584 }
585
586 /* Convenience function to set the line's GC's foreground color */
587 static void
588 set_line_gc_foreground (GnomeCanvasLine *line)
589 {
590         GdkColor c;
591
592         if (!line->gc)
593                 return;
594
595         c.pixel = line->fill_pixel;
596         gdk_gc_set_foreground (line->gc, &c);
597 }
598
599 /* Recalculate the line's width and set it in its GC */
600 static void
601 set_line_gc_width (GnomeCanvasLine *line)
602 {
603         int width;
604
605         if (!line->gc)
606                 return;
607
608         if (line->width_pixels)
609                 width = (int) line->width;
610         else
611                 width = (int) (line->width * line->item.canvas->pixels_per_unit + 0.5);
612
613         gdk_gc_set_line_attributes (line->gc,
614                                     width,
615                                     line->line_style,
616                                     (line->first_arrow || line->last_arrow) ? GDK_CAP_BUTT : line->cap,
617                                     line->join);
618 }
619
620 /* Sets the stipple pattern for the line */
621 static void
622 set_stipple (GnomeCanvasLine *line, GdkBitmap *stipple, int reconfigure)
623 {
624         if (line->stipple && !reconfigure)
625                 g_object_unref (line->stipple);
626
627         line->stipple = stipple;
628         if (stipple && !reconfigure)
629                 g_object_ref (stipple);
630
631         if (line->gc) {
632                 if (stipple) {
633                         gdk_gc_set_stipple (line->gc, stipple);
634                         gdk_gc_set_fill (line->gc, GDK_STIPPLED);
635                 } else
636                         gdk_gc_set_fill (line->gc, GDK_SOLID);
637         }
638 }
639
640 static void
641 gnome_canvas_line_set_property (GObject              *object,
642                                 guint                 param_id,
643                                 const GValue         *value,
644                                 GParamSpec           *pspec)
645 {
646         GnomeCanvasItem *item;
647         GnomeCanvasLine *line;
648         GnomeCanvasPoints *points;
649         GdkColor color = { 0, 0, 0, 0, };
650         GdkColor *pcolor;
651         gboolean color_changed;
652         int have_pixel;
653
654         g_return_if_fail (object != NULL);
655         g_return_if_fail (GNOME_IS_CANVAS_LINE (object));
656
657         item = GNOME_CANVAS_ITEM (object);
658         line = GNOME_CANVAS_LINE (object);
659
660         color_changed = FALSE;
661         have_pixel = FALSE;
662
663         switch (param_id) {
664         case PROP_POINTS:
665                 points = g_value_get_boxed (value);
666
667                 if (line->coords) {
668                         g_free (line->coords);
669                         line->coords = NULL;
670                 }
671
672                 if (!points)
673                         line->num_points = 0;
674                 else {
675                         line->num_points = points->num_points;
676                         line->coords = g_new (double, 2 * line->num_points);
677                         memcpy (line->coords, points->coords, 2 * line->num_points * sizeof (double));
678                 }
679
680                 /* Drop the arrowhead polygons if they exist -- they will be regenerated */
681
682                 if (line->first_coords) {
683                         g_free (line->first_coords);
684                         line->first_coords = NULL;
685                 }
686
687                 if (line->last_coords) {
688                         g_free (line->last_coords);
689                         line->last_coords = NULL;
690                 }
691
692                 /* Since the line's points have changed, we need to re-generate arrowheads in
693                  * addition to recalculating the bounds.
694                  */
695                 gnome_canvas_item_request_update (item);
696                 break;
697
698         case PROP_FILL_COLOR:
699                 if (g_value_get_string (value))
700                         gdk_color_parse (g_value_get_string (value), &color);
701                 line->fill_rgba = ((color.red & 0xff00) << 16 |
702                                    (color.green & 0xff00) << 8 |
703                                    (color.blue & 0xff00) |
704                                    0xff);
705                 color_changed = TRUE;
706                 break;
707
708         case PROP_FILL_COLOR_GDK:
709                 pcolor = g_value_get_boxed (value);
710                 if (pcolor) {
711                         GdkColormap *colormap;
712                         color = *pcolor;
713
714                         colormap = gtk_widget_get_colormap (GTK_WIDGET (item->canvas));
715                         gdk_rgb_find_color (colormap, &color);
716
717                         have_pixel = TRUE;
718                 }
719
720                 line->fill_rgba = ((color.red & 0xff00) << 16 |
721                                    (color.green & 0xff00) << 8 |
722                                    (color.blue & 0xff00) |
723                                    0xff);
724                 color_changed = TRUE;
725                 break;
726
727         case PROP_FILL_COLOR_RGBA:
728                 line->fill_rgba = g_value_get_uint (value);
729                 color_changed = TRUE;
730                 break;
731
732         case PROP_FILL_STIPPLE:
733                 set_stipple (line, (GdkBitmap *) g_value_get_object (value), FALSE);
734                 gnome_canvas_item_request_redraw_svp (item, line->fill_svp);
735                 break;
736
737         case PROP_WIDTH_PIXELS:
738                 line->width = g_value_get_uint (value);
739                 line->width_pixels = TRUE;
740                 set_line_gc_width (line);
741                 gnome_canvas_item_request_update (item);
742                 break;
743
744         case PROP_WIDTH_UNITS:
745                 line->width = fabs (g_value_get_double (value));
746                 line->width_pixels = FALSE;
747                 set_line_gc_width (line);
748                 gnome_canvas_item_request_update (item);
749                 break;
750
751         case PROP_CAP_STYLE:
752                 line->cap = g_value_get_enum (value);
753                 gnome_canvas_item_request_update (item);
754                 break;
755
756         case PROP_JOIN_STYLE:
757                 line->join = g_value_get_enum (value);
758                 gnome_canvas_item_request_update (item);
759                 break;
760
761         case PROP_LINE_STYLE:
762                 line->line_style = g_value_get_enum (value);
763                 set_line_gc_width (line);
764                 gnome_canvas_item_request_update (item);
765                 break;
766
767         case PROP_FIRST_ARROWHEAD:
768                 line->first_arrow = g_value_get_boolean (value);
769                 gnome_canvas_item_request_update (item);
770                 break;
771
772         case PROP_LAST_ARROWHEAD:
773                 line->last_arrow = g_value_get_boolean (value);
774                 gnome_canvas_item_request_update (item);
775                 break;
776
777         case PROP_SMOOTH:
778                 /* FIXME */
779                 break;
780
781         case PROP_SPLINE_STEPS:
782                 /* FIXME */
783                 break;
784
785         case PROP_ARROW_SHAPE_A:
786                 line->shape_a = fabs (g_value_get_double (value));
787                 gnome_canvas_item_request_update (item);
788                 break;
789
790         case PROP_ARROW_SHAPE_B:
791                 line->shape_b = fabs (g_value_get_double (value));
792                 gnome_canvas_item_request_update (item);
793                 break;
794
795         case PROP_ARROW_SHAPE_C:
796                 line->shape_c = fabs (g_value_get_double (value));
797                 gnome_canvas_item_request_update (item);
798                 break;
799
800         default:
801                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
802                 break;
803         }
804
805         if (color_changed) {
806                 if (have_pixel)
807                         line->fill_pixel = color.pixel;
808                 else
809                         line->fill_pixel = gnome_canvas_get_color_pixel (item->canvas,
810                                                                          line->fill_rgba);
811
812                 if (!item->canvas->aa)
813                         set_line_gc_foreground (line);
814
815                 gnome_canvas_item_request_redraw_svp (item, line->fill_svp);
816
817                 if (line->first_svp) 
818                         gnome_canvas_item_request_redraw_svp (item, line->first_svp);
819
820                 if (line->last_svp) 
821                         gnome_canvas_item_request_redraw_svp (item, line->last_svp);
822
823         }
824 }
825
826 /* Returns a copy of the line's points without the endpoint adjustments for
827  * arrowheads.
828  */
829 static GnomeCanvasPoints *
830 get_points (GnomeCanvasLine *line)
831 {
832         GnomeCanvasPoints *points;
833         int start_ofs, end_ofs;
834
835         if (line->num_points == 0)
836                 return NULL;
837
838         start_ofs = end_ofs = 0;
839
840         points = gnome_canvas_points_new (line->num_points);
841
842         /* Invariant:  if first_coords or last_coords exist, then the line's
843          * endpoints have been adjusted.
844          */
845
846         if (line->first_coords) {
847                 start_ofs = 1;
848
849                 points->coords[0] = line->first_coords[0];
850                 points->coords[1] = line->first_coords[1];
851         }
852
853         if (line->last_coords) {
854                 end_ofs = 1;
855
856                 points->coords[2 * (line->num_points - 1)] = line->last_coords[0];
857                 points->coords[2 * (line->num_points - 1) + 1] = line->last_coords[1];
858         }
859
860         memcpy (points->coords + 2 * start_ofs,
861                 line->coords + 2 * start_ofs,
862                 2 * (line->num_points - (start_ofs + end_ofs)) * sizeof (double));
863
864         return points;
865 }
866
867 static void
868 gnome_canvas_line_get_property (GObject              *object,
869                                 guint                 param_id,
870                                 GValue               *value,
871                                 GParamSpec           *pspec)
872 {
873         GnomeCanvasLine *line;
874
875         g_return_if_fail (object != NULL);
876         g_return_if_fail (GNOME_IS_CANVAS_LINE (object));
877
878         line = GNOME_CANVAS_LINE (object);
879
880         switch (param_id) {
881         case PROP_POINTS:
882                 /* get_points returns a copy */
883                 g_value_set_boxed_take_ownership (value, get_points (line));
884                 break;
885
886         case PROP_FILL_COLOR:
887                 g_value_take_string (value,
888                                      g_strdup_printf ("#%02x%02x%02x",
889                                      line->fill_rgba >> 24,
890                                      (line->fill_rgba >> 16) & 0xff,
891                                      (line->fill_rgba >> 8) & 0xff));
892                 break;
893
894         case PROP_FILL_COLOR_GDK: {
895                 GnomeCanvas *canvas = GNOME_CANVAS_ITEM (line)->canvas;
896                 GdkColormap *colormap = gtk_widget_get_colormap (GTK_WIDGET (canvas));
897                 GdkColor color;
898
899                 gdk_colormap_query_color (colormap, line->fill_pixel, &color);
900                 g_value_set_boxed (value, &color);
901                 break;
902         }
903
904         case PROP_FILL_COLOR_RGBA:
905                 g_value_set_uint (value, line->fill_rgba);
906                 break;
907
908         case PROP_FILL_STIPPLE:
909                 g_value_set_object (value, line->stipple);
910                 break;
911
912         case PROP_WIDTH_PIXELS:
913                 g_value_set_uint (value, line->width);
914                 break;
915                 
916         case PROP_WIDTH_UNITS:
917                 g_value_set_double (value, line->width);
918                 break;
919                 
920         case PROP_CAP_STYLE:
921                 g_value_set_enum (value, line->cap);
922                 break;
923
924         case PROP_JOIN_STYLE:
925                 g_value_set_enum (value, line->join);
926                 break;
927
928         case PROP_LINE_STYLE:
929                 g_value_set_enum (value, line->line_style);
930                 break;
931
932         case PROP_FIRST_ARROWHEAD:
933                 g_value_set_boolean (value, line->first_arrow);
934                 break;
935
936         case PROP_LAST_ARROWHEAD:
937                 g_value_set_boolean (value, line->last_arrow);
938                 break;
939
940         case PROP_SMOOTH:
941                 g_value_set_boolean (value, line->smooth);
942                 break;
943
944         case PROP_SPLINE_STEPS:
945                 g_value_set_uint (value, line->spline_steps);
946                 break;
947
948         case PROP_ARROW_SHAPE_A:
949                 g_value_set_double (value, line->shape_a);
950                 break;
951
952         case PROP_ARROW_SHAPE_B:
953                 g_value_set_double (value, line->shape_b);
954                 break;
955
956         case PROP_ARROW_SHAPE_C:
957                 g_value_set_double (value, line->shape_c);
958                 break;
959
960         default:
961                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
962                 break;
963         }
964 }
965
966 static void
967 gnome_canvas_line_render (GnomeCanvasItem *item,
968                           GnomeCanvasBuf *buf)
969 {
970         GnomeCanvasLine *line;
971
972         line = GNOME_CANVAS_LINE (item);
973
974         if (line->fill_svp != NULL)
975                 gnome_canvas_render_svp (buf, line->fill_svp, line->fill_rgba);
976
977         if (line->first_svp != NULL)
978                 gnome_canvas_render_svp (buf, line->first_svp, line->fill_rgba);
979
980         if (line->last_svp != NULL)
981                 gnome_canvas_render_svp (buf, line->last_svp, line->fill_rgba);
982 }
983
984
985 static ArtSVP *
986 svp_from_points (const double *item_coords, int num_points, const double affine[6])
987 {
988         ArtVpath *vpath;
989         ArtSVP *svp;
990         double x, y;
991         int i;
992
993         vpath = art_new (ArtVpath, num_points + 2);
994
995         for (i = 0; i < num_points; i++) {
996                 vpath[i].code = i == 0 ? ART_MOVETO : ART_LINETO;
997                 x = item_coords[i * 2];
998                 y = item_coords[i * 2 + 1];
999                 vpath[i].x = x * affine[0] + y * affine[2] + affine[4];
1000                 vpath[i].y = x * affine[1] + y * affine[3] + affine[5];
1001         }
1002 #if 0
1003         vpath[i].code = ART_LINETO;
1004         vpath[i].x = vpath[0].x;
1005         vpath[i].y = vpath[0].y;
1006         i++;
1007 #endif
1008         vpath[i].code = ART_END;
1009         vpath[i].x = 0;
1010         vpath[i].y = 0;
1011
1012         svp = art_svp_from_vpath (vpath);
1013
1014         art_free (vpath);
1015
1016         return svp;
1017 }
1018
1019 static void
1020 gnome_canvas_line_update (GnomeCanvasItem *item, double *affine, ArtSVP *clip_path, int flags)
1021 {
1022         GnomeCanvasLine *line;
1023         int i;
1024         ArtVpath *vpath;
1025         ArtPoint pi, pc;
1026         double width;
1027         ArtSVP *svp;
1028         double x1, y1, x2, y2;
1029
1030         line = GNOME_CANVAS_LINE (item);
1031
1032         if (parent_class->update)
1033                 (* parent_class->update) (item, affine, clip_path, flags);
1034
1035         reconfigure_arrows (line);
1036
1037         if (item->canvas->aa) {
1038                 gnome_canvas_item_reset_bounds (item);
1039
1040                 vpath = art_new (ArtVpath, line->num_points + 2);
1041
1042                 for (i = 0; i < line->num_points; i++) {
1043                         pi.x = line->coords[i * 2];
1044                         pi.y = line->coords[i * 2 + 1];
1045                         art_affine_point (&pc, &pi, affine);
1046                         vpath[i].code = i == 0 ? ART_MOVETO : ART_LINETO;
1047                         vpath[i].x = pc.x;
1048                         vpath[i].y = pc.y;
1049                 }
1050                 vpath[i].code = ART_END;
1051                 vpath[i].x = 0;
1052                 vpath[i].y = 0;
1053
1054                 if (line->width_pixels)
1055                         width = line->width;
1056                 else
1057                         width = line->width * art_affine_expansion (affine);
1058
1059                 if (width < 0.5)
1060                         width = 0.5;
1061
1062                 svp = art_svp_vpath_stroke (vpath,
1063                                             gnome_canvas_join_gdk_to_art (line->join),
1064                                             gnome_canvas_cap_gdk_to_art (line->cap),
1065                                             width,
1066                                             4,
1067                                             0.25);
1068                 art_free (vpath);
1069
1070                 gnome_canvas_item_update_svp_clip (item, &line->fill_svp, svp, clip_path);
1071
1072                 if (line->first_arrow && line->first_coords) {
1073                         svp = svp_from_points (line->first_coords, NUM_ARROW_POINTS, affine);
1074                         gnome_canvas_item_update_svp_clip (item, 
1075                                         &line->first_svp, svp, clip_path);
1076                 }
1077
1078
1079                 if (line->last_arrow && line->last_coords) {
1080                         svp = svp_from_points (line->last_coords, NUM_ARROW_POINTS, affine);
1081                         gnome_canvas_item_update_svp_clip (item, 
1082                                         &line->last_svp, svp, clip_path);
1083                 }
1084
1085
1086         } else {
1087                 set_line_gc_foreground (line);
1088                 set_line_gc_width (line);
1089                 set_stipple (line, line->stipple, TRUE);
1090
1091                 get_bounds_canvas (line, &x1, &y1, &x2, &y2, affine);
1092                 gnome_canvas_update_bbox (item, x1, y1, x2, y2);
1093         }
1094 }
1095
1096 static void
1097 gnome_canvas_line_realize (GnomeCanvasItem *item)
1098 {
1099         GnomeCanvasLine *line;
1100
1101         line = GNOME_CANVAS_LINE (item);
1102
1103         if (parent_class->realize)
1104                 (* parent_class->realize) (item);
1105
1106         line->gc = gdk_gc_new (item->canvas->layout.bin_window);
1107
1108 #if 0
1109         (* GNOME_CANVAS_ITEM_CLASS (item->object.klass)->update) (item, NULL, NULL, 0);
1110 #endif
1111 }
1112
1113 static void
1114 gnome_canvas_line_unrealize (GnomeCanvasItem *item)
1115 {
1116         GnomeCanvasLine *line;
1117
1118         line = GNOME_CANVAS_LINE (item);
1119
1120         g_object_unref (line->gc);
1121         line->gc = NULL;
1122
1123         if (parent_class->unrealize)
1124                 (* parent_class->unrealize) (item);
1125 }
1126
1127 static void
1128 item_to_canvas (GnomeCanvas * canvas, double *item_coords, GdkPoint *canvas_coords, int num_points,
1129                 int *num_drawn_points, double i2c[6], int x, int y)
1130 {
1131         int i;
1132         int old_cx, old_cy;
1133         int cx, cy;
1134         ArtPoint pi, pc;
1135
1136 #ifdef VERBOSE
1137         {
1138                 char str[128];
1139                 art_affine_to_string (str, i2c);
1140                 g_print ("line item_to_canvas %s\n", str);
1141         }
1142 #endif
1143
1144         /* the first point is always drawn */
1145
1146         pi.x = item_coords[0];
1147         pi.y = item_coords[1];
1148         art_affine_point (&pc, &pi, i2c);
1149         cx = floor (pc.x + 0.5);
1150         cy = floor (pc.y + 0.5);
1151         canvas_coords->x = cx - x;
1152         canvas_coords->y = cy - y;
1153         canvas_coords++;
1154         old_cx = cx;
1155         old_cy = cy;
1156         *num_drawn_points = 1;
1157
1158         for (i = 1; i < num_points; i++) {
1159                 pi.x = item_coords[i * 2];
1160                 pi.y = item_coords[i * 2 + 1];
1161                 art_affine_point (&pc, &pi, i2c);
1162                 cx = floor (pc.x + 0.5);
1163                 cy = floor (pc.y + 0.5);
1164                 if (old_cx != cx || old_cy != cy) {
1165                         canvas_coords->x = cx - x;
1166                         canvas_coords->y = cy - y;
1167                         old_cx = cx;
1168                         old_cy = cy;
1169                         canvas_coords++;
1170                         (*num_drawn_points)++;
1171                 }
1172         }
1173 }
1174
1175 static void
1176 gnome_canvas_line_draw (GnomeCanvasItem *item, GdkDrawable *drawable,
1177                         int x, int y, int width, int height)
1178 {
1179         GnomeCanvasLine *line;
1180         GdkPoint static_points[NUM_STATIC_POINTS];
1181         GdkPoint *points;
1182         int actual_num_points_drawn;
1183         double i2c[6];
1184
1185         line = GNOME_CANVAS_LINE (item);
1186
1187         if (line->num_points == 0)
1188                 return;
1189
1190         /* Build array of canvas pixel coordinates */
1191
1192         if (line->num_points <= NUM_STATIC_POINTS)
1193                 points = static_points;
1194         else
1195                 points = g_new (GdkPoint, line->num_points);
1196
1197
1198         gnome_canvas_item_i2c_affine (item, i2c);
1199
1200         item_to_canvas (item->canvas, line->coords, points, line->num_points,
1201                         &actual_num_points_drawn, i2c, x, y);
1202
1203         if (line->stipple)
1204                 gnome_canvas_set_stipple_origin (item->canvas, line->gc);
1205
1206         gdk_draw_lines (drawable, line->gc, points, actual_num_points_drawn);
1207
1208         if (points != static_points)
1209                 g_free (points);
1210
1211         /* Draw arrowheads */
1212
1213         points = static_points;
1214
1215         if (line->first_arrow) {
1216                 item_to_canvas (item->canvas, line->first_coords, points, NUM_ARROW_POINTS,
1217                                 &actual_num_points_drawn, i2c, x, y);
1218                 gdk_draw_polygon (drawable, line->gc, TRUE, points, actual_num_points_drawn );
1219         }
1220
1221         if (line->last_arrow) {
1222                 item_to_canvas (item->canvas, line->last_coords, points, NUM_ARROW_POINTS,
1223                                 &actual_num_points_drawn, i2c, x, y);
1224                 gdk_draw_polygon (drawable, line->gc, TRUE, points, actual_num_points_drawn );
1225         }
1226 }
1227
1228 static double
1229 gnome_canvas_line_point (GnomeCanvasItem *item, double x, double y,
1230                          int cx, int cy, GnomeCanvasItem **actual_item)
1231 {
1232         GnomeCanvasLine *line;
1233         double *line_points = NULL, *coords;
1234         double static_points[2 * NUM_STATIC_POINTS];
1235         double poly[10];
1236         double best, dist;
1237         double dx, dy;
1238         double width;
1239         int num_points = 0, i;
1240         int changed_miter_to_bevel;
1241
1242 #ifdef VERBOSE
1243         g_print ("gnome_canvas_line_point x, y = (%g, %g); cx, cy = (%d, %d)\n", x, y, cx, cy);
1244 #endif
1245
1246         line = GNOME_CANVAS_LINE (item);
1247
1248         *actual_item = item;
1249
1250         best = 1.0e36;
1251
1252         /* Handle smoothed lines by generating an expanded set ot points */
1253
1254         if (line->smooth && (line->num_points > 2)) {
1255                 /* FIXME */
1256         } else {
1257                 num_points = line->num_points;
1258                 line_points = line->coords;
1259         }
1260
1261         /* Compute a polygon for each edge of the line and test the point against it.  The effective
1262          * width of the line is adjusted so that it will be at least one pixel thick (so that zero
1263          * pixel-wide lines can be pickedup as well).
1264          */
1265
1266         if (line->width_pixels)
1267                 width = line->width / item->canvas->pixels_per_unit;
1268         else
1269                 width = line->width;
1270
1271         if (width < (1.0 / item->canvas->pixels_per_unit))
1272                 width = 1.0 / item->canvas->pixels_per_unit;
1273
1274         changed_miter_to_bevel = 0;
1275
1276         for (i = num_points, coords = line_points; i >= 2; i--, coords += 2) {
1277                 /* If rounding is done around the first point, then compute distance between the
1278                  * point and the first point.
1279                  */
1280
1281                 if (((line->cap == GDK_CAP_ROUND) && (i == num_points))
1282                     || ((line->join == GDK_JOIN_ROUND) && (i != num_points))) {
1283                         dx = coords[0] - x;
1284                         dy = coords[1] - y;
1285                         dist = sqrt (dx * dx + dy * dy) - width / 2.0;
1286                         if (dist < GNOME_CANVAS_EPSILON) {
1287                                 best = 0.0;
1288                                 goto done;
1289                         } else if (dist < best)
1290                                 best = dist;
1291                 }
1292
1293                 /* Compute the polygonal shape corresponding to this edge, with two points for the
1294                  * first point of the edge and two points for the last point of the edge.
1295                  */
1296
1297                 if (i == num_points)
1298                         gnome_canvas_get_butt_points (coords[2], coords[3], coords[0], coords[1],
1299                                                       width, (line->cap == GDK_CAP_PROJECTING),
1300                                                       poly, poly + 1, poly + 2, poly + 3);
1301                 else if ((line->join == GDK_JOIN_MITER) && !changed_miter_to_bevel) {
1302                         poly[0] = poly[6];
1303                         poly[1] = poly[7];
1304                         poly[2] = poly[4];
1305                         poly[3] = poly[5];
1306                 } else {
1307                         gnome_canvas_get_butt_points (coords[2], coords[3], coords[0], coords[1],
1308                                                       width, FALSE,
1309                                                       poly, poly + 1, poly + 2, poly + 3);
1310
1311                         /* If this line uses beveled joints, then check the distance to a polygon
1312                          * comprising the last two points of the previous polygon and the first two
1313                          * from this polygon; this checks the wedges that fill the mitered point.
1314                          */
1315
1316                         if ((line->join == GDK_JOIN_BEVEL) || changed_miter_to_bevel) {
1317                                 poly[8] = poly[0];
1318                                 poly[9] = poly[1];
1319
1320                                 dist = gnome_canvas_polygon_to_point (poly, 5, x, y);
1321                                 if (dist < GNOME_CANVAS_EPSILON) {
1322                                         best = 0.0;
1323                                         goto done;
1324                                 } else if (dist < best)
1325                                         best = dist;
1326
1327                                 changed_miter_to_bevel = FALSE;
1328                         }
1329                 }
1330
1331                 if (i == 2)
1332                         gnome_canvas_get_butt_points (coords[0], coords[1], coords[2], coords[3],
1333                                                       width, (line->cap == GDK_CAP_PROJECTING),
1334                                                       poly + 4, poly + 5, poly + 6, poly + 7);
1335                 else if (line->join == GDK_JOIN_MITER) {
1336                         if (!gnome_canvas_get_miter_points (coords[0], coords[1],
1337                                                             coords[2], coords[3],
1338                                                             coords[4], coords[5],
1339                                                             width,
1340                                                             poly + 4, poly + 5, poly + 6, poly + 7)) {
1341                                 changed_miter_to_bevel = TRUE;
1342                                 gnome_canvas_get_butt_points (coords[0], coords[1], coords[2], coords[3],
1343                                                               width, FALSE,
1344                                                               poly + 4, poly + 5, poly + 6, poly + 7);
1345                         }
1346                 } else
1347                         gnome_canvas_get_butt_points (coords[0], coords[1], coords[2], coords[3],
1348                                                       width, FALSE,
1349                                                       poly + 4, poly + 5, poly + 6, poly + 7);
1350
1351                 poly[8] = poly[0];
1352                 poly[9] = poly[1];
1353
1354                 dist = gnome_canvas_polygon_to_point (poly, 5, x, y);
1355                 if (dist < GNOME_CANVAS_EPSILON) {
1356                         best = 0.0;
1357                         goto done;
1358                 } else if (dist < best)
1359                         best = dist;
1360         }
1361
1362         /* If caps are rounded, check the distance to the cap around the final end point of the line */
1363
1364         if (line->cap == GDK_CAP_ROUND) {
1365                 dx = coords[0] - x;
1366                 dy = coords[1] - y;
1367                 dist = sqrt (dx * dx + dy * dy) - width / 2.0;
1368                 if (dist < GNOME_CANVAS_EPSILON) {
1369                         best = 0.0;
1370                         goto done;
1371                 } else
1372                         best = dist;
1373         }
1374
1375         /* sometimes the GnomeCanvasItem::update signal will not have
1376            been processed between deleting the arrow points and a call
1377            to this routine -- this can cause a segfault here */
1378         if ((line->first_arrow && !line->first_coords) ||
1379             (line->last_arrow && !line->last_coords))
1380                 reconfigure_arrows(line);
1381
1382         /* If there are arrowheads, check the distance to them */
1383
1384         if (line->first_arrow) {
1385                 dist = gnome_canvas_polygon_to_point (line->first_coords, NUM_ARROW_POINTS, x, y);
1386                 if (dist < GNOME_CANVAS_EPSILON) {
1387                         best = 0.0;
1388                         goto done;
1389                 } else
1390                         best = dist;
1391         }
1392
1393         if (line->last_arrow) {
1394                 dist = gnome_canvas_polygon_to_point (line->last_coords, NUM_ARROW_POINTS, x, y);
1395                 if (dist < GNOME_CANVAS_EPSILON) {
1396                         best = 0.0;
1397                         goto done;
1398                 } else
1399                         best = dist;
1400         }
1401
1402 done:
1403
1404         if ((line_points != static_points) && (line_points != line->coords))
1405                 g_free (line_points);
1406
1407         return best;
1408 }
1409
1410 static void
1411 gnome_canvas_line_bounds (GnomeCanvasItem *item, double *x1, double *y1, double *x2, double *y2)
1412 {
1413         GnomeCanvasLine *line;
1414
1415         line = GNOME_CANVAS_LINE (item);
1416
1417         if (line->num_points == 0) {
1418                 *x1 = *y1 = *x2 = *y2 = 0.0;
1419                 return;
1420         }
1421
1422         get_bounds (line, x1, y1, x2, y2);
1423 }