]> err.no Git - mapper/blobdiff - src/gtkmap.c
MapWidget: Add center mark size param, fix colors
[mapper] / src / gtkmap.c
index b0b7446f3fe751011ad34bed754e62cc3d31af4c..7dc5c6dc3c2a71d4723af02430becb85736fdbe0 100644 (file)
@@ -17,6 +17,8 @@
  * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+#include "config.h"
+
 #include <stdlib.h>
 #include <glib/gstdio.h>
 #include <glib-object.h>
 #define BUF_WIDTH_PIXELS (1024)
 #define BUF_HEIGHT_PIXELS (768)
 
-#define SCALE_WIDTH (300)
+#define MARKER_FONT_SIZE_BIG (10)
+#define MARKER_FONT_SIZE_SMALL (8)
+
+/* Filename buffer */
+#define BUFFER_SIZE (2048)
+
+#define SCALE_WIDTH (256)
 
 #define MAP_CACHE_DEFAULT (64)
 
@@ -60,11 +68,14 @@ struct _GtkMapPriv
        guint buf_width_pixels;
        guint buf_height_pixels;
 
-       /* Cairo context for widget->window */
 #ifdef WITH_CAIRO
+       /* Cairo context for widget->window */
        cairo_t *ct;
+       cairo_matrix_t matrix_rotate;
 #endif
 
+       GtkMenu *menu;
+
        PangoContext *context;
        PangoLayout *layout;
        PangoFontDescription *fontdesc;
@@ -77,30 +88,42 @@ struct _GtkMapPriv
        PangoLayout *scale_layout;
        PangoFontDescription *scale_font;
 
-       GdkGC *gc_h;
-       GdkGC *gc_w;
-       GdkGC *gc_d;
+       PangoContext *marker_context;
+       PangoLayout *marker_layout;
+       PangoFontDescription *marker_font;
+
+       GdkGC *gc_track;
+       GdkGC *gc_route;
+       GdkGC *gc_waypoint;
+       GdkGC *gc_break;
+
+       GdkGC *gc_mark;
+       GdkGC *gc_velvec;
 
        GdkGC *speed_gc1;
        GdkGC *speed_gc2;
        GdkGC *speed_gc;
 
+       /* OpenGL data */
+#ifdef WITH_GL
+       GdkGLConfig* gl_config;
+#endif
+       gboolean gl;
+
        GdkRectangle scale_rect;
 
+       guint center_mark_size;
+       GdkRectangle center_rect;
+
        RepoData *curr_repo;
 
        GTimer *timer;
        ImageCache *icache;
-       GList *markers;
-
-       /* OpenGL data */
-#ifdef WITH_GL
-       GdkGLConfig* gl_config;
-#endif
-       gboolean gl;
 
-       GdkGC *gc_mark;
-       GdkGC *gc_velvec;
+       GSList *markers;
+       GtkListStore *marker_store;
+       GdkGC *marker_gc;
+       gint marker_zoom;
 
        /* Cached Location dot x,y values */
        gint mark_x1;
@@ -110,7 +133,6 @@ struct _GtkMapPriv
        GdkRectangle mark_rect;
 
        GtkMapCenterMode center_mode;
-
        Point center;
        Point min_center;
        Point max_center;
@@ -132,6 +154,7 @@ struct _GtkMapPriv
        gint zoom;
        gint max_zoom;
        gint min_zoom;
+       gboolean zoom_to_mouse;
 
        gfloat units_conv;
        gchar *units_str;
@@ -151,31 +174,62 @@ struct _GtkMapPriv
        guint focus_unitheight;
        guint world_size_tiles;
 
+       /* Flags */
        gint show_paths;
        gboolean show_scale;
        gboolean show_location;
        gboolean show_velvec;
        gboolean show_markers;
+       gboolean show_marker_labels;
+       gboolean show_speed;
+       gboolean show_center;
+       gboolean click_to_center;
+       gboolean zoom_in_on_2button;
 
+       /* View rotation */
+       gboolean rotate_view;
+       gfloat rotate_angle;
+
+       /* How wide paths are drawn */
        guint draw_width;
 
+       /* Mouse related stuff */
+       gboolean button_down;
+       gint mouse_x;
+       gint mouse_y;
+       gdouble mouse_lat;
+       gdouble mouse_lon;
+
+       /* Lat/Lon where context menu has popped up */
+       gdouble menu_lat;
+       gdouble menu_lon;
+
        gboolean fast_render;
 
        guint key_zoom_new;
        guint key_zoom_timeout_sid;
+
+       /* Paths */
+       GSList *paths;                  /* A list with paths to draw (tracks, routes, friends, etc) */
+       Path *current_track;    /* Pointer to main track, "We" */
+       Path *current_route;    /* Pointer to main route, "We are following" */
 };
 
 #define tile2grid(tile) ((tile) << 3)
 #define grid2tile(grid) ((grid) >> 3)
+
 #define tile2pixel(tile) ((tile) << 8)
 #define pixel2tile(pixel) ((pixel) >> 8)
+
 #define tile2unit(tile) ((tile) << (8 + priv->zoom))
 #define unit2tile(unit) ((unit) >> (8 + priv->zoom))
+
 #define tile2zunit(tile, zoom) ((tile) << (8 + zoom))
 #define unit2ztile(unit, zoom) ((unit) >> (8 + zoom))
 
 #define grid2pixel(grid) ((grid) << 5)
 #define pixel2grid(pixel) ((pixel) >> 5)
+
 #define grid2unit(grid) ((grid) << (5 + priv->zoom))
 #define unit2grid(unit) ((unit) >> (5 + priv->zoom))
 
@@ -198,14 +252,14 @@ struct _GtkMapPriv
 
 #define GTK_MAP_TILE_SIZE_PIXELS (256)
 #define GTK_MAP_TILE_SIZE_P2 (8)
+#define MAX_ZOOM (16)
 
-/* #define GTK_MAP_WORLD_SIZE_UNITS(max_zoom) (2 << (max_zoom + GTK_MAP_TILE_SIZE_P2)) */
-
-#define GTK_MAP_WORLD_SIZE_UNITS (1<<31)
+#define GTK_MAP_WORLD_SIZE_UNITS (2 << (MAX_ZOOM + GTK_MAP_TILE_SIZE_P2))
 #define WORLD_SIZE_UNITS GTK_MAP_WORLD_SIZE_UNITS
 
-/* Pans are done two "grids" at a time, or 64 pixels. */
-#define GTK_MAP_PAN_UNITS (grid2unit(2))
+#define GTK_MAP_PAN_UNITS_SLOW (grid2unit(1))
+#define GTK_MAP_PAN_UNITS_FAST (grid2unit(2))
+#define GTK_MAP_PAN_UNITS GTK_MAP_PAN_UNITS_FAST
 
 #define BOUND(x, a, b) { \
        if((x) < (a)) \
@@ -220,10 +274,16 @@ static void gtk_map_size_request(GtkWidget *widget, GtkRequisition *requisition)
 static void gtk_map_size_allocate(GtkWidget *widget, GtkAllocation *allocate);
 static void gtk_map_realize(GtkWidget *widget);
 static gboolean gtk_map_expose(GtkWidget *widget, GdkEventExpose *event);
-static gboolean gtk_map_configure(GtkWidget *widget, GdkEventConfigure *event);
 
 static void gtk_map_scale_draw(GtkWidget *widget, GdkEventExpose *event);
 static void gtk_map_mark_draw(GtkWidget *widget, GdkEventExpose *event);
+static void gtk_map_speed_draw(GtkWidget *widget, GdkEventExpose *event);
+
+static void gtk_map_center_mark_draw(GtkWidget *widget, GdkEventExpose *event);
+
+static void gtk_map_render_buffer(GtkWidget *widget, GdkEventExpose *event);
+static void gtk_map_render_markers(GtkWidget *widget, GdkEventExpose *event);
+static void gtk_map_render_paths(GtkWidget *widget, GdkEventExpose *event);
 
 static gboolean gtk_map_update_buffer_size(GtkWidget *widget, gint new_width, gint new_height);
 static void gtk_map_update_size(GtkWidget *widget, gint width, gint height);
@@ -231,49 +291,60 @@ static void gtk_map_update_size(GtkWidget *widget, gint width, gint height);
 static void gtk_map_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec);
 static void gtk_map_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec);
 
+static gboolean gtk_map_button_press_cb(GtkWidget *widget, GdkEventButton *event);
+static gboolean gtk_map_button_release_cb(GtkWidget *widget, GdkEventButton *event);
+static gboolean gtk_map_scroll_event_cb(GtkWidget *widget, GdkEventScroll *event); 
+static gboolean gtk_map_motion_notify_cb(GtkWidget *widget, GdkEventMotion *event, gpointer data);
+
+
 /* Signal IDs */
 enum {
        MAP_LOCATION_CHANGED,
-       
-       MAP_ZOOMED_IN,
-       MAP_ZOOMED_OUT,
-
+       MAP_ZOOM_CHANGED,
        MAP_PANNED,
-
+       MAP_CENTER_MODE_CHANGED,
        MARKER_CLICK,
-
        LAST_SIGNAL
 };
 
 /* Property IDs */
 enum {
-       LAST_PROP
+       PROP_CENTER_MODE=1,
+       PROP_CENTER_LAT,
+       PROP_CENTER_LON,
+       PROP_MIN_LAT,
+       PROP_MAX_LAT,
+       PROP_MIN_LON,
+       PROP_MAX_LON,
+       PROP_LAST
 };
 
 static guint gtk_map_signals[LAST_SIGNAL] = { 0 };
 
-static void
-gtk_map_class_init(GtkMapClass *class)
+#define MERCATOR_SPAN (-6.28318377773622f)
+#define MERCATOR_TOP (3.14159188886811f)
+
+/******************************************************************************/
+
+void 
+latlon2unit(gdouble lat, gdouble lon, guint *unitx, guint *unity) 
 {
-GObjectClass *object_class;
-GtkWidgetClass *widget_class;
-       
-object_class = (GObjectClass*) class;
-widget_class = (GtkWidgetClass*) class;
-       
-object_class->finalize = gtk_map_finalize;
-object_class->set_property = gtk_map_set_property;
-object_class->get_property = gtk_map_get_property;
-       
-widget_class->size_request = gtk_map_size_request;
-widget_class->expose_event = gtk_map_expose;
-/* widget_class->configure_event = gtk_map_configure; */
-widget_class->realize = gtk_map_realize;
-widget_class->size_allocate = gtk_map_size_allocate;
+gdouble tmp;
 
-g_type_class_add_private (object_class, sizeof(GtkMapPriv));
+*unitx=lrint((lon + 180.f) * (GTK_MAP_WORLD_SIZE_UNITS / 360.f));
+tmp=sin(lat * (M_PIl / 180.f));
+*unity=lrint((GTK_MAP_WORLD_SIZE_UNITS / MERCATOR_SPAN) * (log((1.f + tmp) / (1.f - tmp)) * 0.5f - MERCATOR_TOP));
 }
 
