Define ChamplainPoint as a G_TYPE_BOXED
[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   return point;
324 }
325
326 /**
327  * champlain_polygon_insert_point:
328  * @polygon: The polygon
329  * @lat: the latitude
330  * @lon: the longitude
331  * @pos: where to insert the point
332  *
333  * Adds point at the given position in the list of points in the polygon
334  *
335  * Returns the added point, should not be freed.
336  *
337  * Since: 0.4
338  */
339 ChamplainPoint *
340 champlain_polygon_insert_point (ChamplainPolygon *self,
341     gdouble lat,
342     gdouble lon,
343     gint pos)
344 {
345   g_return_val_if_fail (CHAMPLAIN_IS_POLYGON (self), NULL);
346
347   ChamplainPoint *point = champlain_point_new (lat, lon);
348
349   self->priv->points = g_list_insert (self->priv->points, point, pos);
350   return point;
351 }
352
353 /**
354  * champlain_polygon_clear_points:
355  * @polygon: The polygon
356  *
357  * Remove all points from the polygon
358  *
359  * Since: 0.4
360  */
361 void
362 champlain_polygon_clear_points (ChamplainPolygon *self)
363 {
364   g_return_if_fail (CHAMPLAIN_IS_POLYGON (self));
365
366   GList *next = self->priv->points;
367   while (next != NULL)
368   {
369     champlain_point_free (next->data);
370     next = g_list_next (next);
371   }
372   g_list_free (self->priv->points);
373 }
374
375 /**
376  * champlain_polygon_get_points:
377  * @polygon: The polygon
378  *
379  * Returns a list of all points from the polygon, it shouldn't be freed.
380  *
381  * Since: 0.4
382  */
383 GList *
384 champlain_polygon_get_points (ChamplainPolygon *self)
385 {
386   g_return_val_if_fail (CHAMPLAIN_IS_POLYGON (self), NULL);
387
388   return self->priv->points;
389 }
390
391 /**
392  * champlain_polygon_set_fill_color:
393  * @polygon: The polygon
394  * @color: The polygon's fill color or NULL to reset to the
395  *         default color. The color parameter is copied.
396  *
397  * Set the polygon's fill color.
398  *
399  * Since: 0.4
400  */
401 void
402 champlain_polygon_set_fill_color (ChamplainPolygon *polygon,
403     const ClutterColor *color)
404 {
405   g_return_if_fail (CHAMPLAIN_IS_POLYGON (polygon));
406
407   ChamplainPolygonPrivate *priv = polygon->priv;
408
409   if (priv->fill_color != NULL)
410     clutter_color_free (priv->fill_color);
411
412   if (color == NULL)
413      color = &DEFAULT_FILL_COLOR;
414
415   priv->fill_color = clutter_color_copy (color);
416   g_object_notify (G_OBJECT (polygon), "fill-color");
417 }
418
419 /**
420  * champlain_polygon_set_stoke_color:
421  * @polygon: The polygon
422  * @color: The polygon's stroke color or NULL to reset to the
423  *         default color. The color parameter is copied.
424  *
425  * Set the polygon's stroke color.
426  *
427  * Since: 0.4
428  */
429 void
430 champlain_polygon_set_stroke_color (ChamplainPolygon *polygon,
431     const ClutterColor *color)
432 {
433   g_return_if_fail (CHAMPLAIN_IS_POLYGON (polygon));
434
435   ChamplainPolygonPrivate *priv = polygon->priv;
436
437   if (priv->stroke_color != NULL)
438     clutter_color_free (priv->stroke_color);
439
440   if (color == NULL)
441      color = &DEFAULT_STROKE_COLOR;
442
443   priv->stroke_color = clutter_color_copy (color);
444   g_object_notify (G_OBJECT (polygon), "stroke-color");
445 }
446
447 /**
448  * champlain_polygon_get_color:
449  * @polygon: The polygon
450  *
451  * Returns the polygon's fill color.
452  *
453  * Since: 0.4
454  */
455 ClutterColor *
456 champlain_polygon_get_fill_color (ChamplainPolygon *polygon)
457 {
458   g_return_val_if_fail (CHAMPLAIN_IS_POLYGON (polygon), NULL);
459
460   return polygon->priv->fill_color;
461 }
462
463 /**
464  * champlain_polygon_get_stroke_color:
465  * @polygon: The polygon
466  *
467  * Returns the polygon's stroke color.
468  *
469  * Since: 0.4
470  */
471 ClutterColor *
472 champlain_polygon_get_stroke_color (ChamplainPolygon *polygon)
473 {
474   g_return_val_if_fail (CHAMPLAIN_IS_POLYGON (polygon), NULL);
475
476   return polygon->priv->stroke_color;
477 }
478
479 /**
480  * champlain_polygon_set_stroke:
481  * @polygon: The polygon
482  * @value: if the polygon is stroked
483  *
484  * Sets the polygon to have a stroke
485  *
486  * Since: 0.4
487  */
488 void
489 champlain_polygon_set_stroke (ChamplainPolygon *polygon,
490     gboolean value)
491 {
492   g_return_if_fail (CHAMPLAIN_IS_POLYGON (polygon));
493
494   polygon->priv->stroke = value;
495   g_object_notify (G_OBJECT (polygon), "stroke");
496 }
497
498 /**
499  * champlain_polygon_get_stroke:
500  * @polygon: The polygon
501  *
502  * Returns if the polygon has a stroke
503  *
504  * Since: 0.4
505  */
506 gboolean
507 champlain_polygon_get_stroke (ChamplainPolygon *polygon)
508 {
509   g_return_val_if_fail (CHAMPLAIN_IS_POLYGON (polygon), FALSE);
510
511   return polygon->priv->stroke;
512 }
513
514 /**
515  * champlain_polygon_set_fill:
516  * @polygon: The polygon
517  * @value: if the polygon is filled
518  *
519  * Sets the polygon to have be filled
520  *
521  * Since: 0.4
522  */
523 void
524 champlain_polygon_set_fill (ChamplainPolygon *polygon,
525     gboolean value)
526 {
527   g_return_if_fail (CHAMPLAIN_IS_POLYGON (polygon));
528
529   polygon->priv->fill = value;
530   g_object_notify (G_OBJECT (polygon), "fill");
531 }
532
533 /**
534  * champlain_polygon_get_fill:
535  * @polygon: The polygon
536  *
537  * Returns if the polygon is filled
538  *
539  * Since: 0.4
540  */
541 gboolean
542 champlain_polygon_get_fill (ChamplainPolygon *polygon)
543 {
544   g_return_val_if_fail (CHAMPLAIN_IS_POLYGON (polygon), FALSE);
545
546   return polygon->priv->fill;
547 }
548
549 /**
550  * champlain_polygon_set_stroke_width:
551  * @polygon: The polygon
552  * @value: the width of the stroke (in pixels)
553  *
554  * Sets the width of the stroke
555  *
556  * Since: 0.4
557  */
558 void
559 champlain_polygon_set_stroke_width (ChamplainPolygon *polygon,
560     gdouble value)
561 {
562   g_return_if_fail (CHAMPLAIN_IS_POLYGON (polygon));
563
564   polygon->priv->stroke_width = value;
565   g_object_notify (G_OBJECT (polygon), "stroke-width");
566 }
567
568 /**
569  * champlain_polygon_get_stroke_width:
570  * @polygon: The polygon
571  *
572  * Returns the width of the stroke
573  *
574  * Since: 0.4
575  */
576 gdouble
577 champlain_polygon_get_stroke_width (ChamplainPolygon *polygon)
578 {
579   g_return_val_if_fail (CHAMPLAIN_IS_POLYGON (polygon), 0);
580
581   return polygon->priv->stroke_width;
582 }
583
584 /**
585  * champlain_polygon_show:
586  * @polygon: The polygon
587  *
588  * Makes the polygon visible
589  *
590  * Since: 0.4
591  */
592 void
593 champlain_polygon_show (ChamplainPolygon *polygon)
594 {
595   g_return_if_fail (CHAMPLAIN_IS_POLYGON (polygon));
596
597   polygon->priv->visible = TRUE;
598   if (polygon->priv->actor != NULL)
599     clutter_actor_show (polygon->priv->actor);
600   g_object_notify (G_OBJECT (polygon), "visible");
601 }
602
603 /**
604  * champlain_polygon_hide:
605  * @polygon: The polygon
606  *
607  * Hides the polygon
608  *
609  * Since: 0.4
610  */
611 void
612 champlain_polygon_hide (ChamplainPolygon *polygon)
613 {
614   g_return_if_fail (CHAMPLAIN_IS_POLYGON (polygon));
615
616   polygon->priv->visible = FALSE;
617   if (polygon->priv->actor != NULL)
618     clutter_actor_hide (polygon->priv->actor);
619   g_object_notify (G_OBJECT (polygon), "visible");
620 }