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