~ml-launchpad/ubuntu/natty/gcompris/fix-for-777349

« back to all changes in this revision

Viewing changes to src/goocanvas/src/goocanvaspolyline.c

  • Committer: Bazaar Package Importer
  • Author(s): Marc Gariepy, Marc Gariepy, Stephane Graber
  • Date: 2010-01-04 17:42:49 UTC
  • mfrom: (1.1.14 upstream)
  • Revision ID: james.westby@ubuntu.com-20100104174249-7bupatd9dtxyhvs4
Tags: 9.0-0ubuntu1
[Marc Gariepy]
* New upstream release (9.0).
* Remove cache.c from POTFILES to avoid FTBFS
* Remove unneeded rm in debian/rules (file no longer exists upstream)

[Stephane Graber]
* Bump Debian standards to 3.8.3
* Add patch system (dpatch)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * GooCanvas. Copyright (C) 2005 Damon Chaplin.
 
3
 * Released under the GNU LGPL license. See COPYING for details.
 
4
 *
 
5
 * goocanvaspolyline.c - polyline item, with optional arrows.
 
6
 */
 
7
 
 
8
/**
 
9
 * SECTION:goocanvaspolyline
 
10
 * @Title: GooCanvasPolyline
 
11
 * @Short_Description: a polyline item (a series of lines with optional arrows).
 
12
 *
 
13
 * GooCanvasPolyline represents a polyline item, which is a series of one or
 
14
 * more lines, with optional arrows at either end.
 
15
 *
 
16
 * It is a subclass of #GooCanvasItemSimple and so inherits all of the style
 
17
 * properties such as "stroke-color", "fill-color" and "line-width".
 
18
 *
 
19
 * It also implements the #GooCanvasItem interface, so you can use the
 
20
 * #GooCanvasItem functions such as goo_canvas_item_raise() and
 
21
 * goo_canvas_item_rotate().
 
22
 *
 
23
 * To create a #GooCanvasPolyline use goo_canvas_polyline_new(), or
 
24
 * goo_canvas_polyline_new_line() for a simple line between two points.
 
25
 *
 
26
 * To get or set the properties of an existing #GooCanvasPolyline, use
 
27
 * g_object_get() and g_object_set().
 
28
 */
 
29
#include <config.h>
 
30
#include <math.h>
 
31
#include <stdarg.h>
 
32
#include <string.h>
 
33
#include <glib/gi18n-lib.h>
 
34
#include <gtk/gtk.h>
 
35
#include "goocanvaspolyline.h"
 
36
#include "goocanvas.h"
 
37
 
 
38
 
 
39
/**
 
40
 * goo_canvas_points_new:
 
41
 * @num_points: the number of points to create space for.
 
42
 * 
 
43
 * Creates a new #GooCanvasPoints struct with space for the given number of
 
44
 * points. It should be freed with goo_canvas_points_unref().
 
45
 * 
 
46
 * Returns: a new #GooCanvasPoints struct.
 
47
 **/
 
48
GooCanvasPoints*
 
49
goo_canvas_points_new   (int    num_points)
 
50
{
 
51
  GooCanvasPoints *points;
 
52
 
 
53
  points = g_slice_new (GooCanvasPoints);
 
54
  points->num_points = num_points;
 
55
  points->coords = g_slice_alloc (num_points * 2 * sizeof (double));
 
56
  points->ref_count = 1;
 
57
 
 
58
  return points;
 
59
}
 
60
 
 
61
 
 
62
/**
 
63
 * goo_canvas_points_ref:
 
64
 * @points: a #GooCanvasPoints struct.
 
65
 * 
 
66
 * Increments the reference count of the given #GooCanvasPoints struct.
 
67
 * 
 
68
 * Returns: the #GooCanvasPoints struct.
 
69
 **/
 
70
GooCanvasPoints*
 
71
goo_canvas_points_ref   (GooCanvasPoints *points)
 
72
{
 
73
  points->ref_count++;
 
74
  return points;
 
75
}
 
76
 
 
77
 
 
78
/**
 
79
 * goo_canvas_points_unref:
 
80
 * @points: a #GooCanvasPoints struct.
 
81
 * 
 
82
 * Decrements the reference count of the given #GooCanvasPoints struct,
 
83
 * freeing it if the reference count falls to zero.
 
84
 **/
 
85
void
 
86
goo_canvas_points_unref (GooCanvasPoints *points)
 
87
{
 
88
  if (--points->ref_count == 0)
 
89
    {
 
90
      g_slice_free1 (points->num_points * 2 * sizeof (double), points->coords);
 
91
      g_slice_free (GooCanvasPoints, points);
 
92
    }
 
93
}
 
94
 
 
95
 
 
96
GType
 
97
goo_canvas_points_get_type (void)
 
98
{
 
99
  static GType type_canvas_points = 0;
 
100
 
 
101
  if (!type_canvas_points)
 
102
    type_canvas_points = g_boxed_type_register_static
 
103
      ("GooCanvasPoints", 
 
104
       (GBoxedCopyFunc) goo_canvas_points_ref,
 
105
       (GBoxedFreeFunc) goo_canvas_points_unref);
 
106
 
 
107
  return type_canvas_points;
 
108
}
 
109
 
 
110
 
 
111
enum {
 
112
  PROP_0,
 
113
 
 
114
  PROP_POINTS,
 
115
  PROP_CLOSE_PATH,
 
116
  PROP_START_ARROW,
 
117
  PROP_END_ARROW,
 
118
  PROP_ARROW_LENGTH,
 
119
  PROP_ARROW_WIDTH,
 
120
  PROP_ARROW_TIP_LENGTH,
 
121
 
 
122
  PROP_X,
 
123
  PROP_Y,
 
124
  PROP_WIDTH,
 
125
  PROP_HEIGHT
 
126
};
 
127
 
 
128
 
 
129
static void canvas_item_interface_init       (GooCanvasItemIface *iface);
 
130
 
 
131
G_DEFINE_TYPE_WITH_CODE (GooCanvasPolyline, goo_canvas_polyline,
 
132
                         GOO_TYPE_CANVAS_ITEM_SIMPLE,
 
133
                         G_IMPLEMENT_INTERFACE (GOO_TYPE_CANVAS_ITEM,
 
134
                                                canvas_item_interface_init))
 
135
 
 
136
 
 
137
static void
 
138
goo_canvas_polyline_install_common_properties (GObjectClass *gobject_class)
 