+void 
+unit2latlon(guint unitx, guint unity, gdouble *lat, gdouble *lon) 
+{
+*lon=(unitx * (360.f / GTK_MAP_WORLD_SIZE_UNITS)) - 180.f;
+*lat=(360.f * (atan(exp(((unity) * (MERCATOR_SPAN / GTK_MAP_WORLD_SIZE_UNITS)) + MERCATOR_TOP)))) * (1.f / M_PIl) - 90.f;
+}
+
+/******************************************************************************/
+
 static inline void 
 gtk_map_recalc_center(GtkMapPriv *priv)
 {
@@ -287,47 +358,120 @@ switch(priv->center_mode) {
                priv->center.unitx = priv->location.unitx;
                priv->center.unity = priv->location.unity;
        break;
+       case CENTER_MANUAL:
        default:
-#if 0
-               priv->center.unitx = center->unitx;
-               priv->center.unity = center->unity;
-#endif
+               /* Do nothing, use priv->center.* */
        break;
 }
 }
 
-static inline void
+static void
 gtk_map_recalc_offset(GtkMapPriv *priv)
 {
 g_return_if_fail(priv);
-priv->offsetx = grid2pixel(unit2grid(priv->center.unitx) - priv->screen_grids_halfwidth - tile2grid(priv->base_tilex));
-priv->offsety = grid2pixel(unit2grid(priv->center.unity) - priv->screen_grids_halfheight - tile2grid(priv->base_tiley));
+#if 0
+priv->offsetx=grid2pixel(unit2grid(priv->center.unitx) - priv->screen_grids_halfwidth - tile2grid(priv->base_tilex));
+priv->offsety=grid2pixel(unit2grid(priv->center.unity) - priv->screen_grids_halfheight - tile2grid(priv->base_tiley));
+#else
+priv->offsetx=unit2pixel(priv->center.unitx) - priv->screen_width_pixels/2 - tile2pixel(priv->base_tilex);
+priv->offsety=unit2pixel(priv->center.unity) - priv->screen_height_pixels/2 - tile2pixel(priv->base_tiley);
+#endif
 }
 
-static inline void 
+static void 
 gtk_map_recalc_focus_base(GtkMapPriv *priv)
 {
 g_return_if_fail(priv);
-priv->focus.unitx = x2unit(priv->screen_width_pixels * priv->center_ratio / 20);
-priv->focus.unity = y2unit(priv->screen_height_pixels * priv->center_ratio / 20);
+priv->focus.unitx=x2unit(priv->screen_width_pixels * priv->center_ratio / 20);
+priv->focus.unity=y2unit(priv->screen_height_pixels * priv->center_ratio / 20);
 }
 
-static inline void
+static void
 gtk_map_recalc_focus_size(GtkMapPriv *priv)
 {
 g_return_if_fail(priv);
-priv->focus_unitwidth = pixel2unit((10 - priv->center_ratio) * priv->screen_width_pixels / 10);
-priv->focus_unitheight = pixel2unit((10 - priv->center_ratio) * priv->screen_height_pixels / 10);
+priv->focus_unitwidth=pixel2unit((10 - priv->center_ratio) * priv->screen_width_pixels / 10);
+priv->focus_unitheight=pixel2unit((10 - priv->center_ratio) * priv->screen_height_pixels / 10);
 }
 
-static inline void 
+static void 
 gtk_map_recalc_center_bounds(GtkMapPriv *priv)
 {
 g_return_if_fail(priv);
-priv->min_center.unitx = pixel2unit(grid2pixel(priv->screen_grids_halfwidth));
-priv->min_center.unity = pixel2unit(grid2pixel(priv->screen_grids_halfheight));
-priv->max_center.unitx = GTK_MAP_WORLD_SIZE_UNITS-grid2unit(priv->screen_grids_halfwidth) - 1;
-priv->max_center.unity = GTK_MAP_WORLD_SIZE_UNITS-grid2unit(priv->screen_grids_halfheight) - 1;
+priv->min_center.unitx=pixel2unit(grid2pixel(priv->screen_grids_halfwidth));
+priv->min_center.unity=pixel2unit(grid2pixel(priv->screen_grids_halfheight));
+priv->max_center.unitx=GTK_MAP_WORLD_SIZE_UNITS-grid2unit(priv->screen_grids_halfwidth) - 1;
+priv->max_center.unity=GTK_MAP_WORLD_SIZE_UNITS-grid2unit(priv->screen_grids_halfheight) - 1;
+BOUND(priv->center.unitx, priv->min_center.unitx, priv->max_center.unitx);
+BOUND(priv->center.unity, priv->min_center.unity, priv->max_center.unity);
+priv->base_tilex=grid2tile((gint) pixel2grid((gint) unit2pixel((gint) priv->center.unitx)) - (gint) priv->screen_grids_halfwidth);
+priv->base_tiley=grid2tile(pixel2grid(unit2pixel(priv->center.unity)) - priv->screen_grids_halfheight);
+
+priv->center_rect.x=(priv->screen_width_pixels/2)-priv->center_mark_size/2;
+priv->center_rect.y=(priv->screen_height_pixels/2)-priv->center_mark_size/2;
+priv->center_rect.width=priv->center_mark_size;
+priv->center_rect.height=priv->center_mark_size;
+}
+
+/******************************************************************************/
+
+static void
+gtk_map_class_init(GtkMapClass *class)
+{
+GObjectClass *object_class;
+GtkWidgetClass *widget_class;
+GParamSpec *pspec;
+
+object_class=(GObjectClass*) class;
+widget_class=(GtkWidgetClass*) class;
+       
+object_class->finalize=gtk_map_finalize;
+object_class->set_property=gtk_map_set_property;
+object_class->get_property=gtk_map_get_property;
+       
+widget_class->size_request=gtk_map_size_request;
+widget_class->expose_event=gtk_map_expose;
+widget_class->realize=gtk_map_realize;
+widget_class->size_allocate=gtk_map_size_allocate;
+
+g_type_class_add_private(object_class, sizeof(GtkMapPriv));
+
+pspec=g_param_spec_uint("center-mode","Center Mode","Map centering mode on current track tail", 0, 2, 1, G_PARAM_READWRITE);
+g_object_class_install_property (object_class, PROP_CENTER_MODE, pspec);
+
+/* Center lat/lon */
+pspec=g_param_spec_double("center-lat","Center Lat","Latitude", -90, 90, 0, G_PARAM_READABLE);
+g_object_class_install_property (object_class, PROP_CENTER_LAT, pspec);
+
+pspec=g_param_spec_double("center-lon","Center Lon","Longitude", -180, 180, 0, G_PARAM_READABLE);
+g_object_class_install_property (object_class, PROP_CENTER_LAT, pspec);
+
+/* Min/Max latitude */
+pspec=g_param_spec_double("max-lat","Max Lat","View max latitude", -90, 90, 0, G_PARAM_READABLE);
+g_object_class_install_property (object_class, PROP_MAX_LAT, pspec);
+
+pspec=g_param_spec_double("min-lat","Min Lat","View min latitude", -90, 90, 0, G_PARAM_READABLE);
+g_object_class_install_property (object_class, PROP_MIN_LAT, pspec);
+
+/* Min/Max longitude */
+pspec=g_param_spec_double("max-lon","Max Lon","View max longitude", -180, 180, 0, G_PARAM_READABLE);
+g_object_class_install_property (object_class, PROP_MAX_LAT, pspec);
+
+pspec=g_param_spec_double("min-lon","Min Lon","View min longitude", -180, 180, 0, G_PARAM_READABLE);
+g_object_class_install_property (object_class, PROP_MIN_LAT, pspec);
+
+gtk_map_signals[MAP_ZOOM_CHANGED]=g_signal_new("zoom-changed", G_OBJECT_CLASS_TYPE(object_class),
+       G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET(GtkMapClass, zoom_changed),
+       NULL, NULL, g_cclosure_marshal_VOID__INT, G_TYPE_NONE, 1, G_TYPE_INT);
+
+gtk_map_signals[MAP_LOCATION_CHANGED]=g_signal_new("location-changed", G_OBJECT_CLASS_TYPE(object_class),
+       G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET(GtkMapClass, location_changed),
+       NULL, NULL, g_cclosure_marshal_VOID__INT, G_TYPE_NONE, 1, G_TYPE_INT);
+
+gtk_map_signals[MAP_LOCATION_CHANGED]=g_signal_new("center-mode-changed", G_OBJECT_CLASS_TYPE(object_class),
+       G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET(GtkMapClass, center_mode_changed),
+       NULL, NULL, g_cclosure_marshal_VOID__INT, G_TYPE_NONE, 1, G_TYPE_INT);
+
 }
 
 static void
@@ -335,80 +479,75 @@ gtk_map_init(GtkMap *map)
 {
 GtkMapPriv *priv;
 
-g_debug("GTKMAP: %s", __PRETTY_FUNCTION__);
 priv=GTK_MAP_GET_PRIVATE(map);
 
-#ifdef WITH_CAIRO
-priv->ct=gdk_cairo_create(GTK_WIDGET(map)->window);
-#endif
+g_debug("INIT");
+
+priv->base_tilex=0;
+priv->base_tiley=0;
 
-priv->zoom=3;
-priv->center_mode=CENTER_LATLON;
-priv->base_tilex=-5;
-priv->base_tiley=-5;
 priv->draw_width=4;
 
+priv->min_zoom=0;
+priv->max_zoom=17;
+priv->zoom_to_mouse=TRUE;
+
+/* Start with a sensible location and manual mode */
+priv->center_mode=CENTER_MANUAL;
+priv->zoom=0;
+priv->world_size_tiles=unit2tile(GTK_MAP_WORLD_SIZE_UNITS);
+priv->zoom=8;
+latlon2unit(60.45, 22.25,&priv->center.unitx, &priv->center.unity);
+
 priv->speed=-1;
 priv->speed_gc=priv->speed_gc1;
 
 priv->units_conv=1.85200;
 
+priv->center_mark_size=8;
+
 priv->icache=image_cache_new(64);
 
 priv->show_scale=TRUE;
 priv->show_velvec=TRUE;
 priv->show_markers=TRUE;
 priv->show_location=TRUE;
+priv->show_center=TRUE;
 
-priv->gl=FALSE;
+priv->button_down=FALSE;
+priv->click_to_center=FALSE;
+priv->zoom_in_on_2button=FALSE;
 
-#ifdef WITH_GL
-priv->gl_config=gdk_gl_config_new_by_mode(GDK_GL_MODE_RGB | GDK_GL_MODE_DEPTH);
-if (priv->gl_config) {
-    g_print("OpenGL version: %s\n", glGetString (GL_VERSION));
-    g_print("OpenGL vendor: %s\n", glGetString (GL_VENDOR));
-    g_print("OpenGL renderer: %s\n", glGetString (GL_RENDERER));
-       gtk_widget_set_gl_capability(map->widget, priv->gl_config, NULL, TRUE, GDK_GL_RGBA_TYPE);
-       priv->gl=TRUE;
-}
-#endif
+priv->rotate_angle=M_PI;
+priv->rotate_view=FALSE;
 
+priv->gl=FALSE;
 priv->buffer=NULL;
-
-gtk_widget_set_extension_events(GTK_WIDGET(map), GDK_EXTENSION_EVENTS_ALL);
-
-gtk_widget_add_events(GTK_WIDGET(map), GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
-       | GDK_LEAVE_NOTIFY_MASK | GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK);
-
-#if 0
-g_signal_connect(G_OBJECT(map), "button_press_event", G_CALLBACK(gtk_map_cb_button_press), NULL);
-#endif
-
 }
 
