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