139
{
 
140
  g_object_class_install_property (gobject_class, PROP_POINTS,
 
141
                                   g_param_spec_boxed ("points",
 
142
                                                       _("Points"),
 
143
                                                       _("The array of points"),
 
144
                                                       GOO_TYPE_CANVAS_POINTS,
 
145
                                                       G_PARAM_READWRITE));
 
146
 
 
147
  g_object_class_install_property (gobject_class, PROP_CLOSE_PATH,
 
148
                                   g_param_spec_boolean ("close-path",
 
149
                                                         _("Close Path"),
 
150
                                                         _("If the last point should be connected to the first"),
 
151
                                                         FALSE,
 
152
                                                         G_PARAM_READWRITE));
 
153
 
 
154
  g_object_class_install_property (gobject_class, PROP_START_ARROW,
 
155
                                   g_param_spec_boolean ("start-arrow",
 
156
                                                         _("Start Arrow"),
 
157
                                                         _("If an arrow should be displayed at the start of the polyline"),
 
158
                                                         FALSE,
 
159
                                                         G_PARAM_READWRITE));
 
160
 
 
161
  g_object_class_install_property (gobject_class, PROP_END_ARROW,
 
162
                                   g_param_spec_boolean ("end-arrow",
 
163
                                                         _("End Arrow"),
 
164
                                                         _("If an arrow should be displayed at the end of the polyline"),
 
165
                                                         FALSE,
 
166
                                                         G_PARAM_READWRITE));
 
167
 
 
168
  g_object_class_install_property (gobject_class, PROP_ARROW_LENGTH,
 
169
                                   g_param_spec_double ("arrow-length",
 
170
                                                        _("Arrow Length"),
 
171
                                                        _("The length of the arrows, as a multiple of the line width"),
 
172
                                                        0.0, G_MAXDOUBLE, 5.0,
 
173
                                                        G_PARAM_READWRITE));
 
174
 
 
175
  g_object_class_install_property (gobject_class, PROP_ARROW_WIDTH,
 
176
                                   g_param_spec_double ("arrow-width",
 
177
                                                        _("Arrow Width"),
 
178
                                                        _("The width of the arrows, as a multiple of the line width"),
 
179
                                                        0.0, G_MAXDOUBLE, 4.0,
 
180
                                                        G_PARAM_READWRITE));
 
181
 
 
182
  g_object_class_install_property (gobject_class, PROP_ARROW_TIP_LENGTH,
 
183
                                   g_param_spec_double ("arrow-tip-length",
 
184
                                                        _("Arrow Tip Length"),
 
185
                                                        _("The length of the arrow tip, as a multiple of the line width"),
 
186
                                                        0.0, G_MAXDOUBLE, 4.0,
 
187
                                                        G_PARAM_READWRITE));
 
188
 
 
189
  g_object_class_install_property (gobject_class, PROP_X,
 
190
                                   g_param_spec_double ("x",
 
191
                                                        "X",
 
192
                                                        _("The x coordinate of the left-most point of the polyline"),
 
193
                                                        -G_MAXDOUBLE,
 
194
                                                        G_MAXDOUBLE, 0.0,
 
195
                                                        G_PARAM_READWRITE));
 
196
 
 
197
  g_object_class_install_property (gobject_class, PROP_Y,
 
198
                                   g_param_spec_double ("y",
 
199
                                                        "Y",
 
200
                                                        _("The y coordinate of the top-most point of the polyline"),
 
201
                                                        -G_MAXDOUBLE,
 
202
                                                        G_MAXDOUBLE, 0.0,
 
203
                                                        G_PARAM_READWRITE));
 
204
 
 
205
  g_object_class_install_property (gobject_class, PROP_WIDTH,
 
206
                                   g_param_spec_double ("width",
 
207
                                                        _("Width"),
 
208
                                                        _("The width of the polyline"),
 
209
                                                        0.0, G_MAXDOUBLE, 0.0,
 
210
                                                        G_PARAM_READWRITE));
 
211
 
 
212
  g_object_class_install_property (gobject_class, PROP_HEIGHT,
 
213
                                   g_param_spec_double ("height",
 
214
                                                        _("Height"),
 
215
                                                        _("The height of the polyline"),
 
216
                                                        0.0, G_MAXDOUBLE, 0.0,
 
217
                                                        G_PARAM_READWRITE));
 
218
}
 
219
 
 
220
 
 
221
static void
 
222
goo_canvas_polyline_init (GooCanvasPolyline *polyline)
 
223
{
 
224
  polyline->polyline_data = g_slice_new0 (GooCanvasPolylineData);
 
225
}
 
226
 
 
227
 
 
228
static void
 
229
goo_canvas_polyline_finalize (GObject *object)
 
230
{
 
231
  GooCanvasItemSimple *simple = (GooCanvasItemSimple*) object;
 
232
  GooCanvasPolyline *polyline = (GooCanvasPolyline*) object;
 
233
 
 
234
  /* Free our data if we didn't have a model. (If we had a model it would
 
235
     have been reset in dispose() and simple_data will be NULL.) */
 
236
  if (simple->simple_data)
 
237
    {
 
238
      g_slice_free1 (polyline->polyline_data->num_points * 2 * sizeof (gdouble),
 
239
                     polyline->polyline_data->coords);
 
240
      g_slice_free (GooCanvasPolylineArrowData, polyline->polyline_data->arrow_data);
 
241
      g_slice_free (GooCanvasPolylineData, polyline->polyline_data);
 
242
    }
 
243
  polyline->polyline_data = NULL;
 
244
 
 
245
  G_OBJECT_CLASS (goo_canvas_polyline_parent_class)->finalize (object);
 
246
}
 
247
 
 
248
 
 
249
static void
 
250
goo_canvas_polyline_get_extent (GooCanvasPolylineData *polyline_data,
 
251
                                GooCanvasBounds *bounds)
 
252
{
 
253
  guint i;
 
254
 
 
255
  if (polyline_data->num_points == 0)
 
256
    {
 
257
      bounds->x1 = bounds->y1 = bounds->x2 = bounds->y2 = 0.0;
 
258
    }
 
259
  else
 
260
    {
 
261
      bounds->x1 = bounds->x2 = polyline_data->coords[0];
 
262
      bounds->y1 = bounds->y2 = polyline_data->coords[1];
 
263
 
 
264
      for (i = 1; i < polyline_data->num_points; i++)
 
265
        {
 
266
          bounds->x1 = MIN (bounds->x1, polyline_data->coords[2 * i]);
 
267
          bounds->y1 = MIN (bounds->y1, polyline_data->coords[2 * i + 1]);
 
268
          bounds->x2 = MAX (bounds->x2, polyline_data->coords[2 * i]);
 
269
          bounds->y2 = MAX (bounds->y2, polyline_data->coords[2 * i + 1]);
 
270
        }
 
271
    }
 
272
}
 
273
 
 
274
 
 
275
static void
 
276
goo_canvas_polyline_get_common_property (GObject              *object,
 
277
                                         GooCanvasPolylineData *polyline_data,
 
278
                                         guint                 prop_id,
 
279
                                         GValue               *value,
 
280
                                         GParamSpec           *pspec)
 
281
{
 
282
  GooCanvasPoints *points;
 
283
  GooCanvasBounds  extent;
 
284
 
 
285
  switch (prop_id)
 
286
    {
 
287
    case PROP_POINTS:
 
288
      if (polyline_data->num_points == 0)
 
289
        {
 
290
          g_value_set_boxed (value, NULL);
 
291
        }
 
292
      else
 
293
        {
 
294
          points = goo_canvas_points_new (polyline_data->num_points);
 
295
          memcpy (points->coords, polyline_data->coords,
 
296
                  polyline_data->num_points * 2 * sizeof (double));
 
297
          g_value_set_boxed (value, points);
 
298
          goo_canvas_points_unref (points);
 
299
        }
 
300
      break;
 
301
    case PROP_CLOSE_PATH:
 
302
      g_value_set_boolean (value, polyline_data->close_path);
 
303
      break;
 
304
    case PROP_START_ARROW:
 
305
      g_value_set_boolean (value, polyline_data->start_arrow);
 
306
      break;
 
307
    case PROP_END_ARROW:
 
308
      g_value_set_boolean (value, polyline_data->end_arrow);
 
309
      break;
 
310
    case PROP_ARROW_LENGTH:
 
311
      g_value_set_double (value, polyline_data->arrow_data
 
312
                          ? polyline_data->arrow_data->arrow_length : 5.0);
 
313
      break;
 
314
    case PROP_ARROW_WIDTH:
 
315
      g_value_set_double (value, polyline_data->arrow_data
 
316
                          ? polyline_data->arrow_data->arrow_width : 4.0);
 
317
      break;
 
318
    case PROP_ARROW_TIP_LENGTH:
 
319
      g_value_set_double (value, polyline_data->arrow_data
 
320
                          ? polyline_data->arrow_data->arrow_tip_length : 4.0);
 
321
      break;
 
322
    case PROP_X:
 
323
      goo_canvas_polyline_get_extent (polyline_data, &extent);
 
324
      g_value_set_double (value, extent.x1);
 
325
      break;
 
326
    case PROP_Y:
 
327
      goo_canvas_polyline_get_extent (polyline_data, &extent);
 
328
      g_value_set_double (value, extent.y1);
 
329
      break;
 
330
    case PROP_WIDTH:
 
331
      goo_canvas_polyline_get_extent (polyline_data, &extent);
 
332
      g_value_set_double (value, extent.x2 - extent.x1);
 
333
      break;
 
334
    case PROP_HEIGHT:
 
335
      goo_canvas_polyline_get_extent (polyline_data, &extent);
 
336
      g_value_set_double (value, extent.y2 - extent.y1);
 
337
      break;
 
338
    default:
 
339
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 
340
      break;
 
341
  }
 
342
}
 
