Add an option to highligh points in Polygons
[libchamplain:potyl-perl.git] / champlain / champlain-view.c
1 /*
2  * Copyright (C) 2008-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-view
21  * @short_description: A #ClutterActor to display maps
22  *
23  * The #ChamplainView is a ClutterActor to display maps.  It supports two modes
24  * of scrolling:
25  * <itemizedlist>
26  *   <listitem><para>Push: the normal behavior where the maps doesn't move
27  *   after the user stopped scrolling;</para></listitem>
28  *   <listitem><para>Kinetic: the iPhone-like behavior where the maps
29  *   decelerate after the user stopped scrolling.</para></listitem>
30  * </itemizedlist>
31  *
32  * You can use the same #ChamplainView to display many types of maps.  In
33  * Champlain they are called map sources.  You can change the #map-source
34  * property at anytime to replace the current displayed map.
35  *
36  * The maps are downloaded from Internet from open maps sources (like
37  * <ulink role="online-location"
38  * url="http://www.openstreetmap.org">OpenStreetMap</ulink>).  Maps are divided
39  * in tiles for each zoom level.  When a tile is requested, #ChamplainView will
40  * first check if it is in cache (in the user's cache dir under champlain). If
41  * an error occurs during download, an error tile will be displayed.
42  *
43  * The button-press-event and button-release-event signals are emitted each
44  * time a mouse button is pressed on the @view.  Coordinates can be converted
45  * with #champlain_view_get_coords_from_event.
46  */
47
48 #include "config.h"
49
50 #include "champlain-view.h"
51
52 #define DEBUG_FLAG CHAMPLAIN_DEBUG_VIEW
53 #include "champlain-debug.h"
54
55 #include "champlain.h"
56 #include "champlain-defines.h"
57 #include "champlain-enum-types.h"
58 #include "champlain-map.h"
59 #include "champlain-marshal.h"
60 #include "champlain-map-source.h"
61 #include "champlain-map-source-factory.h"
62 #include "champlain-polygon.h"
63 #include "champlain-private.h"
64 #include "champlain-tile.h"
65 #include "champlain-zoom-level.h"
66
67 #include <clutter/clutter.h>
68 #include <glib.h>
69 #include <glib-object.h>
70 #include <math.h>
71 #include <tidy-adjustment.h>
72 #include <tidy-finger-scroll.h>
73 #include <tidy-scrollable.h>
74 #include <tidy-viewport.h>
75
76 enum
77 {
78   /* normal signals */
79   ANIMATION_COMPLETED,
80   LAST_SIGNAL
81 };
82
83 enum
84 {
85   PROP_0,
86   PROP_LONGITUDE,
87   PROP_LATITUDE,
88   PROP_ZOOM_LEVEL,
89   PROP_MIN_ZOOM_LEVEL,
90   PROP_MAX_ZOOM_LEVEL,
91   PROP_MAP_SOURCE,
92   PROP_DECEL_RATE,
93   PROP_SCROLL_MODE,
94   PROP_KEEP_CENTER_ON_RESIZE,
95   PROP_SHOW_LICENSE,
96   PROP_LICENSE_EXTRA,
97   PROP_ZOOM_ON_DOUBLE_CLICK,
98   PROP_STATE,
99   PROP_SHOW_SCALE,
100   PROP_SCALE_UNIT,
101   PROP_MAX_SCALE_WIDTH,
102 };
103
104 #define PADDING 10
105 static guint signals[LAST_SIGNAL] = { 0, };
106
107 #define GET_PRIVATE(obj)     (G_TYPE_INSTANCE_GET_PRIVATE ((obj), CHAMPLAIN_TYPE_VIEW, ChamplainViewPrivate))
108 #define ZOOM_LEVEL_OUT_OF_RANGE(priv, level)    (level < priv->min_zoom_level || level > priv->max_zoom_level)
109
110 /* Between state values for go_to */
111 typedef struct {
112   ChamplainView *view;
113   ClutterAlpha *alpha;
114   ClutterTimeline *timeline;
115   gdouble to_latitude;
116   gdouble to_longitude;
117   gdouble from_latitude;
118   gdouble from_longitude;
119 } GoToContext;
120
121 typedef struct {
122   ChamplainView *view;
123   ChamplainPolygon *polygon;
124 } PolygonRedrawContext;
125
126 struct _ChamplainViewPrivate
127 {
128   ClutterActor *stage;
129
130   ChamplainMapSourceFactory *factory; /* The map source factory */
131   ChamplainMapSource *map_source; /* Current map tile source */
132   ChamplainScrollMode scroll_mode;
133   gint zoom_level; /* Holds the current zoom level number */
134   gint min_zoom_level; /* Lowest allowed zoom level */
135   gint max_zoom_level; /* Highest allowed zoom level */
136
137   /* Represents the (lat, lon) at the center of the viewport */
138   gdouble longitude;
139   gdouble latitude;
140
141   /* Hack to get smaller x,y coordinates as the clutter limit is G_MAXINT16 */
142   ChamplainFloatPoint anchor;
143
144   gdouble anchor_zoom_level; /* the zoom_level for which the current anchor has
145                                 been computed for */
146
147   Map *map; /* Contains the current map model */
148
149   ClutterActor *finger_scroll; /* Contains the viewport */
150   ClutterActor *viewport;  /* Contains the map_layer, license and markers */
151   ClutterActor *map_layer; /* Contains tiles actors (grouped by zoom level) */
152   ChamplainRectangle viewport_size;
153
154   ClutterActor *user_layers; /* Contains the markers */
155
156   gboolean keep_center_on_resize;
157
158   gboolean zoom_on_double_click;
159
160   gboolean show_license;
161   ClutterActor *license_actor; /* Contains the license info */
162   gchar *license_text; /* Extra license text */
163
164   ClutterActor *scale_actor;
165   gboolean show_scale;
166   ChamplainUnit scale_unit;
167   guint max_scale_width;
168
169   ChamplainState state; /* View's global state */
170
171   /* champlain_view_go_to's context, kept for stop_go_to */
172   GoToContext *goto_context;
173
174   /* notify_polygon_cb's context, kept for idle cb */
175   guint polygon_redraw_id;
176
177   /* Lines and shapes */
178   GList *polygons;
179   ClutterActor *polygon_layer;  /* Contains the polygons */
180
181 };
182
183 G_DEFINE_TYPE (ChamplainView, champlain_view, CLUTTER_TYPE_GROUP);
184
185 static gdouble viewport_get_current_longitude (ChamplainViewPrivate *priv);
186 static gdouble viewport_get_current_latitude (ChamplainViewPrivate *priv);
187 static gdouble viewport_get_longitude_at (ChamplainViewPrivate *priv, gint x);
188 static gdouble viewport_get_latitude_at (ChamplainViewPrivate *priv, gint y);
189 static gboolean scroll_event (ClutterActor *actor, ClutterScrollEvent *event,
190     ChamplainView *view);
191 static void marker_reposition_cb (ChamplainMarker *marker, ChamplainView *view);
192 static void layer_reposition_cb (ClutterActor *layer, ChamplainView *view);
193 static gboolean marker_reposition (gpointer data);
194 static void create_initial_map (ChamplainView *view);
195 static void resize_viewport (ChamplainView *view);
196 static void champlain_view_get_property (GObject *object, guint prop_id,
197     GValue *value, GParamSpec *pspec);
198 static void champlain_view_set_property (GObject *object, guint prop_id,
199     const GValue *value, GParamSpec *pspec);
200 static void champlain_view_dispose (GObject *object);
201 static void champlain_view_class_init (ChamplainViewClass *champlainViewClass);
202 static void champlain_view_init (ChamplainView *view);
203 static void viewport_pos_changed_cb (GObject *gobject, GParamSpec *arg1,
204     ChamplainView *view);
205 static void notify_marker_reposition_cb (ChamplainMarker *marker,
206     GParamSpec *arg1, ChamplainView *view);
207 static void layer_add_marker_cb (ClutterGroup *layer, ChamplainMarker *marker,
208     ChamplainView *view);
209 static void connect_marker_notify_cb (ChamplainMarker *marker,
210     ChamplainView *view);
211 static gboolean finger_scroll_button_press_cb (ClutterActor *actor,
212     ClutterButtonEvent *event, ChamplainView *view);
213 static void update_license (ChamplainView *view);
214 static void license_set_position (ChamplainView *view);
215 static void view_load_visible_tiles (ChamplainView *view);
216 static void view_position_tile (ChamplainView* view, ChamplainTile* tile);
217 static void view_tiles_reposition (ChamplainView* view);
218 static void view_update_state (ChamplainView *view);
219 static void view_update_anchor (ChamplainView *view, gint x, gint y);
220 static gboolean view_set_zoom_level_at (ChamplainView *view,
221     gint zoom_level,
222     gint x,
223     gint y);
224 static void tile_state_notify (GObject *gobject,
225     GParamSpec *pspec,
226     gpointer data);
227 static void view_update_polygons (ChamplainView *view);
228 static gboolean finger_scroll_key_press_cb (ClutterActor *actor,
229     ClutterKeyEvent *event,
230     ChamplainView *view);
231 static void champlain_view_go_to_with_duration (ChamplainView *view,
232     gdouble latitude,
233     gdouble longitude,
234     guint duration);
235
236 #define SCALE_HEIGHT  20
237 #define SCALE_PADDING 10
238 #define SCALE_INSIDE_PADDING 10
239 #define SCALE_LINE_WIDTH 2
240
241 static gdouble
242 viewport_get_longitude_at (ChamplainViewPrivate *priv, gint x)
243 {
244   if (!priv->map_source)
245     return 0.0;
246
247   return champlain_map_source_get_longitude (priv->map_source,
248       priv->zoom_level, x);
249 }
250
251 static gdouble
252 viewport_get_current_longitude (ChamplainViewPrivate *priv)
253 {
254   if (!priv->map)
255     return 0.0;
256
257   return viewport_get_longitude_at (priv, priv->anchor.x +
258       priv->viewport_size.x + priv->viewport_size.width / 2.0);
259 }
260
261 static gdouble
262 viewport_get_latitude_at (ChamplainViewPrivate *priv, gint y)
263 {
264   if (!priv->map_source)
265     return 0.0;
266
267   return champlain_map_source_get_latitude (priv->map_source,
268       priv->zoom_level, y);
269 }
270
271 static gdouble
272 viewport_get_current_latitude (ChamplainViewPrivate *priv)
273 {
274   if (!priv->map)
275     return 0.0;
276
277   return viewport_get_latitude_at (priv,
278       priv->anchor.y + priv->viewport_size.y +
279       priv->viewport_size.height / 2.0);
280 }
281
282 static gboolean
283 scroll_event (ClutterActor *actor,
284     ClutterScrollEvent *event,
285     ChamplainView *view)
286 {
287   ChamplainViewPrivate *priv = view->priv;
288
289   gint zoom_level = priv->zoom_level;
290
291   if (event->direction == CLUTTER_SCROLL_UP)
292     zoom_level = priv->zoom_level + 1;
293   else if (event->direction == CLUTTER_SCROLL_DOWN)
294     zoom_level = priv->zoom_level - 1;
295
296   return view_set_zoom_level_at (view, zoom_level, event->x, event->y);
297 }
298
299 static void
300 marker_reposition_cb (ChamplainMarker *marker,
301     ChamplainView *view)
302 {
303   ChamplainViewPrivate *priv = view->priv;
304   ChamplainBaseMarkerPrivate *marker_priv = CHAMPLAIN_BASE_MARKER(marker)->priv;
305
306   gint x, y;
307
308   if (priv->map)
309     {
310       x = champlain_map_source_get_x (priv->map_source, priv->zoom_level, marker_priv->lon);
311       y = champlain_map_source_get_y (priv->map_source, priv->zoom_level, marker_priv->lat);
312
313       clutter_actor_set_position (CLUTTER_ACTOR (marker),
314         x - priv->anchor.x,
315         y - priv->anchor.y);
316     }
317 }
318
319 static void
320 notify_marker_reposition_cb (ChamplainMarker *marker,
321     GParamSpec *arg1,
322     ChamplainView *view)
323 {
324   marker_reposition_cb (marker, view);
325 }
326
327 static void
328 layer_add_marker_cb (ClutterGroup *layer,
329     ChamplainMarker *marker,
330     ChamplainView *view)
331 {
332   g_signal_connect (marker, "notify::longitude",
333       G_CALLBACK (notify_marker_reposition_cb), view);
334
335   g_idle_add (marker_reposition, view);
336 }
337
338 static void
339 connect_marker_notify_cb (ChamplainMarker *marker,
340     ChamplainView *view)
341 {
342   g_signal_connect (marker, "notify::longitude",
343       G_CALLBACK (notify_marker_reposition_cb), view);
344 }
345
346 static void
347 layer_reposition_cb (ClutterActor *layer,
348     ChamplainView *view)
349 {
350   clutter_container_foreach (CLUTTER_CONTAINER (layer),
351       CLUTTER_CALLBACK (marker_reposition_cb), view);
352 }
353
354 static gboolean
355 marker_reposition (gpointer data)
356 {
357   ChamplainView *view = CHAMPLAIN_VIEW (data);
358   ChamplainViewPrivate *priv = view->priv;
359   clutter_container_foreach (CLUTTER_CONTAINER (priv->user_layers),
360       CLUTTER_CALLBACK (layer_reposition_cb), view);
361   return FALSE;
362 }
363
364 static void
365 create_initial_map (ChamplainView *view)
366 {
367   ChamplainViewPrivate *priv = view->priv;
368   ClutterActor *group;
369
370   priv->map = map_new ();
371   map_load_level (priv->map, priv->map_source, priv->zoom_level);
372   group = champlain_zoom_level_get_actor (priv->map->current_level);
373   clutter_container_add_actor (CLUTTER_CONTAINER (priv->map_layer), group);
374
375   g_idle_add (marker_reposition, view);
376   view_tiles_reposition (view);
377   update_license (view);
378
379   g_object_notify (G_OBJECT (view), "zoom-level");
380   g_object_notify (G_OBJECT (view), "map-source");
381 }
382
383 static void
384 license_set_position (ChamplainView *view)
385 {
386   ChamplainViewPrivate *priv = view->priv;
387
388   if (!priv->license_actor)
389     return;
390
391   clutter_actor_set_position (priv->license_actor,
392       priv->viewport_size.width - PADDING,
393       priv->viewport_size.height - PADDING);
394 }
395
396 static void
397 draw_polygon (ChamplainView *view, ChamplainPolygon *polygon)
398 {
399   cairo_t *cr;
400   ChamplainViewPrivate *priv = view->priv;
401
402   if (polygon->priv->visible == FALSE)
403     return;
404
405   cr = clutter_cairo_texture_create (CLUTTER_CAIRO_TEXTURE (polygon->priv->actor));
406
407   /* Clear the drawing area */
408   cairo_set_operator (cr, CAIRO_OPERATOR_CLEAR);
409   cairo_rectangle (cr, 0, 0,
410       view->priv->viewport_size.width,
411       view->priv->viewport_size.height);
412   cairo_fill (cr);
413
414   cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
415   GList *list = g_list_first (polygon->priv->points);
416   while (list != NULL)
417     {
418       ChamplainPoint *point = (ChamplainPoint*) list->data;
419       gfloat x, y;
420
421       x = champlain_map_source_get_x (priv->map_source, priv->zoom_level,
422           point->lon);
423       y = champlain_map_source_get_y (priv->map_source, priv->zoom_level,
424           point->lat);
425
426       x -= priv->viewport_size.x + priv->anchor.x;
427       y -= priv->viewport_size.y + priv->anchor.y;
428
429       cairo_line_to (cr, x, y);
430
431       if (polygon->priv->mark_points)
432         cairo_arc (cr, x, y, polygon->priv->stroke_width, 0, 2 * M_PI);
433
434       list = list->next;
435     }
436
437   if (polygon->priv->closed_path)
438     cairo_close_path (cr);
439
440   cairo_set_source_rgba (cr,
441       polygon->priv->fill_color->red / 255.0,
442       polygon->priv->fill_color->green / 255.0,
443       polygon->priv->fill_color->blue / 255.0,
444       polygon->priv->fill_color->alpha / 255.0);
445
446   if (polygon->priv->fill)
447     cairo_fill_preserve (cr);
448
449   cairo_set_source_rgba (cr,
450       polygon->priv->stroke_color->red / 255.0,
451       polygon->priv->stroke_color->green / 255.0,
452       polygon->priv->stroke_color->blue / 255.0,
453       polygon->priv->stroke_color->alpha / 255.0);
454
455   cairo_set_line_width (cr, polygon->priv->stroke_width);
456
457   if (polygon->priv->stroke)
458     cairo_stroke (cr);
459
460   cairo_destroy (cr);
461 }
462
463 static gboolean
464 redraw_polygon_on_idle (PolygonRedrawContext *ctx)
465 {
466
467   if (ctx->polygon)
468     draw_polygon (ctx->view, ctx->polygon);
469
470   ctx->view->priv->polygon_redraw_id = 0;
471   // ctx is freed by g_idle_add_full
472   return FALSE;
473 }
474
475 static void
476 notify_polygon_cb (ChamplainPolygon *polygon,
477     GParamSpec *arg1,
478     ChamplainView *view)
479 {
480   PolygonRedrawContext *ctx;
481
482   if (view->priv->polygon_redraw_id != 0)
483     return;
484
485   ctx = g_new0 (PolygonRedrawContext, 1);
486   ctx->view = view;
487   ctx->polygon = polygon;
488   g_object_add_weak_pointer (G_OBJECT (polygon), (gpointer *) &ctx->polygon);
489
490   view->priv->polygon_redraw_id = g_idle_add_full (G_PRIORITY_DEFAULT_IDLE,
491       (GSourceFunc) redraw_polygon_on_idle, ctx, g_free);
492 }
493
494 static void
495 resize_viewport (ChamplainView *view)
496 {
497   gdouble lower, upper;
498   TidyAdjustment *hadjust, *vadjust;
499   GList *polygons;
500
501   ChamplainViewPrivate *priv = view->priv;
502
503   if (!priv->map)
504     return;
505
506   clutter_actor_set_size (priv->finger_scroll, priv->viewport_size.width,
507       priv->viewport_size.height);
508
509   tidy_scrollable_get_adjustments (TIDY_SCROLLABLE (priv->viewport), &hadjust,
510       &vadjust);
511
512   if (priv->zoom_level < 8)
513     {
514       lower = -priv->viewport_size.width / 2.0;
515       upper = champlain_zoom_level_get_width (priv->map->current_level) *
516           champlain_map_source_get_tile_size (priv->map_source) -
517           priv->viewport_size.width / 2.0;
518     }
519   else
520     {
521       lower = 0;
522       upper = G_MAXINT16;
523     }
524   g_object_set (hadjust, "lower", lower, "upper", upper,
525       "page-size", 1.0, "step-increment", 1.0, "elastic", TRUE, NULL);
526
527   if (priv->zoom_level < 8)
528     {
529       lower = -priv->viewport_size.height / 2.0;
530       upper = champlain_zoom_level_get_height (priv->map->current_level) *
531           champlain_map_source_get_tile_size (priv->map_source) -
532           priv->viewport_size.height / 2.0;
533     }
534   else
535     {
536       lower = 0;
537       upper = G_MAXINT16;
538     }
539   g_object_set (vadjust, "lower", lower, "upper", upper,
540       "page-size", 1.0, "step-increment", 1.0, "elastic", TRUE, NULL);
541
542   /* Resize polygon actors */
543   if (priv->viewport_size.width == 0 ||
544       priv->viewport_size.height == 0)
545     return;
546
547   polygons = priv->polygons;
548   while (polygons != NULL)
549     {
550       ChamplainPolygon *polygon;
551
552       polygon = CHAMPLAIN_POLYGON (polygons->data);
553
554       if (polygon->priv->actor != NULL)
555         {
556           g_object_unref (polygon->priv->actor);
557           clutter_container_remove_actor (CLUTTER_CONTAINER (view->priv->polygon_layer),
558               polygon->priv->actor);
559         }
560
561       polygon->priv->actor = g_object_ref (clutter_cairo_texture_new (
562           view->priv->viewport_size.width,
563           view->priv->viewport_size.height));
564       g_object_set (G_OBJECT (polygon->priv->actor), "visible",
565           polygon->priv->visible, NULL);
566       clutter_container_add_actor (CLUTTER_CONTAINER (view->priv->polygon_layer),
567           polygon->priv->actor);
568       clutter_actor_set_position (polygon->priv->actor, 0, 0);
569       draw_polygon (view, polygon);
570       polygons = polygons->next;
571     }
572 }
573
574 static void
575 champlain_view_get_property (GObject *object,
576     guint prop_id,
577     GValue *value,
578     GParamSpec *pspec)
579 {
580   ChamplainView *view = CHAMPLAIN_VIEW (object);
581   ChamplainViewPrivate *priv = view->priv;
582
583   switch (prop_id)
584     {
585       case PROP_LONGITUDE:
586         g_value_set_double (value,
587             CLAMP (priv->longitude, CHAMPLAIN_MIN_LONG, CHAMPLAIN_MAX_LONG));
588         break;
589       case PROP_LATITUDE:
590         g_value_set_double (value, 
591             CLAMP (priv->latitude, CHAMPLAIN_MIN_LAT, CHAMPLAIN_MAX_LAT));
592         break;
593       case PROP_ZOOM_LEVEL:
594         g_value_set_int (value, priv->zoom_level);
595         break;
596       case PROP_MIN_ZOOM_LEVEL:
597         g_value_set_int (value, priv->min_zoom_level);
598         break;
599       case PROP_MAX_ZOOM_LEVEL:
600         g_value_set_int (value, priv->max_zoom_level);
601         break;
602       case PROP_MAP_SOURCE:
603         g_value_set_object (value, priv->map_source);
604         break;
605       case PROP_SCROLL_MODE:
606         g_value_set_enum (value, priv->scroll_mode);
607         break;
608       case PROP_SHOW_SCALE:
609         g_value_set_boolean (value, priv->show_scale);
610         break;
611       case PROP_MAX_SCALE_WIDTH:
612         g_value_set_uint (value, priv->max_scale_width);
613         break;
614       case PROP_SCALE_UNIT:
615         g_value_set_enum (value, priv->scale_unit);
616         break;
617       case PROP_DECEL_RATE:
618         {
619           gdouble decel = 0.0;
620           g_object_get (priv->finger_scroll, "decel-rate", &decel, NULL);
621           g_value_set_double (value, decel);
622           break;
623         }
624       case PROP_KEEP_CENTER_ON_RESIZE:
625         g_value_set_boolean (value, priv->keep_center_on_resize);
626         break;
627       case PROP_SHOW_LICENSE:
628         g_value_set_boolean (value, priv->show_license);
629         break;
630       case PROP_LICENSE_EXTRA:
631         g_value_set_string (value, priv->license_text);
632         break;
633       case PROP_ZOOM_ON_DOUBLE_CLICK:
634         g_value_set_boolean (value, priv->zoom_on_double_click);
635         break;
636       case PROP_STATE:
637         g_value_set_enum (value, priv->state);
638         break;
639       default:
640         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
641     }
642 }
643
644 static void
645 champlain_view_set_property (GObject *object,
646     guint prop_id,
647     const GValue *value,
648     GParamSpec *pspec)
649 {
650   ChamplainView *view = CHAMPLAIN_VIEW (object);
651   ChamplainViewPrivate *priv = view->priv;
652
653   switch (prop_id)
654   {
655     case PROP_LONGITUDE:
656       champlain_view_center_on (view, priv->latitude,
657           g_value_get_double (value));
658       break;
659     case PROP_LATITUDE:
660       champlain_view_center_on (view, g_value_get_double (value),
661           priv->longitude);
662       break;
663     case PROP_ZOOM_LEVEL:
664       champlain_view_set_zoom_level (view, g_value_get_int (value));
665       break;
666     case PROP_MIN_ZOOM_LEVEL:
667       champlain_view_set_min_zoom_level (view, g_value_get_int (value));
668       break;
669     case PROP_MAX_ZOOM_LEVEL:
670       champlain_view_set_max_zoom_level (view, g_value_get_int (value));
671       break;
672     case PROP_MAP_SOURCE:
673       champlain_view_set_map_source (view, g_value_get_object (value));
674       break;
675     case PROP_SCROLL_MODE:
676       champlain_view_set_scroll_mode (view, g_value_get_enum (value));
677       break;
678     case PROP_SHOW_SCALE:
679       champlain_view_set_show_scale (view, g_value_get_boolean (value));
680       break;
681     case PROP_MAX_SCALE_WIDTH:
682       champlain_view_set_max_scale_width (view, g_value_get_uint (value));
683       break;
684     case PROP_SCALE_UNIT:
685       champlain_view_set_scale_unit (view, g_value_get_enum (value));
686       break;
687     case PROP_DECEL_RATE:
688       champlain_view_set_decel_rate (view, g_value_get_double (value));
689       break;
690     case PROP_KEEP_CENTER_ON_RESIZE:
691       champlain_view_set_keep_center_on_resize (view, g_value_get_boolean (value));
692       break;
693     case PROP_SHOW_LICENSE:
694       champlain_view_set_show_license (view, g_value_get_boolean (value));
695       break;
696     case PROP_LICENSE_EXTRA:
697       champlain_view_set_license_text (view, g_value_get_string (value));
698       break;
699     case PROP_ZOOM_ON_DOUBLE_CLICK:
700       champlain_view_set_zoom_on_double_click (view, g_value_get_boolean (value));
701       break;
702     default:
703       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
704   }
705 }
706
707 static void
708 champlain_view_dispose (GObject *object)
709 {
710   ChamplainView *view = CHAMPLAIN_VIEW (object);
711   ChamplainViewPrivate *priv = view->priv;
712   GList *polygons;
713
714   if (priv->factory != NULL)
715     {
716       g_object_unref (priv->factory);
717       priv->factory = NULL;
718     }
719
720   if (priv->map_source != NULL)
721     {
722       g_object_unref (priv->map_source);
723       priv->map_source = NULL;
724     }
725
726   if (priv->license_actor != NULL)
727     {
728       g_object_unref (priv->license_actor);
729       priv->license_actor = NULL;
730     }
731
732   if (priv->finger_scroll != NULL)
733     {
734       tidy_finger_scroll_stop (TIDY_FINGER_SCROLL (priv->finger_scroll));
735       g_object_unref (priv->finger_scroll);
736       priv->finger_scroll = NULL;
737     }
738
739   if (priv->viewport != NULL)
740     {
741       tidy_viewport_stop (TIDY_VIEWPORT (priv->viewport));
742       g_object_unref (priv->viewport);
743       priv->viewport = NULL;
744     }
745
746   if (priv->map_layer != NULL)
747     {
748       g_object_unref (priv->map_layer);
749       priv->map_layer = NULL;
750     }
751
752   if (priv->user_layers != NULL)
753     {
754       g_object_unref (priv->user_layers);
755       priv->user_layers = NULL;
756     }
757
758   if (priv->stage != NULL)
759     {
760       g_object_unref (priv->stage);
761       priv->stage = NULL;
762     }
763
764   if (priv->map != NULL)
765     {
766       map_free (priv->map);
767       priv->map = NULL;
768     }
769
770   polygons = priv->polygons;
771   while (polygons != NULL)
772     {
773       g_object_unref (G_OBJECT (polygons->data));
774       polygons = polygons->next;
775     }
776   g_list_free (priv->polygons);
777   priv->polygons = NULL;
778
779   if (priv->goto_context != NULL)
780     champlain_view_stop_go_to (view);
781
782   G_OBJECT_CLASS (champlain_view_parent_class)->dispose (object);
783 }
784
785 static void
786 champlain_view_class_init (ChamplainViewClass *champlainViewClass)
787 {
788   g_type_class_add_private (champlainViewClass, sizeof (ChamplainViewPrivate));
789
790   GObjectClass *object_class = G_OBJECT_CLASS (champlainViewClass);
791   object_class->dispose = champlain_view_dispose;
792   object_class->get_property = champlain_view_get_property;
793   object_class->set_property = champlain_view_set_property;
794
795   /**
796   * ChamplainView:longitude:
797   *
798   * The longitude coordonate of the map
799   *
800   * Since: 0.1
801   */
802   g_object_class_install_property (object_class,
803       PROP_LONGITUDE,
804       g_param_spec_double ("longitude",
805          "Longitude",
806          "The longitude coordonate of the map",
807          -180.0f, 180.0f, 0.0f, CHAMPLAIN_PARAM_READWRITE));
808
809   /**
810   * ChamplainView:latitude:
811   *
812   * The latitude coordonate of the map
813   *
814   * Since: 0.1
815   */
816   g_object_class_install_property (object_class,
817       PROP_LATITUDE,
818       g_param_spec_double ("latitude",
819            "Latitude",
820            "The latitude coordonate of the map",
821            -90.0f, 90.0f, 0.0f, CHAMPLAIN_PARAM_READWRITE));
822
823   /**
824   * ChamplainView:zoom-level:
825   *
826   * The level of zoom of the content.
827   *
828   * Since: 0.1
829   */
830   g_object_class_install_property (object_class,
831       PROP_ZOOM_LEVEL,
832       g_param_spec_int ("zoom-level",
833            "Zoom level",
834            "The level of zoom of the map",
835            0, 20, 3, CHAMPLAIN_PARAM_READWRITE));
836
837   /**
838   * ChamplainView:min-zoom-level:
839   *
840   * The lowest allowed level of zoom of the content.
841   *
842   * Since: 0.4
843   */
844   g_object_class_install_property (object_class,
845       PROP_MIN_ZOOM_LEVEL,
846       g_param_spec_int ("min-zoom-level",
847            "Min zoom level",
848            "The lowest allowed level of zoom",
849            0, 20, 0, CHAMPLAIN_PARAM_READWRITE));
850
851   /**
852   * ChamplainView:max-zoom-level:
853   *
854   * The highest allowed level of zoom of the content.
855   *
856   * Since: 0.4
857   */
858   g_object_class_install_property (object_class,
859       PROP_MAX_ZOOM_LEVEL,
860       g_param_spec_int ("max-zoom-level",
861            "Max zoom level",
862            "The highest allowed level of zoom",
863            0, 20, 20, CHAMPLAIN_PARAM_READWRITE));
864
865   /**
866   * ChamplainView:map-source:
867   *
868   * The #ChamplainMapSource being displayed
869   *
870   * Since: 0.2
871   */
872   g_object_class_install_property (object_class,
873       PROP_MAP_SOURCE,
874       g_param_spec_object ("map-source",
875            "Map source",
876            "The map source being displayed",
877            CHAMPLAIN_TYPE_MAP_SOURCE,
878            CHAMPLAIN_PARAM_READWRITE));
879
880   /**
881   * ChamplainView:scroll-mode:
882   *
883   * Determines the way the view reacts to scroll events.
884   *
885   * Since: 0.4
886   */
887   g_object_class_install_property (object_class,
888       PROP_SCROLL_MODE,
889       g_param_spec_enum ("scroll-mode",
890            "Scroll Mode",
891            "Determines the way the view reacts to scroll events.",
892            CHAMPLAIN_TYPE_SCROLL_MODE,
893            CHAMPLAIN_SCROLL_MODE_KINETIC,
894            CHAMPLAIN_PARAM_READWRITE));
895
896   /**
897   * ChamplainView:decel-rate:
898   *
899   * The deceleration rate for the kinetic mode. The default value is 1.1.
900   *
901   * Since: 0.2
902   */
903   g_object_class_install_property (object_class,
904        PROP_DECEL_RATE,
905        g_param_spec_double ("decel-rate",
906             "Deceleration rate",
907             "Rate at which the view will decelerate in kinetic mode.",
908             1.0001, 2.0, 1.1, CHAMPLAIN_PARAM_READWRITE));
909
910   /**
911   * ChamplainView:keep-center-on-resize:
912   *
913   * Keep the current centered position when resizing the view.
914   *
915   * Since: 0.2.7
916   */
917   g_object_class_install_property (object_class,
918        PROP_KEEP_CENTER_ON_RESIZE,
919        g_param_spec_boolean ("keep-center-on-resize",
920            "Keep center on resize",
921            "Keep the current centered position "
922            "upon resizing",
923            TRUE, CHAMPLAIN_PARAM_READWRITE));
924
925   /**
926   * ChamplainView:show-license:
927   *
928   * Show the license on the map view.  The license information should always be
929   * available in a way or another in your application.  You can have it in
930   * About, or on the map.
931   *
932   * Since: 0.2.8
933   */
934   g_object_class_install_property (object_class,
935        PROP_SHOW_LICENSE,
936        g_param_spec_boolean ("show-license",
937            "Show the map data license",
938            "Show the map data license on the map view",
939            TRUE, CHAMPLAIN_PARAM_READWRITE));
940
941   /**
942   * ChamplainView:license-text:
943   *
944   * Sets additional text to be displayed in the license area.  The map's
945   * license will be added below it. Your text can have multiple line, just use
946   * "\n" in between.
947   *
948   * Since: 0.4.3
949   */
950   g_object_class_install_property (object_class,
951        PROP_LICENSE_EXTRA,
952        g_param_spec_string ("license-text",
953            "Additional license",
954            "Additional license text",
955            "",
956            CHAMPLAIN_PARAM_READWRITE));
957
958   /**
959   * ChamplainView:zoom-on-double-click:
960   *
961   * Should the view zoom in and recenter when the user double click on the map.
962   *
963   * Since: 0.4
964   */
965   g_object_class_install_property (object_class,
966        PROP_ZOOM_ON_DOUBLE_CLICK,
967        g_param_spec_boolean ("zoom-on-double-click",
968            "Zoom in on double click",
969            "Zoom in and recenter on double click on the map",
970            TRUE, CHAMPLAIN_PARAM_READWRITE));
971
972   /**
973   * ChamplainView:state
974   *
975   * The view's global state. Useful to inform using if the view is busy loading
976   * tiles or not.
977   *
978   * Since: 0.4
979   */
980   g_object_class_install_property (object_class,
981        PROP_STATE,
982        g_param_spec_enum ("state",
983            "View's state",
984            "View's global state",
985            CHAMPLAIN_TYPE_STATE,
986            CHAMPLAIN_STATE_INIT,
987            G_PARAM_READABLE));
988
989   /**
990   * ChamplainView:display-scale:
991   *
992   * Display the map scale.
993   *
994   * Since: 0.4.3
995   */
996   g_object_class_install_property (object_class,
997        PROP_SHOW_SCALE,
998        g_param_spec_boolean ("show-scale",
999            "Show the map scale",
1000            "Show the map scale "
1001            "on the screen",
1002            FALSE,
1003            G_PARAM_READWRITE));
1004
1005   /**
1006   * ChamplainView:max-scale-width:
1007   *
1008   * The size of the map scale on screen in pixels.
1009   *
1010   * Since: 0.4.3
1011   */
1012   g_object_class_install_property (object_class,
1013        PROP_MAX_SCALE_WIDTH,
1014        g_param_spec_uint ("max-scale-width",
1015            "The width of the scale",
1016            "The max width of the scale"
1017            "on screen",
1018            1,
1019            2000,
1020            100,
1021            G_PARAM_READWRITE));
1022
1023   /**
1024   * ChamplainView:scale-unit:
1025   *
1026   * The scale's units.
1027   *
1028   * Since: 0.4.3
1029   */
1030   g_object_class_install_property (object_class,
1031        PROP_SCALE_UNIT,
1032        g_param_spec_enum ("scale-unit",
1033            "The scale's unit",
1034            "The map scale's unit",
1035            CHAMPLAIN_TYPE_UNIT,
1036            CHAMPLAIN_UNIT_KM,
1037            G_PARAM_READWRITE));
1038
1039   /**
1040   * ChamplainView::animation-completed:
1041   *
1042   * The ::animation-completed signal is emitted when any animation in the view
1043   * ends.  This is a detailed signal.  For example, if you want to be signaled
1044   * only for go-to animation, you should connect to
1045   * "animation-completed::go-to".
1046   *
1047   * Since: 0.4
1048   */
1049   signals[ANIMATION_COMPLETED] =
1050       g_signal_new ("animation-completed", G_OBJECT_CLASS_TYPE (object_class),
1051           G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, 0, NULL, NULL,
1052           g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 0);
1053
1054 }
1055
1056 static gboolean
1057 button_release_cb (ClutterActor *actor,
1058     ClutterEvent *event,
1059     ChamplainView *view)
1060 {
1061   GList *children = NULL;
1062   gboolean found = FALSE;
1063   ChamplainViewPrivate *priv = view->priv;
1064
1065   children = clutter_container_get_children (CLUTTER_CONTAINER (priv->user_layers));
1066   for (;children != NULL; children = g_list_next (children))
1067     {
1068       if (CHAMPLAIN_IS_SELECTION_LAYER (children->data))
1069         {
1070           champlain_selection_layer_unselect_all (CHAMPLAIN_SELECTION_LAYER (children->data));
1071           found = TRUE;
1072         }
1073     }
1074
1075   g_list_free (children);
1076
1077   return found;
1078 }
1079
1080 static void
1081 update_scale (ChamplainView *view)
1082 {
1083   gboolean is_small_unit = TRUE;  /* indicates if using meters */
1084   ClutterActor *text, *line;
1085   gfloat width;
1086   ChamplainViewPrivate *priv = view->priv;
1087   gfloat m_per_pixel;
1088   gfloat scale_width = priv->max_scale_width;
1089   gchar *label;
1090   cairo_t *cr;
1091   gfloat base;
1092   gfloat factor;
1093   gboolean final_unit = FALSE;
1094
1095   if (!priv || !priv->map || !priv->map->current_level)
1096     return;
1097
1098   if (priv->show_scale)
1099     {
1100       clutter_actor_show (priv->scale_actor);
1101     }
1102   else
1103     {
1104       clutter_actor_hide (priv->scale_actor);
1105       return;
1106     }
1107
1108   m_per_pixel = champlain_map_source_get_meters_per_pixel (priv->map_source,
1109       priv->zoom_level, priv->latitude, priv->longitude);
1110
1111   if (priv->scale_unit == CHAMPLAIN_UNIT_MILES)
1112     m_per_pixel *= 3.28; /* m_per_pixel is now in ft */
1113
1114   /* This loop will find the pretty value to display on the scale.
1115    * It will be run once for metric units, and twice for imperials
1116    * so that both feet and miles have pretty numbers.
1117    */
1118   do
1119     {
1120       /* Keep the previous power of 10 */
1121       base = floor (log (m_per_pixel * scale_width) / log (10));
1122       base = pow (10, base);
1123
1124       /* How many times can it be fitted in our max scale width */
1125       scale_width /= m_per_pixel * scale_width / base;
1126       factor = floor (priv->max_scale_width / scale_width);
1127       base *= factor;
1128       scale_width *= factor;
1129
1130       if (priv->scale_unit == CHAMPLAIN_UNIT_KM)
1131         {
1132           if (base / 1000 >= 1)
1133             {
1134               base /= 1000; /* base is now in km */
1135               is_small_unit = FALSE;
1136             }
1137           final_unit = TRUE; /* Don't need to recompute */
1138         }
1139       else if (priv->scale_unit == CHAMPLAIN_UNIT_MILES)
1140         {
1141           if (is_small_unit && base / 5280 >= 1)
1142             {
1143               m_per_pixel /= 5280; /* m_per_pixel is now in miles */
1144               is_small_unit = FALSE;
1145               /* we need to recompute the base because 1000 ft != 1 mile */
1146             }
1147           else
1148             final_unit = TRUE;
1149         }
1150     }
1151   while (!final_unit);
1152
1153   text = clutter_container_find_child_by_name (CLUTTER_CONTAINER (priv->scale_actor), "scale-far-label");
1154   label = g_strdup_printf ("%g", base);
1155   /* Get only digits width for centering */
1156   clutter_text_set_text (CLUTTER_TEXT (text), label);
1157   g_free (label);
1158   clutter_actor_get_size (text, &width, NULL);
1159   /* actual label with unit */
1160   label = g_strdup_printf ("%g %s", base,
1161       priv->scale_unit == CHAMPLAIN_UNIT_KM ?
1162       (is_small_unit ? "m": "km"):
1163       (is_small_unit ? "ft": "miles")
1164       );
1165   clutter_text_set_text (CLUTTER_TEXT (text), label);
1166   g_free (label);
1167   clutter_actor_set_position (text, (scale_width - width / 2) + SCALE_INSIDE_PADDING, - SCALE_INSIDE_PADDING);
1168
1169   text = clutter_container_find_child_by_name (CLUTTER_CONTAINER (priv->scale_actor), "scale-mid-label");
1170   label = g_strdup_printf ("%g", base / 2.0);
1171   clutter_text_set_text (CLUTTER_TEXT (text), label);
1172   clutter_actor_get_size (text, &width, NULL);
1173   clutter_actor_set_position (text, (scale_width - width) / 2 + SCALE_INSIDE_PADDING, - SCALE_INSIDE_PADDING);
1174   g_free (label);
1175
1176   /* Draw the line */
1177   line = clutter_container_find_child_by_name (CLUTTER_CONTAINER (priv->scale_actor), "scale-line");
1178   clutter_cairo_texture_clear (CLUTTER_CAIRO_TEXTURE (line));
1179   cr = clutter_cairo_texture_create (CLUTTER_CAIRO_TEXTURE (line));
1180
1181   cairo_set_source_rgb (cr, 0, 0, 0);
1182   cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND);
1183   cairo_set_line_width (cr, SCALE_LINE_WIDTH);
1184
1185   /* First tick */
1186   cairo_move_to (cr, SCALE_INSIDE_PADDING, SCALE_HEIGHT / 4);
1187   cairo_line_to (cr, SCALE_INSIDE_PADDING, SCALE_HEIGHT / 2 );
1188   cairo_stroke (cr);
1189
1190   /* Line */
1191   cairo_move_to (cr, SCALE_INSIDE_PADDING, SCALE_HEIGHT / 2);
1192   cairo_line_to (cr, scale_width + SCALE_INSIDE_PADDING, SCALE_HEIGHT / 2);
1193   cairo_stroke (cr);
1194
1195   /* Middle tick */
1196   cairo_move_to (cr, scale_width / 2 + SCALE_INSIDE_PADDING, SCALE_HEIGHT / 4);
1197   cairo_line_to (cr, scale_width / 2 + SCALE_INSIDE_PADDING, SCALE_HEIGHT / 2);
1198   cairo_stroke (cr);
1199
1200   /* Last tick */
1201   cairo_move_to (cr, scale_width + SCALE_INSIDE_PADDING, SCALE_HEIGHT / 4);
1202   cairo_line_to (cr, scale_width + SCALE_INSIDE_PADDING, SCALE_HEIGHT / 2);
1203   cairo_stroke (cr);
1204
1205   cairo_destroy (cr);
1206 }
1207
1208 static void
1209 create_scale (ChamplainView *view)
1210 {
1211   ClutterActor *scale, *text;
1212   gfloat width;
1213   ChamplainViewPrivate *priv = view->priv;
1214
1215   if (priv->scale_actor)
1216     {
1217       g_object_unref (priv->scale_actor);
1218       clutter_container_remove_actor (CLUTTER_CONTAINER (priv->stage), priv->scale_actor);
1219     }
1220
1221   priv->scale_actor = g_object_ref (clutter_group_new());
1222   clutter_container_add_actor (CLUTTER_CONTAINER (priv->stage), priv->scale_actor);
1223
1224   scale = clutter_cairo_texture_new (priv->max_scale_width + 2 * SCALE_INSIDE_PADDING, SCALE_HEIGHT + 2 * SCALE_INSIDE_PADDING);
1225   clutter_actor_set_name (scale, "scale-line");
1226
1227   text = clutter_text_new_with_text ("Sans 9", "X km");
1228   clutter_actor_set_name (text, "scale-far-label");
1229   clutter_container_add_actor (CLUTTER_CONTAINER (priv->scale_actor), text);
1230
1231   text = clutter_text_new_with_text ("Sans 9", "X km");
1232   clutter_actor_set_name (text, "scale-mid-label");
1233   clutter_container_add_actor (CLUTTER_CONTAINER (priv->scale_actor), text);
1234
1235   text = clutter_text_new_with_text ("Sans 9", "0");
1236   clutter_container_add_actor (CLUTTER_CONTAINER (priv->scale_actor), text);
1237   clutter_actor_get_size (text, &width, NULL);
1238   clutter_actor_set_position (text, SCALE_INSIDE_PADDING - width / 2, - SCALE_INSIDE_PADDING);
1239
1240   clutter_container_add_actor (CLUTTER_CONTAINER (priv->scale_actor), scale);
1241   clutter_actor_set_position (priv->scale_actor, SCALE_PADDING - SCALE_INSIDE_PADDING,
1242     priv->viewport_size.height - SCALE_HEIGHT - SCALE_PADDING - SCALE_INSIDE_PADDING);
1243
1244   clutter_actor_set_opacity (priv->scale_actor, 200);
1245 }
1246
1247 static void
1248 champlain_view_init (ChamplainView *view)
1249 {
1250   ChamplainViewPrivate *priv = GET_PRIVATE (view);
1251
1252   champlain_debug_set_flags (g_getenv ("CHAMPLAIN_DEBUG"));
1253
1254   view->priv = priv;
1255
1256   priv->factory = champlain_map_source_factory_dup_default ();
1257   priv->map_source = champlain_map_source_factory_create (priv->factory, CHAMPLAIN_MAP_SOURCE_OSM_MAPNIK);
1258   priv->zoom_level = 0;
1259   priv->min_zoom_level = champlain_map_source_get_min_zoom_level (priv->map_source);
1260   priv->max_zoom_level = champlain_map_source_get_max_zoom_level (priv->map_source);
1261   priv->keep_center_on_resize = TRUE;
1262   priv->zoom_on_double_click = TRUE;
1263   priv->show_license = TRUE;
1264   priv->license_actor = NULL;
1265   priv->license_text = NULL;
1266   priv->stage = g_object_ref (clutter_group_new ());
1267   priv->scroll_mode = CHAMPLAIN_SCROLL_MODE_PUSH;
1268   priv->viewport_size.x = 0;
1269   priv->viewport_size.y = 0;
1270   priv->viewport_size.width = 0;
1271   priv->viewport_size.height = 0;
1272   priv->anchor.x = 0;
1273   priv->anchor.y = 0;
1274   priv->anchor_zoom_level = 0;
1275   priv->state = CHAMPLAIN_STATE_INIT;
1276   priv->latitude = 0.0f;
1277   priv->longitude = 0.0f;
1278   priv->goto_context = NULL;
1279   priv->map = NULL;
1280   priv->polygon_redraw_id = 0;
1281   priv->show_scale = FALSE;
1282   priv->scale_unit = CHAMPLAIN_UNIT_KM;
1283   priv->max_scale_width = 100;
1284
1285   /* Setup viewport */
1286   priv->viewport = g_object_ref (tidy_viewport_new ());
1287   g_object_set (G_OBJECT (priv->viewport), "sync-adjustments", FALSE, NULL);
1288
1289   g_signal_connect (priv->viewport, "notify::x-origin",
1290       G_CALLBACK (viewport_pos_changed_cb), view);
1291   g_signal_connect (priv->viewport, "notify::y-origin",
1292       G_CALLBACK (viewport_pos_changed_cb), view);
1293
1294   /* Setup scale */
1295   create_scale (view);
1296
1297   /* Setup finger scroll */
1298   priv->finger_scroll = g_object_ref (tidy_finger_scroll_new (priv->scroll_mode));
1299
1300   g_signal_connect (priv->finger_scroll, "scroll-event",
1301       G_CALLBACK (scroll_event), view);
1302
1303   clutter_container_add_actor (CLUTTER_CONTAINER (priv->finger_scroll),
1304       priv->viewport);
1305   clutter_container_add_actor (CLUTTER_CONTAINER (priv->stage),
1306       priv->finger_scroll);
1307   clutter_container_add_actor (CLUTTER_CONTAINER (view), priv->stage);
1308
1309   /* Map Layer */
1310   priv->map_layer = g_object_ref (clutter_group_new ());
1311   clutter_actor_show (priv->map_layer);
1312   clutter_container_add_actor (CLUTTER_CONTAINER (priv->viewport),
1313       priv->map_layer);
1314
1315   g_signal_connect (priv->finger_scroll, "button-press-event",
1316       G_CALLBACK (finger_scroll_button_press_cb), view);
1317   g_signal_connect_after (priv->finger_scroll, "button-release-event",
1318       G_CALLBACK (button_release_cb), view);
1319
1320   clutter_stage_set_key_focus (CLUTTER_STAGE (clutter_stage_get_default()),
1321       priv->finger_scroll);
1322   g_signal_connect (priv->finger_scroll, "key-press-event",
1323       G_CALLBACK (finger_scroll_key_press_cb), view);
1324
1325   /* Setup user_layers */
1326   priv->user_layers = g_object_ref (clutter_group_new ());
1327   clutter_actor_show (priv->user_layers);
1328   clutter_container_add_actor (CLUTTER_CONTAINER (priv->viewport),
1329       priv->user_layers);
1330   clutter_actor_raise (priv->user_layers, priv->map_layer);
1331
1332   priv->polygons = NULL;
1333
1334   /* Setup polygon layer */
1335   priv->polygon_layer = g_object_ref (clutter_group_new ());
1336   clutter_actor_show (priv->polygon_layer);
1337   clutter_container_add_actor (CLUTTER_CONTAINER (priv->viewport),
1338       priv->polygon_layer);
1339   clutter_actor_raise (priv->polygon_layer, priv->map_layer);
1340
1341   champlain_view_set_size (view, priv->viewport_size.width,
1342       priv->viewport_size.height);
1343
1344   clutter_actor_raise_top (priv->scale_actor);
1345   resize_viewport (view);
1346
1347   priv->state = CHAMPLAIN_STATE_DONE;
1348   g_object_notify (G_OBJECT (view), "state");
1349 }
1350
1351 static void
1352 viewport_pos_changed_cb (GObject *gobject,
1353     GParamSpec *arg1,
1354     ChamplainView *view)
1355 {
1356   ChamplainViewPrivate *priv = view->priv;
1357
1358   ChamplainFloatPoint rect;
1359   ChamplainFloatPoint old_anchor;
1360
1361   tidy_viewport_get_origin (TIDY_VIEWPORT (priv->viewport), &rect.x, &rect.y,
1362       NULL);
1363
1364   if (rect.x == priv->viewport_size.x &&
1365       rect.y == priv->viewport_size.y)
1366       return;
1367
1368   old_anchor.x = priv->anchor.x;
1369   old_anchor.y = priv->anchor.y;
1370
1371   view_update_anchor (view,
1372       rect.x + priv->anchor.x + priv->viewport_size.width / 2.0,
1373       rect.y + priv->anchor.y + priv->viewport_size.height / 2.0);
1374
1375   if (priv->anchor.x - old_anchor.x != 0)
1376     {
1377       ChamplainFloatPoint diff;
1378
1379       diff.x = priv->anchor.x - old_anchor.x;
1380       diff.y = priv->anchor.y - old_anchor.y;
1381
1382       DEBUG("Relocating the viewport by %f, %f", diff.x, diff.y);
1383       tidy_viewport_set_origin (TIDY_VIEWPORT (priv->viewport),
1384           rect.x - diff.x, rect.y - diff.y, 0);
1385       return;
1386     }
1387
1388   priv->viewport_size.x = rect.x;
1389   priv->viewport_size.y = rect.y;
1390
1391   view_load_visible_tiles (view);
1392   view_tiles_reposition (view);
1393   marker_reposition (view);
1394   view_update_polygons (view);
1395   update_scale (view);
1396
1397   priv->longitude = viewport_get_current_longitude (priv);
1398   priv->latitude = viewport_get_current_latitude (priv);
1399
1400   g_object_notify (G_OBJECT (view), "longitude");
1401   g_object_notify (G_OBJECT (view), "latitude");
1402 }
1403
1404 /**
1405  * champlain_view_set_size:
1406  * @view: a #ChamplainView
1407  * @width: the width in pixels
1408  * @height: the height in pixels
1409  *
1410  * Sets the size of the view.  This function will most probably be deprecated in
1411  * future versions in favor of #clutter_actor_set_size.  In the mean time, you need
1412  * to call both.
1413  *
1414  * Since: 0.1
1415  */
1416 //FIXME: move to an handler of actor size change
1417 void
1418 champlain_view_set_size (ChamplainView *view,
1419     guint width,
1420     guint height)
1421 {
1422   g_return_if_fail (CHAMPLAIN_IS_VIEW (view));
1423
1424   ChamplainViewPrivate *priv = view->priv;
1425
1426   priv->viewport_size.width = width;
1427   priv->viewport_size.height = height;
1428
1429   license_set_position (view);
1430   clutter_actor_set_position (priv->scale_actor, SCALE_PADDING,
1431       priv->viewport_size.height - SCALE_HEIGHT - SCALE_PADDING);
1432   resize_viewport (view);
1433
1434   if (priv->keep_center_on_resize)
1435     champlain_view_center_on (view, priv->latitude, priv->longitude);
1436   else
1437     view_load_visible_tiles (view);
1438 }
1439
1440 static void
1441 update_license (ChamplainView *view)
1442 {
1443   ChamplainViewPrivate *priv = view->priv;
1444   gchar *license;
1445
1446   if (!priv->license_actor)
1447     {
1448       priv->license_actor = g_object_ref (clutter_text_new ());
1449       clutter_text_set_font_name (CLUTTER_TEXT (priv->license_actor), "sans 8");
1450       clutter_text_set_line_alignment (CLUTTER_TEXT (priv->license_actor), PANGO_ALIGN_RIGHT);
1451       clutter_actor_set_opacity (priv->license_actor, 128);
1452       clutter_container_add_actor (CLUTTER_CONTAINER (priv->stage),
1453           priv->license_actor);
1454       clutter_actor_set_anchor_point_from_gravity (priv->license_actor, CLUTTER_GRAVITY_SOUTH_EAST);
1455       clutter_actor_raise_top (priv->license_actor);
1456     }
1457
1458   if (priv->license_text)
1459     license = g_strjoin ("\n",
1460         priv->license_text,
1461         champlain_map_source_get_license (priv->map_source),
1462         NULL);
1463   else
1464     license = g_strdup (champlain_map_source_get_license (priv->map_source));
1465
1466   clutter_text_set_text (CLUTTER_TEXT (priv->license_actor), license);
1467
1468   if (priv->show_license)
1469     {
1470       clutter_actor_show (priv->license_actor);
1471       license_set_position (view);
1472     }
1473   else
1474     clutter_actor_hide (priv->license_actor);
1475
1476   g_free (license);
1477 }
1478
1479 static gboolean
1480 finger_scroll_button_press_cb (ClutterActor *actor,
1481     ClutterButtonEvent *event,
1482     ChamplainView *view)
1483 {
1484   ChamplainViewPrivate *priv = view->priv;
1485
1486   if (priv->zoom_on_double_click && event->button == 1 && event->click_count == 2)
1487     {
1488       return view_set_zoom_level_at (view, priv->zoom_level + 1, event->x, event->y);
1489     }
1490   return FALSE; /* Propagate the event */
1491 }
1492
1493 static void
1494 scroll_to (ChamplainView *view,
1495     gint x,
1496     gint y)
1497 {
1498
1499   ChamplainViewPrivate *priv = view->priv;
1500
1501   if (priv->scroll_mode == CHAMPLAIN_SCROLL_MODE_KINETIC)
1502     {
1503       gfloat lat, lon;
1504
1505       lat = champlain_map_source_get_latitude (priv->map_source, priv->zoom_level, y);
1506       lon = champlain_map_source_get_longitude (priv->map_source, priv->zoom_level, x);
1507
1508       champlain_view_go_to_with_duration (view, lat, lon, 300);
1509     }
1510   else if (priv->scroll_mode == CHAMPLAIN_SCROLL_MODE_PUSH)
1511     {
1512       tidy_viewport_set_origin (TIDY_VIEWPORT (priv->viewport),
1513         x - priv->viewport_size.width / 2.0,
1514         y - priv->viewport_size.height / 2.0,
1515         0);
1516     }
1517 }
1518
1519 /* These functions should be exposed in the next API break */
1520 static void
1521 champlain_view_scroll_left (ChamplainView* view)
1522 {
1523   g_return_if_fail (CHAMPLAIN_IS_VIEW (view));
1524
1525   gint x, y;
1526   ChamplainViewPrivate *priv = view->priv;
1527
1528   x = champlain_map_source_get_x (priv->map_source, priv->zoom_level, priv->longitude);
1529   y = champlain_map_source_get_y (priv->map_source, priv->zoom_level, priv->latitude);
1530
1531   x -= priv->viewport_size.width / 4.0;
1532
1533   scroll_to (view, x, y);
1534 }
1535
1536 static void
1537 champlain_view_scroll_right (ChamplainView* view)
1538 {
1539   g_return_if_fail (CHAMPLAIN_IS_VIEW (view));
1540
1541   gint x, y;
1542   ChamplainViewPrivate *priv = view->priv;
1543
1544   x = champlain_map_source_get_x (priv->map_source, priv->zoom_level, priv->longitude);
1545   y = champlain_map_source_get_y (priv->map_source, priv->zoom_level, priv->latitude);
1546
1547   x += priv->viewport_size.width / 4.0;
1548
1549   scroll_to (view, x, y);
1550 }
1551
1552 static void
1553 champlain_view_scroll_up (ChamplainView* view)
1554 {
1555   g_return_if_fail (CHAMPLAIN_IS_VIEW (view));
1556
1557   gint x, y;
1558   ChamplainViewPrivate *priv = view->priv;
1559
1560   x = champlain_map_source_get_x (priv->map_source, priv->zoom_level, priv->longitude);
1561   y = champlain_map_source_get_y (priv->map_source, priv->zoom_level, priv->latitude);
1562
1563   y -= priv->viewport_size.width / 4.0;
1564
1565   scroll_to (view, x, y);
1566 }
1567
1568 static void
1569 champlain_view_scroll_down (ChamplainView* view)
1570 {
1571   g_return_if_fail (CHAMPLAIN_IS_VIEW (view));
1572
1573   gint x, y;
1574   ChamplainViewPrivate *priv = view->priv;
1575
1576   x = champlain_map_source_get_x (priv->map_source, priv->zoom_level, priv->longitude);
1577   y = champlain_map_source_get_y (priv->map_source, priv->zoom_level, priv->latitude);
1578
1579   y += priv->viewport_size.width / 4.0;
1580
1581   scroll_to (view, x, y);
1582 }
1583
1584
1585 static gboolean
1586 finger_scroll_key_press_cb (ClutterActor *actor,
1587     ClutterKeyEvent *event,
1588     ChamplainView *view)
1589 {
1590
1591   switch (event->keyval)
1592   {
1593     case 65361: // Left
1594       champlain_view_scroll_left (view);
1595       return TRUE;
1596       break;
1597     case 65362: // Up
1598       if (event->modifier_state & CLUTTER_CONTROL_MASK)
1599         champlain_view_zoom_in (view);
1600       else
1601         champlain_view_scroll_up (view);
1602       return TRUE;
1603       break;
1604     case 65363: // Right
1605       champlain_view_scroll_right (view);
1606       return TRUE;
1607       break;
1608     case 65364: // Down
1609       if (event->modifier_state & CLUTTER_CONTROL_MASK)
1610         champlain_view_zoom_out (view);
1611       else
1612         champlain_view_scroll_down (view);
1613       return TRUE;
1614       break;
1615     default:
1616       return FALSE; /* Propagate the event */
1617   }
1618   return FALSE; /* Propagate the event */
1619 }
1620
1621 /**
1622  * champlain_view_new:
1623  *
1624  * Returns: a new #ChamplainView ready to be used as a #ClutterActor.
1625  *
1626  * Since: 0.4
1627  */
1628 ClutterActor *
1629 champlain_view_new (void)
1630 {
1631   return g_object_new (CHAMPLAIN_TYPE_VIEW, NULL);
1632 }
1633
1634 static void
1635 view_update_anchor (ChamplainView *view,
1636     gint x,  /* Absolute x */
1637     gint y)  /* Absolute y */
1638 {
1639   ChamplainViewPrivate *priv = view->priv;
1640   gboolean need_anchor = FALSE;
1641   gboolean need_update = FALSE;
1642
1643   if (priv->zoom_level >= 8)
1644     need_anchor = TRUE;
1645
1646   if (priv->anchor_zoom_level != priv->zoom_level ||
1647       x - priv->anchor.x + priv->viewport_size.width >= G_MAXINT16 ||
1648       y - priv->anchor.y + priv->viewport_size.height >= G_MAXINT16 ||
1649       x - priv->anchor.x + priv->viewport_size.width <= 0 ||
1650       y - priv->anchor.y + priv->viewport_size.height <= 0 )
1651     need_update = TRUE;
1652
1653   if (need_anchor && need_update)
1654     {
1655       gdouble max;
1656
1657       priv->anchor.x = x - G_MAXINT16 / 2;
1658       priv->anchor.y = y - G_MAXINT16 / 2;
1659
1660       if ( priv->anchor.x < 0 )
1661         priv->anchor.x = 0;
1662       if ( priv->anchor.y < 0 )
1663         priv->anchor.y = 0;
1664
1665       max = champlain_zoom_level_get_width (priv->map->current_level) *
1666           champlain_map_source_get_tile_size (priv->map_source) -
1667           (G_MAXINT16 / 2);
1668       if (priv->anchor.x > max)
1669         priv->anchor.x = max;
1670       if (priv->anchor.y > max)
1671         priv->anchor.y = max;
1672
1673       priv->anchor_zoom_level = priv->zoom_level;
1674       DEBUG ("New Anchor (%f, %f) at (%d, %d)", priv->anchor.x, priv->anchor.y, x, y);
1675     }
1676
1677   if (need_anchor == FALSE)
1678     {
1679       priv->anchor.x = 0;
1680       priv->anchor.y = 0;
1681       priv->anchor_zoom_level = priv->zoom_level;
1682       DEBUG ("Clear Anchor at (%d, %d)", x, y);
1683     }
1684 }
1685
1686 /**
1687  * champlain_view_center_on:
1688  * @view: a #ChamplainView
1689  * @latitude: the longitude to center the map at
1690  * @longitude: the longitude to center the map at
1691  *
1692  * Centers the map on these coordinates.
1693  *
1694  * Since: 0.1
1695  */
1696 void
1697 champlain_view_center_on (ChamplainView *view,
1698     gdouble latitude,
1699     gdouble longitude)
1700 {
1701   g_return_if_fail (CHAMPLAIN_IS_VIEW (view));
1702
1703   gint x, y;
1704   ChamplainViewPrivate *priv = view->priv;
1705
1706   priv->longitude = CLAMP (longitude, CHAMPLAIN_MIN_LONG, CHAMPLAIN_MAX_LONG);
1707   priv->latitude = CLAMP (latitude, CHAMPLAIN_MIN_LAT, CHAMPLAIN_MAX_LAT);
1708
1709   if (!priv->map)
1710     {
1711       create_initial_map (view);
1712     }
1713
1714   x = champlain_map_source_get_x (priv->map_source, priv->zoom_level, longitude);
1715   y = champlain_map_source_get_y (priv->map_source, priv->zoom_level, latitude);
1716
1717   DEBUG ("Centering on %f, %f (%d, %d)", latitude, longitude, x, y);
1718
1719   view_update_anchor (view, x, y);
1720
1721   x -= priv->anchor.x;
1722   y -= priv->anchor.y;
1723
1724   tidy_viewport_set_origin (TIDY_VIEWPORT (priv->viewport),
1725     x - priv->viewport_size.width / 2.0,
1726     y - priv->viewport_size.height / 2.0,
1727     0);
1728
1729   g_object_notify (G_OBJECT (view), "longitude");
1730   g_object_notify (G_OBJECT (view), "latitude");
1731
1732   view_load_visible_tiles (view);
1733   view_tiles_reposition (view);
1734   view_update_polygons (view);
1735   update_scale (view);
1736   marker_reposition (view);
1737 }
1738
1739 static void
1740 timeline_new_frame (ClutterTimeline *timeline,
1741     gint frame_num,
1742     GoToContext *ctx)
1743 {
1744   gdouble alpha;
1745   gdouble lat;
1746   gdouble lon;
1747
1748   alpha = clutter_alpha_get_alpha (ctx->alpha);
1749   lat = ctx->to_latitude - ctx->from_latitude;
1750   lon = ctx->to_longitude - ctx->from_longitude;
1751
1752   champlain_view_center_on (ctx->view,
1753       ctx->from_latitude + alpha * lat,
1754       ctx->from_longitude + alpha * lon);
1755 }
1756
1757 static void
1758 timeline_completed (ClutterTimeline *timeline,
1759                     ChamplainView *view)
1760 {
1761   champlain_view_stop_go_to (view);
1762 }
1763
1764 /**
1765  * champlain_view_stop_go_to:
1766  * @view: a #ChamplainView
1767  *
1768  * Stop the go to animation.  The view will stay where it was when the
1769  * animation was stopped.
1770  *
1771  * Since: 0.4
1772  */
1773 void
1774 champlain_view_stop_go_to (ChamplainView *view)
1775 {
1776   g_return_if_fail (CHAMPLAIN_IS_VIEW (view));
1777
1778   ChamplainViewPrivate *priv = view->priv;
1779
1780   if (priv->goto_context == NULL)
1781     return;
1782
1783   clutter_timeline_stop (priv->goto_context->timeline);
1784
1785   g_object_unref (priv->goto_context->timeline);
1786   g_object_unref (priv->goto_context->alpha);
1787
1788   g_signal_emit_by_name (view, "animation-completed::go-to", NULL);
1789
1790   g_free (priv->goto_context);
1791   priv->goto_context = NULL;
1792 }
1793
1794 /**
1795  * champlain_view_go_to:
1796  * @view: a #ChamplainView
1797  * @latitude: the longitude to center the map at
1798  * @longitude: the longitude to center the map at
1799  *
1800  * Move from the current position to these coordinates. All tiles in the
1801  * intermediate view WILL be loaded!
1802  *
1803  * Since: 0.4
1804  */
1805 void
1806 champlain_view_go_to (ChamplainView *view,
1807     gdouble latitude,
1808     gdouble longitude)
1809 {
1810   guint duration;
1811
1812   duration = 500 * view->priv->zoom_level / 2.0;
1813   champlain_view_go_to_with_duration (view, latitude, longitude, duration);
1814 }
1815
1816 /* FIXME: make public after API freeze */
1817 static void
1818 champlain_view_go_to_with_duration (ChamplainView *view,
1819     gdouble latitude,
1820     gdouble longitude,
1821     guint duration) /* In ms */
1822 {
1823   g_return_if_fail (CHAMPLAIN_IS_VIEW (view));
1824
1825   if (duration == 0)
1826     {
1827       champlain_view_center_on (view, latitude, longitude);
1828       return;
1829     }
1830
1831   GoToContext *ctx;
1832
1833   ChamplainViewPrivate *priv = view->priv;
1834
1835   champlain_view_stop_go_to (view);
1836
1837   ctx = g_new0 (GoToContext, 1);
1838   ctx->from_latitude = priv->latitude;
1839   ctx->from_longitude = priv->longitude;
1840   ctx->to_latitude = latitude;
1841   ctx->to_longitude = longitude;
1842   ctx->view = view;
1843
1844   /* We keep a reference for stop */
1845   priv->goto_context = ctx;
1846
1847   /* A ClutterTimeline will be responsible for the animation,
1848    * at each frame, the current position will be computer and set
1849    * using champlain_view_center_on.  Timelines skip frames if the
1850    * computer is not fast enough, so we just need to set the duration.
1851    *
1852    * To have a nice animation, the duration should be longer if the zoom level
1853    * is higher and if the points are far away
1854    */
1855   ctx->timeline = clutter_timeline_new (duration);
1856   ctx->alpha = clutter_alpha_new_full (ctx->timeline, CLUTTER_EASE_IN_OUT_CIRC);
1857
1858   g_signal_connect (ctx->timeline, "new-frame", G_CALLBACK (timeline_new_frame),
1859       ctx);
1860   g_signal_connect (ctx->timeline, "completed", G_CALLBACK (timeline_completed),
1861       view);
1862
1863   clutter_timeline_start (ctx->timeline);
1864 }
1865
1866 /**
1867  * champlain_view_zoom_in:
1868  * @view: a #ChamplainView
1869  *
1870  * Zoom in the map by one level.
1871  *
1872  * Since: 0.1
1873  */
1874 void
1875 champlain_view_zoom_in (ChamplainView *view)
1876 {
1877   g_return_if_fail (CHAMPLAIN_IS_VIEW (view));
1878
1879   ChamplainViewPrivate *priv = view->priv;
1880
1881   champlain_view_set_zoom_level (view, priv->zoom_level + 1);
1882 }
1883
1884 /**
1885  * champlain_view_zoom_out:
1886  * @view: a #ChamplainView
1887  *
1888  * Zoom out the map by one level.
1889  *
1890  * Since: 0.1
1891  */
1892 void
1893 champlain_view_zoom_out (ChamplainView *view)
1894 {
1895   g_return_if_fail (CHAMPLAIN_IS_VIEW (view));
1896
1897   ChamplainViewPrivate *priv = view->priv;
1898
1899   champlain_view_set_zoom_level (view, priv->zoom_level - 1);
1900 }
1901
1902 /**
1903  * champlain_view_set_zoom_level:
1904  * @view: a #ChamplainView
1905  * @zoom_level: a gint
1906  *
1907  * Changes the current zoom level
1908  *
1909  * Since: 0.4
1910  */
1911 void
1912 champlain_view_set_zoom_level (ChamplainView *view,
1913     gint zoom_level)
1914 {
1915   g_return_if_fail (CHAMPLAIN_IS_VIEW (view));
1916
1917   ChamplainViewPrivate *priv = view->priv;
1918   gdouble longitude;
1919   gdouble latitude;
1920
1921   if (priv->map == NULL)
1922     return;
1923
1924   if (zoom_level == priv->zoom_level || ZOOM_LEVEL_OUT_OF_RANGE(priv, zoom_level))
1925     return;
1926
1927   champlain_view_stop_go_to (view);
1928
1929   ClutterActor *group = champlain_zoom_level_get_actor (priv->map->current_level);
1930   if (!map_zoom_to (priv->map, priv->map_source, zoom_level))
1931     return;
1932
1933   DEBUG ("Zooming to %d", zoom_level);
1934
1935   priv->zoom_level = zoom_level;
1936   /* Fix to bug 575133: keep the lat,lon as it gets set to a wrong value
1937    * when resizing the viewport, when passing from zoom_level 7 to 6
1938    * (or more precisely when anchor is set to 0).
1939    */
1940   longitude = priv->longitude;
1941   latitude = priv->latitude;
1942   resize_viewport (view);
1943
1944   ClutterActor *new_group = champlain_zoom_level_get_actor (priv->map->current_level);
1945   clutter_container_remove_actor (CLUTTER_CONTAINER (priv->map_layer), group);
1946   clutter_container_add_actor (CLUTTER_CONTAINER (priv->map_layer), new_group);
1947   champlain_view_center_on (view, latitude, longitude);
1948
1949   g_object_notify (G_OBJECT (view), "zoom-level");
1950 }
1951
1952 /**
1953  * champlain_view_set_min_zoom_level:
1954  * @view: a #ChamplainView
1955  * @zoom_level: a gint
1956  *
1957  * Changes the lowest allowed zoom level
1958  *
1959  * Since: 0.4
1960  */
1961 void
1962 champlain_view_set_min_zoom_level (ChamplainView *view,
1963     gint min_zoom_level)
1964 {
1965   g_return_if_fail (CHAMPLAIN_IS_VIEW (view));
1966
1967   ChamplainViewPrivate *priv = view->priv;
1968
1969   if (priv->min_zoom_level == min_zoom_level ||
1970       min_zoom_level > priv->max_zoom_level ||
1971       min_zoom_level < champlain_map_source_get_min_zoom_level (priv->map_source))
1972     return;
1973
1974   priv->min_zoom_level = min_zoom_level;
1975
1976   if (priv->zoom_level < min_zoom_level)
1977     champlain_view_set_zoom_level (view, min_zoom_level);
1978 }
1979
1980 /**
1981  * champlain_view_set_max_zoom_level:
1982  * @view: a #ChamplainView
1983  * @zoom_level: a gint
1984  *
1985  * Changes the highest allowed zoom level
1986  *
1987  * Since: 0.4
1988  */
1989 void
1990 champlain_view_set_max_zoom_level (ChamplainView *view,
1991     gint max_zoom_level)
1992 {
1993   g_return_if_fail (CHAMPLAIN_IS_VIEW (view));
1994
1995   ChamplainViewPrivate *priv = view->priv;
1996
1997   if (priv->max_zoom_level == max_zoom_level ||
1998       max_zoom_level < priv->min_zoom_level ||
1999       max_zoom_level > champlain_map_source_get_max_zoom_level (priv->map_source))
2000     return;
2001
2002   priv->max_zoom_level = max_zoom_level;
2003
2004   if (priv->zoom_level > max_zoom_level)
2005     champlain_view_set_zoom_level (view, max_zoom_level);
2006 }
2007
2008 /**
2009  * champlain_view_add_layer:
2010  * @view: a #ChamplainView
2011  * @layer: a #ChamplainLayer
2012  *
2013  * Adds a new layer to the view
2014  *
2015  * Since: 0.2
2016  */
2017 void
2018 champlain_view_add_layer (ChamplainView *view,
2019     ChamplainLayer *layer)
2020 {
2021   g_return_if_fail (CHAMPLAIN_IS_VIEW (view));
2022   g_return_if_fail (CHAMPLAIN_IS_LAYER (layer));
2023
2024   ChamplainViewPrivate *priv = view->priv;
2025   clutter_container_add_actor (CLUTTER_CONTAINER (priv->user_layers),
2026       CLUTTER_ACTOR (layer));
2027   clutter_actor_raise_top (CLUTTER_ACTOR (layer));
2028
2029   if (priv->map)
2030     g_idle_add (marker_reposition, view);
2031
2032   g_signal_connect_after (layer, "actor-added",
2033       G_CALLBACK (layer_add_marker_cb), view);
2034
2035   clutter_container_foreach (CLUTTER_CONTAINER (layer),
2036       CLUTTER_CALLBACK (connect_marker_notify_cb), view);
2037 }
2038
2039 /**
2040  * champlain_view_remove_layer:
2041  * @view: a #ChamplainView
2042  * @layer: a #ChamplainLayer
2043  *
2044  * Removes the layer from the view
2045  *
2046  * Since: 0.4.1
2047  */
2048 void
2049 champlain_view_remove_layer (ChamplainView *view,
2050     ChamplainLayer *layer)
2051 {
2052   g_return_if_fail (CHAMPLAIN_IS_VIEW (view));
2053   g_return_if_fail (CHAMPLAIN_IS_LAYER (layer));
2054
2055   ChamplainViewPrivate *priv = view->priv;
2056
2057   g_signal_handlers_disconnect_by_func (layer,
2058       G_CALLBACK (layer_add_marker_cb), view);
2059   clutter_container_remove_actor (CLUTTER_CONTAINER (priv->user_layers),
2060       CLUTTER_ACTOR (layer));
2061 }
2062
2063 /**
2064  * champlain_view_get_coords_from_event:
2065  * @view: a #ChamplainView
2066  * @event: a #ClutterEvent
2067  * @lat: a variable where to put the latitude of the event
2068  * @lon: a variable where to put the longitude of the event
2069  *
2070  * Returns: the latitude, longitude coordinates for the given ClutterEvent.
2071  *
2072  * Since: 0.2.8
2073  */
2074 gboolean
2075 champlain_view_get_coords_from_event (ChamplainView *view,
2076     ClutterEvent *event,
2077     gdouble *latitude,
2078     gdouble *longitude)
2079 {
2080   g_return_val_if_fail (CHAMPLAIN_IS_VIEW (view), FALSE);
2081   /* Apparently there isn a more precise test */
2082   g_return_val_if_fail (event, FALSE);
2083
2084   guint x, y;
2085
2086   switch (clutter_event_type (event))
2087     {
2088       case CLUTTER_BUTTON_PRESS:
2089       case CLUTTER_BUTTON_RELEASE:
2090         {
2091           ClutterButtonEvent *e = (ClutterButtonEvent*) event;
2092           x = e->x;
2093           y = e->y;
2094         }
2095         break;
2096       case CLUTTER_SCROLL:
2097         {
2098           ClutterScrollEvent *e = (ClutterScrollEvent*) event;
2099           x = e->x;
2100           y = e->y;
2101         }
2102         break;
2103       case CLUTTER_MOTION:
2104         {
2105           ClutterMotionEvent *e = (ClutterMotionEvent*) event;
2106           x = e->x;
2107           y = e->y;
2108         }
2109         break;
2110       case CLUTTER_ENTER:
2111       case CLUTTER_LEAVE:
2112         {
2113           ClutterCrossingEvent *e = (ClutterCrossingEvent*) event;
2114           x = e->x;
2115           y = e->y;
2116         }
2117         break;
2118       default:
2119         return FALSE;
2120     }
2121
2122   return champlain_view_get_coords_at (view, x, y, latitude, longitude);
2123 }
2124
2125 /**
2126  * champlain_view_get_coords_at:
2127  * @view: a #ChamplainView
2128  * @x: the x position in the view
2129  * @y: the y position in the view
2130  * @lat: a variable where to put the latitude of the event
2131  * @lon: a variable where to put the longitude of the event
2132  *
2133  * Returns: the latitude, longitude coordinates for the given x, y position in
2134  * the view.  Use if you get coordinates from GtkEvents for example.
2135  *
2136  * Since: 0.4
2137  */
2138 gboolean champlain_view_get_coords_at (ChamplainView *view,
2139     guint x,
2140     guint y,
2141     gdouble *latitude,
2142     gdouble *longitude)
2143 {
2144   g_return_val_if_fail (CHAMPLAIN_IS_VIEW (view), FALSE);
2145   ChamplainViewPrivate *priv = view->priv;
2146   gfloat actor_x, actor_y;
2147   gdouble rel_x, rel_y;
2148
2149   clutter_actor_get_transformed_position (priv->finger_scroll, &actor_x, &actor_y);
2150
2151   rel_x = x - actor_x;
2152   rel_y = y - actor_y;
2153
2154   if (latitude)
2155     *latitude = viewport_get_latitude_at (priv,
2156         priv->viewport_size.y + rel_y + priv->anchor.y);
2157   if (longitude)
2158     *longitude = viewport_get_longitude_at (priv,
2159         priv->viewport_size.x + rel_x + priv->anchor.x);
2160
2161   return TRUE;
2162 }
2163
2164 static void
2165 view_load_visible_tiles (ChamplainView *view)
2166 {
2167   ChamplainViewPrivate *priv = view->priv;
2168   ChamplainRectangle viewport = priv->viewport_size;
2169   gint size;
2170   ChamplainZoomLevel *level;
2171
2172   viewport.x += priv->anchor.x;
2173   viewport.y += priv->anchor.y;
2174
2175   size = champlain_map_source_get_tile_size (priv->map_source);
2176   level = priv->map->current_level;
2177
2178   if (viewport.x < 0)
2179     viewport.x = 0;
2180   if (viewport.y < 0)
2181     viewport.y = 0;
2182
2183   gint x_count = ceil((float)viewport.width / size) + 1;
2184   gint y_count = ceil((float)viewport.height / size) + 1;
2185
2186   gint x_first = viewport.x / size;
2187   gint y_first = viewport.y / size;
2188
2189   x_count += x_first;
2190   y_count += y_first;
2191
2192   if(x_count > champlain_zoom_level_get_width (level))
2193     x_count = champlain_zoom_level_get_width (level);
2194   if(y_count > champlain_zoom_level_get_height (level))
2195     y_count = champlain_zoom_level_get_height (level);
2196
2197   DEBUG ("Range %d, %d to %d, %d", x_first, y_first, x_count, y_count);
2198
2199   int i, j;
2200   guint k = 0;
2201
2202   // Get rid of old tiles first
2203   int count = champlain_zoom_level_tile_count (level);
2204   while (k < count)
2205     {
2206       ChamplainTile *tile = champlain_zoom_level_get_nth_tile (level, k);
2207
2208       if (tile == NULL)
2209         {
2210           k++;
2211           continue;
2212         }
2213
2214       gint tile_x = champlain_tile_get_x (tile);
2215       gint tile_y = champlain_tile_get_y (tile);
2216
2217       if (tile_x < x_first || tile_x > x_count ||
2218           tile_y < y_first || tile_y > y_count)
2219       {
2220         ClutterActor *group, *actor;
2221         if (champlain_tile_get_state (tile) == CHAMPLAIN_STATE_DONE)
2222           {
2223             actor = champlain_tile_get_actor (tile);
2224             group = champlain_zoom_level_get_actor (level);
2225             if (actor != NULL)
2226               clutter_container_remove_actor (CLUTTER_CONTAINER (group), actor);
2227           }
2228         champlain_zoom_level_remove_tile (level, tile);
2229         count = champlain_zoom_level_tile_count (level);
2230       }
2231       else
2232         k++;
2233     }
2234
2235   //Load new tiles if needed
2236   for (i = x_first; i < x_count; i++)
2237     {
2238       for (j = y_first; j < y_count; j++)
2239         {
2240           gboolean exist = FALSE;
2241           for (k = 0; k < champlain_zoom_level_tile_count (level) && !exist; k++)
2242             {
2243               ChamplainTile *tile = champlain_zoom_level_get_nth_tile (level, k);
2244
2245               if (tile == NULL)
2246                 continue;
2247
2248               gint tile_x = champlain_tile_get_x (tile);
2249               gint tile_y = champlain_tile_get_y (tile);
2250
2251               if ( tile_x == i && tile_y == j)
2252                 exist = TRUE;
2253             }
2254
2255           if(!exist)
2256             {
2257               DEBUG ("Loading tile %d, %d, %d", champlain_zoom_level_get_zoom_level (level), i, j);
2258               ChamplainTile *tile = champlain_tile_new ();
2259               g_object_set (G_OBJECT (tile), "x", i, "y", j, "zoom-level", champlain_zoom_level_get_zoom_level (level), NULL);
2260
2261               g_signal_connect (tile, "notify::state", G_CALLBACK (tile_state_notify), view);
2262               clutter_container_add (CLUTTER_CONTAINER (champlain_zoom_level_get_actor (level)),
2263                   champlain_tile_get_actor (tile), NULL);
2264
2265               champlain_zoom_level_add_tile (level, tile);
2266               champlain_map_source_fill_tile (priv->map_source, tile);
2267
2268               g_object_unref (tile);
2269             }
2270         }
2271     }
2272   view_update_state (view);
2273 }
2274
2275 static void
2276 view_position_tile (ChamplainView* view,
2277     ChamplainTile* tile)
2278 {
2279   ChamplainViewPrivate *priv = view->priv;
2280
2281   ClutterActor *actor;
2282   gint x;
2283   gint y;
2284   guint size;
2285
2286   actor = champlain_tile_get_actor (tile);
2287
2288   if (actor == NULL)
2289     return;
2290
2291   x = champlain_tile_get_x (tile);
2292   y = champlain_tile_get_y (tile);
2293   size = champlain_tile_get_size (tile);
2294
2295   clutter_actor_set_position (actor,
2296     (x * size) - priv->anchor.x,
2297     (y * size) - priv->anchor.y);
2298 }
2299
2300 static void
2301 view_tiles_reposition (ChamplainView* view)
2302 {
2303   ChamplainViewPrivate *priv = view->priv;
2304   gint i;
2305
2306   for (i = 0; i < champlain_zoom_level_tile_count (priv->map->current_level); i++)
2307     {
2308       ChamplainTile *tile = champlain_zoom_level_get_nth_tile (priv->map->current_level, i);
2309
2310       if (tile == NULL)
2311         continue;
2312
2313       if (champlain_tile_get_state (tile) == CHAMPLAIN_STATE_DONE)
2314         view_position_tile (view, tile);
2315     }
2316 }
2317
2318 static void
2319 tile_state_notify (GObject *gobject,
2320     GParamSpec *pspec,
2321     gpointer data)
2322 {
2323   view_position_tile (CHAMPLAIN_VIEW (data), CHAMPLAIN_TILE (gobject));
2324   view_update_state (CHAMPLAIN_VIEW (data));
2325 }
2326
2327 static void
2328 view_update_state (ChamplainView *view)
2329 {
2330   ChamplainViewPrivate *priv = view->priv;
2331   ChamplainState new_state = CHAMPLAIN_STATE_DONE;
2332   gint i;
2333
2334   for (i = 0; i < champlain_zoom_level_tile_count (priv->map->current_level); i++)
2335     {
2336       ChamplainTile *tile = champlain_zoom_level_get_nth_tile (priv->map->current_level, i);
2337
2338       if (tile == NULL)
2339         continue;
2340
2341       if (champlain_tile_get_state (tile) == CHAMPLAIN_STATE_LOADING ||
2342           champlain_tile_get_state (tile) == CHAMPLAIN_STATE_VALIDATING_CACHE)
2343         new_state = CHAMPLAIN_STATE_LOADING;
2344     }
2345
2346   if (priv->state != new_state)
2347     {
2348       priv->state = new_state;
2349       g_object_notify (G_OBJECT (view), "state");
2350     }
2351 }
2352
2353 /**
2354  * champlain_view_set_map_source:
2355  * @view: a #ChamplainView
2356  * @map_source: a #ChamplainMapSource
2357  *
2358  * Changes the currently used map source.  #g_object_unref will be called on
2359  * the previous one.
2360  *
2361  * Since: 0.4
2362  */
2363 void
2364 champlain_view_set_map_source (ChamplainView *view,
2365     ChamplainMapSource *source)
2366 {
2367   g_return_if_fail (CHAMPLAIN_IS_VIEW (view) &&
2368       CHAMPLAIN_IS_MAP_SOURCE (source));
2369
2370   ClutterActor *group;
2371
2372   ChamplainViewPrivate *priv = view->priv;
2373
2374   if (priv->map_source == source)
2375     return;
2376
2377   g_object_unref (priv->map_source);
2378   priv->map_source = g_object_ref (source);
2379
2380   priv->min_zoom_level = champlain_map_source_get_min_zoom_level (priv->map_source);
2381   priv->max_zoom_level = champlain_map_source_get_max_zoom_level (priv->map_source);
2382
2383   if (priv->map == NULL)
2384     return;
2385
2386   group = champlain_zoom_level_get_actor (priv->map->current_level);
2387   clutter_container_remove_actor (CLUTTER_CONTAINER (priv->map_layer), group);
2388
2389   map_free (priv->map);
2390   priv->map = map_new ();
2391
2392   /* Keep same zoom level if the new map supports it */
2393   if (priv->zoom_level > priv->max_zoom_level)
2394     {
2395       priv->zoom_level = priv->max_zoom_level;
2396       g_object_notify (G_OBJECT (view), "zoom-level");
2397     }
2398   else if (priv->zoom_level < priv->min_zoom_level)
2399     {
2400       priv->zoom_level = priv->min_zoom_level;
2401       g_object_notify (G_OBJECT (view), "zoom-level");
2402     }
2403
2404   map_load_level (priv->map, priv->map_source, priv->zoom_level);
2405   group = champlain_zoom_level_get_actor (priv->map->current_level);
2406
2407   view_load_visible_tiles (view);
2408   clutter_container_add_actor (CLUTTER_CONTAINER (priv->map_layer), group);
2409
2410   g_object_notify (G_OBJECT (view), "map-source");
2411
2412   update_license (view);
2413   g_idle_add (marker_reposition, view);
2414   view_tiles_reposition (view);
2415   champlain_view_center_on (view, priv->latitude, priv->longitude);
2416 }
2417
2418 /**
2419 * champlain_view_set_decel_rate:
2420 * @view: a #ChamplainView
2421 * @rate: a #gdouble between 1.001 and 2.0
2422 *
2423 * The deceleration rate for the kinetic mode.
2424 *
2425 * Since: 0.4
2426 */
2427 void
2428 champlain_view_set_decel_rate (ChamplainView *view,
2429     gdouble rate)
2430 {
2431   g_return_if_fail (CHAMPLAIN_IS_VIEW (view) &&
2432       rate < 2.0 &&
2433       rate > 1.0001);
2434
2435   ChamplainViewPrivate *priv = view->priv;
2436
2437   g_object_set (priv->finger_scroll, "decel-rate", rate, NULL);
2438 }
2439
2440 /**
2441 * champlain_view_set_scroll_mode:
2442 * @view: a #ChamplainView
2443 * @mode: a #ChamplainScrollMode value
2444 *
2445 * Determines the way the view reacts to scroll events.
2446 *
2447 * Since: 0.4
2448 */
2449 void
2450 champlain_view_set_scroll_mode (ChamplainView *view,
2451     ChamplainScrollMode mode)
2452 {
2453   g_return_if_fail (CHAMPLAIN_IS_VIEW (view));
2454
2455   ChamplainViewPrivate *priv = view->priv;
2456
2457   priv->scroll_mode = mode;
2458
2459   g_object_set (G_OBJECT (priv->finger_scroll), "mode",
2460       priv->scroll_mode, NULL);
2461 }
2462
2463 /**
2464 * champlain_view_set_keep_center_on_resize:
2465 * @view: a #ChamplainView
2466 * @value: a #gboolean
2467 *
2468 * Keep the current centered position when resizing the view.
2469 *
2470 * Since: 0.4
2471 */
2472 void
2473 champlain_view_set_keep_center_on_resize (ChamplainView *view,
2474     gboolean value)
2475 {
2476   g_return_if_fail (CHAMPLAIN_IS_VIEW (view));
2477
2478   ChamplainViewPrivate *priv = view->priv;
2479
2480   priv->keep_center_on_resize = value;
2481 }
2482
2483 /**
2484 * champlain_view_set_license_text:
2485 * @view: a #ChamplainView
2486 * @text: a license
2487 *
2488 * Show the additional license text on the map view.  The text will preceed the
2489 * map's licence when displayed. Use "\n" to separate the lines.
2490 *
2491 * Since: 0.4.3
2492 */
2493 void
2494 champlain_view_set_license_text (ChamplainView *view,
2495     const gchar *text)
2496 {
2497   g_return_if_fail (CHAMPLAIN_IS_VIEW (view));
2498
2499   ChamplainViewPrivate *priv = view->priv;
2500
2501   priv->license_text = g_strdup (text);
2502   update_license (view);
2503 }
2504
2505 /**
2506 * champlain_view_set_show_license:
2507 * @view: a #ChamplainView
2508 * @value: a #gboolean
2509 *
2510 * Show the license on the map view.  The license information should always be
2511 * available in a way or another in your application.  You can have it in
2512 * About, or on the map.
2513 *
2514 * Since: 0.4
2515 */
2516 void
2517 champlain_view_set_show_license (ChamplainView *view,
2518     gboolean value)
2519 {
2520   g_return_if_fail (CHAMPLAIN_IS_VIEW (view));
2521
2522   ChamplainViewPrivate *priv = view->priv;
2523
2524   priv->show_license = value;
2525   update_license (view);
2526 }
2527
2528 /**
2529 * champlain_view_set_show_scale:
2530 * @view: a #ChamplainView
2531 * @value: a #gboolean
2532 *
2533 * Show the scale on the map view.
2534 *
2535 * Since: 0.4.3
2536 */
2537 void
2538 champlain_view_set_show_scale (ChamplainView *view,
2539     gboolean value)
2540 {
2541   g_return_if_fail (CHAMPLAIN_IS_VIEW (view));
2542
2543   ChamplainViewPrivate *priv = view->priv;
2544
2545   priv->show_scale = value;
2546   update_scale (view);
2547 }
2548
2549 /**
2550 * champlain_view_set_max_scale_width:
2551 * @view: a #ChamplainView
2552 * @value: a #guint in pixels
2553 *
2554 * Sets the maximum width of the scale on the screen in pixels
2555 *
2556 * Since: 0.4.3
2557 */
2558 void
2559 champlain_view_set_max_scale_width (ChamplainView *view,
2560     guint value)
2561 {
2562   g_return_if_fail (CHAMPLAIN_IS_VIEW (view));
2563
2564   ChamplainViewPrivate *priv = view->priv;
2565
2566   priv->max_scale_width = value;
2567   create_scale (view);
2568   update_scale (view);
2569 }
2570
2571 /**
2572 * champlain_view_set_scale_unit:
2573 * @view: a #ChamplainView
2574 * @unit: a #ChamplainUnit
2575 *
2576 * Sets the scales unit.
2577 *
2578 * Since: 0.4.3
2579 */
2580 void
2581 champlain_view_set_scale_unit (ChamplainView *view,
2582     ChamplainUnit unit)
2583 {
2584   g_return_if_fail (CHAMPLAIN_IS_VIEW (view));
2585
2586   ChamplainViewPrivate *priv = view->priv;
2587
2588   priv->scale_unit = unit;
2589   update_scale (view);
2590 }
2591
2592 /**
2593 * champlain_view_set_zoom_on_double_click:
2594 * @view: a #ChamplainView
2595 * @value: a #gboolean
2596 *
2597 * Should the view zoom in and recenter when the user double click on the map.
2598 *
2599 * Since: 0.4
2600 */
2601 void
2602 champlain_view_set_zoom_on_double_click (ChamplainView *view,
2603     gboolean value)
2604 {
2605   g_return_if_fail (CHAMPLAIN_IS_VIEW (view));
2606
2607   ChamplainViewPrivate *priv = view->priv;
2608
2609   priv->zoom_on_double_click = value;
2610 }
2611
2612 /**
2613  * champlain_view_ensure_visible:
2614  * @view: a #ChamplainView
2615  * @lat1: the latitude of position 1
2616  * @lon1: the longitude of position 1
2617  * @lat2: the latitude of position 2
2618  * @lon2: the longitude of position 2
2619  * @animate: a #gboolean
2620  *
2621  * Changes the map's zoom level and center to make sure the two given
2622  * positions are visible
2623  *
2624  * Since: 0.4
2625  */
2626 void
2627 champlain_view_ensure_visible (ChamplainView *view,
2628     gdouble lat1,
2629     gdouble lon1,
2630     gdouble lat2,
2631     gdouble lon2,
2632     gboolean animate)
2633 {
2634   ChamplainViewPrivate *priv = view->priv;
2635   gint zoom_level = priv->zoom_level;
2636   gdouble width, height;
2637   gdouble min_lat,min_lon,max_lat,max_lon;
2638   gboolean good_size = FALSE;
2639
2640   /*We first sort the lat,lon in order to have min and max */
2641   if (lat1 < lat2)
2642     {
2643       min_lat = lat1;
2644       max_lat = lat2;
2645     }
2646   else
2647     {
2648       max_lat = lat1;
2649       min_lat = lat2;
2650     }
2651
2652   if (lon1 < lon2)
2653     {
2654       min_lon = lon1;
2655       max_lon = lon2;
2656     }
2657   else
2658     {
2659       max_lon = lon1;
2660       min_lon = lon2;
2661     }
2662
2663   width = max_lon - min_lon;
2664   height = max_lat - min_lat;
2665   width *= 1.1;
2666   height *= 1.1;
2667
2668   DEBUG("Zone to expose (%f, %f) to (%f, %f)", min_lat, min_lon, max_lat, max_lon);
2669   do
2670     {
2671       gint min_x, min_y, max_x, max_y;
2672       min_x = champlain_map_source_get_x (priv->map_source, zoom_level, min_lon);
2673       min_y = champlain_map_source_get_y (priv->map_source, zoom_level, min_lat);
2674
2675       max_x = champlain_map_source_get_x (priv->map_source, zoom_level, max_lon);
2676       max_y = champlain_map_source_get_y (priv->map_source, zoom_level, max_lat);
2677
2678       if (min_y - max_y <= priv->viewport_size.height &&
2679           max_x - min_x <= priv->viewport_size.width)
2680         good_size = TRUE;
2681       else
2682         zoom_level--;
2683
2684       if (zoom_level <= priv->min_zoom_level)
2685         break;
2686     }
2687   while (good_size == FALSE);
2688
2689   if (good_size == FALSE)
2690     {
2691       zoom_level = priv->min_zoom_level;
2692       min_lat = min_lon = width = height = 0;
2693     }
2694
2695   DEBUG ("Ideal zoom level is %d", zoom_level);
2696   champlain_view_set_zoom_level (view, zoom_level);
2697   if (animate)
2698     champlain_view_go_to (view, min_lat + height / 2.0, min_lon + width / 2.0);
2699   else
2700     champlain_view_center_on (view, min_lat + height / 2.0, min_lon + width / 2.0);
2701 }
2702
2703 /**
2704  * champlain_view_ensure_markers_visible:
2705  * @view: a #ChamplainView
2706  * @markers: a NULL terminated array of #ChamplainMarkers
2707  * @animate: a #gboolean
2708  *
2709  * Changes the map's zoom level and center to make sure those markers are
2710  * visible.
2711  *
2712  * FIXME: This doesn't take into account the marker's actor size yet
2713  *
2714  * Since: 0.4
2715  */
2716 void
2717 champlain_view_ensure_markers_visible (ChamplainView *view,
2718     ChamplainBaseMarker *markers[],
2719     gboolean animate)
2720 {
2721   gdouble min_lat, min_lon, max_lat, max_lon;
2722   ChamplainBaseMarker *marker = NULL;
2723   gint i = 0;
2724
2725   min_lat = min_lon = 200;
2726   max_lat = max_lon = -200;
2727
2728   marker = markers[i];
2729   while (marker != NULL)
2730     {
2731       gdouble lat, lon;
2732       g_object_get (G_OBJECT (marker), "latitude", &lat, "longitude", &lon,
2733           NULL);
2734
2735       if (lon < min_lon)
2736         min_lon = lon;
2737
2738       if (lat < min_lat)
2739         min_lat = lat;
2740
2741       if (lon > max_lon)
2742         max_lon = lon;
2743
2744       if (lat > max_lat)
2745         max_lat = lat;
2746
2747       marker = markers[i++];
2748     }
2749   champlain_view_ensure_visible (view, min_lat, min_lon, max_lat, max_lon, animate);
2750 }
2751
2752 /* Sets the zoom level, leaving the (x, y) at the exact same point in the view */
2753 static gboolean
2754 view_set_zoom_level_at (ChamplainView *view,
2755     gint zoom_level,
2756     gint x,
2757     gint y)
2758 {
2759   ChamplainViewPrivate *priv = view->priv;
2760
2761   ClutterActor *group, *new_group;
2762   gdouble lon, lat;
2763   gint x_diff, y_diff;
2764   gfloat actor_x, actor_y;
2765   gdouble rel_x, rel_y;
2766   gfloat x2, y2;
2767   gdouble lat2, lon2;
2768
2769   if (zoom_level == priv->zoom_level || ZOOM_LEVEL_OUT_OF_RANGE(priv, zoom_level))
2770     return FALSE;
2771
2772   champlain_view_stop_go_to (view);
2773
2774   group = champlain_zoom_level_get_actor (priv->map->current_level);
2775   clutter_actor_get_transformed_position (priv->finger_scroll, &actor_x, &actor_y);
2776   rel_x = x - actor_x;
2777   rel_y = y - actor_y;
2778
2779   /* Keep the lon, lat where the mouse is */
2780   lon = viewport_get_longitude_at (priv,
2781     priv->viewport_size.x + rel_x + priv->anchor.x);
2782   lat = viewport_get_latitude_at (priv,
2783     priv->viewport_size.y + rel_y + priv->anchor.y);
2784
2785   /* How far was it from the center of the viewport (in px) */
2786   x_diff = priv->viewport_size.width / 2 - rel_x;
2787   y_diff = priv->viewport_size.height / 2 - rel_y;
2788
2789   if (!map_zoom_to (priv->map, priv->map_source, zoom_level))
2790     return FALSE;
2791
2792   priv->zoom_level = zoom_level;
2793   new_group = champlain_zoom_level_get_actor (priv->map->current_level);
2794
2795   /* Get the new x,y in the new zoom level */
2796   x2 = champlain_map_source_get_x (priv->map_source, priv->zoom_level, lon);
2797   y2 = champlain_map_source_get_y (priv->map_source, priv->zoom_level, lat);
2798   /* Get the new lon,lat of these new x,y minus the distance from the
2799    * viewport center */
2800   lon2 = champlain_map_source_get_longitude (priv->map_source,
2801       priv->zoom_level, x2 + x_diff);
2802   lat2 = champlain_map_source_get_latitude (priv->map_source,
2803       priv->zoom_level, y2 + y_diff);
2804
2805   resize_viewport (view);
2806   clutter_container_remove_actor (CLUTTER_CONTAINER (priv->map_layer),
2807       group);
2808   clutter_container_add_actor (CLUTTER_CONTAINER (priv->map_layer),
2809       new_group);
2810   champlain_view_center_on (view, lat2, lon2);
2811
2812   g_object_notify (G_OBJECT (view), "zoom-level");
2813   return TRUE;
2814 }
2815
2816 /**
2817  * champlain_view_get_zoom_level:
2818  * @view: The view
2819  *
2820  * Returns: the view's current zoom level.
2821  *
2822  * Since: 0.4
2823  */
2824 gint
2825 champlain_view_get_zoom_level (ChamplainView *view)
2826 {
2827   g_return_val_if_fail (CHAMPLAIN_IS_VIEW (view), 0);
2828
2829   ChamplainViewPrivate *priv = view->priv;
2830
2831   return priv->zoom_level;
2832 }
2833
2834 /**
2835  * champlain_view_get_min_zoom_level:
2836  * @view: The view
2837  *
2838  * Returns: the view's minimal zoom level allowed.
2839  *
2840  * Since: 0.4
2841  */
2842 gint
2843 champlain_view_get_min_zoom_level (ChamplainView *view)
2844 {
2845   g_return_val_if_fail (CHAMPLAIN_IS_VIEW (view), 0);
2846
2847   ChamplainViewPrivate *priv = view->priv;
2848
2849   return priv->min_zoom_level;
2850 }
2851
2852 /**
2853  * champlain_view_get_max_zoom_level:
2854  * @view: The view
2855  *
2856  * Returns: the view's maximal zoom level allowed.
2857  *
2858  * Since: 0.4
2859  */
2860 gint
2861 champlain_view_get_max_zoom_level (ChamplainView *view)
2862 {
2863   g_return_val_if_fail (CHAMPLAIN_IS_VIEW (view), 0);
2864
2865   ChamplainViewPrivate *priv = view->priv;
2866
2867   return priv->max_zoom_level;
2868 }
2869
2870 /**
2871  * champlain_view_get_map_source:
2872  * @view: The view
2873  *
2874  * Returns: the view's current map source. If you need to keep a reference to the
2875  * map source then you have to call #g_object_ref.
2876  *
2877  * Since: 0.4
2878  */
2879 ChamplainMapSource*
2880 champlain_view_get_map_source (ChamplainView *view)
2881 {
2882   g_return_val_if_fail (CHAMPLAIN_IS_VIEW (view), NULL);
2883
2884   ChamplainViewPrivate *priv = view->priv;
2885
2886   return priv->map_source;
2887 }
2888
2889 /**
2890  * champlain_view_get_decel_rate:
2891  * @view: The view
2892  *
2893  * Returns: the view's deceleration rate.
2894  *
2895  * Since: 0.4
2896  */
2897 gdouble
2898 champlain_view_get_decel_rate (ChamplainView *view)
2899 {
2900   g_return_val_if_fail (CHAMPLAIN_IS_VIEW (view), 0.0);
2901
2902   ChamplainViewPrivate *priv = view->priv;
2903   gdouble decel = 0.0;
2904   g_object_get (priv->finger_scroll, "decel-rate", &decel, NULL);
2905   return decel;
2906 }
2907
2908 /**
2909  * champlain_view_get_scroll_mode:
2910  * @view: The view
2911  *
2912  * Returns: the view's scroll mode behaviour.
2913  *
2914  * Since: 0.4
2915  */
2916 ChamplainScrollMode
2917 champlain_view_get_scroll_mode (ChamplainView *view)
2918 {
2919   g_return_val_if_fail (CHAMPLAIN_IS_VIEW (view), CHAMPLAIN_SCROLL_MODE_PUSH);
2920
2921   ChamplainViewPrivate *priv = view->priv;
2922   return priv->scroll_mode;
2923 }
2924
2925 /**
2926  * champlain_view_get_keep_center_on_resize:
2927  * @view: The view
2928  *
2929  * Returns: TRUE if the view keeps the center or resize, FALSE otherwise.
2930  *
2931  * Since: 0.4
2932  */
2933 gboolean
2934 champlain_view_get_keep_center_on_resize (ChamplainView *view)
2935 {
2936   g_return_val_if_fail (CHAMPLAIN_IS_VIEW (view), FALSE);
2937
2938   ChamplainViewPrivate *priv = view->priv;
2939   return priv->keep_center_on_resize;
2940 }
2941
2942 /**
2943  * champlain_view_get_show_license:
2944  * @view: The view
2945  *
2946  * Returns: TRUE if the view displays the license, FALSE otherwise.
2947  *
2948  * Since: 0.4
2949  */
2950 gboolean
2951 champlain_view_get_show_license (ChamplainView *view)
2952 {
2953   g_return_val_if_fail (CHAMPLAIN_IS_VIEW (view), FALSE);
2954
2955   ChamplainViewPrivate *priv = view->priv;
2956   return priv->show_license;
2957 }
2958
2959 /**
2960  * champlain_view_get_license_text:
2961  * @view: The view
2962  *
2963  * Returns: the additional license text
2964  *
2965  * Since: 0.4.3
2966  */
2967 const gchar *
2968 champlain_view_get_license_text (ChamplainView *view)
2969 {
2970   g_return_val_if_fail (CHAMPLAIN_IS_VIEW (view), FALSE);
2971
2972   ChamplainViewPrivate *priv = view->priv;
2973   return priv->license_text;
2974 }
2975
2976