]> err.no Git - mapper/blobdiff - src/map.c
More Path/Route/Track cleanups:
[mapper] / src / map.c
index c02a421eb95fd16ae34afbe8c30f64c9dbb4c09b..48f41f02160d2ac6896cc2637a2f4e3027b88d98 100644 (file)
--- a/src/map.c
+++ b/src/map.c
@@ -25,8 +25,6 @@
 
 #include <config.h>
 
-#define _GNU_SOURCE
-
 #include <unistd.h>
 #include <stdlib.h>
 #include <string.h>
@@ -52,7 +50,6 @@
 #include "route.h"
 #include "track.h"
 #include "gps.h"
-#include "bt.h"
 #include "mapper-types.h"
 #include "ui-common.h"
 #include "settings.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;
+
+#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 _min_center = { -1, -1 };
 Point _max_center = { -1, -1 };
 Point _focus = { -1, -1 };
-Point _center = { -1, -1 };    /* current center location, X. */
+/* current center location, X. */
+Point _center = { -1, -1 };    
 
 CenterMode _center_mode = CENTER_LEAD;
 
@@ -84,6 +117,7 @@ static guint press[2] = { 0, 0 };
 static guint release[2] = { 0, 0 };
 static guint before[2] = { 0, 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;
@@ -110,7 +144,7 @@ static gint zoom_timeout_sid=0;
 static gint map_mode=0;
 static gboolean map_data_needs_refresh=FALSE;
 
-static osm_location map_loc = {NULL, NULL, NULL, FALSE, FALSE, 0, 0, 0.0, 0.0, 0 };
+osm_location map_loc = {NULL, NULL, NULL, FALSE, FALSE, 0, 0, 0.0, 0.0, 0 };
 
 static GTimer *map_timer;
 
@@ -119,10 +153,6 @@ static GTimer *map_timer;
 
 #define KM10KNOTS (5.39956803)
 
-void map_render_paths();
-void map_force_redraw();
-void map_draw_position_icon(Position *pos);
-
 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);
@@ -140,9 +170,26 @@ map_new(void)
 GtkWidget *map_widget;
 
 map_widget=gtk_drawing_area_new();
+map_timer=g_timer_new();
+
+map_ic=image_cache_new(MAP_CACHE_MAX);
+
+gtk_widget_set_extension_events(GTK_WIDGET(map_widget), GDK_EXTENSION_EVENTS_ALL);
+
+#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
+
 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);
-map_timer=g_timer_new();
+
 return map_widget;
 }
 
