]> git.street.me.uk Git - andy/viking.git/blob - src/vikmapslayer.c
[QA] Remove very old unused code from vikmapslayer.c
[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, NULL },
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"), NULL, NULL, NULL },
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     default: break;
586   }
587   return TRUE;
588 }
589
590 static VikLayerParamData maps_layer_get_param ( VikMapsLayer *vml, guint16 id, gboolean is_file_operation )
591 {
592   VikLayerParamData rv;
593   switch ( id )
594   {
595     case PARAM_CACHE_DIR:
596     {
597       gboolean set = FALSE;
598       /* Only save a blank when the map cache location equals the default
599           On reading in, when it is blank then the default is reconstructed
600           Since the default changes dependent on the user and OS, it means the resultant file is more portable */
601       if ( is_file_operation && vml->cache_dir && strcmp ( vml->cache_dir, MAPS_CACHE_DIR ) == 0 ) {
602         rv.s = "";
603         set = TRUE;
604       }
605       else if ( is_file_operation ) {
606         if ( a_vik_get_file_ref_format() == VIK_FILE_REF_FORMAT_RELATIVE ) {
607           gchar *cwd = g_get_current_dir();
608           if ( cwd ) {
609             rv.s = file_GetRelativeFilename ( cwd, vml->cache_dir );
610             if ( !rv.s ) rv.s = "";
611             set = TRUE;
612           }
613         }
614       }
615       if ( !set )
616         rv.s = vml->cache_dir ? vml->cache_dir : "";
617       break;
618     }
619     case PARAM_FILE: rv.s = vml->filename; break;
620     case PARAM_MAPTYPE: rv.u = map_index_to_uniq_id ( vml->maptype ); break;
621     case PARAM_ALPHA: rv.u = vml->alpha; break;
622     case PARAM_AUTODOWNLOAD: rv.u = vml->autodownload; break;
623     case PARAM_ONLYMISSING: rv.u = vml->adl_only_missing; break;
624     case PARAM_MAPZOOM: rv.u = vml->mapzoom_id; break;
625     default: break;
626   }
627   return rv;
628 }
629
630 static void maps_layer_change_param ( GtkWidget *widget, ui_change_values values )
631 {
632   switch ( GPOINTER_TO_INT(values[UI_CHG_PARAM_ID]) ) {
633     // Alter sensitivity of download option widgets according to the maptype setting.
634     case PARAM_MAPTYPE: {
635       // Get new value
636       VikLayerParamData vlpd = a_uibuilder_widget_get_value ( widget, values[UI_CHG_PARAM] );
637       // Is it *not* the OSM On Disk Tile Layout or the MBTiles type
638       gboolean sensitive = ( 21 != vlpd.u && 23 != vlpd.u);
639       GtkWidget **ww1 = values[UI_CHG_WIDGETS];
640       GtkWidget **ww2 = values[UI_CHG_LABELS];
641       GtkWidget *w1 = ww1[PARAM_ONLYMISSING];
642       GtkWidget *w2 = ww2[PARAM_ONLYMISSING];
643       GtkWidget *w3 = ww1[PARAM_AUTODOWNLOAD];
644       GtkWidget *w4 = ww2[PARAM_AUTODOWNLOAD];
645       // Depends on autodownload value
646       gboolean missing_sense = sensitive && VIK_MAPS_LAYER(values[UI_CHG_LAYER])->autodownload;
647       if ( w1 ) gtk_widget_set_sensitive ( w1, missing_sense );
648       if ( w2 ) gtk_widget_set_sensitive ( w2, missing_sense );
649       if ( w3 ) gtk_widget_set_sensitive ( w3, sensitive );
650       if ( w4 ) gtk_widget_set_sensitive ( w4, sensitive );
651
652       // File only applicable for MBTiles type
653       // Directory for all other types
654       sensitive = ( 23 == vlpd.u);
655       GtkWidget *w5 = ww1[PARAM_FILE];
656       GtkWidget *w6 = ww2[PARAM_FILE];
657       GtkWidget *w7 = ww1[PARAM_CACHE_DIR];
658       GtkWidget *w8 = ww2[PARAM_CACHE_DIR];
659       if ( w5 ) gtk_widget_set_sensitive ( w5, sensitive );
660       if ( w6 ) gtk_widget_set_sensitive ( w6, sensitive );
661       if ( w7 ) gtk_widget_set_sensitive ( w7, !sensitive );
662       if ( w8 ) gtk_widget_set_sensitive ( w8, !sensitive );
663
664       break;
665     }
666
667     // Alter sensitivity of 'download only missing' widgets according to the autodownload setting.
668     case PARAM_AUTODOWNLOAD: {
669       // Get new value
670       VikLayerParamData vlpd = a_uibuilder_widget_get_value ( widget, values[UI_CHG_PARAM] );
671       GtkWidget **ww1 = values[UI_CHG_WIDGETS];
672       GtkWidget **ww2 = values[UI_CHG_LABELS];
673       GtkWidget *w1 = ww1[PARAM_ONLYMISSING];
674       GtkWidget *w2 = ww2[PARAM_ONLYMISSING];
675       if ( w1 ) gtk_widget_set_sensitive ( w1, vlpd.b );
676       if ( w2 ) gtk_widget_set_sensitive ( w2, vlpd.b );
677       break;
678     }
679     default: break;
680   }
681 }
682
683 /****************************************/
684 /****** CREATING, COPYING, FREEING ******/
685 /****************************************/
686
687 static VikMapsLayer *maps_layer_new ( VikViewport *vvp )
688 {
689   VikMapsLayer *vml = VIK_MAPS_LAYER ( g_object_new ( VIK_MAPS_LAYER_TYPE, NULL ) );
690   vik_layer_set_type ( VIK_LAYER(vml), VIK_LAYER_MAPS );
691
692   vik_layer_set_defaults ( VIK_LAYER(vml), vvp );
693
694   vml->dl_tool_x = vml->dl_tool_y = -1;
695   vml->last_center = NULL;
696   vml->last_xmpp = 0.0;
697   vml->last_ympp = 0.0;
698
699   vml->dl_right_click_menu = NULL;
700   vml->filename = NULL;
701   return vml;
702 }
703
704 static void maps_layer_free ( VikMapsLayer *vml )
705 {
706   g_free ( vml->cache_dir );
707   vml->cache_dir = NULL;
708   if ( vml->dl_right_click_menu )
709     g_object_ref_sink ( G_OBJECT(vml->dl_right_click_menu) );
710   g_free(vml->last_center);
711   vml->last_center = NULL;
712   g_free ( vml->filename );
713   vml->filename = NULL;
714
715 #ifdef HAVE_SQLITE3_H
716   VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
717   if ( vik_map_source_is_mbtiles ( map ) ) {
718     if ( vml->mbtiles ) {
719       int ans = sqlite3_close ( vml->mbtiles );
720       if ( ans != SQLITE_OK ) {
721         // Only to console for information purposes only
722         g_warning ( "SQL Close problem: %d", ans );
723       }
724     }
725   }
726 #endif
727 }
728
729 static void maps_layer_post_read (VikLayer *vl, VikViewport *vp, gboolean from_file)
730 {
731   VikMapsLayer *vml = VIK_MAPS_LAYER(vl);
732   VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
733
734   if (from_file != TRUE)
735   {
736     /* If this method is not called in file reading context
737      * it is called in GUI context.
738      * So, we can check if we have to inform the user about inconsistency */
739     VikViewportDrawMode vp_drawmode;
740     vp_drawmode = vik_viewport_get_drawmode ( vp );
741
742     if (vik_map_source_get_drawmode(map) != vp_drawmode) {
743       const gchar *drawmode_name = vik_viewport_get_drawmode_name (vp, vik_map_source_get_drawmode(map));
744       gchar *msg = g_strdup_printf(_("New map cannot be displayed in the current drawmode.\nSelect \"%s\" from View menu to view it."), drawmode_name);
745       a_dialog_warning_msg ( VIK_GTK_WINDOW_FROM_WIDGET(vp), msg );
746       g_free(msg);
747     }
748   }
749   
750   // Performed in post read as we now know the map type
751 #ifdef HAVE_SQLITE3_H
752   // Do some SQL stuff
753   if ( vik_map_source_is_mbtiles ( map ) ) {
754     int ans = sqlite3_open_v2 ( vml->filename,
755                                 &(vml->mbtiles),
756                                 SQLITE_OPEN_READONLY,
757                                 NULL );
758     if ( ans != SQLITE_OK ) {
759       // That didn't work, so here's why:
760       g_warning ( "%s: %s", __FUNCTION__, sqlite3_errmsg ( vml->mbtiles ) );
761
762       a_dialog_error_msg_extra ( VIK_GTK_WINDOW_FROM_WIDGET(vp),
763                                  _("Failed to open MBTiles file: %s"),
764                                  vml->filename );
765       vml->mbtiles = NULL;
766     }
767   }
768 #endif
769
770   // If the on Disk OSM Tile Layout type
771   if ( vml->maptype == 21 )
772     // Copy the directory into filename
773     //  thus the mapcache look up will be unique when using more than one of these map types
774     vml->filename = g_strdup (vml->cache_dir);
775 }
776
777 static const gchar* maps_layer_tooltip ( VikMapsLayer *vml )
778 {
779   return vik_maps_layer_get_map_label ( vml );
780 }
781
782 static void maps_layer_marshall( VikMapsLayer *vml, guint8 **data, gint *len )
783 {
784   vik_layer_marshall_params ( VIK_LAYER(vml), data, len );
785 }
786
787 static VikMapsLayer *maps_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp )
788 {
789   VikMapsLayer *rv = maps_layer_new ( vvp );
790   vik_layer_unmarshall_params ( VIK_LAYER(rv), data, len, vvp );
791   return rv;
792 }
793
794 /*********************/
795 /****** DRAWING ******/
796 /*********************/
797
798 static GdkPixbuf *pixbuf_set_alpha ( GdkPixbuf *pixbuf, guint8 alpha )
799 {
800   guchar *pixels;
801   gint width, height, iii, jjj;
802
803   if ( ! gdk_pixbuf_get_has_alpha ( pixbuf ) )
804   {
805     GdkPixbuf *tmp = gdk_pixbuf_add_alpha(pixbuf,FALSE,0,0,0);
806     g_object_unref(G_OBJECT(pixbuf));
807     pixbuf = tmp;
808     if ( !pixbuf )
809       return NULL;
810   }
811
812   pixels = gdk_pixbuf_get_pixels(pixbuf);
813   width = gdk_pixbuf_get_width(pixbuf);
814   height = gdk_pixbuf_get_height(pixbuf);
815
816   /* r,g,b,a,r,g,b,a.... */
817   for (iii = 0; iii < width; iii++) for (jjj = 0; jjj < height; jjj++)
818   {
819     pixels += 3;
820     *pixels++ = alpha;
821   }
822   return pixbuf;
823 }
824
825 static GdkPixbuf *pixbuf_shrink ( GdkPixbuf *pixbuf, gdouble xshrinkfactor, gdouble yshrinkfactor )
826 {
827   GdkPixbuf *tmp;
828   guint16 width = gdk_pixbuf_get_width(pixbuf), height = gdk_pixbuf_get_height(pixbuf);
829   tmp = gdk_pixbuf_scale_simple(pixbuf, ceil(width * xshrinkfactor), ceil(height * yshrinkfactor), GDK_INTERP_BILINEAR);
830   g_object_unref ( G_OBJECT(pixbuf) );
831   return tmp;
832 }
833
834 #ifdef HAVE_SQLITE3_H
835 /*
836 static int sql_select_tile_dump_cb (void *data, int cols, char **fields, char **col_names )
837 {
838   g_warning ( "Found %d columns", cols );
839   int i;
840   for ( i = 0; i < cols; i++ ) {
841     g_warning ( "SQL processing %s = %s", col_names[i], fields[i] );
842   }
843   return 0;
844 }
845 */
846
847 /**
848  *
849  */
850 static GdkPixbuf *get_pixbuf_sql_exec ( sqlite3 *sql, gint xx, gint yy, gint zoom )
851 {
852   GdkPixbuf *pixbuf = NULL;
853
854   // MBTiles stored internally with the flipping y thingy (i.e. TMS scheme).
855   gint flip_y = (gint) pow(2, zoom)-1 - yy;
856   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 );
857
858   gboolean finished = FALSE;
859
860   sqlite3_stmt *sql_stmt = NULL;
861   int ans = sqlite3_prepare_v2 ( sql, statement, -1, &sql_stmt, NULL );
862   if ( ans != SQLITE_OK ) {
863     g_warning ( "%s: %s - %d", __FUNCTION__, "prepare failure", ans );
864     finished = TRUE;
865   }
866
867   while ( !finished ) {
868     ans = sqlite3_step ( sql_stmt );
869     switch (ans) {
870       case SQLITE_ROW: {
871         // Get tile_data blob
872         int count = sqlite3_column_count(sql_stmt);
873         if ( count != 1 )  {
874           g_warning ( "%s: %s - %d", __FUNCTION__, "count not one", count );
875           finished = TRUE;
876         }
877         else {
878           const void *data = sqlite3_column_blob ( sql_stmt, 0 );
879           int bytes = sqlite3_column_bytes ( sql_stmt, 0 );
880           if ( bytes < 1 )  {
881             g_warning ( "%s: %s (%d)", __FUNCTION__, "not enough bytes", bytes );
882             finished = TRUE;
883           }
884           else {
885             // Convert these blob bytes into a pixbuf via these streaming operations
886             GInputStream *stream = g_memory_input_stream_new_from_data ( data, bytes, NULL );
887             GError *error = NULL;
888             pixbuf = gdk_pixbuf_new_from_stream ( stream, NULL, &error );
889             if (error || (!pixbuf)) {
890               g_warning ( "%s: %s", __FUNCTION__, error->message );
891               g_error_free ( error );
892             }
893             g_input_stream_close ( stream, NULL, NULL );
894           }
895         }
896         break;
897       }
898       default:
899        // e.g. SQLITE_DONE | SQLITE_ERROR | SQLITE_MISUSE | etc...
900        // Finished normally
901        //  and give up on any errors
902        if ( ans != SQLITE_DONE )
903          g_warning ( "%s: %s - %d", __FUNCTION__, "step issue", ans );
904        finished = TRUE;
905        break;
906     }
907   }
908   ans = sqlite3_finalize ( sql_stmt );
909   
910   g_free ( statement );
911
912   return pixbuf;
913 }
914 #endif
915
916 static GdkPixbuf *get_mbtiles_pixbuf ( VikMapsLayer *vml, gint xx, gint yy, gint zoom )
917 {
918   GdkPixbuf *pixbuf = NULL;
919
920 #ifdef HAVE_SQLITE3_H
921   if ( vml->mbtiles ) {
922     /*
923     gchar *statement = g_strdup_printf ( "SELECT name FROM sqlite_master WHERE type='table';" );
924     char *errMsg = NULL;
925     int ans = sqlite3_exec ( vml->mbtiles, statement, sql_select_tile_dump_cb, pixbuf, &errMsg );
926     if ( ans != SQLITE_OK ) {
927       // Only to console for information purposes only
928       g_warning ( "SQL problem: %d for %s - error: %s", ans, statement, errMsg );
929       sqlite3_free( errMsg );
930     }
931     g_free ( statement );
932     */
933
934     // Reading BLOBS is a bit more involved and so can't use the simpler sqlite3_exec ()
935     // Hence this specific function
936     pixbuf = get_pixbuf_sql_exec ( vml->mbtiles, xx, yy, zoom );
937   }
938 #endif
939
940   return pixbuf;
941 }
942
943 static GdkPixbuf *pixbuf_apply_settings ( GdkPixbuf *pixbuf, VikMapsLayer *vml, MapCoord *mapcoord, gdouble xshrinkfactor, gdouble yshrinkfactor )
944 {
945   // Apply alpha setting
946   if ( pixbuf && vml->alpha < 255 )
947     pixbuf = pixbuf_set_alpha ( pixbuf, vml->alpha );
948
949   if ( pixbuf && ( xshrinkfactor != 1.0 || yshrinkfactor != 1.0 ) )
950     pixbuf = pixbuf_shrink ( pixbuf, xshrinkfactor, yshrinkfactor );
951
952   if ( pixbuf )
953     a_mapcache_add ( pixbuf, mapcoord->x, mapcoord->y,
954                      mapcoord->z, vik_map_source_get_uniq_id(MAPS_LAYER_NTH_TYPE(vml->maptype)),
955                      mapcoord->scale, vml->alpha, xshrinkfactor, yshrinkfactor, vml->filename );
956
957   return pixbuf;
958 }
959
960 static GdkPixbuf *get_pixbuf( VikMapsLayer *vml, gint mode, MapCoord *mapcoord, gchar *filename_buf, gint buf_len, gdouble xshrinkfactor, gdouble yshrinkfactor )
961 {
962   GdkPixbuf *pixbuf;
963
964   /* get the thing */
965   pixbuf = a_mapcache_get ( mapcoord->x, mapcoord->y, mapcoord->z,
966                             mode, mapcoord->scale, vml->alpha, xshrinkfactor, yshrinkfactor, vml->filename );
967
968   if ( ! pixbuf ) {
969     if ( vik_map_source_is_direct_file_access (MAPS_LAYER_NTH_TYPE(vml->maptype)) ) {
970       // ATM MBTiles must be 'a direct access type'
971       if ( vik_map_source_is_mbtiles (MAPS_LAYER_NTH_TYPE(vml->maptype)) ) {
972         pixbuf = get_mbtiles_pixbuf ( vml, mapcoord->x, mapcoord->y, (17 - mapcoord->scale) );
973         pixbuf = pixbuf_apply_settings ( pixbuf, vml, mapcoord, xshrinkfactor, yshrinkfactor );
974         // return now to avoid file tests that aren't appropriate for this map type
975         return pixbuf;
976       }
977       else
978         g_snprintf ( filename_buf, buf_len, DIRECTDIRACCESS,
979                      vml->cache_dir, (17 - mapcoord->scale), mapcoord->x, mapcoord->y, ".png" );
980     }
981     else
982       g_snprintf ( filename_buf, buf_len, DIRSTRUCTURE,
983                    vml->cache_dir, mode,
984                    mapcoord->scale, mapcoord->z, mapcoord->x, mapcoord->y );
985
986     if ( g_file_test ( filename_buf, G_FILE_TEST_EXISTS ) == TRUE)
987     {
988       GError *gx = NULL;
989       pixbuf = gdk_pixbuf_new_from_file ( filename_buf, &gx );
990
991       /* free the pixbuf on error */
992       if (gx)
993       {
994         if ( gx->domain != GDK_PIXBUF_ERROR || gx->code != GDK_PIXBUF_ERROR_CORRUPT_IMAGE ) {
995           // Report a warning
996           if ( IS_VIK_WINDOW ((VikWindow*)VIK_GTK_WINDOW_FROM_LAYER(vml)) ) {
997             gchar* msg = g_strdup_printf ( _("Couldn't open image file: %s"), gx->message );
998             vik_window_statusbar_update ( (VikWindow*)VIK_GTK_WINDOW_FROM_LAYER(vml), msg, VIK_STATUSBAR_INFO );
999             g_free (msg);
1000           }
1001         }
1002
1003         g_error_free ( gx );
1004         if ( pixbuf )
1005           g_object_unref ( G_OBJECT(pixbuf) );
1006         pixbuf = NULL;
1007       } else {
1008         pixbuf = pixbuf_apply_settings ( pixbuf, vml, mapcoord, xshrinkfactor, yshrinkfactor );
1009       }
1010     }
1011   }
1012   return pixbuf;
1013 }
1014
1015 static gboolean should_start_autodownload(VikMapsLayer *vml, VikViewport *vvp)
1016 {
1017   const VikCoord *center = vik_viewport_get_center ( vvp );
1018
1019   if (vik_window_get_pan_move (VIK_WINDOW(VIK_GTK_WINDOW_FROM_WIDGET(GTK_WIDGET(vvp)))))
1020     /* D'n'D pan in action: do not download */
1021     return FALSE;
1022
1023   // TEMPORARY HACK
1024   // Prevent requests for downloading tiles at Zoom Level 19 and above for most map types
1025   // Allow MapQuest Zoom Level up to 19
1026   // TODO: This should be made a property of the map source and then use that value
1027   gdouble xzoom = vik_viewport_get_xmpp ( vvp );
1028   if ( (vml->maptype != 19 && map_utils_mpp_to_scale (xzoom) < -1) || (vml->maptype == 19 && map_utils_mpp_to_scale (xzoom) < -2) )
1029     return FALSE;
1030
1031   if (vml->last_center == NULL) {
1032     VikCoord *new_center = g_malloc(sizeof(VikCoord));
1033     *new_center = *center;
1034     vml->last_center = new_center;
1035     vml->last_xmpp = vik_viewport_get_xmpp(vvp);
1036     vml->last_ympp = vik_viewport_get_ympp(vvp);
1037     return TRUE;
1038   }
1039
1040   /* TODO: perhaps vik_coord_diff() */
1041   if (vik_coord_equals(vml->last_center, center)
1042       && (vml->last_xmpp == vik_viewport_get_xmpp(vvp))
1043       && (vml->last_ympp == vik_viewport_get_ympp(vvp)))
1044     return FALSE;
1045
1046   *(vml->last_center) = *center;
1047     vml->last_xmpp = vik_viewport_get_xmpp(vvp);
1048     vml->last_ympp = vik_viewport_get_ympp(vvp);
1049   return TRUE;
1050 }
1051
1052 static void maps_layer_draw_section ( VikMapsLayer *vml, VikViewport *vvp, VikCoord *ul, VikCoord *br )
1053 {
1054   MapCoord ulm, brm;
1055   gdouble xzoom = vik_viewport_get_xmpp ( vvp );
1056   gdouble yzoom = vik_viewport_get_ympp ( vvp );
1057   gdouble xshrinkfactor = 1.0, yshrinkfactor = 1.0;
1058   gboolean existence_only = FALSE;
1059
1060   if ( vml->xmapzoom && (vml->xmapzoom != xzoom || vml->ymapzoom != yzoom) ) {
1061     xshrinkfactor = vml->xmapzoom / xzoom;
1062     yshrinkfactor = vml->ymapzoom / yzoom;
1063     xzoom = vml->xmapzoom;
1064     yzoom = vml->xmapzoom;
1065     if ( ! (xshrinkfactor > MIN_SHRINKFACTOR && xshrinkfactor < MAX_SHRINKFACTOR &&
1066          yshrinkfactor > MIN_SHRINKFACTOR && yshrinkfactor < MAX_SHRINKFACTOR ) ) {
1067       if ( xshrinkfactor > REAL_MIN_SHRINKFACTOR && yshrinkfactor > REAL_MIN_SHRINKFACTOR ) {
1068         g_debug ( "%s: existence_only due to SHRINKFACTORS", __FUNCTION__ );
1069         existence_only = TRUE;
1070       }
1071       else {
1072         // Report the reason for not drawing
1073         if ( IS_VIK_WINDOW ((VikWindow*)VIK_GTK_WINDOW_FROM_LAYER(vml)) ) {
1074           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));
1075           vik_window_statusbar_update ( (VikWindow*)VIK_GTK_WINDOW_FROM_LAYER(vml), msg, VIK_STATUSBAR_INFO );
1076           g_free (msg);
1077         }
1078         return;
1079       }
1080     }
1081   }
1082
1083   /* coord -> ID */
1084   VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
1085   if ( vik_map_source_coord_to_mapcoord ( map, ul, xzoom, yzoom, &ulm ) &&
1086        vik_map_source_coord_to_mapcoord ( map, br, xzoom, yzoom, &brm ) ) {
1087
1088     /* loop & draw */
1089     gint x, y;
1090     gint xmin = MIN(ulm.x, brm.x), xmax = MAX(ulm.x, brm.x);
1091     gint ymin = MIN(ulm.y, brm.y), ymax = MAX(ulm.y, brm.y);
1092     gint mode = vik_map_source_get_uniq_id(map);
1093
1094     VikCoord coord;
1095     gint xx, yy, width, height;
1096     GdkPixbuf *pixbuf;
1097
1098     // Prevent the program grinding to a halt if trying to deal with thousands of tiles
1099     //  which can happen when using a small fixed zoom level and viewing large areas.
1100     // Also prevents very large number of tile download requests
1101     gint tiles = (xmax-xmin) * (ymax-ymin);
1102     if ( tiles > MAX_TILES ) {
1103       g_debug ( "%s: existence_only due to wanting too many tiles (%d)", __FUNCTION__, tiles );
1104       existence_only = TRUE;
1105     }
1106
1107     guint max_path_len = strlen(vml->cache_dir) + 40;
1108     gchar *path_buf = g_malloc ( max_path_len * sizeof(char) );
1109
1110     if ( (!existence_only) && vml->autodownload  && should_start_autodownload(vml, vvp)) {
1111       g_debug("%s: Starting autodownload", __FUNCTION__);
1112       if ( !vml->adl_only_missing && vik_map_source_supports_download_only_new (map) )
1113         // Try to download newer tiles
1114         start_download_thread ( vml, vvp, ul, br, REDOWNLOAD_NEW );
1115       else
1116         // Download only missing tiles
1117         start_download_thread ( vml, vvp, ul, br, REDOWNLOAD_NONE );
1118     }
1119
1120     if ( vik_map_source_get_tilesize_x(map) == 0 && !existence_only ) {
1121       for ( x = xmin; x <= xmax; x++ ) {
1122         for ( y = ymin; y <= ymax; y++ ) {
1123           ulm.x = x;
1124           ulm.y = y;
1125           pixbuf = get_pixbuf ( vml, mode, &ulm, path_buf, max_path_len, xshrinkfactor, yshrinkfactor );
1126           if ( pixbuf ) {
1127             width = gdk_pixbuf_get_width ( pixbuf );
1128             height = gdk_pixbuf_get_height ( pixbuf );
1129
1130             vik_map_source_mapcoord_to_center_coord ( map, &ulm, &coord );
1131             vik_viewport_coord_to_screen ( vvp, &coord, &xx, &yy );
1132             xx -= (width/2);
1133             yy -= (height/2);
1134
1135             vik_viewport_draw_pixbuf ( vvp, pixbuf, 0, 0, xx, yy, width, height );
1136           }
1137         }
1138       }
1139     } else { /* tilesize is known, don't have to keep converting coords */
1140       gdouble tilesize_x = vik_map_source_get_tilesize_x(map) * xshrinkfactor;
1141       gdouble tilesize_y = vik_map_source_get_tilesize_y(map) * yshrinkfactor;
1142       /* ceiled so tiles will be maximum size in the case of funky shrinkfactor */
1143       gint tilesize_x_ceil = ceil ( tilesize_x );
1144       gint tilesize_y_ceil = ceil ( tilesize_y );
1145       gint8 xinc = (ulm.x == xmin) ? 1 : -1;
1146       gint8 yinc = (ulm.y == ymin) ? 1 : -1;
1147       gint xx_tmp, yy_tmp;
1148       gint base_yy, xend, yend;
1149
1150       xend = (xinc == 1) ? (xmax+1) : (xmin-1);
1151       yend = (yinc == 1) ? (ymax+1) : (ymin-1);
1152
1153       vik_map_source_mapcoord_to_center_coord ( map, &ulm, &coord );
1154       vik_viewport_coord_to_screen ( vvp, &coord, &xx_tmp, &yy_tmp );
1155       xx = xx_tmp; yy = yy_tmp;
1156       /* above trick so xx,yy doubles. this is so shrinkfactors aren't rounded off
1157        * eg if tile size 128, shrinkfactor 0.333 */
1158       xx -= (tilesize_x/2);
1159       base_yy = yy - (tilesize_y/2);
1160
1161       for ( x = ((xinc == 1) ? xmin : xmax); x != xend; x+=xinc ) {
1162         yy = base_yy;
1163         for ( y = ((yinc == 1) ? ymin : ymax); y != yend; y+=yinc ) {
1164           ulm.x = x;
1165           ulm.y = y;
1166
1167           if ( existence_only ) {
1168             if ( vik_map_source_is_direct_file_access (MAPS_LAYER_NTH_TYPE(vml->maptype)) )
1169               g_snprintf ( path_buf, max_path_len, DIRECTDIRACCESS,
1170                            vml->cache_dir, (17 - ulm.scale), ulm.x, ulm.y, ".png" );
1171             else
1172               g_snprintf ( path_buf, max_path_len, DIRSTRUCTURE,
1173                            vml->cache_dir, mode,
1174                            ulm.scale, ulm.z, ulm.x, ulm.y );
1175             if ( g_file_test ( path_buf, G_FILE_TEST_EXISTS ) == TRUE ) {
1176               GdkGC *black_gc = gtk_widget_get_style(GTK_WIDGET(vvp))->black_gc;
1177               vik_viewport_draw_line ( vvp, black_gc, xx+tilesize_x_ceil, yy, xx, yy+tilesize_y_ceil );
1178             }
1179           } else {
1180             int scale_inc;
1181             for (scale_inc = 0; scale_inc < 4; scale_inc ++) {
1182               /* try with correct then smaller zooms */
1183               int scale_factor = 1 << scale_inc;  /*  2^scale_inc */
1184               MapCoord ulm2 = ulm;
1185               ulm2.x = ulm.x / scale_factor;
1186               ulm2.y = ulm.y / scale_factor;
1187               ulm2.scale = ulm.scale + scale_inc;
1188               pixbuf = get_pixbuf ( vml, mode, &ulm2, path_buf, max_path_len, xshrinkfactor * scale_factor, yshrinkfactor * scale_factor );
1189               if ( pixbuf ) {
1190                 gint src_x = (ulm.x % scale_factor) * tilesize_x_ceil;
1191                 gint src_y = (ulm.y % scale_factor) * tilesize_y_ceil;
1192 #ifdef DEBUG
1193                 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);
1194 #endif
1195                 vik_viewport_draw_pixbuf ( vvp, pixbuf, src_x, src_y, xx, yy, tilesize_x_ceil, tilesize_y_ceil );
1196                 break;
1197               }
1198             }
1199             if ( !pixbuf ) {
1200               /* retry with bigger zooms */
1201               int scale_dec;
1202               for (scale_dec = 1; scale_dec < 2; scale_dec ++) {
1203                 int pict_x, pict_y;
1204                 int scale_factor = 1 << scale_dec;  /*  2^scale_dec */
1205                 MapCoord ulm2 = ulm;
1206                 ulm2.x = ulm.x * scale_factor;
1207                 ulm2.y = ulm.y * scale_factor;
1208                 ulm2.scale = ulm.scale - scale_dec;
1209                 for (pict_x = 0; pict_x < scale_factor; pict_x ++) {
1210                   for (pict_y = 0; pict_y < scale_factor; pict_y ++) {
1211                     MapCoord ulm3 = ulm2;
1212                     ulm3.x += pict_x;
1213                     ulm3.y += pict_y;
1214                     pixbuf = get_pixbuf ( vml, mode, &ulm3, path_buf, max_path_len, xshrinkfactor / scale_factor, yshrinkfactor / scale_factor );
1215                     if ( pixbuf ) {
1216                       gint src_x = 0;
1217                       gint src_y = 0;
1218                       gint dest_x = xx + pict_x * (tilesize_x_ceil / scale_factor);
1219                       gint dest_y = yy + pict_y * (tilesize_y_ceil / scale_factor);
1220                       vik_viewport_draw_pixbuf ( vvp, pixbuf, src_x, src_y, dest_x, dest_y, tilesize_x_ceil / scale_factor, tilesize_y_ceil / scale_factor );
1221                     }
1222                   }
1223                 }
1224               }
1225             }
1226           }
1227
1228           yy += tilesize_y;
1229         }
1230         xx += tilesize_x;
1231       }
1232     }
1233
1234     g_free ( path_buf );
1235   }
1236 }
1237
1238 static void maps_layer_draw ( VikMapsLayer *vml, VikViewport *vvp )
1239 {
1240   if ( vik_map_source_get_drawmode(MAPS_LAYER_NTH_TYPE(vml->maptype)) == vik_viewport_get_drawmode ( vvp ) )
1241   {
1242     VikCoord ul, br;
1243
1244     /* Copyright */
1245     gdouble level = vik_viewport_get_zoom ( vvp );
1246     LatLonBBox bbox;
1247     vik_viewport_get_min_max_lat_lon ( vvp, &bbox.south, &bbox.north, &bbox.west, &bbox.east );
1248     vik_map_source_get_copyright ( MAPS_LAYER_NTH_TYPE(vml->maptype), bbox, level, vik_viewport_add_copyright, vvp );
1249
1250     /* Logo */
1251     const GdkPixbuf *logo = vik_map_source_get_logo ( MAPS_LAYER_NTH_TYPE(vml->maptype) );
1252     vik_viewport_add_logo ( vvp, logo );
1253
1254     /* get corner coords */
1255     if ( vik_viewport_get_coord_mode ( vvp ) == VIK_COORD_UTM && ! vik_viewport_is_one_zone ( vvp ) ) {
1256       /* UTM multi-zone stuff by Kit Transue */
1257       gchar leftmost_zone, rightmost_zone, i;
1258       leftmost_zone = vik_viewport_leftmost_zone( vvp );
1259       rightmost_zone = vik_viewport_rightmost_zone( vvp );
1260       for ( i = leftmost_zone; i <= rightmost_zone; ++i ) {
1261         vik_viewport_corners_for_zonen ( vvp, i, &ul, &br );
1262         maps_layer_draw_section ( vml, vvp, &ul, &br );
1263       }
1264     }
1265     else {
1266       vik_viewport_screen_to_coord ( vvp, 0, 0, &ul );
1267       vik_viewport_screen_to_coord ( vvp, vik_viewport_get_width(vvp), vik_viewport_get_height(vvp), &br );
1268
1269       maps_layer_draw_section ( vml, vvp, &ul, &br );
1270     }
1271   }
1272 }
1273
1274 /*************************/
1275 /****** DOWNLOADING ******/
1276 /*************************/
1277
1278 /* pass along data to thread, exists even if layer is deleted. */
1279 typedef struct {
1280   gchar *cache_dir;
1281   gchar *filename_buf;
1282   gint x0, y0, xf, yf;
1283   MapCoord mapcoord;
1284   gint maptype;
1285   gint maxlen;
1286   gint mapstoget;
1287   gint redownload;
1288   gboolean refresh_display;
1289   VikMapsLayer *vml;
1290   VikViewport *vvp;
1291   gboolean map_layer_alive;
1292   GMutex *mutex;
1293 } MapDownloadInfo;
1294
1295 static void mdi_free ( MapDownloadInfo *mdi )
1296 {
1297   g_mutex_free(mdi->mutex);
1298   g_free ( mdi->cache_dir );
1299   mdi->cache_dir = NULL;
1300   g_free ( mdi->filename_buf );
1301   mdi->filename_buf = NULL;
1302   g_free ( mdi );
1303 }
1304
1305 static void weak_ref_cb(gpointer ptr, GObject * dead_vml)
1306 {
1307   MapDownloadInfo *mdi = ptr;
1308   g_mutex_lock(mdi->mutex);
1309   mdi->map_layer_alive = FALSE;
1310   g_mutex_unlock(mdi->mutex);
1311 }
1312
1313 static int map_download_thread ( MapDownloadInfo *mdi, gpointer threaddata )
1314 {
1315   void *handle = vik_map_source_download_handle_init(MAPS_LAYER_NTH_TYPE(mdi->maptype));
1316   guint donemaps = 0;
1317   gint x, y;
1318   for ( x = mdi->x0; x <= mdi->xf; x++ )
1319   {
1320     for ( y = mdi->y0; y <= mdi->yf; y++ )
1321     {
1322       gboolean remove_mem_cache = FALSE;
1323       gboolean need_download = FALSE;
1324       g_snprintf ( mdi->filename_buf, mdi->maxlen, DIRSTRUCTURE,
1325                      mdi->cache_dir, vik_map_source_get_uniq_id(MAPS_LAYER_NTH_TYPE(mdi->maptype)),
1326                      mdi->mapcoord.scale, mdi->mapcoord.z, x, y );
1327
1328       donemaps++;
1329       int res = a_background_thread_progress ( threaddata, ((gdouble)donemaps) / mdi->mapstoget ); /* this also calls testcancel */
1330       if (res != 0) {
1331         vik_map_source_download_handle_cleanup(MAPS_LAYER_NTH_TYPE(mdi->maptype), handle);
1332         return -1;
1333       }
1334
1335       if ( g_file_test ( mdi->filename_buf, G_FILE_TEST_EXISTS ) == FALSE ) {
1336         need_download = TRUE;
1337         remove_mem_cache = TRUE;
1338
1339       } else {  /* in case map file already exists */
1340         switch (mdi->redownload) {
1341           case REDOWNLOAD_NONE:
1342             continue;
1343
1344           case REDOWNLOAD_BAD:
1345           {
1346             /* see if this one is bad or what */
1347             GError *gx = NULL;
1348             GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file ( mdi->filename_buf, &gx );
1349             if (gx || (!pixbuf)) {
1350               g_remove ( mdi->filename_buf );
1351               need_download = TRUE;
1352               remove_mem_cache = TRUE;
1353               g_error_free ( gx );
1354
1355             } else {
1356               g_object_unref ( pixbuf );
1357             }
1358             break;
1359           }
1360
1361           case REDOWNLOAD_NEW:
1362             need_download = TRUE;
1363             remove_mem_cache = TRUE;
1364             break;
1365
1366           case REDOWNLOAD_ALL:
1367             /* FIXME: need a better way than to erase file in case of server/network problem */
1368             g_remove ( mdi->filename_buf );
1369             need_download = TRUE;
1370             remove_mem_cache = TRUE;
1371             break;
1372
1373           case DOWNLOAD_OR_REFRESH:
1374             remove_mem_cache = TRUE;
1375             break;
1376
1377           default:
1378             g_warning ( "redownload state %d unknown\n", mdi->redownload);
1379         }
1380       }
1381
1382       mdi->mapcoord.x = x; mdi->mapcoord.y = y;
1383
1384       if (need_download) {
1385         DownloadResult_t dr = vik_map_source_download( MAPS_LAYER_NTH_TYPE(mdi->maptype), &(mdi->mapcoord), mdi->filename_buf, handle);
1386         switch ( dr ) {
1387           case DOWNLOAD_HTTP_ERROR:
1388           case DOWNLOAD_CONTENT_ERROR: {
1389             // TODO: ?? count up the number of download errors somehow...
1390             gchar* msg = g_strdup_printf ( "%s: %s", vik_maps_layer_get_map_label (mdi->vml), _("Failed to download tile") );
1391             vik_window_statusbar_update ( (VikWindow*)VIK_GTK_WINDOW_FROM_LAYER(mdi->vml), msg, VIK_STATUSBAR_INFO );
1392             g_free (msg);
1393             break;
1394           }
1395           case DOWNLOAD_FILE_WRITE_ERROR: {
1396             gchar* msg = g_strdup_printf ( "%s: %s", vik_maps_layer_get_map_label (mdi->vml), _("Unable to save tile") );
1397             vik_window_statusbar_update ( (VikWindow*)VIK_GTK_WINDOW_FROM_LAYER(mdi->vml), msg, VIK_STATUSBAR_INFO );
1398             g_free (msg);
1399             break;
1400           }
1401           case DOWNLOAD_SUCCESS:
1402           case DOWNLOAD_NOT_REQUIRED:
1403           default:
1404             break;
1405         }
1406       }
1407
1408       g_mutex_lock(mdi->mutex);
1409       if (remove_mem_cache)
1410           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 );
1411       if (mdi->refresh_display && mdi->map_layer_alive) {
1412         /* TODO: check if it's on visible area */
1413         vik_layer_emit_update ( VIK_LAYER(mdi->vml) ); // NB update display from background
1414       }
1415       g_mutex_unlock(mdi->mutex);
1416       mdi->mapcoord.x = mdi->mapcoord.y = 0; /* we're temporarily between downloads */
1417
1418     }
1419   }
1420   vik_map_source_download_handle_cleanup(MAPS_LAYER_NTH_TYPE(mdi->maptype), handle);
1421   g_mutex_lock(mdi->mutex);
1422   if (mdi->map_layer_alive)
1423     g_object_weak_unref(G_OBJECT(mdi->vml), weak_ref_cb, mdi);
1424   g_mutex_unlock(mdi->mutex); 
1425   return 0;
1426 }
1427
1428 static void mdi_cancel_cleanup ( MapDownloadInfo *mdi )
1429 {
1430   if ( mdi->mapcoord.x || mdi->mapcoord.y )
1431   {
1432     g_snprintf ( mdi->filename_buf, mdi->maxlen, DIRSTRUCTURE,
1433                      mdi->cache_dir, vik_map_source_get_uniq_id(MAPS_LAYER_NTH_TYPE(mdi->maptype)),
1434                      mdi->mapcoord.scale, mdi->mapcoord.z, mdi->mapcoord.x, mdi->mapcoord.y );
1435     if ( g_file_test ( mdi->filename_buf, G_FILE_TEST_EXISTS ) == TRUE)
1436     {
1437       g_remove ( mdi->filename_buf );
1438     }
1439   }
1440 }
1441
1442 static void start_download_thread ( VikMapsLayer *vml, VikViewport *vvp, const VikCoord *ul, const VikCoord *br, gint redownload )
1443 {
1444   gdouble xzoom = vml->xmapzoom ? vml->xmapzoom : vik_viewport_get_xmpp ( vvp );
1445   gdouble yzoom = vml->ymapzoom ? vml->ymapzoom : vik_viewport_get_ympp ( vvp );
1446   MapCoord ulm, brm;
1447   VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
1448
1449   // Don't ever attempt download on direct access
1450   if ( vik_map_source_is_direct_file_access ( map ) )
1451     return;
1452
1453   if ( vik_map_source_coord_to_mapcoord ( map, ul, xzoom, yzoom, &ulm ) 
1454     && vik_map_source_coord_to_mapcoord ( map, br, xzoom, yzoom, &brm ) )
1455   {
1456     MapDownloadInfo *mdi = g_malloc ( sizeof(MapDownloadInfo) );
1457     gint a, b;
1458
1459     mdi->vml = vml;
1460     mdi->vvp = vvp;
1461     mdi->map_layer_alive = TRUE;
1462     mdi->mutex = g_mutex_new();
1463     mdi->refresh_display = TRUE;
1464
1465     /* cache_dir and buffer for dest filename */
1466     mdi->cache_dir = g_strdup ( vml->cache_dir );
1467     mdi->maxlen = strlen ( vml->cache_dir ) + 40;
1468     mdi->filename_buf = g_malloc ( mdi->maxlen * sizeof(gchar) );
1469     mdi->maptype = vml->maptype;
1470
1471     mdi->mapcoord = ulm;
1472     mdi->redownload = redownload;
1473
1474     mdi->x0 = MIN(ulm.x, brm.x);
1475     mdi->xf = MAX(ulm.x, brm.x);
1476     mdi->y0 = MIN(ulm.y, brm.y);
1477     mdi->yf = MAX(ulm.y, brm.y);
1478
1479     mdi->mapstoget = 0;
1480
1481     if ( mdi->redownload ) {
1482       mdi->mapstoget = (mdi->xf - mdi->x0 + 1) * (mdi->yf - mdi->y0 + 1);
1483     } else {
1484       /* calculate how many we need */
1485       for ( a = mdi->x0; a <= mdi->xf; a++ )
1486       {
1487         for ( b = mdi->y0; b <= mdi->yf; b++ )
1488         {
1489           g_snprintf ( mdi->filename_buf, mdi->maxlen, DIRSTRUCTURE,
1490                        vml->cache_dir, vik_map_source_get_uniq_id(map), ulm.scale,
1491                        ulm.z, a, b );
1492           if ( g_file_test ( mdi->filename_buf, G_FILE_TEST_EXISTS ) == FALSE )
1493             mdi->mapstoget++;
1494         }
1495       }
1496     }
1497
1498     mdi->mapcoord.x = mdi->mapcoord.y = 0; /* for cleanup -- no current map */
1499
1500     if ( mdi->mapstoget )
1501     {
1502       const gchar *tmp_str;
1503       gchar *tmp;
1504
1505       if (redownload) 
1506       {
1507         if (redownload == REDOWNLOAD_BAD)
1508           tmp_str = ngettext("Redownloading up to %d %s map...", "Redownloading up to %d %s maps...", mdi->mapstoget);
1509         else
1510           tmp_str = ngettext("Redownloading %d %s map...", "Redownloading %d %s maps...", mdi->mapstoget);
1511       } 
1512       else 
1513       {
1514         tmp_str = ngettext("Downloading %d %s map...", "Downloading %d %s maps...", mdi->mapstoget);
1515       }
1516       tmp = g_strdup_printf ( tmp_str, mdi->mapstoget, MAPS_LAYER_NTH_LABEL(vml->maptype));
1517  
1518       g_object_weak_ref(G_OBJECT(mdi->vml), weak_ref_cb, mdi);
1519       /* launch the thread */
1520       a_background_thread ( VIK_GTK_WINDOW_FROM_LAYER(vml), /* parent window */
1521                             tmp,                                              /* description string */
1522                             (vik_thr_func) map_download_thread,               /* function to call within thread */
1523                             mdi,                                              /* pass along data */
1524                             (vik_thr_free_func) mdi_free,                     /* function to free pass along data */
1525                             (vik_thr_free_func) mdi_cancel_cleanup,
1526                             mdi->mapstoget );
1527       g_free ( tmp );
1528     }
1529     else
1530       mdi_free ( mdi );
1531   }
1532 }
1533
1534 static void maps_layer_download_section ( VikMapsLayer *vml, VikViewport *vvp, VikCoord *ul, VikCoord *br, gdouble zoom, gint download_method )
1535 {
1536   MapCoord ulm, brm;
1537   VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
1538
1539   // Don't ever attempt download on direct access
1540   if ( vik_map_source_is_direct_file_access ( map ) )
1541     return;
1542
1543   if (!vik_map_source_coord_to_mapcoord(map, ul, zoom, zoom, &ulm) 
1544     || !vik_map_source_coord_to_mapcoord(map, br, zoom, zoom, &brm)) {
1545     g_warning("%s() coord_to_mapcoord() failed", __PRETTY_FUNCTION__);
1546     return;
1547   }
1548
1549   MapDownloadInfo *mdi = g_malloc(sizeof(MapDownloadInfo));
1550   gint i, j;
1551
1552   mdi->vml = vml;
1553   mdi->vvp = vvp;
1554   mdi->map_layer_alive = TRUE;
1555   mdi->mutex = g_mutex_new();
1556   mdi->refresh_display = TRUE;
1557
1558   mdi->cache_dir = g_strdup ( vml->cache_dir );
1559   mdi->maxlen = strlen ( vml->cache_dir ) + 40;
1560   mdi->filename_buf = g_malloc ( mdi->maxlen * sizeof(gchar) );
1561   mdi->maptype = vml->maptype;
1562
1563   mdi->mapcoord = ulm;
1564   mdi->redownload = download_method;
1565
1566   mdi->x0 = MIN(ulm.x, brm.x);
1567   mdi->xf = MAX(ulm.x, brm.x);
1568   mdi->y0 = MIN(ulm.y, brm.y);
1569   mdi->yf = MAX(ulm.y, brm.y);
1570
1571   mdi->mapstoget = 0;
1572
1573   for (i = mdi->x0; i <= mdi->xf; i++) {
1574     for (j = mdi->y0; j <= mdi->yf; j++) {
1575       g_snprintf ( mdi->filename_buf, mdi->maxlen, DIRSTRUCTURE,
1576                    vml->cache_dir, vik_map_source_get_uniq_id(map), ulm.scale,
1577                    ulm.z, i, j );
1578       if ( g_file_test ( mdi->filename_buf, G_FILE_TEST_EXISTS ) == FALSE )
1579             mdi->mapstoget++;
1580     }
1581   }
1582
1583   mdi->mapcoord.x = mdi->mapcoord.y = 0; /* for cleanup -- no current map */
1584
1585   if (mdi->mapstoget) {
1586     gchar *tmp;
1587     const gchar *fmt;
1588     fmt = ngettext("Downloading %d %s map...",
1589                    "Downloading %d %s maps...",
1590                    mdi->mapstoget);
1591     tmp = g_strdup_printf ( fmt, mdi->mapstoget, MAPS_LAYER_NTH_LABEL(vml->maptype) );
1592
1593     g_object_weak_ref(G_OBJECT(mdi->vml), weak_ref_cb, mdi);
1594       /* launch the thread */
1595     a_background_thread ( VIK_GTK_WINDOW_FROM_LAYER(vml), /* parent window */
1596       tmp,                                /* description string */
1597       (vik_thr_func) map_download_thread, /* function to call within thread */
1598       mdi,                                /* pass along data */
1599       (vik_thr_free_func) mdi_free,       /* function to free pass along data */
1600       (vik_thr_free_func) mdi_cancel_cleanup,
1601       mdi->mapstoget );
1602     g_free ( tmp );
1603   }
1604   else
1605     mdi_free ( mdi );
1606 }
1607
1608 /**
1609  * vik_maps_layer_download_section:
1610  * @vml:  The Map Layer
1611  * @vvp:  The Viewport that the map is on
1612  * @ul:   Upper left coordinate of the area to be downloaded
1613  * @br:   Bottom right coordinate of the area to be downloaded
1614  * @zoom: The zoom level at which the maps are to be download
1615  *
1616  * Download a specified map area at a certain zoom level
1617  */
1618 void vik_maps_layer_download_section ( VikMapsLayer *vml, VikViewport *vvp, VikCoord *ul, VikCoord *br, gdouble zoom )
1619 {
1620   maps_layer_download_section (vml, vvp, ul, br, zoom, REDOWNLOAD_NONE);
1621 }
1622
1623 static void maps_layer_redownload_bad ( VikMapsLayer *vml )
1624 {
1625   start_download_thread ( vml, vml->redownload_vvp, &(vml->redownload_ul), &(vml->redownload_br), REDOWNLOAD_BAD );
1626 }
1627
1628 static void maps_layer_redownload_all ( VikMapsLayer *vml )
1629 {
1630   start_download_thread ( vml, vml->redownload_vvp, &(vml->redownload_ul), &(vml->redownload_br), REDOWNLOAD_ALL );
1631 }
1632
1633 static void maps_layer_redownload_new ( VikMapsLayer *vml )
1634 {
1635   start_download_thread ( vml, vml->redownload_vvp, &(vml->redownload_ul), &(vml->redownload_br), REDOWNLOAD_NEW );
1636 }
1637
1638 /**
1639  * Display a simple dialog with information about this particular map tile
1640  */
1641 static void maps_layer_tile_info ( VikMapsLayer *vml )
1642 {
1643   VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
1644
1645   gdouble xzoom = vml->xmapzoom ? vml->xmapzoom : vik_viewport_get_xmpp ( vml->redownload_vvp );
1646   gdouble yzoom = vml->ymapzoom ? vml->ymapzoom : vik_viewport_get_ympp ( vml->redownload_vvp );
1647   MapCoord ulm;
1648
1649   if ( !vik_map_source_coord_to_mapcoord ( map, &(vml->redownload_ul), xzoom, yzoom, &ulm ) )
1650     return;
1651
1652   gchar *filename = NULL;
1653   gchar *message = NULL;
1654   gchar *source = NULL;
1655
1656   if ( vik_map_source_is_direct_file_access ( map ) ) {
1657     if ( vik_map_source_is_mbtiles ( map ) ) {
1658       filename = g_strdup ( vml->filename );
1659 #ifdef HAVE_SQLITE3_H
1660       // And whether to bother going into the SQL to check it's really there or not...
1661       gchar *exists = NULL;
1662       gint zoom = 17 - ulm.scale;
1663       if ( vml->mbtiles ) {
1664         GdkPixbuf *pixbuf = get_pixbuf_sql_exec ( vml->mbtiles, ulm.x, ulm.y, zoom );
1665         if ( pixbuf ) {
1666           exists = g_strdup ( _("YES") );
1667           g_object_unref ( G_OBJECT(pixbuf) );
1668         }
1669         else {
1670           exists = g_strdup ( _("NO") );
1671         }
1672       }
1673       else
1674         exists = g_strdup ( _("NO") );
1675       gint flip_y = (gint) pow(2, zoom)-1 - ulm.y;
1676       // NB Also handles .jpg automatically due to pixbuf_new_from () support - although just print png for now.
1677       source = g_strdup_printf ( "%s (%d%s%d%s%d.%s %s)", filename, zoom, G_DIR_SEPARATOR_S, ulm.x, G_DIR_SEPARATOR_S, flip_y, "png", exists );
1678       g_free ( exists );
1679 #else
1680       source = g_strdup ( _("Not available") );
1681 #endif
1682     }
1683     else {
1684       filename = g_strdup_printf ( DIRECTDIRACCESS, vml->cache_dir, ulm.scale, ulm.x, ulm.y, ".png" );
1685       source = g_strconcat ( "file://", filename, NULL );
1686     }
1687   }
1688   else {
1689         filename = g_strdup_printf ( DIRSTRUCTURE, vml->cache_dir, vik_map_source_get_uniq_id(map), ulm.scale, ulm.z, ulm.x, ulm.y );
1690     source = g_strdup_printf ( "http://%s%s",
1691                                vik_map_source_default_get_hostname ( VIK_MAP_SOURCE_DEFAULT(map) ),
1692                                vik_map_source_default_get_uri ( VIK_MAP_SOURCE_DEFAULT(map), &ulm ) );
1693   }
1694
1695   if ( g_file_test ( filename, G_FILE_TEST_EXISTS ) ) {
1696
1697     // Get some timestamp information of the tile
1698     struct stat stat_buf;
1699     if ( g_stat ( filename, &stat_buf ) == 0 ) {
1700       gchar time_buf[64];
1701       strftime ( time_buf, sizeof(time_buf), "%c", gmtime((const time_t *)&stat_buf.st_mtime) );
1702       message = g_strdup_printf ( _("\nSource: %s\n\nTile File: %s\nTile File Timestamp: %s"), source, filename, time_buf );
1703     }
1704   }
1705   else
1706     message = g_strdup_printf ( _("Source: %s\n\nNo Tile File!"), source );
1707
1708   // Show the info
1709   a_dialog_info_msg (  VIK_GTK_WINDOW_FROM_LAYER(vml), message );
1710
1711   g_free ( message );
1712   g_free ( source );
1713   g_free ( filename );
1714 }
1715
1716 static gboolean maps_layer_download_release ( VikMapsLayer *vml, GdkEventButton *event, VikViewport *vvp )
1717 {
1718   if (!vml || vml->vl.type != VIK_LAYER_MAPS)
1719     return FALSE;
1720   if ( vml->dl_tool_x != -1 && vml->dl_tool_y != -1 )
1721   {
1722     if ( event->button == 1 )
1723     {
1724       VikCoord ul, br;
1725       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 );
1726       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 );
1727       start_download_thread ( vml, vvp, &ul, &br, DOWNLOAD_OR_REFRESH );
1728       vml->dl_tool_x = vml->dl_tool_y = -1;
1729       return TRUE;
1730     }
1731     else
1732     {
1733       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) );
1734       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) );
1735
1736       vml->redownload_vvp = vvp;
1737
1738       vml->dl_tool_x = vml->dl_tool_y = -1;
1739
1740       if ( ! vml->dl_right_click_menu ) {
1741         GtkWidget *item;
1742         vml->dl_right_click_menu = GTK_MENU ( gtk_menu_new () );
1743
1744         item = gtk_menu_item_new_with_mnemonic ( _("Redownload _Bad Map(s)") );
1745         g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_redownload_bad), vml );
1746         gtk_menu_shell_append ( GTK_MENU_SHELL(vml->dl_right_click_menu), item );
1747
1748         item = gtk_menu_item_new_with_mnemonic ( _("Redownload _New Map(s)") );
1749         g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_redownload_new), vml );
1750         gtk_menu_shell_append ( GTK_MENU_SHELL(vml->dl_right_click_menu), item );
1751
1752         item = gtk_menu_item_new_with_mnemonic ( _("Redownload _All Map(s)") );
1753         g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_redownload_all), vml );
1754         gtk_menu_shell_append ( GTK_MENU_SHELL(vml->dl_right_click_menu), item );
1755
1756         item = gtk_image_menu_item_new_with_mnemonic ( _("_Show Tile Information") );
1757         gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INFO, GTK_ICON_SIZE_MENU) );
1758         g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_tile_info), vml );
1759         gtk_menu_shell_append (GTK_MENU_SHELL(vml->dl_right_click_menu), item);
1760       }
1761
1762       gtk_menu_popup ( vml->dl_right_click_menu, NULL, NULL, NULL, NULL, event->button, event->time );
1763       gtk_widget_show_all ( GTK_WIDGET(vml->dl_right_click_menu) );
1764     }
1765   }
1766   return FALSE;
1767 }
1768
1769 static gpointer maps_layer_download_create ( VikWindow *vw, VikViewport *vvp)
1770 {
1771   return vvp;
1772 }
1773
1774 static gboolean maps_layer_download_click ( VikMapsLayer *vml, GdkEventButton *event, VikViewport *vvp )
1775 {
1776   MapCoord tmp;
1777   if (!vml || vml->vl.type != VIK_LAYER_MAPS)
1778     return FALSE;
1779   VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
1780   if ( vik_map_source_get_drawmode(map) == vik_viewport_get_drawmode ( vvp ) &&
1781        vik_map_source_coord_to_mapcoord ( map, vik_viewport_get_center ( vvp ),
1782            vml->xmapzoom ? vml->xmapzoom : vik_viewport_get_xmpp ( vvp ),
1783            vml->ymapzoom ? vml->ymapzoom : vik_viewport_get_ympp ( vvp ),
1784            &tmp ) ) {
1785     vml->dl_tool_x = event->x, vml->dl_tool_y = event->y;
1786     return TRUE;
1787   }
1788   return FALSE;
1789 }
1790
1791 // A slightly better way of defining the menu callback information
1792 // This should be easier to extend/rework compared to previously
1793 typedef enum {
1794   MA_VML = 0,
1795   MA_VVP,
1796   MA_LAST
1797 } menu_array_index;
1798
1799 typedef gpointer menu_array_values[MA_LAST];
1800
1801 static void download_onscreen_maps ( menu_array_values values, gint redownload )
1802 {
1803   VikMapsLayer *vml = VIK_MAPS_LAYER(values[MA_VML]);
1804   VikViewport *vvp = VIK_VIEWPORT(values[MA_VVP]);
1805   VikViewportDrawMode vp_drawmode = vik_viewport_get_drawmode ( vvp );
1806
1807   gdouble xzoom = vml->xmapzoom ? vml->xmapzoom : vik_viewport_get_xmpp ( vvp );
1808   gdouble yzoom = vml->ymapzoom ? vml->ymapzoom : vik_viewport_get_ympp ( vvp );
1809
1810   VikCoord ul, br;
1811   MapCoord ulm, brm;
1812
1813   vik_viewport_screen_to_coord ( vvp, 0, 0, &ul );
1814   vik_viewport_screen_to_coord ( vvp, vik_viewport_get_width(vvp), vik_viewport_get_height(vvp), &br );
1815
1816   VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
1817   if ( vik_map_source_get_drawmode(map) == vp_drawmode &&
1818        vik_map_source_coord_to_mapcoord ( map, &ul, xzoom, yzoom, &ulm ) &&
1819        vik_map_source_coord_to_mapcoord ( map, &br, xzoom, yzoom, &brm ) )
1820     start_download_thread ( vml, vvp, &ul, &br, redownload );
1821   else if (vik_map_source_get_drawmode(map) != vp_drawmode) {
1822     const gchar *drawmode_name = vik_viewport_get_drawmode_name (vvp, vik_map_source_get_drawmode(map));
1823     gchar *err = g_strdup_printf(_("Wrong drawmode for this map.\nSelect \"%s\" from View menu and try again."), _(drawmode_name));
1824     a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vml), err );
1825     g_free(err);
1826   }
1827   else
1828     a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vml), _("Wrong zoom level for this map.") );
1829
1830 }
1831
1832 static void maps_layer_download_missing_onscreen_maps ( menu_array_values values )
1833 {
1834   download_onscreen_maps( values, REDOWNLOAD_NONE);
1835 }
1836
1837 static void maps_layer_download_new_onscreen_maps ( menu_array_values values )
1838 {
1839   download_onscreen_maps( values, REDOWNLOAD_NEW);
1840 }
1841
1842 static void maps_layer_redownload_all_onscreen_maps ( menu_array_values values )
1843 {
1844   download_onscreen_maps( values, REDOWNLOAD_ALL);
1845 }
1846
1847 static void maps_layers_about ( gpointer vml_vvp[2] )
1848 {
1849   VikMapsLayer *vml = vml_vvp[0];
1850   VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
1851
1852   if ( vik_map_source_get_license (map) )
1853     maps_show_license ( VIK_GTK_WINDOW_FROM_LAYER(vml), map );
1854   else
1855     a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(vml),
1856                         vik_map_source_get_label (map) );
1857 }
1858
1859 /**
1860  * maps_layer_how_many_maps:
1861  * Copied from maps_layer_download_section but without the actual download and this returns a value
1862  */
1863 static gint maps_layer_how_many_maps ( VikMapsLayer *vml, VikViewport *vvp, VikCoord *ul, VikCoord *br, gdouble zoom, gint redownload )
1864 {
1865   MapCoord ulm, brm;
1866   VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
1867
1868   if ( vik_map_source_is_direct_file_access ( map ) )
1869     return 0;
1870
1871   if (!vik_map_source_coord_to_mapcoord(map, ul, zoom, zoom, &ulm)
1872     || !vik_map_source_coord_to_mapcoord(map, br, zoom, zoom, &brm)) {
1873     g_warning("%s() coord_to_mapcoord() failed", __PRETTY_FUNCTION__);
1874     return 0;
1875   }
1876
1877   MapDownloadInfo *mdi = g_malloc(sizeof(MapDownloadInfo));
1878   gint i, j;
1879
1880   mdi->vml = vml;
1881   mdi->vvp = vvp;
1882   mdi->map_layer_alive = TRUE;
1883   mdi->mutex = g_mutex_new();
1884   mdi->refresh_display = FALSE;
1885
1886   mdi->cache_dir = g_strdup ( vml->cache_dir );
1887   mdi->maxlen = strlen ( vml->cache_dir ) + 40;
1888   mdi->filename_buf = g_malloc ( mdi->maxlen * sizeof(gchar) );
1889   mdi->maptype = vml->maptype;
1890
1891   mdi->mapcoord = ulm;
1892   mdi->redownload = redownload;
1893
1894   mdi->x0 = MIN(ulm.x, brm.x);
1895   mdi->xf = MAX(ulm.x, brm.x);
1896   mdi->y0 = MIN(ulm.y, brm.y);
1897   mdi->yf = MAX(ulm.y, brm.y);
1898
1899   mdi->mapstoget = 0;
1900
1901   if ( mdi->redownload == REDOWNLOAD_ALL ) {
1902     mdi->mapstoget = (mdi->xf - mdi->x0 + 1) * (mdi->yf - mdi->y0 + 1);
1903   }
1904   else {
1905     /* calculate how many we need */
1906     for (i = mdi->x0; i <= mdi->xf; i++) {
1907       for (j = mdi->y0; j <= mdi->yf; j++) {
1908         g_snprintf ( mdi->filename_buf, mdi->maxlen, DIRSTRUCTURE,
1909                      vml->cache_dir, vik_map_source_get_uniq_id(map), ulm.scale,
1910                      ulm.z, i, j );
1911         if ( mdi->redownload == REDOWNLOAD_NEW ) {
1912           // Assume the worst - always a new file
1913           // Absolute value would requires server lookup - but that is too slow
1914           mdi->mapstoget++;
1915         }
1916         else {
1917           if ( g_file_test ( mdi->filename_buf, G_FILE_TEST_EXISTS ) == FALSE ) {
1918             // Missing
1919             mdi->mapstoget++;
1920           }
1921           else {
1922             if ( mdi->redownload == REDOWNLOAD_BAD ) {
1923               /* see if this one is bad or what */
1924               GError *gx = NULL;
1925               GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file ( mdi->filename_buf, &gx );
1926               if (gx || (!pixbuf)) {
1927                 mdi->mapstoget++;
1928               }
1929               break;
1930               // Other download cases already considered or just ignored
1931             }
1932           }
1933         }
1934       }
1935     }
1936   }
1937
1938   gint rv = mdi->mapstoget;
1939
1940   mdi_free ( mdi );
1941
1942   return rv;
1943 }
1944
1945 /**
1946  * maps_dialog_zoom_between:
1947  * This dialog is specific to the map layer, so it's here rather than in dialog.c
1948  */
1949 gboolean maps_dialog_zoom_between ( GtkWindow *parent,
1950                                     gchar *title,
1951                                     gchar *zoom_list[],
1952                                     gint default_zoom1,
1953                                     gint default_zoom2,
1954                                     gint *selected_zoom1,
1955                                     gint *selected_zoom2,
1956                                     gchar *download_list[],
1957                                     gint default_download,
1958                                     gint *selected_download )
1959 {
1960   GtkWidget *dialog = gtk_dialog_new_with_buttons ( title,
1961                                                     parent,
1962                                                     GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
1963                                                     GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
1964                                                     GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
1965                                                     NULL );
1966   gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
1967   GtkWidget *response_w = NULL;
1968 #if GTK_CHECK_VERSION (2, 20, 0)
1969   response_w = gtk_dialog_get_widget_for_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
1970 #endif
1971   GtkWidget *zoom_label1 = gtk_label_new ( _("Zoom Start:") );
1972   GtkWidget *zoom_combo1 = vik_combo_box_text_new();
1973   gchar **s;
1974   for (s = zoom_list; *s; s++)
1975     vik_combo_box_text_append ( zoom_combo1, *s );
1976   gtk_combo_box_set_active ( GTK_COMBO_BOX(zoom_combo1), default_zoom1 );
1977
1978   GtkWidget *zoom_label2 = gtk_label_new ( _("Zoom End:") );
1979   GtkWidget *zoom_combo2 = vik_combo_box_text_new();
1980   for (s = zoom_list; *s; s++)
1981     vik_combo_box_text_append ( zoom_combo2, *s );
1982   gtk_combo_box_set_active ( GTK_COMBO_BOX(zoom_combo2), default_zoom2 );
1983
1984   GtkWidget *download_label = gtk_label_new(_("Download Maps Method:"));
1985   GtkWidget *download_combo = vik_combo_box_text_new();
1986   for (s = download_list; *s; s++)
1987     vik_combo_box_text_append ( download_combo, *s );
1988   gtk_combo_box_set_active ( GTK_COMBO_BOX(download_combo), default_download );
1989
1990   GtkTable *box = GTK_TABLE(gtk_table_new(3, 2, FALSE));
1991   gtk_table_attach_defaults (box, GTK_WIDGET(zoom_label1), 0, 1, 0, 1);
1992   gtk_table_attach_defaults (box, GTK_WIDGET(zoom_combo1), 1, 2, 0, 1);
1993   gtk_table_attach_defaults (box, GTK_WIDGET(zoom_label2), 0, 1, 1, 2);
1994   gtk_table_attach_defaults (box, GTK_WIDGET(zoom_combo2), 1, 2, 1, 2);
1995   gtk_table_attach_defaults (box, GTK_WIDGET(download_label), 0, 1, 2, 3);
1996   gtk_table_attach_defaults (box, GTK_WIDGET(download_combo), 1, 2, 2, 3);
1997
1998   gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), GTK_WIDGET(box), FALSE, FALSE, 5 );
1999
2000   if ( response_w )
2001     gtk_widget_grab_focus ( response_w );
2002
2003   gtk_widget_show_all ( dialog );
2004   if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) != GTK_RESPONSE_ACCEPT ) {
2005     gtk_widget_destroy(dialog);
2006     return FALSE;
2007   }
2008
2009   // Return selected options
2010   *selected_zoom1 = gtk_combo_box_get_active ( GTK_COMBO_BOX(zoom_combo1) );
2011   *selected_zoom2 = gtk_combo_box_get_active ( GTK_COMBO_BOX(zoom_combo2) );
2012   *selected_download = gtk_combo_box_get_active ( GTK_COMBO_BOX(download_combo) );
2013
2014   gtk_widget_destroy(dialog);
2015   return TRUE;
2016 }
2017
2018 // My best guess of sensible limits
2019 #define REALLY_LARGE_AMOUNT_OF_TILES 5000
2020 #define CONFIRM_LARGE_AMOUNT_OF_TILES 500
2021
2022 /**
2023  * Get all maps in the region for zoom levels specified by the user
2024  * Sort of similar to trw_layer_download_map_along_track_cb function
2025  */
2026 static void maps_layer_download_all ( menu_array_values values )
2027 {
2028   VikMapsLayer *vml = VIK_MAPS_LAYER(values[MA_VML]);
2029   VikViewport *vvp = VIK_VIEWPORT(values[MA_VVP]);
2030
2031   // I don't think we should allow users to hammer the servers too much...
2032   // Delibrately not allowing lowest zoom levels
2033   // Still can give massive numbers to download
2034   // A screen size of 1600x1200 gives around 300,000 tiles between 1..128 when none exist before !!
2035   gchar *zoom_list[] = {"1", "2", "4", "8", "16", "32", "64", "128", "256", "512", "1024", NULL };
2036   gdouble zoom_vals[] = {1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024};
2037
2038   gint selected_zoom1, selected_zoom2, default_zoom, lower_zoom;
2039   gint selected_download_method;
2040   
2041   gdouble cur_zoom = vik_viewport_get_zoom(vvp);
2042
2043   for (default_zoom = 0; default_zoom < sizeof(zoom_vals)/sizeof(gdouble); default_zoom++) {
2044     if (cur_zoom == zoom_vals[default_zoom])
2045       break;
2046   }
2047   default_zoom = (default_zoom == sizeof(zoom_vals)/sizeof(gdouble)) ? sizeof(zoom_vals)/sizeof(gdouble) - 1 : default_zoom;
2048
2049   // Default to only 2 zoom levels below the current one
2050   if (default_zoom > 1 )
2051     lower_zoom = default_zoom - 2;
2052   else
2053     lower_zoom = default_zoom;
2054
2055   // redownload method - needs to align with REDOWNLOAD* macro values
2056   gchar *download_list[] = { _("Missing"), _("Bad"), _("New"), _("Reload All"), NULL };
2057
2058   gchar *title = g_strdup_printf ( ("%s: %s"), vik_maps_layer_get_map_label (vml), _("Download for Zoom Levels") );
2059
2060   if ( ! maps_dialog_zoom_between ( VIK_GTK_WINDOW_FROM_LAYER(vml),
2061                                     title,
2062                                     zoom_list,
2063                                     lower_zoom,
2064                                     default_zoom,
2065                                     &selected_zoom1,
2066                                     &selected_zoom2,
2067                                     download_list,
2068                                     REDOWNLOAD_NONE, // AKA Missing
2069                                     &selected_download_method ) ) {
2070     // Cancelled
2071     g_free ( title );
2072     return;
2073   }
2074   g_free ( title );
2075
2076   // Find out new current positions
2077   gdouble min_lat, max_lat, min_lon, max_lon;
2078   VikCoord vc_ul, vc_br;
2079   vik_viewport_get_min_max_lat_lon ( vvp, &min_lat, &max_lat, &min_lon, &max_lon );
2080   struct LatLon ll_ul = { max_lat, min_lon };
2081   struct LatLon ll_br = { min_lat, max_lon };
2082   vik_coord_load_from_latlon ( &vc_ul, vik_viewport_get_coord_mode (vvp), &ll_ul );
2083   vik_coord_load_from_latlon ( &vc_br, vik_viewport_get_coord_mode (vvp), &ll_br );
2084
2085   // Get Maps Count - call for each zoom level (in reverse)
2086   // With REDOWNLOAD_NEW this is a possible maximum
2087   // With REDOWNLOAD_NONE this only missing ones - however still has a server lookup per tile
2088   gint map_count = 0;
2089   gint zz;
2090   for ( zz = selected_zoom2; zz >= selected_zoom1; zz-- ) {
2091     map_count = map_count + maps_layer_how_many_maps ( vml, vvp, &vc_ul, &vc_br, zoom_vals[zz], selected_download_method );
2092   }
2093
2094   g_debug ("vikmapslayer: download request map count %d for method %d", map_count, selected_download_method);
2095
2096   // Absolute protection of hammering a map server
2097   if ( map_count > REALLY_LARGE_AMOUNT_OF_TILES ) {
2098     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);
2099     a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vml), str );
2100     g_free (str);
2101     return;
2102   }
2103
2104   // Confirm really want to do this
2105   if ( map_count > CONFIRM_LARGE_AMOUNT_OF_TILES ) {
2106     gchar *str = g_strdup_printf (_("Do you really want to download %d tiles?"), map_count);
2107     gboolean ans = a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vml), str, NULL );
2108     g_free (str);
2109     if ( ! ans )
2110       return;
2111   }
2112
2113   // Get Maps - call for each zoom level (in reverse)
2114   for ( zz = selected_zoom2; zz >= selected_zoom1; zz-- ) {
2115     maps_layer_download_section ( vml, vvp, &vc_ul, &vc_br, zoom_vals[zz], selected_download_method );
2116   }
2117 }
2118
2119 static void maps_layer_add_menu_items ( VikMapsLayer *vml, GtkMenu *menu, VikLayersPanel *vlp )
2120 {
2121   GtkWidget *item;
2122   static menu_array_values values;
2123   values[MA_VML] = vml;
2124   values[MA_VVP] = vik_layers_panel_get_viewport( VIK_LAYERS_PANEL(vlp) );
2125
2126   item = gtk_menu_item_new();
2127   gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
2128   gtk_widget_show ( item );
2129
2130   /* Now with icons */
2131   item = gtk_image_menu_item_new_with_mnemonic ( _("Download _Missing Onscreen Maps") );
2132     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
2133   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_download_missing_onscreen_maps), values );
2134   gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2135   gtk_widget_show ( item );
2136
2137   if ( vik_map_source_supports_download_only_new (MAPS_LAYER_NTH_TYPE(vml->maptype)) ) {
2138     item = gtk_image_menu_item_new_with_mnemonic ( _("Download _New Onscreen Maps") );
2139     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REDO, GTK_ICON_SIZE_MENU) );
2140     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_download_new_onscreen_maps), values );
2141     gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2142     gtk_widget_show ( item );
2143   }
2144
2145   item = gtk_image_menu_item_new_with_mnemonic ( _("Reload _All Onscreen Maps") );
2146   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
2147   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_redownload_all_onscreen_maps), values );
2148   gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2149   gtk_widget_show ( item );
2150
2151   item = gtk_image_menu_item_new_with_mnemonic ( _("Download Maps in _Zoom Levels...") );
2152   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DND_MULTIPLE, GTK_ICON_SIZE_MENU) );
2153   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_download_all), values );
2154   gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2155   gtk_widget_show ( item );
2156
2157   item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_ABOUT, NULL );
2158   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layers_about), values );
2159   gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2160   gtk_widget_show ( item );
2161 }
2162
2163 /**
2164  * Enable downloading maps of the current screen area either 'new' or 'everything'
2165  */
2166 void vik_maps_layer_download ( VikMapsLayer *vml, VikViewport *vvp, gboolean only_new )
2167 {
2168   if ( !vml ) return;
2169   if ( !vvp ) return;
2170
2171   static menu_array_values values;
2172   values[MA_VML] = vml;
2173   values[MA_VVP] = vvp;
2174
2175   if ( only_new )
2176     // Get only new maps
2177     maps_layer_download_new_onscreen_maps ( values );
2178   else
2179     // Redownload everything
2180     maps_layer_redownload_all_onscreen_maps ( values );
2181 }