]> git.street.me.uk Git - andy/viking.git/blob - src/vikmapslayer.c
Add ability to remove the map cache for a single map type.
[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, 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 );
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 ( VIK_GTK_WINDOW_FROM_LAYER(vml), /* parent window */
1760                             tmp,                                              /* description string */
1761                             (vik_thr_func) map_download_thread,               /* function to call within thread */
1762                             mdi,                                              /* pass along data */
1763                             (vik_thr_free_func) mdi_free,                     /* function to free pass along data */
1764                             (vik_thr_free_func) mdi_cancel_cleanup,
1765                             mdi->mapstoget );
1766       g_free ( tmp );
1767     }
1768     else
1769       mdi_free ( mdi );
1770   }
1771 }
1772
1773 static void maps_layer_download_section ( VikMapsLayer *vml, VikViewport *vvp, VikCoord *ul, VikCoord *br, gdouble zoom, gint download_method )
1774 {
1775   MapCoord ulm, brm;
1776   VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
1777
1778   // Don't ever attempt download on direct access
1779   if ( vik_map_source_is_direct_file_access ( map ) )
1780     return;
1781
1782   if (!vik_map_source_coord_to_mapcoord(map, ul, zoom, zoom, &ulm) 
1783     || !vik_map_source_coord_to_mapcoord(map, br, zoom, zoom, &brm)) {
1784     g_warning("%s() coord_to_mapcoord() failed", __PRETTY_FUNCTION__);
1785     return;
1786   }
1787
1788   MapDownloadInfo *mdi = g_malloc(sizeof(MapDownloadInfo));
1789   gint i, j;
1790
1791   mdi->vml = vml;
1792   mdi->vvp = vvp;
1793   mdi->map_layer_alive = TRUE;
1794   mdi->mutex = vik_mutex_new();
1795   mdi->refresh_display = TRUE;
1796
1797   mdi->cache_dir = g_strdup ( vml->cache_dir );
1798   mdi->maxlen = strlen ( vml->cache_dir ) + 40;
1799   mdi->filename_buf = g_malloc ( mdi->maxlen * sizeof(gchar) );
1800   mdi->maptype = vml->maptype;
1801   mdi->cache_layout = vml->cache_layout;
1802
1803   mdi->mapcoord = ulm;
1804   mdi->redownload = download_method;
1805
1806   mdi->x0 = MIN(ulm.x, brm.x);
1807   mdi->xf = MAX(ulm.x, brm.x);
1808   mdi->y0 = MIN(ulm.y, brm.y);
1809   mdi->yf = MAX(ulm.y, brm.y);
1810
1811   mdi->mapstoget = 0;
1812
1813   MapCoord mcoord = mdi->mapcoord;
1814
1815   for (i = mdi->x0; i <= mdi->xf; i++) {
1816     mcoord.x = i;
1817     for (j = mdi->y0; j <= mdi->yf; j++) {
1818       mcoord.y = j;
1819       // Only count tiles from supported areas
1820       if ( is_in_area (map, mcoord) ) {
1821         get_filename ( mdi->cache_dir, mdi->cache_layout,
1822                        vik_map_source_get_uniq_id(map),
1823                        vik_map_source_get_name(map),
1824                        ulm.scale, ulm.z, i, j, mdi->filename_buf, mdi->maxlen,
1825                        vik_map_source_get_file_extension(map) );
1826         if ( g_file_test ( mdi->filename_buf, G_FILE_TEST_EXISTS ) == FALSE )
1827               mdi->mapstoget++;
1828       }
1829     }
1830   }
1831
1832   mdi->mapcoord.x = mdi->mapcoord.y = 0; /* for cleanup -- no current map */
1833
1834   if (mdi->mapstoget) {
1835     gchar *tmp;
1836     const gchar *fmt;
1837     fmt = ngettext("Downloading %d %s map...",
1838                    "Downloading %d %s maps...",
1839                    mdi->mapstoget);
1840     tmp = g_strdup_printf ( fmt, mdi->mapstoget, MAPS_LAYER_NTH_LABEL(vml->maptype) );
1841
1842     g_object_weak_ref(G_OBJECT(mdi->vml), weak_ref_cb, mdi);
1843       /* launch the thread */
1844     a_background_thread ( VIK_GTK_WINDOW_FROM_LAYER(vml), /* parent window */
1845       tmp,                                /* description string */
1846       (vik_thr_func) map_download_thread, /* function to call within thread */
1847       mdi,                                /* pass along data */
1848       (vik_thr_free_func) mdi_free,       /* function to free pass along data */
1849       (vik_thr_free_func) mdi_cancel_cleanup,
1850       mdi->mapstoget );
1851     g_free ( tmp );
1852   }
1853   else
1854     mdi_free ( mdi );
1855 }
1856
1857 /**
1858  * vik_maps_layer_download_section:
1859  * @vml:  The Map Layer
1860  * @vvp:  The Viewport that the map is on
1861  * @ul:   Upper left coordinate of the area to be downloaded
1862  * @br:   Bottom right coordinate of the area to be downloaded
1863  * @zoom: The zoom level at which the maps are to be download
1864  *
1865  * Download a specified map area at a certain zoom level
1866  */
1867 void vik_maps_layer_download_section ( VikMapsLayer *vml, VikViewport *vvp, VikCoord *ul, VikCoord *br, gdouble zoom )
1868 {
1869   maps_layer_download_section (vml, vvp, ul, br, zoom, REDOWNLOAD_NONE);
1870 }
1871
1872 static void maps_layer_redownload_bad ( VikMapsLayer *vml )
1873 {
1874   start_download_thread ( vml, vml->redownload_vvp, &(vml->redownload_ul), &(vml->redownload_br), REDOWNLOAD_BAD );
1875 }
1876
1877 static void maps_layer_redownload_all ( VikMapsLayer *vml )
1878 {
1879   start_download_thread ( vml, vml->redownload_vvp, &(vml->redownload_ul), &(vml->redownload_br), REDOWNLOAD_ALL );
1880 }
1881
1882 static void maps_layer_redownload_new ( VikMapsLayer *vml )
1883 {
1884   start_download_thread ( vml, vml->redownload_vvp, &(vml->redownload_ul), &(vml->redownload_br), REDOWNLOAD_NEW );
1885 }
1886
1887 /**
1888  * Display a simple dialog with information about this particular map tile
1889  */
1890 static void maps_layer_tile_info ( VikMapsLayer *vml )
1891 {
1892   VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
1893
1894   gdouble xzoom = vml->xmapzoom ? vml->xmapzoom : vik_viewport_get_xmpp ( vml->redownload_vvp );
1895   gdouble yzoom = vml->ymapzoom ? vml->ymapzoom : vik_viewport_get_ympp ( vml->redownload_vvp );
1896   MapCoord ulm;
1897
1898   if ( !vik_map_source_coord_to_mapcoord ( map, &(vml->redownload_ul), xzoom, yzoom, &ulm ) )
1899     return;
1900
1901   gchar *filename = NULL;
1902   gchar *source = NULL;
1903
1904   if ( vik_map_source_is_direct_file_access ( map ) ) {
1905     if ( vik_map_source_is_mbtiles ( map ) ) {
1906       filename = g_strdup ( vml->filename );
1907 #ifdef HAVE_SQLITE3_H
1908       // And whether to bother going into the SQL to check it's really there or not...
1909       gchar *exists = NULL;
1910       gint zoom = 17 - ulm.scale;
1911       if ( vml->mbtiles ) {
1912         GdkPixbuf *pixbuf = get_pixbuf_sql_exec ( vml->mbtiles, ulm.x, ulm.y, zoom );
1913         if ( pixbuf ) {
1914           exists = g_strdup ( _("YES") );
1915           g_object_unref ( G_OBJECT(pixbuf) );
1916         }
1917         else {
1918           exists = g_strdup ( _("NO") );
1919         }
1920       }
1921       else
1922         exists = g_strdup ( _("NO") );
1923       gint flip_y = (gint) pow(2, zoom)-1 - ulm.y;
1924       // NB Also handles .jpg automatically due to pixbuf_new_from () support - although just print png for now.
1925       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 );
1926       g_free ( exists );
1927 #else
1928       source = g_strdup ( _("Source: Not available") );
1929 #endif
1930     }
1931     else if ( vik_map_source_is_osm_meta_tiles ( map ) ) {
1932       char path[PATH_MAX];
1933       xyz_to_meta(path, sizeof(path), vml->cache_dir, ulm.x, ulm.y, 17-ulm.scale );
1934       source = g_strdup ( path );
1935       filename = g_strdup ( path );
1936     }
1937     else {
1938       guint max_path_len = strlen(vml->cache_dir) + 40;
1939       filename = g_malloc ( max_path_len * sizeof(char) );
1940       get_filename ( vml->cache_dir, VIK_MAPS_CACHE_LAYOUT_OSM,
1941                      vik_map_source_get_uniq_id(map),
1942                      NULL,
1943                      ulm.scale, ulm.z, ulm.x, ulm.y, filename, max_path_len,
1944                      vik_map_source_get_file_extension(map) );
1945       source = g_strconcat ( "Source: file://", filename, NULL );
1946     }
1947   }
1948   else {
1949     guint max_path_len = strlen(vml->cache_dir) + 40;
1950     filename = g_malloc ( max_path_len * sizeof(char) );
1951     get_filename ( vml->cache_dir, vml->cache_layout,
1952                    vik_map_source_get_uniq_id(map),
1953                    vik_map_source_get_name(map),
1954                    ulm.scale, ulm.z, ulm.x, ulm.y, filename, max_path_len,
1955                    vik_map_source_get_file_extension(map) );
1956     source = g_strdup_printf ( "Source: http://%s%s",
1957                                vik_map_source_default_get_hostname ( VIK_MAP_SOURCE_DEFAULT(map) ),
1958                                vik_map_source_default_get_uri ( VIK_MAP_SOURCE_DEFAULT(map), &ulm ) );
1959   }
1960
1961   GArray *array = g_array_new (FALSE, TRUE, sizeof(gchar*));
1962   g_array_append_val ( array, source );
1963
1964   gchar *filemsg = NULL;
1965   gchar *timemsg = NULL;
1966
1967   if ( g_file_test ( filename, G_FILE_TEST_EXISTS ) ) {
1968     filemsg = g_strconcat ( "Tile File: ", filename, NULL );
1969     // Get some timestamp information of the tile
1970     struct stat stat_buf;
1971     if ( g_stat ( filename, &stat_buf ) == 0 ) {
1972       gchar time_buf[64];
1973       strftime ( time_buf, sizeof(time_buf), "%c", gmtime((const time_t *)&stat_buf.st_mtime) );
1974       timemsg = g_strdup_printf ( _("Tile File Timestamp: %s"), time_buf );
1975     }
1976     else {
1977       timemsg = g_strdup ( _("Tile File Timestamp: Not Available") );
1978     }
1979     g_array_append_val ( array, filemsg );
1980     g_array_append_val ( array, timemsg );
1981   }
1982   else {
1983     filemsg = g_strdup_printf ( "Tile File: %s [Not Available]", filename );
1984     g_array_append_val ( array, filemsg );
1985   }
1986
1987   a_dialog_list (  VIK_GTK_WINDOW_FROM_LAYER(vml), _("Tile Information"), array, 5 );
1988   g_array_free ( array, FALSE );
1989
1990   g_free ( timemsg );
1991   g_free ( filemsg );
1992   g_free ( source );
1993   g_free ( filename );
1994 }
1995
1996 static gboolean maps_layer_download_release ( VikMapsLayer *vml, GdkEventButton *event, VikViewport *vvp )
1997 {
1998   if (!vml || vml->vl.type != VIK_LAYER_MAPS)
1999     return FALSE;
2000   if ( vml->dl_tool_x != -1 && vml->dl_tool_y != -1 )
2001   {
2002     if ( event->button == 1 )
2003     {
2004       VikCoord ul, br;
2005       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 );
2006       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 );
2007       start_download_thread ( vml, vvp, &ul, &br, DOWNLOAD_OR_REFRESH );
2008       vml->dl_tool_x = vml->dl_tool_y = -1;
2009       return TRUE;
2010     }
2011     else
2012     {
2013       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) );
2014       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) );
2015
2016       vml->redownload_vvp = vvp;
2017
2018       vml->dl_tool_x = vml->dl_tool_y = -1;
2019
2020       if ( ! vml->dl_right_click_menu ) {
2021         GtkWidget *item;
2022         vml->dl_right_click_menu = GTK_MENU ( gtk_menu_new () );
2023
2024         item = gtk_menu_item_new_with_mnemonic ( _("Redownload _Bad Map(s)") );
2025         g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_redownload_bad), vml );
2026         gtk_menu_shell_append ( GTK_MENU_SHELL(vml->dl_right_click_menu), item );
2027
2028         item = gtk_menu_item_new_with_mnemonic ( _("Redownload _New Map(s)") );
2029         g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_redownload_new), vml );
2030         gtk_menu_shell_append ( GTK_MENU_SHELL(vml->dl_right_click_menu), item );
2031
2032         item = gtk_menu_item_new_with_mnemonic ( _("Redownload _All Map(s)") );
2033         g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_redownload_all), vml );
2034         gtk_menu_shell_append ( GTK_MENU_SHELL(vml->dl_right_click_menu), item );
2035
2036         item = gtk_image_menu_item_new_with_mnemonic ( _("_Show Tile Information") );
2037         gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INFO, GTK_ICON_SIZE_MENU) );
2038         g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_tile_info), vml );
2039         gtk_menu_shell_append (GTK_MENU_SHELL(vml->dl_right_click_menu), item);
2040       }
2041
2042       gtk_menu_popup ( vml->dl_right_click_menu, NULL, NULL, NULL, NULL, event->button, event->time );
2043       gtk_widget_show_all ( GTK_WIDGET(vml->dl_right_click_menu) );
2044     }
2045   }
2046   return FALSE;
2047 }
2048
2049 static gpointer maps_layer_download_create ( VikWindow *vw, VikViewport *vvp)
2050 {
2051   return vvp;
2052 }
2053
2054 static gboolean maps_layer_download_click ( VikMapsLayer *vml, GdkEventButton *event, VikViewport *vvp )
2055 {
2056   MapCoord tmp;
2057   if (!vml || vml->vl.type != VIK_LAYER_MAPS)
2058     return FALSE;
2059   VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
2060   if ( vik_map_source_get_drawmode(map) == vik_viewport_get_drawmode ( vvp ) &&
2061        vik_map_source_coord_to_mapcoord ( map, vik_viewport_get_center ( vvp ),
2062            vml->xmapzoom ? vml->xmapzoom : vik_viewport_get_xmpp ( vvp ),
2063            vml->ymapzoom ? vml->ymapzoom : vik_viewport_get_ympp ( vvp ),
2064            &tmp ) ) {
2065     vml->dl_tool_x = event->x, vml->dl_tool_y = event->y;
2066     return TRUE;
2067   }
2068   return FALSE;
2069 }
2070
2071 // A slightly better way of defining the menu callback information
2072 // This should be easier to extend/rework compared to previously
2073 typedef enum {
2074   MA_VML = 0,
2075   MA_VVP,
2076   MA_LAST
2077 } menu_array_index;
2078
2079 typedef gpointer menu_array_values[MA_LAST];
2080
2081 static void download_onscreen_maps ( menu_array_values values, gint redownload )
2082 {
2083   VikMapsLayer *vml = VIK_MAPS_LAYER(values[MA_VML]);
2084   VikViewport *vvp = VIK_VIEWPORT(values[MA_VVP]);
2085   VikViewportDrawMode vp_drawmode = vik_viewport_get_drawmode ( vvp );
2086
2087   gdouble xzoom = vml->xmapzoom ? vml->xmapzoom : vik_viewport_get_xmpp ( vvp );
2088   gdouble yzoom = vml->ymapzoom ? vml->ymapzoom : vik_viewport_get_ympp ( vvp );
2089
2090   VikCoord ul, br;
2091   MapCoord ulm, brm;
2092
2093   vik_viewport_screen_to_coord ( vvp, 0, 0, &ul );
2094   vik_viewport_screen_to_coord ( vvp, vik_viewport_get_width(vvp), vik_viewport_get_height(vvp), &br );
2095
2096   VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
2097   if ( vik_map_source_get_drawmode(map) == vp_drawmode &&
2098        vik_map_source_coord_to_mapcoord ( map, &ul, xzoom, yzoom, &ulm ) &&
2099        vik_map_source_coord_to_mapcoord ( map, &br, xzoom, yzoom, &brm ) )
2100     start_download_thread ( vml, vvp, &ul, &br, redownload );
2101   else if (vik_map_source_get_drawmode(map) != vp_drawmode) {
2102     const gchar *drawmode_name = vik_viewport_get_drawmode_name (vvp, vik_map_source_get_drawmode(map));
2103     gchar *err = g_strdup_printf(_("Wrong drawmode for this map.\nSelect \"%s\" from View menu and try again."), _(drawmode_name));
2104     a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vml), err );
2105     g_free(err);
2106   }
2107   else
2108     a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vml), _("Wrong zoom level for this map.") );
2109
2110 }
2111
2112 static void maps_layer_download_missing_onscreen_maps ( menu_array_values values )
2113 {
2114   download_onscreen_maps( values, REDOWNLOAD_NONE);
2115 }
2116
2117 static void maps_layer_download_new_onscreen_maps ( menu_array_values values )
2118 {
2119   download_onscreen_maps( values, REDOWNLOAD_NEW);
2120 }
2121
2122 static void maps_layer_redownload_all_onscreen_maps ( menu_array_values values )
2123 {
2124   download_onscreen_maps( values, REDOWNLOAD_ALL);
2125 }
2126
2127 static void maps_layer_about ( gpointer vml_vvp[2] )
2128 {
2129   VikMapsLayer *vml = vml_vvp[0];
2130   VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
2131
2132   if ( vik_map_source_get_license (map) )
2133     maps_show_license ( VIK_GTK_WINDOW_FROM_LAYER(vml), map );
2134   else
2135     a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(vml),
2136                         vik_map_source_get_label (map) );
2137 }
2138
2139 /**
2140  * maps_layer_how_many_maps:
2141  * Copied from maps_layer_download_section but without the actual download and this returns a value
2142  */
2143 static gint maps_layer_how_many_maps ( VikMapsLayer *vml, VikViewport *vvp, VikCoord *ul, VikCoord *br, gdouble zoom, gint redownload )
2144 {
2145   MapCoord ulm, brm;
2146   VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
2147
2148   if ( vik_map_source_is_direct_file_access ( map ) )
2149     return 0;
2150
2151   if (!vik_map_source_coord_to_mapcoord(map, ul, zoom, zoom, &ulm)
2152     || !vik_map_source_coord_to_mapcoord(map, br, zoom, zoom, &brm)) {
2153     g_warning("%s() coord_to_mapcoord() failed", __PRETTY_FUNCTION__);
2154     return 0;
2155   }
2156
2157   MapDownloadInfo *mdi = g_malloc(sizeof(MapDownloadInfo));
2158   gint i, j;
2159
2160   mdi->vml = vml;
2161   mdi->vvp = vvp;
2162   mdi->map_layer_alive = TRUE;
2163   mdi->mutex = vik_mutex_new();
2164   mdi->refresh_display = FALSE;
2165
2166   mdi->cache_dir = g_strdup ( vml->cache_dir );
2167   mdi->maxlen = strlen ( vml->cache_dir ) + 40;
2168   mdi->filename_buf = g_malloc ( mdi->maxlen * sizeof(gchar) );
2169   mdi->maptype = vml->maptype;
2170   mdi->cache_layout = vml->cache_layout;
2171
2172   mdi->mapcoord = ulm;
2173   mdi->redownload = redownload;
2174
2175   mdi->x0 = MIN(ulm.x, brm.x);
2176   mdi->xf = MAX(ulm.x, brm.x);
2177   mdi->y0 = MIN(ulm.y, brm.y);
2178   mdi->yf = MAX(ulm.y, brm.y);
2179
2180   mdi->mapstoget = 0;
2181
2182   if ( mdi->redownload == REDOWNLOAD_ALL ) {
2183     mdi->mapstoget = (mdi->xf - mdi->x0 + 1) * (mdi->yf - mdi->y0 + 1);
2184   }
2185   else {
2186     /* calculate how many we need */
2187     MapCoord mcoord = mdi->mapcoord;
2188     for (i = mdi->x0; i <= mdi->xf; i++) {
2189       mcoord.x = i;
2190       for (j = mdi->y0; j <= mdi->yf; j++) {
2191         mcoord.y = j;
2192         // Only count tiles from supported areas
2193         if ( is_in_area ( map, mcoord ) ) {
2194           get_filename ( mdi->cache_dir, mdi->cache_layout,
2195                          vik_map_source_get_uniq_id(map),
2196                          vik_map_source_get_name(map),
2197                          ulm.scale, ulm.z, i, j, mdi->filename_buf, mdi->maxlen,
2198                          vik_map_source_get_file_extension(map) );
2199           if ( mdi->redownload == REDOWNLOAD_NEW ) {
2200             // Assume the worst - always a new file
2201             // Absolute value would require a server lookup - but that is too slow
2202             mdi->mapstoget++;
2203           }
2204           else {
2205             if ( g_file_test ( mdi->filename_buf, G_FILE_TEST_EXISTS ) == FALSE ) {
2206               // Missing
2207               mdi->mapstoget++;
2208             }
2209             else {
2210               if ( mdi->redownload == REDOWNLOAD_BAD ) {
2211                 /* see if this one is bad or what */
2212                 GError *gx = NULL;
2213                 GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file ( mdi->filename_buf, &gx );
2214                 if (gx || (!pixbuf)) {
2215                   mdi->mapstoget++;
2216                 }
2217                 break;
2218                 // Other download cases already considered or just ignored
2219               }
2220             }
2221           }
2222         }
2223       }
2224     }
2225   }
2226
2227   gint rv = mdi->mapstoget;
2228
2229   mdi_free ( mdi );
2230
2231   return rv;
2232 }
2233
2234 /**
2235  * maps_dialog_zoom_between:
2236  * This dialog is specific to the map layer, so it's here rather than in dialog.c
2237  */
2238 gboolean maps_dialog_zoom_between ( GtkWindow *parent,
2239                                     gchar *title,
2240                                     gchar *zoom_list[],
2241                                     gint default_zoom1,
2242                                     gint default_zoom2,
2243                                     gint *selected_zoom1,
2244                                     gint *selected_zoom2,
2245                                     gchar *download_list[],
2246                                     gint default_download,
2247                                     gint *selected_download )
2248 {
2249   GtkWidget *dialog = gtk_dialog_new_with_buttons ( title,
2250                                                     parent,
2251                                                     GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
2252                                                     GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
2253                                                     GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
2254                                                     NULL );
2255   gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
2256   GtkWidget *response_w = NULL;
2257 #if GTK_CHECK_VERSION (2, 20, 0)
2258   response_w = gtk_dialog_get_widget_for_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
2259 #endif
2260   GtkWidget *zoom_label1 = gtk_label_new ( _("Zoom Start:") );
2261   GtkWidget *zoom_combo1 = vik_combo_box_text_new();
2262   gchar **s;
2263   for (s = zoom_list; *s; s++)
2264     vik_combo_box_text_append ( zoom_combo1, *s );
2265   gtk_combo_box_set_active ( GTK_COMBO_BOX(zoom_combo1), default_zoom1 );
2266
2267   GtkWidget *zoom_label2 = gtk_label_new ( _("Zoom End:") );
2268   GtkWidget *zoom_combo2 = vik_combo_box_text_new();
2269   for (s = zoom_list; *s; s++)
2270     vik_combo_box_text_append ( zoom_combo2, *s );
2271   gtk_combo_box_set_active ( GTK_COMBO_BOX(zoom_combo2), default_zoom2 );
2272
2273   GtkWidget *download_label = gtk_label_new(_("Download Maps Method:"));
2274   GtkWidget *download_combo = vik_combo_box_text_new();
2275   for (s = download_list; *s; s++)
2276     vik_combo_box_text_append ( download_combo, *s );
2277   gtk_combo_box_set_active ( GTK_COMBO_BOX(download_combo), default_download );
2278
2279   GtkTable *box = GTK_TABLE(gtk_table_new(3, 2, FALSE));
2280   gtk_table_attach_defaults (box, GTK_WIDGET(zoom_label1), 0, 1, 0, 1);
2281   gtk_table_attach_defaults (box, GTK_WIDGET(zoom_combo1), 1, 2, 0, 1);
2282   gtk_table_attach_defaults (box, GTK_WIDGET(zoom_label2), 0, 1, 1, 2);
2283   gtk_table_attach_defaults (box, GTK_WIDGET(zoom_combo2), 1, 2, 1, 2);
2284   gtk_table_attach_defaults (box, GTK_WIDGET(download_label), 0, 1, 2, 3);
2285   gtk_table_attach_defaults (box, GTK_WIDGET(download_combo), 1, 2, 2, 3);
2286
2287   gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), GTK_WIDGET(box), FALSE, FALSE, 5 );
2288
2289   if ( response_w )
2290     gtk_widget_grab_focus ( response_w );
2291
2292   gtk_widget_show_all ( dialog );
2293   if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) != GTK_RESPONSE_ACCEPT ) {
2294     gtk_widget_destroy(dialog);
2295     return FALSE;
2296   }
2297
2298   // Return selected options
2299   *selected_zoom1 = gtk_combo_box_get_active ( GTK_COMBO_BOX(zoom_combo1) );
2300   *selected_zoom2 = gtk_combo_box_get_active ( GTK_COMBO_BOX(zoom_combo2) );
2301   *selected_download = gtk_combo_box_get_active ( GTK_COMBO_BOX(download_combo) );
2302
2303   gtk_widget_destroy(dialog);
2304   return TRUE;
2305 }
2306
2307 // My best guess of sensible limits
2308 #define REALLY_LARGE_AMOUNT_OF_TILES 5000
2309 #define CONFIRM_LARGE_AMOUNT_OF_TILES 500
2310
2311 /**
2312  * Get all maps in the region for zoom levels specified by the user
2313  * Sort of similar to trw_layer_download_map_along_track_cb function
2314  */
2315 static void maps_layer_download_all ( menu_array_values values )
2316 {
2317   VikMapsLayer *vml = VIK_MAPS_LAYER(values[MA_VML]);
2318   VikViewport *vvp = VIK_VIEWPORT(values[MA_VVP]);
2319
2320   // I don't think we should allow users to hammer the servers too much...
2321   // Delibrately not allowing lowest zoom levels
2322   // Still can give massive numbers to download
2323   // A screen size of 1600x1200 gives around 300,000 tiles between 1..128 when none exist before !!
2324   gchar *zoom_list[] = {"1", "2", "4", "8", "16", "32", "64", "128", "256", "512", "1024", NULL };
2325   gdouble zoom_vals[] = {1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024};
2326
2327   gint selected_zoom1, selected_zoom2, default_zoom, lower_zoom;
2328   gint selected_download_method;
2329   
2330   gdouble cur_zoom = vik_viewport_get_zoom(vvp);
2331
2332   for (default_zoom = 0; default_zoom < sizeof(zoom_vals)/sizeof(gdouble); default_zoom++) {
2333     if (cur_zoom == zoom_vals[default_zoom])
2334       break;
2335   }
2336   default_zoom = (default_zoom == sizeof(zoom_vals)/sizeof(gdouble)) ? sizeof(zoom_vals)/sizeof(gdouble) - 1 : default_zoom;
2337
2338   // Default to only 2 zoom levels below the current one
2339   if (default_zoom > 1 )
2340     lower_zoom = default_zoom - 2;
2341   else
2342     lower_zoom = default_zoom;
2343
2344   // redownload method - needs to align with REDOWNLOAD* macro values
2345   gchar *download_list[] = { _("Missing"), _("Bad"), _("New"), _("Reload All"), NULL };
2346
2347   gchar *title = g_strdup_printf ( ("%s: %s"), vik_maps_layer_get_map_label (vml), _("Download for Zoom Levels") );
2348
2349   if ( ! maps_dialog_zoom_between ( VIK_GTK_WINDOW_FROM_LAYER(vml),
2350                                     title,
2351                                     zoom_list,
2352                                     lower_zoom,
2353                                     default_zoom,
2354                                     &selected_zoom1,
2355                                     &selected_zoom2,
2356                                     download_list,
2357                                     REDOWNLOAD_NONE, // AKA Missing
2358                                     &selected_download_method ) ) {
2359     // Cancelled
2360     g_free ( title );
2361     return;
2362   }
2363   g_free ( title );
2364
2365   // Find out new current positions
2366   gdouble min_lat, max_lat, min_lon, max_lon;
2367   VikCoord vc_ul, vc_br;
2368   vik_viewport_get_min_max_lat_lon ( vvp, &min_lat, &max_lat, &min_lon, &max_lon );
2369   struct LatLon ll_ul = { max_lat, min_lon };
2370   struct LatLon ll_br = { min_lat, max_lon };
2371   vik_coord_load_from_latlon ( &vc_ul, vik_viewport_get_coord_mode (vvp), &ll_ul );
2372   vik_coord_load_from_latlon ( &vc_br, vik_viewport_get_coord_mode (vvp), &ll_br );
2373
2374   // Get Maps Count - call for each zoom level (in reverse)
2375   // With REDOWNLOAD_NEW this is a possible maximum
2376   // With REDOWNLOAD_NONE this only missing ones - however still has a server lookup per tile
2377   gint map_count = 0;
2378   gint zz;
2379   for ( zz = selected_zoom2; zz >= selected_zoom1; zz-- ) {
2380     map_count = map_count + maps_layer_how_many_maps ( vml, vvp, &vc_ul, &vc_br, zoom_vals[zz], selected_download_method );
2381   }
2382
2383   g_debug ("vikmapslayer: download request map count %d for method %d", map_count, selected_download_method);
2384
2385   // Absolute protection of hammering a map server
2386   if ( map_count > REALLY_LARGE_AMOUNT_OF_TILES ) {
2387     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);
2388     a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vml), str );
2389     g_free (str);
2390     return;
2391   }
2392
2393   // Confirm really want to do this
2394   if ( map_count > CONFIRM_LARGE_AMOUNT_OF_TILES ) {
2395     gchar *str = g_strdup_printf (_("Do you really want to download %d tiles?"), map_count);
2396     gboolean ans = a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vml), str, NULL );
2397     g_free (str);
2398     if ( ! ans )
2399       return;
2400   }
2401
2402   // Get Maps - call for each zoom level (in reverse)
2403   for ( zz = selected_zoom2; zz >= selected_zoom1; zz-- ) {
2404     maps_layer_download_section ( vml, vvp, &vc_ul, &vc_br, zoom_vals[zz], selected_download_method );
2405   }
2406 }
2407
2408 /**
2409  *
2410  */
2411 static void maps_layer_flush ( menu_array_values values )
2412 {
2413   VikMapsLayer *vml = VIK_MAPS_LAYER(values[MA_VML]);
2414   a_mapcache_flush_type ( vik_map_source_get_uniq_id(MAPS_LAYER_NTH_TYPE(vml->maptype)) );
2415 }
2416
2417 static void maps_layer_add_menu_items ( VikMapsLayer *vml, GtkMenu *menu, VikLayersPanel *vlp )
2418 {
2419   GtkWidget *item;
2420   static menu_array_values values;
2421   values[MA_VML] = vml;
2422   values[MA_VVP] = vik_layers_panel_get_viewport( VIK_LAYERS_PANEL(vlp) );
2423
2424   item = gtk_menu_item_new();
2425   gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
2426   gtk_widget_show ( item );
2427
2428   /* Now with icons */
2429   item = gtk_image_menu_item_new_with_mnemonic ( _("Download _Missing Onscreen Maps") );
2430     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
2431   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_download_missing_onscreen_maps), values );
2432   gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2433   gtk_widget_show ( item );
2434
2435   if ( vik_map_source_supports_download_only_new (MAPS_LAYER_NTH_TYPE(vml->maptype)) ) {
2436     item = gtk_image_menu_item_new_with_mnemonic ( _("Download _New Onscreen Maps") );
2437     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REDO, GTK_ICON_SIZE_MENU) );
2438     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_download_new_onscreen_maps), values );
2439     gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2440     gtk_widget_show ( item );
2441   }
2442
2443   item = gtk_image_menu_item_new_with_mnemonic ( _("Reload _All Onscreen Maps") );
2444   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
2445   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_redownload_all_onscreen_maps), values );
2446   gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2447   gtk_widget_show ( item );
2448
2449   item = gtk_image_menu_item_new_with_mnemonic ( _("Download Maps in _Zoom Levels...") );
2450   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DND_MULTIPLE, GTK_ICON_SIZE_MENU) );
2451   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_download_all), values );
2452   gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2453   gtk_widget_show ( item );
2454
2455   item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_ABOUT, NULL );
2456   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_about), values );
2457   gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2458   gtk_widget_show ( item );
2459
2460   // Typical users shouldn't need to use this functionality - so debug only ATM
2461   if ( vik_debug ) {
2462     item = gtk_image_menu_item_new_with_mnemonic ( _("Flush Map Cache") );
2463     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
2464     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_flush), values );
2465     gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2466     gtk_widget_show ( item );
2467   }
2468 }
2469
2470 /**
2471  * Enable downloading maps of the current screen area either 'new' or 'everything'
2472  */
2473 void vik_maps_layer_download ( VikMapsLayer *vml, VikViewport *vvp, gboolean only_new )
2474 {
2475   if ( !vml ) return;
2476   if ( !vvp ) return;
2477
2478   static menu_array_values values;
2479   values[MA_VML] = vml;
2480   values[MA_VVP] = vvp;
2481
2482   if ( only_new )
2483     // Get only new maps
2484     maps_layer_download_new_onscreen_maps ( values );
2485   else
2486     // Redownload everything
2487     maps_layer_redownload_all_onscreen_maps ( values );
2488 }