@@ -151,7 +198,7 @@ map_cb_after_realize(GtkWidget *map_widget, gpointer data)
 {
 GdkColor color;
 
-_map_pixmap=gdk_pixmap_new(map_widget->window, BUF_WIDTH_PIXELS, BUF_HEIGHT_PIXELS, -1);
+g_debug("MAP: after_realize");
 
 scale_context=gtk_widget_get_pango_context(map_widget);
 scale_layout=pango_layout_new(scale_context);
@@ -159,17 +206,20 @@ 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 */
+/* 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=0;
+color.green=0x1000;
 color.blue=0;
-speed_gc2=gdk_gc_new(map_widget->window);
 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();
@@ -184,7 +234,7 @@ g_signal_connect(G_OBJECT(map_widget), "button_release_event",G_CALLBACK(map_cb_
 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);
+       | GDK_LEAVE_NOTIFY_MASK | GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK);
 
 map_poi_init(map_widget);
 
@@ -194,9 +244,31 @@ return TRUE;
 static gboolean 
 map_cb_configure(GtkWidget *widget, GdkEventConfigure *event)
 {
+guint tw, th;
 
-_screen_width_pixels = _map_widget->allocation.width;
-_screen_height_pixels = _map_widget->allocation.height;
+tw=TILE_SIZE_PIXELS*((widget->allocation.width/TILE_SIZE_PIXELS)+2);
+th=TILE_SIZE_PIXELS*((widget->allocation.height/TILE_SIZE_PIXELS)+2);
+
+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);
+
+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;
+
+_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;
 
@@ -204,14 +276,15 @@ _screen_grids_halfheight = pixel2grid(_screen_height_pixels) / 2;
 scale_rect.x = (_screen_width_pixels - SCALE_WIDTH) / 2;
 scale_rect.width = SCALE_WIDTH;
 
-MACRO_RECALC_FOCUS_BASE();
-MACRO_RECALC_FOCUS_SIZE();
+MACRO_RECALC_FOCUS_BASE(_center_ratio);
+MACRO_RECALC_FOCUS_SIZE(_center_ratio);
 
 _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;
 
+map_force_redraw();
 return TRUE;
 }
 
@@ -220,11 +293,11 @@ return TRUE;
  * _map_widget.  This method does not queue the draw area.
  */
 static void 
-map_draw_mark(void)
+map_draw_mark(Gps *gps)
 {
-gdk_draw_arc(_map_widget->window, _conn_state == RCVR_FIXED ? _gc[COLORABLE_MARK] : _gc[COLORABLE_MARK_OLD], FALSE,
+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, _conn_state == RCVR_FIXED ? (_show_velvec ? _gc[COLORABLE_MARK_VELOCITY] : _gc[COLORABLE_MARK]) : _gc[COLORABLE_MARK_OLD],
+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);
 }
 
@@ -233,12 +306,12 @@ gdk_draw_line(_map_widget->window, _conn_state == RCVR_FIXED ? (_show_velvec ? _
  * units in preparation for drawing the mark with map_draw_mark().
  */
 void 
-map_set_mark(void)
+map_set_mark(GpsData *gps)
 {
-mark_x1 = unit2x(_pos.unitx);
-mark_y1 = unit2y(_pos.unity);
-mark_x2 = mark_x1 + (_show_velvec ? _gps.vel_offsetx : 0);
-mark_y2 = mark_y1 + (_show_velvec ? _gps.vel_offsety : 0);
+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);
@@ -251,7 +324,7 @@ mark_height = abs(mark_y1 - mark_y2) + (4 * _draw_width);
  * 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);
@@ -311,6 +384,7 @@ pixbuf_trim(GdkPixbuf * pixbuf)
 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, 
@@ -321,29 +395,44 @@ g_object_unref(pixbuf);
 return mpixbuf;
 }
 
+GdkPixmap *
+map_pixmap_get(void)
+{
+return map_pixmap;
+}
+
 static GdkPixbuf *
 map_tile_load(guint tilex, guint tiley, gint zoff, gboolean download)
 {
 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=gdk_pixbuf_new_from_file(buffer, &error);
-if (error || !pixbuf) {
+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);
        }
-
        return NULL;
 }
 
+g_object_ref(pixbuf);
+
+#if 0
+/* Check if we need to trim. */
+if (gdk_pixbuf_get_width(pixbuf) != TILE_SIZE_PIXELS || gdk_pixbuf_get_height(pixbuf) != TILE_SIZE_PIXELS)
+       pixbuf=pixbuf_trim(pixbuf);
+#endif
+
 /* Check tile age, if file date is ower a week old, redownload if autodownload enabled */
 se=stat(buffer, &tstat);
 if (se==0) {
@@ -352,8 +441,9 @@ if (se==0) {
        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_printf("Tile: %s is old, re-downloading\n", buffer);
+                               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);
                        }
                }
        }
@@ -362,40 +452,44 @@ if (se==0) {
 return pixbuf;
 }
 
