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