]> err.no Git - mapper/blobdiff - src/map.c
More Path/Route/Track cleanups:
[mapper] / src / map.c
index 147a11e77f6c8a5176ebba624941a4aaae6cbbd5..48f41f02160d2ac6896cc2637a2f4e3027b88d98 100644 (file)
--- a/src/map.c
+++ b/src/map.c
@@ -1,26 +1,43 @@
-#include <config.h>
+/*
+ * This file is part of mapper
+ *
+ * Copyright (C) 2007 Kaj-Michael Lang
+ * Copyright (C) 2006-2007 John Costigan.
+ *
+ * POI and GPS-Info code originally written by Cezary Jackiewicz.
+ *
+ * Default map data provided by http://www.openstreetmap.org/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
 
-#define _GNU_SOURCE
+#include <config.h>
 
 #include <unistd.h>
 #include <stdlib.h>
 #include <string.h>
 #include <strings.h>
 #include <stddef.h>
-#include <locale.h>
 #include <math.h>
 #include <errno.h>
 #include <sys/wait.h>
 #include <glib/gstdio.h>
 #include <gtk/gtk.h>
 #include <fcntl.h>
-#include <libgnomevfs/gnome-vfs.h>
-#include <gconf/gconf-client.h>
-#include <libxml/parser.h>
-#include <curl/multi.h>
 #include <libintl.h>
 #include <locale.h>
-#include <sqlite3.h>
 
 #include "hildon-mapper.h"
 
 #include "osm-db.h"
 #include "poi.h"
 #include "route.h"
+#include "track.h"
 #include "gps.h"
-#include "bt.h"
 #include "mapper-types.h"
 #include "ui-common.h"
 #include "settings.h"
 #include "latlon.h"
 #include "gpx.h"
+#include "speak.h"
+#include "map-poi.h"
+#include "map-download.h"
+#include "announcements.h"
+#include "gtkcompass.h"
+#include "dialogs.h"
+#include "image-cache.h"
+
+#define DEBUG_MAP_TIME 1
+
+#define MAP_THUMB_MARGIN_X (100)
+#define MAP_THUMB_MARGIN_Y (75)
+
+/* Initial size */
+#define BUF_WIDTH_TILES (4)
+#define BUF_HEIGHT_TILES (3)
+#define BUF_WIDTH_PIXELS (1024)
+#define BUF_HEIGHT_PIXELS (768)
+
+static guint buf_width_tiles=BUF_WIDTH_TILES;
+static guint buf_height_tiles=BUF_HEIGHT_TILES;
+static guint buf_width_pixels=BUF_WIDTH_PIXELS;
+static guint buf_height_pixels=BUF_HEIGHT_PIXELS;
+
+#ifdef WITH_GL
+#include <GL/gl.h>
+#include <gtk/gtkgl.h>
+GdkGLConfig* map_gl_config=NULL;
+#endif
+gboolean map_gl=FALSE;
 
-Point _min_center = { -1, -1 };
-Point _max_center = { -1, -1 };
-Point _focus = { -1, -1 };
+#ifdef WITH_CAIRO
+cairo_t *ct_pixmap;
+#endif
+
+/** The backing pixmap of map_widget. */
+static GdkPixmap *map_pixmap;
+
+/* Tile cache, this might need some adjustment */
+#define MAP_CACHE_MAX (64)
+static ImageCache *map_ic;
 
 /** The "base tile" is the upper-left tile in the pixmap. */
 guint _base_tilex = -5;
 guint _base_tiley = -5;
 
+static guint map_max_zoom=MAX_ZOOM;
 guint _zoom = 3;               /* zoom level, from 0 to MAX_ZOOM. */
-Point _center = { -1, -1 };    /* current center location, X. */
 
+Point _min_center = { -1, -1 };
+Point _max_center = { -1, -1 };
+Point _focus = { -1, -1 };
+/* current center location, X. */
+Point _center = { -1, -1 };    
+
+CenterMode _center_mode = CENTER_LEAD;
+
+/* Drag */
 static guint press[2] = { 0, 0 };
 static guint release[2] = { 0, 0 };
 static guint before[2] = { 0, 0 };
-static guint _id = 0;
+static guint map_drag_id = 0;
+static gboolean map_dragged;
+
+/** VARIABLES FOR ACCESSING THE LOCATION/BOUNDS OF THE CURRENT MARK. */
+static gint mark_x1;
+static gint mark_x2;
+static gint mark_y1;
+static gint mark_y2;
+static gint mark_minx;
+static gint mark_miny;
+static gint mark_width;
+static gint mark_height;
+
+static GdkRectangle scale_rect;
+static PangoContext *scale_context;
+static PangoFontDescription *scale_font;
+static PangoLayout *scale_layout;
+
+static GdkGC *speed_gc1;
+static GdkGC *speed_gc2;
+static PangoContext *speed_context;
+static PangoLayout *speed_layout;
+static PangoFontDescription *speed_fontdesc;
+
+static gint zoom_timeout_sid=0;
+static gint map_mode=0;
+static gboolean map_data_needs_refresh=FALSE;
+
+osm_location map_loc = {NULL, NULL, NULL, FALSE, FALSE, 0, 0, 0.0, 0.0, 0 };
+
+static GTimer *map_timer;
+
+/* Tile max age, 1 week */
+#define TILE_MAX_AGE (604800)
+
+#define KM10KNOTS (5.39956803)
+
+static void map_update_location(gdouble lat, gdouble lon, gboolean force);
+static void map_speed_draw(void);
+static gboolean map_cb_after_realize(GtkWidget *map_widget, gpointer data);
+static gboolean map_cb_configure(GtkWidget *widget, GdkEventConfigure *event);
+static gboolean map_cb_expose(GtkWidget * widget, GdkEventExpose * event);
+static gboolean map_cb_button_press(GtkWidget * widget, GdkEventButton * event);
+static gboolean map_cb_button_release(GtkWidget * widget, GdkEventButton * event);
+static gboolean map_cb_scroll_event(GtkWidget * widget, GdkEventScroll * event); 
+
+/******************************************************************************/
+
+GtkWidget * 
+map_new(void) 
+{
+GtkWidget *map_widget;
+
+map_widget=gtk_drawing_area_new();
+map_timer=g_timer_new();
 
-static gint _zoom_timeout_sid=0;
-static gint _map_mode=0;
+map_ic=image_cache_new(MAP_CACHE_MAX);
 
-guint _num_downloads=0;
-guint _curr_download=0;
+gtk_widget_set_extension_events(GTK_WIDGET(map_widget), GDK_EXTENSION_EVENTS_ALL);
 
-static osm_location map_loc = {NULL, NULL, NULL, FALSE, FALSE, 0, 0, 0.0, 0.0 };
+#ifdef WITH_GL
+map_gl_config=gdk_gl_config_new_by_mode(GDK_GL_MODE_RGB | GDK_GL_MODE_DEPTH);
+if (map_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, map_gl_config, NULL, TRUE, GDK_GL_RGBA_TYPE);
+       map_gl=TRUE;
+}
+#endif
 
-typedef struct _map_tile_rdata map_tile_rdata;
-struct _map_tile_rdata {
-       guint tilex, tiley, zoom;
-       guint destx, desty;
-};
+g_signal_connect_after(G_OBJECT(map_widget), "realize", G_CALLBACK(map_cb_after_realize), NULL);
+g_signal_connect(G_OBJECT(map_widget), "configure_event", G_CALLBACK(map_cb_configure), NULL);
 
-void map_render_paths();
-void map_force_redraw();
-gboolean curl_download_timeout();
-gchar *map_construct_url(guint tilex, guint tiley, guint zoom);
-gboolean map_download_idle_refresh(ProgressUpdateInfo * pui);
-static void map_update_location(gint x, gint y, gboolean force);
+return map_widget;
+}
 
-gboolean get_next_pui(gpointer key, gpointer value, ProgressUpdateInfo ** data)
+static gboolean 
+map_cb_after_realize(GtkWidget *map_widget, gpointer data)
 {
-*data = key;
+GdkColor color;
+
+g_debug("MAP: after_realize");
+
+scale_context=gtk_widget_get_pango_context(map_widget);
+scale_layout=pango_layout_new(scale_context);
+scale_font=pango_font_description_new();
+pango_font_description_set_size(scale_font, 12 * PANGO_SCALE);
+pango_layout_set_font_description(scale_layout, scale_font);
+
+/* Speed limit, over limit color */
+speed_gc1=gdk_gc_new(map_widget->window);
+color.red=0xffff;
+color.green=0;
+color.blue=0;
+gdk_gc_set_rgb_fg_color(speed_gc1, &color);
+
+/* Speed limit, under limit color */
+speed_gc2=gdk_gc_new(map_widget->window);
+color.red=0;
+color.green=0x1000;
+color.blue=0;
+gdk_gc_set_rgb_fg_color(speed_gc2, &color);
+
+speed_context=gtk_widget_get_pango_context(map_widget);
+speed_layout=pango_layout_new(speed_context);
+speed_fontdesc=pango_font_description_new();
+pango_font_description_set_size(speed_fontdesc, 48 * PANGO_SCALE);
+pango_layout_set_font_description(speed_layout, speed_fontdesc);
+pango_layout_set_alignment(speed_layout, PANGO_ALIGN_CENTER);
+
+/* Signals */
+g_signal_connect(G_OBJECT(map_widget), "expose_event", G_CALLBACK(map_cb_expose), NULL);
+g_signal_connect(G_OBJECT(map_widget), "button_press_event", G_CALLBACK(map_cb_button_press), NULL);
+g_signal_connect(G_OBJECT(map_widget), "button_release_event",G_CALLBACK(map_cb_button_release), NULL);
+g_signal_connect(G_OBJECT(map_widget), "scroll_event",  G_CALLBACK(map_cb_scroll_event), NULL);
+
+gtk_widget_add_events(map_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);
+
+map_poi_init(map_widget);
+
 return TRUE;
 }
 
-gboolean curl_download_timeout()
+static gboolean 
+map_cb_configure(GtkWidget *widget, GdkEventConfigure *event)
 {
-       static guint destroy_counter = 50;
-       gint num_transfers = 0, num_msgs = 0;
-       gint deletes_left = 50; /* only do 50 deletes at a time. */
-       CURLMsg *msg;
-       vprintf("%s()\n", __PRETTY_FUNCTION__);
-
-       if (_curl_multi && CURLM_CALL_MULTI_PERFORM == curl_multi_perform(_curl_multi, &num_transfers))
-               return TRUE;    /* Give UI a chance first. */
-
-       while (_curl_multi && (msg = curl_multi_info_read(_curl_multi, &num_msgs))) {
-               if (msg->msg == CURLMSG_DONE) {
-                       if (msg->easy_handle == _autoroute_data.curl_easy) {
-                               /* This is the autoroute download. */
-                               /* Now, parse the autoroute and update the display. */
-                               if (_autoroute_data.enabled
-                                   && parse_gpx(&_route,
-                                                _autoroute_data.rdl_data.bytes,
-                                                _autoroute_data.rdl_data.
-                                                bytes_read, 0)) {
-                                       /* Find the nearest route point, if we're connected. */
-                                       route_find_nearest_point();
-                                       map_force_redraw();
-                               }
-                               cancel_autoroute(TRUE); /* We're done. Clean up. */
-                       } else {
-                               ProgressUpdateInfo *pui = g_hash_table_lookup(_pui_by_easy, msg->easy_handle);
-                               g_queue_push_head(_curl_easy_queue, msg->easy_handle);
-                               g_hash_table_remove(_pui_by_easy, msg->easy_handle);
-                               fclose(pui->file);
-                               if (msg->data.result != CURLE_OK)
-                                       g_unlink(pui->dest_str);        /* Delete so we try again. */
-                               curl_multi_remove_handle(_curl_multi, msg->easy_handle);
-                               g_idle_add_full(G_PRIORITY_HIGH_IDLE,
-                                               (GSourceFunc)map_download_idle_refresh, pui, NULL);
-                       }
-               }
-       }
+guint tw, th;
 
-       /* Up to 1 transfer per tile. */
-       while (num_transfers < (BUF_WIDTH_TILES * BUF_HEIGHT_TILES)
-              && g_tree_nnodes(_pui_tree)) {
-               ProgressUpdateInfo *pui;
-               g_tree_foreach(_pui_tree, (GTraverseFunc) get_next_pui, &pui);
-
-               if (pui->retries) {
-                       /* This is a download. */
-                       FILE *f;
-                       g_tree_steal(_pui_tree, pui);
-                       g_tree_insert(_downloading_tree, pui, pui);
-
-                       pui->src_str = map_construct_url(pui->tilex, pui->tiley, pui->zoom);
-                       pui->dest_str = g_strdup_printf("%s/%u/%u/%u.jpg",
-                                           pui->repo->cache_dir, pui->zoom,
-                                           pui->tilex, pui->tiley);
-
-                       if (!pui->src_str) {
-                               /* Failed to generate URL. */
-                               g_idle_add_full(G_PRIORITY_HIGH_IDLE,(GSourceFunc)map_download_idle_refresh, pui, NULL);
-                               continue;
-                       }
+tw=TILE_SIZE_PIXELS*((widget->allocation.width/TILE_SIZE_PIXELS)+2);
+th=TILE_SIZE_PIXELS*((widget->allocation.height/TILE_SIZE_PIXELS)+2);
 
-                       /* Check to see if we need to overwrite. */
-                       if (pui->retries > 0) {
-                               /* We're not updating - check if file already exists. */
-                               if (g_file_test(pui->dest_str, G_FILE_TEST_EXISTS)) {
-                                       g_idle_add_full(G_PRIORITY_HIGH_IDLE,
-                                                       (GSourceFunc)map_download_idle_refresh, pui, NULL);
-                                       continue;
-                               }
-                       }
+if (map_pixmap==NULL) {
+       map_pixmap=gdk_pixmap_new(widget->window, tw, th, -1);
+} else {
+       if (tw>buf_width_pixels || th>buf_height_pixels) {
+               g_object_unref(map_pixmap);
+               map_pixmap=gdk_pixmap_new(widget->window, tw, th, -1);
+       } else if (tw<buf_width_pixels-(TILE_SIZE_PIXELS*2) || th<buf_height_pixels-(TILE_SIZE_PIXELS*2)) {
+               g_object_unref(map_pixmap);
+               map_pixmap=gdk_pixmap_new(widget->window, tw, th, -1);
+       }
+}
+g_assert(map_pixmap);
 
-                       /* Attempt to open the file for writing. */
-                       if (!(f = g_fopen(pui->dest_str, "w"))
-                           && errno == ENOENT) {
-                               /* Directory doesn't exist yet - create it, then we'll retry */
-                               gchar buffer[BUFFER_SIZE];
-                               snprintf(buffer, sizeof(buffer), "%s/%u/%u",
-                                        pui->repo->cache_dir, pui->zoom,
-                                        pui->tilex);
-                               g_mkdir_with_parents(buffer, 0775);
-                               f = g_fopen(pui->dest_str, "w");
-                       }
+buf_width_pixels=tw;
+buf_height_pixels=th;
+buf_width_tiles=buf_width_pixels/TILE_SIZE_PIXELS;
+buf_height_tiles=buf_height_pixels/TILE_SIZE_PIXELS;
 
-                       if (f) {
-                               CURL *curl_easy;
-                               pui->file = f;
-                               curl_easy = g_queue_pop_tail(_curl_easy_queue);
-                               if (!curl_easy) {
-                                       /* Need a new curl_easy. */
-                                       MACRO_CURL_EASY_INIT(curl_easy);
-                               }
-                               curl_easy_setopt(curl_easy, CURLOPT_URL, pui->src_str);
-                               curl_easy_setopt(curl_easy, CURLOPT_WRITEDATA, f);
-                               g_hash_table_insert(_pui_by_easy, curl_easy, pui);
-                               if (!_curl_multi) {
-                                       /* Initialize CURL. */
-                                       _curl_multi = curl_multi_init();
-                                       /*curl_multi_setopt(_curl_multi, CURLMOPT_PIPELINING, 1); */
-                               }
-                               curl_multi_add_handle(_curl_multi, curl_easy);
-                               num_transfers++;
-                       } else {
-                               /* Unable to download file. */
-                               gchar buffer[BUFFER_SIZE];
-                               snprintf(buffer, sizeof(buffer), "%s:\n%s",
-                                        _("Failed to open file for writing"),
-                                        pui->dest_str);
-                               MACRO_BANNER_SHOW_INFO(_window, buffer);
-                               g_idle_add_full(G_PRIORITY_HIGH_IDLE,
-                                               (GSourceFunc)map_download_idle_refresh, pui,
-                                               NULL);
-                               continue;
-                       }
-               } else if (--deletes_left) {
-                       /* This is a delete. */
-                       gchar buffer[BUFFER_SIZE];
-                       g_tree_steal(_pui_tree, pui);
-                       g_tree_insert(_downloading_tree, pui, pui);
-
-                       snprintf(buffer, sizeof(buffer), "%s/%u/%u/%u.jpg",
-                                pui->repo->cache_dir, pui->zoom, pui->tilex,
-                                pui->tiley);
-                       g_unlink(buffer);
-                       g_idle_add_full(G_PRIORITY_HIGH_IDLE,
-                                       (GSourceFunc)map_download_idle_refresh,
-                                       pui, NULL);
-               } else
-                       break;
-       }
+_screen_width_pixels = widget->allocation.width;
+_screen_height_pixels = widget->allocation.height;
+_screen_grids_halfwidth = pixel2grid(_screen_width_pixels) / 2;
+_screen_grids_halfheight = pixel2grid(_screen_height_pixels) / 2;
 
-       if (!(num_transfers || g_tree_nnodes(_pui_tree))) {
-               /* Destroy curl after 50 counts (5 seconds). */
-               if (--destroy_counter) {
-                       /* Clean up curl. */
-                       CURL *curr;
-                       while ((curr = g_queue_pop_tail(_curl_easy_queue)))
-                               curl_easy_cleanup(curr);
+/* Set scale_rect. */
+scale_rect.x = (_screen_width_pixels - SCALE_WIDTH) / 2;
+scale_rect.width = SCALE_WIDTH;
 
-                       curl_multi_cleanup(_curl_multi);
-                       _curl_multi = NULL;
+MACRO_RECALC_FOCUS_BASE(_center_ratio);
+MACRO_RECALC_FOCUS_SIZE(_center_ratio);
 
-                       _curl_sid = 0;
-                       vprintf("%s(): return FALSE\n", __PRETTY_FUNCTION__);
-                       return FALSE;
-               }
-       } else
-               destroy_counter = 50;
+_min_center.unitx = pixel2unit(grid2pixel(_screen_grids_halfwidth));
+_min_center.unity = pixel2unit(grid2pixel(_screen_grids_halfheight));
+_max_center.unitx = WORLD_SIZE_UNITS - grid2unit(_screen_grids_halfwidth) - 1;
+_max_center.unity = WORLD_SIZE_UNITS - grid2unit(_screen_grids_halfheight) - 1;
 
-       vprintf("%s(): return TRUE (%d, %d)\n", __PRETTY_FUNCTION__,
-               num_transfers, g_tree_nnodes(_pui_tree));
-       return TRUE;
+map_force_redraw();
+return TRUE;
 }
 
 /**
  * Draw the current mark (representing the current GPS location) onto
  * _map_widget.  This method does not queue the draw area.
  */