343
 
 
344
 
 
345
static void
 
346
goo_canvas_polyline_get_property (GObject              *object,
 
347
                                 guint                 prop_id,
 
348
                                 GValue               *value,
 
349
                                 GParamSpec           *pspec)
 
350
{
 
351
  GooCanvasPolyline *polyline = (GooCanvasPolyline*) object;
 
352
 
 
353
  goo_canvas_polyline_get_common_property (object, polyline->polyline_data,
 
354
                                           prop_id, value, pspec);
 
355
}
 
356
 
 
357
 
 
358
static void
 
359
ensure_arrow_data (GooCanvasPolylineData *polyline_data)
 
360
{
 
361
  if (!polyline_data->arrow_data)
 
362
    {
 
363
      polyline_data->arrow_data = g_slice_new (GooCanvasPolylineArrowData);
 
364
 
 
365
      /* These seem like reasonable defaults for the arrow dimensions.
 
366
         They are specified in multiples of the line width so they scale OK. */
 
367
      polyline_data->arrow_data->arrow_width = 4.0;
 
368
      polyline_data->arrow_data->arrow_length = 5.0;
 
369
      polyline_data->arrow_data->arrow_tip_length = 4.0;
 
370
    }
 
371
}
 
372
 
 
373
#define GOO_CANVAS_EPSILON 1e-10
 
374
 
 
375
static void
 
376
reconfigure_arrow (GooCanvasPolylineData *polyline_data,
 
377
                   gint                   end_point,
 
378
                   gint                   prev_point,
 
379
                   gdouble                line_width,
 
380
                   gdouble               *line_coords,
 
381
                   gdouble               *arrow_coords)
 
382
{
 
383
  GooCanvasPolylineArrowData *arrow = polyline_data->arrow_data;
 
384
  double dx, dy, length, sin_theta, cos_theta;
 
385
  double half_arrow_width, arrow_length, arrow_tip_length;
 
386
  double arrow_end_center_x, arrow_end_center_y;
 
387
  double arrow_tip_center_x, arrow_tip_center_y;
 
388
  double x_offset, y_offset, half_line_width, line_trim;
 
389
 
 
390
  dx = polyline_data->coords[prev_point] - polyline_data->coords[end_point];
 
391
  dy = polyline_data->coords[prev_point + 1] - polyline_data->coords[end_point + 1];
 
392
  length = sqrt (dx * dx + dy * dy);
 
393
 
 
394
  if (length < GOO_CANVAS_EPSILON)
 
395
    {
 
396
      /* The length is too short to reliably get the angle so just guess. */
 
397
      sin_theta = 1.0;
 
398
      cos_theta = 0.0;
 
399
    }
 
400
  else
 
401
    {
 
402
      /* Calculate a unit vector moving from the arrow point along the line. */
 
403
      sin_theta = dy / length;
 
404
      cos_theta = dx / length;
 
405
    }
 
406
 
 
407
  /* These are all specified in multiples of the line width, so convert. */
 
408
  half_arrow_width = arrow->arrow_width * line_width / 2;
 
409
  arrow_length = arrow->arrow_length * line_width;
 
410
  arrow_tip_length = arrow->arrow_tip_length * line_width;
 
411
 
 
412
  /* The arrow tip is at the end point of the line. */
 
413
  arrow_coords[0] = polyline_data->coords[end_point];
 
414
  arrow_coords[1] = polyline_data->coords[end_point + 1];
 
415
 
 
416
  /* Calculate the end of the arrow, along the center line. */
 
417
  arrow_end_center_x = arrow_coords[0] + (arrow_length * cos_theta);
 
418
  arrow_end_center_y = arrow_coords[1] + (arrow_length * sin_theta);
 
419
 
 
420
  /* Now calculate the 2 end points of the arrow either side of the line. */
 
421
  x_offset = half_arrow_width * sin_theta;
 
422
  y_offset = half_arrow_width * cos_theta;
 
423
 
 
424
  arrow_coords[2] = arrow_end_center_x + x_offset;
 
425
  arrow_coords[3] = arrow_end_center_y - y_offset;
 
426
 
 
427
  arrow_coords[8] = arrow_end_center_x - x_offset;
 
428
  arrow_coords[9] = arrow_end_center_y + y_offset;
 
429
 
 
430
  /* Calculate the end of the arrow tip, along the center line. */
 
431
  arrow_tip_center_x = arrow_coords[0] + (arrow_tip_length * cos_theta);
 
432
  arrow_tip_center_y = arrow_coords[1] + (arrow_tip_length * sin_theta);
 
433
 
 
434
  /* Now calculate the 2 arrow points on either edge of the line. */
 
435
  half_line_width = line_width / 2.0;
 
436
  x_offset = half_line_width * sin_theta;
 
437
  y_offset = half_line_width * cos_theta;
 
438
 
 
439
  arrow_coords[4] = arrow_tip_center_x + x_offset;
 
440
  arrow_coords[5] = arrow_tip_center_y - y_offset;
 
441
 
 
442
  arrow_coords[6] = arrow_tip_center_x - x_offset;
 
443
  arrow_coords[7] = arrow_tip_center_y + y_offset;
 
444
 
 
445
  /* Calculate the new end of the line. We have to move it back slightly so
 
446
     it doesn't draw over the arrow tip. But we overlap the arrow by a small
 
447
     fraction of the line width to avoid a tiny gap. */
 
448
  line_trim = arrow_tip_length - (line_width / 10.0);
 
449
  line_coords[0] = arrow_coords[0] + (line_trim * cos_theta);
 
450
  line_coords[1] = arrow_coords[1] + (line_trim * sin_theta);
 
451
}
 
452
 
 
453
 
 
454
/* Recalculates the arrow polygons for the line */
 
455
static void
 
456
goo_canvas_polyline_reconfigure_arrows (GooCanvasPolyline *polyline)
 
457
{
 
458
  GooCanvasItemSimple *simple = (GooCanvasItemSimple*) polyline;
 
459
  GooCanvasPolylineData *polyline_data = polyline->polyline_data;
 
460
  double line_width;
 
461
 
 
462
  if (!polyline_data->reconfigure_arrows)
 
463
    return;
 
464
 
 
465
  polyline_data->reconfigure_arrows = FALSE;
 
466
 
 
467
  if (polyline_data->num_points < 2
 
468
      || (!polyline_data->start_arrow && !polyline_data->end_arrow))
 
469
    return;
 
470
 
 
471
  line_width = goo_canvas_item_simple_get_line_width (simple);
 
472
  ensure_arrow_data (polyline_data);
 
473
 
 
474
  if (polyline_data->start_arrow)
 
475
    reconfigure_arrow (polyline_data, 0, 2, line_width,
 
476
                       polyline_data->arrow_data->line_start,
 
477
                       polyline_data->arrow_data->start_arrow_coords);
 
478
 
 
479
  if (polyline_data->end_arrow)
 
480
    {
 
481
      int end_point, prev_point;
 
482
 
 
483
      if (polyline_data->close_path)
 
484
        {
 
485
          end_point = 0;
 
486
          prev_point = polyline_data->num_points - 1;
 
487
        }
 
488
      else
 
489
        {
 
490
          end_point = polyline_data->num_points - 1;
 
491
          prev_point = polyline_data->num_points - 2;
 
492
        }
 
493
 
 
494
      reconfigure_arrow (polyline_data, end_point * 2, prev_point * 2,
 
495
                         line_width, polyline_data->arrow_data->line_end,
 
496
                         polyline_data->arrow_data->end_arrow_coords);
 
497
    }
 
498
}
 
499
 
 
500
 
 
501
static void
 
502
goo_canvas_polyline_set_common_property (GObject              *object,
 
503
                                         GooCanvasPolylineData *polyline_data,
 
504
                                         guint                 prop_id,
 
505
                                         const GValue         *value,
 
506
                                         GParamSpec           *pspec)
 
