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