1 /*
2  * Copyright (C) 2008 Pierre-Luc Beaudoin <pierre-luc@pierlux.com>
3  * Copyright (C) 2011-2013 Jiri Techet <techet@gmail.com>
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2.1 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
18  */
19 
20 /**
21  * SECTION:champlain-marker
22  * @short_description: Base class of libchamplain markers
23  *
24  * Markers represent points of interest on a map. Markers need to be
25  * placed on a layer (a #ChamplainMarkerLayer). Layers have to be added to a
26  * #champlainview for the markers to show on the map.
27  *
28  * A marker is nothing more than a regular #clutteractor. You can draw on
29  * it what ever you want.  Set the marker's position
30  * on the map using #champlain_location_set_location. Don't forget to set the
31  * marker's pointer position using #clutter_actor_set_translation.
32  *
33  * This is a base class of all markers. libchamplain has a more evoluted
34  * type of markers with text and image support. See #ChamplainLabel for more details.
35  */
36 
37 #include "config.h"
38 
39 #include "champlain-marker.h"
40 
41 #include "champlain.h"
42 #include "champlain-defines.h"
43 #include "champlain-private.h"
44 #include "champlain-tile.h"
45 
46 #include <clutter/clutter.h>
47 #include <glib.h>
48 #include <glib-object.h>
49 #include <cairo.h>
50 #include <math.h>
51 
52 static ClutterColor SELECTED_COLOR = { 0x00, 0x33, 0xcc, 0xff };
53 static ClutterColor SELECTED_TEXT_COLOR = { 0xff, 0xff, 0xff, 0xff };
54 
55 enum
56 {
57   /* normal signals */
58   BUTTON_PRESS_SIGNAL,
59   BUTTON_RELEASE_SIGNAL,
60   DRAG_MOTION_SIGNAL,
61   DRAG_FINISH_SIGNAL,
62   LAST_SIGNAL,
63 };
64 
65 static guint signals[LAST_SIGNAL] = { 0, };
66 
67 enum
68 {
69   PROP_0,
70   PROP_LONGITUDE,
71   PROP_LATITUDE,
72   PROP_SELECTED,
73   PROP_SELECTABLE,
74   PROP_DRAGGABLE,
75 };
76 
77 /* static guint champlain_marker_signals[LAST_SIGNAL] = { 0, }; */
78 
79 static void set_location (ChamplainLocation *location,
80     gdouble latitude,
81     gdouble longitude);
82 static gdouble get_latitude (ChamplainLocation *location);
83 static gdouble get_longitude (ChamplainLocation *location);
84 
85 static void location_interface_init (ChamplainLocationIface *iface);
86 
87 struct _ChamplainMarkerPrivate
88 {
89   gdouble lon;
90   gdouble lat;
91   gboolean selected;
92   gboolean selectable;
93   gboolean draggable;
94 
95   gfloat click_x;
96   gfloat click_y;
97   gboolean moved;
98 };
99 
G_DEFINE_TYPE_WITH_CODE(ChamplainMarker,champlain_marker,CLUTTER_TYPE_ACTOR,G_ADD_PRIVATE (ChamplainMarker)G_IMPLEMENT_INTERFACE (CHAMPLAIN_TYPE_LOCATION,location_interface_init))100 G_DEFINE_TYPE_WITH_CODE (ChamplainMarker, champlain_marker, CLUTTER_TYPE_ACTOR,
101     G_ADD_PRIVATE (ChamplainMarker)
102     G_IMPLEMENT_INTERFACE (CHAMPLAIN_TYPE_LOCATION, location_interface_init))
103 
104 
105 /**
106  * champlain_marker_set_selection_color:
107  * @color: a #ClutterColor
108  *
109  * Changes the selection color, this is to ensure a better integration with
110  * the desktop, this is automatically done by GtkChamplainEmbed.
111  *
112  * Since: 0.10
113  */
114 void
115 champlain_marker_set_selection_color (ClutterColor *color)
116 {
117   SELECTED_COLOR.red = color->red;
118   SELECTED_COLOR.green = color->green;
119   SELECTED_COLOR.blue = color->blue;
120   SELECTED_COLOR.alpha = color->alpha;
121 }
122 
123 
124 /**
125  * champlain_marker_get_selection_color:
126  *
127  * Gets the selection color.
128  *
129  * Returns: the selection color. Should not be freed.
130  *
131  * Since: 0.10
132  */
133 const ClutterColor *
champlain_marker_get_selection_color()134 champlain_marker_get_selection_color ()
135 {
136   return &SELECTED_COLOR;
137 }
138 
139 
140 /**
141  * champlain_marker_set_selection_text_color:
142  * @color: a #ClutterColor
143  *
144  * Changes the selection text color, this is to ensure a better integration with
145  * the desktop, this is automatically done by GtkChamplainEmbed.
146  *
147  * Since: 0.10
148  */
149 void
champlain_marker_set_selection_text_color(ClutterColor * color)150 champlain_marker_set_selection_text_color (ClutterColor *color)
151 {
152   SELECTED_TEXT_COLOR.red = color->red;
153   SELECTED_TEXT_COLOR.green = color->green;
154   SELECTED_TEXT_COLOR.blue = color->blue;
155   SELECTED_TEXT_COLOR.alpha = color->alpha;
156 }
157 
158 
159 /**
160  * champlain_marker_get_selection_text_color:
161  *
162  * Gets the selection text color.
163  *
164  * Returns: the selection text color. Should not be freed.
165  *
166  * Since: 0.10
167  */
168 const ClutterColor *
champlain_marker_get_selection_text_color()169 champlain_marker_get_selection_text_color ()
170 {
171   return &SELECTED_TEXT_COLOR;
172 }
173 
174 
175 static void
champlain_marker_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)176 champlain_marker_get_property (GObject *object,
177     guint prop_id,
178     GValue *value,
179     GParamSpec *pspec)
180 {
181   ChamplainMarker *marker = CHAMPLAIN_MARKER (object);
182   ChamplainMarkerPrivate *priv = marker->priv;
183 
184   switch (prop_id)
185     {
186     case PROP_LONGITUDE:
187       g_value_set_double (value, priv->lon);
188       break;
189 
190     case PROP_LATITUDE:
191       g_value_set_double (value, priv->lat);
192       break;
193 
194     case PROP_SELECTED:
195       g_value_set_boolean (value, priv->selected);
196       break;
197 
198     case PROP_SELECTABLE:
199       g_value_set_boolean (value, priv->selectable);
200       break;
201 
202     case PROP_DRAGGABLE:
203       g_value_set_boolean (value, priv->draggable);
204       break;
205 
206     default:
207       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
208     }
209 }
210 
211 
212 static void
champlain_marker_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)213 champlain_marker_set_property (GObject *object,
214     guint prop_id,
215     const GValue *value,
216     GParamSpec *pspec)
217 {
218   ChamplainMarker *marker = CHAMPLAIN_MARKER (object);
219   ChamplainMarkerPrivate *priv = marker->priv;
220 
221   switch (prop_id)
222     {
223     case PROP_LONGITUDE:
224       {
225         gdouble lon = g_value_get_double (value);
226         set_location (CHAMPLAIN_LOCATION (marker), priv->lat, lon);
227         break;
228       }
229 
230     case PROP_LATITUDE:
231       {
232         gdouble lat = g_value_get_double (value);
233         set_location (CHAMPLAIN_LOCATION (marker), lat, priv->lon);
234         break;
235       }
236 
237     case PROP_SELECTED:
238       {
239         gboolean bvalue = g_value_get_boolean (value);
240         champlain_marker_set_selected (marker, bvalue);
241         break;
242       }
243 
244     case PROP_SELECTABLE:
245       {
246         gboolean bvalue = g_value_get_boolean (value);
247         champlain_marker_set_selectable (marker, bvalue);
248         break;
249       }
250 
251     case PROP_DRAGGABLE:
252       {
253         gboolean bvalue = g_value_get_boolean (value);
254         champlain_marker_set_draggable (marker, bvalue);
255         break;
256       }
257 
258     default:
259       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
260     }
261 }
262 
263 
264 static void
set_location(ChamplainLocation * location,gdouble latitude,gdouble longitude)265 set_location (ChamplainLocation *location,
266     gdouble latitude,
267     gdouble longitude)
268 {
269   g_return_if_fail (CHAMPLAIN_IS_MARKER (location));
270 
271   ChamplainMarkerPrivate *priv = CHAMPLAIN_MARKER (location)->priv;
272 
273   priv->lon = CLAMP (longitude, CHAMPLAIN_MIN_LONGITUDE, CHAMPLAIN_MAX_LONGITUDE);
274   priv->lat = CLAMP (latitude, CHAMPLAIN_MIN_LATITUDE, CHAMPLAIN_MAX_LATITUDE);
275 
276   g_object_notify (G_OBJECT (location), "latitude");
277   g_object_notify (G_OBJECT (location), "longitude");
278 }
279 
280 
281 static gdouble
get_latitude(ChamplainLocation * location)282 get_latitude (ChamplainLocation *location)
283 {
284   g_return_val_if_fail (CHAMPLAIN_IS_MARKER (location), 0.0);
285 
286   ChamplainMarkerPrivate *priv = CHAMPLAIN_MARKER (location)->priv;
287 
288   return priv->lat;
289 }
290 
291 
292 static gdouble
get_longitude(ChamplainLocation * location)293 get_longitude (ChamplainLocation *location)
294 {
295   g_return_val_if_fail (CHAMPLAIN_IS_MARKER (location), 0.0);
296 
297   ChamplainMarkerPrivate *priv = CHAMPLAIN_MARKER (location)->priv;
298 
299   return priv->lon;
300 }
301 
302 
303 static void
location_interface_init(ChamplainLocationIface * iface)304 location_interface_init (ChamplainLocationIface *iface)
305 {
306   iface->get_latitude = get_latitude;
307   iface->get_longitude = get_longitude;
308   iface->set_location = set_location;
309 }
310 
311 
312 static void
champlain_marker_dispose(GObject * object)313 champlain_marker_dispose (GObject *object)
314 {
315   G_OBJECT_CLASS (champlain_marker_parent_class)->dispose (object);
316 }
317 
318 
319 static void
champlain_marker_finalize(GObject * object)320 champlain_marker_finalize (GObject *object)
321 {
322   G_OBJECT_CLASS (champlain_marker_parent_class)->finalize (object);
323 }
324 
325 
326 static void
champlain_marker_class_init(ChamplainMarkerClass * marker_class)327 champlain_marker_class_init (ChamplainMarkerClass *marker_class)
328 {
329   GObjectClass *object_class = G_OBJECT_CLASS (marker_class);
330   object_class->finalize = champlain_marker_finalize;
331   object_class->dispose = champlain_marker_dispose;
332   object_class->get_property = champlain_marker_get_property;
333   object_class->set_property = champlain_marker_set_property;
334 
335   /**
336    * ChamplainMarker:selected:
337    *
338    * The selected state of the marker
339    *
340    * Since: 0.10
341    */
342   g_object_class_install_property (object_class, PROP_SELECTED,
343       g_param_spec_boolean ("selected",
344           "Selected",
345           "The sighlighted state of the marker",
346           FALSE,
347           CHAMPLAIN_PARAM_READWRITE));
348 
349   /**
350    * ChamplainMarker:selectable:
351    *
352    * The selectable state of the marker
353    *
354    * Since: 0.10
355    */
356   g_object_class_install_property (object_class, PROP_SELECTABLE,
357       g_param_spec_boolean ("selectable",
358           "Selectable",
359           "The draggable state of the marker",
360           FALSE,
361           CHAMPLAIN_PARAM_READWRITE));
362 
363   /**
364    * ChamplainMarker:draggable:
365    *
366    * The draggable state of the marker
367    *
368    * Since: 0.10
369    */
370   g_object_class_install_property (object_class, PROP_DRAGGABLE,
371       g_param_spec_boolean ("draggable",
372           "Draggable",
373           "The draggable state of the marker",
374           FALSE,
375           CHAMPLAIN_PARAM_READWRITE));
376 
377   /**
378    * ChamplainMarker::button-press:
379    * @self: a #ChamplainMarker
380    * @event: the underlying ClutterEvent
381    *
382    * Emitted when button is pressed.
383    *
384    * Since: 0.10
385    */
386   signals[BUTTON_PRESS_SIGNAL] =
387     g_signal_new ("button-press",
388         G_OBJECT_CLASS_TYPE (object_class),
389         G_SIGNAL_RUN_LAST,
390         0, NULL, NULL,
391         NULL,
392         G_TYPE_NONE,
393         1,
394         CLUTTER_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
395 
396   /**
397    * ChamplainMarker::button-release:
398    * @self: a #ChamplainMarker
399    * @event: the underlying ClutterEvent
400    *
401    * Emitted when button is released. This signal is not emmitted at the end of dragging.
402    *
403    * Since: 0.10
404    */
405   signals[BUTTON_RELEASE_SIGNAL] =
406     g_signal_new ("button-release",
407         G_OBJECT_CLASS_TYPE (object_class),
408         G_SIGNAL_RUN_LAST,
409         0, NULL, NULL,
410         NULL,
411         G_TYPE_NONE,
412         1,
413         CLUTTER_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
414 
415   /**
416    * ChamplainMarker::drag-motion:
417    * @self: a #ChamplainMarker
418    * @dx: by how much the marker has been moved in the x direction
419    * @dy: by how much the marker has been moved in the y direction
420    * @event: the underlying ClutterEvent
421    *
422    * Emmitted when the marker is dragged by mouse. dx and dy specify by how much
423    * the marker has been dragged since last time.
424    *
425    * Since: 0.10
426    */
427   signals[DRAG_MOTION_SIGNAL] =
428     g_signal_new ("drag-motion",
429         G_OBJECT_CLASS_TYPE (object_class),
430         G_SIGNAL_RUN_LAST,
431         0, NULL, NULL,
432         NULL,
433         G_TYPE_NONE,
434         3,
435         G_TYPE_DOUBLE, G_TYPE_DOUBLE, CLUTTER_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
436 
437   /**
438    * ChamplainMarker::drag-finish:
439    * @self: a #ChamplainMarker
440    * @event: the underlying ClutterEvent
441    *
442    * Emitted when marker dragging ends (i.e. the button is released at the end
443    * of dragging).
444    *
445    * Since: 0.10
446    */
447   signals[DRAG_FINISH_SIGNAL] =
448     g_signal_new ("drag-finish",
449         G_OBJECT_CLASS_TYPE (object_class),
450         G_SIGNAL_RUN_LAST,
451         0, NULL, NULL,
452         NULL,
453         G_TYPE_NONE,
454         1,
455         CLUTTER_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
456 
457 
458   g_object_class_override_property (object_class,
459       PROP_LONGITUDE,
460       "longitude");
461 
462   g_object_class_override_property (object_class,
463       PROP_LATITUDE,
464       "latitude");
465 }
466 
467 
468 /**
469  * champlain_marker_new:
470  *
471  * Creates an instance of #ChamplainMarker.
472  *
473  * Returns: a new #ChamplainMarker.
474  *
475  * Since: 0.12.4
476  */
477 ClutterActor *
champlain_marker_new(void)478 champlain_marker_new (void)
479 {
480   return CLUTTER_ACTOR (g_object_new (CHAMPLAIN_TYPE_MARKER, NULL));
481 }
482 
483 
484 static gboolean
motion_event_cb(ClutterActor * stage,ClutterMotionEvent * event,ChamplainMarker * marker)485 motion_event_cb (ClutterActor *stage,
486     ClutterMotionEvent *event,
487     ChamplainMarker *marker)
488 {
489   ChamplainMarkerPrivate *priv = marker->priv;
490   gfloat x, y;
491 
492   if (event->type != CLUTTER_MOTION)
493     return FALSE;
494 
495   if (clutter_actor_transform_stage_point (CLUTTER_ACTOR (marker),
496           event->x,
497           event->y,
498           &x, &y))
499     {
500       g_signal_emit_by_name (marker, "drag-motion",
501           x - priv->click_x, y - priv->click_y, event);
502       priv->moved = TRUE;
503     }
504 
505   return TRUE;
506 }
507 
508 
509 static gboolean
capture_release_event_cb(ClutterActor * stage,ClutterButtonEvent * event,ChamplainMarker * marker)510 capture_release_event_cb (ClutterActor *stage,
511     ClutterButtonEvent *event,
512     ChamplainMarker *marker)
513 {
514   ChamplainMarkerPrivate *priv = marker->priv;
515 
516   if ((event->type != CLUTTER_BUTTON_RELEASE) ||
517       (event->button != 1))
518     return FALSE;
519 
520   g_signal_handlers_disconnect_by_func (stage,
521       motion_event_cb,
522       marker);
523   g_signal_handlers_disconnect_by_func (stage,
524       capture_release_event_cb,
525       marker);
526 
527   if (priv->moved)
528     g_signal_emit_by_name (marker, "drag-finish", event);
529   else
530     g_signal_emit_by_name (marker, "button-release", event);
531 
532   return TRUE;
533 }
534 
535 
536 static gboolean
button_release_event_cb(ClutterActor * actor,ClutterButtonEvent * event,ChamplainMarker * marker)537 button_release_event_cb (ClutterActor *actor,
538     ClutterButtonEvent *event,
539     ChamplainMarker *marker)
540 {
541   if ((event->type != CLUTTER_BUTTON_RELEASE) ||
542       (event->button != 1))
543     return FALSE;
544 
545   g_signal_handlers_disconnect_by_func (marker,
546       button_release_event_cb,
547       marker);
548 
549   g_signal_emit_by_name (marker, "button-release", event);
550 
551   return TRUE;
552 }
553 
554 
555 static gboolean
button_press_event_cb(ClutterActor * actor,ClutterEvent * event,ChamplainMarker * marker)556 button_press_event_cb (ClutterActor *actor,
557     ClutterEvent *event,
558     ChamplainMarker *marker)
559 {
560   ChamplainMarkerPrivate *priv = marker->priv;
561   ClutterButtonEvent *bevent = (ClutterButtonEvent *) event;
562   ClutterActor *stage = clutter_actor_get_stage (actor);
563 
564   if (event->type != CLUTTER_BUTTON_PRESS ||
565       bevent->button != 1 ||
566       !stage)
567     {
568       return FALSE;
569     }
570 
571   if (priv->draggable)
572     {
573       if (clutter_actor_transform_stage_point (actor, bevent->x, bevent->y,
574               &priv->click_x, &priv->click_y))
575         {
576           priv->moved = FALSE;
577           g_signal_connect (stage,
578               "captured-event",
579               G_CALLBACK (motion_event_cb),
580               marker);
581 
582           g_signal_connect (stage,
583               "captured-event",
584               G_CALLBACK (capture_release_event_cb),
585               marker);
586         }
587     }
588   else
589     {
590       g_signal_connect (marker,
591           "button-release-event",
592           G_CALLBACK (button_release_event_cb),
593           marker);
594     }
595 
596   if (priv->selectable)
597     champlain_marker_set_selected (marker, TRUE);
598 
599   if (priv->selectable || priv->draggable)
600     {
601       ClutterActor *parent;
602 
603       parent = clutter_actor_get_parent (CLUTTER_ACTOR (marker));
604       clutter_actor_set_child_above_sibling (parent, CLUTTER_ACTOR (marker), NULL);
605     }
606 
607   g_signal_emit_by_name (marker, "button-press", event);
608 
609   return TRUE;
610 }
611 
612 
613 static void
champlain_marker_init(ChamplainMarker * marker)614 champlain_marker_init (ChamplainMarker *marker)
615 {
616   ChamplainMarkerPrivate *priv = champlain_marker_get_instance_private (marker);
617 
618   marker->priv = priv;
619 
620   priv->lat = 0;
621   priv->lon = 0;
622   priv->selected = FALSE;
623   priv->selectable = TRUE;
624   priv->draggable = FALSE;
625 
626   clutter_actor_set_reactive (CLUTTER_ACTOR (marker), TRUE);
627 
628   g_signal_connect (marker,
629       "button-press-event",
630       G_CALLBACK (button_press_event_cb),
631       marker);
632 }
633 
634 
635 /**
636  * champlain_marker_set_selected:
637  * @marker: a #ChamplainMarker
638  * @value: the selected state
639  *
640  * Sets the marker as selected or not. This will affect the "Selected" look
641  * of the marker.
642  *
643  * Since: 0.10
644  */
645 void
champlain_marker_set_selected(ChamplainMarker * marker,gboolean value)646 champlain_marker_set_selected (ChamplainMarker *marker,
647     gboolean value)
648 {
649   g_return_if_fail (CHAMPLAIN_IS_MARKER (marker));
650 
651   marker->priv->selected = value;
652 
653   g_object_notify (G_OBJECT (marker), "selected");
654 }
655 
656 
657 /**
658  * champlain_marker_get_selected:
659  * @marker: a #ChamplainMarker
660  *
661  * Checks whether the marker is selected.
662  *
663  * Returns: the selected or not state of the marker.
664  *
665  * Since: 0.10
666  */
667 gboolean
champlain_marker_get_selected(ChamplainMarker * marker)668 champlain_marker_get_selected (ChamplainMarker *marker)
669 {
670   g_return_val_if_fail (CHAMPLAIN_IS_MARKER (marker), FALSE);
671 
672   return marker->priv->selected;
673 }
674 
675 
676 /**
677  * champlain_marker_set_selectable:
678  * @marker: a #ChamplainMarker
679  * @value: the selectable state
680  *
681  * Sets the marker as selectable or not.
682  *
683  * Since: 0.10
684  */
685 void
champlain_marker_set_selectable(ChamplainMarker * marker,gboolean value)686 champlain_marker_set_selectable (ChamplainMarker *marker,
687     gboolean value)
688 {
689   g_return_if_fail (CHAMPLAIN_IS_MARKER (marker));
690 
691   marker->priv->selectable = value;
692 
693   g_object_notify (G_OBJECT (marker), "selectable");
694 }
695 
696 
697 /**
698  * champlain_marker_get_selectable:
699  * @marker: a #ChamplainMarker
700  *
701  * Checks whether the marker is selectable.
702  *
703  * Returns: the selectable or not state of the marker.
704  *
705  * Since: 0.10
706  */
707 gboolean
champlain_marker_get_selectable(ChamplainMarker * marker)708 champlain_marker_get_selectable (ChamplainMarker *marker)
709 {
710   g_return_val_if_fail (CHAMPLAIN_IS_MARKER (marker), FALSE);
711 
712   return marker->priv->selectable;
713 }
714 
715 
716 /**
717  * champlain_marker_set_draggable:
718  * @marker: a #ChamplainMarker
719  * @value: the draggable state
720  *
721  * Sets the marker as draggable or not.
722  *
723  * Since: 0.10
724  */
725 void
champlain_marker_set_draggable(ChamplainMarker * marker,gboolean value)726 champlain_marker_set_draggable (ChamplainMarker *marker,
727     gboolean value)
728 {
729   g_return_if_fail (CHAMPLAIN_IS_MARKER (marker));
730 
731   marker->priv->draggable = value;
732 
733   g_object_notify (G_OBJECT (marker), "draggable");
734 }
735 
736 
737 /**
738  * champlain_marker_get_draggable:
739  * @marker: a #ChamplainMarker
740  *
741  * Checks whether the marker is draggable.
742  *
743  * Returns: the draggable or not state of the marker.
744  *
745  * Since: 0.10
746  */
747 gboolean
champlain_marker_get_draggable(ChamplainMarker * marker)748 champlain_marker_get_draggable (ChamplainMarker *marker)
749 {
750   g_return_val_if_fail (CHAMPLAIN_IS_MARKER (marker), FALSE);
751 
752   return marker->priv->draggable;
753 }
754 
755 
756 /**
757  * champlain_marker_animate_in:
758  * @marker: a #ChamplainMarker
759  *
760  * Animates the marker as if it were falling from the sky onto the map.
761  *
762  * Since: 0.10
763  */
764 void
champlain_marker_animate_in(ChamplainMarker * marker)765 champlain_marker_animate_in (ChamplainMarker *marker)
766 {
767   champlain_marker_animate_in_with_delay (marker, 0);
768 }
769 
770 
771 /**
772  * champlain_marker_animate_in_with_delay :
773  * @marker: a #ChamplainMarker
774  * @delay: The delay in milliseconds
775  *
776  * Animates the marker as if it were falling from the sky onto the map after
777  * delay.
778  *
779  * Since: 0.10
780  */
781 void
champlain_marker_animate_in_with_delay(ChamplainMarker * marker,guint delay)782 champlain_marker_animate_in_with_delay (ChamplainMarker *marker,
783     guint delay)
784 {
785   gfloat y;
786 
787   g_return_if_fail (CHAMPLAIN_IS_MARKER (marker));
788 
789   clutter_actor_show (CLUTTER_ACTOR (marker));
790   clutter_actor_set_opacity (CLUTTER_ACTOR (marker), 0);
791   clutter_actor_set_scale (CLUTTER_ACTOR (marker), 1.5, 1.5);
792   clutter_actor_get_position (CLUTTER_ACTOR (marker), NULL, &y);
793   clutter_actor_move_by (CLUTTER_ACTOR (marker), 0, -100);
794 
795   clutter_actor_save_easing_state (CLUTTER_ACTOR (marker));
796   clutter_actor_set_easing_delay (CLUTTER_ACTOR (marker), delay);
797   clutter_actor_set_easing_mode (CLUTTER_ACTOR (marker), CLUTTER_EASE_OUT_BOUNCE);
798   clutter_actor_set_easing_duration (CLUTTER_ACTOR (marker), 1000);
799   clutter_actor_set_opacity (CLUTTER_ACTOR (marker), 255);
800   clutter_actor_set_scale (CLUTTER_ACTOR (marker), 1.0, 1.0);
801   clutter_actor_set_y (CLUTTER_ACTOR (marker), y);
802   clutter_actor_restore_easing_state (CLUTTER_ACTOR (marker));
803 }
804 
805 
806 /**
807  * champlain_marker_animate_out:
808  * @marker: a #ChamplainMarker
809  *
810  * Animates the marker as if it were drawn through the sky.
811  *
812  * Since: 0.10
813  */
814 void
champlain_marker_animate_out(ChamplainMarker * marker)815 champlain_marker_animate_out (ChamplainMarker *marker)
816 {
817   champlain_marker_animate_out_with_delay (marker, 0);
818 }
819 
820 
821 static void
on_transition_stopped(ClutterActor * marker,const gchar * transition_name,gboolean is_finished)822 on_transition_stopped (ClutterActor *marker,
823     const gchar *transition_name,
824     gboolean is_finished)
825 {
826   clutter_actor_hide (marker);
827 
828   clutter_actor_move_by (marker, 0, 100);
829   g_signal_handlers_disconnect_by_func (marker, on_transition_stopped, NULL);
830 }
831 
832 
833 /**
834  * champlain_marker_animate_out_with_delay :
835  * @marker: a #ChamplainMarker
836  * @delay: The delay in milliseconds
837  *
838  * Animates the marker as if it were drawn through the sky after
839  * delay.
840  *
841  * Since: 0.10
842  */
843 void
champlain_marker_animate_out_with_delay(ChamplainMarker * marker,guint delay)844 champlain_marker_animate_out_with_delay (ChamplainMarker *marker,
845     guint delay)
846 {
847   gfloat y;
848 
849   g_return_if_fail (CHAMPLAIN_IS_MARKER (marker));
850 
851   clutter_actor_get_position (CLUTTER_ACTOR (marker), NULL, &y);
852   clutter_actor_set_opacity (CLUTTER_ACTOR (marker), 200);
853 
854   clutter_actor_save_easing_state (CLUTTER_ACTOR (marker));
855   clutter_actor_set_easing_delay (CLUTTER_ACTOR (marker), delay);
856   clutter_actor_set_easing_mode (CLUTTER_ACTOR (marker), CLUTTER_EASE_IN_BACK);
857   clutter_actor_set_easing_duration (CLUTTER_ACTOR (marker), 750);
858   clutter_actor_set_opacity (CLUTTER_ACTOR (marker), 0);
859   clutter_actor_set_scale (CLUTTER_ACTOR (marker), 2.0, 2.0);
860   clutter_actor_set_y (CLUTTER_ACTOR (marker), y - 100);
861   clutter_actor_restore_easing_state (CLUTTER_ACTOR (marker));
862 
863   g_signal_connect (CLUTTER_ACTOR (marker),
864       "transition-stopped::opacity",
865       G_CALLBACK (on_transition_stopped),
866       NULL);
867 }
868