507
{
 
508
  GooCanvasPoints *points;
 
509
  GooCanvasBounds  extent;
 
510
  gdouble x_offset, y_offset, x_scale, y_scale;
 
511
  guint i;
 
512
 
 
513
  switch (prop_id)
 
514
    {
 
515
    case PROP_POINTS:
 
516
      points = g_value_get_boxed (value);
 
517
 
 
518
      if (polyline_data->coords)
 
519
        {
 
520
          g_slice_free1 (polyline_data->num_points * 2 * sizeof (double), polyline_data->coords);
 
521
          polyline_data->coords = NULL;
 
522
        }
 
523
 
 
524
      if (!points)
 
525
        {
 
526
          polyline_data->num_points = 0;
 
527
        }
 
528
      else
 
529
        {
 
530
          polyline_data->num_points = points->num_points;
 
531
          polyline_data->coords = g_slice_alloc (polyline_data->num_points * 2 * sizeof (double));
 
532
          memcpy (polyline_data->coords, points->coords,
 
533
                  polyline_data->num_points * 2 * sizeof (double));
 
534
        }
 
535
      polyline_data->reconfigure_arrows = TRUE;
 
536
      g_object_notify (object, "x");
 
537
      g_object_notify (object, "y");
 
538
      g_object_notify (object, "width");
 
539
      g_object_notify (object, "height");
 
540
      break;
 
541
    case PROP_CLOSE_PATH:
 
542
      polyline_data->close_path = g_value_get_boolean (value);
 
543
      polyline_data->reconfigure_arrows = TRUE;
 
544
      break;
 
545
    case PROP_START_ARROW:
 
546
      polyline_data->start_arrow = g_value_get_boolean (value);
 
547
      polyline_data->reconfigure_arrows = TRUE;
 
548
      break;
 
549
    case PROP_END_ARROW:
 
550
      polyline_data->end_arrow = g_value_get_boolean (value);
 
551
      polyline_data->reconfigure_arrows = TRUE;
 
552
      break;
 
553
    case PROP_ARROW_LENGTH:
 
554
      ensure_arrow_data (polyline_data);
 
555
      polyline_data->arrow_data->arrow_length = g_value_get_double (value);
 
556
      polyline_data->reconfigure_arrows = TRUE;
 
557
      break;
 
558
    case PROP_ARROW_WIDTH:
 
559
      ensure_arrow_data (polyline_data);
 
560
      polyline_data->arrow_data->arrow_width = g_value_get_double (value);
 
561
      polyline_data->reconfigure_arrows = TRUE;
 
562
      break;
 
563
    case PROP_ARROW_TIP_LENGTH:
 
564
      ensure_arrow_data (polyline_data);
 
565
      polyline_data->arrow_data->arrow_tip_length = g_value_get_double (value);
 
566
      polyline_data->reconfigure_arrows = TRUE;
 
567
      break;
 
568
    case PROP_X:
 
569
      if (polyline_data->num_points > 0)
 
570
        {
 
571
          /* Calculate the x offset from the current position. */
 
572
          goo_canvas_polyline_get_extent (polyline_data, &extent);
 
573
          x_offset = g_value_get_double (value) - extent.x1;
 
574
 
 
575
          /* Add the offset to all the x coordinates. */
 
576
          for (i = 0; i < polyline_data->num_points; i++)
 
577
            polyline_data->coords[2 * i] += x_offset;
 
578
 
 
579
          g_object_notify (object, "points");
 
580
        }
 
581
      break;
 
582
    case PROP_Y:
 
583
      if (polyline_data->num_points > 0)
 
584
        {
 
585
          /* Calculate the y offset from the current position. */
 
586
          goo_canvas_polyline_get_extent (polyline_data, &extent);
 
587
          y_offset = g_value_get_double (value) - extent.y1;
 
588
 
 
589
          /* Add the offset to all the y coordinates. */
 
590
          for (i = 0; i < polyline_data->num_points; i++)
 
591
            polyline_data->coords[2 * i + 1] += y_offset;
 
592
 
 
593
          g_object_notify (object, "points");
 
594
        }
 
595
      break;
 
596
    case PROP_WIDTH:
 
597
      if (polyline_data->num_points >= 2)
 
598
        {
 
599
          goo_canvas_polyline_get_extent (polyline_data, &extent);
 
600
          if (extent.x2 - extent.x1 != 0.0)
 
601
            {
 
602
              /* Calculate the amount to scale the polyline. */
 
603
              x_scale = g_value_get_double (value) / (extent.x2 - extent.x1);
 
604
 
 
605
              /* Scale the x coordinates, relative to the left-most point. */
 
606
              for (i = 0; i < polyline_data->num_points; i++)
 
607
                polyline_data->coords[2 * i] = extent.x1 + (polyline_data->coords[2 * i] - extent.x1) * x_scale;
 
608
 
 
609
              g_object_notify (object, "points");
 
610
            }
 
611
        }
 
612
      break;
 
613
    case PROP_HEIGHT:
 
614
      if (polyline_data->num_points >= 2)
 
615
        {
 
616
          goo_canvas_polyline_get_extent (polyline_data, &extent);
 
617
          if (extent.y2 - extent.y1 != 0.0)
 
618
            {
 
619
              /* Calculate the amount to scale the polyline. */
 
620
              y_scale = g_value_get_double (value) / (extent.y2 - extent.y1);
 
621
 
 
622
              /* Scale the y coordinates, relative to the top-most point. */
 
623
              for (i = 0; i < polyline_data->num_points; i++)
 
624
                polyline_data->coords[2 * i + 1] = extent.y1 + (polyline_data->coords[2 * i + 1] - extent.y1) * y_scale;
 
625
 
 
626
              g_object_notify (object, "points");
 
627
            }
 
628
        }
 
629
      break;
 
630
    default:
 
631
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 
632
      break;
 
633
  }
 
634
}
 
635
 
 
636
 
 
637
static void
 
638
goo_canvas_polyline_set_property (GObject              *object,
 
639
                                 guint                 prop_id,
 
640
                                 const GValue         *value,
 
641
                                 GParamSpec           *pspec)
 
642
{
 
643
  GooCanvasItemSimple *simple = (GooCanvasItemSimple*) object;
 
644
  GooCanvasPolyline *polyline = (GooCanvasPolyline*) object;
 
645
 
 
646
  if (simple->model)
 
647
    {
 
648
      g_warning ("Can't set property of a canvas item with a model - set the model property instead");
 
649
      return;
 
650
    }
 
651
 
 
652
  goo_canvas_polyline_set_common_property (object, polyline->polyline_data,
 
653
                                           prop_id, value, pspec);
 
654
  goo_canvas_item_simple_changed (simple, TRUE);
 
655
}
 
656
 
 
657
 
 
658
/**
 
659
 * goo_canvas_polyline_new:
 
660
 * @parent: the parent item, or %NULL. If a parent is specified, it will assume
 
661
 *  ownership of the item, and the item will automatically be freed when it is
 
662
 *  removed from the parent. Otherwise call g_object_unref() to free it.
 
663
 * @close_path: if the last point should be connected to the first.
 
664
 * @num_points: the number of points in the polyline.
 
665
 * @...: the pairs of coordinates for each point in the line, followed by
 
666
 *  optional pairs of property names and values, and a terminating %NULL.
 
667
 * 
 
668
 * Creates a new polyline item.
 
669
 * 
 
670
 * <!--PARAMETERS-->
 
671
 *
 
672
 * Here's an example showing how to create a filled triangle with vertices
 
673
 * at (100,100), (300,100), and (200,300).
 
674
 *
 
675
 * <informalexample><programlisting>
 
676
 *  GooCanvasItem *polyline = goo_canvas_polyline_new (mygroup, TRUE, 3,
 
677
 *                                                     100.0, 100.0,
 
678
 *                                                     300.0, 100.0,
 
679
 *                                                     200.0, 300.0,
 
680
 *                                                     "stroke-color", "red",
 
681
 *                                                     "line-width", 5.0,
 
682
 *                                                     "fill-color", "blue",
 
683
 *                                                     NULL);
 
684
 * </programlisting></informalexample>
 
685
 * 
 
686
 * Returns: a new polyline item.
 
687
 **/
 