-void map_draw_mark()
+static void 
+map_draw_mark(Gps *gps)
 {
-       printf("%s()\n", __PRETTY_FUNCTION__);
-
-       gdk_draw_arc(_map_widget->window, _conn_state == RCVR_FIXED ? _gc[COLORABLE_MARK] : _gc[COLORABLE_MARK_OLD], FALSE,     /* not filled. */
-                    _mark_x1 - _draw_width, _mark_y1 - _draw_width,
-                    2 * _draw_width, 2 * _draw_width, 0, 360 * 64);
-       gdk_draw_line(_map_widget->window,
-                     _conn_state == RCVR_FIXED ? (_show_velvec ? _gc[COLORABLE_MARK_VELOCITY] : _gc[COLORABLE_MARK]) : _gc[COLORABLE_MARK_OLD],
-                     _mark_x1, _mark_y1, _mark_x2, _mark_y2);
-
-       vprintf("%s(): return\n", __PRETTY_FUNCTION__);
+gdk_draw_arc(_map_widget->window, gps->io.conn == RCVR_FIXED ? _gc[COLORABLE_MARK] : _gc[COLORABLE_MARK_OLD], FALSE,
+     mark_x1 - _draw_width, mark_y1 - _draw_width, 2 * _draw_width, 2 * _draw_width, 0, 360 * 64);
+gdk_draw_line(_map_widget->window, gps->io.conn == RCVR_FIXED ? (_show_velvec ? _gc[COLORABLE_MARK_VELOCITY] : _gc[COLORABLE_MARK]) : _gc[COLORABLE_MARK_OLD],
+      mark_x1, mark_y1, mark_x2, mark_y2);
 }
 
 /**
  * "Set" the mark, which translates the current GPS position into on-screen
  * units in preparation for drawing the mark with map_draw_mark().
  */
-void map_set_mark()
+void 
+map_set_mark(GpsData *gps)
 {
-       printf("%s()\n", __PRETTY_FUNCTION__);
-
-       _mark_x1 = unit2x(_pos.unitx);
-       _mark_y1 = unit2y(_pos.unity);
-       _mark_x2 = _mark_x1 + (_show_velvec ? _vel_offsetx : 0);
-       _mark_y2 = _mark_y1 + (_show_velvec ? _vel_offsety : 0);
-       _mark_minx = MIN(_mark_x1, _mark_x2) - (2 * _draw_width);
-       _mark_miny = MIN(_mark_y1, _mark_y2) - (2 * _draw_width);
-       _mark_width = abs(_mark_x1 - _mark_x2) + (4 * _draw_width);
-       _mark_height = abs(_mark_y1 - _mark_y2) + (4 * _draw_width);
-
-       vprintf("%s(): return\n", __PRETTY_FUNCTION__);
+mark_x1 = unit2x(gps->unitx);
+mark_y1 = unit2y(gps->unity);
+mark_x2 = mark_x1 + (_show_velvec ? gps->vel_offsetx : 0);
+mark_y2 = mark_y1 + (_show_velvec ? gps->vel_offsety : 0);
+mark_minx = MIN(mark_x1, mark_x2) - (2 * _draw_width);
+mark_miny = MIN(mark_y1, mark_y2) - (2 * _draw_width);
+mark_width = abs(mark_x1 - mark_x2) + (4 * _draw_width);
+mark_height = abs(mark_y1 - mark_y2) + (4 * _draw_width);
 }
 
 /**
@@ -281,457 +324,225 @@ void map_set_mark()
  * this method, but I guess it's not general-purpose enough.
  */
 static void
-map_pixbuf_scale_inplace(GdkPixbuf * pixbuf, guint ratio_p2, guint src_x, guint src_y)
+map_pixbuf_scale_inplace(GdkPixbuf *pixbuf, guint ratio_p2, guint src_x, guint src_y)
 {
-       guint dest_x = 0, dest_y = 0, dest_dim = TILE_SIZE_PIXELS;
-       guint rowstride = gdk_pixbuf_get_rowstride(pixbuf);
-       guint n_channels = gdk_pixbuf_get_n_channels(pixbuf);
-       guchar *pixels = gdk_pixbuf_get_pixels(pixbuf);
+guint dest_x = 0, dest_y = 0, dest_dim = TILE_SIZE_PIXELS;
+guint rowstride = gdk_pixbuf_get_rowstride(pixbuf);
+guint n_channels = gdk_pixbuf_get_n_channels(pixbuf);
+guchar *pixels = gdk_pixbuf_get_pixels(pixbuf);
 
-       vprintf("%s(%d, %d, %d)\n", __PRETTY_FUNCTION__, ratio_p2, src_x, src_y);
+/* Sweep through the entire dest area, copying as necessary, but
+ * DO NOT OVERWRITE THE SOURCE AREA.  We'll copy it afterward. */
+do {
+       guint src_dim = dest_dim >> ratio_p2;
+       guint src_endx = src_x - dest_x + src_dim;
+       gint x, y;
 
-       /* Sweep through the entire dest area, copying as necessary, but
-        * DO NOT OVERWRITE THE SOURCE AREA.  We'll copy it afterward. */
-       do {
-               guint src_dim = dest_dim >> ratio_p2;
-               guint src_endx = src_x - dest_x + src_dim;
-               gint x, y;
+       for (y = dest_dim - 1; y >= 0; y--) {
+               guint src_offset_y, dest_offset_y;
 
-               for (y = dest_dim - 1; y >= 0; y--) {
-                       guint src_offset_y, dest_offset_y;
+               src_offset_y = (src_y + (y >> ratio_p2)) * rowstride;
+               dest_offset_y = (dest_y + y) * rowstride;
+               x = dest_dim - 1;
 
-                       src_offset_y = (src_y + (y >> ratio_p2)) * rowstride;
-                       dest_offset_y = (dest_y + y) * rowstride;
-                       x = dest_dim - 1;
+               if ((unsigned)(dest_y + y - src_y) < src_dim && (unsigned)(dest_x + x - src_x) < src_dim)
+                       x -= src_dim;
 
-                       if ((unsigned)(dest_y + y - src_y) < src_dim
-                           && (unsigned)(dest_x + x - src_x) < src_dim)
-                               x -= src_dim;
-
-                       for (; x >= 0; x--) {
-                               guint src_offset, dest_offset, i;
+               for (; x >= 0; x--) {
+                       guint src_offset, dest_offset, i;
 
-                               src_offset = src_offset_y + (src_x + (x >> ratio_p2)) * n_channels;
-                               dest_offset = dest_offset_y + (dest_x + x) * n_channels;
+                       src_offset = src_offset_y + (src_x + (x >> ratio_p2)) * n_channels;
+                       dest_offset = dest_offset_y + (dest_x + x) * n_channels;
 
-                               pixels[dest_offset] = pixels[src_offset];
-                               for (i = n_channels - 1; i; i--)
-                                       pixels[dest_offset + i] = pixels[src_offset + i];
+                       pixels[dest_offset] = pixels[src_offset];
+                       for (i = n_channels - 1; i; i--)
+                               pixels[dest_offset + i] = pixels[src_offset + i];
 
-                               if ((unsigned)(dest_y + y - src_y) < src_dim && x == src_endx)
-                                       x -= src_dim;
-                       }
+                       if ((unsigned)(dest_y + y - src_y) < src_dim && x == src_endx)
+                               x -= src_dim;
                }
-               /* Reuse src_dim and src_endx to store new src_x and src_y. */
-               src_dim = src_x + ((src_x - dest_x) >> ratio_p2);
-               src_endx = src_y + ((src_y - dest_y) >> ratio_p2);
-               dest_x = src_x;
-               dest_y = src_y;
-               src_x = src_dim;
-               src_y = src_endx;
        }
-       while ((dest_dim >>= ratio_p2) > 1);
 
-       vprintf("%s(): return\n", __PRETTY_FUNCTION__);
+       /* Reuse src_dim and src_endx to store new src_x and src_y. */
+       src_dim = src_x + ((src_x - dest_x) >> ratio_p2);
+       src_endx = src_y + ((src_y - dest_y) >> ratio_p2);
+       dest_x = src_x;
+       dest_y = src_y;
+       src_x = src_dim;
+       src_y = src_endx;
+}
+while ((dest_dim >>= ratio_p2) > 1);
 }
 
 /**
  * Trim pixbufs that are bigger than tiles. (Those pixbufs result, when
  * captions should be cut off.)
  */
-static GdkPixbuf *pixbuf_trim(GdkPixbuf * pixbuf)
+static GdkPixbuf *
+pixbuf_trim(GdkPixbuf * pixbuf)
 {
-       vprintf("%s()\n", __PRETTY_FUNCTION__);
-       GdkPixbuf *mpixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, gdk_pixbuf_get_has_alpha(pixbuf),
-                          8, TILE_SIZE_PIXELS, TILE_SIZE_PIXELS);
-
-       gdk_pixbuf_copy_area(pixbuf,
-                            (gdk_pixbuf_get_width(pixbuf) - TILE_SIZE_PIXELS) / 2,
-                            (gdk_pixbuf_get_height(pixbuf) - TILE_SIZE_PIXELS) / 2, 
-                            TILE_SIZE_PIXELS,
-                            TILE_SIZE_PIXELS, mpixbuf, 0, 0);
-
-       g_object_unref(pixbuf);
-       vprintf("%s(): return\n", __PRETTY_FUNCTION__);
-       return mpixbuf;
+GdkPixbuf *mpixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, gdk_pixbuf_get_has_alpha(pixbuf),
+                  8, TILE_SIZE_PIXELS, TILE_SIZE_PIXELS);
+
+g_debug("TRIM!!");
+gdk_pixbuf_copy_area(pixbuf,
+                    (gdk_pixbuf_get_width(pixbuf) - TILE_SIZE_PIXELS) / 2,
+                    (gdk_pixbuf_get_height(pixbuf) - TILE_SIZE_PIXELS) / 2, 
+                    TILE_SIZE_PIXELS,
+                    TILE_SIZE_PIXELS, mpixbuf, 0, 0);
+
+g_object_unref(pixbuf);
+return mpixbuf;
 }
 
