]> git.street.me.uk Git - andy/viking.git/blob - src/vikmapslayer.c
Merge branch 'MapnikRenderingLayer'
[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  * Copyright (c) 2013, Rob Norris <rw_norris@hotmail.com>
7  * UTM multi-zone stuff by Kit Transue <notlostyet@didactek.com>
8  * Dynamic map type by Guilhem Bonnefille <guilhem.bonnefille@gmail.com>
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
23  *
24  */
25
26 #ifdef HAVE_CONFIG_H
27 #include "config.h"
28 #endif
29
30 #include <gtk/gtk.h>
31 #include <gdk-pixbuf/gdk-pixdata.h>
32 #include <glib.h>
33 #include <glib/gstdio.h>
34 #include <glib/gi18n.h>
35
36 #ifdef HAVE_STRING_H
37 #include <string.h>
38 #endif
39 #ifdef HAVE_MATH_H
40 #include <math.h>
41 #endif
42
43 #ifdef HAVE_UNISTD_H
44 #include <unistd.h>
45 #endif
46
47 #include "viking.h"
48 #include "vikmapsourcedefault.h"
49 #include "vikutils.h"
50 #include "maputils.h"
51 #include "mapcache.h"
52 #include "background.h"
53 #include "preferences.h"
54 #include "vikmapslayer.h"
55 #include "icons/icons.h"
56 #include "metatile.h"
57 #include "ui_util.h"
58 #include "map_ids.h"
59
60 #ifdef HAVE_SQLITE3_H
61 #include "sqlite3.h"
62 #include <gio/gio.h>
63 #endif
64
65 #define VIK_SETTINGS_MAP_MAX_TILES "maps_max_tiles"
66 static gint MAX_TILES = 1000;
67
68 #define VIK_SETTINGS_MAP_MIN_SHRINKFACTOR "maps_min_shrinkfactor"
69 #define VIK_SETTINGS_MAP_MAX_SHRINKFACTOR "maps_max_shrinkfactor"
70 static gdouble MAX_SHRINKFACTOR = 8.0000001; /* zoom 1 viewing 8-tiles */
71 static gdouble MIN_SHRINKFACTOR = 0.0312499; /* zoom 32 viewing 1-tiles */
72
73 #define VIK_SETTINGS_MAP_REAL_MIN_SHRINKFACTOR "maps_real_min_shrinkfactor"
74 static gdouble REAL_MIN_SHRINKFACTOR = 0.0039062499; /* if shrinkfactor is between MAX and REAL_MAX, will only check for existence */
75
76 #define VIK_SETTINGS_MAP_SCALE_INC_UP "maps_scale_inc_up"
77 static guint SCALE_INC_UP = 2;
78 #define VIK_SETTINGS_MAP_SCALE_INC_DOWN "maps_scale_inc_down"
79 static guint SCALE_INC_DOWN = 4;
80 #define VIK_SETTINGS_MAP_SCALE_SMALLER_ZOOM_FIRST "maps_scale_smaller_zoom_first"
81 static gboolean SCALE_SMALLER_ZOOM_FIRST = TRUE;
82
83 /****** MAP TYPES ******/
84
85 static GList *__map_types = NULL;
86
87 #define NUM_MAP_TYPES g_list_length(__map_types)
88
89 /* List of label for each map type */
90 static gchar **params_maptypes = NULL;
91
92 /* Corresponding IDS. (Cf. field uniq_id in VikMapsLayer struct) */
93 static guint *params_maptypes_ids = NULL;
94
95 /******** MAPZOOMS *********/
96
97 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 };
98 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 };
99 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 };
100
101 #define NUM_MAPZOOMS (sizeof(params_mapzooms)/sizeof(params_mapzooms[0]) - 1)
102
103 /**************************/
104
105
106 static void maps_layer_post_read (VikLayer *vl, VikViewport *vp, gboolean from_file);
107 static const gchar* maps_layer_tooltip ( VikMapsLayer *vml );
108 static void maps_layer_marshall( VikMapsLayer *vml, guint8 **data, gint *len );
109 static VikMapsLayer *maps_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp );
110 static gboolean maps_layer_set_param ( VikMapsLayer *vml, guint16 id, VikLayerParamData data, VikViewport *vvp, gboolean is_file_operation );
111 static VikLayerParamData maps_layer_get_param ( VikMapsLayer *vml, guint16 id, gboolean is_file_operation );
112 static void maps_layer_change_param ( GtkWidget *widget, ui_change_values values );
113 static void maps_layer_draw ( VikMapsLayer *vml, VikViewport *vvp );
114 static VikMapsLayer *maps_layer_new ( VikViewport *vvp );
115 static void maps_layer_free ( VikMapsLayer *vml );
116 static gboolean maps_layer_download_release ( VikMapsLayer *vml, GdkEventButton *event, VikViewport *vvp );
117 static gboolean maps_layer_download_click ( VikMapsLayer *vml, GdkEventButton *event, VikViewport *vvp );
118 static gpointer maps_layer_download_create ( VikWindow *vw, VikViewport *vvp );
119 static void maps_layer_set_cache_dir ( VikMapsLayer *vml, const gchar *dir );
120 static void start_download_thread ( VikMapsLayer *vml, VikViewport *vvp, const VikCoord *ul, const VikCoord *br, gint redownload );
121 static void maps_layer_add_menu_items ( VikMapsLayer *vml, GtkMenu *menu, VikLayersPanel *vlp );
122 static guint map_uniq_id_to_index ( guint uniq_id );
123
124
125 static VikLayerParamScale params_scales[] = {
126   /* min, max, step, digits (decimal places) */
127  { 0, 255, 3, 0 }, /* alpha */
128 };
129
130 static VikLayerParamData id_default ( void ) { return VIK_LPD_UINT ( MAP_ID_MAPQUEST_OSM ); }
131 static VikLayerParamData directory_default ( void )
132 {
133   VikLayerParamData data;
134   VikLayerParamData *pref = a_preferences_get(VIKING_PREFERENCES_NAMESPACE "maplayer_default_dir");
135   if (pref) data.s = g_strdup ( pref->s ); else data.s = "";
136   return data;
137 }
138 static VikLayerParamData file_default ( void )
139 {
140   VikLayerParamData data;
141   data.s = "";
142   return data;
143 }
144 static VikLayerParamData alpha_default ( void ) { return VIK_LPD_UINT ( 255 ); }
145 static VikLayerParamData mapzoom_default ( void ) { return VIK_LPD_UINT ( 0 ); }
146
147 static gchar *cache_types[] = { "Viking", N_("OSM"), NULL };
148 static VikMapsCacheLayout cache_layout_default_value = VIK_MAPS_CACHE_LAYOUT_VIKING;
149 static VikLayerParamData cache_layout_default ( void ) { return VIK_LPD_UINT ( cache_layout_default_value ); }
150
151 VikLayerParam maps_layer_params[] = {
152   // NB mode => id - But can't break file format just to rename something better
153   { VIK_LAYER_MAPS, "mode", VIK_LAYER_PARAM_UINT, VIK_LAYER_GROUP_NONE, N_("Map Type:"), VIK_LAYER_WIDGET_COMBOBOX, NULL, NULL, NULL, id_default, NULL, NULL },
154   { VIK_LAYER_MAPS, "directory", VIK_LAYER_PARAM_STRING, VIK_LAYER_GROUP_NONE, N_("Maps Directory:"), VIK_LAYER_WIDGET_FOLDERENTRY, NULL, NULL, NULL, directory_default, NULL, NULL },
155   { VIK_LAYER_MAPS, "cache_type", VIK_LAYER_PARAM_UINT, VIK_LAYER_GROUP_NONE, N_("Cache Layout:"), VIK_LAYER_WIDGET_COMBOBOX, cache_types, NULL, 
156     N_("This determines the tile storage layout on disk"), cache_layout_default, NULL, NULL },
157   { VIK_LAYER_MAPS, "mapfile", VIK_LAYER_PARAM_STRING, VIK_LAYER_GROUP_NONE, N_("Map File:"), VIK_LAYER_WIDGET_FILEENTRY, GINT_TO_POINTER(VF_FILTER_MBTILES), NULL,
158     N_("An MBTiles file. Only applies when the map type method is 'MBTiles'"), file_default, NULL, NULL },
159   { VIK_LAYER_MAPS, "alpha", VIK_LAYER_PARAM_UINT, VIK_LAYER_GROUP_NONE, N_("Alpha:"), VIK_LAYER_WIDGET_HSCALE, params_scales, NULL,
160     N_("Control the Alpha value for transparency effects"), alpha_default, NULL, NULL },
161   { 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, NULL, NULL },
162   { VIK_LAYER_MAPS, "adlonlymissing", VIK_LAYER_PARAM_BOOLEAN, VIK_LAYER_GROUP_NONE, N_("Autodownload Only Gets Missing Maps:"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL,
163     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, NULL, NULL },
164   { VIK_LAYER_MAPS, "mapzoom", VIK_LAYER_PARAM_UINT, VIK_LAYER_GROUP_NONE, N_("Zoom Level:"), VIK_LAYER_WIDGET_COMBOBOX, params_mapzooms, NULL,
165     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."),
166     mapzoom_default, NULL, NULL },
167 };
168
169 enum {
170   PARAM_MAPTYPE=0,
171   PARAM_CACHE_DIR,
172   PARAM_CACHE_LAYOUT,
173   PARAM_FILE,
174   PARAM_ALPHA,
175   PARAM_AUTODOWNLOAD,
176   PARAM_ONLYMISSING,
177   PARAM_MAPZOOM,
178   NUM_PARAMS
179 };
180
181 void maps_layer_set_autodownload_default ( gboolean autodownload )
182 {
183   // Set appropriate function
184   if ( autodownload )
185     maps_layer_params[PARAM_AUTODOWNLOAD].default_value = vik_lpd_true_default;
186   else
187     maps_layer_params[PARAM_AUTODOWNLOAD].default_value = vik_lpd_false_default;
188 }
189
190 void maps_layer_set_cache_default ( VikMapsCacheLayout layout )
191 {
192   // Override default value returned by the default param function
193   cache_layout_default_value = layout;
194 }
195
196 static VikToolInterface maps_tools[] = {
197   { { "MapsDownload", "vik-icon-Maps Download", N_("_Maps Download"), NULL, N_("Maps Download"), 0 },
198     (VikToolConstructorFunc) maps_layer_download_create,
199     NULL,
200     NULL,
201     NULL,
202     (VikToolMouseFunc) maps_layer_download_click,
203     NULL,
204     (VikToolMouseFunc) maps_layer_download_release,
205     NULL,
206     FALSE,
207     GDK_CURSOR_IS_PIXMAP, &cursor_mapdl_pixbuf, NULL },
208 };
209
210 VikLayerInterface vik_maps_layer_interface = {
211   "Map",
212   N_("Map"),
213   "<control><shift>M",
214   &vikmapslayer_pixbuf,
215
216   maps_tools,
217   sizeof(maps_tools) / sizeof(maps_tools[0]),
218
219   maps_layer_params,
220   NUM_PARAMS,
221   NULL,
222   0,
223
224   VIK_MENU_ITEM_ALL,
225
226   (VikLayerFuncCreate)                  maps_layer_new,
227   (VikLayerFuncRealize)                 NULL,
228   (VikLayerFuncPostRead)                maps_layer_post_read,
229   (VikLayerFuncFree)                    maps_layer_free,
230
231   (VikLayerFuncProperties)              NULL,
232   (VikLayerFuncDraw)                    maps_layer_draw,
233   (VikLayerFuncChangeCoordMode)         NULL,
234
235   (VikLayerFuncSetMenuItemsSelection)   NULL,
236   (VikLayerFuncGetMenuItemsSelection)   NULL,
237
238   (VikLayerFuncAddMenuItems)            maps_layer_add_menu_items,
239   (VikLayerFuncSublayerAddMenuItems)    NULL,
240
241   (VikLayerFuncSublayerRenameRequest)   NULL,
242   (VikLayerFuncSublayerToggleVisible)   NULL,
243   (VikLayerFuncSublayerTooltip)         NULL,
244   (VikLayerFuncLayerTooltip)            maps_layer_tooltip,
245   (VikLayerFuncLayerSelected)           NULL,
246
247   (VikLayerFuncMarshall)                maps_layer_marshall,
248   (VikLayerFuncUnmarshall)              maps_layer_unmarshall,
249
250   (VikLayerFuncSetParam)                maps_layer_set_param,
251   (VikLayerFuncGetParam)                maps_layer_get_param,
252   (VikLayerFuncChangeParam)             maps_layer_change_param,
253
254   (VikLayerFuncReadFileData)            NULL,
255   (VikLayerFuncWriteFileData)           NULL,
256
257   (VikLayerFuncDeleteItem)              NULL,
258   (VikLayerFuncCutItem)                 NULL,
259   (VikLayerFuncCopyItem)                NULL,
260   (VikLayerFuncPasteItem)               NULL,
261   (VikLayerFuncFreeCopiedItem)          NULL,
262   (VikLayerFuncDragDropRequest)         NULL,
263
264   (VikLayerFuncSelectClick)             NULL,
265   (VikLayerFuncSelectMove)              NULL,
266   (VikLayerFuncSelectRelease)           NULL,
267   (VikLayerFuncSelectedViewportMenu)    NULL,
268 };
269
270 struct _VikMapsLayer {
271   VikLayer vl;
272   guint maptype;
273   gchar *cache_dir;
274   VikMapsCacheLayout cache_layout;
275   guint8 alpha;
276   guint mapzoom_id;
277   gdouble xmapzoom, ymapzoom;
278
279   gboolean autodownload;
280   gboolean adl_only_missing;
281   VikCoord *last_center;
282   gdouble last_xmpp;
283   gdouble last_ympp;
284
285   gint dl_tool_x, dl_tool_y;
286
287   GtkMenu *dl_right_click_menu;
288   VikCoord redownload_ul, redownload_br; /* right click menu only */
289   VikViewport *redownload_vvp;
290   gchar *filename;
291 #ifdef HAVE_SQLITE3_H
292   sqlite3 *mbtiles;
293 #endif
294 };
295
296 enum { REDOWNLOAD_NONE = 0,    /* download only missing maps */
297        REDOWNLOAD_BAD,         /* download missing and bad maps */
298        REDOWNLOAD_NEW,         /* download missing maps that are newer on server only */
299        REDOWNLOAD_ALL,         /* download all maps */
300        DOWNLOAD_OR_REFRESH };  /* download missing maps and refresh cache */
301
302 static VikLayerParam prefs[] = {
303   { 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"), NULL, NULL, NULL },
304 };
305
306 void maps_layer_init ()
307 {
308   VikLayerParamData tmp;
309   tmp.s = maps_layer_default_dir();
310   a_preferences_register(prefs, tmp, VIKING_PREFERENCES_GROUP_KEY);
311
312   gint max_tiles = MAX_TILES;
313   if ( a_settings_get_integer ( VIK_SETTINGS_MAP_MAX_TILES, &max_tiles ) )
314     MAX_TILES = max_tiles;
315
316   gdouble gdtmp;
317   if ( a_settings_get_double ( VIK_SETTINGS_MAP_MIN_SHRINKFACTOR, &gdtmp ) )
318     MIN_SHRINKFACTOR = gdtmp;
319
320   if ( a_settings_get_double ( VIK_SETTINGS_MAP_MAX_SHRINKFACTOR, &gdtmp ) )
321     MAX_SHRINKFACTOR = gdtmp;
322
323   if ( a_settings_get_double ( VIK_SETTINGS_MAP_REAL_MIN_SHRINKFACTOR, &gdtmp ) )
324     REAL_MIN_SHRINKFACTOR = gdtmp;
325
326   gint gitmp = 0;
327   if ( a_settings_get_integer ( VIK_SETTINGS_MAP_SCALE_INC_UP, &gitmp ) )
328     SCALE_INC_UP = gitmp;
329
330   if ( a_settings_get_integer ( VIK_SETTINGS_MAP_SCALE_INC_DOWN, &gitmp ) )
331     SCALE_INC_DOWN = gitmp;
332
333   gboolean gbtmp = TRUE;
334   if ( a_settings_get_boolean ( VIK_SETTINGS_MAP_SCALE_SMALLER_ZOOM_FIRST, &gbtmp ) )
335     SCALE_SMALLER_ZOOM_FIRST = gbtmp;
336
337 }
338
339 /****************************************/
340 /******** MAPS LAYER TYPES **************/
341 /****************************************/
342
343 void _add_map_source ( guint16 id, const char *label, VikMapSource *map )
344 {
345   gsize len = 0;
346   if (params_maptypes)
347     len = g_strv_length (params_maptypes);
348   /* Add the label */
349   params_maptypes = g_realloc (params_maptypes, (len+2)*sizeof(gchar*));
350   params_maptypes[len] = g_strdup (label);
351   params_maptypes[len+1] = NULL;
352
353   /* Add the id */
354   params_maptypes_ids = g_realloc (params_maptypes_ids, (len+2)*sizeof(guint));
355   params_maptypes_ids[len] = id;
356   params_maptypes_ids[len+1] = 0;
357
358   /* We have to clone */
359   VikMapSource *clone = VIK_MAP_SOURCE(g_object_ref(map));
360   /* Register the clone in the list */
361   __map_types = g_list_append(__map_types, clone);
362
363   /* Hack
364      We have to ensure the mode LayerParam references the up-to-date
365      GLists.
366   */
367   /*
368   memcpy(&maps_layer_params[0].widget_data, &params_maptypes, sizeof(gpointer));
369   memcpy(&maps_layer_params[0].extra_widget_data, &params_maptypes_ids, sizeof(gpointer));
370   */
371   maps_layer_params[0].widget_data = params_maptypes;
372   maps_layer_params[0].extra_widget_data = params_maptypes_ids;
373 }
374
375 void _update_map_source ( const char *label, VikMapSource *map, int index )
376 {
377   GList *item = g_list_nth (__map_types, index);
378   g_object_unref (item->data);
379   item->data = g_object_ref (map);
380   /* Change previous data */
381   g_free (params_maptypes[index]);
382   params_maptypes[index] = g_strdup (label);
383 }
384
385 /**
386  * maps_layer_register_map_source:
387  * @map: the new VikMapSource
388  *
389  * Register a new VikMapSource.
390  * Override existing one (equality of id).
391  */
392 void maps_layer_register_map_source ( VikMapSource *map )
393 {
394   g_assert(map != NULL);
395   
396   guint16 id = vik_map_source_get_uniq_id(map);
397   const char *label = vik_map_source_get_label(map);
398   g_assert(label != NULL);
399
400   int previous = map_uniq_id_to_index (id);
401   if (previous != NUM_MAP_TYPES)
402   {
403     _update_map_source (label, map, previous);
404   }
405   else
406   {
407     _add_map_source (id, label, map);
408   }
409 }
410
411 #define MAPS_LAYER_NTH_LABEL(n) (params_maptypes[n])
412 #define MAPS_LAYER_NTH_ID(n) (params_maptypes_ids[n])
413 #define MAPS_LAYER_NTH_TYPE(n) (VIK_MAP_SOURCE(g_list_nth_data(__map_types, (n))))
414
415 /**
416  * vik_maps_layer_get_map_type:
417  *
418  * Returns the actual map id (rather than the internal type index value)
419  */
420 guint vik_maps_layer_get_map_type(VikMapsLayer *vml)
421 {
422   return MAPS_LAYER_NTH_ID(vml->maptype);
423 }
424
425 /**
426  * vik_maps_layer_set_map_type:
427  *
428  */
429 void vik_maps_layer_set_map_type(VikMapsLayer *vml, guint map_type)
430 {
431    gint maptype = map_uniq_id_to_index(map_type);
432    if ( maptype == NUM_MAP_TYPES )
433       g_warning(_("Unknown map type"));
434    else
435       vml->maptype = maptype;
436 }
437
438 /**
439  * vik_maps_layer_get_default_map_type:
440  *
441  */
442 guint vik_maps_layer_get_default_map_type ()
443 {
444   VikLayerInterface *vli = vik_layer_get_interface ( VIK_LAYER_MAPS );
445   VikLayerParamData vlpd = a_layer_defaults_get ( vli->fixed_layer_name, "mode", VIK_LAYER_PARAM_UINT );
446   if ( vlpd.u == 0 )
447     vlpd = id_default();
448   return vlpd.u;
449 }
450
451 gchar *vik_maps_layer_get_map_label(VikMapsLayer *vml)
452 {
453   return(g_strdup(MAPS_LAYER_NTH_LABEL(vml->maptype)));
454 }
455
456 /****************************************/
457 /******** CACHE DIR STUFF ***************/
458 /****************************************/
459
460 #define DIRECTDIRACCESS "%s%d" G_DIR_SEPARATOR_S "%d" G_DIR_SEPARATOR_S "%d%s"
461 #define DIRECTDIRACCESS_WITH_NAME "%s%s" G_DIR_SEPARATOR_S "%d" G_DIR_SEPARATOR_S "%d" G_DIR_SEPARATOR_S "%d%s"
462 #define DIRSTRUCTURE "%st%ds%dz%d" G_DIR_SEPARATOR_S "%d" G_DIR_SEPARATOR_S "%d"
463 #define MAPS_CACHE_DIR maps_layer_default_dir()
464
465 #ifdef WINDOWS
466 #include <io.h>
467 #define GLOBAL_MAPS_DIR "C:\\VIKING-MAPS\\"
468 #define LOCAL_MAPS_DIR "VIKING-MAPS"
469 #elif defined __APPLE__
470 #include <stdlib.h>
471 #define GLOBAL_MAPS_DIR "/Library/cache/Viking/maps/"
472 #define LOCAL_MAPS_DIR "/Library/Application Support/Viking/viking-maps"
473 #else /* POSIX */
474 #include <stdlib.h>
475 #define GLOBAL_MAPS_DIR "/var/cache/maps/"
476 #define LOCAL_MAPS_DIR ".viking-maps"
477 #endif
478
479 gchar *maps_layer_default_dir ()
480 {
481   static gchar *defaultdir = NULL;
482   if ( ! defaultdir )
483   {
484     /* Thanks to Mike Davison for the $VIKING_MAPS usage */
485     const gchar *mapdir = g_getenv("VIKING_MAPS");
486     if ( mapdir ) {
487       defaultdir = g_strdup ( mapdir );
488     } else if ( g_access ( GLOBAL_MAPS_DIR, W_OK ) == 0 ) {
489       defaultdir = g_strdup ( GLOBAL_MAPS_DIR );
490     } else {
491       const gchar *home = g_get_home_dir();
492       if (!home || g_access(home, W_OK))
493         home = g_get_home_dir ();
494       if ( home )
495         defaultdir = g_build_filename ( home, LOCAL_MAPS_DIR, NULL );
496       else
497         defaultdir = g_strdup ( LOCAL_MAPS_DIR );
498     }
499     if (defaultdir && (defaultdir[strlen(defaultdir)-1] != G_DIR_SEPARATOR))
500     {
501       /* Add the separator at the end */
502       gchar *tmp = defaultdir;
503       defaultdir = g_strconcat(tmp, G_DIR_SEPARATOR_S, NULL);
504       g_free(tmp);
505     }
506     g_debug("%s: defaultdir=%s", __FUNCTION__, defaultdir);
507   }
508   return defaultdir;
509 }
510
511 static void maps_layer_mkdir_if_default_dir ( VikMapsLayer *vml )
512 {
513   if ( vml->cache_dir && strcmp ( vml->cache_dir, MAPS_CACHE_DIR ) == 0 && g_file_test ( vml->cache_dir, G_FILE_TEST_EXISTS ) == FALSE )
514   {
515     g_mkdir ( vml->cache_dir, 0777 );
516   }
517 }
518
519 static void maps_layer_set_cache_dir ( VikMapsLayer *vml, const gchar *dir )
520 {
521   g_assert ( vml != NULL);
522   g_free ( vml->cache_dir );
523   vml->cache_dir = NULL;
524   const gchar *mydir = dir;
525
526   if ( dir == NULL || dir[0] == '\0' )
527   {
528     if ( a_preferences_get(VIKING_PREFERENCES_NAMESPACE "maplayer_default_dir") )
529       mydir = a_preferences_get(VIKING_PREFERENCES_NAMESPACE "maplayer_default_dir")->s;
530   }
531
532   gchar *canonical_dir = vu_get_canonical_filename ( VIK_LAYER(vml), mydir );
533
534   // Ensure cache_dir always ends with a separator
535   guint len = strlen(canonical_dir);
536   if ( canonical_dir[len-1] != G_DIR_SEPARATOR )
537   {
538     vml->cache_dir = g_strconcat ( canonical_dir, G_DIR_SEPARATOR_S, NULL );
539     g_free ( canonical_dir );
540   }
541   else {
542     vml->cache_dir = canonical_dir;
543   }
544
545   maps_layer_mkdir_if_default_dir ( vml );
546 }
547
548 static void maps_layer_set_file ( VikMapsLayer *vml, const gchar *name )
549 {
550   if ( vml->filename )
551     g_free (vml->filename);
552   vml->filename = g_strdup (name);
553 }
554
555 /****************************************/
556 /******** GOBJECT STUFF *****************/
557 /****************************************/
558
559 GType vik_maps_layer_get_type ()
560 {
561   static GType vml_type = 0;
562
563   if (!vml_type)
564   {
565     static const GTypeInfo vml_info =
566     {
567       sizeof (VikMapsLayerClass),
568       NULL, /* base_init */
569       NULL, /* base_finalize */
570       NULL, /* class init */
571       NULL, /* class_finalize */
572       NULL, /* class_data */
573       sizeof (VikMapsLayer),
574       0,
575       NULL /* instance init */
576     };
577     vml_type = g_type_register_static ( VIK_LAYER_TYPE, "VikMapsLayer", &vml_info, 0 );
578   }
579
580   return vml_type;
581 }
582
583 /****************************************/
584 /************** PARAMETERS **************/
585 /****************************************/
586
587 static guint map_index_to_uniq_id (guint16 index)
588 {
589   g_assert ( index < NUM_MAP_TYPES );
590   return vik_map_source_get_uniq_id(MAPS_LAYER_NTH_TYPE(index));
591 }
592
593 static guint map_uniq_id_to_index ( guint uniq_id )
594 {
595   gint i;
596   for ( i = 0; i < NUM_MAP_TYPES; i++ )
597     if ( vik_map_source_get_uniq_id(MAPS_LAYER_NTH_TYPE(i)) == uniq_id )
598       return i;
599   return NUM_MAP_TYPES; /* no such thing */
600 }
601
602 #define VIK_SETTINGS_MAP_LICENSE_SHOWN "map_license_shown"
603
604 /**
605  * Convenience function to display the license
606  */
607 static void maps_show_license ( GtkWindow *parent, VikMapSource *map )
608 {
609   a_dialog_license ( parent,
610                      vik_map_source_get_label (map),
611                      vik_map_source_get_license (map),
612                      vik_map_source_get_license_url (map) );
613 }
614
615 static gboolean maps_layer_set_param ( VikMapsLayer *vml, guint16 id, VikLayerParamData data, VikViewport *vvp, gboolean is_file_operation )
616 {
617   switch ( id )
618   {
619     case PARAM_CACHE_DIR: maps_layer_set_cache_dir ( vml, data.s ); break;
620     case PARAM_CACHE_LAYOUT: if ( data.u < VIK_MAPS_CACHE_LAYOUT_NUM ) vml->cache_layout = data.u; break;
621     case PARAM_FILE: maps_layer_set_file ( vml, data.s ); break;
622     case PARAM_MAPTYPE: {
623       gint maptype = map_uniq_id_to_index(data.u);
624       if ( maptype == NUM_MAP_TYPES )
625         g_warning(_("Unknown map type"));
626       else {
627         vml->maptype = maptype;
628
629         // When loading from a file don't need the license reminder - ensure it's saved into the 'seen' list
630         if ( is_file_operation ) {
631           a_settings_set_integer_list_containing ( VIK_SETTINGS_MAP_LICENSE_SHOWN, data.u );
632         }
633         else {
634           VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
635           if (vik_map_source_get_license (map) != NULL) {
636             // Check if licence for this map type has been shown before
637             if ( ! a_settings_get_integer_list_contains ( VIK_SETTINGS_MAP_LICENSE_SHOWN, data.u ) ) {
638               if ( vvp )
639                 maps_show_license ( VIK_GTK_WINDOW_FROM_WIDGET(vvp), map );
640               a_settings_set_integer_list_containing ( VIK_SETTINGS_MAP_LICENSE_SHOWN, data.u );
641             }
642           }
643         }
644       }
645       break;
646     }
647     case PARAM_ALPHA: if ( data.u <= 255 ) vml->alpha = data.u; break;
648     case PARAM_AUTODOWNLOAD: vml->autodownload = data.b; break;
649     case PARAM_ONLYMISSING: vml->adl_only_missing = data.b; break;
650     case PARAM_MAPZOOM: if ( data.u < NUM_MAPZOOMS ) {
651                           vml->mapzoom_id = data.u;
652                           vml->xmapzoom = __mapzooms_x [data.u];
653                           vml->ymapzoom = __mapzooms_y [data.u];
654                         }else g_warning (_("Unknown Map Zoom")); break;
655     default: break;
656   }
657   return TRUE;
658 }
659
660 static VikLayerParamData maps_layer_get_param ( VikMapsLayer *vml, guint16 id, gboolean is_file_operation )
661 {
662   VikLayerParamData rv;
663   switch ( id )
664   {
665     case PARAM_CACHE_DIR:
666     {
667       gboolean set = FALSE;
668       /* Only save a blank when the map cache location equals the default
669           On reading in, when it is blank then the default is reconstructed
670           Since the default changes dependent on the user and OS, it means the resultant file is more portable */
671       if ( is_file_operation && vml->cache_dir && strcmp ( vml->cache_dir, MAPS_CACHE_DIR ) == 0 ) {
672         rv.s = "";
673         set = TRUE;
674       }
675       else if ( is_file_operation ) {
676         if ( a_vik_get_file_ref_format() == VIK_FILE_REF_FORMAT_RELATIVE ) {
677           gchar *cwd = g_get_current_dir();
678           if ( cwd ) {
679             rv.s = file_GetRelativeFilename ( cwd, vml->cache_dir );
680             if ( !rv.s ) rv.s = "";
681             set = TRUE;
682           }
683         }
684       }
685       if ( !set )
686         rv.s = vml->cache_dir ? vml->cache_dir : "";
687       break;
688     }
689     case PARAM_CACHE_LAYOUT: rv.u = vml->cache_layout; break;
690     case PARAM_FILE: rv.s = vml->filename; break;
691     case PARAM_MAPTYPE: rv.u = map_index_to_uniq_id ( vml->maptype ); break;
692     case PARAM_ALPHA: rv.u = vml->alpha; break;
693     case PARAM_AUTODOWNLOAD: rv.u = vml->autodownload; break;
694     case PARAM_ONLYMISSING: rv.u = vml->adl_only_missing; break;
695     case PARAM_MAPZOOM: rv.u = vml->mapzoom_id; break;
696     default: break;
697   }
698   return rv;
699 }
700
701 static void maps_layer_change_param ( GtkWidget *widget, ui_change_values values )
702 {
703   switch ( GPOINTER_TO_INT(values[UI_CHG_PARAM_ID]) ) {
704     // Alter sensitivity of download option widgets according to the maptype setting.
705     case PARAM_MAPTYPE: {
706       // Get new value
707       VikLayerParamData vlpd = a_uibuilder_widget_get_value ( widget, values[UI_CHG_PARAM] );
708       // Is it *not* the OSM On Disk Tile Layout or the MBTiles type or the OSM Metatiles type
709       gboolean sensitive = ( 21 != vlpd.u && 23 != vlpd.u && 24 != vlpd.u );
710       GtkWidget **ww1 = values[UI_CHG_WIDGETS];
711       GtkWidget **ww2 = values[UI_CHG_LABELS];
712       GtkWidget *w1 = ww1[PARAM_ONLYMISSING];
713       GtkWidget *w2 = ww2[PARAM_ONLYMISSING];
714       GtkWidget *w3 = ww1[PARAM_AUTODOWNLOAD];
715       GtkWidget *w4 = ww2[PARAM_AUTODOWNLOAD];
716       // Depends on autodownload value
717       gboolean missing_sense = sensitive && VIK_MAPS_LAYER(values[UI_CHG_LAYER])->autodownload;
718       if ( w1 ) gtk_widget_set_sensitive ( w1, missing_sense );
719       if ( w2 ) gtk_widget_set_sensitive ( w2, missing_sense );
720       if ( w3 ) gtk_widget_set_sensitive ( w3, sensitive );
721       if ( w4 ) gtk_widget_set_sensitive ( w4, sensitive );
722
723       // Cache type not applicable either
724       GtkWidget *w9 = ww1[PARAM_CACHE_LAYOUT];
725       GtkWidget *w10 = ww2[PARAM_CACHE_LAYOUT];
726       if ( w9 ) gtk_widget_set_sensitive ( w9, sensitive );
727       if ( w10 ) gtk_widget_set_sensitive ( w10, sensitive );
728
729       // File only applicable for MBTiles type
730       // Directory for all other types
731       sensitive = ( 23 == vlpd.u);
732       GtkWidget *w5 = ww1[PARAM_FILE];
733       GtkWidget *w6 = ww2[PARAM_FILE];
734       GtkWidget *w7 = ww1[PARAM_CACHE_DIR];
735       GtkWidget *w8 = ww2[PARAM_CACHE_DIR];
736       if ( w5 ) gtk_widget_set_sensitive ( w5, sensitive );
737       if ( w6 ) gtk_widget_set_sensitive ( w6, sensitive );
738       if ( w7 ) gtk_widget_set_sensitive ( w7, !sensitive );
739       if ( w8 ) gtk_widget_set_sensitive ( w8, !sensitive );
740
741       break;
742     }
743
744     // Alter sensitivity of 'download only missing' widgets according to the autodownload setting.
745     case PARAM_AUTODOWNLOAD: {
746       // Get new value
747       VikLayerParamData vlpd = a_uibuilder_widget_get_value ( widget, values[UI_CHG_PARAM] );
748       GtkWidget **ww1 = values[UI_CHG_WIDGETS];
749       GtkWidget **ww2 = values[UI_CHG_LABELS];
750       GtkWidget *w1 = ww1[PARAM_ONLYMISSING];
751       GtkWidget *w2 = ww2[PARAM_ONLYMISSING];
752       if ( w1 ) gtk_widget_set_sensitive ( w1, vlpd.b );
753       if ( w2 ) gtk_widget_set_sensitive ( w2, vlpd.b );
754       break;
755     }
756     default: break;
757   }
758 }
759
760 /****************************************/
761 /****** CREATING, COPYING, FREEING ******/
762 /****************************************/
763
764 static VikMapsLayer *maps_layer_new ( VikViewport *vvp )
765 {
766   VikMapsLayer *vml = VIK_MAPS_LAYER ( g_object_new ( VIK_MAPS_LAYER_TYPE, NULL ) );
767   vik_layer_set_type ( VIK_LAYER(vml), VIK_LAYER_MAPS );
768
769   vik_layer_set_defaults ( VIK_LAYER(vml), vvp );
770
771   vml->dl_tool_x = vml->dl_tool_y = -1;
772   vml->last_center = NULL;
773   vml->last_xmpp = 0.0;
774   vml->last_ympp = 0.0;
775
776   vml->dl_right_click_menu = NULL;
777   vml->filename = NULL;
778   return vml;
779 }
780
781 static void maps_layer_free ( VikMapsLayer *vml )
782 {
783   g_free ( vml->cache_dir );
784   vml->cache_dir = NULL;
785   if ( vml->dl_right_click_menu )
786     g_object_ref_sink ( G_OBJECT(vml->dl_right_click_menu) );
787   g_free(vml->last_center);
788   vml->last_center = NULL;
789   g_free ( vml->filename );
790   vml->filename = NULL;
791
792 #ifdef HAVE_SQLITE3_H
793   VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
794   if ( vik_map_source_is_mbtiles ( map ) ) {
795     if ( vml->mbtiles ) {
796       int ans = sqlite3_close ( vml->mbtiles );
797       if ( ans != SQLITE_OK ) {
798         // Only to console for information purposes only
799         g_warning ( "SQL Close problem: %d", ans );
800       }
801     }
802   }
803 #endif
804 }
805
806 static void maps_layer_post_read (VikLayer *vl, VikViewport *vp, gboolean from_file)
807 {
808   VikMapsLayer *vml = VIK_MAPS_LAYER(vl);
809   VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
810
811   if (!from_file)
812   {
813     /* If this method is not called in file reading context
814      * it is called in GUI context.
815      * So, we can check if we have to inform the user about inconsistency */
816     VikViewportDrawMode vp_drawmode;
817     vp_drawmode = vik_viewport_get_drawmode ( vp );
818
819     if (vik_map_source_get_drawmode(map) != vp_drawmode) {
820       const gchar *drawmode_name = vik_viewport_get_drawmode_name (vp, vik_map_source_get_drawmode(map));
821       gchar *msg = g_strdup_printf(_("New map cannot be displayed in the current drawmode.\nSelect \"%s\" from View menu to view it."), drawmode_name);
822       a_dialog_warning_msg ( VIK_GTK_WINDOW_FROM_WIDGET(vp), msg );
823       g_free(msg);
824     }
825   }
826   
827   // Performed in post read as we now know the map type
828 #ifdef HAVE_SQLITE3_H
829   // Do some SQL stuff
830   if ( vik_map_source_is_mbtiles ( map ) ) {
831     int ans = sqlite3_open_v2 ( vml->filename,
832                                 &(vml->mbtiles),
833                                 SQLITE_OPEN_READONLY,
834                                 NULL );
835     if ( ans != SQLITE_OK ) {
836       // That didn't work, so here's why:
837       g_warning ( "%s: %s", __FUNCTION__, sqlite3_errmsg ( vml->mbtiles ) );
838
839       a_dialog_error_msg_extra ( VIK_GTK_WINDOW_FROM_WIDGET(vp),
840                                  _("Failed to open MBTiles file: %s"),
841                                  vml->filename );
842       vml->mbtiles = NULL;
843     }
844   }
845 #endif
846
847   // If the on Disk OSM Tile Layout type
848   if ( vml->maptype == 21 )
849     // Copy the directory into filename
850     //  thus the mapcache look up will be unique when using more than one of these map types
851     vml->filename = g_strdup (vml->cache_dir);
852 }
853
854 static const gchar* maps_layer_tooltip ( VikMapsLayer *vml )
855 {
856   return vik_maps_layer_get_map_label ( vml );
857 }
858
859 static void maps_layer_marshall( VikMapsLayer *vml, guint8 **data, gint *len )
860 {
861   vik_layer_marshall_params ( VIK_LAYER(vml), data, len );
862 }
863
864 static VikMapsLayer *maps_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp )
865 {
866   VikMapsLayer *rv = maps_layer_new ( vvp );
867   vik_layer_unmarshall_params ( VIK_LAYER(rv), data, len, vvp );
868   maps_layer_post_read ( VIK_LAYER(rv), vvp, FALSE );
869   return rv;
870 }
871
872 /*********************/
873 /****** DRAWING ******/
874 /*********************/
875
876 static GdkPixbuf *pixbuf_shrink ( GdkPixbuf *pixbuf, gdouble xshrinkfactor, gdouble yshrinkfactor )
877 {
878   GdkPixbuf *tmp;
879   guint16 width = gdk_pixbuf_get_width(pixbuf), height = gdk_pixbuf_get_height(pixbuf);
880   tmp = gdk_pixbuf_scale_simple(pixbuf, ceil(width * xshrinkfactor), ceil(height * yshrinkfactor), GDK_INTERP_BILINEAR);
881   g_object_unref ( G_OBJECT(pixbuf) );
882   return tmp;
883 }
884
885 #ifdef HAVE_SQLITE3_H
886 /*
887 static int sql_select_tile_dump_cb (void *data, int cols, char **fields, char **col_names )
888 {
889   g_warning ( "Found %d columns", cols );
890   int i;
891   for ( i = 0; i < cols; i++ ) {
892     g_warning ( "SQL processing %s = %s", col_names[i], fields[i] );
893   }
894   return 0;
895 }
896 */
897
898 /**
899  *
900  */
901 static GdkPixbuf *get_pixbuf_sql_exec ( sqlite3 *sql, gint xx, gint yy, gint zoom )
902 {
903   GdkPixbuf *pixbuf = NULL;
904
905   // MBTiles stored internally with the flipping y thingy (i.e. TMS scheme).
906   gint flip_y = (gint) pow(2, zoom)-1 - yy;
907   gchar *statement = g_strdup_printf ( "SELECT tile_data FROM tiles WHERE zoom_level=%d AND tile_column=%d AND tile_row=%d;", zoom, xx, flip_y );
908
909   gboolean finished = FALSE;
910
911   sqlite3_stmt *sql_stmt = NULL;
912   int ans = sqlite3_prepare_v2 ( sql, statement, -1, &sql_stmt, NULL );
913   if ( ans != SQLITE_OK ) {
914     g_warning ( "%s: %s - %d", __FUNCTION__, "prepare failure", ans );
915     finished = TRUE;
916   }
917
918   while ( !finished ) {
919     ans = sqlite3_step ( sql_stmt );
920     switch (ans) {
921       case SQLITE_ROW: {
922         // Get tile_data blob
923         int count = sqlite3_column_count(sql_stmt);
924         if ( count != 1 )  {
925           g_warning ( "%s: %s - %d", __FUNCTION__, "count not one", count );
926           finished = TRUE;
927         }
928         else {
929           const void *data = sqlite3_column_blob ( sql_stmt, 0 );
930           int bytes = sqlite3_column_bytes ( sql_stmt, 0 );
931           if ( bytes < 1 )  {
932             g_warning ( "%s: %s (%d)", __FUNCTION__, "not enough bytes", bytes );
933             finished = TRUE;
934           }
935           else {
936             // Convert these blob bytes into a pixbuf via these streaming operations
937             GInputStream *stream = g_memory_input_stream_new_from_data ( data, bytes, NULL );
938             GError *error = NULL;
939             pixbuf = gdk_pixbuf_new_from_stream ( stream, NULL, &error );
940             if (error || (!pixbuf)) {
941               g_warning ( "%s: %s", __FUNCTION__, error->message );
942               g_error_free ( error );
943             }
944             g_input_stream_close ( stream, NULL, NULL );
945           }
946         }
947         break;
948       }
949       default:
950        // e.g. SQLITE_DONE | SQLITE_ERROR | SQLITE_MISUSE | etc...
951        // Finished normally
952        //  and give up on any errors
953        if ( ans != SQLITE_DONE )
954          g_warning ( "%s: %s - %d", __FUNCTION__, "step issue", ans );
955        finished = TRUE;
956        break;
957     }
958   }
959   ans = sqlite3_finalize ( sql_stmt );
960   
961   g_free ( statement );
962
963   return pixbuf;
964 }
965 #endif
966
967 static GdkPixbuf *get_mbtiles_pixbuf ( VikMapsLayer *vml, gint xx, gint yy, gint zoom )
968 {
969   GdkPixbuf *pixbuf = NULL;
970
971 #ifdef HAVE_SQLITE3_H
972   if ( vml->mbtiles ) {
973     /*
974     gchar *statement = g_strdup_printf ( "SELECT name FROM sqlite_master WHERE type='table';" );
975     char *errMsg = NULL;
976     int ans = sqlite3_exec ( vml->mbtiles, statement, sql_select_tile_dump_cb, pixbuf, &errMsg );
977     if ( ans != SQLITE_OK ) {
978       // Only to console for information purposes only
979       g_warning ( "SQL problem: %d for %s - error: %s", ans, statement, errMsg );
980       sqlite3_free( errMsg );
981     }
982     g_free ( statement );
983     */
984
985     // Reading BLOBS is a bit more involved and so can't use the simpler sqlite3_exec ()
986     // Hence this specific function
987     pixbuf = get_pixbuf_sql_exec ( vml->mbtiles, xx, yy, zoom );
988   }
989 #endif
990
991   return pixbuf;
992 }
993
994 static GdkPixbuf *get_pixbuf_from_metatile ( VikMapsLayer *vml, gint xx, gint yy, gint zz )
995 {
996   const int tile_max = METATILE_MAX_SIZE;
997   char err_msg[PATH_MAX];
998   char *buf;
999   int len;
1000   int compressed;
1001
1002   buf = malloc(tile_max);
1003   if (!buf) {
1004       return NULL;
1005   }
1006
1007   err_msg[0] = 0;
1008   len = metatile_read(vml->cache_dir, xx, yy, zz, buf, tile_max, &compressed, err_msg);
1009
1010   if (len > 0) {
1011     if (compressed) {
1012       // Not handled yet - I don't think this is used often - so implement later if necessary
1013       g_warning ( "Compressed metatiles not implemented:%s\n", __FUNCTION__);
1014       return NULL;
1015     }
1016
1017     // Convert these buf bytes into a pixbuf via these streaming operations
1018     GdkPixbuf *pixbuf = NULL;
1019
1020     GInputStream *stream = g_memory_input_stream_new_from_data ( buf, len, NULL );
1021     GError *error = NULL;
1022     pixbuf = gdk_pixbuf_new_from_stream ( stream, NULL, &error );
1023     if (error || (!pixbuf)) {
1024       g_warning ( "%s: %s", __FUNCTION__, error->message );
1025       g_error_free ( error );
1026     }
1027     g_input_stream_close ( stream, NULL, NULL );
1028
1029     free(buf);
1030     return pixbuf;
1031   }
1032   else {
1033     g_warning ( "FAILED:%s %s", __FUNCTION__, err_msg);
1034     return NULL;
1035   }
1036 }
1037
1038
1039 static GdkPixbuf *pixbuf_apply_settings ( GdkPixbuf *pixbuf, VikMapsLayer *vml, MapCoord *mapcoord, gdouble xshrinkfactor, gdouble yshrinkfactor )
1040 {
1041   // Apply alpha setting
1042   if ( pixbuf && vml->alpha < 255 )
1043     pixbuf = ui_pixbuf_set_alpha ( pixbuf, vml->alpha );
1044
1045   if ( pixbuf && ( xshrinkfactor != 1.0 || yshrinkfactor != 1.0 ) )
1046     pixbuf = pixbuf_shrink ( pixbuf, xshrinkfactor, yshrinkfactor );
1047
1048   if ( pixbuf )
1049     a_mapcache_add ( pixbuf, (mapcache_extra_t) {0.0}, mapcoord->x, mapcoord->y,
1050                      mapcoord->z, vik_map_source_get_uniq_id(MAPS_LAYER_NTH_TYPE(vml->maptype)),
1051                      mapcoord->scale, vml->alpha, xshrinkfactor, yshrinkfactor, vml->filename );
1052
1053   return pixbuf;
1054 }
1055
1056 static void get_filename ( const gchar *cache_dir,
1057                            VikMapsCacheLayout cl,
1058                            guint16 id,
1059                            const gchar *name,
1060                            gint scale,
1061                            gint z,
1062                            gint x,
1063                            gint y,
1064                            gchar *filename_buf,
1065                            gint buf_len,
1066                            const gchar* file_extension )
1067 {
1068   switch ( cl ) {
1069     case VIK_MAPS_CACHE_LAYOUT_OSM:
1070       if ( name ) {
1071         if ( g_strcmp0 ( cache_dir, MAPS_CACHE_DIR ) )
1072           // Cache dir not the default - assume it's been directed somewhere specific
1073           g_snprintf ( filename_buf, buf_len, DIRECTDIRACCESS, cache_dir, (17 - scale), x, y, file_extension );
1074         else
1075           // Using default cache - so use the map name in the directory path
1076           g_snprintf ( filename_buf, buf_len, DIRECTDIRACCESS_WITH_NAME, cache_dir, name, (17 - scale), x, y, file_extension );
1077       }
1078       else
1079         g_snprintf ( filename_buf, buf_len, DIRECTDIRACCESS, cache_dir, (17 - scale), x, y, file_extension );
1080       break;
1081     default:
1082       g_snprintf ( filename_buf, buf_len, DIRSTRUCTURE, cache_dir, id, scale, z, x, y );
1083       break;
1084   }
1085 }
1086
1087 static GdkPixbuf *get_pixbuf( VikMapsLayer *vml, guint16 id, const gchar* mapname, MapCoord *mapcoord, gchar *filename_buf, gint buf_len, gdouble xshrinkfactor, gdouble yshrinkfactor )
1088 {
1089   GdkPixbuf *pixbuf;
1090
1091   /* get the thing */
1092   pixbuf = a_mapcache_get ( mapcoord->x, mapcoord->y, mapcoord->z,
1093                             id, mapcoord->scale, vml->alpha, xshrinkfactor, yshrinkfactor, vml->filename );
1094
1095   if ( ! pixbuf ) {
1096     VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
1097     if ( vik_map_source_is_direct_file_access(map) ) {
1098       // ATM MBTiles must be 'a direct access type'
1099       if ( vik_map_source_is_mbtiles(map) ) {
1100         pixbuf = get_mbtiles_pixbuf ( vml, mapcoord->x, mapcoord->y, (17 - mapcoord->scale) );
1101         pixbuf = pixbuf_apply_settings ( pixbuf, vml, mapcoord, xshrinkfactor, yshrinkfactor );
1102         // return now to avoid file tests that aren't appropriate for this map type
1103         return pixbuf;
1104       }
1105       else if ( vik_map_source_is_osm_meta_tiles(map) ) {
1106         pixbuf = get_pixbuf_from_metatile ( vml, mapcoord->x, mapcoord->y, (17 - mapcoord->scale) );
1107         pixbuf = pixbuf_apply_settings ( pixbuf, vml, mapcoord, xshrinkfactor, yshrinkfactor );
1108         return pixbuf;
1109       }
1110       else
1111         get_filename ( vml->cache_dir, VIK_MAPS_CACHE_LAYOUT_OSM, id, NULL,
1112                        mapcoord->scale, mapcoord->z, mapcoord->x, mapcoord->y, filename_buf, buf_len,
1113                        vik_map_source_get_file_extension(map) );
1114     }
1115     else
1116       get_filename ( vml->cache_dir, vml->cache_layout, id, mapname,
1117                      mapcoord->scale, mapcoord->z, mapcoord->x, mapcoord->y, filename_buf, buf_len,
1118                      vik_map_source_get_file_extension(map) );
1119
1120     if ( g_file_test ( filename_buf, G_FILE_TEST_EXISTS ) == TRUE)
1121     {
1122       GError *gx = NULL;
1123       pixbuf = gdk_pixbuf_new_from_file ( filename_buf, &gx );
1124
1125       /* free the pixbuf on error */
1126       if (gx)
1127       {
1128         if ( gx->domain != GDK_PIXBUF_ERROR || gx->code != GDK_PIXBUF_ERROR_CORRUPT_IMAGE ) {
1129           // Report a warning
1130           if ( IS_VIK_WINDOW ((VikWindow*)VIK_GTK_WINDOW_FROM_LAYER(vml)) ) {
1131             gchar* msg = g_strdup_printf ( _("Couldn't open image file: %s"), gx->message );
1132             vik_window_statusbar_update ( (VikWindow*)VIK_GTK_WINDOW_FROM_LAYER(vml), msg, VIK_STATUSBAR_INFO );
1133             g_free (msg);
1134           }
1135         }
1136
1137         g_error_free ( gx );
1138         if ( pixbuf )
1139           g_object_unref ( G_OBJECT(pixbuf) );
1140         pixbuf = NULL;
1141       } else {
1142         pixbuf = pixbuf_apply_settings ( pixbuf, vml, mapcoord, xshrinkfactor, yshrinkfactor );
1143       }
1144     }
1145   }
1146   return pixbuf;
1147 }
1148
1149 static gboolean should_start_autodownload(VikMapsLayer *vml, VikViewport *vvp)
1150 {
1151   const VikCoord *center = vik_viewport_get_center ( vvp );
1152
1153   if (vik_window_get_pan_move (VIK_WINDOW(VIK_GTK_WINDOW_FROM_WIDGET(GTK_WIDGET(vvp)))))
1154     /* D'n'D pan in action: do not download */
1155     return FALSE;
1156
1157   // Don't attempt to download unsupported zoom levels
1158   gdouble xzoom = vik_viewport_get_xmpp ( vvp );
1159   VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
1160   guint8 zl = map_utils_mpp_to_zoom_level ( xzoom );
1161   if ( zl < vik_map_source_get_zoom_min(map) || zl > vik_map_source_get_zoom_max(map) )
1162     return FALSE;
1163
1164   if (vml->last_center == NULL) {
1165     VikCoord *new_center = g_malloc(sizeof(VikCoord));
1166     *new_center = *center;
1167     vml->last_center = new_center;
1168     vml->last_xmpp = vik_viewport_get_xmpp(vvp);
1169     vml->last_ympp = vik_viewport_get_ympp(vvp);
1170     return TRUE;
1171   }
1172
1173   /* TODO: perhaps vik_coord_diff() */
1174   if (vik_coord_equals(vml->last_center, center)
1175       && (vml->last_xmpp == vik_viewport_get_xmpp(vvp))
1176       && (vml->last_ympp == vik_viewport_get_ympp(vvp)))
1177     return FALSE;
1178
1179   *(vml->last_center) = *center;
1180     vml->last_xmpp = vik_viewport_get_xmpp(vvp);
1181     vml->last_ympp = vik_viewport_get_ympp(vvp);
1182   return TRUE;
1183 }
1184
1185 /**
1186  *
1187  */
1188 gboolean try_draw_scale_down (VikMapsLayer *vml, VikViewport *vvp, MapCoord ulm, gint xx, gint yy, gint tilesize_x_ceil, gint tilesize_y_ceil,
1189                               gdouble xshrinkfactor, gdouble yshrinkfactor, guint id, const gchar *mapname, gchar *path_buf, guint max_path_len)
1190 {
1191   GdkPixbuf *pixbuf;
1192   int scale_inc;
1193   for (scale_inc = 1; scale_inc < SCALE_INC_DOWN; scale_inc ++) {
1194     // Try with smaller zooms
1195     int scale_factor = 1 << scale_inc;  /*  2^scale_inc */
1196     MapCoord ulm2 = ulm;
1197     ulm2.x = ulm.x / scale_factor;
1198     ulm2.y = ulm.y / scale_factor;
1199     ulm2.scale = ulm.scale + scale_inc;
1200     pixbuf = get_pixbuf ( vml, id, mapname, &ulm2, path_buf, max_path_len, xshrinkfactor * scale_factor, yshrinkfactor * scale_factor );
1201     if ( pixbuf ) {
1202       gint src_x = (ulm.x % scale_factor) * tilesize_x_ceil;
1203       gint src_y = (ulm.y % scale_factor) * tilesize_y_ceil;
1204       vik_viewport_draw_pixbuf ( vvp, pixbuf, src_x, src_y, xx, yy, tilesize_x_ceil, tilesize_y_ceil );
1205       return TRUE;
1206     }
1207   }
1208   return FALSE;
1209 }
1210
1211 /**
1212  *
1213  */
1214 gboolean try_draw_scale_up (VikMapsLayer *vml, VikViewport *vvp, MapCoord ulm, gint xx, gint yy, gint tilesize_x_ceil, gint tilesize_y_ceil,
1215                             gdouble xshrinkfactor, gdouble yshrinkfactor, guint id, const gchar *mapname, gchar *path_buf, guint max_path_len)
1216 {
1217   GdkPixbuf *pixbuf;
1218   // Try with bigger zooms
1219   int scale_dec;
1220   for (scale_dec = 1; scale_dec < SCALE_INC_UP; scale_dec ++) {
1221     int pict_x, pict_y;
1222     int scale_factor = 1 << scale_dec;  /*  2^scale_dec */
1223     MapCoord ulm2 = ulm;
1224     ulm2.x = ulm.x * scale_factor;
1225     ulm2.y = ulm.y * scale_factor;
1226     ulm2.scale = ulm.scale - scale_dec;
1227     for (pict_x = 0; pict_x < scale_factor; pict_x ++) {
1228       for (pict_y = 0; pict_y < scale_factor; pict_y ++) {
1229         MapCoord ulm3 = ulm2;
1230         ulm3.x += pict_x;
1231         ulm3.y += pict_y;
1232         pixbuf = get_pixbuf ( vml, id, mapname, &ulm3, path_buf, max_path_len, xshrinkfactor / scale_factor, yshrinkfactor / scale_factor );
1233         if ( pixbuf ) {
1234           gint src_x = 0;
1235           gint src_y = 0;
1236           gint dest_x = xx + pict_x * (tilesize_x_ceil / scale_factor);
1237           gint dest_y = yy + pict_y * (tilesize_y_ceil / scale_factor);
1238           vik_viewport_draw_pixbuf ( vvp, pixbuf, src_x, src_y, dest_x, dest_y, tilesize_x_ceil / scale_factor, tilesize_y_ceil / scale_factor );
1239           return TRUE;
1240         }
1241       }
1242     }
1243   }
1244   return FALSE;
1245 }
1246
1247 static void maps_layer_draw_section ( VikMapsLayer *vml, VikViewport *vvp, VikCoord *ul, VikCoord *br )
1248 {
1249   MapCoord ulm, brm;
1250   gdouble xzoom = vik_viewport_get_xmpp ( vvp );
1251   gdouble yzoom = vik_viewport_get_ympp ( vvp );
1252   gdouble xshrinkfactor = 1.0, yshrinkfactor = 1.0;
1253   gboolean existence_only = FALSE;
1254
1255   if ( vml->xmapzoom && (vml->xmapzoom != xzoom || vml->ymapzoom != yzoom) ) {
1256     xshrinkfactor = vml->xmapzoom / xzoom;
1257     yshrinkfactor = vml->ymapzoom / yzoom;
1258     xzoom = vml->xmapzoom;
1259     yzoom = vml->xmapzoom;
1260     if ( ! (xshrinkfactor > MIN_SHRINKFACTOR && xshrinkfactor < MAX_SHRINKFACTOR &&
1261          yshrinkfactor > MIN_SHRINKFACTOR && yshrinkfactor < MAX_SHRINKFACTOR ) ) {
1262       if ( xshrinkfactor > REAL_MIN_SHRINKFACTOR && yshrinkfactor > REAL_MIN_SHRINKFACTOR ) {
1263         g_debug ( "%s: existence_only due to SHRINKFACTORS", __FUNCTION__ );
1264         existence_only = TRUE;
1265       }
1266       else {
1267         // Report the reason for not drawing
1268         if ( IS_VIK_WINDOW ((VikWindow*)VIK_GTK_WINDOW_FROM_LAYER(vml)) ) {
1269           gchar* msg = g_strdup_printf ( _("Cowardly refusing to draw tiles or existence of tiles beyond %d zoom out factor"), (int)( 1.0/REAL_MIN_SHRINKFACTOR));
1270           vik_window_statusbar_update ( (VikWindow*)VIK_GTK_WINDOW_FROM_LAYER(vml), msg, VIK_STATUSBAR_INFO );
1271           g_free (msg);
1272         }
1273         return;
1274       }
1275     }
1276   }
1277
1278   /* coord -> ID */
1279   VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
1280   if ( vik_map_source_coord_to_mapcoord ( map, ul, xzoom, yzoom, &ulm ) &&
1281        vik_map_source_coord_to_mapcoord ( map, br, xzoom, yzoom, &brm ) ) {
1282
1283     /* loop & draw */
1284     gint x, y;
1285     gint xmin = MIN(ulm.x, brm.x), xmax = MAX(ulm.x, brm.x);
1286     gint ymin = MIN(ulm.y, brm.y), ymax = MAX(ulm.y, brm.y);
1287     guint16 id = vik_map_source_get_uniq_id(map);
1288     const gchar *mapname = vik_map_source_get_name(map);
1289
1290     VikCoord coord;
1291     gint xx, yy, width, height;
1292     GdkPixbuf *pixbuf;
1293
1294     // Prevent the program grinding to a halt if trying to deal with thousands of tiles
1295     //  which can happen when using a small fixed zoom level and viewing large areas.
1296     // Also prevents very large number of tile download requests
1297     gint tiles = (xmax-xmin) * (ymax-ymin);
1298     if ( tiles > MAX_TILES ) {
1299       g_debug ( "%s: existence_only due to wanting too many tiles (%d)", __FUNCTION__, tiles );
1300       existence_only = TRUE;
1301     }
1302
1303     guint max_path_len = strlen(vml->cache_dir) + 40;
1304     gchar *path_buf = g_malloc ( max_path_len * sizeof(char) );
1305
1306     if ( (!existence_only) && vml->autodownload  && should_start_autodownload(vml, vvp)) {
1307       g_debug("%s: Starting autodownload", __FUNCTION__);
1308       if ( !vml->adl_only_missing && vik_map_source_supports_download_only_new (map) )
1309         // Try to download newer tiles
1310         start_download_thread ( vml, vvp, ul, br, REDOWNLOAD_NEW );
1311       else
1312         // Download only missing tiles
1313         start_download_thread ( vml, vvp, ul, br, REDOWNLOAD_NONE );
1314     }
1315
1316     if ( vik_map_source_get_tilesize_x(map) == 0 && !existence_only ) {
1317       for ( x = xmin; x <= xmax; x++ ) {
1318         for ( y = ymin; y <= ymax; y++ ) {
1319           ulm.x = x;
1320           ulm.y = y;
1321           pixbuf = get_pixbuf ( vml, id, mapname, &ulm, path_buf, max_path_len, xshrinkfactor, yshrinkfactor );
1322           if ( pixbuf ) {
1323             width = gdk_pixbuf_get_width ( pixbuf );
1324             height = gdk_pixbuf_get_height ( pixbuf );
1325
1326             vik_map_source_mapcoord_to_center_coord ( map, &ulm, &coord );
1327             vik_viewport_coord_to_screen ( vvp, &coord, &xx, &yy );
1328             xx -= (width/2);
1329             yy -= (height/2);
1330
1331             vik_viewport_draw_pixbuf ( vvp, pixbuf, 0, 0, xx, yy, width, height );
1332           }
1333         }
1334       }
1335     } else { /* tilesize is known, don't have to keep converting coords */
1336       gdouble tilesize_x = vik_map_source_get_tilesize_x(map) * xshrinkfactor;
1337       gdouble tilesize_y = vik_map_source_get_tilesize_y(map) * yshrinkfactor;
1338       /* ceiled so tiles will be maximum size in the case of funky shrinkfactor */
1339       gint tilesize_x_ceil = ceil ( tilesize_x );
1340       gint tilesize_y_ceil = ceil ( tilesize_y );
1341       gint8 xinc = (ulm.x == xmin) ? 1 : -1;
1342       gint8 yinc = (ulm.y == ymin) ? 1 : -1;
1343       gint xx_tmp, yy_tmp;
1344       gint base_yy, xend, yend;
1345
1346       xend = (xinc == 1) ? (xmax+1) : (xmin-1);
1347       yend = (yinc == 1) ? (ymax+1) : (ymin-1);
1348
1349       vik_map_source_mapcoord_to_center_coord ( map, &ulm, &coord );
1350       vik_viewport_coord_to_screen ( vvp, &coord, &xx_tmp, &yy_tmp );
1351       xx = xx_tmp; yy = yy_tmp;
1352       /* above trick so xx,yy doubles. this is so shrinkfactors aren't rounded off
1353        * eg if tile size 128, shrinkfactor 0.333 */
1354       xx -= (tilesize_x/2);
1355       base_yy = yy - (tilesize_y/2);
1356
1357       for ( x = ((xinc == 1) ? xmin : xmax); x != xend; x+=xinc ) {
1358         yy = base_yy;
1359         for ( y = ((yinc == 1) ? ymin : ymax); y != yend; y+=yinc ) {
1360           ulm.x = x;
1361           ulm.y = y;
1362
1363           if ( existence_only ) {
1364             if ( vik_map_source_is_direct_file_access (MAPS_LAYER_NTH_TYPE(vml->maptype)) )
1365               get_filename ( vml->cache_dir, VIK_MAPS_CACHE_LAYOUT_OSM, id, vik_map_source_get_name(map),
1366                              ulm.scale, ulm.z, ulm.x, ulm.y, path_buf, max_path_len, vik_map_source_get_file_extension(map) );
1367             else
1368               get_filename ( vml->cache_dir, vml->cache_layout, id, vik_map_source_get_name(map),
1369                              ulm.scale, ulm.z, ulm.x, ulm.y, path_buf, max_path_len, vik_map_source_get_file_extension(map) );
1370
1371             if ( g_file_test ( path_buf, G_FILE_TEST_EXISTS ) == TRUE ) {
1372               GdkGC *black_gc = gtk_widget_get_style(GTK_WIDGET(vvp))->black_gc;
1373               vik_viewport_draw_line ( vvp, black_gc, xx+tilesize_x_ceil, yy, xx, yy+tilesize_y_ceil );
1374             }
1375           } else {
1376             // Try correct scale first
1377             int scale_factor = 1;
1378             pixbuf = get_pixbuf ( vml, id, mapname, &ulm, path_buf, max_path_len, xshrinkfactor * scale_factor, yshrinkfactor * scale_factor );
1379             if ( pixbuf ) {
1380               gint src_x = (ulm.x % scale_factor) * tilesize_x_ceil;
1381               gint src_y = (ulm.y % scale_factor) * tilesize_y_ceil;
1382               vik_viewport_draw_pixbuf ( vvp, pixbuf, src_x, src_y, xx, yy, tilesize_x_ceil, tilesize_y_ceil );
1383             }
1384             else {
1385               // Otherwise try different scales
1386               if ( SCALE_SMALLER_ZOOM_FIRST ) {
1387                 if ( !try_draw_scale_down(vml,vvp,ulm,xx,yy,tilesize_x_ceil,tilesize_y_ceil,xshrinkfactor,yshrinkfactor,id,mapname,path_buf,max_path_len) ) {
1388                   try_draw_scale_up(vml,vvp,ulm,xx,yy,tilesize_x_ceil,tilesize_y_ceil,xshrinkfactor,yshrinkfactor,id,mapname,path_buf,max_path_len);
1389                 }
1390               }
1391               else {
1392                 if ( !try_draw_scale_up(vml,vvp,ulm,xx,yy,tilesize_x_ceil,tilesize_y_ceil,xshrinkfactor,yshrinkfactor,id,mapname,path_buf,max_path_len) ) {
1393                   try_draw_scale_down(vml,vvp,ulm,xx,yy,tilesize_x_ceil,tilesize_y_ceil,xshrinkfactor,yshrinkfactor,id,mapname,path_buf,max_path_len);
1394                 }
1395               }
1396             }
1397           }
1398
1399           yy += tilesize_y;
1400         }
1401         xx += tilesize_x;
1402       }
1403
1404       // ATM Only show tile grid lines in extreme debug mode
1405       if ( vik_debug && vik_verbose ) {
1406         /* Grid drawing here so it gets drawn on top of the map */
1407         /* Thus loop around x & y again, but this time separately */
1408         /* Only showing grid for the current scale */
1409         GdkGC *black_gc = GTK_WIDGET(vvp)->style->black_gc;
1410         /* Draw single grid lines across the whole screen */
1411         gint width = vik_viewport_get_width(vvp);
1412         gint height = vik_viewport_get_height(vvp);
1413         xx = xx_tmp; yy = yy_tmp;
1414         gint base_xx = xx - (tilesize_x/2);
1415         base_yy = yy - (tilesize_y/2);
1416
1417         xx = base_xx;
1418         for ( x = ((xinc == 1) ? xmin : xmax); x != xend; x+=xinc ) {
1419           vik_viewport_draw_line ( vvp, black_gc, xx, base_yy, xx, height );
1420           xx += tilesize_x;
1421         }
1422
1423         yy = base_yy;
1424         for ( y = ((yinc == 1) ? ymin : ymax); y != yend; y+=yinc ) {
1425           vik_viewport_draw_line ( vvp, black_gc, base_xx, yy, width, yy );
1426           yy += tilesize_y;
1427         }
1428       }
1429
1430     }
1431     g_free ( path_buf );
1432   }
1433 }
1434
1435 static void maps_layer_draw ( VikMapsLayer *vml, VikViewport *vvp )
1436 {
1437   if ( vik_map_source_get_drawmode(MAPS_LAYER_NTH_TYPE(vml->maptype)) == vik_viewport_get_drawmode ( vvp ) )
1438   {
1439     VikCoord ul, br;
1440
1441     /* Copyright */
1442     gdouble level = vik_viewport_get_zoom ( vvp );
1443     LatLonBBox bbox;
1444     vik_viewport_get_min_max_lat_lon ( vvp, &bbox.south, &bbox.north, &bbox.west, &bbox.east );
1445     vik_map_source_get_copyright ( MAPS_LAYER_NTH_TYPE(vml->maptype), bbox, level, vik_viewport_add_copyright, vvp );
1446
1447     /* Logo */
1448     const GdkPixbuf *logo = vik_map_source_get_logo ( MAPS_LAYER_NTH_TYPE(vml->maptype) );
1449     vik_viewport_add_logo ( vvp, logo );
1450
1451     /* get corner coords */
1452     if ( vik_viewport_get_coord_mode ( vvp ) == VIK_COORD_UTM && ! vik_viewport_is_one_zone ( vvp ) ) {
1453       /* UTM multi-zone stuff by Kit Transue */
1454       gchar leftmost_zone, rightmost_zone, i;
1455       leftmost_zone = vik_viewport_leftmost_zone( vvp );
1456       rightmost_zone = vik_viewport_rightmost_zone( vvp );
1457       for ( i = leftmost_zone; i <= rightmost_zone; ++i ) {
1458         vik_viewport_corners_for_zonen ( vvp, i, &ul, &br );
1459         maps_layer_draw_section ( vml, vvp, &ul, &br );
1460       }
1461     }
1462     else {
1463       vik_viewport_screen_to_coord ( vvp, 0, 0, &ul );
1464       vik_viewport_screen_to_coord ( vvp, vik_viewport_get_width(vvp), vik_viewport_get_height(vvp), &br );
1465
1466       maps_layer_draw_section ( vml, vvp, &ul, &br );
1467     }
1468   }
1469 }
1470
1471 /*************************/
1472 /****** DOWNLOADING ******/
1473 /*************************/
1474
1475 /* pass along data to thread, exists even if layer is deleted. */
1476 typedef struct {
1477   gchar *cache_dir;
1478   gchar *filename_buf;
1479   VikMapsCacheLayout cache_layout;
1480   gint x0, y0, xf, yf;
1481   MapCoord mapcoord;
1482   gint maptype;
1483   gint maxlen;
1484   gint mapstoget;
1485   gint redownload;
1486   gboolean refresh_display;
1487   VikMapsLayer *vml;
1488   VikViewport *vvp;
1489   gboolean map_layer_alive;
1490   GMutex *mutex;
1491 } MapDownloadInfo;
1492
1493 static void mdi_free ( MapDownloadInfo *mdi )
1494 {
1495   vik_mutex_free(mdi->mutex);
1496   g_free ( mdi->cache_dir );
1497   mdi->cache_dir = NULL;
1498   g_free ( mdi->filename_buf );
1499   mdi->filename_buf = NULL;
1500   g_free ( mdi );
1501 }
1502
1503 static void weak_ref_cb(gpointer ptr, GObject * dead_vml)
1504 {
1505   MapDownloadInfo *mdi = ptr;
1506   g_mutex_lock(mdi->mutex);
1507   mdi->map_layer_alive = FALSE;
1508   g_mutex_unlock(mdi->mutex);
1509 }
1510
1511 static gboolean is_in_area (VikMapSource *map, MapCoord mc)
1512 {
1513   VikCoord vc;
1514   vik_map_source_mapcoord_to_center_coord ( map, &mc, &vc );
1515
1516   struct LatLon tl;
1517   tl.lat = vik_map_source_get_lat_max(map);
1518   tl.lon = vik_map_source_get_lon_min(map);
1519   struct LatLon br;
1520   br.lat = vik_map_source_get_lat_min(map);
1521   br.lon = vik_map_source_get_lon_max(map);
1522   VikCoord vctl;
1523   vik_coord_load_from_latlon (&vctl, VIK_COORD_LATLON, &tl);
1524   VikCoord vcbr;
1525   vik_coord_load_from_latlon (&vcbr, VIK_COORD_LATLON, &br);
1526
1527   return vik_coord_inside ( &vc, &vctl, &vcbr );
1528 }
1529
1530 static int map_download_thread ( MapDownloadInfo *mdi, gpointer threaddata )
1531 {
1532   void *handle = vik_map_source_download_handle_init(MAPS_LAYER_NTH_TYPE(mdi->maptype));
1533   guint donemaps = 0;
1534   MapCoord mcoord = mdi->mapcoord;
1535   gint x, y;
1536   for ( x = mdi->x0; x <= mdi->xf; x++ )
1537   {
1538     mcoord.x = x;
1539     for ( y = mdi->y0; y <= mdi->yf; y++ )
1540     {
1541       mcoord.y = y;
1542       // Only attempt to download a tile from supported areas
1543       if ( is_in_area ( MAPS_LAYER_NTH_TYPE(mdi->maptype), mcoord ) )
1544       {
1545         gboolean remove_mem_cache = FALSE;
1546         gboolean need_download = FALSE;
1547
1548         get_filename ( mdi->cache_dir, mdi->cache_layout,
1549                        vik_map_source_get_uniq_id(MAPS_LAYER_NTH_TYPE(mdi->maptype)),
1550                        vik_map_source_get_name(MAPS_LAYER_NTH_TYPE(mdi->maptype)),
1551                        mdi->mapcoord.scale, mdi->mapcoord.z, x, y, mdi->filename_buf, mdi->maxlen,
1552                        vik_map_source_get_file_extension(MAPS_LAYER_NTH_TYPE(mdi->maptype)) );
1553
1554         donemaps++;
1555         int res = a_background_thread_progress ( threaddata, ((gdouble)donemaps) / mdi->mapstoget ); /* this also calls testcancel */
1556         if (res != 0) {
1557           vik_map_source_download_handle_cleanup(MAPS_LAYER_NTH_TYPE(mdi->maptype), handle);
1558           return -1;
1559         }
1560
1561         if ( g_file_test ( mdi->filename_buf, G_FILE_TEST_EXISTS ) == FALSE ) {
1562           need_download = TRUE;
1563           remove_mem_cache = TRUE;
1564
1565         } else {  /* in case map file already exists */
1566           switch (mdi->redownload) {
1567             case REDOWNLOAD_NONE:
1568               continue;
1569
1570             case REDOWNLOAD_BAD:
1571             {
1572               /* see if this one is bad or what */
1573               GError *gx = NULL;
1574               GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file ( mdi->filename_buf, &gx );
1575               if (gx || (!pixbuf)) {
1576                 g_remove ( mdi->filename_buf );
1577                 need_download = TRUE;
1578                 remove_mem_cache = TRUE;
1579                 g_error_free ( gx );
1580
1581               } else {
1582                 g_object_unref ( pixbuf );
1583               }
1584               break;
1585             }
1586
1587             case REDOWNLOAD_NEW:
1588               need_download = TRUE;
1589               remove_mem_cache = TRUE;
1590               break;
1591
1592             case REDOWNLOAD_ALL:
1593               /* FIXME: need a better way than to erase file in case of server/network problem */
1594               g_remove ( mdi->filename_buf );
1595               need_download = TRUE;
1596               remove_mem_cache = TRUE;
1597               break;
1598
1599             case DOWNLOAD_OR_REFRESH:
1600               remove_mem_cache = TRUE;
1601               break;
1602
1603             default:
1604               g_warning ( "redownload state %d unknown\n", mdi->redownload);
1605           }
1606         }
1607
1608         mdi->mapcoord.x = x; mdi->mapcoord.y = y;
1609
1610         if (need_download) {
1611           DownloadResult_t dr = vik_map_source_download( MAPS_LAYER_NTH_TYPE(mdi->maptype), &(mdi->mapcoord), mdi->filename_buf, handle);
1612           switch ( dr ) {
1613             case DOWNLOAD_HTTP_ERROR:
1614             case DOWNLOAD_CONTENT_ERROR: {
1615               // TODO: ?? count up the number of download errors somehow...
1616               gchar* msg = g_strdup_printf ( "%s: %s", vik_maps_layer_get_map_label (mdi->vml), _("Failed to download tile") );
1617               vik_window_statusbar_update ( (VikWindow*)VIK_GTK_WINDOW_FROM_LAYER(mdi->vml), msg, VIK_STATUSBAR_INFO );
1618               g_free (msg);
1619               break;
1620             }
1621             case DOWNLOAD_FILE_WRITE_ERROR: {
1622               gchar* msg = g_strdup_printf ( "%s: %s", vik_maps_layer_get_map_label (mdi->vml), _("Unable to save tile") );
1623               vik_window_statusbar_update ( (VikWindow*)VIK_GTK_WINDOW_FROM_LAYER(mdi->vml), msg, VIK_STATUSBAR_INFO );
1624               g_free (msg);
1625               break;
1626             }
1627             case DOWNLOAD_SUCCESS:
1628             case DOWNLOAD_NOT_REQUIRED:
1629             default:
1630               break;
1631           }
1632         }
1633
1634         g_mutex_lock(mdi->mutex);
1635         if (remove_mem_cache)
1636             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, mdi->vml->filename );
1637         if (mdi->refresh_display && mdi->map_layer_alive) {
1638           /* TODO: check if it's on visible area */
1639           vik_layer_emit_update ( VIK_LAYER(mdi->vml) ); // NB update display from background
1640         }
1641         g_mutex_unlock(mdi->mutex);
1642         mdi->mapcoord.x = mdi->mapcoord.y = 0; /* we're temporarily between downloads */
1643       }
1644     }
1645   }
1646   vik_map_source_download_handle_cleanup(MAPS_LAYER_NTH_TYPE(mdi->maptype), handle);
1647   g_mutex_lock(mdi->mutex);
1648   if (mdi->map_layer_alive)
1649     g_object_weak_unref(G_OBJECT(mdi->vml), weak_ref_cb, mdi);
1650   g_mutex_unlock(mdi->mutex); 
1651   return 0;
1652 }
1653
1654 static void mdi_cancel_cleanup ( MapDownloadInfo *mdi )
1655 {
1656   if ( mdi->mapcoord.x || mdi->mapcoord.y )
1657   {
1658     get_filename ( mdi->cache_dir, mdi->cache_layout,
1659                    vik_map_source_get_uniq_id(MAPS_LAYER_NTH_TYPE(mdi->maptype)),
1660                    vik_map_source_get_name(MAPS_LAYER_NTH_TYPE(mdi->maptype)),
1661                    mdi->mapcoord.scale, mdi->mapcoord.z, mdi->mapcoord.x, mdi->mapcoord.y, mdi->filename_buf, mdi->maxlen,
1662                    vik_map_source_get_file_extension(MAPS_LAYER_NTH_TYPE(mdi->maptype)) );
1663     if ( g_file_test ( mdi->filename_buf, G_FILE_TEST_EXISTS ) == TRUE)
1664     {
1665       g_remove ( mdi->filename_buf );
1666     }
1667   }
1668 }
1669
1670 static void start_download_thread ( VikMapsLayer *vml, VikViewport *vvp, const VikCoord *ul, const VikCoord *br, gint redownload )
1671 {
1672   gdouble xzoom = vml->xmapzoom ? vml->xmapzoom : vik_viewport_get_xmpp ( vvp );
1673   gdouble yzoom = vml->ymapzoom ? vml->ymapzoom : vik_viewport_get_ympp ( vvp );
1674   MapCoord ulm, brm;
1675   VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
1676
1677   // Don't ever attempt download on direct access
1678   if ( vik_map_source_is_direct_file_access ( map ) )
1679     return;
1680
1681   if ( vik_map_source_coord_to_mapcoord ( map, ul, xzoom, yzoom, &ulm ) 
1682     && vik_map_source_coord_to_mapcoord ( map, br, xzoom, yzoom, &brm ) )
1683   {
1684     MapDownloadInfo *mdi = g_malloc ( sizeof(MapDownloadInfo) );
1685     gint a, b;
1686
1687     mdi->vml = vml;
1688     mdi->vvp = vvp;
1689     mdi->map_layer_alive = TRUE;
1690     mdi->mutex = vik_mutex_new();
1691     mdi->refresh_display = TRUE;
1692
1693     /* cache_dir and buffer for dest filename */
1694     mdi->cache_dir = g_strdup ( vml->cache_dir );
1695     mdi->maxlen = strlen ( vml->cache_dir ) + 40;
1696     mdi->filename_buf = g_malloc ( mdi->maxlen * sizeof(gchar) );
1697     mdi->cache_layout = vml->cache_layout;
1698     mdi->maptype = vml->maptype;
1699
1700     mdi->mapcoord = ulm;
1701     mdi->redownload = redownload;
1702
1703     mdi->x0 = MIN(ulm.x, brm.x);
1704     mdi->xf = MAX(ulm.x, brm.x);
1705     mdi->y0 = MIN(ulm.y, brm.y);
1706     mdi->yf = MAX(ulm.y, brm.y);
1707
1708     mdi->mapstoget = 0;
1709
1710     MapCoord mcoord = mdi->mapcoord;
1711
1712     if ( mdi->redownload ) {
1713       mdi->mapstoget = (mdi->xf - mdi->x0 + 1) * (mdi->yf - mdi->y0 + 1);
1714     } else {
1715       /* calculate how many we need */
1716       for ( a = mdi->x0; a <= mdi->xf; a++ )
1717       {
1718         mcoord.x = a;
1719         for ( b = mdi->y0; b <= mdi->yf; b++ )
1720         {
1721           mcoord.y = b;
1722           // Only count tiles from supported areas
1723           if ( is_in_area (map, mcoord) )
1724           {
1725             get_filename ( mdi->cache_dir, mdi->cache_layout,
1726                            vik_map_source_get_uniq_id(map),
1727                            vik_map_source_get_name(map),
1728                            ulm.scale, ulm.z, a, b, mdi->filename_buf, mdi->maxlen,
1729                            vik_map_source_get_file_extension(map) );
1730             if ( g_file_test ( mdi->filename_buf, G_FILE_TEST_EXISTS ) == FALSE )
1731               mdi->mapstoget++;
1732           }
1733         }
1734       }
1735     }
1736
1737     mdi->mapcoord.x = mdi->mapcoord.y = 0; /* for cleanup -- no current map */
1738
1739     if ( mdi->mapstoget )
1740     {
1741       const gchar *tmp_str;
1742       gchar *tmp;
1743
1744       if (redownload) 
1745       {
1746         if (redownload == REDOWNLOAD_BAD)
1747           tmp_str = ngettext("Redownloading up to %d %s map...", "Redownloading up to %d %s maps...", mdi->mapstoget);
1748         else
1749           tmp_str = ngettext("Redownloading %d %s map...", "Redownloading %d %s maps...", mdi->mapstoget);
1750       } 
1751       else 
1752       {
1753         tmp_str = ngettext("Downloading %d %s map...", "Downloading %d %s maps...", mdi->mapstoget);
1754       }
1755       tmp = g_strdup_printf ( tmp_str, mdi->mapstoget, MAPS_LAYER_NTH_LABEL(vml->maptype));
1756  
1757       g_object_weak_ref(G_OBJECT(mdi->vml), weak_ref_cb, mdi);
1758       /* launch the thread */
1759       a_background_thread ( BACKGROUND_POOL_REMOTE,
1760                             VIK_GTK_WINDOW_FROM_LAYER(vml), /* parent window */
1761                             tmp,                                              /* description string */
1762                             (vik_thr_func) map_download_thread,               /* function to call within thread */
1763                             mdi,                                              /* pass along data */
1764                             (vik_thr_free_func) mdi_free,                     /* function to free pass along data */
1765                             (vik_thr_free_func) mdi_cancel_cleanup,
1766                             mdi->mapstoget );
1767       g_free ( tmp );
1768     }
1769     else
1770       mdi_free ( mdi );
1771   }
1772 }
1773
1774 static void maps_layer_download_section ( VikMapsLayer *vml, VikViewport *vvp, VikCoord *ul, VikCoord *br, gdouble zoom, gint download_method )
1775 {
1776   MapCoord ulm, brm;
1777   VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
1778
1779   // Don't ever attempt download on direct access
1780   if ( vik_map_source_is_direct_file_access ( map ) )
1781     return;
1782
1783   if (!vik_map_source_coord_to_mapcoord(map, ul, zoom, zoom, &ulm) 
1784     || !vik_map_source_coord_to_mapcoord(map, br, zoom, zoom, &brm)) {
1785     g_warning("%s() coord_to_mapcoord() failed", __PRETTY_FUNCTION__);
1786     return;
1787   }
1788
1789   MapDownloadInfo *mdi = g_malloc(sizeof(MapDownloadInfo));
1790   gint i, j;
1791
1792   mdi->vml = vml;
1793   mdi->vvp = vvp;
1794   mdi->map_layer_alive = TRUE;
1795   mdi->mutex = vik_mutex_new();
1796   mdi->refresh_display = TRUE;
1797
1798   mdi->cache_dir = g_strdup ( vml->cache_dir );
1799   mdi->maxlen = strlen ( vml->cache_dir ) + 40;
1800   mdi->filename_buf = g_malloc ( mdi->maxlen * sizeof(gchar) );
1801   mdi->maptype = vml->maptype;
1802   mdi->cache_layout = vml->cache_layout;
1803
1804   mdi->mapcoord = ulm;
1805   mdi->redownload = download_method;
1806
1807   mdi->x0 = MIN(ulm.x, brm.x);
1808   mdi->xf = MAX(ulm.x, brm.x);
1809   mdi->y0 = MIN(ulm.y, brm.y);
1810   mdi->yf = MAX(ulm.y, brm.y);
1811
1812   mdi->mapstoget = 0;
1813
1814   MapCoord mcoord = mdi->mapcoord;
1815
1816   for (i = mdi->x0; i <= mdi->xf; i++) {
1817     mcoord.x = i;
1818     for (j = mdi->y0; j <= mdi->yf; j++) {
1819       mcoord.y = j;
1820       // Only count tiles from supported areas
1821       if ( is_in_area (map, mcoord) ) {
1822         get_filename ( mdi->cache_dir, mdi->cache_layout,
1823                        vik_map_source_get_uniq_id(map),
1824                        vik_map_source_get_name(map),
1825                        ulm.scale, ulm.z, i, j, mdi->filename_buf, mdi->maxlen,
1826                        vik_map_source_get_file_extension(map) );
1827         if ( g_file_test ( mdi->filename_buf, G_FILE_TEST_EXISTS ) == FALSE )
1828               mdi->mapstoget++;
1829       }
1830     }
1831   }
1832
1833   mdi->mapcoord.x = mdi->mapcoord.y = 0; /* for cleanup -- no current map */
1834
1835   if (mdi->mapstoget) {
1836     gchar *tmp;
1837     const gchar *fmt;
1838     fmt = ngettext("Downloading %d %s map...",
1839                    "Downloading %d %s maps...",
1840                    mdi->mapstoget);
1841     tmp = g_strdup_printf ( fmt, mdi->mapstoget, MAPS_LAYER_NTH_LABEL(vml->maptype) );
1842
1843     g_object_weak_ref(G_OBJECT(mdi->vml), weak_ref_cb, mdi);
1844
1845     // launch the thread
1846     a_background_thread ( BACKGROUND_POOL_REMOTE,
1847                           VIK_GTK_WINDOW_FROM_LAYER(vml), /* parent window */
1848                           tmp,                                /* description string */
1849                           (vik_thr_func) map_download_thread, /* function to call within thread */
1850                           mdi,                                /* pass along data */
1851                           (vik_thr_free_func) mdi_free,       /* function to free pass along data */
1852                           (vik_thr_free_func) mdi_cancel_cleanup,
1853                           mdi->mapstoget );
1854     g_free ( tmp );
1855   }
1856   else
1857     mdi_free ( mdi );
1858 }
1859
1860 /**
1861  * vik_maps_layer_download_section:
1862  * @vml:  The Map Layer
1863  * @vvp:  The Viewport that the map is on
1864  * @ul:   Upper left coordinate of the area to be downloaded
1865  * @br:   Bottom right coordinate of the area to be downloaded
1866  * @zoom: The zoom level at which the maps are to be download
1867  *
1868  * Download a specified map area at a certain zoom level
1869  */
1870 void vik_maps_layer_download_section ( VikMapsLayer *vml, VikViewport *vvp, VikCoord *ul, VikCoord *br, gdouble zoom )
1871 {
1872   maps_layer_download_section (vml, vvp, ul, br, zoom, REDOWNLOAD_NONE);
1873 }
1874
1875 static void maps_layer_redownload_bad ( VikMapsLayer *vml )
1876 {
1877   start_download_thread ( vml, vml->redownload_vvp, &(vml->redownload_ul), &(vml->redownload_br), REDOWNLOAD_BAD );
1878 }
1879
1880 static void maps_layer_redownload_all ( VikMapsLayer *vml )
1881 {
1882   start_download_thread ( vml, vml->redownload_vvp, &(vml->redownload_ul), &(vml->redownload_br), REDOWNLOAD_ALL );
1883 }
1884
1885 static void maps_layer_redownload_new ( VikMapsLayer *vml )
1886 {
1887   start_download_thread ( vml, vml->redownload_vvp, &(vml->redownload_ul), &(vml->redownload_br), REDOWNLOAD_NEW );
1888 }
1889
1890 /**
1891  * Display a simple dialog with information about this particular map tile
1892  */
1893 static void maps_layer_tile_info ( VikMapsLayer *vml )
1894 {
1895   VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
1896
1897   gdouble xzoom = vml->xmapzoom ? vml->xmapzoom : vik_viewport_get_xmpp ( vml->redownload_vvp );
1898   gdouble yzoom = vml->ymapzoom ? vml->ymapzoom : vik_viewport_get_ympp ( vml->redownload_vvp );
1899   MapCoord ulm;
1900
1901   if ( !vik_map_source_coord_to_mapcoord ( map, &(vml->redownload_ul), xzoom, yzoom, &ulm ) )
1902     return;
1903
1904   gchar *filename = NULL;
1905   gchar *source = NULL;
1906
1907   if ( vik_map_source_is_direct_file_access ( map ) ) {
1908     if ( vik_map_source_is_mbtiles ( map ) ) {
1909       filename = g_strdup ( vml->filename );
1910 #ifdef HAVE_SQLITE3_H
1911       // And whether to bother going into the SQL to check it's really there or not...
1912       gchar *exists = NULL;
1913       gint zoom = 17 - ulm.scale;
1914       if ( vml->mbtiles ) {
1915         GdkPixbuf *pixbuf = get_pixbuf_sql_exec ( vml->mbtiles, ulm.x, ulm.y, zoom );
1916         if ( pixbuf ) {
1917           exists = g_strdup ( _("YES") );
1918           g_object_unref ( G_OBJECT(pixbuf) );
1919         }
1920         else {
1921           exists = g_strdup ( _("NO") );
1922         }
1923       }
1924       else
1925         exists = g_strdup ( _("NO") );
1926       gint flip_y = (gint) pow(2, zoom)-1 - ulm.y;
1927       // NB Also handles .jpg automatically due to pixbuf_new_from () support - although just print png for now.
1928       source = g_strdup_printf ( "Source: %s (%d%s%d%s%d.%s %s)", filename, zoom, G_DIR_SEPARATOR_S, ulm.x, G_DIR_SEPARATOR_S, flip_y, "png", exists );
1929       g_free ( exists );
1930 #else
1931       source = g_strdup ( _("Source: Not available") );
1932 #endif
1933     }
1934     else if ( vik_map_source_is_osm_meta_tiles ( map ) ) {
1935       char path[PATH_MAX];
1936       xyz_to_meta(path, sizeof(path), vml->cache_dir, ulm.x, ulm.y, 17-ulm.scale );
1937       source = g_strdup ( path );
1938       filename = g_strdup ( path );
1939     }
1940     else {
1941       guint max_path_len = strlen(vml->cache_dir) + 40;
1942       filename = g_malloc ( max_path_len * sizeof(char) );
1943       get_filename ( vml->cache_dir, VIK_MAPS_CACHE_LAYOUT_OSM,
1944                      vik_map_source_get_uniq_id(map),
1945                      NULL,
1946                      ulm.scale, ulm.z, ulm.x, ulm.y, filename, max_path_len,
1947                      vik_map_source_get_file_extension(map) );
1948       source = g_strconcat ( "Source: file://", filename, NULL );
1949     }
1950   }
1951   else {
1952     guint max_path_len = strlen(vml->cache_dir) + 40;
1953     filename = g_malloc ( max_path_len * sizeof(char) );
1954     get_filename ( vml->cache_dir, vml->cache_layout,
1955                    vik_map_source_get_uniq_id(map),
1956                    vik_map_source_get_name(map),
1957                    ulm.scale, ulm.z, ulm.x, ulm.y, filename, max_path_len,
1958                    vik_map_source_get_file_extension(map) );
1959     source = g_strdup_printf ( "Source: http://%s%s",
1960                                vik_map_source_default_get_hostname ( VIK_MAP_SOURCE_DEFAULT(map) ),
1961                                vik_map_source_default_get_uri ( VIK_MAP_SOURCE_DEFAULT(map), &ulm ) );
1962   }
1963
1964   GArray *array = g_array_new (FALSE, TRUE, sizeof(gchar*));
1965   g_array_append_val ( array, source );
1966
1967   gchar *filemsg = NULL;
1968   gchar *timemsg = NULL;
1969
1970   if ( g_file_test ( filename, G_FILE_TEST_EXISTS ) ) {
1971     filemsg = g_strconcat ( "Tile File: ", filename, NULL );
1972     // Get some timestamp information of the tile
1973     struct stat stat_buf;
1974     if ( g_stat ( filename, &stat_buf ) == 0 ) {
1975       gchar time_buf[64];
1976       strftime ( time_buf, sizeof(time_buf), "%c", gmtime((const time_t *)&stat_buf.st_mtime) );
1977       timemsg = g_strdup_printf ( _("Tile File Timestamp: %s"), time_buf );
1978     }
1979     else {
1980       timemsg = g_strdup ( _("Tile File Timestamp: Not Available") );
1981     }
1982     g_array_append_val ( array, filemsg );
1983     g_array_append_val ( array, timemsg );
1984   }
1985   else {
1986     filemsg = g_strdup_printf ( "Tile File: %s [Not Available]", filename );
1987     g_array_append_val ( array, filemsg );
1988   }
1989
1990   a_dialog_list (  VIK_GTK_WINDOW_FROM_LAYER(vml), _("Tile Information"), array, 5 );
1991   g_array_free ( array, FALSE );
1992
1993   g_free ( timemsg );
1994   g_free ( filemsg );
1995   g_free ( source );
1996   g_free ( filename );
1997 }
1998
1999 static gboolean maps_layer_download_release ( VikMapsLayer *vml, GdkEventButton *event, VikViewport *vvp )
2000 {
2001   if (!vml || vml->vl.type != VIK_LAYER_MAPS)
2002     return FALSE;
2003   if ( vml->dl_tool_x != -1 && vml->dl_tool_y != -1 )
2004   {
2005     if ( event->button == 1 )
2006     {
2007       VikCoord ul, br;
2008       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 );
2009       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 );
2010       start_download_thread ( vml, vvp, &ul, &br, DOWNLOAD_OR_REFRESH );
2011       vml->dl_tool_x = vml->dl_tool_y = -1;
2012       return TRUE;
2013     }
2014     else
2015     {
2016       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) );
2017       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) );
2018
2019       vml->redownload_vvp = vvp;
2020
2021       vml->dl_tool_x = vml->dl_tool_y = -1;
2022
2023       if ( ! vml->dl_right_click_menu ) {
2024         GtkWidget *item;
2025         vml->dl_right_click_menu = GTK_MENU ( gtk_menu_new () );
2026
2027         item = gtk_menu_item_new_with_mnemonic ( _("Redownload _Bad Map(s)") );
2028         g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_redownload_bad), vml );
2029         gtk_menu_shell_append ( GTK_MENU_SHELL(vml->dl_right_click_menu), item );
2030
2031         item = gtk_menu_item_new_with_mnemonic ( _("Redownload _New Map(s)") );
2032         g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_redownload_new), vml );
2033         gtk_menu_shell_append ( GTK_MENU_SHELL(vml->dl_right_click_menu), item );
2034
2035         item = gtk_menu_item_new_with_mnemonic ( _("Redownload _All Map(s)") );
2036         g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_redownload_all), vml );
2037         gtk_menu_shell_append ( GTK_MENU_SHELL(vml->dl_right_click_menu), item );
2038
2039         item = gtk_image_menu_item_new_with_mnemonic ( _("_Show Tile Information") );
2040         gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INFO, GTK_ICON_SIZE_MENU) );
2041         g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_tile_info), vml );
2042         gtk_menu_shell_append (GTK_MENU_SHELL(vml->dl_right_click_menu), item);
2043       }
2044
2045       gtk_menu_popup ( vml->dl_right_click_menu, NULL, NULL, NULL, NULL, event->button, event->time );
2046       gtk_widget_show_all ( GTK_WIDGET(vml->dl_right_click_menu) );
2047     }
2048   }
2049   return FALSE;
2050 }
2051
2052 static gpointer maps_layer_download_create ( VikWindow *vw, VikViewport *vvp)
2053 {
2054   return vvp;
2055 }
2056
2057 static gboolean maps_layer_download_click ( VikMapsLayer *vml, GdkEventButton *event, VikViewport *vvp )
2058 {
2059   MapCoord tmp;
2060   if (!vml || vml->vl.type != VIK_LAYER_MAPS)
2061     return FALSE;
2062   VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
2063   if ( vik_map_source_get_drawmode(map) == vik_viewport_get_drawmode ( vvp ) &&
2064        vik_map_source_coord_to_mapcoord ( map, vik_viewport_get_center ( vvp ),
2065            vml->xmapzoom ? vml->xmapzoom : vik_viewport_get_xmpp ( vvp ),
2066            vml->ymapzoom ? vml->ymapzoom : vik_viewport_get_ympp ( vvp ),
2067            &tmp ) ) {
2068     vml->dl_tool_x = event->x, vml->dl_tool_y = event->y;
2069     return TRUE;
2070   }
2071   return FALSE;
2072 }
2073
2074 // A slightly better way of defining the menu callback information
2075 // This should be easier to extend/rework compared to previously
2076 typedef enum {
2077   MA_VML = 0,
2078   MA_VVP,
2079   MA_LAST
2080 } menu_array_index;
2081
2082 typedef gpointer menu_array_values[MA_LAST];
2083
2084 static void download_onscreen_maps ( menu_array_values values, gint redownload )
2085 {
2086   VikMapsLayer *vml = VIK_MAPS_LAYER(values[MA_VML]);
2087   VikViewport *vvp = VIK_VIEWPORT(values[MA_VVP]);
2088   VikViewportDrawMode vp_drawmode = vik_viewport_get_drawmode ( vvp );
2089
2090   gdouble xzoom = vml->xmapzoom ? vml->xmapzoom : vik_viewport_get_xmpp ( vvp );
2091   gdouble yzoom = vml->ymapzoom ? vml->ymapzoom : vik_viewport_get_ympp ( vvp );
2092
2093   VikCoord ul, br;
2094   MapCoord ulm, brm;
2095
2096   vik_viewport_screen_to_coord ( vvp, 0, 0, &ul );
2097   vik_viewport_screen_to_coord ( vvp, vik_viewport_get_width(vvp), vik_viewport_get_height(vvp), &br );
2098
2099   VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
2100   if ( vik_map_source_get_drawmode(map) == vp_drawmode &&
2101        vik_map_source_coord_to_mapcoord ( map, &ul, xzoom, yzoom, &ulm ) &&
2102        vik_map_source_coord_to_mapcoord ( map, &br, xzoom, yzoom, &brm ) )
2103     start_download_thread ( vml, vvp, &ul, &br, redownload );
2104   else if (vik_map_source_get_drawmode(map) != vp_drawmode) {
2105     const gchar *drawmode_name = vik_viewport_get_drawmode_name (vvp, vik_map_source_get_drawmode(map));
2106     gchar *err = g_strdup_printf(_("Wrong drawmode for this map.\nSelect \"%s\" from View menu and try again."), _(drawmode_name));
2107     a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vml), err );
2108     g_free(err);
2109   }
2110   else
2111     a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vml), _("Wrong zoom level for this map.") );
2112
2113 }
2114
2115 static void maps_layer_download_missing_onscreen_maps ( menu_array_values values )
2116 {
2117   download_onscreen_maps( values, REDOWNLOAD_NONE);
2118 }
2119
2120 static void maps_layer_download_new_onscreen_maps ( menu_array_values values )
2121 {
2122   download_onscreen_maps( values, REDOWNLOAD_NEW);
2123 }
2124
2125 static void maps_layer_redownload_all_onscreen_maps ( menu_array_values values )
2126 {
2127   download_onscreen_maps( values, REDOWNLOAD_ALL);
2128 }
2129
2130 static void maps_layer_about ( gpointer vml_vvp[2] )
2131 {
2132   VikMapsLayer *vml = vml_vvp[0];
2133   VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
2134
2135   if ( vik_map_source_get_license (map) )
2136     maps_show_license ( VIK_GTK_WINDOW_FROM_LAYER(vml), map );
2137   else
2138     a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(vml),
2139                         vik_map_source_get_label (map) );
2140 }
2141
2142 /**
2143  * maps_layer_how_many_maps:
2144  * Copied from maps_layer_download_section but without the actual download and this returns a value
2145  */
2146 static gint maps_layer_how_many_maps ( VikMapsLayer *vml, VikViewport *vvp, VikCoord *ul, VikCoord *br, gdouble zoom, gint redownload )
2147 {
2148   MapCoord ulm, brm;
2149   VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
2150
2151   if ( vik_map_source_is_direct_file_access ( map ) )
2152     return 0;
2153
2154   if (!vik_map_source_coord_to_mapcoord(map, ul, zoom, zoom, &ulm)
2155     || !vik_map_source_coord_to_mapcoord(map, br, zoom, zoom, &brm)) {
2156     g_warning("%s() coord_to_mapcoord() failed", __PRETTY_FUNCTION__);
2157     return 0;
2158   }
2159
2160   MapDownloadInfo *mdi = g_malloc(sizeof(MapDownloadInfo));
2161   gint i, j;
2162
2163   mdi->vml = vml;
2164   mdi->vvp = vvp;
2165   mdi->map_layer_alive = TRUE;
2166   mdi->mutex = vik_mutex_new();
2167   mdi->refresh_display = FALSE;
2168
2169   mdi->cache_dir = g_strdup ( vml->cache_dir );
2170   mdi->maxlen = strlen ( vml->cache_dir ) + 40;
2171   mdi->filename_buf = g_malloc ( mdi->maxlen * sizeof(gchar) );
2172   mdi->maptype = vml->maptype;
2173   mdi->cache_layout = vml->cache_layout;
2174
2175   mdi->mapcoord = ulm;
2176   mdi->redownload = redownload;
2177
2178   mdi->x0 = MIN(ulm.x, brm.x);
2179   mdi->xf = MAX(ulm.x, brm.x);
2180   mdi->y0 = MIN(ulm.y, brm.y);
2181   mdi->yf = MAX(ulm.y, brm.y);
2182
2183   mdi->mapstoget = 0;
2184
2185   if ( mdi->redownload == REDOWNLOAD_ALL ) {
2186     mdi->mapstoget = (mdi->xf - mdi->x0 + 1) * (mdi->yf - mdi->y0 + 1);
2187   }
2188   else {
2189     /* calculate how many we need */
2190     MapCoord mcoord = mdi->mapcoord;
2191     for (i = mdi->x0; i <= mdi->xf; i++) {
2192       mcoord.x = i;
2193       for (j = mdi->y0; j <= mdi->yf; j++) {
2194         mcoord.y = j;
2195         // Only count tiles from supported areas
2196         if ( is_in_area ( map, mcoord ) ) {
2197           get_filename ( mdi->cache_dir, mdi->cache_layout,
2198                          vik_map_source_get_uniq_id(map),
2199                          vik_map_source_get_name(map),
2200                          ulm.scale, ulm.z, i, j, mdi->filename_buf, mdi->maxlen,
2201                          vik_map_source_get_file_extension(map) );
2202           if ( mdi->redownload == REDOWNLOAD_NEW ) {
2203             // Assume the worst - always a new file
2204             // Absolute value would require a server lookup - but that is too slow
2205             mdi->mapstoget++;
2206           }
2207           else {
2208             if ( g_file_test ( mdi->filename_buf, G_FILE_TEST_EXISTS ) == FALSE ) {
2209               // Missing
2210               mdi->mapstoget++;
2211             }
2212             else {
2213               if ( mdi->redownload == REDOWNLOAD_BAD ) {
2214                 /* see if this one is bad or what */
2215                 GError *gx = NULL;
2216                 GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file ( mdi->filename_buf, &gx );
2217                 if (gx || (!pixbuf)) {
2218                   mdi->mapstoget++;
2219                 }
2220                 break;
2221                 // Other download cases already considered or just ignored
2222               }
2223             }
2224           }
2225         }
2226       }
2227     }
2228   }
2229
2230   gint rv = mdi->mapstoget;
2231
2232   mdi_free ( mdi );
2233
2234   return rv;
2235 }
2236
2237 /**
2238  * maps_dialog_zoom_between:
2239  * This dialog is specific to the map layer, so it's here rather than in dialog.c
2240  */
2241 gboolean maps_dialog_zoom_between ( GtkWindow *parent,
2242                                     gchar *title,
2243                                     gchar *zoom_list[],
2244                                     gint default_zoom1,
2245                                     gint default_zoom2,
2246                                     gint *selected_zoom1,
2247                                     gint *selected_zoom2,
2248                                     gchar *download_list[],
2249                                     gint default_download,
2250                                     gint *selected_download )
2251 {
2252   GtkWidget *dialog = gtk_dialog_new_with_buttons ( title,
2253                                                     parent,
2254                                                     GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
2255                                                     GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
2256                                                     GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
2257                                                     NULL );
2258   gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
2259   GtkWidget *response_w = NULL;
2260 #if GTK_CHECK_VERSION (2, 20, 0)
2261   response_w = gtk_dialog_get_widget_for_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
2262 #endif
2263   GtkWidget *zoom_label1 = gtk_label_new ( _("Zoom Start:") );
2264   GtkWidget *zoom_combo1 = vik_combo_box_text_new();
2265   gchar **s;
2266   for (s = zoom_list; *s; s++)
2267     vik_combo_box_text_append ( zoom_combo1, *s );
2268   gtk_combo_box_set_active ( GTK_COMBO_BOX(zoom_combo1), default_zoom1 );
2269
2270   GtkWidget *zoom_label2 = gtk_label_new ( _("Zoom End:") );
2271   GtkWidget *zoom_combo2 = vik_combo_box_text_new();
2272   for (s = zoom_list; *s; s++)
2273     vik_combo_box_text_append ( zoom_combo2, *s );
2274   gtk_combo_box_set_active ( GTK_COMBO_BOX(zoom_combo2), default_zoom2 );
2275
2276   GtkWidget *download_label = gtk_label_new(_("Download Maps Method:"));
2277   GtkWidget *download_combo = vik_combo_box_text_new();
2278   for (s = download_list; *s; s++)
2279     vik_combo_box_text_append ( download_combo, *s );
2280   gtk_combo_box_set_active ( GTK_COMBO_BOX(download_combo), default_download );
2281
2282   GtkTable *box = GTK_TABLE(gtk_table_new(3, 2, FALSE));
2283   gtk_table_attach_defaults (box, GTK_WIDGET(zoom_label1), 0, 1, 0, 1);
2284   gtk_table_attach_defaults (box, GTK_WIDGET(zoom_combo1), 1, 2, 0, 1);
2285   gtk_table_attach_defaults (box, GTK_WIDGET(zoom_label2), 0, 1, 1, 2);
2286   gtk_table_attach_defaults (box, GTK_WIDGET(zoom_combo2), 1, 2, 1, 2);
2287   gtk_table_attach_defaults (box, GTK_WIDGET(download_label), 0, 1, 2, 3);
2288   gtk_table_attach_defaults (box, GTK_WIDGET(download_combo), 1, 2, 2, 3);
2289
2290   gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), GTK_WIDGET(box), FALSE, FALSE, 5 );
2291
2292   if ( response_w )
2293     gtk_widget_grab_focus ( response_w );
2294
2295   gtk_widget_show_all ( dialog );
2296   if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) != GTK_RESPONSE_ACCEPT ) {
2297     gtk_widget_destroy(dialog);
2298     return FALSE;
2299   }
2300
2301   // Return selected options
2302   *selected_zoom1 = gtk_combo_box_get_active ( GTK_COMBO_BOX(zoom_combo1) );
2303   *selected_zoom2 = gtk_combo_box_get_active ( GTK_COMBO_BOX(zoom_combo2) );
2304   *selected_download = gtk_combo_box_get_active ( GTK_COMBO_BOX(download_combo) );
2305
2306   gtk_widget_destroy(dialog);
2307   return TRUE;
2308 }
2309
2310 // My best guess of sensible limits
2311 #define REALLY_LARGE_AMOUNT_OF_TILES 5000
2312 #define CONFIRM_LARGE_AMOUNT_OF_TILES 500
2313
2314 /**
2315  * Get all maps in the region for zoom levels specified by the user
2316  * Sort of similar to trw_layer_download_map_along_track_cb function
2317  */
2318 static void maps_layer_download_all ( menu_array_values values )
2319 {
2320   VikMapsLayer *vml = VIK_MAPS_LAYER(values[MA_VML]);
2321   VikViewport *vvp = VIK_VIEWPORT(values[MA_VVP]);
2322
2323   // I don't think we should allow users to hammer the servers too much...
2324   // Delibrately not allowing lowest zoom levels
2325   // Still can give massive numbers to download
2326   // A screen size of 1600x1200 gives around 300,000 tiles between 1..128 when none exist before !!
2327   gchar *zoom_list[] = {"1", "2", "4", "8", "16", "32", "64", "128", "256", "512", "1024", NULL };
2328   gdouble zoom_vals[] = {1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024};
2329
2330   gint selected_zoom1, selected_zoom2, default_zoom, lower_zoom;
2331   gint selected_download_method;
2332   
2333   gdouble cur_zoom = vik_viewport_get_zoom(vvp);
2334
2335   for (default_zoom = 0; default_zoom < sizeof(zoom_vals)/sizeof(gdouble); default_zoom++) {
2336     if (cur_zoom == zoom_vals[default_zoom])
2337       break;
2338   }
2339   default_zoom = (default_zoom == sizeof(zoom_vals)/sizeof(gdouble)) ? sizeof(zoom_vals)/sizeof(gdouble) - 1 : default_zoom;
2340
2341   // Default to only 2 zoom levels below the current one
2342   if (default_zoom > 1 )
2343     lower_zoom = default_zoom - 2;
2344   else
2345     lower_zoom = default_zoom;
2346
2347   // redownload method - needs to align with REDOWNLOAD* macro values
2348   gchar *download_list[] = { _("Missing"), _("Bad"), _("New"), _("Reload All"), NULL };
2349
2350   gchar *title = g_strdup_printf ( ("%s: %s"), vik_maps_layer_get_map_label (vml), _("Download for Zoom Levels") );
2351
2352   if ( ! maps_dialog_zoom_between ( VIK_GTK_WINDOW_FROM_LAYER(vml),
2353                                     title,
2354                                     zoom_list,
2355                                     lower_zoom,
2356                                     default_zoom,
2357                                     &selected_zoom1,
2358                                     &selected_zoom2,
2359                                     download_list,
2360                                     REDOWNLOAD_NONE, // AKA Missing
2361                                     &selected_download_method ) ) {
2362     // Cancelled
2363     g_free ( title );
2364     return;
2365   }
2366   g_free ( title );
2367
2368   // Find out new current positions
2369   gdouble min_lat, max_lat, min_lon, max_lon;
2370   VikCoord vc_ul, vc_br;
2371   vik_viewport_get_min_max_lat_lon ( vvp, &min_lat, &max_lat, &min_lon, &max_lon );
2372   struct LatLon ll_ul = { max_lat, min_lon };
2373   struct LatLon ll_br = { min_lat, max_lon };
2374   vik_coord_load_from_latlon ( &vc_ul, vik_viewport_get_coord_mode (vvp), &ll_ul );
2375   vik_coord_load_from_latlon ( &vc_br, vik_viewport_get_coord_mode (vvp), &ll_br );
2376
2377   // Get Maps Count - call for each zoom level (in reverse)
2378   // With REDOWNLOAD_NEW this is a possible maximum
2379   // With REDOWNLOAD_NONE this only missing ones - however still has a server lookup per tile
2380   gint map_count = 0;
2381   gint zz;
2382   for ( zz = selected_zoom2; zz >= selected_zoom1; zz-- ) {
2383     map_count = map_count + maps_layer_how_many_maps ( vml, vvp, &vc_ul, &vc_br, zoom_vals[zz], selected_download_method );
2384   }
2385
2386   g_debug ("vikmapslayer: download request map count %d for method %d", map_count, selected_download_method);
2387
2388   // Absolute protection of hammering a map server
2389   if ( map_count > REALLY_LARGE_AMOUNT_OF_TILES ) {
2390     gchar *str = g_strdup_printf (_("You are not allowed to download more than %d tiles in one go (requested %d)"), REALLY_LARGE_AMOUNT_OF_TILES, map_count);
2391     a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vml), str );
2392     g_free (str);
2393     return;
2394   }
2395
2396   // Confirm really want to do this
2397   if ( map_count > CONFIRM_LARGE_AMOUNT_OF_TILES ) {
2398     gchar *str = g_strdup_printf (_("Do you really want to download %d tiles?"), map_count);
2399     gboolean ans = a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vml), str, NULL );
2400     g_free (str);
2401     if ( ! ans )
2402       return;
2403   }
2404
2405   // Get Maps - call for each zoom level (in reverse)
2406   for ( zz = selected_zoom2; zz >= selected_zoom1; zz-- ) {
2407     maps_layer_download_section ( vml, vvp, &vc_ul, &vc_br, zoom_vals[zz], selected_download_method );
2408   }
2409 }
2410
2411 /**
2412  *
2413  */
2414 static void maps_layer_flush ( menu_array_values values )
2415 {
2416   VikMapsLayer *vml = VIK_MAPS_LAYER(values[MA_VML]);
2417   a_mapcache_flush_type ( vik_map_source_get_uniq_id(MAPS_LAYER_NTH_TYPE(vml->maptype)) );
2418 }
2419
2420 static void maps_layer_add_menu_items ( VikMapsLayer *vml, GtkMenu *menu, VikLayersPanel *vlp )
2421 {
2422   GtkWidget *item;
2423   static menu_array_values values;
2424   values[MA_VML] = vml;
2425   values[MA_VVP] = vik_layers_panel_get_viewport( VIK_LAYERS_PANEL(vlp) );
2426
2427   item = gtk_menu_item_new();
2428   gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
2429   gtk_widget_show ( item );
2430
2431   /* Now with icons */
2432   item = gtk_image_menu_item_new_with_mnemonic ( _("Download _Missing Onscreen Maps") );
2433     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
2434   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_download_missing_onscreen_maps), values );
2435   gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2436   gtk_widget_show ( item );
2437
2438   if ( vik_map_source_supports_download_only_new (MAPS_LAYER_NTH_TYPE(vml->maptype)) ) {
2439     item = gtk_image_menu_item_new_with_mnemonic ( _("Download _New Onscreen Maps") );
2440     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REDO, GTK_ICON_SIZE_MENU) );
2441     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_download_new_onscreen_maps), values );
2442     gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2443     gtk_widget_show ( item );
2444   }
2445
2446   item = gtk_image_menu_item_new_with_mnemonic ( _("Reload _All Onscreen Maps") );
2447   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
2448   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_redownload_all_onscreen_maps), values );
2449   gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2450   gtk_widget_show ( item );
2451
2452   item = gtk_image_menu_item_new_with_mnemonic ( _("Download Maps in _Zoom Levels...") );
2453   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DND_MULTIPLE, GTK_ICON_SIZE_MENU) );
2454   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_download_all), values );
2455   gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2456   gtk_widget_show ( item );
2457
2458   item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_ABOUT, NULL );
2459   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_about), values );
2460   gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2461   gtk_widget_show ( item );
2462
2463   // Typical users shouldn't need to use this functionality - so debug only ATM
2464   if ( vik_debug ) {
2465     item = gtk_image_menu_item_new_with_mnemonic ( _("Flush Map Cache") );
2466     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
2467     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_flush), values );
2468     gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2469     gtk_widget_show ( item );
2470   }
2471 }
2472
2473 /**
2474  * Enable downloading maps of the current screen area either 'new' or 'everything'
2475  */
2476 void vik_maps_layer_download ( VikMapsLayer *vml, VikViewport *vvp, gboolean only_new )
2477 {
2478   if ( !vml ) return;
2479   if ( !vvp ) return;
2480
2481   static menu_array_values values;
2482   values[MA_VML] = vml;
2483   values[MA_VVP] = vvp;
2484
2485   if ( only_new )
2486     // Get only new maps
2487     maps_layer_download_new_onscreen_maps ( values );
2488   else
2489     // Redownload everything
2490     maps_layer_redownload_all_onscreen_maps ( values );
2491 }