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