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