]> git.itanic.dy.fi Git - maemo-mapper/blob - src/maps.c
3a7cc3e588f5b35accd1de002807353471b3d91b
[maemo-mapper] / src / maps.c
1 /*
2  * Copyright (C) 2006, 2007 John Costigan.
3  *
4  * POI and GPS-Info code originally written by Cezary Jackiewicz.
5  *
6  * Default map data provided by http://www.openstreetmap.org/
7  *
8  * This file is part of Maemo Mapper.
9  *
10  * Maemo Mapper is free software: you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation, either version 3 of the License, or
13  * (at your option) any later version.
14  *
15  * Maemo Mapper is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with Maemo Mapper.  If not, see <http://www.gnu.org/licenses/>.
22  */
23
24 #ifdef HAVE_CONFIG_H
25 #    include "config.h"
26 #endif
27
28 #define _GNU_SOURCE
29
30 #include <stdlib.h>
31 #include <string.h>
32 #include <math.h>
33 #include <glib/gstdio.h>
34 #include <fcntl.h>
35 #include <locale.h>
36 #include <time.h>
37
38 #ifndef LEGACY
39 #    include <hildon/hildon-help.h>
40 #    include <hildon/hildon-note.h>
41 #    include <hildon/hildon-file-chooser-dialog.h>
42 #    include <hildon/hildon-number-editor.h>
43 #    include <hildon/hildon-banner.h>
44 #else
45 #    include <osso-helplib.h>
46 #    include <hildon-widgets/hildon-note.h>
47 #    include <hildon-widgets/hildon-file-chooser-dialog.h>
48 #    include <hildon-widgets/hildon-number-editor.h>
49 #    include <hildon-widgets/hildon-banner.h>
50 #    include <hildon-widgets/hildon-input-mode-hint.h>
51 #endif
52
53
54 #include "types.h"
55 #include "data.h"
56 #include "defines.h"
57
58 #include "display.h"
59 #include "main.h"
60 #include "maps.h"
61 #include "menu.h"
62 #include "settings.h"
63 #include "util.h"
64
65
66 typedef struct _RepoManInfo RepoManInfo;
67 struct _RepoManInfo {
68     GtkWidget *dialog;
69     GtkWidget *notebook;
70     GtkWidget *cmb_repos;
71     GList *repo_edits;
72 };
73
74 typedef struct _RepoEditInfo RepoEditInfo;
75 struct _RepoEditInfo {
76     gchar *name;
77     GtkWidget *txt_url;
78     GtkWidget *txt_db_filename;
79     GtkWidget *num_dl_zoom_steps;
80     GtkWidget *num_view_zoom_steps;
81     GtkWidget *chk_double_size;
82     GtkWidget *chk_nextable;
83     GtkWidget *btn_browse;
84     GtkWidget *btn_compact;
85     GtkWidget *num_min_zoom;
86     GtkWidget *num_max_zoom;
87     BrowseInfo browse_info;
88     RepoData *repo;
89 };
90
91
92 typedef struct _RepoLayersInfo RepoLayersInfo;
93 struct _RepoLayersInfo {
94     GtkWidget *dialog;
95     GtkWidget *notebook;
96     GtkListStore *layers_store;
97     GtkWidget *layers_list;
98     GList *layer_edits;
99 };
100
101
102 typedef struct _LayerEditInfo LayerEditInfo;
103 struct _LayerEditInfo {
104     RepoLayersInfo *rli;
105
106     GtkWidget *txt_name;
107     GtkWidget *txt_url;
108     GtkWidget *txt_db;
109     GtkWidget *num_autofetch;
110     GtkWidget *chk_visible;
111     GtkWidget *vbox;
112 };
113
114
115
116 typedef struct _MapmanInfo MapmanInfo;
117 struct _MapmanInfo {
118     GtkWidget *dialog;
119     GtkWidget *notebook;
120     GtkWidget *tbl_area;
121
122     /* The "Setup" tab. */
123     GtkWidget *rad_download;
124     GtkWidget *rad_delete;
125     GtkWidget *chk_overwrite;
126     GtkWidget *rad_by_area;
127     GtkWidget *rad_by_route;
128     GtkWidget *num_route_radius;
129
130     /* The "Area" tab. */
131     GtkWidget *txt_topleft_lat;
132     GtkWidget *txt_topleft_lon;
133     GtkWidget *txt_botright_lat;
134     GtkWidget *txt_botright_lon;
135
136     /* The "Zoom" tab. */
137     GtkWidget *chk_zoom_levels[MAX_ZOOM + 1];
138 };
139
140
141 typedef struct _CompactInfo CompactInfo;
142 struct _CompactInfo {
143     GtkWidget *dialog;
144     GtkWidget *txt;
145     GtkWidget *banner;
146     const gchar *db_filename;
147     gchar *status_msg;
148 };
149
150 typedef struct _MapCacheKey MapCacheKey;
151 struct _MapCacheKey {
152     RepoData      *repo;
153     gint           zoom;
154     gint           tilex;
155     gint           tiley;
156 };
157
158 typedef struct _MapCacheEntry MapCacheEntry;
159 struct _MapCacheEntry {
160     MapCacheKey    key;
161     int            list;
162     guint          size;
163     guint          data_sz;
164     gchar         *data;
165     GdkPixbuf     *pixbuf;
166     MapCacheEntry *next;
167     MapCacheEntry *prev;
168 };
169
170 typedef struct _MapCacheList MapCacheList;
171 struct _MapCacheList {
172     MapCacheEntry *head;
173     MapCacheEntry *tail;
174     size_t         size;
175     size_t         data_sz;
176 };
177
178 typedef struct _MapCache MapCache;
179 struct _MapCache {
180     MapCacheList  lists[4];
181     size_t        cache_size;
182     size_t        p;
183     size_t        thits;
184     size_t        bhits;
185     size_t        misses;
186     GHashTable   *entries;
187 };
188
189 static MapCache _map_cache;
190
191 const gchar* layer_timestamp_key = "tEXt::mm_ts";
192
193
194 static guint
195 mapdb_get_data(RepoData *repo, gint zoom, gint tilex, gint tiley, gchar **data)
196 {
197     guint size;
198     vprintf("%s(%s, %d, %d, %d)\n", __PRETTY_FUNCTION__,
199             repo->name, zoom, tilex, tiley);
200     *data = NULL;
201     size = 0;
202
203     if(!repo->db)
204     {
205         /* There is no cache.  Return NULL. */
206         vprintf("%s(): return %u\n", __PRETTY_FUNCTION__,size);
207         return size;
208     }
209
210 #ifdef MAPDB_SQLITE
211     /* Attempt to retrieve map from database. */
212     if(SQLITE_OK == sqlite3_bind_int(repo->stmt_map_select, 1, zoom)
213     && SQLITE_OK == sqlite3_bind_int(repo->stmt_map_select, 2, tilex)
214     && SQLITE_OK == sqlite3_bind_int(repo->stmt_map_select, 3, tiley)
215     && SQLITE_ROW == sqlite3_step(repo->stmt_map_select))
216     {
217         const gchar *bytes = NULL;
218         size = sqlite3_column_bytes(repo->stmt_map_select, 0);
219
220         /* "Pixbufs" of size less than or equal to MAX_PIXBUF_DUP_SIZE are
221          * actually keys into the dups table. */
222         if(size <= MAX_PIXBUF_DUP_SIZE)
223         {
224             gint hash = sqlite3_column_int(repo->stmt_map_select, 0);
225             if(SQLITE_OK == sqlite3_bind_int(repo->stmt_dup_select, 1, hash)
226             && SQLITE_ROW == sqlite3_step(repo->stmt_dup_select))
227             {
228                 bytes = sqlite3_column_blob(repo->stmt_dup_select, 0);
229                 size = sqlite3_column_bytes(repo->stmt_dup_select, 0);
230             }
231             else
232             {
233                 /* Not there?  Delete the entry, then. */
234                 if(SQLITE_OK != sqlite3_bind_int(
235                             repo->stmt_map_delete, 1, zoom)
236                 || SQLITE_OK != sqlite3_bind_int(
237                     repo->stmt_map_delete, 2, tilex)
238                 || SQLITE_OK != sqlite3_bind_int(
239                     repo->stmt_map_delete, 3, tiley)
240                 || SQLITE_DONE != sqlite3_step(repo->stmt_map_delete))
241                 {
242                     printf("Error in stmt_map_delete: %s\n", 
243                                 sqlite3_errmsg(repo->db));
244                 }
245                 sqlite3_reset(repo->stmt_map_delete);
246
247                 /* We have no bytes to return to the caller. */
248                 bytes = NULL;
249                 size = 0;
250             }
251             /* Don't reset the statement yet - we need the blob. */
252         }
253         else
254         {
255             bytes = sqlite3_column_blob(repo->stmt_map_select, 0);
256         }
257         if(bytes)
258         {
259             *data = g_slice_alloc(size);
260             memcpy(*data, bytes, size);
261         }
262         if(size <= MAX_PIXBUF_DUP_SIZE)
263             sqlite3_reset(repo->stmt_dup_select);
264     }
265     sqlite3_reset(repo->stmt_map_select);
266 #else
267     {
268         datum d;
269         gint32 key[] = {
270             GINT32_TO_BE(zoom),
271             GINT32_TO_BE(tilex),
272             GINT32_TO_BE(tiley)
273         };
274         d.dptr = (gchar*)&key;
275         d.dsize = sizeof(key);
276         d = gdbm_fetch(repo->db, d);
277         if(d.dptr)
278         {
279             size = d.dsize;
280             *data = g_slice_alloc(size);
281             memcpy(*data, d.dptr, size);
282             free(d.dptr);
283         }
284     }
285 #endif
286
287     vprintf("%s(): return %u\n", __PRETTY_FUNCTION__, size);
288     return size;
289 }
290
291 static void map_cache_list_remove(MapCacheList *_list, MapCacheEntry *_entry)
292 {
293     _list->size -= _entry->size;
294     _list->data_sz -= _entry->data_sz;
295     *(_entry->prev != NULL?&_entry->prev->next:&_list->head) = _entry->next;
296     *(_entry->next != NULL?&_entry->next->prev:&_list->tail) = _entry->prev;
297 }
298
299 static void map_cache_list_prepend(MapCacheList *_list, int _li,
300  MapCacheEntry *_entry)
301 {
302     _entry->prev = NULL;
303     _entry->next = _list[_li].head;
304     *(_list[_li].head != NULL?&_list[_li].head->prev:&_list[_li].tail) = _entry;
305     _list[_li].head = _entry;
306     _list[_li].size += _entry->size;
307     _list[_li].data_sz += _entry->data_sz;
308     _entry->list = _li;
309 }
310
311 static guint map_cache_key_hash(gconstpointer _key){
312     const MapCacheKey *key;
313     key = (const MapCacheKey *)_key;
314     return g_direct_hash(key->repo)+g_int_hash(&key->zoom)+
315      g_int_hash(&key->tilex)+g_int_hash(&key->tiley);
316 }
317
318 static gboolean map_cache_key_equal(gconstpointer _v1, gconstpointer _v2){
319     const MapCacheKey *key1;
320     const MapCacheKey *key2;
321     key1 = (const MapCacheKey *)_v1;
322     key2 = (const MapCacheKey *)_v2;
323     return key1->tilex == key2->tilex && key1->tiley == key2->tiley &&
324      key1->zoom == key2->zoom && key1->repo == key2->repo;
325 }
326
327 static void map_cache_entry_make_pixbuf(MapCacheEntry *_entry){
328     if (_entry->data != NULL)
329     {
330         GError *error;
331         GdkPixbufLoader *loader;
332         error = NULL;
333         loader = gdk_pixbuf_loader_new();
334         gdk_pixbuf_loader_write(loader, _entry->data, _entry->data_sz, NULL);
335         gdk_pixbuf_loader_close(loader, &error);
336         if(!error)
337         {
338             _entry->pixbuf = g_object_ref(gdk_pixbuf_loader_get_pixbuf(loader));
339             _entry->size = _entry->data_sz+
340              gdk_pixbuf_get_rowstride(_entry->pixbuf)*
341              gdk_pixbuf_get_height(_entry->pixbuf);
342             g_object_unref(loader);
343             return;
344         }
345         g_object_unref(loader);
346         g_slice_free1(_entry->data_sz, _entry->data);
347         _entry->data = NULL;
348         _entry->data_sz = 0;
349     }
350     _entry->pixbuf = NULL;
351     _entry->size = _entry->data_sz;
352 }
353
354 static void map_cache_entry_free_pixbuf(MapCacheEntry *_entry){
355     if(_entry->pixbuf!=NULL)
356     {
357         g_object_unref(_entry->pixbuf);
358         _entry->pixbuf = NULL;
359     }
360 }
361
362 static void map_cache_entry_free(MapCacheEntry *_entry){
363     if(_entry->list >= 0)
364         map_cache_list_remove(_map_cache.lists+_entry->list, _entry);
365     map_cache_entry_free_pixbuf(_entry);
366     g_slice_free1(_entry->data_sz, _entry->data);
367     g_slice_free(MapCacheEntry, _entry);
368 }
369
370 static gboolean
371 map_cache_replace(size_t _size, gboolean _b2)
372 {
373     gboolean ret;
374     size_t total_size;
375     total_size = _map_cache.lists[0].size+_map_cache.lists[1].data_sz
376      +_map_cache.lists[2].size+_map_cache.lists[3].data_sz;
377     ret = FALSE;
378     while(total_size+_size > _map_cache.cache_size)
379     {
380         MapCacheEntry *entry;
381         int list;
382         if(_map_cache.lists[0].tail != NULL &&
383          (_map_cache.lists[0].size > _map_cache.p ||
384          (_b2 && _map_cache.lists[0].size == _map_cache.p)))
385             list = 0;
386         else
387             list = 2;
388         entry = _map_cache.lists[list].tail;
389         if(entry == NULL)
390             break;
391         map_cache_list_remove(_map_cache.lists+list, entry);
392         map_cache_list_prepend(_map_cache.lists, list+1, entry);
393         total_size -= entry->size - entry->data_sz;
394         ret = TRUE;
395         _b2 = FALSE;
396     }
397     return ret;
398 }
399
400 static void
401 map_cache_evict(size_t _size)
402 {
403     size_t total_size;
404     size_t max_size;
405     total_size = _map_cache.lists[0].size+_map_cache.lists[1].size
406      +_map_cache.lists[2].size+_map_cache.lists[3].size;
407     max_size = _map_cache.cache_size<<1;
408     for(;;)
409     {
410         if(_map_cache.lists[0].size+_map_cache.lists[1].size+_size >
411          _map_cache.cache_size)
412         {
413             if(_map_cache.lists[1].tail != NULL)
414             {
415                 g_hash_table_remove(_map_cache.entries,
416                  &_map_cache.lists[1].tail->key);
417                 map_cache_replace(_size, FALSE);
418             }
419             else if(_map_cache.lists[0].tail != NULL)
420             {
421                 g_hash_table_remove(_map_cache.entries,
422                  &_map_cache.lists[0].tail->key);
423             }
424             else break;
425         }
426         else if(total_size+_size > _map_cache.cache_size)
427         {
428             if(total_size+_size > max_size &&
429              _map_cache.lists[3].tail != NULL)
430             {
431                 g_hash_table_remove(_map_cache.entries,
432                  &_map_cache.lists[3].tail->key);
433                 map_cache_replace(_size, FALSE);
434             }
435             else if(!map_cache_replace(_size, FALSE))
436                 break;
437         }
438         else break;
439         total_size = _map_cache.lists[0].size+_map_cache.lists[1].size
440          +_map_cache.lists[2].size+_map_cache.lists[3].size;
441     }
442 }
443
444 static GdkPixbuf *
445 map_cache_get(RepoData *repo, gint zoom, gint tilex, gint tiley)
446 {
447     MapCacheKey key;
448     MapCacheEntry *entry;
449     key.repo = repo;
450     key.zoom = zoom;
451     key.tilex = tilex;
452     key.tiley = tiley;
453     entry = (MapCacheEntry *)g_hash_table_lookup(_map_cache.entries, &key);
454     if(entry != NULL)
455     {
456         map_cache_list_remove(_map_cache.lists+entry->list, entry);
457         if(entry->pixbuf == NULL)
458         {
459             size_t bsize;
460             size_t dp;
461             map_cache_entry_make_pixbuf(entry);
462             bsize = _map_cache.lists[entry->list].size+entry->size;
463             if(bsize < 1)
464                 bsize = 1;
465             dp = _map_cache.lists[entry->list^2].size/bsize;
466             if(dp < 1)
467                 dp = 1;
468             if(entry->list == 1)
469             {
470                 _map_cache.p += dp;
471                 if(_map_cache.p > _map_cache.cache_size)
472                     _map_cache.p = _map_cache.cache_size;
473                 map_cache_replace(entry->size, FALSE);
474             }
475             else
476             {
477                 if(dp > _map_cache.p)
478                     _map_cache.p = 0;
479                 else
480                     _map_cache.p -= dp;
481                 map_cache_replace(entry->size, TRUE);
482             }
483             _map_cache.bhits++;
484         }
485         else
486             _map_cache.thits++;
487         map_cache_list_prepend(_map_cache.lists, 2, entry);
488     }
489     else
490     {
491         gchar *data;
492         guint  data_sz;
493         data_sz = mapdb_get_data(repo, zoom, tilex, tiley, &data);
494         entry = g_slice_new(MapCacheEntry);
495         *&entry->key = *&key;
496         entry->data = data;
497         entry->data_sz = data_sz;
498         map_cache_entry_make_pixbuf(entry);
499         map_cache_evict(entry->size);
500         map_cache_list_prepend(_map_cache.lists, 0, entry);
501         g_hash_table_insert(_map_cache.entries, &entry->key, entry);
502         _map_cache.misses++;
503     }
504     if(entry->pixbuf != NULL)
505         g_object_ref(entry->pixbuf);
506     return entry->pixbuf;
507 }
508
509 static void
510 map_cache_update(RepoData *repo, gint zoom, gint tilex, gint tiley,
511  gchar *data,guint size)
512 {
513     MapCacheKey key;
514     MapCacheEntry *entry;
515     key.repo = repo;
516     key.zoom = zoom;
517     key.tilex = tilex;
518     key.tiley = tiley;
519     entry = (MapCacheEntry *)g_hash_table_lookup(_map_cache.entries, &key);
520     if(entry != NULL)
521     {
522         g_slice_free1(entry->data_sz, entry->data);
523         entry->data = g_slice_alloc(size);
524         memcpy(entry->data, data, size);
525         entry->data_sz = size;
526         if(entry->pixbuf != NULL)
527         {
528             map_cache_entry_free_pixbuf(entry);
529             map_cache_list_remove(_map_cache.lists+entry->list, entry);
530             map_cache_list_prepend(_map_cache.lists, entry->list+1, entry);
531         }
532     }
533 }
534
535 static void
536 map_cache_remove(RepoData *repo, gint zoom, gint tilex, gint tiley)
537 {
538     MapCacheKey key;
539     key.repo = repo;
540     key.zoom = zoom;
541     key.tilex = tilex;
542     key.tiley = tiley;
543     g_hash_table_remove(_map_cache.entries, &key);
544 }
545
546 void
547 map_cache_init(size_t cache_size)
548 {
549     g_mutex_lock(_mapdb_mutex);
550     if(_map_cache.entries == NULL)
551         _map_cache.entries = g_hash_table_new_full(map_cache_key_hash,
552          map_cache_key_equal, NULL, (GDestroyNotify)map_cache_entry_free);
553     _map_cache.cache_size = cache_size;
554     if(_map_cache.p > cache_size)
555         _map_cache.p = cache_size;
556     map_cache_evict(0);
557     g_mutex_unlock(_mapdb_mutex);
558 }
559
560 size_t
561 map_cache_resize(size_t cache_size)
562 {
563     size_t total_size;
564     g_mutex_lock(_mapdb_mutex);
565     _map_cache.cache_size = cache_size;
566     total_size = _map_cache.lists[0].size+_map_cache.lists[1].data_sz
567      +_map_cache.lists[2].size+_map_cache.lists[3].data_sz;
568     g_mutex_unlock(_mapdb_mutex);
569     return total_size;
570 }
571
572 void
573 map_cache_destroy(void)
574 {
575     g_mutex_lock(_mapdb_mutex);
576     if(_map_cache.entries != NULL)
577     {
578         g_hash_table_destroy(_map_cache.entries);
579         _map_cache.entries = NULL;
580         printf("thits: %u (%0.2f%%)  bhits: %u (%0.2f%%)  "
581          "misses: %u (%0.2f%%)\n",
582          _map_cache.thits, 100*_map_cache.thits/(double)(
583          _map_cache.thits+_map_cache.bhits+_map_cache.misses),
584          _map_cache.bhits, 100*_map_cache.bhits/(double)(
585          _map_cache.thits+_map_cache.bhits+_map_cache.misses),
586          _map_cache.misses, 100*_map_cache.misses/(double)(
587          _map_cache.thits+_map_cache.bhits+_map_cache.misses));
588     }
589     g_mutex_unlock(_mapdb_mutex);
590 }
591
592
593 void
594 map_cache_clean (void)
595 {
596     g_mutex_lock(_mapdb_mutex);
597     if(_map_cache.entries != NULL)
598         g_hash_table_remove_all (_map_cache.entries);
599     g_mutex_unlock(_mapdb_mutex);
600 }
601
602
603 gboolean
604 mapdb_exists(RepoData *repo, gint zoom, gint tilex, gint tiley)
605 {
606     gboolean exists;
607     vprintf("%s(%s, %d, %d, %d)\n", __PRETTY_FUNCTION__,
608             repo->name, zoom, tilex, tiley);
609
610     g_mutex_lock(_mapdb_mutex);
611
612     if(!repo->db)
613     {
614         /* There is no cache.  Return FALSE. */
615         g_mutex_unlock(_mapdb_mutex);
616         vprintf("%s(): return FALSE\n", __PRETTY_FUNCTION__);
617         return FALSE;
618     }
619
620     /* Search the cache first. */
621     {
622         MapCacheKey key;
623         MapCacheEntry *entry;
624         key.repo = repo;
625         key.zoom = zoom;
626         key.tilex = tilex;
627         key.tiley = tiley;
628         entry = (MapCacheEntry *)g_hash_table_lookup(_map_cache.entries, &key);
629         if(entry != NULL)
630         {
631             gboolean ret;
632             ret = entry->data != NULL;
633             g_mutex_unlock(_mapdb_mutex);
634             return ret;
635         }
636     }
637
638 #ifdef MAPDB_SQLITE
639     /* Attempt to retrieve map from database. */
640     if(SQLITE_OK == sqlite3_bind_int(repo->stmt_map_exists, 1, zoom)
641     && SQLITE_OK == sqlite3_bind_int(repo->stmt_map_exists, 2, tilex)
642     && SQLITE_OK == sqlite3_bind_int(repo->stmt_map_exists, 3, tiley)
643     && SQLITE_ROW == sqlite3_step(repo->stmt_map_exists)
644     && sqlite3_column_int(repo->stmt_map_exists, 0) > 0)
645     {
646         exists = TRUE;
647     }
648     else
649     {
650         exists = FALSE;
651     }
652     sqlite3_reset(repo->stmt_map_exists);
653 #else
654     {
655         datum d;
656         gint32 key[] = {
657             GINT32_TO_BE(zoom),
658             GINT32_TO_BE(tilex),
659             GINT32_TO_BE(tiley)
660         };
661         d.dptr = (gchar*)&key;
662         d.dsize = sizeof(key);
663         exists = gdbm_exists(repo->db, d);
664     }
665 #endif
666
667     g_mutex_unlock(_mapdb_mutex);
668
669     vprintf("%s(): return %d\n", __PRETTY_FUNCTION__, exists);
670     return exists;
671 }
672
673 GdkPixbuf*
674 mapdb_get(RepoData *repo, gint zoom, gint tilex, gint tiley)
675 {
676     GdkPixbuf *pixbuf;
677     vprintf("%s(%s, %d, %d, %d)\n", __PRETTY_FUNCTION__,
678             repo->name, zoom, tilex, tiley);
679     g_mutex_lock(_mapdb_mutex);
680     pixbuf = map_cache_get(repo, zoom, tilex, tiley);
681     g_mutex_unlock(_mapdb_mutex);
682     vprintf("%s(): return %p\n", __PRETTY_FUNCTION__, pixbuf);
683     return pixbuf;
684 }
685
686 #ifdef MAPDB_SQLITE
687 static gboolean
688 mapdb_checkdec(RepoData *repo, gint zoom, gint tilex, gint tiley)
689 {
690     gboolean success = TRUE;
691     vprintf("%s(%s, %d, %d, %d)\n", __PRETTY_FUNCTION__,
692             repo->name, zoom, tilex, tiley);
693
694     /* First, we have to check if the old map was a dup. */
695     if(SQLITE_OK == sqlite3_bind_int(repo->stmt_map_select, 1, zoom)
696     && SQLITE_OK == sqlite3_bind_int(repo->stmt_map_select, 2, tilex)
697     && SQLITE_OK == sqlite3_bind_int(repo->stmt_map_select, 3, tiley)
698     && SQLITE_ROW == sqlite3_step(repo->stmt_map_select)
699     && sqlite3_column_bytes(repo->stmt_map_select, 0)
700             <= MAX_PIXBUF_DUP_SIZE)
701     {
702         /* Old map was indeed a dup. Decrement the reference count. */
703         gint hash = sqlite3_column_int(repo->stmt_map_select, 0);
704         if(SQLITE_OK != sqlite3_bind_int(
705                     repo->stmt_dup_decrem, 1, hash)
706         || SQLITE_DONE != sqlite3_step(repo->stmt_dup_decrem)
707         || SQLITE_OK != sqlite3_bind_int(
708                     repo->stmt_dup_delete, 1, hash)
709         || SQLITE_DONE != sqlite3_step(repo->stmt_dup_delete))
710         {
711             success = FALSE;
712             printf("Error in stmt_dup_decrem: %s\n",
713                     sqlite3_errmsg(repo->db));
714         }
715         sqlite3_reset(repo->stmt_dup_delete);
716         sqlite3_reset(repo->stmt_dup_decrem);
717     }
718     sqlite3_reset(repo->stmt_map_select);
719
720     vprintf("%s(): return %d\n", __PRETTY_FUNCTION__, success);
721     return success;
722 }
723 #endif
724
725 static gboolean
726 mapdb_update(gboolean exists, RepoData *repo,
727         gint zoom, gint tilex, gint tiley, void *bytes, gint size)
728 {
729 #ifdef MAPDB_SQLITE
730     sqlite3_stmt *stmt;
731     gint hash = 0;
732 #endif
733     gint success = TRUE;
734     vprintf("%s(%s, %d, %d, %d)\n", __PRETTY_FUNCTION__,
735             repo->name, zoom, tilex, tiley);
736
737     g_mutex_lock(_mapdb_mutex);
738     map_cache_update(repo, zoom, tilex, tiley, bytes, size);
739
740     if(!repo->db)
741     {
742         /* There is no cache.  Return FALSE. */
743         g_mutex_unlock(_mapdb_mutex);
744         vprintf("%s(): return FALSE\n", __PRETTY_FUNCTION__);
745         return FALSE;
746     }
747
748 #ifdef MAPDB_SQLITE
749     /* At least try to open a transaction. */
750     sqlite3_step(repo->stmt_trans_begin);
751     sqlite3_reset(repo->stmt_trans_begin);
752
753     /* Pixbufs of size MAX_PIXBUF_DUP_SIZE or less are special.  They are
754      * probably PNGs of a single color (like blue for water or beige for empty
755      * land).  To reduce redundancy in the database, we will store them in a
756      * separate table and, in the maps table, only refer to them. */
757     if(size <= MAX_PIXBUF_DUP_SIZE)
758     {
759         /* Duplicate pixbuf. */
760         if(exists)
761         {
762             /* First, check if we need to remove a count from the dups table.*/
763             mapdb_checkdec(repo, zoom, tilex, tiley);
764         }
765         if(success)
766         {
767             /* Compute hash of the bytes. */
768             gchar *cur = bytes, *end = bytes + size;
769             hash = *cur;
770             while(cur < end)
771                 hash = (hash << 5) - hash + *(++cur);
772
773             /* Check if dup already exists. */
774             if(SQLITE_OK == sqlite3_bind_int(repo->stmt_dup_exists, 1, hash)
775             && SQLITE_ROW == sqlite3_step(repo->stmt_dup_exists)
776             && sqlite3_column_int(repo->stmt_dup_exists, 0) > 0)
777             {
778                 /* Dup already exists - increment existing entry. */
779                 if(SQLITE_OK != sqlite3_bind_int(repo->stmt_dup_increm,1, hash)
780                 || SQLITE_DONE != sqlite3_step(repo->stmt_dup_increm))
781                 {
782                     success = FALSE;
783                     printf("Error in stmt_dup_increm: %s\n",
784                             sqlite3_errmsg(repo->db));
785                 }
786                 sqlite3_reset(repo->stmt_dup_increm);
787             }
788             else
789             {
790                 /* Dup doesn't exist - add new entry. */
791                 if(SQLITE_OK != sqlite3_bind_int(repo->stmt_dup_insert,1, hash)
792                 || SQLITE_OK != sqlite3_bind_blob(repo->stmt_dup_insert,
793                     2, bytes, size, NULL)
794                 || SQLITE_DONE != sqlite3_step(repo->stmt_dup_insert))
795                 {
796                     success = FALSE;
797                     printf("Error in stmt_dup_insert: %s\n",
798                             sqlite3_errmsg(repo->db));
799                 }
800                 sqlite3_reset(repo->stmt_dup_insert);
801             }
802             sqlite3_reset(repo->stmt_dup_exists);
803         }
804         /* Now, if successful so far, we fall through the end of this if
805          * statement and insert the hash as the blob.  Setting bytes to NULL
806          * is the signal to do this. */
807         bytes = NULL;
808     }
809
810     if(success)
811     {
812         stmt = exists ? repo->stmt_map_update : repo->stmt_map_insert;
813
814         /* Attempt to insert map from database. */
815         if(SQLITE_OK != (bytes ? sqlite3_bind_blob(stmt, 1, bytes, size, NULL)
816                     : sqlite3_bind_int(stmt, 1, hash))
817         || SQLITE_OK != sqlite3_bind_int(stmt, 2, zoom)
818         || SQLITE_OK != sqlite3_bind_int(stmt, 3, tilex)
819         || SQLITE_OK != sqlite3_bind_int(stmt, 4, tiley)
820         || SQLITE_DONE != sqlite3_step(stmt))
821         {
822             success = FALSE;
823             printf("Error in mapdb_update: %s\n", sqlite3_errmsg(repo->db));
824         }
825         sqlite3_reset(stmt);
826     }
827
828     if(success)
829     {
830         sqlite3_step(repo->stmt_trans_commit);
831         sqlite3_reset(repo->stmt_trans_commit);
832     }
833     else
834     {
835         sqlite3_step(repo->stmt_trans_rollback);
836         sqlite3_reset(repo->stmt_trans_rollback);
837     }
838
839 #else
840     {
841         datum dkey, dcon;
842         gint32 key[] = {
843             GINT32_TO_BE(zoom),
844             GINT32_TO_BE(tilex),
845             GINT32_TO_BE(tiley)
846         };
847         dkey.dptr = (gchar*)&key;
848         dkey.dsize = sizeof(key);
849         dcon.dptr = bytes;
850         dcon.dsize = size;
851         success = !gdbm_store(repo->db, dkey, dcon, GDBM_REPLACE);
852     }
853 #endif
854     g_mutex_unlock(_mapdb_mutex);
855
856     vprintf("%s(): return %d\n", __PRETTY_FUNCTION__, success);
857     return success;
858 }
859
860 static gboolean
861 mapdb_delete(RepoData *repo, gint zoom, gint tilex, gint tiley)
862 {
863     gint success = FALSE;
864     vprintf("%s(%s, %d, %d, %d)\n", __PRETTY_FUNCTION__,
865             repo->name, zoom, tilex, tiley);
866
867     g_mutex_lock(_mapdb_mutex);
868     map_cache_remove(repo, zoom, tilex, tiley);
869
870     if(!repo->db)
871     {
872         /* There is no cache.  Return FALSE. */
873         g_mutex_unlock(_mapdb_mutex);
874         vprintf("%s(): return FALSE\n", __PRETTY_FUNCTION__);
875         return FALSE;
876     }
877
878 #ifdef MAPDB_SQLITE
879     /* At least try to open a transaction. */
880     sqlite3_step(repo->stmt_trans_begin);
881     sqlite3_reset(repo->stmt_trans_begin);
882
883     /* First, check if we need to remove a count from the dups table. */
884     /* Then, attempt to delete map from database. */
885     if(!mapdb_checkdec(repo, zoom, tilex, tiley)
886     || SQLITE_OK != sqlite3_bind_int(repo->stmt_map_delete, 1, zoom)
887     || SQLITE_OK != sqlite3_bind_int(repo->stmt_map_delete, 2, tilex)
888     || SQLITE_OK != sqlite3_bind_int(repo->stmt_map_delete, 3, tiley)
889     || SQLITE_DONE != sqlite3_step(repo->stmt_map_delete))
890     {
891         success = FALSE;
892         printf("Error in stmt_map_delete: %s\n", 
893                     sqlite3_errmsg(repo->db));
894     }
895     sqlite3_reset(repo->stmt_map_delete);
896
897     if(success)
898     {
899         sqlite3_step(repo->stmt_trans_commit);
900         sqlite3_reset(repo->stmt_trans_commit);
901     }
902     else
903     {
904         sqlite3_step(repo->stmt_trans_rollback);
905         sqlite3_reset(repo->stmt_trans_rollback);
906     }
907 #else
908     {
909         datum d;
910         gint32 key[] = {
911             GINT32_TO_BE(zoom),
912             GINT32_TO_BE(tilex),
913             GINT32_TO_BE(tiley)
914         };
915         d.dptr = (gchar*)&key;
916         d.dsize = sizeof(key);
917         success = !gdbm_delete(repo->db, d);
918     }
919 #endif
920     g_mutex_unlock(_mapdb_mutex);
921
922     vprintf("%s(): return %d\n", __PRETTY_FUNCTION__, success);
923     return success;
924 }
925
926 void
927 set_repo_type(RepoData *repo)
928 {
929     printf("%s(%s)\n", __PRETTY_FUNCTION__, repo->url);
930
931     if(repo->url && *repo->url)
932     {
933         gchar *url = g_utf8_strdown(repo->url, -1);
934
935         /* Determine type of repository. */
936         if(strstr(url, "service=wms"))
937             repo->type = REPOTYPE_WMS;
938         else if(strstr(url, "%s"))
939             repo->type = REPOTYPE_QUAD_QRST;
940         else if(strstr(url, "%0d"))
941             repo->type = REPOTYPE_XYZ_INV;
942         else if(strstr(url, "%-d"))
943             repo->type = REPOTYPE_XYZ_SIGNED;
944         else if(strstr(url, "%0s"))
945             repo->type = REPOTYPE_QUAD_ZERO;
946         else
947             repo->type = REPOTYPE_XYZ;
948
949         g_free(url);
950     }
951     else
952         repo->type = REPOTYPE_NONE;
953
954     vprintf("%s(): return\n", __PRETTY_FUNCTION__);
955 }
956
957 /* Returns the directory containing the given database filename, or NULL
958  * if the database file could not be created. */
959 static gboolean
960 repo_make_db(RepoData *rd)
961 {
962     printf("%s(%s)\n", __PRETTY_FUNCTION__, rd->db_filename);
963     gchar *db_dirname;
964     gint fd;
965
966     db_dirname = g_path_get_dirname(rd->db_filename);
967     
968     /* Check if db_filename is a directory and ask to upgrade. */
969     if(g_file_test(rd->db_filename, G_FILE_TEST_IS_DIR))
970     {
971         gchar buffer[BUFFER_SIZE];
972         gchar *new_name = g_strdup_printf("%s.db", rd->db_filename);
973         g_free(rd->db_filename);
974         rd->db_filename = new_name;
975
976         snprintf(buffer, sizeof(buffer), "%s",
977                 _("The current repository is in a legacy format and will "
978                     "be converted.  You should delete your old maps if you "
979                     "no longer plan to use them."));
980         popup_error(_window, buffer);
981     }
982
983     if(g_mkdir_with_parents(db_dirname, 0755))
984     {
985         g_free(db_dirname);
986         return FALSE;
987     }
988     g_free(db_dirname);
989
990     if(!g_file_test(rd->db_filename, G_FILE_TEST_EXISTS))
991     {
992         fd = g_creat(rd->db_filename, 0644);
993         if(fd == -1)
994         {
995             g_free(db_dirname);
996             return FALSE;
997         }
998         close(fd);
999     }
1000
1001     vprintf("%s(): return %d\n", __PRETTY_FUNCTION__,
1002            g_file_test(rd->db_filename, G_FILE_TEST_EXISTS));
1003     return g_file_test(rd->db_filename, G_FILE_TEST_EXISTS);
1004 }
1005
1006 gboolean
1007 repo_set_curr(RepoData *rd)
1008 {
1009     RepoData* repo_p;
1010
1011     printf("%s()\n", __PRETTY_FUNCTION__);
1012     if(!rd->db_filename || !*rd->db_filename
1013             || repo_make_db(rd))
1014     {
1015         repo_p = _curr_repo;
1016
1017         while (repo_p)
1018         {
1019             if(repo_p->db)
1020             {
1021                 g_mutex_lock(_mapdb_mutex);
1022 #ifdef MAPDB_SQLITE
1023                 sqlite3_close(repo_p->db);
1024                 repo_p->db = NULL;
1025 #else
1026                 gdbm_close(repo_p->db);
1027                 repo_p->db = NULL;
1028 #endif
1029                 g_mutex_unlock(_mapdb_mutex);
1030             }
1031             repo_p = repo_p->layers;
1032         }
1033
1034         /* Set the current repository! */
1035         _curr_repo = rd;
1036
1037         /* Set up the database. */
1038         if(_curr_repo->db_filename && *_curr_repo->db_filename)
1039         {
1040
1041 #ifdef MAPDB_SQLITE
1042             if(SQLITE_OK != (sqlite3_open(_curr_repo->db_filename,
1043                             &(_curr_repo->db)))
1044             /* Open worked. Now create tables, failing if they already exist.*/
1045             || (sqlite3_exec(_curr_repo->db,
1046                         "create table maps ("
1047                         "zoom integer, "
1048                         "tilex integer, "
1049                         "tiley integer, "
1050                         "pixbuf blob, "
1051                         "primary key (zoom, tilex, tiley))"
1052                         ";"
1053                         "create table dups ("
1054                         "hash integer primary key, "
1055                         "uses integer, "
1056                         "pixbuf blob)",
1057                         NULL, NULL, NULL), FALSE) /* !! Comma operator !! */
1058                 /* Prepare select map statement. */
1059              || SQLITE_OK != sqlite3_prepare(_curr_repo->db,
1060                         "select pixbuf from maps "
1061                         "where zoom = ? and tilex = ? and tiley = ?",
1062                         -1, &_curr_repo->stmt_map_select, NULL)
1063                 /* Prepare exists map statement. */
1064              || SQLITE_OK != sqlite3_prepare(_curr_repo->db,
1065                         "select count(*) from maps "
1066                         "where zoom = ? and tilex = ? and tiley = ?",
1067                         -1, &_curr_repo->stmt_map_exists, NULL)
1068                 /* Prepare insert map statement. */
1069              || SQLITE_OK != sqlite3_prepare(_curr_repo->db,
1070                         "insert into maps (pixbuf, zoom, tilex, tiley)"
1071                         " values (?, ?, ?, ?)",
1072                         -1, &_curr_repo->stmt_map_insert, NULL)
1073                 /* Prepare update map statement. */
1074              || SQLITE_OK != sqlite3_prepare(_curr_repo->db,
1075                         "update maps set pixbuf = ? "
1076                         "where zoom = ? and tilex = ? and tiley = ?",
1077                         -1, &_curr_repo->stmt_map_update, NULL)
1078                 /* Prepare delete map statement. */
1079              || SQLITE_OK != sqlite3_prepare(_curr_repo->db,
1080                         "delete from maps "
1081                         "where zoom = ? and tilex = ? and tiley = ?",
1082                         -1, &_curr_repo->stmt_map_delete, NULL)
1083
1084                 /* Prepare select-by-map dup statement. */
1085                 /* Prepare select-by-hash dup statement. */
1086              || SQLITE_OK != sqlite3_prepare(_curr_repo->db,
1087                         "select pixbuf from dups "
1088                         "where hash = ?",
1089                         -1, &_curr_repo->stmt_dup_select, NULL)
1090                 /* Prepare exists map statement. */
1091              || SQLITE_OK != sqlite3_prepare(_curr_repo->db,
1092                         "select count(*) from dups "
1093                         "where hash = ?",
1094                         -1, &_curr_repo->stmt_dup_exists, NULL)
1095                 /* Prepare insert dup statement. */
1096              || SQLITE_OK != sqlite3_prepare(_curr_repo->db,
1097                         "insert into dups (hash, pixbuf, uses) "
1098                         "values (?, ?, 1)",
1099                         -1, &_curr_repo->stmt_dup_insert, NULL)
1100                 /* Prepare increment dup statement. */
1101              || SQLITE_OK != sqlite3_prepare(_curr_repo->db,
1102                         "update dups "
1103                         "set uses = uses + 1 "
1104                         "where hash = ?",
1105                         -1, &_curr_repo->stmt_dup_increm, NULL)
1106                 /* Prepare decrement dup statement. */
1107              || SQLITE_OK != sqlite3_prepare(_curr_repo->db,
1108                         "update dups "
1109                         "set uses = uses - 1 "
1110                         "where hash = ? ",
1111                         -1, &_curr_repo->stmt_dup_decrem, NULL)
1112                 /* Prepare delete dup statement. */
1113              || SQLITE_OK != sqlite3_prepare(_curr_repo->db,
1114                         "delete from dups "
1115                         "where hash = ? and uses <= 0",
1116                         -1, &_curr_repo->stmt_dup_delete, NULL)
1117
1118                 /* Prepare begin-transaction statement. */
1119              || SQLITE_OK != sqlite3_prepare(_curr_repo->db,
1120                      "begin transaction",
1121                         -1, &_curr_repo->stmt_trans_begin, NULL)
1122              || SQLITE_OK != sqlite3_prepare(_curr_repo->db,
1123                      "commit transaction",
1124                         -1, &_curr_repo->stmt_trans_commit, NULL)
1125              || SQLITE_OK != sqlite3_prepare(_curr_repo->db,
1126                      "rollback transaction", -1,
1127                      &_curr_repo->stmt_trans_rollback, NULL))
1128             {
1129                 gchar buffer[BUFFER_SIZE];
1130                 snprintf(buffer, sizeof(buffer), "%s: %s\n%s",
1131                         _("Failed to open map database for repository"),
1132                         sqlite3_errmsg(_curr_repo->db),
1133                         _("Downloaded maps will not be cached."));
1134                 sqlite3_close(_curr_repo->db);
1135                 _curr_repo->db = NULL;
1136                 popup_error(_window, buffer);
1137             }
1138 #else
1139             /* initialize all databases for all layers */
1140             repo_p = _curr_repo;
1141             while (repo_p) {
1142                 if (repo_p->db_filename && *repo_p->db_filename
1143                         && repo_make_db (repo_p))
1144                     repo_p->db = gdbm_open(repo_p->db_filename,
1145                             0, GDBM_WRCREAT | GDBM_FAST, 0644, NULL);
1146                 repo_p = repo_p->layers;
1147             }
1148
1149             if(!_curr_repo->db)
1150             {
1151                 gchar buffer[BUFFER_SIZE];
1152                 snprintf(buffer, sizeof(buffer), "%s\n%s",
1153                         _("Failed to open map database for repository"),
1154                         _("Downloaded maps will not be cached."));
1155                 _curr_repo->db = NULL;
1156                 popup_error(_window, buffer);
1157             }
1158 #endif
1159         }
1160         else
1161         {
1162             _curr_repo->db = NULL;
1163         }
1164         vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__);
1165         return TRUE;
1166     }
1167     else
1168     {
1169         gchar buffer[BUFFER_SIZE];
1170         snprintf(buffer, sizeof(buffer), "%s: %s",
1171                 _("Unable to create map database for repository"), rd->name);
1172         popup_error(_window, buffer);
1173         _curr_repo = rd;
1174         vprintf("%s(): return FALSE\n", __PRETTY_FUNCTION__);
1175         return FALSE;
1176     }
1177 }
1178
1179
1180 /**
1181  * Returns true if:
1182  * 1. base == layer, or
1183  * 2. layer is sublayer of base
1184  */
1185 gboolean repo_is_layer (RepoData* base, RepoData* layer)
1186 {
1187     while (base) {
1188         if (base == layer)
1189             return TRUE;
1190         base = base->layers;
1191     }
1192
1193     return FALSE;
1194 }
1195
1196
1197 /**
1198  * Given a wms uri pattern, compute the coordinate transformation and
1199  * trimming.
1200  * 'proj' is used for the conversion
1201  */
1202 static gchar*
1203 map_convert_wms_to_wms(gint tilex, gint tiley, gint zoomlevel, gchar* uri)
1204 {
1205     gint system_retcode;
1206     gchar cmd[BUFFER_SIZE], srs[BUFFER_SIZE];
1207     gchar *ret = NULL;
1208     FILE* in;
1209     gdouble lon1, lat1, lon2, lat2;
1210
1211     gchar *widthstr   = strcasestr(uri,"WIDTH=");
1212     gchar *heightstr  = strcasestr(uri,"HEIGHT=");
1213     gchar *srsstr     = strcasestr(uri,"SRS=EPSG");
1214     gchar *srsstre    = strchr(srsstr,'&');
1215     vprintf("%s()\n", __PRETTY_FUNCTION__);
1216
1217     /* missing: test if found */
1218     strcpy(srs,"epsg");
1219     strncpy(srs+4,srsstr+8,256);
1220     /* missing: test srsstre-srsstr < 526 */
1221     srs[srsstre-srsstr-4] = 0;
1222     /* convert to lower, as WMC is EPSG and cs2cs is epsg */
1223
1224     gint dwidth  = widthstr ? atoi(widthstr+6) - TILE_SIZE_PIXELS : 0;
1225     gint dheight = heightstr ? atoi(heightstr+7) - TILE_SIZE_PIXELS : 0;
1226
1227     unit2latlon(tile2zunit(tilex,zoomlevel)
1228             - pixel2zunit(dwidth/2,zoomlevel),
1229             tile2zunit(tiley+1,zoomlevel)
1230             + pixel2zunit((dheight+1)/2,zoomlevel),
1231             lat1, lon1);
1232
1233     unit2latlon(tile2zunit(tilex+1,zoomlevel)
1234             + pixel2zunit((dwidth+1)/2,zoomlevel),
1235             tile2zunit(tiley,zoomlevel)
1236             - pixel2zunit(dheight/2,zoomlevel),
1237             lat2, lon2);
1238
1239     setlocale(LC_NUMERIC, "C");
1240
1241     snprintf(cmd, sizeof(cmd),
1242             "(echo \"%.6f %.6f\"; echo \"%.6f %.6f\") | "
1243             "/usr/bin/cs2cs +proj=longlat +datum=WGS84 +to +init=%s -f %%.6f "
1244             " > /tmp/tmpcs2cs ",
1245             lon1, lat1, lon2, lat2, srs);
1246     vprintf("Running command: %s\n", cmd);
1247     system_retcode = system(cmd);
1248
1249     if(system_retcode)
1250         g_printerr("cs2cs returned error code %d\n",
1251                 WEXITSTATUS(system_retcode));
1252     else if(!(in = g_fopen("/tmp/tmpcs2cs","r")))
1253         g_printerr("Cannot open results of conversion\n");
1254     else if(5 != fscanf(in,"%lf %lf %s %lf %lf",
1255                 &lon1, &lat1, cmd, &lon2, &lat2))
1256     {
1257         g_printerr("Wrong conversion\n");
1258         fclose(in);
1259     }
1260     else
1261     {
1262         fclose(in);
1263         ret = g_strdup_printf(uri, lon1, lat1, lon2, lat2);
1264     }
1265
1266     setlocale(LC_NUMERIC, "");
1267
1268     vprintf("%s(): return %s\n", __PRETTY_FUNCTION__, ret);
1269     return ret;
1270 }
1271
1272
1273 /**
1274  * Given the xyz coordinates of our map coordinate system, write the qrst
1275  * quadtree coordinates to buffer.
1276  */
1277 static void
1278 map_convert_coords_to_quadtree_string(gint x, gint y, gint zoomlevel,
1279                                       gchar *buffer, const gchar initial,
1280                                       const gchar *const quadrant)
1281 {
1282     gchar *ptr = buffer;
1283     gint n;
1284     vprintf("%s()\n", __PRETTY_FUNCTION__);
1285
1286     if (initial)
1287         *ptr++ = initial;
1288
1289     for(n = MAX_ZOOM - zoomlevel; n >= 0; n--)
1290     {
1291         gint xbit = (x >> n) & 1;
1292         gint ybit = (y >> n) & 1;
1293         *ptr++ = quadrant[xbit + 2 * ybit];
1294     }
1295     *ptr++ = '\0';
1296     vprintf("%s(): return\n", __PRETTY_FUNCTION__);
1297 }
1298
1299 /**
1300  * Construct the URL that we should fetch, based on the current URI format.
1301  * This method works differently depending on if a "%s" string is present in
1302  * the URI format, since that would indicate a quadtree-based map coordinate
1303  * system.
1304  */
1305 static gchar*
1306 map_construct_url(RepoData *repo, gint zoom, gint tilex, gint tiley)
1307 {
1308     gchar *retval;
1309     vprintf("%s(%p, %d, %d, %d)\n", __PRETTY_FUNCTION__,
1310             repo, zoom, tilex, tiley);
1311     switch(repo->type)
1312     {
1313         case REPOTYPE_XYZ:
1314             retval = g_strdup_printf(repo->url,
1315                     tilex, tiley,  zoom - (MAX_ZOOM - 16));
1316             break;
1317
1318         case REPOTYPE_XYZ_INV:
1319             retval = g_strdup_printf(repo->url,
1320                     MAX_ZOOM + 1 - zoom, tilex, tiley);
1321             break;
1322
1323         case REPOTYPE_XYZ_SIGNED:
1324             retval = g_strdup_printf(repo->url,
1325                     tilex,
1326                     (1 << (MAX_ZOOM - zoom)) - tiley - 1,
1327                     zoom - (MAX_ZOOM - 17));
1328             break;
1329
1330         case REPOTYPE_QUAD_QRST:
1331         {
1332             gchar location[MAX_ZOOM + 2];
1333             map_convert_coords_to_quadtree_string(
1334                     tilex, tiley, zoom, location, 't', "qrts");
1335             retval = g_strdup_printf(repo->url, location);
1336             break;
1337         }
1338
1339         case REPOTYPE_QUAD_ZERO:
1340         {
1341             /* This is a zero-based quadtree URI. */
1342             gchar location[MAX_ZOOM + 2];
1343             map_convert_coords_to_quadtree_string(
1344                     tilex, tiley, zoom, location, '\0', "0123");
1345             retval = g_strdup_printf(repo->url, location);
1346             break;
1347         }
1348
1349         case REPOTYPE_WMS:
1350             retval = map_convert_wms_to_wms(tilex, tiley, zoom, repo->url);
1351             break;
1352
1353         default:
1354             retval = g_strdup(repo->url);
1355             break;
1356     }
1357     vprintf("%s(): return \"%s\"\n", __PRETTY_FUNCTION__, retval);
1358     return retval;
1359 }
1360
1361 static gboolean
1362 mapdb_initiate_update_banner_idle()
1363 {
1364     if(!_download_banner && _num_downloads != _curr_download)
1365     {
1366         _download_banner = hildon_banner_show_progress(
1367                 _window, NULL, _("Processing Maps"));
1368         /* If we're not connected, then hide the banner immediately.  It will
1369          * be unhidden if/when we're connected. */
1370         if(!_conic_is_connected)
1371             gtk_widget_hide(_download_banner);
1372     }
1373     return FALSE;
1374 }
1375
1376 /**
1377  * Initiate a download of the given xyz coordinates using the given buffer
1378  * as the URL.  If the map already exists on disk, or if we are already
1379  * downloading the map, then this method does nothing.
1380  */
1381 gboolean
1382 mapdb_initiate_update(RepoData *repo, gint zoom, gint tilex, gint tiley,
1383         gint update_type, gint batch_id, gint priority,
1384         ThreadLatch *refresh_latch)
1385 {
1386     MapUpdateTask *mut;
1387     MapUpdateTask *old_mut;
1388     gboolean is_replacing = FALSE;
1389     vprintf("%s(%s, %d, %d, %d, %d)\n", __PRETTY_FUNCTION__,
1390             repo->name, zoom, tilex, tiley, update_type);
1391
1392     mut = g_slice_new(MapUpdateTask);
1393     if(!mut)
1394     {
1395         /* Could not allocate memory. */
1396         g_printerr("Out of memory in allocation of update task #%d\n",
1397                 _num_downloads + 1);
1398         return FALSE;
1399     }
1400     mut->zoom = zoom;
1401     mut->tilex = tilex;
1402     mut->tiley = tiley;
1403     mut->update_type = update_type;
1404     mut->layer_level = repo->layer_level;
1405
1406     /* Lock the mutex if this is an auto-update. */
1407     if(update_type == MAP_UPDATE_AUTO)
1408         g_mutex_lock(_mut_priority_mutex);
1409     if(NULL != (old_mut = g_hash_table_lookup(_mut_exists_table, mut)))
1410     {
1411         /* Check if new mut is in a newer batch that the old mut.
1412          * We use vfs_result to indicate a MUT that is already in the process
1413          * of being downloaded. */
1414         if(old_mut->batch_id < batch_id && old_mut->vfs_result < 0)
1415         {
1416             /* It is, so remove the old one so we can re-add this one. */
1417             g_hash_table_remove(_mut_exists_table, old_mut);
1418             g_tree_remove(_mut_priority_tree, old_mut);
1419             g_slice_free(MapUpdateTask, old_mut);
1420             is_replacing = TRUE;
1421         }
1422         else
1423         {
1424             /* It's not, so just ignore it. */
1425             if(update_type == MAP_UPDATE_AUTO)
1426                 g_mutex_unlock(_mut_priority_mutex);
1427             g_slice_free(MapUpdateTask, mut);
1428             vprintf("%s(): return FALSE (1)\n", __PRETTY_FUNCTION__);
1429             return FALSE;
1430         }
1431     }
1432
1433     g_hash_table_insert(_mut_exists_table, mut, mut);
1434
1435     mut->repo = repo;
1436     mut->refresh_latch = refresh_latch;
1437     mut->priority = priority;
1438     mut->batch_id = batch_id;
1439     mut->pixbuf = NULL;
1440     mut->vfs_result = -1;
1441
1442     g_tree_insert(_mut_priority_tree, mut, mut);
1443
1444     /* Unlock the mutex if this is an auto-update. */
1445     if(update_type == MAP_UPDATE_AUTO)
1446         g_mutex_unlock(_mut_priority_mutex);
1447
1448     if(!is_replacing)
1449     {
1450         /* Increment download count and (possibly) display banner. */
1451         if(++_num_downloads == 20 && !_download_banner)
1452             g_idle_add((GSourceFunc)mapdb_initiate_update_banner_idle, NULL);
1453
1454         /* This doesn't need to be thread-safe.  Extras in the pool don't
1455          * really make a difference. */
1456         if(g_thread_pool_get_num_threads(_mut_thread_pool)
1457                 < g_thread_pool_get_max_threads(_mut_thread_pool))
1458             g_thread_pool_push(_mut_thread_pool, (gpointer)1, NULL);
1459     }
1460
1461     vprintf("%s(): return FALSE (2)\n", __PRETTY_FUNCTION__);
1462     return FALSE;
1463 }
1464
1465 static gboolean
1466 get_next_mut(gpointer key, gpointer value, MapUpdateTask **data)
1467 {
1468     *data = key;
1469     return TRUE;
1470 }
1471
1472 gboolean
1473 thread_proc_mut()
1474 {
1475     printf("%s()\n", __PRETTY_FUNCTION__);
1476
1477     /* Make sure things are inititalized. */
1478     gnome_vfs_init();
1479
1480     while(conic_ensure_connected())
1481     {
1482         gint retries;
1483         gboolean refresh_sent = FALSE, layer_tile;
1484         MapUpdateTask *mut = NULL;
1485
1486         /* Get the next MUT from the mut tree. */
1487         g_mutex_lock(_mut_priority_mutex);
1488         g_tree_foreach(_mut_priority_tree, (GTraverseFunc)get_next_mut, &mut);
1489         if(!mut)
1490         {
1491             /* No more MUTs to process.  Return. */
1492             g_mutex_unlock(_mut_priority_mutex);
1493             return FALSE;
1494         }
1495         /* Mark this MUT as "in-progress". */
1496         mut->vfs_result = GNOME_VFS_NUM_ERRORS;
1497         g_tree_remove(_mut_priority_tree, mut);
1498         g_mutex_unlock(_mut_priority_mutex);
1499
1500         printf("%s(%s, %d, %d, %d)\n", __PRETTY_FUNCTION__,
1501                 mut->repo->name, mut->zoom, mut->tilex, mut->tiley);
1502
1503         layer_tile = mut->repo != _curr_repo && repo_is_layer (_curr_repo, mut->repo);
1504
1505         if (mut->repo != _curr_repo && !layer_tile)
1506         {
1507             /* Do nothing, except report that there is no error. */
1508             mut->vfs_result = GNOME_VFS_OK;
1509         }
1510         else if(mut->update_type == MAP_UPDATE_DELETE)
1511         {
1512             /* Easy - just delete the entry from the database.  We don't care
1513              * about failures (sorry). */
1514             if(mut->repo->db)
1515                 mapdb_delete(mut->repo, mut->zoom, mut->tilex, mut->tiley);
1516
1517             /* Report that there is no error. */
1518             mut->vfs_result = GNOME_VFS_OK;
1519         }
1520         else for(retries = mut->repo->layer_level ? 1 : INITIAL_DOWNLOAD_RETRIES; retries > 0; --retries)
1521         {
1522             gboolean exists = FALSE;
1523             gchar *src_url;
1524             gchar *bytes;
1525             gint size;
1526             GdkPixbufLoader *loader;
1527             RepoData *repo;
1528             gint zoom, tilex, tiley;
1529             GError *error = NULL;
1530
1531 #ifdef MAPDB_SQLITE
1532             /* First check for existence. */
1533             exists = mut->repo->db
1534                 ? mapdb_exists(mut->repo, mut->zoom,
1535                         mut->tilex, mut->tiley)
1536                 : FALSE;
1537             if(exists && mut->update_type == MAP_UPDATE_ADD)
1538             {
1539                 /* Map already exists, and we're not going to overwrite. */
1540                 /* Report that there is no error. */
1541                 mut->vfs_result = GNOME_VFS_OK;
1542                 break;
1543             }
1544 #else
1545             /* First check for existence. */
1546             if(mut->update_type == MAP_UPDATE_ADD)
1547             {
1548                 /* We don't want to overwrite, so check for existence. */
1549                 /* Map already exists, and we're not going to overwrite. */
1550                 if(mapdb_exists(mut->repo, mut->zoom,
1551                             mut->tilex,mut->tiley))
1552                 {
1553                     /* Report that there is no error. */
1554                     mut->vfs_result = GNOME_VFS_OK;
1555                     break;
1556                 }
1557             }
1558 #endif
1559
1560             /* First, construct the URL from which we will get the data. */
1561             src_url = map_construct_url(mut->repo, mut->zoom,
1562                     mut->tilex, mut->tiley);
1563
1564             /* Now, attempt to read the entire contents of the URL. */
1565             mut->vfs_result = gnome_vfs_read_entire_file(
1566                     src_url, &size, &bytes);
1567             g_free(src_url);
1568             if(mut->vfs_result != GNOME_VFS_OK || !bytes)
1569             {
1570                 /* Try again. */
1571                 printf("Error reading URL: %s\n",
1572                         gnome_vfs_result_to_string(mut->vfs_result));
1573                 g_free(bytes);
1574                 continue;
1575             }
1576             /* usleep(100000); DEBUG */
1577
1578             /* Attempt to parse the bytes into a pixbuf. */
1579             loader = gdk_pixbuf_loader_new();
1580             gdk_pixbuf_loader_write(loader, bytes, size, NULL);
1581             gdk_pixbuf_loader_close(loader, &error);
1582             if(error || (NULL == (mut->pixbuf = g_object_ref(
1583                         gdk_pixbuf_loader_get_pixbuf(loader)))))
1584             {
1585                 mut->vfs_result = GNOME_VFS_NUM_ERRORS;
1586                 if(mut->pixbuf)
1587                     g_object_unref(mut->pixbuf);
1588                 mut->pixbuf = NULL;
1589                 g_free(bytes);
1590                 g_object_unref(loader);
1591                 printf("Error parsing pixbuf: %s\n",
1592                         error ? error->message : "?");
1593                 continue;
1594             }
1595             g_object_unref(loader);
1596
1597             /* attach timestamp with loaded pixbuf */
1598             {
1599                 gchar* new_bytes;
1600                 gsize new_size;
1601                 GError* error = NULL;
1602                 char ts_val[12];
1603
1604                 sprintf (ts_val, "%u", (unsigned int)time (NULL));
1605
1606                 /* update bytes with new, timestamped pixbuf */
1607                 if (gdk_pixbuf_save_to_buffer (mut->pixbuf, &new_bytes, &new_size, "png", &error, layer_timestamp_key, ts_val, NULL))
1608                 {
1609                     g_free (bytes);
1610                     bytes = new_bytes;
1611                     size = new_size;
1612                 }
1613             }
1614
1615             /* Copy database-relevant mut data before we release it. */
1616             repo = mut->repo;
1617             zoom = mut->zoom;
1618             tilex = mut->tilex;
1619             tiley = mut->tiley;
1620
1621             /* Pass the mut to the GTK thread for redrawing, but only if a
1622              * redraw isn't already in the pipeline. */
1623             if(mut->refresh_latch)
1624             {
1625                 /* Wait until the latch is open. */
1626                 g_mutex_lock(mut->refresh_latch->mutex);
1627                 while(!mut->refresh_latch->is_open)
1628                 {
1629                     g_cond_wait(mut->refresh_latch->cond,
1630                             mut->refresh_latch->mutex);
1631                 }
1632                 /* Latch is open.  Decrement the number of waiters and
1633                  * check if we're the last waiter to run. */
1634                 if(mut->refresh_latch->is_done_adding_tasks)
1635                 {
1636                     if(++mut->refresh_latch->num_done
1637                                 == mut->refresh_latch->num_tasks)
1638                     {
1639                         /* Last waiter.  Free the latch resources. */
1640                         g_mutex_unlock(mut->refresh_latch->mutex);
1641                         g_cond_free(mut->refresh_latch->cond);
1642                         g_mutex_free(mut->refresh_latch->mutex);
1643                         g_slice_free(ThreadLatch, mut->refresh_latch);
1644                         mut->refresh_latch = NULL;
1645                     }
1646                     else
1647                     {
1648                         /* Not the last waiter. Signal the next waiter.*/
1649                         g_cond_signal(mut->refresh_latch->cond);
1650                         g_mutex_unlock(mut->refresh_latch->mutex);
1651                     }
1652                 }
1653                 else
1654                     g_mutex_unlock(mut->refresh_latch->mutex);
1655             }
1656
1657             g_idle_add_full(G_PRIORITY_HIGH_IDLE,
1658                     (GSourceFunc)map_download_refresh_idle, mut, NULL);
1659             refresh_sent = TRUE;
1660
1661             /* DO NOT USE mut FROM THIS POINT ON. */
1662
1663             /* Also attempt to add to the database. */
1664             mapdb_update(exists, repo, zoom,
1665                     tilex, tiley, bytes, size);
1666
1667             /* Success! */
1668             g_free(bytes);
1669             break;
1670         }
1671
1672         if(!refresh_sent)
1673             g_idle_add_full(G_PRIORITY_HIGH_IDLE,
1674                     (GSourceFunc)map_download_refresh_idle, mut, NULL);
1675     }
1676
1677     vprintf("%s(): return FALSE\n", __PRETTY_FUNCTION__);
1678     return FALSE;
1679 }
1680
1681 guint
1682 mut_exists_hashfunc(const MapUpdateTask *a)
1683 {
1684     gint sum = a->zoom + a->tilex + a->tiley + a->update_type + a->layer_level;
1685     return g_int_hash(&sum);
1686 }
1687
1688 gboolean
1689 mut_exists_equalfunc(const MapUpdateTask *a, const MapUpdateTask *b)
1690 {
1691     return (a->tilex == b->tilex
1692             && a->tiley == b->tiley
1693             && a->zoom == b->zoom
1694             && a->update_type == b->update_type
1695             && a->layer_level == b->layer_level);
1696 }
1697
1698 gint
1699 mut_priority_comparefunc(const MapUpdateTask *a, const MapUpdateTask *b)
1700 {
1701     /* The update_type enum is sorted in order of ascending priority. */
1702     gint diff = (b->update_type - a->update_type);
1703     if(diff)
1704         return diff;
1705     diff = (b->batch_id - a->batch_id); /* More recent ones first. */
1706     if(diff)
1707         return diff;
1708     diff = (a->priority - b->priority); /* Lower priority numbers first. */
1709     if(diff)
1710         return diff;
1711     diff = (a->layer_level - b->layer_level); /* Lower layers first. */
1712     if(diff)
1713         return diff;
1714
1715     /* At this point, we don't care, so just pick arbitrarily. */
1716     diff = (a->tilex - b->tilex);
1717     if(diff)
1718         return diff;
1719     diff = (a->tiley - b->tiley);
1720     if(diff)
1721         return diff;
1722     return (a->zoom - b->zoom);
1723 }
1724
1725 static gboolean
1726 repoman_dialog_select(GtkWidget *widget, RepoManInfo *rmi)
1727 {
1728     printf("%s()\n", __PRETTY_FUNCTION__);
1729     gint curr_index = gtk_combo_box_get_active(GTK_COMBO_BOX(rmi->cmb_repos));
1730     gtk_notebook_set_current_page(GTK_NOTEBOOK(rmi->notebook), curr_index);
1731     vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__);
1732     return TRUE;
1733 }
1734
1735 static gboolean
1736 repoman_dialog_browse(GtkWidget *widget, BrowseInfo *browse_info)
1737 {
1738     GtkWidget *dialog;
1739     gchar *basename;
1740     printf("%s()\n", __PRETTY_FUNCTION__);
1741
1742     dialog = GTK_WIDGET(
1743             hildon_file_chooser_dialog_new(GTK_WINDOW(browse_info->dialog),
1744             GTK_FILE_CHOOSER_ACTION_SAVE));
1745
1746     gtk_file_chooser_set_uri(GTK_FILE_CHOOSER(dialog),
1747             gtk_entry_get_text(GTK_ENTRY(browse_info->txt)));
1748
1749     /* Work around a bug in HildonFileChooserDialog. */
1750     basename = g_path_get_basename(
1751             gtk_entry_get_text(GTK_ENTRY(browse_info->txt)));
1752     g_object_set(G_OBJECT(dialog), "autonaming", FALSE, NULL);
1753     gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), basename);
1754
1755     if(GTK_RESPONSE_OK == gtk_dialog_run(GTK_DIALOG(dialog)))
1756     {
1757         gchar *filename = gtk_file_chooser_get_filename(
1758                 GTK_FILE_CHOOSER(dialog));
1759         gtk_entry_set_text(GTK_ENTRY(browse_info->txt), filename);
1760         g_free(filename);
1761     }
1762
1763     gtk_widget_destroy(dialog);
1764
1765     vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__);
1766     return TRUE;
1767 }
1768
1769 static gboolean
1770 repoman_compact_complete_idle(CompactInfo *ci)
1771 {
1772     printf("%s()\n", __PRETTY_FUNCTION__);
1773
1774     gtk_widget_destroy(GTK_WIDGET(ci->banner));
1775     popup_error(ci->dialog, ci->status_msg);
1776     gtk_widget_destroy(ci->dialog);
1777     g_free(ci);
1778
1779     vprintf("%s(): return FALSE\n", __PRETTY_FUNCTION__);
1780     return FALSE;
1781 }
1782
1783 static void
1784 thread_repoman_compact(CompactInfo *ci)
1785 {
1786     GDBM_FILE db;
1787     printf("%s()\n", __PRETTY_FUNCTION__);
1788
1789     if(!(db = gdbm_open((gchar*)ci->db_filename, 0, GDBM_WRITER | GDBM_FAST,
1790                     0644, NULL)))
1791         ci->status_msg = _("Failed to open map database for compacting.");
1792     else
1793     {
1794         if(gdbm_reorganize(db))
1795             ci->status_msg = _("An error occurred while trying to "
1796                         "compact the database.");
1797         else
1798             ci->status_msg = _("Successfully compacted database.");
1799         gdbm_close(db);
1800     }
1801
1802     g_idle_add((GSourceFunc)repoman_compact_complete_idle, ci);
1803
1804     vprintf("%s(): return\n", __PRETTY_FUNCTION__);
1805 }
1806
1807 static void
1808 repoman_dialog_compact(GtkWidget *widget, BrowseInfo *browse_info)
1809 {
1810     CompactInfo *ci;
1811     GtkWidget *sw;
1812     printf("%s()\n", __PRETTY_FUNCTION__);
1813
1814     ci = g_new0(CompactInfo, 1);
1815
1816     ci->dialog = gtk_dialog_new_with_buttons(_("Compact Database"),
1817             GTK_WINDOW(browse_info->dialog), GTK_DIALOG_MODAL,
1818             GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
1819             GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
1820             NULL);
1821
1822     sw = gtk_scrolled_window_new (NULL, NULL);
1823     gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (sw),
1824             GTK_SHADOW_ETCHED_IN);
1825     gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
1826             GTK_POLICY_NEVER,
1827             GTK_POLICY_ALWAYS);
1828     gtk_box_pack_start(GTK_BOX(GTK_DIALOG(ci->dialog)->vbox),
1829             sw, TRUE, TRUE, 0);
1830
1831     gtk_container_add(GTK_CONTAINER(sw), ci->txt = gtk_text_view_new());
1832     gtk_text_view_set_editable(GTK_TEXT_VIEW(ci->txt), FALSE);
1833     gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(ci->txt), FALSE);
1834     gtk_text_buffer_set_text(
1835             gtk_text_view_get_buffer(GTK_TEXT_VIEW(ci->txt)),
1836             _("Generally, deleted maps create an empty space in the "
1837                 "database that is later reused when downloading new maps.  "
1838                 "Compacting the database reorganizes it such that all "
1839                 "that blank space is eliminated.  This is the only way "
1840                 "that the size of the database can decrease.\n"
1841                 "This reorganization requires creating a new file and "
1842                 "inserting all the maps in the old database file into the "
1843                 "new file. The new file is then renamed to the same name "
1844                 "as the old file and dbf is updated to contain all the "
1845                 "correct information about the new file.  Note that this "
1846                 "can require free space on disk of an amount up to the size "
1847                 "of the map database.\n"
1848                 "This process may take several minutes, especially if "
1849                 "your map database is large.  As a rough estimate, you can "
1850                 "expect to wait approximately 2-5 seconds per megabyte of "
1851                 "map data (34-85 minutes per gigabyte).  There is no progress "
1852                 "indicator, although you can watch the new file grow in any "
1853                 "file manager.  Do not attempt to close Maemo Mapper while "
1854                 "the compacting operation is in progress."),
1855             -1);
1856     {
1857         GtkTextIter iter;
1858         gtk_text_buffer_get_iter_at_offset(
1859                 gtk_text_view_get_buffer(GTK_TEXT_VIEW(ci->txt)),
1860                 &iter, 0);
1861         gtk_text_view_scroll_to_iter(GTK_TEXT_VIEW(ci->txt),
1862                 &iter, 0.0, FALSE, 0, 0);
1863     }
1864
1865     gtk_widget_set_size_request(GTK_WIDGET(sw), 600, 200);
1866     gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(ci->txt), GTK_WRAP_WORD);
1867
1868     gtk_widget_show_all(ci->dialog);
1869
1870     if(GTK_RESPONSE_ACCEPT == gtk_dialog_run(GTK_DIALOG(ci->dialog)))
1871     {
1872         gtk_widget_set_sensitive(GTK_DIALOG(ci->dialog)->action_area, FALSE);
1873         ci->db_filename = gtk_entry_get_text(GTK_ENTRY(browse_info->txt));
1874         ci->banner = hildon_banner_show_animation(ci->dialog, NULL,
1875                 _("Compacting database..."));
1876
1877         g_thread_create((GThreadFunc)thread_repoman_compact, ci, FALSE, NULL);
1878     }
1879     else
1880     {
1881         gtk_widget_destroy(ci->dialog);
1882     }
1883
1884     vprintf("%s(): return\n", __PRETTY_FUNCTION__);
1885 }
1886
1887 static gboolean
1888 repoman_dialog_rename(GtkWidget *widget, RepoManInfo *rmi)
1889 {
1890     static GtkWidget *hbox = NULL;
1891     static GtkWidget *label = NULL;
1892     static GtkWidget *txt_name = NULL;
1893     static GtkWidget *dialog = NULL;
1894     printf("%s()\n", __PRETTY_FUNCTION__);
1895
1896     if(dialog == NULL)
1897     {
1898         dialog = gtk_dialog_new_with_buttons(_("New Name"),
1899                 GTK_WINDOW(rmi->dialog), GTK_DIALOG_MODAL,
1900                 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
1901                 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
1902                 NULL);
1903
1904         gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox),
1905                 hbox = gtk_hbox_new(FALSE, 4), FALSE, FALSE, 4);
1906
1907         gtk_box_pack_start(GTK_BOX(hbox),
1908                 label = gtk_label_new(_("Name")),
1909                 FALSE, FALSE, 0);
1910         gtk_box_pack_start(GTK_BOX(hbox),
1911                 txt_name = gtk_entry_new(),
1912                 TRUE, TRUE, 0);
1913     }
1914
1915     {
1916         gint active = gtk_combo_box_get_active(GTK_COMBO_BOX(rmi->cmb_repos));
1917         RepoEditInfo *rei = g_list_nth_data(rmi->repo_edits, active);
1918         gtk_entry_set_text(GTK_ENTRY(txt_name), rei->name);
1919     }
1920
1921     gtk_widget_show_all(dialog);
1922
1923     while(GTK_RESPONSE_ACCEPT == gtk_dialog_run(GTK_DIALOG(dialog)))
1924     {
1925         gint active = gtk_combo_box_get_active(GTK_COMBO_BOX(rmi->cmb_repos));
1926         RepoEditInfo *rei = g_list_nth_data(rmi->repo_edits, active);
1927         g_free(rei->name);
1928         rei->name = g_strdup(gtk_entry_get_text(GTK_ENTRY(txt_name)));
1929         gtk_combo_box_insert_text(GTK_COMBO_BOX(rmi->cmb_repos),
1930                 active, g_strdup(rei->name));
1931         gtk_combo_box_set_active(GTK_COMBO_BOX(rmi->cmb_repos), active);
1932         gtk_combo_box_remove_text(GTK_COMBO_BOX(rmi->cmb_repos), active + 1);
1933         break;
1934     }
1935
1936     gtk_widget_hide(dialog);
1937
1938     vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__);
1939     return TRUE;
1940 }
1941
1942 static void
1943 repoman_delete(RepoManInfo *rmi, gint index)
1944 {
1945     gtk_combo_box_remove_text(GTK_COMBO_BOX(rmi->cmb_repos), index);
1946     gtk_notebook_remove_page(GTK_NOTEBOOK(rmi->notebook), index);
1947     rmi->repo_edits = g_list_remove_link(
1948             rmi->repo_edits,
1949             g_list_nth(rmi->repo_edits, index));
1950 }
1951
1952 static gboolean
1953 repoman_dialog_delete(GtkWidget *widget, RepoManInfo *rmi, gint index)
1954 {
1955     gchar buffer[100];
1956     GtkWidget *confirm;
1957     printf("%s()\n", __PRETTY_FUNCTION__);
1958
1959     if(gtk_tree_model_iter_n_children(GTK_TREE_MODEL(
1960                     gtk_combo_box_get_model(GTK_COMBO_BOX(rmi->cmb_repos))),
1961                                 NULL) <= 1)
1962     {
1963         popup_error(rmi->dialog,
1964                 _("Cannot delete the last repository - there must be at"
1965                 " lease one repository."));
1966         vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__);
1967         return TRUE;
1968     }
1969
1970     snprintf(buffer, sizeof(buffer), "%s:\n%s\n",
1971             _("Confirm delete of repository"),
1972             gtk_combo_box_get_active_text(GTK_COMBO_BOX(rmi->cmb_repos)));
1973
1974     confirm = hildon_note_new_confirmation(GTK_WINDOW(rmi->dialog),buffer);
1975
1976     if(GTK_RESPONSE_OK == gtk_dialog_run(GTK_DIALOG(confirm)))
1977     {
1978         gint active = gtk_combo_box_get_active(GTK_COMBO_BOX(rmi->cmb_repos));
1979         repoman_delete(rmi, active);
1980         gtk_combo_box_set_active(GTK_COMBO_BOX(rmi->cmb_repos),
1981                 MAX(0, index - 1));
1982     }
1983
1984     gtk_widget_destroy(confirm);
1985
1986     return TRUE;
1987 }
1988
1989 static RepoEditInfo*
1990 repoman_dialog_add_repo(RepoManInfo *rmi, gchar *name)
1991 {
1992     GtkWidget *vbox;
1993     GtkWidget *table;
1994     GtkWidget *label;
1995     GtkWidget *hbox;
1996     RepoEditInfo *rei = g_new(RepoEditInfo, 1);
1997     printf("%s(%s)\n", __PRETTY_FUNCTION__, name);
1998
1999     rei->name = name;
2000
2001     /* Maps page. */
2002     gtk_notebook_append_page(GTK_NOTEBOOK(rmi->notebook),
2003             vbox = gtk_vbox_new(FALSE, 4),
2004             gtk_label_new(name));
2005
2006     /* Prevent destruction of notebook page, because the destruction causes
2007      * a seg fault (!?!?) */
2008     gtk_object_ref(GTK_OBJECT(vbox));
2009
2010     gtk_box_pack_start(GTK_BOX(vbox),
2011             table = gtk_table_new(2, 2, FALSE),
2012             FALSE, FALSE, 0);
2013     /* Map download URI. */
2014     gtk_table_attach(GTK_TABLE(table),
2015             label = gtk_label_new(_("URL Format")),
2016             0, 1, 0, 1, GTK_FILL, 0, 2, 0);
2017     gtk_misc_set_alignment(GTK_MISC(label), 1.f, 0.5f);
2018     gtk_table_attach(GTK_TABLE(table),
2019             rei->txt_url = gtk_entry_new(),
2020             1, 2, 0, 1, GTK_EXPAND | GTK_FILL, 0, 2, 0);
2021
2022     /* Map Directory. */
2023     gtk_table_attach(GTK_TABLE(table),
2024             label = gtk_label_new(_("Cache DB")),
2025             0, 1, 1, 2, GTK_FILL, 0, 2, 0);
2026     gtk_misc_set_alignment(GTK_MISC(label), 1.f, 0.5f);
2027     gtk_table_attach(GTK_TABLE(table),
2028             hbox = gtk_hbox_new(FALSE, 4),
2029             1, 2, 1, 2, GTK_EXPAND | GTK_FILL, 0, 2, 0);
2030     gtk_box_pack_start(GTK_BOX(hbox),
2031             rei->txt_db_filename = gtk_entry_new(),
2032             TRUE, TRUE, 0);
2033     gtk_box_pack_start(GTK_BOX(hbox),
2034             rei->btn_browse = gtk_button_new_with_label(_("Browse...")),
2035             FALSE, FALSE, 0);
2036     gtk_box_pack_start(GTK_BOX(hbox),
2037             rei->btn_compact = gtk_button_new_with_label(_("Compact...")),
2038             FALSE, FALSE, 0);
2039
2040     /* Initialize cache dir */
2041     {
2042         gchar buffer[BUFFER_SIZE];
2043         snprintf(buffer, sizeof(buffer), "%s.db", name);
2044         gchar *db_base = gnome_vfs_expand_initial_tilde(
2045                 REPO_DEFAULT_CACHE_BASE);
2046         gchar *db_filename = gnome_vfs_uri_make_full_from_relative(
2047                 db_base, buffer);
2048         gtk_entry_set_text(GTK_ENTRY(rei->txt_db_filename), db_filename);
2049         g_free(db_filename);
2050         g_free(db_base);
2051     }
2052
2053     gtk_box_pack_start(GTK_BOX(vbox),
2054             table = gtk_table_new(3, 2, FALSE),
2055             FALSE, FALSE, 0);
2056
2057     /* Download Zoom Steps. */
2058     gtk_table_attach(GTK_TABLE(table),
2059             label = gtk_label_new(_("Download Zoom Steps")),
2060             0, 1, 0, 1, GTK_FILL, 0, 2, 0);
2061     gtk_misc_set_alignment(GTK_MISC(label), 1.f, 0.5f);
2062     gtk_table_attach(GTK_TABLE(table),
2063             label = gtk_alignment_new(0.f, 0.5f, 0.f, 0.f),
2064             1, 2, 0, 1, GTK_FILL, 0, 2, 0);
2065     gtk_container_add(GTK_CONTAINER(label),
2066             rei->num_dl_zoom_steps = hildon_controlbar_new());
2067     hildon_controlbar_set_range(
2068             HILDON_CONTROLBAR(rei->num_dl_zoom_steps), 1, 4);
2069     hildon_controlbar_set_value(HILDON_CONTROLBAR(rei->num_dl_zoom_steps),
2070             REPO_DEFAULT_DL_ZOOM_STEPS);
2071     force_min_visible_bars(HILDON_CONTROLBAR(rei->num_dl_zoom_steps), 1);
2072
2073     /* Download Zoom Steps. */
2074     gtk_table_attach(GTK_TABLE(table),
2075             label = gtk_label_new(_("View Zoom Steps")),
2076             0, 1, 1, 2, GTK_FILL, 0, 2, 0);
2077     gtk_misc_set_alignment(GTK_MISC(label), 1.f, 0.5f);
2078     gtk_table_attach(GTK_TABLE(table),
2079             label = gtk_alignment_new(0.f, 0.5f, 0.f, 0.f),
2080             1, 2, 1, 2, GTK_FILL, 0, 2, 0);
2081     gtk_container_add(GTK_CONTAINER(label),
2082             rei->num_view_zoom_steps = hildon_controlbar_new());
2083     hildon_controlbar_set_range(
2084             HILDON_CONTROLBAR(rei->num_view_zoom_steps), 1, 4);
2085     hildon_controlbar_set_value(HILDON_CONTROLBAR(rei->num_view_zoom_steps),
2086             REPO_DEFAULT_VIEW_ZOOM_STEPS);
2087     force_min_visible_bars(HILDON_CONTROLBAR(rei->num_view_zoom_steps), 1);
2088
2089     gtk_table_attach(GTK_TABLE(table),
2090             label = gtk_vseparator_new(),
2091             2, 3, 0, 2, GTK_FILL, GTK_FILL, 4, 0);
2092
2093     /* Double-size. */
2094     gtk_table_attach(GTK_TABLE(table),
2095             rei->chk_double_size = gtk_check_button_new_with_label(
2096                 _("Double Pixels")),
2097             3, 4, 0, 1, GTK_FILL, GTK_FILL, 0, 0);
2098     gtk_toggle_button_set_active(
2099             GTK_TOGGLE_BUTTON(rei->chk_double_size), FALSE);
2100
2101     /* Next-able */
2102     gtk_table_attach(GTK_TABLE(table),
2103             rei->chk_nextable = gtk_check_button_new_with_label(
2104                 _("Next-able")),
2105             3, 4, 1, 2, GTK_FILL, GTK_FILL, 0, 0);
2106     gtk_toggle_button_set_active(
2107             GTK_TOGGLE_BUTTON(rei->chk_nextable), TRUE);
2108
2109     /* Downloadable Zoom Levels. */
2110     gtk_table_attach(GTK_TABLE(table),
2111             label = gtk_label_new(_("Downloadable Zooms:")),
2112             0, 1, 2, 3, GTK_FILL, 0, 2, 0);
2113     gtk_misc_set_alignment(GTK_MISC(label), 1.f, 0.5f);
2114     gtk_table_attach(GTK_TABLE(table),
2115             label = gtk_alignment_new(0.f, 0.5f, 0.f, 0.f),
2116             1, 4, 2, 3, GTK_FILL, 0, 2, 0);
2117     gtk_container_add(GTK_CONTAINER(label),
2118             hbox = gtk_hbox_new(FALSE, 4));
2119     gtk_box_pack_start(GTK_BOX(hbox),
2120             label = gtk_label_new(_("Min.")),
2121             TRUE, TRUE, 0);
2122     gtk_misc_set_alignment(GTK_MISC(label), 1.f, 0.5f);
2123     gtk_box_pack_start(GTK_BOX(hbox),
2124             rei->num_min_zoom = hildon_number_editor_new(MIN_ZOOM, MAX_ZOOM),
2125             FALSE, FALSE, 0);
2126     hildon_number_editor_set_value(HILDON_NUMBER_EDITOR(rei->num_min_zoom), 4);
2127     gtk_box_pack_start(GTK_BOX(hbox),
2128             label = gtk_label_new(""),
2129             TRUE, TRUE, 4);
2130     gtk_box_pack_start(GTK_BOX(hbox),
2131             label = gtk_label_new(_("Max.")),
2132             TRUE, TRUE, 0);
2133     gtk_misc_set_alignment(GTK_MISC(label), 1.f, 0.5f);
2134     gtk_box_pack_start(GTK_BOX(hbox),
2135             rei->num_max_zoom = hildon_number_editor_new(MIN_ZOOM, MAX_ZOOM),
2136             FALSE, FALSE, 0);
2137     hildon_number_editor_set_value(HILDON_NUMBER_EDITOR(rei->num_max_zoom),20);
2138
2139     rmi->repo_edits = g_list_append(rmi->repo_edits, rei);
2140
2141     /* Connect signals. */
2142     rei->browse_info.dialog = rmi->dialog;
2143     rei->browse_info.txt = rei->txt_db_filename;
2144     g_signal_connect(G_OBJECT(rei->btn_browse), "clicked",
2145                       G_CALLBACK(repoman_dialog_browse),
2146                       &rei->browse_info);
2147     g_signal_connect(G_OBJECT(rei->btn_compact), "clicked",
2148                       G_CALLBACK(repoman_dialog_compact),
2149                       &rei->browse_info);
2150
2151     gtk_widget_show_all(vbox);
2152
2153     gtk_combo_box_append_text(GTK_COMBO_BOX(rmi->cmb_repos), name);
2154     gtk_combo_box_set_active(GTK_COMBO_BOX(rmi->cmb_repos),
2155             gtk_tree_model_iter_n_children(GTK_TREE_MODEL(
2156                     gtk_combo_box_get_model(GTK_COMBO_BOX(rmi->cmb_repos))),
2157                 NULL) - 1);
2158
2159     /* newly created repos keep this NULL in rei, indicating
2160        that layes cannot be added so far */
2161     rei->repo = NULL;
2162
2163     vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__);
2164     return rei;
2165 }
2166
2167 static gboolean
2168 repoman_dialog_new(GtkWidget *widget, RepoManInfo *rmi)
2169 {
2170     static GtkWidget *hbox = NULL;
2171     static GtkWidget *label = NULL;
2172     static GtkWidget *txt_name = NULL;
2173     static GtkWidget *dialog = NULL;
2174     printf("%s()\n", __PRETTY_FUNCTION__);
2175
2176     if(dialog == NULL)
2177     {
2178         dialog = gtk_dialog_new_with_buttons(_("New Repository"),
2179                 GTK_WINDOW(rmi->dialog), GTK_DIALOG_MODAL,
2180                 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
2181                 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
2182                 NULL);
2183
2184         gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox),
2185                 hbox = gtk_hbox_new(FALSE, 4), FALSE, FALSE, 4);
2186
2187         gtk_box_pack_start(GTK_BOX(hbox),
2188                 label = gtk_label_new(_("Name")),
2189                 FALSE, FALSE, 0);
2190         gtk_box_pack_start(GTK_BOX(hbox),
2191                 txt_name = gtk_entry_new(),
2192                 TRUE, TRUE, 0);
2193     }
2194
2195     gtk_entry_set_text(GTK_ENTRY(txt_name), "");
2196
2197     gtk_widget_show_all(dialog);
2198
2199     while(GTK_RESPONSE_ACCEPT == gtk_dialog_run(GTK_DIALOG(dialog)))
2200     {
2201         repoman_dialog_add_repo(rmi,
2202                 g_strdup(gtk_entry_get_text(GTK_ENTRY(txt_name))));
2203         break;
2204     }
2205
2206     gtk_widget_hide(dialog);
2207
2208     vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__);
2209     return TRUE;
2210 }
2211
2212 static gboolean
2213 repoman_reset(GtkWidget *widget, RepoManInfo *rmi)
2214 {
2215     GtkWidget *confirm;
2216     printf("%s()\n", __PRETTY_FUNCTION__);
2217
2218     confirm = hildon_note_new_confirmation(GTK_WINDOW(rmi->dialog),
2219             _("Replace all repositories with the default repository?"));
2220
2221     if(GTK_RESPONSE_OK == gtk_dialog_run(GTK_DIALOG(confirm)))
2222     {
2223         /* First, delete all existing repositories. */
2224         while(rmi->repo_edits)
2225             repoman_delete(rmi, 0);
2226
2227         /* Now, add the default repository. */
2228         repoman_dialog_add_repo(rmi, REPO_DEFAULT_NAME);
2229         gtk_entry_set_text(
2230                 GTK_ENTRY(((RepoEditInfo*)rmi->repo_edits->data)->txt_url),
2231                 REPO_DEFAULT_MAP_URI);
2232
2233         gtk_combo_box_set_active(GTK_COMBO_BOX(rmi->cmb_repos), 0);
2234     }
2235     gtk_widget_destroy(confirm);
2236
2237     vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__);
2238     return TRUE;
2239 }
2240
2241 gboolean
2242 repoman_download()
2243 {
2244     GtkWidget *confirm;
2245     printf("%s()\n", __PRETTY_FUNCTION__);
2246
2247     confirm = hildon_note_new_confirmation(GTK_WINDOW(_window),
2248             _("Maemo Mapper will now download and add a list of "
2249                 "possibly-duplicate repositories from the internet.  "
2250                 "Continue?"));
2251
2252     if(GTK_RESPONSE_OK == gtk_dialog_run(GTK_DIALOG(confirm)))
2253     {
2254         gchar *bytes;
2255         gchar *head;
2256         gchar *tail;
2257         gint size;
2258         GnomeVFSResult vfs_result;
2259         printf("%s()\n", __PRETTY_FUNCTION__);
2260
2261         /* Get repo config file from www.gnuite.com. */
2262         if(GNOME_VFS_OK != (vfs_result = gnome_vfs_read_entire_file(
2263                     "http://www.gnuite.com/nokia770/maemo-mapper/"
2264                     "repos-with-layers.txt",
2265                     &size, &bytes)))
2266         {
2267             popup_error(_window,
2268                     _("An error occurred while retrieving the repositories.  "
2269                         "The web service may be temporarily down."));
2270             g_printerr("Error while download repositories: %s\n",
2271                     gnome_vfs_result_to_string(vfs_result));
2272         }
2273         /* Parse each line as a reposotory. */
2274         else
2275         {
2276             RepoData *prev_repo = NULL;
2277             menu_maps_remove_repos();
2278             for(head = bytes; head && *head; head = tail)
2279             {
2280                 RepoData *rd;
2281                 tail = strchr(head, '\n');
2282                 *tail++ = '\0';
2283
2284                 rd = settings_parse_repo(head);
2285                 if (rd->layer_level == 0) {
2286                     _repo_list = g_list_append(_repo_list, rd);
2287                 }
2288                 else
2289                     prev_repo->layers = rd;
2290
2291                 prev_repo = rd;
2292             }
2293             g_free(bytes);
2294             menu_maps_add_repos();
2295             settings_save();
2296         }
2297     }
2298     gtk_widget_destroy(confirm);
2299
2300     vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__);
2301     return TRUE;
2302 }
2303
2304
2305 static gint
2306 layer_get_page_index (RepoLayersInfo *rli, GtkTreeIter list_it)
2307 {
2308     GtkTreePath *p1, *p2;
2309     GtkTreeIter p;
2310     gint index = 0;
2311
2312     gtk_tree_model_get_iter_first (GTK_TREE_MODEL (rli->layers_store), &p);
2313
2314     p1 = gtk_tree_model_get_path (GTK_TREE_MODEL (rli->layers_store), &list_it);
2315     p2 = gtk_tree_model_get_path (GTK_TREE_MODEL (rli->layers_store), &p);
2316
2317     while (gtk_tree_path_compare (p1, p2) != 0) {
2318         gtk_tree_path_next (p2);
2319         index++;
2320     }
2321
2322     gtk_tree_path_free (p1);
2323     gtk_tree_path_free (p2);
2324
2325     return index;
2326 }
2327
2328
2329 static gboolean
2330 layer_name_changed (GtkWidget *entry, LayerEditInfo *lei)
2331 {
2332     const gchar* name;
2333     GtkTreeSelection *selection;
2334     GtkTreeIter iter;
2335
2336     printf("%s()\n", __PRETTY_FUNCTION__);
2337
2338     /* take new name  */
2339     name = gtk_entry_get_text (GTK_ENTRY (entry));
2340
2341     /* find selected entry in list view */
2342     selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (lei->rli->layers_list));
2343
2344     if (!gtk_tree_selection_get_selected (selection, NULL, &iter)) {
2345         vprintf("%s(): return FALSE\n", __PRETTY_FUNCTION__);
2346         return FALSE;
2347     }
2348
2349     gtk_list_store_set (lei->rli->layers_store, &iter, 0, name, -1);
2350
2351     vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__);
2352     return TRUE;
2353 }
2354
2355
2356 static gboolean
2357 layer_dialog_browse (GtkWidget *widget, LayerEditInfo *lei)
2358 {
2359     GtkWidget *dialog;
2360     gchar *basename;
2361     printf("%s()\n", __PRETTY_FUNCTION__);
2362
2363     dialog = GTK_WIDGET(
2364             hildon_file_chooser_dialog_new(GTK_WINDOW(lei->rli->dialog),
2365             GTK_FILE_CHOOSER_ACTION_SAVE));
2366
2367     gtk_file_chooser_set_uri(GTK_FILE_CHOOSER(dialog),
2368             gtk_entry_get_text(GTK_ENTRY(lei->txt_db)));
2369
2370     /* Work around a bug in HildonFileChooserDialog. */
2371     basename = g_path_get_basename(
2372             gtk_entry_get_text(GTK_ENTRY(lei->txt_db)));
2373     g_object_set(G_OBJECT(dialog), "autonaming", FALSE, NULL);
2374     gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), basename);
2375
2376     if(GTK_RESPONSE_OK == gtk_dialog_run(GTK_DIALOG(dialog)))
2377     {
2378         gchar *filename = gtk_file_chooser_get_filename(
2379                 GTK_FILE_CHOOSER(dialog));
2380         gtk_entry_set_text(GTK_ENTRY(lei->txt_db), filename);
2381         g_free(filename);
2382     }
2383
2384     gtk_widget_destroy(dialog);
2385
2386     vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__);
2387     return TRUE;
2388 }
2389
2390
2391
2392 static LayerEditInfo*
2393 repoman_layers_add_layer (RepoLayersInfo *rli, gchar* name)
2394 {
2395     LayerEditInfo *lei = g_new (LayerEditInfo, 1);
2396     GtkWidget *vbox;
2397     GtkWidget *hbox2;
2398     GtkWidget *table;
2399     GtkWidget *label;
2400     GtkWidget *btn_browse;
2401     GtkTreeIter layers_iter;
2402     
2403     printf("%s(%s)\n", __PRETTY_FUNCTION__, name);
2404
2405     lei->rli = rli;
2406
2407     rli->layer_edits = g_list_append (rli->layer_edits, lei);
2408
2409     gtk_notebook_append_page (GTK_NOTEBOOK (rli->notebook), vbox = gtk_vbox_new (FALSE, 4),
2410                               gtk_label_new (name));
2411
2412     gtk_box_pack_start (GTK_BOX (vbox), table = gtk_table_new (4, 2, FALSE),
2413                         FALSE, FALSE, 0);
2414
2415     /* Layer name */
2416     gtk_table_attach (GTK_TABLE (table), label = gtk_label_new (_("Name")),
2417                       0, 1, 0, 1, GTK_FILL, 0, 2, 0);
2418     gtk_misc_set_alignment (GTK_MISC (label), 1.f, 0.5f);
2419     gtk_table_attach (GTK_TABLE (table), lei->txt_name = gtk_entry_new (),
2420                       1, 2, 0, 1, GTK_EXPAND | GTK_FILL, 0, 2, 0);
2421     gtk_entry_set_text (GTK_ENTRY (lei->txt_name), name);
2422
2423     /* signals */
2424     g_signal_connect(G_OBJECT(lei->txt_name), "changed", G_CALLBACK(layer_name_changed), lei);
2425
2426     /* URL format */
2427     gtk_table_attach (GTK_TABLE (table), label = gtk_label_new (_("URL")),
2428                       0, 1, 1, 2, GTK_FILL, 0, 2, 0);
2429     gtk_misc_set_alignment (GTK_MISC (label), 1.f, 0.5f);
2430     gtk_table_attach (GTK_TABLE (table), lei->txt_url = gtk_entry_new (),
2431                       1, 2, 1, 2, GTK_EXPAND | GTK_FILL, 0, 2, 0);
2432
2433     /* Map directory */
2434     gtk_table_attach (GTK_TABLE (table), label = gtk_label_new (_("Cache DB")),
2435                       0, 1, 2, 3, GTK_FILL, 0, 2, 0);
2436     gtk_misc_set_alignment (GTK_MISC (label), 1.f, 0.5f);
2437     gtk_table_attach (GTK_TABLE (table), hbox2 = gtk_hbox_new (FALSE, 4),
2438                       1, 2, 2, 3, GTK_EXPAND | GTK_FILL, 0, 2, 0);
2439     gtk_box_pack_start (GTK_BOX (hbox2), lei->txt_db = gtk_entry_new (),
2440                         TRUE, TRUE, 0);
2441     gtk_box_pack_start (GTK_BOX (hbox2), btn_browse = gtk_button_new_with_label (_("Browse...")),
2442                         FALSE, FALSE, 0);
2443
2444     g_signal_connect(G_OBJECT(btn_browse), "clicked", G_CALLBACK(layer_dialog_browse), lei);
2445
2446     /* Autorefresh */
2447     gtk_table_attach (GTK_TABLE (table), label = gtk_label_new (_("Autofetch")),
2448                       0, 1, 3, 4, GTK_FILL, 0, 2, 0);
2449     gtk_misc_set_alignment(GTK_MISC(label), 1.f, 0.5f);
2450     gtk_table_attach (GTK_TABLE (table), hbox2 = gtk_hbox_new (FALSE, 4),
2451                       1, 2, 3, 4, GTK_EXPAND | GTK_FILL, 0, 2, 0);
2452     gtk_box_pack_start (GTK_BOX (hbox2), lei->num_autofetch = hildon_number_editor_new (0, 120),
2453                         FALSE, FALSE, 4);
2454     gtk_box_pack_start (GTK_BOX (hbox2), label = gtk_label_new (_("min.")), FALSE, FALSE, 4);
2455     gtk_misc_set_alignment(GTK_MISC(label), 1.f, 0.5f);
2456
2457     /* Visible */
2458     gtk_box_pack_start (GTK_BOX (vbox), lei->chk_visible = gtk_check_button_new_with_label (_("Layer is visible")),
2459                         FALSE, FALSE, 4);
2460
2461     gtk_widget_show_all (vbox);
2462
2463     /* Side list view with layers */
2464     gtk_list_store_append (rli->layers_store, &layers_iter);
2465     gtk_list_store_set (rli->layers_store, &layers_iter, 0, name, -1);
2466
2467     vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__);
2468
2469     return lei;
2470 }
2471
2472
2473
2474 static gboolean
2475 repoman_layers_new (GtkWidget *widget, RepoLayersInfo *rli)
2476 {
2477     static GtkWidget *hbox = NULL;
2478     static GtkWidget *label = NULL;
2479     static GtkWidget *txt_name = NULL;
2480     static GtkWidget *dialog = NULL;
2481     printf("%s()\n", __PRETTY_FUNCTION__);
2482
2483     if(dialog == NULL)
2484     {
2485         dialog = gtk_dialog_new_with_buttons(_("New Layer"),
2486                 GTK_WINDOW(rli->dialog), GTK_DIALOG_MODAL,
2487                 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
2488                 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
2489                 NULL);
2490
2491         gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox),
2492                 hbox = gtk_hbox_new(FALSE, 4), FALSE, FALSE, 4);
2493
2494         gtk_box_pack_start(GTK_BOX(hbox),
2495                 label = gtk_label_new(_("Name")),
2496                 FALSE, FALSE, 0);
2497         gtk_box_pack_start(GTK_BOX(hbox),
2498                 txt_name = gtk_entry_new(),
2499                 TRUE, TRUE, 0);
2500     }
2501
2502     gtk_entry_set_text(GTK_ENTRY(txt_name), "");
2503
2504     gtk_widget_show_all(dialog);
2505
2506     while(GTK_RESPONSE_ACCEPT == gtk_dialog_run(GTK_DIALOG(dialog)))
2507     {
2508         repoman_layers_add_layer(rli,
2509                 g_strdup(gtk_entry_get_text(GTK_ENTRY(txt_name))));
2510         break;
2511     }
2512
2513     gtk_widget_hide(dialog);
2514     vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__);
2515     return TRUE;
2516 }
2517
2518
2519 static gboolean
2520 repoman_layers_del (GtkWidget *widget, RepoLayersInfo *rli)
2521 {
2522     GtkTreeIter iter;
2523     GtkTreeSelection *selection;
2524     gint index;
2525
2526     printf("%s()\n", __PRETTY_FUNCTION__);
2527
2528     /* delete list item */
2529     selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (rli->layers_list));
2530
2531     if (!gtk_tree_selection_get_selected (selection, NULL, &iter)) {
2532         vprintf("%s(): return FALSE\n", __PRETTY_FUNCTION__);
2533         return FALSE;
2534     }
2535
2536     index = layer_get_page_index (rli, iter);
2537     gtk_list_store_remove (rli->layers_store, &iter);
2538
2539     rli->layer_edits = g_list_remove_link (rli->layer_edits, g_list_nth (rli->layer_edits, index));
2540
2541     /* delete notebook page */
2542     gtk_notebook_remove_page (GTK_NOTEBOOK (rli->notebook), index);
2543
2544     vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__);
2545     return TRUE;
2546 }
2547
2548
2549 static gboolean
2550 repoman_layers_up (GtkWidget *widget, RepoLayersInfo *rli)
2551 {
2552     GtkTreeSelection *selection;
2553     GtkTreeIter iter, iter2;
2554     GtkTreePath *path;
2555     gint page;
2556     LayerEditInfo *lei;
2557     GList *list_elem;
2558
2559     printf("%s()\n", __PRETTY_FUNCTION__);
2560
2561     /* find selected entry in list view */
2562     selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (rli->layers_list));
2563
2564     if (!gtk_tree_selection_get_selected (selection, NULL, &iter)) {
2565         vprintf("%s(): return FALSE\n", __PRETTY_FUNCTION__);
2566         return FALSE;
2567     }
2568
2569     iter2 = iter;
2570     path = gtk_tree_model_get_path (GTK_TREE_MODEL (rli->layers_store), &iter);
2571     if (!gtk_tree_path_prev (path) || !gtk_tree_model_get_iter (GTK_TREE_MODEL (rli->layers_store), &iter, path)) {
2572         vprintf("%s(): return FALSE\n", __PRETTY_FUNCTION__);
2573         return FALSE;
2574     }
2575
2576     gtk_tree_path_free (path);
2577
2578     /* move it up */
2579     gtk_list_store_move_before (rli->layers_store, &iter2, &iter);
2580
2581     /* reorder notebook tabs */
2582     page = gtk_notebook_get_current_page (GTK_NOTEBOOK (rli->notebook));
2583     gtk_notebook_reorder_child (GTK_NOTEBOOK (rli->notebook), gtk_notebook_get_nth_page (GTK_NOTEBOOK (rli->notebook), page), page-1);
2584
2585     /* reorder layer edits */
2586     list_elem = g_list_nth (rli->layer_edits, page);
2587     lei = list_elem->data;
2588     rli->layer_edits = g_list_remove_link (rli->layer_edits, list_elem);
2589     rli->layer_edits = g_list_insert (rli->layer_edits, lei, page-1);
2590
2591     vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__);
2592     return TRUE;
2593 }
2594
2595
2596 static gboolean
2597 repoman_layers_dn (GtkWidget *widget, RepoLayersInfo *rli)
2598 {
2599     GtkTreeSelection *selection;
2600     GtkTreeIter iter, iter2;
2601     gint page;
2602     LayerEditInfo *lei;
2603     GList *list_elem;
2604
2605     printf("%s()\n", __PRETTY_FUNCTION__);
2606
2607     /* find selected entry in list view */
2608     selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (rli->layers_list));
2609
2610     if (!gtk_tree_selection_get_selected (selection, NULL, &iter)) {
2611         vprintf("%s(): return FALSE\n", __PRETTY_FUNCTION__);
2612         return FALSE;
2613     }
2614
2615     iter2 = iter;
2616     if (!gtk_tree_model_iter_next (GTK_TREE_MODEL (rli->layers_store), &iter)) {
2617         vprintf("%s(): return FALSE\n", __PRETTY_FUNCTION__);
2618         return FALSE;
2619     }
2620
2621     /* move it down */
2622     gtk_list_store_move_after (rli->layers_store, &iter2, &iter);
2623
2624     /* reorder notebook tabs */
2625     page = gtk_notebook_get_current_page (GTK_NOTEBOOK (rli->notebook));
2626     gtk_notebook_reorder_child (GTK_NOTEBOOK (rli->notebook), gtk_notebook_get_nth_page (GTK_NOTEBOOK (rli->notebook), page), page+1);
2627
2628     /* reorder layer edits */
2629     list_elem = g_list_nth (rli->layer_edits, page);
2630     lei = list_elem->data;
2631     rli->layer_edits = g_list_remove_link (rli->layer_edits, list_elem);
2632     rli->layer_edits = g_list_insert (rli->layer_edits, lei, page+1);
2633
2634     vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__);
2635     return TRUE;
2636 }
2637
2638
2639 static gboolean
2640 repoman_layer_selected (GtkTreeSelection *selection, RepoLayersInfo *rli)
2641 {
2642     GtkTreeIter cur;
2643
2644     printf("%s()\n", __PRETTY_FUNCTION__);
2645
2646     if (!gtk_tree_selection_get_selected (selection, NULL, &cur)) {
2647         vprintf("%s(): return FALSE\n", __PRETTY_FUNCTION__);
2648         return FALSE;
2649     }
2650
2651     gtk_notebook_set_current_page (GTK_NOTEBOOK (rli->notebook), layer_get_page_index (rli, cur));
2652
2653     vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__);
2654     return TRUE;
2655 }
2656
2657
2658 static gboolean
2659 repoman_layers(GtkWidget *widget, RepoManInfo *rmi)
2660 {
2661     GtkWidget *hbox = NULL;
2662     GtkWidget *layers_vbox = NULL;
2663     GtkWidget *buttons_hbox = NULL;
2664     GtkWidget *frame;
2665     GtkCellRenderer *layers_rendeder = NULL;
2666     GtkTreeViewColumn *layers_column = NULL;
2667     GtkTreeSelection *selection;
2668
2669     /* layers buttons */
2670     GtkWidget *btn_new = NULL;
2671     GtkWidget *btn_del = NULL;
2672     GtkWidget *btn_up = NULL;
2673     GtkWidget *btn_dn = NULL;
2674
2675     const char* t_header = _("Manage layers [%s]");
2676     char* header = NULL;
2677     RepoEditInfo* rei = NULL;
2678     RepoLayersInfo rli;
2679     gint curr_repo_index = gtk_combo_box_get_active (GTK_COMBO_BOX (rmi->cmb_repos));
2680     RepoData *rd;
2681
2682     printf("%s()\n", __PRETTY_FUNCTION__);
2683
2684     if (curr_repo_index < 0) {
2685         vprintf("%s(): return FALSE (1)\n", __PRETTY_FUNCTION__);
2686         return FALSE;
2687     }
2688
2689     rei = g_list_nth_data (rmi->repo_edits, curr_repo_index);
2690
2691     if (!rei) {
2692         vprintf("%s(): return FALSE (2)\n", __PRETTY_FUNCTION__);
2693         return FALSE;
2694     }
2695
2696     /* check that rei have repo data structure. If it haven't, it means that repository have just
2697        added, so report about this */
2698     if (!rei->repo) {
2699         GtkWidget *msg = hildon_note_new_information ( GTK_WINDOW (rmi->dialog),
2700                            _("You cannot add layers to not saved repository,\nsorry. So, press ok in repository manager\n"
2701                              "and open this dialog again."));
2702
2703         gtk_dialog_run (GTK_DIALOG (msg));
2704         gtk_widget_destroy (msg);
2705
2706         vprintf("%s(): return FALSE (3)\n", __PRETTY_FUNCTION__);
2707         return FALSE;
2708     }
2709
2710     header = g_malloc (strlen (t_header) + strlen (rei->name));
2711     sprintf (header, t_header, rei->name);
2712
2713     printf ("Creating dialog with header: %s\n", header);
2714
2715     rli.layer_edits = NULL;
2716     rli.dialog = gtk_dialog_new_with_buttons (header, GTK_WINDOW (rmi->dialog), GTK_DIALOG_MODAL, 
2717                                               GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
2718                                               GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL);
2719
2720     rli.layers_store = gtk_list_store_new (1, G_TYPE_STRING);
2721     rli.layers_list = gtk_tree_view_new_with_model (GTK_TREE_MODEL (rli.layers_store));
2722     layers_rendeder = gtk_cell_renderer_text_new ();
2723     layers_column = gtk_tree_view_column_new_with_attributes ("Column", layers_rendeder, "text", 0, NULL);
2724     gtk_tree_view_append_column (GTK_TREE_VIEW (rli.layers_list), layers_column);
2725
2726     selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (rli.layers_list));
2727
2728     frame = gtk_frame_new (NULL);
2729     gtk_container_add (GTK_CONTAINER (frame), rli.layers_list);
2730     gtk_widget_set_size_request (frame, -1, 100);
2731
2732     /* beside layers list with have buttons on bottom */
2733     layers_vbox = gtk_vbox_new (FALSE, 4);
2734     gtk_box_pack_start (GTK_BOX (layers_vbox), frame, TRUE, TRUE, 0);
2735     gtk_box_pack_start (GTK_BOX (layers_vbox), buttons_hbox = gtk_hbox_new (FALSE, 4), FALSE, FALSE, 0);
2736
2737     /* buttons */
2738     gtk_box_pack_start (GTK_BOX (buttons_hbox), btn_new = gtk_button_new_with_label (_("New")), FALSE, FALSE, 0);
2739     gtk_box_pack_start (GTK_BOX (buttons_hbox), btn_del = gtk_button_new_with_label (_("Del")), FALSE, FALSE, 0);
2740     gtk_box_pack_start (GTK_BOX (buttons_hbox), btn_up  = gtk_button_new_with_label (_("Up")), FALSE, FALSE, 0);
2741     gtk_box_pack_start (GTK_BOX (buttons_hbox), btn_dn  = gtk_button_new_with_label (_("Dn")), FALSE, FALSE, 0);
2742
2743     /* signals */
2744     g_signal_connect(G_OBJECT(selection), "changed", G_CALLBACK(repoman_layer_selected), &rli);
2745     g_signal_connect(G_OBJECT(btn_new), "clicked", G_CALLBACK(repoman_layers_new), &rli);
2746     g_signal_connect(G_OBJECT(btn_del), "clicked", G_CALLBACK(repoman_layers_del), &rli);
2747     g_signal_connect(G_OBJECT(btn_up),  "clicked", G_CALLBACK(repoman_layers_up), &rli);
2748     g_signal_connect(G_OBJECT(btn_dn),  "clicked", G_CALLBACK(repoman_layers_dn), &rli);
2749
2750     /* notebook with layers' attributes */
2751     rli.notebook = gtk_notebook_new ();
2752
2753     gtk_notebook_set_show_tabs(GTK_NOTEBOOK(rli.notebook), FALSE);
2754     gtk_notebook_set_show_border(GTK_NOTEBOOK(rli.notebook), FALSE);
2755
2756     /* walk through all layers and add notebook pages */
2757     rd = rei->repo->layers;
2758     while (rd) {
2759         LayerEditInfo *lei = repoman_layers_add_layer (&rli, rd->name);
2760
2761         gtk_entry_set_text (GTK_ENTRY (lei->txt_url),  rd->url);
2762         gtk_entry_set_text (GTK_ENTRY (lei->txt_db),   rd->db_filename);
2763         hildon_number_editor_set_value (HILDON_NUMBER_EDITOR (lei->num_autofetch), rd->layer_refresh_interval);
2764         gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (lei->chk_visible), rd->layer_enabled);
2765
2766         rd = rd->layers;
2767     }
2768
2769     /* pack all widgets together */
2770     hbox = gtk_hbox_new (FALSE, 4);
2771
2772     gtk_box_pack_start (GTK_BOX (hbox), layers_vbox, TRUE, TRUE, 4);
2773     gtk_box_pack_start (GTK_BOX (hbox), rli.notebook, TRUE, TRUE, 4);
2774
2775     gtk_box_pack_start (GTK_BOX (GTK_DIALOG (rli.dialog)->vbox), hbox, FALSE, FALSE, 4);
2776
2777     gtk_widget_show_all (rli.dialog);
2778
2779     while (GTK_RESPONSE_ACCEPT == gtk_dialog_run (GTK_DIALOG (rli.dialog)))
2780     {
2781         RepoData **rdp;
2782         gint i;
2783         GList *curr;
2784
2785         menu_layers_remove_repos ();
2786
2787         /* iterate over notebook's pages and build layers */
2788         /* keep list in memory in case downloads use it (TODO: reference counting) */
2789         rdp = &rei->repo->layers;
2790         *rdp = NULL;
2791
2792         for (i = 0, curr = rli.layer_edits; curr; curr = curr->next, i++)  {
2793             LayerEditInfo *lei = curr->data;
2794
2795             rd = g_new0 (RepoData, 1);
2796             *rdp = rd;
2797
2798             rd->name = g_strdup (gtk_entry_get_text (GTK_ENTRY (lei->txt_name)));
2799             rd->url = g_strdup (gtk_entry_get_text (GTK_ENTRY (lei->txt_url)));
2800             rd->db_filename = g_strdup (gtk_entry_get_text (GTK_ENTRY (lei->txt_db)));
2801             rd->layer_enabled = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (lei->chk_visible));
2802             rd->layer_refresh_interval = hildon_number_editor_get_value (HILDON_NUMBER_EDITOR (lei->num_autofetch));
2803             rd->layer_refresh_countdown = rd->layer_refresh_interval;
2804             rd->layer_level = i+1;
2805
2806             rd->dl_zoom_steps = rei->repo->dl_zoom_steps;
2807             rd->view_zoom_steps = rei->repo->view_zoom_steps;
2808             rd->double_size = rei->repo->double_size;
2809             rd->nextable = rei->repo->nextable;
2810             rd->min_zoom = rei->repo->min_zoom;
2811             rd->max_zoom = rei->repo->max_zoom;
2812
2813             set_repo_type (rd);
2814             rdp = &rd->layers;
2815         }
2816
2817         menu_layers_add_repos ();
2818         repo_set_curr(_curr_repo);
2819         settings_save ();
2820         map_cache_clean ();
2821         map_refresh_mark (TRUE);
2822         break;
2823     }
2824
2825     gtk_widget_destroy (rli.dialog);
2826
2827     vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__);
2828     return TRUE;
2829 }
2830
2831
2832 gboolean
2833 repoman_dialog()
2834 {
2835     static RepoManInfo rmi;
2836     static GtkWidget *dialog = NULL;
2837     static GtkWidget *hbox = NULL;
2838     static GtkWidget *btn_rename = NULL;
2839     static GtkWidget *btn_delete = NULL;
2840     static GtkWidget *btn_new = NULL;
2841     static GtkWidget *btn_reset = NULL;
2842     static GtkWidget *btn_layers = NULL;
2843     gint i, curr_repo_index = 0;
2844     GList *curr;
2845     printf("%s()\n", __PRETTY_FUNCTION__);
2846
2847     if(dialog == NULL)
2848     {
2849         rmi.dialog = dialog = gtk_dialog_new_with_buttons(
2850                 _("Manage Repositories"),
2851                 GTK_WINDOW(_window), GTK_DIALOG_MODAL,
2852                 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
2853                 NULL);
2854
2855         /* Enable the help button. */
2856 #ifndef LEGACY
2857         hildon_help_dialog_help_enable(
2858 #else
2859         ossohelp_dialog_help_enable(
2860 #endif
2861                 GTK_DIALOG(dialog), HELP_ID_REPOMAN, _osso);
2862
2863         /* Reset button. */
2864         gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->action_area),
2865                 btn_reset = gtk_button_new_with_label(_("Reset...")));
2866         g_signal_connect(G_OBJECT(btn_reset), "clicked",
2867                           G_CALLBACK(repoman_reset), &rmi);
2868
2869         /* Layers button. */
2870         gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->action_area),
2871                 btn_layers = gtk_button_new_with_label(_("Layers...")));
2872         g_signal_connect(G_OBJECT(btn_layers), "clicked",
2873                           G_CALLBACK(repoman_layers), &rmi);
2874
2875         /* Cancel button. */
2876         gtk_dialog_add_button(GTK_DIALOG(dialog),
2877                 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT);
2878
2879         hbox = gtk_hbox_new(FALSE, 4);
2880
2881         gtk_box_pack_start(GTK_BOX(hbox),
2882                 rmi.cmb_repos = gtk_combo_box_new_text(), TRUE, TRUE, 4);
2883
2884         gtk_box_pack_start(GTK_BOX(hbox),
2885                 gtk_vseparator_new(), FALSE, FALSE, 4);
2886         gtk_box_pack_start(GTK_BOX(hbox),
2887                 btn_rename = gtk_button_new_with_label(_("Rename...")),
2888                 FALSE, FALSE, 4);
2889         gtk_box_pack_start(GTK_BOX(hbox),
2890                 btn_delete = gtk_button_new_with_label(_("Delete...")),
2891                 FALSE, FALSE, 4);
2892         gtk_box_pack_start(GTK_BOX(hbox),
2893                 btn_new = gtk_button_new_with_label(_("New...")),
2894                 FALSE, FALSE, 4);
2895
2896         gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox),
2897                 hbox, FALSE, FALSE, 4);
2898
2899         gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox),
2900                 gtk_hseparator_new(), TRUE, TRUE, 4);
2901         gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox),
2902                 rmi.notebook = gtk_notebook_new(), TRUE, TRUE, 4);
2903
2904         gtk_notebook_set_show_tabs(GTK_NOTEBOOK(rmi.notebook), FALSE);
2905         gtk_notebook_set_show_border(GTK_NOTEBOOK(rmi.notebook), FALSE);
2906
2907         rmi.repo_edits = NULL;
2908
2909         /* Connect signals. */
2910         g_signal_connect(G_OBJECT(btn_rename), "clicked",
2911                 G_CALLBACK(repoman_dialog_rename), &rmi);
2912         g_signal_connect(G_OBJECT(btn_delete), "clicked",
2913                 G_CALLBACK(repoman_dialog_delete), &rmi);
2914         g_signal_connect(G_OBJECT(btn_new), "clicked",
2915                 G_CALLBACK(repoman_dialog_new), &rmi);
2916         g_signal_connect(G_OBJECT(rmi.cmb_repos), "changed",
2917                 G_CALLBACK(repoman_dialog_select), &rmi);
2918     }
2919
2920     /* Populate combo box and pages in notebook. */
2921     for(i = 0, curr = _repo_list; curr; curr = curr->next, i++)
2922     {
2923         RepoData *rd = (RepoData*)curr->data;
2924         RepoEditInfo *rei = repoman_dialog_add_repo(&rmi, g_strdup(rd->name));
2925
2926         /* store this to be able to walk through layers attached to repo */
2927         rei->repo = rd;
2928
2929         /* Initialize fields with data from the RepoData object. */
2930         gtk_entry_set_text(GTK_ENTRY(rei->txt_url), rd->url);
2931         gtk_entry_set_text(GTK_ENTRY(rei->txt_db_filename),
2932                 rd->db_filename);
2933         hildon_controlbar_set_value(
2934                 HILDON_CONTROLBAR(rei->num_dl_zoom_steps),
2935                 rd->dl_zoom_steps);
2936         hildon_controlbar_set_value(
2937                 HILDON_CONTROLBAR(rei->num_view_zoom_steps),
2938                 rd->view_zoom_steps);
2939         gtk_toggle_button_set_active(
2940                 GTK_TOGGLE_BUTTON(rei->chk_double_size),
2941                 rd->double_size);
2942         gtk_toggle_button_set_active(
2943                 GTK_TOGGLE_BUTTON(rei->chk_nextable),
2944                 rd->nextable);
2945         hildon_number_editor_set_value(
2946                 HILDON_NUMBER_EDITOR(rei->num_min_zoom),
2947                 rd->min_zoom);
2948         hildon_number_editor_set_value(
2949                 HILDON_NUMBER_EDITOR(rei->num_max_zoom),
2950                 rd->max_zoom);
2951         if(rd == _curr_repo)
2952             curr_repo_index = i;
2953     }
2954
2955     gtk_combo_box_set_active(GTK_COMBO_BOX(rmi.cmb_repos), curr_repo_index);
2956     gtk_notebook_set_current_page(GTK_NOTEBOOK(rmi.notebook), curr_repo_index);
2957
2958     gtk_widget_show_all(dialog);
2959
2960     while(GTK_RESPONSE_ACCEPT == gtk_dialog_run(GTK_DIALOG(dialog)))
2961     {
2962         /* Iterate through repos and verify each. */
2963         gboolean verified = TRUE;
2964         gint i;
2965         GList *curr;
2966         gchar *old_curr_repo_name = _curr_repo->name;
2967
2968         for(i = 0, curr = rmi.repo_edits; curr; curr = curr->next, i++)
2969         {
2970             /* Check the ranges for the min and max zoom levels. */
2971             RepoEditInfo *rei = curr->data;
2972             if(hildon_number_editor_get_value(
2973                         HILDON_NUMBER_EDITOR(rei->num_max_zoom))
2974                  < hildon_number_editor_get_value(
2975                         HILDON_NUMBER_EDITOR(rei->num_min_zoom)))
2976             {
2977                 verified = FALSE;
2978                 break;
2979             }
2980         }
2981         if(!verified)
2982         {
2983             gtk_combo_box_set_active(GTK_COMBO_BOX(rmi.cmb_repos), i);
2984             popup_error(dialog,
2985                     _("Minimum Downloadable Zoom must be less than "
2986                         "Maximum Downloadable Zoom."));
2987             continue;
2988         }
2989
2990         /* We're good to replace.  Remove old _repo_list menu items. */
2991         menu_maps_remove_repos();
2992         /* But keep the repo list in memory, in case downloads are using it. */
2993         _repo_list = NULL;
2994
2995         /* Write new _repo_list. */
2996         curr_repo_index = gtk_combo_box_get_active(
2997                 GTK_COMBO_BOX(rmi.cmb_repos));
2998         _curr_repo = NULL;
2999         for(i = 0, curr = rmi.repo_edits; curr; curr = curr->next, i++)
3000         {
3001             RepoEditInfo *rei = curr->data;
3002             RepoData *rd = g_new0(RepoData, 1);
3003             RepoData *rd0, **rd1;
3004             rd->name = g_strdup(rei->name);
3005             rd->url = g_strdup(gtk_entry_get_text(GTK_ENTRY(rei->txt_url)));
3006             rd->db_filename = gnome_vfs_expand_initial_tilde(
3007                     gtk_entry_get_text(GTK_ENTRY(rei->txt_db_filename)));
3008             rd->dl_zoom_steps = hildon_controlbar_get_value(
3009                     HILDON_CONTROLBAR(rei->num_dl_zoom_steps));
3010             rd->view_zoom_steps = hildon_controlbar_get_value(
3011                     HILDON_CONTROLBAR(rei->num_view_zoom_steps));
3012             rd->double_size = gtk_toggle_button_get_active(
3013                     GTK_TOGGLE_BUTTON(rei->chk_double_size));
3014             rd->nextable = gtk_toggle_button_get_active(
3015                     GTK_TOGGLE_BUTTON(rei->chk_nextable));
3016             rd->min_zoom = hildon_number_editor_get_value(
3017                     HILDON_NUMBER_EDITOR(rei->num_min_zoom));
3018             rd->max_zoom = hildon_number_editor_get_value(
3019                     HILDON_NUMBER_EDITOR(rei->num_max_zoom));
3020
3021             if (rei->repo) {
3022                 /* clone layers */
3023                 rd0 = rei->repo->layers;
3024                 rd1 = &rd->layers;
3025
3026                 while (rd0) {
3027                     *rd1 = g_new0 (RepoData, 1);
3028                     (*rd1)->name = rd0->name;
3029                     (*rd1)->url = rd0->url;
3030                     (*rd1)->db_filename = rd0->db_filename;
3031                     (*rd1)->layer_enabled = rd0->layer_enabled;
3032                     (*rd1)->layer_refresh_interval = rd0->layer_refresh_interval;
3033                     (*rd1)->layer_refresh_countdown = rd0->layer_refresh_countdown;
3034                     (*rd1)->layer_level = rd0->layer_level;
3035
3036                     (*rd1)->dl_zoom_steps = rd0->dl_zoom_steps;
3037                     (*rd1)->view_zoom_steps = rd0->view_zoom_steps;
3038                     (*rd1)->double_size = rd0->double_size;
3039                     (*rd1)->nextable = rd0->nextable;
3040                     (*rd1)->min_zoom = rd0->min_zoom;
3041                     (*rd1)->max_zoom = rd0->max_zoom;
3042
3043                     set_repo_type (*rd1);
3044
3045                     rd0 = rd0->layers;
3046                     rd1 = &(*rd1)->layers;
3047                 }
3048                 *rd1 = NULL;
3049             }
3050             else
3051                 rd->layers = NULL;
3052
3053             rd->layer_level = 0;
3054             set_repo_type(rd);
3055
3056             _repo_list = g_list_append(_repo_list, rd);
3057
3058             if(!_curr_repo && !strcmp(old_curr_repo_name, rd->name))
3059                 repo_set_curr(rd);
3060             else if(i == curr_repo_index)
3061                 repo_set_curr(rd);
3062         }
3063         if(!_curr_repo)
3064             repo_set_curr((RepoData*)g_list_first(_repo_list)->data);
3065         menu_maps_add_repos();
3066
3067         settings_save();
3068         break;
3069     }
3070
3071     gtk_widget_hide(dialog);
3072
3073     /* Clear out the notebook entries. */
3074     while(rmi.repo_edits)
3075         repoman_delete(&rmi, 0);
3076
3077     map_set_zoom(_zoom); /* make sure we're at an appropriate zoom level. */
3078     map_refresh_mark (TRUE);
3079
3080     vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__);
3081     return TRUE;
3082 }
3083
3084 static gboolean
3085 mapman_by_area(gdouble start_lat, gdouble start_lon,
3086         gdouble end_lat, gdouble end_lon, MapmanInfo *mapman_info,
3087         MapUpdateType update_type,
3088         gint download_batch_id)
3089 {
3090     gint start_unitx, start_unity, end_unitx, end_unity;
3091     gint num_maps = 0;
3092     gint z;
3093     gchar buffer[80];
3094     GtkWidget *confirm;
3095     printf("%s(%f, %f, %f, %f)\n", __PRETTY_FUNCTION__, start_lat, start_lon,
3096             end_lat, end_lon);
3097
3098     latlon2unit(start_lat, start_lon, start_unitx, start_unity);
3099     latlon2unit(end_lat, end_lon, end_unitx, end_unity);
3100
3101     /* Swap if they specified flipped lats or lons. */
3102     if(start_unitx > end_unitx)
3103     {
3104         gint swap = start_unitx;
3105         start_unitx = end_unitx;
3106         end_unitx = swap;
3107     }
3108     if(start_unity > end_unity)
3109     {
3110         gint swap = start_unity;
3111         start_unity = end_unity;
3112         end_unity = swap;
3113     }
3114
3115     /* First, get the number of maps to download. */
3116     for(z = 0; z <= MAX_ZOOM; ++z)
3117     {
3118         if(gtk_toggle_button_get_active(
3119                     GTK_TOGGLE_BUTTON(mapman_info->chk_zoom_levels[z])))
3120         {
3121             gint start_tilex, start_tiley, end_tilex, end_tiley;
3122             start_tilex = unit2ztile(start_unitx, z);
3123             start_tiley = unit2ztile(start_unity, z);
3124             end_tilex = unit2ztile(end_unitx, z);
3125             end_tiley = unit2ztile(end_unity, z);
3126             num_maps += (end_tilex - start_tilex + 1)
3127                 * (end_tiley - start_tiley + 1);
3128         }
3129     }
3130
3131     if(update_type == MAP_UPDATE_DELETE)
3132     {
3133         snprintf(buffer, sizeof(buffer), "%s %d %s", _("Confirm DELETION of"),
3134                 num_maps, _("maps "));
3135     }
3136     else
3137     {
3138         snprintf(buffer, sizeof(buffer),
3139                 "%s %d %s\n(%s %.2f MB)\n", _("Confirm download of"),
3140                 num_maps, _("maps"), _("up to about"),
3141                 num_maps * (strstr(_curr_repo->url, "%s") ? 18e-3 : 6e-3));
3142     }
3143     confirm = hildon_note_new_confirmation(
3144             GTK_WINDOW(mapman_info->dialog), buffer);
3145
3146     if(GTK_RESPONSE_OK != gtk_dialog_run(GTK_DIALOG(confirm)))
3147     {
3148         gtk_widget_destroy(confirm);
3149         vprintf("%s(): return FALSE\n", __PRETTY_FUNCTION__);
3150         return FALSE;
3151     }
3152
3153     g_mutex_lock(_mut_priority_mutex);
3154     for(z = 0; z <= MAX_ZOOM; ++z)
3155     {
3156         if(gtk_toggle_button_get_active(
3157                     GTK_TOGGLE_BUTTON(mapman_info->chk_zoom_levels[z])))
3158         {
3159             gint start_tilex, start_tiley, end_tilex, end_tiley;
3160             gint tilex, tiley;
3161             start_tilex = unit2ztile(start_unitx, z);
3162             start_tiley = unit2ztile(start_unity, z);
3163             end_tilex = unit2ztile(end_unitx, z);
3164             end_tiley = unit2ztile(end_unity, z);
3165             for(tiley = start_tiley; tiley <= end_tiley; tiley++)
3166             {
3167                 for(tilex = start_tilex; tilex <= end_tilex; tilex++)
3168                 {
3169                     /* Make sure this tile is even possible. */
3170                     if((unsigned)tilex < unit2ztile(WORLD_SIZE_UNITS, z)
3171                       && (unsigned)tiley < unit2ztile(WORLD_SIZE_UNITS, z))
3172                     {
3173                         RepoData* rd = _curr_repo;
3174
3175                         while (rd) {
3176                             if (rd == _curr_repo || (rd->layer_enabled && rd->db))
3177                                 mapdb_initiate_update(rd, z, tilex, tiley,
3178                                                       update_type, download_batch_id,
3179                                                       (abs(tilex - unit2tile(_next_center.unitx))
3180                                                        + abs(tiley - unit2tile(_next_center.unity))),
3181                                                       NULL);
3182                             rd = rd->layers;
3183                         }
3184                     }
3185                 }
3186             }
3187         }
3188     }
3189     g_mutex_unlock(_mut_priority_mutex);
3190
3191     gtk_widget_destroy(confirm);
3192     vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__);
3193     return TRUE;
3194 }
3195
3196 static gboolean
3197 mapman_by_route(MapmanInfo *mapman_info, MapUpdateType update_type,
3198         gint download_batch_id)
3199 {
3200     GtkWidget *confirm;
3201     gint prev_tilex, prev_tiley, num_maps = 0, z;
3202     Point *curr;
3203     gchar buffer[80];
3204     gint radius = hildon_number_editor_get_value(
3205             HILDON_NUMBER_EDITOR(mapman_info->num_route_radius));
3206     printf("%s()\n", __PRETTY_FUNCTION__);
3207
3208     /* First, get the number of maps to download. */
3209     for(z = 0; z <= MAX_ZOOM; ++z)
3210     {
3211         if(gtk_toggle_button_get_active(
3212                     GTK_TOGGLE_BUTTON(mapman_info->chk_zoom_levels[z])))
3213         {
3214             prev_tilex = 0;
3215             prev_tiley = 0;
3216             for(curr = _route.head - 1; curr++ != _route.tail; )
3217             {
3218                 if(curr->unity)
3219                 {
3220                     gint tilex = unit2ztile(curr->unitx, z);
3221                     gint tiley = unit2ztile(curr->unity, z);
3222                     if(tilex != prev_tilex || tiley != prev_tiley)
3223                     {
3224                         if(prev_tiley)
3225                             num_maps += (abs((gint)tilex - prev_tilex) + 1)
3226                                 * (abs((gint)tiley - prev_tiley) + 1) - 1;
3227                         prev_tilex = tilex;
3228                         prev_tiley = tiley;
3229                     }
3230                 }
3231             }
3232         }
3233     }
3234     num_maps *= 0.625 * pow(radius + 1, 1.85);
3235
3236     if(update_type == MAP_UPDATE_DELETE)
3237     {
3238         snprintf(buffer, sizeof(buffer), "%s %s %d %s",
3239                 _("Confirm DELETION of"), _("about"),
3240                 num_maps, _("maps "));
3241     }
3242     else
3243     {
3244         snprintf(buffer, sizeof(buffer),
3245                 "%s %s %d %s\n(%s %.2f MB)\n", _("Confirm download of"),
3246                 _("about"),
3247                 num_maps, _("maps"), _("up to about"),
3248                 num_maps * (strstr(_curr_repo->url, "%s") ? 18e-3 : 6e-3));
3249     }
3250     confirm = hildon_note_new_confirmation(
3251             GTK_WINDOW(mapman_info->dialog), buffer);
3252
3253     if(GTK_RESPONSE_OK != gtk_dialog_run(GTK_DIALOG(confirm)))
3254     {
3255         gtk_widget_destroy(confirm);
3256         vprintf("%s(): return FALSE\n", __PRETTY_FUNCTION__);
3257         return FALSE;
3258     }
3259
3260     /* Now, do the actual download. */
3261     g_mutex_lock(_mut_priority_mutex);
3262     for(z = 0; z <= MAX_ZOOM; ++z)
3263     {
3264         if(gtk_toggle_button_get_active(
3265                     GTK_TOGGLE_BUTTON(mapman_info->chk_zoom_levels[z])))
3266         {
3267             prev_tilex = 0;
3268             prev_tiley = 0;
3269             for(curr = _route.head - 1; curr++ != _route.tail; )
3270             {
3271                 if(curr->unity)
3272                 {
3273                     gint tilex = unit2ztile(curr->unitx, z);
3274                     gint tiley = unit2ztile(curr->unity, z);
3275                     if(tilex != prev_tilex || tiley != prev_tiley)
3276                     {
3277                         gint minx, miny, maxx, maxy, x, y;
3278                         if(prev_tiley != 0)
3279                         {
3280                             minx = MIN(tilex, prev_tilex) - radius;
3281                             miny = MIN(tiley, prev_tiley) - radius;
3282                             maxx = MAX(tilex, prev_tilex) + radius;
3283                             maxy = MAX(tiley, prev_tiley) + radius;
3284                         }
3285                         else
3286                         {
3287                             minx = tilex - radius;
3288                             miny = tiley - radius;
3289                             maxx = tilex + radius;
3290                             maxy = tiley + radius;
3291                         }
3292                         for(x = minx; x <= maxx; x++)
3293                         {
3294                             for(y = miny; y <= maxy; y++)
3295                             {
3296                                 /* Make sure this tile is even possible. */
3297                                 if((unsigned)tilex
3298                                         < unit2ztile(WORLD_SIZE_UNITS, z)
3299                                   && (unsigned)tiley
3300                                         < unit2ztile(WORLD_SIZE_UNITS, z))
3301                                 {
3302                                     mapdb_initiate_update(_curr_repo, z, x, y,
3303                                         update_type, download_batch_id,
3304                                         (abs(tilex - unit2tile(
3305                                                  _next_center.unitx))
3306                                          + abs(tiley - unit2tile(
3307                                                  _next_center.unity))),
3308                                         NULL);
3309                                 }
3310                             }
3311                         }
3312                         prev_tilex = tilex;
3313                         prev_tiley = tiley;
3314                     }
3315                 }
3316             }
3317         }
3318     }
3319     g_mutex_unlock(_mut_priority_mutex);
3320     _route_dl_radius = radius;
3321     gtk_widget_destroy(confirm);
3322     vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__);
3323     return TRUE;
3324 }
3325
3326 static void
3327 mapman_clear(GtkWidget *widget, MapmanInfo *mapman_info)
3328 {
3329     gint z;
3330     printf("%s()\n", __PRETTY_FUNCTION__);
3331     if(gtk_notebook_get_current_page(GTK_NOTEBOOK(mapman_info->notebook)))
3332         /* This is the second page (the "Zoom" page) - clear the checks. */
3333         for(z = 0; z <= MAX_ZOOM; ++z)
3334             gtk_toggle_button_set_active(
3335                     GTK_TOGGLE_BUTTON(mapman_info->chk_zoom_levels[z]), FALSE);
3336     else
3337     {
3338         /* This is the first page (the "Area" page) - clear the text fields. */
3339         gtk_entry_set_text(GTK_ENTRY(mapman_info->txt_topleft_lat), "");
3340         gtk_entry_set_text(GTK_ENTRY(mapman_info->txt_topleft_lon), "");
3341         gtk_entry_set_text(GTK_ENTRY(mapman_info->txt_botright_lat), "");
3342         gtk_entry_set_text(GTK_ENTRY(mapman_info->txt_botright_lon), "");
3343     }
3344     vprintf("%s(): return\n", __PRETTY_FUNCTION__);
3345 }
3346
3347 void mapman_update_state(GtkWidget *widget, MapmanInfo *mapman_info)
3348 {
3349     printf("%s()\n", __PRETTY_FUNCTION__);
3350     gtk_widget_set_sensitive( mapman_info->chk_overwrite,
3351             gtk_toggle_button_get_active(
3352                 GTK_TOGGLE_BUTTON(mapman_info->rad_download)));
3353
3354     if(gtk_toggle_button_get_active(
3355                 GTK_TOGGLE_BUTTON(mapman_info->rad_by_area)))
3356         gtk_widget_show(mapman_info->tbl_area);
3357     else if(gtk_notebook_get_n_pages(GTK_NOTEBOOK(mapman_info->notebook)) == 3)
3358         gtk_widget_hide(mapman_info->tbl_area);
3359
3360     gtk_widget_set_sensitive(mapman_info->num_route_radius,
3361             gtk_toggle_button_get_active(
3362                 GTK_TOGGLE_BUTTON(mapman_info->rad_by_route)));
3363     vprintf("%s(): return\n", __PRETTY_FUNCTION__);
3364 }
3365
3366 gboolean
3367 mapman_dialog()
3368 {
3369     static GtkWidget *dialog = NULL;
3370     static GtkWidget *vbox = NULL;
3371     static GtkWidget *hbox = NULL;
3372     static GtkWidget *table = NULL;
3373     static GtkWidget *label = NULL;
3374     static GtkWidget *button = NULL;
3375     static GtkWidget *lbl_gps_lat = NULL;
3376     static GtkWidget *lbl_gps_lon = NULL;
3377     static GtkWidget *lbl_center_lat = NULL;
3378     static GtkWidget *lbl_center_lon = NULL;
3379     static MapmanInfo mapman_info;
3380     gchar buffer[80];
3381     gdouble lat, lon;
3382     gint z;
3383     printf("%s()\n", __PRETTY_FUNCTION__);
3384
3385     if(!_curr_repo->db)
3386     {
3387         popup_error(_window, "To manage maps, you must set a valid repository "
3388                 "database filename in the \"Manage Repositories\" dialog.");
3389         vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__);
3390         return TRUE;
3391     }
3392
3393     if(dialog == NULL)
3394     {
3395         mapman_info.dialog = dialog = gtk_dialog_new_with_buttons(
3396                 _("Manage Maps"),
3397                 GTK_WINDOW(_window), GTK_DIALOG_MODAL,
3398                 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
3399                 NULL);
3400
3401         /* Enable the help button. */
3402 #ifndef LEGACY
3403         hildon_help_dialog_help_enable(
3404 #else
3405         ossohelp_dialog_help_enable(
3406 #endif
3407                 GTK_DIALOG(mapman_info.dialog), HELP_ID_MAPMAN, _osso);
3408
3409         /* Clear button. */
3410         gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->action_area),
3411                 button = gtk_button_new_with_label(_("Clear")));
3412         g_signal_connect(G_OBJECT(button), "clicked",
3413                           G_CALLBACK(mapman_clear), &mapman_info);
3414
3415         /* Cancel button. */
3416         gtk_dialog_add_button(GTK_DIALOG(dialog),
3417                 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT);
3418
3419         gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox),
3420                 mapman_info.notebook = gtk_notebook_new(), TRUE, TRUE, 0);
3421
3422         /* Setup page. */
3423         gtk_notebook_append_page(GTK_NOTEBOOK(mapman_info.notebook),
3424                 vbox = gtk_vbox_new(FALSE, 2),
3425                 label = gtk_label_new(_("Setup")));
3426         gtk_notebook_set_tab_label_packing(
3427                 GTK_NOTEBOOK(mapman_info.notebook), vbox,
3428                 FALSE, FALSE, GTK_PACK_START);
3429
3430         gtk_box_pack_start(GTK_BOX(vbox),
3431                 hbox = gtk_hbox_new(FALSE, 4),
3432                 FALSE, FALSE, 0);
3433         gtk_box_pack_start(GTK_BOX(hbox),
3434                 mapman_info.rad_download = gtk_radio_button_new_with_label(
3435                     NULL,_("Download Maps")),
3436                 FALSE, FALSE, 0);
3437         gtk_box_pack_start(GTK_BOX(hbox),
3438                 label = gtk_alignment_new(0.f, 0.5f, 0.f, 0.f),
3439                 FALSE, FALSE, 0);
3440         gtk_container_add(GTK_CONTAINER(label),
3441                 mapman_info.chk_overwrite
3442                         = gtk_check_button_new_with_label(_("Overwrite"))),
3443
3444         gtk_box_pack_start(GTK_BOX(vbox),
3445                 mapman_info.rad_delete
3446                         = gtk_radio_button_new_with_label_from_widget(
3447                             GTK_RADIO_BUTTON(mapman_info.rad_download),
3448                             _("Delete Maps")),
3449                 FALSE, FALSE, 0);
3450
3451         gtk_box_pack_start(GTK_BOX(vbox),
3452                 gtk_hseparator_new(),
3453                 FALSE, FALSE, 0);
3454
3455         gtk_box_pack_start(GTK_BOX(vbox),
3456                 mapman_info.rad_by_area
3457                         = gtk_radio_button_new_with_label(NULL,
3458                             _("By Area (see tab)")),
3459                 FALSE, FALSE, 0);
3460         gtk_box_pack_start(GTK_BOX(vbox),
3461                 hbox = gtk_hbox_new(FALSE, 4),
3462                 FALSE, FALSE, 0);
3463         gtk_box_pack_start(GTK_BOX(hbox),
3464                 mapman_info.rad_by_route
3465                         = gtk_radio_button_new_with_label_from_widget(
3466                             GTK_RADIO_BUTTON(mapman_info.rad_by_area),
3467                             _("Along Route - Radius (tiles):")),
3468                 FALSE, FALSE, 0);
3469         gtk_box_pack_start(GTK_BOX(hbox),
3470                 mapman_info.num_route_radius = hildon_number_editor_new(0,100),
3471                 FALSE, FALSE, 0);
3472         hildon_number_editor_set_value(
3473                 HILDON_NUMBER_EDITOR(mapman_info.num_route_radius),
3474                 _route_dl_radius);
3475
3476
3477         /* Zoom page. */
3478         gtk_notebook_append_page(GTK_NOTEBOOK(mapman_info.notebook),
3479                 table = gtk_table_new(5, 5, FALSE),
3480                 label = gtk_label_new(_("Zoom")));
3481         gtk_notebook_set_tab_label_packing(
3482                 GTK_NOTEBOOK(mapman_info.notebook), table,
3483                 FALSE, FALSE, GTK_PACK_START);
3484         gtk_table_attach(GTK_TABLE(table),
3485                 label = gtk_label_new(
3486                     _("Zoom Levels to Download: (0 = most detail)")),
3487                 0, 4, 0, 1, GTK_FILL, 0, 4, 0);
3488         gtk_misc_set_alignment(GTK_MISC(label), 0.f, 0.5f);
3489         snprintf(buffer, sizeof(buffer), "%d", 0);
3490         gtk_table_attach(GTK_TABLE(table),
3491                 mapman_info.chk_zoom_levels[0]
3492                         = gtk_check_button_new_with_label(buffer),
3493                 4, 5 , 0, 1, GTK_FILL, 0, 0, 0);
3494         for(z = 0; z < MAX_ZOOM; ++z)
3495         {
3496             snprintf(buffer, sizeof(buffer), "%d", z + 1);
3497             gtk_table_attach(GTK_TABLE(table),
3498                     mapman_info.chk_zoom_levels[z + 1]
3499                             = gtk_check_button_new_with_label(buffer),
3500                     z / 4, z / 4 + 1, z % 4 + 1, z % 4 + 2,
3501                     GTK_FILL, 0, 0, 0);
3502         }
3503
3504         /* Area page. */
3505         gtk_notebook_append_page(GTK_NOTEBOOK(mapman_info.notebook),
3506             mapman_info.tbl_area = gtk_table_new(5, 3, FALSE),
3507             label = gtk_label_new(_("Area")));
3508
3509         /* Label Columns. */
3510         gtk_table_attach(GTK_TABLE(mapman_info.tbl_area),
3511                 label = gtk_label_new(_("Latitude")),
3512                 1, 2, 0, 1, GTK_FILL, 0, 4, 0);
3513         gtk_misc_set_alignment(GTK_MISC(label), 1.f, 0.5f);
3514         gtk_table_attach(GTK_TABLE(mapman_info.tbl_area),
3515                 label = gtk_label_new(_("Longitude")),
3516                 2, 3, 0, 1, GTK_FILL, 0, 4, 0);
3517         gtk_misc_set_alignment(GTK_MISC(label), 1.f, 0.5f);
3518
3519         /* GPS. */
3520         gtk_table_attach(GTK_TABLE(mapman_info.tbl_area),
3521                 label = gtk_label_new(_("GPS Location")),
3522                 0, 1, 1, 2, GTK_FILL, 0, 4, 0);
3523         gtk_misc_set_alignment(GTK_MISC(label), 1.f, 0.5f);
3524         gtk_table_attach(GTK_TABLE(mapman_info.tbl_area),
3525                 lbl_gps_lat = gtk_label_new(""),
3526                 1, 2, 1, 2, GTK_FILL, 0, 4, 0);
3527         gtk_label_set_selectable(GTK_LABEL(lbl_gps_lat), TRUE);
3528         gtk_misc_set_alignment(GTK_MISC(lbl_gps_lat), 1.f, 0.5f);
3529         gtk_table_attach(GTK_TABLE(mapman_info.tbl_area),
3530                 lbl_gps_lon = gtk_label_new(""),
3531                 2, 3, 1, 2, GTK_FILL, 0, 4, 0);
3532         gtk_label_set_selectable(GTK_LABEL(lbl_gps_lon), TRUE);
3533         gtk_misc_set_alignment(GTK_MISC(lbl_gps_lon), 1.f, 0.5f);
3534
3535         /* Center. */
3536         gtk_table_attach(GTK_TABLE(mapman_info.tbl_area),
3537                 label = gtk_label_new(_("View Center")),
3538                 0, 1, 2, 3, GTK_FILL, 0, 4, 0);
3539         gtk_misc_set_alignment(GTK_MISC(label), 1.f, 0.5f);
3540         gtk_table_attach(GTK_TABLE(mapman_info.tbl_area),
3541                 lbl_center_lat = gtk_label_new(""),
3542                 1, 2, 2, 3, GTK_FILL, 0, 4, 0);
3543         gtk_label_set_selectable(GTK_LABEL(lbl_center_lat), TRUE);
3544         gtk_misc_set_alignment(GTK_MISC(lbl_center_lat), 1.f, 0.5f);
3545         gtk_table_attach(GTK_TABLE(mapman_info.tbl_area),
3546                 lbl_center_lon = gtk_label_new(""),
3547                 2, 3, 2, 3, GTK_FILL, 0, 4, 0);
3548         gtk_label_set_selectable(GTK_LABEL(lbl_center_lon), TRUE);
3549         gtk_misc_set_alignment(GTK_MISC(lbl_center_lon), 1.f, 0.5f);
3550
3551         /* default values for Top Left and Bottom Right are defined by the
3552          * rectangle of the current and the previous Center */
3553
3554         /* Top Left. */
3555         gtk_table_attach(GTK_TABLE(mapman_info.tbl_area),
3556                 label = gtk_label_new(_("Top-Left")),
3557                 0, 1, 3, 4, GTK_FILL, 0, 4, 0);
3558         gtk_misc_set_alignment(GTK_MISC(label), 1.f, 0.5f);
3559         gtk_table_attach(GTK_TABLE(mapman_info.tbl_area),
3560                 mapman_info.txt_topleft_lat = gtk_entry_new(),
3561                 1, 2, 3, 4, GTK_FILL, 0, 4, 0);
3562         gtk_entry_set_width_chars(GTK_ENTRY(mapman_info.txt_topleft_lat), 12);
3563         gtk_entry_set_alignment(GTK_ENTRY(mapman_info.txt_topleft_lat), 1.f);
3564 #ifdef MAEMO_CHANGES
3565         g_object_set(G_OBJECT(mapman_info.txt_topleft_lat),
3566 #ifndef LEGACY
3567                 "hildon-input-mode",
3568                 HILDON_GTK_INPUT_MODE_FULL, NULL);
3569 #else
3570                 HILDON_INPUT_MODE_HINT,
3571                 HILDON_INPUT_MODE_HINT_ALPHANUMERICSPECIAL, NULL);
3572         g_object_set(G_OBJECT(mapman_info.txt_topleft_lat),
3573                 HILDON_AUTOCAP,
3574                 FALSE, NULL);
3575 #endif
3576 #endif
3577         gtk_table_attach(GTK_TABLE(mapman_info.tbl_area),
3578                 mapman_info.txt_topleft_lon = gtk_entry_new(),
3579                 2, 3, 3, 4, GTK_FILL, 0, 4, 0);
3580         gtk_entry_set_width_chars(GTK_ENTRY(mapman_info.txt_topleft_lon), 12);
3581         gtk_entry_set_alignment(GTK_ENTRY(mapman_info.txt_topleft_lon), 1.f);
3582 #ifdef MAEMO_CHANGES
3583         g_object_set(G_OBJECT(mapman_info.txt_topleft_lon),
3584 #ifndef LEGACY
3585                 "hildon-input-mode",
3586                 HILDON_GTK_INPUT_MODE_FULL, NULL);
3587 #else
3588                 HILDON_INPUT_MODE_HINT,
3589                 HILDON_INPUT_MODE_HINT_ALPHANUMERICSPECIAL, NULL);
3590         g_object_set(G_OBJECT(mapman_info.txt_topleft_lon),
3591                 HILDON_AUTOCAP,
3592                 FALSE, NULL);
3593 #endif
3594 #endif
3595
3596         /* Bottom Right. */
3597         gtk_table_attach(GTK_TABLE(mapman_info.tbl_area),
3598                 label = gtk_label_new(_("Bottom-Right")),
3599                 0, 1, 4, 5, GTK_FILL, 0, 4, 0);
3600         gtk_misc_set_alignment(GTK_MISC(label), 1.f, 0.5f);
3601         gtk_table_attach(GTK_TABLE(mapman_info.tbl_area),
3602                 mapman_info.txt_botright_lat = gtk_entry_new(),
3603                 1, 2, 4, 5, GTK_FILL, 0, 4, 0);
3604         gtk_entry_set_width_chars(GTK_ENTRY(mapman_info.txt_botright_lat), 12);
3605         gtk_entry_set_alignment(GTK_ENTRY(mapman_info.txt_botright_lat), 1.f);
3606 #ifdef MAEMO_CHANGES
3607         g_object_set(G_OBJECT(mapman_info.txt_botright_lat),
3608 #ifndef LEGACY
3609                 "hildon-input-mode",
3610                 HILDON_GTK_INPUT_MODE_FULL, NULL);
3611 #else
3612                 HILDON_INPUT_MODE_HINT,
3613                 HILDON_INPUT_MODE_HINT_ALPHANUMERICSPECIAL, NULL);
3614         g_object_set(G_OBJECT(mapman_info.txt_botright_lat),
3615                 HILDON_AUTOCAP,
3616                 FALSE, NULL);
3617 #endif
3618 #endif
3619         gtk_table_attach(GTK_TABLE(mapman_info.tbl_area),
3620                 mapman_info.txt_botright_lon = gtk_entry_new(),
3621                 2, 3, 4, 5, GTK_FILL, 0, 4, 0);
3622         gtk_entry_set_width_chars(GTK_ENTRY(mapman_info.txt_botright_lat), 12);
3623         gtk_entry_set_alignment(GTK_ENTRY(mapman_info.txt_botright_lon), 1.f);
3624 #ifdef MAEMO_CHANGES
3625         g_object_set(G_OBJECT(mapman_info.txt_botright_lon),
3626 #ifndef LEGACY
3627                 "hildon-input-mode",
3628                 HILDON_GTK_INPUT_MODE_FULL, NULL);
3629 #else
3630                 HILDON_INPUT_MODE_HINT,
3631                 HILDON_INPUT_MODE_HINT_ALPHANUMERICSPECIAL, NULL);
3632         g_object_set(G_OBJECT(mapman_info.txt_botright_lon),
3633                 HILDON_AUTOCAP,
3634                 FALSE, NULL);
3635 #endif
3636 #endif
3637
3638         /* Default action is to download by area. */
3639         gtk_toggle_button_set_active(
3640                 GTK_TOGGLE_BUTTON(mapman_info.rad_by_area), TRUE);
3641
3642         g_signal_connect(G_OBJECT(mapman_info.rad_download), "clicked",
3643                           G_CALLBACK(mapman_update_state), &mapman_info);
3644         g_signal_connect(G_OBJECT(mapman_info.rad_delete), "clicked",
3645                           G_CALLBACK(mapman_update_state), &mapman_info);
3646         g_signal_connect(G_OBJECT(mapman_info.rad_by_area), "clicked",
3647                           G_CALLBACK(mapman_update_state), &mapman_info);
3648         g_signal_connect(G_OBJECT(mapman_info.rad_by_route), "clicked",
3649                           G_CALLBACK(mapman_update_state), &mapman_info);
3650     }
3651
3652     /* Initialize fields.  Do no use g_ascii_formatd; these strings will be
3653      * output (and parsed) as locale-dependent. */
3654
3655     gtk_widget_set_sensitive(mapman_info.rad_by_route,
3656             _route.head != _route.tail);
3657
3658     lat_format(_gps.lat, buffer);
3659     gtk_label_set_text(GTK_LABEL(lbl_gps_lat), buffer);
3660     lon_format(_gps.lon, buffer);
3661     gtk_label_set_text(GTK_LABEL(lbl_gps_lon), buffer);
3662
3663     unit2latlon(_center.unitx, _center.unity, lat, lon);
3664     lat_format(lat, buffer);
3665     gtk_label_set_text(GTK_LABEL(lbl_center_lat), buffer);
3666     lon_format(lon, buffer);
3667     gtk_label_set_text(GTK_LABEL(lbl_center_lon), buffer);
3668
3669     /* Initialize to the bounds of the screen. */
3670     unit2latlon(
3671             _center.unitx - pixel2unit(MAX(_view_width_pixels,
3672                     _view_height_pixels) / 2),
3673             _center.unity - pixel2unit(MAX(_view_width_pixels,
3674                     _view_height_pixels) / 2), lat, lon);
3675     BOUND(lat, -90.f, 90.f);
3676     BOUND(lon, -180.f, 180.f);
3677     lat_format(lat, buffer);
3678     gtk_entry_set_text(GTK_ENTRY(mapman_info.txt_topleft_lat), buffer);
3679     lon_format(lon, buffer);
3680     gtk_entry_set_text(GTK_ENTRY(mapman_info.txt_topleft_lon), buffer);
3681
3682     unit2latlon(
3683             _center.unitx + pixel2unit(MAX(_view_width_pixels,
3684                     _view_height_pixels) / 2),
3685             _center.unity + pixel2unit(MAX(_view_width_pixels,
3686                     _view_height_pixels) / 2), lat, lon);
3687     BOUND(lat, -90.f, 90.f);
3688     BOUND(lon, -180.f, 180.f);
3689     lat_format(lat, buffer);
3690     gtk_entry_set_text(GTK_ENTRY(mapman_info.txt_botright_lat), buffer);
3691     lon_format(lon, buffer);
3692     gtk_entry_set_text(GTK_ENTRY(mapman_info.txt_botright_lon), buffer);
3693
3694     /* Initialize zoom levels. */
3695     {
3696         gint i;
3697         for(i = 0; i <= MAX_ZOOM; i++)
3698         {
3699             gtk_toggle_button_set_active(
3700                     GTK_TOGGLE_BUTTON(mapman_info.chk_zoom_levels[i]), FALSE);
3701         }
3702     }
3703     gtk_toggle_button_set_active(
3704             GTK_TOGGLE_BUTTON(mapman_info.chk_zoom_levels[
3705                 _zoom + (_curr_repo->double_size ? 1 : 0)]), TRUE);
3706
3707     gtk_widget_show_all(dialog);
3708
3709     mapman_update_state(NULL, &mapman_info);
3710
3711     if(_curr_repo->type != REPOTYPE_NONE)
3712     {
3713         gtk_widget_set_sensitive(mapman_info.rad_download, TRUE);
3714     }
3715     else
3716     {
3717         gtk_widget_set_sensitive(mapman_info.rad_download, FALSE);
3718         popup_error(dialog,
3719                 _("NOTE: You must set a Map URI in the current repository in "
3720                     "order to download maps."));
3721     }
3722
3723     while(GTK_RESPONSE_ACCEPT == gtk_dialog_run(GTK_DIALOG(dialog)))
3724     {
3725         MapUpdateType update_type;
3726         static gint8 download_batch_id = INT8_MIN;
3727
3728         if(gtk_toggle_button_get_active(
3729                     GTK_TOGGLE_BUTTON(mapman_info.rad_delete)))
3730             update_type = MAP_UPDATE_DELETE;
3731         else if(gtk_toggle_button_get_active(
3732                 GTK_TOGGLE_BUTTON(mapman_info.chk_overwrite)))
3733             update_type = MAP_UPDATE_OVERWRITE;
3734         else
3735             update_type = MAP_UPDATE_ADD;
3736
3737         ++download_batch_id;
3738         if(gtk_toggle_button_get_active(
3739                     GTK_TOGGLE_BUTTON(mapman_info.rad_by_route)))
3740         {
3741             if(mapman_by_route(&mapman_info, update_type, download_batch_id))
3742                 break;
3743         }
3744         else
3745         {
3746             const gchar *text;
3747             gchar *error_check;
3748             gdouble start_lat, start_lon, end_lat, end_lon;
3749
3750             text = gtk_entry_get_text(GTK_ENTRY(mapman_info.txt_topleft_lat));
3751             start_lat = strdmstod(text, &error_check);
3752             if(text == error_check || start_lat < -90. || start_lat > 90.) {
3753                 popup_error(dialog, _("Invalid Top-Left Latitude"));
3754                 continue;
3755             }
3756
3757             text = gtk_entry_get_text(GTK_ENTRY(mapman_info.txt_topleft_lon));
3758             start_lon = strdmstod(text, &error_check);
3759             if(text == error_check || start_lon < -180. || start_lon>180.) {
3760                 popup_error(dialog, _("Invalid Top-Left Longitude"));
3761                 continue;
3762             }
3763
3764             text = gtk_entry_get_text(GTK_ENTRY(mapman_info.txt_botright_lat));
3765             end_lat = strdmstod(text, &error_check);
3766             if(text == error_check || end_lat < -90. || end_lat > 90.) {
3767                 popup_error(dialog, _("Invalid Bottom-Right Latitude"));
3768                 continue;
3769             }
3770
3771             text = gtk_entry_get_text(GTK_ENTRY(mapman_info.txt_botright_lon));
3772             end_lon = strdmstod(text, &error_check);
3773             if(text == error_check || end_lon < -180. || end_lon > 180.) {
3774                 popup_error(dialog,_("Invalid Bottom-Right Longitude"));
3775                 continue;
3776             }
3777
3778             if(mapman_by_area(start_lat, start_lon, end_lat, end_lon,
3779                         &mapman_info, update_type, download_batch_id))
3780                 break;
3781         }
3782     }
3783
3784     gtk_widget_hide(dialog);
3785
3786     vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__);
3787     return TRUE;
3788 }
3789
3790
3791 /* changes visibility of current repo's layers to it's previous state */
3792 void maps_toggle_visible_layers ()
3793 {
3794     RepoData *rd = _curr_repo;
3795     gboolean changed = FALSE;
3796
3797     printf("%s()\n", __PRETTY_FUNCTION__);
3798
3799     if (!rd) {
3800         vprintf("%s(): return\n", __PRETTY_FUNCTION__);
3801         return;
3802     }
3803
3804     rd = rd->layers;
3805
3806     while (rd) {
3807         if (rd->layer_enabled) {
3808             changed = TRUE;
3809             rd->layer_was_enabled = rd->layer_enabled;
3810             rd->layer_enabled = FALSE;
3811         }
3812         else {
3813             rd->layer_enabled = rd->layer_was_enabled;
3814             if (rd->layer_was_enabled)
3815                 changed = TRUE;
3816         }
3817
3818         rd = rd->layers;
3819     }
3820
3821     /* redraw map */
3822     if (changed) {
3823         menu_layers_remove_repos ();
3824         menu_layers_add_repos ();
3825         map_cache_clean ();
3826         map_refresh_mark (TRUE);
3827     }
3828
3829     vprintf("%s(): return\n", __PRETTY_FUNCTION__);
3830 }
3831
3832
3833
3834 /* this routine fired by timer every minute and decrements refetch counter of every active layer of
3835    current repository. If one of layer is expired, it forces map redraw. Redraw routine checks every
3836    layer's tile download timestamp and desides performs refetch if needed */
3837 gboolean
3838 map_layer_refresh_cb (gpointer data)
3839 {
3840     RepoData* rd = _curr_repo;
3841     gboolean refresh = FALSE;
3842     printf("%s()\n", __PRETTY_FUNCTION__);
3843
3844     if (rd) {
3845         rd = rd->layers;
3846
3847         while (rd) {
3848             if (rd->layer_enabled && rd->layer_refresh_interval) {
3849                 rd->layer_refresh_countdown--;
3850                 if (rd->layer_refresh_countdown <= 0) {
3851                     rd->layer_refresh_countdown = rd->layer_refresh_interval;
3852                     refresh = TRUE;
3853                 }
3854             }
3855
3856             rd = rd->layers;
3857         }
3858     }
3859
3860     if (refresh)
3861         map_refresh_mark (TRUE);
3862
3863     vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__);
3864     return TRUE;
3865 }
3866
3867
3868
3869 /*
3870    Returns amount of seconds since tile downloaded or 0 if tile
3871    have no such information.
3872 */
3873 gint get_tile_age (GdkPixbuf* pixbuf)
3874 {
3875     const char* ts;
3876     guint val;
3877
3878     ts = gdk_pixbuf_get_option (pixbuf, layer_timestamp_key);
3879
3880     if (!ts)
3881         return 0;
3882
3883     if (sscanf (ts, "%u", &val))
3884         return time (NULL) - val;
3885     else
3886         return 0;
3887 }