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