]> git.itanic.dy.fi Git - maemo-mapper/commitdiff
Added support for layered maps. This support is still very
authorgnuite <gnuite@gmail.com>
Sun, 12 Oct 2008 03:06:36 +0000 (03:06 +0000)
committergnuite <gnuite@gmail.com>
Sun, 12 Oct 2008 03:06:36 +0000 (03:06 +0000)
experimental, and the potential for crashes is rather large.  Still,
there is demand for it, so I'm posting it early.  By default, layers are
disabled, so it should not affect normal users.

git-svn-id: svn+ssh://garage/var/lib/gforge/svnroot/maemo-mapper/trunk@211 6c538b50-5814-0410-93ad-8bdf4c0149d1

src/data.c
src/data.h
src/display.c
src/input.c
src/main.c
src/maps.c
src/maps.h
src/menu.c
src/menu.h
src/settings.c
src/types.h

index deb2895f05e6e7ae27fb23b42693f4d7a62512b5..fc68babc204cce591b6d690f93ee8ad36b4613bb 100644 (file)
@@ -159,6 +159,10 @@ GMutex *_mut_priority_mutex = NULL;
 GThreadPool *_mut_thread_pool = NULL;
 GThreadPool *_mrt_thread_pool = NULL;
 
+/* Need to refresh map after downloads finished. This is needed when during render task we find tile
+   to download and we have something to draw on top of it. */
+gboolean _refresh_map_after_download = FALSE;
+
 /** CONFIGURATION INFORMATION. */
 GpsRcvrInfo _gri = { 0, 0, 0, 0, 0 };
 ConnState _gps_state;
@@ -248,9 +252,11 @@ GtkWidget *_menu_poi_categories_item = NULL;
 
 /* Menu items for the "Maps" submenu. */
 GtkWidget *_menu_maps_submenu = NULL;
+GtkWidget *_menu_layers_submenu = NULL;
 GtkWidget *_menu_maps_mapman_item = NULL;
-GtkWidget *_menu_maps_repoman_item = NULL;
 GtkWidget *_menu_maps_auto_download_item = NULL;
+GtkWidget *_menu_maps_repoman_item = NULL;
+GtkWidget *_menu_maps_repodown_item = NULL;
 
 /* Menu items for the "View" submenu. */
 GtkWidget *_menu_view_zoom_in_item = NULL;
index e27aa26bf6532ed06f10221d6d0773456045d687..1ac3b3da1678ca35da68ca574e4559d7b3aaf3cb 100644 (file)
@@ -137,6 +137,7 @@ extern GTree *_mut_priority_tree;
 extern GMutex *_mut_priority_mutex;
 extern GThreadPool *_mut_thread_pool;
 extern GThreadPool *_mrt_thread_pool;
+extern gboolean _refresh_map_after_download;
 
 /** CONFIGURATION INFORMATION. */
 extern GpsRcvrInfo _gri;
@@ -227,9 +228,11 @@ extern GtkWidget *_menu_poi_categories_item;
 
 /* Menu items for the "Maps" submenu. */
 extern GtkWidget *_menu_maps_submenu;
+extern GtkWidget *_menu_layers_submenu;
 extern GtkWidget *_menu_maps_mapman_item;
-extern GtkWidget *_menu_maps_repoman_item;
 extern GtkWidget *_menu_maps_auto_download_item;
+extern GtkWidget *_menu_maps_repoman_item;
+extern GtkWidget *_menu_maps_repodown_item;
 
 /* Menu items for the "View" submenu. */
 extern GtkWidget *_menu_view_zoom_in_item;
index 60e61c80a8aa04086e5bba9137cb8a05f1ba92e9..f1182652d78aeeb3a4dbf7f70b6e2e0e7a8bc4e7 100644 (file)
@@ -1420,7 +1420,7 @@ map_download_refresh_idle(MapUpdateTask *mut)
             mut->zoom, mut->tilex, mut->tiley);
 
     /* Test if download succeeded (only if retries != 0). */
