]> git.street.me.uk Git - andy/viking.git/blob - src/vikmapslayer.c
Extend set/get param interface to have knowledge of what type of operation it is...
[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 #ifdef DEBUG
696       fputs(stderr, "DEBUG: Starting autodownload\n");
697 #endif
698       if ( vik_map_source_supports_if_modified_since (map) )
699         // Try to download newer tiles
700         start_download_thread ( vml, vvp, ul, br, REDOWNLOAD_NEW );
701       else
702         // Download only missing tiles
703         start_download_thread ( vml, vvp, ul, br, REDOWNLOAD_NONE );
704     }
705
706     if ( vik_map_source_get_tilesize_x(map) == 0 && !existence_only ) {
707       for ( x = xmin; x <= xmax; x++ ) {
708         for ( y = ymin; y <= ymax; y++ ) {
709           ulm.x = x;
710           ulm.y = y;
711           pixbuf = get_pixbuf ( vml, mode, &ulm, path_buf, max_path_len, xshrinkfactor, yshrinkfactor );
712           if ( pixbuf ) {
713             width = gdk_pixbuf_get_width ( pixbuf );
714             height = gdk_pixbuf_get_height ( pixbuf );
715
716             vik_map_source_mapcoord_to_center_coord ( map, &ulm, &coord );
717             vik_viewport_coord_to_screen ( vvp, &coord, &xx, &yy );
718             xx -= (width/2);
719             yy -= (height/2);
720
721             vik_viewport_draw_pixbuf ( vvp, pixbuf, 0, 0, xx, yy, width, height );
722           }
723         }
724       }
725     } else { /* tilesize is known, don't have to keep converting coords */
726       gdouble tilesize_x = vik_map_source_get_tilesize_x(map) * xshrinkfactor;
727       gdouble tilesize_y = vik_map_source_get_tilesize_y(map) * yshrinkfactor;
728       /* ceiled so tiles will be maximum size in the case of funky shrinkfactor */
729       gint tilesize_x_ceil = ceil ( tilesize_x );
730       gint tilesize_y_ceil = ceil ( tilesize_y );
731       gint8 xinc = (ulm.x == xmin) ? 1 : -1;
732       gint8 yinc = (ulm.y == ymin) ? 1 : -1;
733       gdouble xx, yy; gint xx_tmp, yy_tmp;
734       gint base_yy, xend, yend;
735
736       GdkGC *black_gc = GTK_WIDGET(vvp)->style->black_gc;
737
738       xend = (xinc == 1) ? (xmax+1) : (xmin-1);
739       yend = (yinc == 1) ? (ymax+1) : (ymin-1);
740
741       vik_map_source_mapcoord_to_center_coord ( map, &ulm, &coord );
742       vik_viewport_coord_to_screen ( vvp, &coord, &xx_tmp, &yy_tmp );
743       xx = xx_tmp; yy = yy_tmp;
744       /* above trick so xx,yy doubles. this is so shrinkfactors aren't rounded off
745        * eg if tile size 128, shrinkfactor 0.333 */
746       xx -= (tilesize_x/2);
747       base_yy = yy - (tilesize_y/2);
748
749       for ( x = ((xinc == 1) ? xmin : xmax); x != xend; x+=xinc ) {
750         yy = base_yy;
751         for ( y = ((yinc == 1) ? ymin : ymax); y != yend; y+=yinc ) {
752           ulm.x = x;
753           ulm.y = y;
754
755           if ( existence_only ) {
756             g_snprintf ( path_buf, max_path_len, DIRSTRUCTURE,
757                      vml->cache_dir, mode,
758                      ulm.scale, ulm.z, ulm.x, ulm.y );
759             if ( g_file_test ( path_buf, G_FILE_TEST_EXISTS ) == TRUE ) {
760               vik_viewport_draw_line ( vvp, black_gc, xx+tilesize_x_ceil, yy, xx, yy+tilesize_y_ceil );
761             }
762           } else {
763             int scale_inc;
764             for (scale_inc = 0; scale_inc < 4; scale_inc ++) {
765               /* try with correct then smaller zooms */
766               int scale_factor = 1 << scale_inc;  /*  2^scale_inc */
767               MapCoord ulm2 = ulm;
768               ulm2.x = ulm.x / scale_factor;
769               ulm2.y = ulm.y / scale_factor;
770               ulm2.scale = ulm.scale + scale_inc;
771               pixbuf = get_pixbuf ( vml, mode, &ulm2, path_buf, max_path_len, xshrinkfactor * scale_factor, yshrinkfactor * scale_factor );
772               if ( pixbuf ) {
773                 gint src_x = (ulm.x % scale_factor) * tilesize_x_ceil;
774                 gint src_y = (ulm.y % scale_factor) * tilesize_y_ceil;
775 #ifdef DEBUG
776                 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);
777 #endif
778                 vik_viewport_draw_pixbuf ( vvp, pixbuf, src_x, src_y, xx, yy, tilesize_x_ceil, tilesize_y_ceil );
779                 break;
780               }
781             }
782             if ( !pixbuf ) {
783               /* retry with bigger zooms */
784               int scale_dec;
785               for (scale_dec = 1; scale_dec < 2; scale_dec ++) {
786                 int pict_x, pict_y;
787                 int scale_factor = 1 << scale_dec;  /*  2^scale_dec */
788                 MapCoord ulm2 = ulm;
789                 ulm2.x = ulm.x * scale_factor;
790                 ulm2.y = ulm.y * scale_factor;
791                 ulm2.scale = ulm.scale - scale_dec;
792                 for (pict_x = 0; pict_x < scale_factor; pict_x ++) {
793                   for (pict_y = 0; pict_y < scale_factor; pict_y ++) {
794                     MapCoord ulm3 = ulm2;
795                     ulm3.x += pict_x;
796                     ulm3.y += pict_y;
797                     pixbuf = get_pixbuf ( vml, mode, &ulm3, path_buf, max_path_len, xshrinkfactor / scale_factor, yshrinkfactor / scale_factor );
798                     if ( pixbuf ) {
799                       gint src_x = 0;
800                       gint src_y = 0;
801                       gint dest_x = xx + pict_x * (tilesize_x_ceil / scale_factor);
802                       gint dest_y = yy + pict_y * (tilesize_y_ceil / scale_factor);
803                       vik_viewport_draw_pixbuf ( vvp, pixbuf, src_x, src_y, dest_x, dest_y, tilesize_x_ceil / scale_factor, tilesize_y_ceil / scale_factor );
804                     }
805                   }
806                 }
807               }
808             }
809           }
810
811           yy += tilesize_y;
812         }
813         xx += tilesize_x;
814       }
815     }
816
817     g_free ( path_buf );
818   }
819 }
820
821 static void maps_layer_draw ( VikMapsLayer *vml, VikViewport *vvp )
822 {
823   if ( vik_map_source_get_drawmode(MAPS_LAYER_NTH_TYPE(vml->maptype)) == vik_viewport_get_drawmode ( vvp ) )
824   {
825     VikCoord ul, br;
826
827     /* get corner coords */
828     if ( vik_viewport_get_coord_mode ( vvp ) == VIK_COORD_UTM && ! vik_viewport_is_one_zone ( vvp ) ) {
829       /* UTM multi-zone stuff by Kit Transue */
830       gchar leftmost_zone, rightmost_zone, i;
831       leftmost_zone = vik_viewport_leftmost_zone( vvp );
832       rightmost_zone = vik_viewport_rightmost_zone( vvp );
833       for ( i = leftmost_zone; i <= rightmost_zone; ++i ) {
834         vik_viewport_corners_for_zonen ( vvp, i, &ul, &br );
835         maps_layer_draw_section ( vml, vvp, &ul, &br );
836       }
837     }
838     else {
839       vik_viewport_screen_to_coord ( vvp, 0, 0, &ul );
840       vik_viewport_screen_to_coord ( vvp, vik_viewport_get_width(vvp), vik_viewport_get_height(vvp), &br );
841
842       maps_layer_draw_section ( vml, vvp, &ul, &br );
843     }
844   }
845 }
846
847 /*************************/
848 /****** DOWNLOADING ******/
849 /*************************/
850
851 /* pass along data to thread, exists even if layer is deleted. */
852 typedef struct {
853   gchar *cache_dir;
854   gchar *filename_buf;
855   gint x0, y0, xf, yf;
856   MapCoord mapcoord;
857   gint maptype;
858   gint maxlen;
859   gint mapstoget;
860   gint redownload;
861   gboolean refresh_display;
862   VikMapsLayer *vml;
863   VikViewport *vvp;
864   gboolean map_layer_alive;
865   GMutex *mutex;
866 } MapDownloadInfo;
867
868 static void mdi_free ( MapDownloadInfo *mdi )
869 {
870   g_mutex_free(mdi->mutex);
871   g_free ( mdi->cache_dir );
872   mdi->cache_dir = NULL;
873   g_free ( mdi->filename_buf );
874   mdi->filename_buf = NULL;
875   g_free ( mdi );
876 }
877
878 static void weak_ref_cb(gpointer ptr, GObject * dead_vml)
879 {
880   MapDownloadInfo *mdi = ptr;
881   g_mutex_lock(mdi->mutex);
882   mdi->map_layer_alive = FALSE;
883   g_mutex_unlock(mdi->mutex);
884 }
885
886 static int map_download_thread ( MapDownloadInfo *mdi, gpointer threaddata )
887 {
888   void *handle = vik_map_source_download_handle_init(MAPS_LAYER_NTH_TYPE(mdi->maptype));
889   guint donemaps = 0;
890   gint x, y;
891   for ( x = mdi->x0; x <= mdi->xf; x++ )
892   {
893     for ( y = mdi->y0; y <= mdi->yf; y++ )
894     {
895       gboolean remove_mem_cache = FALSE;
896       gboolean need_download = FALSE;
897       g_snprintf ( mdi->filename_buf, mdi->maxlen, DIRSTRUCTURE,
898                      mdi->cache_dir, vik_map_source_get_uniq_id(MAPS_LAYER_NTH_TYPE(mdi->maptype)),
899                      mdi->mapcoord.scale, mdi->mapcoord.z, x, y );
900
901       donemaps++;
902       int res = a_background_thread_progress ( threaddata, ((gdouble)donemaps) / mdi->mapstoget ); /* this also calls testcancel */
903       if (res != 0) {
904         vik_map_source_download_handle_cleanup(MAPS_LAYER_NTH_TYPE(mdi->maptype), handle);
905         return -1;
906       }
907
908       if ( g_file_test ( mdi->filename_buf, G_FILE_TEST_EXISTS ) == FALSE ) {
909         need_download = TRUE;
910         remove_mem_cache = TRUE;
911
912       } else {  /* in case map file already exists */
913         switch (mdi->redownload) {
914           case REDOWNLOAD_NONE:
915             continue;
916
917           case REDOWNLOAD_BAD:
918           {
919             /* see if this one is bad or what */
920             GError *gx = NULL;
921             GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file ( mdi->filename_buf, &gx );
922             if (gx || (!pixbuf)) {
923               g_remove ( mdi->filename_buf );
924               need_download = TRUE;
925               remove_mem_cache = TRUE;
926               g_error_free ( gx );
927
928             } else {
929               g_object_unref ( pixbuf );
930             }
931             break;
932           }
933
934           case REDOWNLOAD_NEW:
935             need_download = TRUE;
936             remove_mem_cache = TRUE;
937             break;
938
939           case REDOWNLOAD_ALL:
940             /* FIXME: need a better way than to erase file in case of server/network problem */
941             g_remove ( mdi->filename_buf );
942             need_download = TRUE;
943             remove_mem_cache = TRUE;
944             break;
945
946           case DOWNLOAD_OR_REFRESH:
947             remove_mem_cache = TRUE;
948             break;
949
950           default:
951             g_warning ( "redownload state %d unknown\n", mdi->redownload);
952         }
953       }
954
955       mdi->mapcoord.x = x; mdi->mapcoord.y = y;
956
957       if (need_download) {
958         if ( vik_map_source_download( MAPS_LAYER_NTH_TYPE(mdi->maptype), &(mdi->mapcoord), mdi->filename_buf, handle))
959           continue;
960       }
961
962       gdk_threads_enter();
963       g_mutex_lock(mdi->mutex);
964       if (remove_mem_cache)
965           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 );
966       if (mdi->refresh_display && mdi->map_layer_alive) {
967         /* TODO: check if it's on visible area */
968         vik_layer_emit_update ( VIK_LAYER(mdi->vml) );
969       }
970       g_mutex_unlock(mdi->mutex);
971       gdk_threads_leave();
972       mdi->mapcoord.x = mdi->mapcoord.y = 0; /* we're temporarily between downloads */
973
974     }
975   }
976   vik_map_source_download_handle_cleanup(MAPS_LAYER_NTH_TYPE(mdi->maptype), handle);
977   g_mutex_lock(mdi->mutex);
978   if (mdi->map_layer_alive)
979     g_object_weak_unref(G_OBJECT(mdi->vml), weak_ref_cb, mdi);
980   g_mutex_unlock(mdi->mutex); 
981   return 0;
982 }
983
984 static void mdi_cancel_cleanup ( MapDownloadInfo *mdi )
985 {
986   if ( mdi->mapcoord.x || mdi->mapcoord.y )
987   {
988     g_snprintf ( mdi->filename_buf, mdi->maxlen, DIRSTRUCTURE,
989                      mdi->cache_dir, vik_map_source_get_uniq_id(MAPS_LAYER_NTH_TYPE(mdi->maptype)),
990                      mdi->mapcoord.scale, mdi->mapcoord.z, mdi->mapcoord.x, mdi->mapcoord.y );
991     if ( g_file_test ( mdi->filename_buf, G_FILE_TEST_EXISTS ) == TRUE)
992     {
993       g_remove ( mdi->filename_buf );
994     }
995   }
996 }
997
998 static void start_download_thread ( VikMapsLayer *vml, VikViewport *vvp, const VikCoord *ul, const VikCoord *br, gint redownload )
999 {
1000   gdouble xzoom = vml->xmapzoom ? vml->xmapzoom : vik_viewport_get_xmpp ( vvp );
1001   gdouble yzoom = vml->ymapzoom ? vml->ymapzoom : vik_viewport_get_ympp ( vvp );
1002   MapCoord ulm, brm;
1003   VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
1004   if ( vik_map_source_coord_to_mapcoord ( map, ul, xzoom, yzoom, &ulm ) 
1005     && vik_map_source_coord_to_mapcoord ( map, br, xzoom, yzoom, &brm ) )
1006   {
1007     MapDownloadInfo *mdi = g_malloc ( sizeof(MapDownloadInfo) );
1008     gint a, b;
1009
1010     mdi->vml = vml;
1011     mdi->vvp = vvp;
1012     mdi->map_layer_alive = TRUE;
1013     mdi->mutex = g_mutex_new();
1014     mdi->refresh_display = TRUE;
1015
1016     /* cache_dir and buffer for dest filename */
1017     mdi->cache_dir = g_strdup ( vml->cache_dir );
1018     mdi->maxlen = strlen ( vml->cache_dir ) + 40;
1019     mdi->filename_buf = g_malloc ( mdi->maxlen * sizeof(gchar) );
1020     mdi->maptype = vml->maptype;
1021
1022     mdi->mapcoord = ulm;
1023
1024     mdi->redownload = redownload;
1025
1026     mdi->x0 = MIN(ulm.x, brm.x);
1027     mdi->xf = MAX(ulm.x, brm.x);
1028     mdi->y0 = MIN(ulm.y, brm.y);
1029     mdi->yf = MAX(ulm.y, brm.y);
1030
1031     mdi->mapstoget = 0;
1032
1033     if ( mdi->redownload ) {
1034       mdi->mapstoget = (mdi->xf - mdi->x0 + 1) * (mdi->yf - mdi->y0 + 1);
1035     } else {
1036       /* calculate how many we need */
1037       for ( a = mdi->x0; a <= mdi->xf; a++ )
1038       {
1039         for ( b = mdi->y0; b <= mdi->yf; b++ )
1040         {
1041           g_snprintf ( mdi->filename_buf, mdi->maxlen, DIRSTRUCTURE,
1042                        vml->cache_dir, vik_map_source_get_uniq_id(map), ulm.scale,
1043                        ulm.z, a, b );
1044           if ( g_file_test ( mdi->filename_buf, G_FILE_TEST_EXISTS ) == FALSE )
1045             mdi->mapstoget++;
1046         }
1047       }
1048     }
1049
1050     mdi->mapcoord.x = mdi->mapcoord.y = 0; /* for cleanup -- no current map */
1051
1052     if ( mdi->mapstoget )
1053     {
1054       const gchar *tmp_str;
1055       gchar *tmp;
1056
1057       if (redownload) 
1058       {
1059         if (redownload == REDOWNLOAD_BAD)
1060           tmp_str = ngettext("Redownloading up to %d %s map...", "Redownloading up to %d %s maps...", mdi->mapstoget);
1061         else
1062           tmp_str = ngettext("Redownloading %d %s map...", "Redownloading %d %s maps...", mdi->mapstoget);
1063       } 
1064       else 
1065       {
1066         tmp_str = ngettext("Downloading %d %s map...", "Downloading %d %s maps...", mdi->mapstoget);
1067       }
1068       tmp = g_strdup_printf ( tmp_str, mdi->mapstoget, MAPS_LAYER_NTH_LABEL(vml->maptype));
1069  
1070       g_object_weak_ref(G_OBJECT(mdi->vml), weak_ref_cb, mdi);
1071       /* launch the thread */
1072       a_background_thread ( VIK_GTK_WINDOW_FROM_LAYER(vml), /* parent window */
1073                             tmp,                                              /* description string */
1074                             (vik_thr_func) map_download_thread,               /* function to call within thread */
1075                             mdi,                                              /* pass along data */
1076                             (vik_thr_free_func) mdi_free,                     /* function to free pass along data */
1077                             (vik_thr_free_func) mdi_cancel_cleanup,
1078                             mdi->mapstoget );
1079       g_free ( tmp );
1080     }
1081     else
1082       mdi_free ( mdi );
1083   }
1084 }
1085
1086 void maps_layer_download_section_without_redraw( VikMapsLayer *vml, VikViewport *vvp, VikCoord *ul, VikCoord *br, gdouble zoom)
1087 {
1088   MapCoord ulm, brm;
1089   VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
1090
1091   if (!vik_map_source_coord_to_mapcoord(map, ul, zoom, zoom, &ulm) 
1092     || !vik_map_source_coord_to_mapcoord(map, br, zoom, zoom, &brm)) {
1093     g_warning("%s() coord_to_mapcoord() failed", __PRETTY_FUNCTION__);
1094     return;
1095   }
1096
1097   MapDownloadInfo *mdi = g_malloc(sizeof(MapDownloadInfo));
1098   gint i, j;
1099
1100   mdi->vml = vml;
1101   mdi->vvp = vvp;
1102   mdi->map_layer_alive = TRUE;
1103   mdi->mutex = g_mutex_new();
1104   mdi->refresh_display = FALSE;
1105
1106   mdi->cache_dir = g_strdup ( vml->cache_dir );
1107   mdi->maxlen = strlen ( vml->cache_dir ) + 40;
1108   mdi->filename_buf = g_malloc ( mdi->maxlen * sizeof(gchar) );
1109   mdi->maptype = vml->maptype;
1110
1111   mdi->mapcoord = ulm;
1112
1113   mdi->redownload = REDOWNLOAD_NONE;
1114
1115   mdi->x0 = MIN(ulm.x, brm.x);
1116   mdi->xf = MAX(ulm.x, brm.x);
1117   mdi->y0 = MIN(ulm.y, brm.y);
1118   mdi->yf = MAX(ulm.y, brm.y);
1119
1120   mdi->mapstoget = 0;
1121
1122   for (i = mdi->x0; i <= mdi->xf; i++) {
1123     for (j = mdi->y0; j <= mdi->yf; j++) {
1124       g_snprintf ( mdi->filename_buf, mdi->maxlen, DIRSTRUCTURE,
1125                    vml->cache_dir, vik_map_source_get_uniq_id(map), ulm.scale,
1126                    ulm.z, i, j );
1127       if ( g_file_test ( mdi->filename_buf, G_FILE_TEST_EXISTS ) == FALSE )
1128             mdi->mapstoget++;
1129     }
1130   }
1131
1132   mdi->mapcoord.x = mdi->mapcoord.y = 0; /* for cleanup -- no current map */
1133
1134   if (mdi->mapstoget) {
1135     gchar *tmp;
1136     const gchar *fmt;
1137     fmt = ngettext("Downloading %d %s map...",
1138                    "Downloading %d %s maps...",
1139                    mdi->mapstoget);
1140     tmp = g_strdup_printf ( fmt, mdi->mapstoget, MAPS_LAYER_NTH_LABEL(vml->maptype) );
1141
1142     g_object_weak_ref(G_OBJECT(mdi->vml), weak_ref_cb, mdi);
1143       /* launch the thread */
1144     a_background_thread ( VIK_GTK_WINDOW_FROM_LAYER(vml), /* parent window */
1145       tmp,                                /* description string */
1146       (vik_thr_func) map_download_thread, /* function to call within thread */
1147       mdi,                                /* pass along data */
1148       (vik_thr_free_func) mdi_free,       /* function to free pass along data */
1149       (vik_thr_free_func) mdi_cancel_cleanup,
1150       mdi->mapstoget );
1151     g_free ( tmp );
1152   }
1153   else
1154     mdi_free ( mdi );
1155 }
1156
1157 static void maps_layer_redownload_bad ( VikMapsLayer *vml )
1158 {
1159   start_download_thread ( vml, vml->redownload_vvp, &(vml->redownload_ul), &(vml->redownload_br), REDOWNLOAD_BAD );
1160 }
1161
1162 static void maps_layer_redownload_all ( VikMapsLayer *vml )
1163 {
1164   start_download_thread ( vml, vml->redownload_vvp, &(vml->redownload_ul), &(vml->redownload_br), REDOWNLOAD_ALL );
1165 }
1166
1167 static void maps_layer_redownload_new ( VikMapsLayer *vml )
1168 {
1169   start_download_thread ( vml, vml->redownload_vvp, &(vml->redownload_ul), &(vml->redownload_br), REDOWNLOAD_NEW );
1170 }
1171
1172 static gboolean maps_layer_download_release ( VikMapsLayer *vml, GdkEventButton *event, VikViewport *vvp )
1173 {
1174   if (!vml || vml->vl.type != VIK_LAYER_MAPS)
1175     return FALSE;
1176   if ( vml->dl_tool_x != -1 && vml->dl_tool_y != -1 )
1177   {
1178     if ( event->button == 1 )
1179     {
1180       VikCoord ul, br;
1181       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 );
1182       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 );
1183       start_download_thread ( vml, vvp, &ul, &br, DOWNLOAD_OR_REFRESH );
1184       vml->dl_tool_x = vml->dl_tool_y = -1;
1185       return TRUE;
1186     }
1187     else
1188     {
1189       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) );
1190       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) );
1191
1192       vml->redownload_vvp = vvp;
1193
1194       vml->dl_tool_x = vml->dl_tool_y = -1;
1195
1196       if ( ! vml->dl_right_click_menu ) {
1197         GtkWidget *item;
1198         vml->dl_right_click_menu = GTK_MENU ( gtk_menu_new () );
1199
1200         item = gtk_menu_item_new_with_label ( _("Redownload bad map(s)") );
1201         g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_redownload_bad), vml );
1202         gtk_menu_shell_append ( GTK_MENU_SHELL(vml->dl_right_click_menu), item );
1203
1204         item = gtk_menu_item_new_with_label ( _("Redownload new map(s)") );
1205         g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_redownload_new), vml );
1206         gtk_menu_shell_append ( GTK_MENU_SHELL(vml->dl_right_click_menu), item );
1207
1208         item = gtk_menu_item_new_with_label ( _("Redownload all map(s)") );
1209         g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_redownload_all), vml );
1210         gtk_menu_shell_append ( GTK_MENU_SHELL(vml->dl_right_click_menu), item );
1211       }
1212
1213       gtk_menu_popup ( vml->dl_right_click_menu, NULL, NULL, NULL, NULL, event->button, event->time );
1214       gtk_widget_show_all ( GTK_WIDGET(vml->dl_right_click_menu) );
1215     }
1216   }
1217   return FALSE;
1218 }
1219
1220 static gpointer maps_layer_download_create ( VikWindow *vw, VikViewport *vvp)
1221 {
1222   return vvp;
1223 }
1224
1225 static gboolean maps_layer_download_click ( VikMapsLayer *vml, GdkEventButton *event, VikViewport *vvp )
1226 {
1227   MapCoord tmp;
1228   if (!vml || vml->vl.type != VIK_LAYER_MAPS)
1229     return FALSE;
1230   VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
1231   if ( vik_map_source_get_drawmode(map) == vik_viewport_get_drawmode ( vvp ) &&
1232        vik_map_source_coord_to_mapcoord ( map, vik_viewport_get_center ( vvp ),
1233            vml->xmapzoom ? vml->xmapzoom : vik_viewport_get_xmpp ( vvp ),
1234            vml->ymapzoom ? vml->ymapzoom : vik_viewport_get_ympp ( vvp ),
1235            &tmp ) ) {
1236     vml->dl_tool_x = event->x, vml->dl_tool_y = event->y;
1237     return TRUE;
1238   }
1239   return FALSE;
1240
1241  
1242 #if 0
1243   if ( __map_types[vml->maptype].drawmode == vik_viewport_get_drawmode ( vvp ) )
1244   {
1245     VikCoord coord;
1246     MapCoord mapcoord;
1247     vik_viewport_screen_to_coord ( vvp, event->x, event->y, &coord );
1248     if ( __map_types[vml->maptype].coord_to_mapcoord ( &coord,
1249                 vml->xmapzoom ? vml->xmapzoom : vik_viewport_get_xmpp ( vvp ),
1250                 vml->ymapzoom ? vml->ymapzoom : vik_viewport_get_ympp ( vvp ),
1251                 &mapcoord ) ) {
1252       gchar *filename_buf = g_strdup_printf ( DIRSTRUCTURE,
1253                      vml->cache_dir, __map_types[vml->maptype].uniq_id,
1254                      mapcoord.scale, mapcoord.z, mapcoord.x, mapcoord.y );
1255
1256       __map_types[vml->maptype].download ( &mapcoord, filename_buf );
1257       g_free ( filename_buf );
1258       vik_layer_emit_update ( VIK_LAYER(vml) );
1259       return TRUE;
1260     }
1261   }
1262   return FALSE;
1263 #endif
1264 }
1265
1266 static void download_onscreen_maps ( gpointer vml_vvp[2], gint redownload )
1267 {
1268   VikMapsLayer *vml = vml_vvp[0];
1269   VikViewport *vvp = vml_vvp[1];
1270   VikViewportDrawMode vp_drawmode = vik_viewport_get_drawmode ( vvp );
1271
1272   gdouble xzoom = vml->xmapzoom ? vml->xmapzoom : vik_viewport_get_xmpp ( vvp );
1273   gdouble yzoom = vml->ymapzoom ? vml->ymapzoom : vik_viewport_get_ympp ( vvp );
1274
1275   VikCoord ul, br;
1276   MapCoord ulm, brm;
1277
1278   vik_viewport_screen_to_coord ( vvp, 0, 0, &ul );
1279   vik_viewport_screen_to_coord ( vvp, vik_viewport_get_width(vvp), vik_viewport_get_height(vvp), &br );
1280
1281   VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
1282   if ( vik_map_source_get_drawmode(map) == vp_drawmode &&
1283        vik_map_source_coord_to_mapcoord ( map, &ul, xzoom, yzoom, &ulm ) &&
1284        vik_map_source_coord_to_mapcoord ( map, &br, xzoom, yzoom, &brm ) )
1285     start_download_thread ( vml, vvp, &ul, &br, redownload );
1286   else if (vik_map_source_get_drawmode(map) != vp_drawmode) {
1287     const gchar *drawmode_name = vik_viewport_get_drawmode_name (vvp, vik_map_source_get_drawmode(map));
1288     gchar *err = g_strdup_printf(_("Wrong drawmode for this map.\nSelect \"%s\" from View menu and try again."), _(drawmode_name));
1289     a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vml), err );
1290     g_free(err);
1291   }
1292   else
1293     a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vml), _("Wrong zoom level for this map.") );
1294
1295 }
1296
1297 static void maps_layer_download_missing_onscreen_maps ( gpointer vml_vvp[2] )
1298 {
1299   download_onscreen_maps( vml_vvp, REDOWNLOAD_NONE);
1300 }
1301
1302 static void maps_layer_download_new_onscreen_maps ( gpointer vml_vvp[2] )
1303 {
1304   download_onscreen_maps( vml_vvp, REDOWNLOAD_NEW);
1305 }
1306
1307 static void maps_layer_redownload_all_onscreen_maps ( gpointer vml_vvp[2] )
1308 {
1309   download_onscreen_maps( vml_vvp, REDOWNLOAD_ALL);
1310 }
1311
1312 static void maps_layer_add_menu_items ( VikMapsLayer *vml, GtkMenu *menu, VikLayersPanel *vlp )
1313 {
1314   static gpointer pass_along[2];
1315   GtkWidget *item;
1316   pass_along[0] = vml;
1317   pass_along[1] = vik_layers_panel_get_viewport( VIK_LAYERS_PANEL(vlp) );
1318
1319   item = gtk_menu_item_new();
1320   gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
1321   gtk_widget_show ( item );
1322
1323   item = gtk_menu_item_new_with_label ( _("Download missing Onscreen Maps") );
1324   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_download_missing_onscreen_maps), pass_along );
1325   gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1326   gtk_widget_show ( item );
1327
1328   if ( vik_map_source_supports_if_modified_since (MAPS_LAYER_NTH_TYPE(vml->maptype)) ) {
1329     item = gtk_menu_item_new_with_label ( _("Download new Onscreen Maps") );
1330     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_download_new_onscreen_maps), pass_along );
1331     gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1332     gtk_widget_show ( item );
1333   }
1334
1335   /* TODO Add GTK_STOCK_REFRESH icon */
1336   item = gtk_menu_item_new_with_label ( _("Reload all Onscreen Maps") );
1337   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_redownload_all_onscreen_maps), pass_along );
1338   gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1339   gtk_widget_show ( item );
1340 }