From 0d2b891f5f4673f0dd1142dcd7e1eb08e4248090 Mon Sep 17 00:00:00 2001 From: Rob Norris Date: Wed, 17 Oct 2012 01:37:18 +0100 Subject: [PATCH] Add support for GPX routes - as a new sublayer type of the now less accurately named TrackWaypoint layer Most previous track functionality is made avaliable on this Route type. Routepoints can have an elevation property. GPX Routes can be optionally downloaded/uploaded from GPS devices via the Acquire, GPS layer or menu methods. GPX files with routes may be imported and exported. Viking .vik files save route and routepoints. Internally, a route is implemented as a simple boolean flag as part of the track's property. --- src/acquire.c | 4 +- src/datasource_gps.c | 128 +++- src/datasource_gps.h | 3 +- src/file.c | 4 +- src/gpspoint.c | 35 +- src/gpx.c | 71 ++- src/gpx.h | 1 + src/osm-traces.c | 2 +- src/vikgpslayer.c | 141 +++-- src/vikgpslayer.h | 7 + src/viktrack.c | 7 +- src/viktrack.h | 7 + src/viktrwlayer.c | 1198 ++++++++++++++++++++++++++++++------- src/viktrwlayer.h | 8 +- src/viktrwlayer_propwin.c | 24 +- 15 files changed, 1329 insertions(+), 311 deletions(-) diff --git a/src/acquire.c b/src/acquire.c index 0cd79dc7..5d8d4025 100644 --- a/src/acquire.c +++ b/src/acquire.c @@ -152,8 +152,10 @@ static void get_from_anything ( w_and_interface_t *wi ) gtk_label_set_text ( GTK_LABEL(w->status), _("Done.") ); if ( creating_new_layer ) { /* Only create the layer if it actually contains anything useful */ + // TODO: create function for this operation to hide detail: if ( g_hash_table_size (vik_trw_layer_get_tracks(vtl)) || - g_hash_table_size (vik_trw_layer_get_waypoints(vtl)) ) { + g_hash_table_size (vik_trw_layer_get_waypoints(vtl)) || + g_hash_table_size (vik_trw_layer_get_routes(vtl)) ) { vik_layer_post_read ( VIK_LAYER(vtl), w->vvp, TRUE ); vik_aggregate_layer_add_layer( vik_layers_panel_get_top_layer(w->vlp), VIK_LAYER(vtl)); } diff --git a/src/datasource_gps.c b/src/datasource_gps.c index 8e2fe9ed..5ef75f20 100644 --- a/src/datasource_gps.c +++ b/src/datasource_gps.c @@ -42,6 +42,7 @@ static gboolean gps_acquire_in_progress = FALSE; static gint last_active = -1; static gboolean last_get_tracks = TRUE; +static gboolean last_get_routes = TRUE; static gboolean last_get_waypoints = TRUE; static gpointer datasource_gps_init_func ( ); @@ -88,6 +89,8 @@ typedef struct { GtkCheckButton *off_request_b; GtkWidget *get_tracks_l; GtkCheckButton *get_tracks_b; + GtkWidget *get_routes_l; + GtkCheckButton *get_routes_b; GtkWidget *get_waypoints_l; GtkCheckButton *get_waypoints_b; @@ -97,7 +100,9 @@ typedef struct { GtkWidget *id_label; GtkWidget *wp_label; GtkWidget *trk_label; + GtkWidget *rte_label; GtkWidget *progress_label; + vik_gps_xfer_type progress_type; /* state */ int total_count; @@ -150,6 +155,18 @@ gboolean datasource_gps_get_do_tracks ( gpointer user_data ) return last_get_tracks; } +/** + * datasource_gps_get_do_routes: + * + * Method to get the route handling behaviour from the widget structure + */ +gboolean datasource_gps_get_do_routes ( gpointer user_data ) +{ + gps_user_data_t *w = (gps_user_data_t *)user_data; + last_get_routes = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(w->get_routes_b)); + return last_get_routes; +} + /** * datasource_gps_get_do_waypoints: * @@ -166,6 +183,7 @@ static void datasource_gps_get_cmd_string ( gpointer user_data, gchar **babelarg { char *device = NULL; char *tracks = NULL; + char *routes = NULL; char *waypoints = NULL; if (gps_acquire_in_progress) { @@ -181,15 +199,21 @@ static void datasource_gps_get_cmd_string ( gpointer user_data, gchar **babelarg else tracks = ""; + if ( datasource_gps_get_do_routes ( user_data ) ) + routes = "-r"; + else + routes = ""; + if ( datasource_gps_get_do_waypoints ( user_data ) ) waypoints = "-w"; else waypoints = ""; - *babelargs = g_strdup_printf("-D 9 %s %s -i %s", tracks, waypoints, device); + *babelargs = g_strdup_printf("-D 9 %s %s %s -i %s", tracks, routes, waypoints, device); /* device points to static content => no free */ device = NULL; tracks = NULL; + routes = NULL; waypoints = NULL; *input_file = g_strdup(datasource_gps_get_descriptor(user_data)); @@ -269,14 +293,21 @@ static void set_total_count(gint cnt, acq_dialog_widgets_t *w) if (w->ok) { gps_user_data_t *gps_data = (gps_user_data_t *)w->user_data; const gchar *tmp_str; - if (gps_data->progress_label == gps_data->wp_label) - tmp_str = ngettext("Downloading %d waypoint...", "Downloading %d waypoints...", cnt); - else - tmp_str = ngettext("Downloading %d trackpoint...", "Downloading %d trackpoints...", cnt); + switch (gps_data->progress_type) { + case WPT: tmp_str = ngettext("Downloading %d waypoint...", "Downloading %d waypoints...", cnt); gps_data->total_count = cnt; break; + case TRK: tmp_str = ngettext("Downloading %d trackpoint...", "Downloading %d trackpoints...", cnt); gps_data->total_count = cnt; break; + default: + { + // Maybe a gpsbabel bug/feature (upto at least v1.4.3 or maybe my Garmin device) but the count always seems x2 too many for routepoints + gint mycnt = (cnt / 2) + 1; + tmp_str = ngettext("Downloading %d routepoint...", "Downloading %d routepoints...", mycnt); + gps_data->total_count = mycnt; + break; + } + } s = g_strdup_printf(tmp_str, cnt); gtk_label_set_text ( GTK_LABEL(gps_data->progress_label), s ); gtk_widget_show ( gps_data->progress_label ); - gps_data->total_count = cnt; } g_free(s); s = NULL; gdk_threads_leave(); @@ -290,10 +321,18 @@ static void set_current_count(gint cnt, acq_dialog_widgets_t *w) gps_user_data_t *gps_data = (gps_user_data_t *)w->user_data; if (cnt < gps_data->total_count) { - s = g_strdup_printf(_("Downloaded %d out of %d %s..."), cnt, gps_data->total_count, (gps_data->progress_label == gps_data->wp_label) ? "waypoints" : "trackpoints"); + switch (gps_data->progress_type) { + case WPT: s = g_strdup_printf(_("Downloaded %d out of %d %s..."), cnt, gps_data->total_count, "waypoints"); break; + case TRK: s = g_strdup_printf(_("Downloaded %d out of %d %s..."), cnt, gps_data->total_count, "trackpoints"); break; + default: s = g_strdup_printf(_("Downloaded %d out of %d %s..."), cnt, gps_data->total_count, "routepoints"); break; + } } else { - s = g_strdup_printf(_("Downloaded %d %s."), cnt, (gps_data->progress_label == gps_data->wp_label) ? "waypoints" : "trackpoints"); - } + switch (gps_data->progress_type) { + case WPT: s = g_strdup_printf(_("Downloaded %d %s."), cnt, "waypoints"); break; + case TRK: s = g_strdup_printf(_("Downloaded %d %s."), cnt, "trackpoints"); break; + default: s = g_strdup_printf(_("Downloaded %d %s."), cnt, "routepoints"); break; + } + } gtk_label_set_text ( GTK_LABEL(gps_data->progress_label), s ); } g_free(s); s = NULL; @@ -332,13 +371,20 @@ static void datasource_gps_progress ( BabelProgressCode c, gpointer data, acq_di } gdk_threads_leave(); - /* tells us how many items there will be */ - if (strstr(line, "Xfer Wpt")) { + /* tells us the type of items that will follow */ + if (strstr(line, "Xfer Wpt")) { gps_data->progress_label = gps_data->wp_label; + gps_data->progress_type = WPT; } - if (strstr(line, "Xfer Trk")) { + if (strstr(line, "Xfer Trk")) { gps_data->progress_label = gps_data->trk_label; + gps_data->progress_type = TRK; + } + if (strstr(line, "Xfer Rte")) { + gps_data->progress_label = gps_data->rte_label; + gps_data->progress_type = RTE; } + if (strstr(line, "PRDDAT")) { gchar **tokens = g_strsplit(line, " ", 0); gchar info[128]; @@ -372,7 +418,8 @@ static void datasource_gps_progress ( BabelProgressCode c, gpointer data, acq_di } g_strfreev(tokens); } - if (strstr(line, "RECORD")) { + /* tells us how many items there will be */ + if (strstr(line, "RECORD")) { int lsb, msb, cnt; if (strlen(line) > 20) { @@ -383,7 +430,7 @@ static void datasource_gps_progress ( BabelProgressCode c, gpointer data, acq_di gps_data->count = 0; } } - if ( strstr(line, "WPTDAT") || strstr(line, "TRKHDR") || strstr(line, "TRKDAT") ) { + if ( strstr(line, "WPTDAT") || strstr(line, "TRKHDR") || strstr(line, "TRKDAT") || strstr(line, "RTEHDR") || strstr(line, "RTEWPT") ) { gps_data->count++; set_current_count(gps_data->count, w); } @@ -466,6 +513,10 @@ static void datasource_gps_add_setup_widgets ( GtkWidget *dialog, VikViewport *v w->get_tracks_b = GTK_CHECK_BUTTON ( gtk_check_button_new () ); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w->get_tracks_b), last_get_tracks); + w->get_routes_l = gtk_label_new (_("Routes:")); + w->get_routes_b = GTK_CHECK_BUTTON ( gtk_check_button_new () ); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w->get_routes_b), last_get_routes); + w->get_waypoints_l = gtk_label_new (_("Waypoints:")); w->get_waypoints_b = GTK_CHECK_BUTTON ( gtk_check_button_new () ); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w->get_waypoints_b), last_get_waypoints); @@ -479,8 +530,10 @@ static void datasource_gps_add_setup_widgets ( GtkWidget *dialog, VikViewport *v gtk_table_attach_defaults(box, GTK_WIDGET(w->ser_b), 1, 2, 1, 2); gtk_table_attach_defaults(data_type_box, GTK_WIDGET(w->get_tracks_l), 0, 1, 0, 1); gtk_table_attach_defaults(data_type_box, GTK_WIDGET(w->get_tracks_b), 1, 2, 0, 1); - gtk_table_attach_defaults(data_type_box, GTK_WIDGET(w->get_waypoints_l), 2, 3, 0, 1); - gtk_table_attach_defaults(data_type_box, GTK_WIDGET(w->get_waypoints_b), 3, 4, 0, 1); + gtk_table_attach_defaults(data_type_box, GTK_WIDGET(w->get_routes_l), 2, 3, 0, 1); + gtk_table_attach_defaults(data_type_box, GTK_WIDGET(w->get_routes_b), 3, 4, 0, 1); + gtk_table_attach_defaults(data_type_box, GTK_WIDGET(w->get_waypoints_l), 4, 5, 0, 1); + gtk_table_attach_defaults(data_type_box, GTK_WIDGET(w->get_waypoints_b), 5, 6, 0, 1); gtk_table_attach_defaults(box, GTK_WIDGET(data_type_box), 0, 2, 2, 3); gtk_table_attach_defaults(box, GTK_WIDGET(w->off_request_l), 0, 1, 3, 4); gtk_table_attach_defaults(box, GTK_WIDGET(w->off_request_b), 1, 3, 3, 4); @@ -492,31 +545,49 @@ static void datasource_gps_add_setup_widgets ( GtkWidget *dialog, VikViewport *v /** * datasource_gps_setup: * @dialog: The GTK dialog. The caller is responsible for managing the dialog creation/deletion - * @only_tracks: When only tracks are specified, waypoints will be disabled. + * @xfer: The default type of items enabled for transfer, others disabled. + * @xfer_all: When specified all items are enabled for transfer. * * Returns: A gpointer to the private structure for GPS progress/information widgets * Pass this pointer back into the other exposed datasource_gps_X functions */ -gpointer datasource_gps_setup ( GtkWidget *dialog, gboolean only_tracks ) +gpointer datasource_gps_setup ( GtkWidget *dialog, vik_gps_xfer_type xfer, gboolean xfer_all ) { gps_user_data_t *w_gps = (gps_user_data_t *)datasource_gps_init_func(); datasource_gps_add_setup_widgets ( dialog, NULL, w_gps ); - if ( only_tracks ) { - // Indicate tracks enabled (although no option to turn off): - gtk_toggle_button_set_active ( GTK_TOGGLE_BUTTON(w_gps->get_tracks_b), TRUE); - gtk_widget_set_sensitive ( GTK_WIDGET(w_gps->get_tracks_b), FALSE ); - // Disable waypoints - gtk_toggle_button_set_active ( GTK_TOGGLE_BUTTON(w_gps->get_waypoints_b), FALSE); - gtk_widget_set_sensitive ( GTK_WIDGET(w_gps->get_waypoints_l), FALSE ); - gtk_widget_set_sensitive ( GTK_WIDGET(w_gps->get_waypoints_b), FALSE ); + gboolean way = xfer_all; + gboolean trk = xfer_all; + gboolean rte = xfer_all; + + // Selectively turn bits on + if ( !xfer_all ) { + switch (xfer) { + case WPT: way = TRUE; break; + case RTE: rte = TRUE; break; + default: trk = TRUE; break; + } } + + // Apply + gtk_toggle_button_set_active ( GTK_TOGGLE_BUTTON(w_gps->get_tracks_b), trk ); + gtk_widget_set_sensitive ( GTK_WIDGET(w_gps->get_tracks_l), trk ); + gtk_widget_set_sensitive ( GTK_WIDGET(w_gps->get_tracks_b), trk ); + + gtk_toggle_button_set_active ( GTK_TOGGLE_BUTTON(w_gps->get_routes_b), rte ); + gtk_widget_set_sensitive ( GTK_WIDGET(w_gps->get_routes_l), rte ); + gtk_widget_set_sensitive ( GTK_WIDGET(w_gps->get_routes_b), rte ); + + gtk_toggle_button_set_active ( GTK_TOGGLE_BUTTON(w_gps->get_waypoints_b), way ); + gtk_widget_set_sensitive ( GTK_WIDGET(w_gps->get_waypoints_l), way ); + gtk_widget_set_sensitive ( GTK_WIDGET(w_gps->get_waypoints_b), way ); + return (gpointer)w_gps; } void datasource_gps_add_progress_widgets ( GtkWidget *dialog, gpointer user_data ) { - GtkWidget *gpslabel, *verlabel, *idlabel, *wplabel, *trklabel; + GtkWidget *gpslabel, *verlabel, *idlabel, *wplabel, *trklabel, *rtelabel; gps_user_data_t *w_gps = (gps_user_data_t *)user_data; @@ -525,10 +596,12 @@ void datasource_gps_add_progress_widgets ( GtkWidget *dialog, gpointer user_data idlabel = gtk_label_new (""); wplabel = gtk_label_new (""); trklabel = gtk_label_new (""); + rtelabel = gtk_label_new (""); gtk_box_pack_start ( GTK_BOX(GTK_DIALOG(dialog)->vbox), gpslabel, FALSE, FALSE, 5 ); gtk_box_pack_start ( GTK_BOX(GTK_DIALOG(dialog)->vbox), wplabel, FALSE, FALSE, 5 ); gtk_box_pack_start ( GTK_BOX(GTK_DIALOG(dialog)->vbox), trklabel, FALSE, FALSE, 5 ); + gtk_box_pack_start ( GTK_BOX(GTK_DIALOG(dialog)->vbox), rtelabel, FALSE, FALSE, 5 ); gtk_widget_show_all ( dialog ); @@ -537,5 +610,6 @@ void datasource_gps_add_progress_widgets ( GtkWidget *dialog, gpointer user_data w_gps->ver_label = verlabel; w_gps->progress_label = w_gps->wp_label = wplabel; w_gps->trk_label = trklabel; + w_gps->rte_label = rtelabel; w_gps->total_count = -1; } diff --git a/src/datasource_gps.h b/src/datasource_gps.h index a5e7d9e2..39f1e97e 100644 --- a/src/datasource_gps.h +++ b/src/datasource_gps.h @@ -28,13 +28,14 @@ G_BEGIN_DECLS -gpointer datasource_gps_setup ( GtkWidget *dialog, gboolean only_tracks ); +gpointer datasource_gps_setup ( GtkWidget *dialog, vik_gps_xfer_type xfer, gboolean xfer_all ); void datasource_gps_clean_up ( gpointer user_data ); gchar* datasource_gps_get_protocol ( gpointer user_data ); gchar* datasource_gps_get_descriptor ( gpointer user_data ); gboolean datasource_gps_get_do_tracks ( gpointer user_data ); +gboolean datasource_gps_get_do_routes ( gpointer user_data ); gboolean datasource_gps_get_do_waypoints ( gpointer user_data ); gboolean datasource_gps_get_off ( gpointer user_data ); diff --git a/src/file.c b/src/file.c index ceed0c24..b3e2abeb 100644 --- a/src/file.c +++ b/src/file.c @@ -734,13 +734,15 @@ gboolean check_file_ext ( const gchar *filename, const gchar *fileext ) */ gboolean a_file_export ( VikTrwLayer *vtl, const gchar *filename, VikFileType_t file_type, VikTrack *trk, gboolean write_hidden ) { - GpxWritingOptions options = { FALSE, FALSE, write_hidden }; + GpxWritingOptions options = { FALSE, FALSE, write_hidden, FALSE }; FILE *f = g_fopen ( filename, "w" ); if ( f ) { if ( trk ) { switch ( file_type ) { case FILE_TYPE_GPX: + // trk defined so can set the option + options.is_route = trk->is_route; a_gpx_write_track_file ( trk, f, &options ); break; default: diff --git a/src/gpspoint.c b/src/gpspoint.c index 5018d8cf..82785779 100644 --- a/src/gpspoint.c +++ b/src/gpspoint.c @@ -37,9 +37,13 @@ #include /* strtod */ +typedef struct { + FILE *f; + gboolean is_route; +} TP_write_info_type; static void a_gpspoint_write_track ( const gpointer id, const VikTrack *t, FILE *f ); -static void a_gpspoint_write_trackpoint ( VikTrackpoint *tp, FILE *f ); +static void a_gpspoint_write_trackpoint ( VikTrackpoint *tp, TP_write_info_type *write_info ); static void a_gpspoint_write_waypoint ( const gpointer id, const VikWaypoint *wp, FILE *f ); @@ -62,10 +66,9 @@ static char line_buffer[2048]; #define GPSPOINT_TYPE_NONE 0 #define GPSPOINT_TYPE_WAYPOINT 1 #define GPSPOINT_TYPE_TRACKPOINT 2 -/* #define GPSPOINT_TYPE_ROUTEPOINT 3 */ +#define GPSPOINT_TYPE_ROUTEPOINT 3 #define GPSPOINT_TYPE_TRACK 4 - -/* #define GPSPOINT_TYPE_ROUTE 5 */ +#define GPSPOINT_TYPE_ROUTE 5 static VikTrack *current_track; /* pointer to pointer to first GList */ @@ -247,7 +250,7 @@ gboolean a_gpspoint_read_file(VikTrwLayer *trw, FILE *f ) { line_symbol = NULL; } } - else if (line_type == GPSPOINT_TYPE_TRACK && line_name) + else if ((line_type == GPSPOINT_TYPE_TRACK || line_type == GPSPOINT_TYPE_ROUTE) && line_name) { have_read_something = TRUE; VikTrack *pl = vik_track_new(); @@ -256,6 +259,7 @@ gboolean a_gpspoint_read_file(VikTrwLayer *trw, FILE *f ) { if (!line_name) line_name = g_strdup("UNK"); pl->visible = line_visible; + pl->is_route = (line_type == GPSPOINT_TYPE_ROUTE); if ( line_comment ) { @@ -276,7 +280,7 @@ gboolean a_gpspoint_read_file(VikTrwLayer *trw, FILE *f ) { current_track = pl; } - else if (line_type == GPSPOINT_TYPE_TRACKPOINT && current_track) + else if ((line_type == GPSPOINT_TYPE_TRACKPOINT || line_type == GPSPOINT_TYPE_ROUTEPOINT) && current_track) { have_read_something = TRUE; VikTrackpoint *tp = vik_trackpoint_new(); @@ -390,6 +394,10 @@ static void gpspoint_process_key_and_value ( const gchar *key, gint key_len, con line_type = GPSPOINT_TYPE_TRACKPOINT; else if (value_len == 8 && strncasecmp( value, "waypoint", value_len ) == 0 ) line_type = GPSPOINT_TYPE_WAYPOINT; + else if (value_len == 5 && strncasecmp( value, "route", value_len ) == 0 ) + line_type = GPSPOINT_TYPE_ROUTE; + else if (value_len == 10 && strncasecmp( value, "routepoint", value_len ) == 0 ) + line_type = GPSPOINT_TYPE_ROUTEPOINT; else /* all others are ignored */ line_type = GPSPOINT_TYPE_NONE; @@ -515,17 +523,19 @@ static void a_gpspoint_write_waypoint ( const gpointer id, const VikWaypoint *wp fprintf ( f, "\n" ); } -static void a_gpspoint_write_trackpoint ( VikTrackpoint *tp, FILE *f ) +static void a_gpspoint_write_trackpoint ( VikTrackpoint *tp, TP_write_info_type *write_info ) { static struct LatLon ll; gchar *s_lat, *s_lon; vik_coord_to_latlon ( &(tp->coord), &ll ); + FILE *f = write_info->f; + /* TODO: modify a_coords_dtostr() to accept (optional) buffer * instead of doing malloc/free everytime */ s_lat = a_coords_dtostr(ll.lat); s_lon = a_coords_dtostr(ll.lon); - fprintf ( f, "type=\"trackpoint\" latitude=\"%s\" longitude=\"%s\"", s_lat, s_lon ); + fprintf ( f, "type=\"%spoint\" latitude=\"%s\" longitude=\"%s\"", write_info->is_route ? "route" : "track", s_lat, s_lon ); g_free ( s_lat ); g_free ( s_lon ); @@ -568,7 +578,7 @@ static void a_gpspoint_write_track ( const gpointer id, const VikTrack *trk, FIL if ( !(trk->name) ) return; - fprintf ( f, "type=\"track\" name=\"%s\"", trk->name); + fprintf ( f, "type=\"%s\" name=\"%s\"", trk->is_route ? "route" : "track", trk->name); if ( trk->comment ) { gchar *tmp = slashdup(trk->comment); @@ -587,17 +597,20 @@ static void a_gpspoint_write_track ( const gpointer id, const VikTrack *trk, FIL } fprintf ( f, "\n" ); - g_list_foreach ( trk->trackpoints, (GFunc) a_gpspoint_write_trackpoint, f ); - fprintf ( f, "type=\"trackend\"\n" ); + TP_write_info_type tp_write_info = { f, trk->is_route }; + g_list_foreach ( trk->trackpoints, (GFunc) a_gpspoint_write_trackpoint, &tp_write_info ); + fprintf ( f, "type=\"%send\"\n", trk->is_route ? "route" : "track" ); } void a_gpspoint_write_file ( VikTrwLayer *trw, FILE *f ) { GHashTable *tracks = vik_trw_layer_get_tracks ( trw ); + GHashTable *routes = vik_trw_layer_get_routes ( trw ); GHashTable *waypoints = vik_trw_layer_get_waypoints ( trw ); fprintf ( f, "type=\"waypointlist\"\n" ); g_hash_table_foreach ( waypoints, (GHFunc) a_gpspoint_write_waypoint, f ); fprintf ( f, "type=\"waypointlistend\"\n" ); g_hash_table_foreach ( tracks, (GHFunc) a_gpspoint_write_track, f ); + g_hash_table_foreach ( routes, (GHFunc) a_gpspoint_write_track, f ); } diff --git a/src/gpx.c b/src/gpx.c index a10a177c..4b24af66 100644 --- a/src/gpx.c +++ b/src/gpx.c @@ -5,6 +5,7 @@ * Copyright (C) 2007, Quy Tonthat * Copyright (C) 2008, Hein Ragas * Copyright (C) 2009, Tal B + * Copyright (c) 2012, Rob Norris * * Some of the code adapted from GPSBabel 1.2.7 * http://gpsbabel.sf.net/ @@ -61,6 +62,8 @@ typedef enum { tt_trk_desc, tt_trk_name, + tt_rte, + tt_trk_trkseg, tt_trk_trkseg_trkpt, tt_trk_trkseg_trkpt_ele, @@ -113,13 +116,11 @@ tag_mapping tag_path_map[] = { { tt_wpt_link, "/gpx/wpt/link" }, /* GPX 1.1 */ { tt_trk, "/gpx/trk" }, - { tt_trk, "/gpx/rte" }, { tt_trk_name, "/gpx/trk/name" }, { tt_trk_cmt, "/gpx/trk/cmt" }, { tt_trk_desc, "/gpx/trk/desc" }, { tt_trk_trkseg, "/gpx/trk/trkseg" }, { tt_trk_trkseg_trkpt, "/gpx/trk/trkseg/trkpt" }, - { tt_trk_trkseg_trkpt, "/gpx/rte/rtept" }, { tt_trk_trkseg_trkpt_ele, "/gpx/trk/trkseg/trkpt/ele" }, { tt_trk_trkseg_trkpt_time, "/gpx/trk/trkseg/trkpt/time" }, /* extended */ @@ -131,6 +132,15 @@ tag_mapping tag_path_map[] = { { tt_trk_trkseg_trkpt_hdop, "/gpx/trk/trkseg/trkpt/hdop" }, { tt_trk_trkseg_trkpt_vdop, "/gpx/trk/trkseg/trkpt/vdop" }, { tt_trk_trkseg_trkpt_pdop, "/gpx/trk/trkseg/trkpt/pdop" }, + + { tt_rte, "/gpx/rte" }, + // NB Route reuses track point feature tags + { tt_trk_name, "/gpx/rte/name" }, + { tt_trk_cmt, "/gpx/rte/cmt" }, + { tt_trk_desc, "/gpx/rte/desc" }, + { tt_trk_trkseg_trkpt, "/gpx/rte/rtept" }, + { tt_trk_trkseg_trkpt_ele, "/gpx/rte/rtept/ele" }, + {0} }; @@ -209,7 +219,9 @@ static void gpx_start(VikTrwLayer *vtl, const char *el, const char **attr) break; case tt_trk: + case tt_rte: c_tr = vik_track_new (); + c_tr->is_route = (current_tag == tt_rte) ? TRUE : FALSE; c_tr->visible = TRUE; if ( get_attr ( attr, "hidden" ) ) c_tr->visible = FALSE; @@ -286,6 +298,7 @@ static void gpx_end(VikTrwLayer *vtl, const char *el) break; case tt_trk: + case tt_rte: if ( ! c_tr_name ) c_tr_name = g_strdup_printf("VIKING_TR%d", unnamed_tracks++); vik_trw_layer_filein_add_track ( vtl, c_tr_name, c_tr ); @@ -710,12 +723,13 @@ static void gpx_write_trackpoint ( VikTrackpoint *tp, GpxWritingContext *context gchar *time_iso8601; vik_coord_to_latlon ( &(tp->coord), &ll ); - if ( tp->newsegment ) + // No such thing as a rteseg! So make sure we don't put them in + if ( context->options && !context->options->is_route && tp->newsegment ) fprintf ( f, " \n \n" ); s_lat = a_coords_dtostr( ll.lat ); s_lon = a_coords_dtostr( ll.lon ); - fprintf ( f, " \n", s_lat, s_lon ); + fprintf ( f, " <%spt lat=\"%s\" lon=\"%s\">\n", (context->options && context->options->is_route) ? "rte" : "trk", s_lat, s_lon ); g_free ( s_lat ); s_lat = NULL; g_free ( s_lon ); s_lon = NULL; @@ -794,8 +808,7 @@ static void gpx_write_trackpoint ( VikTrackpoint *tp, GpxWritingContext *context fprintf ( f, " %s\n", s_dop ); g_free ( s_dop ); s_dop = NULL; - - fprintf ( f, " \n" ); + fprintf ( f, " \n", (context->options && context->options->is_route) ? "rte" : "trk" ); } @@ -817,7 +830,10 @@ static void gpx_write_track ( VikTrack *t, GpxWritingContext *context ) // NB 'hidden' is not part of any GPX standard - this appears to be a made up Viking 'extension' // luckily most other GPX processing software ignores things they don't understand - fprintf ( f, "\n %s\n", t->visible ? "" : " hidden=\"hidden\"", tmp ); + fprintf ( f, "<%s%s>\n %s\n", + t->is_route ? "rte" : "trk", + t->visible ? "" : " hidden=\"hidden\"", + tmp ); g_free ( tmp ); if ( t->comment ) @@ -833,7 +849,10 @@ static void gpx_write_track ( VikTrack *t, GpxWritingContext *context ) fprintf ( f, " %s\n", tmp ); g_free ( tmp ); } - fprintf ( f, " \n" ); + + /* No such thing as a rteseg! */ + if ( !t->is_route ) + fprintf ( f, " \n" ); if ( t->trackpoints && t->trackpoints->data ) { first_tp_is_newsegment = VIK_TRACKPOINT(t->trackpoints->data)->newsegment; @@ -842,7 +861,11 @@ static void gpx_write_track ( VikTrack *t, GpxWritingContext *context ) VIK_TRACKPOINT(t->trackpoints->data)->newsegment = first_tp_is_newsegment; /* restore state */ } - fprintf ( f, "\n\n" ); + /* NB apparently no such thing as a rteseg! */ + if (!t->is_route) + fprintf ( f, " \n"); + + fprintf ( f, "\n", t->is_route ? "rte" : "trk" ); } static void gpx_write_header( FILE *f ) @@ -866,6 +889,13 @@ static int gpx_waypoint_compare(const void *x, const void *y) return strcmp(a->name,b->name); } +static int gpx_track_compare_name(const void *x, const void *y) +{ + VikTrack *a = (VikTrack *)x; + VikTrack *b = (VikTrack *)y; + return strcmp(a->name,b->name); +} + /* Function to compare two tracks by their first timestamp */ static int gpx_track_compare_timestamp (const void *x, const void *y) { @@ -917,12 +947,33 @@ void a_gpx_write_file ( VikTrwLayer *vtl, FILE *f, GpxWritingOptions *options ) /* Sort by timestamp */ gl = g_hash_table_get_values ( vik_trw_layer_get_tracks ( vtl ) ); gl = g_list_sort ( gl, gpx_track_compare_timestamp ); + // Routes sorted by name + GList *glrte = g_hash_table_get_values ( vik_trw_layer_get_routes ( vtl ) ); + glrte = g_list_sort ( glrte, gpx_track_compare_name ); + // g_list_concat doesn't copy memory properly + // so process each list separately + + GpxWritingContext context_tmp = context; + GpxWritingOptions opt_tmp = { FALSE, FALSE, FALSE }; + // Force trackpoints on tracks + if ( !context.options ) + context_tmp.options = &opt_tmp; + context_tmp.options->is_route = FALSE; + + // Loop around each list and write each one for (iter = g_list_first (gl); iter != NULL; iter = g_list_next (iter)) { - gpx_write_track ( (VikTrack*)iter->data, &context ); + gpx_write_track ( (VikTrack*)iter->data, &context_tmp ); + } + + // Routes (to get routepoints) + context_tmp.options->is_route = TRUE; + for (iter = g_list_first (glrte); iter != NULL; iter = g_list_next (iter)) { + gpx_write_track ( (VikTrack*)iter->data, &context_tmp ); } g_list_free ( gl ); + g_list_free ( glrte ); gpx_write_footer ( f ); } diff --git a/src/gpx.h b/src/gpx.h index 343061a9..d8f7d4e2 100644 --- a/src/gpx.h +++ b/src/gpx.h @@ -33,6 +33,7 @@ typedef struct { gboolean force_ele; /// Force ele field gboolean force_time; /// Force time field gboolean hidden; /// Write invisible tracks/waypoints (default is yes) + gboolean is_route; /// For internal convience } GpxWritingOptions; gboolean a_gpx_read_file ( VikTrwLayer *trw, FILE *f ); diff --git a/src/osm-traces.c b/src/osm-traces.c index e22f1cdb..91cd192f 100644 --- a/src/osm-traces.c +++ b/src/osm-traces.c @@ -260,7 +260,7 @@ static void osm_traces_upload_thread ( OsmTracesInfo *oti, gpointer threaddata ) { /* Due to OSM limits, we have to enforce ele and time fields also don't upload invisible tracks */ - static GpxWritingOptions options = { TRUE, TRUE, FALSE }; + static GpxWritingOptions options = { TRUE, TRUE, FALSE, FALSE }; FILE *file = NULL; gchar *filename = NULL; int fd; diff --git a/src/vikgpslayer.c b/src/vikgpslayer.c index 499593ce..049938c3 100644 --- a/src/vikgpslayer.c +++ b/src/vikgpslayer.c @@ -112,8 +112,10 @@ typedef struct { GtkWidget *ver_label; GtkWidget *id_label; GtkWidget *wp_label; - GtkWidget *progress_label; GtkWidget *trk_label; + GtkWidget *rte_label; + GtkWidget *progress_label; + vik_gps_xfer_type progress_type; VikViewport *vvp; #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION) gboolean realtime_tracking; @@ -149,6 +151,8 @@ static VikLayerParam gps_layer_params[] = { { "gps_port", VIK_LAYER_PARAM_STRING, GROUP_DATA_MODE, N_("Serial Port:"), VIK_LAYER_WIDGET_COMBOBOX, params_ports, NULL}, { "gps_download_tracks", VIK_LAYER_PARAM_BOOLEAN, GROUP_DATA_MODE, N_("Download Tracks:"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL}, { "gps_upload_tracks", VIK_LAYER_PARAM_BOOLEAN, GROUP_DATA_MODE, N_("Upload Tracks:"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL}, + { "gps_download_routes", VIK_LAYER_PARAM_BOOLEAN, GROUP_DATA_MODE, N_("Download Routes:"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL}, + { "gps_upload_routes", VIK_LAYER_PARAM_BOOLEAN, GROUP_DATA_MODE, N_("Upload Routes:"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL}, { "gps_download_waypoints", VIK_LAYER_PARAM_BOOLEAN, GROUP_DATA_MODE, N_("Download Waypoints:"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL}, { "gps_upload_waypoints", VIK_LAYER_PARAM_BOOLEAN, GROUP_DATA_MODE, N_("Upload Waypoints:"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL}, #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION) @@ -163,6 +167,7 @@ static VikLayerParam gps_layer_params[] = { enum { PARAM_PROTOCOL=0, PARAM_PORT, PARAM_DOWNLOAD_TRACKS, PARAM_UPLOAD_TRACKS, + PARAM_DOWNLOAD_ROUTES, PARAM_UPLOAD_ROUTES, PARAM_DOWNLOAD_WAYPOINTS, PARAM_UPLOAD_WAYPOINTS, #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION) PARAM_REALTIME_REC, PARAM_REALTIME_CENTER_START, PARAM_VEHICLE_POSITION, PARAM_GPSD_HOST, PARAM_GPSD_PORT, PARAM_GPSD_RETRY_INTERVAL, @@ -287,8 +292,10 @@ struct _VikGpsLayer { gchar *protocol; gchar *serial_port; gboolean download_tracks; + gboolean download_routes; gboolean download_waypoints; gboolean upload_tracks; + gboolean upload_routes; gboolean upload_waypoints; }; @@ -467,6 +474,12 @@ static gboolean gps_layer_set_param ( VikGpsLayer *vgl, guint16 id, VikLayerPara case PARAM_UPLOAD_TRACKS: vgl->upload_tracks = data.b; break; + case PARAM_DOWNLOAD_ROUTES: + vgl->download_routes = data.b; + break; + case PARAM_UPLOAD_ROUTES: + vgl->upload_routes = data.b; + break; case PARAM_DOWNLOAD_WAYPOINTS: vgl->download_waypoints = data.b; break; @@ -523,6 +536,12 @@ static VikLayerParamData gps_layer_get_param ( VikGpsLayer *vgl, guint16 id, gbo case PARAM_UPLOAD_TRACKS: rv.b = vgl->upload_tracks; break; + case PARAM_DOWNLOAD_ROUTES: + rv.b = vgl->download_routes; + break; + case PARAM_UPLOAD_ROUTES: + rv.b = vgl->upload_routes; + break; case PARAM_DOWNLOAD_WAYPOINTS: rv.b = vgl->download_waypoints; break; @@ -593,6 +612,8 @@ VikGpsLayer *vik_gps_layer_new (VikViewport *vp) vgl->serial_port = NULL; vgl->download_tracks = TRUE; vgl->download_waypoints = TRUE; + vgl->download_routes = TRUE; + vgl->upload_routes = TRUE; vgl->upload_tracks = TRUE; vgl->upload_waypoints = TRUE; @@ -859,17 +880,25 @@ static void set_total_count(gint cnt, GpsSession *sess) const gchar *tmp_str; if (sess->direction == GPS_DOWN) { - if (sess->progress_label == sess->wp_label) - tmp_str = ngettext("Downloading %d waypoint...", "Downloading %d waypoints...", cnt); - else - tmp_str = ngettext("Downloading %d trackpoint...", "Downloading %d trackpoints...", cnt); + switch (sess->progress_type) { + case WPT: tmp_str = ngettext("Downloading %d waypoint...", "Downloading %d waypoints...", cnt); sess->total_count = cnt; break; + case TRK: tmp_str = ngettext("Downloading %d trackpoint...", "Downloading %d trackpoints...", cnt); sess->total_count = cnt; break; + default: + { + // Maybe a gpsbabel bug/feature (upto at least v1.4.3 or maybe my Garmin device) but the count always seems x2 too many for routepoints + gint mycnt = (cnt / 2) + 1; + tmp_str = ngettext("Downloading %d routepoint...", "Downloading %d routepoints...", mycnt); break; + sess->total_count = mycnt; + } + } } - else + else { - if (sess->progress_label == sess->wp_label) - tmp_str = ngettext("Uploading %d waypoint...", "Uploading %d waypoints...", cnt); - else - tmp_str = ngettext("Uploading %d trackpoint...", "Uploading %d trackpoints...", cnt); + switch (sess->progress_type) { + case WPT: tmp_str = ngettext("Uploading %d waypoint...", "Uploading %d waypoints...", cnt); break; + case TRK: tmp_str = ngettext("Uploading %d trackpoint...", "Uploading %d trackpoints...", cnt); break; + default: tmp_str = ngettext("Uploading %d routepoint...", "Uploading %d routepoints...", cnt); break; + } } g_snprintf(s, 128, tmp_str, cnt); @@ -892,31 +921,35 @@ static void set_current_count(gint cnt, GpsSession *sess) if (cnt < sess->total_count) { if (sess->direction == GPS_DOWN) { - if (sess->progress_label == sess->wp_label) - tmp_str = ngettext("Downloaded %d out of %d waypoint...", "Downloaded %d out of %d waypoints...", sess->total_count); - else - tmp_str = ngettext("Downloaded %d out of %d trackpoint...", "Downloaded %d out of %d trackpoints...", sess->total_count); + switch (sess->progress_type) { + case WPT: tmp_str = ngettext("Downloaded %d out of %d waypoint...", "Downloaded %d out of %d waypoints...", sess->total_count); break; + case TRK: tmp_str = ngettext("Downloaded %d out of %d trackpoint...", "Downloaded %d out of %d trackpoints...", sess->total_count); break; + default: tmp_str = ngettext("Downloaded %d out of %d routepoint...", "Downloaded %d out of %d routepoints...", sess->total_count); break; + } } else { - if (sess->progress_label == sess->wp_label) - tmp_str = ngettext("Uploaded %d out of %d waypoint...", "Uploaded %d out of %d waypoints...", sess->total_count); - else - tmp_str = ngettext("Uploaded %d out of %d trackpoint...", "Uploaded %d out of %d trackpoints...", sess->total_count); + switch (sess->progress_type) { + case WPT: tmp_str = ngettext("Uploaded %d out of %d waypoint...", "Uploaded %d out of %d waypoints...", sess->total_count); break; + case TRK: tmp_str = ngettext("Uploaded %d out of %d trackpoint...", "Uploaded %d out of %d trackpoints...", sess->total_count); break; + default: tmp_str = ngettext("Uploaded %d out of %d routepoint...", "Uploaded %d out of %d routepoints...", sess->total_count); break; + } } g_snprintf(s, 128, tmp_str, cnt, sess->total_count); } else { if (sess->direction == GPS_DOWN) { - if (sess->progress_label == sess->wp_label) - tmp_str = ngettext("Downloaded %d waypoint", "Downloaded %d waypoints", cnt); - else - tmp_str = ngettext("Downloaded %d trackpoint", "Downloaded %d trackpoints", cnt); + switch (sess->progress_type) { + case WPT: tmp_str = ngettext("Downloaded %d waypoint", "Downloaded %d waypoints", cnt); break; + case TRK: tmp_str = ngettext("Downloaded %d trackpoint", "Downloaded %d trackpoints", cnt); break; + default: tmp_str = ngettext("Downloaded %d routepoint", "Downloaded %d routepoints", cnt); break; + } } else { - if (sess->progress_label == sess->wp_label) - tmp_str = ngettext("Uploaded %d waypoint", "Uploaded %d waypoints", cnt); - else - tmp_str = ngettext("Uploaded %d trackpoint", "Uploaded %d trackpoints", cnt); + switch (sess->progress_type) { + case WPT: tmp_str = ngettext("Uploaded %d waypoint", "Uploaded %d waypoints", cnt); break; + case TRK: tmp_str = ngettext("Uploaded %d trackpoint", "Uploaded %d trackpoints", cnt); break; + default: tmp_str = ngettext("Uploaded %d routepoint", "Uploaded %d routepoints", cnt); break; + } } g_snprintf(s, 128, tmp_str, cnt); } @@ -966,13 +999,20 @@ static void gps_download_progress_func(BabelProgressCode c, gpointer data, GpsSe g_mutex_unlock(sess->mutex); gdk_threads_leave(); - /* tells us how many items there will be */ - if (strstr(line, "Xfer Wpt")) { + /* tells us the type of items that will follow */ + if (strstr(line, "Xfer Wpt")) { sess->progress_label = sess->wp_label; + sess->progress_type = WPT; } - if (strstr(line, "Xfer Trk")) { + if (strstr(line, "Xfer Trk")) { sess->progress_label = sess->trk_label; + sess->progress_type = TRK; } + if (strstr(line, "Xfer Rte")) { + sess->progress_label = sess->rte_label; + sess->progress_type = RTE; + } + if (strstr(line, "PRDDAT")) { gchar **tokens = g_strsplit(line, " ", 0); gchar info[128]; @@ -1006,7 +1046,7 @@ static void gps_download_progress_func(BabelProgressCode c, gpointer data, GpsSe } g_strfreev(tokens); } - if (strstr(line, "RECORD")) { + if (strstr(line, "RECORD")) { int lsb, msb, cnt; if (strlen(line) > 20) { @@ -1017,7 +1057,7 @@ static void gps_download_progress_func(BabelProgressCode c, gpointer data, GpsSe sess->count = 0; } } - if ( strstr(line, "WPTDAT") || strstr(line, "TRKHDR") || strstr(line, "TRKDAT") ) { + if ( strstr(line, "WPTDAT") || strstr(line, "TRKHDR") || strstr(line, "TRKDAT") || strstr(line, "RTEHDR") || strstr(line, "RTEWPT") ) { sess->count++; set_current_count(sess->count, sess); } @@ -1093,15 +1133,28 @@ static void gps_upload_progress_func(BabelProgressCode c, gpointer data, GpsSess if ( strstr(line, "WPTDAT")) { if (sess->count == 0) { sess->progress_label = sess->wp_label; + sess->progress_type = WPT; set_total_count(cnt, sess); } sess->count++; set_current_count(sess->count, sess); - + } + if ( strstr(line, "RTEHDR") || strstr(line, "RTEWPT") ) { + if (sess->count == 0) { + sess->progress_label = sess->rte_label; + sess->progress_type = RTE; + // Maybe a gpsbabel bug/feature (upto at least v1.4.3 or maybe my Garmin device) but the count always seems x2 too many for routepoints + // Anyway since we're uploading - we should know how many points we're going to put! + cnt = (cnt / 2) + 1; + set_total_count(cnt, sess); + } + sess->count++; + set_current_count(sess->count, sess); } if ( strstr(line, "TRKHDR") || strstr(line, "TRKDAT") ) { if (sess->count == 0) { sess->progress_label = sess->trk_label; + sess->progress_type = TRK; set_total_count(cnt, sess); } sess->count++; @@ -1192,11 +1245,13 @@ gint vik_gps_comm ( VikTrwLayer *vtl, VikViewport *vvp, VikLayersPanel *vlp, gboolean do_tracks, + gboolean do_routes, gboolean do_waypoints, gboolean turn_off ) { GpsSession *sess = g_malloc(sizeof(GpsSession)); char *tracks = NULL; + char *routes = NULL; char *waypoints = NULL; sess->mutex = g_mutex_new(); @@ -1227,18 +1282,22 @@ gint vik_gps_comm ( VikTrwLayer *vtl, tracks = "-t"; else tracks = ""; + if (do_routes) + routes = "-r"; + else + routes = ""; if (do_waypoints) waypoints = "-w"; else waypoints = ""; - sess->cmd_args = g_strdup_printf("-D 9 %s %s -%c %s", - tracks, waypoints, (dir == GPS_DOWN) ? 'i' : 'o', protocol); + sess->cmd_args = g_strdup_printf("-D 9 %s %s %s -%c %s", + tracks, routes, waypoints, (dir == GPS_DOWN) ? 'i' : 'o', protocol); tracks = NULL; waypoints = NULL; // Only create dialog if we're going to do some transferring - if ( do_tracks || do_waypoints ) { + if ( do_tracks || do_waypoints || do_routes ) { sess->dialog = gtk_dialog_new_with_buttons ( "", VIK_GTK_WINDOW_FROM_LAYER(vtl), 0, GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL ); gtk_dialog_set_response_sensitive ( GTK_DIALOG(sess->dialog), GTK_RESPONSE_ACCEPT, FALSE ); @@ -1253,10 +1312,12 @@ gint vik_gps_comm ( VikTrwLayer *vtl, sess->id_label = gtk_label_new (""); sess->wp_label = gtk_label_new (""); sess->trk_label = gtk_label_new (""); + sess->rte_label = gtk_label_new (""); gtk_box_pack_start ( GTK_BOX(GTK_DIALOG(sess->dialog)->vbox), sess->gps_label, FALSE, FALSE, 5 ); gtk_box_pack_start ( GTK_BOX(GTK_DIALOG(sess->dialog)->vbox), sess->wp_label, FALSE, FALSE, 5 ); gtk_box_pack_start ( GTK_BOX(GTK_DIALOG(sess->dialog)->vbox), sess->trk_label, FALSE, FALSE, 5 ); + gtk_box_pack_start ( GTK_BOX(GTK_DIALOG(sess->dialog)->vbox), sess->rte_label, FALSE, FALSE, 5 ); gtk_widget_show_all(sess->dialog); @@ -1305,7 +1366,7 @@ static void gps_upload_cb( gpointer layer_and_vlp[2] ) VikTrwLayer *vtl = vgl->trw_children[TRW_UPLOAD]; VikWindow *vw = VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vgl)); VikViewport *vvp = vik_window_viewport(vw); - vik_gps_comm(vtl, NULL, GPS_UP, vgl->protocol, vgl->serial_port, FALSE, vvp, vlp, vgl->upload_tracks, vgl->upload_waypoints, FALSE); + vik_gps_comm(vtl, NULL, GPS_UP, vgl->protocol, vgl->serial_port, FALSE, vvp, vlp, vgl->upload_tracks, vgl->upload_routes, vgl->upload_waypoints, FALSE); } static void gps_download_cb( gpointer layer_and_vlp[2] ) @@ -1315,9 +1376,9 @@ static void gps_download_cb( gpointer layer_and_vlp[2] ) VikWindow *vw = VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vgl)); VikViewport *vvp = vik_window_viewport(vw); #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION) - vik_gps_comm(vtl, NULL, GPS_DOWN, vgl->protocol, vgl->serial_port, vgl->realtime_tracking, vvp, NULL, vgl->download_tracks, vgl->download_waypoints, FALSE); + vik_gps_comm(vtl, NULL, GPS_DOWN, vgl->protocol, vgl->serial_port, vgl->realtime_tracking, vvp, NULL, vgl->download_tracks, vgl->download_routes, vgl->download_waypoints, FALSE); #else - vik_gps_comm(vtl, NULL, GPS_DOWN, vgl->protocol, vgl->serial_port, FALSE, vvp, NULL, vgl->download_tracks, vgl->download_waypoints, FALSE); + vik_gps_comm(vtl, NULL, GPS_DOWN, vgl->protocol, vgl->serial_port, FALSE, vvp, NULL, vgl->download_tracks, vgl->download_routes, vgl->download_waypoints, FALSE); #endif } @@ -1331,6 +1392,7 @@ static void gps_empty_upload_cb( gpointer layer_and_vlp[2] ) return; vik_trw_layer_delete_all_waypoints ( vgl-> trw_children[TRW_UPLOAD]); vik_trw_layer_delete_all_tracks ( vgl-> trw_children[TRW_UPLOAD]); + vik_trw_layer_delete_all_routes ( vgl-> trw_children[TRW_UPLOAD]); } static void gps_empty_download_cb( gpointer layer_and_vlp[2] ) @@ -1343,6 +1405,7 @@ static void gps_empty_download_cb( gpointer layer_and_vlp[2] ) return; vik_trw_layer_delete_all_waypoints ( vgl-> trw_children[TRW_DOWNLOAD]); vik_trw_layer_delete_all_tracks ( vgl-> trw_children[TRW_DOWNLOAD]); + vik_trw_layer_delete_all_routes ( vgl-> trw_children[TRW_DOWNLOAD]); } #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION) @@ -1369,8 +1432,10 @@ static void gps_empty_all_cb( gpointer layer_and_vlp[2] ) return; vik_trw_layer_delete_all_waypoints ( vgl-> trw_children[TRW_UPLOAD]); vik_trw_layer_delete_all_tracks ( vgl-> trw_children[TRW_UPLOAD]); + vik_trw_layer_delete_all_routes ( vgl-> trw_children[TRW_UPLOAD]); vik_trw_layer_delete_all_waypoints ( vgl-> trw_children[TRW_DOWNLOAD]); vik_trw_layer_delete_all_tracks ( vgl-> trw_children[TRW_DOWNLOAD]); + vik_trw_layer_delete_all_routes ( vgl-> trw_children[TRW_DOWNLOAD]); #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION) vik_trw_layer_delete_all_waypoints ( vgl-> trw_children[TRW_REALTIME]); vik_trw_layer_delete_all_tracks ( vgl-> trw_children[TRW_REALTIME]); diff --git a/src/vikgpslayer.h b/src/vikgpslayer.h index cef7924f..47d5ac66 100644 --- a/src/vikgpslayer.h +++ b/src/vikgpslayer.h @@ -47,6 +47,12 @@ typedef enum { GPS_UP } vik_gps_dir; +typedef enum { + WPT=0, + TRK=1, + RTE=2 +} vik_gps_xfer_type; + typedef struct _VikGpsLayer VikGpsLayer; gboolean vik_gps_layer_is_empty ( VikGpsLayer *vgl ); @@ -63,6 +69,7 @@ gint vik_gps_comm ( VikTrwLayer *vtl, VikViewport *vvp, VikLayersPanel *vlp, gboolean do_tracks, + gboolean do_routes, gboolean do_waypoints, gboolean turn_off); diff --git a/src/viktrack.c b/src/viktrack.c index aa3a2fe8..5ad64cda 100644 --- a/src/viktrack.c +++ b/src/viktrack.c @@ -2,6 +2,7 @@ * viking -- GPS Data and Topo Analyzer, Explorer, and Manager * * Copyright (C) 2003-2005, Evan Battaglia + * Copyright (c) 2012, Rob Norris * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -127,6 +128,7 @@ VikTrack *vik_track_copy ( const VikTrack *tr ) VikTrackpoint *new_tp; GList *tp_iter = tr->trackpoints; new_tr->visible = tr->visible; + new_tr->is_route = tr->is_route; new_tr->trackpoints = NULL; while ( tp_iter ) { @@ -350,6 +352,7 @@ VikTrack **vik_track_split_into_segments(VikTrack *t, guint *ret_len) if ( tr->description ) vik_track_set_description ( rv[i], tr->description ); rv[i]->visible = tr->visible; + rv[i]->is_route = tr->is_route; rv[i]->trackpoints = iter; i++; } @@ -1235,8 +1238,10 @@ VikTrack *vik_track_unmarshall (guint8 *data, guint datalen) guint ntp; gint i; - /* only the visibility is needed */ + /* basic properties: */ new_tr->visible = ((VikTrack *)data)->visible; + new_tr->is_route = ((VikTrack *)data)->is_route; + data += sizeof(*new_tr); ntp = *(guint *)data; diff --git a/src/viktrack.h b/src/viktrack.h index 6fdd7fdf..53d50897 100644 --- a/src/viktrack.h +++ b/src/viktrack.h @@ -55,10 +55,17 @@ struct _VikTrackpoint { gdouble pdop; /* VIK_DEFAULT_DOP if data unavailable */ }; +// Instead of having a separate VikRoute type, routes are considered tracks +// Thus all track operations must cope with a 'route' version +// [track functions handle having no timestamps anyway - so there is no practical difference in most cases] +// This is simpler than having to rewrite particularly every track function for route version +// given that they do the same things +// Mostly this matters in the display in deciding where and how they are shown typedef struct _VikTrack VikTrack; struct _VikTrack { GList *trackpoints; gboolean visible; + gboolean is_route; gchar *comment; gchar *description; guint8 ref_count; diff --git a/src/viktrwlayer.c b/src/viktrwlayer.c index 46d4730b..dc8d1623 100644 --- a/src/viktrwlayer.c +++ b/src/viktrwlayer.c @@ -23,7 +23,7 @@ * */ /* WARNING: If you go beyond this point, we are NOT responsible for any ill effects on your sanity */ -/* viktrwlayer.c -- 5000+ lines can make a difference in the state of things */ +/* viktrwlayer.c -- 8000+ lines can make a difference in the state of things */ #ifdef HAVE_CONFIG_H #include "config.h" @@ -122,10 +122,12 @@ struct _VikTrwLayer { VikLayer vl; GHashTable *tracks; GHashTable *tracks_iters; + GHashTable *routes; + GHashTable *routes_iters; GHashTable *waypoints_iters; GHashTable *waypoints; - GtkTreeIter waypoints_iter, tracks_iter; - gboolean tracks_visible, waypoints_visible; + GtkTreeIter tracks_iter, routes_iter, waypoints_iter; + gboolean tracks_visible, routes_visible, waypoints_visible; guint8 drawmode; guint8 drawpoints; guint8 drawpoints_size; @@ -154,7 +156,8 @@ struct _VikTrwLayer { GdkGC *waypoint_gc; GdkGC *waypoint_text_gc; GdkGC *waypoint_bg_gc; - VikTrack *current_track; + GdkFont *waypoint_font; + VikTrack *current_track; // ATM shared between new tracks and new routes guint16 ct_x1, ct_y1, ct_x2, ct_y2; gboolean draw_sync_done; gboolean draw_sync_do; @@ -269,6 +272,7 @@ static void trw_layer_export ( gpointer layer_and_vlp[2], const gchar* title, co static void trw_layer_goto_wp ( gpointer layer_and_vlp[2] ); static void trw_layer_new_wp ( gpointer lav[2] ); static void trw_layer_new_track ( gpointer lav[2] ); +static void trw_layer_new_route ( gpointer lav[2] ); static void trw_layer_finish_track ( gpointer lav[2] ); static void trw_layer_auto_waypoints_view ( gpointer lav[2] ); static void trw_layer_auto_tracks_view ( gpointer lav[2] ); @@ -300,6 +304,12 @@ static void trw_layer_acquire_geotagged_cb ( gpointer lav[2] ); static void trw_layer_acquire_file_cb ( gpointer lav[2] ); static void trw_layer_gps_upload ( gpointer lav[2] ); +// Specific route versions: +// Most track handling functions can handle operating on the route list +// However these ones are easier in separate functions +static void trw_layer_auto_routes_view ( gpointer lav[2] ); +static void trw_layer_delete_routes_from_selection ( gpointer lav[2] ); + /* pop-up items */ static void trw_layer_properties_item ( gpointer pass_along[7] ); static void trw_layer_goto_waypoint ( gpointer pass_along[6] ); @@ -456,6 +466,7 @@ static gchar* params_font_sizes[] = { VikLayerParam trw_layer_params[] = { { "tracks_visible", VIK_LAYER_PARAM_BOOLEAN, VIK_LAYER_NOT_IN_PROPERTIES }, { "waypoints_visible", VIK_LAYER_PARAM_BOOLEAN, VIK_LAYER_NOT_IN_PROPERTIES }, + { "routes_visible", VIK_LAYER_PARAM_BOOLEAN, VIK_LAYER_NOT_IN_PROPERTIES }, { "drawmode", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Track Drawing Mode:"), VIK_LAYER_WIDGET_RADIOGROUP, NULL }, { "drawlines", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Track Lines"), VIK_LAYER_WIDGET_CHECKBUTTON }, @@ -495,6 +506,7 @@ enum { // Sublayer visibilities PARAM_TV, PARAM_WV, + PARAM_RV, // Tracks PARAM_DM, PARAM_DL, @@ -711,13 +723,20 @@ static void trw_layer_copy_item_cb ( gpointer pass_along[6]) else name = NULL; // Broken :( } - else { + else if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) { VikTrack *trk = g_hash_table_lookup ( vtl->tracks, sublayer); if ( trk && trk->name ) name = trk->name; else name = NULL; // Broken :( } + else { + VikTrack *trk = g_hash_table_lookup ( vtl->routes, sublayer); + if ( trk && trk->name ) + name = trk->name; + else + name = NULL; // Broken :( + } a_clipboard_copy( VIK_CLIPBOARD_DATA_SUBLAYER, VIK_LAYER_TRW, subtype, len, name, data); @@ -756,14 +775,20 @@ static void trw_layer_copy_item ( VikTrwLayer *vtl, gint subtype, gpointer subla fi = g_malloc ( *len ); fi->len = *len; memcpy(fi->data, id, il); - } else { - + } else if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) { vik_track_marshall ( g_hash_table_lookup ( vtl->tracks, sublayer ), &id, &il ); // less magic than before... *len = sizeof(FlatItem) + 1 + il; fi = g_malloc ( *len ); fi->len = *len; memcpy(fi->data, id, il); + } else { + vik_track_marshall ( g_hash_table_lookup ( vtl->routes, sublayer ), &id, &il ); + // less magic than before... + *len = sizeof(FlatItem) + 1 + il; + fi = g_malloc ( *len ); + fi->len = *len; + memcpy(fi->data, id, il); } g_free(id); @@ -799,13 +824,29 @@ static gboolean trw_layer_paste_item ( VikTrwLayer *vtl, gint subtype, guint8 *i // When copying - we'll create a new name based on the original name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, t->name); vik_trw_layer_add_track ( vtl, name, t ); - track_convert (name, t, &vtl->coord_mode); + vik_track_convert (t, vtl->coord_mode); // Consider if redraw necessary for the new item if ( vtl->vl.visible && vtl->tracks_visible && t->visible ) vik_layer_emit_update ( VIK_LAYER(vtl), FALSE ); return TRUE; } + if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE && fi ) + { + VikTrack *t; + gchar *name; + + t = vik_track_unmarshall(fi->data, fi->len); + // When copying - we'll create a new name based on the original + name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, t->name); + vik_trw_layer_add_route ( vtl, name, t ); + vik_track_convert (t, vtl->coord_mode); + + // Consider if redraw necessary for the new item + if ( vtl->vl.visible && vtl->routes_visible && t->visible ) + vik_layer_emit_update ( VIK_LAYER(vtl), FALSE ); + return TRUE; + } return FALSE; } @@ -822,6 +863,7 @@ static gboolean trw_layer_set_param ( VikTrwLayer *vtl, guint16 id, VikLayerPara { case PARAM_TV: vtl->tracks_visible = data.b; break; case PARAM_WV: vtl->waypoints_visible = data.b; break; + case PARAM_RV: vtl->routes_visible = data.b; break; case PARAM_DM: vtl->drawmode = data.u; break; case PARAM_DP: vtl->drawpoints = data.b; break; case PARAM_DPS: @@ -890,6 +932,7 @@ static VikLayerParamData trw_layer_get_param ( VikTrwLayer *vtl, guint16 id, gbo { case PARAM_TV: rv.b = vtl->tracks_visible; break; case PARAM_WV: rv.b = vtl->waypoints_visible; break; + case PARAM_RV: rv.b = vtl->routes_visible; break; case PARAM_DM: rv.u = vtl->drawmode; break; case PARAM_DP: rv.b = vtl->drawpoints; break; case PARAM_DPS: rv.u = vtl->drawpoints_size; break; @@ -1029,9 +1072,11 @@ static VikTrwLayer* trw_layer_new ( gint drawmode ) rv->waypoints_iters = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, g_free ); rv->tracks = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) vik_track_free ); rv->tracks_iters = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, g_free ); + rv->routes = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) vik_track_free ); + rv->routes_iters = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, g_free ); /* TODO: constants at top */ - rv->waypoints_visible = rv->tracks_visible = TRUE; + rv->waypoints_visible = rv->tracks_visible = rv->routes_visible = TRUE; rv->drawmode = drawmode; rv->drawpoints = TRUE; rv->drawpoints_size = MIN_POINT_SIZE; @@ -1234,7 +1279,8 @@ static void trw_layer_draw_track ( const gchar *name, VikTrack *track, struct Dr then draw in the highlight colour. NB this supercedes the drawmode */ if ( dp->vtl && ( ( dp->vtl == vik_window_get_selected_trw_layer ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ) || - ( dp->vtl->tracks == vik_window_get_selected_tracks ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ) || + ( !track->is_route && ( dp->vtl->tracks == vik_window_get_selected_tracks ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ) ) || + ( track->is_route && ( dp->vtl->routes == vik_window_get_selected_tracks ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ) ) || track == vik_window_get_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ) ) { main_gc = vik_viewport_get_gc_highlight (dp->vp); drawing_highlight = TRUE; @@ -1645,6 +1691,9 @@ static void trw_layer_draw ( VikTrwLayer *l, gpointer data ) if ( l->tracks_visible ) g_hash_table_foreach ( l->tracks, (GHFunc) trw_layer_draw_track_cb, &dp ); + if ( l->routes_visible ) + g_hash_table_foreach ( l->routes, (GHFunc) trw_layer_draw_track_cb, &dp ); + if (l->waypoints_visible) g_hash_table_foreach ( l->waypoints, (GHFunc) trw_layer_draw_waypoint, &dp ); } @@ -1773,7 +1822,10 @@ static void trw_layer_realize_track ( gpointer id, VikTrack *track, gpointer pas #endif *new_iter = *((GtkTreeIter *) pass_along[1]); - g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->tracks_iters, id, new_iter ); + if ( track->is_route ) + g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->routes_iters, id, new_iter ); + else + g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->tracks_iters, id, new_iter ); if ( ! track->visible ) vik_treeview_item_set_visible ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[1], FALSE ); @@ -1813,6 +1865,15 @@ static void trw_layer_add_sublayer_waypoints ( VikTrwLayer *vtl, VikTreeview *vt #endif } +static void trw_layer_add_sublayer_routes ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter ) +{ +#ifdef VIK_CONFIG_ALPHABETIZED_TRW + vik_treeview_add_sublayer_alphabetized ( (VikTreeview *) vt, layer_iter, &(vtl->routes_iter), _("Routes"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_ROUTES, NULL, TRUE, FALSE ); +#else + vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->routes_iter), _("Routes"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_ROUTES, NULL, TRUE, FALSE ); +#endif +} + static void trw_layer_realize ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter ) { GtkTreeIter iter2; @@ -1825,8 +1886,20 @@ static void trw_layer_realize ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter * vik_treeview_item_set_visible ( (VikTreeview *) vt, &(vtl->tracks_iter), vtl->tracks_visible ); } + if ( g_hash_table_size (vtl->routes) > 0 ) { + + trw_layer_add_sublayer_routes ( vtl, vt, layer_iter ); + + pass_along[0] = &(vtl->routes_iter); + pass_along[4] = GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_ROUTE); + + g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_realize_track, pass_along ); + + vik_treeview_item_set_visible ( (VikTreeview *) vt, &(vtl->routes_iter), vtl->routes_visible ); + } + if ( g_hash_table_size (vtl->waypoints) > 0 ) { - trw_layer_add_sublayer_waypoints ( vtl, vt , layer_iter ); + trw_layer_add_sublayer_waypoints ( vtl, vt, layer_iter ); pass_along[0] = &(vtl->waypoints_iter); pass_along[4] = GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_WAYPOINT); @@ -1835,6 +1908,7 @@ static void trw_layer_realize ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter * vik_treeview_item_set_visible ( (VikTreeview *) vt, &(vtl->waypoints_iter), vtl->waypoints_visible ); } + } static gboolean trw_layer_sublayer_toggle_visible ( VikTrwLayer *l, gint subtype, gpointer sublayer ) @@ -1843,6 +1917,7 @@ static gboolean trw_layer_sublayer_toggle_visible ( VikTrwLayer *l, gint subtype { case VIK_TRW_LAYER_SUBLAYER_TRACKS: return (l->tracks_visible ^= 1); case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS: return (l->waypoints_visible ^= 1); + case VIK_TRW_LAYER_SUBLAYER_ROUTES: return (l->routes_visible ^= 1); case VIK_TRW_LAYER_SUBLAYER_TRACK: { VikTrack *t = g_hash_table_lookup ( l->tracks, sublayer ); @@ -1859,6 +1934,14 @@ static gboolean trw_layer_sublayer_toggle_visible ( VikTrwLayer *l, gint subtype else return TRUE; } + case VIK_TRW_LAYER_SUBLAYER_ROUTE: + { + VikTrack *t = g_hash_table_lookup ( l->routes, sublayer ); + if (t) + return (t->visible ^= 1); + else + return TRUE; + } } return TRUE; } @@ -1988,8 +2071,8 @@ static const gchar* trw_layer_layer_tooltip ( VikTrwLayer *vtl ) // Put together all the elements to form compact tooltip text g_snprintf (tmp_buf, sizeof(tmp_buf), - _("Tracks: %d - Waypoints: %d%s"), - g_hash_table_size (vtl->tracks), g_hash_table_size (vtl->waypoints), tbuf2); + _("Tracks: %d - Waypoints: %d - Routes: %d%s"), + g_hash_table_size (vtl->tracks), g_hash_table_size (vtl->waypoints), g_hash_table_size (vtl->routes), tbuf2); g_date_free (gdate_start); g_date_free (gdate_end); @@ -2013,9 +2096,27 @@ static const gchar* trw_layer_sublayer_tooltip ( VikTrwLayer *l, gint subtype, g return tmp_buf; } break; + case VIK_TRW_LAYER_SUBLAYER_ROUTES: + { + // Very simple tooltip - may expand detail in the future... + static gchar tmp_buf[32]; + g_snprintf (tmp_buf, sizeof(tmp_buf), + _("Routes: %d"), + g_hash_table_size (l->routes)); + return tmp_buf; + } + break; + + case VIK_TRW_LAYER_SUBLAYER_ROUTE: + // Same tooltip for a route case VIK_TRW_LAYER_SUBLAYER_TRACK: { - VikTrack *tr = g_hash_table_lookup ( l->tracks, sublayer ); + VikTrack *tr; + if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) + tr = g_hash_table_lookup ( l->tracks, sublayer ); + else + tr = g_hash_table_lookup ( l->routes, sublayer ); + if ( tr ) { // Could be a better way of handling strings - but this works... gchar time_buf1[20]; @@ -2200,6 +2301,21 @@ static gboolean trw_layer_selected ( VikTrwLayer *l, gint subtype, gpointer subl return TRUE; } break; + case VIK_TRW_LAYER_SUBLAYER_ROUTES: + { + vik_window_set_selected_tracks ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->routes, l ); + /* Mark for redraw */ + return TRUE; + } + break; + case VIK_TRW_LAYER_SUBLAYER_ROUTE: + { + VikTrack *track = g_hash_table_lookup ( l->routes, sublayer ); + vik_window_set_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), (gpointer)track, l ); + /* Mark for redraw */ + return TRUE; + } + break; case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS: { vik_window_set_selected_waypoints ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->waypoints, l ); @@ -2240,6 +2356,11 @@ GHashTable *vik_trw_layer_get_tracks ( VikTrwLayer *l ) return l->tracks; } +GHashTable *vik_trw_layer_get_routes ( VikTrwLayer *l ) +{ + return l->routes; +} + GHashTable *vik_trw_layer_get_waypoints ( VikTrwLayer *l ) { return l->waypoints; @@ -2287,6 +2408,15 @@ VikTrack *vik_trw_layer_get_track ( VikTrwLayer *vtl, const gchar *name ) return g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find, (gpointer) name ); } +/* + * Get route by name - not guaranteed to be unique + * Finds the first one + */ +VikTrack *vik_trw_layer_get_route ( VikTrwLayer *vtl, const gchar *name ) +{ + return g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find, (gpointer) name ); +} + static void trw_layer_find_maxmin_waypoints ( const gchar *name, const VikWaypoint *w, struct LatLon maxmin[2] ) { static VikCoord fixme; @@ -2323,35 +2453,10 @@ static void trw_layer_find_maxmin_tracks ( const gchar *name, const VikTrack *tr static void trw_layer_find_maxmin (VikTrwLayer *vtl, struct LatLon maxmin[2]) { - struct LatLon wpt_maxmin[2] = { {0.0,0.0}, {0.0,0.0} }; - struct LatLon trk_maxmin[2] = { {0.0,0.0}, {0.0,0.0} }; - - g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_find_maxmin_waypoints, wpt_maxmin ); - g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_find_maxmin_tracks, trk_maxmin ); - if ((wpt_maxmin[0].lat != 0.0 && wpt_maxmin[0].lat > trk_maxmin[0].lat) || trk_maxmin[0].lat == 0.0) { - maxmin[0].lat = wpt_maxmin[0].lat; - } - else { - maxmin[0].lat = trk_maxmin[0].lat; - } - if ((wpt_maxmin[0].lon != 0.0 && wpt_maxmin[0].lon > trk_maxmin[0].lon) || trk_maxmin[0].lon == 0.0) { - maxmin[0].lon = wpt_maxmin[0].lon; - } - else { - maxmin[0].lon = trk_maxmin[0].lon; - } - if ((wpt_maxmin[1].lat != 0.0 && wpt_maxmin[1].lat < trk_maxmin[1].lat) || trk_maxmin[1].lat == 0.0) { - maxmin[1].lat = wpt_maxmin[1].lat; - } - else { - maxmin[1].lat = trk_maxmin[1].lat; - } - if ((wpt_maxmin[1].lon != 0.0 && wpt_maxmin[1].lon < trk_maxmin[1].lon) || trk_maxmin[1].lon == 0.0) { - maxmin[1].lon = wpt_maxmin[1].lon; - } - else { - maxmin[1].lon = trk_maxmin[1].lon; - } + // Continually reuse maxmin to find the latest maximum and minimum values + g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_find_maxmin_waypoints, maxmin ); + g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_find_maxmin_tracks, maxmin ); + g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_find_maxmin_tracks, maxmin ); } gboolean vik_trw_layer_find_center ( VikTrwLayer *vtl, VikCoord *dest ) @@ -2569,7 +2674,13 @@ static void trw_layer_export_gpx_track ( gpointer pass_along[6] ) gpointer layer_and_vlp[2]; layer_and_vlp[0] = pass_along[0]; layer_and_vlp[1] = pass_along[1]; - VikTrack *trk = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ); + + VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]); + VikTrack *trk; + if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE ) + trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] ); + else + trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] ); if ( !trk || !trk->name ) return; @@ -2875,12 +2986,32 @@ static void trw_layer_gps_upload_any ( gpointer pass_along[6] ) VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]); VikLayersPanel *vlp = VIK_LAYERS_PANEL(pass_along[1]); - // May not actually get a track here as pass_along[3] can be null - VikTrack *track = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ); + // May not actually get a track here as pass_along[2&3] can be null + VikTrack *track = NULL; + vik_gps_xfer_type xfer_type = TRK; // VIK_TRW_LAYER_SUBLAYER_TRACKS = 0 so hard to test different from NULL! + gboolean xfer_all = FALSE; - gboolean on_track = track ? TRUE : FALSE; + if ( pass_along[2] ) { + xfer_all = FALSE; + if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE ) { + track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] ); + xfer_type = RTE; + } + else if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_TRACK ) { + track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] ); + xfer_type = TRK; + } + else if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS ) { + xfer_type = WPT; + } + else if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTES ) { + xfer_type = RTE; + } + } + else if ( !pass_along[4] ) + xfer_all = TRUE; // i.e. whole layer - if (on_track && !track->visible) { + if (track && !track->visible) { a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Can not upload invisible track.") ); return; } @@ -2903,7 +3034,7 @@ static void trw_layer_gps_upload_any ( gpointer pass_along[6] ) if ( response_w ) gtk_widget_grab_focus ( response_w ); - gpointer dgs = datasource_gps_setup ( dialog, on_track ); + gpointer dgs = datasource_gps_setup ( dialog, xfer_type, xfer_all ); if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) != GTK_RESPONSE_ACCEPT ) { datasource_gps_clean_up ( dgs ); @@ -2916,6 +3047,7 @@ static void trw_layer_gps_upload_any ( gpointer pass_along[6] ) gchar* port = datasource_gps_get_descriptor ( dgs ); // NB don't free the above strings as they're references to values held elsewhere gboolean do_tracks = datasource_gps_get_do_tracks ( dgs ); + gboolean do_routes = datasource_gps_get_do_routes ( dgs ); gboolean do_waypoints = datasource_gps_get_do_waypoints ( dgs ); gboolean turn_off = datasource_gps_get_off ( dgs ); @@ -2936,6 +3068,7 @@ static void trw_layer_gps_upload_any ( gpointer pass_along[6] ) vik_layers_panel_get_viewport (vlp), vlp, do_tracks, + do_routes, do_waypoints, turn_off ); } @@ -2977,6 +3110,35 @@ static void trw_layer_new_track ( gpointer lav[2] ) } } +static void trw_layer_new_route ( gpointer lav[2] ) +{ + VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]); + + if ( ! vtl->current_track ) { + gchar *name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, _("Route")) ; + vtl->current_track = vik_track_new(); + vtl->current_track->visible = TRUE; + vtl->current_track->is_route = TRUE; + vik_trw_layer_add_route ( vtl, name, vtl->current_track ); + + vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_CREATE_TRACK );// CREATE_ROUTE?? + } +} + +static void trw_layer_auto_routes_view ( gpointer lav[2] ) +{ + VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]); + VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]); + + if ( g_hash_table_size (vtl->routes) > 0 ) { + struct LatLon maxmin[2] = { {0,0}, {0,0} }; + g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_find_maxmin_tracks, maxmin ); + trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin ); + vik_layers_panel_emit_update ( vlp ); + } +} + + static void trw_layer_finish_track ( gpointer lav[2] ) { VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]); @@ -3037,7 +3199,10 @@ static void trw_layer_add_menu_items ( VikTrwLayer *vtl, GtkMenu *menu, gpointer gtk_widget_show ( item ); if ( vtl->current_track ) { - item = gtk_menu_item_new_with_mnemonic ( _("_Finish Track") ); + if ( vtl->current_track->is_route ) + item = gtk_menu_item_new_with_mnemonic ( _("_Finish Route") ); + else + item = gtk_menu_item_new_with_mnemonic ( _("_Finish Track") ); g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along ); gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); gtk_widget_show ( item ); @@ -3067,6 +3232,11 @@ static void trw_layer_add_menu_items ( VikTrwLayer *vtl, GtkMenu *menu, gpointer gtk_menu_shell_append (GTK_MENU_SHELL (view_submenu), item); gtk_widget_show ( item ); + item = gtk_menu_item_new_with_mnemonic ( _("View All _Routes") ); + g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_routes_view), pass_along ); + gtk_menu_shell_append (GTK_MENU_SHELL (view_submenu), item); + gtk_widget_show ( item ); + item = gtk_menu_item_new_with_mnemonic ( _("View All _Waypoints") ); g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_waypoints_view), pass_along ); gtk_menu_shell_append (GTK_MENU_SHELL (view_submenu), item); @@ -3145,6 +3315,14 @@ static void trw_layer_add_menu_items ( VikTrwLayer *vtl, GtkMenu *menu, gpointer // Make it available only when a new track *not* already in progress gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(vtl->current_track) ); + item = gtk_image_menu_item_new_with_mnemonic ( _("New _Route") ); + gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) ); + g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_route), pass_along ); + gtk_menu_shell_append (GTK_MENU_SHELL (new_submenu), item); + gtk_widget_show ( item ); + // Make it available only when a new track *not* already in progress + gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(vtl->current_track) ); + #ifdef VIK_CONFIG_GEONAMES GtkWidget *wikipedia_submenu = gtk_menu_new(); item = gtk_image_menu_item_new_with_mnemonic ( _("_Add Wikipedia Waypoints") ); @@ -3225,6 +3403,12 @@ static void trw_layer_add_menu_items ( VikTrwLayer *vtl, GtkMenu *menu, gpointer gtk_widget_show ( item ); gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), upload_submenu ); + item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _GPS...") ); + gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_FORWARD, GTK_ICON_SIZE_MENU) ); + g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_gps_upload), pass_along ); + gtk_menu_shell_append (GTK_MENU_SHELL (upload_submenu), item); + gtk_widget_show ( item ); + #ifdef VIK_CONFIG_OPENSTREETMAP item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _OSM...") ); gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) ); @@ -3233,12 +3417,6 @@ static void trw_layer_add_menu_items ( VikTrwLayer *vtl, GtkMenu *menu, gpointer gtk_widget_show ( item ); #endif - item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _GPS...") ); - gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_FORWARD, GTK_ICON_SIZE_MENU) ); - g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_gps_upload), pass_along ); - gtk_menu_shell_append (GTK_MENU_SHELL (upload_submenu), item); - gtk_widget_show ( item ); - GtkWidget *delete_submenu = gtk_menu_new (); item = gtk_image_menu_item_new_with_mnemonic ( _("De_lete") ); gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) ); @@ -3353,24 +3531,74 @@ void vik_trw_layer_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *t ) } +// Fake Route UUIDs vi simple increasing integer +static guint rt_uuid = 0; + +void vik_trw_layer_add_route ( VikTrwLayer *vtl, gchar *name, VikTrack *t ) +{ + rt_uuid++; + + vik_track_set_name (t, name); + + if ( VIK_LAYER(vtl)->realized ) + { + // Do we need to create the sublayer: + if ( g_hash_table_size (vtl->routes) == 0 ) { + trw_layer_add_sublayer_routes ( vtl, VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter) ); + } + + GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter)); + // Visibility column always needed for tracks +#ifdef VIK_CONFIG_ALPHABETIZED_TRW + vik_treeview_add_sublayer_alphabetized ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter), iter, name, vtl, GUINT_TO_POINTER(rt_uuid), VIK_TRW_LAYER_SUBLAYER_ROUTE, NULL, TRUE, TRUE ); +#else + vik_treeview_add_sublayer ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter), iter, name, vtl, GUINT_TO_POINTER(rt_uuid), VIK_TRW_LAYER_SUBLAYER_ROUTE, NULL, TRUE, TRUE ); +#endif + // Actual setting of visibility dependent on the track + vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, t->visible ); + + g_hash_table_insert ( vtl->routes_iters, GUINT_TO_POINTER(rt_uuid), iter ); + } + + g_hash_table_insert ( vtl->routes, GUINT_TO_POINTER(rt_uuid), t ); + +} + /* to be called whenever a track has been deleted or may have been changed. */ void trw_layer_cancel_tps_of_track ( VikTrwLayer *vtl, VikTrack *trk ) { if (vtl->current_tp_track == trk ) trw_layer_cancel_current_tp ( vtl, FALSE ); } - + gchar *trw_layer_new_unique_sublayer_name (VikTrwLayer *vtl, gint sublayer_type, const gchar *name) { - gint i = 2; - gchar *newname = g_strdup(name); - while ((sublayer_type == VIK_TRW_LAYER_SUBLAYER_TRACK) ? - (void *)vik_trw_layer_get_track(vtl, newname) : (void *)vik_trw_layer_get_waypoint(vtl, newname)) { - gchar *new_newname = g_strdup_printf("%s#%d", name, i); - g_free(newname); - newname = new_newname; - i++; - } + gint i = 2; + gchar *newname = g_strdup(name); + + gpointer id = NULL; + do { + id = NULL; + switch ( sublayer_type ) { + case VIK_TRW_LAYER_SUBLAYER_TRACK: + id = (gpointer) vik_trw_layer_get_track ( vtl, newname ); + break; + case VIK_TRW_LAYER_SUBLAYER_WAYPOINT: + id = (gpointer) vik_trw_layer_get_waypoint ( vtl, newname ); + break; + default: + id = (gpointer) vik_trw_layer_get_route ( vtl, newname ); + break; + } + // If found a name already in use try adding 1 to it and we try again + if ( id ) { + gchar *new_newname = g_strdup_printf("%s#%d", name, i); + g_free(newname); + newname = new_newname; + i++; + } + } while ( id != NULL); + return newname; } @@ -3391,7 +3619,10 @@ void vik_trw_layer_filein_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *t } else { // No more uniqueness of name forced when loading from a file - vik_trw_layer_add_track ( vtl, name, tr ); + if ( tr->is_route ) + vik_trw_layer_add_route ( vtl, name, tr ); + else + vik_trw_layer_add_track ( vtl, name, tr ); if ( vtl->route_finder_check_added_track ) { vik_track_remove_dup_points ( tr ); /* make "double point" track work to undo */ @@ -3420,6 +3651,16 @@ static void trw_layer_move_item ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, g vik_trw_layer_delete_track ( vtl_src, trk ); } + if (type == VIK_TRW_LAYER_SUBLAYER_ROUTE) { + VikTrack *trk = g_hash_table_lookup ( vtl_src->routes, id ); + + gchar *newname = trw_layer_new_unique_sublayer_name(vtl_dest, type, trk->name); + + VikTrack *trk2 = vik_track_copy ( trk ); + vik_trw_layer_add_route ( vtl_dest, newname, trk2 ); + vik_trw_layer_delete_route ( vtl_src, trk ); + } + if (type == VIK_TRW_LAYER_SUBLAYER_WAYPOINT) { VikWaypoint *wp = g_hash_table_lookup ( vtl_src->waypoints, id ); @@ -3446,13 +3687,19 @@ static void trw_layer_drag_drop_request ( VikTrwLayer *vtl_src, VikTrwLayer *vtl if (type==VIK_TRW_LAYER_SUBLAYER_WAYPOINTS) { g_hash_table_foreach ( vtl_src->waypoints, (GHFunc)trw_layer_enum_item, &items); } - + if (type==VIK_TRW_LAYER_SUBLAYER_ROUTES) { + g_hash_table_foreach ( vtl_src->routes, (GHFunc)trw_layer_enum_item, &items); + } + iter = items; while (iter) { if (type==VIK_TRW_LAYER_SUBLAYER_TRACKS) { - trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_TRACK); + trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_TRACK); + } + else if (type==VIK_TRW_LAYER_SUBLAYER_ROUTES) { + trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_ROUTE); } else { - trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_WAYPOINT); + trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_WAYPOINT); } iter = iter->next; } @@ -3528,6 +3775,55 @@ gboolean vik_trw_layer_delete_track ( VikTrwLayer *vtl, VikTrack *trk ) return was_visible; } +gboolean vik_trw_layer_delete_route ( VikTrwLayer *vtl, VikTrack *trk ) +{ + gboolean was_visible = FALSE; + + if ( trk && trk->name ) { + + if ( trk == vtl->current_track ) { + vtl->current_track = NULL; + vtl->current_tp_track = NULL; + vtl->current_tp_id = NULL; + vtl->moving_tp = FALSE; + } + + was_visible = trk->visible; + + if ( trk == vtl->route_finder_current_track ) + vtl->route_finder_current_track = NULL; + + if ( trk == vtl->route_finder_added_track ) + vtl->route_finder_added_track = NULL; + + trku_udata udata; + udata.trk = trk; + udata.uuid = NULL; + + // Hmmm, want key of it + gpointer *trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata ); + + if ( trkf && udata.uuid ) { + /* could be current_tp, so we have to check */ + trw_layer_cancel_tps_of_track ( vtl, trk ); + + GtkTreeIter *it = g_hash_table_lookup ( vtl->routes_iters, udata.uuid ); + + if ( it ) { + vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it ); + g_hash_table_remove ( vtl->routes_iters, udata.uuid ); + g_hash_table_remove ( vtl->routes, udata.uuid ); + + // If last sublayer, then remove sublayer container + if ( g_hash_table_size (vtl->routes) == 0 ) { + vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter) ); + } + } + } + } + return was_visible; +} + static gboolean trw_layer_delete_waypoint ( VikTrwLayer *vtl, VikWaypoint *wp ) { gboolean was_visible = FALSE; @@ -3626,9 +3922,9 @@ static gboolean trw_layer_track_find_uuid_by_name ( const gpointer id, const Vik /* * Delete a track by the given name * NOTE: ATM this will delete the first encountered Track with the specified name - * as there be multiple track with the same name + * as there may be multiple tracks with the same name within the specified hash table */ -static gboolean trw_layer_delete_track_by_name ( VikTrwLayer *vtl, const gchar *name ) +static gboolean trw_layer_delete_track_by_name ( VikTrwLayer *vtl, const gchar *name, GHashTable *ht_tracks ) { tpu_udata udata; // Fake a track with the given name @@ -3638,12 +3934,18 @@ static gboolean trw_layer_delete_track_by_name ( VikTrwLayer *vtl, const gchar * udata.uuid = NULL; // Hmmm, want key of it - gpointer *trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid_by_name, &udata ); + gpointer *trkf = g_hash_table_find ( ht_tracks, (GHRFunc) trw_layer_track_find_uuid_by_name, &udata ); vik_track_free (udata.trk); - if ( trkf && udata.uuid ) - return vik_trw_layer_delete_track (vtl, g_hash_table_lookup ( vtl->tracks, udata.uuid )); + if ( trkf && udata.uuid ) { + // This could be a little better written... + if ( vtl->tracks == ht_tracks ) + return vik_trw_layer_delete_track (vtl, g_hash_table_lookup ( ht_tracks, udata.uuid )); + if ( vtl->routes == ht_tracks ) + return vik_trw_layer_delete_route (vtl, g_hash_table_lookup ( ht_tracks, udata.uuid )); + return FALSE; + } else return FALSE; } @@ -3653,6 +3955,24 @@ static void remove_item_from_treeview(const gchar *name, GtkTreeIter *it, VikTre vik_treeview_item_delete (vt, it ); } +void vik_trw_layer_delete_all_routes ( VikTrwLayer *vtl ) +{ + + vtl->current_track = NULL; + vtl->route_finder_current_track = NULL; + vtl->route_finder_added_track = NULL; + if (vtl->current_tp_track) + trw_layer_cancel_current_tp(vtl, FALSE); + + g_hash_table_foreach(vtl->routes_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt); + g_hash_table_remove_all(vtl->routes_iters); + g_hash_table_remove_all(vtl->routes); + + vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter) ); + + vik_layer_emit_update ( VIK_LAYER(vtl), FALSE ); +} + void vik_trw_layer_delete_all_tracks ( VikTrwLayer *vtl ) { @@ -3698,17 +4018,27 @@ static void trw_layer_delete_all_tracks ( gpointer lav[2] ) vik_trw_layer_delete_all_tracks (vtl); } -static void trw_layer_delete_all_waypoints ( gpointer lav[2] ) +static void trw_layer_delete_all_routes ( gpointer lav[2] ) { VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]); // Get confirmation from the user if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl), - _("Are you sure you want to delete all waypoints in %s?"), - vik_layer_get_name ( VIK_LAYER(vtl) ) ) ) - vik_trw_layer_delete_all_waypoints (vtl); + _("Are you sure you want to delete all routes in %s?"), + vik_layer_get_name ( VIK_LAYER(vtl) ) ) ) + vik_trw_layer_delete_all_routes (vtl); } -static void trw_layer_delete_item ( gpointer pass_along[6] ) +static void trw_layer_delete_all_waypoints ( gpointer lav[2] ) +{ + VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]); + // Get confirmation from the user + if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl), + _("Are you sure you want to delete all waypoints in %s?"), + vik_layer_get_name ( VIK_LAYER(vtl) ) ) ) + vik_trw_layer_delete_all_waypoints (vtl); +} + +static void trw_layer_delete_item ( gpointer pass_along[6] ) { VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]); gboolean was_visible = FALSE; @@ -3726,7 +4056,7 @@ static void trw_layer_delete_item ( gpointer pass_along[6] ) was_visible = trw_layer_delete_waypoint ( vtl, wp ); } } - else + else if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_TRACK ) { VikTrack *trk = g_hash_table_lookup ( vtl->tracks, pass_along[3] ); if ( trk && trk->name ) { @@ -3739,6 +4069,19 @@ static void trw_layer_delete_item ( gpointer pass_along[6] ) was_visible = vik_trw_layer_delete_track ( vtl, trk ); } } + else + { + VikTrack *trk = g_hash_table_lookup ( vtl->routes, pass_along[3] ); + if ( trk && trk->name ) { + if ( GPOINTER_TO_INT ( pass_along[4]) ) + // Get confirmation from the user + if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl), + _("Are you sure you want to delete the route \"%s\""), + trk->name ) ) + return; + was_visible = vik_trw_layer_delete_route ( vtl, trk ); + } + } if ( was_visible ) vik_layer_emit_update ( VIK_LAYER(vtl), FALSE ); } @@ -3762,7 +4105,12 @@ static void trw_layer_properties_item ( gpointer pass_along[7] ) } else { - VikTrack *tr = g_hash_table_lookup ( vtl->tracks, pass_along[3] ); + VikTrack *tr; + if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_TRACK ) + tr = g_hash_table_lookup ( vtl->tracks, pass_along[3] ); + else + tr = g_hash_table_lookup ( vtl->routes, pass_along[3] ); + if ( tr && tr->name ) { vik_trw_layer_propwin_run ( VIK_GTK_WINDOW_FROM_LAYER(vtl), @@ -3795,14 +4143,26 @@ static void goto_coord ( gpointer *vlp, gpointer vl, gpointer vvp, const VikCoor static void trw_layer_goto_track_startpoint ( gpointer pass_along[6] ) { - GList *trps = ((VikTrack *) g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ))->trackpoints; - if ( trps && trps->data ) - goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(((VikTrackpoint *) trps->data)->coord)); + VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0]; + VikTrack *track; + if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE ) + track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] ); + else + track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] ); + + if ( track && track->trackpoints ) + goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(((VikTrackpoint *) track->trackpoints->data)->coord) ); } static void trw_layer_goto_track_center ( gpointer pass_along[6] ) { - VikTrack *track = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ); + VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0]; + VikTrack *track; + if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE ) + track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] ); + else + track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] ); + if ( track && track->trackpoints ) { struct LatLon average, maxmin[2] = { {0,0}, {0,0} }; @@ -3810,7 +4170,7 @@ static void trw_layer_goto_track_center ( gpointer pass_along[6] ) trw_layer_find_maxmin_tracks ( NULL, track, maxmin ); average.lat = (maxmin[0].lat+maxmin[1].lat)/2; average.lon = (maxmin[0].lon+maxmin[1].lon)/2; - vik_coord_load_from_latlon ( &coord, VIK_TRW_LAYER(pass_along[0])->coord_mode, &average ); + vik_coord_load_from_latlon ( &coord, vtl->coord_mode, &average ); goto_coord ( pass_along[1], pass_along[0], pass_along[5], &coord); } } @@ -3818,7 +4178,14 @@ static void trw_layer_goto_track_center ( gpointer pass_along[6] ) static void trw_layer_extend_track_end ( gpointer pass_along[6] ) { VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]); - VikTrack *track = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ); + VikTrack *track; + if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE ) + track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] ); + else + track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] ); + + if ( !track ) + return; vtl->current_track = track; vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_CREATE_TRACK); @@ -3852,14 +4219,30 @@ static void trw_layer_apply_dem_data ( gpointer pass_along[6] ) { /* TODO: check & warn if no DEM data, or no applicable DEM data. */ /* Also warn if overwrite old elevation data */ - VikTrack *track = (VikTrack *) g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ); + VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0]; + VikTrack *track; + if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE ) + track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] ); + else + track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] ); - vik_track_apply_dem_data ( track ); + if ( track ) + vik_track_apply_dem_data ( track ); } static void trw_layer_goto_track_endpoint ( gpointer pass_along[6] ) { - GList *trps = ((VikTrack *) g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ))->trackpoints; + VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0]; + VikTrack *track; + if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE ) + track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] ); + else + track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] ); + + if ( !track ) + return; + + GList *trps = track->trackpoints; if ( !trps ) return; trps = g_list_last(trps); @@ -3868,7 +4251,17 @@ static void trw_layer_goto_track_endpoint ( gpointer pass_along[6] ) static void trw_layer_goto_track_max_speed ( gpointer pass_along[6] ) { - VikTrackpoint* vtp = vik_track_get_tp_by_max_speed ( g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ) ); + VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0]; + VikTrack *track; + if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE ) + track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] ); + else + track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] ); + + if ( !track ) + return; + + VikTrackpoint* vtp = vik_track_get_tp_by_max_speed ( track ); if ( !vtp ) return; goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(vtp->coord)); @@ -3876,7 +4269,17 @@ static void trw_layer_goto_track_max_speed ( gpointer pass_along[6] ) static void trw_layer_goto_track_max_alt ( gpointer pass_along[6] ) { - VikTrackpoint* vtp = vik_track_get_tp_by_max_alt ( g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ) ); + VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0]; + VikTrack *track; + if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE ) + track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] ); + else + track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] ); + + if ( !track ) + return; + + VikTrackpoint* vtp = vik_track_get_tp_by_max_alt ( track ); if ( !vtp ) return; goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(vtp->coord)); @@ -3884,7 +4287,17 @@ static void trw_layer_goto_track_max_alt ( gpointer pass_along[6] ) static void trw_layer_goto_track_min_alt ( gpointer pass_along[6] ) { - VikTrackpoint* vtp = vik_track_get_tp_by_min_alt ( g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ) ); + VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0]; + VikTrack *track; + if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE ) + track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] ); + else + track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] ); + + if ( !track ) + return; + + VikTrackpoint* vtp = vik_track_get_tp_by_min_alt ( track ); if ( !vtp ) return; goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(vtp->coord)); @@ -3895,7 +4308,13 @@ static void trw_layer_goto_track_min_alt ( gpointer pass_along[6] ) */ static void trw_layer_auto_track_view ( gpointer pass_along[6] ) { - VikTrack *trk = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ); + VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0]; + VikTrack *trk; + if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE ) + trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] ); + else + trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] ); + if ( trk && trk->trackpoints ) { struct LatLon maxmin[2] = { {0,0}, {0,0} }; @@ -4055,7 +4474,16 @@ static void trw_layer_merge_with_other ( gpointer pass_along[6] ) { VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0]; GList *other_tracks = NULL; - VikTrack *track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] ); + GHashTable *ght_tracks; + if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE ) + ght_tracks = vtl->routes; + else + ght_tracks = vtl->tracks; + + VikTrack *track = (VikTrack *) g_hash_table_lookup ( ght_tracks, pass_along[3] ); + + if ( !track ) + return; if ( !track->trackpoints ) return; @@ -4067,7 +4495,7 @@ static void trw_layer_merge_with_other ( gpointer pass_along[6] ) // i.e. either those times, or those without udata.with_timestamps = (VIK_TRACKPOINT(track->trackpoints->data)->has_timestamp); - g_hash_table_foreach(vtl->tracks, find_tracks_with_timestamp_type, (gpointer)&udata); + g_hash_table_foreach(ght_tracks, find_tracks_with_timestamp_type, (gpointer)&udata); other_tracks = g_list_reverse(other_tracks); if ( !other_tracks ) { @@ -4084,15 +4512,17 @@ static void trw_layer_merge_with_other ( gpointer pass_along[6] ) GList *other_tracks_names = NULL; GList *iter = g_list_first ( other_tracks ); while ( iter ) { - other_tracks_names = g_list_append ( other_tracks_names, VIK_TRACK(g_hash_table_lookup (vtl->tracks, iter->data))->name ); + other_tracks_names = g_list_append ( other_tracks_names, VIK_TRACK(g_hash_table_lookup (ght_tracks, iter->data))->name ); iter = g_list_next ( iter ); } other_tracks_names = g_list_sort_with_data (other_tracks_names, sort_alphabetically, NULL); GList *merge_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl), - other_tracks_names, TRUE, - _("Merge with..."), _("Select track to merge with")); + other_tracks_names, + TRUE, + _("Merge with..."), + track->is_route ? _("Select route to merge with") : _("Select track to merge with")); g_list_free(other_tracks); g_list_free(other_tracks_names); @@ -4100,11 +4530,19 @@ static void trw_layer_merge_with_other ( gpointer pass_along[6] ) { GList *l; for (l = merge_list; l != NULL; l = g_list_next(l)) { - VikTrack *merge_track = vik_trw_layer_get_track ( vtl, l->data ); + VikTrack *merge_track; + if ( track->is_route ) + merge_track = vik_trw_layer_get_route ( vtl, l->data ); + else + merge_track = vik_trw_layer_get_track ( vtl, l->data ); + if (merge_track) { track->trackpoints = g_list_concat(track->trackpoints, merge_track->trackpoints); merge_track->trackpoints = NULL; - vik_trw_layer_delete_track (vtl, merge_track); + if ( track->is_route ) + vik_trw_layer_delete_route (vtl, merge_track); + else + vik_trw_layer_delete_track (vtl, merge_track); track->trackpoints = g_list_sort(track->trackpoints, trackpoint_compare); } } @@ -4132,7 +4570,7 @@ static void trw_layer_sorted_track_id_by_name_list_exclude_self (const gpointer } /** - * Join - this allows combining 'routes' and 'tracks' + * Join - this allows combining 'tracks' and 'track routes' * i.e. doesn't care about whether tracks have consistent timestamps * ATM can only append one track at a time to the currently selected track */ @@ -4140,7 +4578,17 @@ static void trw_layer_append_track ( gpointer pass_along[6] ) { VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0]; - VikTrack *trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] ); + VikTrack *trk; + GHashTable *ght_tracks; + if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE ) + ght_tracks = vtl->routes; + else + ght_tracks = vtl->tracks; + + trk = (VikTrack *) g_hash_table_lookup ( ght_tracks, pass_along[3] ); + + if ( !trk ) + return; GList *other_tracks_names = NULL; @@ -4151,16 +4599,17 @@ static void trw_layer_append_track ( gpointer pass_along[6] ) udata.result = &other_tracks_names; udata.exclude = trk->trackpoints; - g_hash_table_foreach(vtl->tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list_exclude_self, (gpointer)&udata); + g_hash_table_foreach(ght_tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list_exclude_self, (gpointer)&udata); // Note the limit to selecting one track only // this is to control the ordering of appending tracks, i.e. the selected track always goes after the current track // (otherwise with multiple select the ordering would not be controllable by the user - automatically being alphabetically) GList *append_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl), - other_tracks_names, - FALSE, - _("Append Track"), - _("Select the track to append after the current track")); + other_tracks_names, + FALSE, + trk->is_route ? _("Append Route"): _("Append Track"), + trk->is_route ? _("Select the route to append after the current route") : + _("Select the track to append after the current track") ); g_list_free(other_tracks_names); @@ -4170,11 +4619,19 @@ static void trw_layer_append_track ( gpointer pass_along[6] ) for (l = append_list; l != NULL; l = g_list_next(l)) { // TODO: at present this uses the first track found by name, // which with potential multiple same named tracks may not be the one selected... - VikTrack *append_track = vik_trw_layer_get_track ( vtl, l->data ); + VikTrack *append_track; + if ( trk->is_route ) + append_track = vik_trw_layer_get_route ( vtl, l->data ); + else + append_track = vik_trw_layer_get_track ( vtl, l->data ); + if ( append_track ) { trk->trackpoints = g_list_concat(trk->trackpoints, append_track->trackpoints); append_track->trackpoints = NULL; - vik_trw_layer_delete_track (vtl, append_track); + if ( trk->is_route ) + vik_trw_layer_delete_route (vtl, append_track); + else + vik_trw_layer_delete_track (vtl, append_track); } } for (l = append_list; l != NULL; l = g_list_next(l)) @@ -4300,13 +4757,13 @@ static void trw_layer_merge_by_timestamp ( gpointer pass_along[6] ) /** * Split a track at the currently selected trackpoint */ -static void trw_layer_split_at_selected_trackpoint ( VikTrwLayer *vtl ) +static void trw_layer_split_at_selected_trackpoint ( VikTrwLayer *vtl, gint subtype ) { if ( !vtl->current_tpl ) return; if ( vtl->current_tpl->next && vtl->current_tpl->prev ) { - gchar *name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, vtl->current_tp_track->name); + gchar *name = trw_layer_new_unique_sublayer_name(vtl, subtype, vtl->current_tp_track->name); if ( name ) { VikTrack *tr = vik_track_new (); GList *newglist = g_list_alloc (); @@ -4314,6 +4771,8 @@ static void trw_layer_split_at_selected_trackpoint ( VikTrwLayer *vtl ) newglist->next = vtl->current_tpl->next; newglist->data = vik_trackpoint_copy(VIK_TRACKPOINT(vtl->current_tpl->data)); tr->trackpoints = newglist; + tr->is_route = vtl->current_tp_track->is_route; + tr->visible = TRUE; vtl->current_tpl->next->prev = newglist; /* end old track here */ vtl->current_tpl->next = NULL; @@ -4321,16 +4780,22 @@ static void trw_layer_split_at_selected_trackpoint ( VikTrwLayer *vtl ) vtl->current_tpl = newglist; /* change tp to first of new track. */ vtl->current_tp_track = tr; - tr->visible = TRUE; - - vik_trw_layer_add_track ( vtl, name, tr ); + if ( tr->is_route ) + vik_trw_layer_add_route ( vtl, name, tr ); + else + vik_trw_layer_add_track ( vtl, name, tr ); trku_udata udata; udata.trk = tr; udata.uuid = NULL; // Also need id of newly created track - gpointer *trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata ); + gpointer *trkf; + if ( tr->is_route ) + trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata ); + else + trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata ); + if ( trkf && udata.uuid ) vtl->current_tp_id = udata.uuid; else @@ -4421,7 +4886,14 @@ static void trw_layer_split_by_timestamp ( gpointer pass_along[6] ) static void trw_layer_split_by_n_points ( gpointer pass_along[6] ) { VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0]; - VikTrack *track = (VikTrack *)g_hash_table_lookup ( vtl->tracks, pass_along[3] ); + VikTrack *track; + if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE ) + track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] ); + else + track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] ); + + if ( !track ) + return; // Check valid track GList *trps = track->trackpoints; @@ -4475,15 +4947,24 @@ static void trw_layer_split_by_n_points ( gpointer pass_along[6] ) tr = vik_track_new(); tr->visible = track->visible; + tr->is_route = track->is_route; tr->trackpoints = (GList *)(iter->data); - new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, track->name); - vik_trw_layer_add_track(vtl, new_tr_name, tr); - + if ( track->is_route ) { + new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, track->name); + vik_trw_layer_add_route(vtl, new_tr_name, tr); + } + else { + new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, track->name); + vik_trw_layer_add_track(vtl, new_tr_name, tr); + } iter = g_list_next(iter); } // Remove original track and then update the display - vik_trw_layer_delete_track (vtl, track); + if ( track->is_route ) + vik_trw_layer_delete_route (vtl, track); + else + vik_trw_layer_delete_track (vtl, track); vik_layer_emit_update(VIK_LAYER(pass_along[0]), FALSE); } g_list_free(newlists); @@ -4495,16 +4976,22 @@ static void trw_layer_split_by_n_points ( gpointer pass_along[6] ) static void trw_layer_split_at_trackpoint ( gpointer pass_along[6] ) { VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0]; - trw_layer_split_at_selected_trackpoint ( vtl ); + gint subtype = GPOINTER_TO_INT (pass_along[2]); + trw_layer_split_at_selected_trackpoint ( vtl, subtype ); } /** * Split a track by its segments + * Routes do not have segments so don't call this for routes */ static void trw_layer_split_segments ( gpointer pass_along[6] ) { VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0]; - VikTrack *trk = (VikTrack *)g_hash_table_lookup ( vtl->tracks, pass_along[3] ); + VikTrack *trk = g_hash_table_lookup ( vtl->tracks, pass_along[3] ); + + if ( !trk ) + return; + guint ntracks; VikTrack **tracks = vik_track_split_into_segments (trk, &ntracks); @@ -4535,7 +5022,14 @@ static void trw_layer_split_segments ( gpointer pass_along[6] ) static void trw_layer_delete_points_same_position ( gpointer pass_along[6] ) { VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0]; - VikTrack *trk = (VikTrack *)g_hash_table_lookup ( vtl->tracks, pass_along[3] ); + VikTrack *trk; + if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE ) + trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] ); + else + trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] ); + + if ( !trk ) + return; gulong removed = vik_track_remove_dup_points ( trk ); @@ -4558,7 +5052,14 @@ static void trw_layer_delete_points_same_position ( gpointer pass_along[6] ) static void trw_layer_delete_points_same_time ( gpointer pass_along[6] ) { VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0]; - VikTrack *trk = (VikTrack *)g_hash_table_lookup ( vtl->tracks, pass_along[3] ); + VikTrack *trk; + if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE ) + trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] ); + else + trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] ); + + if ( !trk ) + return; gulong removed = vik_track_remove_same_time_points ( trk ); @@ -4580,7 +5081,14 @@ static void trw_layer_delete_points_same_time ( gpointer pass_along[6] ) static void trw_layer_reverse ( gpointer pass_along[6] ) { VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0]; - VikTrack *track = (VikTrack *)g_hash_table_lookup ( vtl->tracks, pass_along[3] ); + VikTrack *track; + if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE ) + track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] ); + else + track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] ); + + if ( ! track ) + return; // Check valid track GList *trps = track->trackpoints; @@ -4651,14 +5159,14 @@ static gint check_tracks_for_same_name ( gconstpointer aa, gconstpointer bb, gpo } /** - * Find out if any tracks have the same name in this layer + * Find out if any tracks have the same name in this hash table */ -static gboolean trw_layer_has_same_track_names ( VikTrwLayer *vtl ) +static gboolean trw_layer_has_same_track_names ( GHashTable *ht_tracks ) { // Sort items by name, then compare if any next to each other are the same GList *track_names = NULL; - g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names ); + g_hash_table_foreach ( ht_tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names ); // No tracks if ( ! track_names ) @@ -4678,10 +5186,11 @@ static gboolean trw_layer_has_same_track_names ( VikTrwLayer *vtl ) } /** - * Force unqiue track names for this layer + * Force unqiue track names for the track table specified * Note the panel is a required parameter to enable the update of the names displayed + * Specify if on tracks or else on routes */ -static void vik_trw_layer_uniquify_tracks ( VikTrwLayer *vtl, VikLayersPanel *vlp ) +static void vik_trw_layer_uniquify_tracks ( VikTrwLayer *vtl, VikLayersPanel *vlp, GHashTable *track_table, gboolean ontrack ) { // . Search list for an instance of repeated name // . get track of this name @@ -4695,7 +5204,7 @@ static void vik_trw_layer_uniquify_tracks ( VikTrwLayer *vtl, VikLayersPanel *vl udata.has_same_track_name = FALSE; udata.same_track_name = NULL; - g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names ); + g_hash_table_foreach ( track_table, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names ); // No tracks if ( ! track_names ) @@ -4710,7 +5219,11 @@ static void vik_trw_layer_uniquify_tracks ( VikTrwLayer *vtl, VikLayersPanel *vl while ( udata.has_same_track_name ) { // Find a track with the same name - VikTrack *trk = vik_trw_layer_get_track ( vtl, (gpointer) udata.same_track_name ); + VikTrack *trk; + if ( ontrack ) + trk = vik_trw_layer_get_track ( vtl, (gpointer) udata.same_track_name ); + else + trk = vik_trw_layer_get_route ( vtl, (gpointer) udata.same_track_name ); if ( ! trk ) { // Broken :( @@ -4729,11 +5242,15 @@ static void vik_trw_layer_uniquify_tracks ( VikTrwLayer *vtl, VikLayersPanel *vl udataU.uuid = NULL; // Need want key of it for treeview update - gpointer *trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udataU ); + gpointer *trkf = g_hash_table_find ( track_table, (GHRFunc) trw_layer_track_find_uuid, &udataU ); if ( trkf && udataU.uuid ) { - GtkTreeIter *it = g_hash_table_lookup ( vtl->tracks_iters, udataU.uuid ); + GtkTreeIter *it; + if ( ontrack ) + it = g_hash_table_lookup ( vtl->tracks_iters, udataU.uuid ); + else + it = g_hash_table_lookup ( vtl->routes_iters, udataU.uuid ); if ( it ) { vik_treeview_item_set_name ( VIK_LAYER(vtl)->vt, it, newname ); @@ -4745,7 +5262,7 @@ static void vik_trw_layer_uniquify_tracks ( VikTrwLayer *vtl, VikLayersPanel *vl // Start trying to find same names again... track_names = NULL; - g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names ); + g_hash_table_foreach ( track_table, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names ); udata.has_same_track_name = FALSE; GList *dummy_list2 = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata ); @@ -4767,10 +5284,10 @@ static void trw_layer_delete_tracks_from_selection ( gpointer lav[2] ) GList *all = NULL; // Ensure list of track names offered is unique - if ( trw_layer_has_same_track_names ( vtl ) ) { + if ( trw_layer_has_same_track_names ( vtl->tracks ) ) { if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) { - vik_trw_layer_uniquify_tracks ( vtl, VIK_LAYERS_PANEL(lav[1]) ); + vik_trw_layer_uniquify_tracks ( vtl, VIK_LAYERS_PANEL(lav[1]), vtl->tracks, TRUE ); } else return; @@ -4798,7 +5315,54 @@ static void trw_layer_delete_tracks_from_selection ( gpointer lav[2] ) GList *l; for (l = delete_list; l != NULL; l = g_list_next(l)) { // This deletes first trk it finds of that name (but uniqueness is enforced above) - trw_layer_delete_track_by_name (vtl, l->data); + trw_layer_delete_track_by_name (vtl, l->data, vtl->tracks); + } + g_list_free(delete_list); + vik_layer_emit_update( VIK_LAYER(vtl), FALSE ); + } +} + +/** + * + */ +static void trw_layer_delete_routes_from_selection ( gpointer lav[2] ) +{ + VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]); + GList *all = NULL; + + // Ensure list of track names offered is unique + if ( trw_layer_has_same_track_names ( vtl->routes ) ) { + if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl), + _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) { + vik_trw_layer_uniquify_tracks ( vtl, VIK_LAYERS_PANEL(lav[1]), vtl->routes, FALSE ); + } + else + return; + } + + // Sort list alphabetically for better presentation + g_hash_table_foreach(vtl->routes, (GHFunc) trw_layer_sorted_track_id_by_name_list, &all); + + if ( ! all ) { + a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No routes found")); + return; + } + + // Get list of items to delete from the user + GList *delete_list = a_dialog_select_from_list ( VIK_GTK_WINDOW_FROM_LAYER(vtl), + all, + TRUE, + _("Delete Selection"), + _("Select routes to delete") ); + g_list_free(all); + + // Delete requested routes + // since specificly requested, IMHO no need for extra confirmation + if ( delete_list ) { + GList *l; + for (l = delete_list; l != NULL; l = g_list_next(l)) { + // This deletes first route it finds of that name (but uniqueness is enforced above) + trw_layer_delete_track_by_name (vtl, l->data, vtl->routes); } g_list_free(delete_list); vik_layer_emit_update( VIK_LAYER(vtl), FALSE ); @@ -5060,6 +5624,43 @@ static const gchar* trw_layer_sublayer_rename_request ( VikTrwLayer *l, const gc // Property Dialog of the track vik_trw_layer_propwin_update ( trk ); +#ifdef VIK_CONFIG_ALPHABETIZED_TRW + vik_treeview_sublayer_realphabetize ( VIK_LAYER(l)->vt, iter, newname ); +#endif + + vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) ); + + return newname; + } + + if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE ) + { + VikTrack *trk = g_hash_table_lookup ( l->routes, sublayer ); + + // No actual change to the name supplied + if (strcmp(newname, trk->name) == 0) + return NULL; + + VikTrack *trkf = vik_trw_layer_get_route ( l, (gpointer) newname ); + + if ( trkf ) { + // An existing track has been found with the requested name + if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l), + _("A route with the name \"%s\" already exists. Really rename to the same name?"), + newname ) ) + return NULL; + } + // Update track name and refresh GUI parts + vik_track_set_name (trk, newname); + + // Update any subwindows that could be displaying this track which has changed name + // Only one Track Edit Window + if ( l->current_tp_track == trk && l->tpwin ) { + vik_trw_layer_tpwin_set_track_name ( l->tpwin, newname ); + } + // Property Dialog of the track + vik_trw_layer_propwin_update ( trk ); + #ifdef VIK_CONFIG_ALPHABETIZED_TRW vik_treeview_sublayer_realphabetize ( VIK_LAYER(l)->vt, iter, newname ); #endif @@ -5120,7 +5721,7 @@ static gboolean trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *men pass_along[6] = iter; pass_along[7] = NULL; // For misc purposes - maybe track or waypoint - if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT || subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) + if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT || subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE ) { rv = TRUE; @@ -5130,8 +5731,12 @@ static gboolean trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *men gtk_widget_show ( item ); if (subtype == VIK_TRW_LAYER_SUBLAYER_TRACK) { - VikTrwLayer *vtl = l; - VikTrack *tr = g_hash_table_lookup ( vtl->tracks, sublayer ); + VikTrack *tr = g_hash_table_lookup ( l->tracks, sublayer ); + if (tr && tr->property_dialog) + gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE ); + } + if (subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE) { + VikTrack *tr = g_hash_table_lookup ( l->routes, sublayer ); if (tr && tr->property_dialog) gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE ); } @@ -5230,7 +5835,7 @@ static gboolean trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *men } } - if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS ) { + if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTES ) { item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_PASTE, NULL ); g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_paste_item_cb), pass_along ); gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item ); @@ -5288,7 +5893,7 @@ static gboolean trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *men { rv = TRUE; - if ( l->current_track ) { + if ( l->current_track && !l->current_track->is_route ) { item = gtk_menu_item_new_with_mnemonic ( _("_Finish Track") ); g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along ); gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); @@ -5326,14 +5931,62 @@ static gboolean trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *men gtk_widget_show ( item ); } - if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) + if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTES ) + { + rv = TRUE; + + if ( l->current_track && l->current_track->is_route ) { + item = gtk_menu_item_new_with_mnemonic ( _("_Finish Route") ); + // Reuse finish track method + g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along ); + gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); + gtk_widget_show ( item ); + // Add separator + item = gtk_menu_item_new (); + gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item ); + gtk_widget_show ( item ); + } + + item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Routes") ); + gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) ); + g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_routes_view), pass_along ); + gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); + gtk_widget_show ( item ); + + item = gtk_image_menu_item_new_with_mnemonic ( _("_New Route") ); + gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) ); + g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_route), pass_along ); + gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); + gtk_widget_show ( item ); + // Make it available only when a new track *not* already in progress + gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(l->current_track) ); + + item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Routes") ); + gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) ); + g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_routes), pass_along ); + gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item ); + gtk_widget_show ( item ); + + item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Routes From Selection...") ); + gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) ); + g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_routes_from_selection), pass_along ); + gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item ); + gtk_widget_show ( item ); + } + + GtkWidget *upload_submenu = gtk_menu_new (); + + if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE ) { item = gtk_menu_item_new (); gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item ); gtk_widget_show ( item ); - if ( l->current_track ) { + if ( l->current_track && subtype == VIK_TRW_LAYER_SUBLAYER_TRACK && !l->current_track->is_route ) item = gtk_menu_item_new_with_mnemonic ( _("_Finish Track") ); + if ( l->current_track && subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE && l->current_track->is_route ) + item = gtk_menu_item_new_with_mnemonic ( _("_Finish Route") ); + if ( l->current_track ) { g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along ); gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); gtk_widget_show ( item ); @@ -5344,7 +5997,10 @@ static gboolean trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *men gtk_widget_show ( item ); } - item = gtk_image_menu_item_new_with_mnemonic ( _("_View Track") ); + if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) + item = gtk_image_menu_item_new_with_mnemonic ( _("_View Track") ); + else + item = gtk_image_menu_item_new_with_mnemonic ( _("_View Route") ); gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) ); g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_track_view), pass_along ); gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item ); @@ -5388,11 +6044,14 @@ static gboolean trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *men gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item ); gtk_widget_show ( item ); - item = gtk_image_menu_item_new_with_mnemonic ( _("_Maximum Speed") ); - gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_MEDIA_FORWARD, GTK_ICON_SIZE_MENU) ); - g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_speed), pass_along ); - gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item ); - gtk_widget_show ( item ); + // Routes don't have speeds + if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) { + item = gtk_image_menu_item_new_with_mnemonic ( _("_Maximum Speed") ); + gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_MEDIA_FORWARD, GTK_ICON_SIZE_MENU) ); + g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_speed), pass_along ); + gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item ); + gtk_widget_show ( item ); + } GtkWidget *combine_submenu; combine_submenu = gtk_menu_new (); @@ -5402,22 +6061,28 @@ static gboolean trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *men gtk_widget_show ( item ); gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), combine_submenu ); - item = gtk_menu_item_new_with_mnemonic ( _("_Merge By Time...") ); - g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_by_timestamp), pass_along ); - gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item ); - gtk_widget_show ( item ); + // Routes don't have times or segments... + if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) { + item = gtk_menu_item_new_with_mnemonic ( _("_Merge By Time...") ); + g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_by_timestamp), pass_along ); + gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item ); + gtk_widget_show ( item ); + + item = gtk_menu_item_new_with_mnemonic ( _("Merge _Segments") ); + g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_by_segment), pass_along ); + gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item ); + gtk_widget_show ( item ); + } item = gtk_menu_item_new_with_mnemonic ( _("Merge _With Other Tracks...") ); g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_with_other), pass_along ); gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item ); gtk_widget_show ( item ); - item = gtk_menu_item_new_with_mnemonic ( _("Merge _Segments") ); - g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_by_segment), pass_along ); - gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item ); - gtk_widget_show ( item ); - - item = gtk_menu_item_new_with_mnemonic ( _("_Append Track...") ); + if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) + item = gtk_menu_item_new_with_mnemonic ( _("_Append Track...") ); + else + item = gtk_menu_item_new_with_mnemonic ( _("_Append Route...") ); g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_append_track), pass_along ); gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item ); gtk_widget_show ( item ); @@ -5430,22 +6095,25 @@ static gboolean trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *men gtk_widget_show ( item ); gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), split_submenu ); - item = gtk_menu_item_new_with_mnemonic ( _("_Split By Time...") ); - g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_timestamp), pass_along ); - gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item ); - gtk_widget_show ( item ); + // Routes don't have times or segments... + if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) { + item = gtk_menu_item_new_with_mnemonic ( _("_Split By Time...") ); + g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_timestamp), pass_along ); + gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item ); + gtk_widget_show ( item ); + + // ATM always enable this entry - don't want to have to analyse the track before displaying the menu - to keep the menu speedy + item = gtk_menu_item_new_with_mnemonic ( _("Split Se_gments") ); + g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_segments), pass_along ); + gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item ); + gtk_widget_show ( item ); + } item = gtk_menu_item_new_with_mnemonic ( _("Split By _Number of Points...") ); g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_n_points), pass_along ); gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item ); gtk_widget_show ( item ); - // ATM always enable this entry - don't want to have to analyse the track before displaying the menu - to keep the menu speedy - item = gtk_menu_item_new_with_mnemonic ( _("Split Se_gments") ); - g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_segments), pass_along ); - gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item ); - gtk_widget_show ( item ); - item = gtk_menu_item_new_with_mnemonic ( _("Split at _Trackpoint") ); g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_at_trackpoint), pass_along ); gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item ); @@ -5471,7 +6139,10 @@ static gboolean trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *men gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item ); gtk_widget_show ( item ); - item = gtk_image_menu_item_new_with_mnemonic ( _("_Reverse Track") ); + if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) + item = gtk_image_menu_item_new_with_mnemonic ( _("_Reverse Track") ); + else + item = gtk_image_menu_item_new_with_mnemonic ( _("_Reverse Route") ); gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_BACK, GTK_ICON_SIZE_MENU) ); g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_reverse), pass_along ); gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item ); @@ -5479,7 +6150,10 @@ static gboolean trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *men /* ATM This function is only available via the layers panel, due to the method in finding out the maps in use */ if ( vlp ) { - item = gtk_image_menu_item_new_with_mnemonic ( _("Down_load Maps Along Track...") ); + if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) + item = gtk_image_menu_item_new_with_mnemonic ( _("Down_load Maps Along Track...") ); + else + item = gtk_image_menu_item_new_with_mnemonic ( _("Down_load Maps Along Route...") ); gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock ("Maps Download", GTK_ICON_SIZE_MENU) ); // Own icon - see stock_icons in vikwindow.c g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_download_map_along_track_cb), pass_along ); gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item ); @@ -5492,13 +6166,19 @@ static gboolean trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *men gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item ); gtk_widget_show ( item ); - item = gtk_image_menu_item_new_with_mnemonic ( _("Export Trac_k as GPX...") ); + if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) + item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Track as GPX...") ); + else + item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Route as GPX...") ); gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_HARDDISK, GTK_ICON_SIZE_MENU) ); g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpx_track), pass_along ); gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item ); gtk_widget_show ( item ); - item = gtk_image_menu_item_new_with_mnemonic ( _("E_xtend Track End") ); + if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) + item = gtk_image_menu_item_new_with_mnemonic ( _("E_xtend Track End") ); + else + item = gtk_image_menu_item_new_with_mnemonic ( _("E_xtend Route End") ); gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) ); g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end), pass_along ); gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item ); @@ -5512,13 +6192,24 @@ static gboolean trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *men gtk_widget_show ( item ); #endif - GtkWidget *upload_submenu = gtk_menu_new (); - item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload") ); - gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) ); - gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); - gtk_widget_show ( item ); - gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), upload_submenu ); + // ATM can't upload a single waypoint but can do waypoints to a GPS + if ( subtype != VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) { + item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload") ); + gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) ); + gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); + gtk_widget_show ( item ); + gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), upload_submenu ); + + item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload to GPS...") ); + gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_FORWARD, GTK_ICON_SIZE_MENU) ); + g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_gps_upload_any), pass_along ); + gtk_menu_shell_append ( GTK_MENU_SHELL(upload_submenu), item ); + gtk_widget_show ( item ); + } + } + // Some things aren't usable with routes + if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) { #ifdef VIK_CONFIG_OPENSTREETMAP item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _OSM...") ); // Convert internal pointer into actual track for usage outside this file @@ -5555,21 +6246,23 @@ static gboolean trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *men /* ATM This function is only available via the layers panel, due to needing a vlp */ if ( vlp ) { item = a_acquire_track_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(l)), vlp, - vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)), - g_hash_table_lookup ( l->tracks, (gchar *) sublayer ) ); + vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)), + g_hash_table_lookup ( l->tracks, (gchar *) sublayer ) ); if ( item ) { - gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); - gtk_widget_show ( item ); + gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); + gtk_widget_show ( item ); } } #ifdef VIK_CONFIG_GEOTAG - item = gtk_menu_item_new_with_mnemonic ( _("Geotag _Images...") ); - g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_track), pass_along ); - gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); - gtk_widget_show ( item ); + item = gtk_menu_item_new_with_mnemonic ( _("Geotag _Images...") ); + g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_track), pass_along ); + gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); + gtk_widget_show ( item ); #endif + } + if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE ) { // Only show on viewport popmenu when a trackpoint is selected if ( ! vlp && l->current_tpl ) { // Add separator @@ -5583,7 +6276,6 @@ static gboolean trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *men gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item ); gtk_widget_show ( item ); } - } return rv; @@ -5634,10 +6326,16 @@ static void trw_layer_insert_tp_after_current_tp ( VikTrwLayer *vtl ) /* DOP / sat values remain at defaults as not they do not seem applicable to a dreamt up point */ /* Insert new point into the trackpoints list after the current TP */ - VikTrack *tr = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_id ); - gint index = g_list_index ( tr->trackpoints, tp_current ); + VikTrack *trk = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_id ); + if ( !trk ) + // Otherwise try routes + trk = g_hash_table_lookup ( vtl->routes, vtl->current_tp_id ); + if ( !trk ) + return; + + gint index = g_list_index ( trk->trackpoints, tp_current ); if ( index > -1 ) { - tr->trackpoints = g_list_insert (tr->trackpoints, tp_new, index+1 ); + trk->trackpoints = g_list_insert ( trk->trackpoints, tp_new, index+1 ); } } } @@ -5674,12 +6372,14 @@ static void trw_layer_tpwin_response ( VikTrwLayer *vtl, gint response ) if ( response == VIK_TRW_LAYER_TPWIN_SPLIT && vtl->current_tpl->next && vtl->current_tpl->prev ) { - trw_layer_split_at_selected_trackpoint ( vtl ); + trw_layer_split_at_selected_trackpoint ( vtl, vtl->current_tp_track->is_route ? VIK_TRW_LAYER_SUBLAYER_ROUTE : VIK_TRW_LAYER_SUBLAYER_TRACK ); vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name ); } else if ( response == VIK_TRW_LAYER_TPWIN_DELETE ) { VikTrack *tr = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_id ); + if ( tr == NULL ) + tr = g_hash_table_lookup ( vtl->routes, vtl->current_tp_id ); if ( tr == NULL ) return; @@ -5834,6 +6534,8 @@ static void track_search_closest_tp ( gpointer id, VikTrack *t, TPSearchParams * } } +// ATM: Leave this as 'Track' only. +// Not overly bothered about having a snap to route trackpoint capability static VikTrackpoint *closest_tp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y ) { TPSearchParams params; @@ -5963,7 +6665,7 @@ static gboolean trw_layer_select_click ( VikTrwLayer *vtl, GdkEventButton *event if (!vtl || vtl->vl.type != VIK_LAYER_TRW) return FALSE; - if ( !vtl->tracks_visible && !vtl->waypoints_visible ) + if ( !vtl->tracks_visible && !vtl->waypoints_visible && !vtl->routes_visible ) return FALSE; // Go for waypoints first as these often will be near a track, but it's likely the wp is wanted rather then the track @@ -6004,14 +6706,15 @@ static gboolean trw_layer_select_click ( VikTrwLayer *vtl, GdkEventButton *event } } - if (vtl->tracks_visible) { - TPSearchParams tp_params; - tp_params.vvp = vvp; - tp_params.x = event->x; - tp_params.y = event->y; - tp_params.closest_track_id = NULL; - tp_params.closest_tp = NULL; + // Used for both track and route lists + TPSearchParams tp_params; + tp_params.vvp = vvp; + tp_params.x = event->x; + tp_params.y = event->y; + tp_params.closest_track_id = NULL; + tp_params.closest_tp = NULL; + if (vtl->tracks_visible) { g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, &tp_params); if ( tp_params.closest_tp ) { @@ -6045,6 +6748,41 @@ static gboolean trw_layer_select_click ( VikTrwLayer *vtl, GdkEventButton *event } } + // Try again for routes + if (vtl->routes_visible) { + g_hash_table_foreach ( vtl->routes, (GHFunc) track_search_closest_tp, &tp_params); + + if ( tp_params.closest_tp ) { + + // Always select + highlight the track + vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->routes_iters, tp_params.closest_track_id ), TRUE ); + + tet->is_waypoint = FALSE; + + // Select the Trackpoint + // Can move it immediately when control held or it's the previously selected tp + if ( event->state & GDK_CONTROL_MASK || + vtl->current_tpl == tp_params.closest_tpl ) { + // Put into 'move buffer' + // NB vvp & vw already set in tet + tet->vtl = (gpointer)vtl; + marker_begin_move (tet, event->x, event->y); + } + + vtl->current_tpl = tp_params.closest_tpl; + vtl->current_tp_id = tp_params.closest_track_id; + vtl->current_tp_track = g_hash_table_lookup ( vtl->routes, tp_params.closest_track_id ); + + set_statusbar_msg_info_trkpt ( vtl, tp_params.closest_tp ); + + if ( vtl->tpwin ) + vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name ); + + vik_layer_emit_update ( VIK_LAYER(vtl), FALSE ); + return TRUE; + } + } + /* these aren't the droids you're looking for */ vtl->current_wp = NULL; vtl->current_wp_id = NULL; @@ -6064,7 +6802,7 @@ static gboolean trw_layer_show_selected_viewport_menu ( VikTrwLayer *vtl, GdkEve if (!vtl || vtl->vl.type != VIK_LAYER_TRW) return FALSE; - if ( !vtl->tracks_visible && !vtl->waypoints_visible ) + if ( !vtl->tracks_visible && !vtl->waypoints_visible && !vtl->routes_visible ) return FALSE; /* Post menu for the currently selected item */ @@ -6084,16 +6822,24 @@ static gboolean trw_layer_show_selected_viewport_menu ( VikTrwLayer *vtl, GdkEve udataU.trk = track; udataU.uuid = NULL; - gpointer *trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udataU ); + gpointer *trkf; + if ( track->is_route ) + trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udataU ); + else + trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udataU ); if ( trkf && udataU.uuid ) { - GtkTreeIter *iter = g_hash_table_lookup ( vtl->tracks_iters, udataU.uuid ); + GtkTreeIter *iter; + if ( track->is_route ) + iter = g_hash_table_lookup ( vtl->routes_iters, udataU.uuid ); + else + iter = g_hash_table_lookup ( vtl->tracks_iters, udataU.uuid ); trw_layer_sublayer_add_menu_items ( vtl, vtl->track_right_click_menu, NULL, - VIK_TRW_LAYER_SUBLAYER_TRACK, + track->is_route ? VIK_TRW_LAYER_SUBLAYER_ROUTE : VIK_TRW_LAYER_SUBLAYER_TRACK, udataU.uuid, iter, vvp ); @@ -6756,7 +7502,7 @@ static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *e if (!vtl || vtl->vl.type != VIK_LAYER_TRW) return FALSE; - if ( !vtl->vl.visible || !vtl->tracks_visible ) + if ( !vtl->vl.visible || !vtl->tracks_visible || !vtl->routes_visible ) return FALSE; if ( vtl->current_tpl ) @@ -6779,7 +7525,8 @@ static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *e } - g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, ¶ms); + if ( vtl->tracks_visible ) + g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, ¶ms); if ( params.closest_tp ) { @@ -6793,6 +7540,21 @@ static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *e return TRUE; } + if ( vtl->routes_visible ) + g_hash_table_foreach ( vtl->routes, (GHFunc) track_search_closest_tp, ¶ms); + + if ( params.closest_tp ) + { + vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->routes_iters, params.closest_track_id ), TRUE ); + vtl->current_tpl = params.closest_tpl; + vtl->current_tp_id = params.closest_track_id; + vtl->current_tp_track = g_hash_table_lookup ( vtl->routes, params.closest_track_id ); + trw_layer_tpwin_init ( vtl ); + set_statusbar_msg_info_trkpt ( vtl, params.closest_tp ); + vik_layer_emit_update ( VIK_LAYER(vtl), FALSE ); + return TRUE; + } + /* these aren't the droids you're looking for */ return FALSE; } @@ -7097,7 +7859,8 @@ VikCoordMode vik_trw_layer_get_coord_mode ( VikTrwLayer *vtl ) gboolean vik_trw_layer_uniquify ( VikTrwLayer *vtl, VikLayersPanel *vlp ) { if ( vtl && vlp ) { - vik_trw_layer_uniquify_tracks ( vtl, vlp ); + vik_trw_layer_uniquify_tracks ( vtl, vlp, vtl->tracks, TRUE ); + vik_trw_layer_uniquify_tracks ( vtl, vlp, vtl->routes, FALSE ); vik_trw_layer_uniquify_waypoints ( vtl, vlp ); return TRUE; } @@ -7298,7 +8061,14 @@ static void trw_layer_download_map_along_track_cb ( gpointer pass_along[6] ) VikTrwLayer *vtl = pass_along[0]; VikLayersPanel *vlp = pass_along[1]; - VikTrack *tr = (VikTrack *) g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ); + VikTrack *trk; + if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE ) + trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] ); + else + trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] ); + if ( !trk ) + return; + VikViewport *vvp = vik_window_viewport((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl))); GList *vmls = vik_layers_panel_get_all_layers_of_type(vlp, VIK_LAYER_MAPS, TRUE); // Includes hidden map layer types @@ -7350,7 +8120,7 @@ static void trw_layer_download_map_along_track_cb ( gpointer pass_along[6] ) if (!a_dialog_map_n_zoom(VIK_GTK_WINDOW_FROM_LAYER(vtl), map_names, default_map, zoomlist, default_zoom, &selected_map, &selected_zoom)) goto done; - vik_track_download_map(tr, map_layers[selected_map], vvp, zoom_vals[selected_zoom]); + vik_track_download_map(trk, map_layers[selected_map], vvp, zoom_vals[selected_zoom]); done: for (i = 0; i < num_maps; i++) diff --git a/src/viktrwlayer.h b/src/viktrwlayer.h index 6f93a562..3ccbb559 100644 --- a/src/viktrwlayer.h +++ b/src/viktrwlayer.h @@ -41,7 +41,9 @@ enum { VIK_TRW_LAYER_SUBLAYER_TRACKS, VIK_TRW_LAYER_SUBLAYER_WAYPOINTS, VIK_TRW_LAYER_SUBLAYER_TRACK, - VIK_TRW_LAYER_SUBLAYER_WAYPOINT + VIK_TRW_LAYER_SUBLAYER_WAYPOINT, + VIK_TRW_LAYER_SUBLAYER_ROUTES, + VIK_TRW_LAYER_SUBLAYER_ROUTE }; typedef struct _VikTrwLayerClass VikTrwLayerClass; @@ -64,6 +66,7 @@ gint vik_trw_layer_get_property_tracks_line_thickness ( VikTrwLayer *vtl ); void vik_trw_layer_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp ); void vik_trw_layer_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *t ); +void vik_trw_layer_add_route ( VikTrwLayer *vtl, gchar *name, VikTrack *t ); // Waypoint returned is the first one VikWaypoint *vik_trw_layer_get_waypoint ( VikTrwLayer *vtl, const gchar *name ); @@ -71,10 +74,12 @@ VikWaypoint *vik_trw_layer_get_waypoint ( VikTrwLayer *vtl, const gchar *name ); // Track returned is the first one VikTrack *vik_trw_layer_get_track ( VikTrwLayer *vtl, const gchar *name ); gboolean vik_trw_layer_delete_track ( VikTrwLayer *vtl, VikTrack *trk ); +gboolean vik_trw_layer_delete_route ( VikTrwLayer *vtl, VikTrack *trk ); gboolean vik_trw_layer_auto_set_view ( VikTrwLayer *vtl, VikViewport *vvp ); gboolean vik_trw_layer_find_center ( VikTrwLayer *vtl, VikCoord *dest ); GHashTable *vik_trw_layer_get_tracks ( VikTrwLayer *l ); +GHashTable *vik_trw_layer_get_routes ( VikTrwLayer *l ); GHashTable *vik_trw_layer_get_waypoints ( VikTrwLayer *l ); gboolean vik_trw_layer_new_waypoint ( VikTrwLayer *vtl, GtkWindow *w, const VikCoord *def_coord ); @@ -84,6 +89,7 @@ gboolean vik_trw_layer_uniquify ( VikTrwLayer *vtl, VikLayersPanel *vlp ); void vik_trw_layer_delete_all_waypoints ( VikTrwLayer *vtl ); void vik_trw_layer_delete_all_tracks ( VikTrwLayer *vtl ); +void vik_trw_layer_delete_all_routes ( VikTrwLayer *vtl ); void trw_layer_cancel_tps_of_track ( VikTrwLayer *vtl, VikTrack *trk ); /* Exposed Layer Interface function definitions */ diff --git a/src/viktrwlayer_propwin.c b/src/viktrwlayer_propwin.c index 2395cec1..78fca4d9 100644 --- a/src/viktrwlayer_propwin.c +++ b/src/viktrwlayer_propwin.c @@ -2642,8 +2642,13 @@ static void propwin_response_cb( GtkDialog *dialog, gint resp, PropWidgets *widg for ( i = 0; i < ntracks; i++ ) { if ( tracks[i] ) { - new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, widgets->tr->name); - vik_trw_layer_add_track ( vtl, new_tr_name, tracks[i] ); + new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, + widgets->tr->is_route ? VIK_TRW_LAYER_SUBLAYER_ROUTE : VIK_TRW_LAYER_SUBLAYER_TRACK, + widgets->tr->name); + if ( widgets->tr->is_route ) + vik_trw_layer_add_route ( vtl, new_tr_name, tracks[i] ); + else + vik_trw_layer_add_track ( vtl, new_tr_name, tracks[i] ); } } if ( tracks ) @@ -2651,7 +2656,10 @@ static void propwin_response_cb( GtkDialog *dialog, gint resp, PropWidgets *widg g_free ( tracks ); /* Don't let track destroy this dialog */ vik_track_clear_property_dialog(tr); - vik_trw_layer_delete_track ( vtl, tr ); + if ( widgets->tr->is_route ) + vik_trw_layer_delete_route ( vtl, tr ); + else + vik_trw_layer_delete_track ( vtl, tr ); vik_layer_emit_update ( VIK_LAYER(vtl), FALSE ); /* chase thru the hoops */ } } @@ -2670,16 +2678,22 @@ static void propwin_response_cb( GtkDialog *dialog, gint resp, PropWidgets *widg break; } - gchar *r_name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, widgets->tr->name); + gchar *r_name = trw_layer_new_unique_sublayer_name(vtl, + widgets->tr->is_route ? VIK_TRW_LAYER_SUBLAYER_ROUTE : VIK_TRW_LAYER_SUBLAYER_TRACK, + widgets->tr->name); iter->prev->next = NULL; iter->prev = NULL; VikTrack *tr_right = vik_track_new(); if ( tr->comment ) vik_track_set_comment ( tr_right, tr->comment ); tr_right->visible = tr->visible; + tr_right->is_route = tr->is_route; tr_right->trackpoints = iter; - vik_trw_layer_add_track(vtl, r_name, tr_right); + if ( widgets->tr->is_route ) + vik_trw_layer_add_route(vtl, r_name, tr_right); + else + vik_trw_layer_add_track(vtl, r_name, tr_right); vik_layer_emit_update ( VIK_LAYER(vtl), FALSE ); } break; -- 2.39.5