688
GooCanvasItem*
 
689
goo_canvas_polyline_new               (GooCanvasItem *parent,
 
690
                                       gboolean       close_path,
 
691
                                       gint           num_points,
 
692
                                       ...)
 
693
{
 
694
  GooCanvasItem *item;
 
695
  GooCanvasPolyline *polyline;
 
696
  GooCanvasPolylineData *polyline_data;
 
697
  const char *first_property;
 
698
  va_list var_args;
 
699
  gint i;
 
700
 
 
701
  item = g_object_new (GOO_TYPE_CANVAS_POLYLINE, NULL);
 
702
  polyline = (GooCanvasPolyline*) item;
 
703
 
 
704
  polyline_data = polyline->polyline_data;
 
705
  polyline_data->close_path = close_path;
 
706
  polyline_data->num_points = num_points;
 
707
  if (num_points)
 
708
    polyline_data->coords = g_slice_alloc (num_points * 2 * sizeof (gdouble));
 
709
 
 
710
  va_start (var_args, num_points);
 
711
  for (i = 0; i < num_points * 2; i++)
 
712
    polyline_data->coords[i] = va_arg (var_args, gdouble);
 
713
 
 
714
  first_property = va_arg (var_args, char*);
 
715
  if (first_property)
 
716
    g_object_set_valist ((GObject*) item, first_property, var_args);
 
717
  va_end (var_args);
 
718
 
 
719
  if (parent)
 
720
    {
 
721
      goo_canvas_item_add_child (parent, item, -1);
 
722
      g_object_unref (item);
 
723
    }
 
724
 
 
725
  return item;
 
726
}
 
727
 
 
728
 
 
729
/**
 
730
 * goo_canvas_polyline_new_line:
 
731
 * @parent: the parent item, or %NULL.
 
732
 * @x1: the x coordinate of the start of the line.
 
733
 * @y1: the y coordinate of the start of the line.
 
734
 * @x2: the x coordinate of the end of the line.
 
735
 * @y2: the y coordinate of the end of the line.
 
736
 * @...: optional pairs of property names and values, and a terminating %NULL.
 
737
 * 
 
738
 * Creates a new polyline item with a single line.
 
739
 * 
 
740
 * <!--PARAMETERS-->
 
741
 *
 
742
 * Here's an example showing how to create a line from (100,100) to (300,300).
 
743
 *
 
744
 * <informalexample><programlisting>
 
745
 *  GooCanvasItem *polyline = goo_canvas_polyline_new_line (mygroup,
 
746
 *                                                          100.0, 100.0,
 
747
 *                                                          300.0, 300.0,
 
748
 *                                                          "stroke-color", "red",
 
749
 *                                                          "line-width", 5.0,
 
750
 *                                                          NULL);
 
751
 * </programlisting></informalexample>
 
752
 * 
 
753
 * Returns: a new polyline item.
 
754
 **/
 
755
GooCanvasItem*
 
756
goo_canvas_polyline_new_line          (GooCanvasItem *parent,
 
757
                                       gdouble        x1,
 
758
                                       gdouble        y1,
 
759
                                       gdouble        x2,
 
760
                                       gdouble        y2,
 
761
                                       ...)
 
762
{
 
763
  GooCanvasItem *item;
 
764
  GooCanvasPolyline *polyline;
 
765
  GooCanvasPolylineData *polyline_data;
 
766
  const char *first_property;
 
767
  va_list var_args;
 
768
 
 
769
  item = g_object_new (GOO_TYPE_CANVAS_POLYLINE, NULL);
 
770
  polyline = (GooCanvasPolyline*) item;
 
771
 
 
772
  polyline_data = polyline->polyline_data;
 
773
  polyline_data->close_path = FALSE;
 
774
  polyline_data->num_points = 2;
 
775
  polyline_data->coords = g_slice_alloc (4 * sizeof (gdouble));
 
776
  polyline_data->coords[0] = x1;
 
777
  polyline_data->coords[1] = y1;
 
778
  polyline_data->coords[2] = x2;
 
779
  polyline_data->coords[3] = y2;
 
780
 
 
781
  va_start (var_args, y2);
 
782
  first_property = va_arg (var_args, char*);
 
783
  if (first_property)
 
784
    g_object_set_valist ((GObject*) item, first_property, var_args);
 
785
  va_end (var_args);
 
786
 
 
787
  if (parent)
 
788
    {
 
789
      goo_canvas_item_add_child (parent, item, -1);
 
790
      g_object_unref (item);
 
791
    }
 
792
 
 
793
  return item;
 
794
}
 
795
 
 
796
 
 
797
static void
 
798
goo_canvas_polyline_create_path (GooCanvasPolyline *polyline,
 
799
                                 cairo_t           *cr)
 
800
{
 
801
  GooCanvasPolylineData *polyline_data = polyline->polyline_data;
 
802
  GooCanvasPolylineArrowData *arrow = polyline_data->arrow_data;
 
803
  gint i;
 
804
 
 
805
  cairo_new_path (cr);
 
806
 
 
807
  if (polyline_data->num_points == 0)
 
808
    return;
 
809
 
 
810
  /* If there is an arrow at the start of the polyline, we need to move the
 
811
     start of the line slightly to avoid drawing over the arrow tip. */
 
812
  if (polyline_data->start_arrow && polyline_data->num_points >= 2)
 
813
    cairo_move_to (cr, arrow->line_start[0], arrow->line_start[1]);
 
814
  else
 
815
    cairo_move_to (cr, polyline_data->coords[0], polyline_data->coords[1]);
 
816
 
 
817
  if (polyline_data->end_arrow && polyline_data->num_points >= 2)
 
818
    {
 
819
      gint last_point = polyline_data->num_points - 1;
 
820
 
 
821
      if (!polyline_data->close_path)
 
822
        last_point--;
 
823
 
 
824
      for (i = 1; i <= last_point; i++)
 
825
        cairo_line_to (cr, polyline_data->coords[i * 2],
 
826
                       polyline_data->coords[i * 2 + 1]);
 
827
 
 
828
      cairo_line_to (cr, arrow->line_end[0], arrow->line_end[1]);
 
829
    }
 
830
  else
 
831
    {
 
832
      for (i = 1; i < polyline_data->num_points; i++)
 
833
        cairo_line_to (cr, polyline_data->coords[i * 2],
 
834
                       polyline_data->coords[i * 2 + 1]);
 
835
 
 
836
      if (polyline_data->close_path)
 
837
        cairo_close_path (cr);
 
838
    }
 
839
}
 
840
 
 
841
 
 
842
static void
 
843
goo_canvas_polyline_create_start_arrow_path (GooCanvasPolyline *polyline,
 
844
                                             cairo_t           *cr)
 
845
{
 
846
  GooCanvasPolylineData *polyline_data = polyline->polyline_data;
 
847
  GooCanvasPolylineArrowData *arrow = polyline_data->arrow_data;
 
848
  gint i;
 
849
 
 
850
  cairo_new_path (cr);
 
851
 
 
852
  if (polyline_data->num_points < 2)
 
853
    return;
 
854
 
 
855
  cairo_move_to (cr, arrow->start_arrow_coords[0],
 
856
                 arrow->start_arrow_coords[1]);
 
857
  for (i = 1; i < NUM_ARROW_POINTS; i++)
 
858
    {
 
859
      cairo_line_to (cr, arrow->start_arrow_coords[i * 2],
 
860
                     arrow->start_arrow_coords[i * 2 + 1]);
 
861
    }
 
862
  cairo_close_path (cr);
 
863
}
 
864
 
 
865
 
 
866
static void
 
867
goo_canvas_polyline_create_end_arrow_path (GooCanvasPolyline *polyline,
 
868
                                           cairo_t           *cr)
 