-    if(mut->pixbuf && mut->repo == _curr_repo)
+    if(mut->pixbuf)
     {
         gint zoff = mut->zoom - _zoom;
         /* Update the UI to reflect the updated map database. */
@@ -1496,13 +1496,16 @@ map_download_refresh_idle(MapUpdateTask *mut)
 #endif
         if(_dl_errors)
         {
-            gchar buffer[BUFFER_SIZE];
-            snprintf(buffer, sizeof(buffer), "%d %s", _dl_errors,
-                    _("maps failed to download."));
-            MACRO_BANNER_SHOW_INFO(_window, buffer);
+            if (mut->repo->layer_level == 0) {
+                gchar buffer[BUFFER_SIZE];
+                snprintf(buffer, sizeof(buffer), "%d %s", _dl_errors,
+                         _("maps failed to download."));
+                MACRO_BANNER_SHOW_INFO(_window, buffer);
+            }
             _dl_errors = 0;
         }
-        else if(mut->update_type != MAP_UPDATE_AUTO)
+
+        if(mut->update_type != MAP_UPDATE_AUTO || _refresh_map_after_download)
         {
             /* Update the map. */
             map_refresh_mark(TRUE);
@@ -1599,6 +1602,64 @@ map_replace_pixbuf_idle(MapRenderTask *mrt)
     return FALSE;
 }
 
+
+/* Routine draws one partly-transparent pixbuf on top of the another (base map). For efficiency, we
+   assume that base map's tile have no transparent pixels (because it should not have them). We also
+   assume that pixbufs are have the same size. */
+static void
+combine_tiles (GdkPixbuf *dst_pixbuf, GdkPixbuf *src_pixbuf)
+{
+    gint s_n_channels = gdk_pixbuf_get_n_channels (src_pixbuf);
+    gint d_n_channels = gdk_pixbuf_get_n_channels (dst_pixbuf);
+    gint bps = gdk_pixbuf_get_bits_per_sample (dst_pixbuf);
+    gint width, height, x, y, d_delta, s_delta;
+    guchar *d_p, *s_p;
+
+    if (gdk_pixbuf_get_colorspace (dst_pixbuf) != gdk_pixbuf_get_colorspace (src_pixbuf)) {
+        printf ("combine return (1)\n");
+        return;
+    }
+    if (gdk_pixbuf_get_colorspace (dst_pixbuf) != GDK_COLORSPACE_RGB) {
+        printf ("combine return (2)\n");
+        return;
+    }
+
+    if (bps != gdk_pixbuf_get_bits_per_sample (src_pixbuf)) {
+        printf ("combine return (5)\n");
+        return;
+    }
+
+    width = gdk_pixbuf_get_width (dst_pixbuf);
+    height = gdk_pixbuf_get_height (dst_pixbuf);
+
+    if (width != gdk_pixbuf_get_width (src_pixbuf)) {
+        printf ("combine return (6)\n");
+        return;
+    }
+    if (height != gdk_pixbuf_get_height (src_pixbuf)) {
+        printf ("combine return (7)\n");
+        return;
+    }
+
+    s_delta = (bps >> 3) * s_n_channels;
+    d_delta = (bps >> 3) * d_n_channels;
+    d_p = gdk_pixbuf_get_pixels (dst_pixbuf);
+    s_p = gdk_pixbuf_get_pixels (src_pixbuf);
+
+    /* ok, we're ready to combine */
+    for (y = 0; y < height; y++) {
+        for (x = 0; x < width; x++, d_p += d_delta, s_p += s_delta) {
+            /* TODO: alpha blending? */
+            if (s_n_channels == 3 || s_p[3]) {
+                d_p[0] = s_p[0];
+                d_p[1] = s_p[1];
+                d_p[2] = s_p[2];
+            }
+        }
+    }
+}
+
+
 gboolean
 thread_render_map(MapRenderTask *mrt)
 {
@@ -1716,6 +1777,7 @@ thread_render_map(MapRenderTask *mrt)
 
     mrt->pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, FALSE, 8,
             mrt->screen_width_pixels, mrt->screen_height_pixels);
+    _refresh_map_after_download = FALSE;
 
     /* Iterate through the tiles, get them (or queue a download if they're
      * not in the cache), and rotate them into the pixbuf. */
@@ -1724,93 +1786,145 @@ thread_render_map(MapRenderTask *mrt)
         gint tiley = y + start_tiley;
         for(x = 0; x < num_tilex; ++x)
         {
-            GdkPixbuf *tile_pixbuf = NULL;
+            GdkPixbuf *tile_pixbuf = NULL, *layer_pixbuf = NULL;
             gboolean started_download = FALSE;
-            gint zoff;
+            gint zoff, zoff_base;
             gint tilex;
+            RepoData* repo_p = mrt->repo;
 
             tilex = x + start_tilex;
+            zoff_base = mrt->repo->double_size ? 1 : 0;
 
-            zoff = mrt->repo->double_size ? 1 : 0;
-
-            /* Iteratively try to retrieve a map to draw the tile. */
-            while((mrt->zoom + zoff) <= MAX_ZOOM && zoff < TILE_SIZE_P2)
+            /* iterating over tile and all it's layers */
+            while (repo_p)
             {
-                /* Check if we're actually going to draw this map. */
-                if(tile_dev[2 * (y*num_tilex + x)] != FLT_MAX)
-                {
-                    if(NULL != (tile_pixbuf = mapdb_get(
-                                    mrt->repo, mrt->zoom + zoff,
-                                    tilex >> zoff,
-                                    tiley >> zoff)))
-                    {
-                        /* Found a map. */
-                        break;
-                    }
-                }
-                /* Else we're not going to be drawing this map, so just check
-                 * if it's in the database. */
-                else if(mapdb_exists(
-                                    mrt->repo, mrt->zoom + zoff,
-                                    tilex >> zoff,
-                                    tiley >> zoff))
+                started_download = FALSE;
+
+                /* for layers we must use resolution of underlying map */
+                zoff = zoff_base;
+
+                /* if this is not a bottom layer and layer not enabled, skip it */
+                if (repo_p != mrt->repo && !repo_p->layer_enabled)
                 {
-                    break;
+                    repo_p = repo_p->layers;
+                    continue;
                 }
 
-                /* No map; download, if we should. */
-                if(!started_download && _auto_download
-                        && mrt->repo->type != REPOTYPE_NONE
-                        /* Make sure this map is within dl zoom limits. */
-                        && ((unsigned)(mrt->zoom + zoff - mrt->repo->min_zoom)
-                            <= (mrt->repo->max_zoom - mrt->repo->min_zoom))
-                        /* Make sure this map matches the dl_zoom_steps,
-                         * or that there currently is no cache. */
-                        && (!mrt->repo->db || !((mrt->zoom + zoff
-                                    - (mrt->repo->double_size ? 1 : 0))
-                                % mrt->repo->dl_zoom_steps))
-                    /* Make sure this tile is even possible. */
-                    && ((unsigned)(tilex >> zoff)
-                            < unit2ztile(WORLD_SIZE_UNITS, mrt->zoom + zoff)
-                      && (unsigned)(tiley >> zoff)
-                            < unit2ztile(WORLD_SIZE_UNITS, mrt->zoom + zoff)))
+                /* Iteratively try to retrieve a map to draw the tile. */
+                while((mrt->zoom + zoff) <= MAX_ZOOM && zoff < TILE_SIZE_P2)
                 {
-                    started_download = TRUE;
+                    /* Check if we're actually going to draw this map. */
+                    if(tile_dev[2 * (y*num_tilex + x)] != FLT_MAX)
+                    {
+                        if(NULL != (layer_pixbuf = mapdb_get(
+                                        repo_p, mrt->zoom + zoff,
+                                        tilex >> zoff,
+                                        tiley >> zoff)))
+                        {
+                            /* Found a map. Check for it's age. */
+                            gint age = get_tile_age (layer_pixbuf);
+                            printf ("Tile age (%d)\n", age);
+
+                            /* throw away tile only if we can download something */
+                            if (!repo_p->layer_refresh_interval ||
+                                age < repo_p->layer_refresh_interval * 60 ||
+                                !_auto_download)
+                            {
+                                /* if this is a layer's tile, join with main tile */
+                                if (repo_p != mrt->repo)
+                                {
+                                    /* but only if main layer is exists */
+                                    if (tile_pixbuf)
+                                        combine_tiles (tile_pixbuf, layer_pixbuf);
+                                    g_object_unref (layer_pixbuf);
+                                }
+                                else {
+                                    tile_pixbuf = layer_pixbuf;
+                                    zoff_base = zoff;
+                                }
+                                break;
+                            }
+                            else
+                                g_object_unref (layer_pixbuf);
+                        }
+                        else
+                            if (repo_p->layers)
+                                _refresh_map_after_download = TRUE;
+                    }
+                    /* Else we're not going to be drawing this map, so just check
+                     * if it's in the database. */
+                    else if(mapdb_exists(
+                                        repo_p, mrt->zoom + zoff,
+                                        tilex >> zoff,
+                                        tiley >> zoff))
+                    {
+                        zoff_base = zoff;
+                        break;
+                    }
 
-                    if(!refresh_latch)
+                    /* No map; download, if we should. */
+                    if(!started_download && _auto_download
+                       && mrt->repo->type != REPOTYPE_NONE
+                       /* Make sure this map is within dl zoom limits. */
+                       && ((unsigned)(mrt->zoom + zoff - mrt->repo->min_zoom)
+                           <= (mrt->repo->max_zoom - mrt->repo->min_zoom))
+                       /* Make sure this map matches the dl_zoom_steps,
+                        * or that there currently is no cache. */
+                       && (!repo_p->db || !((mrt->zoom + zoff
+                                             - (mrt->repo->double_size ? 1 : 0))
+                                            % mrt->repo->dl_zoom_steps))
+                       /* Make sure this tile is even possible. */
+                       && ((unsigned)(tilex >> zoff)
+                           < unit2ztile(WORLD_SIZE_UNITS, mrt->zoom + zoff)
+                           && (unsigned)(tiley >> zoff)
+                           < unit2ztile(WORLD_SIZE_UNITS, mrt->zoom + zoff)))
                     {
-                        refresh_latch = g_slice_new(ThreadLatch);
-                        refresh_latch->is_open = FALSE;
-                        refresh_latch->is_done_adding_tasks = FALSE;
-                        refresh_latch->num_tasks = 1;
-                        refresh_latch->num_done = 0;
-                        refresh_latch->mutex = g_mutex_new();
-                        refresh_latch->cond = g_cond_new();
+                        started_download = TRUE;
+
+                        if(!refresh_latch)
+                        {
+                            refresh_latch = g_slice_new(ThreadLatch);
+                            refresh_latch->is_open = FALSE;
+                            refresh_latch->is_done_adding_tasks = FALSE;
+                            refresh_latch->num_tasks = 1;
+                            refresh_latch->num_done = 0;
+                            refresh_latch->mutex = g_mutex_new();
+                            refresh_latch->cond = g_cond_new();
+                        }
+                        else
+                            ++refresh_latch->num_tasks;
+
+                        mapdb_initiate_update(
+                                repo_p,
+                                mrt->zoom + zoff,
+                                tilex >> zoff,
+                                tiley >> zoff,
+                                MAP_UPDATE_AUTO,
+                                auto_download_batch_id,
+                                (abs((tilex >> zoff) - unit2ztile(
+                                     mrt->new_center.unitx, mrt->zoom + zoff))
+                                 + abs((tiley >> zoff) - unit2ztile(
+                                     mrt->new_center.unity, mrt->zoom + zoff))),
+                                refresh_latch);
                     }
+
+                    /* Try again at a coarser resolution. Only for underlying map.*/
+                    if (repo_p == mrt->repo)
+                        ++zoff;
                     else
-                        ++refresh_latch->num_tasks;
-
-                    mapdb_initiate_update(
-                            mrt->repo,
-                            mrt->zoom + zoff,
-                            tilex >> zoff,
-                            tiley >> zoff,
-                            MAP_UPDATE_AUTO,
-                            auto_download_batch_id,
-                            (abs((tilex >> zoff) - unit2ztile(
-                                     mrt->new_center.unitx, mrt->zoom + zoff))
-                             + abs((tiley >> zoff) - unit2ztile(
-                                    mrt->new_center.unity, mrt->zoom + zoff))),
-                            refresh_latch);
+                        break;
                 }
 
-                /* Try again at a coarser resolution. */
-                ++zoff;
+                repo_p = repo_p->layers;
             }
 
+            /* use zoom of the base map */
+            zoff = zoff_base;
+
             if(tile_pixbuf)
             {
                 gint boundx, boundy, width, height;
+
                 if(zoff)
                     gdk_pixbuf_rotate_matrix_mult_number(matrix, 1 << zoff);
                 gdk_pixbuf_rotate(mrt->pixbuf,
index 25a9b7b3067a9a3ca75d091c259f577512af1c45..bc48c1f9984324906dcb31ae78138563cdd46f58 100644 (file)
@@ -436,6 +436,10 @@ window_cb_key_press(GtkWidget* widget, GdkEventKey *event)
             _speed_limit_on ^= 1;
             break;
 
+        case CUSTOM_ACTION_TOGGLE_LAYERS:
+            maps_toggle_visible_layers ();
+            break;
+
         default:
             vprintf("%s(): return FALSE\n", __PRETTY_FUNCTION__);
             return FALSE;
index 9e44dbec4717340d8adf52af48f59f0f4e637286..5ce2abf349c819d5205b9880722a8b48e04bd96c 100644 (file)
@@ -203,6 +203,7 @@ maemo_mapper_destroy()
 
     if(_curr_repo->db)
     {
+        RepoData* repo_p;
 #ifdef MAPDB_SQLITE
         g_mutex_lock(_mapdb_mutex);
         sqlite3_close(_curr_repo->db);
@@ -210,8 +211,17 @@ maemo_mapper_destroy()
         g_mutex_unlock(_mapdb_mutex);
 #else
         g_mutex_lock(_mapdb_mutex);
-        gdbm_close(_curr_repo->db);
-        _curr_repo->db = NULL;
+        repo_p = _curr_repo;
+        while (repo_p) {
+            if (repo_p->db) {
+/*                 /\* perform reorganization for layers which are auto refreshed *\/ */
+/*                 if (repo_p->layer_level && repo_p->layer_refresh_interval) */
+/*                     gdbm_reorganize (repo_p->db); */
+                gdbm_close(repo_p->db);
+            }
+            repo_p->db = NULL;
+            repo_p = repo_p->layers;
+        }
         g_mutex_unlock(_mapdb_mutex);
 #endif
     }
@@ -333,6 +343,7 @@ maemo_mapper_init(gint argc, gchar **argv)
         = _("Toggle Speed Limit");
     CUSTOM_ACTION_ENUM_TEXT[CUSTOM_ACTION_RESET_BLUETOOTH]
         = _("Reset Bluetooth");
+    CUSTOM_ACTION_ENUM_TEXT[CUSTOM_ACTION_TOGGLE_LAYERS] = _("Toggle Layers");
 
     DEG_FORMAT_ENUM_TEXT[DDPDDDDD] = "-dd.ddddd°";
     DEG_FORMAT_ENUM_TEXT[DD_MMPMMM] = "-dd°mm.mmm'";
index cd24a1e7da97b92ee41b752d58fbc3e53be48921..3a7cc3e588f5b35accd1de002807353471b3d91b 100644 (file)
@@ -33,6 +33,7 @@
 #include <glib/gstdio.h>
 #include <fcntl.h>
 #include <locale.h>
+#include <time.h>
 
 #ifndef LEGACY
 #    include <hildon/hildon-help.h>
@@ -84,8 +85,34 @@ struct _RepoEditInfo {
     GtkWidget *num_min_zoom;
     GtkWidget *num_max_zoom;
     BrowseInfo browse_info;
+    RepoData *repo;
 };
 
+
+typedef struct _RepoLayersInfo RepoLayersInfo;
+struct _RepoLayersInfo {
+    GtkWidget *dialog;
+    GtkWidget *notebook;
+    GtkListStore *layers_store;
+    GtkWidget *layers_list;
+    GList *layer_edits;
+};
+
+
+typedef struct _LayerEditInfo LayerEditInfo;
+struct _LayerEditInfo {
+    RepoLayersInfo *rli;
+
+    GtkWidget *txt_name;
+    GtkWidget *txt_url;
+    GtkWidget *txt_db;
+    GtkWidget *num_autofetch;
+    GtkWidget *chk_visible;
+    GtkWidget *vbox;
+};
+
+
+
 typedef struct _MapmanInfo MapmanInfo;
 struct _MapmanInfo {
     GtkWidget *dialog;
@@ -110,6 +137,7 @@ struct _MapmanInfo {
     GtkWidget *chk_zoom_levels[MAX_ZOOM + 1];
 };
 
+
 typedef struct _CompactInfo CompactInfo;
 struct _CompactInfo {
     GtkWidget *dialog;
@@ -160,6 +188,8 @@ struct _MapCache {
 
 static MapCache _map_cache;
 
+const gchar* layer_timestamp_key = "tEXt::mm_ts";
+
 
 static guint
 mapdb_get_data(RepoData *repo, gint zoom, gint tilex, gint tiley, gchar **data)
@@ -559,6 +589,17 @@ map_cache_destroy(void)
     g_mutex_unlock(_mapdb_mutex);
 }
 
+
+void
+map_cache_clean (void)
+{
+    g_mutex_lock(_mapdb_mutex);
+    if(_map_cache.entries != NULL)
+        g_hash_table_remove_all (_map_cache.entries);
+    g_mutex_unlock(_mapdb_mutex);
+}
+
+
 gboolean
 mapdb_exists(RepoData *repo, gint zoom, gint tilex, gint tiley)
 {
@@ -965,23 +1006,29 @@ repo_make_db(RepoData *rd)
 gboolean
 repo_set_curr(RepoData *rd)
 {
+    RepoData* repo_p;
+
     printf("%s()\n", __PRETTY_FUNCTION__);
     if(!rd->db_filename || !*rd->db_filename
             || repo_make_db(rd))
     {
-        if(_curr_repo)
+        repo_p = _curr_repo;
+
+        while (repo_p)
         {
-            if(_curr_repo->db)
+            if(repo_p->db)
             {
                 g_mutex_lock(_mapdb_mutex);
 #ifdef MAPDB_SQLITE
-                sqlite3_close(_curr_repo->db);
+                sqlite3_close(repo_p->db);
+                repo_p->db = NULL;
 #else
-                gdbm_close(_curr_repo->db);
+                gdbm_close(repo_p->db);
+                repo_p->db = NULL;
 #endif
-                _curr_repo->db = NULL;
                 g_mutex_unlock(_mapdb_mutex);
             }
+            repo_p = repo_p->layers;
         }
 
         /* Set the current repository! */
@@ -1089,8 +1136,16 @@ repo_set_curr(RepoData *rd)
                 popup_error(_window, buffer);
             }
 #else
-            _curr_repo->db = gdbm_open(_curr_repo->db_filename,
-                    0, GDBM_WRCREAT | GDBM_FAST, 0644, NULL);
+            /* initialize all databases for all layers */
+            repo_p = _curr_repo;
+            while (repo_p) {
+                if (repo_p->db_filename && *repo_p->db_filename
+                        && repo_make_db (repo_p))
+                    repo_p->db = gdbm_open(repo_p->db_filename,
+                            0, GDBM_WRCREAT | GDBM_FAST, 0644, NULL);
+                repo_p = repo_p->layers;
+            }
+
             if(!_curr_repo->db)
             {
                 gchar buffer[BUFFER_SIZE];
@@ -1121,6 +1176,24 @@ repo_set_curr(RepoData *rd)
     }
 }
 
+
+/**
+ * Returns true if:
+ * 1. base == layer, or
+ * 2. layer is sublayer of base
+ */
+gboolean repo_is_layer (RepoData* base, RepoData* layer)
+{
+    while (base) {
+        if (base == layer)
+            return TRUE;
+        base = base->layers;
+    }
+
+    return FALSE;
+}
+
+
 /**
  * Given a wms uri pattern, compute the coordinate transformation and
  * trimming.
@@ -1328,6 +1401,7 @@ mapdb_initiate_update(RepoData *repo, gint zoom, gint tilex, gint tiley,
     mut->tilex = tilex;
     mut->tiley = tiley;
     mut->update_type = update_type;
+    mut->layer_level = repo->layer_level;
 
     /* Lock the mutex if this is an auto-update. */
     if(update_type == MAP_UPDATE_AUTO)
@@ -1406,7 +1480,7 @@ thread_proc_mut()
     while(conic_ensure_connected())
     {
         gint retries;
-        gboolean refresh_sent = FALSE;
+        gboolean refresh_sent = FALSE, layer_tile;
         MapUpdateTask *mut = NULL;
 
         /* Get the next MUT from the mut tree. */
@@ -1426,7 +1500,9 @@ thread_proc_mut()
         printf("%s(%s, %d, %d, %d)\n", __PRETTY_FUNCTION__,
                 mut->repo->name, mut->zoom, mut->tilex, mut->tiley);
 
-        if(mut->repo != _curr_repo)
+        layer_tile = mut->repo != _curr_repo && repo_is_layer (_curr_repo, mut->repo);
+
+        if (mut->repo != _curr_repo && !layer_tile)
         {
             /* Do nothing, except report that there is no error. */
             mut->vfs_result = GNOME_VFS_OK;
@@ -1441,7 +1517,7 @@ thread_proc_mut()
             /* Report that there is no error. */
             mut->vfs_result = GNOME_VFS_OK;
         }
-        else for(retries = INITIAL_DOWNLOAD_RETRIES; retries > 0; --retries)
+        else for(retries = mut->repo->layer_level ? 1 : INITIAL_DOWNLOAD_RETRIES; retries > 0; --retries)
         {
             gboolean exists = FALSE;
             gchar *src_url;
@@ -1518,6 +1594,24 @@ thread_proc_mut()
             }
             g_object_unref(loader);
 
+            /* attach timestamp with loaded pixbuf */
+            {
+                gchar* new_bytes;
+                gsize new_size;
+                GError* error = NULL;
+                char ts_val[12];
+
+                sprintf (ts_val, "%u", (unsigned int)time (NULL));
+
+                /* update bytes with new, timestamped pixbuf */
+                if (gdk_pixbuf_save_to_buffer (mut->pixbuf, &new_bytes, &new_size, "png", &error, layer_timestamp_key, ts_val, NULL))
+                {
+                    g_free (bytes);
+                    bytes = new_bytes;
+                    size = new_size;
+                }
+            }
+
             /* Copy database-relevant mut data before we release it. */
             repo = mut->repo;
             zoom = mut->zoom;
@@ -1587,7 +1681,7 @@ thread_proc_mut()
 guint
 mut_exists_hashfunc(const MapUpdateTask *a)
 {
-    gint sum = a->zoom + a->tilex + a->tiley + a->update_type;
+    gint sum = a->zoom + a->tilex + a->tiley + a->update_type + a->layer_level;
     return g_int_hash(&sum);
 }
 
@@ -1597,7 +1691,8 @@ mut_exists_equalfunc(const MapUpdateTask *a, const MapUpdateTask *b)
     return (a->tilex == b->tilex
             && a->tiley == b->tiley
             && a->zoom == b->zoom
-            && a->update_type == b->update_type);
+            && a->update_type == b->update_type
+            && a->layer_level == b->layer_level);
 }
 
 gint
@@ -1611,6 +1706,9 @@ mut_priority_comparefunc(const MapUpdateTask *a, const MapUpdateTask *b)
     if(diff)
         return diff;
     diff = (a->priority - b->priority); /* Lower priority numbers first. */
+    if(diff)
+        return diff;
+    diff = (a->layer_level - b->layer_level); /* Lower layers first. */
     if(diff)
         return diff;
 
@@ -2058,6 +2156,10 @@ repoman_dialog_add_repo(RepoManInfo *rmi, gchar *name)
                     gtk_combo_box_get_model(GTK_COMBO_BOX(rmi->cmb_repos))),
                 NULL) - 1);
 
+    /* newly created repos keep this NULL in rei, indicating
+       that layes cannot be added so far */
+    rei->repo = NULL;
+
     vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__);
     return rei;
 }
@@ -2136,14 +2238,13 @@ repoman_reset(GtkWidget *widget, RepoManInfo *rmi)
     return TRUE;
 }
 