-static gboolean 
-gtk_map_configure(GtkWidget *widget, GdkEventConfigure *event)
+GtkWidget*
+gtk_map_new(void)
 {
-GtkMap *map;
-GtkMapPriv *priv;
-
-g_return_val_if_fail(GTK_IS_MAP(widget), TRUE);
-map=GTK_MAP(widget);
-priv=GTK_MAP_GET_PRIVATE(map);
-
-g_debug("GTKMAP: %s", __PRETTY_FUNCTION__);
-
-return TRUE;
+return g_object_new(GTK_MAP_TYPE, NULL);
 }
 
+/******************************************************************************/
 
 static void
-gtk_map_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
+gtk_map_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
 {
 GtkMap *map;
+GtkMapPriv *priv;
+
 g_return_if_fail(GTK_IS_MAP(object));
+
 map=GTK_MAP(object);
+priv=GTK_MAP_GET_PRIVATE(map);
+
 switch (prop_id) {
+       case PROP_CENTER_MODE:
+               gtk_map_set_center_mode(map, g_value_get_int(value));
+       break;
        default:
                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
        break;
@@ -419,32 +558,66 @@ static void
 gtk_map_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec   *pspec)
 {
 GtkMap *map;
+GtkMapPriv *priv;
+gdouble lat,lon;
+
 g_return_if_fail(GTK_IS_MAP(object));
 map=GTK_MAP(object);
+priv=GTK_MAP_GET_PRIVATE(map);
+
 switch (prop_id) {
+       case PROP_CENTER_MODE:
+               g_value_set_int(value, priv->center_mode);
+       break;
+       case PROP_CENTER_LAT:
+               unit2latlon(priv->center.unitx, priv->center.unity, &lat, &lon);
+               g_value_set_double(value, lat);
+       break;
+       case PROP_CENTER_LON:
+               unit2latlon(priv->center.unitx, priv->center.unity, &lat, &lon);
+               g_value_set_double(value, lon);
+       break;
+       case PROP_MAX_LAT:
+               unit2latlon(x2unit(priv->screen_width_pixels), y2unit(priv->screen_height_pixels), &lat, &lon);
+               g_value_set_double(value, lat);
+       break;
+       case PROP_MIN_LAT:
+               unit2latlon(x2unit(0), y2unit(0), &lat, &lon);
+               g_value_set_double(value, lat);
+       break;
+       case PROP_MAX_LON:
+               unit2latlon(x2unit(priv->screen_width_pixels), y2unit(priv->screen_height_pixels), &lat, &lon);
+               g_value_set_double(value, lon);
+       break;
+       case PROP_MIN_LON:
+               unit2latlon(x2unit(0), y2unit(0), &lat, &lon);
+               g_value_set_double(value, lon);
+       break;
        default:
                G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
        break;
 }
 }
 
-GtkWidget*
-gtk_map_new(void)
-{
-g_debug("GTKMAP: %s", __PRETTY_FUNCTION__);
-return g_object_new(GTK_MAP_TYPE, NULL);
-}
+/******************************************************************************/
 
 static void
 gtk_map_finalize(GObject *object)
 {
 GtkMap *map;
+GtkMapPriv *priv;
        
 g_return_if_fail(GTK_IS_MAP(object));
 map=GTK_MAP(object);
+priv=GTK_MAP_GET_PRIVATE(map);
 
 g_debug("GTKMAP: %s", __PRETTY_FUNCTION__);
 
+#ifdef WITH_CAIRO
+if (priv->ct)
+       cairo_destroy(priv->ct);
+#endif
+
 if (GTK_WIDGET(object)->parent && GTK_WIDGET_MAPPED(object)) {
        gtk_widget_unmap(GTK_WIDGET(object));
 }
@@ -466,19 +639,12 @@ priv=GTK_MAP_GET_PRIVATE(map);
 tw=GTK_MAP_TILE_SIZE_PIXELS*((width/GTK_MAP_TILE_SIZE_PIXELS)+2);
 th=GTK_MAP_TILE_SIZE_PIXELS*((height/GTK_MAP_TILE_SIZE_PIXELS)+2);
 
-gtk_map_update_buffer_size(GTK_MAP(widget), tw, th);
+gtk_map_update_buffer_size(widget, tw, th);
 if (!priv->buffer) {
+       g_debug("Failed to get map back buffer!");
        return;
 }
 
-map->width=width;
-map->height=height;
-
-priv->buf_width_pixels=tw;
-priv->buf_height_pixels=th;
-priv->buf_width_tiles=priv->buf_width_pixels/GTK_MAP_TILE_SIZE_PIXELS;
-priv->buf_height_tiles=priv->buf_height_pixels/GTK_MAP_TILE_SIZE_PIXELS;
-
 priv->screen_width_pixels = width;
 priv->screen_height_pixels = height;
 priv->screen_grids_halfwidth = pixel2grid(priv->screen_width_pixels) / 2;
@@ -490,11 +656,7 @@ priv->scale_rect.width = SCALE_WIDTH;
 
 gtk_map_recalc_focus_base(priv);
 gtk_map_recalc_focus_size(priv);
-
-priv->min_center.unitx = pixel2unit(grid2pixel(priv->screen_grids_halfwidth));
-priv->min_center.unity = pixel2unit(grid2pixel(priv->screen_grids_halfheight));
-priv->max_center.unitx = GTK_MAP_WORLD_SIZE_UNITS - grid2unit(priv->screen_grids_halfwidth) - 1;
-priv->max_center.unity = GTK_MAP_WORLD_SIZE_UNITS - grid2unit(priv->screen_grids_halfheight) - 1;
+gtk_map_recalc_center_bounds(priv);
 }
 
 static void
@@ -508,12 +670,17 @@ map=GTK_MAP(widget);
 priv=GTK_MAP_GET_PRIVATE(map);
 widget->allocation = *allocation;
 
-g_debug("GTKMAP: Size allocate (%d, %d)", widget->allocation.width, widget->allocation.height);
+g_debug("GTKMAP: Size allocate (%d, %d)@%d %d", widget->allocation.width, widget->allocation.height, priv->center.unitx, priv->center.unity);
 
-if (GTK_WIDGET_REALIZED(widget))
+if (GTK_WIDGET_REALIZED(widget)) {
+       g_debug("Realized");
        gdk_window_move_resize(widget->window, allocation->x, allocation->y, allocation->width, allocation->height);
+       gtk_map_update_size(widget, allocation->width, allocation->height);
+       gtk_map_refresh(widget);
+} else {
+       g_debug("Not realized yet");
+}
 
-gtk_map_update_size(widget, allocation->width, allocation->height);
 }
 
 static void
@@ -525,12 +692,9 @@ g_return_if_fail(GTK_IS_MAP(widget));
 g_return_if_fail(requisition != NULL);
        
 map=GTK_MAP(widget);
-
-g_debug("GTKMAP: Size request");
-       
+g_debug("gtk_map_size_request");
 requisition->width=GTK_MAP_TILE_SIZE_PIXELS/2;
 requisition->height=GTK_MAP_TILE_SIZE_PIXELS/2;
-gtk_map_update_size(widget, requisition->width, requisition->height);
 }
 
 static gboolean
@@ -547,15 +711,16 @@ priv=GTK_MAP_GET_PRIVATE(map);
 
 g_debug("GTKMAP: %s (%d, %d)", __PRETTY_FUNCTION__, new_width, new_height);
 