869
{
 
870
  GooCanvasPolylineData *polyline_data = polyline->polyline_data;
 
871
  GooCanvasPolylineArrowData *arrow = polyline_data->arrow_data;
 
872
  gint i;
 
873
 
 
874
  cairo_new_path (cr);
 
875
 
 
876
  if (polyline_data->num_points < 2)
 
877
    return;
 
878
 
 
879
  cairo_move_to (cr, arrow->end_arrow_coords[0],
 
880
                 arrow->end_arrow_coords[1]);
 
881
  for (i = 1; i < NUM_ARROW_POINTS; i++)
 
882
    {
 
883
      cairo_line_to (cr, arrow->end_arrow_coords[i * 2],
 
884
                     arrow->end_arrow_coords[i * 2 + 1]);
 
885
    }
 
886
  cairo_close_path (cr);
 
887
}
 
888
 
 
889
 
 
890
static gboolean
 
891
goo_canvas_polyline_is_item_at (GooCanvasItemSimple *simple,
 
892
                                gdouble              x,
 
893
                                gdouble              y,
 
894
                                cairo_t             *cr,
 
895
                                gboolean             is_pointer_event)
 
896
{
 
897
  GooCanvasItemSimpleData *simple_data = simple->simple_data;
 
898
  GooCanvasPolyline *polyline = (GooCanvasPolyline*) simple;
 
899
  GooCanvasPolylineData *polyline_data = polyline->polyline_data;
 
900
  GooCanvasPointerEvents pointer_events = GOO_CANVAS_EVENTS_ALL;
 
901
  gboolean do_stroke;
 
902
 
 
903
  if (polyline_data->num_points == 0)
 
904
    return FALSE;
 
905
 
 
906
  /* Check if the item should receive events. */
 
907
  if (is_pointer_event)
 
908
    pointer_events = simple_data->pointer_events;
 
909
 
 
910
  /* If the path isn't closed, we never check the fill. */
 
911
  if (!(polyline_data->close_path && polyline_data->num_points > 2))
 
912
    pointer_events &= ~GOO_CANVAS_EVENTS_FILL_MASK;
 
913
 
 
914
  goo_canvas_polyline_create_path (polyline, cr);
 
915
  if (goo_canvas_item_simple_check_in_path (simple, x, y, cr, pointer_events))
 
916
    return TRUE;
 
917
 
 
918
  /* Check the arrows, if the polyline has them. */
 
919
  if ((polyline_data->start_arrow || polyline_data->end_arrow)
 
920
      && polyline_data->num_points >= 2
 
921
      && (pointer_events & GOO_CANVAS_EVENTS_STROKE_MASK))
 
922
    {
 
923
      /* We use the stroke pattern to match the style of the line. */
 
924
      do_stroke = goo_canvas_style_set_stroke_options (simple_data->style, cr);
 
925
      if (!(pointer_events & GOO_CANVAS_EVENTS_PAINTED_MASK) || do_stroke)
 
926
        {
 
927
          if (polyline_data->start_arrow)
 
928
            {
 
929
              goo_canvas_polyline_create_start_arrow_path (polyline, cr);
 
930
              if (cairo_in_fill (cr, x, y))
 
931
                return TRUE;
 
932
            }
 
933
 
 
934
          if (polyline_data->end_arrow)
 
935
            {
 
936
              goo_canvas_polyline_create_end_arrow_path (polyline, cr);
 
937
              if (cairo_in_fill (cr, x, y))
 
938
                return TRUE;
 
939
            }
 
940
        }
 
941
    }
 
942
 
 
943
  return FALSE;
 
944
}
 
945
 
 
946
 
 
947
static void
 
948
goo_canvas_polyline_compute_bounds (GooCanvasPolyline     *polyline,
 
949
                                    cairo_t               *cr,
 
950
                                    GooCanvasBounds       *bounds)
 
951
{
 
952
  GooCanvasItemSimple *simple = (GooCanvasItemSimple*) polyline;
 
953
  GooCanvasItemSimpleData *simple_data = simple->simple_data;
 
954
  GooCanvasPolylineData *polyline_data = polyline->polyline_data;
 
955
  GooCanvasBounds tmp_bounds;
 
956
  cairo_matrix_t transform;
 
957
 
 
958
  if (polyline_data->num_points == 0)
 
959
    {
 
960
      bounds->x1 = bounds->x2 = bounds->y1 = bounds->y2 = 0.0;
 
961
      return;
 
962
    }
 
963
 
 
964
  /* Use the identity matrix to get the bounds completely in user space. */
 
965
  cairo_get_matrix (cr, &transform);
 
966
  cairo_identity_matrix (cr);
 
967
 
 
968
  goo_canvas_polyline_create_path (polyline, cr);
 
969
  goo_canvas_item_simple_get_path_bounds (simple, cr, bounds);
 
970
 
 
971
  /* Add on the arrows, if required. */
 
972
  if ((polyline_data->start_arrow || polyline_data->end_arrow)
 
973
      && polyline_data->num_points >= 2)
 
974
    {
 
975
      /* We use the stroke pattern to match the style of the line. */
 
976
      goo_canvas_style_set_stroke_options (simple_data->style, cr);
 
977
 
 
978
      if (polyline_data->start_arrow)
 
979
        {
 
980
          goo_canvas_polyline_create_start_arrow_path (polyline, cr);
 
981
          cairo_fill_extents (cr, &tmp_bounds.x1, &tmp_bounds.y1,
 
982
                              &tmp_bounds.x2, &tmp_bounds.y2);
 
983
          bounds->x1 = MIN (bounds->x1, tmp_bounds.x1);
 
984
          bounds->y1 = MIN (bounds->y1, tmp_bounds.y1);
 
985
          bounds->x2 = MAX (bounds->x2, tmp_bounds.x2);
 
986
          bounds->y2 = MAX (bounds->y2, tmp_bounds.y2);
 
987
        }
 
988
 
 
989
      if (polyline_data->end_arrow)
 
990
        {
 
991
          goo_canvas_polyline_create_end_arrow_path (polyline, cr);
 
992
          cairo_fill_extents (cr, &tmp_bounds.x1, &tmp_bounds.y1,
 
993
                              &tmp_bounds.x2, &tmp_bounds.y2);
 
994
          bounds->x1 = MIN (bounds->x1, tmp_bounds.x1);
 
995
          bounds->y1 = MIN (bounds->y1, tmp_bounds.y1);
 
996
          bounds->x2 = MAX (bounds->x2, tmp_bounds.x2);
 
997
          bounds->y2 = MAX (bounds->y2, tmp_bounds.y2);
 
998
        }
 
999
    }
 
1000
 
 
1001
  cairo_set_matrix (cr, &transform);
 
1002
}
 
1003
 
 
1004
 
 
1005
static void
 
1006
goo_canvas_polyline_update  (GooCanvasItemSimple *simple,
 
1007
                             cairo_t             *cr)
 
1008
{
 
1009
  GooCanvasPolyline *polyline = (GooCanvasPolyline*) simple;
 
1010
  GooCanvasPolylineData *polyline_data = polyline->polyline_data;
 
1011
 
 
1012
  if (polyline_data->reconfigure_arrows)
 
1013
    goo_canvas_polyline_reconfigure_arrows (polyline);
 
1014
 
 
1015
  /* Compute the new bounds. */
 
1016
  goo_canvas_polyline_compute_bounds (polyline, cr, &simple->bounds);
 
1017
}
 
1018
 
 
1019
 
 
1020
static void
 
1021
goo_canvas_polyline_paint (GooCanvasItemSimple   *simple,
 
1022
                           cairo_t               *cr,
 
1023
                           const GooCanvasBounds *bounds)
 