-static gboolean
-repoman_download(GtkWidget *widget, RepoManInfo *rmi)
+gboolean
+repoman_download()
 {
     GtkWidget *confirm;
     printf("%s()\n", __PRETTY_FUNCTION__);
 
-    confirm = hildon_note_new_confirmation(
-            GTK_WINDOW(rmi->dialog),
+    confirm = hildon_note_new_confirmation(GTK_WINDOW(_window),
             _("Maemo Mapper will now download and add a list of "
                 "possibly-duplicate repositories from the internet.  "
                 "Continue?"));
@@ -2159,10 +2260,11 @@ repoman_download(GtkWidget *widget, RepoManInfo *rmi)
 
         /* Get repo config file from www.gnuite.com. */
         if(GNOME_VFS_OK != (vfs_result = gnome_vfs_read_entire_file(
-                    "http://www.gnuite.com/nokia770/maemo-mapper/repos.txt",
+                    "http://www.gnuite.com/nokia770/maemo-mapper/"
+                    "repos-with-layers.txt",
                     &size, &bytes)))
         {
-            popup_error(rmi->dialog,
+            popup_error(_window,
                     _("An error occurred while retrieving the repositories.  "
                         "The web service may be temporarily down."));
             g_printerr("Error while download repositories: %s\n",
@@ -2171,41 +2273,26 @@ repoman_download(GtkWidget *widget, RepoManInfo *rmi)
         /* Parse each line as a reposotory. */
         else
         {
+            RepoData *prev_repo = NULL;
+            menu_maps_remove_repos();
             for(head = bytes; head && *head; head = tail)
             {
-                gchar buffer[BUFFER_SIZE];
                 RepoData *rd;
-                RepoEditInfo *rei;
                 tail = strchr(head, '\n');
                 *tail++ = '\0';
 
                 rd = settings_parse_repo(head);
-                snprintf(buffer, sizeof(buffer), "%s.db", rd->db_filename);
-                rei = repoman_dialog_add_repo(
-                        rmi, g_strdup(rd->name));
-                /* Initialize fields with data from the RepoData object. */
-                gtk_entry_set_text(GTK_ENTRY(rei->txt_url), rd->url);
-                gtk_entry_set_text(GTK_ENTRY(rei->txt_db_filename), buffer);
-                hildon_controlbar_set_value(
-                        HILDON_CONTROLBAR(rei->num_dl_zoom_steps),
-                        rd->dl_zoom_steps);
-                hildon_controlbar_set_value(
-                        HILDON_CONTROLBAR(rei->num_view_zoom_steps),
-                        rd->view_zoom_steps);
-                gtk_toggle_button_set_active(
-                        GTK_TOGGLE_BUTTON(rei->chk_double_size),
-                        rd->double_size);
-                gtk_toggle_button_set_active(
-                        GTK_TOGGLE_BUTTON(rei->chk_nextable),
-                        rd->nextable);
-                hildon_number_editor_set_value(
-                        HILDON_NUMBER_EDITOR(rei->num_min_zoom),
-                        rd->min_zoom);
-                hildon_number_editor_set_value(
-                        HILDON_NUMBER_EDITOR(rei->num_max_zoom),
-                        rd->max_zoom);
+                if (rd->layer_level == 0) {
+                    _repo_list = g_list_append(_repo_list, rd);
+                }
+                else
+                    prev_repo->layers = rd;
+
+                prev_repo = rd;
             }
             g_free(bytes);
+            menu_maps_add_repos();
+            settings_save();
         }
     }
     gtk_widget_destroy(confirm);
@@ -2214,6 +2301,534 @@ repoman_download(GtkWidget *widget, RepoManInfo *rmi)
     return TRUE;
 }
 
+
+static gint
+layer_get_page_index (RepoLayersInfo *rli, GtkTreeIter list_it)
+{
+    GtkTreePath *p1, *p2;
+    GtkTreeIter p;
+    gint index = 0;
+
+    gtk_tree_model_get_iter_first (GTK_TREE_MODEL (rli->layers_store), &p);
+
+    p1 = gtk_tree_model_get_path (GTK_TREE_MODEL (rli->layers_store), &list_it);
+    p2 = gtk_tree_model_get_path (GTK_TREE_MODEL (rli->layers_store), &p);
+
+    while (gtk_tree_path_compare (p1, p2) != 0) {
+        gtk_tree_path_next (p2);
+        index++;
+    }
+
+    gtk_tree_path_free (p1);
+    gtk_tree_path_free (p2);
+
+    return index;
+}
+
+
+static gboolean
+layer_name_changed (GtkWidget *entry, LayerEditInfo *lei)
+{
+    const gchar* name;
+    GtkTreeSelection *selection;
+    GtkTreeIter iter;
+
+    printf("%s()\n", __PRETTY_FUNCTION__);
+
+    /* take new name  */
+    name = gtk_entry_get_text (GTK_ENTRY (entry));
+
+    /* find selected entry in list view */
+    selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (lei->rli->layers_list));
+
+    if (!gtk_tree_selection_get_selected (selection, NULL, &iter)) {
+        vprintf("%s(): return FALSE\n", __PRETTY_FUNCTION__);
+        return FALSE;
+    }
+
+    gtk_list_store_set (lei->rli->layers_store, &iter, 0, name, -1);
+
+    vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__);
+    return TRUE;
+}
+
+
+static gboolean
+layer_dialog_browse (GtkWidget *widget, LayerEditInfo *lei)
+{
+    GtkWidget *dialog;
+    gchar *basename;
+    printf("%s()\n", __PRETTY_FUNCTION__);
+
+    dialog = GTK_WIDGET(
+            hildon_file_chooser_dialog_new(GTK_WINDOW(lei->rli->dialog),
+            GTK_FILE_CHOOSER_ACTION_SAVE));
+
+    gtk_file_chooser_set_uri(GTK_FILE_CHOOSER(dialog),
+            gtk_entry_get_text(GTK_ENTRY(lei->txt_db)));
+
+    /* Work around a bug in HildonFileChooserDialog. */
+    basename = g_path_get_basename(
+            gtk_entry_get_text(GTK_ENTRY(lei->txt_db)));
+    g_object_set(G_OBJECT(dialog), "autonaming", FALSE, NULL);
+    gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), basename);
+
+    if(GTK_RESPONSE_OK == gtk_dialog_run(GTK_DIALOG(dialog)))
+    {
+        gchar *filename = gtk_file_chooser_get_filename(
+                GTK_FILE_CHOOSER(dialog));
+        gtk_entry_set_text(GTK_ENTRY(lei->txt_db), filename);
+        g_free(filename);
+    }
+
+    gtk_widget_destroy(dialog);
+
+    vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__);
+    return TRUE;
+}
+
+
+
+static LayerEditInfo*
+repoman_layers_add_layer (RepoLayersInfo *rli, gchar* name)
+{
+    LayerEditInfo *lei = g_new (LayerEditInfo, 1);
+    GtkWidget *vbox;
+    GtkWidget *hbox2;
+    GtkWidget *table;
+    GtkWidget *label;
+    GtkWidget *btn_browse;
+    GtkTreeIter layers_iter;
+    
+    printf("%s(%s)\n", __PRETTY_FUNCTION__, name);
+
+    lei->rli = rli;
+
+    rli->layer_edits = g_list_append (rli->layer_edits, lei);
+
+    gtk_notebook_append_page (GTK_NOTEBOOK (rli->notebook), vbox = gtk_vbox_new (FALSE, 4),
+                              gtk_label_new (name));
+
+    gtk_box_pack_start (GTK_BOX (vbox), table = gtk_table_new (4, 2, FALSE),
+                        FALSE, FALSE, 0);
+
+    /* Layer name */
+    gtk_table_attach (GTK_TABLE (table), label = gtk_label_new (_("Name")),
+                      0, 1, 0, 1, GTK_FILL, 0, 2, 0);
+    gtk_misc_set_alignment (GTK_MISC (label), 1.f, 0.5f);
+    gtk_table_attach (GTK_TABLE (table), lei->txt_name = gtk_entry_new (),
+                      1, 2, 0, 1, GTK_EXPAND | GTK_FILL, 0, 2, 0);
+    gtk_entry_set_text (GTK_ENTRY (lei->txt_name), name);
+
+    /* signals */
+    g_signal_connect(G_OBJECT(lei->txt_name), "changed", G_CALLBACK(layer_name_changed), lei);
+
+    /* URL format */
+    gtk_table_attach (GTK_TABLE (table), label = gtk_label_new (_("URL")),
+                      0, 1, 1, 2, GTK_FILL, 0, 2, 0);
+    gtk_misc_set_alignment (GTK_MISC (label), 1.f, 0.5f);
+    gtk_table_attach (GTK_TABLE (table), lei->txt_url = gtk_entry_new (),
+                      1, 2, 1, 2, GTK_EXPAND | GTK_FILL, 0, 2, 0);
+
+    /* Map directory */
+    gtk_table_attach (GTK_TABLE (table), label = gtk_label_new (_("Cache DB")),
+                      0, 1, 2, 3, GTK_FILL, 0, 2, 0);
+    gtk_misc_set_alignment (GTK_MISC (label), 1.f, 0.5f);
+    gtk_table_attach (GTK_TABLE (table), hbox2 = gtk_hbox_new (FALSE, 4),
+                      1, 2, 2, 3, GTK_EXPAND | GTK_FILL, 0, 2, 0);
+    gtk_box_pack_start (GTK_BOX (hbox2), lei->txt_db = gtk_entry_new (),
+                        TRUE, TRUE, 0);
+    gtk_box_pack_start (GTK_BOX (hbox2), btn_browse = gtk_button_new_with_label (_("Browse...")),
+                        FALSE, FALSE, 0);
+
+    g_signal_connect(G_OBJECT(btn_browse), "clicked", G_CALLBACK(layer_dialog_browse), lei);
+
+    /* Autorefresh */
+    gtk_table_attach (GTK_TABLE (table), label = gtk_label_new (_("Autofetch")),
+                      0, 1, 3, 4, GTK_FILL, 0, 2, 0);
+    gtk_misc_set_alignment(GTK_MISC(label), 1.f, 0.5f);
+    gtk_table_attach (GTK_TABLE (table), hbox2 = gtk_hbox_new (FALSE, 4),
+                      1, 2, 3, 4, GTK_EXPAND | GTK_FILL, 0, 2, 0);
+    gtk_box_pack_start (GTK_BOX (hbox2), lei->num_autofetch = hildon_number_editor_new (0, 120),
+                        FALSE, FALSE, 4);
+    gtk_box_pack_start (GTK_BOX (hbox2), label = gtk_label_new (_("min.")), FALSE, FALSE, 4);
+    gtk_misc_set_alignment(GTK_MISC(label), 1.f, 0.5f);
+
+    /* Visible */
+    gtk_box_pack_start (GTK_BOX (vbox), lei->chk_visible = gtk_check_button_new_with_label (_("Layer is visible")),
+                        FALSE, FALSE, 4);
+
+    gtk_widget_show_all (vbox);
+
+    /* Side list view with layers */
+    gtk_list_store_append (rli->layers_store, &layers_iter);
+    gtk_list_store_set (rli->layers_store, &layers_iter, 0, name, -1);
+
+    vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__);
+
+    return lei;
+}
+
+
+
+static gboolean
+repoman_layers_new (GtkWidget *widget, RepoLayersInfo *rli)
+{
+    static GtkWidget *hbox = NULL;
+    static GtkWidget *label = NULL;
+    static GtkWidget *txt_name = NULL;
+    static GtkWidget *dialog = NULL;
+    printf("%s()\n", __PRETTY_FUNCTION__);
+
+    if(dialog == NULL)
+    {
+        dialog = gtk_dialog_new_with_buttons(_("New Layer"),
+                GTK_WINDOW(rli->dialog), GTK_DIALOG_MODAL,
+                GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
+                GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
+                NULL);
+
+        gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox),
+                hbox = gtk_hbox_new(FALSE, 4), FALSE, FALSE, 4);
+
+        gtk_box_pack_start(GTK_BOX(hbox),
+                label = gtk_label_new(_("Name")),
+                FALSE, FALSE, 0);
+        gtk_box_pack_start(GTK_BOX(hbox),
+                txt_name = gtk_entry_new(),
+                TRUE, TRUE, 0);
+    }
+
+    gtk_entry_set_text(GTK_ENTRY(txt_name), "");
+
+    gtk_widget_show_all(dialog);
+
+    while(GTK_RESPONSE_ACCEPT == gtk_dialog_run(GTK_DIALOG(dialog)))
+    {
+        repoman_layers_add_layer(rli,
+                g_strdup(gtk_entry_get_text(GTK_ENTRY(txt_name))));
+        break;
+    }
+
+    gtk_widget_hide(dialog);
+    vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__);
+    return TRUE;
+}
+
+
+static gboolean
+repoman_layers_del (GtkWidget *widget, RepoLayersInfo *rli)
+{
+    GtkTreeIter iter;
+    GtkTreeSelection *selection;
+    gint index;
+
+    printf("%s()\n", __PRETTY_FUNCTION__);
+
+    /* delete list item */
+    selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (rli->layers_list));
+
+    if (!gtk_tree_selection_get_selected (selection, NULL, &iter)) {
+        vprintf("%s(): return FALSE\n", __PRETTY_FUNCTION__);
+        return FALSE;
+    }
+
+    index = layer_get_page_index (rli, iter);
+    gtk_list_store_remove (rli->layers_store, &iter);
+
+    rli->layer_edits = g_list_remove_link (rli->layer_edits, g_list_nth (rli->layer_edits, index));
+
+    /* delete notebook page */
+    gtk_notebook_remove_page (GTK_NOTEBOOK (rli->notebook), index);
+
+    vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__);
+    return TRUE;
+}
+
+
+static gboolean
+repoman_layers_up (GtkWidget *widget, RepoLayersInfo *rli)
+{
+    GtkTreeSelection *selection;
+    GtkTreeIter iter, iter2;
+    GtkTreePath *path;
+    gint page;
+    LayerEditInfo *lei;
+    GList *list_elem;
+
+    printf("%s()\n", __PRETTY_FUNCTION__);
+
+    /* find selected entry in list view */
+    selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (rli->layers_list));
+
+    if (!gtk_tree_selection_get_selected (selection, NULL, &iter)) {
+        vprintf("%s(): return FALSE\n", __PRETTY_FUNCTION__);
+        return FALSE;
+    }
+
+    iter2 = iter;
+    path = gtk_tree_model_get_path (GTK_TREE_MODEL (rli->layers_store), &iter);
+    if (!gtk_tree_path_prev (path) || !gtk_tree_model_get_iter (GTK_TREE_MODEL (rli->layers_store), &iter, path)) {
+        vprintf("%s(): return FALSE\n", __PRETTY_FUNCTION__);
+        return FALSE;
+    }
+
+    gtk_tree_path_free (path);
+
+    /* move it up */
+    gtk_list_store_move_before (rli->layers_store, &iter2, &iter);
+
+    /* reorder notebook tabs */
+    page = gtk_notebook_get_current_page (GTK_NOTEBOOK (rli->notebook));
+    gtk_notebook_reorder_child (GTK_NOTEBOOK (rli->notebook), gtk_notebook_get_nth_page (GTK_NOTEBOOK (rli->notebook), page), page-1);
+
+    /* reorder layer edits */
+    list_elem = g_list_nth (rli->layer_edits, page);
+    lei = list_elem->data;
+    rli->layer_edits = g_list_remove_link (rli->layer_edits, list_elem);
+    rli->layer_edits = g_list_insert (rli->layer_edits, lei, page-1);
+
+    vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__);
+    return TRUE;
+}
+
+
+static gboolean
+repoman_layers_dn (GtkWidget *widget, RepoLayersInfo *rli)
+{
+    GtkTreeSelection *selection;
+    GtkTreeIter iter, iter2;
+    gint page;
+    LayerEditInfo *lei;
+    GList *list_elem;
+
+    printf("%s()\n", __PRETTY_FUNCTION__);
+
+    /* find selected entry in list view */
+    selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (rli->layers_list));
+
+    if (!gtk_tree_selection_get_selected (selection, NULL, &iter)) {
+        vprintf("%s(): return FALSE\n", __PRETTY_FUNCTION__);
+        return FALSE;
+    }
+
+    iter2 = iter;
+    if (!gtk_tree_model_iter_next (GTK_TREE_MODEL (rli->layers_store), &iter)) {
+        vprintf("%s(): return FALSE\n", __PRETTY_FUNCTION__);
+        return FALSE;
+    }
+
+    /* move it down */
+    gtk_list_store_move_after (rli->layers_store, &iter2, &iter);
+
+    /* reorder notebook tabs */
+    page = gtk_notebook_get_current_page (GTK_NOTEBOOK (rli->notebook));
+    gtk_notebook_reorder_child (GTK_NOTEBOOK (rli->notebook), gtk_notebook_get_nth_page (GTK_NOTEBOOK (rli->notebook), page), page+1);
+
+    /* reorder layer edits */
+    list_elem = g_list_nth (rli->layer_edits, page);
+    lei = list_elem->data;
+    rli->layer_edits = g_list_remove_link (rli->layer_edits, list_elem);
+    rli->layer_edits = g_list_insert (rli->layer_edits, lei, page+1);
+
+    vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__);
+    return TRUE;
+}
+
+
+static gboolean
+repoman_layer_selected (GtkTreeSelection *selection, RepoLayersInfo *rli)
+{
+    GtkTreeIter cur;
+
+    printf("%s()\n", __PRETTY_FUNCTION__);
+
+    if (!gtk_tree_selection_get_selected (selection, NULL, &cur)) {
+        vprintf("%s(): return FALSE\n", __PRETTY_FUNCTION__);
+        return FALSE;
+    }
+
+    gtk_notebook_set_current_page (GTK_NOTEBOOK (rli->notebook), layer_get_page_index (rli, cur));
+
+    vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__);
+    return TRUE;
+}
+
+
+static gboolean
+repoman_layers(GtkWidget *widget, RepoManInfo *rmi)
+{
+    GtkWidget *hbox = NULL;
+    GtkWidget *layers_vbox = NULL;
+    GtkWidget *buttons_hbox = NULL;
+    GtkWidget *frame;
+    GtkCellRenderer *layers_rendeder = NULL;
+    GtkTreeViewColumn *layers_column = NULL;
+    GtkTreeSelection *selection;
+
+    /* layers buttons */
+    GtkWidget *btn_new = NULL;
+    GtkWidget *btn_del = NULL;
+    GtkWidget *btn_up = NULL;
+    GtkWidget *btn_dn = NULL;
+
+    const char* t_header = _("Manage layers [%s]");
+    char* header = NULL;
+    RepoEditInfo* rei = NULL;
+    RepoLayersInfo rli;
+    gint curr_repo_index = gtk_combo_box_get_active (GTK_COMBO_BOX (rmi->cmb_repos));
+    RepoData *rd;
+
+    printf("%s()\n", __PRETTY_FUNCTION__);
+
+    if (curr_repo_index < 0) {
+        vprintf("%s(): return FALSE (1)\n", __PRETTY_FUNCTION__);
+        return FALSE;
+    }
+
+    rei = g_list_nth_data (rmi->repo_edits, curr_repo_index);
+
+    if (!rei) {
+        vprintf("%s(): return FALSE (2)\n", __PRETTY_FUNCTION__);
+        return FALSE;
+    }
+
+    /* check that rei have repo data structure. If it haven't, it means that repository have just
+       added, so report about this */
+    if (!rei->repo) {
+        GtkWidget *msg = hildon_note_new_information ( GTK_WINDOW (rmi->dialog),
+                           _("You cannot add layers to not saved repository,\nsorry. So, press ok in repository manager\n"
+                             "and open this dialog again."));
+
+        gtk_dialog_run (GTK_DIALOG (msg));
+        gtk_widget_destroy (msg);
+
+        vprintf("%s(): return FALSE (3)\n", __PRETTY_FUNCTION__);
+        return FALSE;
+    }
+
+    header = g_malloc (strlen (t_header) + strlen (rei->name));
+    sprintf (header, t_header, rei->name);
+
+    printf ("Creating dialog with header: %s\n", header);
+
+    rli.layer_edits = NULL;
+    rli.dialog = gtk_dialog_new_with_buttons (header, GTK_WINDOW (rmi->dialog), GTK_DIALOG_MODAL, 
+                                              GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
+                                              GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL);
+
+    rli.layers_store = gtk_list_store_new (1, G_TYPE_STRING);
+    rli.layers_list = gtk_tree_view_new_with_model (GTK_TREE_MODEL (rli.layers_store));
+    layers_rendeder = gtk_cell_renderer_text_new ();
+    layers_column = gtk_tree_view_column_new_with_attributes ("Column", layers_rendeder, "text", 0, NULL);
+    gtk_tree_view_append_column (GTK_TREE_VIEW (rli.layers_list), layers_column);
+
+    selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (rli.layers_list));
+
+    frame = gtk_frame_new (NULL);
+    gtk_container_add (GTK_CONTAINER (frame), rli.layers_list);
+    gtk_widget_set_size_request (frame, -1, 100);
+
+    /* beside layers list with have buttons on bottom */
+    layers_vbox = gtk_vbox_new (FALSE, 4);
+    gtk_box_pack_start (GTK_BOX (layers_vbox), frame, TRUE, TRUE, 0);
+    gtk_box_pack_start (GTK_BOX (layers_vbox), buttons_hbox = gtk_hbox_new (FALSE, 4), FALSE, FALSE, 0);
+
+    /* buttons */
+    gtk_box_pack_start (GTK_BOX (buttons_hbox), btn_new = gtk_button_new_with_label (_("New")), FALSE, FALSE, 0);
+    gtk_box_pack_start (GTK_BOX (buttons_hbox), btn_del = gtk_button_new_with_label (_("Del")), FALSE, FALSE, 0);
+    gtk_box_pack_start (GTK_BOX (buttons_hbox), btn_up  = gtk_button_new_with_label (_("Up")), FALSE, FALSE, 0);
+    gtk_box_pack_start (GTK_BOX (buttons_hbox), btn_dn  = gtk_button_new_with_label (_("Dn")), FALSE, FALSE, 0);
+
+    /* signals */
+    g_signal_connect(G_OBJECT(selection), "changed", G_CALLBACK(repoman_layer_selected), &rli);
+    g_signal_connect(G_OBJECT(btn_new), "clicked", G_CALLBACK(repoman_layers_new), &rli);
+    g_signal_connect(G_OBJECT(btn_del), "clicked", G_CALLBACK(repoman_layers_del), &rli);
+    g_signal_connect(G_OBJECT(btn_up),  "clicked", G_CALLBACK(repoman_layers_up), &rli);
+    g_signal_connect(G_OBJECT(btn_dn),  "clicked", G_CALLBACK(repoman_layers_dn), &rli);
+
+    /* notebook with layers' attributes */
+    rli.notebook = gtk_notebook_new ();
+
+    gtk_notebook_set_show_tabs(GTK_NOTEBOOK(rli.notebook), FALSE);
+    gtk_notebook_set_show_border(GTK_NOTEBOOK(rli.notebook), FALSE);
+
+    /* walk through all layers and add notebook pages */
+    rd = rei->repo->layers;
+    while (rd) {
+        LayerEditInfo *lei = repoman_layers_add_layer (&rli, rd->name);
+
+        gtk_entry_set_text (GTK_ENTRY (lei->txt_url),  rd->url);
+        gtk_entry_set_text (GTK_ENTRY (lei->txt_db),   rd->db_filename);
+        hildon_number_editor_set_value (HILDON_NUMBER_EDITOR (lei->num_autofetch), rd->layer_refresh_interval);
+        gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (lei->chk_visible), rd->layer_enabled);
+
+        rd = rd->layers;
+    }
+
+    /* pack all widgets together */
+    hbox = gtk_hbox_new (FALSE, 4);
+
+    gtk_box_pack_start (GTK_BOX (hbox), layers_vbox, TRUE, TRUE, 4);
+    gtk_box_pack_start (GTK_BOX (hbox), rli.notebook, TRUE, TRUE, 4);
+
+    gtk_box_pack_start (GTK_BOX (GTK_DIALOG (rli.dialog)->vbox), hbox, FALSE, FALSE, 4);
+
+    gtk_widget_show_all (rli.dialog);
+
+    while (GTK_RESPONSE_ACCEPT == gtk_dialog_run (GTK_DIALOG (rli.dialog)))
+    {
+        RepoData **rdp;
+        gint i;
+        GList *curr;
+
+        menu_layers_remove_repos ();
+
+        /* iterate over notebook's pages and build layers */
+        /* keep list in memory in case downloads use it (TODO: reference counting) */
+        rdp = &rei->repo->layers;
+        *rdp = NULL;
+
+        for (i = 0, curr = rli.layer_edits; curr; curr = curr->next, i++)  {
+            LayerEditInfo *lei = curr->data;
+
+            rd = g_new0 (RepoData, 1);
+            *rdp = rd;
+
+            rd->name = g_strdup (gtk_entry_get_text (GTK_ENTRY (lei->txt_name)));
+            rd->url = g_strdup (gtk_entry_get_text (GTK_ENTRY (lei->txt_url)));
+            rd->db_filename = g_strdup (gtk_entry_get_text (GTK_ENTRY (lei->txt_db)));
+            rd->layer_enabled = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (lei->chk_visible));
+            rd->layer_refresh_interval = hildon_number_editor_get_value (HILDON_NUMBER_EDITOR (lei->num_autofetch));
+            rd->layer_refresh_countdown = rd->layer_refresh_interval;
+            rd->layer_level = i+1;
+
+            rd->dl_zoom_steps = rei->repo->dl_zoom_steps;
+            rd->view_zoom_steps = rei->repo->view_zoom_steps;
+            rd->double_size = rei->repo->double_size;
+            rd->nextable = rei->repo->nextable;
+            rd->min_zoom = rei->repo->min_zoom;
+            rd->max_zoom = rei->repo->max_zoom;
+
+            set_repo_type (rd);
+            rdp = &rd->layers;
+        }
+
+        menu_layers_add_repos ();
+        repo_set_curr(_curr_repo);
+        settings_save ();
+        map_cache_clean ();
+        map_refresh_mark (TRUE);
+        break;
+    }
+
+    gtk_widget_destroy (rli.dialog);
+
+    vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__);
+    return TRUE;
+}
+
+
 gboolean
 repoman_dialog()
 {
@@ -2224,7 +2839,7 @@ repoman_dialog()
     static GtkWidget *btn_delete = NULL;
     static GtkWidget *btn_new = NULL;
     static GtkWidget *btn_reset = NULL;
-    static GtkWidget *btn_download = NULL;
+    static GtkWidget *btn_layers = NULL;
     gint i, curr_repo_index = 0;
     GList *curr;
     printf("%s()\n", __PRETTY_FUNCTION__);
@@ -2251,11 +2866,11 @@ repoman_dialog()
         g_signal_connect(G_OBJECT(btn_reset), "clicked",
                           G_CALLBACK(repoman_reset), &rmi);
 
-        /* Download button. */
+        /* Layers button. */
         gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->action_area),