-/**
- * Given a wms uri pattern, compute the coordinate transformation and
- * trimming.
- * 'proj' is used for the conversion
- */
-static gchar *map_convert_wms_to_wms(gint tilex, gint tiley, gint zoomlevel,
-                             gchar * uri)
+GdkPixmap *
+map_pixmap_get(void)
 {
-       gint system_retcode;
-       gchar cmd[BUFFER_SIZE], srs[BUFFER_SIZE];
-       gchar *ret = NULL;
-       FILE *in;
-       gdouble lon1, lat1, lon2, lat2;
-
-       gchar *widthstr = strcasestr(uri, "WIDTH=");
-       gchar *heightstr = strcasestr(uri, "HEIGHT=");
-       gchar *srsstr = strcasestr(uri, "SRS=EPSG");
-       gchar *srsstre = strchr(srsstr, '&');
-       vprintf("%s()\n", __PRETTY_FUNCTION__);
-
-       /* missing: test if found */
-       strcpy(srs, "epsg");
-       strncpy(srs + 4, srsstr + 8, 256);
-       /* missing: test srsstre-srsstr < 526 */
-       srs[srsstre - srsstr - 4] = 0;
-       /* convert to lower, as WMC is EPSG and cs2cs is epsg */
-
-       gint dwidth = widthstr ? atoi(widthstr + 6) - TILE_SIZE_PIXELS : 0;
-       gint dheight = heightstr ? atoi(heightstr + 7) - TILE_SIZE_PIXELS : 0;
-
-       unit2latlon(tile2zunit(tilex, zoomlevel)
-                   - pixel2zunit(dwidth / 2, zoomlevel),
-                   tile2zunit(tiley + 1, zoomlevel)
-                   + pixel2zunit((dheight + 1) / 2, zoomlevel), lat1, lon1);
-
-       unit2latlon(tile2zunit(tilex + 1, zoomlevel)
-                   + pixel2zunit((dwidth + 1) / 2, zoomlevel),
-                   tile2zunit(tiley, zoomlevel)
-                   - pixel2zunit(dheight / 2, zoomlevel), lat2, lon2);
-
-       setlocale(LC_NUMERIC, "C");
-
-       snprintf(cmd, sizeof(cmd),
-                "(echo \"%.6f %.6f\"; echo \"%.6f %.6f\") | "
-                "/usr/bin/cs2cs +proj=longlat +datum=WGS84 +to +init=%s -f %%.6f "
-                " > /tmp/tmpcs2cs ", lon1, lat1, lon2, lat2, srs);
-       vprintf("Running command: %s\n", cmd);
-       system_retcode = system(cmd);
-
-       if (system_retcode)
-               g_printerr("cs2cs returned error code %d\n",
-                          WEXITSTATUS(system_retcode));
-       else if (!(in = g_fopen("/tmp/tmpcs2cs", "r")))
-               g_printerr("Cannot open results of conversion\n");
-       else if (5 !=
-                fscanf(in, "%lf %lf %s %lf %lf", &lon1, &lat1, cmd, &lon2, &lat2)) {
-               g_printerr("Wrong conversion\n");
-               fclose(in);
-       } else {
-               fclose(in);
-               ret = g_strdup_printf(uri, lon1, lat1, lon2, lat2);
-       }
-
-       setlocale(LC_NUMERIC, "");
-
-       vprintf("%s(): return %s\n", __PRETTY_FUNCTION__, ret);
-       return ret;
+return map_pixmap;
 }
 
-/**
- * Given the xyz coordinates of our map coordinate system, write the qrst
- * quadtree coordinates to buffer.
- */
-static void
-map_convert_coords_to_quadtree_string(gint x, gint y, gint zoomlevel,
-                                     gchar * buffer, const gchar initial,
-                                     const gchar * const quadrant)
+static GdkPixbuf *
+map_tile_load(guint tilex, guint tiley, gint zoff, gboolean download)
 {
-       gchar *ptr = buffer;
-       gint n;
-       vprintf("%s()\n", __PRETTY_FUNCTION__);
-
-       if (initial)
-               *ptr++ = initial;
-
-       for (n = 16 - zoomlevel; n >= 0; n--) {
-               gint xbit = (x >> n) & 1;
-               gint ybit = (y >> n) & 1;
-               *ptr++ = quadrant[xbit + 2 * ybit];
+GdkPixbuf *pixbuf;
+GError *error = NULL;
+gchar buffer[BUFFER_SIZE];
+gchar key[BUFFER_SIZE];
+struct stat tstat;
+gint se;
+
+g_snprintf(buffer, sizeof(buffer), "%s/%u/%u/%u.jpg", _curr_repo->cache_dir, _zoom + zoff, (tilex >> zoff), (tiley >> zoff));
+g_snprintf(key, sizeof(key), "%s/%u/%u/%u", _curr_repo->cache_dir, _zoom + zoff, (tilex >> zoff), (tiley >> zoff));
+
+pixbuf=image_cache_get(map_ic, key, buffer);
+if (!pixbuf) {
+       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);
        }
-       *ptr++ = '\0';
-       vprintf("%s(): return\n", __PRETTY_FUNCTION__);
+       return NULL;
 }
 
