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