]> err.no Git - mapper/blob - src/map.c
Query place only if have moved from the previously known way.
[mapper] / src / map.c
1 #include <config.h>
2
3 #define _GNU_SOURCE
4
5 #include <unistd.h>
6 #include <stdlib.h>
7 #include <string.h>
8 #include <strings.h>
9 #include <stddef.h>
10 #include <locale.h>
11 #include <math.h>
12 #include <errno.h>
13 #include <sys/wait.h>
14 #include <glib/gstdio.h>
15 #include <gtk/gtk.h>
16 #include <fcntl.h>
17 #include <libgnomevfs/gnome-vfs.h>
18 #include <gconf/gconf-client.h>
19 #include <libxml/parser.h>
20 #include <curl/multi.h>
21 #include <libintl.h>
22 #include <locale.h>
23 #include <sqlite3.h>
24
25 #include "hildon-mapper.h"
26
27 #include "utils.h"
28 #include "map.h"
29 #include "osm.h"
30 #include "db.h"
31 #include "osm-db.h"
32 #include "poi.h"
33 #include "route.h"
34 #include "gps.h"
35 #include "bt.h"
36 #include "mapper-types.h"
37 #include "ui-common.h"
38 #include "settings.h"
39
40 Point _min_center = { -1, -1 };
41 Point _max_center = { -1, -1 };
42 Point _focus = { -1, -1 };
43
44 /** The "base tile" is the upper-left tile in the pixmap. */
45 guint _base_tilex = -5;
46 guint _base_tiley = -5;
47
48 guint _zoom = 3;                /* zoom level, from 0 to MAX_ZOOM. */
49 Point _center = { -1, -1 };     /* current center location, X. */
50
51 static guint press[2] = { 0, 0 };
52 static guint release[2] = { 0, 0 };
53 static guint before[2] = { 0, 0 };
54 static guint _id = 0;
55
56 static gint _zoom_timeout_sid=0;
57 static gint _map_mode=0;
58
59 guint _num_downloads=0;
60 guint _curr_download=0;
61
62 static osm_location map_loc = {NULL, NULL, NULL};
63 static GHashTable *map_tile_hash = NULL;
64
65 typedef struct _map_tile_rdata map_tile_rdata;
66 struct _map_tile_rdata {
67         guint tilex, tiley, zoom;
68         guint destx, desty;
69 };
70
71 void map_render_paths();
72 void map_force_redraw();
73 gboolean curl_download_timeout();
74 gchar *map_construct_url(guint tilex, guint tiley, guint zoom);
75 gboolean map_download_idle_refresh(ProgressUpdateInfo * pui);
76 static void map_update_location(gint x, gint y);
77
78 gboolean get_next_pui(gpointer key, gpointer value, ProgressUpdateInfo ** data)
79 {
80 *data = key;
81 return TRUE;
82 }
83
84 gboolean curl_download_timeout()
85 {
86         static guint destroy_counter = 50;
87         gint num_transfers = 0, num_msgs = 0;
88         gint deletes_left = 50; /* only do 50 deletes at a time. */
89         CURLMsg *msg;
90         vprintf("%s()\n", __PRETTY_FUNCTION__);
91
92         if (_curl_multi && CURLM_CALL_MULTI_PERFORM == curl_multi_perform(_curl_multi, &num_transfers))
93                 return TRUE;    /* Give UI a chance first. */
94
95         while (_curl_multi && (msg = curl_multi_info_read(_curl_multi, &num_msgs))) {
96                 if (msg->msg == CURLMSG_DONE) {
97                         if (msg->easy_handle == _autoroute_data.curl_easy) {
98                                 /* This is the autoroute download. */
99                                 /* Now, parse the autoroute and update the display. */
100                                 if (_autoroute_data.enabled
101                                     && parse_gpx(&_route,
102                                                  _autoroute_data.rdl_data.bytes,
103                                                  _autoroute_data.rdl_data.
104                                                  bytes_read, 0)) {
105                                         /* Find the nearest route point, if we're connected. */
106                                         route_find_nearest_point();
107                                         map_force_redraw();
108                                 }
109                                 cancel_autoroute(TRUE); /* We're done. Clean up. */
110                         } else {
111                                 ProgressUpdateInfo *pui = g_hash_table_lookup(_pui_by_easy, msg->easy_handle);
112                                 g_queue_push_head(_curl_easy_queue, msg->easy_handle);
113                                 g_hash_table_remove(_pui_by_easy, msg->easy_handle);
114                                 fclose(pui->file);
115                                 if (msg->data.result != CURLE_OK)
116                                         g_unlink(pui->dest_str);        /* Delete so we try again. */
117                                 curl_multi_remove_handle(_curl_multi,
118                                                          msg->easy_handle);
119                                 g_idle_add_full(G_PRIORITY_HIGH_IDLE,
120                                                 (GSourceFunc)
121                                                 map_download_idle_refresh, pui,
122                                                 NULL);
123                         }
124                 }
125         }
126
127         /* Up to 1 transfer per tile. */
128         while (num_transfers < (BUF_WIDTH_TILES * BUF_HEIGHT_TILES)
129                && g_tree_nnodes(_pui_tree)) {
130                 ProgressUpdateInfo *pui;
131                 g_tree_foreach(_pui_tree, (GTraverseFunc) get_next_pui, &pui);
132
133                 if (pui->retries) {
134                         /* This is a download. */
135                         FILE *f;
136                         g_tree_steal(_pui_tree, pui);
137                         g_tree_insert(_downloading_tree, pui, pui);
138
139                         pui->src_str = map_construct_url(pui->tilex, pui->tiley, pui->zoom);
140                         pui->dest_str = g_strdup_printf("%s/%u/%u/%u.jpg",
141                                             pui->repo->cache_dir, pui->zoom,
142                                             pui->tilex, pui->tiley);
143
144                         if (!pui->src_str) {
145                                 /* Failed to generate URL. */
146                                 g_idle_add_full(G_PRIORITY_HIGH_IDLE,
147                                                 (GSourceFunc)map_download_idle_refresh, pui,
148                                                 NULL);
149                                 continue;
150                         }
151
152                         /* Check to see if we need to overwrite. */
153                         if (pui->retries > 0) {
154                                 /* We're not updating - check if file already exists. */
155                                 if (g_file_test(pui->dest_str, G_FILE_TEST_EXISTS)) {
156                                         g_idle_add_full(G_PRIORITY_HIGH_IDLE,
157                                                         (GSourceFunc)
158                                                         map_download_idle_refresh,
159                                                         pui, NULL);
160                                         continue;
161                                 }
162                         }
163
164                         /* Attempt to open the file for writing. */
165                         if (!(f = g_fopen(pui->dest_str, "w"))
166                             && errno == ENOENT) {
167                                 /* Directory doesn't exist yet - create it, then we'll retry */
168                                 gchar buffer[BUFFER_SIZE];
169                                 snprintf(buffer, sizeof(buffer), "%s/%u/%u",
170                                          pui->repo->cache_dir, pui->zoom,
171                                          pui->tilex);
172                                 g_mkdir_with_parents(buffer, 0775);
173                                 f = g_fopen(pui->dest_str, "w");
174                         }
175
176                         if (f) {
177                                 CURL *curl_easy;
178                                 pui->file = f;
179                                 curl_easy = g_queue_pop_tail(_curl_easy_queue);
180                                 if (!curl_easy) {
181                                         /* Need a new curl_easy. */
182                                         MACRO_CURL_EASY_INIT(curl_easy);
183                                 }
184                                 curl_easy_setopt(curl_easy, CURLOPT_URL, pui->src_str);
185                                 curl_easy_setopt(curl_easy, CURLOPT_WRITEDATA, f);
186                                 g_hash_table_insert(_pui_by_easy, curl_easy, pui);
187                                 if (!_curl_multi) {
188                                         /* Initialize CURL. */
189                                         _curl_multi = curl_multi_init();
190                                         /*curl_multi_setopt(_curl_multi, CURLMOPT_PIPELINING, 1); */
191                                 }
192                                 curl_multi_add_handle(_curl_multi, curl_easy);
193                                 num_transfers++;
194                         } else {
195                                 /* Unable to download file. */
196                                 gchar buffer[BUFFER_SIZE];
197                                 snprintf(buffer, sizeof(buffer), "%s:\n%s",
198                                          _("Failed to open file for writing"),
199                                          pui->dest_str);
200                                 MACRO_BANNER_SHOW_INFO(_window, buffer);
201                                 g_idle_add_full(G_PRIORITY_HIGH_IDLE,
202                                                 (GSourceFunc)
203                                                 map_download_idle_refresh, pui,
204                                                 NULL);
205                                 continue;
206                         }
207                 } else if (--deletes_left) {
208                         /* This is a delete. */
209                         gchar buffer[BUFFER_SIZE];
210                         g_tree_steal(_pui_tree, pui);
211                         g_tree_insert(_downloading_tree, pui, pui);
212
213                         snprintf(buffer, sizeof(buffer), "%s/%u/%u/%u.jpg",
214                                  pui->repo->cache_dir, pui->zoom, pui->tilex,
215                                  pui->tiley);
216                         g_unlink(buffer);
217                         g_idle_add_full(G_PRIORITY_HIGH_IDLE,
218                                         (GSourceFunc) map_download_idle_refresh,
219                                         pui, NULL);
220                 } else
221                         break;
222         }
223
224         if (!(num_transfers || g_tree_nnodes(_pui_tree))) {
225                 /* Destroy curl after 50 counts (5 seconds). */
226                 if (--destroy_counter) {
227                         /* Clean up curl. */
228                         CURL *curr;
229                         while ((curr = g_queue_pop_tail(_curl_easy_queue)))
230                                 curl_easy_cleanup(curr);
231
232                         curl_multi_cleanup(_curl_multi);
233                         _curl_multi = NULL;
234
235                         _curl_sid = 0;
236                         vprintf("%s(): return FALSE\n", __PRETTY_FUNCTION__);
237                         return FALSE;
238                 }
239         } else
240                 destroy_counter = 50;
241
242         vprintf("%s(): return TRUE (%d, %d)\n", __PRETTY_FUNCTION__,
243                 num_transfers, g_tree_nnodes(_pui_tree));
244         return TRUE;
245 }
246
247 /**
248  * Draw the current mark (representing the current GPS location) onto
249  * _map_widget.  This method does not queue the draw area.
250  */
251 void map_draw_mark()
252 {
253         printf("%s()\n", __PRETTY_FUNCTION__);
254
255         gdk_draw_arc(_map_widget->window, _conn_state == RCVR_FIXED ? _gc[COLORABLE_MARK] : _gc[COLORABLE_MARK_OLD], FALSE,     /* not filled. */
256                      _mark_x1 - _draw_width, _mark_y1 - _draw_width,
257                      2 * _draw_width, 2 * _draw_width, 0, 360 * 64);
258         gdk_draw_line(_map_widget->window,
259                       _conn_state == RCVR_FIXED ? (_show_velvec ? _gc[COLORABLE_MARK_VELOCITY] : _gc[COLORABLE_MARK]) : _gc[COLORABLE_MARK_OLD],
260                       _mark_x1, _mark_y1, _mark_x2, _mark_y2);
261
262         vprintf("%s(): return\n", __PRETTY_FUNCTION__);
263 }
264
265 /**
266  * "Set" the mark, which translates the current GPS position into on-screen
267  * units in preparation for drawing the mark with map_draw_mark().
268  */
269 void map_set_mark()
270 {
271         printf("%s()\n", __PRETTY_FUNCTION__);
272
273         _mark_x1 = unit2x(_pos.unitx);
274         _mark_y1 = unit2y(_pos.unity);
275         _mark_x2 = _mark_x1 + (_show_velvec ? _vel_offsetx : 0);
276         _mark_y2 = _mark_y1 + (_show_velvec ? _vel_offsety : 0);
277         _mark_minx = MIN(_mark_x1, _mark_x2) - (2 * _draw_width);
278         _mark_miny = MIN(_mark_y1, _mark_y2) - (2 * _draw_width);
279         _mark_width = abs(_mark_x1 - _mark_x2) + (4 * _draw_width);
280         _mark_height = abs(_mark_y1 - _mark_y2) + (4 * _draw_width);
281
282         vprintf("%s(): return\n", __PRETTY_FUNCTION__);
283 }
284
285 /**
286  * Do an in-place scaling of a pixbuf's pixels at the given ratio from the
287  * given source location.  It would have been nice if gdk_pixbuf provided
288  * this method, but I guess it's not general-purpose enough.
289  */
290 void
291 map_pixbuf_scale_inplace(GdkPixbuf * pixbuf, guint ratio_p2,
292                          guint src_x, guint src_y)
293 {
294         guint dest_x = 0, dest_y = 0, dest_dim = TILE_SIZE_PIXELS;
295         guint rowstride = gdk_pixbuf_get_rowstride(pixbuf);
296         guint n_channels = gdk_pixbuf_get_n_channels(pixbuf);
297         guchar *pixels = gdk_pixbuf_get_pixels(pixbuf);
298
299         vprintf("%s(%d, %d, %d)\n", __PRETTY_FUNCTION__, ratio_p2, src_x, src_y);
300
301         /* Sweep through the entire dest area, copying as necessary, but
302          * DO NOT OVERWRITE THE SOURCE AREA.  We'll copy it afterward. */
303         do {
304                 guint src_dim = dest_dim >> ratio_p2;
305                 guint src_endx = src_x - dest_x + src_dim;
306                 gint x, y;
307
308                 for (y = dest_dim - 1; y >= 0; y--) {
309                         guint src_offset_y, dest_offset_y;
310
311                         src_offset_y = (src_y + (y >> ratio_p2)) * rowstride;
312                         dest_offset_y = (dest_y + y) * rowstride;
313                         x = dest_dim - 1;
314
315                         if ((unsigned)(dest_y + y - src_y) < src_dim
316                             && (unsigned)(dest_x + x - src_x) < src_dim)
317                                 x -= src_dim;
318
319                         for (; x >= 0; x--) {
320                                 guint src_offset, dest_offset, i;
321
322                                 src_offset = src_offset_y + (src_x + (x >> ratio_p2)) * n_channels;
323                                 dest_offset = dest_offset_y + (dest_x + x) * n_channels;
324
325                                 pixels[dest_offset] = pixels[src_offset];
326                                 for (i = n_channels - 1; i; i--)
327                                         pixels[dest_offset + i] = pixels[src_offset + i];
328
329                                 if ((unsigned)(dest_y + y - src_y) < src_dim && x == src_endx)
330                                         x -= src_dim;
331                         }
332                 }
333                 /* Reuse src_dim and src_endx to store new src_x and src_y. */
334                 src_dim = src_x + ((src_x - dest_x) >> ratio_p2);
335                 src_endx = src_y + ((src_y - dest_y) >> ratio_p2);
336                 dest_x = src_x;
337                 dest_y = src_y;
338                 src_x = src_dim;
339                 src_y = src_endx;
340         }
341         while ((dest_dim >>= ratio_p2) > 1);
342
343         vprintf("%s(): return\n", __PRETTY_FUNCTION__);
344 }
345
346 /**
347  * Trim pixbufs that are bigger than tiles. (Those pixbufs result, when
348  * captions should be cut off.)
349  */
350 GdkPixbuf *pixbuf_trim(GdkPixbuf * pixbuf)
351 {
352         vprintf("%s()\n", __PRETTY_FUNCTION__);
353         GdkPixbuf *mpixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, gdk_pixbuf_get_has_alpha(pixbuf),
354                            8, TILE_SIZE_PIXELS, TILE_SIZE_PIXELS);
355
356         gdk_pixbuf_copy_area(pixbuf,
357                              (gdk_pixbuf_get_width(pixbuf) - TILE_SIZE_PIXELS) / 2,
358                              (gdk_pixbuf_get_height(pixbuf) - TILE_SIZE_PIXELS) / 2, 
359                              TILE_SIZE_PIXELS,
360                              TILE_SIZE_PIXELS, mpixbuf, 0, 0);
361
362         g_object_unref(pixbuf);
363         vprintf("%s(): return\n", __PRETTY_FUNCTION__);
364         return mpixbuf;
365 }
366
367 /**
368  * Given a wms uri pattern, compute the coordinate transformation and
369  * trimming.
370  * 'proj' is used for the conversion
371  */
372 gchar *map_convert_wms_to_wms(gint tilex, gint tiley, gint zoomlevel,
373                               gchar * uri)
374 {
375         gint system_retcode;
376         gchar cmd[BUFFER_SIZE], srs[BUFFER_SIZE];
377         gchar *ret = NULL;
378         FILE *in;
379         gfloat lon1, lat1, lon2, lat2;
380
381         gchar *widthstr = strcasestr(uri, "WIDTH=");
382         gchar *heightstr = strcasestr(uri, "HEIGHT=");
383         gchar *srsstr = strcasestr(uri, "SRS=EPSG");
384         gchar *srsstre = strchr(srsstr, '&');
385         vprintf("%s()\n", __PRETTY_FUNCTION__);
386
387         /* missing: test if found */
388         strcpy(srs, "epsg");
389         strncpy(srs + 4, srsstr + 8, 256);
390         /* missing: test srsstre-srsstr < 526 */
391         srs[srsstre - srsstr - 4] = 0;
392         /* convert to lower, as WMC is EPSG and cs2cs is epsg */
393
394         gint dwidth = widthstr ? atoi(widthstr + 6) - TILE_SIZE_PIXELS : 0;
395         gint dheight = heightstr ? atoi(heightstr + 7) - TILE_SIZE_PIXELS : 0;
396
397         unit2latlon(tile2zunit(tilex, zoomlevel)
398                     - pixel2zunit(dwidth / 2, zoomlevel),
399                     tile2zunit(tiley + 1, zoomlevel)
400                     + pixel2zunit((dheight + 1) / 2, zoomlevel), lat1, lon1);
401
402         unit2latlon(tile2zunit(tilex + 1, zoomlevel)
403                     + pixel2zunit((dwidth + 1) / 2, zoomlevel),
404                     tile2zunit(tiley, zoomlevel)
405                     - pixel2zunit(dheight / 2, zoomlevel), lat2, lon2);
406
407         setlocale(LC_NUMERIC, "C");
408
409         snprintf(cmd, sizeof(cmd),
410                  "(echo \"%.6f %.6f\"; echo \"%.6f %.6f\") | "
411                  "/usr/bin/cs2cs +proj=longlat +datum=WGS84 +to +init=%s -f %%.6f "
412                  " > /tmp/tmpcs2cs ", lon1, lat1, lon2, lat2, srs);
413         vprintf("Running command: %s\n", cmd);
414         system_retcode = system(cmd);
415
416         if (system_retcode)
417                 g_printerr("cs2cs returned error code %d\n",
418                            WEXITSTATUS(system_retcode));
419         else if (!(in = g_fopen("/tmp/tmpcs2cs", "r")))
420                 g_printerr("Cannot open results of conversion\n");
421         else if (5 !=
422                  fscanf(in, "%f %f %s %f %f", &lon1, &lat1, cmd, &lon2,
423                         &lat2)) {
424                 g_printerr("Wrong conversion\n");
425                 fclose(in);
426         } else {
427                 fclose(in);
428                 ret = g_strdup_printf(uri, lon1, lat1, lon2, lat2);
429         }
430
431         setlocale(LC_NUMERIC, "");
432
433         vprintf("%s(): return %s\n", __PRETTY_FUNCTION__, ret);
434         return ret;
435 }
436
437 /**
438  * Given the xyz coordinates of our map coordinate system, write the qrst
439  * quadtree coordinates to buffer.
440  */
441 void
442 map_convert_coords_to_quadtree_string(gint x, gint y, gint zoomlevel,
443                                       gchar * buffer, const gchar initial,
444                                       const gchar * const quadrant)
445 {
446         gchar *ptr = buffer;
447         gint n;
448         vprintf("%s()\n", __PRETTY_FUNCTION__);
449
450         if (initial)
451                 *ptr++ = initial;
452
453         for (n = 16 - zoomlevel; n >= 0; n--) {
454                 gint xbit = (x >> n) & 1;
455                 gint ybit = (y >> n) & 1;
456                 *ptr++ = quadrant[xbit + 2 * ybit];
457         }
458         *ptr++ = '\0';
459         vprintf("%s(): return\n", __PRETTY_FUNCTION__);
460 }
461
462 /**
463  * Construct the URL that we should fetch, based on the current URI format.
464  * This method works differently depending on if a "%s" string is present in
465  * the URI format, since that would indicate a quadtree-based map coordinate
466  * system.
467  */
468 gchar *map_construct_url(guint tilex, guint tiley, guint zoom)
469 {
470         vprintf("%s()\n", __PRETTY_FUNCTION__);
471         switch (_curr_repo->type) {
472         case REPOTYPE_XYZ:
473                 return g_strdup_printf(_curr_repo->url, tilex, tiley, zoom);
474
475         case REPOTYPE_XYZ_INV:
476                 return g_strdup_printf(_curr_repo->url, 17 - zoom, tilex,
477                                        tiley);
478
479         case REPOTYPE_QUAD_QRST:
480                 {
481                         gchar location[MAX_ZOOM + 2];
482                         map_convert_coords_to_quadtree_string(tilex, tiley,
483                                                               zoom, location,
484                                                               't', "qrts");
485                         return g_strdup_printf(_curr_repo->url, location);
486                 }
487
488         case REPOTYPE_QUAD_ZERO:
489                 {
490                         /* This is a zero-based quadtree URI. */
491                         gchar location[MAX_ZOOM + 2];
492                         map_convert_coords_to_quadtree_string(tilex, tiley,
493                                                               zoom, location,
494                                                               '\0', "0123");
495                         return g_strdup_printf(_curr_repo->url, location);
496                 }
497
498         case REPOTYPE_WMS:
499                 return map_convert_wms_to_wms(tilex, tiley, zoom,
500                                               _curr_repo->url);
501
502         default:
503                 return NULL;
504         }
505         vprintf("%s(): ERROR\n", __PRETTY_FUNCTION__);
506         return "";
507 }
508
509 #ifdef WITH_OSSO
510 void con_check_connection(void)
511 {
512 iap_connect();
513 }
514 #else
515 void con_check_connection(void) 
516 {
517 /* NetworkManager support ? */
518 }
519 #endif
520
521 /**
522  * Initiate a download of the given xyz coordinates using the given buffer
523  * as the URL.  If the map already exists on disk, or if we are already
524  * downloading the map, then this method does nothing.
525  */
526 void map_initiate_download(guint tilex, guint tiley, guint zoom, gint retries)
527 {
528         ProgressUpdateInfo *pui;
529         vprintf("%s(%u, %u, %u, %d)\n", __PRETTY_FUNCTION__, tilex, tiley, zoom,
530                 retries);
531
532         con_check_connection();
533
534         pui = g_slice_new(ProgressUpdateInfo);
535         pui->tilex = tilex;
536         pui->tiley = tiley;
537         pui->zoom = zoom;
538         pui->priority = (abs((gint) tilex - unit2tile(_center.unitx))
539                          + abs((gint) tiley - unit2tile(_center.unity)));
540         if (!retries)
541                 pui->priority = -pui->priority; /* "Negative" makes them lowest pri. */
542         pui->retries = retries;
543         pui->repo = _curr_repo;
544
545         if (g_tree_lookup(_pui_tree, pui)
546             || g_tree_lookup(_downloading_tree, pui)) {
547                 /* Already downloading. */
548                 g_slice_free(ProgressUpdateInfo, pui);
549                 return;
550         }
551         pui->src_str = NULL;
552         pui->dest_str = NULL;
553         pui->file = NULL;
554
555         g_tree_insert(_pui_tree, pui, pui);
556 #ifdef WITH_OSSO
557         if (iap_is_connected() && !_curl_sid)
558 #endif
559                 _curl_sid = g_timeout_add(100, (GSourceFunc) curl_download_timeout, NULL);
560
561         if (!_num_downloads++ && !_download_banner)
562                 _download_banner = hildon_banner_show_progress(_window, NULL,_("Downloading maps"));
563
564         vprintf("%s(): return\n", __PRETTY_FUNCTION__);
565 }
566
567 void
568 map_render_tile(guint tilex, guint tiley, guint destx, guint desty,
569                 gboolean fast_fail)
570 {
571         GdkPixbuf *pixbuf = NULL;
572         gchar buffer[BUFFER_SIZE];
573         GError *error = NULL;
574         gint zoff;
575
576         vprintf("%s(%u, %u, %u, %u)\n", __PRETTY_FUNCTION__,
577                 tilex, tiley, destx, desty);
578
579         if (tilex < _world_size_tiles && tiley < _world_size_tiles) {
580                 /* The tile is possible. */
581                 for (zoff = (_curr_repo->double_size ? 1 : 0);
582                      !pixbuf && (_zoom + zoff) <= MAX_ZOOM
583                      && zoff <= TILE_SIZE_P2; zoff += 1) {
584                         snprintf(buffer, sizeof(buffer), "%s/%u/%u/%u.jpg",
585                                  _curr_repo->cache_dir, _zoom + zoff,
586                                  (tilex >> zoff), (tiley >> zoff));
587
588                         pixbuf = gdk_pixbuf_new_from_file(buffer, &error);
589                         if (error || !pixbuf) {
590                                 /* Delete so we try again some other day. */
591                                 g_unlink(buffer);       
592                                 pixbuf = NULL;
593                                 error = NULL;
594
595                                 /* Download, if we should. */
596                                 if (_auto_download && _curr_repo->type != REPOTYPE_NONE &&
597                                     !((_zoom + zoff -
598                                        (_curr_repo->double_size ? 1 : 0))
599                                       % _curr_repo->dl_zoom_steps)) {
600                                         if (!fast_fail)
601                                                 map_initiate_download(tilex >> zoff,
602                                                                       tiley >> zoff,
603                                                                       _zoom + zoff,
604                                                                       -INITIAL_DOWNLOAD_RETRIES);
605                                         fast_fail = TRUE;
606                                 }
607                         } else {
608                                 /* Check if we need to trim. */
609                                 if (gdk_pixbuf_get_width(pixbuf) != TILE_SIZE_PIXELS
610                                     || gdk_pixbuf_get_height(pixbuf) != TILE_SIZE_PIXELS)
611                                         pixbuf = pixbuf_trim(pixbuf);
612                                 /* Check if we need to blit. */
613                                 if (zoff) {
614                                         map_pixbuf_scale_inplace(pixbuf, zoff,
615                                                                  (tilex - ((tilex >> zoff) << zoff)) << (TILE_SIZE_P2 - zoff),
616                                                                  (tiley - ((tiley >> zoff) << zoff)) << (TILE_SIZE_P2 - zoff));
617 #ifdef DEBUG
618                                         g_printf("I: %s (%d)\n", buffer, zoff);
619 #endif
620                                 }
621                         }
622                 }
623         }
624         if (pixbuf) {
625                 gdk_draw_pixbuf(_map_pixmap,
626                                 _gc[COLORABLE_MARK],
627                                 pixbuf, 0, 0, destx, desty,
628                                 TILE_SIZE_PIXELS, TILE_SIZE_PIXELS,
629                                 GDK_RGB_DITHER_NONE, 0, 0);
630                 g_object_unref(pixbuf);
631         } else {
632                 gdk_draw_rectangle(_map_pixmap, _map_widget->style->black_gc,
633                                    TRUE, destx, desty, TILE_SIZE_PIXELS, TILE_SIZE_PIXELS);
634         }
635
636         vprintf("%s(): return\n", __PRETTY_FUNCTION__);
637 }
638
639 gboolean map_render_tile_idle(map_tile_rdata *mtr)
640 {
641 map_render_tile(mtr->tilex, mtr->tiley, mtr->destx, mtr->desty, FALSE);
642 gtk_widget_queue_draw_area(_map_widget, mtr->destx, mtr->desty, TILE_SIZE_PIXELS, TILE_SIZE_PIXELS);
643
644 g_slice_free(map_tile_rdata, mtr);
645 return FALSE;
646 }
647
648 gboolean map_download_idle_refresh(ProgressUpdateInfo * pui)
649 {
650         vprintf("%s(%p, %s)\n", __PRETTY_FUNCTION__, pui, pui->src_str);
651
652         /* Test if download succeeded (only if retries != 0). */
653         if (!pui->retries || g_file_test(pui->dest_str, G_FILE_TEST_EXISTS)) {
654                 gint zoom_diff = pui->zoom - _zoom;
655                 /* Only refresh at same or "lower" (more detailed) zoom level. */
656                 if (zoom_diff >= 0) {
657                         /* If zoom has changed since we first put in the request for
658                          * this tile, then we may have to update more than one tile. */
659                         guint tilex, tiley, tilex_end, tiley_end;
660                         for (tilex = pui->tilex << zoom_diff,
661                              tilex_end = tilex + (1 << zoom_diff);
662                              tilex < tilex_end; tilex++) {
663                                 for (tiley = pui->tiley << zoom_diff,
664                                      tiley_end = tiley + (1 << zoom_diff);
665                                      tiley < tiley_end; tiley++) {
666                                         if ((tilex - _base_tilex) < BUF_WIDTH_TILES && (tiley - _base_tiley) < BUF_HEIGHT_TILES) {
667                                                 map_render_tile(tilex, tiley,
668                                                                 ((tilex - _base_tilex) << TILE_SIZE_P2),
669                                                                 ((tiley - _base_tiley) << TILE_SIZE_P2),
670                                                                 TRUE);
671                                                 MACRO_MAP_RENDER_DATA();
672                                                 gtk_widget_queue_draw_area
673                                                     (_map_widget,
674                                                      ((tilex - _base_tilex) << TILE_SIZE_P2) - _offsetx,
675                                                      ((tiley - _base_tiley) << TILE_SIZE_P2) - _offsety,
676                                                      TILE_SIZE_PIXELS, TILE_SIZE_PIXELS);
677                                         }
678                                 }
679                         }
680                 }
681         }
682         /* Else the download failed. Update retries and maybe try again. */
683         else {
684                 if (pui->retries > 0)
685                         --pui->retries;
686                 else if (pui->retries < 0)
687                         ++pui->retries;
688                 if (pui->retries) {
689                         /* removal automatically calls progress_update_info_free(). */
690                         g_tree_steal(_downloading_tree, pui);
691                         g_tree_insert(_pui_tree, pui, pui);
692 #ifdef WITH_OSSO
693                         if (iap_is_connected() && !_curl_sid)
694 #endif
695                                 _curl_sid = g_timeout_add(100,
696                                                           (GSourceFunc)
697                                                           curl_download_timeout,
698                                                           NULL);
699                         /* Don't do anything else. */
700                         return FALSE;
701                 } else {
702                         /* No more retries left - something must be wrong. */
703                         MACRO_BANNER_SHOW_INFO(_window,
704                                                _("Error in download.  Check internet connection"
705                                                 " and/or Map Repository URL Format."));
706                 }
707         }
708
709         /* removal automatically calls progress_update_info_free(). */
710         g_tree_remove(_downloading_tree, pui);
711
712         if (++_curr_download == _num_downloads) {
713 #ifdef WITH_HILDON
714                 gtk_widget_destroy(_download_banner);
715                 _download_banner = NULL;
716 #else
717                 gtk_widget_hide(GTK_WIDGET(_progress_item));
718 #endif
719                 _num_downloads = _curr_download = 0;
720         } else
721                 hildon_banner_set_fraction(HILDON_BANNER(_download_banner),
722                                            _curr_download / (double)_num_downloads);
723
724         vprintf("%s(): return\n", __PRETTY_FUNCTION__);
725         return FALSE;
726 }
727
728 /**
729  * Force a redraw of the entire _map_pixmap, including fetching the
730  * background maps from disk and redrawing the tracks on top of them.
731  */
732 void map_force_redraw()
733 {
734         guint new_x, new_y;
735         printf("%s()\n", __PRETTY_FUNCTION__);
736
737         for (new_y = 0; new_y < BUF_HEIGHT_TILES; ++new_y)
738                 for (new_x = 0; new_x < BUF_WIDTH_TILES; ++new_x) {
739                         map_render_tile(_base_tilex + new_x,
740                                         _base_tiley + new_y,
741                                         new_x * TILE_SIZE_PIXELS,
742                                         new_y * TILE_SIZE_PIXELS, FALSE);
743                 }
744         MACRO_MAP_RENDER_DATA();
745         MACRO_QUEUE_DRAW_AREA();
746
747         vprintf("%s(): return\n", __PRETTY_FUNCTION__);
748 }
749
750 /**
751  * Set the current zoom level.  If the given zoom level is the same as the
752  * current zoom level, or if the new zoom is invalid
753  * (not MIN_ZOOM <= new_zoom < MAX_ZOOM), then this method does nothing.
754  */
755 void map_set_zoom(guint new_zoom)
756 {
757         printf("%s(%d)\n", __PRETTY_FUNCTION__, _zoom);
758
759         /* Note that, since new_zoom is a guint and MIN_ZOOM is 0, this if
760          * condition also checks for new_zoom >= MIN_ZOOM. */
761         if (new_zoom > (MAX_ZOOM - 1))
762                 return;
763         if (new_zoom == _zoom)
764                 return;
765
766         _zoom = new_zoom / _curr_repo->view_zoom_steps
767             * _curr_repo->view_zoom_steps;
768         _world_size_tiles = unit2tile(WORLD_SIZE_UNITS);
769
770         /* If we're leading, update the center to reflect new zoom level. */
771         MACRO_RECALC_CENTER(_center.unitx, _center.unity);
772
773         /* Update center bounds to reflect new zoom level. */
774         _min_center.unitx = pixel2unit(grid2pixel(_screen_grids_halfwidth));
775         _min_center.unity = pixel2unit(grid2pixel(_screen_grids_halfheight));
776         _max_center.unitx = WORLD_SIZE_UNITS - grid2unit(_screen_grids_halfwidth) - 1;
777         _max_center.unity = WORLD_SIZE_UNITS - grid2unit(_screen_grids_halfheight) - 1;
778
779         BOUND(_center.unitx, _min_center.unitx, _max_center.unitx);
780         BOUND(_center.unity, _min_center.unity, _max_center.unity);
781
782         _base_tilex = grid2tile((gint) pixel2grid((gint) unit2pixel((gint) _center.unitx))
783                                 - (gint) _screen_grids_halfwidth);
784         _base_tiley = grid2tile(pixel2grid(unit2pixel(_center.unity))
785                                 - _screen_grids_halfheight);
786
787         /* New zoom level, so we can't reuse the old buffer's pixels. */
788
789         /* Update state variables. */
790         MACRO_RECALC_OFFSET();
791         MACRO_RECALC_FOCUS_BASE();
792         MACRO_RECALC_FOCUS_SIZE();
793
794         map_set_mark();
795         map_force_redraw();
796
797         vprintf("%s(): return\n", __PRETTY_FUNCTION__);
798 }
799
800 /**
801  * Center the view on the given unitx/unity.
802  */
803 void map_center_unit(guint new_center_unitx, guint new_center_unity)
804 {
805         gint new_base_tilex, new_base_tiley;
806         guint new_x, new_y;
807         guint j, k, base_new_x, base_old_x, old_x, old_y, iox, ioy;
808         printf("%s(%d, %d)\n", __PRETTY_FUNCTION__,
809                new_center_unitx, new_center_unity);
810
811         /* Assure that _center.unitx/y are bounded. */
812         BOUND(new_center_unitx, _min_center.unitx, _max_center.unitx);
813         BOUND(new_center_unity, _min_center.unity, _max_center.unity);
814
815         _center.unitx = new_center_unitx;
816         _center.unity = new_center_unity;
817
818         new_base_tilex = grid2tile((gint) pixel2grid((gint)unit2pixel((gint) _center.unitx))
819                                    - (gint)_screen_grids_halfwidth);
820         new_base_tiley = grid2tile(pixel2grid(unit2pixel(_center.unity))
821                                    - _screen_grids_halfheight);
822
823         /* Same zoom level, so it's likely that we can reuse some of the old
824          * buffer's pixels. */
825
826         if (new_base_tilex != _base_tilex || new_base_tiley != _base_tiley) {
827                 /* If copying from old parts to new parts, we need to make sure we
828                  * don't overwrite the old parts when copying, so set up new_x,
829                  * new_y, old_x, old_y, iox, and ioy with that in mind. */
830                 if (new_base_tiley < _base_tiley) {
831                         /* New is lower than old - start at bottom and go up. */
832                         new_y = BUF_HEIGHT_TILES - 1;
833                         ioy = -1;
834                 } else {
835                         /* New is higher than old - start at top and go down. */
836                         new_y = 0;
837                         ioy = 1;
838                 }
839                 if (new_base_tilex < _base_tilex) {
840                         /* New is righter than old - start at right and go left. */
841                         base_new_x = BUF_WIDTH_TILES - 1;
842                         iox = -1;
843                 } else {
844                         /* New is lefter than old - start at left and go right. */
845                         base_new_x = 0;
846                         iox = 1;
847                 }
848
849                 /* Iterate over the y tile values. */
850                 old_y = new_y + new_base_tiley - _base_tiley;
851                 base_old_x = base_new_x + new_base_tilex - _base_tilex;
852                 _base_tilex = new_base_tilex;
853                 _base_tiley = new_base_tiley;
854                 for (j = 0; j < BUF_HEIGHT_TILES;
855                      ++j, new_y += ioy, old_y += ioy) {
856                         new_x = base_new_x;
857                         old_x = base_old_x;
858                         /* Iterate over the x tile values. */
859                         for (k = 0; k < BUF_WIDTH_TILES;
860                              ++k, new_x += iox, old_x += iox) {
861                                 /* Can we get this grid block from the old buffer?. */
862                                 if (old_x >= 0 && old_x < BUF_WIDTH_TILES
863                                     && old_y >= 0 && old_y < BUF_HEIGHT_TILES) {
864                                         /* Copy from old buffer to new buffer. */
865                                         gdk_draw_drawable(_map_pixmap,
866                                                           _gc[COLORABLE_MARK],
867                                                           _map_pixmap,
868                                                           old_x * TILE_SIZE_PIXELS,
869                                                           old_y * TILE_SIZE_PIXELS,
870                                                           new_x * TILE_SIZE_PIXELS,
871                                                           new_y * TILE_SIZE_PIXELS,
872                                                           TILE_SIZE_PIXELS,
873                                                           TILE_SIZE_PIXELS);
874                                 } else {
875 #if 1                   
876                                         map_tile_rdata *mtr;
877
878                                         mtr=g_slice_new(map_tile_rdata);
879                                         mtr->tilex=new_base_tilex + new_x;
880                                         mtr->tiley=new_base_tiley + new_y;
881                                         mtr->destx=new_x * TILE_SIZE_PIXELS;
882                                         mtr->desty=new_y * TILE_SIZE_PIXELS;
883
884                                 gdk_draw_rectangle(_map_pixmap, _gc[COLORABLE_MARK_VELOCITY],
885                        TRUE, mtr->destx, mtr->desty, TILE_SIZE_PIXELS, TILE_SIZE_PIXELS);
886
887                                         g_idle_add_full(G_PRIORITY_HIGH_IDLE,(GSourceFunc)map_render_tile_idle, mtr,NULL);
888 #else
889                                         map_render_tile(new_base_tilex + new_x,
890                                                         new_base_tiley + new_y,
891                                                         new_x * TILE_SIZE_PIXELS,
892                                                         new_y * TILE_SIZE_PIXELS,
893                                                         FALSE);
894 #endif
895                                 }
896                         }
897                 }
898                 MACRO_MAP_RENDER_DATA();
899         }
900
901         MACRO_RECALC_OFFSET();
902         MACRO_RECALC_FOCUS_BASE();
903
904         map_set_mark();
905         MACRO_QUEUE_DRAW_AREA();
906
907         vprintf("%s(): return\n", __PRETTY_FUNCTION__);
908 }
909
910 /**
911  * Pan the view by the given number of units in the X and Y directions.
912  */
913 void map_pan(gint delta_unitx, gint delta_unity)
914 {
915         printf("%s(%d, %d)\n", __PRETTY_FUNCTION__, delta_unitx, delta_unity);
916
917         if (_center_mode > 0)
918                 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM
919                                                (_menu_ac_none_item), TRUE);
920         map_center_unit(_center.unitx + delta_unitx,
921                         _center.unity + delta_unity);
922
923         vprintf("%s(): return\n", __PRETTY_FUNCTION__);
924 }
925
926 /**
927  * Initiate a move of the mark from the old location to the current
928  * location.  This function queues the draw area of the old mark (to force
929  * drawing of the background map), then updates the mark, then queus the
930  * draw area of the new mark.
931  */
932 void map_move_mark()
933 {
934         printf("%s()\n", __PRETTY_FUNCTION__);
935
936         /* Just queue the old and new draw areas. */
937         gtk_widget_queue_draw_area(_map_widget,
938                                    _mark_minx<0 ? 0 : _mark_minx,
939                                    _mark_miny<0 ? 0 : _mark_miny, 
940                                    _mark_width, _mark_height);
941         map_set_mark();
942         gtk_widget_queue_draw_area(_map_widget,
943                                    _mark_minx<0 ? 0 : _mark_minx,
944                                    _mark_miny<0 ? 0 : _mark_miny, 
945                                    _mark_width, _mark_height);
946
947         vprintf("%s(): return\n", __PRETTY_FUNCTION__);
948 }
949
950 gboolean
951 map_update_location_from_gps()
952 {
953 map_update_location(_pos.unitx, _pos.unity);
954 return FALSE;
955 }
956
957 gboolean
958 map_update_location_from_center()
959 {
960 map_update_location(_center.unitx, _center.unity);
961 return FALSE;
962 }
963
964 /**
965  * Make sure the mark is up-to-date.  This function triggers a panning of
966  * the view if the mark is appropriately close to the edge of the view.
967  */
968 void refresh_mark()
969 {
970         printf("%s()\n", __PRETTY_FUNCTION__);
971
972         guint new_center_unitx;
973         guint new_center_unity;
974
975         MACRO_RECALC_CENTER(new_center_unitx, new_center_unity);
976
977         if ((new_center_unitx - _focus.unitx) < _focus_unitwidth
978             && (new_center_unity - _focus.unity) < _focus_unitheight)
979                 /* We're not changing the view - just move the mark. */
980                 map_move_mark();
981         else
982                 map_center_unit(new_center_unitx, new_center_unity);
983
984         /* Draw speed info */
985         if (_speed_limit_on)
986                 speed_limit();
987
988         if (_center_mode>0)
989                 g_idle_add((GSourceFunc)map_update_location_from_gps, NULL);
990
991         vprintf("%s(): return\n", __PRETTY_FUNCTION__);
992 }
993
994 /**
995  * Render a single track line to _map_pixmap.  If either point on the line
996  * is a break (defined as unity == 0), a circle is drawn at the other point.
997  * IT IS AN ERROR FOR BOTH POINTS TO INDICATE A BREAK.
998  */
999 void
1000 map_render_segment(GdkGC * gc_norm, GdkGC * gc_alt,
1001                    guint unitx1, guint unity1, guint unitx2, guint unity2)
1002 {
1003         vprintf("%s()\n", __PRETTY_FUNCTION__);
1004
1005         if (!unity1) {
1006                 guint x2, y2;
1007                 x2 = unit2bufx(unitx2);
1008                 y2 = unit2bufy(unity2);
1009                 /* Make sure this circle will be visible. */
1010                 if ((x2 < BUF_WIDTH_PIXELS)
1011                     && (y2 < BUF_HEIGHT_PIXELS))
1012                         gdk_draw_arc(_map_pixmap, gc_alt, FALSE,        /* FALSE: not filled. */
1013                                      x2 - _draw_width, y2 - _draw_width, 2 * _draw_width, 2 * _draw_width, 0,   /* start at 0 degrees. */
1014                                      360 * 64);
1015         } else if (!unity2) {
1016                 guint x1, y1;
1017                 x1 = unit2bufx(unitx1);
1018                 y1 = unit2bufy(unity1);
1019                 /* Make sure this circle will be visible. */
1020                 if ((x1 < BUF_WIDTH_PIXELS)
1021                     && ((unsigned)y1 < BUF_HEIGHT_PIXELS))
1022                         gdk_draw_arc(_map_pixmap, gc_alt, FALSE,        /* FALSE: not filled. */
1023                                      x1 - _draw_width, y1 - _draw_width, 2 * _draw_width, 2 * _draw_width, 0,   /* start at 0 degrees. */
1024                                      360 * 64);
1025         } else {
1026                 gint x1, y1, x2, y2;
1027                 x1 = unit2bufx(unitx1);
1028                 y1 = unit2bufy(unity1);
1029                 x2 = unit2bufx(unitx2);
1030                 y2 = unit2bufy(unity2);
1031                 /* Make sure this line could possibly be visible. */
1032                 if (!((x1 > BUF_WIDTH_PIXELS && x2 > BUF_WIDTH_PIXELS)
1033                       || (x1 < 0 && x2 < 0)
1034                       || (y1 > BUF_HEIGHT_PIXELS && y2 > BUF_HEIGHT_PIXELS)
1035                       || (y1 < 0 && y2 < 0)))
1036                         gdk_draw_line(_map_pixmap, gc_norm, x1, y1, x2, y2);
1037         }
1038
1039         vprintf("%s(): return\n", __PRETTY_FUNCTION__);
1040 }
1041
1042 /**
1043  * Render all track data onto the _map_pixmap.  Note that this does not
1044  * clear the pixmap of previous track data (use map_force_redraw() for
1045  * that), and also note that this method does not queue any redraws, so it
1046  * is up to the caller to decide which part of the track really needs to be
1047  * redrawn.
1048  */
1049 void map_render_path(Path * path, GdkGC ** gc)
1050 {
1051         Point *curr;
1052         WayPoint *wcurr;
1053         printf("%s()\n", __PRETTY_FUNCTION__);
1054
1055         /* gc is a pointer to the first GC to use (for plain points).  (gc + 1)
1056          * is a pointer to the GC to use for waypoints, and (gc + 2) is a pointer
1057          * to the GC to use for breaks. */
1058
1059         /* else there is a route to draw. */
1060         for (curr = path->head, wcurr = path->whead; curr++ != path->tail;) {
1061                 /* Draw the line from (curr - 1) to (curr). */
1062                 map_render_segment(gc[0], gc[2],
1063                                    curr[-1].unitx, curr[-1].unity, curr->unitx,
1064                                    curr->unity);
1065
1066                 /* Now, check if curr is a waypoint. */
1067                 if (wcurr && wcurr <= path->wtail && wcurr->point == curr) {
1068                         guint x1 = unit2bufx(wcurr->point->unitx);
1069                         guint y1 = unit2bufy(wcurr->point->unity);
1070                         if ((x1 < BUF_WIDTH_PIXELS)
1071                             && (y1 < BUF_HEIGHT_PIXELS)) {
1072                                 gdk_draw_arc(_map_pixmap, gc[1], FALSE, /* FALSE: not filled. */
1073                                              x1 - _draw_width, y1 - _draw_width, 2 * _draw_width, 2 * _draw_width, 0,   /* start at 0 degrees. */
1074                                              360 * 64);
1075                         }
1076                         wcurr++;
1077                 }
1078         }
1079
1080         vprintf("%s(): return\n", __PRETTY_FUNCTION__);
1081 }
1082
1083 void map_render_paths()
1084 {
1085         printf("%s()\n", __PRETTY_FUNCTION__);
1086
1087         if ((_show_tracks & ROUTES_MASK) && _route.head != _route.tail) {
1088                 map_render_path(&_route, _gc + COLORABLE_ROUTE);
1089
1090                 /* Now, draw the next waypoint on top of all other waypoints. */
1091                 if (_next_way) {
1092                         guint x1 = unit2bufx(_next_way->point->unitx);
1093                         guint y1 = unit2bufy(_next_way->point->unity);
1094                         if ((x1 < BUF_WIDTH_PIXELS)
1095                             && (y1 < BUF_HEIGHT_PIXELS)) {
1096                                 /* Draw the next waypoint as a break. */
1097                                 gdk_draw_arc(_map_pixmap, _gc[COLORABLE_ROUTE_BREAK], FALSE,    /* FALSE: not filled. */
1098                                              x1 - _draw_width, y1 - _draw_width, 2 * _draw_width, 2 * _draw_width, 0,   /* start at 0 degrees. */
1099                                              360 * 64);
1100                         }
1101                 }
1102         }
1103         if (_show_tracks & TRACKS_MASK)
1104                 map_render_path(&_track, _gc + COLORABLE_TRACK);
1105
1106         vprintf("%s(): return\n", __PRETTY_FUNCTION__);
1107 }
1108
1109 static gboolean map_follow_move(GtkWidget * widget, GdkEventMotion * event)
1110 {
1111         gint xx, yy;
1112         GdkModifierType state;
1113         guint unitx, unity;
1114         guint cx, cy;
1115
1116         if (event->is_hint) {
1117                 gdk_window_get_pointer(event->window, &xx, &yy, &state);
1118                 state = event->state;
1119         } else {
1120                 xx = event->x;
1121                 yy = event->y;
1122                 state = event->state;
1123         }
1124
1125         unitx = x2unit((gint) (xx - before[0]));
1126         unity = y2unit((gint) (yy - before[1]));
1127         cx = unit2x(_center.unitx);
1128         cy = unit2y(_center.unity);
1129
1130         map_center_unit(x2unit((gint) (cx - (xx - before[0]))),
1131                         y2unit((gint) (cy - (yy - before[1]))));
1132
1133         before[0] = xx;
1134         before[1] = yy;
1135         return FALSE;
1136 }
1137
1138 gboolean map_key_zoom_timeout()
1139 {
1140         printf("%s()\n", __PRETTY_FUNCTION__);
1141         if (_key_zoom_new < _zoom) {
1142                 /* We're currently zooming in (_zoom is decreasing). */
1143                 guint test = _key_zoom_new - _curr_repo->view_zoom_steps;
1144                 if (test < MAX_ZOOM)
1145                         /* We can zoom some more.  Hurray! */
1146                         _key_zoom_new = test;
1147                 else
1148                         /* We can't zoom anymore.  Booooo! */
1149                         return FALSE;
1150         } else {
1151                 /* We're currently zooming out (_zoom is increasing). */
1152                 guint test = _key_zoom_new + _curr_repo->view_zoom_steps;
1153                 if (test < MAX_ZOOM)
1154                         /* We can zoom some more.  Hurray! */
1155                         _key_zoom_new = test;
1156                 else
1157                         /* We can't zoom anymore.  Booooo! */
1158                         return FALSE;
1159         }
1160
1161         /* We can zoom more - tell them how much they're zooming. */
1162         {
1163                 gchar buffer[32];
1164                 snprintf(buffer, sizeof(buffer),
1165                          "%s %d", _("Zoom to Level"), _key_zoom_new);
1166                 MACRO_BANNER_SHOW_INFO(_window, buffer);
1167         }
1168         vprintf("%s(): return\n", __PRETTY_FUNCTION__);
1169         return TRUE;
1170 }
1171
1172 void map_scale_draw(GdkEventExpose *event)
1173 {
1174         gdk_rectangle_intersect(&event->area, &_scale_rect, &event->area);
1175
1176         if (event->area.width && event->area.height) {
1177                 gdk_draw_rectangle(_map_widget->window,
1178                                    _map_widget->style->
1179                                    bg_gc[GTK_WIDGET_STATE(_map_widget)],
1180                                    TRUE, _scale_rect.x, _scale_rect.y,
1181                                    _scale_rect.width,
1182                                    _scale_rect.height);
1183                 gdk_draw_rectangle(_map_widget->window,
1184                                    _map_widget->style->
1185                                    fg_gc[GTK_WIDGET_STATE(_map_widget)],
1186                                    FALSE, _scale_rect.x, _scale_rect.y,
1187                                    _scale_rect.width,
1188                                    _scale_rect.height);
1189
1190                 /* Now calculate and draw the distance. */
1191                 {
1192                         gchar buffer[16];
1193                         gfloat distance;
1194                         gfloat lat1, lon1, lat2, lon2;
1195                         gint width;
1196
1197                         unit2latlon(_center.unitx - pixel2unit(SCALE_WIDTH / 2 - 4),
1198                                     _center.unity, lat1, lon1);
1199                         unit2latlon(_center.unitx + pixel2unit(SCALE_WIDTH / 2 - 4),
1200                                     _center.unity, lat2, lon2);
1201                         distance = calculate_distance(lat1, lon1, lat2, lon2) * UNITS_CONVERT[_units];
1202
1203                         if (distance < 1.f)
1204                                 snprintf(buffer, sizeof(buffer),
1205                                          "%0.2f %s", distance,
1206                                          UNITS_TEXT[_units]);
1207                         else if (distance < 10.f)
1208                                 snprintf(buffer, sizeof(buffer),
1209                                          "%0.1f %s", distance,
1210                                          UNITS_TEXT[_units]);
1211                         else
1212                                 snprintf(buffer, sizeof(buffer),
1213                                          "%0.f %s", distance,
1214                                          UNITS_TEXT[_units]);
1215
1216                         pango_layout_set_text(_scale_layout, buffer, -1);
1217                         pango_layout_get_pixel_size(_scale_layout, &width, NULL);
1218
1219                         /* Draw the layout itself. */
1220                         gdk_draw_layout(_map_widget->window,
1221                                         _map_widget->style->
1222                                         fg_gc[GTK_WIDGET_STATE
1223                                               (_map_widget)],
1224                                         _scale_rect.x +
1225                                         (_scale_rect.width - width) / 2,
1226                                         _scale_rect.y, _scale_layout);
1227
1228                         /* Draw little hashes on the ends. */
1229                         gdk_draw_line(_map_widget->window,
1230                                       _map_widget->style->
1231                                       fg_gc[GTK_WIDGET_STATE
1232                                             (_map_widget)],
1233                                       _scale_rect.x + 4,
1234                                       _scale_rect.y +
1235                                       _scale_rect.height / 2 - 4,
1236                                       _scale_rect.x + 4,
1237                                       _scale_rect.y +
1238                                       _scale_rect.height / 2 + 4);
1239                         gdk_draw_line(_map_widget->window,
1240                                       _map_widget->style->
1241                                       fg_gc[GTK_WIDGET_STATE
1242                                             (_map_widget)],
1243                                       _scale_rect.x + 4,
1244                                       _scale_rect.y +
1245                                       _scale_rect.height / 2,
1246                                       _scale_rect.x +
1247                                       (_scale_rect.width - width) / 2 -
1248                                       4,
1249                                       _scale_rect.y +
1250                                       _scale_rect.height / 2);
1251                         gdk_draw_line(_map_widget->window,
1252                                       _map_widget->style->
1253                                       fg_gc[GTK_WIDGET_STATE
1254                                             (_map_widget)],
1255                                       _scale_rect.x +
1256                                       _scale_rect.width - 4,
1257                                       _scale_rect.y +
1258                                       _scale_rect.height / 2 - 4,
1259                                       _scale_rect.x +
1260                                       _scale_rect.width - 4,
1261                                       _scale_rect.y +
1262                                       _scale_rect.height / 2 + 4);
1263                         gdk_draw_line(_map_widget->window,
1264                                       _map_widget->style->
1265                                       fg_gc[GTK_WIDGET_STATE
1266                                             (_map_widget)],
1267                                       _scale_rect.x +
1268                                       _scale_rect.width - 4,
1269                                       _scale_rect.y +
1270                                       _scale_rect.height / 2,
1271                                       _scale_rect.x +
1272                                       (_scale_rect.width + width) / 2 +
1273                                       4,
1274                                       _scale_rect.y +
1275                                       _scale_rect.height / 2);
1276                 }
1277         }
1278 }
1279
1280 gboolean map_cb_expose(GtkWidget * widget, GdkEventExpose * event)
1281 {
1282         printf("%s(%d, %d, %d, %d)\n", __PRETTY_FUNCTION__,
1283                event->area.x, event->area.y,
1284                event->area.width, event->area.height);
1285
1286         gdk_draw_drawable(GDK_DRAWABLE(_map_widget->window),
1287                           _gc[COLORABLE_MARK],
1288                           _map_pixmap,
1289                           event->area.x + _offsetx, event->area.y + _offsety,
1290                           event->area.x, event->area.y,
1291                           event->area.width, event->area.height);
1292         map_draw_mark();
1293
1294         /* Draw scale, if necessary. */
1295         if (_show_scale)
1296                 map_scale_draw(event);
1297
1298         vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__);
1299         return TRUE;
1300 }
1301
1302 int map_zoom(gint zdir)
1303 {
1304         gchar buffer[80];
1305         gint nzoom;
1306
1307         nzoom = _zoom + zdir;
1308
1309         if ((nzoom > 0) && (nzoom < MAX_ZOOM - 1)) {
1310                 snprintf(buffer, sizeof(buffer), "%s %d",
1311                          _("Zoom to Level"), nzoom);
1312                 MACRO_BANNER_SHOW_INFO(_window, buffer);
1313                 map_set_zoom(nzoom);
1314         }
1315
1316         return nzoom;
1317 }
1318
1319 static gboolean
1320 map_autozoomer()
1321 {
1322 static gfloat z=5.0;
1323 static gint last=5;
1324 gint iz;
1325
1326 if (_zoom_timeout_sid==0)
1327         return FALSE;
1328
1329 z=(z+_gps.speed+1)/3;
1330 if (z>5) z=5.0; else if (z<1) z=1.0;
1331 iz=(gint)roundf(z);
1332 g_printf("Setting autozoom to: %f %d S:%f\n", z, iz, _gps.speed);
1333 if (iz>last) 
1334         iz=last+1; 
1335 else if (iz<last) 
1336         iz=last-1;
1337 last=iz;
1338 map_set_zoom(iz);
1339
1340 return TRUE;
1341 }
1342
1343 void
1344 map_set_autozoom(gboolean az)
1345 {
1346 if (az==TRUE) {
1347         _zoom_timeout_sid=g_timeout_add(_gps.speed<5 ? 2000 : 5000, (GSourceFunc) map_autozoomer, NULL);
1348         return;
1349 } else {
1350         if (_zoom_timeout_sid) {
1351                 g_source_remove(_zoom_timeout_sid);
1352                 _zoom_timeout_sid=0;
1353         }
1354         return;
1355 }
1356
1357 }
1358
1359 static void 
1360 map_draw_route(gint x, gint y)
1361 {
1362 cmenu_route_add_way(x, y);
1363 }
1364
1365 static void 
1366 map_draw_track(gint x, gint y)
1367 {
1368 _pos.unitx = x2unit((gint) (x + 0.5));
1369 _pos.unity = y2unit((gint) (y + 0.5));
1370 unit2latlon(_pos.unitx, _pos.unity, _gps.lat, _gps.lon);
1371 _gps.speed = 20.f;
1372 integerize_data();
1373 track_add(time(NULL), FALSE);
1374 refresh_mark();
1375 }
1376
1377 static void
1378 map_set_place_information(osm_way *s, osm_place *mp, osm_place *sp)
1379 {
1380 gchar buffer[256];
1381
1382 if (!s && !mp && !sp) {
1383         snprintf(buffer, sizeof(buffer), "Unknown location");
1384 } else {
1385         /* oh, fun */
1386         snprintf(buffer, sizeof(buffer), "On %s%s%s%s%s%s%s%s%s", 
1387         s ? s->name ? s->name : _("unknown") : "?",
1388         s ? s->ref ? ", " : "" : "",
1389         s ? s->ref ? s->ref : "" : "",
1390         s ? s->int_ref ? ", " : "" : "",
1391         s ? s->int_ref ? s->int_ref : "" : "",
1392         (sp && sp->name) ? " in " : "",
1393         (sp && sp->name) ? sp->name : "",
1394         (mp && mp->name) ? " in " : "",
1395         (mp && mp->name) ? mp->name : "");
1396 }
1397 gtk_label_set_label(GTK_LABEL(info_banner.location), buffer);
1398 }
1399
1400 /* XXX: Move this to osm-db.c */
1401 static void 
1402 map_update_location(gint x, gint y)
1403 {
1404 gint ilat, ilon;
1405 gdouble lat,lon, dist;
1406 gboolean fs, check_place=FALSE;
1407 static gboolean inp=FALSE;
1408
1409 /* We run the gtk mainloop in progress callback so we can be called again, we don't like that */
1410 if (inp==TRUE)
1411         return;
1412 inp=TRUE;
1413
1414 unit2latlon(x, y, lat, lon);
1415 ilat=lat2mp_int(lat);
1416 ilon=lon2mp_int(lon);
1417
1418 osm_progress_set_widget(_db, _progress_item);
1419
1420 /* Check if we are still near the same way as last time */
1421 if (map_loc.street && osm_way_distance(ilat, ilon, map_loc.street->node_f, map_loc.street->node_t, &dist)==TRUE) {
1422         if (dist>15000.0) {
1423                 osm_way_free(map_loc.street);
1424                 map_loc.street=osm_find_nearest_way(ilat, ilon);
1425                 check_place=FALSE;
1426         } else {
1427                 check_place=FALSE;
1428         }
1429 } else {
1430         osm_way_free(map_loc.street);
1431         map_loc.street=osm_find_nearest_way(ilat, ilon);
1432         check_place=TRUE;
1433 }
1434
1435 _map_location_known=map_loc.street ? TRUE : FALSE;
1436 _map_location_dist=map_loc.street ? map_loc.street->dist : 900000.0;
1437
1438 if (check_place==TRUE) {
1439
1440 fs=osm_find_nearest_place(NODE_PLACE_SUBURB, ilat, ilon, &map_loc.secondary);
1441 if (fs==TRUE && map_loc.secondary && map_loc.secondary->isin!=0) {
1442         if (osm_place_get(map_loc.secondary->isin, ilat, ilon, &map_loc.primary)==FALSE) {
1443                 if (osm_find_nearest_place(NODE_PLACE_CITY, ilat, ilon, &map_loc.primary)==TRUE)
1444                         g_printf("Near city: %s\n", map_loc.primary->name);
1445                 else if (osm_find_nearest_place(NODE_PLACE_TOWN, ilat, ilon, &map_loc.primary)==TRUE)
1446                         g_printf("Near town: %s\n", map_loc.primary->name);
1447                 else
1448                         g_printf("Unknown\n");
1449         } else {
1450                 g_printf("In: %s\n", map_loc.primary ? map_loc.primary->name : "?");
1451         }
1452 } else if (map_loc.street && map_loc.street->isin!=0) {
1453         if (osm_place_get(map_loc.street->isin, ilat, ilon, &map_loc.primary)==FALSE) {
1454                 g_printf("Street location not know.\n");
1455         } else {
1456                 g_printf("Street is in: %s\n", map_loc.primary ? map_loc.primary->name : "?");
1457         }
1458 } else {
1459         if (osm_find_nearest_place(NODE_PLACE_CITY, ilat, ilon, &map_loc.primary)==TRUE)
1460                 g_printf("Near city: %s\n", map_loc.primary->name);
1461         else if (osm_find_nearest_place(NODE_PLACE_TOWN, ilat, ilon, &map_loc.primary)==TRUE)
1462                 g_printf("Near town: %s\n", map_loc.primary->name);
1463         else
1464                 g_printf("Unknown\n");
1465
1466 }
1467 }
1468
1469 map_set_place_information(map_loc.street, map_loc.primary, map_loc.secondary);
1470 osm_progress_set_widget(_db, NULL);
1471 inp=FALSE;
1472 }
1473
1474 gboolean map_cb_scroll_event(GtkWidget * widget, GdkEventScroll * event)
1475 {
1476
1477         if (event->direction == GDK_SCROLL_UP) {
1478                 map_center_unit(x2unit((gint) (event->x + 0.5)),
1479                                 y2unit((gint) (event->y + 0.5)));
1480                 map_set_zoom(_zoom - 1);
1481         } else if (event->direction == GDK_SCROLL_DOWN) {
1482                 map_center_unit(x2unit((gint) (event->x + 0.5)),
1483                                 y2unit((gint) (event->y + 0.5)));
1484                 map_set_zoom(_zoom + 1);
1485         }
1486
1487         return FALSE;
1488 }
1489
1490 gboolean map_cb_button_press(GtkWidget * widget, GdkEventButton * event)
1491 {
1492         printf("%s()\n", __PRETTY_FUNCTION__);
1493
1494         _cmenu_position_x = event->x + 0.5;
1495         _cmenu_position_y = event->y + 0.5;
1496
1497         switch (event->button) {
1498         case 1:
1499                 if (event->type == GDK_2BUTTON_PRESS) {
1500                         map_set_zoom(_zoom - 1);
1501                         return FALSE;
1502                 }
1503                 if (event->type == GDK_3BUTTON_PRESS)
1504                         return FALSE;
1505                 break;
1506         case 2:
1507                 press[0] = event->x;
1508                 press[1] = event->y;
1509                 before[0] = press[0];
1510                 before[1] = press[1];
1511
1512                 _id = g_signal_connect(G_OBJECT(_map_widget),
1513                                      "motion_notify_event",
1514                                      G_CALLBACK(map_follow_move), NULL);
1515                 break;
1516         case 3:
1517 #ifndef WITH_HILDON
1518                 gtk_menu_popup(GTK_MENU(_menu_map), NULL, NULL, NULL, NULL,
1519                                event->button, gtk_get_current_event_time());
1520 #endif
1521                 break;
1522         }
1523
1524         /* Return FALSE to allow context menu to work. */
1525         vprintf("%s(): return FALSE\n", __PRETTY_FUNCTION__);
1526         return FALSE;
1527 }
1528
1529 gboolean map_cb_button_release(GtkWidget * widget, GdkEventButton * event)
1530 {
1531         printf("%s()\n", __PRETTY_FUNCTION__);
1532
1533         switch (event->button) {
1534         case 1:
1535                 if (_center_mode > 0)
1536                         gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM
1537                                                        (_menu_ac_none_item),
1538                                                        TRUE);
1539
1540                 switch (_map_mode) {
1541                 case MAP_MODE_DRAW_TRACK:
1542                         map_draw_track(event->x, event->y);
1543                 break;
1544                 case MAP_MODE_DRAW_ROUTE:
1545                         map_draw_route(event->x, event->y);
1546                 break;
1547                 case MAP_MODE_SET_ROUTE_FROM:
1548                 case MAP_MODE_SET_ROUTE_POINT:
1549                 case MAP_MODE_SET_ROUTE_TO:
1550                 break;
1551                 }
1552
1553 #if 0
1554                 ux = x2unit((gint) (event->x + 0.5));
1555                 uy = y2unit((gint) (event->y + 0.5));
1556                 map_update_location(ux, uy);
1557 #endif
1558
1559                 g_idle_add((GSourceFunc)map_update_location_from_center, NULL);
1560
1561                 map_center_unit(x2unit((gint) (event->x + 0.5)),
1562                                 y2unit((gint) (event->y + 0.5)));
1563
1564                 break;
1565         case 2:
1566                 release[0] = event->x;
1567                 release[1] = event->y;
1568
1569                 g_signal_handler_disconnect(G_OBJECT(_map_widget), _id);
1570
1571                 press[0] = 0;
1572                 press[1] = 0;
1573                 release[0] = 0;
1574                 release[1] = 0;
1575                 before[0] = 0;
1576                 before[1] = 0;
1577                 break;
1578         case 3:
1579                 break;
1580         }
1581
1582         /* Return FALSE to avoid context menu (if it hasn't popped up already). */
1583         vprintf("%s(): return FALSE\n", __PRETTY_FUNCTION__);
1584         return FALSE;
1585 }