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