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