-/**
- * Construct the URL that we should fetch, based on the current URI format.
- * This method works differently depending on if a "%s" string is present in
- * the URI format, since that would indicate a quadtree-based map coordinate
- * system.
- */
-static gchar *map_construct_url(guint tilex, guint tiley, guint zoom)
-{
-       vprintf("%s()\n", __PRETTY_FUNCTION__);
-       switch (_curr_repo->type) {
-       case REPOTYPE_XYZ:
-               return g_strdup_printf(_curr_repo->url, tilex, tiley, zoom);
-
-       case REPOTYPE_XYZ_INV:
-               return g_strdup_printf(_curr_repo->url, 17 - zoom, tilex,
-                                      tiley);
-
-       case REPOTYPE_QUAD_QRST:
-               {
-                       gchar location[MAX_ZOOM + 2];
-                       map_convert_coords_to_quadtree_string(tilex, tiley,
-                                                             zoom, location,
-                                                             't', "qrts");
-                       return g_strdup_printf(_curr_repo->url, location);
-               }
-
-       case REPOTYPE_QUAD_ZERO:
-               {
-                       /* This is a zero-based quadtree URI. */
-                       gchar location[MAX_ZOOM + 2];
-                       map_convert_coords_to_quadtree_string(tilex, tiley,
-                                                             zoom, location,
-                                                             '\0', "0123");
-                       return g_strdup_printf(_curr_repo->url, location);
-               }
+g_object_ref(pixbuf);
 
-       case REPOTYPE_WMS:
-               return map_convert_wms_to_wms(tilex, tiley, zoom,
-                                             _curr_repo->url);
+#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
 
-       default:
-               return NULL;
+/* 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);
+                       }
+               }
        }
-       vprintf("%s(): ERROR\n", __PRETTY_FUNCTION__);
-       return "";
 }
 
-#ifdef WITH_OSSO
-void con_check_connection(void)
-{
-iap_connect();
-}
-#else
-void con_check_connection(void) 
-{
-/* NetworkManager support ? */
+return pixbuf;
 }
-#endif
 
-/**
- * Initiate a download of the given xyz coordinates using the given buffer
- * as the URL.  If the map already exists on disk, or if we are already
- * downloading the map, then this method does nothing.
- */
-void map_initiate_download(guint tilex, guint tiley, guint zoom, gint retries)
+gboolean
+map_render_tile(guint tilex, guint tiley, guint destx, guint desty, gboolean fast_fail)
 {
-       ProgressUpdateInfo *pui;
-       vprintf("%s(%u, %u, %u, %d)\n", __PRETTY_FUNCTION__, tilex, tiley, zoom,
-               retries);
-
-       con_check_connection();
-
-       pui = g_slice_new(ProgressUpdateInfo);
-       pui->tilex = tilex;
-       pui->tiley = tiley;
-       pui->zoom = zoom;
-       pui->priority = (abs((gint) tilex - unit2tile(_center.unitx))
-                        + abs((gint) tiley - unit2tile(_center.unity)));
-       if (!retries)
-               pui->priority = -pui->priority; /* "Negative" makes them lowest pri. */
-       pui->retries = retries;
-       pui->repo = _curr_repo;
-
-       if (g_tree_lookup(_pui_tree, pui)
-           || g_tree_lookup(_downloading_tree, pui)) {
-               /* Already downloading. */
-               g_slice_free(ProgressUpdateInfo, pui);
-               return;
-       }
-       pui->src_str = NULL;
-       pui->dest_str = NULL;
-       pui->file = NULL;
+GdkPixbuf *pixbuf=NULL;
+gint zoff;
 
-       g_tree_insert(_pui_tree, pui, pui);
-#ifdef WITH_OSSO
-       if (iap_is_connected() && !_curl_sid)
-#endif
-               _curl_sid = g_timeout_add(100, (GSourceFunc) curl_download_timeout, NULL);
+if (destx > buf_width_pixels || desty > buf_height_pixels)
+       return FALSE;
 
-       if (!_num_downloads++ && !_download_banner)
-               _download_banner = hildon_banner_show_progress(_window, NULL,_("Downloading maps"));
+if (tilex > _world_size_tiles || tiley > _world_size_tiles)
+       return FALSE;
 
-       vprintf("%s(): return\n", __PRETTY_FUNCTION__);
-}
+g_debug("MAP RT: %u %u (%u) %u %u (%u, %u)", tilex, tiley, _world_size_tiles, destx, desty, buf_width_tiles, buf_height_tiles);
 
-void
-map_render_tile(guint tilex, guint tiley, guint destx, guint desty,
-               gboolean fast_fail)
-{
-       GdkPixbuf *pixbuf = NULL;
-       gchar buffer[BUFFER_SIZE];
-       GError *error = NULL;
-       gint zoff;
-
-       vprintf("%s(%u, %u, %u, %u)\n", __PRETTY_FUNCTION__,
-               tilex, tiley, destx, desty);
-
-       if (tilex < _world_size_tiles && tiley < _world_size_tiles) {
-               /* The tile is possible. */
-               for (zoff = (_curr_repo->double_size ? 1 : 0);
-                    !pixbuf && (_zoom + zoff) <= MAX_ZOOM
-                    && zoff <= TILE_SIZE_P2; zoff += 1) {
-                       snprintf(buffer, sizeof(buffer), "%s/%u/%u/%u.jpg",
-                                _curr_repo->cache_dir, _zoom + zoff,
-                                (tilex >> zoff), (tiley >> zoff));
-
-                       pixbuf = gdk_pixbuf_new_from_file(buffer, &error);
-                       if (error || !pixbuf) {
-                               /* Delete so we try again some other day. */
-                               g_unlink(buffer);       
-                               pixbuf = NULL;
-                               error = NULL;
-
-                               /* Download, if we should. */
-                               if (_auto_download && _curr_repo->type != REPOTYPE_NONE &&
-                                   !((_zoom + zoff - (_curr_repo->double_size ? 1 : 0)) % _curr_repo->dl_zoom_steps)) {
-                                       if (!fast_fail)
-                                               map_initiate_download(tilex >> zoff,
-                                                                     tiley >> zoff,
-                                                                     _zoom + zoff,
-                                                                     -INITIAL_DOWNLOAD_RETRIES);
-                                       fast_fail = TRUE;
-                               }
-                       } else {
-                               /* 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);
-
-                               /* Check if we need to blit. */
-                               if (zoff) {
-                                       map_pixbuf_scale_inplace(pixbuf, zoff,
-                                                        (tilex - ((tilex >> zoff) << zoff)) << (TILE_SIZE_P2 - zoff),
-                                                        (tiley - ((tiley >> zoff) << zoff)) << (TILE_SIZE_P2 - zoff));
-                               }
-                       }
-               }
-       }
-
-       if (pixbuf) {
-               gdk_draw_pixbuf(_map_pixmap,
-                               _gc[COLORABLE_MARK],
-                               pixbuf, 0, 0, destx, desty,
-                               TILE_SIZE_PIXELS, TILE_SIZE_PIXELS,
-                               GDK_RGB_DITHER_NONE, 0, 0);
-               g_object_unref(pixbuf);
+/* The tile is possible. */
+for (zoff = (_curr_repo->double_size ? 1 : 0); !pixbuf && (_zoom + zoff) <= map_max_zoom && zoff <= TILE_SIZE_P2; zoff += 1) {
+       pixbuf=map_tile_load(tilex, tiley, zoff, !fast_fail);
+       if (!pixbuf) {
+               if (!fast_fail)
+                       fast_fail=TRUE;
        } else {
-               gdk_draw_rectangle(_map_pixmap, _map_widget->style->black_gc,
-                                  TRUE, destx, desty, TILE_SIZE_PIXELS, TILE_SIZE_PIXELS);
+               /* Check if we need to blit. */
+               if (zoff) {
+                       map_pixbuf_scale_inplace(pixbuf, zoff,
+                                       (tilex - ((tilex >> zoff) << zoff)) << (TILE_SIZE_P2 - zoff),
+                                       (tiley - ((tiley >> zoff) << zoff)) << (TILE_SIZE_P2 - zoff));
+                       image_cache_invalidate_by_image(map_ic, pixbuf);
+               }
        }
-
-       vprintf("%s(): return\n", __PRETTY_FUNCTION__);
 }
 
-gboolean map_render_tile_idle(map_tile_rdata *mtr)
-{
-map_render_tile(mtr->tilex, mtr->tiley, mtr->destx, mtr->desty, FALSE);
-gtk_widget_queue_draw_area(_map_widget, mtr->destx, mtr->desty, TILE_SIZE_PIXELS, TILE_SIZE_PIXELS);
-
-g_slice_free(map_tile_rdata, mtr);
-return FALSE;
+if (pixbuf) {
+       gdk_draw_pixbuf(map_pixmap, _gc[COLORABLE_MARK], pixbuf, 0, 0, destx, desty, TILE_SIZE_PIXELS, TILE_SIZE_PIXELS, GDK_RGB_DITHER_NONE, 0, 0);
+       g_object_unref(pixbuf);
+       return TRUE;
+}
+gdk_draw_rectangle(map_pixmap, _map_widget->style->black_gc, TRUE, destx, desty, TILE_SIZE_PIXELS, TILE_SIZE_PIXELS);
+return TRUE;
 }
 
-gboolean map_download_idle_refresh(ProgressUpdateInfo * pui)
+void
+map_render_data(void)
 {
-       vprintf("%s(%p, %s)\n", __PRETTY_FUNCTION__, pui, pui->src_str);
-
-       /* Test if download succeeded (only if retries != 0). */
-       if (!pui->retries || g_file_test(pui->dest_str, G_FILE_TEST_EXISTS)) {
-               gint zoom_diff = pui->zoom - _zoom;
-               /* Only refresh at same or "lower" (more detailed) zoom level. */
-               if (zoom_diff >= 0) {
-                       /* If zoom has changed since we first put in the request for
-                        * this tile, then we may have to update more than one tile. */
-                       guint tilex, tiley, tilex_end, tiley_end;
-                       for (tilex = pui->tilex << zoom_diff,
-                            tilex_end = tilex + (1 << zoom_diff);
-                            tilex < tilex_end; tilex++) {
-                               for (tiley = pui->tiley << zoom_diff,
-                                    tiley_end = tiley + (1 << zoom_diff);
-                                    tiley < tiley_end; tiley++) {
-                                       if ((tilex - _base_tilex) < BUF_WIDTH_TILES && (tiley - _base_tiley) < BUF_HEIGHT_TILES) {
-                                               map_render_tile(tilex, tiley,
-                                                               ((tilex - _base_tilex) << TILE_SIZE_P2),
-                                                               ((tiley - _base_tiley) << TILE_SIZE_P2),
-                                                               TRUE);
-                                               MACRO_MAP_RENDER_DATA();
-                                               gtk_widget_queue_draw_area
-                                                   (_map_widget,
-                                                    ((tilex - _base_tilex) << TILE_SIZE_P2) - _offsetx,
-                                                    ((tiley - _base_tiley) << TILE_SIZE_P2) - _offsety,
-                                                    TILE_SIZE_PIXELS, TILE_SIZE_PIXELS);
-                                       }
-                               }
-                       }
-               }
-       }
-       /* Else the download failed. Update retries and maybe try again. */
-       else {
-               if (pui->retries > 0)
-                       --pui->retries;
-               else if (pui->retries < 0)
-                       ++pui->retries;
-               if (pui->retries) {
-                       /* removal automatically calls progress_update_info_free(). */
-                       g_tree_steal(_downloading_tree, pui);
-                       g_tree_insert(_pui_tree, pui, pui);
-#ifdef WITH_OSSO
-                       if (iap_is_connected() && !_curl_sid)
-#endif
-                               _curl_sid = g_timeout_add(100,
-                                                         (GSourceFunc)
-                                                         curl_download_timeout,
-                                                         NULL);
-                       /* Don't do anything else. */
-                       return FALSE;
-               } else {
-                       /* No more retries left - something must be wrong. */
-                       MACRO_BANNER_SHOW_INFO(_window,
-                                              _("Error in download.  Check internet connection"
-                                               " and/or Map Repository URL Format."));
-               }
-       }
+/* Don't update if dragging, too much lag */
+if (map_drag_id>0)
+       return;
 
-       /* removal automatically calls progress_update_info_free(). */
-       g_tree_remove(_downloading_tree, pui);
+if(_show_poi)
+       map_render_all_pois(buf_width_pixels, buf_height_pixels);
 
-       if (++_curr_download == _num_downloads) {
-#ifdef WITH_HILDON
-               gtk_widget_destroy(_download_banner);
-               _download_banner = NULL;
-#else
-               gtk_widget_hide(GTK_WIDGET(_progress_item));
-#endif
-               _num_downloads = _curr_download = 0;
-       } else
-               hildon_banner_set_fraction(HILDON_BANNER(_download_banner),
-                                          _curr_download / (double)_num_downloads);
+if (_home.valid)
+       map_draw_position_icon(&_home, "home");
 
-       vprintf("%s(): return\n", __PRETTY_FUNCTION__);
-       return FALSE;
+if (_dest.valid)
+       map_draw_position_icon(&_dest, "destination");
+
+if(_show_tracks>0)
+       map_render_paths();
 }
 
 /**
- * Force a redraw of the entire _map_pixmap, including fetching the
+ * Force a redraw of the entire map_pixmap, including fetching the
  * background maps from disk and redrawing the tracks on top of them.
  */
-void map_force_redraw()
+void 
+map_force_redraw(void)
 {
-       guint new_x, new_y;
-       printf("%s()\n", __PRETTY_FUNCTION__);
-
-       for (new_y = 0; new_y < BUF_HEIGHT_TILES; ++new_y)
-               for (new_x = 0; new_x < BUF_WIDTH_TILES; ++new_x) {
-                       map_render_tile(_base_tilex + new_x,
-                                       _base_tiley + new_y,
-                                       new_x * TILE_SIZE_PIXELS,
-                                       new_y * TILE_SIZE_PIXELS, FALSE);
-               }
-       MACRO_MAP_RENDER_DATA();
-       MACRO_QUEUE_DRAW_AREA();
+guint new_x, new_y;
+#ifdef DEBUG
+gulong tms;
 
-       vprintf("%s(): return\n", __PRETTY_FUNCTION__);
+g_timer_start(map_timer);
+#endif
+for (new_y = 0; new_y < buf_height_tiles; ++new_y)
+       for (new_x = 0; new_x < buf_width_tiles; ++new_x) {
+               map_render_tile(_base_tilex + new_x, _base_tiley + new_y,
+                               new_x * TILE_SIZE_PIXELS, new_y * TILE_SIZE_PIXELS, FALSE);
+       }
+#ifdef DEBUG
+g_timer_stop(map_timer);
+g_debug("Full redraw: %f sec\n", g_timer_elapsed(map_timer, &tms));
+#endif
+
+map_render_data();
+MACRO_QUEUE_DRAW_AREA();
+}
+
+guint
+map_get_zoom(void)
+{
+return _zoom;
 }
 
 /**
@@ -739,175 +550,172 @@ void map_force_redraw()
  * current zoom level, or if the new zoom is invalid
  * (not MIN_ZOOM <= new_zoom < MAX_ZOOM), then this method does nothing.
  */
-void map_set_zoom(guint new_zoom)
+gboolean 
+map_set_zoom(guint new_zoom)
 {
-       printf("%s(%d)\n", __PRETTY_FUNCTION__, _zoom);
-
-       /* Note that, since new_zoom is a guint and MIN_ZOOM is 0, this if
-        * condition also checks for new_zoom >= MIN_ZOOM. */
-       if (new_zoom > (MAX_ZOOM - 1))
-               return;
-       if (new_zoom == _zoom)
-               return;
-
-       _zoom = new_zoom / _curr_repo->view_zoom_steps
-           * _curr_repo->view_zoom_steps;
-       _world_size_tiles = unit2tile(WORLD_SIZE_UNITS);
-
-       /* If we're leading, update the center to reflect new zoom level. */
-       MACRO_RECALC_CENTER(_center.unitx, _center.unity);
+/* Note that, since new_zoom is a guint and MIN_ZOOM is 0, this if
+ * condition also checks for new_zoom >= MIN_ZOOM. */
+if (new_zoom > (map_max_zoom - 1))
+       return FALSE;
+if (new_zoom == _zoom)
+       return FALSE;
 
-       /* Update center bounds to reflect new zoom level. */
-       _min_center.unitx = pixel2unit(grid2pixel(_screen_grids_halfwidth));
-       _min_center.unity = pixel2unit(grid2pixel(_screen_grids_halfheight));
-       _max_center.unitx = WORLD_SIZE_UNITS - grid2unit(_screen_grids_halfwidth) - 1;
-       _max_center.unity = WORLD_SIZE_UNITS - grid2unit(_screen_grids_halfheight) - 1;
+_zoom = new_zoom / _curr_repo->view_zoom_steps * _curr_repo->view_zoom_steps;
+_world_size_tiles = unit2tile(WORLD_SIZE_UNITS);
 
-       BOUND(_center.unitx, _min_center.unitx, _max_center.unitx);
-       BOUND(_center.unity, _min_center.unity, _max_center.unity);
+/* If we're leading, update the center to reflect new zoom level. */
+MACRO_RECALC_CENTER(_gps->data, _center.unitx, _center.unity);
 
-       _base_tilex = grid2tile((gint) pixel2grid((gint) unit2pixel((gint) _center.unitx))
-                               - (gint) _screen_grids_halfwidth);
-       _base_tiley = grid2tile(pixel2grid(unit2pixel(_center.unity))
-                               - _screen_grids_halfheight);
+/* Update center bounds to reflect new zoom level. */
+_min_center.unitx = pixel2unit(grid2pixel(_screen_grids_halfwidth));
+_min_center.unity = pixel2unit(grid2pixel(_screen_grids_halfheight));
+_max_center.unitx = WORLD_SIZE_UNITS - grid2unit(_screen_grids_halfwidth) - 1;
+_max_center.unity = WORLD_SIZE_UNITS - grid2unit(_screen_grids_halfheight) - 1;
 
-       /* New zoom level, so we can't reuse the old buffer's pixels. */
+BOUND(_center.unitx, _min_center.unitx, _max_center.unitx);
+BOUND(_center.unity, _min_center.unity, _max_center.unity);
 
-       /* Update state variables. */
-       MACRO_RECALC_OFFSET();
-       MACRO_RECALC_FOCUS_BASE();
-       MACRO_RECALC_FOCUS_SIZE();
+_base_tilex = grid2tile((gint) pixel2grid((gint) unit2pixel((gint) _center.unitx)) - (gint) _screen_grids_halfwidth);
+_base_tiley = grid2tile(pixel2grid(unit2pixel(_center.unity)) - _screen_grids_halfheight);
 
-       map_set_mark();
-       map_force_redraw();
+/* New zoom level, so we can't reuse the old buffer's pixels. */
+/* Update state variables. */
+MACRO_RECALC_OFFSET();
+MACRO_RECALC_FOCUS_BASE(_center_ratio);
+MACRO_RECALC_FOCUS_SIZE(_center_ratio);
 
-       vprintf("%s(): return\n", __PRETTY_FUNCTION__);
+map_set_mark(&_gps->data);
+map_force_redraw();
+return TRUE;
 }
 
 /**
  * Center the view on the given unitx/unity.
  */
-void map_center_unit(guint new_center_unitx, guint new_center_unity)
+void 
+map_center_unit(guint new_center_unitx, guint new_center_unity)
 {
-       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;
-       printf("%s(%d, %d)\n", __PRETTY_FUNCTION__,
-              new_center_unitx, new_center_unity);
-
-       /* Assure that _center.unitx/y are bounded. */
-       BOUND(new_center_unitx, _min_center.unitx, _max_center.unitx);
-       BOUND(new_center_unity, _min_center.unity, _max_center.unity);
-
-       _center.unitx = new_center_unitx;
-       _center.unity = new_center_unity;
-
-       new_base_tilex = grid2tile((gint) pixel2grid((gint)unit2pixel((gint) _center.unitx))
-                                  - (gint)_screen_grids_halfwidth);
-       new_base_tiley = grid2tile(pixel2grid(unit2pixel(_center.unity))
-                                  - _screen_grids_halfheight);
-
-       /* Same zoom level, so it's likely that we can reuse some of the old
-        * buffer's pixels. */
-
-       if (new_base_tilex != _base_tilex || new_base_tiley != _base_tiley) {
-               /* If copying from old parts to new parts, we need to make sure we
-                * don't overwrite the old parts when copying, so set up new_x,
-                * new_y, old_x, old_y, iox, and ioy with that in mind. */
-               if (new_base_tiley < _base_tiley) {
-                       /* New is lower than old - start at bottom and go up. */
-                       new_y = BUF_HEIGHT_TILES - 1;
-                       ioy = -1;
-               } else {
-                       /* New is higher than old - start at top and go down. */
-                       new_y = 0;
-                       ioy = 1;
-               }
-               if (new_base_tilex < _base_tilex) {
-                       /* New is righter than old - start at right and go left. */
-                       base_new_x = BUF_WIDTH_TILES - 1;
-                       iox = -1;
-               } else {
-                       /* New is lefter than old - start at left and go right. */
-                       base_new_x = 0;
-                       iox = 1;
-               }
+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;
+
+/* Assure that _center.unitx/y are bounded. */
+BOUND(new_center_unitx, _min_center.unitx, _max_center.unitx);
+BOUND(new_center_unity, _min_center.unity, _max_center.unity);
+
+_center.unitx = new_center_unitx;
+_center.unity = new_center_unity;
+
+new_base_tilex = grid2tile((gint) pixel2grid((gint)unit2pixel((gint) _center.unitx)) - (gint)_screen_grids_halfwidth);
+new_base_tiley = grid2tile(pixel2grid(unit2pixel(_center.unity)) - _screen_grids_halfheight);
+
+/* Same zoom level, so it's likely that we can reuse some of the old
+ * buffer's pixels. */
+
+if (new_base_tilex != _base_tilex || new_base_tiley != _base_tiley) {
+       /* If copying from old parts to new parts, we need to make sure we
+        * don't overwrite the old parts when copying, so set up new_x,
+        * new_y, old_x, old_y, iox, and ioy with that in mind. */
+       if (new_base_tiley < _base_tiley) {
+               /* New is lower than old - start at bottom and go up. */
+               new_y = buf_height_tiles - 1;
+               ioy = -1;
+       } else {
+               /* New is higher than old - start at top and go down. */
+               new_y = 0;
+               ioy = 1;
+       }
+       if (new_base_tilex < _base_tilex) {
+               /* New is righter than old - start at right and go left. */
+               base_new_x = buf_width_tiles - 1;
+               iox = -1;
+       } else {
+               /* New is lefter than old - start at left and go right. */
+               base_new_x = 0;
+               iox = 1;
+       }
 
-               /* Iterate over the y tile values. */
-               old_y = new_y + new_base_tiley - _base_tiley;
-               base_old_x = base_new_x + new_base_tilex - _base_tilex;
-               _base_tilex = new_base_tilex;
-               _base_tiley = new_base_tiley;
-               for (j = 0; j < BUF_HEIGHT_TILES;
-                    ++j, new_y += ioy, old_y += ioy) {
-                       new_x = base_new_x;
-                       old_x = base_old_x;
-                       /* Iterate over the x tile values. */
-                       for (k = 0; k < BUF_WIDTH_TILES;
-                            ++k, new_x += iox, old_x += iox) {
-                               /* Can we get this grid block from the old buffer?. */
-                               if (old_x >= 0 && old_x < BUF_WIDTH_TILES
-                                   && old_y >= 0 && old_y < BUF_HEIGHT_TILES) {
-                                       /* Copy from old buffer to new buffer. */
-                                       gdk_draw_drawable(_map_pixmap,
-                                                         _gc[COLORABLE_MARK],
-                                                         _map_pixmap,
-                                                         old_x * TILE_SIZE_PIXELS,
-                                                         old_y * TILE_SIZE_PIXELS,
-                                                         new_x * TILE_SIZE_PIXELS,
-                                                         new_y * TILE_SIZE_PIXELS,
-                                                         TILE_SIZE_PIXELS,
-                                                         TILE_SIZE_PIXELS);
-                               } else {
-#if 0
-                                       map_tile_rdata *mtr;
-
-                                       mtr=g_slice_new(map_tile_rdata);
-                                       mtr->tilex=new_base_tilex + new_x;
-                                       mtr->tiley=new_base_tiley + new_y;
-                                       mtr->destx=new_x * TILE_SIZE_PIXELS;
-                                       mtr->desty=new_y * TILE_SIZE_PIXELS;
-
-                               gdk_draw_rectangle(_map_pixmap, _gc[COLORABLE_MARK_VELOCITY],
-                      TRUE, mtr->destx, mtr->desty, TILE_SIZE_PIXELS, TILE_SIZE_PIXELS);
-
-                                       g_idle_add_full(G_PRIORITY_HIGH_IDLE,(GSourceFunc)map_render_tile_idle, mtr,NULL);
-#else
-                                       map_render_tile(new_base_tilex + new_x,
-                                                       new_base_tiley + new_y,
-                                                       new_x * TILE_SIZE_PIXELS,
-                                                       new_y * TILE_SIZE_PIXELS,
-                                                       FALSE);
-#endif
-                               }
+       /* Iterate over the y tile values. */
+       old_y = new_y + new_base_tiley - _base_tiley;
+       base_old_x = base_new_x + new_base_tilex - _base_tilex;
+       _base_tilex = new_base_tilex;
+       _base_tiley = new_base_tiley;
+
+       for (j = 0; j < buf_height_tiles; ++j, new_y += ioy, old_y += ioy) {
+               new_x = base_new_x;
+               old_x = base_old_x;
+               /* Iterate over the x tile values. */
+               for (k = 0; k < buf_width_tiles; ++k, new_x += iox, old_x += iox) {
+                       /* Can we get this grid block from the old buffer?. */
+                       if (old_x >= 0 && old_x < buf_width_tiles && old_y >= 0 && old_y < buf_height_tiles) {
+                               /* Copy from old buffer to new buffer. */
+                               gdk_draw_drawable(map_pixmap,
+                                                 _gc[COLORABLE_MARK],
+                                                 map_pixmap,
+                                                 old_x * TILE_SIZE_PIXELS,
+                                                 old_y * TILE_SIZE_PIXELS,
+                                                 new_x * TILE_SIZE_PIXELS,
+                                                 new_y * TILE_SIZE_PIXELS,
+                                                 TILE_SIZE_PIXELS,
+                                                 TILE_SIZE_PIXELS);
+                       } else {
+                               map_render_tile(new_base_tilex + new_x,
+                                               new_base_tiley + new_y,
+                                               new_x * TILE_SIZE_PIXELS,
+                                               new_y * TILE_SIZE_PIXELS,
+                                               map_drag_id!=0 ? TRUE : FALSE);
                        }
                }
-               MACRO_MAP_RENDER_DATA();
        }
+       map_render_data();
+}
 
-       MACRO_RECALC_OFFSET();
-       MACRO_RECALC_FOCUS_BASE();
-
-       map_set_mark();
-       MACRO_QUEUE_DRAW_AREA();
+MACRO_RECALC_OFFSET();
+MACRO_RECALC_FOCUS_BASE(_center_ratio);
 
-       vprintf("%s(): return\n", __PRETTY_FUNCTION__);
+map_set_mark(&_gps->data);
+MACRO_QUEUE_DRAW_AREA();
 }
 
 /**
  * Pan the view by the given number of units in the X and Y directions.
  */
-void map_pan(gint delta_unitx, gint delta_unity)
+void 
+map_pan(gint delta_unitx, gint delta_unity)
 {
-       printf("%s(%d, %d)\n", __PRETTY_FUNCTION__, delta_unitx, delta_unity);
+if (_center_mode > 0)
+       set_action_activate("autocenter_none", TRUE);
 
-       if (_center_mode > 0)
-               gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM
-                                              (_menu_ac_none_item), TRUE);
-       map_center_unit(_center.unitx + delta_unitx,
-                       _center.unity + delta_unity);
+map_center_unit(_center.unitx + delta_unitx, _center.unity + delta_unity);
+}
+
+/**
+ * Helper to center map on given lat/lon
+ */
+void
+map_center_latlon(gdouble lat, gdouble lon)
+{
+guint unitx, unity;
+
+latlon2unit(lat, lon, unitx, unity);
+map_center_unit(unitx, unity);
+}
+
+/**
+ * Helper to goto given point and update location
+ * 
+ */
+gboolean
+map_goto_position(Position *pos)
+{
+if (pos->valid==FALSE)
+       return FALSE;
 
-       vprintf("%s(): return\n", __PRETTY_FUNCTION__);
+_center_mode=CENTER_MANUAL;
+map_center_latlon(pos->lat, pos->lon);
+map_set_autozoom(FALSE, 0);
+g_idle_add_full(G_PRIORITY_HIGH_IDLE,(GSourceFunc)map_update_location_from_center, NULL, NULL);
+return TRUE;
 }
 
 /**
@@ -916,411 +724,491 @@ void map_pan(gint delta_unitx, gint delta_unity)
  * drawing of the background map), then updates the mark, then queus the
  * draw area of the new mark.
  */
-void map_move_mark()
+void 
+map_move_mark(void)
 {
-       printf("%s()\n", __PRETTY_FUNCTION__);
-
-       /* Just queue the old and new draw areas. */
-       gtk_widget_queue_draw_area(_map_widget,
-                                  _mark_minx<0 ? 0 : _mark_minx,
-                                  _mark_miny<0 ? 0 : _mark_miny, 
-                                  _mark_width, _mark_height);
-       map_set_mark();
-       gtk_widget_queue_draw_area(_map_widget,
-                                  _mark_minx<0 ? 0 : _mark_minx,
-                                  _mark_miny<0 ? 0 : _mark_miny, 
-                                  _mark_width, _mark_height);
-
-       vprintf("%s(): return\n", __PRETTY_FUNCTION__);
+/* Just queue the old and new draw areas. */
+gtk_widget_queue_draw_area(_map_widget,
+                  mark_minx<0 ? 0 : mark_minx,
+                  mark_miny<0 ? 0 : mark_miny, 
+                  mark_width, mark_height);
+map_set_mark(&_gps->data);
+gtk_widget_queue_draw_area(_map_widget,
+                  mark_minx<0 ? 0 : mark_minx,
+                  mark_miny<0 ? 0 : mark_miny, 
+                  mark_width, mark_height);
 }
 
 gboolean
-map_update_location_from_gps()
+map_update_location_from_gps(Gps *gps)
 {
-map_update_location(_pos.unitx, _pos.unity, FALSE);
+g_assert(gps);
+map_update_location(gps->data.lat, gps->data.lon, FALSE);
 return FALSE;
 }
 
 gboolean
-map_update_location_from_center()
+map_update_location_from_center(void)
 {
+gdouble lat, lon;
 /* Force re-validation of place if user is clicking around */
 map_loc.valid=FALSE;
-map_update_location(_center.unitx, _center.unity, TRUE);
+/* XXX: hmm, not the right place for this */
+#if 0
+if (_gps->fix==FIX_NOFIX) {
+       _gps->data.unitx=_center.unitx;
+       _gps->data.unity=_center.unity;
+       unit2latlon(_gps->data.unitx, _gps->data.unity, _gps->data.lat, _gps->data.lon);
+}
+#endif
+unit2latlon(_center.unitx, _center.unity, lat, lon);
+map_update_location(lat, lon, TRUE);
 return FALSE;
 }
 
+void
+map_draw_pixbuf_on_buffer(gint x, gint y, GdkPixbuf *p)
+{
+if ((x < 0) || (y < 0))
+       return;
+
+if ((x > buf_width_pixels) || (y > buf_height_pixels))
+       return;
+
+gdk_draw_pixbuf(map_pixmap, _gc[COLORABLE_POI],
+       p, 0, 0,
+       x - gdk_pixbuf_get_width(p) / 2,
+       y - gdk_pixbuf_get_height(p) / 2,
+       -1, -1, GDK_RGB_DITHER_NONE, 0, 0);
+}
+
 /**
- * Make sure the mark is up-to-date.  This function triggers a panning of
- * the view if the mark is appropriately close to the edge of the view.
+ * Draw given pixbuf on map, centered on x,y
  */
-void refresh_mark()
+void
+map_draw_pixbuf(guint unitx, guint unity, GdkPixbuf *p)
 {
-       printf("%s()\n", __PRETTY_FUNCTION__);
+gint x,y;
+x=unit2bufx(unitx);
+y=unit2bufy(unity);
 
-       guint new_center_unitx;
-       guint new_center_unity;
+map_draw_pixbuf_on_buffer(x, y, p);
+}
 
-       MACRO_RECALC_CENTER(new_center_unitx, new_center_unity);
+/**
+ * Draw an icon at given Position.
+ * 
+ */
+static void
+map_draw_position_icon(Position *pos, const gchar *icon)
+{
+gint x, y;
+guint ux, uy;
+gchar buffer[128];
+GdkPixbuf *p;
 
-       if ((new_center_unitx - _focus.unitx) < _focus_unitwidth
-           && (new_center_unity - _focus.unity) < _focus_unitheight)
-               /* We're not changing the view - just move the mark. */
-               map_move_mark();
-       else
-               map_center_unit(new_center_unitx, new_center_unity);
+latlon2unit(pos->lat, pos->lon, ux, uy);
+x=unit2bufx(ux);
+y=unit2bufy(uy);
 
-       /* Draw speed info */
-       if (_speed_limit_on)
-               speed_limit();
+if ((x < 0) || (y < 0))
+       return;
 
-       if (_center_mode>0)
-               g_idle_add((GSourceFunc)map_update_location_from_gps, NULL);
+if ((x > buf_width_pixels) || (y > buf_height_pixels))
+       return;
 
-       vprintf("%s(): return\n", __PRETTY_FUNCTION__);
+g_snprintf(buffer, sizeof(buffer), "%s/pixmaps/mapper/%s.png", DATADIR, icon);
+p=gdk_pixbuf_new_from_file(buffer, NULL);
+if (!p)
+       return;
+
+map_draw_pixbuf_on_buffer(x, y, p);
+g_object_unref(p);
 }
 
 /**
- * Render a single track line to _map_pixmap.  If either point on the line
+ * Make sure the mark is up-to-date.  This function triggers a panning of
+ * the view if the mark is appropriately close to the edge of the view.
+ */
+void
+map_refresh_mark(void)
+{
+guint new_center_unitx;
+guint new_center_unity;
+
+MACRO_RECALC_CENTER(_gps->data, new_center_unitx, new_center_unity);
+
+if ((new_center_unitx - _focus.unitx) < _focus_unitwidth && (new_center_unity - _focus.unity) < _focus_unitheight)
+       /* We're not changing the view - just move the mark. */
+       map_move_mark();
+else
+       map_center_unit(new_center_unitx, new_center_unity);
+
+/* Draw speed info */
+if (_speed_on)
+       map_speed_draw();
+
+if (_center_mode>0)
+       g_idle_add_full(G_PRIORITY_HIGH_IDLE,(GSourceFunc)map_update_location_from_gps, _gps, NULL);
+}
+
+/**
+ * Render a single track line to map_pixmap.  If either point on the line
  * is a break (defined as unity == 0), a circle is drawn at the other point.
  * IT IS AN ERROR FOR BOTH POINTS TO INDICATE A BREAK.
  */
 void
-map_render_segment(GdkGC * gc_norm, GdkGC * gc_alt,
-                  guint unitx1, guint unity1, guint unitx2, guint unity2)
+map_render_segment(GdkGC *gc_norm, GdkGC *gc_alt, guint unitx1, guint unity1, guint unitx2, guint unity2)
 {
-       vprintf("%s()\n", __PRETTY_FUNCTION__);
-
-       if (!unity1) {
-               guint x2, y2;
-               x2 = unit2bufx(unitx2);
-               y2 = unit2bufy(unity2);
-               /* Make sure this circle will be visible. */
-               if ((x2 < BUF_WIDTH_PIXELS)
-                   && (y2 < BUF_HEIGHT_PIXELS))
-                       gdk_draw_arc(_map_pixmap, gc_alt, FALSE,        /* FALSE: not filled. */
-                                    x2 - _draw_width, y2 - _draw_width, 2 * _draw_width, 2 * _draw_width, 0,   /* start at 0 degrees. */
-                                    360 * 64);
-       } else if (!unity2) {
-               guint x1, y1;
-               x1 = unit2bufx(unitx1);
-               y1 = unit2bufy(unity1);
-               /* Make sure this circle will be visible. */
-               if ((x1 < BUF_WIDTH_PIXELS)
-                   && ((unsigned)y1 < BUF_HEIGHT_PIXELS))
-                       gdk_draw_arc(_map_pixmap, gc_alt, FALSE,        /* FALSE: not filled. */
-                                    x1 - _draw_width, y1 - _draw_width, 2 * _draw_width, 2 * _draw_width, 0,   /* start at 0 degrees. */
-                                    360 * 64);
-       } else {
-               gint x1, y1, x2, y2;
-               x1 = unit2bufx(unitx1);
-               y1 = unit2bufy(unity1);
-               x2 = unit2bufx(unitx2);
-               y2 = unit2bufy(unity2);
-               /* Make sure this line could possibly be visible. */
-               if (!((x1 > BUF_WIDTH_PIXELS && x2 > BUF_WIDTH_PIXELS)
-                     || (x1 < 0 && x2 < 0)
-                     || (y1 > BUF_HEIGHT_PIXELS && y2 > BUF_HEIGHT_PIXELS)
-                     || (y1 < 0 && y2 < 0)))
-                       gdk_draw_line(_map_pixmap, gc_norm, x1, y1, x2, y2);
+if (!unity1) {
+       guint x2, y2;
+
+       x2 = unit2bufx(unitx2);
+       y2 = unit2bufy(unity2);
+       /* Make sure this circle will be visible. */
+       if ((x2 < buf_width_pixels) && (y2 < buf_height_pixels))
+               gdk_draw_arc(map_pixmap, gc_alt, FALSE, /* FALSE: not filled. */
+                    x2 - _draw_width, y2 - _draw_width, 2 * _draw_width, 2 * _draw_width, 0,   /* start at 0 degrees. */
+                    360 * 64);
+} else if (!unity2) {
+       guint x1, y1;
+
+       x1 = unit2bufx(unitx1);
+       y1 = unit2bufy(unity1);
+       /* Make sure this circle will be visible. */
+       if ((x1 < buf_width_pixels) && ((unsigned)y1 < buf_height_pixels))
+               gdk_draw_arc(map_pixmap, gc_alt, FALSE, /* FALSE: not filled. */
+                    x1 - _draw_width, y1 - _draw_width, 2 * _draw_width, 2 * _draw_width, 0,   /* start at 0 degrees. */
+                    360 * 64);
+} else {
+       gint x1, y1, x2, y2;
+
+       x1 = unit2bufx(unitx1);
+       y1 = unit2bufy(unity1);
+       x2 = unit2bufx(unitx2);
+       y2 = unit2bufy(unity2);
+       /* Make sure this line could possibly be visible. */
+       if (!((x1 > buf_width_pixels && x2 > buf_width_pixels)
+             || (x1 < 0 && x2 < 0)
+             || (y1 > buf_height_pixels && y2 > buf_height_pixels)
+             || (y1 < 0 && y2 < 0)))
+               gdk_draw_line(map_pixmap, gc_norm, x1, y1, x2, y2);
        }
+}
 
-       vprintf("%s(): return\n", __PRETTY_FUNCTION__);
+void
+map_render_waypoint(guint x1, guint y1, GdkGC *gc)
+{
+if ((x1 > buf_width_pixels) || (y1 > buf_height_pixels))
+       return;
+gdk_draw_arc(map_pixmap, gc, FALSE, x1 - _draw_width, y1 - _draw_width, 2 * _draw_width, 2 * _draw_width, 0, 360 * 64);
 }
 
 /**
- * Render all track data onto the _map_pixmap.  Note that this does not
+ * Render all track data onto the map_pixmap.  Note that this does not
  * clear the pixmap of previous track data (use map_force_redraw() for
  * that), and also note that this method does not queue any redraws, so it
  * is up to the caller to decide which part of the track really needs to be
  * redrawn.
  */
-void map_render_path(Path * path, GdkGC ** gc)
+void 
+map_render_path(Path * path, GdkGC ** gc)
 {
-       Point *curr;
-       WayPoint *wcurr;
-       printf("%s()\n", __PRETTY_FUNCTION__);
-
-       /* gc is a pointer to the first GC to use (for plain points).  (gc + 1)
-        * is a pointer to the GC to use for waypoints, and (gc + 2) is a pointer
-        * to the GC to use for breaks. */
-
-       /* else there is a route to draw. */
-       for (curr = path->head, wcurr = path->whead; curr++ != path->tail;) {
-               /* Draw the line from (curr - 1) to (curr). */
-               map_render_segment(gc[0], gc[2],
-                                  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);
-                       if ((x1 < BUF_WIDTH_PIXELS)
-                           && (y1 < BUF_HEIGHT_PIXELS)) {
-                               gdk_draw_arc(_map_pixmap, gc[1], FALSE, /* FALSE: not filled. */
-                                            x1 - _draw_width, y1 - _draw_width, 2 * _draw_width, 2 * _draw_width, 0,   /* start at 0 degrees. */
-                                            360 * 64);
-                       }
-                       wcurr++;
-               }
-       }
+Point *curr;
+WayPoint *wcurr;
 
-       vprintf("%s(): return\n", __PRETTY_FUNCTION__);
+/* gc is a pointer to the first GC to use (for plain points).  (gc + 1)
+ * is a pointer to the GC to use for waypoints, and (gc + 2) is a pointer
+ * to the GC to use for breaks. */
+
+/* else there is a route to draw. */
+for (curr = path->head, wcurr = path->whead; curr++ != path->tail;) {
+       /* Draw the line from (curr - 1) to (curr). */
+       map_render_segment(gc[0], gc[2], 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);
+
+               map_render_waypoint(x1, y1, _gc[COLORABLE_TRACK_BREAK]);
+               wcurr++;
+       }
+}
 }
 
-void map_render_paths()
+void 
+map_render_paths(void)
 {
-       printf("%s()\n", __PRETTY_FUNCTION__);
-
-       if ((_show_tracks & ROUTES_MASK) && _route.head != _route.tail) {
-               map_render_path(&_route, _gc + COLORABLE_ROUTE);
-
-               /* Now, draw the next waypoint on top of all other waypoints. */
-               if (_next_way) {
-                       guint x1 = unit2bufx(_next_way->point->unitx);
-                       guint y1 = unit2bufy(_next_way->point->unity);
-                       if ((x1 < BUF_WIDTH_PIXELS)
-                           && (y1 < BUF_HEIGHT_PIXELS)) {
-                               /* Draw the next waypoint as a break. */
-                               gdk_draw_arc(_map_pixmap, _gc[COLORABLE_ROUTE_BREAK], FALSE,    /* FALSE: not filled. */
-                                            x1 - _draw_width, y1 - _draw_width, 2 * _draw_width, 2 * _draw_width, 0,   /* start at 0 degrees. */
-                                            360 * 64);
-                       }
+if ((_show_tracks & ROUTES_MASK) && _route->head != _route->tail) {
+       map_render_path(_route, _gc + COLORABLE_ROUTE);
+
+       /* Now, draw the next waypoint on top of all other waypoints. */
+       if (_next_way) {
+               guint x1 = unit2bufx(_next_way->point->unitx);
+               guint y1 = unit2bufy(_next_way->point->unity);
+
+               if ((x1 < buf_width_pixels) && (y1 < buf_height_pixels)) {
+                       /* Draw the next waypoint as a break. */
+                       gdk_draw_arc(map_pixmap, _gc[COLORABLE_ROUTE_BREAK], FALSE,     /* FALSE: not filled. */
+                            x1 - _draw_width, y1 - _draw_width, 4 * _draw_width, 4 * _draw_width, 0,   /* start at 0 degrees. */
+                            360 * 64);
                }
        }
-       if (_show_tracks & TRACKS_MASK)
-               map_render_path(&_track, _gc + COLORABLE_TRACK);
-
-       vprintf("%s(): return\n", __PRETTY_FUNCTION__);
+}
+if (_show_tracks & TRACKS_MASK)
+       map_render_path(_track, _gc + COLORABLE_TRACK);
 }
 
-static gboolean map_follow_move(GtkWidget * widget, GdkEventMotion * event)
+/**
+ * 
+ *
+ */
+static gboolean 
+map_follow_move_cb(GtkWidget *widget, GdkEventMotion *event, gpointer data)
 {
-       gint xx, yy;
-       GdkModifierType state;
-       guint unitx, unity;
-       guint cx, cy;
+GdkModifierType state;
+gint xx, yy;
+guint unitx, unity, nunitx, nunity;
+guint cx, cy;
 
-       if (event->is_hint) {
-               gdk_window_get_pointer(event->window, &xx, &yy, &state);
-               state = event->state;
-       } else {
-               xx = event->x;
-               yy = event->y;
-               state = event->state;
-       }
+if (!(event->state & GDK_BUTTON1_MASK)) {
+       map_drag_id=0;
+       return TRUE;
+}
 
-       unitx = x2unit((gint) (xx - before[0]));
-       unity = y2unit((gint) (yy - before[1]));
-       cx = unit2x(_center.unitx);
-       cy = unit2y(_center.unity);
+if (event->is_hint) {
+       gdk_window_get_pointer(event->window, &xx, &yy, &state);
+       state = event->state;
+} else {
+       xx = event->x;
+       yy = event->y;
+       state = event->state;
+}
 
-       map_center_unit(x2unit((gint) (cx - (xx - before[0]))),
-                       y2unit((gint) (cy - (yy - before[1]))));
+unitx = x2unit((gint) (xx - before[0]));
+unity = y2unit((gint) (yy - before[1]));
+cx = unit2x(_center.unitx);
+cy = unit2y(_center.unity);
 
-       before[0] = xx;
-       before[1] = yy;
-       return FALSE;
+nunitx=x2unit((gint) (cx - (xx - before[0])));
+nunity=y2unit((gint) (cy - (yy - before[1])));
+
+map_center_unit(nunitx, nunity);
+
+map_dragged=TRUE;
+g_debug("MAP PAN: %d %d %d %d (%d, %d)", cx, cy, xx, yy, nunitx, nunity);
+before[0] = xx;
+before[1] = yy;
+return FALSE;
 }
 
-gboolean map_key_zoom_timeout()
+gboolean 
+map_key_zoom_timeout(void)
 {
-       printf("%s()\n", __PRETTY_FUNCTION__);
-       if (_key_zoom_new < _zoom) {
-               /* We're currently zooming in (_zoom is decreasing). */
-               guint test = _key_zoom_new - _curr_repo->view_zoom_steps;
-               if (test < MAX_ZOOM)
-                       /* We can zoom some more.  Hurray! */
-                       _key_zoom_new = test;
-               else
-                       /* We can't zoom anymore.  Booooo! */
-                       return FALSE;
-       } else {
-               /* We're currently zooming out (_zoom is increasing). */
-               guint test = _key_zoom_new + _curr_repo->view_zoom_steps;
-               if (test < MAX_ZOOM)
-                       /* We can zoom some more.  Hurray! */
-                       _key_zoom_new = test;
-               else
-                       /* We can't zoom anymore.  Booooo! */
-                       return FALSE;
-       }
+if (_key_zoom_new < _zoom) {
+       /* We're currently zooming in (_zoom is decreasing). */
+       guint test = _key_zoom_new - _curr_repo->view_zoom_steps;
+       if (test < map_max_zoom)
+               _key_zoom_new = test;
+       else
+               return FALSE;
+} else {
+       /* We're currently zooming out (_zoom is increasing). */
+       guint test = _key_zoom_new + _curr_repo->view_zoom_steps;
+       if (test < map_max_zoom)
+               _key_zoom_new = test;
+       else
+               return FALSE;
+}
 
-       /* We can zoom more - tell them how much they're zooming. */
-       {
-               gchar buffer[32];
-               snprintf(buffer, sizeof(buffer),
-                        "%s %d", _("Zoom to Level"), _key_zoom_new);
-               MACRO_BANNER_SHOW_INFO(_window, buffer);
-       }
-       vprintf("%s(): return\n", __PRETTY_FUNCTION__);
-       return TRUE;
+return TRUE;
 }
 
-void map_scale_draw(GdkEventExpose *event)
+static void
+map_information_text(guint x, guint y, GdkGC *gc, gchar *msg)
 {
-       gdk_rectangle_intersect(&event->area, &_scale_rect, &event->area);
-
-       if (event->area.width && event->area.height) {
-               gdk_draw_rectangle(_map_widget->window,
-                                  _map_widget->style->
-                                  bg_gc[GTK_WIDGET_STATE(_map_widget)],
-                                  TRUE, _scale_rect.x, _scale_rect.y,
-                                  _scale_rect.width,
-                                  _scale_rect.height);
-               gdk_draw_rectangle(_map_widget->window,
-                                  _map_widget->style->
-                                  fg_gc[GTK_WIDGET_STATE(_map_widget)],
-                                  FALSE, _scale_rect.x, _scale_rect.y,
-                                  _scale_rect.width,
-                                  _scale_rect.height);
-
-               /* Now calculate and draw the distance. */
-               {
-                       gchar buffer[16];
-                       gdouble distance;
-                       gdouble lat1, lon1, lat2, lon2;
-                       gint width;
-
-                       unit2latlon(_center.unitx - pixel2unit(SCALE_WIDTH / 2 - 4),
-                                   _center.unity, lat1, lon1);
-                       unit2latlon(_center.unitx + pixel2unit(SCALE_WIDTH / 2 - 4),
-                                   _center.unity, lat2, lon2);
-                       distance = calculate_distance(lat1, lon1, lat2, lon2) * UNITS_CONVERT[_units];
-
-                       if (distance < 1.f)
-                               snprintf(buffer, sizeof(buffer),
-                                        "%0.2f %s", distance,
-                                        UNITS_TEXT[_units]);
-                       else if (distance < 10.f)
-                               snprintf(buffer, sizeof(buffer),
-                                        "%0.1f %s", distance,
-                                        UNITS_TEXT[_units]);
-                       else
-                               snprintf(buffer, sizeof(buffer),
-                                        "%0.f %s", distance,
-                                        UNITS_TEXT[_units]);
-
-                       pango_layout_set_text(_scale_layout, buffer, -1);
-                       pango_layout_get_pixel_size(_scale_layout, &width, NULL);
-
-                       /* Draw the layout itself. */
-                       gdk_draw_layout(_map_widget->window,
-                                       _map_widget->style->
-                                       fg_gc[GTK_WIDGET_STATE
-                                             (_map_widget)],
-                                       _scale_rect.x +
-                                       (_scale_rect.width - width) / 2,
-                                       _scale_rect.y, _scale_layout);
-
-                       /* Draw little hashes on the ends. */
-                       gdk_draw_line(_map_widget->window,
-                                     _map_widget->style->
-                                     fg_gc[GTK_WIDGET_STATE
-                                           (_map_widget)],
-                                     _scale_rect.x + 4,
-                                     _scale_rect.y +
-                                     _scale_rect.height / 2 - 4,
-                                     _scale_rect.x + 4,
-                                     _scale_rect.y +
-                                     _scale_rect.height / 2 + 4);
-                       gdk_draw_line(_map_widget->window,
-                                     _map_widget->style->
-                                     fg_gc[GTK_WIDGET_STATE
-                                           (_map_widget)],
-                                     _scale_rect.x + 4,
-                                     _scale_rect.y +
-                                     _scale_rect.height / 2,
-                                     _scale_rect.x +
-                                     (_scale_rect.width - width) / 2 -
-                                     4,
-                                     _scale_rect.y +
-                                     _scale_rect.height / 2);
-                       gdk_draw_line(_map_widget->window,
-                                     _map_widget->style->
-                                     fg_gc[GTK_WIDGET_STATE
-                                           (_map_widget)],
-                                     _scale_rect.x +
-                                     _scale_rect.width - 4,
-                                     _scale_rect.y +
-                                     _scale_rect.height / 2 - 4,
-                                     _scale_rect.x +
-                                     _scale_rect.width - 4,
-                                     _scale_rect.y +
-                                     _scale_rect.height / 2 + 4);
-                       gdk_draw_line(_map_widget->window,
-                                     _map_widget->style->
-                                     fg_gc[GTK_WIDGET_STATE
-                                           (_map_widget)],
-                                     _scale_rect.x +
-                                     _scale_rect.width - 4,
-                                     _scale_rect.y +
-                                     _scale_rect.height / 2,
-                                     _scale_rect.x +
-                                     (_scale_rect.width + width) / 2 +
-                                     4,
-                                     _scale_rect.y +
-                                     _scale_rect.height / 2);
-               }
-       }
+guint width, height;
+
+pango_layout_set_text(speed_layout, msg, -1);
+pango_layout_get_pixel_size(speed_layout, &width, &height);
+gtk_widget_queue_draw_area(_map_widget, x - 5, y - 5, width * 3 + 15, height + 5);
+gdk_window_process_all_updates();
+gdk_draw_layout(_map_widget->window, gc, x, y, speed_layout);
 }
 
-gboolean map_cb_expose(GtkWidget * widget, GdkEventExpose * event)
+static void
+map_speed_draw(void)
 {
-       printf("%s(%d, %d, %d, %d)\n", __PRETTY_FUNCTION__,
-              event->area.x, event->area.y,
-              event->area.width, event->area.height);
-
-       gdk_draw_drawable(GDK_DRAWABLE(_map_widget->window),
-                         _gc[COLORABLE_MARK],
-                         _map_pixmap,
-                         event->area.x + _offsetx, event->area.y + _offsety,
-                         event->area.x, event->area.y,
-                         event->area.width, event->area.height);
-       map_draw_mark();
-
-       /* Draw scale, if necessary. */
-       if (_show_scale)
-               map_scale_draw(event);
-
-       vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__);
-       return TRUE;
+GdkGC *gc;
+gfloat cur_speed;
+gchar *buffer;
+
+cur_speed = _gps->data.speed * UNITS_CONVERT[_units];
+gc=(cur_speed > _speed_limit) ? speed_gc1 : speed_gc2;
+buffer = g_strdup_printf("%0.0f", cur_speed);
+map_information_text(10, 10, gc, buffer);
+g_free(buffer);
 }
 
-int map_zoom(gint zdir)
+static void 
+map_scale_draw(GdkEventExpose *event)
 {
-       gchar buffer[80];
-       gint nzoom;
+gchar buffer[16];
+gdouble distance;
+gdouble lat1, lon1, lat2, lon2;
+gint width;
+
+pango_layout_set_text(scale_layout, "0", -1);
+pango_layout_get_pixel_size(scale_layout, NULL, &scale_rect.height);
+scale_rect.y = _screen_height_pixels - scale_rect.height - 1;
+
+gdk_rectangle_intersect(&event->area, &scale_rect, &event->area);
+
+if (event->area.width && event->area.height) {
+       gdk_draw_rectangle(_map_widget->window,
+                          _map_widget->style->bg_gc[GTK_WIDGET_STATE(_map_widget)],
+                          TRUE, scale_rect.x, scale_rect.y,
+                          scale_rect.width,
+                          scale_rect.height);
+       gdk_draw_rectangle(_map_widget->window,
+                          _map_widget->style->fg_gc[GTK_WIDGET_STATE(_map_widget)],
+                          FALSE, scale_rect.x, scale_rect.y,
+                          scale_rect.width,
+                          scale_rect.height);
+
+       /* Now calculate and draw the distance. */
+       unit2latlon(_center.unitx - pixel2unit(SCALE_WIDTH / 2 - 4), _center.unity, lat1, lon1);
+       unit2latlon(_center.unitx + pixel2unit(SCALE_WIDTH / 2 - 4), _center.unity, lat2, lon2);
+       distance=calculate_distance(lat1, lon1, lat2, lon2) * UNITS_CONVERT[_units];
+
+       if (distance < 1.f)
+               g_snprintf(buffer, sizeof(buffer), "%0.2f %s", distance, UNITS_TEXT[_units]);
+       else if (distance < 10.f)
+               g_snprintf(buffer, sizeof(buffer), "%0.1f %s", distance, UNITS_TEXT[_units]);
+       else
+               g_snprintf(buffer, sizeof(buffer), "%0.f %s", distance, UNITS_TEXT[_units]);
+
+       pango_layout_set_text(scale_layout, buffer, -1);
+       pango_layout_get_pixel_size(scale_layout, &width, NULL);
 
-       nzoom = _zoom + zdir;
+       /* Draw the layout itself. */
+       gdk_draw_layout(_map_widget->window,
+                       _map_widget->style->fg_gc[GTK_WIDGET_STATE(_map_widget)],
+                       scale_rect.x + (scale_rect.width - width) / 2,
+                       scale_rect.y, scale_layout);
 
-       if ((nzoom > 0) && (nzoom < MAX_ZOOM - 1)) {
-               snprintf(buffer, sizeof(buffer), "%s %d",
-                        _("Zoom to Level"), nzoom);
-               MACRO_BANNER_SHOW_INFO(_window, buffer);
-               map_set_zoom(nzoom);
+       /* Draw little hashes on the ends. */
+       gdk_draw_line(_map_widget->window,
+                     _map_widget->style->fg_gc[GTK_WIDGET_STATE(_map_widget)],
+                     scale_rect.x + 4,
+                     scale_rect.y + scale_rect.height / 2 - 4,
+                     scale_rect.x + 4,
+                     scale_rect.y + scale_rect.height / 2 + 4);
+       gdk_draw_line(_map_widget->window,
+                     _map_widget->style->fg_gc[GTK_WIDGET_STATE(_map_widget)],
+                     scale_rect.x + 4,
+                     scale_rect.y + scale_rect.height / 2,
+                     scale_rect.x + (scale_rect.width - width) / 2 - 4,
+                     scale_rect.y + scale_rect.height / 2);
+       gdk_draw_line(_map_widget->window,
+                     _map_widget->style->fg_gc[GTK_WIDGET_STATE(_map_widget)],
+                     scale_rect.x + scale_rect.width - 4,
+                     scale_rect.y + scale_rect.height / 2 - 4,
+                     scale_rect.x + scale_rect.width - 4,
+                     scale_rect.y + scale_rect.height / 2 + 4);
+       gdk_draw_line(_map_widget->window,
+                     _map_widget->style->fg_gc[GTK_WIDGET_STATE(_map_widget)],
+                     scale_rect.x + scale_rect.width - 4,
+                     scale_rect.y + scale_rect.height / 2,
+                     scale_rect.x + (scale_rect.width + width) / 2 + 4,
+                     scale_rect.y + scale_rect.height / 2);
        }
+}
+
+static gboolean 
+map_cb_expose(GtkWidget *widget, GdkEventExpose *event)
+{
+gdk_draw_drawable(GDK_DRAWABLE(_map_widget->window),
+               _gc[COLORABLE_MARK],
+               map_pixmap,
+               event->area.x + _offsetx, event->area.y + _offsety,
+               event->area.x, event->area.y,
+               event->area.width, event->area.height);
+map_draw_mark(_gps);
+
+/* Draw scale, if necessary. */
+if (_show_scale)
+       map_scale_draw(event);
+
+#if 0
+if (_speed_on)
+       map_speed_draw();
+#endif
 
-       return nzoom;
+return TRUE;
+}
+
+int 
+map_zoom(gint zdir)
+{
+gint nzoom;
+
+nzoom=_zoom+zdir;
+if ((nzoom >= 0) && (nzoom < map_max_zoom - 1)) {
+       image_cache_clear(map_ic);
+       map_set_zoom(nzoom);
+}
+return nzoom;
+}
+
+gboolean
+map_zoom_in(void)
+{
+map_zoom(-1);
+return FALSE;
+}
+
+gboolean
+map_zoom_out(void)
+{
+map_zoom(1);
+return FALSE;
 }
 
 static gboolean
-map_autozoomer()
+map_autozoomer(void)
 {
 static gfloat z=5.0;
 static gint last=5;
+gint b=0;
 gint iz;
 
-if (_zoom_timeout_sid==0)
+if (zoom_timeout_sid==0)
        return FALSE;
 
-z=(z+_gps.speed+1)/5;
+/* If location is known, use road type and speed for zoom setting */
+if (map_loc.valid && map_loc.street) {
+       switch (map_loc.street->type) {
+       case WAY_MOTORWAY:
+       case WAY_TRUNK:
+               b=2;
+       break;
+       case WAY_PRIMARY:
+       case WAY_SECONDARY:
+               b=1;
+       break;
+       default:
+               b=0;
+       break;
+       }
+}
+
+z=(z+_gps->data.speed+1)/5;
+z+=b;
 if (z>5) z=5.0; else if (z<1) z=1.0;
 iz=(gint)roundf(z);
-#ifdef DEBUG
-g_printf("Setting autozoom to: %f %d S:%f\n", z, iz, _gps.speed);
-#endif
+g_debug("Setting autozoom to: %f %d S:%f\n", z, iz, _gps->data.speed);
 if (iz>last) 
        iz=last+1; 
 else if (iz<last) 
@@ -1332,16 +1220,16 @@ return TRUE;
 }
 
 void
-map_set_autozoom(gboolean az)
+map_set_autozoom(gboolean az, gfloat speed)
 {
 if (az==TRUE) {
+       zoom_timeout_sid=g_timeout_add(speed<5 ? 2000 : 5000, (GSourceFunc) map_autozoomer, NULL);
        MACRO_BANNER_SHOW_INFO(_window, "Autozoom enabled");
-       _zoom_timeout_sid=g_timeout_add(_gps.speed<5 ? 2000 : 5000, (GSourceFunc) map_autozoomer, NULL);
        return;
 } else {
-       if (_zoom_timeout_sid) {
-               g_source_remove(_zoom_timeout_sid);
-               _zoom_timeout_sid=0;
+       if (zoom_timeout_sid) {
+               g_source_remove(zoom_timeout_sid);
+               zoom_timeout_sid=0;
                MACRO_BANNER_SHOW_INFO(_window, "Autozoom disabled");
        }
        return;
@@ -1358,13 +1246,14 @@ cmenu_route_add_way(x, y);
 static void 
 map_draw_track(gint x, gint y)
 {
-_pos.unitx = x2unit((gint) (x + 0.5));
-_pos.unity = y2unit((gint) (y + 0.5));
-unit2latlon(_pos.unitx, _pos.unity, _gps.lat, _gps.lon);
-_gps.speed = 20.f;
-integerize_data();
-track_add(time(NULL), FALSE);
-refresh_mark();
+_gps->data.unitx=x2unit((gint) (x + 0.5));
+_gps->data.unity=y2unit((gint) (y + 0.5));
+unit2latlon(_gps->data.unitx, _gps->data.unity, _gps->data.lat, _gps->data.lon);
+_gps->data.speed=20.f;
+gps_data_integerize(&_gps->data);
+_gps->data.time=time(NULL);
+track_add(_track, &_gps->data);
+map_refresh_mark();
 }
 
 static void
@@ -1373,15 +1262,16 @@ map_set_place_information(osm_way *s, osm_place *mp, osm_place *sp)
 gchar buffer[256];
 
 if (!s && !mp && !sp) {
-       snprintf(buffer, sizeof(buffer), "Unknown location");
+       g_snprintf(buffer, sizeof(buffer), _("Unknown location"));
 } else {
        /* oh, fun */
-       snprintf(buffer, sizeof(buffer), "On %s%s%s%s%s%s%s%s%s", 
-       s ? s->name ? s->name : _("unknown") : "?",
+       g_snprintf(buffer, sizeof(buffer), "%s%s%s%s%s%s%s%s%s%s", 
+       s ? s->name ? s->name : "" : "",
        s ? s->ref ? ", " : "" : "",
        s ? s->ref ? s->ref : "" : "",
-       s ? s->int_ref ? "" : "" : "",
+       s ? s->int_ref ? " (" : "" : "",
        s ? s->int_ref ? s->int_ref : "" : "",
+       s ? s->int_ref ? ") " : "" : "",
        (sp && sp->name) ? " in " : "",
        (sp && sp->name) ? sp->name : "",
        (mp && mp->name) ? " in " : "",
@@ -1393,159 +1283,366 @@ gtk_label_set_label(GTK_LABEL(info_banner.location), buffer);
 static void
 map_update_destination(gdouble lat, gdouble lon)
 {
+gdouble dh=0.0;
+gdouble dt, cdist;
+static gdouble prev_dt=99999.0;
+static gboolean dest_reached=FALSE;
+gchar buffer[64];
+
 if (_dest.valid) {
-       gchar buffer[32];
-       gdouble dt=calculate_distance(lat, lon, _dest.lat, _dest.lon);
-       gdouble dh=calculate_course(lat, lon, _dest.lat, _dest.lon);
-       snprintf(buffer, sizeof(buffer), "%.02f %s (%0.02f)", dt * UNITS_CONVERT[_units], UNITS_TEXT[_units], dh);
+       dt=calculate_distance(lat, lon, _dest.lat, _dest.lon);
+       dh=calculate_course(lat, lon, _dest.lat, _dest.lon);
+       cdist=dt*UNITS_CONVERT[_units];
+
+       g_snprintf(buffer, sizeof(buffer), "%.02f %s (%0.02f)", cdist, UNITS_TEXT[_units], dh<0 ? 360+dh : dh);
        gtk_label_set_label(GTK_LABEL(info_banner.distance), buffer);
+
+       if (dt<0.005 && dest_reached==FALSE) {
+               if (_center_mode>0)
+                       announce_destination_reached();
+               dest_reached=TRUE;
+       } else if (dt<prev_dt-KM10KNOTS) {
+               if (_center_mode>0 && _announce_destination==TRUE)
+                       announce_distance_to_destination(cdist, UNITS_TEXT[_units], KM10KNOTS);
+               prev_dt=dt;
+       } else if (dt>prev_dt+KM10KNOTS/4) {
+               prev_dt=dt;
+       }
+       if (dt>0.05) {
+               dest_reached=FALSE;
+       }
+       g_debug("%f (Prev:%f)\n", prev_dt, dt);
 } else {
+       dest_reached=FALSE;
+       prev_dt=99999.0;
        gtk_label_set_label(GTK_LABEL(info_banner.distance), "");
 }
+
+gtk_compass_set_dest_heading(_gps_compass, _dest.valid, (gfloat)dh);
+gtk_compass_set_dest_heading(_tab_compass, _dest.valid, (gfloat)dh);
+
+if (_next_way && _next_way->point) {
+       gdouble wp_lat, wp_lon;
+       gfloat wc;
+
+       unit2latlon(_next_way->point->unitx,_next_way->point->unity, wp_lat, wp_lon);
+       wc=calculate_course(lat, lon, wp_lat, wp_lon);
+       gtk_compass_set_way_heading(_gps_compass, TRUE, wc);
+       gtk_compass_set_way_heading(_tab_compass, TRUE, wc);
+} else {
+       gtk_compass_set_way_heading(_gps_compass, FALSE, 0);
+       gtk_compass_set_way_heading(_tab_compass, FALSE, 0);
+}
+
 }
 
+/**
+ * Query the OSM database for where we are.
+ * 
+ * XXX: This is the wrong place for this function.
+ */
 static void 
-map_update_location(gint x, gint y, gboolean force)
+map_update_location(gdouble lat, gdouble lon, gboolean force)
 {
 gint ilat, ilon;
-gdouble lat,lon;
 static gboolean inp=FALSE;
 
 /* We run the gtk mainloop in progress callback so we can be called again, we don't like that */
-if (inp==TRUE)
+if (inp==TRUE) {
+       g_debug("LOC: Query in progress");
        return;
+}
 inp=TRUE;
 
-unit2latlon(x, y, lat, lon);
 ilat=lat2mp_int(lat);
 ilon=lon2mp_int(lon);
 
-if (_gps.fix>1 && !force)
-       osm_set_way_range_from_speed(_gps.speed);
+if (_gps->data.fix>1 && !force)
+       osm_set_way_range_from_speed(_gps->data.speed);
 else
-       osm_set_way_range(OSM_RANGE_WAY/4);
-
-osm_progress_set_widget(_db, _progress_item);
-_map_location_known=osm_get_location_data(ilat, ilon, &map_loc);
-_map_location_dist=map_loc.street ? map_loc.street->dist : 900000.0;
-if (map_loc.valid)
-       map_set_place_information(map_loc.street, map_loc.primary, map_loc.secondary);
-else
-       map_set_place_information(NULL, NULL, NULL);
-osm_progress_set_widget(_db, NULL);
+       osm_set_way_range(2800);
 
 map_update_destination(lat, lon);
 
+if (osm_check_location(&map_loc, ilat, ilon)==FALSE) {
+       osm_progress_set_widget(_db, _progress_item);
+       osm_get_location_data(ilat, ilon, _gps->data.heading, &map_loc);
+       if (map_loc.valid)
+               map_set_place_information(map_loc.street, map_loc.primary, map_loc.secondary);
+       else
+               map_set_place_information(NULL, NULL, NULL);
+       osm_progress_set_widget(_db, NULL);
+} else g_debug("IN PLACE");
+
 inp=FALSE;
 }
 
-gboolean map_cb_scroll_event(GtkWidget * widget, GdkEventScroll * event)
+/**
+ * Mouse scroller zoom in/out callback
+ */
+static gboolean
+map_cb_scroll_event(GtkWidget * widget, GdkEventScroll * event)
 {
+if (event->direction == GDK_SCROLL_UP) {
+       map_center_unit(x2unit((gint) (event->x + 0.5)), y2unit((gint) (event->y + 0.5)));
+       map_set_zoom(_zoom - 1);
+} else if (event->direction == GDK_SCROLL_DOWN) {
+       map_center_unit(x2unit((gint) (event->x + 0.5)), y2unit((gint) (event->y + 0.5)));
+       map_set_zoom(_zoom + 1);
+}
+return FALSE;
+}
 
-       if (event->direction == GDK_SCROLL_UP) {
-               map_center_unit(x2unit((gint) (event->x + 0.5)),
-                               y2unit((gint) (event->y + 0.5)));
-               map_set_zoom(_zoom - 1);
-       } else if (event->direction == GDK_SCROLL_DOWN) {
-               map_center_unit(x2unit((gint) (event->x + 0.5)),
-                               y2unit((gint) (event->y + 0.5)));
-               map_set_zoom(_zoom + 1);
+/** 
+ * Start map drag operation
+ */
+static void
+map_drag_start(gint x,gint y)
+{
+press[0] = x;
+press[1] = y;
+before[0] = press[0];
+before[1] = press[1];
+_center_mode=CENTER_MANUAL;
+if (map_drag_id!=0)
+       g_signal_handler_disconnect(G_OBJECT(_map_widget), map_drag_id);
+map_dragged=FALSE;
+map_drag_id=g_signal_connect(G_OBJECT(_map_widget), "motion_notify_event", G_CALLBACK(map_follow_move_cb), NULL);
+}
+
+/**
+ * Stop map drag operation
+ */
+static void
+map_drag_stop(gint x, gint y)
+{
+if (map_drag_id==0)
+       return;
+
+g_signal_handler_disconnect(G_OBJECT(_map_widget), map_drag_id);
+
+release[0]=x;
+release[1]=y;
+press[0]=0;
+press[1]=0;
+release[0]=0;
+release[1]=0;
+before[0]=0;
+before[1]=0;
+map_drag_id=0;
+if (map_dragged==TRUE)
+       map_force_redraw();
+}
+
+/* Workaround hildon content menu problem */
+static gboolean
+map_cb_show_poi_info_dialog(gpointer data)
+{
+guint poi_id=GPOINTER_TO_INT(data);
+if (poi_info_dialog(_window, poi_id)==FALSE)
+       g_printerr("Huh? Failed to display info dialog\n");
+return FALSE;
+}
+
+static gboolean 
+map_cb_button_press(GtkWidget *widget, GdkEventButton *event)
+{
+_cmenu_position_x = event->x + 0.5;
+_cmenu_position_y = event->y + 0.5;
+
+switch (event->button) {
+case 1:
+       if (event->type==GDK_2BUTTON_PRESS) {
+               map_center_unit(x2unit((gint) (event->x + 0.5)), y2unit((gint) (event->y + 0.5)));
+#ifdef MAP_ZOOM_ON_DOUBLE_CLICK
+               map_set_zoom(_zoom-1);
+#endif
+               map_data_needs_refresh=TRUE;
+               map_drag_stop(event->x, event->y);
+               return FALSE;
+       }
+       if (event->type==GDK_3BUTTON_PRESS) {
+               map_drag_stop(event->x, event->y);
+               return FALSE;
        }
 
+       map_drag_start(event->x, event->y);
        return FALSE;
+break;
+case 2:
+       /* */
+break;
+case 3:
+#ifndef WITH_HILDON
+       gtk_menu_popup(GTK_MENU(_menu_map), NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time());
+#endif
+break;
+}
+/* Return FALSE to allow context menu to work. */
+return FALSE;
 }
 
-gboolean map_cb_button_press(GtkWidget * widget, GdkEventButton * event)
+static gboolean
+map_pan_click_check(gint x, gint y)
 {
-       printf("%s()\n", __PRETTY_FUNCTION__);
+gint pns=0, pew=0;
 
-       _cmenu_position_x = event->x + 0.5;
-       _cmenu_position_y = event->y + 0.5;
+if (x<MAP_THUMB_MARGIN_X)
+       pns=-PAN_UNITS;
+else if (x>(_screen_width_pixels-MAP_THUMB_MARGIN_X))
+       pns=PAN_UNITS;
 
-       switch (event->button) {
-       case 1:
-               if (event->type == GDK_2BUTTON_PRESS) {
-                       map_set_zoom(_zoom - 1);
-                       return FALSE;
-               }
-               if (event->type == GDK_3BUTTON_PRESS)
+if (y<MAP_THUMB_MARGIN_Y)
+       pew=-PAN_UNITS;
+else if (y>(_screen_height_pixels-MAP_THUMB_MARGIN_Y))
+       pew=PAN_UNITS;
+
+if (pns!=0 || pew!=0) {
+       map_pan(pns, pew);
+       g_debug("MPAN: %d, %d", pns, pew);
+       return TRUE;
+}
+return FALSE;
+}
+
+static gboolean 
+map_cb_button_release(GtkWidget *widget, GdkEventButton *event)
+{
+gdouble lat, lon;
+guint poi_id;
+gint ux, uy;
+
+switch (event->button) {
+case 1:
+
+#if defined(WITH_HILDON_NEW)
+       if (hildon_helper_event_button_is_finger(event)) {
+               g_debug("Thumb down");
+               if (map_pan_click_check(event->x, event->y))
                        return FALSE;
-               break;
-       case 2:
-               press[0] = event->x;
-               press[1] = event->y;
-               before[0] = press[0];
-               before[1] = press[1];
-
-               _id = g_signal_connect(G_OBJECT(_map_widget),
-                                    "motion_notify_event",
-                                    G_CALLBACK(map_follow_move), NULL);
-               break;
-       case 3:
-#ifndef WITH_HILDON
-               gtk_menu_popup(GTK_MENU(_menu_map), NULL, NULL, NULL, NULL,
-                              event->button, gtk_get_current_event_time());
-#endif
-               break;
        }
+#endif
+
+       switch (map_mode) {
+       case MAP_MODE_DRAW_TRACK:
+               map_draw_track(event->x, event->y);
+       break;
+       case MAP_MODE_DRAW_ROUTE:
+               map_draw_route(event->x, event->y);
+       break;
+       case MAP_MODE_SET_ROUTE_FROM:
+       case MAP_MODE_SET_ROUTE_POINT:
+       case MAP_MODE_SET_ROUTE_TO:
+       break;
+       default:
+               ux=x2unit(_cmenu_position_x);
+               uy=y2unit(_cmenu_position_y);
 
-       /* Return FALSE to allow context menu to work. */
-       vprintf("%s(): return FALSE\n", __PRETTY_FUNCTION__);
+               unit2latlon(ux, uy, lat, lon);
+               if (map_poi_find_at_latlon(lat, lon, &poi_id)==TRUE) {
+                       g_idle_add_full(G_PRIORITY_HIGH_IDLE,(GSourceFunc)map_cb_show_poi_info_dialog, GINT_TO_POINTER(poi_id), NULL);
+               }
+               if (map_data_needs_refresh==TRUE) {
+                       map_data_needs_refresh=FALSE;
+                       map_render_data();
+                       g_idle_add_full(G_PRIORITY_HIGH_IDLE,(GSourceFunc)map_update_location_from_center, NULL, NULL);
+               }
+               if (_center_mode>0)
+                       set_action_activate("autocenter_none", TRUE);
+               map_drag_stop(event->x, event->y);
+       break;
+       }
+break;
+case 2:
+       if (map_pan_click_check(event->x, event->y)==FALSE)
+               map_set_zoom(_zoom-1);
        return FALSE;
+break;
+case 3:
+       /* */
+break;
+}
+
+/* Return FALSE to avoid context menu (if it hasn't popped up already). */
+return FALSE;
 }
 
-gboolean map_cb_button_release(GtkWidget * widget, GdkEventButton * event)
+gboolean 
+map_dialog_goto_latlon(void)
 {
-       printf("%s()\n", __PRETTY_FUNCTION__);
-
-       switch (event->button) {
-       case 1:
-               if (_center_mode > 0)
-                       gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(_menu_ac_none_item), TRUE);
-
-               switch (_map_mode) {
-               case MAP_MODE_DRAW_TRACK:
-                       map_draw_track(event->x, event->y);
-               break;
-               case MAP_MODE_DRAW_ROUTE:
-                       map_draw_route(event->x, event->y);
-               break;
-               case MAP_MODE_SET_ROUTE_FROM:
-               case MAP_MODE_SET_ROUTE_POINT:
-               case MAP_MODE_SET_ROUTE_TO:
-               break;
-               }
+GtkWidget *dialog;
+GtkWidget *table;
+GtkWidget *label;
+GtkWidget *txt_lat;
+GtkWidget *txt_lon;
 
-#if 0
-               ux = x2unit((gint) (event->x + 0.5));
-               uy = y2unit((gint) (event->y + 0.5));
-               map_update_location(ux, uy);
-#else
-               g_idle_add((GSourceFunc)map_update_location_from_center, NULL);
+dialog = gtk_dialog_new_with_buttons(_("Go to Lat/Lon"),
+                    GTK_WINDOW(_window),
+                    GTK_DIALOG_MODAL, GTK_STOCK_OK,
+                    GTK_RESPONSE_ACCEPT,
+                    GTK_STOCK_CANCEL,
+                    GTK_RESPONSE_REJECT, NULL);
+
+gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), table = gtk_table_new(2, 3, FALSE), TRUE, TRUE, 0);
+
+gtk_table_attach(GTK_TABLE(table), label = gtk_label_new(_("Latitude")),0, 1, 0, 1, GTK_FILL, 0, 2, 4);
+gtk_misc_set_alignment(GTK_MISC(label), 1.f, 0.5f);
+
+gtk_table_attach(GTK_TABLE(table), txt_lat = gtk_entry_new(), 1, 2, 0, 1, GTK_FILL, 0, 2, 4);
+gtk_misc_set_alignment(GTK_MISC(label), 0.0f, 0.5f);
+
+gtk_table_attach(GTK_TABLE(table), label = gtk_label_new(_("Longitude")), 0, 1, 1, 2, GTK_FILL, 0, 2, 4);
+gtk_misc_set_alignment(GTK_MISC(label), 1.f, 0.5f);
+
+gtk_table_attach(GTK_TABLE(table), txt_lon = gtk_entry_new(), 1, 2, 1, 2, GTK_FILL, 0, 2, 4);
+gtk_misc_set_alignment(GTK_MISC(label), 0.0f, 0.5f);
+
+#if defined (WITH_DEVICE_770) && !defined(WITH_HILDON_NEW)
+g_object_set(G_OBJECT(txt_lat), HILDON_INPUT_MODE_HINT, HILDON_INPUT_MODE_HINT_NUMERICSPECIAL, NULL);
+g_object_set(G_OBJECT(txt_lon), HILDON_INPUT_MODE_HINT, HILDON_INPUT_MODE_HINT_NUMERICSPECIAL, NULL);
 #endif
 
-               map_center_unit(x2unit((gint) (event->x + 0.5)),
-                               y2unit((gint) (event->y + 0.5)));
-
-               break;
-       case 2:
-               release[0] = event->x;
-               release[1] = event->y;
-
-               g_signal_handler_disconnect(G_OBJECT(_map_widget), _id);
-
-               press[0] = 0;
-               press[1] = 0;
-               release[0] = 0;
-               release[1] = 0;
-               before[0] = 0;
-               before[1] = 0;
-               break;
-       case 3:
-               break;
+/* Initialize with the current center position. */
+{
+       gchar buffer[32];
+       gdouble lat, lon;
+       unit2latlon(_center.unitx, _center.unity, lat, lon);
+       g_snprintf(buffer, sizeof(buffer), "%.06f", lat);
+       gtk_label_set_text(GTK_LABEL(txt_lat), buffer);
+       g_snprintf(buffer, sizeof(buffer), "%.06f", lon);
+       gtk_label_set_text(GTK_LABEL(txt_lon), buffer);
+}
+
+gtk_widget_show_all(dialog);
+
+while (GTK_RESPONSE_ACCEPT == gtk_dialog_run(GTK_DIALOG(dialog))) {
+       const gchar *text;
+       gchar *error_check;
+       gdouble lat, lon;
+       guint unitx, unity;
+
+       text = gtk_entry_get_text(GTK_ENTRY(txt_lat));
+       lat = strtof(text, &error_check);
+       if (text == error_check || lat < -90.f || lat > 90.f) {
+               popup_error(dialog, _("Invalid Latitude"));
+               continue;
        }
 
-       /* Return FALSE to avoid context menu (if it hasn't popped up already). */
-       vprintf("%s(): return FALSE\n", __PRETTY_FUNCTION__);
-       return FALSE;
+       text = gtk_entry_get_text(GTK_ENTRY(txt_lon));
+       lon = strtof(text, &error_check);
+       if (text == error_check || lon < -180.f || lon > 180.f) {
+               popup_error(dialog, _("Invalid Longitude"));
+               continue;
+       }
+
+       latlon2unit(lat, lon, unitx, unity);
+       if (_center_mode > 0)
+               set_action_activate("autocenter_none", TRUE);
+
+       map_center_unit(unitx, unity);
+       break;
 }
+gtk_widget_destroy(dialog);
+return TRUE;
+}
+