]> git.street.me.uk Git - andy/viking.git/blob - src/vikmapslayer.c
[QA] Improve create zoom menu to use G_N_ELEMENTS to know array bounds.
[andy/viking.git] / src / vikmapslayer.c
1 /*
2  * viking -- GPS Data and Topo Analyzer, Explorer, and Manager
3  *
4  * Copyright (C) 2005, Evan Battaglia <viking@greentorch.org>
5  * Copyright (C) 2010, Guilhem Bonnefille <guilhem.bonnefille@gmail.com>
6  * UTM multi-zone stuff by Kit Transue <notlostyet@didactek.com>
7  * Dynamic map type by Guilhem Bonnefille <guilhem.bonnefille@gmail.com>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
22  *
23  */
24
25 #ifdef HAVE_CONFIG_H
26 #include "config.h"
27 #endif
28
29 // TODO: Make these configurable
30 #define MAX_TILES 1000
31
32 #define MAX_SHRINKFACTOR 8.0000001 /* zoom 1 viewing 8-tiles */
33 #define MIN_SHRINKFACTOR 0.0312499 /* zoom 32 viewing 1-tiles */
34
35 #define REAL_MIN_SHRINKFACTOR 0.0039062499 /* if shrinkfactor is between MAX and REAL_MAX, will only check for existence */
36
37 #include <gtk/gtk.h>
38 #include <gdk-pixbuf/gdk-pixdata.h>
39 #include <glib.h>
40 #include <glib/gstdio.h>
41 #include <glib/gi18n.h>
42
43 #ifdef HAVE_STRING_H
44 #include <string.h>
45 #endif
46 #ifdef HAVE_MATH_H
47 #include <math.h>
48 #endif
49
50 #ifdef HAVE_UNISTD_H
51 #include <unistd.h>
52 #endif
53
54 #include "viking.h"
55 #include "vikmapsourcedefault.h"
56 #include "maputils.h"
57 #include "mapcache.h"
58 #include "background.h"
59 #include "preferences.h"
60 #include "vikmapslayer.h"
61 #include "icons/icons.h"
62
63 /****** MAP TYPES ******/
64
65 static GList *__map_types = NULL;
66
67 #define NUM_MAP_TYPES g_list_length(__map_types)
68
69 /* List of label for each map type */
70 static gchar **params_maptypes = NULL;
71
72 /* Corresponding IDS. (Cf. field uniq_id in VikMapsLayer struct) */
73 static guint *params_maptypes_ids = NULL;
74
75 /******** MAPZOOMS *********/
76
77 static gchar *params_mapzooms[] = { N_("Use Viking Zoom Level"), "0.25", "0.5", "1", "2", "4", "8", "16", "32", "64", "128", "256", "512", "1024", "USGS 10k", "USGS 24k", "USGS 25k", "USGS 50k", "USGS 100k", "USGS 200k", "USGS 250k", NULL };
78 static gdouble __mapzooms_x[] = { 0.0, 0.25, 0.5, 1.0, 2.0, 4.0, 8.0, 16.0, 32.0, 64.0, 128.0, 256.0, 512.0, 1024.0, 1.016, 2.4384, 2.54, 5.08, 10.16, 20.32, 25.4 };
79 static gdouble __mapzooms_y[] = { 0.0, 0.25, 0.5, 1.0, 2.0, 4.0, 8.0, 16.0, 32.0, 64.0, 128.0, 256.0, 512.0, 1024.0, 1.016, 2.4384, 2.54, 5.08, 10.16, 20.32, 25.4 };
80
81 #define NUM_MAPZOOMS (sizeof(params_mapzooms)/sizeof(params_mapzooms[0]) - 1)
82
83 /**************************/
84
85
86 static void maps_layer_post_read (VikLayer *vl, VikViewport *vp, gboolean from_file);
87 static const gchar* maps_layer_tooltip ( VikMapsLayer *vml );
88 static void maps_layer_marshall( VikMapsLayer *vml, guint8 **data, gint *len );
89 static VikMapsLayer *maps_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp );
90 static gboolean maps_layer_set_param ( VikMapsLayer *vml, guint16 id, VikLayerParamData data, VikViewport *vvp, gboolean is_file_operation );
91 static VikLayerParamData maps_layer_get_param ( VikMapsLayer *vml, guint16 id, gboolean is_file_operation );
92 static void maps_layer_draw ( VikMapsLayer *vml, VikViewport *vvp );
93 static VikMapsLayer *maps_layer_new ( VikViewport *vvp );
94 static void maps_layer_free ( VikMapsLayer *vml );
95 static gboolean maps_layer_download_release ( VikMapsLayer *vml, GdkEventButton *event, VikViewport *vvp );
96 static gboolean maps_layer_download_click ( VikMapsLayer *vml, GdkEventButton *event, VikViewport *vvp );
97 static gpointer maps_layer_download_create ( VikWindow *vw, VikViewport *vvp );
98 static void maps_layer_set_cache_dir ( VikMapsLayer *vml, const gchar *dir );
99 static void start_download_thread ( VikMapsLayer *vml, VikViewport *vvp, const VikCoord *ul, const VikCoord *br, gint redownload );
100 static void maps_layer_add_menu_items ( VikMapsLayer *vml, GtkMenu *menu, VikLayersPanel *vlp );
101 static guint map_uniq_id_to_index ( guint uniq_id );
102
103
104 static VikLayerParamScale params_scales[] = {
105   /* min, max, step, digits (decimal places) */
106  { 0, 255, 3, 0 }, /* alpha */
107 };
108
109 static VikLayerParamData mode_default ( void ) { return VIK_LPD_UINT ( 19 ); } // OSM MapQuest maps
110 static VikLayerParamData directory_default ( void )
111 {
112   VikLayerParamData data;
113   data.s = g_strdup ( a_preferences_get(VIKING_PREFERENCES_NAMESPACE "maplayer_default_dir")->s );
114   return data;
115 }
116 static VikLayerParamData alpha_default ( void ) { return VIK_LPD_UINT ( 255 ); }
117 static VikLayerParamData mapzoom_default ( void ) { return VIK_LPD_UINT ( 0 ); }
118
119 VikLayerParam maps_layer_params[] = {
120   { VIK_LAYER_MAPS, "mode", VIK_LAYER_PARAM_UINT, VIK_LAYER_GROUP_NONE, N_("Map Type:"), VIK_LAYER_WIDGET_COMBOBOX, NULL, NULL, NULL, mode_default },
121   { VIK_LAYER_MAPS, "directory", VIK_LAYER_PARAM_STRING, VIK_LAYER_GROUP_NONE, N_("Maps Directory:"), VIK_LAYER_WIDGET_FOLDERENTRY, NULL, NULL, NULL, directory_default },
122   { VIK_LAYER_MAPS, "alpha", VIK_LAYER_PARAM_UINT, VIK_LAYER_GROUP_NONE, N_("Alpha:"), VIK_LAYER_WIDGET_HSCALE, params_scales, NULL,
123     N_("Control the Alpha value for transparency effects"), alpha_default },
124   { VIK_LAYER_MAPS, "autodownload", VIK_LAYER_PARAM_BOOLEAN, VIK_LAYER_GROUP_NONE, N_("Autodownload maps:"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL, NULL, vik_lpd_true_default },
125   { VIK_LAYER_MAPS, "adlonlymissing", VIK_LAYER_PARAM_BOOLEAN, VIK_LAYER_GROUP_NONE, N_("Autodownload Only Gets Missing Maps:"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL,
126     N_("Using this option avoids attempting to update already acquired tiles. This can be useful if you want to restrict the network usage, without having to resort to manual control. Only applies when 'Autodownload Maps' is on."), vik_lpd_false_default },
127   { VIK_LAYER_MAPS, "mapzoom", VIK_LAYER_PARAM_UINT, VIK_LAYER_GROUP_NONE, N_("Zoom Level:"), VIK_LAYER_WIDGET_COMBOBOX, params_mapzooms, NULL,
128     N_("Determines the method of displaying map tiles for the current zoom level. 'Viking Zoom Level' uses the best matching level, otherwise setting a fixed value will always use map tiles of the specified value regardless of the actual zoom level."),
129     mapzoom_default },
130 };
131
132 enum {
133   PARAM_MAPTYPE=0,
134   PARAM_CACHE_DIR,
135   PARAM_ALPHA,
136   PARAM_AUTODOWNLOAD,
137   PARAM_ONLYMISSING,
138   PARAM_MAPZOOM,
139   NUM_PARAMS
140 };
141
142 static VikToolInterface maps_tools[] = {
143   { { "MapsDownload", "vik-icon-Maps Download", N_("_Maps Download"), NULL, N_("Maps Download"), 0 },
144     (VikToolConstructorFunc) maps_layer_download_create,
145     NULL,
146     NULL,
147     NULL,
148     (VikToolMouseFunc) maps_layer_download_click,
149     NULL,
150     (VikToolMouseFunc) maps_layer_download_release,
151     NULL,
152     FALSE,
153     GDK_CURSOR_IS_PIXMAP, &cursor_mapdl_pixbuf },
154 };
155
156 VikLayerInterface vik_maps_layer_interface = {
157   "Map",
158   N_("Map"),
159   "<control><shift>M",
160   &vikmapslayer_pixbuf,
161
162   maps_tools,
163   sizeof(maps_tools) / sizeof(maps_tools[0]),
164
165   maps_layer_params,
166   NUM_PARAMS,
167   NULL,
168   0,
169
170   VIK_MENU_ITEM_ALL,
171
172   (VikLayerFuncCreate)                  maps_layer_new,
173   (VikLayerFuncRealize)                 NULL,
174   (VikLayerFuncPostRead)                maps_layer_post_read,
175   (VikLayerFuncFree)                    maps_layer_free,
176
177   (VikLayerFuncProperties)              NULL,
178   (VikLayerFuncDraw)                    maps_layer_draw,
179   (VikLayerFuncChangeCoordMode)         NULL,
180
181   (VikLayerFuncSetMenuItemsSelection)   NULL,
182   (VikLayerFuncGetMenuItemsSelection)   NULL,
183
184   (VikLayerFuncAddMenuItems)            maps_layer_add_menu_items,
185   (VikLayerFuncSublayerAddMenuItems)    NULL,
186
187   (VikLayerFuncSublayerRenameRequest)   NULL,
188   (VikLayerFuncSublayerToggleVisible)   NULL,
189   (VikLayerFuncSublayerTooltip)         NULL,
190   (VikLayerFuncLayerTooltip)            maps_layer_tooltip,
191   (VikLayerFuncLayerSelected)           NULL,
192
193   (VikLayerFuncMarshall)                maps_layer_marshall,
194   (VikLayerFuncUnmarshall)              maps_layer_unmarshall,
195
196   (VikLayerFuncSetParam)                maps_layer_set_param,
197   (VikLayerFuncGetParam)                maps_layer_get_param,
198
199   (VikLayerFuncReadFileData)            NULL,
200   (VikLayerFuncWriteFileData)           NULL,
201
202   (VikLayerFuncDeleteItem)              NULL,
203   (VikLayerFuncCutItem)                 NULL,
204   (VikLayerFuncCopyItem)                NULL,
205   (VikLayerFuncPasteItem)               NULL,
206   (VikLayerFuncFreeCopiedItem)          NULL,
207   (VikLayerFuncDragDropRequest)         NULL,
208
209   (VikLayerFuncSelectClick)             NULL,
210   (VikLayerFuncSelectMove)              NULL,
211   (VikLayerFuncSelectRelease)           NULL,
212   (VikLayerFuncSelectedViewportMenu)    NULL,
213 };
214
215 struct _VikMapsLayer {
216   VikLayer vl;
217   guint maptype;
218   gchar *cache_dir;
219   guint8 alpha;
220   guint mapzoom_id;
221   gdouble xmapzoom, ymapzoom;
222
223   gboolean autodownload;
224   gboolean adl_only_missing;
225   VikCoord *last_center;
226   gdouble last_xmpp;
227   gdouble last_ympp;
228
229   gint dl_tool_x, dl_tool_y;
230
231   GtkMenu *dl_right_click_menu;
232   VikCoord redownload_ul, redownload_br; /* right click menu only */
233   VikViewport *redownload_vvp;
234
235   gboolean license_notice_shown; // FALSE for new maps only, otherwise
236                                  // TRUE for saved maps & other layer changes as we don't need to show it again
237 };
238
239 enum { REDOWNLOAD_NONE = 0,    /* download only missing maps */
240        REDOWNLOAD_BAD,         /* download missing and bad maps */
241        REDOWNLOAD_NEW,         /* download missing maps that are newer on server only */
242        REDOWNLOAD_ALL,         /* download all maps */
243        DOWNLOAD_OR_REFRESH };  /* download missing maps and refresh cache */
244
245 static VikLayerParam prefs[] = {
246   { VIK_LAYER_NUM_TYPES, VIKING_PREFERENCES_NAMESPACE "maplayer_default_dir", VIK_LAYER_PARAM_STRING, VIK_LAYER_GROUP_NONE, N_("Default map layer directory:"), VIK_LAYER_WIDGET_FOLDERENTRY, NULL, NULL, N_("Choose a directory to store cached Map tiles for this layer") },
247 };
248
249 void maps_layer_init ()
250 {
251   VikLayerParamData tmp;
252   tmp.s = maps_layer_default_dir();
253   a_preferences_register(prefs, tmp, VIKING_PREFERENCES_GROUP_KEY);
254 }
255
256 /****************************************/
257 /******** MAPS LAYER TYPES **************/
258 /****************************************/
259
260 int _get_index_for_id ( guint id )
261 {
262   int index = 0 ;
263   while (params_maptypes_ids[index] != 0)
264   {
265     if (params_maptypes_ids[index] == id)
266       return index;
267     index++;
268   }
269   return -1;
270 }
271
272 void _add_map_source ( guint id, const char *label, VikMapSource *map )
273 {
274   gsize len = 0;
275   if (params_maptypes)
276     len = g_strv_length (params_maptypes);
277   /* Add the label */
278   params_maptypes = g_realloc (params_maptypes, (len+2)*sizeof(gchar*));
279   params_maptypes[len] = g_strdup (label);
280   params_maptypes[len+1] = NULL;
281
282   /* Add the id */
283   params_maptypes_ids = g_realloc (params_maptypes_ids, (len+2)*sizeof(guint));
284   params_maptypes_ids[len] = id;
285   params_maptypes_ids[len+1] = 0;
286
287   /* We have to clone */
288   VikMapSource *clone = VIK_MAP_SOURCE(g_object_ref(map));
289   /* Register the clone in the list */
290   __map_types = g_list_append(__map_types, clone);
291
292   /* Hack
293      We have to ensure the mode LayerParam references the up-to-date
294      GLists.
295   */
296   /*
297   memcpy(&maps_layer_params[0].widget_data, &params_maptypes, sizeof(gpointer));
298   memcpy(&maps_layer_params[0].extra_widget_data, &params_maptypes_ids, sizeof(gpointer));
299   */
300   maps_layer_params[0].widget_data = params_maptypes;
301   maps_layer_params[0].extra_widget_data = params_maptypes_ids;
302 }
303
304 void _update_map_source ( const char *label, VikMapSource *map, int index )
305 {
306   GList *item = g_list_nth (__map_types, index);
307   g_object_unref (item->data);
308   item->data = g_object_ref (map);
309   /* Change previous data */
310   g_free (params_maptypes[index]);
311   params_maptypes[index] = g_strdup (label);
312 }
313
314 /**
315  * maps_layer_register_map_source:
316  * @map: the new VikMapSource
317  *
318  * Register a new VikMapSource.
319  * Override existing one (equality of id).
320  */
321 void maps_layer_register_map_source ( VikMapSource *map )
322 {
323   g_assert(map != NULL);
324   
325   guint id = vik_map_source_get_uniq_id(map);
326   const char *label = vik_map_source_get_label(map);
327   g_assert(label != NULL);
328
329   int previous = map_uniq_id_to_index (id);
330   if (previous != NUM_MAP_TYPES)
331   {
332     _update_map_source (label, map, previous);
333   }
334   else
335   {
336     _add_map_source (id, label, map);
337   }
338 }
339
340 #define MAPS_LAYER_NTH_LABEL(n) (params_maptypes[n])
341 #define MAPS_LAYER_NTH_ID(n) (params_maptypes_ids[n])
342 #define MAPS_LAYER_NTH_TYPE(n) (VIK_MAP_SOURCE(g_list_nth_data(__map_types, (n))))
343
344 gint vik_maps_layer_get_map_type(VikMapsLayer *vml)
345 {
346   return(vml->maptype);
347 }
348
349 gchar *vik_maps_layer_get_map_label(VikMapsLayer *vml)
350 {
351   return(g_strdup(MAPS_LAYER_NTH_LABEL(vml->maptype)));
352 }
353
354 /****************************************/
355 /******** CACHE DIR STUFF ***************/
356 /****************************************/
357
358 #define DIRECTDIRACCESS "%s%d" G_DIR_SEPARATOR_S "%d" G_DIR_SEPARATOR_S "%d%s"
359 #define DIRSTRUCTURE "%st%ds%dz%d" G_DIR_SEPARATOR_S "%d" G_DIR_SEPARATOR_S "%d"
360 #define MAPS_CACHE_DIR maps_layer_default_dir()
361
362 #ifdef WINDOWS
363 #include <io.h>
364 #define GLOBAL_MAPS_DIR "C:\\VIKING-MAPS\\"
365 #define LOCAL_MAPS_DIR "VIKING-MAPS"
366 #elif defined __APPLE__
367 #include <stdlib.h>
368 #define GLOBAL_MAPS_DIR "/Library/cache/Viking/maps/"
369 #define LOCAL_MAPS_DIR "/Library/Application Support/Viking/viking-maps"
370 #else /* POSIX */
371 #include <stdlib.h>
372 #define GLOBAL_MAPS_DIR "/var/cache/maps/"
373 #define LOCAL_MAPS_DIR ".viking-maps"
374 #endif
375
376 gchar *maps_layer_default_dir ()
377 {
378   static gchar *defaultdir = NULL;
379   if ( ! defaultdir )
380   {
381     /* Thanks to Mike Davison for the $VIKING_MAPS usage */
382     const gchar *mapdir = g_getenv("VIKING_MAPS");
383     if ( mapdir ) {
384       defaultdir = g_strdup ( mapdir );
385     } else if ( g_access ( GLOBAL_MAPS_DIR, W_OK ) == 0 ) {
386       defaultdir = g_strdup ( GLOBAL_MAPS_DIR );
387     } else {
388       const gchar *home = g_get_home_dir();
389       if (!home || g_access(home, W_OK))
390         home = g_get_home_dir ();
391       if ( home )
392         defaultdir = g_build_filename ( home, LOCAL_MAPS_DIR, NULL );
393       else
394         defaultdir = g_strdup ( LOCAL_MAPS_DIR );
395     }
396     if (defaultdir && (defaultdir[strlen(defaultdir)-1] != G_DIR_SEPARATOR))
397     {
398       /* Add the separator at the end */
399       gchar *tmp = defaultdir;
400       defaultdir = g_strconcat(tmp, G_DIR_SEPARATOR_S, NULL);
401       g_free(tmp);
402     }
403     g_debug("%s: defaultdir=%s", __FUNCTION__, defaultdir);
404   }
405   return defaultdir;
406 }
407
408 static void maps_layer_mkdir_if_default_dir ( VikMapsLayer *vml )
409 {
410   if ( vml->cache_dir && strcmp ( vml->cache_dir, MAPS_CACHE_DIR ) == 0 && g_file_test ( vml->cache_dir, G_FILE_TEST_EXISTS ) == FALSE )
411   {
412     g_mkdir ( vml->cache_dir, 0777 );
413   }
414 }
415
416 static void maps_layer_set_cache_dir ( VikMapsLayer *vml, const gchar *dir )
417 {
418   guint len;
419   g_assert ( vml != NULL);
420   g_free ( vml->cache_dir );
421   vml->cache_dir = NULL;
422
423   if ( dir == NULL || dir[0] == '\0' )
424   {
425     if ( a_preferences_get(VIKING_PREFERENCES_NAMESPACE "maplayer_default_dir") )
426       vml->cache_dir = g_strdup ( a_preferences_get(VIKING_PREFERENCES_NAMESPACE "maplayer_default_dir")->s );
427   }
428   else
429   {
430     len = strlen(dir);
431     if ( dir[len-1] != G_DIR_SEPARATOR )
432     {
433       vml->cache_dir = g_malloc ( len+2 );
434       strncpy ( vml->cache_dir, dir, len );
435       vml->cache_dir[len] = G_DIR_SEPARATOR;
436       vml->cache_dir[len+1] = '\0';
437     }
438     else
439       vml->cache_dir = g_strdup ( dir );
440   }
441   maps_layer_mkdir_if_default_dir ( vml );
442 }
443
444 /****************************************/
445 /******** GOBJECT STUFF *****************/
446 /****************************************/
447
448 GType vik_maps_layer_get_type ()
449 {
450   static GType vml_type = 0;
451
452   if (!vml_type)
453   {
454     static const GTypeInfo vml_info =
455     {
456       sizeof (VikMapsLayerClass),
457       NULL, /* base_init */
458       NULL, /* base_finalize */
459       NULL, /* class init */
460       NULL, /* class_finalize */
461       NULL, /* class_data */
462       sizeof (VikMapsLayer),
463       0,
464       NULL /* instance init */
465     };
466     vml_type = g_type_register_static ( VIK_LAYER_TYPE, "VikMapsLayer", &vml_info, 0 );
467   }
468
469   return vml_type;
470 }
471
472 /****************************************/
473 /************** PARAMETERS **************/
474 /****************************************/
475
476 static guint map_index_to_uniq_id (guint8 index)
477 {
478   g_assert ( index < NUM_MAP_TYPES );
479   return vik_map_source_get_uniq_id(MAPS_LAYER_NTH_TYPE(index));
480 }
481
482 static guint map_uniq_id_to_index ( guint uniq_id )
483 {
484   gint i;
485   for ( i = 0; i < NUM_MAP_TYPES; i++ )
486     if ( vik_map_source_get_uniq_id(MAPS_LAYER_NTH_TYPE(i)) == uniq_id )
487       return i;
488   return NUM_MAP_TYPES; /* no such thing */
489 }
490
491 static gboolean maps_layer_set_param ( VikMapsLayer *vml, guint16 id, VikLayerParamData data, VikViewport *vvp, gboolean is_file_operation )
492 {
493   // When loading from a file don't need the license reminder
494   if ( is_file_operation )
495     vml->license_notice_shown = TRUE;
496
497   switch ( id )
498   {
499     case PARAM_CACHE_DIR: maps_layer_set_cache_dir ( vml, data.s ); break;
500     case PARAM_MAPTYPE: {
501       gint maptype = map_uniq_id_to_index(data.u);
502       if ( maptype == NUM_MAP_TYPES ) g_warning(_("Unknown map type"));
503       else vml->maptype = maptype;
504       break;
505     }
506     case PARAM_ALPHA: if ( data.u <= 255 ) vml->alpha = data.u; break;
507     case PARAM_AUTODOWNLOAD: vml->autodownload = data.b; break;
508     case PARAM_ONLYMISSING: vml->adl_only_missing = data.b; break;
509     case PARAM_MAPZOOM: if ( data.u < NUM_MAPZOOMS ) {
510                           vml->mapzoom_id = data.u;
511                           vml->xmapzoom = __mapzooms_x [data.u];
512                           vml->ymapzoom = __mapzooms_y [data.u];
513                         }else g_warning (_("Unknown Map Zoom")); break;
514   }
515   return TRUE;
516 }
517
518 static VikLayerParamData maps_layer_get_param ( VikMapsLayer *vml, guint16 id, gboolean is_file_operation )
519 {
520   VikLayerParamData rv;
521   switch ( id )
522   {
523     case PARAM_CACHE_DIR:
524     {
525       gboolean set = FALSE;
526       /* Only save a blank when the map cache location equals the default
527           On reading in, when it is blank then the default is reconstructed
528           Since the default changes dependent on the user and OS, it means the resultant file is more portable */
529       if ( is_file_operation && vml->cache_dir && strcmp ( vml->cache_dir, MAPS_CACHE_DIR ) == 0 ) {
530         rv.s = "";
531         set = TRUE;
532       }
533       else if ( is_file_operation ) {
534         if ( a_vik_get_file_ref_format() == VIK_FILE_REF_FORMAT_RELATIVE ) {
535           gchar *cwd = g_get_current_dir();
536           if ( cwd ) {
537             rv.s = file_GetRelativeFilename ( cwd, vml->cache_dir );
538             if ( !rv.s ) rv.s = "";
539             set = TRUE;
540           }
541         }
542       }
543       if ( !set )
544         rv.s = vml->cache_dir ? vml->cache_dir : "";
545       break;
546     }
547     case PARAM_MAPTYPE: rv.u = map_index_to_uniq_id ( vml->maptype ); break;
548     case PARAM_ALPHA: rv.u = vml->alpha; break;
549     case PARAM_AUTODOWNLOAD: rv.u = vml->autodownload; break;
550     case PARAM_ONLYMISSING: rv.u = vml->adl_only_missing; break;
551     case PARAM_MAPZOOM: rv.u = vml->mapzoom_id; break;
552   }
553   return rv;
554 }
555
556 /****************************************/
557 /****** CREATING, COPYING, FREEING ******/
558 /****************************************/
559
560 static VikMapsLayer *maps_layer_new ( VikViewport *vvp )
561 {
562   VikMapsLayer *vml = VIK_MAPS_LAYER ( g_object_new ( VIK_MAPS_LAYER_TYPE, NULL ) );
563   vik_layer_set_type ( VIK_LAYER(vml), VIK_LAYER_MAPS );
564
565   vik_layer_set_defaults ( VIK_LAYER(vml), vvp );
566
567   vml->dl_tool_x = vml->dl_tool_y = -1;
568   vml->last_center = NULL;
569   vml->last_xmpp = 0.0;
570   vml->last_ympp = 0.0;
571
572   vml->dl_right_click_menu = NULL;
573   vml->license_notice_shown = FALSE;
574
575   return vml;
576 }
577
578 static void maps_layer_free ( VikMapsLayer *vml )
579 {
580   g_free ( vml->cache_dir );
581   vml->cache_dir = NULL;
582   if ( vml->dl_right_click_menu )
583     g_object_ref_sink ( G_OBJECT(vml->dl_right_click_menu) );
584   g_free(vml->last_center);
585   vml->last_center = NULL;
586 }
587
588 static void maps_layer_post_read (VikLayer *vl, VikViewport *vp, gboolean from_file)
589 {
590   if (from_file != TRUE)
591   {
592     /* If this method is not called in file reading context
593      * it is called in GUI context.
594      * So, we can check if we have to inform the user about inconsistency */
595     VikViewportDrawMode vp_drawmode;
596     VikMapsLayer *vml = VIK_MAPS_LAYER(vl);
597     VikMapSource *map = NULL;
598  
599     vp_drawmode = vik_viewport_get_drawmode ( vp );
600     map = MAPS_LAYER_NTH_TYPE(vml->maptype);
601     if (vik_map_source_get_drawmode(map) != vp_drawmode) {
602       const gchar *drawmode_name = vik_viewport_get_drawmode_name (vp, vik_map_source_get_drawmode(map));
603       gchar *msg = g_strdup_printf(_("New map cannot be displayed in the current drawmode.\nSelect \"%s\" from View menu to view it."), drawmode_name);
604       a_dialog_warning_msg ( VIK_GTK_WINDOW_FROM_WIDGET(vp), msg );
605       g_free(msg);
606     }
607
608     if (vik_map_source_get_license (map) != NULL) {
609       if ( ! vml->license_notice_shown ) {
610         a_dialog_license (VIK_GTK_WINDOW_FROM_WIDGET(vp), vik_map_source_get_label (map),
611                           vik_map_source_get_license (map), vik_map_source_get_license_url (map) );
612         vml->license_notice_shown = TRUE;
613       }
614     }
615   }
616 }
617
618 static const gchar* maps_layer_tooltip ( VikMapsLayer *vml )
619 {
620   return vik_maps_layer_get_map_label ( vml );
621 }
622
623 static void maps_layer_marshall( VikMapsLayer *vml, guint8 **data, gint *len )
624 {
625   vik_layer_marshall_params ( VIK_LAYER(vml), data, len );
626 }
627
628 static VikMapsLayer *maps_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp )
629 {
630   VikMapsLayer *rv = maps_layer_new ( vvp );
631   vik_layer_unmarshall_params ( VIK_LAYER(rv), data, len, vvp );
632   return rv;
633 }
634
635 /*********************/
636 /****** DRAWING ******/
637 /*********************/
638
639 static GdkPixbuf *pixbuf_set_alpha ( GdkPixbuf *pixbuf, guint8 alpha )
640 {
641   guchar *pixels;
642   gint width, height, iii, jjj;
643
644   if ( ! gdk_pixbuf_get_has_alpha ( pixbuf ) )
645   {
646     GdkPixbuf *tmp = gdk_pixbuf_add_alpha(pixbuf,FALSE,0,0,0);
647     g_object_unref(G_OBJECT(pixbuf));
648     pixbuf = tmp;
649   }
650
651   pixels = gdk_pixbuf_get_pixels(pixbuf);
652   width = gdk_pixbuf_get_width(pixbuf);
653   height = gdk_pixbuf_get_height(pixbuf);
654
655   /* r,g,b,a,r,g,b,a.... */
656   for (iii = 0; iii < width; iii++) for (jjj = 0; jjj < height; jjj++)
657   {
658     pixels += 3;
659     *pixels++ = alpha;
660   }
661   return pixbuf;
662 }
663
664 static GdkPixbuf *pixbuf_shrink ( GdkPixbuf *pixbuf, gdouble xshrinkfactor, gdouble yshrinkfactor )
665 {
666   GdkPixbuf *tmp;
667   guint16 width = gdk_pixbuf_get_width(pixbuf), height = gdk_pixbuf_get_height(pixbuf);
668   tmp = gdk_pixbuf_scale_simple(pixbuf, ceil(width * xshrinkfactor), ceil(height * yshrinkfactor), GDK_INTERP_BILINEAR);
669   g_object_unref ( G_OBJECT(pixbuf) );
670   return tmp;
671 }
672
673 static GdkPixbuf *get_pixbuf( VikMapsLayer *vml, gint mode, MapCoord *mapcoord, gchar *filename_buf, gint buf_len, gdouble xshrinkfactor, gdouble yshrinkfactor )
674 {
675   GdkPixbuf *pixbuf;
676
677   /* get the thing */
678   pixbuf = a_mapcache_get ( mapcoord->x, mapcoord->y, mapcoord->z,
679                             mode, mapcoord->scale, vml->alpha, xshrinkfactor, yshrinkfactor );
680
681   if ( ! pixbuf ) {
682     if ( vik_map_source_is_direct_file_access (MAPS_LAYER_NTH_TYPE(vml->maptype)) )
683       g_snprintf ( filename_buf, buf_len, DIRECTDIRACCESS,
684                    vml->cache_dir, (17 - mapcoord->scale), mapcoord->x, mapcoord->y, ".png" );
685     else
686       g_snprintf ( filename_buf, buf_len, DIRSTRUCTURE,
687                    vml->cache_dir, mode,
688                    mapcoord->scale, mapcoord->z, mapcoord->x, mapcoord->y );
689
690     if ( g_file_test ( filename_buf, G_FILE_TEST_EXISTS ) == TRUE)
691     {
692       GError *gx = NULL;
693       pixbuf = gdk_pixbuf_new_from_file ( filename_buf, &gx );
694
695       /* free the pixbuf on error */
696       if (gx)
697       {
698         if ( gx->domain != GDK_PIXBUF_ERROR || gx->code != GDK_PIXBUF_ERROR_CORRUPT_IMAGE )
699           g_warning ( _("Couldn't open image file: %s"), gx->message );
700
701         g_error_free ( gx );
702         if ( pixbuf )
703           g_object_unref ( G_OBJECT(pixbuf) );
704         pixbuf = NULL;
705       } else {
706           if ( vml->alpha < 255 )
707             pixbuf = pixbuf_set_alpha ( pixbuf, vml->alpha );
708           if ( xshrinkfactor != 1.0 || yshrinkfactor != 1.0 )
709             pixbuf = pixbuf_shrink ( pixbuf, xshrinkfactor, yshrinkfactor );
710
711           a_mapcache_add ( pixbuf, mapcoord->x, mapcoord->y, 
712               mapcoord->z, vik_map_source_get_uniq_id(MAPS_LAYER_NTH_TYPE(vml->maptype)),
713               mapcoord->scale, vml->alpha, xshrinkfactor, yshrinkfactor );
714       }
715     }
716   }
717   return pixbuf;
718 }
719
720 static gboolean should_start_autodownload(VikMapsLayer *vml, VikViewport *vvp)
721 {
722   const VikCoord *center = vik_viewport_get_center ( vvp );
723
724   if (vik_window_get_pan_move (VIK_WINDOW(VIK_GTK_WINDOW_FROM_WIDGET(GTK_WIDGET(vvp)))))
725     /* D'n'D pan in action: do not download */
726     return FALSE;
727
728   // TEMPORARY HACK
729   // Prevent requests for downloading tiles at Zoom Level 19 and above for most map types
730   // Allow MapQuest Zoom Level up to 19
731   // TODO: This should be made a property of the map source and then use that value
732   gdouble xzoom = vik_viewport_get_xmpp ( vvp );
733   if ( (vml->maptype != 19 && map_utils_mpp_to_scale (xzoom) < -1) || (vml->maptype == 19 && map_utils_mpp_to_scale (xzoom) < -2) )
734     return FALSE;
735
736   if (vml->last_center == NULL) {
737     VikCoord *new_center = g_malloc(sizeof(VikCoord));
738     *new_center = *center;
739     vml->last_center = new_center;
740     vml->last_xmpp = vik_viewport_get_xmpp(vvp);
741     vml->last_ympp = vik_viewport_get_ympp(vvp);
742     return TRUE;
743   }
744
745   /* TODO: perhaps vik_coord_diff() */
746   if (vik_coord_equals(vml->last_center, center)
747       && (vml->last_xmpp == vik_viewport_get_xmpp(vvp))
748       && (vml->last_ympp == vik_viewport_get_ympp(vvp)))
749     return FALSE;
750
751   *(vml->last_center) = *center;
752     vml->last_xmpp = vik_viewport_get_xmpp(vvp);
753     vml->last_ympp = vik_viewport_get_ympp(vvp);
754   return TRUE;
755 }
756
757 static void maps_layer_draw_section ( VikMapsLayer *vml, VikViewport *vvp, VikCoord *ul, VikCoord *br )
758 {
759   MapCoord ulm, brm;
760   gdouble xzoom = vik_viewport_get_xmpp ( vvp );
761   gdouble yzoom = vik_viewport_get_ympp ( vvp );
762   gdouble xshrinkfactor = 1.0, yshrinkfactor = 1.0;
763   gboolean existence_only = FALSE;
764
765   if ( vml->xmapzoom && (vml->xmapzoom != xzoom || vml->ymapzoom != yzoom) ) {
766     xshrinkfactor = vml->xmapzoom / xzoom;
767     yshrinkfactor = vml->ymapzoom / yzoom;
768     xzoom = vml->xmapzoom;
769     yzoom = vml->xmapzoom;
770     if ( ! (xshrinkfactor > MIN_SHRINKFACTOR && xshrinkfactor < MAX_SHRINKFACTOR &&
771          yshrinkfactor > MIN_SHRINKFACTOR && yshrinkfactor < MAX_SHRINKFACTOR ) ) {
772       if ( xshrinkfactor > REAL_MIN_SHRINKFACTOR && yshrinkfactor > REAL_MIN_SHRINKFACTOR ) {
773         g_debug ( "%s: existence_only due to SHRINKFACTORS", __FUNCTION__ );
774         existence_only = TRUE;
775       }
776       else {
777         g_warning ( _("Cowardly refusing to draw tiles or existence of tiles beyond %d zoom out factor"), (int)( 1.0/REAL_MIN_SHRINKFACTOR));
778         return;
779       }
780     }
781   }
782
783   /* coord -> ID */
784   VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
785   if ( vik_map_source_coord_to_mapcoord ( map, ul, xzoom, yzoom, &ulm ) &&
786        vik_map_source_coord_to_mapcoord ( map, br, xzoom, yzoom, &brm ) ) {
787
788     /* loop & draw */
789     gint x, y;
790     gint xmin = MIN(ulm.x, brm.x), xmax = MAX(ulm.x, brm.x);
791     gint ymin = MIN(ulm.y, brm.y), ymax = MAX(ulm.y, brm.y);
792     gint mode = vik_map_source_get_uniq_id(map);
793
794     VikCoord coord;
795     gint xx, yy, width, height;
796     GdkPixbuf *pixbuf;
797
798     // Prevent the program grinding to a halt if trying to deal with thousands of tiles
799     //  which can happen when using a small fixed zoom level and viewing large areas.
800     // Also prevents very large number of tile download requests
801     gint tiles = (xmax-xmin) * (ymax-ymin);
802     if ( tiles > MAX_TILES ) {
803       g_debug ( "%s: existence_only due to wanting too many tiles (%d)", __FUNCTION__, tiles );
804       existence_only = TRUE;
805     }
806
807     guint max_path_len = strlen(vml->cache_dir) + 40;
808     gchar *path_buf = g_malloc ( max_path_len * sizeof(char) );
809
810     if ( (!existence_only) && vml->autodownload  && should_start_autodownload(vml, vvp)) {
811       g_debug("%s: Starting autodownload", __FUNCTION__);
812       if ( !vml->adl_only_missing && vik_map_source_supports_download_only_new (map) )
813         // Try to download newer tiles
814         start_download_thread ( vml, vvp, ul, br, REDOWNLOAD_NEW );
815       else
816         // Download only missing tiles
817         start_download_thread ( vml, vvp, ul, br, REDOWNLOAD_NONE );
818     }
819
820     if ( vik_map_source_get_tilesize_x(map) == 0 && !existence_only ) {
821       for ( x = xmin; x <= xmax; x++ ) {
822         for ( y = ymin; y <= ymax; y++ ) {
823           ulm.x = x;
824           ulm.y = y;
825           pixbuf = get_pixbuf ( vml, mode, &ulm, path_buf, max_path_len, xshrinkfactor, yshrinkfactor );
826           if ( pixbuf ) {
827             width = gdk_pixbuf_get_width ( pixbuf );
828             height = gdk_pixbuf_get_height ( pixbuf );
829
830             vik_map_source_mapcoord_to_center_coord ( map, &ulm, &coord );
831             vik_viewport_coord_to_screen ( vvp, &coord, &xx, &yy );
832             xx -= (width/2);
833             yy -= (height/2);
834
835             vik_viewport_draw_pixbuf ( vvp, pixbuf, 0, 0, xx, yy, width, height );
836           }
837         }
838       }
839     } else { /* tilesize is known, don't have to keep converting coords */
840       gdouble tilesize_x = vik_map_source_get_tilesize_x(map) * xshrinkfactor;
841       gdouble tilesize_y = vik_map_source_get_tilesize_y(map) * yshrinkfactor;
842       /* ceiled so tiles will be maximum size in the case of funky shrinkfactor */
843       gint tilesize_x_ceil = ceil ( tilesize_x );
844       gint tilesize_y_ceil = ceil ( tilesize_y );
845       gint8 xinc = (ulm.x == xmin) ? 1 : -1;
846       gint8 yinc = (ulm.y == ymin) ? 1 : -1;
847       gdouble xx, yy; gint xx_tmp, yy_tmp;
848       gint base_yy, xend, yend;
849
850       xend = (xinc == 1) ? (xmax+1) : (xmin-1);
851       yend = (yinc == 1) ? (ymax+1) : (ymin-1);
852
853       vik_map_source_mapcoord_to_center_coord ( map, &ulm, &coord );
854       vik_viewport_coord_to_screen ( vvp, &coord, &xx_tmp, &yy_tmp );
855       xx = xx_tmp; yy = yy_tmp;
856       /* above trick so xx,yy doubles. this is so shrinkfactors aren't rounded off
857        * eg if tile size 128, shrinkfactor 0.333 */
858       xx -= (tilesize_x/2);
859       base_yy = yy - (tilesize_y/2);
860
861       for ( x = ((xinc == 1) ? xmin : xmax); x != xend; x+=xinc ) {
862         yy = base_yy;
863         for ( y = ((yinc == 1) ? ymin : ymax); y != yend; y+=yinc ) {
864           ulm.x = x;
865           ulm.y = y;
866
867           if ( existence_only ) {
868             if ( vik_map_source_is_direct_file_access (MAPS_LAYER_NTH_TYPE(vml->maptype)) )
869               g_snprintf ( path_buf, max_path_len, DIRECTDIRACCESS,
870                            vml->cache_dir, (17 - ulm.scale), ulm.x, ulm.y, ".png" );
871             else
872               g_snprintf ( path_buf, max_path_len, DIRSTRUCTURE,
873                            vml->cache_dir, mode,
874                            ulm.scale, ulm.z, ulm.x, ulm.y );
875             if ( g_file_test ( path_buf, G_FILE_TEST_EXISTS ) == TRUE ) {
876               GdkGC *black_gc = gtk_widget_get_style(GTK_WIDGET(vvp))->black_gc;
877               vik_viewport_draw_line ( vvp, black_gc, xx+tilesize_x_ceil, yy, xx, yy+tilesize_y_ceil );
878             }
879           } else {
880             int scale_inc;
881             for (scale_inc = 0; scale_inc < 4; scale_inc ++) {
882               /* try with correct then smaller zooms */
883               int scale_factor = 1 << scale_inc;  /*  2^scale_inc */
884               MapCoord ulm2 = ulm;
885               ulm2.x = ulm.x / scale_factor;
886               ulm2.y = ulm.y / scale_factor;
887               ulm2.scale = ulm.scale + scale_inc;
888               pixbuf = get_pixbuf ( vml, mode, &ulm2, path_buf, max_path_len, xshrinkfactor * scale_factor, yshrinkfactor * scale_factor );
889               if ( pixbuf ) {
890                 gint src_x = (ulm.x % scale_factor) * tilesize_x_ceil;
891                 gint src_y = (ulm.y % scale_factor) * tilesize_y_ceil;
892 #ifdef DEBUG
893                 printf("maps_layer_draw_section - x=%d, y=%d, z=%d, src_x=%d, src_y=%d, xx=%d, yy=%d - %x\n", ulm.x, ulm.y, ulm.scale, src_x, src_y, (int)xx, (int)yy, vvp);
894 #endif
895                 vik_viewport_draw_pixbuf ( vvp, pixbuf, src_x, src_y, xx, yy, tilesize_x_ceil, tilesize_y_ceil );
896                 break;
897               }
898             }
899             if ( !pixbuf ) {
900               /* retry with bigger zooms */
901               int scale_dec;
902               for (scale_dec = 1; scale_dec < 2; scale_dec ++) {
903                 int pict_x, pict_y;
904                 int scale_factor = 1 << scale_dec;  /*  2^scale_dec */
905                 MapCoord ulm2 = ulm;
906                 ulm2.x = ulm.x * scale_factor;
907                 ulm2.y = ulm.y * scale_factor;
908                 ulm2.scale = ulm.scale - scale_dec;
909                 for (pict_x = 0; pict_x < scale_factor; pict_x ++) {
910                   for (pict_y = 0; pict_y < scale_factor; pict_y ++) {
911                     MapCoord ulm3 = ulm2;
912                     ulm3.x += pict_x;
913                     ulm3.y += pict_y;
914                     pixbuf = get_pixbuf ( vml, mode, &ulm3, path_buf, max_path_len, xshrinkfactor / scale_factor, yshrinkfactor / scale_factor );
915                     if ( pixbuf ) {
916                       gint src_x = 0;
917                       gint src_y = 0;
918                       gint dest_x = xx + pict_x * (tilesize_x_ceil / scale_factor);
919                       gint dest_y = yy + pict_y * (tilesize_y_ceil / scale_factor);
920                       vik_viewport_draw_pixbuf ( vvp, pixbuf, src_x, src_y, dest_x, dest_y, tilesize_x_ceil / scale_factor, tilesize_y_ceil / scale_factor );
921                     }
922                   }
923                 }
924               }
925             }
926           }
927
928           yy += tilesize_y;
929         }
930         xx += tilesize_x;
931       }
932     }
933
934     g_free ( path_buf );
935   }
936 }
937
938 static void maps_layer_draw ( VikMapsLayer *vml, VikViewport *vvp )
939 {
940   if ( vik_map_source_get_drawmode(MAPS_LAYER_NTH_TYPE(vml->maptype)) == vik_viewport_get_drawmode ( vvp ) )
941   {
942     VikCoord ul, br;
943
944     /* Copyright */
945     gdouble level = vik_viewport_get_zoom ( vvp );
946     LatLonBBox bbox;
947     vik_viewport_get_min_max_lat_lon ( vvp, &bbox.south, &bbox.north, &bbox.west, &bbox.east );
948     vik_map_source_get_copyright ( MAPS_LAYER_NTH_TYPE(vml->maptype), bbox, level, vik_viewport_add_copyright, vvp );
949
950     /* Logo */
951     const GdkPixbuf *logo = vik_map_source_get_logo ( MAPS_LAYER_NTH_TYPE(vml->maptype) );
952     vik_viewport_add_logo ( vvp, logo );
953
954     /* get corner coords */
955     if ( vik_viewport_get_coord_mode ( vvp ) == VIK_COORD_UTM && ! vik_viewport_is_one_zone ( vvp ) ) {
956       /* UTM multi-zone stuff by Kit Transue */
957       gchar leftmost_zone, rightmost_zone, i;
958       leftmost_zone = vik_viewport_leftmost_zone( vvp );
959       rightmost_zone = vik_viewport_rightmost_zone( vvp );
960       for ( i = leftmost_zone; i <= rightmost_zone; ++i ) {
961         vik_viewport_corners_for_zonen ( vvp, i, &ul, &br );
962         maps_layer_draw_section ( vml, vvp, &ul, &br );
963       }
964     }
965     else {
966       vik_viewport_screen_to_coord ( vvp, 0, 0, &ul );
967       vik_viewport_screen_to_coord ( vvp, vik_viewport_get_width(vvp), vik_viewport_get_height(vvp), &br );
968
969       maps_layer_draw_section ( vml, vvp, &ul, &br );
970     }
971   }
972 }
973
974 /*************************/
975 /****** DOWNLOADING ******/
976 /*************************/
977
978 /* pass along data to thread, exists even if layer is deleted. */
979 typedef struct {
980   gchar *cache_dir;
981   gchar *filename_buf;
982   gint x0, y0, xf, yf;
983   MapCoord mapcoord;
984   gint maptype;
985   gint maxlen;
986   gint mapstoget;
987   gint redownload;
988   gboolean refresh_display;
989   VikMapsLayer *vml;
990   VikViewport *vvp;
991   gboolean map_layer_alive;
992   GMutex *mutex;
993 } MapDownloadInfo;
994
995 static void mdi_free ( MapDownloadInfo *mdi )
996 {
997   g_mutex_free(mdi->mutex);
998   g_free ( mdi->cache_dir );
999   mdi->cache_dir = NULL;
1000   g_free ( mdi->filename_buf );
1001   mdi->filename_buf = NULL;
1002   g_free ( mdi );
1003 }
1004
1005 static void weak_ref_cb(gpointer ptr, GObject * dead_vml)
1006 {
1007   MapDownloadInfo *mdi = ptr;
1008   g_mutex_lock(mdi->mutex);
1009   mdi->map_layer_alive = FALSE;
1010   g_mutex_unlock(mdi->mutex);
1011 }
1012
1013 static int map_download_thread ( MapDownloadInfo *mdi, gpointer threaddata )
1014 {
1015   void *handle = vik_map_source_download_handle_init(MAPS_LAYER_NTH_TYPE(mdi->maptype));
1016   guint donemaps = 0;
1017   gint x, y;
1018   for ( x = mdi->x0; x <= mdi->xf; x++ )
1019   {
1020     for ( y = mdi->y0; y <= mdi->yf; y++ )
1021     {
1022       gboolean remove_mem_cache = FALSE;
1023       gboolean need_download = FALSE;
1024       g_snprintf ( mdi->filename_buf, mdi->maxlen, DIRSTRUCTURE,
1025                      mdi->cache_dir, vik_map_source_get_uniq_id(MAPS_LAYER_NTH_TYPE(mdi->maptype)),
1026                      mdi->mapcoord.scale, mdi->mapcoord.z, x, y );
1027
1028       donemaps++;
1029       int res = a_background_thread_progress ( threaddata, ((gdouble)donemaps) / mdi->mapstoget ); /* this also calls testcancel */
1030       if (res != 0) {
1031         vik_map_source_download_handle_cleanup(MAPS_LAYER_NTH_TYPE(mdi->maptype), handle);
1032         return -1;
1033       }
1034
1035       if ( g_file_test ( mdi->filename_buf, G_FILE_TEST_EXISTS ) == FALSE ) {
1036         need_download = TRUE;
1037         remove_mem_cache = TRUE;
1038
1039       } else {  /* in case map file already exists */
1040         switch (mdi->redownload) {
1041           case REDOWNLOAD_NONE:
1042             continue;
1043
1044           case REDOWNLOAD_BAD:
1045           {
1046             /* see if this one is bad or what */
1047             GError *gx = NULL;
1048             GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file ( mdi->filename_buf, &gx );
1049             if (gx || (!pixbuf)) {
1050               g_remove ( mdi->filename_buf );
1051               need_download = TRUE;
1052               remove_mem_cache = TRUE;
1053               g_error_free ( gx );
1054
1055             } else {
1056               g_object_unref ( pixbuf );
1057             }
1058             break;
1059           }
1060
1061           case REDOWNLOAD_NEW:
1062             need_download = TRUE;
1063             remove_mem_cache = TRUE;
1064             break;
1065
1066           case REDOWNLOAD_ALL:
1067             /* FIXME: need a better way than to erase file in case of server/network problem */
1068             g_remove ( mdi->filename_buf );
1069             need_download = TRUE;
1070             remove_mem_cache = TRUE;
1071             break;
1072
1073           case DOWNLOAD_OR_REFRESH:
1074             remove_mem_cache = TRUE;
1075             break;
1076
1077           default:
1078             g_warning ( "redownload state %d unknown\n", mdi->redownload);
1079         }
1080       }
1081
1082       mdi->mapcoord.x = x; mdi->mapcoord.y = y;
1083
1084       if (need_download) {
1085         if ( vik_map_source_download( MAPS_LAYER_NTH_TYPE(mdi->maptype), &(mdi->mapcoord), mdi->filename_buf, handle))
1086           continue;
1087       }
1088
1089       g_mutex_lock(mdi->mutex);
1090       if (remove_mem_cache)
1091           a_mapcache_remove_all_shrinkfactors ( x, y, mdi->mapcoord.z, vik_map_source_get_uniq_id(MAPS_LAYER_NTH_TYPE(mdi->maptype)), mdi->mapcoord.scale );
1092       if (mdi->refresh_display && mdi->map_layer_alive) {
1093         /* TODO: check if it's on visible area */
1094         vik_layer_emit_update ( VIK_LAYER(mdi->vml) ); // NB update display from background
1095       }
1096       g_mutex_unlock(mdi->mutex);
1097       mdi->mapcoord.x = mdi->mapcoord.y = 0; /* we're temporarily between downloads */
1098
1099     }
1100   }
1101   vik_map_source_download_handle_cleanup(MAPS_LAYER_NTH_TYPE(mdi->maptype), handle);
1102   g_mutex_lock(mdi->mutex);
1103   if (mdi->map_layer_alive)
1104     g_object_weak_unref(G_OBJECT(mdi->vml), weak_ref_cb, mdi);
1105   g_mutex_unlock(mdi->mutex); 
1106   return 0;
1107 }
1108
1109 static void mdi_cancel_cleanup ( MapDownloadInfo *mdi )
1110 {
1111   if ( mdi->mapcoord.x || mdi->mapcoord.y )
1112   {
1113     g_snprintf ( mdi->filename_buf, mdi->maxlen, DIRSTRUCTURE,
1114                      mdi->cache_dir, vik_map_source_get_uniq_id(MAPS_LAYER_NTH_TYPE(mdi->maptype)),
1115                      mdi->mapcoord.scale, mdi->mapcoord.z, mdi->mapcoord.x, mdi->mapcoord.y );
1116     if ( g_file_test ( mdi->filename_buf, G_FILE_TEST_EXISTS ) == TRUE)
1117     {
1118       g_remove ( mdi->filename_buf );
1119     }
1120   }
1121 }
1122
1123 static void start_download_thread ( VikMapsLayer *vml, VikViewport *vvp, const VikCoord *ul, const VikCoord *br, gint redownload )
1124 {
1125   gdouble xzoom = vml->xmapzoom ? vml->xmapzoom : vik_viewport_get_xmpp ( vvp );
1126   gdouble yzoom = vml->ymapzoom ? vml->ymapzoom : vik_viewport_get_ympp ( vvp );
1127   MapCoord ulm, brm;
1128   VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
1129
1130   // Don't ever attempt download on direct access
1131   if ( vik_map_source_is_direct_file_access ( map ) )
1132     return;
1133
1134   if ( vik_map_source_coord_to_mapcoord ( map, ul, xzoom, yzoom, &ulm ) 
1135     && vik_map_source_coord_to_mapcoord ( map, br, xzoom, yzoom, &brm ) )
1136   {
1137     MapDownloadInfo *mdi = g_malloc ( sizeof(MapDownloadInfo) );
1138     gint a, b;
1139
1140     mdi->vml = vml;
1141     mdi->vvp = vvp;
1142     mdi->map_layer_alive = TRUE;
1143     mdi->mutex = g_mutex_new();
1144     mdi->refresh_display = TRUE;
1145
1146     /* cache_dir and buffer for dest filename */
1147     mdi->cache_dir = g_strdup ( vml->cache_dir );
1148     mdi->maxlen = strlen ( vml->cache_dir ) + 40;
1149     mdi->filename_buf = g_malloc ( mdi->maxlen * sizeof(gchar) );
1150     mdi->maptype = vml->maptype;
1151
1152     mdi->mapcoord = ulm;
1153
1154     mdi->redownload = redownload;
1155
1156     mdi->x0 = MIN(ulm.x, brm.x);
1157     mdi->xf = MAX(ulm.x, brm.x);
1158     mdi->y0 = MIN(ulm.y, brm.y);
1159     mdi->yf = MAX(ulm.y, brm.y);
1160
1161     mdi->mapstoget = 0;
1162
1163     if ( mdi->redownload ) {
1164       mdi->mapstoget = (mdi->xf - mdi->x0 + 1) * (mdi->yf - mdi->y0 + 1);
1165     } else {
1166       /* calculate how many we need */
1167       for ( a = mdi->x0; a <= mdi->xf; a++ )
1168       {
1169         for ( b = mdi->y0; b <= mdi->yf; b++ )
1170         {
1171           g_snprintf ( mdi->filename_buf, mdi->maxlen, DIRSTRUCTURE,
1172                        vml->cache_dir, vik_map_source_get_uniq_id(map), ulm.scale,
1173                        ulm.z, a, b );
1174           if ( g_file_test ( mdi->filename_buf, G_FILE_TEST_EXISTS ) == FALSE )
1175             mdi->mapstoget++;
1176         }
1177       }
1178     }
1179
1180     mdi->mapcoord.x = mdi->mapcoord.y = 0; /* for cleanup -- no current map */
1181
1182     if ( mdi->mapstoget )
1183     {
1184       const gchar *tmp_str;
1185       gchar *tmp;
1186
1187       if (redownload) 
1188       {
1189         if (redownload == REDOWNLOAD_BAD)
1190           tmp_str = ngettext("Redownloading up to %d %s map...", "Redownloading up to %d %s maps...", mdi->mapstoget);
1191         else
1192           tmp_str = ngettext("Redownloading %d %s map...", "Redownloading %d %s maps...", mdi->mapstoget);
1193       } 
1194       else 
1195       {
1196         tmp_str = ngettext("Downloading %d %s map...", "Downloading %d %s maps...", mdi->mapstoget);
1197       }
1198       tmp = g_strdup_printf ( tmp_str, mdi->mapstoget, MAPS_LAYER_NTH_LABEL(vml->maptype));
1199  
1200       g_object_weak_ref(G_OBJECT(mdi->vml), weak_ref_cb, mdi);
1201       /* launch the thread */
1202       a_background_thread ( VIK_GTK_WINDOW_FROM_LAYER(vml), /* parent window */
1203                             tmp,                                              /* description string */
1204                             (vik_thr_func) map_download_thread,               /* function to call within thread */
1205                             mdi,                                              /* pass along data */
1206                             (vik_thr_free_func) mdi_free,                     /* function to free pass along data */
1207                             (vik_thr_free_func) mdi_cancel_cleanup,
1208                             mdi->mapstoget );
1209       g_free ( tmp );
1210     }
1211     else
1212       mdi_free ( mdi );
1213   }
1214 }
1215
1216 void maps_layer_download_section ( VikMapsLayer *vml, VikViewport *vvp, VikCoord *ul, VikCoord *br, gdouble zoom)
1217 {
1218   MapCoord ulm, brm;
1219   VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
1220
1221   // Don't ever attempt download on direct access
1222   if ( vik_map_source_is_direct_file_access ( map ) )
1223     return;
1224
1225   if (!vik_map_source_coord_to_mapcoord(map, ul, zoom, zoom, &ulm) 
1226     || !vik_map_source_coord_to_mapcoord(map, br, zoom, zoom, &brm)) {
1227     g_warning("%s() coord_to_mapcoord() failed", __PRETTY_FUNCTION__);
1228     return;
1229   }
1230
1231   MapDownloadInfo *mdi = g_malloc(sizeof(MapDownloadInfo));
1232   gint i, j;
1233
1234   mdi->vml = vml;
1235   mdi->vvp = vvp;
1236   mdi->map_layer_alive = TRUE;
1237   mdi->mutex = g_mutex_new();
1238   mdi->refresh_display = TRUE;
1239
1240   mdi->cache_dir = g_strdup ( vml->cache_dir );
1241   mdi->maxlen = strlen ( vml->cache_dir ) + 40;
1242   mdi->filename_buf = g_malloc ( mdi->maxlen * sizeof(gchar) );
1243   mdi->maptype = vml->maptype;
1244
1245   mdi->mapcoord = ulm;
1246
1247   mdi->redownload = REDOWNLOAD_NONE;
1248
1249   mdi->x0 = MIN(ulm.x, brm.x);
1250   mdi->xf = MAX(ulm.x, brm.x);
1251   mdi->y0 = MIN(ulm.y, brm.y);
1252   mdi->yf = MAX(ulm.y, brm.y);
1253
1254   mdi->mapstoget = 0;
1255
1256   for (i = mdi->x0; i <= mdi->xf; i++) {
1257     for (j = mdi->y0; j <= mdi->yf; j++) {
1258       g_snprintf ( mdi->filename_buf, mdi->maxlen, DIRSTRUCTURE,
1259                    vml->cache_dir, vik_map_source_get_uniq_id(map), ulm.scale,
1260                    ulm.z, i, j );
1261       if ( g_file_test ( mdi->filename_buf, G_FILE_TEST_EXISTS ) == FALSE )
1262             mdi->mapstoget++;
1263     }
1264   }
1265
1266   mdi->mapcoord.x = mdi->mapcoord.y = 0; /* for cleanup -- no current map */
1267
1268   if (mdi->mapstoget) {
1269     gchar *tmp;
1270     const gchar *fmt;
1271     fmt = ngettext("Downloading %d %s map...",
1272                    "Downloading %d %s maps...",
1273                    mdi->mapstoget);
1274     tmp = g_strdup_printf ( fmt, mdi->mapstoget, MAPS_LAYER_NTH_LABEL(vml->maptype) );
1275
1276     g_object_weak_ref(G_OBJECT(mdi->vml), weak_ref_cb, mdi);
1277       /* launch the thread */
1278     a_background_thread ( VIK_GTK_WINDOW_FROM_LAYER(vml), /* parent window */
1279       tmp,                                /* description string */
1280       (vik_thr_func) map_download_thread, /* function to call within thread */
1281       mdi,                                /* pass along data */
1282       (vik_thr_free_func) mdi_free,       /* function to free pass along data */
1283       (vik_thr_free_func) mdi_cancel_cleanup,
1284       mdi->mapstoget );
1285     g_free ( tmp );
1286   }
1287   else
1288     mdi_free ( mdi );
1289 }
1290
1291 static void maps_layer_redownload_bad ( VikMapsLayer *vml )
1292 {
1293   start_download_thread ( vml, vml->redownload_vvp, &(vml->redownload_ul), &(vml->redownload_br), REDOWNLOAD_BAD );
1294 }
1295
1296 static void maps_layer_redownload_all ( VikMapsLayer *vml )
1297 {
1298   start_download_thread ( vml, vml->redownload_vvp, &(vml->redownload_ul), &(vml->redownload_br), REDOWNLOAD_ALL );
1299 }
1300
1301 static void maps_layer_redownload_new ( VikMapsLayer *vml )
1302 {
1303   start_download_thread ( vml, vml->redownload_vvp, &(vml->redownload_ul), &(vml->redownload_br), REDOWNLOAD_NEW );
1304 }
1305
1306 /**
1307  * Display a simple dialog with information about this particular map tile
1308  */
1309 static void maps_layer_tile_info ( VikMapsLayer *vml )
1310 {
1311   VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
1312
1313   gdouble xzoom = vml->xmapzoom ? vml->xmapzoom : vik_viewport_get_xmpp ( vml->redownload_vvp );
1314   gdouble yzoom = vml->ymapzoom ? vml->ymapzoom : vik_viewport_get_ympp ( vml->redownload_vvp );
1315   MapCoord ulm;
1316
1317   if ( !vik_map_source_coord_to_mapcoord ( map, &(vml->redownload_ul), xzoom, yzoom, &ulm ) )
1318     return;
1319
1320   gchar *filename = NULL;
1321   gchar *message = NULL;
1322   gchar *source = NULL;
1323
1324   if ( vik_map_source_is_direct_file_access ( map ) ) {
1325     filename = g_strdup_printf ( DIRECTDIRACCESS, vml->cache_dir, ulm.scale, ulm.x, ulm.y, ".png" );
1326     source = g_strconcat ( "file://", filename, NULL );
1327   }
1328   else {
1329         filename = g_strdup_printf ( DIRSTRUCTURE, vml->cache_dir, vik_map_source_get_uniq_id(map), ulm.scale, ulm.z, ulm.x, ulm.y );
1330     source = g_strdup_printf ( "http://%s%s",
1331                                vik_map_source_default_get_hostname ( VIK_MAP_SOURCE_DEFAULT(map) ),
1332                                vik_map_source_default_get_uri ( VIK_MAP_SOURCE_DEFAULT(map), &ulm ) );
1333   }
1334
1335   if ( g_file_test ( filename, G_FILE_TEST_EXISTS ) ) {
1336
1337     // Get some timestamp information of the tile
1338     struct stat stat_buf;
1339     if ( g_stat ( filename, &stat_buf ) == 0 ) {
1340       gchar time_buf[64];
1341       strftime ( time_buf, sizeof(time_buf), "%c", gmtime((const time_t *)&stat_buf.st_mtime) );
1342       message = g_strdup_printf ( _("\nSource: %s\n\nTile File: %s\nTile File Timestamp: %s"), source, filename, time_buf );
1343     }
1344   }
1345   else
1346     message = g_strdup_printf ( _("Source: %s\n\nNo Tile File!"), source );
1347
1348   // Show the info
1349   a_dialog_info_msg (  VIK_GTK_WINDOW_FROM_LAYER(vml), message );
1350
1351   g_free ( message );
1352   g_free ( source );
1353   g_free ( filename );
1354 }
1355
1356 static gboolean maps_layer_download_release ( VikMapsLayer *vml, GdkEventButton *event, VikViewport *vvp )
1357 {
1358   if (!vml || vml->vl.type != VIK_LAYER_MAPS)
1359     return FALSE;
1360   if ( vml->dl_tool_x != -1 && vml->dl_tool_y != -1 )
1361   {
1362     if ( event->button == 1 )
1363     {
1364       VikCoord ul, br;
1365       vik_viewport_screen_to_coord ( vvp, MAX(0, MIN(event->x, vml->dl_tool_x)), MAX(0, MIN(event->y, vml->dl_tool_y)), &ul );
1366       vik_viewport_screen_to_coord ( vvp, MIN(vik_viewport_get_width(vvp), MAX(event->x, vml->dl_tool_x)), MIN(vik_viewport_get_height(vvp), MAX ( event->y, vml->dl_tool_y ) ), &br );
1367       start_download_thread ( vml, vvp, &ul, &br, DOWNLOAD_OR_REFRESH );
1368       vml->dl_tool_x = vml->dl_tool_y = -1;
1369       return TRUE;
1370     }
1371     else
1372     {
1373       vik_viewport_screen_to_coord ( vvp, MAX(0, MIN(event->x, vml->dl_tool_x)), MAX(0, MIN(event->y, vml->dl_tool_y)), &(vml->redownload_ul) );
1374       vik_viewport_screen_to_coord ( vvp, MIN(vik_viewport_get_width(vvp), MAX(event->x, vml->dl_tool_x)), MIN(vik_viewport_get_height(vvp), MAX ( event->y, vml->dl_tool_y ) ), &(vml->redownload_br) );
1375
1376       vml->redownload_vvp = vvp;
1377
1378       vml->dl_tool_x = vml->dl_tool_y = -1;
1379
1380       if ( ! vml->dl_right_click_menu ) {
1381         GtkWidget *item;
1382         vml->dl_right_click_menu = GTK_MENU ( gtk_menu_new () );
1383
1384         item = gtk_menu_item_new_with_mnemonic ( _("Redownload _Bad Map(s)") );
1385         g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_redownload_bad), vml );
1386         gtk_menu_shell_append ( GTK_MENU_SHELL(vml->dl_right_click_menu), item );
1387
1388         item = gtk_menu_item_new_with_mnemonic ( _("Redownload _New Map(s)") );
1389         g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_redownload_new), vml );
1390         gtk_menu_shell_append ( GTK_MENU_SHELL(vml->dl_right_click_menu), item );
1391
1392         item = gtk_menu_item_new_with_mnemonic ( _("Redownload _All Map(s)") );
1393         g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_redownload_all), vml );
1394         gtk_menu_shell_append ( GTK_MENU_SHELL(vml->dl_right_click_menu), item );
1395
1396         item = gtk_image_menu_item_new_with_mnemonic ( _("_Show Tile Information") );
1397         gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INFO, GTK_ICON_SIZE_MENU) );
1398         g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_tile_info), vml );
1399         gtk_menu_shell_append (GTK_MENU_SHELL(vml->dl_right_click_menu), item);
1400       }
1401
1402       gtk_menu_popup ( vml->dl_right_click_menu, NULL, NULL, NULL, NULL, event->button, event->time );
1403       gtk_widget_show_all ( GTK_WIDGET(vml->dl_right_click_menu) );
1404     }
1405   }
1406   return FALSE;
1407 }
1408
1409 static gpointer maps_layer_download_create ( VikWindow *vw, VikViewport *vvp)
1410 {
1411   return vvp;
1412 }
1413
1414 static gboolean maps_layer_download_click ( VikMapsLayer *vml, GdkEventButton *event, VikViewport *vvp )
1415 {
1416   MapCoord tmp;
1417   if (!vml || vml->vl.type != VIK_LAYER_MAPS)
1418     return FALSE;
1419   VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
1420   if ( vik_map_source_get_drawmode(map) == vik_viewport_get_drawmode ( vvp ) &&
1421        vik_map_source_coord_to_mapcoord ( map, vik_viewport_get_center ( vvp ),
1422            vml->xmapzoom ? vml->xmapzoom : vik_viewport_get_xmpp ( vvp ),
1423            vml->ymapzoom ? vml->ymapzoom : vik_viewport_get_ympp ( vvp ),
1424            &tmp ) ) {
1425     vml->dl_tool_x = event->x, vml->dl_tool_y = event->y;
1426     return TRUE;
1427   }
1428   return FALSE;
1429
1430  
1431 #if 0
1432   if ( __map_types[vml->maptype].drawmode == vik_viewport_get_drawmode ( vvp ) )
1433   {
1434     VikCoord coord;
1435     MapCoord mapcoord;
1436     vik_viewport_screen_to_coord ( vvp, event->x, event->y, &coord );
1437     if ( __map_types[vml->maptype].coord_to_mapcoord ( &coord,
1438                 vml->xmapzoom ? vml->xmapzoom : vik_viewport_get_xmpp ( vvp ),
1439                 vml->ymapzoom ? vml->ymapzoom : vik_viewport_get_ympp ( vvp ),
1440                 &mapcoord ) ) {
1441       gchar *filename_buf = g_strdup_printf ( DIRSTRUCTURE,
1442                      vml->cache_dir, __map_types[vml->maptype].uniq_id,
1443                      mapcoord.scale, mapcoord.z, mapcoord.x, mapcoord.y );
1444
1445       __map_types[vml->maptype].download ( &mapcoord, filename_buf );
1446       g_free ( filename_buf );
1447       vik_layer_emit_update ( VIK_LAYER(vml) );
1448       return TRUE;
1449     }
1450   }
1451   return FALSE;
1452 #endif
1453 }
1454
1455 static void download_onscreen_maps ( gpointer vml_vvp[2], gint redownload )
1456 {
1457   VikMapsLayer *vml = vml_vvp[0];
1458   VikViewport *vvp = vml_vvp[1];
1459   VikViewportDrawMode vp_drawmode = vik_viewport_get_drawmode ( vvp );
1460
1461   gdouble xzoom = vml->xmapzoom ? vml->xmapzoom : vik_viewport_get_xmpp ( vvp );
1462   gdouble yzoom = vml->ymapzoom ? vml->ymapzoom : vik_viewport_get_ympp ( vvp );
1463
1464   VikCoord ul, br;
1465   MapCoord ulm, brm;
1466
1467   vik_viewport_screen_to_coord ( vvp, 0, 0, &ul );
1468   vik_viewport_screen_to_coord ( vvp, vik_viewport_get_width(vvp), vik_viewport_get_height(vvp), &br );
1469
1470   VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
1471   if ( vik_map_source_get_drawmode(map) == vp_drawmode &&
1472        vik_map_source_coord_to_mapcoord ( map, &ul, xzoom, yzoom, &ulm ) &&
1473        vik_map_source_coord_to_mapcoord ( map, &br, xzoom, yzoom, &brm ) )
1474     start_download_thread ( vml, vvp, &ul, &br, redownload );
1475   else if (vik_map_source_get_drawmode(map) != vp_drawmode) {
1476     const gchar *drawmode_name = vik_viewport_get_drawmode_name (vvp, vik_map_source_get_drawmode(map));
1477     gchar *err = g_strdup_printf(_("Wrong drawmode for this map.\nSelect \"%s\" from View menu and try again."), _(drawmode_name));
1478     a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vml), err );
1479     g_free(err);
1480   }
1481   else
1482     a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vml), _("Wrong zoom level for this map.") );
1483
1484 }
1485
1486 static void maps_layer_download_missing_onscreen_maps ( gpointer vml_vvp[2] )
1487 {
1488   download_onscreen_maps( vml_vvp, REDOWNLOAD_NONE);
1489 }
1490
1491 static void maps_layer_download_new_onscreen_maps ( gpointer vml_vvp[2] )
1492 {
1493   download_onscreen_maps( vml_vvp, REDOWNLOAD_NEW);
1494 }
1495
1496 static void maps_layer_redownload_all_onscreen_maps ( gpointer vml_vvp[2] )
1497 {
1498   download_onscreen_maps( vml_vvp, REDOWNLOAD_ALL);
1499 }
1500
1501 static void maps_layer_add_menu_items ( VikMapsLayer *vml, GtkMenu *menu, VikLayersPanel *vlp )
1502 {
1503   static gpointer pass_along[2];
1504   GtkWidget *item;
1505   pass_along[0] = vml;
1506   pass_along[1] = vik_layers_panel_get_viewport( VIK_LAYERS_PANEL(vlp) );
1507
1508   item = gtk_menu_item_new();
1509   gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
1510   gtk_widget_show ( item );
1511
1512   /* Now with icons */
1513   item = gtk_image_menu_item_new_with_mnemonic ( _("Download _Missing Onscreen Maps") );
1514     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
1515   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_download_missing_onscreen_maps), pass_along );
1516   gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1517   gtk_widget_show ( item );
1518
1519   if ( vik_map_source_supports_download_only_new (MAPS_LAYER_NTH_TYPE(vml->maptype)) ) {
1520     item = gtk_image_menu_item_new_with_mnemonic ( _("Download _New Onscreen Maps") );
1521     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REDO, GTK_ICON_SIZE_MENU) );
1522     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_download_new_onscreen_maps), pass_along );
1523     gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1524     gtk_widget_show ( item );
1525   }
1526
1527   item = gtk_image_menu_item_new_with_mnemonic ( _("Reload _All Onscreen Maps") );
1528   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
1529   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_redownload_all_onscreen_maps), pass_along );
1530   gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1531   gtk_widget_show ( item );
1532 }
1533
1534 /**
1535  * Enable downloading maps of the current screen area either 'new' or 'everything'
1536  */
1537 void vik_maps_layer_download ( VikMapsLayer *vml, VikViewport *vvp, gboolean only_new )
1538 {
1539   if ( !vml ) return;
1540   if ( !vvp ) return;
1541
1542   static gpointer pass_along[2];
1543   pass_along[0] = vml;
1544   pass_along[1] = vvp;
1545
1546   if ( only_new )
1547     // Get only new maps
1548     maps_layer_download_new_onscreen_maps ( pass_along );
1549   else
1550     // Redownload everything
1551     maps_layer_redownload_all_onscreen_maps ( pass_along );
1552 }