-                btn_download = gtk_button_new_with_label(_("Download...")));
-        g_signal_connect(G_OBJECT(btn_download), "clicked",
-                          G_CALLBACK(repoman_download), &rmi);
+                btn_layers = gtk_button_new_with_label(_("Layers...")));
+        g_signal_connect(G_OBJECT(btn_layers), "clicked",
+                          G_CALLBACK(repoman_layers), &rmi);
 
         /* Cancel button. */
         gtk_dialog_add_button(GTK_DIALOG(dialog),
@@ -2308,6 +2923,9 @@ repoman_dialog()
         RepoData *rd = (RepoData*)curr->data;
         RepoEditInfo *rei = repoman_dialog_add_repo(&rmi, g_strdup(rd->name));
 
+        /* store this to be able to walk through layers attached to repo */
+        rei->repo = rd;
+
         /* Initialize fields with data from the RepoData object. */
         gtk_entry_set_text(GTK_ENTRY(rei->txt_url), rd->url);
         gtk_entry_set_text(GTK_ENTRY(rei->txt_db_filename),
@@ -2381,7 +2999,8 @@ repoman_dialog()
         for(i = 0, curr = rmi.repo_edits; curr; curr = curr->next, i++)
         {
             RepoEditInfo *rei = curr->data;
-            RepoData *rd = g_new(RepoData, 1);
+            RepoData *rd = g_new0(RepoData, 1);
+            RepoData *rd0, **rd1;
             rd->name = g_strdup(rei->name);
             rd->url = g_strdup(gtk_entry_get_text(GTK_ENTRY(rei->txt_url)));
             rd->db_filename = gnome_vfs_expand_initial_tilde(
@@ -2398,6 +3017,40 @@ repoman_dialog()
                     HILDON_NUMBER_EDITOR(rei->num_min_zoom));
             rd->max_zoom = hildon_number_editor_get_value(
                     HILDON_NUMBER_EDITOR(rei->num_max_zoom));
+
+            if (rei->repo) {
+                /* clone layers */
+                rd0 = rei->repo->layers;
+                rd1 = &rd->layers;
+
+                while (rd0) {
+                    *rd1 = g_new0 (RepoData, 1);
+                    (*rd1)->name = rd0->name;
+                    (*rd1)->url = rd0->url;
+                    (*rd1)->db_filename = rd0->db_filename;
+                    (*rd1)->layer_enabled = rd0->layer_enabled;
+                    (*rd1)->layer_refresh_interval = rd0->layer_refresh_interval;
+                    (*rd1)->layer_refresh_countdown = rd0->layer_refresh_countdown;
+                    (*rd1)->layer_level = rd0->layer_level;
+
+                    (*rd1)->dl_zoom_steps = rd0->dl_zoom_steps;
+                    (*rd1)->view_zoom_steps = rd0->view_zoom_steps;
+                    (*rd1)->double_size = rd0->double_size;
+                    (*rd1)->nextable = rd0->nextable;
+                    (*rd1)->min_zoom = rd0->min_zoom;
+                    (*rd1)->max_zoom = rd0->max_zoom;
+
+                    set_repo_type (*rd1);
+
+                    rd0 = rd0->layers;
+                    rd1 = &(*rd1)->layers;
+                }
+                *rd1 = NULL;
+            }
+            else
+                rd->layers = NULL;
+
+            rd->layer_level = 0;
             set_repo_type(rd);
 
             _repo_list = g_list_append(_repo_list, rd);
@@ -2422,6 +3075,7 @@ repoman_dialog()
         repoman_delete(&rmi, 0);
 
     map_set_zoom(_zoom); /* make sure we're at an appropriate zoom level. */
+    map_refresh_mark (TRUE);
 
     vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__);
     return TRUE;
@@ -2516,11 +3170,17 @@ mapman_by_area(gdouble start_lat, gdouble start_lon,
                     if((unsigned)tilex < unit2ztile(WORLD_SIZE_UNITS, z)
                       && (unsigned)tiley < unit2ztile(WORLD_SIZE_UNITS, z))
                     {
-                        mapdb_initiate_update(_curr_repo, z, tilex, tiley,
-                                update_type, download_batch_id,
-                                (abs(tilex - unit2tile(_next_center.unitx))
-                                 + abs(tiley - unit2tile(_next_center.unity))),
-                                NULL);
+                        RepoData* rd = _curr_repo;
+
+                        while (rd) {
+                            if (rd == _curr_repo || (rd->layer_enabled && rd->db))
+                                mapdb_initiate_update(rd, z, tilex, tiley,
+                                                      update_type, download_batch_id,
+                                                      (abs(tilex - unit2tile(_next_center.unitx))
+                                                       + abs(tiley - unit2tile(_next_center.unity))),
+                                                      NULL);
+                            rd = rd->layers;
+                        }
                     }
                 }
             }
@@ -3127,3 +3787,101 @@ mapman_dialog()
     return TRUE;
 }
 