-if (priv->buffer==NULL) {
-       priv->buffer=gdk_pixmap_new(widget->window, 
-               new_width<GTK_MAP_TILE_SIZE_PIXELS ? GTK_MAP_TILE_SIZE_PIXELS : new_width, 
-               new_height<GTK_MAP_TILE_SIZE_PIXELS ? GTK_MAP_TILE_SIZE_PIXELS : new_height, -1);
-       return TRUE;
-} else if (new_width>priv->buf_width_pixels || new_height>priv->buf_height_pixels || 
-               new_width<priv->buf_width_pixels-(GTK_MAP_TILE_SIZE_PIXELS*2) || new_height<priv->buf_height_pixels-(GTK_MAP_TILE_SIZE_PIXELS*2) ) {
-       g_object_unref(priv->buffer);
-       priv->buffer=gdk_pixmap_new(widget->window, new_width, new_height, -1);
+if ( (priv->buffer==NULL) || (new_width>priv->buf_width_pixels || new_height>priv->buf_height_pixels || new_width<priv->buf_width_pixels-(GTK_MAP_TILE_SIZE_PIXELS*2) || new_height<priv->buf_height_pixels-(GTK_MAP_TILE_SIZE_PIXELS*2) )) {
+       priv->buf_width_pixels=new_width<GTK_MAP_TILE_SIZE_PIXELS ? GTK_MAP_TILE_SIZE_PIXELS : new_width;
+       priv->buf_height_pixels=new_height<GTK_MAP_TILE_SIZE_PIXELS ? GTK_MAP_TILE_SIZE_PIXELS : new_height;
+       priv->buf_width_tiles=priv->buf_width_pixels/GTK_MAP_TILE_SIZE_PIXELS;
+       priv->buf_height_tiles=priv->buf_height_pixels/GTK_MAP_TILE_SIZE_PIXELS;
+       if (priv->buffer)
+               g_object_unref(priv->buffer);
+       priv->buffer=gdk_pixmap_new(widget->window, priv->buf_width_pixels, priv->buf_height_pixels, -1);
+       if (priv->buffer)
+               gdk_draw_rectangle(priv->buffer, widget->style->black_gc, TRUE, 0, 0, priv->buf_width_pixels, priv->buf_height_pixels);
        return TRUE;
 }
 return FALSE;
@@ -575,6 +740,7 @@ map=GTK_MAP(widget);
 priv=GTK_MAP_GET_PRIVATE(map);
 
 g_debug("GTKMAP: Realize");
+g_debug("GTKMAP: %d@%d %d", priv->zoom, priv->center.unitx, priv->center.unity);
 
 GTK_WIDGET_SET_FLAGS(widget, GTK_REALIZED);
 attributes.window_type=GDK_WINDOW_CHILD;
@@ -584,7 +750,7 @@ attributes.width=widget->allocation.width;
 attributes.height=widget->allocation.height;
 
 attributes.wclass=GDK_INPUT_OUTPUT;
-attributes.event_mask=gtk_widget_get_events(widget) | GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK;
+attributes.event_mask=gtk_widget_get_events(widget) | GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_LEAVE_NOTIFY_MASK | GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK;
 attributes_mask=GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
 
 attributes.visual=gtk_widget_get_visual(widget);
@@ -593,16 +759,48 @@ attributes.colormap=gtk_widget_get_colormap(widget);
 widget->window=gdk_window_new(gtk_widget_get_parent_window(widget), &attributes, attributes_mask);
 
 gdk_window_set_user_data(widget->window, widget);
+gtk_widget_set_app_paintable(widget, TRUE);
+gtk_widget_set_extension_events(widget, GDK_EXTENSION_EVENTS_ALL);
 
+#if 0
 widget->style=gtk_style_attach(widget->style, widget->window);
 gtk_style_set_background(widget->style, widget->window, GTK_STATE_NORMAL);
+#endif
+
+#ifdef WITH_GL_NOTYET
+priv->gl_config=gdk_gl_config_new_by_mode(GDK_GL_MODE_RGB | GDK_GL_MODE_DEPTH);
+if (priv->gl_config) {
+    g_print("OpenGL version: %s\n", glGetString (GL_VERSION));
+    g_print("OpenGL vendor: %s\n", glGetString (GL_VENDOR));
+    g_print("OpenGL renderer: %s\n", glGetString (GL_RENDERER));
+       gtk_widget_set_gl_capability(widget, priv->gl_config, NULL, TRUE, GDK_GL_RGBA_TYPE);
+       priv->gl=TRUE;
+}
+#endif
 
 priv->scale_context=gtk_widget_get_pango_context(widget);
 priv->scale_layout=pango_layout_new(priv->scale_context);
 priv->scale_font=pango_font_description_new();
-pango_font_description_set_size(priv->scale_font, 12 * PANGO_SCALE);
+pango_font_description_set_family(priv->scale_font,"Sans Serif");
+pango_font_description_set_size(priv->scale_font, 14 * PANGO_SCALE);
 pango_layout_set_font_description(priv->scale_layout, priv->scale_font);
 
+priv->marker_context=gtk_widget_get_pango_context(widget);
+priv->marker_layout=pango_layout_new(priv->marker_context);
+priv->marker_font=pango_font_description_new();
+pango_font_description_set_family(priv->marker_font,"Sans Serif");
+pango_font_description_set_size(priv->marker_font, MARKER_FONT_SIZE_SMALL*PANGO_SCALE);
+pango_layout_set_font_description (priv->marker_layout, priv->marker_font);
+pango_layout_set_alignment(priv->marker_layout, PANGO_ALIGN_CENTER);
+pango_layout_set_width(priv->marker_layout, 80*PANGO_SCALE);
+
+priv->speed_context=gtk_widget_get_pango_context(widget);
+priv->speed_layout=pango_layout_new(priv->speed_context);
+priv->speed_font=pango_font_description_new();
+pango_font_description_set_size(priv->speed_font, 48 * PANGO_SCALE);
+pango_layout_set_font_description(priv->speed_layout, priv->speed_font);
+pango_layout_set_alignment(priv->speed_layout, PANGO_ALIGN_LEFT);
+
 /* Speed limit, over limit color */
 priv->speed_gc1=gdk_gc_new(widget->window);
 color.red=0xffff;
@@ -617,12 +815,107 @@ color.green=0x1000;
 color.blue=0;
 gdk_gc_set_rgb_fg_color(priv->speed_gc2, &color);
 
-priv->speed_context=gtk_widget_get_pango_context(widget);
-priv->speed_layout=pango_layout_new(priv->speed_context);
-priv->speed_font=pango_font_description_new();
-pango_font_description_set_size(priv->speed_font, 48 * PANGO_SCALE);
-pango_layout_set_font_description(priv->speed_layout, priv->speed_font);
-pango_layout_set_alignment(priv->speed_layout, PANGO_ALIGN_LEFT);
+priv->gc_track=gdk_gc_new(widget->window);
+color.red=0xffff;
+color.green=0;
+color.blue=0;
+gdk_gc_set_rgb_fg_color(priv->gc_track, &color);
+
+priv->gc_route=gdk_gc_new(widget->window);
+color.red=0;
+color.green=0xffff;
+color.blue=0;
+gdk_gc_set_rgb_fg_color(priv->gc_route, &color);
+
+priv->gc_waypoint=gdk_gc_new(widget->window);
+color.red=0xffff;
+color.green=0xffff;
+color.blue=0;
+gdk_gc_set_rgb_fg_color(priv->gc_waypoint, &color);
+
+priv->gc_break=gdk_gc_new(widget->window);
+color.red=0xffff;
+color.green=0;
+color.blue=0xffff;
+gdk_gc_set_rgb_fg_color(priv->gc_break, &color);
+
+priv->gc_mark=gdk_gc_new(widget->window);
+color.red=0;
+color.green=0;
+color.blue=0xffff;
+gdk_gc_set_rgb_fg_color(priv->gc_mark, &color);
+
+g_signal_connect(G_OBJECT(map), "button_press_event", G_CALLBACK(gtk_map_button_press_cb), NULL);
+g_signal_connect(G_OBJECT(map), "button_release_event",G_CALLBACK(gtk_map_button_release_cb), NULL);
+g_signal_connect(G_OBJECT(map), "scroll_event",  G_CALLBACK(gtk_map_scroll_event_cb), NULL);
+g_signal_connect(G_OBJECT(map), "motion_notify_event", G_CALLBACK(gtk_map_motion_notify_cb), NULL);
+
+gtk_map_update_size(widget, widget->allocation.width, widget->allocation.height);
+gtk_map_refresh(widget);
+
+g_debug("GTKMAP: %d@%d %d", priv->zoom, priv->center.unitx, priv->center.unity);
+
+gtk_widget_queue_resize(widget);
+}
+
+#ifdef WITH_CAIRO
+static void
+gtk_map_update_rotation_matrix(GtkMapPriv *priv, guint x, guint y)
+{
+cairo_matrix_init_identity(&priv->matrix_rotate);
+cairo_matrix_translate(&priv->matrix_rotate, (gdouble)x, (gdouble)y);
+cairo_matrix_rotate(&priv->matrix_rotate, (gdouble)priv->rotate_angle);
+cairo_matrix_translate(&priv->matrix_rotate, (gdouble)-x, (gdouble)-y);
+} 
+#endif
+
+static void 
+gtk_map_render_buffer(GtkWidget *widget, GdkEventExpose *event)
+{
+GtkMap *map;
+GtkMapPriv *priv;
+
+g_return_if_fail(GTK_IS_MAP(widget));
+g_return_if_fail(event != NULL);
+
+map=GTK_MAP(widget);
+priv=GTK_MAP_GET_PRIVATE(map);
+
+if (!priv->buffer) {
+       g_debug("No buffer to paint");
+       return;
+}
+
+/* XXX: WTF, scale is not drawn properly with cairo without this thing slowing us down ????*/
+g_debug("HUH");g_debug("HUH");g_debug("HUH");g_debug("HUH");g_debug("HUH");g_debug("HUH");g_debug("HUH");g_debug("HUH");
+g_debug("HUH");g_debug("HUH");g_debug("HUH");g_debug("HUH");g_debug("HUH");g_debug("HUH");g_debug("HUH");g_debug("HUH");
+
+#ifdef WITH_CAIRO
+cairo_save(priv->ct);
+cairo_rectangle(priv->ct, event->area.x, event->area.y, event->area.width, event->area.height);
+cairo_clip(priv->ct);
+if (priv->rotate_view) {
+       gtk_map_update_rotation_matrix(priv,unit2x(priv->center.unitx),unit2y(priv->center.unity) );
+       cairo_set_matrix(priv->ct, &priv->matrix_rotate);
+
+       gdk_cairo_set_source_pixmap(priv->ct, priv->buffer, -priv->offsetx, -priv->offsety);
+       cairo_set_operator (priv->ct, CAIRO_OPERATOR_SOURCE);
+       cairo_paint(priv->ct);
+       cairo_identity_matrix(priv->ct);
+} else {
+       cairo_set_source_rgb (priv->ct, 0, 0, 0);
+       cairo_translate(priv->ct, -(event->area.x+priv->offsetx), -(event->area.y+priv->offsety));
+       gdk_cairo_set_source_pixmap(priv->ct, priv->buffer, event->area.x, event->area.y);
+
+       cairo_set_operator (priv->ct, CAIRO_OPERATOR_SOURCE);
+       cairo_paint(priv->ct);
+}
+cairo_restore(priv->ct);
+#else
+gdk_draw_drawable(widget->window, widget->style->fg_gc[GTK_STATE_NORMAL], priv->buffer,
+       event->area.x + priv->offsetx, event->area.y + priv->offsety,
+       event->area.x, event->area.y, event->area.width, event->area.height);
+#endif
 }
 
 static gboolean
@@ -637,22 +930,22 @@ g_return_val_if_fail(event != NULL, FALSE);
 map=GTK_MAP(widget);
 priv=GTK_MAP_GET_PRIVATE(map);
 
-g_debug("GTKMAP: expose (%d, %d)-(%d, %d)", event->area.x, event->area.y, event->area.width, event->area.height);
+g_debug("Expose: %d,%d %d,%d", event->area.x, event->area.y, event->area.width, event->area.height);
+
+#ifdef WITH_CAIRO
+priv->ct=gdk_cairo_create(widget->window);
+#endif
 
-if (priv->buffer)
-       gdk_draw_drawable(widget->window, widget->style->fg_gc[GTK_STATE_NORMAL], priv->buffer,
-               event->area.x + priv->offsetx, event->area.y + priv->offsety,
-               event->area.x, event->area.y, event->area.width, event->area.height);
+gtk_map_render_buffer(widget, event);
 
-#if 0
-gtk_map_paths_draw(widget, event);
+if (priv->show_paths!=0)
+       gtk_map_render_paths(widget, event);
 
 if (priv->show_markers)
-       gtk_map_markers_draw(widget, event);
+       gtk_map_render_markers(widget, event);
 
 if (priv->show_speed)
        gtk_map_speed_draw(widget, event);
-#endif
 
 if (priv->show_location)
        gtk_map_mark_draw(widget, event);
@@ -660,49 +953,238 @@ if (priv->show_location)
 if (priv->show_scale)
        gtk_map_scale_draw(widget, event);
 
+if (priv->show_center)
+       gtk_map_center_mark_draw(widget, event);
+
+#ifdef WITH_CAIRO
+cairo_destroy(priv->ct);
+priv->ct=NULL;
+#endif
+
 return TRUE;
 }
 
-static void 
-gtk_map_mark_draw(GtkWidget *widget, GdkEventExpose *event)
+static void
+gtk_map_render_waypoint(GtkWidget *widget, guint x1, guint y1)
 {
 GtkMap *map;
 GtkMapPriv *priv;
+GdkGC *gc;
+
+g_return_if_fail(GTK_IS_MAP(widget));
 
 map=GTK_MAP(widget);
 priv=GTK_MAP_GET_PRIVATE(map);
 
-g_debug("GTKMAP: %s", __PRETTY_FUNCTION__);
+gc=priv->gc_waypoint;
 
-if (!gdk_rectangle_intersect(&event->area, &priv->mark_rect, NULL))
+if ((x1 > priv->buf_width_pixels) || (y1 > priv->buf_height_pixels))
        return;
+gdk_draw_arc(widget->window, gc, FALSE, x1 - priv->draw_width, y1 - priv->draw_width, 2 * priv->draw_width, 2 * priv->draw_width, 0, 360 * 64);
+}
 
