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