+
+/* changes visibility of current repo's layers to it's previous state */
+void maps_toggle_visible_layers ()
+{
+    RepoData *rd = _curr_repo;
+    gboolean changed = FALSE;
+
+    printf("%s()\n", __PRETTY_FUNCTION__);
+
+    if (!rd) {
+        vprintf("%s(): return\n", __PRETTY_FUNCTION__);
+        return;
+    }
+
+    rd = rd->layers;
+
+    while (rd) {
+        if (rd->layer_enabled) {
+            changed = TRUE;
+            rd->layer_was_enabled = rd->layer_enabled;
+            rd->layer_enabled = FALSE;
+        }
+        else {
+            rd->layer_enabled = rd->layer_was_enabled;
+            if (rd->layer_was_enabled)
+                changed = TRUE;
+        }
+
+        rd = rd->layers;
+    }
+
+    /* redraw map */
+    if (changed) {
+        menu_layers_remove_repos ();
+        menu_layers_add_repos ();
+        map_cache_clean ();
+        map_refresh_mark (TRUE);
+    }
+
+    vprintf("%s(): return\n", __PRETTY_FUNCTION__);
+}
+
+
+
+/* this routine fired by timer every minute and decrements refetch counter of every active layer of
+   current repository. If one of layer is expired, it forces map redraw. Redraw routine checks every
+   layer's tile download timestamp and desides performs refetch if needed */
+gboolean
+map_layer_refresh_cb (gpointer data)
+{
+    RepoData* rd = _curr_repo;
+    gboolean refresh = FALSE;
+    printf("%s()\n", __PRETTY_FUNCTION__);
+
+    if (rd) {
+        rd = rd->layers;
+
+        while (rd) {
+            if (rd->layer_enabled && rd->layer_refresh_interval) {
+                rd->layer_refresh_countdown--;
+                if (rd->layer_refresh_countdown <= 0) {
+                    rd->layer_refresh_countdown = rd->layer_refresh_interval;
+                    refresh = TRUE;
+                }
+            }
+
+            rd = rd->layers;
+        }
+    }
+
+    if (refresh)
+        map_refresh_mark (TRUE);
+
+    vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__);
+    return TRUE;
+}
+
+
+
+/*
+   Returns amount of seconds since tile downloaded or 0 if tile
+   have no such information.
+*/
+gint get_tile_age (GdkPixbuf* pixbuf)
+{
+    const char* ts;
+    guint val;
+
+    ts = gdk_pixbuf_get_option (pixbuf, layer_timestamp_key);
+
+    if (!ts)
+        return 0;
+
+    if (sscanf (ts, "%u", &val))
+        return time (NULL) - val;
+    else
+        return 0;
+}
index 1452e365c50b1cd340bd4907d6f0829a03a6e1c7..50087eb8cc934b65b2353409620795fc67a8b948 100644 (file)
 void map_cache_init(size_t cache_size);
 size_t map_cache_resize(size_t cache_size);
 void map_cache_destroy(void);
