]> err.no Git - mapper/blob - src/map.c
Use g_debug for any output.
[mapper] / src / map.c
1 /*
2  * This file is part of mapper
3  *
4  * Copyright (C) 2007 Kaj-Michael Lang
5  * Copyright (C) 2006-2007 John Costigan.
6  *
7  * POI and GPS-Info code originally written by Cezary Jackiewicz.
8  *
9  * Default map data provided by http://www.openstreetmap.org/
10  *
11  * This program is free software; you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published by
13  * the Free Software Foundation; either version 2 of the License, or
14  * (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License along
22  * with this program; if not, write to the Free Software Foundation, Inc.,
23  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
24  */
25
26 #include <config.h>
27
28 #include <unistd.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <strings.h>
32 #include <stddef.h>
33 #include <math.h>
34 #include <errno.h>
35 #include <sys/wait.h>
36 #include <glib/gstdio.h>
37 #include <gtk/gtk.h>
38 #include <fcntl.h>
39 #include <libintl.h>
40 #include <locale.h>
41
42 #include "hildon-mapper.h"
43
44 #include "utils.h"
45 #include "map.h"
46 #include "osm.h"
47 #include "db.h"
48 #include "osm-db.h"
49 #include "poi.h"
50 #include "route.h"
51 #include "track.h"
52 #include "gps.h"
53 #include "mapper-types.h"
54 #include "ui-common.h"
55 #include "settings.h"
56 #include "latlon.h"
57 #include "gpx.h"
58 #include "speak.h"
59 #include "map-poi.h"
60 #include "map-download.h"
61 #include "announcements.h"
62 #include "gtkcompass.h"
63 #include "dialogs.h"
64
65 #define DEBUG_MAP_TIME 1
66
67 /** The "base tile" is the upper-left tile in the pixmap. */
68 guint _base_tilex = -5;
69 guint _base_tiley = -5;
70
71 guint _zoom = 3;                /* zoom level, from 0 to MAX_ZOOM. */
72
73 Point _min_center = { -1, -1 };
74 Point _max_center = { -1, -1 };
75 Point _focus = { -1, -1 };
76 Point _center = { -1, -1 };     /* current center location, X. */
77
78 CenterMode _center_mode = CENTER_LEAD;
79
80 /* Drag */
81 static guint press[2] = { 0, 0 };
82 static guint release[2] = { 0, 0 };
83 static guint before[2] = { 0, 0 };
84 static guint map_drag_id = 0;
85
86 /** VARIABLES FOR ACCESSING THE LOCATION/BOUNDS OF THE CURRENT MARK. */
87 static gint mark_x1;
88 static gint mark_x2;
89 static gint mark_y1;
90 static gint mark_y2;
91 static gint mark_minx;
92 static gint mark_miny;
93 static gint mark_width;
94 static gint mark_height;
95
96 static GdkRectangle scale_rect;
97 static PangoContext *scale_context;
98 static PangoFontDescription *scale_font;
99 static PangoLayout *scale_layout;
100
101 static GdkGC *speed_gc1;
102 static GdkGC *speed_gc2;
103 static PangoContext *speed_context;
104 static PangoLayout *speed_layout;
105 static PangoFontDescription *speed_fontdesc;
106
107 static gint zoom_timeout_sid=0;
108 static gint map_mode=0;
109 static gboolean map_data_needs_refresh=FALSE;
110
111 osm_location map_loc = {NULL, NULL, NULL, FALSE, FALSE, 0, 0, 0.0, 0.0, 0 };
112
113 static GTimer *map_timer;
114
115 /* Tile max age, 1 week */
116 #define TILE_MAX_AGE (604800)
117
118 #define KM10KNOTS (5.39956803)
119
120 void map_render_paths(void);
121 void map_force_redraw(void);
122 void map_draw_position_icon(Position *pos);
123
124 static void map_update_location(gdouble lat, gdouble lon, gboolean force);
125 static void map_speed_draw(void);
126 static gboolean map_cb_after_realize(GtkWidget *map_widget, gpointer data);
127 static gboolean map_cb_configure(GtkWidget *widget, GdkEventConfigure *event);
128 static gboolean map_cb_expose(GtkWidget * widget, GdkEventExpose * event);
129 static gboolean map_cb_button_press(GtkWidget * widget, GdkEventButton * event);
130 static gboolean map_cb_button_release(GtkWidget * widget, GdkEventButton * event);
131 static gboolean map_cb_scroll_event(GtkWidget * widget, GdkEventScroll * event); 
132
133 /******************************************************************************/
134
135 GtkWidget * 
136 map_new(void) 
137 {
138 GtkWidget *map_widget;
139
140 map_widget=gtk_drawing_area_new();
141 g_signal_connect_after(G_OBJECT(map_widget), "realize", G_CALLBACK(map_cb_after_realize), NULL);
142 g_signal_connect(G_OBJECT(map_widget), "configure_event", G_CALLBACK(map_cb_configure), NULL);
143 map_timer=g_timer_new();
144 return map_widget;
145 }
146
147 static gboolean 
148 map_cb_after_realize(GtkWidget *map_widget, gpointer data)
149 {
150 GdkColor color;
151
152 _map_pixmap=gdk_pixmap_new(map_widget->window, BUF_WIDTH_PIXELS, BUF_HEIGHT_PIXELS, -1);
153
154 scale_context=gtk_widget_get_pango_context(map_widget);
155 scale_layout=pango_layout_new(scale_context);
156 scale_font=pango_font_description_new();
157 pango_font_description_set_size(scale_font, 12 * PANGO_SCALE);
158 pango_layout_set_font_description(scale_layout, scale_font);
159
160 /* Speed limit */
161 speed_gc1=gdk_gc_new(map_widget->window);
162 color.red=0xffff;
163 color.green=0;
164 color.blue=0;
165 gdk_gc_set_rgb_fg_color(speed_gc1, &color);
166 color.red=0;
167 color.green=0;
168 color.blue=0;
169 speed_gc2=gdk_gc_new(map_widget->window);
170 gdk_gc_set_rgb_fg_color(speed_gc2, &color);
171 speed_context=gtk_widget_get_pango_context(map_widget);
172 speed_layout=pango_layout_new(speed_context);
173 speed_fontdesc=pango_font_description_new();
174 pango_font_description_set_size(speed_fontdesc, 48 * PANGO_SCALE);
175 pango_layout_set_font_description(speed_layout, speed_fontdesc);
176 pango_layout_set_alignment(speed_layout, PANGO_ALIGN_CENTER);
177
178 /* Signals */
179 g_signal_connect(G_OBJECT(map_widget), "expose_event", G_CALLBACK(map_cb_expose), NULL);
180 g_signal_connect(G_OBJECT(map_widget), "button_press_event", G_CALLBACK(map_cb_button_press), NULL);
181 g_signal_connect(G_OBJECT(map_widget), "button_release_event",G_CALLBACK(map_cb_button_release), NULL);
182 g_signal_connect(G_OBJECT(map_widget), "scroll_event",  G_CALLBACK(map_cb_scroll_event), NULL);
183
184 gtk_widget_add_events(map_widget, GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
185       | GDK_LEAVE_NOTIFY_MASK | GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK);
186
187 map_poi_init(map_widget);
188
189 return TRUE;
190 }
191
192 static gboolean 
193 map_cb_configure(GtkWidget *widget, GdkEventConfigure *event)
194 {
195
196 _screen_width_pixels = _map_widget->allocation.width;
197 _screen_height_pixels = _map_widget->allocation.height;
198 _screen_grids_halfwidth = pixel2grid(_screen_width_pixels) / 2;
199 _screen_grids_halfheight = pixel2grid(_screen_height_pixels) / 2;
200
201 /* Set scale_rect. */
202 scale_rect.x = (_screen_width_pixels - SCALE_WIDTH) / 2;
203 scale_rect.width = SCALE_WIDTH;
204
205 MACRO_RECALC_FOCUS_BASE();
206 MACRO_RECALC_FOCUS_SIZE();
207
208 _min_center.unitx = pixel2unit(grid2pixel(_screen_grids_halfwidth));
209 _min_center.unity = pixel2unit(grid2pixel(_screen_grids_halfheight));
210 _max_center.unitx = WORLD_SIZE_UNITS - grid2unit(_screen_grids_halfwidth) - 1;
211 _max_center.unity = WORLD_SIZE_UNITS - grid2unit(_screen_grids_halfheight) - 1;
212
213 return TRUE;
214 }
215
216 /**
217  * Draw the current mark (representing the current GPS location) onto
218  * _map_widget.  This method does not queue the draw area.
219  */
220 static void 
221 map_draw_mark(Gps *gps)
222 {
223 gdk_draw_arc(_map_widget->window, gps->io.conn == RCVR_FIXED ? _gc[COLORABLE_MARK] : _gc[COLORABLE_MARK_OLD], FALSE,
224      mark_x1 - _draw_width, mark_y1 - _draw_width, 2 * _draw_width, 2 * _draw_width, 0, 360 * 64);
225 gdk_draw_line(_map_widget->window, gps->io.conn == RCVR_FIXED ? (_show_velvec ? _gc[COLORABLE_MARK_VELOCITY] : _gc[COLORABLE_MARK]) : _gc[COLORABLE_MARK_OLD],
226       mark_x1, mark_y1, mark_x2, mark_y2);
227 }
228
229 /**
230  * "Set" the mark, which translates the current GPS position into on-screen
231  * units in preparation for drawing the mark with map_draw_mark().
232  */
233 void 
234 map_set_mark(GpsData *gps)
235 {
236 mark_x1 = unit2x(gps->unitx);
237 mark_y1 = unit2y(gps->unity);
238 mark_x2 = mark_x1 + (_show_velvec ? gps->vel_offsetx : 0);
239 mark_y2 = mark_y1 + (_show_velvec ? gps->vel_offsety : 0);
240 mark_minx = MIN(mark_x1, mark_x2) - (2 * _draw_width);
241 mark_miny = MIN(mark_y1, mark_y2) - (2 * _draw_width);
242 mark_width = abs(mark_x1 - mark_x2) + (4 * _draw_width);
243 mark_height = abs(mark_y1 - mark_y2) + (4 * _draw_width);
244 }
245
246 /**
247  * Do an in-place scaling of a pixbuf's pixels at the given ratio from the
248  * given source location.  It would have been nice if gdk_pixbuf provided
249  * this method, but I guess it's not general-purpose enough.
250  */
251 static void
252 map_pixbuf_scale_inplace(GdkPixbuf * pixbuf, guint ratio_p2, guint src_x, guint src_y)
253 {
254 guint dest_x = 0, dest_y = 0, dest_dim = TILE_SIZE_PIXELS;
255 guint rowstride = gdk_pixbuf_get_rowstride(pixbuf);
256 guint n_channels = gdk_pixbuf_get_n_channels(pixbuf);
257 guchar *pixels = gdk_pixbuf_get_pixels(pixbuf);
258
259 /* Sweep through the entire dest area, copying as necessary, but
260  * DO NOT OVERWRITE THE SOURCE AREA.  We'll copy it afterward. */
261 do {
262         guint src_dim = dest_dim >> ratio_p2;
263         guint src_endx = src_x - dest_x + src_dim;
264         gint x, y;
265
266         for (y = dest_dim - 1; y >= 0; y--) {
267                 guint src_offset_y, dest_offset_y;
268
269                 src_offset_y = (src_y + (y >> ratio_p2)) * rowstride;
270                 dest_offset_y = (dest_y + y) * rowstride;
271                 x = dest_dim - 1;
272
273                 if ((unsigned)(dest_y + y - src_y) < src_dim && (unsigned)(dest_x + x - src_x) < src_dim)
274                         x -= src_dim;
275
276                 for (; x >= 0; x--) {
277                         guint src_offset, dest_offset, i;
278
279                         src_offset = src_offset_y + (src_x + (x >> ratio_p2)) * n_channels;
280                         dest_offset = dest_offset_y + (dest_x + x) * n_channels;
281
282                         pixels[dest_offset] = pixels[src_offset];
283                         for (i = n_channels - 1; i; i--)
284                                 pixels[dest_offset + i] = pixels[src_offset + i];
285
286                         if ((unsigned)(dest_y + y - src_y) < src_dim && x == src_endx)
287                                 x -= src_dim;
288                 }
289         }
290
291         /* Reuse src_dim and src_endx to store new src_x and src_y. */
292         src_dim = src_x + ((src_x - dest_x) >> ratio_p2);
293         src_endx = src_y + ((src_y - dest_y) >> ratio_p2);
294         dest_x = src_x;
295         dest_y = src_y;
296         src_x = src_dim;
297         src_y = src_endx;
298 }
299 while ((dest_dim >>= ratio_p2) > 1);
300 }
301
302 /**
303  * Trim pixbufs that are bigger than tiles. (Those pixbufs result, when
304  * captions should be cut off.)
305  */
306 static GdkPixbuf *
307 pixbuf_trim(GdkPixbuf * pixbuf)
308 {
309 GdkPixbuf *mpixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, gdk_pixbuf_get_has_alpha(pixbuf),
310                    8, TILE_SIZE_PIXELS, TILE_SIZE_PIXELS);
311
312 gdk_pixbuf_copy_area(pixbuf,
313                      (gdk_pixbuf_get_width(pixbuf) - TILE_SIZE_PIXELS) / 2,
314                      (gdk_pixbuf_get_height(pixbuf) - TILE_SIZE_PIXELS) / 2, 
315                      TILE_SIZE_PIXELS,
316                      TILE_SIZE_PIXELS, mpixbuf, 0, 0);
317
318 g_object_unref(pixbuf);
319 return mpixbuf;
320 }
321
322 static GdkPixbuf *
323 map_tile_load(guint tilex, guint tiley, gint zoff, gboolean download)
324 {
325 GdkPixbuf *pixbuf;
326 GError *error = NULL;
327 gchar buffer[BUFFER_SIZE];
328 struct stat tstat;
329 gint se;
330
331 g_snprintf(buffer, sizeof(buffer), "%s/%u/%u/%u.jpg", _curr_repo->cache_dir, _zoom + zoff, (tilex >> zoff), (tiley >> zoff));
332
333 pixbuf=gdk_pixbuf_new_from_file(buffer, &error);
334 if (error || !pixbuf) {
335         g_unlink(buffer);
336
337         if (_auto_download && _curr_repo->type != REPOTYPE_NONE && !((_zoom + zoff - (_curr_repo->double_size ? 1 : 0)) % _curr_repo->dl_zoom_steps)) {
338                 if (download)
339                         map_initiate_download(tilex >> zoff, tiley >> zoff, _zoom + zoff, -INITIAL_DOWNLOAD_RETRIES);
340         }
341
342         return NULL;
343 }
344
345 /* Check tile age, if file date is ower a week old, redownload if autodownload enabled */
346 se=stat(buffer, &tstat);
347 if (se==0) {
348         time_t t;
349         t=time(NULL);
350         if (t-tstat.st_mtime>TILE_MAX_AGE) {
351                 if (_auto_download && _curr_repo->type != REPOTYPE_NONE && !((_zoom + zoff - (_curr_repo->double_size ? 1 : 0)) % _curr_repo->dl_zoom_steps)) {
352                         if (download) {
353                                 g_debug("Tile: %s is old, re-downloading\n", buffer);
354                                 map_initiate_download(tilex >> zoff, tiley >> zoff, _zoom + zoff, -INITIAL_DOWNLOAD_RETRIES);
355                         }
356                 }
357         }
358 }
359
360 return pixbuf;
361 }
362
363 void
364 map_render_tile(guint tilex, guint tiley, guint destx, guint desty, gboolean fast_fail)
365 {
366 GdkPixbuf *pixbuf=NULL;
367 gint zoff;
368
369 if (tilex<_world_size_tiles && tiley<_world_size_tiles) {
370         /* The tile is possible. */
371         for (zoff = (_curr_repo->double_size ? 1 : 0); !pixbuf && (_zoom + zoff) <= MAX_ZOOM && zoff <= TILE_SIZE_P2; zoff += 1) {
372                 pixbuf=map_tile_load(tilex, tiley, zoff, !fast_fail);
373                 if (!pixbuf) {
374                         if (!fast_fail)
375                                 fast_fail=TRUE;
376                 } else {
377                         /* Check if we need to trim. */
378                         if (gdk_pixbuf_get_width(pixbuf) != TILE_SIZE_PIXELS || gdk_pixbuf_get_height(pixbuf) != TILE_SIZE_PIXELS)
379                                 pixbuf = pixbuf_trim(pixbuf);
380
381                         /* Check if we need to blit. */
382                         if (zoff) {
383                                 map_pixbuf_scale_inplace(pixbuf, zoff,
384                                                  (tilex - ((tilex >> zoff) << zoff)) << (TILE_SIZE_P2 - zoff),
385                                                  (tiley - ((tiley >> zoff) << zoff)) << (TILE_SIZE_P2 - zoff));
386                         }
387                 }
388         }
389 }
390
391 if (pixbuf) {
392         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);
393         g_object_unref(pixbuf);
394         return;
395 }
396 gdk_draw_rectangle(_map_pixmap, _map_widget->style->black_gc, TRUE, destx, desty, TILE_SIZE_PIXELS, TILE_SIZE_PIXELS);
397 }
398
399 void
400 map_render_data(void)
401 {
402 /* Don't update if dragging, too much lag */
403 if (map_drag_id>0)
404         return;
405
406 if (_home.valid)
407         map_draw_position_icon(&_home);
408 if(_show_poi)
409         map_render_all_pois();
410 if(_show_tracks>0)
411         map_render_paths();
412 }
413
414 /**
415  * Force a redraw of the entire _map_pixmap, including fetching the
416  * background maps from disk and redrawing the tracks on top of them.
417  */
418 void 
419 map_force_redraw(void)
420 {
421 guint new_x, new_y;
422 #ifdef DEBUG_MAP_TIME
423 gulong tms;
424
425 g_timer_start(map_timer);
426 #endif
427 for (new_y = 0; new_y < BUF_HEIGHT_TILES; ++new_y)
428         for (new_x = 0; new_x < BUF_WIDTH_TILES; ++new_x) {
429                 map_render_tile(_base_tilex + new_x,
430                                 _base_tiley + new_y,
431                                 new_x * TILE_SIZE_PIXELS,
432                                 new_y * TILE_SIZE_PIXELS, FALSE);
433         }
434 #ifdef DEBUG_MAP_TIME
435 g_timer_stop(map_timer);
436 g_debug("Full redraw: %f sec\n", g_timer_elapsed(map_timer, &tms));
437 #endif
438 map_render_data();
439 MACRO_QUEUE_DRAW_AREA();
440 }
441
442 /**
443  * Set the current zoom level.  If the given zoom level is the same as the
444  * current zoom level, or if the new zoom is invalid
445  * (not MIN_ZOOM <= new_zoom < MAX_ZOOM), then this method does nothing.
446  */
447 void 
448 map_set_zoom(guint new_zoom)
449 {
450 /* Note that, since new_zoom is a guint and MIN_ZOOM is 0, this if
451  * condition also checks for new_zoom >= MIN_ZOOM. */
452 if (new_zoom > (MAX_ZOOM - 1))
453         return;
454 if (new_zoom == _zoom)
455         return;
456
457 _zoom = new_zoom / _curr_repo->view_zoom_steps * _curr_repo->view_zoom_steps;
458 _world_size_tiles = unit2tile(WORLD_SIZE_UNITS);
459
460 /* If we're leading, update the center to reflect new zoom level. */
461 MACRO_RECALC_CENTER(_gps->data, _center.unitx, _center.unity);
462
463 /* Update center bounds to reflect new zoom level. */
464 _min_center.unitx = pixel2unit(grid2pixel(_screen_grids_halfwidth));
465 _min_center.unity = pixel2unit(grid2pixel(_screen_grids_halfheight));
466 _max_center.unitx = WORLD_SIZE_UNITS - grid2unit(_screen_grids_halfwidth) - 1;
467 _max_center.unity = WORLD_SIZE_UNITS - grid2unit(_screen_grids_halfheight) - 1;
468
469 BOUND(_center.unitx, _min_center.unitx, _max_center.unitx);
470 BOUND(_center.unity, _min_center.unity, _max_center.unity);
471
472 _base_tilex = grid2tile((gint) pixel2grid((gint) unit2pixel((gint) _center.unitx)) - (gint) _screen_grids_halfwidth);
473 _base_tiley = grid2tile(pixel2grid(unit2pixel(_center.unity)) - _screen_grids_halfheight);
474
475 /* New zoom level, so we can't reuse the old buffer's pixels. */
476 /* Update state variables. */
477 MACRO_RECALC_OFFSET();
478 MACRO_RECALC_FOCUS_BASE();
479 MACRO_RECALC_FOCUS_SIZE();
480
481 map_set_mark(&_gps->data);
482 map_force_redraw();
483 }
484
485 /**
486  * Center the view on the given unitx/unity.
487  */
488 void 
489 map_center_unit(guint new_center_unitx, guint new_center_unity)
490 {
491 gint new_base_tilex, new_base_tiley;
492 guint new_x, new_y;
493 guint j, k, base_new_x, base_old_x, old_x, old_y, iox, ioy;
494
495 /* Assure that _center.unitx/y are bounded. */
496 BOUND(new_center_unitx, _min_center.unitx, _max_center.unitx);
497 BOUND(new_center_unity, _min_center.unity, _max_center.unity);
498
499 _center.unitx = new_center_unitx;
500 _center.unity = new_center_unity;
501
502 new_base_tilex = grid2tile((gint) pixel2grid((gint)unit2pixel((gint) _center.unitx)) - (gint)_screen_grids_halfwidth);
503 new_base_tiley = grid2tile(pixel2grid(unit2pixel(_center.unity)) - _screen_grids_halfheight);
504
505 /* Same zoom level, so it's likely that we can reuse some of the old
506  * buffer's pixels. */
507
508 if (new_base_tilex != _base_tilex || new_base_tiley != _base_tiley) {
509         /* If copying from old parts to new parts, we need to make sure we
510          * don't overwrite the old parts when copying, so set up new_x,
511          * new_y, old_x, old_y, iox, and ioy with that in mind. */
512         if (new_base_tiley < _base_tiley) {
513                 /* New is lower than old - start at bottom and go up. */
514                 new_y = BUF_HEIGHT_TILES - 1;
515                 ioy = -1;
516         } else {
517                 /* New is higher than old - start at top and go down. */
518                 new_y = 0;
519                 ioy = 1;
520         }
521         if (new_base_tilex < _base_tilex) {
522                 /* New is righter than old - start at right and go left. */
523                 base_new_x = BUF_WIDTH_TILES - 1;
524                 iox = -1;
525         } else {
526                 /* New is lefter than old - start at left and go right. */
527                 base_new_x = 0;
528                 iox = 1;
529         }
530
531         /* Iterate over the y tile values. */
532         old_y = new_y + new_base_tiley - _base_tiley;
533         base_old_x = base_new_x + new_base_tilex - _base_tilex;
534         _base_tilex = new_base_tilex;
535         _base_tiley = new_base_tiley;
536         for (j = 0; j < BUF_HEIGHT_TILES; ++j, new_y += ioy, old_y += ioy) {
537                 new_x = base_new_x;
538                 old_x = base_old_x;
539                 /* Iterate over the x tile values. */
540                 for (k = 0; k < BUF_WIDTH_TILES; ++k, new_x += iox, old_x += iox) {
541                         /* Can we get this grid block from the old buffer?. */
542                         if (old_x >= 0 && old_x < BUF_WIDTH_TILES && old_y >= 0 && old_y < BUF_HEIGHT_TILES) {
543                                 /* Copy from old buffer to new buffer. */
544                                 gdk_draw_drawable(_map_pixmap,
545                                                   _gc[COLORABLE_MARK],
546                                                   _map_pixmap,
547                                                   old_x * TILE_SIZE_PIXELS,
548                                                   old_y * TILE_SIZE_PIXELS,
549                                                   new_x * TILE_SIZE_PIXELS,
550                                                   new_y * TILE_SIZE_PIXELS,
551                                                   TILE_SIZE_PIXELS,
552                                                   TILE_SIZE_PIXELS);
553                         } else {
554                                 map_render_tile(new_base_tilex + new_x,
555                                                 new_base_tiley + new_y,
556                                                 new_x * TILE_SIZE_PIXELS,
557                                                 new_y * TILE_SIZE_PIXELS,
558                                                 map_drag_id!=0 ? TRUE : FALSE);
559                         }
560                 }
561         }
562         map_render_data();
563 }
564
565 MACRO_RECALC_OFFSET();
566 MACRO_RECALC_FOCUS_BASE();
567
568 map_set_mark(&_gps->data);
569 MACRO_QUEUE_DRAW_AREA();
570 }
571
572 /**
573  * Pan the view by the given number of units in the X and Y directions.
574  */
575 void 
576 map_pan(gint delta_unitx, gint delta_unity)
577 {
578 if (_center_mode > 0)
579         set_action_activate("autocenter_none", TRUE);
580
581 map_center_unit(_center.unitx + delta_unitx, _center.unity + delta_unity);
582 }
583
584 /**
585  * Helper to center map on given lat/lon
586  */
587 void
588 map_center_latlon(gdouble lat, gdouble lon)
589 {
590 guint unitx, unity;
591
592 latlon2unit(lat, lon, unitx, unity);
593 map_center_unit(unitx, unity);
594 }
595
596 /**
597  * Helper to goto given point and update location
598  * 
599  */
600 gboolean
601 map_goto_position(Position *pos)
602 {
603 if (pos->valid==FALSE)
604         return FALSE;
605
606 _center_mode=CENTER_MANUAL;
607 map_center_latlon(pos->lat, pos->lon);
608 map_set_autozoom(FALSE, 0);
609 g_idle_add_full(G_PRIORITY_HIGH_IDLE,(GSourceFunc)map_update_location_from_center, NULL, NULL);
610 return TRUE;
611 }
612
613 /**
614  * Initiate a move of the mark from the old location to the current
615  * location.  This function queues the draw area of the old mark (to force
616  * drawing of the background map), then updates the mark, then queus the
617  * draw area of the new mark.
618  */
619 void 
620 map_move_mark(void)
621 {
622 /* Just queue the old and new draw areas. */
623 gtk_widget_queue_draw_area(_map_widget,
624                    mark_minx<0 ? 0 : mark_minx,
625                    mark_miny<0 ? 0 : mark_miny, 
626                    mark_width, mark_height);
627 map_set_mark(&_gps->data);
628 gtk_widget_queue_draw_area(_map_widget,
629                    mark_minx<0 ? 0 : mark_minx,
630                    mark_miny<0 ? 0 : mark_miny, 
631                    mark_width, mark_height);
632 }
633
634 gboolean
635 map_update_location_from_gps(Gps *gps)
636 {
637 g_assert(gps);
638 map_update_location(gps->data.lat, gps->data.lon, FALSE);
639 return FALSE;
640 }
641
642 gboolean
643 map_update_location_from_center(void)
644 {
645 gdouble lat, lon;
646 /* Force re-validation of place if user is clicking around */
647 map_loc.valid=FALSE;
648 /* XXX: hmm, not the right place for this */
649 #if 0
650 if (_gps->fix==FIX_NOFIX) {
651         _gps->data.unitx=_center.unitx;
652         _gps->data.unity=_center.unity;
653         unit2latlon(_gps->data.unitx, _gps->data.unity, _gps->data.lat, _gps->data.lon);
654 }
655 #endif
656 unit2latlon(_center.unitx, _center.unity, lat, lon);
657 map_update_location(lat, lon, TRUE);
658 return FALSE;
659 }
660
661 /**
662  * Draw given pixbuf on map, centered on x,y
663  */
664 void
665 map_draw_pixbuf(guint unitx, guint unity, GdkPixbuf *p)
666 {
667 gint x,y;
668 x = unit2bufx(unitx);
669 y = unit2bufy(unity);
670
671 gdk_draw_pixbuf(_map_pixmap, _gc[COLORABLE_POI],
672         p, 0, 0,
673         x - gdk_pixbuf_get_width(p) / 2,
674         y - gdk_pixbuf_get_height(p) / 2,
675         -1, -1, GDK_RGB_DITHER_NONE, 0, 0);
676 }
677
678 /**
679  * Draw an icon on given Position.
680  * 
681  * XXX: don't hardcode as home.png !
682  */
683 void
684 map_draw_position_icon(Position *pos)
685 {
686 guint x,y, x1,y1;
687 GdkPixbuf *p;
688
689 latlon2unit(pos->lat, pos->lon, x, y);
690 x1=unit2bufx(x);
691 y1=unit2bufy(y);
692
693 if ((x1 > BUF_WIDTH_PIXELS) || (y1 > BUF_HEIGHT_PIXELS))
694         return;
695
696 p=gdk_pixbuf_new_from_file(DATADIR "/pixmaps/mapper/home.png", NULL);
697 if (p) {
698         map_draw_pixbuf(x,y,p);
699         g_object_unref(p);
700 }
701
702 }
703
704 /**
705  * Make sure the mark is up-to-date.  This function triggers a panning of
706  * the view if the mark is appropriately close to the edge of the view.
707  */
708 void
709 map_refresh_mark(void)
710 {
711 guint new_center_unitx;
712 guint new_center_unity;
713
714 MACRO_RECALC_CENTER(_gps->data, new_center_unitx, new_center_unity);
715
716 if ((new_center_unitx - _focus.unitx) < _focus_unitwidth && (new_center_unity - _focus.unity) < _focus_unitheight)
717         /* We're not changing the view - just move the mark. */
718         map_move_mark();
719 else
720         map_center_unit(new_center_unitx, new_center_unity);
721
722 /* Draw speed info */
723 if (_speed_on)
724         map_speed_draw();
725
726 if (_center_mode>0)
727         g_idle_add_full(G_PRIORITY_HIGH_IDLE,(GSourceFunc)map_update_location_from_gps, _gps, NULL);
728 }
729
730 /**
731  * Render a single track line to _map_pixmap.  If either point on the line
732  * is a break (defined as unity == 0), a circle is drawn at the other point.
733  * IT IS AN ERROR FOR BOTH POINTS TO INDICATE A BREAK.
734  */
735 void
736 map_render_segment(GdkGC * gc_norm, GdkGC * gc_alt, guint unitx1, guint unity1, guint unitx2, guint unity2)
737 {
738         if (!unity1) {
739                 guint x2, y2;
740                 x2 = unit2bufx(unitx2);
741                 y2 = unit2bufy(unity2);
742                 /* Make sure this circle will be visible. */
743                 if ((x2 < BUF_WIDTH_PIXELS) && (y2 < BUF_HEIGHT_PIXELS))
744                         gdk_draw_arc(_map_pixmap, gc_alt, FALSE,        /* FALSE: not filled. */
745                                      x2 - _draw_width, y2 - _draw_width, 2 * _draw_width, 2 * _draw_width, 0,   /* start at 0 degrees. */
746                                      360 * 64);
747         } else if (!unity2) {
748                 guint x1, y1;
749                 x1 = unit2bufx(unitx1);
750                 y1 = unit2bufy(unity1);
751                 /* Make sure this circle will be visible. */
752                 if ((x1 < BUF_WIDTH_PIXELS) && ((unsigned)y1 < BUF_HEIGHT_PIXELS))
753                         gdk_draw_arc(_map_pixmap, gc_alt, FALSE,        /* FALSE: not filled. */
754                                      x1 - _draw_width, y1 - _draw_width, 2 * _draw_width, 2 * _draw_width, 0,   /* start at 0 degrees. */
755                                      360 * 64);
756         } else {
757                 gint x1, y1, x2, y2;
758                 x1 = unit2bufx(unitx1);
759                 y1 = unit2bufy(unity1);
760                 x2 = unit2bufx(unitx2);
761                 y2 = unit2bufy(unity2);
762                 /* Make sure this line could possibly be visible. */
763                 if (!((x1 > BUF_WIDTH_PIXELS && x2 > BUF_WIDTH_PIXELS)
764                       || (x1 < 0 && x2 < 0)
765                       || (y1 > BUF_HEIGHT_PIXELS && y2 > BUF_HEIGHT_PIXELS)
766                       || (y1 < 0 && y2 < 0)))
767                         gdk_draw_line(_map_pixmap, gc_norm, x1, y1, x2, y2);
768         }
769 }
770
771 /**
772  * Render all track data onto the _map_pixmap.  Note that this does not
773  * clear the pixmap of previous track data (use map_force_redraw() for
774  * that), and also note that this method does not queue any redraws, so it
775  * is up to the caller to decide which part of the track really needs to be
776  * redrawn.
777  */
778 void 
779 map_render_path(Path * path, GdkGC ** gc)
780 {
781         Point *curr;
782         WayPoint *wcurr;
783
784         /* gc is a pointer to the first GC to use (for plain points).  (gc + 1)
785          * is a pointer to the GC to use for waypoints, and (gc + 2) is a pointer
786          * to the GC to use for breaks. */
787
788         /* else there is a route to draw. */
789         for (curr = path->head, wcurr = path->whead; curr++ != path->tail;) {
790                 /* Draw the line from (curr - 1) to (curr). */
791                 map_render_segment(gc[0], gc[2],
792                                    curr[-1].unitx, curr[-1].unity, curr->unitx,
793                                    curr->unity);
794
795                 /* Now, check if curr is a waypoint. */
796                 if (wcurr && wcurr <= path->wtail && wcurr->point == curr) {
797                         guint x1 = unit2bufx(wcurr->point->unitx);
798                         guint y1 = unit2bufy(wcurr->point->unity);
799                         if ((x1 < BUF_WIDTH_PIXELS)
800                             && (y1 < BUF_HEIGHT_PIXELS)) {
801                                 gdk_draw_arc(_map_pixmap, gc[1], FALSE, /* FALSE: not filled. */
802                                              x1 - _draw_width, y1 - _draw_width, 2 * _draw_width, 2 * _draw_width, 0,   /* start at 0 degrees. */
803                                              360 * 64);
804                         }
805                         wcurr++;
806                 }
807         }
808 }
809
810 void 
811 map_render_paths(void)
812 {
813 if ((_show_tracks & ROUTES_MASK) && _route.head != _route.tail) {
814         map_render_path(&_route, _gc + COLORABLE_ROUTE);
815
816         /* Now, draw the next waypoint on top of all other waypoints. */
817         if (_next_way) {
818                 guint x1 = unit2bufx(_next_way->point->unitx);
819                 guint y1 = unit2bufy(_next_way->point->unity);
820
821                 if ((x1 < BUF_WIDTH_PIXELS) && (y1 < BUF_HEIGHT_PIXELS)) {
822                         /* Draw the next waypoint as a break. */
823                         gdk_draw_arc(_map_pixmap, _gc[COLORABLE_ROUTE_BREAK], FALSE,    /* FALSE: not filled. */
824                              x1 - _draw_width, y1 - _draw_width, 4 * _draw_width, 4 * _draw_width, 0,   /* start at 0 degrees. */
825                              360 * 64);
826                 }
827         }
828 }
829 if (_show_tracks & TRACKS_MASK)
830         map_render_path(&_track, _gc + COLORABLE_TRACK);
831 }
832
833 /**
834  * 
835  *
836  */
837 static gboolean 
838 map_follow_move_cb(GtkWidget *widget, GdkEventMotion *event, gpointer data)
839 {
840 GdkModifierType state;
841 gint xx, yy;
842 guint unitx, unity, nunitx, nunity;
843 guint cx, cy;
844
845 if (!(event->state & GDK_BUTTON1_MASK))
846         return FALSE;
847
848 if (event->is_hint) {
849         gdk_window_get_pointer(event->window, &xx, &yy, &state);
850         state = event->state;
851 } else {
852         xx = event->x;
853         yy = event->y;
854         state = event->state;
855 }
856
857 unitx = x2unit((gint) (xx - before[0]));
858 unity = y2unit((gint) (yy - before[1]));
859 cx = unit2x(_center.unitx);
860 cy = unit2y(_center.unity);
861
862 nunitx=x2unit((gint) (cx - (xx - before[0])));
863 nunity=y2unit((gint) (cy - (yy - before[1])));
864
865 map_center_unit(nunitx, nunity);
866
867 g_debug("MAP PAN: %d %d %d %d (%d, %d)", cx, cy, xx, yy, nunitx, nunity);
868 before[0] = xx;
869 before[1] = yy;
870 return FALSE;
871 }
872
873 gboolean 
874 map_key_zoom_timeout(void)
875 {
876 if (_key_zoom_new < _zoom) {
877         /* We're currently zooming in (_zoom is decreasing). */
878         guint test = _key_zoom_new - _curr_repo->view_zoom_steps;
879         if (test < MAX_ZOOM)
880                 _key_zoom_new = test;
881         else
882                 return FALSE;
883 } else {
884         /* We're currently zooming out (_zoom is increasing). */
885         guint test = _key_zoom_new + _curr_repo->view_zoom_steps;
886         if (test < MAX_ZOOM)
887                 _key_zoom_new = test;
888         else
889                 return FALSE;
890 }
891
892 return TRUE;
893 }
894
895 static void
896 map_information_text(guint x, guint y, GdkGC *gc, gchar *msg)
897 {
898 guint width, height;
899
900 pango_layout_set_text(speed_layout, msg, -1);
901 pango_layout_get_pixel_size(speed_layout, &width, &height);
902 gtk_widget_queue_draw_area(_map_widget, x - 5, y - 5, width * 3 + 15, height + 5);
903 gdk_window_process_all_updates();
904 gdk_draw_layout(_map_widget->window, gc, x, y, speed_layout);
905 }
906
907 static void
908 map_speed_draw(void)
909 {
910 GdkGC *gc;
911 gfloat cur_speed;
912 gchar *buffer;
913
914 cur_speed = _gps->data.speed * UNITS_CONVERT[_units];
915 gc=(cur_speed > _speed_limit) ? speed_gc1 : speed_gc2;
916 buffer = g_strdup_printf("%0.0f", cur_speed);
917 map_information_text(10, 10, gc, buffer);
918 g_free(buffer);
919 }
920
921 static void 
922 map_scale_draw(GdkEventExpose *event)
923 {
924 gchar buffer[16];
925 gdouble distance;
926 gdouble lat1, lon1, lat2, lon2;
927 gint width;
928
929 pango_layout_set_text(scale_layout, "0", -1);
930 pango_layout_get_pixel_size(scale_layout, NULL, &scale_rect.height);
931 scale_rect.y = _screen_height_pixels - scale_rect.height - 1;
932
933 gdk_rectangle_intersect(&event->area, &scale_rect, &event->area);
934
935 if (event->area.width && event->area.height) {
936         gdk_draw_rectangle(_map_widget->window,
937                            _map_widget->style->bg_gc[GTK_WIDGET_STATE(_map_widget)],
938                            TRUE, scale_rect.x, scale_rect.y,
939                            scale_rect.width,
940                            scale_rect.height);
941         gdk_draw_rectangle(_map_widget->window,
942                            _map_widget->style->fg_gc[GTK_WIDGET_STATE(_map_widget)],
943                            FALSE, scale_rect.x, scale_rect.y,
944                            scale_rect.width,
945                            scale_rect.height);
946
947         /* Now calculate and draw the distance. */
948         unit2latlon(_center.unitx - pixel2unit(SCALE_WIDTH / 2 - 4), _center.unity, lat1, lon1);
949         unit2latlon(_center.unitx + pixel2unit(SCALE_WIDTH / 2 - 4), _center.unity, lat2, lon2);
950         distance=calculate_distance(lat1, lon1, lat2, lon2) * UNITS_CONVERT[_units];
951
952         if (distance < 1.f)
953                 g_snprintf(buffer, sizeof(buffer), "%0.2f %s", distance, UNITS_TEXT[_units]);
954         else if (distance < 10.f)
955                 g_snprintf(buffer, sizeof(buffer), "%0.1f %s", distance, UNITS_TEXT[_units]);
956         else
957                 g_snprintf(buffer, sizeof(buffer), "%0.f %s", distance, UNITS_TEXT[_units]);
958
959         pango_layout_set_text(scale_layout, buffer, -1);
960         pango_layout_get_pixel_size(scale_layout, &width, NULL);
961
962         /* Draw the layout itself. */
963         gdk_draw_layout(_map_widget->window,
964                         _map_widget->style->fg_gc[GTK_WIDGET_STATE(_map_widget)],
965                         scale_rect.x + (scale_rect.width - width) / 2,
966                         scale_rect.y, scale_layout);
967
968         /* Draw little hashes on the ends. */
969         gdk_draw_line(_map_widget->window,
970                       _map_widget->style->fg_gc[GTK_WIDGET_STATE(_map_widget)],
971                       scale_rect.x + 4,
972                       scale_rect.y + scale_rect.height / 2 - 4,
973                       scale_rect.x + 4,
974                       scale_rect.y + scale_rect.height / 2 + 4);
975         gdk_draw_line(_map_widget->window,
976                       _map_widget->style->fg_gc[GTK_WIDGET_STATE(_map_widget)],
977                       scale_rect.x + 4,
978                       scale_rect.y + scale_rect.height / 2,
979                       scale_rect.x + (scale_rect.width - width) / 2 - 4,
980                       scale_rect.y + scale_rect.height / 2);
981         gdk_draw_line(_map_widget->window,
982                       _map_widget->style->fg_gc[GTK_WIDGET_STATE(_map_widget)],
983                       scale_rect.x + scale_rect.width - 4,
984                       scale_rect.y + scale_rect.height / 2 - 4,
985                       scale_rect.x + scale_rect.width - 4,
986                       scale_rect.y + scale_rect.height / 2 + 4);
987         gdk_draw_line(_map_widget->window,
988                       _map_widget->style->fg_gc[GTK_WIDGET_STATE(_map_widget)],
989                       scale_rect.x + scale_rect.width - 4,
990                       scale_rect.y + scale_rect.height / 2,
991                       scale_rect.x + (scale_rect.width + width) / 2 + 4,
992                       scale_rect.y + scale_rect.height / 2);
993         }
994 }
995
996 static gboolean 
997 map_cb_expose(GtkWidget * widget, GdkEventExpose * event)
998 {
999 gdk_draw_drawable(GDK_DRAWABLE(_map_widget->window),
1000                 _gc[COLORABLE_MARK],
1001                 _map_pixmap,
1002                 event->area.x + _offsetx, event->area.y + _offsety,
1003                 event->area.x, event->area.y,
1004                 event->area.width, event->area.height);
1005 map_draw_mark(_gps);
1006
1007 /* Draw scale, if necessary. */
1008 if (_show_scale)
1009         map_scale_draw(event);
1010
1011 #if 0
1012 if (_speed_on)
1013         map_speed_draw();
1014 #endif
1015
1016 return TRUE;
1017 }
1018
1019 int 
1020 map_zoom(gint zdir)
1021 {
1022 gint nzoom;
1023
1024 nzoom=_zoom+zdir;
1025 if ((nzoom > 0) && (nzoom < MAX_ZOOM - 1)) {
1026         map_set_zoom(nzoom);
1027 }
1028 return nzoom;
1029 }
1030
1031 gboolean
1032 map_zoom_in(void)
1033 {
1034 map_zoom(-1);
1035 return FALSE;
1036 }
1037
1038 gboolean
1039 map_zoom_out(void)
1040 {
1041 map_zoom(1);
1042 return FALSE;
1043 }
1044
1045 static gboolean
1046 map_autozoomer(void)
1047 {
1048 static gfloat z=5.0;
1049 static gint last=5;
1050 gint iz;
1051
1052 if (zoom_timeout_sid==0)
1053         return FALSE;
1054
1055 z=(z+_gps->data.speed+1)/5;
1056 if (z>5) z=5.0; else if (z<1) z=1.0;
1057 iz=(gint)roundf(z);
1058 #ifdef DEBUG
1059 g_debug("Setting autozoom to: %f %d S:%f\n", z, iz, _gps->data.speed);
1060 #endif
1061 if (iz>last) 
1062         iz=last+1; 
1063 else if (iz<last) 
1064         iz=last-1;
1065 last=iz;
1066 map_set_zoom(iz);
1067
1068 return TRUE;
1069 }
1070
1071 void
1072 map_set_autozoom(gboolean az, gfloat speed)
1073 {
1074 if (az==TRUE) {
1075         MACRO_BANNER_SHOW_INFO(_window, "Autozoom enabled");
1076         zoom_timeout_sid=g_timeout_add(speed<5 ? 2000 : 5000, (GSourceFunc) map_autozoomer, NULL);
1077         return;
1078 } else {
1079         if (zoom_timeout_sid) {
1080                 g_source_remove(zoom_timeout_sid);
1081                 zoom_timeout_sid=0;
1082                 MACRO_BANNER_SHOW_INFO(_window, "Autozoom disabled");
1083         }
1084         return;
1085 }
1086
1087 }
1088
1089 static void 
1090 map_draw_route(gint x, gint y)
1091 {
1092 cmenu_route_add_way(x, y);
1093 }
1094
1095 static void 
1096 map_draw_track(gint x, gint y)
1097 {
1098 _gps->data.unitx=x2unit((gint) (x + 0.5));
1099 _gps->data.unity=y2unit((gint) (y + 0.5));
1100 unit2latlon(_gps->data.unitx, _gps->data.unity, _gps->data.lat, _gps->data.lon);
1101 _gps->data.speed=20.f;
1102 gps_data_integerize(&_gps->data);
1103 _gps->data.time=time(NULL);
1104 track_add(&_gps->data);
1105 map_refresh_mark();
1106 }
1107
1108 static void
1109 map_set_place_information(osm_way *s, osm_place *mp, osm_place *sp)
1110 {
1111 gchar buffer[256];
1112
1113 if (!s && !mp && !sp) {
1114         g_snprintf(buffer, sizeof(buffer), _("Unknown location"));
1115 } else {
1116         /* oh, fun */
1117         g_snprintf(buffer, sizeof(buffer), "%s%s%s%s%s%s%s%s%s%s", 
1118         s ? s->name ? s->name : "" : "",
1119         s ? s->ref ? ", " : "" : "",
1120         s ? s->ref ? s->ref : "" : "",
1121         s ? s->int_ref ? " (" : "" : "",
1122         s ? s->int_ref ? s->int_ref : "" : "",
1123         s ? s->int_ref ? ") " : "" : "",
1124         (sp && sp->name) ? " in " : "",
1125         (sp && sp->name) ? sp->name : "",
1126         (mp && mp->name) ? " in " : "",
1127         (mp && mp->name) ? mp->name : "");
1128 }
1129 gtk_label_set_label(GTK_LABEL(info_banner.location), buffer);
1130 }
1131
1132 static void
1133 map_update_destination(gdouble lat, gdouble lon)
1134 {
1135 gdouble dh=0.0;
1136 gdouble dt, cdist;
1137 static gdouble prev_dt=99999.0;
1138 static gboolean dest_reached=FALSE;
1139 gchar buffer[64];
1140
1141 if (_dest.valid) {
1142         dt=calculate_distance(lat, lon, _dest.lat, _dest.lon);
1143         dh=calculate_course(lat, lon, _dest.lat, _dest.lon);
1144         cdist=dt*UNITS_CONVERT[_units];
1145
1146         g_snprintf(buffer, sizeof(buffer), "%.02f %s (%0.02f)", cdist, UNITS_TEXT[_units], dh<0 ? 360+dh : dh);
1147         gtk_label_set_label(GTK_LABEL(info_banner.distance), buffer);
1148
1149         if (dt<0.005 && dest_reached==FALSE) {
1150                 if (_center_mode>0)
1151                         announce_destination_reached();
1152                 dest_reached=TRUE;
1153         } else if (dt<prev_dt-KM10KNOTS) {
1154                 if (_center_mode>0)
1155                         announce_distance_to_destination(cdist, UNITS_TEXT[_units], KM10KNOTS);
1156                 prev_dt=dt;
1157         } else if (dt>prev_dt+KM10KNOTS/4) {
1158                 prev_dt=dt;
1159         }
1160         if (dt>0.05) {
1161                 dest_reached=FALSE;
1162         }
1163         g_debug("%f (Prev:%f)\n", prev_dt, dt);
1164 } else {
1165         dest_reached=FALSE;
1166         prev_dt=99999.0;
1167         gtk_label_set_label(GTK_LABEL(info_banner.distance), "");
1168 }
1169 gtk_compass_set_dest_heading(_gps_compass, _dest.valid, (gfloat)dh);
1170 gtk_compass_set_dest_heading(_tab_compass, _dest.valid, (gfloat)dh);
1171
1172 if (_next_way && _next_way->point) {
1173         gdouble wp_lat, wp_lon;
1174         gfloat wc;
1175
1176         unit2latlon(_next_way->point->unitx,_next_way->point->unity, wp_lat, wp_lon);
1177         wc=calculate_course(lat, lon, wp_lat, wp_lon);
1178         gtk_compass_set_way_heading(_gps_compass, TRUE, wc);
1179         gtk_compass_set_way_heading(_tab_compass, TRUE, wc);
1180 } else {
1181         gtk_compass_set_way_heading(_gps_compass, FALSE, 0);
1182         gtk_compass_set_way_heading(_tab_compass, FALSE, 0);
1183 }
1184
1185 }
1186
1187 /**
1188  * Query the OSM database for where we are.
1189  * 
1190  * XXX: This is the wrong place for this function.
1191  */
1192 static void 
1193 map_update_location(gdouble lat, gdouble lon, gboolean force)
1194 {
1195 gint ilat, ilon;
1196 static gboolean inp=FALSE;
1197
1198 /* We run the gtk mainloop in progress callback so we can be called again, we don't like that */
1199 if (inp==TRUE)
1200         return;
1201 inp=TRUE;
1202
1203 ilat=lat2mp_int(lat);
1204 ilon=lon2mp_int(lon);
1205
1206 if (_gps->data.fix>1 && !force)
1207         osm_set_way_range_from_speed(_gps->data.speed);
1208 else
1209         osm_set_way_range(OSM_RANGE_WAY/4);
1210
1211 osm_progress_set_widget(_db, _progress_item);
1212 osm_get_location_data(ilat, ilon, _gps->data.heading, &map_loc);
1213 if (map_loc.valid)
1214         map_set_place_information(map_loc.street, map_loc.primary, map_loc.secondary);
1215 else
1216         map_set_place_information(NULL, NULL, NULL);
1217 osm_progress_set_widget(_db, NULL);
1218
1219 map_update_destination(lat, lon);
1220
1221 inp=FALSE;
1222 }
1223
1224 /**
1225  * Mouse scroller zoom in/out callback
1226  */
1227 static gboolean
1228 map_cb_scroll_event(GtkWidget * widget, GdkEventScroll * event)
1229 {
1230 if (event->direction == GDK_SCROLL_UP) {
1231         map_center_unit(x2unit((gint) (event->x + 0.5)), y2unit((gint) (event->y + 0.5)));
1232         map_set_zoom(_zoom - 1);
1233 } else if (event->direction == GDK_SCROLL_DOWN) {
1234         map_center_unit(x2unit((gint) (event->x + 0.5)), y2unit((gint) (event->y + 0.5)));
1235         map_set_zoom(_zoom + 1);
1236 }
1237 return FALSE;
1238 }
1239
1240 /** 
1241  * Start map drag operation
1242  */
1243 static void
1244 map_drag_start(gint x,gint y)
1245 {
1246 press[0] = x;
1247 press[1] = y;
1248 before[0] = press[0];
1249 before[1] = press[1];
1250 _center_mode=CENTER_MANUAL;
1251 if (map_drag_id!=0)
1252         g_signal_handler_disconnect(G_OBJECT(_map_widget), map_drag_id);
1253
1254 map_drag_id=g_signal_connect(G_OBJECT(_map_widget), "motion_notify_event", G_CALLBACK(map_follow_move_cb), NULL);
1255 }
1256
1257 /**
1258  * Stop map drag operation
1259  */
1260 static void
1261 map_drag_stop(gint x, gint y)
1262 {
1263 if (map_drag_id==0)
1264         return;
1265
1266 g_signal_handler_disconnect(G_OBJECT(_map_widget), map_drag_id);
1267
1268 release[0]=x;
1269 release[1]=y;
1270 press[0]=0;
1271 press[1]=0;
1272 release[0]=0;
1273 release[1]=0;
1274 before[0]=0;
1275 before[1]=0;
1276 map_drag_id=0;
1277 map_force_redraw();
1278 }
1279
1280 /* Workaround hildon content menu problem */
1281 static gboolean
1282 map_cb_show_poi_info_dialog(gpointer data)
1283 {
1284 guint poi_id=GPOINTER_TO_INT(data);
1285 if (poi_info_dialog(poi_id)==FALSE)
1286         g_printerr("Huh? Failed to display info dialog\n");
1287 return FALSE;
1288 }
1289
1290 static gboolean 
1291 map_cb_button_press(GtkWidget * widget, GdkEventButton * event)
1292 {
1293 _cmenu_position_x = event->x + 0.5;
1294 _cmenu_position_y = event->y + 0.5;
1295
1296 switch (event->button) {
1297 case 1:
1298         if (event->type==GDK_2BUTTON_PRESS) {
1299                 map_center_unit(x2unit((gint) (event->x + 0.5)), y2unit((gint) (event->y + 0.5)));
1300 #ifdef MAP_ZOOM_ON_DOUBLE_CLICK
1301                 map_set_zoom(_zoom-1);
1302 #endif
1303                 map_data_needs_refresh=TRUE;
1304                 return FALSE;
1305         }
1306         if (event->type==GDK_3BUTTON_PRESS) {
1307                 return FALSE;
1308         }
1309
1310         map_drag_start(event->x, event->y);
1311         return FALSE;
1312 break;
1313 case 2:
1314         map_set_zoom(_zoom - 1);
1315 break;
1316 case 3:
1317 #ifndef WITH_HILDON
1318         gtk_menu_popup(GTK_MENU(_menu_map), NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time());
1319 #endif
1320 break;
1321 }
1322 /* Return FALSE to allow context menu to work. */
1323 return FALSE;
1324 }
1325
1326 static gboolean 
1327 map_cb_button_release(GtkWidget *widget, GdkEventButton *event)
1328 {
1329 gdouble lat, lon;
1330 guint poi_id;
1331 gint ux, uy;
1332
1333 switch (event->button) {
1334 case 1:
1335         if (_center_mode>0)
1336                 set_action_activate("autocenter_none", TRUE);
1337
1338         switch (map_mode) {
1339         case MAP_MODE_DRAW_TRACK:
1340                 map_draw_track(event->x, event->y);
1341         break;
1342         case MAP_MODE_DRAW_ROUTE:
1343                 map_draw_route(event->x, event->y);
1344         break;
1345         case MAP_MODE_SET_ROUTE_FROM:
1346         case MAP_MODE_SET_ROUTE_POINT:
1347         case MAP_MODE_SET_ROUTE_TO:
1348         break;
1349         default:
1350                 ux=x2unit(_cmenu_position_x);
1351                 uy=y2unit(_cmenu_position_y);
1352
1353                 unit2latlon(ux, uy, lat, lon);
1354                 if (map_poi_find_at_latlon(lat, lon, &poi_id)==TRUE) {
1355                         g_debug("POI: %d\n", poi_id);
1356                         g_idle_add_full(G_PRIORITY_HIGH_IDLE,(GSourceFunc)map_cb_show_poi_info_dialog, GINT_TO_POINTER(poi_id), NULL);
1357                 }
1358                 if (map_data_needs_refresh==TRUE) {
1359                         map_data_needs_refresh=FALSE;
1360                         map_render_data();
1361                         g_idle_add_full(G_PRIORITY_HIGH_IDLE,(GSourceFunc)map_update_location_from_center, NULL, NULL);
1362                 }
1363                 map_drag_stop(event->x, event->y);
1364         break;
1365         }
1366 break;
1367 case 2:
1368         /* */
1369 break;
1370 case 3:
1371 break;
1372 }
1373
1374 /* Return FALSE to avoid context menu (if it hasn't popped up already). */
1375 return FALSE;
1376 }
1377
1378 gboolean 
1379 map_dialog_goto_latlon(void)
1380 {
1381 GtkWidget *dialog;
1382 GtkWidget *table;
1383 GtkWidget *label;
1384 GtkWidget *txt_lat;
1385 GtkWidget *txt_lon;
1386
1387 dialog = gtk_dialog_new_with_buttons(_("Go to Lat/Lon"),
1388                      GTK_WINDOW(_window),
1389                      GTK_DIALOG_MODAL, GTK_STOCK_OK,
1390                      GTK_RESPONSE_ACCEPT,
1391                      GTK_STOCK_CANCEL,
1392                      GTK_RESPONSE_REJECT, NULL);
1393
1394 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), table = gtk_table_new(2, 3, FALSE), TRUE, TRUE, 0);
1395
1396 gtk_table_attach(GTK_TABLE(table), label = gtk_label_new(_("Latitude")),0, 1, 0, 1, GTK_FILL, 0, 2, 4);
1397 gtk_misc_set_alignment(GTK_MISC(label), 1.f, 0.5f);
1398
1399 gtk_table_attach(GTK_TABLE(table), txt_lat = gtk_entry_new(), 1, 2, 0, 1, GTK_FILL, 0, 2, 4);
1400 gtk_misc_set_alignment(GTK_MISC(label), 0.0f, 0.5f);
1401
1402 gtk_table_attach(GTK_TABLE(table), label = gtk_label_new(_("Longitude")), 0, 1, 1, 2, GTK_FILL, 0, 2, 4);
1403 gtk_misc_set_alignment(GTK_MISC(label), 1.f, 0.5f);
1404
1405 gtk_table_attach(GTK_TABLE(table), txt_lon = gtk_entry_new(), 1, 2, 1, 2, GTK_FILL, 0, 2, 4);
1406 gtk_misc_set_alignment(GTK_MISC(label), 0.0f, 0.5f);
1407
1408 #if defined (WITH_DEVICE_770) && !defined(WITH_HILDON_NEW)
1409 g_object_set(G_OBJECT(txt_lat), HILDON_INPUT_MODE_HINT, HILDON_INPUT_MODE_HINT_NUMERICSPECIAL, NULL);
1410 g_object_set(G_OBJECT(txt_lon), HILDON_INPUT_MODE_HINT, HILDON_INPUT_MODE_HINT_NUMERICSPECIAL, NULL);
1411 #endif
1412
1413 /* Initialize with the current center position. */
1414 {
1415         gchar buffer[32];
1416         gdouble lat, lon;
1417         unit2latlon(_center.unitx, _center.unity, lat, lon);
1418         g_snprintf(buffer, sizeof(buffer), "%.06f", lat);
1419         gtk_label_set_text(GTK_LABEL(txt_lat), buffer);
1420         g_snprintf(buffer, sizeof(buffer), "%.06f", lon);
1421         gtk_label_set_text(GTK_LABEL(txt_lon), buffer);
1422 }
1423
1424 gtk_widget_show_all(dialog);
1425
1426 while (GTK_RESPONSE_ACCEPT == gtk_dialog_run(GTK_DIALOG(dialog))) {
1427         const gchar *text;
1428         gchar *error_check;
1429         gdouble lat, lon;
1430         guint unitx, unity;
1431
1432         text = gtk_entry_get_text(GTK_ENTRY(txt_lat));
1433         lat = strtof(text, &error_check);
1434         if (text == error_check || lat < -90.f || lat > 90.f) {
1435                 popup_error(dialog, _("Invalid Latitude"));
1436                 continue;
1437         }
1438
1439         text = gtk_entry_get_text(GTK_ENTRY(txt_lon));
1440         lon = strtof(text, &error_check);
1441         if (text == error_check || lon < -180.f || lon > 180.f) {
1442                 popup_error(dialog, _("Invalid Longitude"));
1443                 continue;
1444         }
1445
1446         latlon2unit(lat, lon, unitx, unity);
1447         if (_center_mode > 0)
1448                 set_action_activate("autocenter_none", TRUE);
1449
1450         map_center_unit(unitx, unity);
1451         break;
1452 }
1453 gtk_widget_destroy(dialog);
1454 return TRUE;
1455 }
1456