-void
+gboolean
 map_render_tile(guint tilex, guint tiley, guint destx, guint desty, gboolean fast_fail)
 {
 GdkPixbuf *pixbuf=NULL;
 gint zoff;
 
-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) {
-               pixbuf=map_tile_load(tilex, tiley, zoff, !fast_fail);
-               if (!pixbuf) {
-                       if (!fast_fail)
-                               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 (destx > buf_width_pixels || desty > buf_height_pixels)
+       return FALSE;
+
+if (tilex > _world_size_tiles || tiley > _world_size_tiles)
+       return FALSE;
+
+g_debug("MAP RT: %u %u (%u) %u %u (%u, %u)", tilex, tiley, _world_size_tiles, destx, desty, buf_width_tiles, buf_height_tiles);
+
+/* 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 {
+               /* 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);
                }
        }
 }
 
 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);
+       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;
+       return TRUE;
 }
-gdk_draw_rectangle(_map_pixmap, _map_widget->style->black_gc, TRUE, destx, desty, TILE_SIZE_PIXELS, TILE_SIZE_PIXELS);
+gdk_draw_rectangle(map_pixmap, _map_widget->style->black_gc, TRUE, destx, desty, TILE_SIZE_PIXELS, TILE_SIZE_PIXELS);
+return TRUE;
 }
 
 void
@@ -405,62 +499,72 @@ map_render_data(void)
 if (map_drag_id>0)
        return;
 
-if (_home.valid)
-       map_draw_position_icon(&_home);
 if(_show_poi)
-       map_render_all_pois();
+       map_render_all_pois(buf_width_pixels, buf_height_pixels);
+
+if (_home.valid)
+       map_draw_position_icon(&_home, "home");
+
+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)
 {
 guint new_x, new_y;
-#ifdef DEBUG_MAP_TIME
+#ifdef DEBUG
 gulong tms;
 
 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);
+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_MAP_TIME
+#ifdef DEBUG
 g_timer_stop(map_timer);
-g_printf("Full redraw: %f sec\n", g_timer_elapsed(map_timer, &tms));
+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;
+}
+
 /**
  * Set the current zoom level.  If the given zoom level is the same as the
  * current zoom level, or if the new zoom is invalid
  * (not MIN_ZOOM <= new_zoom < MAX_ZOOM), then this method does nothing.
  */
-void 
+gboolean 
 map_set_zoom(guint new_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 > (map_max_zoom - 1))
+       return FALSE;
 if (new_zoom == _zoom)
-       return;
+       return FALSE;
 
 _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);
+MACRO_RECALC_CENTER(_gps->data, _center.unitx, _center.unity);
 
 /* Update center bounds to reflect new zoom level. */
 _min_center.unitx = pixel2unit(grid2pixel(_screen_grids_halfwidth));
@@ -477,11 +581,12 @@ _base_tiley = grid2tile(pixel2grid(unit2pixel(_center.unity)) - _screen_grids_ha
 /* New zoom level, so we can't reuse the old buffer's pixels. */
 /* Update state variables. */
 MACRO_RECALC_OFFSET();
-MACRO_RECALC_FOCUS_BASE();
-MACRO_RECALC_FOCUS_SIZE();
+MACRO_RECALC_FOCUS_BASE(_center_ratio);
+MACRO_RECALC_FOCUS_SIZE(_center_ratio);
 
-map_set_mark();
+map_set_mark(&_gps->data);
 map_force_redraw();
+return TRUE;
 }
 
 /**
@@ -513,7 +618,7 @@ if (new_base_tilex != _base_tilex || new_base_tiley != _base_tiley) {
         * 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;
+               new_y = buf_height_tiles - 1;
                ioy = -1;
        } else {
                /* New is higher than old - start at top and go down. */
@@ -522,7 +627,7 @@ if (new_base_tilex != _base_tilex || new_base_tiley != _base_tiley) {
        }
        if (new_base_tilex < _base_tilex) {
                /* New is righter than old - start at right and go left. */
-               base_new_x = BUF_WIDTH_TILES - 1;
+               base_new_x = buf_width_tiles - 1;
                iox = -1;
        } else {
                /* New is lefter than old - start at left and go right. */
@@ -535,17 +640,18 @@ if (new_base_tilex != _base_tilex || 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) {
+
+       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) {
+               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) {
+                       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,
+                               gdk_draw_drawable(map_pixmap,
                                                  _gc[COLORABLE_MARK],
-                                                 _map_pixmap,
+                                                 map_pixmap,
                                                  old_x * TILE_SIZE_PIXELS,
                                                  old_y * TILE_SIZE_PIXELS,
                                                  new_x * TILE_SIZE_PIXELS,
@@ -557,7 +663,7 @@ if (new_base_tilex != _base_tilex || new_base_tiley != _base_tiley) {
                                                new_base_tiley + new_y,
                                                new_x * TILE_SIZE_PIXELS,
                                                new_y * TILE_SIZE_PIXELS,
-                                               FALSE);
+                                               map_drag_id!=0 ? TRUE : FALSE);
                        }
                }
        }
