]> git.street.me.uk Git - andy/viking.git/blob - src/vikmapslayer.c
Better safety checking code
[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   {
379     if ( a_preferences_get(VIKING_PREFERENCES_NAMESPACE "maplayer_default_dir") )
380       vml->cache_dir = g_strdup ( a_preferences_get(VIKING_PREFERENCES_NAMESPACE "maplayer_default_dir")->s );
381   }
382   else
383   {
384     len = strlen(dir);
385     if ( dir[len-1] != G_DIR_SEPARATOR )
386     {
387       vml->cache_dir = g_malloc ( len+2 );
388       strncpy ( vml->cache_dir, dir, len );
389       vml->cache_dir[len] = G_DIR_SEPARATOR;
390       vml->cache_dir[len+1] = '\0';
391     }
392     else
393       vml->cache_dir = g_strdup ( dir );
394   }
395   maps_layer_mkdir_if_default_dir ( vml );
396 }
397
398 /****************************************/
399 /******** GOBJECT STUFF *****************/
400 /****************************************/
401
402 GType vik_maps_layer_get_type ()
403 {
404   static GType vml_type = 0;
405
406   if (!vml_type)
407   {
408     static const GTypeInfo vml_info =
409     {
410       sizeof (VikMapsLayerClass),
411       NULL, /* base_init */
412       NULL, /* base_finalize */
413       NULL, /* class init */
414       NULL, /* class_finalize */
415       NULL, /* class_data */
416       sizeof (VikMapsLayer),
417       0,
418       NULL /* instance init */
419     };
420     vml_type = g_type_register_static ( VIK_LAYER_TYPE, "VikMapsLayer", &vml_info, 0 );
421   }
422
423   return vml_type;
424 }
425
426 /****************************************/
427 /************** PARAMETERS **************/
428 /****************************************/
429
430 static guint map_index_to_uniq_id (guint8 index)
431 {
432   g_assert ( index < NUM_MAP_TYPES );
433   return vik_map_source_get_uniq_id(MAPS_LAYER_NTH_TYPE(index));
434 }
435
436 static guint map_uniq_id_to_index ( guint uniq_id )
437 {
438   gint i;
439   for ( i = 0; i < NUM_MAP_TYPES; i++ )
440     if ( vik_map_source_get_uniq_id(MAPS_LAYER_NTH_TYPE(i)) == uniq_id )
441       return i;
442   return NUM_MAP_TYPES; /* no such thing */
443 }
444
445 static gboolean maps_layer_set_param ( VikMapsLayer *vml, guint16 id, VikLayerParamData data, VikViewport *vvp, gboolean is_file_operation )
446 {
447   switch ( id )
448   {
449     case PARAM_CACHE_DIR: maps_layer_set_cache_dir ( vml, data.s ); break;
450     case PARAM_MAPTYPE: {
451       gint maptype = map_uniq_id_to_index(data.u);
452       if ( maptype == NUM_MAP_TYPES ) g_warning(_("Unknown map type"));
453       else vml->maptype = maptype;
454       break;
455     }
456     case PARAM_ALPHA: if ( data.u <= 255 ) vml->alpha = data.u; break;
457     case PARAM_AUTODOWNLOAD: vml->autodownload = data.b; break;
458     case PARAM_MAPZOOM: if ( data.u < NUM_MAPZOOMS ) {
459                           vml->mapzoom_id = data.u;
460                           vml->xmapzoom = __mapzooms_x [data.u];
461                           vml->ymapzoom = __mapzooms_y [data.u];
462                         }else g_warning (_("Unknown Map Zoom")); break;
463   }
464   return TRUE;
465 }
466
467 static VikLayerParamData maps_layer_get_param ( VikMapsLayer *vml, guint16 id, gboolean is_file_operation )
468 {
469   VikLayerParamData rv;
470   switch ( id )
471   {
472     case PARAM_CACHE_DIR: rv.s = vml->cache_dir ? vml->cache_dir : ""; break;
473     case PARAM_MAPTYPE: rv.u = map_index_to_uniq_id ( vml->maptype ); break;
474     case PARAM_ALPHA: rv.u = vml->alpha; break;
475     case PARAM_AUTODOWNLOAD: rv.u = vml->autodownload; break;
476     case PARAM_MAPZOOM: rv.u = vml->mapzoom_id; break;
477   }
478   return rv;
479 }
480
481 /****************************************/
482 /****** CREATING, COPYING, FREEING ******/
483 /****************************************/
484
485 static VikMapsLayer *maps_layer_new ( VikViewport *vvp )
486 {
487   int idx;
488   VikMapsLayer *vml = VIK_MAPS_LAYER ( g_object_new ( VIK_MAPS_LAYER_TYPE, NULL ) );
489   vik_layer_init ( VIK_LAYER(vml), VIK_LAYER_MAPS );
490   idx = map_uniq_id_to_index(13); /* 13 is id for OSM Mapnik maps */
491     vml->maptype = (idx < NUM_MAP_TYPES) ? idx : 0;
492   vml->alpha = 255;
493   vml->mapzoom_id = 0;
494   vml->dl_tool_x = vml->dl_tool_y = -1;
495   maps_layer_set_cache_dir ( vml, NULL );
496   vml->autodownload = FALSE;
497   vml->last_center = NULL;
498   vml->last_xmpp = 0.0;
499   vml->last_ympp = 0.0;
500
501   vml->dl_right_click_menu = NULL;
502
503   return vml;
504 }
505
506 static void maps_layer_free ( VikMapsLayer *vml )
507 {
508   g_free ( vml->cache_dir );
509   vml->cache_dir = NULL;
510   if ( vml->dl_right_click_menu )
511     gtk_object_sink ( GTK_OBJECT(vml->dl_right_click_menu) );
512   g_free(vml->last_center);
513   vml->last_center = NULL;
514 }
515
516 static void maps_layer_post_read (VikLayer *vl, VikViewport *vp, gboolean from_file)
517 {
518   if (from_file != TRUE)
519   {
520     /* If this method is not called in file reading context
521      * it is called in GUI context.
522      * So, we can check if we have to inform the user about inconsistency */
523     VikViewportDrawMode vp_drawmode;
524     VikMapsLayer *vml = VIK_MAPS_LAYER(vl);
525     VikMapSource *map = NULL;
526  
527     vp_drawmode = vik_viewport_get_drawmode ( VIK_VIEWPORT(vp) );
528     map = MAPS_LAYER_NTH_TYPE(vml->maptype);
529     if (vik_map_source_get_drawmode(map) != vp_drawmode) {
530       const gchar *drawmode_name = vik_viewport_get_drawmode_name (VIK_VIEWPORT(vp), vik_map_source_get_drawmode(map));
531       gchar *msg = g_strdup_printf(_("New map cannot be displayed in the current drawmode.\nSelect \"%s\" from View menu to view it."), drawmode_name);
532       a_dialog_warning_msg ( VIK_GTK_WINDOW_FROM_WIDGET(vp), msg );
533       g_free(msg);
534     }
535   }
536 }
537
538 static void maps_layer_marshall( VikMapsLayer *vml, guint8 **data, gint *len )
539 {
540   vik_layer_marshall_params ( VIK_LAYER(vml), data, len );
541 }
542
543 static VikMapsLayer *maps_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp )
544 {
545   VikMapsLayer *rv = maps_layer_new ( vvp );
546   vik_layer_unmarshall_params ( VIK_LAYER(rv), data, len, vvp );
547   return rv;
548 }
549
550 /*********************/
551 /****** DRAWING ******/
552 /*********************/
553
554 static GdkPixbuf *pixbuf_set_alpha ( GdkPixbuf *pixbuf, guint8 alpha )
555 {
556   guchar *pixels;
557   gint width, height, iii, jjj;
558
559   if ( ! gdk_pixbuf_get_has_alpha ( pixbuf ) )
560   {
561     GdkPixbuf *tmp = gdk_pixbuf_add_alpha(pixbuf,FALSE,0,0,0);
562     g_object_unref(G_OBJECT(pixbuf));
563     pixbuf = tmp;
564   }
565
566   pixels = gdk_pixbuf_get_pixels(pixbuf);
567   width = gdk_pixbuf_get_width(pixbuf);
568   height = gdk_pixbuf_get_height(pixbuf);
569
570   /* r,g,b,a,r,g,b,a.... */
571   for (iii = 0; iii < width; iii++) for (jjj = 0; jjj < height; jjj++)
572   {
573     pixels += 3;
574     *pixels++ = alpha;
575   }
576   return pixbuf;
577 }
578
579 static GdkPixbuf *pixbuf_shrink ( GdkPixbuf *pixbuf, gdouble xshrinkfactor, gdouble yshrinkfactor )
580 {
581   GdkPixbuf *tmp;
582   guint16 width = gdk_pixbuf_get_width(pixbuf), height = gdk_pixbuf_get_height(pixbuf);
583   tmp = gdk_pixbuf_scale_simple(pixbuf, ceil(width * xshrinkfactor), ceil(height * yshrinkfactor), GDK_INTERP_NEAREST);
584   g_object_unref ( G_OBJECT(pixbuf) );
585   return tmp;
586 }
587
588 static GdkPixbuf *get_pixbuf( VikMapsLayer *vml, gint mode, MapCoord *mapcoord, gchar *filename_buf, gint buf_len, gdouble xshrinkfactor, gdouble yshrinkfactor )
589 {
590   GdkPixbuf *pixbuf;
591
592   /* get the thing */
593   pixbuf = a_mapcache_get ( mapcoord->x, mapcoord->y, mapcoord->z,
594                             mode, mapcoord->scale, vml->alpha, xshrinkfactor, yshrinkfactor );
595
596   if ( ! pixbuf ) {
597     g_snprintf ( filename_buf, buf_len, DIRSTRUCTURE,
598                      vml->cache_dir, mode,
599                      mapcoord->scale, mapcoord->z, mapcoord->x, mapcoord->y );
600     if ( g_file_test ( filename_buf, G_FILE_TEST_EXISTS ) == TRUE)
601     {
602       GError *gx = NULL;
603       pixbuf = gdk_pixbuf_new_from_file ( filename_buf, &gx );
604
605       /* free the pixbuf on error */
606       if (gx)
607       {
608         if ( gx->domain != GDK_PIXBUF_ERROR || gx->code != GDK_PIXBUF_ERROR_CORRUPT_IMAGE )
609           g_warning ( _("Couldn't open image file: %s"), gx->message );
610
611         g_error_free ( gx );
612         if ( pixbuf )
613           g_object_unref ( G_OBJECT(pixbuf) );
614         pixbuf = NULL;
615       } else {
616           if ( vml->alpha < 255 )
617             pixbuf = pixbuf_set_alpha ( pixbuf, vml->alpha );
618           if ( xshrinkfactor != 1.0 || yshrinkfactor != 1.0 )
619             pixbuf = pixbuf_shrink ( pixbuf, xshrinkfactor, yshrinkfactor );
620
621           a_mapcache_add ( pixbuf, mapcoord->x, mapcoord->y, 
622               mapcoord->z, vik_map_source_get_uniq_id(MAPS_LAYER_NTH_TYPE(vml->maptype)),
623               mapcoord->scale, vml->alpha, xshrinkfactor, yshrinkfactor );
624       }
625     }
626   }
627   return pixbuf;
628 }
629
630 gboolean should_start_autodownload(VikMapsLayer *vml, VikViewport *vvp)
631 {
632   const VikCoord *center = vik_viewport_get_center ( vvp );
633
634   if (vik_window_get_pan_move (VIK_GTK_WINDOW_FROM_WIDGET(GTK_WIDGET(vvp))))
635     /* D'n'D pan in action: do not download */
636     return FALSE;
637
638   if (vml->last_center == NULL) {
639     VikCoord *new_center = g_malloc(sizeof(VikCoord));
640     *new_center = *center;
641     vml->last_center = new_center;
642     vml->last_xmpp = vik_viewport_get_xmpp(vvp);
643     vml->last_ympp = vik_viewport_get_ympp(vvp);
644     return TRUE;
645   }
646
647   /* TODO: perhaps vik_coord_diff() */
648   if (vik_coord_equals(vml->last_center, center)
649       && (vml->last_xmpp == vik_viewport_get_xmpp(vvp))
650       && (vml->last_ympp == vik_viewport_get_ympp(vvp)))
651     return FALSE;
652
653   *(vml->last_center) = *center;
654     vml->last_xmpp = vik_viewport_get_xmpp(vvp);
655     vml->last_ympp = vik_viewport_get_ympp(vvp);
656   return TRUE;
657 }
658
659 static void maps_layer_draw_section ( VikMapsLayer *vml, VikViewport *vvp, VikCoord *ul, VikCoord *br )
660 {
661   MapCoord ulm, brm;
662   gdouble xzoom = vik_viewport_get_xmpp ( vvp );
663   gdouble yzoom = vik_viewport_get_ympp ( vvp );
664   gdouble xshrinkfactor = 1.0, yshrinkfactor = 1.0;
665   gdouble existence_only = FALSE;
666
667   if ( vml->xmapzoom && (vml->xmapzoom != xzoom || vml->ymapzoom != yzoom) ) {
668     xshrinkfactor = vml->xmapzoom / xzoom;
669     yshrinkfactor = vml->ymapzoom / yzoom;
670     xzoom = vml->xmapzoom;
671     yzoom = vml->xmapzoom;
672     if ( ! (xshrinkfactor > MIN_SHRINKFACTOR && xshrinkfactor < MAX_SHRINKFACTOR &&
673          yshrinkfactor > MIN_SHRINKFACTOR && yshrinkfactor < MAX_SHRINKFACTOR ) ) {
674       if ( xshrinkfactor > REAL_MIN_SHRINKFACTOR && yshrinkfactor > REAL_MIN_SHRINKFACTOR )
675         existence_only = TRUE;
676       else {
677         g_warning ( _("Cowardly refusing to draw tiles or existence of tiles beyond %d zoom out factor"), (int)( 1.0/REAL_MIN_SHRINKFACTOR));
678         return;
679       }
680     }
681   }
682
683   /* coord -> ID */
684   VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
685   if ( vik_map_source_coord_to_mapcoord ( map, ul, xzoom, yzoom, &ulm ) &&
686        vik_map_source_coord_to_mapcoord ( map, br, xzoom, yzoom, &brm ) ) {
687
688     /* loop & draw */
689     gint x, y;
690     gint xmin = MIN(ulm.x, brm.x), xmax = MAX(ulm.x, brm.x);
691     gint ymin = MIN(ulm.y, brm.y), ymax = MAX(ulm.y, brm.y);
692     gint mode = vik_map_source_get_uniq_id(map);
693
694     VikCoord coord;
695     gint xx, yy, width, height;
696     GdkPixbuf *pixbuf;
697
698     guint max_path_len = strlen(vml->cache_dir) + 40;
699     gchar *path_buf = g_malloc ( max_path_len * sizeof(char) );
700
701     if ( (!existence_only) && vml->autodownload  && should_start_autodownload(vml, vvp)) {
702       g_debug("%s: Starting autodownload", __FUNCTION__);
703       if ( vik_map_source_supports_download_only_new (map) )
704         // Try to download newer tiles
705         start_download_thread ( vml, vvp, ul, br, REDOWNLOAD_NEW );
706       else
707         // Download only missing tiles
708         start_download_thread ( vml, vvp, ul, br, REDOWNLOAD_NONE );
709     }
710
711     if ( vik_map_source_get_tilesize_x(map) == 0 && !existence_only ) {
712       for ( x = xmin; x <= xmax; x++ ) {
713         for ( y = ymin; y <= ymax; y++ ) {
714           ulm.x = x;
715           ulm.y = y;
716           pixbuf = get_pixbuf ( vml, mode, &ulm, path_buf, max_path_len, xshrinkfactor, yshrinkfactor );
717           if ( pixbuf ) {
718             width = gdk_pixbuf_get_width ( pixbuf );
719             height = gdk_pixbuf_get_height ( pixbuf );
720
721             vik_map_source_mapcoord_to_center_coord ( map, &ulm, &coord );
722             vik_viewport_coord_to_screen ( vvp, &coord, &xx, &yy );
723             xx -= (width/2);
724             yy -= (height/2);
725
726             vik_viewport_draw_pixbuf ( vvp, pixbuf, 0, 0, xx, yy, width, height );
727           }
728         }
729       }
730     } else { /* tilesize is known, don't have to keep converting coords */
731       gdouble tilesize_x = vik_map_source_get_tilesize_x(map) * xshrinkfactor;
732       gdouble tilesize_y = vik_map_source_get_tilesize_y(map) * yshrinkfactor;
733       /* ceiled so tiles will be maximum size in the case of funky shrinkfactor */
734       gint tilesize_x_ceil = ceil ( tilesize_x );
735       gint tilesize_y_ceil = ceil ( tilesize_y );
736       gint8 xinc = (ulm.x == xmin) ? 1 : -1;
737       gint8 yinc = (ulm.y == ymin) ? 1 : -1;
738       gdouble xx, yy; gint xx_tmp, yy_tmp;
739       gint base_yy, xend, yend;
740
741       GdkGC *black_gc = GTK_WIDGET(vvp)->style->black_gc;
742
743       xend = (xinc == 1) ? (xmax+1) : (xmin-1);
744       yend = (yinc == 1) ? (ymax+1) : (ymin-1);
745
746       vik_map_source_mapcoord_to_center_coord ( map, &ulm, &coord );
747       vik_viewport_coord_to_screen ( vvp, &coord, &xx_tmp, &yy_tmp );
748       xx = xx_tmp; yy = yy_tmp;
749       /* above trick so xx,yy doubles. this is so shrinkfactors aren't rounded off
750        * eg if tile size 128, shrinkfactor 0.333 */
751       xx -= (tilesize_x/2);
752       base_yy = yy - (tilesize_y/2);
753
754       for ( x = ((xinc == 1) ? xmin : xmax); x != xend; x+=xinc ) {
755         yy = base_yy;
756         for ( y = ((yinc == 1) ? ymin : ymax); y != yend; y+=yinc ) {
757           ulm.x = x;
758           ulm.y = y;
759
760           if ( existence_only ) {
761             g_snprintf ( path_buf, max_path_len, DIRSTRUCTURE,
762                      vml->cache_dir, mode,
763                      ulm.scale, ulm.z, ulm.x, ulm.y );
764             if ( g_file_test ( path_buf, G_FILE_TEST_EXISTS ) == TRUE ) {
765               vik_viewport_draw_line ( vvp, black_gc, xx+tilesize_x_ceil, yy, xx, yy+tilesize_y_ceil );
766             }
767           } else {
768             int scale_inc;
769             for (scale_inc = 0; scale_inc < 4; scale_inc ++) {
770               /* try with correct then smaller zooms */
771               int scale_factor = 1 << scale_inc;  /*  2^scale_inc */
772               MapCoord ulm2 = ulm;
773               ulm2.x = ulm.x / scale_factor;
774               ulm2.y = ulm.y / scale_factor;
775               ulm2.scale = ulm.scale + scale_inc;
776               pixbuf = get_pixbuf ( vml, mode, &ulm2, path_buf, max_path_len, xshrinkfactor * scale_factor, yshrinkfactor * scale_factor );
777               if ( pixbuf ) {
778                 gint src_x = (ulm.x % scale_factor) * tilesize_x_ceil;
779                 gint src_y = (ulm.y % scale_factor) * tilesize_y_ceil;
780 #ifdef DEBUG
781                 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);
782 #endif
783                 vik_viewport_draw_pixbuf ( vvp, pixbuf, src_x, src_y, xx, yy, tilesize_x_ceil, tilesize_y_ceil );
784                 break;
785               }
786             }
787             if ( !pixbuf ) {
788               /* retry with bigger zooms */
789               int scale_dec;
790               for (scale_dec = 1; scale_dec < 2; scale_dec ++) {
791                 int pict_x, pict_y;
792                 int scale_factor = 1 << scale_dec;  /*  2^scale_dec */
793                 MapCoord ulm2 = ulm;
794                 ulm2.x = ulm.x * scale_factor;
795                 ulm2.y = ulm.y * scale_factor;
796                 ulm2.scale = ulm.scale - scale_dec;
797                 for (pict_x = 0; pict_x < scale_factor; pict_x ++) {
798                   for (pict_y = 0; pict_y < scale_factor; pict_y ++) {
799                     MapCoord ulm3 = ulm2;
800                     ulm3.x += pict_x;
801                     ulm3.y += pict_y;
802                     pixbuf = get_pixbuf ( vml, mode, &ulm3, path_buf, max_path_len, xshrinkfactor / scale_factor, yshrinkfactor / scale_factor );
803                     if ( pixbuf ) {
804                       gint src_x = 0;
805                       gint src_y = 0;
806                       gint dest_x = xx + pict_x * (tilesize_x_ceil / scale_factor);
807                       gint dest_y = yy + pict_y * (tilesize_y_ceil / scale_factor);
808                       vik_viewport_draw_pixbuf ( vvp, pixbuf, src_x, src_y, dest_x, dest_y, tilesize_x_ceil / scale_factor, tilesize_y_ceil / scale_factor );
809                     }
810                   }
811                 }
812               }
813             }
814           }
815
816           yy += tilesize_y;
817         }
818         xx += tilesize_x;
819       }
820     }
821
822     g_free ( path_buf );
823   }
824 }
825
826 static void maps_layer_draw ( VikMapsLayer *vml, VikViewport *vvp )
827 {
828   if ( vik_map_source_get_drawmode(MAPS_LAYER_NTH_TYPE(vml->maptype)) == vik_viewport_get_drawmode ( vvp ) )
829   {
830     VikCoord ul, br;
831
832     /* get corner coords */
833     if ( vik_viewport_get_coord_mode ( vvp ) == VIK_COORD_UTM && ! vik_viewport_is_one_zone ( vvp ) ) {
834       /* UTM multi-zone stuff by Kit Transue */
835       gchar leftmost_zone, rightmost_zone, i;
836       leftmost_zone = vik_viewport_leftmost_zone( vvp );
837       rightmost_zone = vik_viewport_rightmost_zone( vvp );
838       for ( i = leftmost_zone; i <= rightmost_zone; ++i ) {
839         vik_viewport_corners_for_zonen ( vvp, i, &ul, &br );
840         maps_layer_draw_section ( vml, vvp, &ul, &br );
841       }
842     }
843     else {
844       vik_viewport_screen_to_coord ( vvp, 0, 0, &ul );
845       vik_viewport_screen_to_coord ( vvp, vik_viewport_get_width(vvp), vik_viewport_get_height(vvp), &br );
846
847       maps_layer_draw_section ( vml, vvp, &ul, &br );
848     }
849   }
850 }
851
852 /*************************/
853 /****** DOWNLOADING ******/
854 /*************************/
855
856 /* pass along data to thread, exists even if layer is deleted. */
857 typedef struct {
858   gchar *cache_dir;
859   gchar *filename_buf;
860   gint x0, y0, xf, yf;
861   MapCoord mapcoord;
862   gint maptype;
863   gint maxlen;
864   gint mapstoget;
865   gint redownload;
866   gboolean refresh_display;
867   VikMapsLayer *vml;
868   VikViewport *vvp;
869   gboolean map_layer_alive;
870   GMutex *mutex;
871 } MapDownloadInfo;
872
873 static void mdi_free ( MapDownloadInfo *mdi )
874 {
875   g_mutex_free(mdi->mutex);
876   g_free ( mdi->cache_dir );
877   mdi->cache_dir = NULL;
878   g_free ( mdi->filename_buf );
879   mdi->filename_buf = NULL;
880   g_free ( mdi );
881 }
882
883 static void weak_ref_cb(gpointer ptr, GObject * dead_vml)
884 {
885   MapDownloadInfo *mdi = ptr;
886   g_mutex_lock(mdi->mutex);
887   mdi->map_layer_alive = FALSE;
888   g_mutex_unlock(mdi->mutex);
889 }
890
891 static int map_download_thread ( MapDownloadInfo *mdi, gpointer threaddata )
892 {
893   void *handle = vik_map_source_download_handle_init(MAPS_LAYER_NTH_TYPE(mdi->maptype));
894   guint donemaps = 0;
895   gint x, y;
896   for ( x = mdi->x0; x <= mdi->xf; x++ )
897   {
898     for ( y = mdi->y0; y <= mdi->yf; y++ )
899     {
900       gboolean remove_mem_cache = FALSE;
901       gboolean need_download = FALSE;
902       g_snprintf ( mdi->filename_buf, mdi->maxlen, DIRSTRUCTURE,
903                      mdi->cache_dir, vik_map_source_get_uniq_id(MAPS_LAYER_NTH_TYPE(mdi->maptype)),
904                      mdi->mapcoord.scale, mdi->mapcoord.z, x, y );
905
906       donemaps++;
907       int res = a_background_thread_progress ( threaddata, ((gdouble)donemaps) / mdi->mapstoget ); /* this also calls testcancel */
908       if (res != 0) {
909         vik_map_source_download_handle_cleanup(MAPS_LAYER_NTH_TYPE(mdi->maptype), handle);
910         return -1;
911       }
912
913       if ( g_file_test ( mdi->filename_buf, G_FILE_TEST_EXISTS ) == FALSE ) {
914         need_download = TRUE;
915         remove_mem_cache = TRUE;
916
917       } else {  /* in case map file already exists */
918         switch (mdi->redownload) {
919           case REDOWNLOAD_NONE:
920             continue;
921
922           case REDOWNLOAD_BAD:
923           {
924             /* see if this one is bad or what */
925             GError *gx = NULL;
926             GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file ( mdi->filename_buf, &gx );
927             if (gx || (!pixbuf)) {
928               g_remove ( mdi->filename_buf );
929               need_download = TRUE;
930               remove_mem_cache = TRUE;
931               g_error_free ( gx );
932
933             } else {
934               g_object_unref ( pixbuf );
935             }
936             break;
937           }
938
939           case REDOWNLOAD_NEW:
940             need_download = TRUE;
941             remove_mem_cache = TRUE;
942             break;
943
944           case REDOWNLOAD_ALL:
945             /* FIXME: need a better way than to erase file in case of server/network problem */
946             g_remove ( mdi->filename_buf );
947             need_download = TRUE;
948             remove_mem_cache = TRUE;
949             break;
950
951           case DOWNLOAD_OR_REFRESH:
952             remove_mem_cache = TRUE;
953             break;
954
955           default:
956             g_warning ( "redownload state %d unknown\n", mdi->redownload);
957         }
958       }
959
960       mdi->mapcoord.x = x; mdi->mapcoord.y = y;
961
962       if (need_download) {
963         if ( vik_map_source_download( MAPS_LAYER_NTH_TYPE(mdi->maptype), &(mdi->mapcoord), mdi->filename_buf, handle))
964           continue;
965       }
966
967       gdk_threads_enter();
968       g_mutex_lock(mdi->mutex);
969       if (remove_mem_cache)
970           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 );
971       if (mdi->refresh_display && mdi->map_layer_alive) {
972         /* TODO: check if it's on visible area */
973         vik_layer_emit_update ( VIK_LAYER(mdi->vml) );
974       }
975       g_mutex_unlock(mdi->mutex);
976       gdk_threads_leave();
977       mdi->mapcoord.x = mdi->mapcoord.y = 0; /* we're temporarily between downloads */
978
979     }
980   }
981   vik_map_source_download_handle_cleanup(MAPS_LAYER_NTH_TYPE(mdi->maptype), handle);
982   g_mutex_lock(mdi->mutex);
983   if (mdi->map_layer_alive)
984     g_object_weak_unref(G_OBJECT(mdi->vml), weak_ref_cb, mdi);
985   g_mutex_unlock(mdi->mutex); 
986   return 0;
987 }
988
989 static void mdi_cancel_cleanup ( MapDownloadInfo *mdi )
990 {
991   if ( mdi->mapcoord.x || mdi->mapcoord.y )
992   {
993     g_snprintf ( mdi->filename_buf, mdi->maxlen, DIRSTRUCTURE,
994                      mdi->cache_dir, vik_map_source_get_uniq_id(MAPS_LAYER_NTH_TYPE(mdi->maptype)),
995                      mdi->mapcoord.scale, mdi->mapcoord.z, mdi->mapcoord.x, mdi->mapcoord.y );
996     if ( g_file_test ( mdi->filename_buf, G_FILE_TEST_EXISTS ) == TRUE)
997     {
998       g_remove ( mdi->filename_buf );
999     }
1000   }
1001 }
1002
1003 static void start_download_thread ( VikMapsLayer *vml, VikViewport *vvp, const VikCoord *ul, const VikCoord *br, gint redownload )
1004 {
1005   gdouble xzoom = vml->xmapzoom ? vml->xmapzoom : vik_viewport_get_xmpp ( vvp );
1006   gdouble yzoom = vml->ymapzoom ? vml->ymapzoom : vik_viewport_get_ympp ( vvp );
1007   MapCoord ulm, brm;
1008   VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
1009   if ( vik_map_source_coord_to_mapcoord ( map, ul, xzoom, yzoom, &ulm ) 
1010     && vik_map_source_coord_to_mapcoord ( map, br, xzoom, yzoom, &brm ) )
1011   {
1012     MapDownloadInfo *mdi = g_malloc ( sizeof(MapDownloadInfo) );
1013     gint a, b;
1014
1015     mdi->vml = vml;
1016     mdi->vvp = vvp;
1017     mdi->map_layer_alive = TRUE;
1018     mdi->mutex = g_mutex_new();
1019     mdi->refresh_display = TRUE;
1020
1021     /* cache_dir and buffer for dest filename */
1022     mdi->cache_dir = g_strdup ( vml->cache_dir );
1023     mdi->maxlen = strlen ( vml->cache_dir ) + 40;
1024     mdi->filename_buf = g_malloc ( mdi->maxlen * sizeof(gchar) );
1025     mdi->maptype = vml->maptype;
1026
1027     mdi->mapcoord = ulm;
1028
1029     mdi->redownload = redownload;
1030
1031     mdi->x0 = MIN(ulm.x, brm.x);
1032     mdi->xf = MAX(ulm.x, brm.x);
1033     mdi->y0 = MIN(ulm.y, brm.y);
1034     mdi->yf = MAX(ulm.y, brm.y);
1035
1036     mdi->mapstoget = 0;
1037
1038     if ( mdi->redownload ) {
1039       mdi->mapstoget = (mdi->xf - mdi->x0 + 1) * (mdi->yf - mdi->y0 + 1);
1040     } else {
1041       /* calculate how many we need */
1042       for ( a = mdi->x0; a <= mdi->xf; a++ )
1043       {
1044         for ( b = mdi->y0; b <= mdi->yf; b++ )
1045         {
1046           g_snprintf ( mdi->filename_buf, mdi->maxlen, DIRSTRUCTURE,
1047                        vml->cache_dir, vik_map_source_get_uniq_id(map), ulm.scale,
1048                        ulm.z, a, b );
1049           if ( g_file_test ( mdi->filename_buf, G_FILE_TEST_EXISTS ) == FALSE )
1050             mdi->mapstoget++;
1051         }
1052       }
1053     }
1054
1055     mdi->mapcoord.x = mdi->mapcoord.y = 0; /* for cleanup -- no current map */
1056
1057     if ( mdi->mapstoget )
1058     {
1059       const gchar *tmp_str;
1060       gchar *tmp;
1061
1062       if (redownload) 
1063       {
1064         if (redownload == REDOWNLOAD_BAD)
1065           tmp_str = ngettext("Redownloading up to %d %s map...", "Redownloading up to %d %s maps...", mdi->mapstoget);
1066         else
1067           tmp_str = ngettext("Redownloading %d %s map...", "Redownloading %d %s maps...", mdi->mapstoget);
1068       } 
1069       else 
1070       {
1071         tmp_str = ngettext("Downloading %d %s map...", "Downloading %d %s maps...", mdi->mapstoget);
1072       }
1073       tmp = g_strdup_printf ( tmp_str, mdi->mapstoget, MAPS_LAYER_NTH_LABEL(vml->maptype));
1074  
1075       g_object_weak_ref(G_OBJECT(mdi->vml), weak_ref_cb, mdi);
1076       /* launch the thread */
1077       a_background_thread ( VIK_GTK_WINDOW_FROM_LAYER(vml), /* parent window */
1078                             tmp,                                              /* description string */
1079                             (vik_thr_func) map_download_thread,               /* function to call within thread */
1080                             mdi,                                              /* pass along data */
1081                             (vik_thr_free_func) mdi_free,                     /* function to free pass along data */
1082                             (vik_thr_free_func) mdi_cancel_cleanup,
1083                             mdi->mapstoget );
1084       g_free ( tmp );
1085     }
1086     else
1087       mdi_free ( mdi );
1088   }
1089 }
1090
1091 void maps_layer_download_section_without_redraw( VikMapsLayer *vml, VikViewport *vvp, VikCoord *ul, VikCoord *br, gdouble zoom)
1092 {
1093   MapCoord ulm, brm;
1094   VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
1095
1096   if (!vik_map_source_coord_to_mapcoord(map, ul, zoom, zoom, &ulm) 
1097     || !vik_map_source_coord_to_mapcoord(map, br, zoom, zoom, &brm)) {
1098     g_warning("%s() coord_to_mapcoord() failed", __PRETTY_FUNCTION__);
1099     return;
1100   }
1101
1102   MapDownloadInfo *mdi = g_malloc(sizeof(MapDownloadInfo));
1103   gint i, j;
1104
1105   mdi->vml = vml;
1106   mdi->vvp = vvp;
1107   mdi->map_layer_alive = TRUE;
1108   mdi->mutex = g_mutex_new();
1109   mdi->refresh_display = FALSE;
1110
1111   mdi->cache_dir = g_strdup ( vml->cache_dir );
1112   mdi->maxlen = strlen ( vml->cache_dir ) + 40;
1113   mdi->filename_buf = g_malloc ( mdi->maxlen * sizeof(gchar) );
1114   mdi->maptype = vml->maptype;
1115
1116   mdi->mapcoord = ulm;
1117
1118   mdi->redownload = REDOWNLOAD_NONE;
1119
1120   mdi->x0 = MIN(ulm.x, brm.x);
1121   mdi->xf = MAX(ulm.x, brm.x);
1122   mdi->y0 = MIN(ulm.y, brm.y);
1123   mdi->yf = MAX(ulm.y, brm.y);
1124
1125   mdi->mapstoget = 0;
1126
1127   for (i = mdi->x0; i <= mdi->xf; i++) {
1128     for (j = mdi->y0; j <= mdi->yf; j++) {
1129       g_snprintf ( mdi->filename_buf, mdi->maxlen, DIRSTRUCTURE,
1130                    vml->cache_dir, vik_map_source_get_uniq_id(map), ulm.scale,
1131                    ulm.z, i, j );
1132       if ( g_file_test ( mdi->filename_buf, G_FILE_TEST_EXISTS ) == FALSE )
1133             mdi->mapstoget++;
1134     }
1135   }
1136
1137   mdi->mapcoord.x = mdi->mapcoord.y = 0; /* for cleanup -- no current map */
1138
1139   if (mdi->mapstoget) {
1140     gchar *tmp;
1141     const gchar *fmt;
1142     fmt = ngettext("Downloading %d %s map...",
1143                    "Downloading %d %s maps...",
1144                    mdi->mapstoget);
1145     tmp = g_strdup_printf ( fmt, mdi->mapstoget, MAPS_LAYER_NTH_LABEL(vml->maptype) );
1146
1147     g_object_weak_ref(G_OBJECT(mdi->vml), weak_ref_cb, mdi);
1148       /* launch the thread */
1149     a_background_thread ( VIK_GTK_WINDOW_FROM_LAYER(vml), /* parent window */
1150       tmp,                                /* description string */
1151       (vik_thr_func) map_download_thread, /* function to call within thread */
1152       mdi,                                /* pass along data */
1153       (vik_thr_free_func) mdi_free,       /* function to free pass along data */
1154       (vik_thr_free_func) mdi_cancel_cleanup,
1155       mdi->mapstoget );
1156     g_free ( tmp );
1157   }
1158   else
1159     mdi_free ( mdi );
1160 }
1161
1162 static void maps_layer_redownload_bad ( VikMapsLayer *vml )
1163 {
1164   start_download_thread ( vml, vml->redownload_vvp, &(vml->redownload_ul), &(vml->redownload_br), REDOWNLOAD_BAD );
1165 }
1166
1167 static void maps_layer_redownload_all ( VikMapsLayer *vml )
1168 {
1169   start_download_thread ( vml, vml->redownload_vvp, &(vml->redownload_ul), &(vml->redownload_br), REDOWNLOAD_ALL );
1170 }
1171
1172 static void maps_layer_redownload_new ( VikMapsLayer *vml )
1173 {
1174   start_download_thread ( vml, vml->redownload_vvp, &(vml->redownload_ul), &(vml->redownload_br), REDOWNLOAD_NEW );
1175 }
1176
1177 static gboolean maps_layer_download_release ( VikMapsLayer *vml, GdkEventButton *event, VikViewport *vvp )
1178 {
1179   if (!vml || vml->vl.type != VIK_LAYER_MAPS)
1180     return FALSE;
1181   if ( vml->dl_tool_x != -1 && vml->dl_tool_y != -1 )
1182   {
1183     if ( event->button == 1 )
1184     {
1185       VikCoord ul, br;
1186       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 );
1187       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 );
1188       start_download_thread ( vml, vvp, &ul, &br, DOWNLOAD_OR_REFRESH );
1189       vml->dl_tool_x = vml->dl_tool_y = -1;
1190       return TRUE;
1191     }
1192     else
1193     {
1194       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) );
1195       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) );
1196
1197       vml->redownload_vvp = vvp;
1198
1199       vml->dl_tool_x = vml->dl_tool_y = -1;
1200
1201       if ( ! vml->dl_right_click_menu ) {
1202         GtkWidget *item;
1203         vml->dl_right_click_menu = GTK_MENU ( gtk_menu_new () );
1204
1205         item = gtk_menu_item_new_with_mnemonic ( _("Redownload _Bad Map(s)") );
1206         g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_redownload_bad), vml );
1207         gtk_menu_shell_append ( GTK_MENU_SHELL(vml->dl_right_click_menu), item );
1208
1209         item = gtk_menu_item_new_with_mnemonic ( _("Redownload _New Map(s)") );
1210         g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_redownload_new), vml );
1211         gtk_menu_shell_append ( GTK_MENU_SHELL(vml->dl_right_click_menu), item );
1212
1213         item = gtk_menu_item_new_with_mnemonic ( _("Redownload _All Map(s)") );
1214         g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_redownload_all), vml );
1215         gtk_menu_shell_append ( GTK_MENU_SHELL(vml->dl_right_click_menu), item );
1216       }
1217
1218       gtk_menu_popup ( vml->dl_right_click_menu, NULL, NULL, NULL, NULL, event->button, event->time );
1219       gtk_widget_show_all ( GTK_WIDGET(vml->dl_right_click_menu) );
1220     }
1221   }
1222   return FALSE;
1223 }
1224
1225 static gpointer maps_layer_download_create ( VikWindow *vw, VikViewport *vvp)
1226 {
1227   return vvp;
1228 }
1229
1230 static gboolean maps_layer_download_click ( VikMapsLayer *vml, GdkEventButton *event, VikViewport *vvp )
1231 {
1232   MapCoord tmp;
1233   if (!vml || vml->vl.type != VIK_LAYER_MAPS)
1234     return FALSE;
1235   VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
1236   if ( vik_map_source_get_drawmode(map) == vik_viewport_get_drawmode ( vvp ) &&
1237        vik_map_source_coord_to_mapcoord ( map, vik_viewport_get_center ( vvp ),
1238            vml->xmapzoom ? vml->xmapzoom : vik_viewport_get_xmpp ( vvp ),
1239            vml->ymapzoom ? vml->ymapzoom : vik_viewport_get_ympp ( vvp ),
1240            &tmp ) ) {
1241     vml->dl_tool_x = event->x, vml->dl_tool_y = event->y;
1242     return TRUE;
1243   }
1244   return FALSE;
1245
1246  
1247 #if 0
1248   if ( __map_types[vml->maptype].drawmode == vik_viewport_get_drawmode ( vvp ) )
1249   {
1250     VikCoord coord;
1251     MapCoord mapcoord;
1252     vik_viewport_screen_to_coord ( vvp, event->x, event->y, &coord );
1253     if ( __map_types[vml->maptype].coord_to_mapcoord ( &coord,
1254                 vml->xmapzoom ? vml->xmapzoom : vik_viewport_get_xmpp ( vvp ),
1255                 vml->ymapzoom ? vml->ymapzoom : vik_viewport_get_ympp ( vvp ),
1256                 &mapcoord ) ) {
1257       gchar *filename_buf = g_strdup_printf ( DIRSTRUCTURE,
1258                      vml->cache_dir, __map_types[vml->maptype].uniq_id,
1259                      mapcoord.scale, mapcoord.z, mapcoord.x, mapcoord.y );
1260
1261       __map_types[vml->maptype].download ( &mapcoord, filename_buf );
1262       g_free ( filename_buf );
1263       vik_layer_emit_update ( VIK_LAYER(vml) );
1264       return TRUE;
1265     }
1266   }
1267   return FALSE;
1268 #endif
1269 }
1270
1271 static void download_onscreen_maps ( gpointer vml_vvp[2], gint redownload )
1272 {
1273   VikMapsLayer *vml = vml_vvp[0];
1274   VikViewport *vvp = vml_vvp[1];
1275   VikViewportDrawMode vp_drawmode = vik_viewport_get_drawmode ( vvp );
1276
1277   gdouble xzoom = vml->xmapzoom ? vml->xmapzoom : vik_viewport_get_xmpp ( vvp );
1278   gdouble yzoom = vml->ymapzoom ? vml->ymapzoom : vik_viewport_get_ympp ( vvp );
1279
1280   VikCoord ul, br;
1281   MapCoord ulm, brm;
1282
1283   vik_viewport_screen_to_coord ( vvp, 0, 0, &ul );
1284   vik_viewport_screen_to_coord ( vvp, vik_viewport_get_width(vvp), vik_viewport_get_height(vvp), &br );
1285
1286   VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
1287   if ( vik_map_source_get_drawmode(map) == vp_drawmode &&
1288        vik_map_source_coord_to_mapcoord ( map, &ul, xzoom, yzoom, &ulm ) &&
1289        vik_map_source_coord_to_mapcoord ( map, &br, xzoom, yzoom, &brm ) )
1290     start_download_thread ( vml, vvp, &ul, &br, redownload );
1291   else if (vik_map_source_get_drawmode(map) != vp_drawmode) {
1292     const gchar *drawmode_name = vik_viewport_get_drawmode_name (vvp, vik_map_source_get_drawmode(map));
1293     gchar *err = g_strdup_printf(_("Wrong drawmode for this map.\nSelect \"%s\" from View menu and try again."), _(drawmode_name));
1294     a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vml), err );
1295     g_free(err);
1296   }
1297   else
1298     a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vml), _("Wrong zoom level for this map.") );
1299
1300 }
1301
1302 static void maps_layer_download_missing_onscreen_maps ( gpointer vml_vvp[2] )
1303 {
1304   download_onscreen_maps( vml_vvp, REDOWNLOAD_NONE);
1305 }
1306
1307 static void maps_layer_download_new_onscreen_maps ( gpointer vml_vvp[2] )
1308 {
1309   download_onscreen_maps( vml_vvp, REDOWNLOAD_NEW);
1310 }
1311
1312 static void maps_layer_redownload_all_onscreen_maps ( gpointer vml_vvp[2] )
1313 {
1314   download_onscreen_maps( vml_vvp, REDOWNLOAD_ALL);
1315 }
1316
1317 static void maps_layer_add_menu_items ( VikMapsLayer *vml, GtkMenu *menu, VikLayersPanel *vlp )
1318 {
1319   static gpointer pass_along[2];
1320   GtkWidget *item;
1321   pass_along[0] = vml;
1322   pass_along[1] = vik_layers_panel_get_viewport( VIK_LAYERS_PANEL(vlp) );
1323
1324   item = gtk_menu_item_new();
1325   gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
1326   gtk_widget_show ( item );
1327
1328   item = gtk_menu_item_new_with_mnemonic ( _("Download _Missing Onscreen Maps") );
1329   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_download_missing_onscreen_maps), pass_along );
1330   gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1331   gtk_widget_show ( item );
1332
1333   if ( vik_map_source_supports_download_only_new (MAPS_LAYER_NTH_TYPE(vml->maptype)) ) {
1334     item = gtk_menu_item_new_with_mnemonic ( _("Download _New Onscreen Maps") );
1335     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_download_new_onscreen_maps), pass_along );
1336     gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1337     gtk_widget_show ( item );
1338   }
1339
1340   /* TODO Add GTK_STOCK_REFRESH icon */
1341   item = gtk_menu_item_new_with_mnemonic ( _("Reload _All Onscreen Maps") );
1342   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_redownload_all_onscreen_maps), pass_along );
1343   gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1344   gtk_widget_show ( item );
1345 }