]> git.street.me.uk Git - andy/viking.git/blob - src/vikmapslayer.c
Fix compiler warning for dynamic copyright.
[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  * UTM multi-zone stuff by Kit Transue <notlostyet@didactek.com>
7  * Dynamic map type by Guilhem Bonnefille <guilhem.bonnefille@gmail.com>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
22  *
23  */
24
25 #ifdef HAVE_CONFIG_H
26 #include "config.h"
27 #endif
28
29 #define MAX_SHRINKFACTOR 8.0000001 /* zoom 1 viewing 8-tiles */
30 #define MIN_SHRINKFACTOR 0.0312499 /* zoom 32 viewing 1-tiles */
31
32 #define REAL_MIN_SHRINKFACTOR 0.0039062499 /* if shrinkfactor is between MAX and REAL_MAX, will only check for existence */
33
34 #include <gtk/gtk.h>
35 #include <gdk-pixbuf/gdk-pixdata.h>
36 #include <glib.h>
37 #include <glib/gstdio.h>
38 #include <glib/gi18n.h>
39
40 #ifdef HAVE_STRING_H
41 #include <string.h>
42 #endif
43 #ifdef HAVE_MATH_H
44 #include <math.h>
45 #endif
46
47 #include "globals.h"
48 #include "util.h"
49 #include "coords.h"
50 #include "vikcoord.h"
51 #include "viktreeview.h"
52 #include "vikviewport.h"
53 #include "viklayer.h"
54 #include "vikmapslayer.h"
55
56 #ifdef HAVE_UNISTD_H
57 #include <unistd.h>
58 #endif
59
60 #include "mapcache.h"
61 /* only for dialog.h -- ugh */
62 #include "vikwaypoint.h"
63 #include "dialog.h"
64 #include "preferences.h"
65
66 #include "vikstatus.h"
67 #include "background.h"
68
69 #include "vikaggregatelayer.h"
70 #include "viklayerspanel.h"
71
72 #include "mapcoord.h"
73 #include "terraserver.h"
74
75 #include "icons/icons.h"
76
77 /****** MAP TYPES ******/
78
79 static GList *__map_types = NULL;
80
81 #define NUM_MAP_TYPES g_list_length(__map_types)
82
83 /* List of label for each map type */
84 static gchar **params_maptypes = NULL;
85
86 /* Corresponding IDS. (Cf. field uniq_id in VikMapsLayer struct) */
87 static guint *params_maptypes_ids = NULL;
88
89 /******** MAPZOOMS *********/
90
91 static gchar *params_mapzooms[] = { N_("Use Viking Zoom Level"), "0.25", "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 };
92 static gdouble __mapzooms_x[] = { 0.0, 0.25, 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 };
93 static gdouble __mapzooms_y[] = { 0.0, 0.25, 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 };
94
95 #define NUM_MAPZOOMS (sizeof(params_mapzooms)/sizeof(params_mapzooms[0]) - 1)
96
97 /**************************/
98
99
100 static void maps_layer_post_read (VikLayer *vl, VikViewport *vp, gboolean from_file);
101 static const gchar* maps_layer_tooltip ( VikMapsLayer *vml );
102 static void maps_layer_marshall( VikMapsLayer *vml, guint8 **data, gint *len );
103 static VikMapsLayer *maps_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp );
104 static gboolean maps_layer_set_param ( VikMapsLayer *vml, guint16 id, VikLayerParamData data, VikViewport *vvp, gboolean is_file_operation );
105 static VikLayerParamData maps_layer_get_param ( VikMapsLayer *vml, guint16 id, gboolean is_file_operation );
106 static void maps_layer_draw ( VikMapsLayer *vml, VikViewport *vvp );
107 static VikMapsLayer *maps_layer_new ( VikViewport *vvp );
108 static void maps_layer_free ( VikMapsLayer *vml );
109 static gboolean maps_layer_download_release ( VikMapsLayer *vml, GdkEventButton *event, VikViewport *vvp );
110 static gboolean maps_layer_download_click ( VikMapsLayer *vml, GdkEventButton *event, VikViewport *vvp );
111 static gpointer maps_layer_download_create ( VikWindow *vw, VikViewport *vvp );
112 static void maps_layer_set_cache_dir ( VikMapsLayer *vml, const gchar *dir );
113 static void start_download_thread ( VikMapsLayer *vml, VikViewport *vvp, const VikCoord *ul, const VikCoord *br, gint redownload );
114 static void maps_layer_add_menu_items ( VikMapsLayer *vml, GtkMenu *menu, VikLayersPanel *vlp );
115 static guint map_uniq_id_to_index ( guint uniq_id );
116
117
118 static VikLayerParamScale params_scales[] = {
119   /* min, max, step, digits (decimal places) */
120  { 0, 255, 3, 0 }, /* alpha */
121 };
122
123 VikLayerParam maps_layer_params[] = {
124   { "mode", VIK_LAYER_PARAM_UINT, VIK_LAYER_GROUP_NONE, N_("Map Type:"), VIK_LAYER_WIDGET_COMBOBOX, NULL, NULL },
125   { "directory", VIK_LAYER_PARAM_STRING, VIK_LAYER_GROUP_NONE, N_("Maps Directory:"), VIK_LAYER_WIDGET_FOLDERENTRY },
126   { "alpha", VIK_LAYER_PARAM_UINT, VIK_LAYER_GROUP_NONE, N_("Alpha:"), VIK_LAYER_WIDGET_HSCALE, params_scales },
127   { "autodownload", VIK_LAYER_PARAM_BOOLEAN, VIK_LAYER_GROUP_NONE, N_("Autodownload maps:"), VIK_LAYER_WIDGET_CHECKBUTTON },
128   { "mapzoom", VIK_LAYER_PARAM_UINT, VIK_LAYER_GROUP_NONE, N_("Zoom Level:"), VIK_LAYER_WIDGET_COMBOBOX, params_mapzooms, NULL },
129 };
130
131 enum { PARAM_MAPTYPE=0, PARAM_CACHE_DIR, PARAM_ALPHA, PARAM_AUTODOWNLOAD, PARAM_MAPZOOM, NUM_PARAMS };
132
133 static VikToolInterface maps_tools[] = {
134   { N_("Maps Download"), (VikToolConstructorFunc) maps_layer_download_create, NULL, NULL, NULL,  
135     (VikToolMouseFunc) maps_layer_download_click, NULL,  (VikToolMouseFunc) maps_layer_download_release,
136     (VikToolKeyFunc) NULL, GDK_CURSOR_IS_PIXMAP, &cursor_mapdl_pixbuf },
137 };
138
139 VikLayerInterface vik_maps_layer_interface = {
140   N_("Map"),
141   &vikmapslayer_pixbuf,
142
143   maps_tools,
144   sizeof(maps_tools) / sizeof(maps_tools[0]),
145
146   maps_layer_params,
147   NUM_PARAMS,
148   NULL,
149   0,
150
151   VIK_MENU_ITEM_ALL,
152
153   (VikLayerFuncCreate)                  maps_layer_new,
154   (VikLayerFuncRealize)                 NULL,
155   (VikLayerFuncPostRead)                maps_layer_post_read,
156   (VikLayerFuncFree)                    maps_layer_free,
157
158   (VikLayerFuncProperties)              NULL,
159   (VikLayerFuncDraw)                    maps_layer_draw,
160   (VikLayerFuncChangeCoordMode)         NULL,
161
162   (VikLayerFuncSetMenuItemsSelection)   NULL,
163   (VikLayerFuncGetMenuItemsSelection)   NULL,
164
165   (VikLayerFuncAddMenuItems)            maps_layer_add_menu_items,
166   (VikLayerFuncSublayerAddMenuItems)    NULL,
167
168   (VikLayerFuncSublayerRenameRequest)   NULL,
169   (VikLayerFuncSublayerToggleVisible)   NULL,
170   (VikLayerFuncSublayerTooltip)         NULL,
171   (VikLayerFuncLayerTooltip)            maps_layer_tooltip,
172
173   (VikLayerFuncMarshall)                maps_layer_marshall,
174   (VikLayerFuncUnmarshall)              maps_layer_unmarshall,
175
176   (VikLayerFuncSetParam)                maps_layer_set_param,
177   (VikLayerFuncGetParam)                maps_layer_get_param,
178
179   (VikLayerFuncReadFileData)            NULL,
180   (VikLayerFuncWriteFileData)           NULL,
181
182   (VikLayerFuncDeleteItem)              NULL,
183   (VikLayerFuncCutItem)                 NULL,
184   (VikLayerFuncCopyItem)                NULL,
185   (VikLayerFuncPasteItem)               NULL,
186   (VikLayerFuncFreeCopiedItem)          NULL,
187   (VikLayerFuncDragDropRequest)         NULL,
188 };
189
190 struct _VikMapsLayer {
191   VikLayer vl;
192   guint maptype;
193   gchar *cache_dir;
194   guint8 alpha;
195   guint mapzoom_id;
196   gdouble xmapzoom, ymapzoom;
197
198   gboolean autodownload;
199   VikCoord *last_center;
200   gdouble last_xmpp;
201   gdouble last_ympp;
202
203   gint dl_tool_x, dl_tool_y;
204
205   GtkMenu *dl_right_click_menu;
206   VikCoord redownload_ul, redownload_br; /* right click menu only */
207   VikViewport *redownload_vvp;
208
209   gboolean license_notice_shown; // FALSE for new maps only, otherwise
210                                  // TRUE for saved maps & other layer changes as we don't need to show it again
211 };
212
213 enum { REDOWNLOAD_NONE = 0,    /* download only missing maps */
214        REDOWNLOAD_BAD,         /* download missing and bad maps */
215        REDOWNLOAD_NEW,         /* download missing maps that are newer on server only */
216        REDOWNLOAD_ALL,         /* download all maps */
217        DOWNLOAD_OR_REFRESH };  /* download missing maps and refresh cache */
218
219 static VikLayerParam prefs[] = {
220   { VIKING_PREFERENCES_NAMESPACE "maplayer_default_dir", VIK_LAYER_PARAM_STRING, VIK_LAYER_GROUP_NONE, N_("Default maplayer directory:"), VIK_LAYER_WIDGET_FOLDERENTRY, NULL, NULL },
221 };
222
223 void maps_layer_init ()
224 {
225   VikLayerParamData tmp;
226   tmp.s = maps_layer_default_dir();
227   a_preferences_register(prefs, tmp, VIKING_PREFERENCES_GROUP_KEY);
228 }
229
230 /****************************************/
231 /******** MAPS LAYER TYPES **************/
232 /****************************************/
233
234 int _get_index_for_id ( guint id )
235 {
236   int index = 0 ;
237   while (params_maptypes_ids[index] != 0)
238   {
239     if (params_maptypes_ids[index] == id)
240       return index;
241     index++;
242   }
243   return -1;
244 }
245
246 void _add_map_source ( guint id, const char *label, VikMapSource *map )
247 {
248   gsize len = 0;
249   if (params_maptypes)
250     len = g_strv_length (params_maptypes);
251   /* Add the label */
252   params_maptypes = g_realloc (params_maptypes, (len+2)*sizeof(gchar*));
253   params_maptypes[len] = g_strdup (label);
254   params_maptypes[len+1] = NULL;
255
256   /* Add the id */
257   params_maptypes_ids = g_realloc (params_maptypes_ids, (len+2)*sizeof(guint));
258   params_maptypes_ids[len] = id;
259   params_maptypes_ids[len+1] = 0;
260
261   /* We have to clone */
262   VikMapSource *clone = VIK_MAP_SOURCE(g_object_ref(map));
263   /* Register the clone in the list */
264   __map_types = g_list_append(__map_types, clone);
265
266   /* Hack
267      We have to ensure the mode LayerParam references the up-to-date
268      GLists.
269   */
270   /*
271   memcpy(&maps_layer_params[0].widget_data, &params_maptypes, sizeof(gpointer));
272   memcpy(&maps_layer_params[0].extra_widget_data, &params_maptypes_ids, sizeof(gpointer));
273   */
274   maps_layer_params[0].widget_data = params_maptypes;
275   maps_layer_params[0].extra_widget_data = params_maptypes_ids;
276 }
277
278 void _update_map_source ( const char *label, VikMapSource *map, int index )
279 {
280   GList *item = g_list_nth (__map_types, index);
281   g_object_unref (item->data);
282   item->data = g_object_ref (map);
283   /* Change previous data */
284   g_free (params_maptypes[index]);
285   params_maptypes[index] = g_strdup (label);
286 }
287
288 void maps_layer_register_map_source ( VikMapSource *map )
289 {
290   g_assert(map != NULL);
291   
292   guint id = vik_map_source_get_uniq_id(map);
293   const char *label = vik_map_source_get_label(map);
294   g_assert(label != NULL);
295
296   int previous = map_uniq_id_to_index (id);
297   if (previous != NUM_MAP_TYPES)
298   {
299     _update_map_source (label, map, previous);
300   }
301   else
302   {
303     _add_map_source (id, label, map);
304   }
305 }
306
307 #define MAPS_LAYER_NTH_LABEL(n) (params_maptypes[n])
308 #define MAPS_LAYER_NTH_ID(n) (params_maptypes_ids[n])
309 #define MAPS_LAYER_NTH_TYPE(n) (VIK_MAP_SOURCE(g_list_nth_data(__map_types, (n))))
310
311 gint vik_maps_layer_get_map_type(VikMapsLayer *vml)
312 {
313   return(vml->maptype);
314 }
315
316 gchar *vik_maps_layer_get_map_label(VikMapsLayer *vml)
317 {
318   return(g_strdup(MAPS_LAYER_NTH_LABEL(vml->maptype)));
319 }
320
321 /****************************************/
322 /******** CACHE DIR STUFF ***************/
323 /****************************************/
324
325 #define DIRSTRUCTURE "%st%ds%dz%d" G_DIR_SEPARATOR_S "%d" G_DIR_SEPARATOR_S "%d"
326 #define MAPS_CACHE_DIR maps_layer_default_dir()
327
328 #ifdef WINDOWS
329 #include <io.h>
330 #define GLOBAL_MAPS_DIR "C:\\VIKING-MAPS\\"
331 #define LOCAL_MAPS_DIR "VIKING-MAPS"
332 #else /* POSIX */
333 #include <stdlib.h>
334 #define GLOBAL_MAPS_DIR "/var/cache/maps/"
335 #define LOCAL_MAPS_DIR ".viking-maps"
336 #endif
337
338 gchar *maps_layer_default_dir ()
339 {
340   static gchar *defaultdir = NULL;
341   if ( ! defaultdir )
342   {
343     /* Thanks to Mike Davison for the $VIKING_MAPS usage */
344     const gchar *mapdir = g_getenv("VIKING_MAPS");
345     if ( mapdir ) {
346       defaultdir = g_strdup ( mapdir );
347     } else if ( g_access ( GLOBAL_MAPS_DIR, W_OK ) == 0 ) {
348       defaultdir = g_strdup ( GLOBAL_MAPS_DIR );
349     } else {
350       const gchar *home = g_get_home_dir();
351       if (!home || g_access(home, W_OK))
352         home = g_get_home_dir ();
353       if ( home )
354         defaultdir = g_build_filename ( home, LOCAL_MAPS_DIR, NULL );
355       else
356         defaultdir = g_strdup ( LOCAL_MAPS_DIR );
357     }
358     if (defaultdir && (defaultdir[strlen(defaultdir)-1] != G_DIR_SEPARATOR))
359     {
360       /* Add the separator at the end */
361       gchar *tmp = defaultdir;
362       defaultdir = g_strconcat(tmp, G_DIR_SEPARATOR_S, NULL);
363       g_free(tmp);
364     }
365     g_debug("%s: defaultdir=%s", __FUNCTION__, defaultdir);
366   }
367   return defaultdir;
368 }
369
370 static void maps_layer_mkdir_if_default_dir ( VikMapsLayer *vml )
371 {
372   if ( vml->cache_dir && strcmp ( vml->cache_dir, MAPS_CACHE_DIR ) == 0 && g_file_test ( vml->cache_dir, G_FILE_TEST_EXISTS ) == FALSE )
373   {
374     g_mkdir ( vml->cache_dir, 0777 );
375   }
376 }
377
378 static void maps_layer_set_cache_dir ( VikMapsLayer *vml, const gchar *dir )
379 {
380   guint len;
381   g_assert ( vml != NULL);
382   g_free ( vml->cache_dir );
383   vml->cache_dir = NULL;
384
385   if ( dir == NULL || dir[0] == '\0' )
386   {
387     if ( a_preferences_get(VIKING_PREFERENCES_NAMESPACE "maplayer_default_dir") )
388       vml->cache_dir = g_strdup ( a_preferences_get(VIKING_PREFERENCES_NAMESPACE "maplayer_default_dir")->s );
389   }
390   else
391   {
392     len = strlen(dir);
393     if ( dir[len-1] != G_DIR_SEPARATOR )
394     {
395       vml->cache_dir = g_malloc ( len+2 );
396       strncpy ( vml->cache_dir, dir, len );
397       vml->cache_dir[len] = G_DIR_SEPARATOR;
398       vml->cache_dir[len+1] = '\0';
399     }
400     else
401       vml->cache_dir = g_strdup ( dir );
402   }
403   maps_layer_mkdir_if_default_dir ( vml );
404 }
405
406 /****************************************/
407 /******** GOBJECT STUFF *****************/
408 /****************************************/
409
410 GType vik_maps_layer_get_type ()
411 {
412   static GType vml_type = 0;
413
414   if (!vml_type)
415   {
416     static const GTypeInfo vml_info =
417     {
418       sizeof (VikMapsLayerClass),
419       NULL, /* base_init */
420       NULL, /* base_finalize */
421       NULL, /* class init */
422       NULL, /* class_finalize */
423       NULL, /* class_data */
424       sizeof (VikMapsLayer),
425       0,
426       NULL /* instance init */
427     };
428     vml_type = g_type_register_static ( VIK_LAYER_TYPE, "VikMapsLayer", &vml_info, 0 );
429   }
430
431   return vml_type;
432 }
433
434 /****************************************/
435 /************** PARAMETERS **************/
436 /****************************************/
437
438 static guint map_index_to_uniq_id (guint8 index)
439 {
440   g_assert ( index < NUM_MAP_TYPES );
441   return vik_map_source_get_uniq_id(MAPS_LAYER_NTH_TYPE(index));
442 }
443
444 static guint map_uniq_id_to_index ( guint uniq_id )
445 {
446   gint i;
447   for ( i = 0; i < NUM_MAP_TYPES; i++ )
448     if ( vik_map_source_get_uniq_id(MAPS_LAYER_NTH_TYPE(i)) == uniq_id )
449       return i;
450   return NUM_MAP_TYPES; /* no such thing */
451 }
452
453 static gboolean maps_layer_set_param ( VikMapsLayer *vml, guint16 id, VikLayerParamData data, VikViewport *vvp, gboolean is_file_operation )
454 {
455   // When loading from a file don't need the license reminder
456   if ( is_file_operation )
457     vml->license_notice_shown = TRUE;
458
459   switch ( id )
460   {
461     case PARAM_CACHE_DIR: maps_layer_set_cache_dir ( vml, data.s ); break;
462     case PARAM_MAPTYPE: {
463       gint maptype = map_uniq_id_to_index(data.u);
464       if ( maptype == NUM_MAP_TYPES ) g_warning(_("Unknown map type"));
465       else vml->maptype = maptype;
466       break;
467     }
468     case PARAM_ALPHA: if ( data.u <= 255 ) vml->alpha = data.u; break;
469     case PARAM_AUTODOWNLOAD: vml->autodownload = data.b; break;
470     case PARAM_MAPZOOM: if ( data.u < NUM_MAPZOOMS ) {
471                           vml->mapzoom_id = data.u;
472                           vml->xmapzoom = __mapzooms_x [data.u];
473                           vml->ymapzoom = __mapzooms_y [data.u];
474                         }else g_warning (_("Unknown Map Zoom")); break;
475   }
476   return TRUE;
477 }
478
479 static VikLayerParamData maps_layer_get_param ( VikMapsLayer *vml, guint16 id, gboolean is_file_operation )
480 {
481   VikLayerParamData rv;
482   switch ( id )
483   {
484     case PARAM_CACHE_DIR: rv.s = vml->cache_dir ? vml->cache_dir : ""; break;
485     case PARAM_MAPTYPE: rv.u = map_index_to_uniq_id ( vml->maptype ); break;
486     case PARAM_ALPHA: rv.u = vml->alpha; break;
487     case PARAM_AUTODOWNLOAD: rv.u = vml->autodownload; break;
488     case PARAM_MAPZOOM: rv.u = vml->mapzoom_id; break;
489   }
490   return rv;
491 }
492
493 /****************************************/
494 /****** CREATING, COPYING, FREEING ******/
495 /****************************************/
496
497 static VikMapsLayer *maps_layer_new ( VikViewport *vvp )
498 {
499   int idx;
500   VikMapsLayer *vml = VIK_MAPS_LAYER ( g_object_new ( VIK_MAPS_LAYER_TYPE, NULL ) );
501   vik_layer_init ( VIK_LAYER(vml), VIK_LAYER_MAPS );
502   idx = map_uniq_id_to_index(13); /* 13 is id for OSM Mapnik maps */
503     vml->maptype = (idx < NUM_MAP_TYPES) ? idx : 0;
504   vml->alpha = 255;
505   vml->mapzoom_id = 0;
506   vml->dl_tool_x = vml->dl_tool_y = -1;
507   maps_layer_set_cache_dir ( vml, NULL );
508   vml->autodownload = FALSE;
509   vml->last_center = NULL;
510   vml->last_xmpp = 0.0;
511   vml->last_ympp = 0.0;
512
513   vml->dl_right_click_menu = NULL;
514   vml->license_notice_shown = FALSE;
515
516   return vml;
517 }
518
519 static void maps_layer_free ( VikMapsLayer *vml )
520 {
521   g_free ( vml->cache_dir );
522   vml->cache_dir = NULL;
523   if ( vml->dl_right_click_menu )
524     g_object_ref_sink ( G_OBJECT(vml->dl_right_click_menu) );
525   g_free(vml->last_center);
526   vml->last_center = NULL;
527 }
528
529 static void maps_layer_post_read (VikLayer *vl, VikViewport *vp, gboolean from_file)
530 {
531   if (from_file != TRUE)
532   {
533     /* If this method is not called in file reading context
534      * it is called in GUI context.
535      * So, we can check if we have to inform the user about inconsistency */
536     VikViewportDrawMode vp_drawmode;
537     VikMapsLayer *vml = VIK_MAPS_LAYER(vl);
538     VikMapSource *map = NULL;
539  
540     vp_drawmode = vik_viewport_get_drawmode ( vp );
541     map = MAPS_LAYER_NTH_TYPE(vml->maptype);
542     if (vik_map_source_get_drawmode(map) != vp_drawmode) {
543       const gchar *drawmode_name = vik_viewport_get_drawmode_name (vp, vik_map_source_get_drawmode(map));
544       gchar *msg = g_strdup_printf(_("New map cannot be displayed in the current drawmode.\nSelect \"%s\" from View menu to view it."), drawmode_name);
545       a_dialog_warning_msg ( VIK_GTK_WINDOW_FROM_WIDGET(vp), msg );
546       g_free(msg);
547     }
548
549     if (vik_map_source_get_license (map) != NULL) {
550       if ( ! vml->license_notice_shown ) {
551         a_dialog_license (VIK_GTK_WINDOW_FROM_WIDGET(vp), vik_map_source_get_label (map),
552                           vik_map_source_get_license (map), vik_map_source_get_license_url (map) );
553         vml->license_notice_shown = TRUE;
554       }
555     }
556   }
557 }
558
559 static const gchar* maps_layer_tooltip ( VikMapsLayer *vml )
560 {
561   return vik_maps_layer_get_map_label ( vml );
562 }
563
564 static void maps_layer_marshall( VikMapsLayer *vml, guint8 **data, gint *len )
565 {
566   vik_layer_marshall_params ( VIK_LAYER(vml), data, len );
567 }
568
569 static VikMapsLayer *maps_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp )
570 {
571   VikMapsLayer *rv = maps_layer_new ( vvp );
572   vik_layer_unmarshall_params ( VIK_LAYER(rv), data, len, vvp );
573   return rv;
574 }
575
576 /*********************/
577 /****** DRAWING ******/
578 /*********************/
579
580 static GdkPixbuf *pixbuf_set_alpha ( GdkPixbuf *pixbuf, guint8 alpha )
581 {
582   guchar *pixels;
583   gint width, height, iii, jjj;
584
585   if ( ! gdk_pixbuf_get_has_alpha ( pixbuf ) )
586   {
587     GdkPixbuf *tmp = gdk_pixbuf_add_alpha(pixbuf,FALSE,0,0,0);
588     g_object_unref(G_OBJECT(pixbuf));
589     pixbuf = tmp;
590   }
591
592   pixels = gdk_pixbuf_get_pixels(pixbuf);
593   width = gdk_pixbuf_get_width(pixbuf);
594   height = gdk_pixbuf_get_height(pixbuf);
595
596   /* r,g,b,a,r,g,b,a.... */
597   for (iii = 0; iii < width; iii++) for (jjj = 0; jjj < height; jjj++)
598   {
599     pixels += 3;
600     *pixels++ = alpha;
601   }
602   return pixbuf;
603 }
604
605 static GdkPixbuf *pixbuf_shrink ( GdkPixbuf *pixbuf, gdouble xshrinkfactor, gdouble yshrinkfactor )
606 {
607   GdkPixbuf *tmp;
608   guint16 width = gdk_pixbuf_get_width(pixbuf), height = gdk_pixbuf_get_height(pixbuf);
609   tmp = gdk_pixbuf_scale_simple(pixbuf, ceil(width * xshrinkfactor), ceil(height * yshrinkfactor), GDK_INTERP_NEAREST);
610   g_object_unref ( G_OBJECT(pixbuf) );
611   return tmp;
612 }
613
614 static GdkPixbuf *get_pixbuf( VikMapsLayer *vml, gint mode, MapCoord *mapcoord, gchar *filename_buf, gint buf_len, gdouble xshrinkfactor, gdouble yshrinkfactor )
615 {
616   GdkPixbuf *pixbuf;
617
618   /* get the thing */
619   pixbuf = a_mapcache_get ( mapcoord->x, mapcoord->y, mapcoord->z,
620                             mode, mapcoord->scale, vml->alpha, xshrinkfactor, yshrinkfactor );
621
622   if ( ! pixbuf ) {
623     g_snprintf ( filename_buf, buf_len, DIRSTRUCTURE,
624                      vml->cache_dir, mode,
625                      mapcoord->scale, mapcoord->z, mapcoord->x, mapcoord->y );
626     if ( g_file_test ( filename_buf, G_FILE_TEST_EXISTS ) == TRUE)
627     {
628       GError *gx = NULL;
629       pixbuf = gdk_pixbuf_new_from_file ( filename_buf, &gx );
630
631       /* free the pixbuf on error */
632       if (gx)
633       {
634         if ( gx->domain != GDK_PIXBUF_ERROR || gx->code != GDK_PIXBUF_ERROR_CORRUPT_IMAGE )
635           g_warning ( _("Couldn't open image file: %s"), gx->message );
636
637         g_error_free ( gx );
638         if ( pixbuf )
639           g_object_unref ( G_OBJECT(pixbuf) );
640         pixbuf = NULL;
641       } else {
642           if ( vml->alpha < 255 )
643             pixbuf = pixbuf_set_alpha ( pixbuf, vml->alpha );
644           if ( xshrinkfactor != 1.0 || yshrinkfactor != 1.0 )
645             pixbuf = pixbuf_shrink ( pixbuf, xshrinkfactor, yshrinkfactor );
646
647           a_mapcache_add ( pixbuf, mapcoord->x, mapcoord->y, 
648               mapcoord->z, vik_map_source_get_uniq_id(MAPS_LAYER_NTH_TYPE(vml->maptype)),
649               mapcoord->scale, vml->alpha, xshrinkfactor, yshrinkfactor );
650       }
651     }
652   }
653   return pixbuf;
654 }
655
656 gboolean should_start_autodownload(VikMapsLayer *vml, VikViewport *vvp)
657 {
658   const VikCoord *center = vik_viewport_get_center ( vvp );
659
660   if (vik_window_get_pan_move (VIK_WINDOW(VIK_GTK_WINDOW_FROM_WIDGET(GTK_WIDGET(vvp)))))
661     /* D'n'D pan in action: do not download */
662     return FALSE;
663
664   if (vml->last_center == NULL) {
665     VikCoord *new_center = g_malloc(sizeof(VikCoord));
666     *new_center = *center;
667     vml->last_center = new_center;
668     vml->last_xmpp = vik_viewport_get_xmpp(vvp);
669     vml->last_ympp = vik_viewport_get_ympp(vvp);
670     return TRUE;
671   }
672
673   /* TODO: perhaps vik_coord_diff() */
674   if (vik_coord_equals(vml->last_center, center)
675       && (vml->last_xmpp == vik_viewport_get_xmpp(vvp))
676       && (vml->last_ympp == vik_viewport_get_ympp(vvp)))
677     return FALSE;
678
679   *(vml->last_center) = *center;
680     vml->last_xmpp = vik_viewport_get_xmpp(vvp);
681     vml->last_ympp = vik_viewport_get_ympp(vvp);
682   return TRUE;
683 }
684
685 static void maps_layer_draw_section ( VikMapsLayer *vml, VikViewport *vvp, VikCoord *ul, VikCoord *br )
686 {
687   MapCoord ulm, brm;
688   gdouble xzoom = vik_viewport_get_xmpp ( vvp );
689   gdouble yzoom = vik_viewport_get_ympp ( vvp );
690   gdouble xshrinkfactor = 1.0, yshrinkfactor = 1.0;
691   gdouble existence_only = FALSE;
692
693   if ( vml->xmapzoom && (vml->xmapzoom != xzoom || vml->ymapzoom != yzoom) ) {
694     xshrinkfactor = vml->xmapzoom / xzoom;
695     yshrinkfactor = vml->ymapzoom / yzoom;
696     xzoom = vml->xmapzoom;
697     yzoom = vml->xmapzoom;
698     if ( ! (xshrinkfactor > MIN_SHRINKFACTOR && xshrinkfactor < MAX_SHRINKFACTOR &&
699          yshrinkfactor > MIN_SHRINKFACTOR && yshrinkfactor < MAX_SHRINKFACTOR ) ) {
700       if ( xshrinkfactor > REAL_MIN_SHRINKFACTOR && yshrinkfactor > REAL_MIN_SHRINKFACTOR )
701         existence_only = TRUE;
702       else {
703         g_warning ( _("Cowardly refusing to draw tiles or existence of tiles beyond %d zoom out factor"), (int)( 1.0/REAL_MIN_SHRINKFACTOR));
704         return;
705       }
706     }
707   }
708
709   /* coord -> ID */
710   VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
711   if ( vik_map_source_coord_to_mapcoord ( map, ul, xzoom, yzoom, &ulm ) &&
712        vik_map_source_coord_to_mapcoord ( map, br, xzoom, yzoom, &brm ) ) {
713
714     /* loop & draw */
715     gint x, y;
716     gint xmin = MIN(ulm.x, brm.x), xmax = MAX(ulm.x, brm.x);
717     gint ymin = MIN(ulm.y, brm.y), ymax = MAX(ulm.y, brm.y);
718     gint mode = vik_map_source_get_uniq_id(map);
719
720     VikCoord coord;
721     gint xx, yy, width, height;
722     GdkPixbuf *pixbuf;
723
724     guint max_path_len = strlen(vml->cache_dir) + 40;
725     gchar *path_buf = g_malloc ( max_path_len * sizeof(char) );
726
727     if ( (!existence_only) && vml->autodownload  && should_start_autodownload(vml, vvp)) {
728       g_debug("%s: Starting autodownload", __FUNCTION__);
729       if ( vik_map_source_supports_download_only_new (map) )
730         // Try to download newer tiles
731         start_download_thread ( vml, vvp, ul, br, REDOWNLOAD_NEW );
732       else
733         // Download only missing tiles
734         start_download_thread ( vml, vvp, ul, br, REDOWNLOAD_NONE );
735     }
736
737     if ( vik_map_source_get_tilesize_x(map) == 0 && !existence_only ) {
738       for ( x = xmin; x <= xmax; x++ ) {
739         for ( y = ymin; y <= ymax; y++ ) {
740           ulm.x = x;
741           ulm.y = y;
742           pixbuf = get_pixbuf ( vml, mode, &ulm, path_buf, max_path_len, xshrinkfactor, yshrinkfactor );
743           if ( pixbuf ) {
744             width = gdk_pixbuf_get_width ( pixbuf );
745             height = gdk_pixbuf_get_height ( pixbuf );
746
747             vik_map_source_mapcoord_to_center_coord ( map, &ulm, &coord );
748             vik_viewport_coord_to_screen ( vvp, &coord, &xx, &yy );
749             xx -= (width/2);
750             yy -= (height/2);
751
752             vik_viewport_draw_pixbuf ( vvp, pixbuf, 0, 0, xx, yy, width, height );
753           }
754         }
755       }
756     } else { /* tilesize is known, don't have to keep converting coords */
757       gdouble tilesize_x = vik_map_source_get_tilesize_x(map) * xshrinkfactor;
758       gdouble tilesize_y = vik_map_source_get_tilesize_y(map) * yshrinkfactor;
759       /* ceiled so tiles will be maximum size in the case of funky shrinkfactor */
760       gint tilesize_x_ceil = ceil ( tilesize_x );
761       gint tilesize_y_ceil = ceil ( tilesize_y );
762       gint8 xinc = (ulm.x == xmin) ? 1 : -1;
763       gint8 yinc = (ulm.y == ymin) ? 1 : -1;
764       gdouble xx, yy; gint xx_tmp, yy_tmp;
765       gint base_yy, xend, yend;
766
767       GdkGC *black_gc = GTK_WIDGET(vvp)->style->black_gc;
768
769       xend = (xinc == 1) ? (xmax+1) : (xmin-1);
770       yend = (yinc == 1) ? (ymax+1) : (ymin-1);
771
772       vik_map_source_mapcoord_to_center_coord ( map, &ulm, &coord );
773       vik_viewport_coord_to_screen ( vvp, &coord, &xx_tmp, &yy_tmp );
774       xx = xx_tmp; yy = yy_tmp;
775       /* above trick so xx,yy doubles. this is so shrinkfactors aren't rounded off
776        * eg if tile size 128, shrinkfactor 0.333 */
777       xx -= (tilesize_x/2);
778       base_yy = yy - (tilesize_y/2);
779
780       for ( x = ((xinc == 1) ? xmin : xmax); x != xend; x+=xinc ) {
781         yy = base_yy;
782         for ( y = ((yinc == 1) ? ymin : ymax); y != yend; y+=yinc ) {
783           ulm.x = x;
784           ulm.y = y;
785
786           if ( existence_only ) {
787             g_snprintf ( path_buf, max_path_len, DIRSTRUCTURE,
788                      vml->cache_dir, mode,
789                      ulm.scale, ulm.z, ulm.x, ulm.y );
790             if ( g_file_test ( path_buf, G_FILE_TEST_EXISTS ) == TRUE ) {
791               vik_viewport_draw_line ( vvp, black_gc, xx+tilesize_x_ceil, yy, xx, yy+tilesize_y_ceil );
792             }
793           } else {
794             int scale_inc;
795             for (scale_inc = 0; scale_inc < 4; scale_inc ++) {
796               /* try with correct then smaller zooms */
797               int scale_factor = 1 << scale_inc;  /*  2^scale_inc */
798               MapCoord ulm2 = ulm;
799               ulm2.x = ulm.x / scale_factor;
800               ulm2.y = ulm.y / scale_factor;
801               ulm2.scale = ulm.scale + scale_inc;
802               pixbuf = get_pixbuf ( vml, mode, &ulm2, path_buf, max_path_len, xshrinkfactor * scale_factor, yshrinkfactor * scale_factor );
803               if ( pixbuf ) {
804                 gint src_x = (ulm.x % scale_factor) * tilesize_x_ceil;
805                 gint src_y = (ulm.y % scale_factor) * tilesize_y_ceil;
806 #ifdef DEBUG
807                 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);
808 #endif
809                 vik_viewport_draw_pixbuf ( vvp, pixbuf, src_x, src_y, xx, yy, tilesize_x_ceil, tilesize_y_ceil );
810                 break;
811               }
812             }
813             if ( !pixbuf ) {
814               /* retry with bigger zooms */
815               int scale_dec;
816               for (scale_dec = 1; scale_dec < 2; scale_dec ++) {
817                 int pict_x, pict_y;
818                 int scale_factor = 1 << scale_dec;  /*  2^scale_dec */
819                 MapCoord ulm2 = ulm;
820                 ulm2.x = ulm.x * scale_factor;
821                 ulm2.y = ulm.y * scale_factor;
822                 ulm2.scale = ulm.scale - scale_dec;
823                 for (pict_x = 0; pict_x < scale_factor; pict_x ++) {
824                   for (pict_y = 0; pict_y < scale_factor; pict_y ++) {
825                     MapCoord ulm3 = ulm2;
826                     ulm3.x += pict_x;
827                     ulm3.y += pict_y;
828                     pixbuf = get_pixbuf ( vml, mode, &ulm3, path_buf, max_path_len, xshrinkfactor / scale_factor, yshrinkfactor / scale_factor );
829                     if ( pixbuf ) {
830                       gint src_x = 0;
831                       gint src_y = 0;
832                       gint dest_x = xx + pict_x * (tilesize_x_ceil / scale_factor);
833                       gint dest_y = yy + pict_y * (tilesize_y_ceil / scale_factor);
834                       vik_viewport_draw_pixbuf ( vvp, pixbuf, src_x, src_y, dest_x, dest_y, tilesize_x_ceil / scale_factor, tilesize_y_ceil / scale_factor );
835                     }
836                   }
837                 }
838               }
839             }
840           }
841
842           yy += tilesize_y;
843         }
844         xx += tilesize_x;
845       }
846     }
847
848     g_free ( path_buf );
849   }
850 }
851
852 static void maps_layer_draw ( VikMapsLayer *vml, VikViewport *vvp )
853 {
854   if ( vik_map_source_get_drawmode(MAPS_LAYER_NTH_TYPE(vml->maptype)) == vik_viewport_get_drawmode ( vvp ) )
855   {
856     VikCoord ul, br;
857
858     /* Copyright */
859     gdouble level = vik_viewport_get_zoom ( vvp );
860     LatLonBBox bbox;
861     vik_viewport_get_min_max_lat_lon ( vvp, &bbox.south, &bbox.north, &bbox.west, &bbox.east );
862     vik_map_source_get_copyright ( MAPS_LAYER_NTH_TYPE(vml->maptype), bbox, level, vik_viewport_add_copyright, vvp );
863
864     /* Logo */
865     const GdkPixbuf *logo = vik_map_source_get_logo ( MAPS_LAYER_NTH_TYPE(vml->maptype) );
866     vik_viewport_add_logo ( vvp, logo );
867
868     /* get corner coords */
869     if ( vik_viewport_get_coord_mode ( vvp ) == VIK_COORD_UTM && ! vik_viewport_is_one_zone ( vvp ) ) {
870       /* UTM multi-zone stuff by Kit Transue */
871       gchar leftmost_zone, rightmost_zone, i;
872       leftmost_zone = vik_viewport_leftmost_zone( vvp );
873       rightmost_zone = vik_viewport_rightmost_zone( vvp );
874       for ( i = leftmost_zone; i <= rightmost_zone; ++i ) {
875         vik_viewport_corners_for_zonen ( vvp, i, &ul, &br );
876         maps_layer_draw_section ( vml, vvp, &ul, &br );
877       }
878     }
879     else {
880       vik_viewport_screen_to_coord ( vvp, 0, 0, &ul );
881       vik_viewport_screen_to_coord ( vvp, vik_viewport_get_width(vvp), vik_viewport_get_height(vvp), &br );
882
883       maps_layer_draw_section ( vml, vvp, &ul, &br );
884     }
885   }
886 }
887
888 /*************************/
889 /****** DOWNLOADING ******/
890 /*************************/
891
892 /* pass along data to thread, exists even if layer is deleted. */
893 typedef struct {
894   gchar *cache_dir;
895   gchar *filename_buf;
896   gint x0, y0, xf, yf;
897   MapCoord mapcoord;
898   gint maptype;
899   gint maxlen;
900   gint mapstoget;
901   gint redownload;
902   gboolean refresh_display;
903   VikMapsLayer *vml;
904   VikViewport *vvp;
905   gboolean map_layer_alive;
906   GMutex *mutex;
907 } MapDownloadInfo;
908
909 static void mdi_free ( MapDownloadInfo *mdi )
910 {
911   g_mutex_free(mdi->mutex);
912   g_free ( mdi->cache_dir );
913   mdi->cache_dir = NULL;
914   g_free ( mdi->filename_buf );
915   mdi->filename_buf = NULL;
916   g_free ( mdi );
917 }
918
919 static void weak_ref_cb(gpointer ptr, GObject * dead_vml)
920 {
921   MapDownloadInfo *mdi = ptr;
922   g_mutex_lock(mdi->mutex);
923   mdi->map_layer_alive = FALSE;
924   g_mutex_unlock(mdi->mutex);
925 }
926
927 static int map_download_thread ( MapDownloadInfo *mdi, gpointer threaddata )
928 {
929   void *handle = vik_map_source_download_handle_init(MAPS_LAYER_NTH_TYPE(mdi->maptype));
930   guint donemaps = 0;
931   gint x, y;
932   for ( x = mdi->x0; x <= mdi->xf; x++ )
933   {
934     for ( y = mdi->y0; y <= mdi->yf; y++ )
935     {
936       gboolean remove_mem_cache = FALSE;
937       gboolean need_download = FALSE;
938       g_snprintf ( mdi->filename_buf, mdi->maxlen, DIRSTRUCTURE,
939                      mdi->cache_dir, vik_map_source_get_uniq_id(MAPS_LAYER_NTH_TYPE(mdi->maptype)),
940                      mdi->mapcoord.scale, mdi->mapcoord.z, x, y );
941
942       donemaps++;
943       int res = a_background_thread_progress ( threaddata, ((gdouble)donemaps) / mdi->mapstoget ); /* this also calls testcancel */
944       if (res != 0) {
945         vik_map_source_download_handle_cleanup(MAPS_LAYER_NTH_TYPE(mdi->maptype), handle);
946         return -1;
947       }
948
949       if ( g_file_test ( mdi->filename_buf, G_FILE_TEST_EXISTS ) == FALSE ) {
950         need_download = TRUE;
951         remove_mem_cache = TRUE;
952
953       } else {  /* in case map file already exists */
954         switch (mdi->redownload) {
955           case REDOWNLOAD_NONE:
956             continue;
957
958           case REDOWNLOAD_BAD:
959           {
960             /* see if this one is bad or what */
961             GError *gx = NULL;
962             GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file ( mdi->filename_buf, &gx );
963             if (gx || (!pixbuf)) {
964               g_remove ( mdi->filename_buf );
965               need_download = TRUE;
966               remove_mem_cache = TRUE;
967               g_error_free ( gx );
968
969             } else {
970               g_object_unref ( pixbuf );
971             }
972             break;
973           }
974
975           case REDOWNLOAD_NEW:
976             need_download = TRUE;
977             remove_mem_cache = TRUE;
978             break;
979
980           case REDOWNLOAD_ALL:
981             /* FIXME: need a better way than to erase file in case of server/network problem */
982             g_remove ( mdi->filename_buf );
983             need_download = TRUE;
984             remove_mem_cache = TRUE;
985             break;
986
987           case DOWNLOAD_OR_REFRESH:
988             remove_mem_cache = TRUE;
989             break;
990
991           default:
992             g_warning ( "redownload state %d unknown\n", mdi->redownload);
993         }
994       }
995
996       mdi->mapcoord.x = x; mdi->mapcoord.y = y;
997
998       if (need_download) {
999         if ( vik_map_source_download( MAPS_LAYER_NTH_TYPE(mdi->maptype), &(mdi->mapcoord), mdi->filename_buf, handle))
1000           continue;
1001       }
1002
1003       gdk_threads_enter();
1004       g_mutex_lock(mdi->mutex);
1005       if (remove_mem_cache)
1006           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 );
1007       if (mdi->refresh_display && mdi->map_layer_alive) {
1008         /* TODO: check if it's on visible area */
1009         vik_layer_emit_update ( VIK_LAYER(mdi->vml) );
1010       }
1011       g_mutex_unlock(mdi->mutex);
1012       gdk_threads_leave();
1013       mdi->mapcoord.x = mdi->mapcoord.y = 0; /* we're temporarily between downloads */
1014
1015     }
1016   }
1017   vik_map_source_download_handle_cleanup(MAPS_LAYER_NTH_TYPE(mdi->maptype), handle);
1018   g_mutex_lock(mdi->mutex);
1019   if (mdi->map_layer_alive)
1020     g_object_weak_unref(G_OBJECT(mdi->vml), weak_ref_cb, mdi);
1021   g_mutex_unlock(mdi->mutex); 
1022   return 0;
1023 }
1024
1025 static void mdi_cancel_cleanup ( MapDownloadInfo *mdi )
1026 {
1027   if ( mdi->mapcoord.x || mdi->mapcoord.y )
1028   {
1029     g_snprintf ( mdi->filename_buf, mdi->maxlen, DIRSTRUCTURE,
1030                      mdi->cache_dir, vik_map_source_get_uniq_id(MAPS_LAYER_NTH_TYPE(mdi->maptype)),
1031                      mdi->mapcoord.scale, mdi->mapcoord.z, mdi->mapcoord.x, mdi->mapcoord.y );
1032     if ( g_file_test ( mdi->filename_buf, G_FILE_TEST_EXISTS ) == TRUE)
1033     {
1034       g_remove ( mdi->filename_buf );
1035     }
1036   }
1037 }
1038
1039 static void start_download_thread ( VikMapsLayer *vml, VikViewport *vvp, const VikCoord *ul, const VikCoord *br, gint redownload )
1040 {
1041   gdouble xzoom = vml->xmapzoom ? vml->xmapzoom : vik_viewport_get_xmpp ( vvp );
1042   gdouble yzoom = vml->ymapzoom ? vml->ymapzoom : vik_viewport_get_ympp ( vvp );
1043   MapCoord ulm, brm;
1044   VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
1045   if ( vik_map_source_coord_to_mapcoord ( map, ul, xzoom, yzoom, &ulm ) 
1046     && vik_map_source_coord_to_mapcoord ( map, br, xzoom, yzoom, &brm ) )
1047   {
1048     MapDownloadInfo *mdi = g_malloc ( sizeof(MapDownloadInfo) );
1049     gint a, b;
1050
1051     mdi->vml = vml;
1052     mdi->vvp = vvp;
1053     mdi->map_layer_alive = TRUE;
1054     mdi->mutex = g_mutex_new();
1055     mdi->refresh_display = TRUE;
1056
1057     /* cache_dir and buffer for dest filename */
1058     mdi->cache_dir = g_strdup ( vml->cache_dir );
1059     mdi->maxlen = strlen ( vml->cache_dir ) + 40;
1060     mdi->filename_buf = g_malloc ( mdi->maxlen * sizeof(gchar) );
1061     mdi->maptype = vml->maptype;
1062
1063     mdi->mapcoord = ulm;
1064
1065     mdi->redownload = redownload;
1066
1067     mdi->x0 = MIN(ulm.x, brm.x);
1068     mdi->xf = MAX(ulm.x, brm.x);
1069     mdi->y0 = MIN(ulm.y, brm.y);
1070     mdi->yf = MAX(ulm.y, brm.y);
1071
1072     mdi->mapstoget = 0;
1073
1074     if ( mdi->redownload ) {
1075       mdi->mapstoget = (mdi->xf - mdi->x0 + 1) * (mdi->yf - mdi->y0 + 1);
1076     } else {
1077       /* calculate how many we need */
1078       for ( a = mdi->x0; a <= mdi->xf; a++ )
1079       {
1080         for ( b = mdi->y0; b <= mdi->yf; b++ )
1081         {
1082           g_snprintf ( mdi->filename_buf, mdi->maxlen, DIRSTRUCTURE,
1083                        vml->cache_dir, vik_map_source_get_uniq_id(map), ulm.scale,
1084                        ulm.z, a, b );
1085           if ( g_file_test ( mdi->filename_buf, G_FILE_TEST_EXISTS ) == FALSE )
1086             mdi->mapstoget++;
1087         }
1088       }
1089     }
1090
1091     mdi->mapcoord.x = mdi->mapcoord.y = 0; /* for cleanup -- no current map */
1092
1093     if ( mdi->mapstoget )
1094     {
1095       const gchar *tmp_str;
1096       gchar *tmp;
1097
1098       if (redownload) 
1099       {
1100         if (redownload == REDOWNLOAD_BAD)
1101           tmp_str = ngettext("Redownloading up to %d %s map...", "Redownloading up to %d %s maps...", mdi->mapstoget);
1102         else
1103           tmp_str = ngettext("Redownloading %d %s map...", "Redownloading %d %s maps...", mdi->mapstoget);
1104       } 
1105       else 
1106       {
1107         tmp_str = ngettext("Downloading %d %s map...", "Downloading %d %s maps...", mdi->mapstoget);
1108       }
1109       tmp = g_strdup_printf ( tmp_str, mdi->mapstoget, MAPS_LAYER_NTH_LABEL(vml->maptype));
1110  
1111       g_object_weak_ref(G_OBJECT(mdi->vml), weak_ref_cb, mdi);
1112       /* launch the thread */
1113       a_background_thread ( VIK_GTK_WINDOW_FROM_LAYER(vml), /* parent window */
1114                             tmp,                                              /* description string */
1115                             (vik_thr_func) map_download_thread,               /* function to call within thread */
1116                             mdi,                                              /* pass along data */
1117                             (vik_thr_free_func) mdi_free,                     /* function to free pass along data */
1118                             (vik_thr_free_func) mdi_cancel_cleanup,
1119                             mdi->mapstoget );
1120       g_free ( tmp );
1121     }
1122     else
1123       mdi_free ( mdi );
1124   }
1125 }
1126
1127 void maps_layer_download_section_without_redraw( VikMapsLayer *vml, VikViewport *vvp, VikCoord *ul, VikCoord *br, gdouble zoom)
1128 {
1129   MapCoord ulm, brm;
1130   VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
1131
1132   if (!vik_map_source_coord_to_mapcoord(map, ul, zoom, zoom, &ulm) 
1133     || !vik_map_source_coord_to_mapcoord(map, br, zoom, zoom, &brm)) {
1134     g_warning("%s() coord_to_mapcoord() failed", __PRETTY_FUNCTION__);
1135     return;
1136   }
1137
1138   MapDownloadInfo *mdi = g_malloc(sizeof(MapDownloadInfo));
1139   gint i, j;
1140
1141   mdi->vml = vml;
1142   mdi->vvp = vvp;
1143   mdi->map_layer_alive = TRUE;
1144   mdi->mutex = g_mutex_new();
1145   mdi->refresh_display = FALSE;
1146
1147   mdi->cache_dir = g_strdup ( vml->cache_dir );
1148   mdi->maxlen = strlen ( vml->cache_dir ) + 40;
1149   mdi->filename_buf = g_malloc ( mdi->maxlen * sizeof(gchar) );
1150   mdi->maptype = vml->maptype;
1151
1152   mdi->mapcoord = ulm;
1153
1154   mdi->redownload = REDOWNLOAD_NONE;
1155
1156   mdi->x0 = MIN(ulm.x, brm.x);
1157   mdi->xf = MAX(ulm.x, brm.x);
1158   mdi->y0 = MIN(ulm.y, brm.y);
1159   mdi->yf = MAX(ulm.y, brm.y);
1160
1161   mdi->mapstoget = 0;
1162
1163   for (i = mdi->x0; i <= mdi->xf; i++) {
1164     for (j = mdi->y0; j <= mdi->yf; j++) {
1165       g_snprintf ( mdi->filename_buf, mdi->maxlen, DIRSTRUCTURE,
1166                    vml->cache_dir, vik_map_source_get_uniq_id(map), ulm.scale,
1167                    ulm.z, i, j );
1168       if ( g_file_test ( mdi->filename_buf, G_FILE_TEST_EXISTS ) == FALSE )
1169             mdi->mapstoget++;
1170     }
1171   }
1172
1173   mdi->mapcoord.x = mdi->mapcoord.y = 0; /* for cleanup -- no current map */
1174
1175   if (mdi->mapstoget) {
1176     gchar *tmp;
1177     const gchar *fmt;
1178     fmt = ngettext("Downloading %d %s map...",
1179                    "Downloading %d %s maps...",
1180                    mdi->mapstoget);
1181     tmp = g_strdup_printf ( fmt, mdi->mapstoget, MAPS_LAYER_NTH_LABEL(vml->maptype) );
1182
1183     g_object_weak_ref(G_OBJECT(mdi->vml), weak_ref_cb, mdi);
1184       /* launch the thread */
1185     a_background_thread ( VIK_GTK_WINDOW_FROM_LAYER(vml), /* parent window */
1186       tmp,                                /* description string */
1187       (vik_thr_func) map_download_thread, /* function to call within thread */
1188       mdi,                                /* pass along data */
1189       (vik_thr_free_func) mdi_free,       /* function to free pass along data */
1190       (vik_thr_free_func) mdi_cancel_cleanup,
1191       mdi->mapstoget );
1192     g_free ( tmp );
1193   }
1194   else
1195     mdi_free ( mdi );
1196 }
1197
1198 static void maps_layer_redownload_bad ( VikMapsLayer *vml )
1199 {
1200   start_download_thread ( vml, vml->redownload_vvp, &(vml->redownload_ul), &(vml->redownload_br), REDOWNLOAD_BAD );
1201 }
1202
1203 static void maps_layer_redownload_all ( VikMapsLayer *vml )
1204 {
1205   start_download_thread ( vml, vml->redownload_vvp, &(vml->redownload_ul), &(vml->redownload_br), REDOWNLOAD_ALL );
1206 }
1207
1208 static void maps_layer_redownload_new ( VikMapsLayer *vml )
1209 {
1210   start_download_thread ( vml, vml->redownload_vvp, &(vml->redownload_ul), &(vml->redownload_br), REDOWNLOAD_NEW );
1211 }
1212
1213 static gboolean maps_layer_download_release ( VikMapsLayer *vml, GdkEventButton *event, VikViewport *vvp )
1214 {
1215   if (!vml || vml->vl.type != VIK_LAYER_MAPS)
1216     return FALSE;
1217   if ( vml->dl_tool_x != -1 && vml->dl_tool_y != -1 )
1218   {
1219     if ( event->button == 1 )
1220     {
1221       VikCoord ul, br;
1222       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 );
1223       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 );
1224       start_download_thread ( vml, vvp, &ul, &br, DOWNLOAD_OR_REFRESH );
1225       vml->dl_tool_x = vml->dl_tool_y = -1;
1226       return TRUE;
1227     }
1228     else
1229     {
1230       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) );
1231       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) );
1232
1233       vml->redownload_vvp = vvp;
1234
1235       vml->dl_tool_x = vml->dl_tool_y = -1;
1236
1237       if ( ! vml->dl_right_click_menu ) {
1238         GtkWidget *item;
1239         vml->dl_right_click_menu = GTK_MENU ( gtk_menu_new () );
1240
1241         item = gtk_menu_item_new_with_mnemonic ( _("Redownload _Bad Map(s)") );
1242         g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_redownload_bad), vml );
1243         gtk_menu_shell_append ( GTK_MENU_SHELL(vml->dl_right_click_menu), item );
1244
1245         item = gtk_menu_item_new_with_mnemonic ( _("Redownload _New Map(s)") );
1246         g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_redownload_new), vml );
1247         gtk_menu_shell_append ( GTK_MENU_SHELL(vml->dl_right_click_menu), item );
1248
1249         item = gtk_menu_item_new_with_mnemonic ( _("Redownload _All Map(s)") );
1250         g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_redownload_all), vml );
1251         gtk_menu_shell_append ( GTK_MENU_SHELL(vml->dl_right_click_menu), item );
1252       }
1253
1254       gtk_menu_popup ( vml->dl_right_click_menu, NULL, NULL, NULL, NULL, event->button, event->time );
1255       gtk_widget_show_all ( GTK_WIDGET(vml->dl_right_click_menu) );
1256     }
1257   }
1258   return FALSE;
1259 }
1260
1261 static gpointer maps_layer_download_create ( VikWindow *vw, VikViewport *vvp)
1262 {
1263   return vvp;
1264 }
1265
1266 static gboolean maps_layer_download_click ( VikMapsLayer *vml, GdkEventButton *event, VikViewport *vvp )
1267 {
1268   MapCoord tmp;
1269   if (!vml || vml->vl.type != VIK_LAYER_MAPS)
1270     return FALSE;
1271   VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
1272   if ( vik_map_source_get_drawmode(map) == vik_viewport_get_drawmode ( vvp ) &&
1273        vik_map_source_coord_to_mapcoord ( map, vik_viewport_get_center ( vvp ),
1274            vml->xmapzoom ? vml->xmapzoom : vik_viewport_get_xmpp ( vvp ),
1275            vml->ymapzoom ? vml->ymapzoom : vik_viewport_get_ympp ( vvp ),
1276            &tmp ) ) {
1277     vml->dl_tool_x = event->x, vml->dl_tool_y = event->y;
1278     return TRUE;
1279   }
1280   return FALSE;
1281
1282  
1283 #if 0
1284   if ( __map_types[vml->maptype].drawmode == vik_viewport_get_drawmode ( vvp ) )
1285   {
1286     VikCoord coord;
1287     MapCoord mapcoord;
1288     vik_viewport_screen_to_coord ( vvp, event->x, event->y, &coord );
1289     if ( __map_types[vml->maptype].coord_to_mapcoord ( &coord,
1290                 vml->xmapzoom ? vml->xmapzoom : vik_viewport_get_xmpp ( vvp ),
1291                 vml->ymapzoom ? vml->ymapzoom : vik_viewport_get_ympp ( vvp ),
1292                 &mapcoord ) ) {
1293       gchar *filename_buf = g_strdup_printf ( DIRSTRUCTURE,
1294                      vml->cache_dir, __map_types[vml->maptype].uniq_id,
1295                      mapcoord.scale, mapcoord.z, mapcoord.x, mapcoord.y );
1296
1297       __map_types[vml->maptype].download ( &mapcoord, filename_buf );
1298       g_free ( filename_buf );
1299       vik_layer_emit_update ( VIK_LAYER(vml) );
1300       return TRUE;
1301     }
1302   }
1303   return FALSE;
1304 #endif
1305 }
1306
1307 static void download_onscreen_maps ( gpointer vml_vvp[2], gint redownload )
1308 {
1309   VikMapsLayer *vml = vml_vvp[0];
1310   VikViewport *vvp = vml_vvp[1];
1311   VikViewportDrawMode vp_drawmode = vik_viewport_get_drawmode ( vvp );
1312
1313   gdouble xzoom = vml->xmapzoom ? vml->xmapzoom : vik_viewport_get_xmpp ( vvp );
1314   gdouble yzoom = vml->ymapzoom ? vml->ymapzoom : vik_viewport_get_ympp ( vvp );
1315
1316   VikCoord ul, br;
1317   MapCoord ulm, brm;
1318
1319   vik_viewport_screen_to_coord ( vvp, 0, 0, &ul );
1320   vik_viewport_screen_to_coord ( vvp, vik_viewport_get_width(vvp), vik_viewport_get_height(vvp), &br );
1321
1322   VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
1323   if ( vik_map_source_get_drawmode(map) == vp_drawmode &&
1324        vik_map_source_coord_to_mapcoord ( map, &ul, xzoom, yzoom, &ulm ) &&
1325        vik_map_source_coord_to_mapcoord ( map, &br, xzoom, yzoom, &brm ) )
1326     start_download_thread ( vml, vvp, &ul, &br, redownload );
1327   else if (vik_map_source_get_drawmode(map) != vp_drawmode) {
1328     const gchar *drawmode_name = vik_viewport_get_drawmode_name (vvp, vik_map_source_get_drawmode(map));
1329     gchar *err = g_strdup_printf(_("Wrong drawmode for this map.\nSelect \"%s\" from View menu and try again."), _(drawmode_name));
1330     a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vml), err );
1331     g_free(err);
1332   }
1333   else
1334     a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vml), _("Wrong zoom level for this map.") );
1335
1336 }
1337
1338 static void maps_layer_download_missing_onscreen_maps ( gpointer vml_vvp[2] )
1339 {
1340   download_onscreen_maps( vml_vvp, REDOWNLOAD_NONE);
1341 }
1342
1343 static void maps_layer_download_new_onscreen_maps ( gpointer vml_vvp[2] )
1344 {
1345   download_onscreen_maps( vml_vvp, REDOWNLOAD_NEW);
1346 }
1347
1348 static void maps_layer_redownload_all_onscreen_maps ( gpointer vml_vvp[2] )
1349 {
1350   download_onscreen_maps( vml_vvp, REDOWNLOAD_ALL);
1351 }
1352
1353 static void maps_layer_add_menu_items ( VikMapsLayer *vml, GtkMenu *menu, VikLayersPanel *vlp )
1354 {
1355   static gpointer pass_along[2];
1356   GtkWidget *item;
1357   pass_along[0] = vml;
1358   pass_along[1] = vik_layers_panel_get_viewport( VIK_LAYERS_PANEL(vlp) );
1359
1360   item = gtk_menu_item_new();
1361   gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
1362   gtk_widget_show ( item );
1363
1364   /* Now with icons */
1365   item = gtk_image_menu_item_new_with_mnemonic ( _("Download _Missing Onscreen Maps") );
1366     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
1367   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_download_missing_onscreen_maps), pass_along );
1368   gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1369   gtk_widget_show ( item );
1370
1371   if ( vik_map_source_supports_download_only_new (MAPS_LAYER_NTH_TYPE(vml->maptype)) ) {
1372     item = gtk_image_menu_item_new_with_mnemonic ( _("Download _New Onscreen Maps") );
1373     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REDO, GTK_ICON_SIZE_MENU) );
1374     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_download_new_onscreen_maps), pass_along );
1375     gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1376     gtk_widget_show ( item );
1377   }
1378
1379   item = gtk_image_menu_item_new_with_mnemonic ( _("Reload _All Onscreen Maps") );
1380   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
1381   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_redownload_all_onscreen_maps), pass_along );
1382   gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1383   gtk_widget_show ( item );
1384 }