Redraw polygons when their properties change and points are changed
[libchamplain:potyl-perl.git] / champlain / champlain-polygon.c
1 /*
2  * Copyright (C) 2009 Pierre-Luc Beaudoin <pierre-luc@pierlux.com>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
17  */
18
19 /**
20  * SECTION:champlain-polygon
21  * @short_description: A polygon to be drawn on the map
22  *
23  * A ChamplainPolygon is a set of point forming a shape on the map.  This API is based on Cairo's.
24  */
25
26 #include "config.h"
27
28 #include "champlain-polygon.h"
29
30 #include "champlain-defines.h"
31 #include "champlain-private.h"
32
33 #include <clutter/clutter.h>
34 #include <clutter-cairo/clutter-cairo.h>
35 #include <glib.h>
36
37 static ClutterColor DEFAULT_FILL_COLOR = {0xcc, 0x00, 0x00, 0xaa};
38 static ClutterColor DEFAULT_STROKE_COLOR = {0xa4, 0x00, 0x00, 0xff};
39
40 G_DEFINE_TYPE (ChamplainPolygon, champlain_polygon, G_TYPE_OBJECT)
41
42 #define GET_PRIVATE(o) \
43   (G_TYPE_INSTANCE_GET_PRIVATE ((o), CHAMPLAIN_TYPE_POLYGON, ChamplainPolygonPrivate))
44
45 enum
46 {
47   PROP_0,
48   PROP_CLOSED_PATH,
49   PROP_STROKE_WIDTH,
50   PROP_STROKE_COLOR,
51   PROP_FILL,
52   PROP_FILL_COLOR,
53   PROP_STROKE,
54   PROP_VISIBLE,
55 };
56
57 static void
58 champlain_polygon_get_property (GObject *object,
59     guint property_id,
60     GValue *value,
61     GParamSpec *pspec)
62 {
63   ChamplainPolygonPrivate *priv = GET_PRIVATE (object);
64
65   switch (property_id)
66     {
67       case PROP_CLOSED_PATH:
68         g_value_set_boolean (value, priv->closed_path);
69         break;
70       case PROP_FILL:
71         g_value_set_boolean (value, priv->fill);
72         break;
73       case PROP_STROKE:
74         g_value_set_boolean (value, priv->stroke);
75         break;
76       case PROP_FILL_COLOR:
77         clutter_value_set_color (value, priv->fill_color);
78         break;
79       case PROP_STROKE_COLOR:
80         clutter_value_set_color (value, priv->stroke_color);
81         break;
82       case PROP_STROKE_WIDTH:
83         g_value_set_double (value, priv->stroke_width);
84         break;
85       case PROP_VISIBLE:
86         g_value_set_boolean (value, priv->visible);
87         break;
88       default:
89         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
90     }
91 }
92
93 static void
94 champlain_polygon_set_property (GObject *object,
95     guint property_id,
96     const GValue *value,
97     GParamSpec *pspec)
98 {
99   ChamplainPolygonPrivate *priv = GET_PRIVATE (object);
100
101   switch (property_id)
102     {
103       case PROP_CLOSED_PATH:
104         priv->closed_path = g_value_get_boolean (value);
105         break;
106       case PROP_FILL:
107         champlain_polygon_set_fill (CHAMPLAIN_POLYGON (object),
108             g_value_get_boolean (value));
109         break;
110       case PROP_STROKE:
111         champlain_polygon_set_stroke (CHAMPLAIN_POLYGON (object),
112             g_value_get_boolean (value));
113         break;
114       case PROP_FILL_COLOR:
115         champlain_polygon_set_fill_color (CHAMPLAIN_POLYGON (object),
116             clutter_value_get_color (value));
117         break;
118       case PROP_STROKE_COLOR:
119         champlain_polygon_set_stroke_color (CHAMPLAIN_POLYGON (object),
120             clutter_value_get_color (value));
121         break;
122       case PROP_STROKE_WIDTH:
123         champlain_polygon_set_stroke_width (CHAMPLAIN_POLYGON (object),
124             g_value_get_double (value));
125         break;
126       case PROP_VISIBLE:
127         if (g_value_get_boolean (value) == TRUE)
128             champlain_polygon_show (CHAMPLAIN_POLYGON (object));
129         else
130             champlain_polygon_hide (CHAMPLAIN_POLYGON (object));
131         break;
132       default:
133         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
134     }
135 }
136
137 static void
138 champlain_polygon_dispose (GObject *object)
139 {
140   ChamplainPolygonPrivate *priv = GET_PRIVATE (object);
141
142   if (priv->actor != NULL)
143     {
144       g_object_unref (priv->actor);
145       priv->actor = NULL;
146     }
147
148   G_OBJECT_CLASS (champlain_polygon_parent_class)->dispose (object);
149 }
150
151 static void
152 champlain_polygon_finalize (GObject *object)
153 {
154   G_OBJECT_CLASS (champlain_polygon_parent_class)->finalize (object);
155 }
156
157 static void
158 champlain_polygon_class_init (ChamplainPolygonClass *klass)
159 {
160   GObjectClass *object_class = G_OBJECT_CLASS (klass);
161
162   g_type_class_add_private (klass, sizeof (ChamplainPolygonPrivate));
163
164   object_class->get_property = champlain_polygon_get_property;
165   object_class->set_property = champlain_polygon_set_property;
166   object_class->dispose = champlain_polygon_dispose;
167   object_class->finalize = champlain_polygon_finalize;
168
169   /**
170   * ChamplainPolygon:close-path:
171   *
172   * The shape is a closed path
173   *
174   * Since: 0.4
175   */
176   g_object_class_install_property (object_class,
177       PROP_CLOSED_PATH,
178       g_param_spec_boolean ("closed-path",
179           "Closed Path",
180           "The Path is Closed",
181           FALSE, CHAMPLAIN_PARAM_READWRITE));
182
183   /**
184   * ChamplainPolygon:fill:
185   *
186   * The shape should be filled
187   *
188   * Since: 0.4
189   */
190   g_object_class_install_property (object_class,
191       PROP_FILL,
192       g_param_spec_boolean ("fill",
193           "Fill",
194           "The shape is filled",
195           FALSE, CHAMPLAIN_PARAM_READWRITE));
196
197   /**
198   * ChamplainPolygon:stroke:
199   *
200   * The shape should be stroked
201   *
202   * Since: 0.4
203   */
204   g_object_class_install_property (object_class,
205       PROP_STROKE,
206       g_param_spec_boolean ("stroke",
207           "Stroke",
208           "The shape is stroked",
209           TRUE, CHAMPLAIN_PARAM_READWRITE));
210
211   /**
212   * ChamplainPolygon:stroke-color:
213   *
214   * The polygon's stroke color
215   *
216   * Since: 0.4
217   */
218   g_object_class_install_property (object_class,
219       PROP_STROKE_COLOR,
220       clutter_param_spec_color ("stroke-color",
221         "Stroke Color",
222         "The polygon's stroke color",
223         &DEFAULT_STROKE_COLOR,
224         CHAMPLAIN_PARAM_READWRITE));
225
226   /**
227   * ChamplainPolygon:text-color:
228   *
229   * The polygon's fill color
230   *
231   * Since: 0.4
232   */
233   g_object_class_install_property (object_class,
234       PROP_FILL_COLOR,
235       clutter_param_spec_color ("fill-color",
236           "Fill Color",
237           "The polygon's fill color",
238           &DEFAULT_FILL_COLOR,
239           CHAMPLAIN_PARAM_READWRITE));
240
241   /**
242   * ChamplainPolygon:stroke-width:
243   *
244   * The polygon's stroke width (in pixels)
245   *
246   * Since: 0.4
247   */
248   g_object_class_install_property (object_class,
249       PROP_STROKE_WIDTH,
250       g_param_spec_double ("stroke-width",
251           "Stroke Width",
252           "The polygon's stroke width",
253           0, 100.0,
254           2.0,
255           CHAMPLAIN_PARAM_READWRITE));
256
257   /**
258   * ChamplainPolygon:visible:
259   *
260   * Wether the polygon is visible
261   *
262   * Since: 0.4
263   */
264   g_object_class_install_property (object_class,
265       PROP_VISIBLE,
266       g_param_spec_boolean ("visible",
267           "Visible",
268           "The polygon's visibility",
269           TRUE,
270           CHAMPLAIN_PARAM_READWRITE));
271 }
272
273 static void
274 champlain_polygon_init (ChamplainPolygon *self)
275 {
276   self->priv = GET_PRIVATE (self);
277
278   self->priv->visible = TRUE;
279   self->priv->points = NULL;
280   self->priv->fill = FALSE;
281   self->priv->stroke = TRUE;
282   self->priv->stroke_width = 2.0;
283
284   self->priv->fill_color = clutter_color_copy (&DEFAULT_FILL_COLOR);
285   self->priv->stroke_color = clutter_color_copy (&DEFAULT_STROKE_COLOR);
286 }
287
288 /**
289  * champlain_polygon_new:
290  *
291  * Returns a new empty #ChamplainPolygon
292  *
293  * Since: 0.4
294  */
295 ChamplainPolygon *
296 champlain_polygon_new ()
297 {
298   return g_object_new (CHAMPLAIN_TYPE_POLYGON, NULL);
299 }
300
301 /**
302  * champlain_polygon_append_point:
303  * @polygon: The polygon
304  * @lat: the latitude
305  * @lon: the longitude
306  *
307  * Adds point at the end of the list of points in the polygon
308  *
309  * Returns the added point, should not be freed.
310  *
311  * Since: 0.4
312  */
313 ChamplainPoint *
314 champlain_polygon_append_point (ChamplainPolygon *self,
315     gdouble lat,
316     gdouble lon)
317 {
318   g_return_val_if_fail (CHAMPLAIN_IS_POLYGON (self), NULL);
319
320   ChamplainPoint *point = champlain_point_new (lat, lon);
321
322   self->priv->points = g_list_append (self->priv->points, point);
323   g_object_notify (G_OBJECT (self), "visible");
324   return point;
325 }
326
327 /**
328  * champlain_polygon_insert_point:
329  * @polygon: The polygon
330  * @lat: the latitude
331  * @lon: the longitude
332  * @pos: where to insert the point
333  *
334  * Adds point at the given position in the list of points in the polygon
335  *
336  * Returns the added point, should not be freed.
337  *
338  * Since: 0.4
339  */
340 ChamplainPoint *
341 champlain_polygon_insert_point (ChamplainPolygon *self,
342     gdouble lat,
343     gdouble lon,
344     gint pos)
345 {
346   g_return_val_if_fail (CHAMPLAIN_IS_POLYGON (self), NULL);
347
348   ChamplainPoint *point = champlain_point_new (lat, lon);
349
350   self->priv->points = g_list_insert (self->priv->points, point, pos);
351   g_object_notify (G_OBJECT (self), "visible");
352   return point;
353 }
354
355 /**
356  * champlain_polygon_clear_points:
357  * @polygon: The polygon
358  *
359  * Remove all points from the polygon
360  *
361  * Since: 0.4
362  */
363 void
364 champlain_polygon_clear_points (ChamplainPolygon *self)
365 {
366   g_return_if_fail (CHAMPLAIN_IS_POLYGON (self));
367
368   GList *next = self->priv->points;
369   while (next != NULL)
370   {
371     champlain_point_free (next->data);
372     next = g_list_next (next);
373   }
374   g_list_free (self->priv->points);
375   g_object_notify (G_OBJECT (self), "visible");
376 }
377
378 /**
379  * champlain_polygon_get_points:
380  * @polygon: The polygon
381  *
382  * Returns a list of all points from the polygon, it shouldn't be freed.
383  *
384  * Since: 0.4
385  */
386 GList *
387 champlain_polygon_get_points (ChamplainPolygon *self)
388 {
389   g_return_val_if_fail (CHAMPLAIN_IS_POLYGON (self), NULL);
390
391   return self->priv->points;
392 }
393
394 /**
395  * champlain_polygon_set_fill_color:
396  * @polygon: The polygon
397  * @color: The polygon's fill color or NULL to reset to the
398  *         default color. The color parameter is copied.
399  *
400  * Set the polygon's fill color.
401  *
402  * Since: 0.4
403  */
404 void
405 champlain_polygon_set_fill_color (ChamplainPolygon *polygon,
406     const ClutterColor *color)
407 {
408   g_return_if_fail (CHAMPLAIN_IS_POLYGON (polygon));
409
410   ChamplainPolygonPrivate *priv = polygon->priv;
411
412   if (priv->fill_color != NULL)
413     clutter_color_free (priv->fill_color);
414
415   if (color == NULL)
416      color = &DEFAULT_FILL_COLOR;
417
418   priv->fill_color = clutter_color_copy (color);
419   g_object_notify (G_OBJECT (polygon), "fill-color");
420 }
421
422 /**
423  * champlain_polygon_set_stoke_color:
424  * @polygon: The polygon
425  * @color: The polygon's stroke color or NULL to reset to the
426  *         default color. The color parameter is copied.
427  *
428  * Set the polygon's stroke color.
429  *
430  * Since: 0.4
431  */
432 void
433 champlain_polygon_set_stroke_color (ChamplainPolygon *polygon,
434     const ClutterColor *color)
435 {
436   g_return_if_fail (CHAMPLAIN_IS_POLYGON (polygon));
437
438   ChamplainPolygonPrivate *priv = polygon->priv;
439
440   if (priv->stroke_color != NULL)
441     clutter_color_free (priv->stroke_color);
442
443   if (color == NULL)
444      color = &DEFAULT_STROKE_COLOR;
445
446   priv->stroke_color = clutter_color_copy (color);
447   g_object_notify (G_OBJECT (polygon), "stroke-color");
448 }
449
450 /**
451  * champlain_polygon_get_color:
452  * @polygon: The polygon
453  *
454  * Returns the polygon's fill color.
455  *
456  * Since: 0.4
457  */
458 ClutterColor *
459 champlain_polygon_get_fill_color (ChamplainPolygon *polygon)
460 {
461   g_return_val_if_fail (CHAMPLAIN_IS_POLYGON (polygon), NULL);
462
463   return polygon->priv->fill_color;
464 }
465
466 /**
467  * champlain_polygon_get_stroke_color:
468  * @polygon: The polygon
469  *
470  * Returns the polygon's stroke color.
471  *
472  * Since: 0.4
473  */
474 ClutterColor *
475 champlain_polygon_get_stroke_color (ChamplainPolygon *polygon)
476 {
477   g_return_val_if_fail (CHAMPLAIN_IS_POLYGON (polygon), NULL);
478
479   return polygon->priv->stroke_color;
480 }
481
482 /**
483  * champlain_polygon_set_stroke:
484  * @polygon: The polygon
485  * @value: if the polygon is stroked
486  *
487  * Sets the polygon to have a stroke
488  *
489  * Since: 0.4
490  */
491 void
492 champlain_polygon_set_stroke (ChamplainPolygon *polygon,
493     gboolean value)
494 {
495   g_return_if_fail (CHAMPLAIN_IS_POLYGON (polygon));
496
497   polygon->priv->stroke = value;
498   g_object_notify (G_OBJECT (polygon), "stroke");
499 }
500
501 /**
502  * champlain_polygon_get_stroke:
503  * @polygon: The polygon
504  *
505  * Returns if the polygon has a stroke
506  *
507  * Since: 0.4
508  */
509 gboolean
510 champlain_polygon_get_stroke (ChamplainPolygon *polygon)
511 {
512   g_return_val_if_fail (CHAMPLAIN_IS_POLYGON (polygon), FALSE);
513
514   return polygon->priv->stroke;
515 }
516
517 /**
518  * champlain_polygon_set_fill:
519  * @polygon: The polygon
520  * @value: if the polygon is filled
521  *
522  * Sets the polygon to have be filled
523  *
524  * Since: 0.4
525  */
526 void
527 champlain_polygon_set_fill (ChamplainPolygon *polygon,
528     gboolean value)
529 {
530   g_return_if_fail (CHAMPLAIN_IS_POLYGON (polygon));
531
532   polygon->priv->fill = value;
533   g_object_notify (G_OBJECT (polygon), "fill");
534 }
535
536 /**
537  * champlain_polygon_get_fill:
538  * @polygon: The polygon
539  *
540  * Returns if the polygon is filled
541  *
542  * Since: 0.4
543  */
544 gboolean
545 champlain_polygon_get_fill (ChamplainPolygon *polygon)
546 {
547   g_return_val_if_fail (CHAMPLAIN_IS_POLYGON (polygon), FALSE);
548
549   return polygon->priv->fill;
550 }
551
552 /**
553  * champlain_polygon_set_stroke_width:
554  * @polygon: The polygon
555  * @value: the width of the stroke (in pixels)
556  *
557  * Sets the width of the stroke
558  *
559  * Since: 0.4
560  */
561 void
562 champlain_polygon_set_stroke_width (ChamplainPolygon *polygon,
563     gdouble value)
564 {
565   g_return_if_fail (CHAMPLAIN_IS_POLYGON (polygon));
566
567   polygon->priv->stroke_width = value;
568   g_object_notify (G_OBJECT (polygon), "stroke-width");
569 }
570
571 /**
572  * champlain_polygon_get_stroke_width:
573  * @polygon: The polygon
574  *
575  * Returns the width of the stroke
576  *
577  * Since: 0.4
578  */
579 gdouble
580 champlain_polygon_get_stroke_width (ChamplainPolygon *polygon)
581 {
582   g_return_val_if_fail (CHAMPLAIN_IS_POLYGON (polygon), 0);
583
584   return polygon->priv->stroke_width;
585 }
586
587 /**
588  * champlain_polygon_show:
589  * @polygon: The polygon
590  *
591  * Makes the polygon visible
592  *
593  * Since: 0.4
594  */
595 void
596 champlain_polygon_show (ChamplainPolygon *polygon)
597 {
598   g_return_if_fail (CHAMPLAIN_IS_POLYGON (polygon));
599
600   polygon->priv->visible = TRUE;
601   if (polygon->priv->actor != NULL)
602     clutter_actor_show (polygon->priv->actor);
603   g_object_notify (G_OBJECT (polygon), "visible");
604 }
605
606 /**
607  * champlain_polygon_hide:
608  * @polygon: The polygon
609  *
610  * Hides the polygon
611  *
612  * Since: 0.4
613  */
614 void
615 champlain_polygon_hide (ChamplainPolygon *polygon)
616 {
617   g_return_if_fail (CHAMPLAIN_IS_POLYGON (polygon));
618
619   polygon->priv->visible = FALSE;
620   if (polygon->priv->actor != NULL)
621     clutter_actor_hide (polygon->priv->actor);
622   g_object_notify (G_OBJECT (polygon), "visible");
623 }