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