@@ -565,9 +671,9 @@ if (new_base_tilex != _base_tilex || new_base_tiley != _base_tiley) {
 }
 
 MACRO_RECALC_OFFSET();
-MACRO_RECALC_FOCUS_BASE();
+MACRO_RECALC_FOCUS_BASE(_center_ratio);
 
-map_set_mark();
+map_set_mark(&_gps->data);
 MACRO_QUEUE_DRAW_AREA();
 }
 
@@ -607,7 +713,7 @@ if (pos->valid==FALSE)
 
 _center_mode=CENTER_MANUAL;
 map_center_latlon(pos->lat, pos->lon);
-map_set_autozoom(FALSE);
+map_set_autozoom(FALSE, 0);
 g_idle_add_full(G_PRIORITY_HIGH_IDLE,(GSourceFunc)map_update_location_from_center, NULL, NULL);
 return TRUE;
 }
@@ -626,7 +732,7 @@ 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();
+map_set_mark(&_gps->data);
 gtk_widget_queue_draw_area(_map_widget,
                   mark_minx<0 ? 0 : mark_minx,
                   mark_miny<0 ? 0 : mark_miny, 
@@ -634,9 +740,10 @@ gtk_widget_queue_draw_area(_map_widget,
 }
 
 gboolean
-map_update_location_from_gps(void)
+map_update_location_from_gps(Gps *gps)
 {
-map_update_location(_gps.lat, _gps.lon, FALSE);
+g_assert(gps);
+map_update_location(gps->data.lat, gps->data.lon, FALSE);
 return FALSE;
 }
 
@@ -647,16 +754,34 @@ gdouble lat, lon;
 /* Force re-validation of place if user is clicking around */
 map_loc.valid=FALSE;
 /* XXX: hmm, not the right place for this */
-if (_gps.fix<2) {
-       _pos.unitx=_center.unitx;
-       _pos.unity=_center.unity;
-       unit2latlon(_pos.unitx, _pos.unity, _gps.lat, _gps.lon);
+#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);
+}
+
 /**
  * Draw given pixbuf on map, centered on x,y
  */
@@ -664,40 +789,41 @@ void
 map_draw_pixbuf(guint unitx, guint unity, GdkPixbuf *p)
 {
 gint x,y;
-x = unit2bufx(unitx);
-y = unit2bufy(unity);
+x=unit2bufx(unitx);
+y=unit2bufy(unity);
 
-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);
+map_draw_pixbuf_on_buffer(x, y, p);
 }
 
 /**
- * Draw an icon on given Position.
+ * Draw an icon at given Position.
  * 
- * XXX: don't hardcode as home.png !
  */
-void
-map_draw_position_icon(Position *pos)
+static void
+map_draw_position_icon(Position *pos, const gchar *icon)
 {
-guint x,y, x1,y1;
+gint x, y;
+guint ux, uy;
+gchar buffer[128];
 GdkPixbuf *p;
 
-latlon2unit(pos->lat, pos->lon, x, y);
-x1=unit2bufx(x);
-y1=unit2bufy(y);
+latlon2unit(pos->lat, pos->lon, ux, uy);
+x=unit2bufx(ux);
+y=unit2bufy(uy);
 
-if ((x1 > BUF_WIDTH_PIXELS) || (y1 > BUF_HEIGHT_PIXELS))
+if ((x < 0) || (y < 0))
        return;
 
-p=gdk_pixbuf_new_from_file(DATADIR "/pixmaps/mapper/home.png", NULL);
-if (p) {
-       map_draw_pixbuf(x,y,p);
-       g_object_unref(p);
-}
+if ((x > buf_width_pixels) || (y > buf_height_pixels))
+       return;
 
+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);
 }
 
 /**
@@ -710,7 +836,7 @@ map_refresh_mark(void)
 guint new_center_unitx;
 guint new_center_unity;
 
-MACRO_RECALC_CENTER(new_center_unitx, 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. */
@@ -723,52 +849,63 @@ if (_speed_on)
        map_speed_draw();
 
 if (_center_mode>0)
