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