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