-       g_idle_add_full(G_PRIORITY_HIGH_IDLE,(GSourceFunc)map_update_location_from_gps, NULL, NULL);
+       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
+ * 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)
 {
-       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);
        }
 }
 
+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
@@ -777,56 +914,50 @@ map_render_segment(GdkGC * gc_norm, GdkGC * gc_alt, guint unitx1, guint unity1,
 void 
 map_render_path(Path * path, GdkGC ** gc)
 {
-       Point *curr;
-       WayPoint *wcurr;
-
-       /* 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;
+
+/* 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)
 {
-if ((_show_tracks & ROUTES_MASK) && _route.head != _route.tail) {
-       map_render_path(&_route, _gc + COLORABLE_ROUTE);
+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)) {
+               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. */
+                       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);
+       map_render_path(_track, _gc + COLORABLE_TRACK);
 }
 
 /**
@@ -838,11 +969,13 @@ map_follow_move_cb(GtkWidget *widget, GdkEventMotion *event, gpointer data)
 {
 GdkModifierType state;
 gint xx, yy;
-guint unitx, unity;
+guint unitx, unity, nunitx, nunity;
 guint cx, cy;
 
-if (!(event->state & GDK_BUTTON1_MASK))
-       return FALSE;
+if (!(event->state & GDK_BUTTON1_MASK)) {
+       map_drag_id=0;
+       return TRUE;
+}
 
 if (event->is_hint) {
        gdk_window_get_pointer(event->window, &xx, &yy, &state);
@@ -858,9 +991,13 @@ unity = y2unit((gint) (yy - before[1]));
 cx = unit2x(_center.unitx);
 cy = unit2y(_center.unity);
 
-map_center_unit(x2unit((gint) (cx - (xx - before[0]))),        y2unit((gint) (cy - (yy - before[1]))));
-map_data_needs_refresh=TRUE;
+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;
@@ -872,14 +1009,14 @@ map_key_zoom_timeout(void)
 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)
+       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 < MAX_ZOOM)
+       if (test < map_max_zoom)
                _key_zoom_new = test;
        else
                return FALSE;
@@ -907,7 +1044,7 @@ GdkGC *gc;
 gfloat cur_speed;
 gchar *buffer;
 
-cur_speed = _gps.speed * UNITS_CONVERT[_units];
+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);
@@ -990,15 +1127,15 @@ if (event->area.width && event->area.height) {
 }
 
 static gboolean 
-map_cb_expose(GtkWidget * widget, GdkEventExpose * event)
+map_cb_expose(GtkWidget *widget, GdkEventExpose *event)
 {
 gdk_draw_drawable(GDK_DRAWABLE(_map_widget->window),
                _gc[COLORABLE_MARK],
-               _map_pixmap,
+               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();
+map_draw_mark(_gps);
 
 /* Draw scale, if necessary. */
 if (_show_scale)
@@ -1018,7 +1155,8 @@ map_zoom(gint zdir)
 gint nzoom;
 
 nzoom=_zoom+zdir;
-if ((nzoom > 0) && (nzoom < MAX_ZOOM - 1)) {
+if ((nzoom >= 0) && (nzoom < map_max_zoom - 1)) {
+       image_cache_clear(map_ic);
        map_set_zoom(nzoom);
 }
 return nzoom;
@@ -1043,17 +1181,34 @@ map_autozoomer(void)
 {
 static gfloat z=5.0;
 static gint last=5;
+gint b=0;
 gint iz;
 
 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) 
@@ -1065,11 +1220,11 @@ 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) {
@@ -1091,12 +1246,13 @@ 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(&_gps, &_pos);
-track_add(time(NULL), FALSE);
+_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();
 }
 
