From: gnuite Date: Sun, 12 Oct 2008 03:06:36 +0000 (+0000) Subject: Added support for layered maps. This support is still very X-Git-Tag: fremantle/3.0+alpha0~135 X-Git-Url: http://git.itanic.dy.fi/?p=maemo-mapper;a=commitdiff_plain;h=bf1268426abbbc44ca5cb86a7ea41a3505a130f3 Added support for layered maps. This support is still very 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 --- diff --git a/src/data.c b/src/data.c index deb2895..fc68bab 100644 --- a/src/data.c +++ b/src/data.c @@ -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; diff --git a/src/data.h b/src/data.h index e27aa26..1ac3b3d 100644 --- a/src/data.h +++ b/src/data.h @@ -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; diff --git a/src/display.c b/src/display.c index 60e61c8..f118265 100644 --- a/src/display.c +++ b/src/display.c @@ -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, diff --git a/src/input.c b/src/input.c index 25a9b7b..bc48c1f 100644 --- a/src/input.c +++ b/src/input.c @@ -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; diff --git a/src/main.c b/src/main.c index 9e44dbe..5ce2abf 100644 --- a/src/main.c +++ b/src/main.c @@ -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'"; diff --git a/src/maps.c b/src/maps.c index cd24a1e..3a7cc3e 100644 --- a/src/maps.c +++ b/src/maps.c @@ -33,6 +33,7 @@ #include #include #include +#include #ifndef LEGACY # include @@ -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; +} diff --git a/src/maps.h b/src/maps.h index 1452e36..50087eb 100644 --- a/src/maps.h +++ b/src/maps.h @@ -27,12 +27,14 @@ 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 */ diff --git a/src/menu.c b/src/menu.c index a8e6dd6..0d9171c 100644 --- a/src/menu.c +++ b/src/menu.c @@ -28,6 +28,7 @@ #define _GNU_SOURCE #include +#include #ifndef LEGACY # include @@ -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", diff --git a/src/menu.h b/src/menu.h index 955f5e3..a29321e 100644 --- a/src/menu.h +++ b/src/menu.h @@ -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 */ diff --git a/src/settings.c b/src/settings.c index bde2170..e1ef2dd 100644 --- a/src/settings.c +++ b/src/settings.c @@ -33,6 +33,7 @@ #include #include #include +#include #ifndef LEGACY # include @@ -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); diff --git a/src/types.h b/src/types.h index b6ec785..08fa064 100644 --- a/src/types.h +++ b/src/types.h @@ -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. */