X-Git-Url: https://git.street.me.uk/andy/viking.git/blobdiff_plain/415b0e21cfbd2d8a1e22ac02d7e6ccd2f9743f7c..1417eec83c55bc61a970a88222f0db5b6efee437:/src/viktrwlayer.c diff --git a/src/viktrwlayer.c b/src/viktrwlayer.c index c2476fa1..720ff8ff 100644 --- a/src/viktrwlayer.c +++ b/src/viktrwlayer.c @@ -5,7 +5,7 @@ * Copyright (C) 2005-2008, Alex Foobarian * Copyright (C) 2007, Quy Tonthat * Copyright (C) 2009, Hein Ragas - * Copyright (c) 2012, Rob Norris + * Copyright (c) 2012-2015, Rob Norris * Copyright (c) 2012-2013, Guilhem Bonnefille * * This program is free software; you can redistribute it and/or modify @@ -35,6 +35,7 @@ #include "vikgpslayer.h" #include "viktrwlayer_export.h" #include "viktrwlayer_tpwin.h" +#include "viktrwlayer_wpwin.h" #include "viktrwlayer_propwin.h" #include "viktrwlayer_analysis.h" #include "viktrwlayer_tracklist.h" @@ -47,6 +48,7 @@ #include "thumbnails.h" #include "background.h" #include "gpx.h" +#include "geojson.h" #include "babel.h" #include "dem.h" #include "dems.h" @@ -57,10 +59,9 @@ #include "acquire.h" #include "datasources.h" #include "datasource_gps.h" +#include "vikexttools.h" #include "vikexttool_datasources.h" -#include "util.h" -#include "vikutils.h" - +#include "ui_util.h" #include "vikrouting.h" #include "icons/icons.h" @@ -177,7 +178,6 @@ struct _VikTrwLayer { GdkGC *waypoint_text_gc; GdkColor waypoint_text_color; GdkGC *waypoint_bg_gc; GdkColor waypoint_bg_color; gboolean wpbgand; - 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; @@ -367,6 +367,8 @@ static void trw_layer_cancel_current_tp ( VikTrwLayer *vtl, gboolean destroy ); static void trw_layer_tpwin_response ( VikTrwLayer *vtl, gint response ); static void trw_layer_tpwin_init ( VikTrwLayer *vtl ); +static void trw_layer_sort_all ( VikTrwLayer *vtl ); + static gpointer tool_edit_trackpoint_create ( VikWindow *vw, VikViewport *vvp); static void tool_edit_trackpoint_destroy ( tool_ed_t *t ); static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data ); @@ -531,6 +533,8 @@ static gchar* params_sort_order[] = { N_("None"), N_("Name Ascending"), N_("Name Descending"), + N_("Date Ascending"), + N_("Date Descending"), NULL }; @@ -683,10 +687,11 @@ enum { /* Layer Interface function definitions */ static VikTrwLayer* trw_layer_create ( VikViewport *vp ); static void trw_layer_realize ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter ); -static void trw_layer_post_read ( VikTrwLayer *vtl, GtkWidget *vvp, gboolean from_file ); +static void trw_layer_post_read ( VikTrwLayer *vtl, VikViewport *vvp, gboolean from_file ); static void trw_layer_free ( VikTrwLayer *trwlayer ); static void trw_layer_draw ( VikTrwLayer *l, gpointer data ); static void trw_layer_change_coord_mode ( VikTrwLayer *vtl, VikCoordMode dest_mode ); +static time_t trw_layer_get_timestamp ( VikTrwLayer *vtl ); static void trw_layer_set_menu_selection ( VikTrwLayer *vtl, guint16 ); static guint16 trw_layer_get_menu_selection ( VikTrwLayer *vtl ); static void trw_layer_add_menu_items ( VikTrwLayer *vtl, GtkMenu *menu, gpointer vlp ); @@ -708,7 +713,7 @@ static gboolean trw_layer_paste_item ( VikTrwLayer *vtl, gint subtype, guint8 *i static void trw_layer_free_copied_item ( gint subtype, gpointer item ); static void trw_layer_drag_drop_request ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, GtkTreeIter *src_item_iter, GtkTreePath *dest_path ); static gboolean trw_layer_select_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t *t ); -static gboolean trw_layer_select_move ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t *t ); +static gboolean trw_layer_select_move ( VikTrwLayer *vtl, GdkEventMotion *event, VikViewport *vvp, tool_ed_t *t ); static gboolean trw_layer_select_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t *t ); static gboolean trw_layer_show_selected_viewport_menu ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp ); /* End Layer Interface function definitions */ @@ -737,6 +742,7 @@ VikLayerInterface vik_trw_layer_interface = { (VikLayerFuncProperties) NULL, (VikLayerFuncDraw) trw_layer_draw, (VikLayerFuncChangeCoordMode) trw_layer_change_coord_mode, + (VikLayerFuncGetTimestamp) trw_layer_get_timestamp, (VikLayerFuncSetMenuItemsSelection) trw_layer_set_menu_selection, (VikLayerFuncGetMenuItemsSelection) trw_layer_get_menu_selection, @@ -775,41 +781,87 @@ VikLayerInterface vik_trw_layer_interface = { }; static gboolean have_diary_program = FALSE; +static gchar *diary_program = NULL; +#define VIK_SETTINGS_EXTERNAL_DIARY_PROGRAM "external_diary_program" + +static gboolean have_geojson_export = FALSE; + +static gboolean have_astro_program = FALSE; +static gchar *astro_program = NULL; +#define VIK_SETTINGS_EXTERNAL_ASTRO_PROGRAM "external_astro_program" // NB Only performed once per program run static void vik_trwlayer_class_init ( VikTrwLayerClass *klass ) { - if ( g_find_program_in_path( "rednotebook" ) ) { - gchar *stdout = NULL; - gchar *stderr = NULL; + if ( ! a_settings_get_string ( VIK_SETTINGS_EXTERNAL_DIARY_PROGRAM, &diary_program ) ) { +#ifdef WINDOWS + //diary_program = g_strdup ( "C:\\Program Files\\Rednotebook\\rednotebook.exe" ); + diary_program = g_strdup ( "C:/Progra~1/Rednotebook/rednotebook.exe" ); +#else + diary_program = g_strdup ( "rednotebook" ); +#endif + } + else { + // User specified so assume it works + have_diary_program = TRUE; + } + + if ( g_find_program_in_path( diary_program ) ) { + gchar *mystdout = NULL; + gchar *mystderr = NULL; // Needs RedNotebook 1.7.3+ for support of opening on a specified date - if ( g_spawn_command_line_sync ( "rednotebook --version", &stdout, &stderr, NULL, NULL ) ) { + gchar *cmd = g_strconcat ( diary_program, " --version", NULL ); // "rednotebook --version" + if ( g_spawn_command_line_sync ( cmd, &mystdout, &mystderr, NULL, NULL ) ) { // Annoyingly 1.7.1|2|3 versions of RedNotebook prints the version to stderr!! - if ( stdout ) - g_debug ("Diary: %s", stdout ); // Should be something like 'RedNotebook 1.4' - if ( stderr ) - g_warning ("Diary: stderr: %s", stderr ); + if ( mystdout ) + g_debug ("Diary: %s", mystdout ); // Should be something like 'RedNotebook 1.4' + if ( mystderr ) + g_warning ("Diary: stderr: %s", mystderr ); gchar **tokens = NULL; - if ( stdout && g_strcmp0(stdout, "") ) - tokens = g_strsplit(stdout, " ", 0); - else if ( stderr ) - tokens = g_strsplit(stderr, " ", 0); - - gint num = 0; - gchar *token = tokens[num]; - while ( token && num < 2 ) { - if (num == 1) { - if ( viking_version_to_number(token) >= viking_version_to_number("1.7.3") ) - have_diary_program = TRUE; + if ( mystdout && g_strcmp0(mystdout, "") ) + tokens = g_strsplit(mystdout, " ", 0); + else if ( mystderr ) + tokens = g_strsplit(mystderr, " ", 0); + + if ( tokens ) { + gint num = 0; + gchar *token = tokens[num]; + while ( token && num < 2 ) { + if (num == 1) { + if ( viking_version_to_number(token) >= viking_version_to_number("1.7.3") ) + have_diary_program = TRUE; + } + num++; + token = tokens[num]; } - num++; - token = tokens[num]; } g_strfreev ( tokens ); } - g_free ( stdout ); - g_free ( stderr ); + g_free ( mystdout ); + g_free ( mystderr ); + g_free ( cmd ); + } + + if ( g_find_program_in_path ( a_geojson_program_export() ) ) { + have_geojson_export = TRUE; + } + + // Astronomy Domain + if ( ! a_settings_get_string ( VIK_SETTINGS_EXTERNAL_ASTRO_PROGRAM, &astro_program ) ) { +#ifdef WINDOWS + //astro_program = g_strdup ( "C:\\Program Files\\Stellarium\\stellarium.exe" ); + astro_program = g_strdup ( "C:/Progra~1/Stellarium/stellarium.exe" ); +#else + astro_program = g_strdup ( "stellarium" ); +#endif + } + else { + // User specified so assume it works + have_astro_program = TRUE; + } + if ( g_find_program_in_path( astro_program ) ) { + have_astro_program = TRUE; } } @@ -863,8 +915,8 @@ typedef struct { const gchar *date_str; const VikTrack *trk; const VikWaypoint *wpt; - gpointer *trk_id; - gpointer *wpt_id; + gpointer trk_id; + gpointer wpt_id; } date_finder_type; static gboolean trw_layer_find_date_track ( const gpointer id, const VikTrack *trk, date_finder_type *df ) @@ -976,7 +1028,7 @@ static void trw_layer_copy_item_cb ( menu_array_sublayer values) { VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]); gint subtype = GPOINTER_TO_INT (values[MA_SUBTYPE]); - gpointer * sublayer = values[MA_SUBLAYER_ID]; + gpointer sublayer = values[MA_SUBLAYER_ID]; guint8 *data = NULL; guint len; @@ -1119,6 +1171,12 @@ static void trw_layer_free_copied_item ( gint subtype, gpointer item ) } } +static void image_cache_free ( VikTrwLayer *vtl ) +{ + g_list_foreach ( vtl->image_cache->head, (GFunc)cached_pixbuf_free, NULL ); + g_queue_free ( vtl->image_cache ); +} + static gboolean trw_layer_set_param ( VikTrwLayer *vtl, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation ) { switch ( id ) @@ -1190,12 +1248,17 @@ static gboolean trw_layer_set_param ( VikTrwLayer *vtl, guint16 id, VikLayerPara case PARAM_IS: if ( data.u != vtl->image_size ) { vtl->image_size = data.u; - g_list_foreach ( vtl->image_cache->head, (GFunc) cached_pixbuf_free, NULL ); - g_queue_free ( vtl->image_cache ); + image_cache_free ( vtl ); + vtl->image_cache = g_queue_new (); + } + break; + case PARAM_IA: if ( data.u != vtl->image_alpha ) + { + vtl->image_alpha = data.u; + image_cache_free ( vtl ); vtl->image_cache = g_queue_new (); } break; - case PARAM_IA: vtl->image_alpha = data.u; break; case PARAM_ICS: vtl->image_cache_size = data.u; while ( vtl->image_cache->length > vtl->image_cache_size ) /* if shrinking cache_size, free pixbuf ASAP */ cached_pixbuf_free ( g_queue_pop_tail ( vtl->image_cache ) ); @@ -1472,23 +1535,27 @@ static VikTrwLayer *trw_layer_unmarshall( guint8 *data, gint len, VikViewport *v // Reuse pl to read the subtype from the data stream memcpy(&pl, data+sizeof(gint), sizeof(pl)); + // Also remember to (attempt to) convert each coordinate in case this is pasted into a different drawmode if ( pl == VIK_TRW_LAYER_SUBLAYER_TRACK ) { VikTrack *trk = vik_track_unmarshall ( data + sizeof_len_and_subtype, 0 ); gchar *name = g_strdup ( trk->name ); vik_trw_layer_add_track ( vtl, name, trk ); g_free ( name ); + vik_track_convert (trk, vtl->coord_mode); } if ( pl == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) { VikWaypoint *wp = vik_waypoint_unmarshall ( data + sizeof_len_and_subtype, 0 ); gchar *name = g_strdup ( wp->name ); vik_trw_layer_add_waypoint ( vtl, name, wp ); g_free ( name ); + waypoint_convert (NULL, wp, &vtl->coord_mode); } if ( pl == VIK_TRW_LAYER_SUBLAYER_ROUTE ) { VikTrack *trk = vik_track_unmarshall ( data + sizeof_len_and_subtype, 0 ); gchar *name = g_strdup ( trk->name ); vik_trw_layer_add_route ( vtl, name, trk ); g_free ( name ); + vik_track_convert (trk, vtl->coord_mode); } } consumed_length += tlm_size + sizeof_len_and_subtype; @@ -1564,6 +1631,7 @@ static VikTrwLayer* trw_layer_new1 ( VikViewport *vvp ) rv->metadata = vik_trw_metadata_new (); rv->draw_sync_done = TRUE; rv->draw_sync_do = TRUE; + rv->coord_mode = VIK_COORD_LATLON; // Everything else is 0, FALSE or NULL return rv; @@ -1612,8 +1680,7 @@ static void trw_layer_free ( VikTrwLayer *trwlayer ) if ( trwlayer->tracks_analysis_dialog != NULL ) gtk_widget_destroy ( GTK_WIDGET(trwlayer->tracks_analysis_dialog) ); - g_list_foreach ( trwlayer->image_cache->head, (GFunc) cached_pixbuf_free, NULL ); - g_queue_free ( trwlayer->image_cache ); + image_cache_free ( trwlayer ); } static void init_drawing_params ( struct DrawingParams *dp, VikTrwLayer *vtl, VikViewport *vp, gboolean highlight ) @@ -1732,6 +1799,9 @@ static gdouble distance_in_preferred_units ( gdouble dist ) case VIK_UNITS_DISTANCE_MILES: mydist = VIK_METERS_TO_MILES(dist); break; + case VIK_UNITS_DISTANCE_NAUTICAL_MILES: + mydist = VIK_METERS_TO_NAUTICAL_MILES(dist); + break; // VIK_UNITS_DISTANCE_KILOMETRES: default: mydist = dist/1000.0; @@ -1777,6 +1847,9 @@ static void trw_layer_draw_dist_labels ( struct DrawingParams *dp, VikTrack *trk case VIK_UNITS_DISTANCE_MILES: dist_i = VIK_MILES_TO_METERS(dist_i); break; + case VIK_UNITS_DISTANCE_NAUTICAL_MILES: + dist_i = VIK_NAUTICAL_MILES_TO_METERS(dist_i); + break; // VIK_UNITS_DISTANCE_KILOMETRES: default: dist_i = dist_i*1000.0; @@ -1802,6 +1875,9 @@ static void trw_layer_draw_dist_labels ( struct DrawingParams *dp, VikTrack *trk case VIK_UNITS_DISTANCE_MILES: units = g_strdup ( _("miles") ); break; + case VIK_UNITS_DISTANCE_NAUTICAL_MILES: + units = g_strdup ( _("NM") ); + break; // VIK_UNITS_DISTANCE_KILOMETRES: default: units = g_strdup ( _("km") ); @@ -1952,6 +2028,40 @@ static void trw_layer_draw_track_name_labels ( struct DrawingParams *dp, VikTrac g_free ( ename ); } + +/** + * trw_layer_draw_point_names: + * + * Draw a point labels along a track + * This might slow things down if there's many tracks being displayed with this on. + */ +static void trw_layer_draw_point_names ( struct DrawingParams *dp, VikTrack *trk, gboolean drawing_highlight ) +{ + GList *list = trk->trackpoints; + if (!list) return; + VikTrackpoint *tp = VIK_TRACKPOINT(list->data); + gchar *fgcolour; + if ( dp->vtl->drawmode == DRAWMODE_BY_TRACK ) + fgcolour = gdk_color_to_string ( &(trk->color) ); + else + fgcolour = gdk_color_to_string ( &(dp->vtl->track_color) ); + gchar *bgcolour; + if ( drawing_highlight ) + bgcolour = g_strdup ( vik_viewport_get_highlight_color ( dp->vp ) ); + else + bgcolour = gdk_color_to_string ( &(dp->vtl->track_bg_color) ); + if ( tp->name ) + trw_layer_draw_track_label ( tp->name, fgcolour, bgcolour, dp, &tp->coord ); + while ((list = g_list_next(list))) + { + tp = VIK_TRACKPOINT(list->data); + if ( tp->name ) + trw_layer_draw_track_label ( tp->name, fgcolour, bgcolour, dp, &tp->coord ); + }; + g_free ( fgcolour ); + g_free ( bgcolour ); +} + static void trw_layer_draw_track ( const gpointer id, VikTrack *track, struct DrawingParams *dp, gboolean draw_track_outline ) { if ( ! track->visible ) @@ -2052,6 +2162,16 @@ static void trw_layer_draw_track ( const gpointer id, VikTrack *track, struct Dr tp = VIK_TRACKPOINT(list->data); tp_size = (list == dp->vtl->current_tpl) ? tp_size_cur : tp_size_reg; + VikTrackpoint *tp2 = VIK_TRACKPOINT(list->prev->data); + // See if in a different lat/lon 'quadrant' so don't draw massively long lines (presumably wrong way around the Earth) + // Mainly to prevent wrong lines drawn when a track crosses the 180 degrees East-West longitude boundary + // (since vik_viewport_draw_line() only copes with pixel value and has no concept of the globe) + if ( dp->lat_lon && + (( tp2->coord.east_west < -90.0 && tp->coord.east_west > 90.0 ) || + ( tp2->coord.east_west > 90.0 && tp->coord.east_west < -90.0 )) ) { + useoldvals = FALSE; + continue; + } /* check some stuff -- but only if we're in UTM and there's only ONE ZONE; or lat lon */ if ( (!dp->one_zone && !dp->lat_lon) || /* UTM & zones; do everything */ ( ((!dp->one_zone) || tp->coord.utm_zone == dp->center->utm_zone) && /* only check zones if UTM & one_zone */ @@ -2068,12 +2188,11 @@ static void trw_layer_draw_track ( const gpointer id, VikTrack *track, struct Dr // Still need to process points to ensure 'stops' are drawn if required if ( drawstops && drawpoints && ! draw_track_outline && list->next && (VIK_TRACKPOINT(list->next->data)->timestamp - VIK_TRACKPOINT(list->data)->timestamp > dp->vtl->stop_length) ) - vik_viewport_draw_arc ( dp->vp, g_array_index(dp->vtl->track_gc, GdkGC *, 11), TRUE, x-(3*tp_size), y-(3*tp_size), 6*tp_size, 6*tp_size, 0, 360*64 ); + vik_viewport_draw_arc ( dp->vp, g_array_index(dp->vtl->track_gc, GdkGC *, VIK_TRW_LAYER_TRACK_GC_STOP), TRUE, x-(3*tp_size), y-(3*tp_size), 6*tp_size, 6*tp_size, 0, 360*64 ); goto skip; } - VikTrackpoint *tp2 = VIK_TRACKPOINT(list->prev->data); if ( drawpoints || dp->vtl->drawlines ) { // setup main_gc for both point and line drawing if ( !drawing_highlight && (dp->vtl->drawmode == DRAWMODE_BY_SPEED) ) { @@ -2171,7 +2290,6 @@ static void trw_layer_draw_track ( const gpointer id, VikTrack *track, struct Dr else { if (useoldvals && dp->vtl->drawlines && (!tp->newsegment)) { - VikTrackpoint *tp2 = VIK_TRACKPOINT(list->prev->data); if ( dp->vtl->coord_mode != VIK_COORD_UTM || tp->coord.utm_zone == dp->center->utm_zone ) { vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y ); @@ -2212,6 +2330,7 @@ static void trw_layer_draw_track ( const gpointer id, VikTrack *track, struct Dr if ( track->max_number_dist_labels > 0 ) { trw_layer_draw_dist_labels ( dp, track, drawing_highlight ); } + trw_layer_draw_point_names (dp, track, drawing_highlight ); if ( track->draw_name_mode != TRACK_DRAWNAME_NO ) { trw_layer_draw_track_name_labels ( dp, track, drawing_highlight ); @@ -2284,6 +2403,10 @@ static void trw_layer_draw_waypoint ( const gpointer id, VikWaypoint *wp, struct } cp->image = g_strdup ( image ); + // Apply alpha setting to the image before the pixbuf gets stored in the cache + if ( dp->vtl->image_alpha != 255 ) + cp->pixbuf = ui_pixbuf_set_alpha ( cp->pixbuf, dp->vtl->image_alpha ); + /* needed so 'click picture' tool knows how big the pic is; we don't * store it in cp because they may have been freed already. */ wp->image_width = gdk_pixbuf_get_width ( cp->pixbuf ); @@ -2317,10 +2440,7 @@ static void trw_layer_draw_waypoint ( const gpointer id, VikWaypoint *wp, struct x - (w/2) - 2, y - (h/2) - 2, w + 4, h + 4 ); } - if ( dp->vtl->image_alpha == 255 ) - vik_viewport_draw_pixbuf ( dp->vp, pixbuf, 0, 0, x - (w/2), y - (h/2), w, h ); - else - vik_viewport_draw_pixbuf_with_alpha ( dp->vp, pixbuf, dp->vtl->image_alpha, 0, 0, x - (w/2), y - (h/2), w, h ); + vik_viewport_draw_pixbuf ( dp->vp, pixbuf, 0, 0, x - (w/2), y - (h/2), w, h ); } return; /* if failed to draw picture, default to drawing regular waypoint (below) */ } @@ -2616,7 +2736,12 @@ static void trw_layer_realize_track ( gpointer id, VikTrack *track, gpointer pas gdk_pixbuf_fill ( pixbuf, pixel ); } - vik_treeview_add_sublayer ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[0], (GtkTreeIter *) pass_along[1], track->name, pass_along[2], id, GPOINTER_TO_INT (pass_along[4]), pixbuf, TRUE, TRUE ); + time_t timestamp = 0; + VikTrackpoint *tpt = vik_track_get_tp_first(track); + if ( tpt && tpt->has_timestamp ) + timestamp = tpt->timestamp; + + vik_treeview_add_sublayer ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[0], (GtkTreeIter *) pass_along[1], track->name, pass_along[2], id, GPOINTER_TO_INT (pass_along[4]), pixbuf, TRUE, timestamp ); if ( pixbuf ) g_object_unref (pixbuf); @@ -2635,7 +2760,11 @@ static void trw_layer_realize_waypoint ( gpointer id, VikWaypoint *wp, gpointer { GtkTreeIter *new_iter = g_malloc(sizeof(GtkTreeIter)); - vik_treeview_add_sublayer ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[0], (GtkTreeIter *) pass_along[1], wp->name, pass_along[2], id, GPOINTER_TO_UINT (pass_along[4]), get_wp_sym_small (wp->symbol), TRUE, TRUE ); + time_t timestamp = 0; + if ( wp->has_timestamp ) + timestamp = wp->timestamp; + + vik_treeview_add_sublayer ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[0], (GtkTreeIter *) pass_along[1], wp->name, pass_along[2], id, GPOINTER_TO_UINT (pass_along[4]), get_wp_sym_small (wp->symbol), TRUE, timestamp ); *new_iter = *((GtkTreeIter *) pass_along[1]); g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->waypoints_iters, id, new_iter ); @@ -2646,17 +2775,17 @@ static void trw_layer_realize_waypoint ( gpointer id, VikWaypoint *wp, gpointer static void trw_layer_add_sublayer_tracks ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter ) { - vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->tracks_iter), _("Tracks"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_TRACKS, NULL, TRUE, FALSE ); + vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->tracks_iter), _("Tracks"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_TRACKS, NULL, FALSE, 0 ); } static void trw_layer_add_sublayer_waypoints ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter ) { - vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->waypoints_iter), _("Waypoints"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_WAYPOINTS, NULL, TRUE, FALSE ); + vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->waypoints_iter), _("Waypoints"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_WAYPOINTS, NULL, FALSE, 0 ); } static void trw_layer_add_sublayer_routes ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter ) { - vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->routes_iter), _("Routes"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_ROUTES, NULL, TRUE, FALSE ); + vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->routes_iter), _("Routes"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_ROUTES, NULL, FALSE, 0 ); } static void trw_layer_realize ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter ) @@ -2694,6 +2823,9 @@ static void trw_layer_realize ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter * vik_treeview_item_set_visible ( (VikTreeview *) vt, &(vtl->waypoints_iter), vtl->waypoints_visible ); } + trw_layer_verify_thumbnails ( vtl ); + + trw_layer_sort_all ( vtl ); } static gboolean trw_layer_sublayer_toggle_visible ( VikTrwLayer *l, gint subtype, gpointer sublayer ) @@ -2740,6 +2872,14 @@ gint vik_trw_layer_get_property_tracks_line_thickness ( VikTrwLayer *vtl ) return vtl->line_thickness; } +/* + * Build up multiple routes information + */ +static void trw_layer_routes_tooltip ( const gpointer id, VikTrack *tr, gdouble *length ) +{ + *length = *length + vik_track_get_length (tr); +} + // Structure to hold multiple track information for a layer typedef struct { gdouble length; @@ -2751,36 +2891,37 @@ typedef struct { /* * Build up layer multiple track information via updating the tooltip_tracks structure */ -static void trw_layer_tracks_tooltip ( const gchar *name, VikTrack *tr, tooltip_tracks *tt ) +static void trw_layer_tracks_tooltip ( const gpointer id, VikTrack *tr, tooltip_tracks *tt ) { tt->length = tt->length + vik_track_get_length (tr); // Ensure times are available - if ( tr->trackpoints && - vik_track_get_tp_first(tr)->has_timestamp && - vik_track_get_tp_last(tr)->has_timestamp ) { + if ( tr->trackpoints && vik_track_get_tp_first(tr)->has_timestamp ) { + // Get trkpt only once - as using vik_track_get_tp_last() iterates whole track each time + VikTrackpoint *trkpt_last = vik_track_get_tp_last(tr); + if ( trkpt_last->has_timestamp ) { + time_t t1, t2; + t1 = vik_track_get_tp_first(tr)->timestamp; + t2 = trkpt_last->timestamp; - time_t t1, t2; - t1 = vik_track_get_tp_first(tr)->timestamp; - t2 = vik_track_get_tp_last(tr)->timestamp; + // Assume never actually have a track with a time of 0 (1st Jan 1970) + // Hence initialize to the first 'proper' value + if ( tt->start_time == 0 ) + tt->start_time = t1; + if ( tt->end_time == 0 ) + tt->end_time = t2; - // Assume never actually have a track with a time of 0 (1st Jan 1970) - // Hence initialize to the first 'proper' value - if ( tt->start_time == 0 ) - tt->start_time = t1; - if ( tt->end_time == 0 ) - tt->end_time = t2; + // Update find the earliest / last times + if ( t1 < tt->start_time ) + tt->start_time = t1; + if ( t2 > tt->end_time ) + tt->end_time = t2; - // Update find the earliest / last times - if ( t1 < tt->start_time ) - tt->start_time = t1; - if ( t2 > tt->end_time ) - tt->end_time = t2; - - // Keep track of total time - // there maybe gaps within a track (eg segments) - // but this should be generally good enough for a simple indicator - tt->duration = tt->duration + (int)(t2-t1); + // Keep track of total time + // there maybe gaps within a track (eg segments) + // but this should be generally good enough for a simple indicator + tt->duration = tt->duration + (int)(t2-t1); + } } } @@ -2792,7 +2933,7 @@ static void trw_layer_tracks_tooltip ( const gchar *name, VikTrack *tr, tooltip_ */ static const gchar* trw_layer_layer_tooltip ( VikTrwLayer *vtl ) { - gchar tbuf1[32]; + gchar tbuf1[64]; gchar tbuf2[64]; gchar tbuf3[64]; gchar tbuf4[10]; @@ -2834,35 +2975,63 @@ static const gchar* trw_layer_layer_tooltip ( VikTrwLayer *vtl ) gdouble len_in_units; // Setup info dependent on distance units - if ( a_vik_get_units_distance() == VIK_UNITS_DISTANCE_MILES ) { - g_snprintf (tbuf4, sizeof(tbuf4), "miles"); - len_in_units = VIK_METERS_TO_MILES(tt.length); - } - else { - g_snprintf (tbuf4, sizeof(tbuf4), "kms"); - len_in_units = tt.length/1000.0; + switch ( a_vik_get_units_distance() ) { + case VIK_UNITS_DISTANCE_MILES: + g_snprintf (tbuf4, sizeof(tbuf4), "miles"); + len_in_units = VIK_METERS_TO_MILES(tt.length); + break; + case VIK_UNITS_DISTANCE_NAUTICAL_MILES: + g_snprintf (tbuf4, sizeof(tbuf4), "NM"); + len_in_units = VIK_METERS_TO_NAUTICAL_MILES(tt.length); + break; + default: + g_snprintf (tbuf4, sizeof(tbuf4), "kms"); + len_in_units = tt.length/1000.0; + break; } // Timing information if available tbuf1[0] = '\0'; if ( tt.duration > 0 ) { - g_snprintf (tbuf1, sizeof(tbuf1), - _(" in %d:%02d hrs:mins"), - (int)round(tt.duration/3600), (int)round((tt.duration/60)%60)); + g_snprintf (tbuf1, sizeof(tbuf1), + _(" in %d:%02d hrs:mins"), + (int)(tt.duration/3600), (int)round(tt.duration/60.0)%60); } g_snprintf (tbuf2, sizeof(tbuf2), _("\n%sTotal Length %.1f %s%s"), tbuf3, len_in_units, tbuf4, tbuf1); } + tbuf1[0] = '\0'; + gdouble rlength = 0.0; + g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_routes_tooltip, &rlength ); + if ( rlength > 0.0 ) { + gdouble len_in_units; + // Setup info dependent on distance units + switch ( a_vik_get_units_distance() ) { + case VIK_UNITS_DISTANCE_MILES: + g_snprintf (tbuf4, sizeof(tbuf4), "miles"); + len_in_units = VIK_METERS_TO_MILES(rlength); + break; + case VIK_UNITS_DISTANCE_NAUTICAL_MILES: + g_snprintf (tbuf4, sizeof(tbuf4), "NM"); + len_in_units = VIK_METERS_TO_NAUTICAL_MILES(rlength); + break; + default: + g_snprintf (tbuf4, sizeof(tbuf4), "kms"); + len_in_units = rlength/1000.0; + break; + } + g_snprintf (tbuf1, sizeof(tbuf1), _("\nTotal route length %.1f %s"), len_in_units, tbuf4); + } + // Put together all the elements to form compact tooltip text g_snprintf (tmp_buf, sizeof(tmp_buf), - _("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); + _("Tracks: %d - Waypoints: %d - Routes: %d%s%s"), + g_hash_table_size (vtl->tracks), g_hash_table_size (vtl->waypoints), g_hash_table_size (vtl->routes), tbuf2, tbuf1); g_date_free (gdate_start); g_date_free (gdate_end); - } return tmp_buf; @@ -2915,11 +3084,9 @@ static const gchar* trw_layer_sublayer_tooltip ( VikTrwLayer *l, gint subtype, g if ( tr->trackpoints && vik_track_get_tp_first(tr)->has_timestamp ) { // %x The preferred date representation for the current locale without the time. strftime (time_buf1, sizeof(time_buf1), "%x: ", gmtime(&(vik_track_get_tp_first(tr)->timestamp))); - if ( vik_track_get_tp_last(tr)->has_timestamp ) { - gint dur = ( (vik_track_get_tp_last(tr)->timestamp) - (vik_track_get_tp_first(tr)->timestamp) ); - if ( dur > 0 ) - g_snprintf ( time_buf2, sizeof(time_buf2), _("- %d:%02d hrs:mins"), (int)round(dur/3600), (int)round((dur/60)%60) ); - } + time_t dur = vik_track_get_duration ( tr, TRUE ); + if ( dur > 0 ) + g_snprintf ( time_buf2, sizeof(time_buf2), _("- %d:%02d hrs:mins"), (int)(dur/3600), (int)round(dur/60.0)%60 ); } // Get length and consider the appropriate distance units gdouble tr_len = vik_track_get_length(tr); @@ -2931,6 +3098,9 @@ static const gchar* trw_layer_sublayer_tooltip ( VikTrwLayer *l, gint subtype, g case VIK_UNITS_DISTANCE_MILES: g_snprintf (tmp_buf, sizeof(tmp_buf), _("%s%.1f miles %s"), time_buf1, VIK_METERS_TO_MILES(tr_len), time_buf2); break; + case VIK_UNITS_DISTANCE_NAUTICAL_MILES: + g_snprintf (tmp_buf, sizeof(tmp_buf), _("%s%.1f NM %s"), time_buf1, VIK_METERS_TO_NAUTICAL_MILES(tr_len), time_buf2); + break; default: break; } @@ -2977,13 +3147,18 @@ static void set_statusbar_msg_info_trkpt ( VikTrwLayer *vtl, VikTrackpoint *trkp { gchar *statusbar_format_code = NULL; gboolean need2free = FALSE; + VikTrackpoint *trkpt_prev = NULL; if ( !a_settings_get_string ( VIK_SETTINGS_TRKPT_SELECTED_STATUSBAR_FORMAT, &statusbar_format_code ) ) { // Otherwise use default statusbar_format_code = g_strdup ( "KEATDN" ); need2free = TRUE; } + else { + // Format code may want to show speed - so may need previous trkpt to work it out + trkpt_prev = vik_track_get_tp_prev ( vtl->current_tp_track, trkpt ); + } - gchar *msg = vu_trackpoint_formatted_message ( statusbar_format_code, trkpt, NULL, vtl->current_tp_track ); + gchar *msg = vu_trackpoint_formatted_message ( statusbar_format_code, trkpt, trkpt_prev, vtl->current_tp_track, NAN ); vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, msg ); g_free ( msg ); @@ -3146,7 +3321,7 @@ GHashTable *vik_trw_layer_get_routes_iters ( VikTrwLayer *vtl ) GHashTable *vik_trw_layer_get_waypoints_iters ( VikTrwLayer *vtl ) { - return vtl->waypoints; + return vtl->waypoints_iters; } gboolean vik_trw_layer_is_empty ( VikTrwLayer *vtl ) @@ -3273,51 +3448,7 @@ static void trw_layer_centerize ( menu_array_layer values ) void trw_layer_zoom_to_show_latlons ( VikTrwLayer *vtl, VikViewport *vvp, struct LatLon maxmin[2] ) { - /* First set the center [in case previously viewing from elsewhere] */ - /* Then loop through zoom levels until provided positions are in view */ - /* This method is not particularly fast - but should work well enough */ - struct LatLon average = { (maxmin[0].lat+maxmin[1].lat)/2, (maxmin[0].lon+maxmin[1].lon)/2 }; - VikCoord coord; - vik_coord_load_from_latlon ( &coord, vtl->coord_mode, &average ); - vik_viewport_set_center_coord ( vvp, &coord, TRUE ); - - /* Convert into definite 'smallest' and 'largest' positions */ - struct LatLon minmin; - if ( maxmin[0].lat < maxmin[1].lat ) - minmin.lat = maxmin[0].lat; - else - minmin.lat = maxmin[1].lat; - - struct LatLon maxmax; - if ( maxmin[0].lon > maxmin[1].lon ) - maxmax.lon = maxmin[0].lon; - else - maxmax.lon = maxmin[1].lon; - - /* Never zoom in too far - generally not that useful, as too close ! */ - /* Always recalculate the 'best' zoom level */ - gdouble zoom = 1.0; - vik_viewport_set_zoom ( vvp, zoom ); - - gdouble min_lat, max_lat, min_lon, max_lon; - /* Should only be a maximum of about 18 iterations from min to max zoom levels */ - while ( zoom <= VIK_VIEWPORT_MAX_ZOOM ) { - vik_viewport_get_min_max_lat_lon ( vvp, &min_lat, &max_lat, &min_lon, &max_lon ); - /* NB I think the logic used in this test to determine if the bounds is within view - fails if track goes across 180 degrees longitude. - Hopefully that situation is not too common... - Mind you viking doesn't really do edge locations to well anyway */ - if ( min_lat < minmin.lat && - max_lat > minmin.lat && - min_lon < maxmax.lon && - max_lon > maxmax.lon ) - /* Found within zoom level */ - break; - - /* Try next */ - zoom = zoom * 2; - vik_viewport_set_zoom ( vvp, zoom ); - } + vu_zoom_to_show_latlons ( vtl->coord_mode, vvp, maxmin ); } gboolean vik_trw_layer_auto_set_view ( VikTrwLayer *vtl, VikViewport *vvp ) @@ -3380,6 +3511,15 @@ static void trw_layer_export_kml ( menu_array_layer values ) g_free ( auto_save_name ); } +static void trw_layer_export_geojson ( menu_array_layer values ) +{ + gchar *auto_save_name = append_file_ext ( vik_layer_get_name(VIK_LAYER(values[MA_VTL])), FILE_TYPE_GEOJSON ); + + vik_trw_layer_export ( VIK_TRW_LAYER (values[MA_VTL]), _("Export Layer"), auto_save_name, NULL, FILE_TYPE_GEOJSON ); + + g_free ( auto_save_name ); +} + static void trw_layer_export_babel ( gpointer layer_and_vlp[2] ) { const gchar *auto_save_name = vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])); @@ -3445,13 +3585,13 @@ static void trw_layer_goto_wp ( menu_array_layer values ) GtkWidget *label, *entry; label = gtk_label_new(_("Waypoint Name:")); - entry = gtk_entry_new(); + entry = ui_entry_new ( NULL, GTK_ENTRY_ICON_SECONDARY ); gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dia))), label, FALSE, FALSE, 0); gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dia))), entry, FALSE, FALSE, 0); - gtk_widget_show_all ( label ); - gtk_widget_show_all ( entry ); - + gtk_widget_show_all ( dia ); + // 'ok' when press return in the entry + g_signal_connect_swapped ( entry, "activate", G_CALLBACK(a_dialog_response_accept), dia ); gtk_dialog_set_default_response ( GTK_DIALOG(dia), GTK_RESPONSE_ACCEPT ); while ( gtk_dialog_run ( GTK_DIALOG(dia) ) == GTK_RESPONSE_ACCEPT ) @@ -3473,7 +3613,7 @@ static void trw_layer_goto_wp ( menu_array_layer values ) udata.uuid = NULL; // Hmmm, want key of it - gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata ); + gpointer wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata ); if ( wpf && udata.uuid ) { GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udata.uuid ); @@ -3608,7 +3748,10 @@ static void trw_layer_acquire ( menu_array_layer values, VikDataSourceInterface VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)); VikViewport *vvp = vik_window_viewport(vw); - a_acquire ( vw, vlp, vvp, datasource, NULL, NULL ); + vik_datasource_mode_t mode = datasource->mode; + if ( mode == VIK_DATASOURCE_AUTO_LAYER_MANAGEMENT ) + mode = VIK_DATASOURCE_ADDTOLAYER; + a_acquire ( vw, vlp, vvp, mode, datasource, NULL, NULL ); } /* @@ -3616,7 +3759,6 @@ static void trw_layer_acquire ( menu_array_layer values, VikDataSourceInterface */ static void trw_layer_acquire_gps_cb ( menu_array_layer values ) { - vik_datasource_gps_interface.mode = VIK_DATASOURCE_ADDTOLAYER; trw_layer_acquire ( values, &vik_datasource_gps_interface ); } @@ -3633,7 +3775,6 @@ static void trw_layer_acquire_routing_cb ( menu_array_layer values ) */ static void trw_layer_acquire_url_cb ( menu_array_layer values ) { - vik_datasource_url_interface.mode = VIK_DATASOURCE_ADDTOLAYER; trw_layer_acquire ( values, &vik_datasource_url_interface ); } @@ -3673,15 +3814,22 @@ static void trw_layer_acquire_geotagged_cb ( menu_array_layer values ) { VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]); - vik_datasource_geotag_interface.mode = VIK_DATASOURCE_ADDTOLAYER; trw_layer_acquire ( values, &vik_datasource_geotag_interface ); // Reverify thumbnails as they may have changed vtl->has_verified_thumbnails = FALSE; - trw_layer_verify_thumbnails ( vtl, NULL ); + trw_layer_verify_thumbnails ( vtl ); } #endif +/* + * Acquire into this TRW Layer from any GPS Babel supported file + */ +static void trw_layer_acquire_file_cb ( menu_array_layer values ) +{ + trw_layer_acquire ( values, &vik_datasource_file_interface ); +} + static void trw_layer_gps_upload ( menu_array_layer values ) { menu_array_sublayer data; @@ -3789,28 +3937,16 @@ static void trw_layer_gps_upload_any ( menu_array_sublayer values ) turn_off ); } -/* - * Acquire into this TRW Layer from any GPS Babel supported file - */ -static void trw_layer_acquire_file_cb ( menu_array_layer values ) -{ - VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]); - VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]); - VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)); - VikViewport *vvp = vik_window_viewport(vw); - - a_acquire ( vw, vlp, vvp, &vik_datasource_file_interface, NULL, NULL ); -} - static void trw_layer_new_wp ( menu_array_layer values ) { VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]); VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]); /* TODO longone: okay, if layer above (aggregate) is invisible but vtl->visible is true, this redraws for no reason. instead return true if you want to update. */ - if ( vik_trw_layer_new_waypoint ( vtl, VIK_GTK_WINDOW_FROM_LAYER(vtl), vik_viewport_get_center(vik_layers_panel_get_viewport(vlp))) && VIK_LAYER(vtl)->visible ) { + if ( vik_trw_layer_new_waypoint ( vtl, VIK_GTK_WINDOW_FROM_LAYER(vtl), vik_viewport_get_center(vik_layers_panel_get_viewport(vlp))) ) { trw_layer_calculate_bounds_waypoints ( vtl ); - vik_layers_panel_emit_update ( vlp ); + if ( VIK_LAYER(vtl)->visible ) + vik_layers_panel_emit_update ( vlp ); } } @@ -3943,6 +4079,17 @@ void trw_layer_osm_traces_upload_track_cb ( menu_array_sublayer values ) } } +static GtkWidget* create_external_submenu ( GtkMenu *menu ) +{ + GtkWidget *external_submenu = gtk_menu_new (); + GtkWidget *item = gtk_image_menu_item_new_with_mnemonic ( _("Externa_l") ); + gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_EXECUTE, 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), external_submenu ); + return external_submenu; +} + static void trw_layer_add_menu_items ( VikTrwLayer *vtl, GtkMenu *menu, gpointer vlp ) { static menu_array_layer pass_along; @@ -4032,15 +4179,26 @@ static void trw_layer_add_menu_items ( VikTrwLayer *vtl, GtkMenu *menu, gpointer gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item); gtk_widget_show ( item ); - item = gtk_menu_item_new_with_mnemonic ( _("Export as _KML...") ); - g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_kml), pass_along ); - gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item); - gtk_widget_show ( item ); + if ( a_babel_available () ) { + item = gtk_menu_item_new_with_mnemonic ( _("Export as _KML...") ); + g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_kml), pass_along ); + gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item); + gtk_widget_show ( item ); + } - item = gtk_menu_item_new_with_mnemonic ( _("Export via GPSbabel...") ); - g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_babel), pass_along ); - gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item); - gtk_widget_show ( item ); + if ( have_geojson_export ) { + item = gtk_menu_item_new_with_mnemonic ( _("Export as GEO_JSON...") ); + g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_geojson), pass_along ); + gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item); + gtk_widget_show ( item ); + } + + if ( a_babel_available () ) { + item = gtk_menu_item_new_with_mnemonic ( _("Export via GPSbabel...") ); + g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_babel), pass_along ); + gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item); + gtk_widget_show ( item ); + } gchar* external1 = g_strdup_printf ( _("Open with External Program_1: %s"), a_vik_get_external_gpx_program_1() ); item = gtk_menu_item_new_with_mnemonic ( external1 ); @@ -4162,11 +4320,13 @@ static void trw_layer_add_menu_items ( VikTrwLayer *vtl, GtkMenu *menu, gpointer gtk_widget_show ( item ); #endif - item = gtk_menu_item_new_with_mnemonic ( _("From _File...") ); - g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_file_cb), pass_along ); - gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item); - gtk_widget_set_tooltip_text (item, _("Import File With GPS_Babel...")); - gtk_widget_show ( item ); + if ( a_babel_available () ) { + item = gtk_menu_item_new_with_mnemonic ( _("From _File...") ); + g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_file_cb), pass_along ); + gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item); + gtk_widget_set_tooltip_text (item, _("Import File With GPS_Babel...")); + gtk_widget_show ( item ); + } vik_ext_tool_datasources_add_menu_items_to_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), GTK_MENU (acquire_submenu) ); @@ -4261,6 +4421,10 @@ static void trw_layer_add_menu_items ( VikTrwLayer *vtl, GtkMenu *menu, gpointer gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item ); gtk_widget_show ( item ); gtk_widget_set_sensitive ( item, (gboolean)(g_hash_table_size (vtl->waypoints)) ); + + GtkWidget *external_submenu = create_external_submenu ( menu ); + // TODO: Should use selected layer's centre - rather than implicitly using the current viewport + vik_ext_tools_add_menu_items_to_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), GTK_MENU (external_submenu), NULL ); } // Fake Waypoint UUIDs vi simple increasing integer @@ -4281,8 +4445,12 @@ void vik_trw_layer_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter)); + time_t timestamp = 0; + if ( wp->has_timestamp ) + timestamp = wp->timestamp; + // Visibility column always needed for waypoints - vik_treeview_add_sublayer ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), iter, name, vtl, GUINT_TO_POINTER(wp_uuid), VIK_TRW_LAYER_SUBLAYER_WAYPOINT, get_wp_sym_small (wp->symbol), TRUE, TRUE ); + vik_treeview_add_sublayer ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), iter, name, vtl, GUINT_TO_POINTER(wp_uuid), VIK_TRW_LAYER_SUBLAYER_WAYPOINT, get_wp_sym_small (wp->symbol), TRUE, timestamp ); // Actual setting of visibility dependent on the waypoint vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, wp->visible ); @@ -4315,8 +4483,14 @@ void vik_trw_layer_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *t ) } GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter)); + + time_t timestamp = 0; + VikTrackpoint *tpt = vik_track_get_tp_first(t); + if ( tpt && tpt->has_timestamp ) + timestamp = tpt->timestamp; + // Visibility column always needed for tracks - vik_treeview_add_sublayer ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), iter, name, vtl, GUINT_TO_POINTER(tr_uuid), VIK_TRW_LAYER_SUBLAYER_TRACK, NULL, TRUE, TRUE ); + vik_treeview_add_sublayer ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), iter, name, vtl, GUINT_TO_POINTER(tr_uuid), VIK_TRW_LAYER_SUBLAYER_TRACK, NULL, TRUE, timestamp ); // Actual setting of visibility dependent on the track vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, t->visible ); @@ -4350,7 +4524,7 @@ void vik_trw_layer_add_route ( VikTrwLayer *vtl, gchar *name, VikTrack *t ) GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter)); // Visibility column always needed for routes - 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 ); + 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, 0 ); // Routes don't have times // Actual setting of visibility dependent on the route vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, t->visible ); @@ -4419,7 +4593,19 @@ gchar *trw_layer_new_unique_sublayer_name (VikTrwLayer *vtl, gint sublayer_type, } // 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); + const gchar *corename = newname; + gint newi = i; + // If name is already of the form text#N + // set name to text and i to N+1 + gchar **tokens = g_regex_split_simple ( "#(\\d+)", newname, G_REGEX_CASELESS, 0 ); + if ( tokens ) { + corename = tokens[0]; + if ( tokens[1] ) { + newi = atoi ( tokens[1] ) + 1; + } + } + gchar *new_newname = g_strdup_printf("%s#%d", corename, newi); + g_strfreev ( tokens ); g_free(newname); newname = new_newname; i++; @@ -4444,10 +4630,12 @@ void vik_trw_layer_filein_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *t // enforce end of current track equal to start of tr VikTrackpoint *cur_end = vik_track_get_tp_last ( vtl->current_track ); VikTrackpoint *new_start = vik_track_get_tp_first ( tr ); - if ( ! vik_coord_equals ( &cur_end->coord, &new_start->coord ) ) { - vik_track_add_trackpoint ( vtl->current_track, - vik_trackpoint_copy ( cur_end ), - FALSE ); + if ( cur_end && new_start ) { + if ( ! vik_coord_equals ( &cur_end->coord, &new_start->coord ) ) { + vik_track_add_trackpoint ( vtl->current_track, + vik_trackpoint_copy ( cur_end ), + FALSE ); + } } vik_track_steal_and_append_trackpoints ( vtl->current_track, tr ); @@ -4478,34 +4666,31 @@ static void trw_layer_enum_item ( gpointer id, GList **tr, GList **l ) */ static void trw_layer_move_item ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, gpointer id, gint type ) { + // When an item is moved the name is checked to see if it clashes with an existing name + // in the destination layer and if so then it is allocated a new name + // TODO reconsider strategy when moving within layer (if anything...) - gboolean rename = ( vtl_src != vtl_dest ); - if ( ! rename ) + if ( vtl_src == vtl_dest ) return; if (type == VIK_TRW_LAYER_SUBLAYER_TRACK) { VikTrack *trk = g_hash_table_lookup ( vtl_src->tracks, id ); - gchar *newname; - if ( rename ) - newname = trw_layer_new_unique_sublayer_name ( vtl_dest, type, trk->name ); - else - newname = g_strdup ( trk->name ); + gchar *newname = trw_layer_new_unique_sublayer_name ( vtl_dest, type, trk->name ); VikTrack *trk2 = vik_track_copy ( trk, TRUE ); vik_trw_layer_add_track ( vtl_dest, newname, trk2 ); g_free ( newname ); vik_trw_layer_delete_track ( vtl_src, trk ); + // Reset layer timestamps in case they have now changed + vik_treeview_item_set_timestamp ( vtl_dest->vl.vt, &vtl_dest->vl.iter, trw_layer_get_timestamp(vtl_dest) ); + vik_treeview_item_set_timestamp ( vtl_src->vl.vt, &vtl_src->vl.iter, trw_layer_get_timestamp(vtl_src) ); } if (type == VIK_TRW_LAYER_SUBLAYER_ROUTE) { VikTrack *trk = g_hash_table_lookup ( vtl_src->routes, id ); - gchar *newname; - if ( rename ) - newname = trw_layer_new_unique_sublayer_name ( vtl_dest, type, trk->name ); - else - newname = g_strdup ( trk->name ); + gchar *newname = trw_layer_new_unique_sublayer_name ( vtl_dest, type, trk->name ); VikTrack *trk2 = vik_track_copy ( trk, TRUE ); vik_trw_layer_add_route ( vtl_dest, newname, trk2 ); @@ -4516,11 +4701,7 @@ static void trw_layer_move_item ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, g if (type == VIK_TRW_LAYER_SUBLAYER_WAYPOINT) { VikWaypoint *wp = g_hash_table_lookup ( vtl_src->waypoints, id ); - gchar *newname; - if ( rename ) - newname = trw_layer_new_unique_sublayer_name ( vtl_dest, type, wp->name ); - else - newname = g_strdup ( wp->name ); + gchar *newname = trw_layer_new_unique_sublayer_name ( vtl_dest, type, wp->name ); VikWaypoint *wp2 = vik_waypoint_copy ( wp ); vik_trw_layer_add_waypoint ( vtl_dest, newname, wp2 ); @@ -4530,6 +4711,9 @@ static void trw_layer_move_item ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, g // Recalculate bounds even if not renamed as maybe dragged between layers trw_layer_calculate_bounds_waypoints ( vtl_dest ); trw_layer_calculate_bounds_waypoints ( vtl_src ); + // Reset layer timestamps in case they have now changed + vik_treeview_item_set_timestamp ( vtl_dest->vl.vt, &vtl_dest->vl.iter, trw_layer_get_timestamp(vtl_dest) ); + vik_treeview_item_set_timestamp ( vtl_src->vl.vt, &vtl_src->vl.iter, trw_layer_get_timestamp(vtl_src) ); } } @@ -4604,7 +4788,7 @@ gboolean vik_trw_layer_delete_track ( VikTrwLayer *vtl, VikTrack *trk ) udata.uuid = NULL; // Hmmm, want key of it - gpointer *trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata ); + gpointer trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata ); if ( trkf && udata.uuid ) { /* could be current_tp, so we have to check */ @@ -4652,7 +4836,7 @@ gboolean vik_trw_layer_delete_route ( VikTrwLayer *vtl, VikTrack *trk ) udata.uuid = NULL; // Hmmm, want key of it - gpointer *trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata ); + 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 */ @@ -4696,7 +4880,7 @@ static gboolean trw_layer_delete_waypoint ( VikTrwLayer *vtl, VikWaypoint *wp ) udata.uuid = NULL; // Hmmm, want key of it - gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata ); + gpointer wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata ); if ( wpf && udata.uuid ) { GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udata.uuid ); @@ -4748,7 +4932,7 @@ static gboolean trw_layer_delete_waypoint_by_name ( VikTrwLayer *vtl, const gcha udata.uuid = NULL; // Hmmm, want key of it - gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid_by_name, (gpointer) &udata ); + gpointer wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid_by_name, (gpointer) &udata ); vik_waypoint_free (udata.wp); @@ -4789,7 +4973,7 @@ 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 ( ht_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); @@ -4820,9 +5004,10 @@ void vik_trw_layer_delete_all_routes ( VikTrwLayer *vtl ) 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) ); + if ( g_hash_table_size (vtl->routes) > 0 ) + vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter) ); + g_hash_table_remove_all(vtl->routes); vik_layer_emit_update ( VIK_LAYER(vtl) ); } @@ -4837,9 +5022,10 @@ void vik_trw_layer_delete_all_tracks ( VikTrwLayer *vtl ) g_hash_table_foreach(vtl->tracks_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt); g_hash_table_remove_all(vtl->tracks_iters); - g_hash_table_remove_all(vtl->tracks); - vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter) ); + if ( g_hash_table_size (vtl->tracks) > 0 ) + vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter) ); + g_hash_table_remove_all(vtl->tracks); vik_layer_emit_update ( VIK_LAYER(vtl) ); } @@ -4854,9 +5040,10 @@ void vik_trw_layer_delete_all_waypoints ( VikTrwLayer *vtl ) g_hash_table_foreach(vtl->waypoints_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt); g_hash_table_remove_all(vtl->waypoints_iters); - g_hash_table_remove_all(vtl->waypoints); - vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter) ); + if ( g_hash_table_size (vtl->waypoints) > 0 ) + vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter) ); + g_hash_table_remove_all(vtl->waypoints); vik_layer_emit_update ( VIK_LAYER(vtl) ); } @@ -4907,6 +5094,9 @@ static void trw_layer_delete_item ( menu_array_sublayer values ) wp->name ) ) return; was_visible = trw_layer_delete_waypoint ( vtl, wp ); + trw_layer_calculate_bounds_waypoints ( vtl ); + // Reset layer timestamp in case it has now changed + vik_treeview_item_set_timestamp ( vtl->vl.vt, &vtl->vl.iter, trw_layer_get_timestamp(vtl) ); } } else if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_TRACK ) @@ -4920,6 +5110,8 @@ static void trw_layer_delete_item ( menu_array_sublayer values ) trk->name ) ) return; was_visible = vik_trw_layer_delete_track ( vtl, trk ); + // Reset layer timestamp in case it has now changed + vik_treeview_item_set_timestamp ( vtl->vl.vt, &vtl->vl.iter, trw_layer_get_timestamp(vtl) ); } } else @@ -4952,7 +5144,7 @@ void trw_layer_waypoint_rename ( VikTrwLayer *vtl, VikWaypoint *wp, const gchar udataU.uuid = NULL; // Need key of it for treeview update - gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, &udataU ); + gpointer wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, &udataU ); if ( wpf && udataU.uuid ) { GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udataU.uuid ); @@ -4975,7 +5167,7 @@ void trw_layer_waypoint_reset_icon ( VikTrwLayer *vtl, VikWaypoint *wp ) udataU.uuid = NULL; // Need key of it for treeview update - gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, &udataU ); + gpointer wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, &udataU ); if ( wpf && udataU.uuid ) { GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udataU.uuid ); @@ -5062,7 +5254,7 @@ void trw_layer_update_treeview ( VikTrwLayer *vtl, VikTrack *trk ) udata.trk = trk; udata.uuid = NULL; - gpointer *trkf = NULL; + gpointer trkf = NULL; if ( trk->is_route ) trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata ); else @@ -5208,6 +5400,19 @@ static void trw_layer_anonymize_times ( menu_array_sublayer values ) vik_track_anonymize_times ( track ); } +static void trw_layer_interpolate_times ( menu_array_sublayer values ) +{ + VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL]; + VikTrack *track; + if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE ) + track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] ); + else + track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] ); + + if ( track ) + vik_track_interpolate_times ( track ); +} + static void trw_layer_extend_track_end ( menu_array_sublayer values ) { VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]); @@ -5652,16 +5857,22 @@ static void find_tracks_with_timestamp_type(gpointer key, gpointer value, gpoint *(user_data->result) = g_list_prepend(*(user_data->result), key); } -/* called for each key in track hash table. if original track user_data[1] is close enough - * to the passed one, add it to list in user_data[0] +/** + * find_nearby_tracks_by_time: + * + * Called for each track in track hash table. + * If the original track (in user_data[1]) is close enough (threshold period in user_data[2]) + * to the current track, then the current track is added to the list in user_data[0] */ static void find_nearby_tracks_by_time (gpointer key, gpointer value, gpointer user_data) { - time_t t1, t2; - VikTrackpoint *p1, *p2; VikTrack *trk = VIK_TRACK(value); GList **nearby_tracks = ((gpointer *)user_data)[0]; + VikTrack *orig_trk = VIK_TRACK(((gpointer *)user_data)[1]); + + if ( !orig_trk || !orig_trk->trackpoints ) + return; /* outline: * detect reasons for not merging, and return @@ -5674,12 +5885,13 @@ static void find_nearby_tracks_by_time (gpointer key, gpointer value, gpointer u return; } - t1 = vik_track_get_tp_first(trk)->timestamp; - t2 = vik_track_get_tp_last(trk)->timestamp; + time_t t1 = vik_track_get_tp_first(orig_trk)->timestamp; + time_t t2 = vik_track_get_tp_last(orig_trk)->timestamp; if (trk->trackpoints) { - p1 = vik_track_get_tp_first(trk); - p2 = vik_track_get_tp_last(trk); + + VikTrackpoint *p1 = vik_track_get_tp_first(trk); + VikTrackpoint *p2 = vik_track_get_tp_last(trk); if (!p1->has_timestamp || !p2->has_timestamp) { //g_print("no timestamp\n"); @@ -5688,11 +5900,11 @@ static void find_nearby_tracks_by_time (gpointer key, gpointer value, gpointer u guint threshold = GPOINTER_TO_UINT (((gpointer *)user_data)[2]); //g_print("Got track named %s, times %d, %d\n", trk->name, p1->timestamp, p2->timestamp); - if (! (abs(t1 - p2->timestamp) < threshold || - /* p1 p2 t1 t2 */ - abs(p1->timestamp - t2) < threshold) - /* t1 t2 p1 p2 */ - ) { + if (! (labs(t1 - p2->timestamp) < threshold || + /* p1 p2 t1 t2 */ + labs(p1->timestamp - t2) < threshold) + /* t1 t2 p1 p2 */ + ) { return; } } @@ -6081,7 +6293,7 @@ static void trw_layer_merge_by_timestamp ( menu_array_sublayer values ) } params[0] = &nearby_tracks; - params[1] = (gpointer)trps; + params[1] = orig_trk; params[2] = GUINT_TO_POINTER (threshold_in_minutes*60); // In seconds /* get a list of adjacent-in-time tracks */ @@ -6148,7 +6360,7 @@ static void trw_layer_split_at_selected_trackpoint ( VikTrwLayer *vtl, gint subt udata.uuid = NULL; // Also need id of newly created track - gpointer *trkf; + gpointer trkf; if ( tr->is_route ) trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata ); else @@ -6555,14 +6767,17 @@ static void trw_layer_reverse ( menu_array_sublayer values ) } /** - * Open a diary at the specified date + * Open a program at the specified date + * Mainly for RedNotebook - http://rednotebook.sourceforge.net/ + * But could work with any program that accepts a command line of --date= + * FUTURE: Allow configuring of command line options + date format */ static void trw_layer_diary_open ( VikTrwLayer *vtl, const gchar *date_str ) { GError *err = NULL; - gchar *cmd = g_strdup_printf ( "%s%s", "rednotebook --date=", date_str ); + gchar *cmd = g_strdup_printf ( "%s %s%s", diary_program, "--date=", date_str ); if ( ! g_spawn_command_line_async ( cmd, &err ) ) { - a_dialog_error_msg_extra ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Could not launch %s to open file."), "rednotebook" ); + a_dialog_error_msg_extra ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Could not launch %s to open file."), diary_program ); g_error_free ( err ); } g_free ( cmd ); @@ -6605,6 +6820,136 @@ static void trw_layer_diary ( menu_array_sublayer values ) } } +/** + * Open a program at the specified date + * Mainly for Stellarium - http://stellarium.org/ + * But could work with any program that accepts the same command line options... + * FUTURE: Allow configuring of command line options + format or parameters + */ +static void trw_layer_astro_open ( VikTrwLayer *vtl, const gchar *date_str, const gchar *time_str, const gchar *lat_str, const gchar *lon_str, const gchar *alt_str ) +{ + GError *err = NULL; + gchar *tmp; + gint fd = g_file_open_tmp ( "vik-astro-XXXXXX.ini", &tmp, &err ); + if (fd < 0) { + g_warning ( "%s: Failed to open temporary file: %s", __FUNCTION__, err->message ); + g_clear_error ( &err ); + return; + } + gchar *cmd = g_strdup_printf ( "%s %s %s %s %s %s %s %s %s %s %s %s %s %s", + astro_program, "-c", tmp, "--full-screen no", "--sky-date", date_str, "--sky-time", time_str, "--latitude", lat_str, "--longitude", lon_str, "--altitude", alt_str ); + g_warning ( "%s", cmd ); + if ( ! g_spawn_command_line_async ( cmd, &err ) ) { + a_dialog_error_msg_extra ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Could not launch %s"), astro_program ); + g_warning ( "%s", err->message ); + g_error_free ( err ); + } + util_add_to_deletion_list ( tmp ); + g_free ( tmp ); + g_free ( cmd ); +} + +// Format of stellarium lat & lon seems designed to be particularly awkward +// who uses ' & " in the parameters for the command line?! +// -1d4'27.48" +// +53d58'16.65" +static gchar *convert_to_dms ( gdouble dec ) +{ + gdouble tmp; + gchar sign_c = ' '; + gint val_d, val_m; + gdouble val_s; + gchar *result = NULL; + + if ( dec > 0 ) + sign_c = '+'; + else if ( dec < 0 ) + sign_c = '-'; + else // Nul value + sign_c = ' '; + + // Degrees + tmp = fabs(dec); + val_d = (gint)tmp; + + // Minutes + tmp = (tmp - val_d) * 60; + val_m = (gint)tmp; + + // Seconds + val_s = (tmp - val_m) * 60; + + // Format + result = g_strdup_printf ( "%c%dd%d\\\'%.4f\\\"", sign_c, val_d, val_m, val_s ); + return result; +} + +/** + * Open an astronomy program at the date & position of the track center, trackpoint or waypoint + */ +static void trw_layer_astro ( menu_array_sublayer values ) +{ + VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]); + + if ( GPOINTER_TO_INT(values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_TRACK ) { + VikTrack *trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] ); + if ( ! trk ) + return; + + VikTrackpoint *tp = NULL; + if ( vtl->current_tpl ) + // Current Trackpoint + tp = VIK_TRACKPOINT(vtl->current_tpl->data); + else if ( trk->trackpoints ) + // Otherwise first trackpoint + tp = VIK_TRACKPOINT(trk->trackpoints->data); + else + // Give up + return; + + if ( tp->has_timestamp ) { + gchar date_buf[20]; + strftime (date_buf, sizeof(date_buf), "%Y%m%d", gmtime(&(tp->timestamp))); + gchar time_buf[20]; + strftime (time_buf, sizeof(time_buf), "%H:%M:%S", gmtime(&(tp->timestamp))); + struct LatLon ll; + vik_coord_to_latlon ( &tp->coord, &ll ); + gchar *lat_str = convert_to_dms ( ll.lat ); + gchar *lon_str = convert_to_dms ( ll.lon ); + gchar alt_buf[20]; + snprintf (alt_buf, sizeof(alt_buf), "%d", (gint)round(tp->altitude) ); + trw_layer_astro_open ( vtl, date_buf, time_buf, lat_str, lon_str, alt_buf); + g_free ( lat_str ); + g_free ( lon_str ); + } + else + a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("This track has no date information.") ); + } + else if ( GPOINTER_TO_INT(values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) { + VikWaypoint *wpt = (VikWaypoint *) g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] ); + if ( ! wpt ) + return; + + if ( wpt->has_timestamp ) { + gchar date_buf[20]; + strftime (date_buf, sizeof(date_buf), "%Y%m%d", gmtime(&(wpt->timestamp))); + gchar time_buf[20]; + strftime (time_buf, sizeof(time_buf), "%H:%M:%S", gmtime(&(wpt->timestamp))); + struct LatLon ll; + vik_coord_to_latlon ( &wpt->coord, &ll ); + gchar *lat_str = convert_to_dms ( ll.lat ); + gchar *lon_str = convert_to_dms ( ll.lon ); + gchar alt_buf[20]; + snprintf (alt_buf, sizeof(alt_buf), "%d", (gint)round(wpt->altitude) ); + trw_layer_astro_open ( vtl, date_buf, time_buf, lat_str, lon_str, alt_buf ); + g_free ( lat_str ); + g_free ( lon_str ); + } + else + a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("This waypoint has no date information.") ); + } +} + /** * Similar to trw_layer_enum_item, but this uses a sorted method */ @@ -6747,7 +7092,7 @@ 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 ( track_table, (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 ) { @@ -6760,11 +7105,12 @@ static void vik_trw_layer_uniquify_tracks ( VikTrwLayer *vtl, VikLayersPanel *vl if ( it ) { vik_treeview_item_set_name ( VIK_LAYER(vtl)->vt, it, newname ); if ( ontrack ) - vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), vtl->wp_sort_order ); - else - vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter), vtl->wp_sort_order ); + vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), vtl->track_sort_order ); + else + vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter), vtl->track_sort_order ); } } + g_free ( newname ); // Start trying to find same names again... track_names = NULL; @@ -6781,50 +7127,50 @@ static void vik_trw_layer_uniquify_tracks ( VikTrwLayer *vtl, VikLayersPanel *vl vik_layers_panel_emit_update ( vlp ); } -static void trw_layer_sort_order_a2z ( menu_array_sublayer values ) +static void trw_layer_sort_order_specified ( VikTrwLayer *vtl, guint sublayer_type, vik_layer_sort_order_t order ) { - VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]); GtkTreeIter *iter; - switch (GPOINTER_TO_INT (values[MA_SUBTYPE])) { + switch (sublayer_type) { case VIK_TRW_LAYER_SUBLAYER_TRACKS: iter = &(vtl->tracks_iter); - vtl->track_sort_order = VL_SO_ALPHABETICAL_ASCENDING; + vtl->track_sort_order = order; break; case VIK_TRW_LAYER_SUBLAYER_ROUTES: iter = &(vtl->routes_iter); - vtl->track_sort_order = VL_SO_ALPHABETICAL_ASCENDING; + vtl->track_sort_order = order; break; default: // VIK_TRW_LAYER_SUBLAYER_WAYPOINTS: iter = &(vtl->waypoints_iter); - vtl->wp_sort_order = VL_SO_ALPHABETICAL_ASCENDING; + vtl->wp_sort_order = order; break; } - vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, iter, VL_SO_ALPHABETICAL_ASCENDING ); + vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, iter, order ); +} + +static void trw_layer_sort_order_a2z ( menu_array_sublayer values ) +{ + VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]); + trw_layer_sort_order_specified ( vtl, GPOINTER_TO_INT(values[MA_SUBTYPE]), VL_SO_ALPHABETICAL_ASCENDING ); } static void trw_layer_sort_order_z2a ( menu_array_sublayer values ) { VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]); - GtkTreeIter *iter; + trw_layer_sort_order_specified ( vtl, GPOINTER_TO_INT(values[MA_SUBTYPE]), VL_SO_ALPHABETICAL_DESCENDING ); +} - switch (GPOINTER_TO_INT (values[MA_SUBTYPE])) { - case VIK_TRW_LAYER_SUBLAYER_TRACKS: - iter = &(vtl->tracks_iter); - vtl->track_sort_order = VL_SO_ALPHABETICAL_DESCENDING; - break; - case VIK_TRW_LAYER_SUBLAYER_ROUTES: - iter = &(vtl->routes_iter); - vtl->track_sort_order = VL_SO_ALPHABETICAL_DESCENDING; - break; - default: // VIK_TRW_LAYER_SUBLAYER_WAYPOINTS: - iter = &(vtl->waypoints_iter); - vtl->wp_sort_order = VL_SO_ALPHABETICAL_DESCENDING; - break; - } +static void trw_layer_sort_order_timestamp_ascend ( menu_array_sublayer values ) +{ + VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]); + trw_layer_sort_order_specified ( vtl, GPOINTER_TO_INT(values[MA_SUBTYPE]), VL_SO_DATE_ASCENDING ); +} - vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, iter, VL_SO_ALPHABETICAL_DESCENDING ); +static void trw_layer_sort_order_timestamp_descend ( menu_array_sublayer values ) +{ + VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]); + trw_layer_sort_order_specified ( vtl, GPOINTER_TO_INT(values[MA_SUBTYPE]), VL_SO_DATE_DESCENDING ); } /** @@ -6870,6 +7216,9 @@ static void trw_layer_delete_tracks_from_selection ( menu_array_layer values ) trw_layer_delete_track_by_name (vtl, l->data, vtl->tracks); } g_list_free(delete_list); + // Reset layer timestamps in case they have now changed + vik_treeview_item_set_timestamp ( vtl->vl.vt, &vtl->vl.iter, trw_layer_get_timestamp(vtl) ); + vik_layer_emit_update( VIK_LAYER(vtl) ); } } @@ -7020,6 +7369,8 @@ static void vik_trw_layer_uniquify_waypoints ( VikTrwLayer *vtl, VikLayersPanel trw_layer_waypoint_rename ( vtl, waypoint, newname ); + g_free (newname); + // Start trying to find same names again... waypoint_names = NULL; g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names ); @@ -7081,6 +7432,8 @@ static void trw_layer_delete_waypoints_from_selection ( menu_array_layer values g_list_free(delete_list); trw_layer_calculate_bounds_waypoints ( vtl ); + // Reset layer timestamp in case it has now changed + vik_treeview_item_set_timestamp ( vtl->vl.vt, &vtl->vl.iter, trw_layer_get_timestamp(vtl) ); vik_layer_emit_update( VIK_LAYER(vtl) ); } @@ -7511,11 +7864,13 @@ static gboolean is_valid_geocache_name ( gchar *str ) return len >= 3 && len <= 7 && str[0] == 'G' && str[1] == 'C' && isalnum(str[2]) && (len < 4 || isalnum(str[3])) && (len < 5 || isalnum(str[4])) && (len < 6 || isalnum(str[5])) && (len < 7 || isalnum(str[6])); } +#ifndef WINDOWS static void trw_layer_track_use_with_filter ( menu_array_sublayer values ) { VikTrack *trk = g_hash_table_lookup ( VIK_TRW_LAYER(values[MA_VTL])->tracks, values[MA_SUBLAYER_ID] ); a_acquire_set_filter_track ( trk ); } +#endif #ifdef VIK_CONFIG_GOOGLE static gboolean is_valid_google_route ( VikTrwLayer *vtl, const gpointer track_id ) @@ -7528,7 +7883,7 @@ static void trw_layer_google_route_webpage ( menu_array_sublayer values ) { VikTrack *tr = g_hash_table_lookup ( VIK_TRW_LAYER(values[MA_VTL])->routes, values[MA_SUBLAYER_ID] ); if ( tr ) { - gchar *escaped = uri_escape ( tr->comment ); + gchar *escaped = g_uri_escape_string ( tr->comment, NULL, TRUE ); gchar *webpage = g_strdup_printf("http://maps.google.com/maps?f=q&hl=en&q=%s", escaped ); open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(values[MA_VTL])), webpage); g_free ( escaped ); @@ -7926,6 +8281,18 @@ static gboolean trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *men g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_sort_order_z2a), pass_along ); gtk_menu_shell_append ( GTK_MENU_SHELL(submenu_sort), item ); gtk_widget_show ( item ); + + item = gtk_image_menu_item_new_with_mnemonic ( _("Date Ascending") ); + gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_SORT_ASCENDING, GTK_ICON_SIZE_MENU) ); + g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_sort_order_timestamp_ascend), pass_along ); + gtk_menu_shell_append ( GTK_MENU_SHELL(submenu_sort), item ); + gtk_widget_show ( item ); + + item = gtk_image_menu_item_new_with_mnemonic ( _("Date Descending") ); + gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_SORT_DESCENDING, GTK_ICON_SIZE_MENU) ); + g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_sort_order_timestamp_descend), pass_along ); + gtk_menu_shell_append ( GTK_MENU_SHELL(submenu_sort), item ); + gtk_widget_show ( item ); } GtkWidget *upload_submenu = gtk_menu_new (); @@ -8190,13 +8557,19 @@ static gboolean trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *men gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item ); gtk_widget_show ( item ); - // Routes don't have timestamps - so this is only available for tracks + // Routes don't have timestamps - so these are only available for tracks if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) { item = gtk_image_menu_item_new_with_mnemonic ( _("_Anonymize Times") ); g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_anonymize_times), pass_along ); gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item ); gtk_widget_set_tooltip_text (item, _("Shift timestamps to a relative offset from 1901-01-01")); gtk_widget_show ( item ); + + item = gtk_image_menu_item_new_with_mnemonic ( _("_Interpolate Times") ); + g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_interpolate_times), pass_along ); + gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item ); + gtk_widget_set_tooltip_text (item, _("Reset trackpoint timestamps between the first and last points such that track is traveled at equal speed")); + gtk_widget_show ( item ); } if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) @@ -8270,17 +8643,46 @@ static gboolean trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *men } } - // Only made available if a suitable program is installed - if ( have_diary_program ) { - if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) { - item = gtk_image_menu_item_new_with_mnemonic ( _("Diar_y") ); + GtkWidget *external_submenu = create_external_submenu ( menu ); + + // These are only made available if a suitable program is installed + if ( (have_astro_program || have_diary_program) && + (subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT) ) { + + if ( have_diary_program ) { + item = gtk_image_menu_item_new_with_mnemonic ( _("_Diary") ); gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_SPELL_CHECK, GTK_ICON_SIZE_MENU) ); g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_diary), pass_along ); - gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item ); + gtk_menu_shell_append ( GTK_MENU_SHELL(external_submenu), item ); + gtk_widget_set_tooltip_text (item, _("Open diary program at this date")); + gtk_widget_show ( item ); + } + + if ( have_astro_program ) { + item = gtk_image_menu_item_new_with_mnemonic ( _("_Astronomy") ); + g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_astro), pass_along ); + gtk_menu_shell_append ( GTK_MENU_SHELL(external_submenu), item ); + gtk_widget_set_tooltip_text (item, _("Open astronomy program at this date and location")); gtk_widget_show ( item ); } } + if ( l->current_tpl || l->current_wp ) { + // For the selected point + VikCoord *vc; + if ( l->current_tpl ) + vc = &(VIK_TRACKPOINT(l->current_tpl->data)->coord); + else + vc = &(l->current_wp->coord); + vik_ext_tools_add_menu_items_to_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(l)), GTK_MENU (external_submenu), vc ); + } + else { + // Otherwise for the selected sublayer + // TODO: Should use selected items centre - rather than implicitly using the current viewport + vik_ext_tools_add_menu_items_to_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(l)), GTK_MENU (external_submenu), NULL ); + } + + #ifdef VIK_CONFIG_GOOGLE if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE && is_valid_google_route ( l, sublayer ) ) { @@ -8304,11 +8706,14 @@ static gboolean trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *men gtk_widget_show ( item ); #endif + // Currently filter with functions all use shellcommands and thus don't work in Windows +#ifndef WINDOWS item = gtk_image_menu_item_new_with_mnemonic ( _("Use with _Filter") ); 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_track_use_with_filter), pass_along ); gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item ); gtk_widget_show ( item ); +#endif /* ATM This function is only available via the layers panel, due to needing a vlp */ if ( vlp ) { @@ -8471,6 +8876,16 @@ static void trw_layer_cancel_current_tp ( VikTrwLayer *vtl, gboolean destroy ) } } +static void my_tpwin_set_tp ( VikTrwLayer *vtl ) +{ + VikTrack *trk = vtl->current_tp_track; + VikCoord vc; + // Notional center of a track is simply an average of the bounding box extremities + struct LatLon center = { (trk->bbox.north+trk->bbox.south)/2, (trk->bbox.east+trk->bbox.west)/2 }; + vik_coord_load_from_latlon ( &vc, vtl->coord_mode, ¢er ); + vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, trk->name, vtl->current_tp_track->is_route ); +} + static void trw_layer_tpwin_response ( VikTrwLayer *vtl, gint response ) { g_assert ( vtl->tpwin != NULL ); @@ -8483,7 +8898,7 @@ 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, 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 ); + my_tpwin_set_tp ( vtl ); } else if ( response == VIK_TRW_LAYER_TPWIN_DELETE ) { @@ -8497,20 +8912,24 @@ static void trw_layer_tpwin_response ( VikTrwLayer *vtl, gint response ) if ( vtl->current_tpl ) // Reset dialog with the available adjacent trackpoint - vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name ); + my_tpwin_set_tp ( vtl ); vik_layer_emit_update(VIK_LAYER(vtl)); } else if ( response == VIK_TRW_LAYER_TPWIN_FORWARD && vtl->current_tpl->next ) { - if ( vtl->current_tp_track ) - vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl = vtl->current_tpl->next, vtl->current_tp_track->name ); + if ( vtl->current_tp_track ) { + vtl->current_tpl = vtl->current_tpl->next; + my_tpwin_set_tp ( vtl ); + } vik_layer_emit_update(VIK_LAYER(vtl)); /* TODO longone: either move or only update if tp is inside drawing window */ } else if ( response == VIK_TRW_LAYER_TPWIN_BACK && vtl->current_tpl->prev ) { - if ( vtl->current_tp_track ) - vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl = vtl->current_tpl->prev, vtl->current_tp_track->name ); + if ( vtl->current_tp_track ) { + vtl->current_tpl = vtl->current_tpl->prev; + my_tpwin_set_tp ( vtl ); + } vik_layer_emit_update(VIK_LAYER(vtl)); } else if ( response == VIK_TRW_LAYER_TPWIN_INSERT && vtl->current_tpl->next ) @@ -8637,7 +9056,7 @@ static void trw_layer_tpwin_init ( VikTrwLayer *vtl ) if ( vtl->current_tpl ) if ( vtl->current_tp_track ) - vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name ); + my_tpwin_set_tp ( vtl ); /* set layer name and TP data */ } @@ -8651,7 +9070,7 @@ typedef struct { gint x, y; gint closest_x, closest_y; gboolean draw_images; - gpointer *closest_wp_id; + gpointer closest_wp_id; VikWaypoint *closest_wp; VikViewport *vvp; } WPSearchParams; @@ -8766,7 +9185,7 @@ static void marker_moveto ( tool_ed_t *t, gint x, gint y ); static void marker_end_move ( tool_ed_t *t ); // -static gboolean trw_layer_select_move ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* t ) +static gboolean trw_layer_select_move ( VikTrwLayer *vtl, GdkEventMotion *event, VikViewport *vvp, tool_ed_t* t ) { if ( t->holding ) { VikCoord new_coord; @@ -8806,6 +9225,11 @@ static gboolean trw_layer_select_release ( VikTrwLayer *vtl, GdkEventButton *eve { if ( t->holding && event->button == 1 ) { + // Prevent accidental (small) shifts when specific movement has not been requested + // (as the click release has occurred within the click object detection area) + if ( !t->moving ) + return FALSE; + VikCoord new_coord; vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord ); @@ -8845,7 +9269,7 @@ static gboolean trw_layer_select_release ( VikTrwLayer *vtl, GdkEventButton *eve if ( vtl->tpwin ) if ( vtl->current_tp_track ) - vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name ); + my_tpwin_set_tp ( vtl ); // NB don't reset the selected trackpoint, thus ensuring it's still in the tpwin } } @@ -8960,7 +9384,7 @@ static gboolean trw_layer_select_click ( VikTrwLayer *vtl, GdkEventButton *event 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 ); + my_tpwin_set_tp ( vtl ); vik_layer_emit_update ( VIK_LAYER(vtl) ); return TRUE; @@ -8995,7 +9419,7 @@ static gboolean trw_layer_select_click ( VikTrwLayer *vtl, GdkEventButton *event 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 ); + my_tpwin_set_tp ( vtl ); vik_layer_emit_update ( VIK_LAYER(vtl) ); return TRUE; @@ -9041,7 +9465,7 @@ static gboolean trw_layer_show_selected_viewport_menu ( VikTrwLayer *vtl, GdkEve udataU.trk = track; udataU.uuid = NULL; - gpointer *trkf; + gpointer trkf; if ( track->is_route ) trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udataU ); else @@ -9084,7 +9508,7 @@ static gboolean trw_layer_show_selected_viewport_menu ( VikTrwLayer *vtl, GdkEve udata.wp = waypoint; udata.uuid = NULL; - gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata ); + gpointer wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata ); if ( wpf && udata.uuid ) { GtkTreeIter *iter = g_hash_table_lookup ( vtl->waypoints_iters, udata.uuid ); @@ -9112,10 +9536,8 @@ static gboolean tool_sync_done = TRUE; static gboolean tool_sync(gpointer data) { VikViewport *vvp = data; - gdk_threads_enter(); vik_viewport_sync(vvp); tool_sync_done = TRUE; - gdk_threads_leave(); return FALSE; } @@ -9128,6 +9550,7 @@ static void marker_begin_move ( tool_ed_t *t, gint x, gint y ) vik_viewport_sync(t->vvp); t->oldx = x; t->oldy = y; + t->moving = FALSE; } static void marker_moveto ( tool_ed_t *t, gint x, gint y ) @@ -9137,6 +9560,7 @@ static void marker_moveto ( tool_ed_t *t, gint x, gint y ) vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, x-3, y-3, 6, 6 ); t->oldx = x; t->oldy = y; + t->moving = TRUE; if (tool_sync_done) { g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, tool_sync, vvp, NULL); @@ -9149,6 +9573,7 @@ static void marker_end_move ( tool_ed_t *t ) vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 ); g_object_unref ( t->gc ); t->holding = FALSE; + t->moving = FALSE; } /*** Edit waypoint ****/ @@ -9188,8 +9613,8 @@ static gboolean tool_edit_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *eve gint x, y; vik_viewport_coord_to_screen ( vvp, &(vtl->current_wp->coord), &x, &y ); - if ( abs(x - event->x) <= WAYPOINT_SIZE_APPROX && - abs(y - event->y) <= WAYPOINT_SIZE_APPROX ) + if ( abs(x - (int)round(event->x)) <= WAYPOINT_SIZE_APPROX && + abs(y - (int)round(event->y)) <= WAYPOINT_SIZE_APPROX ) { if ( event->button == 3 ) vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */ @@ -9354,13 +9779,11 @@ static gboolean draw_sync ( gpointer data ) // normally because another update has taken precedent such as panning the display // which means this pixmap is no longer valid if ( ds->vtl->draw_sync_do ) { - gdk_threads_enter(); gdk_draw_drawable (ds->drawable, ds->gc, ds->pixmap, 0, 0, 0, 0, -1, -1); ds->vtl->draw_sync_done = TRUE; - gdk_threads_leave(); } g_free ( ds ); return FALSE; @@ -9382,6 +9805,15 @@ static gchar* distance_string (gdouble distance) g_sprintf(str, "%d miles", (int)VIK_METERS_TO_MILES(distance)); } break; + case VIK_UNITS_DISTANCE_NAUTICAL_MILES: + if (distance >= VIK_NAUTICAL_MILES_TO_METERS(1) && distance < VIK_NAUTICAL_MILES_TO_METERS(100)) { + g_sprintf(str, "%3.2f NM", VIK_METERS_TO_NAUTICAL_MILES(distance)); + } else if (distance < VIK_NAUTICAL_MILES_TO_METERS(1)) { + g_sprintf(str, "%d yards", (int)(distance*1.0936133)); + } else { + g_sprintf(str, "%d NM", (int)VIK_METERS_TO_NAUTICAL_MILES(distance)); + } + break; default: // VIK_UNITS_DISTANCE_KILOMETRES if (distance >= 1000 && distance < 100000) { @@ -9582,6 +10014,13 @@ static void undo_trackpoint_add ( VikTrwLayer *vtl ) static gboolean tool_new_track_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp ) { if ( vtl->current_track && event->keyval == GDK_Escape ) { + // Bin track if only one point as it's not very useful + if ( vik_track_get_tp_count(vtl->current_track) == 1 ) { + if ( vtl->current_track->is_route ) + vik_trw_layer_delete_route ( vtl, vtl->current_track ); + else + vik_trw_layer_delete_track ( vtl, vtl->current_track ); + } vtl->current_track = NULL; vik_layer_emit_update ( VIK_LAYER(vtl) ); return TRUE; @@ -9676,13 +10115,13 @@ static gboolean tool_new_track_click ( VikTrwLayer *vtl, GdkEventButton *event, if ( event->button == 1 && ( ! vtl->current_track || (vtl->current_track && vtl->current_track->is_route ) )) { gchar *name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, _("Track")); - if ( ( name = a_dialog_new_track ( VIK_GTK_WINDOW_FROM_LAYER(vtl), name, FALSE ) ) ) - { - new_track_create_common ( vtl, name ); - g_free ( name ); + if ( a_vik_get_ask_for_create_track_name() ) { + name = a_dialog_new_track ( VIK_GTK_WINDOW_FROM_LAYER(vtl), name, FALSE ); + if ( !name ) + return FALSE; } - else - return TRUE; + new_track_create_common ( vtl, name ); + g_free ( name ); } return tool_new_track_or_route_click ( vtl, event, vvp ); } @@ -9713,12 +10152,13 @@ static gboolean tool_new_route_click ( VikTrwLayer *vtl, GdkEventButton *event, (vtl->current_track && !vtl->current_track->is_route ) ) ) { gchar *name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, _("Route")); - if ( ( name = a_dialog_new_track ( VIK_GTK_WINDOW_FROM_LAYER(vtl), name, TRUE ) ) ) { - new_route_create_common ( vtl, name ); - g_free ( name ); + if ( a_vik_get_ask_for_create_track_name() ) { + name = a_dialog_new_track ( VIK_GTK_WINDOW_FROM_LAYER(vtl), name, TRUE ); + if ( !name ) + return FALSE; } - else - return TRUE; + new_route_create_common ( vtl, name ); + g_free ( name ); } return tool_new_track_or_route_click ( vtl, event, vvp ); } @@ -9736,9 +10176,10 @@ static gboolean tool_new_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *even if (!vtl || vtl->vl.type != VIK_LAYER_TRW) return FALSE; vik_viewport_screen_to_coord ( vvp, event->x, event->y, &coord ); - if (vik_trw_layer_new_waypoint ( vtl, VIK_GTK_WINDOW_FROM_LAYER(vtl), &coord ) && VIK_LAYER(vtl)->visible) { + if ( vik_trw_layer_new_waypoint (vtl, VIK_GTK_WINDOW_FROM_LAYER(vtl), &coord) ) { trw_layer_calculate_bounds_waypoints ( vtl ); - vik_layer_emit_update ( VIK_LAYER(vtl) ); + if ( VIK_LAYER(vtl)->visible ) + vik_layer_emit_update ( VIK_LAYER(vtl) ); } return TRUE; } @@ -9759,22 +10200,24 @@ static void tool_edit_trackpoint_destroy ( tool_ed_t *t ) g_free ( t ); } +/** + * tool_edit_trackpoint_click: + * + * On 'initial' click: search for the nearest trackpoint or routepoint and store it as the current trackpoint + * Then update the viewport, statusbar and edit dialog to draw the point as being selected and it's information. + * On subsequent clicks: (as the current trackpoint is defined) and the click is very near the same point + * then initiate the move operation to drag the point to a new destination. + * NB The current trackpoint will get reset elsewhere. + */ static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data ) { tool_ed_t *t = data; VikViewport *vvp = t->vvp; TPSearchParams params; - /* OUTDATED DOCUMENTATION: - find 5 pixel range on each side. then put these UTM, and a pointer - to the winning track name (and maybe the winning track itself), and a - pointer to the winning trackpoint, inside an array or struct. pass - this along, do a foreach on the tracks which will do a foreach on the - trackpoints. */ params.vvp = vvp; params.x = event->x; params.y = event->y; params.closest_track_id = NULL; - /* TODO: should get track listitem so we can break it up, make a new track, mess it up, all that. */ params.closest_tp = NULL; params.closest_tpl = NULL; vik_viewport_get_min_max_lat_lon ( vvp, &(params.bbox.south), &(params.bbox.north), &(params.bbox.west), &(params.bbox.east) ); @@ -9785,7 +10228,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 || !vtl->routes_visible ) + if ( !vtl->vl.visible || !(vtl->tracks_visible || vtl->routes_visible) ) return FALSE; if ( vtl->current_tpl ) @@ -9793,6 +10236,8 @@ static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *e /* first check if it is within range of prev. tp. and if current_tp track is shown. (if it is, we are moving that trackpoint.) */ VikTrackpoint *tp = VIK_TRACKPOINT(vtl->current_tpl->data); VikTrack *current_tr = VIK_TRACK(g_hash_table_lookup(vtl->tracks, vtl->current_tp_id)); + if ( !current_tr ) + current_tr = VIK_TRACK(g_hash_table_lookup(vtl->routes, vtl->current_tp_id)); if ( !current_tr ) return FALSE; @@ -9800,8 +10245,8 @@ static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *e vik_viewport_coord_to_screen ( vvp, &(tp->coord), &x, &y ); if ( current_tr->visible && - abs(x - event->x) < TRACKPOINT_SIZE_APPROX && - abs(y - event->y) < TRACKPOINT_SIZE_APPROX ) { + abs(x - (int)round(event->x)) < TRACKPOINT_SIZE_APPROX && + abs(y - (int)round(event->y)) < TRACKPOINT_SIZE_APPROX ) { marker_begin_move ( t, event->x, event->y ); return TRUE; } @@ -9904,7 +10349,8 @@ static gboolean tool_edit_trackpoint_release ( VikTrwLayer *vtl, GdkEventButton /* diff dist is diff from orig */ if ( vtl->tpwin ) - vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name ); + if ( vtl->current_tp_track ) + my_tpwin_set_tp ( vtl ); vik_layer_emit_update ( VIK_LAYER(vtl) ); return TRUE; @@ -10140,7 +10586,7 @@ static void thumbnail_create_thread_free ( thumbnail_create_thread_data *tctd ) g_free ( tctd ); } -void trw_layer_verify_thumbnails ( VikTrwLayer *vtl, GtkWidget *vp ) +void trw_layer_verify_thumbnails ( VikTrwLayer *vtl ) { if ( ! vtl->has_verified_thumbnails ) { @@ -10153,7 +10599,8 @@ void trw_layer_verify_thumbnails ( VikTrwLayer *vtl, GtkWidget *vp ) thumbnail_create_thread_data *tctd = g_malloc ( sizeof(thumbnail_create_thread_data) ); tctd->vtl = vtl; tctd->pics = pics; - a_background_thread ( VIK_GTK_WINDOW_FROM_LAYER(vtl), + a_background_thread ( BACKGROUND_POOL_LOCAL, + VIK_GTK_WINDOW_FROM_LAYER(vtl), tmp, (vik_thr_func) create_thumbnails_thread, tctd, @@ -10279,7 +10726,7 @@ static void trw_layer_calculate_bounds_track ( gpointer id, VikTrack *trk ) vik_track_calculate_bounds ( trk ); } -static void trw_layer_calculate_bounds_tracks ( VikTrwLayer *vtl ) +void trw_layer_calculate_bounds_tracks ( VikTrwLayer *vtl ) { g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_calculate_bounds_track, NULL ); g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_calculate_bounds_track, NULL ); @@ -10301,10 +10748,78 @@ static void trw_layer_sort_all ( VikTrwLayer *vtl ) vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), vtl->wp_sort_order ); } -static void trw_layer_post_read ( VikTrwLayer *vtl, GtkWidget *vvp, gboolean from_file ) +/** + * Get the earliest timestamp available from all tracks + */ +static time_t trw_layer_get_timestamp_tracks ( VikTrwLayer *vtl ) +{ + time_t timestamp = 0; + GList *gl = g_hash_table_get_values ( vtl->tracks ); + gl = g_list_sort ( gl, vik_track_compare_timestamp ); + gl = g_list_first ( gl ); + + if ( gl ) { + // Only need to check the first track as they have been sorted by time + VikTrack *trk = (VikTrack*)gl->data; + // Assume trackpoints already sorted by time + VikTrackpoint *tpt = vik_track_get_tp_first(trk); + if ( tpt && tpt->has_timestamp ) { + timestamp = tpt->timestamp; + } + g_list_free ( gl ); + } + return timestamp; +} + +/** + * Get the earliest timestamp available from all waypoints + */ +static time_t trw_layer_get_timestamp_waypoints ( VikTrwLayer *vtl ) +{ + time_t timestamp = 0; + GList *gl = g_hash_table_get_values ( vtl->waypoints ); + GList *iter; + for (iter = g_list_first (gl); iter != NULL; iter = g_list_next (iter)) { + VikWaypoint *wpt = (VikWaypoint*)iter->data; + if ( wpt->has_timestamp ) { + // When timestamp not set yet - use the first value encountered + if ( timestamp == 0 ) + timestamp = wpt->timestamp; + else if ( timestamp > wpt->timestamp ) + timestamp = wpt->timestamp; + } + } + g_list_free ( gl ); + + return timestamp; +} + +/** + * Get the earliest timestamp available for this layer + */ +static time_t trw_layer_get_timestamp ( VikTrwLayer *vtl ) +{ + time_t timestamp_tracks = trw_layer_get_timestamp_tracks ( vtl ); + time_t timestamp_waypoints = trw_layer_get_timestamp_waypoints ( vtl ); + // NB routes don't have timestamps - hence they are not considered + + if ( !timestamp_tracks && !timestamp_waypoints ) { + // Fallback to get time from the metadata when no other timestamps available + GTimeVal gtv; + if ( vtl->metadata && vtl->metadata->timestamp && g_time_val_from_iso8601 ( vtl->metadata->timestamp, >v ) ) + return gtv.tv_sec; + } + if ( timestamp_tracks && !timestamp_waypoints ) + return timestamp_tracks; + if ( timestamp_tracks && timestamp_waypoints && (timestamp_tracks < timestamp_waypoints) ) + return timestamp_tracks; + return timestamp_waypoints; +} + +static void trw_layer_post_read ( VikTrwLayer *vtl, VikViewport *vvp, gboolean from_file ) { if ( VIK_LAYER(vtl)->realized ) - trw_layer_verify_thumbnails ( vtl, vvp ); + trw_layer_verify_thumbnails ( vtl ); trw_layer_track_alloc_colors ( vtl ); trw_layer_calculate_bounds_waypoints ( vtl ); @@ -10328,47 +10843,13 @@ static void trw_layer_post_read ( VikTrwLayer *vtl, GtkWidget *vvp, gboolean fro } if ( need_to_set_time ) { - // Could rewrite this as a general get first time of a TRW Layer function GTimeVal timestamp; timestamp.tv_usec = 0; - gboolean has_timestamp = FALSE; - - GList *gl = NULL; - gl = g_hash_table_get_values ( vtl->tracks ); - gl = g_list_sort ( gl, vik_track_compare_timestamp ); - gl = g_list_first ( gl ); - - // Check times of tracks - if ( gl ) { - // Only need to check the first track as they have been sorted by time - VikTrack *trk = (VikTrack*)gl->data; - // Assume trackpoints already sorted by time - VikTrackpoint *tpt = vik_track_get_tp_first(trk); - if ( tpt && tpt->has_timestamp ) { - timestamp.tv_sec = tpt->timestamp; - has_timestamp = TRUE; - } - g_list_free ( gl ); - } + timestamp.tv_sec = trw_layer_get_timestamp ( vtl ); - if ( !has_timestamp ) { - // 'Last' resort - current time - // Get before waypoint tests - so that if a waypoint time value (in the past) is found it should be used + // No time found - so use 'now' for the metadata time + if ( timestamp.tv_sec == 0 ) { g_get_current_time ( ×tamp ); - - // Check times of waypoints - gl = g_hash_table_get_values ( vtl->waypoints ); - GList *iter; - for (iter = g_list_first (gl); iter != NULL; iter = g_list_next (iter)) { - VikWaypoint *wpt = (VikWaypoint*)iter->data; - if ( wpt->has_timestamp ) { - if ( timestamp.tv_sec > wpt->timestamp ) { - timestamp.tv_sec = wpt->timestamp; - has_timestamp = TRUE; - } - } - } - g_list_free ( gl ); } vtl->metadata->timestamp = g_time_val_to_iso8601 ( ×tamp ); @@ -10609,8 +11090,8 @@ static void trw_layer_download_map_along_track_cb ( menu_array_sublayer values ) } // Convert from list of vmls to list of names. Allowing the user to select one of them - gchar **map_names = g_malloc(1 + num_maps * sizeof(gpointer)); - VikMapsLayer **map_layers = g_malloc(1 + num_maps * sizeof(gpointer)); + gchar **map_names = g_malloc_n(1 + num_maps, sizeof(gpointer)); + VikMapsLayer **map_layers = g_malloc_n(1 + num_maps, sizeof(gpointer)); gchar **np = map_names; VikMapsLayer **lp = map_layers; @@ -10663,7 +11144,7 @@ static gint highest_wp_number_name_to_number(const gchar *name) { static void highest_wp_number_reset(VikTrwLayer *vtl) { - vtl->highest_wp_number = -1; + vtl->highest_wp_number = 0; } static void highest_wp_number_add_wp(VikTrwLayer *vtl, const gchar *new_wp_name)