1024
{
 
1025
  GooCanvasItemSimpleData *simple_data = simple->simple_data;
 
1026
  GooCanvasPolyline *polyline = (GooCanvasPolyline*) simple;
 
1027
  GooCanvasPolylineData *polyline_data = polyline->polyline_data;
 
1028
 
 
1029
  if (polyline_data->num_points == 0)
 
1030
    return;
 
1031
 
 
1032
  goo_canvas_polyline_create_path (polyline, cr);
 
1033
  goo_canvas_item_simple_paint_path (simple, cr);
 
1034
 
 
1035
  /* Paint the arrows, if required. */
 
1036
  if ((polyline_data->start_arrow || polyline_data->end_arrow)
 
1037
      && polyline_data->num_points >= 2)
 
1038
    {
 
1039
      /* We use the stroke pattern to match the style of the line. */
 
1040
      goo_canvas_style_set_stroke_options (simple_data->style, cr);
 
1041
 
 
1042
      if (polyline_data->start_arrow)
 
1043
        {
 
1044
          goo_canvas_polyline_create_start_arrow_path (polyline, cr);
 
1045
          cairo_fill (cr);
 
1046
        }
 
1047
 
 
1048
      if (polyline_data->end_arrow)
 
1049
        {
 
1050
          goo_canvas_polyline_create_end_arrow_path (polyline, cr);
 
1051
          cairo_fill (cr);
 
1052
        }
 
1053
    }
 
1054
}
 
1055
 
 
1056
 
 
1057
static void
 
1058
goo_canvas_polyline_set_model    (GooCanvasItem      *item,
 
1059
                                 GooCanvasItemModel *model)
 
1060
{
 
1061
  GooCanvasItemSimple *simple = (GooCanvasItemSimple*) item;
 
1062
  GooCanvasPolyline *polyline = (GooCanvasPolyline*) item;
 
1063
  GooCanvasPolylineModel *pmodel = (GooCanvasPolylineModel*) model;
 
1064
 
 
1065
  /* If our polyline_data was allocated, free it. */
 
1066
  if (!simple->model)
 
1067
    g_slice_free (GooCanvasPolylineData, polyline->polyline_data);
 
1068
 
 
1069
  /* Now use the new model's polyline_data instead. */
 
1070
  polyline->polyline_data = &pmodel->polyline_data;
 
1071
 
 
1072
  /* Let the parent GooCanvasItemSimple code do the rest. */
 
1073
  goo_canvas_item_simple_set_model (simple, model);
 
1074
}
 
1075
 
 
1076
 
 
1077
static void
 
1078
canvas_item_interface_init (GooCanvasItemIface *iface)
 
1079
{
 
1080
  iface->set_model   = goo_canvas_polyline_set_model;
 
1081
}
 
1082
 
 
1083
 
 
1084
static void
 
1085
goo_canvas_polyline_class_init (GooCanvasPolylineClass *klass)
 
1086
{
 
1087
  GObjectClass *gobject_class = (GObjectClass*) klass;
 
1088
  GooCanvasItemSimpleClass *simple_class = (GooCanvasItemSimpleClass*) klass;
 
1089
 
 
1090
  gobject_class->finalize  = goo_canvas_polyline_finalize;
 
1091
 
 
1092
  gobject_class->get_property = goo_canvas_polyline_get_property;
 
1093
  gobject_class->set_property = goo_canvas_polyline_set_property;
 
1094
 
 
1095
  simple_class->simple_update        = goo_canvas_polyline_update;
 
1096
  simple_class->simple_paint         = goo_canvas_polyline_paint;
 
1097
  simple_class->simple_is_item_at    = goo_canvas_polyline_is_item_at;
 
1098
 
 
1099
  goo_canvas_polyline_install_common_properties (gobject_class);
 
1100
}
 
1101
 
 
1102
 
 
1103
/**
 
1104
 * SECTION:goocanvaspolylinemodel
 
1105
 * @Title: GooCanvasPolylineModel
 
1106
 * @Short_Description: a model for polyline items (a series of lines with
 
1107
 *  optional arrows).
 
1108
 *
 
1109
 * GooCanvasPolylineModel represents a model for polyline items, which are a
 
1110
 * series of one or more lines, with optional arrows at either end.
 
1111
 *
 
1112
 * It is a subclass of #GooCanvasItemModelSimple and so inherits all of the
 
1113
 * style properties such as "stroke-color", "fill-color" and "line-width".
 
1114
 *
 
1115
 * It also implements the #GooCanvasItemModel interface, so you can use the
 
1116
 * #GooCanvasItemModel functions such as goo_canvas_item_model_raise() and
 
1117
 * goo_canvas_item_model_rotate().
 
1118
 *
 
1119
 * To create a #GooCanvasPolylineModel use goo_canvas_polyline_model_new(), or
 
1120
 * goo_canvas_polyline_model_new_line() for a simple line between two points.
 
1121
 *
 
1122
 * To get or set the properties of an existing #GooCanvasPolylineModel, use
 
1123
 * g_object_get() and g_object_set().
 
1124
 *
 
1125
 * To respond to events such as mouse clicks on the polyline you must connect
 
1126
 * to the signal handlers of the corresponding #GooCanvasPolyline objects.
 
1127
 * (See goo_canvas_get_item() and #GooCanvas::item-created.)
 
1128
 */
 
1129
 
 
1130
static void item_model_interface_init (GooCanvasItemModelIface *iface);
 
1131
static void goo_canvas_polyline_model_finalize     (GObject          *object);
 
1132
static void goo_canvas_polyline_model_get_property (GObject          *object,
 
1133
                                                    guint             param_id,
 
1134
                                                    GValue           *value,
 
1135
                                                    GParamSpec       *pspec);
 
1136
static void goo_canvas_polyline_model_set_property (GObject          *object,
 
1137
                                                    guint             param_id,
 
1138
                                                    const GValue     *value,
 
1139
                                                    GParamSpec       *pspec);
 
1140
 
 
1141
G_DEFINE_TYPE_WITH_CODE (GooCanvasPolylineModel, goo_canvas_polyline_model,
 
1142
                         GOO_TYPE_CANVAS_ITEM_MODEL_SIMPLE,
 
1143
                         G_IMPLEMENT_INTERFACE (GOO_TYPE_CANVAS_ITEM_MODEL,
 
1144
                                                item_model_interface_init))
 
1145
 
 
1146
 
 
1147
static void
 
1148
goo_canvas_polyline_model_class_init (GooCanvasPolylineModelClass *klass)
 
1149
{
 
1150
  GObjectClass *gobject_class = (GObjectClass*) klass;
 
1151
 
 
1152
  gobject_class->finalize     = goo_canvas_polyline_model_finalize;
 
1153
 
 
1154
  gobject_class->get_property = goo_canvas_polyline_model_get_property;
 
1155
  gobject_class->set_property = goo_canvas_polyline_model_set_property;
 
1156
 
 
1157
  goo_canvas_polyline_install_common_properties (gobject_class);
 
1158
}
 
1159
 
 
1160
 
 
1161
static void
 
1162
goo_canvas_polyline_model_init (GooCanvasPolylineModel *pmodel)
 
1163
{
 
1164
 
 
1165
}
 
1166
 
 
1167
 
 
1168
/**
 
1169
 * goo_canvas_polyline_model_new:
 
1170
 * @parent: the parent model, or %NULL. If a parent is specified, it will
 
1171
 *  assume ownership of the item, and the item will automatically be freed when
 
1172
 *  it is removed from the parent. Otherwise call g_object_unref() to free it.
 
1173
 * @close_path: if the last point should be connected to the first.
 
1174
 * @num_points: the number of points in the polyline.
 
1175
 * @...: the pairs of coordinates for each point in the line, followed by
 
1176
 *  optional pairs of property names and values, and a terminating %NULL.
 
1177
 * 
 
1178
 * Creates a new polyline model.
 
1179
 * 
 
1180
 * <!--PARAMETERS-->
 
1181
 *
 
1182
 * Here's an example showing how to create a filled triangle with vertices
 
1183
 * at (100,100), (300,100), and (200,300).
 
1184
 *
 
1185
 * <informalexample><programlisting>
 
1186
 *  GooCanvasItemModel *polyline = goo_canvas_polyline_model_new (mygroup, TRUE, 3,
 
1187
 *                                                                100.0, 100.0,
 
1188
 *                                                                300.0, 100.0,
 
1189
 *                                                                200.0, 300.0,
 
1190
 *                                                                "stroke-color", "red",
 
1191
 *                                                                "line-width", 5.0,
 
1192
 *                                                                "fill-color", "blue",
 
1193
 *                                                                NULL);
 
1194
 * </programlisting></informalexample>
 
1195
 * 
 
1196
 * Returns: a new polyline model.
 
1197
 **/
 