+void map_cache_clean (void);
 
 gboolean mapdb_exists(RepoData *repo, gint zoom, gint tilex, gint tiley);
 GdkPixbuf* mapdb_get(RepoData *repo, gint zoom, gint tilex, gint tiley);
 
 void set_repo_type(RepoData *repo);
 gboolean repo_set_curr(RepoData *rd);
+gboolean repo_is_layer (RepoData* base, RepoData* layer);
 
 gboolean mapdb_initiate_update(RepoData *repo, gint zoom, gint tilex,
         gint tiley, gint update_type, gint batch_id, gint priority,
@@ -45,6 +47,15 @@ gboolean thread_proc_mut(void);
 
 gboolean repoman_dialog(void);
 
+gboolean repoman_download(void);
+
 gboolean mapman_dialog(void);
 
+gboolean map_layer_refresh_cb (gpointer data);
+
+void maps_toggle_visible_layers ();
+
+/* returns amount of seconds since tile downloaded */
+gint get_tile_age (GdkPixbuf* pixbuf);
+
 #endif /* ifndef MAEMO_MAPPER_MAPS_H */
index a8e6dd6d423e9add5f6935f307b1b3d9451dfbd3..0d9171c3178d166420cef4ee39ff7bfde46d6e86 100644 (file)
@@ -28,6 +28,7 @@
 #define _GNU_SOURCE
 
 #include <math.h>
+#include <string.h>
 
 #ifndef LEGACY
 #    include <hildon/hildon-help.h>
@@ -468,6 +469,15 @@ menu_cb_maps_repoman(GtkMenuItem *item)
     return TRUE;
 }
 