@@ -1146,7 +1302,7 @@ if (_dest.valid) {
                        announce_destination_reached();
                dest_reached=TRUE;
        } else if (dt<prev_dt-KM10KNOTS) {
-               if (_center_mode>0)
+               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) {
@@ -1155,14 +1311,13 @@ if (_dest.valid) {
        if (dt>0.05) {
                dest_reached=FALSE;
        }
-#if 0
-       g_printf("%f (Prev:%f)\n", prev_dt, dt);
-#endif
+       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);
 
@@ -1181,6 +1336,11 @@ if (_next_way && _next_way->point) {
 
 }
 
+/**
+ * Query the OSM database for where we are.
+ * 
+ * XXX: This is the wrong place for this function.
+ */
 static void 
 map_update_location(gdouble lat, gdouble lon, gboolean force)
 {
@@ -1188,36 +1348,39 @@ gint ilat, ilon;
 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;
 
 ilat=lat2mp_int(lat);
 ilon=lon2mp_int(lon);
 
-if (_gps.fix>1 && !force)
-       osm_set_way_range_from_speed(_gps.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);
+if (_gps->data.fix>1 && !force)
+       osm_set_way_range_from_speed(_gps->data.speed);
 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;
 }
 
 /**
  * Mouse scroller zoom in/out callback
  */
-static gboolean 
+static gboolean
 map_cb_scroll_event(GtkWidget * widget, GdkEventScroll * event)
 {
 if (event->direction == GDK_SCROLL_UP) {
@@ -1243,7 +1406,7 @@ 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);
 }
 
@@ -1267,6 +1430,8 @@ 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 */
@@ -1274,22 +1439,17 @@ static gboolean
 map_cb_show_poi_info_dialog(gpointer data)
 {
 guint poi_id=GPOINTER_TO_INT(data);
-if (poi_info_dialog(poi_id)==FALSE)
+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)
+map_cb_button_press(GtkWidget *widget, GdkEventButton *event)
 {
-gdouble lat, lon;
-guint poi_id;
-gint ux, uy;
-
 _cmenu_position_x = event->x + 0.5;
 _cmenu_position_y = event->y + 0.5;
 
-g_printf("BtnPress\n");
 switch (event->button) {
 case 1:
        if (event->type==GDK_2BUTTON_PRESS) {
@@ -1298,26 +1458,19 @@ case 1:
                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;
        }
 
-       ux=x2unit(_cmenu_position_x);
-       uy=y2unit(_cmenu_position_y);
-
-       unit2latlon(ux, uy, lat, lon);
-       if (map_poi_find_at_latlon(lat, lon, &poi_id)==TRUE) {
-               g_printf("POI: %d\n", poi_id);
-               g_idle_add_full(G_PRIORITY_HIGH_IDLE,(GSourceFunc)map_cb_show_poi_info_dialog, GINT_TO_POINTER(poi_id), NULL);
-       } else {
-               map_drag_start(event->x, event->y);
-       }
+       map_drag_start(event->x, event->y);
        return FALSE;
 break;
 case 2:
-       map_set_zoom(_zoom - 1);
+       /* */
 break;
 case 3:
 #ifndef WITH_HILDON
@@ -1329,14 +1482,46 @@ break;
 return FALSE;
 }
 
+static gboolean
+map_pan_click_check(gint x, gint y)
+{
+gint pns=0, pew=0;
+
+if (x<MAP_THUMB_MARGIN_X)
+       pns=-PAN_UNITS;
+else if (x>(_screen_width_pixels-MAP_THUMB_MARGIN_X))
+       pns=PAN_UNITS;
+
+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)
 {
-g_printf("BtnRelease\n");
+gdouble lat, lon;
+guint poi_id;
+gint ux, uy;
+
 switch (event->button) {
 case 1:
-       if (_center_mode>0)
-               set_action_activate("autocenter_none", TRUE);
+
+#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;
+       }
+#endif
 
        switch (map_mode) {
        case MAP_MODE_DRAW_TRACK:
@@ -1350,19 +1535,31 @@ case 1:
        case MAP_MODE_SET_ROUTE_TO:
        break;
        default:
-               map_drag_stop(event->x, event->y);
+               ux=x2unit(_cmenu_position_x);
+               uy=y2unit(_cmenu_position_y);
+
+               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;
 }