1198
GooCanvasItemModel*
 
1199
goo_canvas_polyline_model_new (GooCanvasItemModel *parent,
 
1200
                               gboolean            close_path,
 
1201
                               gint                num_points,
 
1202
                               ...)
 
1203
{
 
1204
  GooCanvasItemModel *model;
 
1205
  GooCanvasPolylineModel *pmodel;
 
1206
  GooCanvasPolylineData *polyline_data;
 
1207
  const char *first_property;
 
1208
  va_list var_args;
 
1209
  gint i;
 
1210
 
 
1211
  model = g_object_new (GOO_TYPE_CANVAS_POLYLINE_MODEL, NULL);
 
1212
  pmodel = (GooCanvasPolylineModel*) model;
 
1213
 
 
1214
  polyline_data = &pmodel->polyline_data;
 
1215
  polyline_data->close_path = close_path;
 
1216
  polyline_data->num_points = num_points;
 
1217
  if (num_points)
 
1218
    polyline_data->coords = g_slice_alloc (num_points * 2 * sizeof (gdouble));
 
1219
 
 
1220
  va_start (var_args, num_points);
 
1221
  for (i = 0; i < num_points * 2; i++)
 
1222
    polyline_data->coords[i] = va_arg (var_args, gdouble);
 
1223
 
 
1224
  first_property = va_arg (var_args, char*);
 
1225
  if (first_property)
 
1226
    g_object_set_valist ((GObject*) model, first_property, var_args);
 
1227
  va_end (var_args);
 
1228
 
 
1229
  if (parent)
 
1230
    {
 
1231
      goo_canvas_item_model_add_child (parent, model, -1);
 
1232
      g_object_unref (model);
 
1233
    }
 
1234
 
 
1235
  return model;
 
1236
}
 
1237
 
 
1238
 
 
1239
/**
 
1240
 * goo_canvas_polyline_model_new_line:
 
1241
 * @parent: the parent model, or %NULL.
 
1242
 * @x1: the x coordinate of the start of the line.
 
1243
 * @y1: the y coordinate of the start of the line.
 
1244
 * @x2: the x coordinate of the end of the line.
 
1245
 * @y2: the y coordinate of the end of the line.
 
1246
 * @...: optional pairs of property names and values, and a terminating %NULL.
 
1247
 * 
 
1248
 * Creates a new polyline model with a single line.
 
1249
 * 
 
1250
 * <!--PARAMETERS-->
 
1251
 *
 
1252
 * Here's an example showing how to create a line from (100,100) to (300,300).
 
1253
 *
 
1254
 * <informalexample><programlisting>
 
1255
 *  GooCanvasItemModel *polyline = goo_canvas_polyline_model_new_line (mygroup,
 
1256
 *                                                                     100.0, 100.0,
 
1257
 *                                                                     300.0, 300.0,
 
1258
 *                                                                     "stroke-color", "red",
 
1259
 *                                                                     "line-width", 5.0,
 
1260
 *                                                                     NULL);
 
1261
 * </programlisting></informalexample>
 
1262
 * 
 
1263
 * Returns: a new polyline model.
 
1264
 **/
 
1265
GooCanvasItemModel*
 
1266
goo_canvas_polyline_model_new_line (GooCanvasItemModel *parent,
 
1267
                                    gdouble             x1,
 
1268
                                    gdouble             y1,
 
1269
                                    gdouble             x2,
 
1270
                                    gdouble             y2,
 
1271
                                    ...)
 
1272
{
 
1273
  GooCanvasItemModel *model;
 
1274
  GooCanvasPolylineModel *pmodel;
 
1275
  GooCanvasPolylineData *polyline_data;
 
1276
  const char *first_property;
 
1277
  va_list var_args;
 
1278
 
 
1279
  model = g_object_new (GOO_TYPE_CANVAS_POLYLINE_MODEL, NULL);
 
1280
  pmodel = (GooCanvasPolylineModel*) model;
 
1281
 
 
1282
  polyline_data = &pmodel->polyline_data;
 
1283
  polyline_data->close_path = FALSE;
 
1284
  polyline_data->num_points = 2;
 
1285
  polyline_data->coords = g_slice_alloc (4 * sizeof (gdouble));
 
1286
  polyline_data->coords[0] = x1;
 
1287
  polyline_data->coords[1] = y1;
 
1288
  polyline_data->coords[2] = x2;
 
1289
  polyline_data->coords[3] = y2;
 
1290
 
 
1291
  va_start (var_args, y2);
 
1292
  first_property = va_arg (var_args, char*);
 
1293
  if (first_property)
 
1294
    g_object_set_valist ((GObject*) model, first_property, var_args);
 
1295
  va_end (var_args);
 
1296
 
 
1297
  if (parent)
 
1298
    {
 
1299
      goo_canvas_item_model_add_child (parent, model, -1);
 
1300
      g_object_unref (model);
 
1301
    }
 
1302
 
 
1303
  return model;
 
1304
}
 
1305
 
 
1306
 
 
1307
static void
 
1308
goo_canvas_polyline_model_finalize (GObject *object)
 
1309
{
 
1310
  GooCanvasPolylineModel *pmodel = (GooCanvasPolylineModel*) object;
 
1311
 
 
1312
  g_slice_free1 (pmodel->polyline_data.num_points * 2 * sizeof (gdouble),
 
1313
                 pmodel->polyline_data.coords);
 
1314
  g_slice_free (GooCanvasPolylineArrowData, pmodel->polyline_data.arrow_data);
 
1315
 
 
1316
  G_OBJECT_CLASS (goo_canvas_polyline_model_parent_class)->finalize (object);
 
1317
}
 
1318
 
 
1319
 
 
1320
static void
 
1321
goo_canvas_polyline_model_get_property (GObject              *object,
 
1322
                                        guint                 prop_id,
 
1323
                                        GValue               *value,
 
1324
                                        GParamSpec           *pspec)
 
1325
{
 
1326
  GooCanvasPolylineModel *pmodel = (GooCanvasPolylineModel*) object;
 
1327
 
 
1328
  goo_canvas_polyline_get_common_property (object, &pmodel->polyline_data,
 
1329
                                           prop_id, value, pspec);
 
1330
}
 
1331
 
 
1332
 
 
1333
static void
 
1334
goo_canvas_polyline_model_set_property (GObject              *object,
 
1335
                                        guint                 prop_id,
 
1336
                                        const GValue         *value,
 
1337
                                        GParamSpec           *pspec)
 
1338
{
 
1339
  GooCanvasPolylineModel *pmodel = (GooCanvasPolylineModel*) object;
 
1340
 
 
1341
  goo_canvas_polyline_set_common_property (object, &pmodel->polyline_data,
 
1342
                                           prop_id, value, pspec);
 
1343
  g_signal_emit_by_name (pmodel, "changed", TRUE);
 
1344
}
 
1345
 
 
1346
 
 
1347
static GooCanvasItem*
 
1348
goo_canvas_polyline_model_create_item (GooCanvasItemModel *model,
 
1349
                                       GooCanvas          *canvas)
 
1350
{
 
1351
  GooCanvasItem *item;
 
1352
 
 
1353
  item = g_object_new (GOO_TYPE_CANVAS_POLYLINE, NULL);
 
1354
  goo_canvas_item_set_model (item, model);
 
1355
 
 
1356
  return item;
 
1357
}
 
1358
 
 
1359
 
 
1360
static void
 
1361
item_model_interface_init (GooCanvasItemModelIface *iface)
 
1362
{
 
1363
  iface->create_item    = goo_canvas_polyline_model_create_item;
 
1364
}