+static gboolean
+menu_cb_maps_repodown(GtkMenuItem *item)
+{
+    printf("%s()\n", __PRETTY_FUNCTION__);
+    repoman_download();
+    vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__);
+    return TRUE;
+}
+
 static gboolean
 menu_cb_maps_select(GtkMenuItem *item, gpointer new_repo)
 {
@@ -516,6 +526,35 @@ menu_cb_maps_auto_download(GtkMenuItem *item)
  * ABOVE: MAPS MENU *********************************************************
  ****************************************************************************/
 
+/****************************************************************************
+ * BELOW: LAYERS MENU *******************************************************
+ ****************************************************************************/
+
+static gboolean
+menu_cb_layers_toggle(GtkCheckMenuItem *item, gpointer layer)
+{
+    RepoData* rd = (RepoData*)layer;
+
+    printf("%s()\n", __PRETTY_FUNCTION__);
+
+    rd->layer_enabled = !rd->layer_enabled;
+
+    /* refresh if layer is on top of active map */
+    if (repo_is_layer (_curr_repo, rd)) {
+        /* reset layer's countdown */
+        rd->layer_refresh_countdown = rd->layer_refresh_interval;
+        map_cache_clean ();
+        map_refresh_mark (TRUE);
+    }
+
+    vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__);
+    return TRUE;
+}
+
+/****************************************************************************
+ * ABOVE: LAYERS MENU *******************************************************
+ ****************************************************************************/
+
 /****************************************************************************
  * BELOW: VIEW/ZOOM MENU ****************************************************
  ****************************************************************************/
@@ -1330,9 +1369,27 @@ menu_maps_remove_repos()
         gtk_widget_destroy(gtk_container_get_children(
                     GTK_CONTAINER(_menu_maps_submenu))->data);
     }
+
+    menu_layers_remove_repos ();
+
     vprintf("%s(): return\n", __PRETTY_FUNCTION__);
 }
 
+
+void
+menu_layers_remove_repos()
+{
+    GList *child;
+    printf("%s()\n", __PRETTY_FUNCTION__);
+
+    /* Delete one menu item for each repo. */
+    while ((child = gtk_container_get_children(GTK_CONTAINER(_menu_layers_submenu))))
+        gtk_widget_destroy (child->data);
+
+    vprintf("%s(): return\n", __PRETTY_FUNCTION__);
+}
+
+
 void
 menu_maps_add_repos()
 {
@@ -1371,9 +1428,65 @@ menu_maps_add_repos()
     }
 
     gtk_widget_show_all(_menu_maps_submenu);
+    menu_layers_add_repos ();
+
+    vprintf("%s(): return\n", __PRETTY_FUNCTION__);
+}
+
+
+void
+menu_layers_add_repos()
+{
+    GList *curr;
+
+    printf("%s()\n", __PRETTY_FUNCTION__);
+
+    for(curr = _repo_list; curr; curr = curr->next)
+    {
+        RepoData* rd = (RepoData*)curr->data;
+        GtkWidget *item, *submenu = NULL, *layer_item;
+
+        /* if repository doesn't have layers, skip it */
+        if (!rd->layers)
+            continue;
+
+        /* if it has only one layer, add just one check menu item */
+        if (!rd->layers->layers) {
+            gchar *title = g_malloc (strlen (rd->name) + strlen (rd->layers->name) + 3);
+
+            sprintf (title, "%s[%s]", rd->name, rd->layers->name);
+
+            rd = rd->layers;
+            gtk_menu_append (_menu_layers_submenu, layer_item = gtk_check_menu_item_new_with_label (title));
+            gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (layer_item), rd->layer_enabled);
+            g_signal_connect (G_OBJECT (layer_item), "toggled", G_CALLBACK (menu_cb_layers_toggle), rd);
+            rd->menu_item = layer_item;
+        }
+        else {
+            /* append main repository menu item  */
+            gtk_menu_append (_menu_layers_submenu, item = gtk_menu_item_new_with_label(rd->name));
+
+            rd = rd->layers;
+            while (rd) {
+                if (!submenu)
+                    submenu = gtk_menu_new ();
+                gtk_menu_append (submenu, layer_item = gtk_check_menu_item_new_with_label (rd->name));
+                gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (layer_item), rd->layer_enabled);
+                g_signal_connect (G_OBJECT (layer_item), "toggled", G_CALLBACK (menu_cb_layers_toggle), rd);
+                rd->menu_item = layer_item;
+                rd = rd->layers;
+            }
+
+            if (submenu)
+                gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), submenu);
+        }
+    }
+
+    gtk_widget_show_all(_menu_layers_submenu);
     vprintf("%s(): return\n", __PRETTY_FUNCTION__);
 }
 
+
 /**
  * Create the menu items needed for the drop down menu.
  */
@@ -1450,6 +1563,8 @@ menu_init()
     gtk_menu_append(submenu, _menu_poi_categories_item
             = gtk_menu_item_new_with_label(_("Categories...")));
 
+    _menu_layers_submenu = gtk_menu_new();
+
     /* The "Maps" submenu. */
     gtk_menu_append(menu, menu_item
             = gtk_menu_item_new_with_label(_("Maps")));
@@ -1458,12 +1573,16 @@ menu_init()
     gtk_menu_append(_menu_maps_submenu, gtk_separator_menu_item_new());
     gtk_menu_append(_menu_maps_submenu, _menu_maps_mapman_item
             = gtk_menu_item_new_with_label(_("Manage Maps...")));
-    gtk_menu_append(_menu_maps_submenu, _menu_maps_repoman_item
-            = gtk_menu_item_new_with_label(_("Manage Repositories...")));
     gtk_menu_append(_menu_maps_submenu, _menu_maps_auto_download_item
             = gtk_check_menu_item_new_with_label(_("Auto-Download")));
     gtk_check_menu_item_set_active(
             GTK_CHECK_MENU_ITEM(_menu_maps_auto_download_item),_auto_download);