-#ifdef WITH_CAIRO
-cairo_arc(priv->ct, priv->mark_x1, priv->marky1, priv->draw_width*2, 0, 2 * M_PI);
-cairo_set_source_rgb(priv->ct, 1, 1, 1);
-cairo_fill_preserve(priv->ct);
-cairo_set_source_rgb(priv->ct, 0, 0, 0);
+static void
+gtk_map_render_path_segment(GtkWidget *widget, guint unitx1, guint unity1, guint unitx2, guint unity2)
+{
+GtkMap *map;
+GtkMapPriv *priv;
+GdkGC *gc;
+gint x1, y1, x2, y2;
+
+g_return_if_fail(GTK_IS_MAP(widget));
+
+map=GTK_MAP(widget);
+priv=GTK_MAP_GET_PRIVATE(map);
+
+x1=unit2bufx(unitx1);
+y1=unit2bufy(unity1);
+x2=unit2bufx(unitx2);
+y2=unit2bufy(unity2);
+
+/* Make sure this line could possibly be visible. */
+if (!((x1 > priv->buf_width_pixels && x2 > priv->buf_width_pixels) || (x1 < 0 && x2 < 0)
+      || (y1 > priv->buf_height_pixels && y2 > priv->buf_height_pixels) || (y1 < 0 && y2 < 0))) {
+       gc=priv->gc_track;
+       gdk_draw_line(widget->window, gc, x1, y1, x2, y2);
+}
+}
+
+static void
+gtk_map_render_path(GtkWidget *widget, Path *path, GdkEventExpose *event)
+{
+GtkMap *map;
+GtkMapPriv *priv;
+Point *curr;
+WayPoint *wcurr;
+
+g_return_if_fail(path);
+
+if (path->head==path->tail)
+       return;
+
+map=GTK_MAP(widget);
+priv=GTK_MAP_GET_PRIVATE(map);
+
+for (curr = path->head, wcurr = path->whead; curr++ != path->tail;) {
+       /* Draw the line from (curr - 1) to (curr). */
+
+       gtk_map_render_path_segment(widget, curr[-1].unitx, curr[-1].unity, curr->unitx, curr->unity);
+       /* Now, check if curr is a waypoint. */
+       if (wcurr && wcurr <= path->wtail && wcurr->point == curr) {
+               guint x1 = unit2bufx(wcurr->point->unitx);
+               guint y1 = unit2bufy(wcurr->point->unity);
+
+               gtk_map_render_waypoint(widget, x1, y1);
+               wcurr++;
+       }
+}
+#if 0
+if (path->type==PATH_TYPE_ROUTE)
+       gtk_map_render_next_waypoint(widget, path);
+#endif
+}
+
+void
+gtk_map_set_path_display(GtkWidget *widget, gint path_mask)
+{
+GtkMap *map;
+GtkMapPriv *priv;
+
+map=GTK_MAP(widget);
+priv=GTK_MAP_GET_PRIVATE(map);
+
+priv->show_paths=path_mask;
+}
+
+static void 
+gtk_map_render_paths(GtkWidget *widget, GdkEventExpose *event)
+{
+GtkMap *map;
+GtkMapPriv *priv;
+GSList *iter;
+
+map=GTK_MAP(widget);
+priv=GTK_MAP_GET_PRIVATE(map);
+
+/* No paths defined so return */
+if (!priv->paths)
+       return;
+
+for (iter=priv->paths; iter!=NULL; iter=iter->next) {
+       Path *p=(Path *)iter->data;
+
+       if ( (!(priv->show_paths & ROUTES_MASK) && p->type==PATH_TYPE_ROUTE) || 
+                       (!(priv->show_paths & TRACKS_MASK) && p->type==PATH_TYPE_ROUTE) )
+               continue;
+
+       gtk_map_render_path(widget, p, event);
+}
+}
+
+gboolean
+gtk_map_add_path(GtkWidget *widget, Path *path)
+{
+GtkMap *map;
+GtkMapPriv *priv;
+
+g_return_val_if_fail(GTK_IS_MAP(widget), FALSE);
+g_return_val_if_fail(path, FALSE);
+
+map=GTK_MAP(widget);
+priv=GTK_MAP_GET_PRIVATE(map);
+
+/* Don't allow duplicates */
+if (g_slist_find(priv->paths, path)!=NULL)
+       return FALSE;
+g_object_ref(path);
+priv->paths=g_slist_append(priv->paths, path);
+return TRUE;
+}
+
+gboolean
+gtk_map_remove_path(GtkWidget *widget, Path *path)
+{
+GtkMap *map;
+GtkMapPriv *priv;
+
+g_return_val_if_fail(GTK_IS_MAP(widget), FALSE);
+g_return_val_if_fail(path, FALSE);
+
+map=GTK_MAP(widget);
+priv=GTK_MAP_GET_PRIVATE(map);
+
+if (g_slist_find(priv->paths, path)==NULL)
+       return FALSE;
+priv->paths=g_slist_remove(priv->paths, path);
+g_object_unref(path);
+return TRUE;
+}
+
+static void
+gtk_map_center_mark_draw(GtkWidget *widget, GdkEventExpose *event)
+{
+GtkMap *map;
+GtkMapPriv *priv;
+
+map=GTK_MAP(widget);
+priv=GTK_MAP_GET_PRIVATE(map);
+
+if (!gdk_rectangle_intersect(&event->area, &priv->center_rect, NULL))
+       return;
+
+#ifdef WITH_CAIRO
+cairo_save(priv->ct);
+cairo_rectangle(priv->ct, priv->center_rect.x, priv->center_rect.y, priv->center_rect.width, priv->center_rect.height);
+cairo_set_source_rgba(priv->ct, 1, 1, 1, 0.8);
+cairo_fill_preserve(priv->ct);
+cairo_set_source_rgba(priv->ct, 0, 0, 0, 0.5);
+cairo_stroke(priv->ct);
+cairo_restore(priv->ct);
+#else
+gdk_draw_rectangle(widget->window, priv->gc_mark, FALSE, priv->center_rect.x, priv->center_rect.y, priv->center_rect.width, priv->center_rect.height);
+#endif
+}
+
+static void 
+gtk_map_mark_draw(GtkWidget *widget, GdkEventExpose *event)
+{
+GtkMap *map;
+GtkMapPriv *priv;
+
+map=GTK_MAP(widget);
+priv=GTK_MAP_GET_PRIVATE(map);
+
+g_debug("GTKMAP: %s", __PRETTY_FUNCTION__);
+
+if (!gdk_rectangle_intersect(&event->area, &priv->mark_rect, NULL))
+       return;
+
+return;
+
+#ifdef WITH_CAIRO
+cairo_save(priv->ct);
+cairo_arc(priv->ct, priv->mark_x1, priv->mark_y1, priv->draw_width*2, 0, 2 * M_PI);
+cairo_set_source_rgb(priv->ct, 1, 1, 1);
+cairo_fill_preserve(priv->ct);
+cairo_set_source_rgba(priv->ct, 0.2, 0.2, 0.8, 0.7);
 if (priv->show_velvec) {
        cairo_save(priv->ct);
        cairo_set_line_width(priv->ct, priv->draw_width);
-       cairo_move_to(priv->ct, priv->mark_x1, priv->marky1);
-       cairo_line_to(priv->ct, priv->mark_x2, priv->marky2);
+       cairo_move_to(priv->ct, priv->mark_x1, priv->mark_y1);
+       cairo_line_to(priv->ct, priv->mark_x2, priv->mark_y2);
        cairo_restore(priv->ct);
 }
 cairo_stroke(priv->ct);
+cairo_restore(priv->ct);
 #else
-gdk_draw_arc(widget->window,
-       priv->gc_mark,
-       FALSE,
+gdk_draw_arc(widget->window, priv->gc_mark, FALSE,
        priv->mark_x1 - priv->draw_width,
        priv->mark_y1 - priv->draw_width,
-       2 * priv->draw_width,
-       2 * priv->draw_width,
+       2 * priv->draw_width, 2 * priv->draw_width,
        0, 360 * 64);
 
 if (priv->show_velvec)
-       gdk_draw_line(widget->window, 
-               priv->gc_velvec,
+       gdk_draw_line(widget->window, priv->gc_velvec,
                priv->mark_x1, priv->mark_y1, 
                priv->mark_x2, priv->mark_y2);
 #endif
@@ -728,6 +1210,8 @@ priv->mark_rect.width = abs(priv->mark_x1 - priv->mark_x2) + (4 * priv->draw_wid
 priv->mark_rect.height = abs(priv->mark_y1 - priv->mark_y2) + (4 * priv->draw_width);
 }
 
+#define SCALE_PADDING (4)
+
 static void 
 gtk_map_scale_draw(GtkWidget *widget, GdkEventExpose *event)
 {
@@ -751,17 +1235,9 @@ priv->scale_rect.y = priv->screen_height_pixels - priv->scale_rect.height - 1;
 if (!gdk_rectangle_intersect(&event->area, &priv->scale_rect, NULL))
        return;
 
-gdk_draw_rectangle(widget->window, widget->style->bg_gc[GTK_WIDGET_STATE(widget)],
-          TRUE, priv->scale_rect.x, priv->scale_rect.y,
-          priv->scale_rect.width, priv->scale_rect.height);
-
-gdk_draw_rectangle(widget->window, widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
-          FALSE, priv->scale_rect.x, priv->scale_rect.y,
-          priv->scale_rect.width, priv->scale_rect.height);
-
 /* Now calculate and draw the distance. */
-unit2latlon(priv->center.unitx - pixel2unit(SCALE_WIDTH / 2 - 4), priv->center.unity, lat1, lon1);
-unit2latlon(priv->center.unitx + pixel2unit(SCALE_WIDTH / 2 - 4), priv->center.unity, lat2, lon2);
+unit2latlon(priv->center.unitx - pixel2unit(SCALE_WIDTH / 2 - SCALE_PADDING), priv->center.unity, &lat1, &lon1);
+unit2latlon(priv->center.unitx + pixel2unit(SCALE_WIDTH / 2 - SCALE_PADDING), priv->center.unity, &lat2, &lon2);
 distance=calculate_distance(lat1, lon1, lat2, lon2) * priv->units_conv;
 
 if (distance < 1.f)
@@ -773,40 +1249,58 @@ else
 
 g_debug("SCALE: %s", buffer);
 
+#ifdef WITH_CAIRO
+cairo_save(priv->ct);
+cairo_rectangle(priv->ct, priv->scale_rect.x, priv->scale_rect.y, priv->scale_rect.width, priv->scale_rect.height);
+cairo_set_source_rgba(priv->ct, 1, 1, 1, 0.65);
+cairo_fill_preserve(priv->ct);
+cairo_set_source_rgba(priv->ct, 0, 0, 0, 0.81);
+#else
+gdk_draw_rectangle(widget->window, widget->style->bg_gc[GTK_WIDGET_STATE(widget)],
+          TRUE, priv->scale_rect.x, priv->scale_rect.y, priv->scale_rect.width, priv->scale_rect.height);
+gdk_draw_rectangle(widget->window, widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
+          FALSE, priv->scale_rect.x, priv->scale_rect.y, priv->scale_rect.width, priv->scale_rect.height);
+#endif
+
 pango_layout_set_text(priv->scale_layout, buffer, -1);
 pango_layout_get_pixel_size(priv->scale_layout, &width, NULL);
 
+/* Draw little hashes on the ends. */
+#ifdef WITH_CAIRO
+cairo_set_source_rgb(priv->ct, 0, 0, 0);
+cairo_move_to(priv->ct, priv->scale_rect.x + SCALE_PADDING, priv->scale_rect.y + priv->scale_rect.height / 2 - SCALE_PADDING);
+cairo_line_to(priv->ct, priv->scale_rect.x + SCALE_PADDING, priv->scale_rect.y + priv->scale_rect.height / 2 + SCALE_PADDING);
+
+cairo_move_to(priv->ct, priv->scale_rect.x + SCALE_PADDING, priv->scale_rect.y + priv->scale_rect.height / 2);
+cairo_line_to(priv->ct, priv->scale_rect.x + (priv->scale_rect.width - width) / 2 - SCALE_PADDING, priv->scale_rect.y + priv->scale_rect.height / 2);
+
+cairo_move_to(priv->ct, priv->scale_rect.x + priv->scale_rect.width - SCALE_PADDING, priv->scale_rect.y + priv->scale_rect.height / 2 - SCALE_PADDING);
+cairo_line_to(priv->ct, priv->scale_rect.x + priv->scale_rect.width - SCALE_PADDING, priv->scale_rect.y + priv->scale_rect.height / 2 + SCALE_PADDING);
+
+cairo_move_to(priv->ct, priv->scale_rect.x + priv->scale_rect.width - SCALE_PADDING, priv->scale_rect.y + priv->scale_rect.height / 2);
+cairo_line_to(priv->ct, priv->scale_rect.x + (priv->scale_rect.width + width) / 2 + SCALE_PADDING, priv->scale_rect.y + priv->scale_rect.height / 2);
+
+cairo_stroke(priv->ct);
+cairo_restore(priv->ct);
+#else
+gdk_draw_line(widget->window, widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
+           priv->scale_rect.x + SCALE_PADDING, priv->scale_rect.y + priv->scale_rect.height / 2 - SCALE_PADDING,
+           priv->scale_rect.x + SCALE_PADDING, priv->scale_rect.y + priv->scale_rect.height / 2 + SCALE_PADDING);
+gdk_draw_line(widget->window, widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
+               priv->scale_rect.x + SCALE_PADDING, priv->scale_rect.y + priv->scale_rect.height / 2,
+           priv->scale_rect.x + (priv->scale_rect.width - width) / 2 - SCALE_PADDING, priv->scale_rect.y + priv->scale_rect.height / 2);
+gdk_draw_line(widget->window, widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
+           priv->scale_rect.x + priv->scale_rect.width - SCALE_PADDING, priv->scale_rect.y + priv->scale_rect.height / 2 - SCALE_PADDING,
+           priv->scale_rect.x + priv->scale_rect.width - SCALE_PADDING, priv->scale_rect.y + priv->scale_rect.height / 2 + SCALE_PADDING);
+gdk_draw_line(widget->window, widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
+           priv->scale_rect.x + priv->scale_rect.width - SCALE_PADDING, priv->scale_rect.y + priv->scale_rect.height / 2,
+           priv->scale_rect.x + (priv->scale_rect.width + width) / 2 + SCALE_PADDING, priv->scale_rect.y + priv->scale_rect.height / 2);
+#endif
+
 /* Draw the layout itself. */
-gdk_draw_layout(widget->window,
-               widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
-               priv->scale_rect.x + (priv->scale_rect.width - width) / 2,
-               priv->scale_rect.y, priv->scale_layout);
+gdk_draw_layout(widget->window,        widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
+               priv->scale_rect.x + (priv->scale_rect.width - width) / 2, priv->scale_rect.y, priv->scale_layout);
 
-/* Draw little hashes on the ends. */
-gdk_draw_line(widget->window,
-           widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
-           priv->scale_rect.x + 4,
-           priv->scale_rect.y + priv->scale_rect.height / 2 - 4,
-           priv->scale_rect.x + 4,
-           priv->scale_rect.y + priv->scale_rect.height / 2 + 4);
-gdk_draw_line(widget->window,
-           widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
-               priv->scale_rect.x + 4,
-           priv->scale_rect.y + priv->scale_rect.height / 2,
-           priv->scale_rect.x + (priv->scale_rect.width - width) / 2 - 4,
-           priv->scale_rect.y + priv->scale_rect.height / 2);
-gdk_draw_line(widget->window,
-           widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
-           priv->scale_rect.x + priv->scale_rect.width - 4,
-           priv->scale_rect.y + priv->scale_rect.height / 2 - 4,
-           priv->scale_rect.x + priv->scale_rect.width - 4,
-           priv->scale_rect.y + priv->scale_rect.height / 2 + 4);
-gdk_draw_line(widget->window,
-           widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
-           priv->scale_rect.x + priv->scale_rect.width - 4,
-           priv->scale_rect.y + priv->scale_rect.height / 2,
-           priv->scale_rect.x + (priv->scale_rect.width + width) / 2 + 4,
-           priv->scale_rect.y + priv->scale_rect.height / 2);
 }
 
 static void
@@ -828,6 +1322,8 @@ gdk_draw_layout(widget->window, gc, x, y, priv->speed_layout);
 gdk_window_process_all_updates();
 }
 
+/******************************************************************************/
+
 static void
 gtk_map_speed_draw(GtkWidget *widget, GdkEventExpose *event)
 {
@@ -843,7 +1339,7 @@ if (priv->speed<0)
        return;
 
 g_snprintf(buffer, sizeof(buffer), "%0.0f %s", priv->speed * priv->units_conv, priv->units_str);
-map_information_text(10, 10, priv->speed_gc, buffer);
+gtk_map_information_text(widget, 10, 10, priv->speed_gc, buffer);
 }
 
 void
@@ -860,6 +1356,199 @@ priv->speed_gc=(overspeed) ? priv->speed_gc1 : priv->speed_gc2;
 priv->speed=speed;
 }
 
+/******************************************************************************/
+
+static gboolean
+gtk_map_marker_find_at_latlon(GtkWidget *widget, gdouble lat, gdouble lon, guint *id)
+{
+GtkMap *map;
+GtkMapPriv *priv;
+GtkTreeIter iter;
+gboolean found=FALSE;
+gboolean valid;
+gdouble pdist=99999.0;
+
+g_return_val_if_fail(GTK_IS_MAP(widget), FALSE);
+
+map=GTK_MAP(widget);
+priv=GTK_MAP_GET_PRIVATE(map);
+
+g_return_val_if_fail(priv->marker_store, FALSE);
+
+if (!priv->show_markers)
+       return FALSE;
+
+if (priv->marker_zoom<=priv->zoom) 
+       return FALSE;
+
+valid=gtk_tree_model_get_iter_first(GTK_TREE_MODEL(priv->marker_store), &iter);
+g_return_val_if_fail(valid, FALSE);
+
+while (valid) {
+       gdouble dist, plat, plon;
+       guint tid;
+       GtkMapMarkerType type;
+
+       gtk_tree_model_get(GTK_TREE_MODEL(priv->marker_store),
+                       &iter,
+                       MAP_MARKER_ID, &tid,
+                       MAP_MARKER_TYPE, &type,
+                       MAP_MARKER_LAT, &plat,
+                       MAP_MARKER_LON, &plon,
+                       -1);
+
+       /* XXX: Use quicker and simple distance check, convert to x,y values, check against +-icon size */
+       dist=calculate_distance(lat, lon, plat, plon);
+       if ((dist<pdist) && (dist<0.011)) {
+               found=TRUE;
+               pdist=dist;
+               *id=id;
+       }
+
+       valid=gtk_tree_model_iter_next(GTK_TREE_MODEL(priv->marker_store), &iter);
+}
+
+return found;
+}
+
+static inline GdkGC *
+gtk_map_set_marker_fg_color_from_string(GtkMapPriv *priv, const gchar *hexcolor, GdkGC *def)
+{
+GdkColor color;
+
+if (!hexcolor)
+       return def;
+
+if (!gdk_color_parse(hexcolor, &color))
+       return def;
+
+gdk_gc_set_rgb_fg_color(priv->marker_gc, &color);
+return priv->marker_gc;
+}
+
+static void 
+gtk_map_poi_title(GtkWidget *widget, gint x, gint y, GdkGC *gc, const gchar *title)
+{
+GtkMap *map;
+GtkMapPriv *priv;
+gint w,h;
+
+g_return_if_fail(GTK_IS_MAP(widget));
+
+map=GTK_MAP(widget);
+priv=GTK_MAP_GET_PRIVATE(map);
+
+/* XXX: Add cairo version */
+pango_layout_set_text(priv->layout, title, -1);
+pango_layout_get_pixel_size(priv->layout, &w, &h);
+gdk_draw_layout(widget->window, gc, x-(w>>1), y-h-priv->draw_width, priv->layout);
+}
+
+/**
+ * Render all the POI data.
+ * This should be done before rendering track data.
+ */
+static void 
+gtk_map_render_markers(GtkWidget *widget, GdkEventExpose *event)
+{
+GtkTreeIter iter;
+GtkMap *map;
+GtkMapPriv *priv;
+guint unitx, unity;
+gint poix, poiy;
+gdouble lat1, lat2, lon1, lon2;
+gboolean valid;
+
+g_return_if_fail(GTK_IS_MAP(widget));
+
+map=GTK_MAP(widget);
+priv=GTK_MAP_GET_PRIVATE(map);
+
+if (!priv->marker_store)
+       return;
+
+if (priv->marker_zoom<=priv->zoom) 
+       return;
+
+unitx=x2unit(0);
+unity=y2unit(priv->screen_height_pixels);
+unit2latlon(unitx, unity, &lat1, &lon1);
+
+unitx=x2unit(priv->screen_width_pixels);
+unity=y2unit(0);
+unit2latlon(unitx, unity, &lat2, &lon2);
+
+valid=gtk_tree_model_get_iter_first(GTK_TREE_MODEL(priv->marker_store), &iter);
+if (!valid)
+       return;
+
+if (priv->zoom<2 && priv->show_marker_labels==TRUE) {
+       pango_font_description_set_size(priv->fontdesc, MARKER_FONT_SIZE_SMALL*PANGO_SCALE);
+       pango_layout_set_font_description (priv->layout, priv->fontdesc);
+       pango_layout_set_ellipsize(priv->layout, PANGO_ELLIPSIZE_NONE);
+}
+
+while (valid) {
+       GdkPixbuf *pixbuf;
+       GdkGC *gc;
+       GdkRectangle marker_rect;
+       gchar *label, *icon, *color;
+
+       gtk_tree_model_get(GTK_TREE_MODEL(priv->marker_store),
+                       &iter,
+                       MAP_MARKER_LAT, &lat1,
+                       MAP_MARKER_LON, &lon1,
+                       MAP_MARKER_LABEL, &label,
+                       MAP_MARKER_ICON, &icon,
+                       MAP_MARKER_COLOR, &color,
+                       -1);
+
+       latlon2unit(lat1, lon1, &unitx, &unity);
+       poix=unit2x(unitx);
+       poiy=unit2y(unity);
+
+       marker_rect.x=poix-16;
+       marker_rect.y=poiy-16;
+       marker_rect.width=poix+16;
+       marker_rect.height=poiy+16;
+
+       /* Don't bother if it's outside our event area */
+       if (!gdk_rectangle_intersect(&event->area, &marker_rect, NULL))
+               continue;
+
+#if 0
+       pixbuf=poi_get_icon(icon, priv->zoom<2 ? TRUE : FALSE);
+#else
+       pixbuf=NULL;
+#endif
+       gc=gtk_map_set_marker_fg_color_from_string(priv, color, widget->style->fg_gc[GTK_WIDGET_STATE(widget)]);
+
+       if (!pixbuf) {
+               /* No icon for POI or for category - draw default. */
+               gdk_draw_arc(widget->window, gc, FALSE, poix - priv->draw_width, poiy - priv->draw_width,
+                       (MAX_ZOOM-priv->zoom)/6 * priv->draw_width, (MAX_ZOOM-priv->zoom)/6 * priv->draw_width, 0, 360 * 64);
+       } else {
+               guint w,h;
+
+               w=gdk_pixbuf_get_width(pixbuf);
+               h=gdk_pixbuf_get_height(pixbuf);
+               gdk_draw_pixbuf(widget->window, gc, pixbuf, 0, 0, poix-w/2, poiy-h/2, -1, -1, GDK_RGB_DITHER_NONE, 0, 0);
+#if 0
+               /* Draw rectangle around icon. Not really need with the square icons */
+               if (_zoom<2)
+                       gdk_draw_rectangle(widget->window, gc, FALSE, poix-w/2-1, poiy-h/2-1, w+1, h+1);
+#endif
+       }
+
+       if (priv->zoom<2 && priv->show_marker_labels==TRUE && label)
+               gtk_map_poi_title(widget, poix, poiy, gc, label);
+
+       valid=gtk_tree_model_iter_next(GTK_TREE_MODEL(priv->marker_store), &iter);
+}
+}
+
+/******************************************************************************/
+
 /**
  * Do an in-place scaling of a pixbuf's pixels at the given ratio from the given source location.
  */
@@ -919,12 +1608,77 @@ gtk_map_tile_load(GtkWidget *widget, guint tilex, guint tiley, gint zoff, gboole
 {
 GtkMap *map;
 GtkMapPriv *priv;
+GdkPixbuf *pixbuf;
+GError *error = NULL;
+gchar buffer[BUFFER_SIZE];
+gchar key[BUFFER_SIZE];
+struct stat tstat;
+gint se;
 
 g_return_val_if_fail(GTK_IS_MAP(widget), FALSE);
 map=GTK_MAP(widget);
 priv=GTK_MAP_GET_PRIVATE(map);
 
-return NULL;
+g_snprintf(buffer, sizeof(buffer), "%s/%u/%u/%u.jpg", priv->curr_repo->cache_dir, priv->zoom + zoff, (tilex >> zoff), (tiley >> zoff));
+g_snprintf(key, sizeof(key), "%s/%u/%u/%u", priv->curr_repo->cache_dir, priv->zoom + zoff, (tilex >> zoff), (tiley >> zoff));
+
+g_debug("LOAD: %u, %u @ (%d+%d): %s", tilex, tiley, priv->zoom, zoff, buffer);
+
+pixbuf=image_cache_get(priv->icache, key, buffer);
+if (!pixbuf) {
+#if 0
+       g_unlink(buffer);
+
+       if (_auto_download && _curr_repo->type != REPOTYPE_NONE && !((_zoom + zoff - (_curr_repo->double_size ? 1 : 0)) % _curr_repo->dl_zoom_steps)) {
+               if (download)
+                       map_initiate_download(tilex >> zoff, tiley >> zoff, _zoom + zoff, -INITIAL_DOWNLOAD_RETRIES);
+       }
+#endif
+       return NULL;
+}
+
+g_object_ref(pixbuf);
+
+#if 0
+/* Check if we need to trim. */
+if (gdk_pixbuf_get_width(pixbuf) != TILE_SIZE_PIXELS || gdk_pixbuf_get_height(pixbuf) != TILE_SIZE_PIXELS)
+       pixbuf=pixbuf_trim(pixbuf);
+#endif
+
+#if 0
+/* Check tile age, if file date is ower a week old, redownload if autodownload enabled */
+se=stat(buffer, &tstat);
+if (se==0) {
+       time_t t;
+       t=time(NULL);
+       if (t-tstat.st_mtime>TILE_MAX_AGE) {
+               if (_auto_download && _curr_repo->type != REPOTYPE_NONE && !((_zoom + zoff - (_curr_repo->double_size ? 1 : 0)) % _curr_repo->dl_zoom_steps)) {
+                       if (download) {
+                               g_debug("Tile: %s is old, re-downloading\n", buffer);
+                               map_initiate_download(tilex >> zoff, tiley >> zoff, _zoom + zoff, -INITIAL_DOWNLOAD_RETRIES);
+                               image_cache_invalidate(map_ic, key);
+                       }
+               }
+       }
+}
+#endif
+
+return pixbuf;
+}
+
+void 
+gtk_map_set_tile_repository(GtkWidget *widget, RepoData *rd)
+{
+GtkMap *map;
+GtkMapPriv *priv;
+
+g_return_if_fail(GTK_IS_MAP(widget));
+map=GTK_MAP(widget);
+priv=GTK_MAP_GET_PRIVATE(map);
+
+g_return_if_fail(rd);
+
+priv->curr_repo=rd;
 }
 
 static gboolean
@@ -939,6 +1693,9 @@ g_return_val_if_fail(GTK_IS_MAP(widget), FALSE);
 map=GTK_MAP(widget);
 priv=GTK_MAP_GET_PRIVATE(map);
 
+g_debug("RT: %u %u (%u) %u %u (%u, %u)", tilex, tiley, 
+       priv->world_size_tiles, destx, desty, priv->buf_width_tiles, priv->buf_height_tiles);
+
 g_return_val_if_fail(priv->buffer, FALSE);
 g_return_val_if_fail(priv->curr_repo, FALSE);
 
@@ -948,8 +1705,6 @@ if (destx > priv->buf_width_pixels || desty > priv->buf_height_pixels)
 if (tilex > priv->world_size_tiles || tiley > priv->world_size_tiles)
        return FALSE;
 
-/* g_debug("MAP RT: %u %u (%u) %u %u (%u, %u)", tilex, tiley, priv->world_size_tiles, destx, desty, buf_width_tiles, buf_height_tiles); */
-
 for (zoff = (priv->curr_repo->double_size ? 1 : 0); !pixbuf && (priv->zoom + zoff) <= priv->max_zoom && zoff <= GTK_MAP_TILE_SIZE_P2; zoff += 1) {
        pixbuf=gtk_map_tile_load(map, tilex, tiley, zoff, !fast_fail);
        if (!pixbuf) {
@@ -984,29 +1739,64 @@ gdk_draw_rectangle(priv->buffer,
 return TRUE;
 }
 
+/******************************************************************************/
+
 gboolean
-gtk_map_set_center(GtkWidget *widget, guint new_center_unitx, guint new_center_unity)
+gtk_map_set_center_mode(GtkWidget *widget, GtkMapCenterMode mode)
 {
 GtkMap *map;
 GtkMapPriv *priv;
-GtkStyle *style;
+GtkMapCenterMode omode;
 
 g_return_val_if_fail(GTK_IS_MAP(widget), FALSE);
 
 map=GTK_MAP(widget);
 priv=GTK_MAP_GET_PRIVATE(map);
-style=widget->style;
 
+omode=priv->center_mode;
+
+/* If we have a track, then we can center on it */
+if (priv->current_track) {
+       priv->center_mode=mode;
+       if (omode!=mode)
+               g_signal_emit(widget, gtk_map_signals[MAP_CENTER_MODE_CHANGED], 0, NULL);
+       return TRUE;
+} else if (mode==CENTER_MANUAL) {
+       priv->center_mode=mode;
+       if (omode!=mode)
+               g_signal_emit(widget, gtk_map_signals[MAP_CENTER_MODE_CHANGED], 0, NULL);
+       return TRUE;
+} else {
+       priv->center_mode=CENTER_MANUAL;
+       if (omode!=CENTER_MANUAL)
+               g_signal_emit(widget, gtk_map_signals[MAP_CENTER_MODE_CHANGED], 0, NULL);
+       return FALSE;
+}
+
+}
+
+void
+gtk_map_set_center(GtkWidget *widget, guint unitx, guint unity)
+{
+GtkMap *map;
+GtkMapPriv *priv;
+GtkStyle *style;
 gint new_base_tilex, new_base_tiley;
 guint new_x, new_y;
 guint j, k, base_new_x, base_old_x, old_x, old_y, iox, ioy;
 
+g_return_if_fail(GTK_IS_MAP(widget));
+
+map=GTK_MAP(widget);
+priv=GTK_MAP_GET_PRIVATE(map);
+style=widget->style;
+
 /* Assure that _center.unitx/y are bounded. */
-BOUND(new_center_unitx, priv->min_center.unitx, priv->max_center.unitx);
-BOUND(new_center_unity, priv->min_center.unity, priv->max_center.unity);
+BOUND(unitx, priv->min_center.unitx, priv->max_center.unitx);
+BOUND(unity, priv->min_center.unity, priv->max_center.unity);
 
-priv->center.unitx = new_center_unitx;
-priv->center.unity = new_center_unity;
+priv->center.unitx = unitx;
+priv->center.unity = unity;
 
 new_base_tilex = grid2tile((gint) pixel2grid((gint)unit2pixel((gint) priv->center.unitx)) - (gint)priv->screen_grids_halfwidth);
 new_base_tiley = grid2tile(pixel2grid(unit2pixel(priv->center.unity)) - priv->screen_grids_halfheight);
@@ -1073,21 +1863,91 @@ if (new_base_tilex != priv->base_tilex || new_base_tiley != priv->base_tiley) {
 
 gtk_map_recalc_offset(priv);
 gtk_map_recalc_focus_base(priv);
+gtk_map_refresh(widget);
+g_signal_emit(widget, gtk_map_signals[MAP_LOCATION_CHANGED], 0, priv->zoom);
+}
+
+void
+gtk_map_set_center_latlon(GtkWidget *widget, gdouble lat, gdouble lon)
+{
+guint unitx, unity;
 
-gtk_map_refresh(map);
+g_return_if_fail(GTK_IS_MAP(widget));
+latlon2unit(lat, lon, &unitx, &unity);
+gtk_map_set_center(widget, unitx, unity);
 }
 
 void
-gtk_map_refresh(GtkWidget *widget)
+gtk_map_get_center_latlon(GtkWidget *widget, gdouble *lat, gdouble *lon)
+{
+GtkMap *map;
+GtkMapPriv *priv;
+
+g_return_if_fail(GTK_IS_MAP(widget));
+map=GTK_MAP(widget);
+priv=GTK_MAP_GET_PRIVATE(map);
+
+unit2latlon(priv->center.unitx, priv->center.unity, lat, lon);
+}
+
+void
+gtk_map_pan(GtkWidget *widget, gint delta_x, gint delta_y)
+{
+GtkMap *map;
+GtkMapPriv *priv;
+
+g_return_if_fail(GTK_IS_MAP(widget));
+map=GTK_MAP(widget);
+priv=GTK_MAP_GET_PRIVATE(map);
+priv->center_mode=CENTER_MANUAL;
+
+gtk_map_set_center(widget, priv->center.unitx + delta_x*GTK_MAP_PAN_UNITS, priv->center.unity + delta_y*GTK_MAP_PAN_UNITS);
+}
+
+gboolean 
+gtk_map_rotate(GtkWidget *widget, gfloat angle)
 {
+#ifdef WITH_CAIRO
 GtkMap *map;
+GtkMapPriv *priv;
 
 g_return_if_fail(GTK_IS_MAP(widget));
+map=GTK_MAP(widget);
+priv=GTK_MAP_GET_PRIVATE(map);
+
+if (angle==0.0 || angle==NAN) {
+       priv->rotate_view=FALSE;
+} else {
+       priv->rotate_view=TRUE;
+       priv->rotate_angle=angle*(M_PIl / 180.f);
+}
+gtk_widget_queue_draw(widget);
+return TRUE;
+#else
+return FALSE;
+#endif
+}
 
+void
+gtk_map_refresh(GtkWidget *widget)
+{
+GtkMap *map;
+GtkMapPriv *priv;
+guint x, y;
+
+g_return_if_fail(GTK_IS_MAP(widget));
 map=GTK_MAP(widget);
-gtk_widget_queue_draw_area(widget, 0, 0, map->width, map->height);
+priv=GTK_MAP_GET_PRIVATE(map);
+
+for (y = 0; y < priv->buf_height_tiles; ++y)
+       for (x = 0; x < priv->buf_width_tiles; ++x)
+               gtk_map_render_tile(map, priv->base_tilex + x, priv->base_tiley + y, x * GTK_MAP_TILE_SIZE_PIXELS, y * GTK_MAP_TILE_SIZE_PIXELS, FALSE);
+
+gtk_widget_queue_draw(widget);
 }
 
+/******************************************************************************/
+
 gint 
 gtk_map_get_zoom(GtkWidget *widget)
 {
@@ -1102,6 +1962,16 @@ priv=GTK_MAP_GET_PRIVATE(map);
 return priv->zoom;
 }
 
+gboolean
+gtk_map_set_autozoom(GtkWidget *widget, gboolean az)
+{
+GtkMap *map;
+GtkMapPriv *priv;
+
+/* Check that we have a track path set */
+return TRUE;
+}
+
 gboolean
 gtk_map_set_zoom(GtkWidget *widget, gint new_zoom)
 {
@@ -1113,36 +1983,35 @@ g_return_val_if_fail(GTK_IS_MAP(widget), FALSE);
 map=GTK_MAP(widget);
 priv=GTK_MAP_GET_PRIVATE(map);
 
+g_debug("GTKMAP: req zoom is %d @ %d,%d", new_zoom, priv->center.unitx, priv->center.unity);
+
 if (new_zoom > (priv->max_zoom - 1))
        return FALSE;
 
 if (new_zoom == priv->zoom)
        return FALSE;
 
-priv->zoom = new_zoom / priv->curr_repo->view_zoom_steps * priv->curr_repo->view_zoom_steps;
-priv->world_size_tiles = unit2tile(GTK_MAP_WORLD_SIZE_UNITS);
+if (priv->curr_repo)
+       priv->zoom = new_zoom / priv->curr_repo->view_zoom_steps * priv->curr_repo->view_zoom_steps;
+else
+       priv->zoom = new_zoom;
 
 /* If we're leading, update the center to reflect new zoom level. */
 gtk_map_recalc_center(priv);
 
 /* Update center bounds to reflect new zoom level. */
-priv->min_center.unitx = pixel2unit(grid2pixel(priv->screen_grids_halfwidth));
-priv->min_center.unity = pixel2unit(grid2pixel(priv->screen_grids_halfheight));
-priv->max_center.unitx = GTK_MAP_WORLD_SIZE_UNITS - grid2unit(priv->screen_grids_halfwidth) - 1;
-priv->max_center.unity = GTK_MAP_WORLD_SIZE_UNITS - grid2unit(priv->screen_grids_halfheight) - 1;
-
-BOUND(priv->center.unitx, priv->min_center.unitx, priv->max_center.unitx);
-BOUND(priv->center.unity, priv->min_center.unity, priv->max_center.unity);
-
-priv->base_tilex = grid2tile((gint) pixel2grid((gint) unit2pixel((gint) priv->center.unitx)) - (gint) priv->screen_grids_halfwidth);
-priv->base_tiley = grid2tile(pixel2grid(unit2pixel(priv->center.unity)) - priv->screen_grids_halfheight);
+gtk_map_recalc_center_bounds(priv);
 
 /* New zoom level, so we can't reuse the old buffer's pixels. Update state variables. */
 gtk_map_recalc_offset(priv);
 gtk_map_recalc_focus_base(priv);
 gtk_map_recalc_focus_size(priv);
 
-gtk_map_refresh(GTK_WIDGET(map));
+gtk_map_refresh(widget);
+
+g_debug("GTKMAP: zoom is %d @ %d,%d", priv->zoom, priv->center.unitx, priv->center.unity);
+
+g_signal_emit(widget, gtk_map_signals[MAP_ZOOM_CHANGED], 0, priv->zoom);
 
 return TRUE;
 }
@@ -1182,6 +2051,62 @@ gtk_map_zoom(widget, 1);
 return FALSE;
 }
 
+/******************************************************************************/
+
+void
+gtk_map_set_context_menu(GtkWidget *widget, GtkMenu *menu)
+{
+GtkMap *map;
+GtkMapPriv *priv;
+
+g_return_if_fail(GTK_IS_MAP(widget));
+map=GTK_MAP(widget);
+priv=GTK_MAP_GET_PRIVATE(map);
+
+#ifdef WITH_DEVICE_770
+gtk_widget_tap_and_hold_setup(widget, GTK_WIDGET(menu), NULL, 0);
+#else
+g_object_ref(menu);
+priv->menu=menu;
+#endif
+}
+
+void
+gtk_map_clear_context_menu(GtkWidget *widget)
+{
+GtkMap *map;
+GtkMapPriv *priv;
+
+g_return_if_fail(GTK_IS_MAP(widget));
+map=GTK_MAP(widget);
+priv=GTK_MAP_GET_PRIVATE(map);
+
+g_return_if_fail(priv->menu);
+
+#ifdef WITH_DEVICE_770
+gtk_widget_tap_and_hold_setup(widget, NULL, NULL, 0);
+#else
+g_object_unref(priv->menu);
+#endif
+priv->menu=NULL;
+}
+
+void
+gtk_map_get_menu_latlon(GtkWidget *widget, gdouble *lat, gdouble *lon)
+{
+GtkMap *map;
+GtkMapPriv *priv;
+
+g_return_if_fail(GTK_IS_MAP(widget));
+map=GTK_MAP(widget);
+priv=GTK_MAP_GET_PRIVATE(map);
+
+*lat=priv->menu_lat;
+*lat=priv->menu_lon;
+}
+
+/******************************************************************************/
+
 void
 gtk_map_set_cache_size(GtkWidget *widget, guint cache_size)
 {
@@ -1195,3 +2120,110 @@ if (cache_size>512)
        cache_size=512;
 image_cache_set_size(priv->icache, cache_size);
 }
+
+/******************************************************************************/
+/* Internal widget callback handlers                                          */
+/******************************************************************************/
+
+/**
+ * Mouse scroller zoom in/out callback
+ */
+static gboolean
+gtk_map_scroll_event_cb(GtkWidget *widget, GdkEventScroll *event)
+{
+GtkMap *map;
+GtkMapPriv *priv;
+
+g_return_val_if_fail(GTK_IS_MAP(widget), FALSE);
+
+map=GTK_MAP(widget);
+priv=GTK_MAP_GET_PRIVATE(map);
+gtk_map_zoom(widget, event->direction==GDK_SCROLL_UP ? -1 : 1);
+if (priv->zoom_to_mouse)
+       gtk_map_set_center(widget, x2unit((gint) (event->x + 0.5)), y2unit((gint) (event->y + 0.5)));
+return FALSE;
+}
+
+static gboolean 
+gtk_map_motion_notify_cb(GtkWidget *widget, GdkEventMotion *event, gpointer data)
+{
+GtkMap *map;
+GtkMapPriv *priv;
+
+g_return_val_if_fail(GTK_IS_MAP(widget), FALSE);
+map=GTK_MAP(widget);
+priv=GTK_MAP_GET_PRIVATE(map);
+
+priv->mouse_x=(gint)event->x;
+priv->mouse_y=(gint)event->y;
+unit2latlon(x2unit(priv->mouse_x), y2unit(priv->mouse_y), &priv->mouse_lat, &priv->mouse_lon);
+
+g_debug("MOUSE(%d): %d,%d (%f,%f)", priv->button_down, priv->mouse_x, priv->mouse_y, priv->mouse_lat, priv->mouse_lon);
+
+return FALSE;
+}
+
+static gboolean
+gtk_map_button_press_cb(GtkWidget *widget, GdkEventButton *event)
+{
+GtkMap *map;
+GtkMapPriv *priv;
+
+g_return_val_if_fail(GTK_IS_MAP(widget), FALSE);
+map=GTK_MAP(widget);
+priv=GTK_MAP_GET_PRIVATE(map);
+
+unit2latlon(x2unit((gint) (event->x+0.5)), y2unit((gint) (event->y+0.5)), &priv->menu_lat, &priv->menu_lon);
+
+switch (event->button) {
+case 1:
+       priv->button_down=TRUE;
+       if (priv->click_to_center) {
+               gtk_map_set_center(widget, x2unit((gint) (event->x+0.5)), y2unit((gint) (event->y+0.5)));
+               return FALSE;
+       } else if (event->type==GDK_2BUTTON_PRESS) {
+               priv->center_mode=CENTER_MANUAL;
+               gtk_map_set_center(widget, x2unit((gint) (event->x+0.5)), y2unit((gint) (event->y+0.5)));
+               if (priv->zoom_in_on_2button)
+                       gtk_map_zoom_in(widget);
+               return FALSE;
+       }
+break;
+case 2:
+
+break;
+case 3:
+       if (priv->menu)
+               gtk_menu_popup(GTK_MENU(priv->menu), NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time());
+break;
+}
+return FALSE;
+}
+
+static gboolean
+gtk_map_button_release_cb(GtkWidget *widget, GdkEventButton *event)
+{
+GtkMap *map;
+GtkMapPriv *priv;
+
+g_return_val_if_fail(GTK_IS_MAP(widget), FALSE);
+map=GTK_MAP(widget);
+priv=GTK_MAP_GET_PRIVATE(map);
+
+switch (event->button) {
+case 1:
+       priv->button_down=FALSE;
+       /* Check if a marker was clicked, if they are shown and if there is something to show */
+       if (priv->show_markers && priv->marker_store) {
+               /* XXX */
+       }
+break;
+case 2:
+
+break;
+case 3:
+
+break;
+}
+return FALSE;
+}