+    gtk_menu_append(_menu_maps_submenu, gtk_separator_menu_item_new());
+    gtk_menu_append(_menu_maps_submenu, _menu_maps_repoman_item
+            = gtk_menu_item_new_with_label(_("Manage Repositories...")));
+    gtk_menu_append(_menu_maps_submenu, _menu_maps_repodown_item
+            = gtk_menu_item_new_with_label(
+                _("Download Sample Repositories...")));
     menu_maps_add_repos();
 
     gtk_menu_append(menu, gtk_separator_menu_item_new());
@@ -1474,6 +1593,11 @@ menu_init()
     gtk_menu_item_set_submenu(GTK_MENU_ITEM(menu_item),
             submenu = gtk_menu_new());
 
+    /* The View/Layers submenu */
+    gtk_menu_append(submenu, menu_item
+            = gtk_menu_item_new_with_label(_("Layers")));
+    gtk_menu_item_set_submenu(GTK_MENU_ITEM(menu_item), _menu_layers_submenu);
+
     /* The "View"/"Zoom" submenu. */
     gtk_menu_append(submenu, menu_item
             = gtk_menu_item_new_with_label(_("Zoom")));
@@ -1696,6 +1820,8 @@ menu_init()
     /* Connect the "Maps" signals. */
     g_signal_connect(G_OBJECT(_menu_maps_repoman_item), "activate",
                       G_CALLBACK(menu_cb_maps_repoman), NULL);
+    g_signal_connect(G_OBJECT(_menu_maps_repodown_item), "activate",
+                      G_CALLBACK(menu_cb_maps_repodown), NULL);
     g_signal_connect(G_OBJECT(_menu_maps_mapman_item), "activate",
                       G_CALLBACK(menu_cb_maps_mapman), NULL);
     g_signal_connect(G_OBJECT(_menu_maps_auto_download_item), "toggled",
index 955f5e3901e2a48fcaf49eca1b27d649c8bbe47e..a29321ee329e14356587a816680f69ea0b1855b5 100644 (file)
@@ -29,6 +29,8 @@ gboolean menu_cb_view_goto_nearpoi(GtkMenuItem *item);
 
 void menu_maps_remove_repos(void);
 void menu_maps_add_repos(void);
+void menu_layers_remove_repos(void);
+void menu_layers_add_repos(void);
 void menu_init(void);
 
 #endif /* ifndef MAEMO_MAPPER_MENU_H */
index bde2170a2f3ec2021cc8565df26ce9723f58d1bf..e1ef2dd12950572aca1a0f130a11cbf1b74e4899 100644 (file)
@@ -33,6 +33,7 @@
 #include <dbus/dbus-glib.h>
 #include <bt-dbus.h>
 #include <gconf/gconf-client.h>
+#include <glib.h>
 
 #ifndef LEGACY
 #    include <hildon/hildon-help.h>
@@ -371,28 +372,56 @@ settings_save()
         for(curr = _repo_list; curr != NULL; curr = curr->next)
         {
             /* Build from each part of a repo, delimited by newline characters:
-             * 1. url
-             * 2. db_filename
-             * 3. dl_zoom_steps
-             * 4. view_zoom_steps
+             * 1. name
+             * 2. url
+             * 3. db_filename
+             * 4. dl_zoom_steps
+             * 5. view_zoom_steps
+             * 6. layer_level
+             *
+             * If layer_level > 0, have additional fields:
+             * 7. layer_enabled
+             * 8. layer_refresh_interval
              */
             RepoData *rd = curr->data;
             gchar buffer[BUFFER_SIZE];
-            snprintf(buffer, sizeof(buffer),
-                    "%s\t%s\t%s\t%d\t%d\t%d\t%d\t%d\t%d",
-                    rd->name,
-                    rd->url,
-                    rd->db_filename,
-                    rd->dl_zoom_steps,
-                    rd->view_zoom_steps,
-                    rd->double_size,
-                    rd->nextable,
-                    rd->min_zoom,
-                    rd->max_zoom);
-            temp_list = g_slist_append(temp_list, g_strdup(buffer));
-            if(rd == _curr_repo)
-                gconf_client_set_int(gconf_client,
-                        GCONF_KEY_CURRREPO, curr_repo_index, NULL);
+
+            while (rd) {
+                if (!rd->layer_level)
+                    snprintf(buffer, sizeof(buffer),
+                             "%s\t%s\t%s\t%d\t%d\t%d\t%d\t%d\t%d\t%d",
+                             rd->name,
+                             rd->url,
+                             rd->db_filename,
+                             rd->dl_zoom_steps,
+                             rd->view_zoom_steps,
+                             rd->double_size,
+                             rd->nextable,
+                             rd->min_zoom,
+                             rd->max_zoom,
+                             rd->layer_level);
+                else
+                    snprintf(buffer, sizeof(buffer),
+                             "%s\t%s\t%s\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d",
+                             rd->name,
+                             rd->url,
+                             rd->db_filename,
+                             rd->dl_zoom_steps,
+                             rd->view_zoom_steps,
+                             rd->double_size,
+                             rd->nextable,
+                             rd->min_zoom,
+                             rd->max_zoom,
+                             rd->layer_level,
+                             rd->layer_enabled,
+                             rd->layer_refresh_interval
+                             );
+                temp_list = g_slist_append(temp_list, g_strdup(buffer));
+                if(rd == _curr_repo)
+                    gconf_client_set_int(gconf_client,
+                                         GCONF_KEY_CURRREPO, curr_repo_index, NULL);
+                rd = rd->layers;
+            }
             curr_repo_index++;
         }
         gconf_client_set_list(gconf_client,
@@ -1815,6 +1844,11 @@ settings_parse_repo(gchar *str)
      * 3. db_filename
      * 4. dl_zoom_steps
      * 5. view_zoom_steps
+     * 6. layer_level
+     *
+     * If layer_level > 0, have additional fields:
+     * 7. layer_enabled
+     * 8. layer_refresh_interval
      */
     gchar *token, *error_check;
     printf("%s(%s)\n", __PRETTY_FUNCTION__, str);
@@ -1869,6 +1903,26 @@ settings_parse_repo(gchar *str)
             || (rd->max_zoom = strtol(token, &error_check, 10), token == str))
         rd->max_zoom = 20;
 
+    /* Parse layer_level */
+    token = strsep(&str, "\n\t");
+    if(!token || !*token
+            || (rd->layer_level = strtol(token, &error_check, 10), token == str))
+        rd->layer_level = 0;
+
+    if (rd->layer_level) {
+        /* Parse layer_enabled */
+        token = strsep(&str, "\n\t");
+        if(!token || !*token || (rd->layer_enabled = strtol(token, &error_check, 10), token == str))
+            rd->layer_enabled = 0;
+
+        /* Parse layer_refresh_interval */
+        token = strsep(&str, "\n\t");
+        if(!token || !*token || (rd->layer_refresh_interval = strtol(token, &error_check, 10), token == str))
+            rd->layer_refresh_interval = 0;
+
+        rd->layer_refresh_countdown = rd->layer_refresh_interval;
+    }
+
     set_repo_type(rd);
 
     vprintf("%s(): return %p\n", __PRETTY_FUNCTION__, rd);
@@ -2219,6 +2273,7 @@ settings_init()
     /* Load the repositories. */
     {
         GSList *list, *curr;
+        RepoData *prev_repo = NULL, *curr_repo = NULL;
         gint curr_repo_index = gconf_client_get_int(gconf_client,
             GCONF_KEY_CURRREPO, NULL);
         list = gconf_client_get_list(gconf_client,
@@ -2227,19 +2282,31 @@ settings_init()
         for(curr = list; curr != NULL; curr = curr->next)
         {
             RepoData *rd = settings_parse_repo(curr->data);
-            _repo_list = g_list_append(_repo_list, rd);
-            if(!curr_repo_index--)
-                repo_set_curr(rd);
+
+            if (rd->layer_level == 0) {
+                _repo_list = g_list_append(_repo_list, rd);
+                if(!curr_repo_index--)
+                    curr_repo = rd;
+            }
+            else
+                prev_repo->layers = rd;
+            prev_repo = rd;
             g_free(curr->data);
         }
         g_slist_free(list);
+
+        if (curr_repo)
+            repo_set_curr(curr_repo);
+
+        /* this timer decrements layers' counters and frefresh map if needed */
+        g_timeout_add (60 * 1000, map_layer_refresh_cb, NULL);
     }
 
 
     if(_repo_list == NULL)
     {
         /* We have no repositories - create a default one. */
-        RepoData *repo = g_new(RepoData, 1);
+        RepoData *repo = g_new0(RepoData, 1);
 
         repo->db_filename = gnome_vfs_expand_initial_tilde(
                 REPO_DEFAULT_CACHE_DIR);
@@ -2251,6 +2318,8 @@ settings_init()
         repo->nextable = TRUE;
         repo->min_zoom = REPO_DEFAULT_MIN_ZOOM;
         repo->max_zoom = REPO_DEFAULT_MAX_ZOOM;
+        repo->layers = NULL;
+        repo->layer_level = 0;
         set_repo_type(repo);
 
         _repo_list = g_list_append(_repo_list, repo);
index b6ec785bfb33e1d499599068bcee5b9e8dae00d0..08fa06462636c9fb56e940722ccc2d836a4efac6 100644 (file)
@@ -192,6 +192,7 @@ typedef enum
     CUSTOM_ACTION_TOGGLE_GPSINFO,
     CUSTOM_ACTION_TOGGLE_SPEEDLIMIT,
     CUSTOM_ACTION_RESET_BLUETOOTH,
+    CUSTOM_ACTION_TOGGLE_LAYERS,
     CUSTOM_ACTION_ENUM_COUNT
 } CustomAction;
 
@@ -319,6 +320,12 @@ struct _RepoData {
     gint min_zoom;
     gint max_zoom;
     RepoType type;
+    RepoData *layers;
+    gint8 layer_level;
+    gboolean layer_enabled;
+    gboolean layer_was_enabled; /* needed for ability to temporarily toggle layers on and off */
+    gint layer_refresh_interval;
+    gint layer_refresh_countdown;
 #ifdef MAPDB_SQLITE
     sqlite3 *db;
     sqlite3_stmt *stmt_map_select;
@@ -421,6 +428,7 @@ struct _MapUpdateTask
     gint8 zoom;
     gint8 vfs_result;
     gint8 batch_id;
+    gint8 layer_level;
 };
 
 /** Data used during the asynchronous automatic route downloading operation. */