X-Git-Url: https://git.street.me.uk/andy/viking.git/blobdiff_plain/17720e63b8f29a0909212c9cd13cc71c848c9097..93dd774414b1ebd932126f6a2e68d17e92cf7254:/src/viktrwlayer.c diff --git a/src/viktrwlayer.c b/src/viktrwlayer.c index 7c9d81a3..7a1aab78 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,8 +59,9 @@ #include "acquire.h" #include "datasources.h" #include "datasource_gps.h" +#include "vikexttools.h" #include "vikexttool_datasources.h" -#include "util.h" +#include "ui_util.h" #include "vikutils.h" #include "vikrouting.h" @@ -177,7 +180,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; @@ -202,10 +204,8 @@ struct _VikTrwLayer { /* route finder tool */ gboolean route_finder_started; - VikCoord route_finder_coord; gboolean route_finder_check_added_track; VikTrack *route_finder_added_track; - VikTrack *route_finder_current_track; gboolean route_finder_append; gboolean drawlabels; @@ -250,6 +250,7 @@ struct DrawingParams { gboolean one_zone, lat_lon; gdouble ce1, ce2, cn1, cn2; LatLonBBox bbox; + gboolean highlight; }; static gboolean trw_layer_delete_waypoint ( VikTrwLayer *vtl, VikWaypoint *wp ); @@ -362,13 +363,14 @@ static void trw_layer_waypoint_webpage ( menu_array_sublayer values ); static void trw_layer_realize_waypoint ( gpointer id, VikWaypoint *wp, gpointer pass_along[5] ); static void trw_layer_realize_track ( gpointer id, VikTrack *track, gpointer pass_along[5] ); -static void init_drawing_params ( struct DrawingParams *dp, VikTrwLayer *vtl, VikViewport *vp ); static void trw_layer_insert_tp_beside_current_tp ( VikTrwLayer *vtl, gboolean ); 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 ); @@ -390,8 +392,9 @@ static void tool_new_track_release ( VikTrwLayer *vtl, GdkEventButton *event, Vi static gboolean tool_new_track_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp ); static gpointer tool_new_waypoint_create ( VikWindow *vw, VikViewport *vvp); static gboolean tool_new_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp ); -static gpointer tool_route_finder_create ( VikWindow *vw, VikViewport *vvp); -static gboolean tool_route_finder_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp ); +static gpointer tool_extended_route_finder_create ( VikWindow *vw, VikViewport *vvp); +static gboolean tool_extended_route_finder_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp ); +static gboolean tool_extended_route_finder_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp ); static void cached_pixbuf_free ( CachedPixbuf *cp ); static gint cached_pixbuf_cmp ( CachedPixbuf *cp, const gchar *name ); @@ -418,7 +421,7 @@ static VikToolInterface trw_layer_tools[] = { (VikToolConstructorFunc) tool_new_waypoint_create, NULL, NULL, NULL, (VikToolMouseFunc) tool_new_waypoint_click, NULL, NULL, (VikToolKeyFunc) NULL, FALSE, - GDK_CURSOR_IS_PIXMAP, &cursor_addwp_pixbuf }, + GDK_CURSOR_IS_PIXMAP, &cursor_addwp_pixbuf, NULL }, { { "CreateTrack", "vik-icon-Create Track", N_("Create _Track"), "T", N_("Create Track"), 0 }, (VikToolConstructorFunc) tool_new_track_create, NULL, NULL, NULL, @@ -427,7 +430,7 @@ static VikToolInterface trw_layer_tools[] = { (VikToolMouseFunc) tool_new_track_release, (VikToolKeyFunc) tool_new_track_key_press, TRUE, // Still need to handle clicks when in PAN mode to disable the potential trackpoint drawing - GDK_CURSOR_IS_PIXMAP, &cursor_addtr_pixbuf }, + GDK_CURSOR_IS_PIXMAP, &cursor_addtr_pixbuf, NULL }, { { "CreateRoute", "vik-icon-Create Route", N_("Create _Route"), "B", N_("Create Route"), 0 }, (VikToolConstructorFunc) tool_new_route_create, NULL, NULL, NULL, @@ -436,7 +439,16 @@ static VikToolInterface trw_layer_tools[] = { (VikToolMouseFunc) tool_new_track_release, // -> Reuse these track methods on a route (VikToolKeyFunc) tool_new_track_key_press, // -/# TRUE, // Still need to handle clicks when in PAN mode to disable the potential trackpoint drawing - GDK_CURSOR_IS_PIXMAP, &cursor_new_route_pixbuf }, + GDK_CURSOR_IS_PIXMAP, &cursor_new_route_pixbuf, NULL }, + + { { "ExtendedRouteFinder", "vik-icon-Route Finder", N_("Route _Finder"), "F", N_("Route Finder"), 0 }, + (VikToolConstructorFunc) tool_extended_route_finder_create, NULL, NULL, NULL, + (VikToolMouseFunc) tool_extended_route_finder_click, + (VikToolMouseMoveFunc) tool_new_track_move, // -\# + (VikToolMouseFunc) tool_new_track_release, // -> Reuse these track methods on a route + (VikToolKeyFunc) tool_extended_route_finder_key_press, + TRUE, // Still need to handle clicks when in PAN mode to disable the potential trackpoint drawing + GDK_CURSOR_IS_PIXMAP, &cursor_route_finder_pixbuf, NULL }, { { "EditWaypoint", "vik-icon-Edit Waypoint", N_("_Edit Waypoint"), "E", N_("Edit Waypoint"), 0 }, (VikToolConstructorFunc) tool_edit_waypoint_create, @@ -446,7 +458,7 @@ static VikToolInterface trw_layer_tools[] = { (VikToolMouseMoveFunc) tool_edit_waypoint_move, (VikToolMouseFunc) tool_edit_waypoint_release, (VikToolKeyFunc) NULL, FALSE, - GDK_CURSOR_IS_PIXMAP, &cursor_edwp_pixbuf }, + GDK_CURSOR_IS_PIXMAP, &cursor_edwp_pixbuf, NULL }, { { "EditTrackpoint", "vik-icon-Edit Trackpoint", N_("Edit Trac_kpoint"), "K", N_("Edit Trackpoint"), 0 }, (VikToolConstructorFunc) tool_edit_trackpoint_create, @@ -456,29 +468,24 @@ static VikToolInterface trw_layer_tools[] = { (VikToolMouseMoveFunc) tool_edit_trackpoint_move, (VikToolMouseFunc) tool_edit_trackpoint_release, (VikToolKeyFunc) NULL, FALSE, - GDK_CURSOR_IS_PIXMAP, &cursor_edtr_pixbuf }, + GDK_CURSOR_IS_PIXMAP, &cursor_edtr_pixbuf, NULL }, { { "ShowPicture", "vik-icon-Show Picture", N_("Show P_icture"), "I", N_("Show Picture"), 0 }, (VikToolConstructorFunc) tool_show_picture_create, NULL, NULL, NULL, (VikToolMouseFunc) tool_show_picture_click, NULL, NULL, (VikToolKeyFunc) NULL, FALSE, - GDK_CURSOR_IS_PIXMAP, &cursor_showpic_pixbuf }, + GDK_CURSOR_IS_PIXMAP, &cursor_showpic_pixbuf, NULL }, - { { "RouteFinder", "vik-icon-Route Finder", N_("Route _Finder"), "F", N_("Route Finder"), 0 }, - (VikToolConstructorFunc) tool_route_finder_create, NULL, NULL, NULL, - (VikToolMouseFunc) tool_route_finder_click, NULL, NULL, (VikToolKeyFunc) NULL, - FALSE, - GDK_CURSOR_IS_PIXMAP, &cursor_route_finder_pixbuf }, }; enum { TOOL_CREATE_WAYPOINT=0, TOOL_CREATE_TRACK, TOOL_CREATE_ROUTE, + TOOL_ROUTE_FINDER, TOOL_EDIT_WAYPOINT, TOOL_EDIT_TRACKPOINT, TOOL_SHOW_PICTURE, - TOOL_ROUTE_FINDER, NUM_TOOLS }; @@ -528,6 +535,8 @@ static gchar* params_sort_order[] = { N_("None"), N_("Name Ascending"), N_("Name Descending"), + N_("Date Ascending"), + N_("Date Descending"), NULL }; @@ -684,6 +693,7 @@ static void trw_layer_post_read ( VikTrwLayer *vtl, GtkWidget *vvp, gboolean fro 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 ); @@ -705,7 +715,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 */ @@ -734,6 +744,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, @@ -771,6 +782,91 @@ VikLayerInterface vik_trw_layer_interface = { (VikLayerFuncSelectedViewportMenu) trw_layer_show_selected_viewport_menu, }; +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 ( ! 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 + 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 ( 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 ( 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]; + } + } + g_strfreev ( tokens ); + } + 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; + } +} + GType vik_trw_layer_get_type () { static GType vtl_type = 0; @@ -782,7 +878,7 @@ GType vik_trw_layer_get_type () sizeof (VikTrwLayerClass), NULL, /* base_init */ NULL, /* base_finalize */ - NULL, /* class init */ + (GClassInitFunc) vik_trwlayer_class_init, /* class init */ NULL, /* class_finalize */ NULL, /* class_data */ sizeof (VikTrwLayer), @@ -791,7 +887,6 @@ GType vik_trw_layer_get_type () }; vtl_type = g_type_register_static ( VIK_LAYER_TYPE, "VikTrwLayer", &vtl_info, 0 ); } - return vtl_type; } @@ -822,8 +917,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 ) @@ -884,7 +979,7 @@ gboolean vik_trw_layer_find_date ( VikTrwLayer *vtl, const gchar *date_str, VikC vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup (vtl->tracks_iters, df.trk_id), TRUE ); } else if ( df.wpt ) { - vik_viewport_set_center_coord ( vvp, &(df.wpt->coord) ); + vik_viewport_set_center_coord ( vvp, &(df.wpt->coord), TRUE ); vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup (vtl->waypoints_iters, df.wpt_id), TRUE ); } vik_layer_emit_update ( VIK_LAYER(vtl) ); @@ -935,7 +1030,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; @@ -1078,6 +1173,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 ) @@ -1149,12 +1250,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 ) ); @@ -1402,7 +1508,7 @@ static void trw_layer_marshall( VikTrwLayer *vtl, guint8 **data, gint *len ) static VikTrwLayer *trw_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp ) { - VikTrwLayer *vtl = VIK_TRW_LAYER(vik_layer_create ( VIK_LAYER_TRW, vvp, NULL, FALSE )); + VikTrwLayer *vtl = VIK_TRW_LAYER(vik_layer_create ( VIK_LAYER_TRW, vvp, FALSE )); gint pl; gint consumed_length; @@ -1431,23 +1537,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; @@ -1523,6 +1633,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; @@ -1571,14 +1682,14 @@ 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 ) +static void init_drawing_params ( struct DrawingParams *dp, VikTrwLayer *vtl, VikViewport *vp, gboolean highlight ) { dp->vtl = vtl; dp->vp = vp; + dp->highlight = highlight; dp->vw = (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl); dp->xmpp = vik_viewport_get_xmpp ( vp ); dp->ympp = vik_viewport_get_ympp ( vp ); @@ -1690,6 +1801,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; @@ -1735,6 +1849,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; @@ -1760,6 +1877,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") ); @@ -1910,6 +2030,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 ) @@ -1952,18 +2106,11 @@ static void trw_layer_draw_track ( const gpointer id, VikTrack *track, struct Dr if ( track == dp->vtl->current_track ) main_gc = dp->vtl->current_track_gc; else { - if ( vik_viewport_get_draw_highlight ( dp->vp ) ) { - /* Draw all tracks of the layer in special colour */ - /* if track is member of selected layer or is the current selected track - then draw in the highlight colour. - NB this supercedes the drawmode */ - if ( ( dp->vtl == vik_window_get_selected_trw_layer ( dp->vw ) ) || - ( !track->is_route && ( dp->vtl->tracks == vik_window_get_selected_tracks ( dp->vw ) ) ) || - ( track->is_route && ( dp->vtl->routes == vik_window_get_selected_tracks ( dp->vw ) ) ) || - ( track == vik_window_get_selected_track ( dp->vw ) ) ) { - main_gc = vik_viewport_get_gc_highlight (dp->vp); - drawing_highlight = TRUE; - } + if ( dp->highlight ) { + /* Draw all tracks of the layer in special colour + NB this supercedes the drawmode */ + main_gc = vik_viewport_get_gc_highlight (dp->vp); + drawing_highlight = TRUE; } if ( !drawing_highlight ) { // Still need to figure out the gc according to the drawing mode: @@ -2017,6 +2164,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 */ @@ -2033,12 +2190,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) ) { @@ -2136,7 +2292,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 ); @@ -2177,6 +2332,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 ); @@ -2249,6 +2405,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 ); @@ -2273,22 +2433,16 @@ static void trw_layer_draw_waypoint ( const gpointer id, VikWaypoint *wp, struct if ( x+(w/2) > 0 && y+(h/2) > 0 && x-(w/2) < dp->width && y-(h/2) < dp->height ) /* always draw within boundaries */ { - if ( vik_viewport_get_draw_highlight ( dp->vp ) ) { - if ( dp->vtl == vik_window_get_selected_trw_layer ( dp->vw ) || - dp->vtl->waypoints == vik_window_get_selected_waypoints ( dp->vw ) || - wp == vik_window_get_selected_waypoint ( dp->vw ) ) { - // Highlighted - so draw a little border around the chosen one - // single line seems a little weak so draw 2 of them - vik_viewport_draw_rectangle (dp->vp, vik_viewport_get_gc_highlight (dp->vp), FALSE, - x - (w/2) - 1, y - (h/2) - 1, w + 2, h + 2 ); - vik_viewport_draw_rectangle (dp->vp, vik_viewport_get_gc_highlight (dp->vp), FALSE, - 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 ); + if ( dp->highlight ) { + // Highlighted - so draw a little border around the chosen one + // single line seems a little weak so draw 2 of them + vik_viewport_draw_rectangle (dp->vp, vik_viewport_get_gc_highlight (dp->vp), FALSE, + x - (w/2) - 1, y - (h/2) - 1, w + 2, h + 2 ); + vik_viewport_draw_rectangle (dp->vp, vik_viewport_get_gc_highlight (dp->vp), FALSE, + x - (w/2) - 2, y - (h/2) - 2, w + 4, h + 4 ); + } + + 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) */ } @@ -2305,6 +2459,7 @@ static void trw_layer_draw_waypoint ( const gpointer id, VikWaypoint *wp, struct case WP_SYMBOL_CIRCLE: vik_viewport_draw_arc ( dp->vp, dp->vtl->waypoint_gc, TRUE, x - dp->vtl->wp_size, y - dp->vtl->wp_size, dp->vtl->wp_size, dp->vtl->wp_size, 0, 360*64 ); break; case WP_SYMBOL_X: vik_viewport_draw_line ( dp->vp, dp->vtl->waypoint_gc, x - dp->vtl->wp_size*2, y - dp->vtl->wp_size*2, x + dp->vtl->wp_size*2, y + dp->vtl->wp_size*2 ); vik_viewport_draw_line ( dp->vp, dp->vtl->waypoint_gc, x - dp->vtl->wp_size*2, y + dp->vtl->wp_size*2, x + dp->vtl->wp_size*2, y - dp->vtl->wp_size*2 ); + default: break; } } else { @@ -2314,6 +2469,7 @@ static void trw_layer_draw_waypoint ( const gpointer id, VikWaypoint *wp, struct case WP_SYMBOL_CIRCLE: vik_viewport_draw_arc ( dp->vp, dp->vtl->waypoint_gc, TRUE, x-dp->vtl->wp_size/2, y-dp->vtl->wp_size/2, dp->vtl->wp_size, dp->vtl->wp_size, 0, 360*64 ); break; case WP_SYMBOL_X: vik_viewport_draw_line ( dp->vp, dp->vtl->waypoint_gc, x-dp->vtl->wp_size, y-dp->vtl->wp_size, x+dp->vtl->wp_size, y+dp->vtl->wp_size ); vik_viewport_draw_line ( dp->vp, dp->vtl->waypoint_gc, x-dp->vtl->wp_size, y+dp->vtl->wp_size, x+dp->vtl->wp_size, y-dp->vtl->wp_size ); break; + default: break; } } @@ -2343,17 +2499,10 @@ static void trw_layer_draw_waypoint ( const gpointer id, VikWaypoint *wp, struct label_y = y - dp->vtl->wp_size - height - 2; /* if highlight mode on, then draw background text in highlight colour */ - if ( vik_viewport_get_draw_highlight ( dp->vp ) ) { - if ( dp->vtl == vik_window_get_selected_trw_layer ( dp->vw ) || - dp->vtl->waypoints == vik_window_get_selected_waypoints ( dp->vw ) || - wp == vik_window_get_selected_waypoint ( dp->vw ) ) - vik_viewport_draw_rectangle ( dp->vp, vik_viewport_get_gc_highlight (dp->vp), TRUE, label_x - 1, label_y-1,width+2,height+2); - else - vik_viewport_draw_rectangle ( dp->vp, dp->vtl->waypoint_bg_gc, TRUE, label_x - 1, label_y-1,width+2,height+2); - } - else { - vik_viewport_draw_rectangle ( dp->vp, dp->vtl->waypoint_bg_gc, TRUE, label_x - 1, label_y-1,width+2,height+2); - } + if ( dp->highlight ) + vik_viewport_draw_rectangle ( dp->vp, vik_viewport_get_gc_highlight (dp->vp), TRUE, label_x - 1, label_y-1,width+2,height+2); + else + vik_viewport_draw_rectangle ( dp->vp, dp->vtl->waypoint_bg_gc, TRUE, label_x - 1, label_y-1,width+2,height+2); vik_viewport_draw_layout ( dp->vp, dp->vtl->waypoint_text_gc, label_x, label_y, dp->vtl->wplabellayout ); } } @@ -2366,12 +2515,12 @@ static void trw_layer_draw_waypoint_cb ( gpointer id, VikWaypoint *wp, struct Dr } } -static void trw_layer_draw ( VikTrwLayer *l, gpointer data ) +static void trw_layer_draw_with_highlight ( VikTrwLayer *l, gpointer data, gboolean highlight ) { static struct DrawingParams dp; g_assert ( l != NULL ); - init_drawing_params ( &dp, l, VIK_VIEWPORT(data) ); + init_drawing_params ( &dp, l, VIK_VIEWPORT(data), highlight ); if ( l->tracks_visible ) g_hash_table_foreach ( l->tracks, (GHFunc) trw_layer_draw_track_cb, &dp ); @@ -2383,6 +2532,77 @@ static void trw_layer_draw ( VikTrwLayer *l, gpointer data ) g_hash_table_foreach ( l->waypoints, (GHFunc) trw_layer_draw_waypoint_cb, &dp ); } +static void trw_layer_draw ( VikTrwLayer *l, gpointer data ) +{ + // If this layer is to be highlighted - then don't draw now - as it will be drawn later on in the specific highlight draw stage + // This may seem slightly inefficient to test each time for every layer + // but for a layer with *lots* of tracks & waypoints this can save some effort by not drawing the items twice + if ( vik_viewport_get_draw_highlight ( (VikViewport*)data ) && + vik_window_get_selected_trw_layer ((VikWindow*)VIK_GTK_WINDOW_FROM_LAYER((VikLayer*)l)) == l ) + return; + trw_layer_draw_with_highlight ( l, data, FALSE ); +} + +void vik_trw_layer_draw_highlight ( VikTrwLayer *vtl, VikViewport *vvp ) +{ + // Check the layer for visibility (including all the parents visibilities) + if ( !vik_treeview_item_get_visible_tree (VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter)) ) + return; + trw_layer_draw_with_highlight ( vtl, vvp, TRUE ); +} + +/** + * vik_trw_layer_draw_highlight_item: + * + * Only handles a single track or waypoint ATM + * It assumes the track or waypoint belongs to the TRW Layer (it doesn't check this is the case) + */ +void vik_trw_layer_draw_highlight_item ( VikTrwLayer *vtl, VikTrack *trk, VikWaypoint *wpt, VikViewport *vvp ) +{ + // Check the layer for visibility (including all the parents visibilities) + if ( !vik_treeview_item_get_visible_tree (VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter)) ) + return; + + static struct DrawingParams dp; + init_drawing_params ( &dp, vtl, vvp, TRUE ); + + if ( trk ) { + gboolean draw = ( trk->is_route && vtl->routes_visible ) || ( !trk->is_route && vtl->tracks_visible ); + if ( draw ) + trw_layer_draw_track_cb ( NULL, trk, &dp ); + } + if ( vtl->waypoints_visible && wpt ) { + trw_layer_draw_waypoint_cb ( NULL, wpt, &dp ); + } +} + +/** + * vik_trw_layer_draw_highlight_item: + * + * Generally for drawing all tracks or routes or waypoints + * trks may be actually routes + * It assumes they belong to the TRW Layer (it doesn't check this is the case) + */ +void vik_trw_layer_draw_highlight_items ( VikTrwLayer *vtl, GHashTable *trks, GHashTable *wpts, VikViewport *vvp ) +{ + // Check the layer for visibility (including all the parents visibilities) + if ( !vik_treeview_item_get_visible_tree (VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter)) ) + return; + + static struct DrawingParams dp; + init_drawing_params ( &dp, vtl, vvp, TRUE ); + + if ( trks ) { + gboolean is_routes = (trks == vtl->routes); + gboolean draw = ( is_routes && vtl->routes_visible ) || ( !is_routes && vtl->tracks_visible ); + if ( draw ) + g_hash_table_foreach ( trks, (GHFunc) trw_layer_draw_track_cb, &dp ); + } + + if ( vtl->waypoints_visible && wpts ) + g_hash_table_foreach ( wpts, (GHFunc) trw_layer_draw_waypoint_cb, &dp ); +} + static void trw_layer_free_track_gcs ( VikTrwLayer *vtl ) { int i; @@ -2518,7 +2738,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); @@ -2537,7 +2762,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 ); @@ -2548,17 +2777,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 ) @@ -2596,6 +2825,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, NULL ); + + trw_layer_sort_all ( vtl ); } static gboolean trw_layer_sublayer_toggle_visible ( VikTrwLayer *l, gint subtype, gpointer sublayer ) @@ -2629,6 +2861,7 @@ static gboolean trw_layer_sublayer_toggle_visible ( VikTrwLayer *l, gint subtype else return TRUE; } + default: break; } return TRUE; } @@ -2641,6 +2874,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; @@ -2652,36 +2893,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); + } } } @@ -2693,7 +2935,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]; @@ -2709,7 +2951,7 @@ static const gchar* trw_layer_layer_tooltip ( VikTrwLayer *vtl ) // Safety check - I think these should always be valid if ( vtl->tracks && vtl->waypoints ) { - tooltip_tracks tt = { 0.0, 0, 0 }; + tooltip_tracks tt = { 0.0, 0, 0, 0 }; g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_tooltip, &tt ); GDate* gdate_start = g_date_new (); @@ -2735,35 +2977,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; @@ -2816,11 +3086,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); @@ -2832,6 +3100,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; } @@ -2878,13 +3149,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 ); @@ -3047,7 +3323,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 ) @@ -3174,51 +3450,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 ); - - /* 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 ) @@ -3281,9 +3513,18 @@ 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] ) { - gchar *auto_save_name = vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])); + const gchar *auto_save_name = vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])); vik_trw_layer_export_gpsbabel ( VIK_TRW_LAYER (layer_and_vlp[0]), _("Export Layer"), auto_save_name ); } @@ -3299,10 +3540,6 @@ static void trw_layer_export_external_gpx_2 ( menu_array_layer values ) static void trw_layer_export_gpx_track ( menu_array_sublayer values ) { - menu_array_layer data; - data[MA_VTL] = values[MA_VTL]; - data[MA_VLP] = values[MA_VLP]; - VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]); VikTrack *trk; if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE ) @@ -3350,13 +3587,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 ) @@ -3369,7 +3606,7 @@ static void trw_layer_goto_wp ( menu_array_layer values ) a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Waypoint not found in this layer.") ); else { - vik_viewport_set_center_coord ( vik_layers_panel_get_viewport(vlp), &(wp->coord) ); + vik_viewport_set_center_coord ( vik_layers_panel_get_viewport(vlp), &(wp->coord), TRUE ); vik_layers_panel_emit_update ( vlp ); // Find and select on the side panel @@ -3378,7 +3615,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 ); @@ -3513,7 +3750,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 ); } /* @@ -3521,7 +3761,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 ); } @@ -3538,7 +3777,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 ); } @@ -3578,7 +3816,6 @@ 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 @@ -3587,6 +3824,14 @@ static void trw_layer_acquire_geotagged_cb ( menu_array_layer values ) } #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; @@ -3694,28 +3939,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 ); } } @@ -3788,6 +4021,7 @@ static void trw_layer_finish_track ( menu_array_layer values ) { VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]); vtl->current_track = NULL; + vtl->route_finder_started = FALSE; vik_layer_emit_update ( VIK_LAYER(vtl) ); } @@ -3807,7 +4041,7 @@ static void trw_layer_auto_tracks_view ( menu_array_layer values ) static void trw_layer_single_waypoint_jump ( const gpointer id, const VikWaypoint *wp, gpointer vvp ) { /* NB do not care if wp is visible or not */ - vik_viewport_set_center_coord ( VIK_VIEWPORT(vvp), &(wp->coord) ); + vik_viewport_set_center_coord ( VIK_VIEWPORT(vvp), &(wp->coord), TRUE ); } static void trw_layer_auto_waypoints_view ( menu_array_layer values ) @@ -3847,6 +4081,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; @@ -3936,15 +4181,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 ); @@ -4066,11 +4322,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) ); @@ -4165,6 +4423,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 @@ -4185,8 +4447,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 ); @@ -4219,8 +4485,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 ); @@ -4254,7 +4526,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 ); @@ -4323,7 +4595,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++; @@ -4342,9 +4626,21 @@ void vik_trw_layer_filein_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypo void vik_trw_layer_filein_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *tr ) { - if ( vtl->route_finder_append && vtl->route_finder_current_track ) { + if ( vtl->route_finder_append && vtl->current_track ) { vik_track_remove_dup_points ( tr ); /* make "double point" track work to undo */ - vik_track_steal_and_append_trackpoints ( vtl->route_finder_current_track, tr ); + + // 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 ( 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 ); vik_track_free ( tr ); vtl->route_finder_append = FALSE; /* this means we have added it */ } else { @@ -4372,34 +4668,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 ); @@ -4410,11 +4703,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 ); @@ -4424,6 +4713,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) ); } } @@ -4478,7 +4770,6 @@ gboolean trw_layer_track_find_uuid ( const gpointer id, const VikTrack *trk, gpo gboolean vik_trw_layer_delete_track ( VikTrwLayer *vtl, VikTrack *trk ) { gboolean was_visible = FALSE; - if ( trk && trk->name ) { if ( trk == vtl->current_track ) { @@ -4486,13 +4777,11 @@ gboolean vik_trw_layer_delete_track ( VikTrwLayer *vtl, VikTrack *trk ) vtl->current_tp_track = NULL; vtl->current_tp_id = NULL; vtl->moving_tp = FALSE; + vtl->route_finder_started = FALSE; } was_visible = trk->visible; - if ( trk == vtl->route_finder_current_track ) - vtl->route_finder_current_track = NULL; - if ( trk == vtl->route_finder_added_track ) vtl->route_finder_added_track = NULL; @@ -4501,7 +4790,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 */ @@ -4519,6 +4808,8 @@ gboolean vik_trw_layer_delete_track ( VikTrwLayer *vtl, VikTrack *trk ) vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter) ); } } + // Incase it was selected (no item delete signal ATM) + vik_window_clear_highlight ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) ); } } return was_visible; @@ -4539,9 +4830,6 @@ gboolean vik_trw_layer_delete_route ( VikTrwLayer *vtl, VikTrack *trk ) was_visible = trk->visible; - if ( trk == vtl->route_finder_current_track ) - vtl->route_finder_current_track = NULL; - if ( trk == vtl->route_finder_added_track ) vtl->route_finder_added_track = NULL; @@ -4550,7 +4838,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 */ @@ -4568,6 +4856,8 @@ gboolean vik_trw_layer_delete_route ( VikTrwLayer *vtl, VikTrack *trk ) vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter) ); } } + // Incase it was selected (no item delete signal ATM) + vik_window_clear_highlight ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) ); } } return was_visible; @@ -4592,7 +4882,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 ); @@ -4609,6 +4899,8 @@ static gboolean trw_layer_delete_waypoint ( VikTrwLayer *vtl, VikWaypoint *wp ) vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter) ); } } + // Incase it was selected (no item delete signal ATM) + vik_window_clear_highlight ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) ); } } @@ -4642,7 +4934,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); @@ -4683,7 +4975,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); @@ -4708,7 +5000,6 @@ void vik_trw_layer_delete_all_routes ( VikTrwLayer *vtl ) { vtl->current_track = NULL; - vtl->route_finder_current_track = NULL; vtl->route_finder_added_track = NULL; if (vtl->current_tp_track) trw_layer_cancel_current_tp(vtl, FALSE); @@ -4726,7 +5017,6 @@ void vik_trw_layer_delete_all_tracks ( VikTrwLayer *vtl ) { vtl->current_track = NULL; - vtl->route_finder_current_track = NULL; vtl->route_finder_added_track = NULL; if (vtl->current_tp_track) trw_layer_cancel_current_tp(vtl, FALSE); @@ -4803,6 +5093,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 ) @@ -4816,6 +5109,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 @@ -4848,7 +5143,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 ); @@ -4871,7 +5166,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 ); @@ -4958,7 +5253,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 @@ -4994,13 +5289,13 @@ void trw_layer_update_treeview ( VikTrwLayer *vtl, VikTrack *trk ) static void goto_coord ( gpointer *vlp, gpointer vl, gpointer vvp, const VikCoord *coord ) { if ( vlp ) { - vik_viewport_set_center_coord ( vik_layers_panel_get_viewport (VIK_LAYERS_PANEL(vlp)), coord ); + vik_viewport_set_center_coord ( vik_layers_panel_get_viewport (VIK_LAYERS_PANEL(vlp)), coord, TRUE ); vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) ); } else { /* since vlp not set, vl & vvp should be valid instead! */ if ( vl && vvp ) { - vik_viewport_set_center_coord ( VIK_VIEWPORT(vvp), coord ); + vik_viewport_set_center_coord ( VIK_VIEWPORT(vvp), coord, TRUE ); vik_layer_emit_update ( VIK_LAYER(vl) ); } } @@ -5104,6 +5399,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]); @@ -5132,15 +5440,13 @@ static void trw_layer_extend_track_end_route_finder ( menu_array_sublayer values VikTrack *track = g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] ); if ( !track ) return; - if ( !track->trackpoints ) - return; vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_ROUTE_FINDER ); - vtl->route_finder_coord = vik_track_get_tp_last(track)->coord; - vtl->route_finder_current_track = track; + vtl->current_track = track; vtl->route_finder_started = TRUE; - goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &vtl->route_finder_coord ); + if ( track->trackpoints ) + goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &vik_track_get_tp_last(track)->coord ); } /** @@ -5518,7 +5824,7 @@ static void trw_layer_edit_trackpoint ( menu_array_sublayer values ) */ typedef struct { GList **result; - GList *exclude; + VikTrack *exclude; gboolean with_timestamps; } twt_udata; static void find_tracks_with_timestamp_type(gpointer key, gpointer value, gpointer udata) @@ -5526,7 +5832,7 @@ static void find_tracks_with_timestamp_type(gpointer key, gpointer value, gpoint twt_udata *user_data = udata; VikTrackpoint *p1, *p2; VikTrack *trk = VIK_TRACK(value); - if (trk == (VikTrack *)user_data->exclude) { + if (trk == user_data->exclude) { return; } @@ -5550,34 +5856,41 @@ 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]; - GList *tpoints = ((gpointer *)user_data)[1]; + VikTrack *orig_trk = VIK_TRACK(((gpointer *)user_data)[1]); + + if ( !orig_trk || !orig_trk->trackpoints ) + return; /* outline: * detect reasons for not merging, and return * if no reason is found not to merge, then do it. */ + twt_udata *udata = user_data; // Exclude the original track from the compiled list - if (trk->trackpoints == tpoints) { + if (trk == udata->exclude) { 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"); @@ -5586,11 +5899,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; } } @@ -5663,7 +5976,7 @@ static void trw_layer_merge_with_other ( menu_array_sublayer values ) twt_udata udata; udata.result = &other_tracks; - udata.exclude = track->trackpoints; + udata.exclude = track; // Allow merging with 'similar' time type time tracks // i.e. either those times, or those without udata.with_timestamps = vik_track_get_tp_first(track)->has_timestamp; @@ -5733,7 +6046,7 @@ static void trw_layer_sorted_track_id_by_name_list_exclude_self (const gpointer twt_udata *user_data = udata; // Skip self - if (trk->trackpoints == user_data->exclude) { + if (trk == user_data->exclude) { return; } @@ -5769,7 +6082,7 @@ static void trw_layer_append_track ( menu_array_sublayer values ) // TODO: Need to consider how to work best when we can have multiple tracks the same name... twt_udata udata; udata.result = &other_tracks_names; - udata.exclude = trk->trackpoints; + udata.exclude = trk; g_hash_table_foreach(ght_tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list_exclude_self, (gpointer)&udata); @@ -5846,7 +6159,7 @@ static void trw_layer_append_other ( menu_array_sublayer values ) // TODO: Need to consider how to work best when we can have multiple tracks the same name... twt_udata udata; udata.result = &other_tracks_names; - udata.exclude = trk->trackpoints; + udata.exclude = trk; g_hash_table_foreach(ght_others, (GHFunc) trw_layer_sorted_track_id_by_name_list_exclude_self, (gpointer)&udata); @@ -5939,7 +6252,7 @@ static void trw_layer_merge_by_timestamp ( menu_array_sublayer values ) twt_udata udata; udata.result = &tracks_with_timestamp; - udata.exclude = orig_trk->trackpoints; + udata.exclude = orig_trk; udata.with_timestamps = TRUE; g_hash_table_foreach(vtl->tracks, find_tracks_with_timestamp_type, (gpointer)&udata); tracks_with_timestamp = g_list_reverse(tracks_with_timestamp); @@ -5979,7 +6292,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 */ @@ -6046,7 +6359,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 @@ -6452,6 +6765,190 @@ static void trw_layer_reverse ( menu_array_sublayer values ) vik_layer_emit_update ( VIK_LAYER(vtl) ); } +/** + * 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%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."), diary_program ); + g_error_free ( err ); + } + g_free ( cmd ); +} + +/** + * Open a diary at the date of the track or waypoint + */ +static void trw_layer_diary ( 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; + + gchar date_buf[20]; + date_buf[0] = '\0'; + if ( trk->trackpoints && VIK_TRACKPOINT(trk->trackpoints->data)->has_timestamp ) { + strftime (date_buf, sizeof(date_buf), "%Y-%m-%d", gmtime(&(VIK_TRACKPOINT(trk->trackpoints->data)->timestamp))); + trw_layer_diary_open ( vtl, date_buf ); + } + 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; + + gchar date_buf[20]; + date_buf[0] = '\0'; + if ( wpt->has_timestamp ) { + strftime (date_buf, sizeof(date_buf), "%Y-%m-%d", gmtime(&(wpt->timestamp))); + trw_layer_diary_open ( vtl, date_buf ); + } + else + a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("This waypoint has no date information.") ); + } +} + +/** + * 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 */ @@ -6594,7 +7091,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 ) { @@ -6607,11 +7104,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; @@ -6628,50 +7126,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 ); } /** @@ -6717,6 +7215,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) ); } } @@ -6867,6 +7368,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 ); @@ -6928,6 +7431,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) ); } @@ -7235,7 +7740,9 @@ static void trw_layer_waypoint_webpage ( menu_array_sublayer values ) VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] ); if ( !wp ) return; - if ( !strncmp(wp->comment, "http", 4) ) { + if ( wp->url ) { + open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(vtl)), wp->url); + } else if ( !strncmp(wp->comment, "http", 4) ) { open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(vtl)), wp->comment); } else if ( !strncmp(wp->description, "http", 4) ) { open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(vtl)), wp->description); @@ -7356,11 +7863,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 ) @@ -7373,7 +7882,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 ); @@ -7501,7 +8010,8 @@ static gboolean trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *men if ( wp ) { - if ( ( wp->comment && !strncmp(wp->comment, "http", 4) ) || + if ( wp->url || + ( wp->comment && !strncmp(wp->comment, "http", 4) ) || ( wp->description && !strncmp(wp->description, "http", 4) )) { item = gtk_image_menu_item_new_with_mnemonic ( _("Visit _Webpage") ); gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NETWORK, GTK_ICON_SIZE_MENU) ); @@ -7770,6 +8280,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 (); @@ -8034,13 +8556,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 ) @@ -8114,6 +8642,46 @@ static gboolean trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *men } } + 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(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 ) ) { @@ -8137,11 +8705,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 ) { @@ -8304,6 +8875,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 ); @@ -8316,7 +8897,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 ) { @@ -8330,20 +8911,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 ) @@ -8470,7 +9055,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 */ } @@ -8484,7 +9069,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; @@ -8599,7 +9184,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; @@ -8639,6 +9224,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 ); @@ -8678,7 +9268,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 } } @@ -8793,7 +9383,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; @@ -8828,7 +9418,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; @@ -8874,7 +9464,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 @@ -8917,7 +9507,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 ); @@ -8961,6 +9551,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 ) @@ -8970,6 +9561,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); @@ -8982,6 +9574,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 ****/ @@ -9021,8 +9614,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 */ @@ -9042,7 +9635,10 @@ static gboolean tool_edit_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *eve g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, ¶ms); if ( vtl->current_wp && (vtl->current_wp == params.closest_wp) ) { - marker_begin_move(t, event->x, event->y); + if ( event->button == 3 ) + vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */ + else + marker_begin_move(t, event->x, event->y); return FALSE; } else if ( params.closest_wp ) @@ -9212,6 +9808,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) { @@ -9412,6 +10017,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; @@ -9499,17 +10111,20 @@ static gboolean tool_new_track_or_route_click ( VikTrwLayer *vtl, GdkEventButton static gboolean tool_new_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp ) { + // if we were running the route finder, cancel it + vtl->route_finder_started = FALSE; + // ----------------------------------------------------- if current is a route - switch to new track 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 ); } @@ -9532,16 +10147,21 @@ static gpointer tool_new_route_create ( VikWindow *vw, VikViewport *vvp) static gboolean tool_new_route_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp ) { - // -------------------------- if current is a track - switch to new route - if ( event->button == 1 && ( ! vtl->current_track || (vtl->current_track && !vtl->current_track->is_route ) ) ) + // if we were running the route finder, cancel it + vtl->route_finder_started = FALSE; + + // -------------------------- if current is a track - switch to new route, + 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_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 ); } @@ -9559,9 +10179,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; } @@ -9582,22 +10203,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) ); @@ -9608,7 +10231,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 ) @@ -9616,6 +10239,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; @@ -9623,8 +10248,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; } @@ -9727,7 +10352,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; @@ -9736,77 +10362,121 @@ static gboolean tool_edit_trackpoint_release ( VikTrwLayer *vtl, GdkEventButton } -/*** Route Finder ***/ -static gpointer tool_route_finder_create ( VikWindow *vw, VikViewport *vvp) +/*** Extended Route Finder ***/ + +static gpointer tool_extended_route_finder_create ( VikWindow *vw, VikViewport *vvp) { return vvp; } -static gboolean tool_route_finder_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp ) +static void tool_extended_route_finder_undo ( VikTrwLayer *vtl ) +{ + VikCoord *new_end; + new_end = vik_track_cut_back_to_double_point ( vtl->current_track ); + if ( new_end ) { + g_free ( new_end ); + vik_layer_emit_update ( VIK_LAYER(vtl) ); + + /* remove last ' to:...' */ + if ( vtl->current_track->comment ) { + gchar *last_to = strrchr ( vtl->current_track->comment, 't' ); + if ( last_to && (last_to - vtl->current_track->comment > 1) ) { + gchar *new_comment = g_strndup ( vtl->current_track->comment, + last_to - vtl->current_track->comment - 1); + vik_track_set_comment_no_copy ( vtl->current_track, new_comment ); + } + } + } +} + + +static gboolean tool_extended_route_finder_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp ) { VikCoord tmp; if ( !vtl ) return FALSE; vik_viewport_screen_to_coord ( vvp, event->x, event->y, &tmp ); - if ( event->button == 3 && vtl->route_finder_current_track ) { - VikCoord *new_end; - new_end = vik_track_cut_back_to_double_point ( vtl->route_finder_current_track ); - if ( new_end ) { - vtl->route_finder_coord = *new_end; - g_free ( new_end ); - vik_layer_emit_update ( VIK_LAYER(vtl) ); - /* remove last ' to:...' */ - if ( vtl->route_finder_current_track->comment ) { - gchar *last_to = strrchr ( vtl->route_finder_current_track->comment, 't' ); - if ( last_to && (last_to - vtl->route_finder_current_track->comment > 1) ) { - gchar *new_comment = g_strndup ( vtl->route_finder_current_track->comment, - last_to - vtl->route_finder_current_track->comment - 1); - vik_track_set_comment_no_copy ( vtl->route_finder_current_track, new_comment ); - } - } - } + if ( event->button == 3 && vtl->current_track ) { + tool_extended_route_finder_undo ( vtl ); } - else if ( vtl->route_finder_started || (event->state & GDK_CONTROL_MASK && vtl->route_finder_current_track) ) { + else if ( event->button == 2 ) { + vtl->draw_sync_do = FALSE; + return FALSE; + } + // if we started the track but via undo deleted all the track points, begin again + else if ( vtl->current_track && vtl->current_track->is_route && ! vik_track_get_tp_first ( vtl->current_track ) ) { + return tool_new_track_or_route_click ( vtl, event, vvp ); + } + else if ( ( vtl->current_track && vtl->current_track->is_route ) || + ( event->state & GDK_CONTROL_MASK && vtl->current_track ) ) { struct LatLon start, end; - vik_coord_to_latlon ( &(vtl->route_finder_coord), &start ); + VikTrackpoint *tp_start = vik_track_get_tp_last ( vtl->current_track ); + vik_coord_to_latlon ( &(tp_start->coord), &start ); vik_coord_to_latlon ( &(tmp), &end ); - vtl->route_finder_coord = tmp; /* for continuations */ - /* these are checked when adding a track from a file (vik_trw_layer_filein_add_track) */ - if ( event->state & GDK_CONTROL_MASK && vtl->route_finder_current_track ) { - vtl->route_finder_append = TRUE; // merge tracks. keep started true. - } else { - vtl->route_finder_check_added_track = TRUE; - vtl->route_finder_started = FALSE; + vtl->route_finder_started = TRUE; + vtl->route_finder_append = TRUE; // merge tracks. keep started true. + + // update UI to let user know what's going on + VikStatusbar *sb = vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))); + VikRoutingEngine *engine = vik_routing_default_engine ( ); + if ( ! engine ) { + vik_statusbar_set_message ( sb, VIK_STATUSBAR_INFO, "Cannot plan route without a default routing engine." ); + return TRUE; } + gchar *msg = g_strdup_printf ( _("Querying %s for route between (%.3f, %.3f) and (%.3f, %.3f)."), + vik_routing_engine_get_label ( engine ), + start.lat, start.lon, end.lat, end.lon ); + vik_statusbar_set_message ( sb, VIK_STATUSBAR_INFO, msg ); + g_free ( msg ); + vik_window_set_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) ); - vik_routing_default_find ( vtl, start, end); - /* see if anything was done -- a track was added or appended to */ - if ( vtl->route_finder_check_added_track && vtl->route_finder_added_track ) { - vik_track_set_comment_no_copy ( vtl->route_finder_added_track, g_strdup_printf("from: %f,%f to: %f,%f", start.lat, start.lon, end.lat, end.lon ) ); - } else if ( vtl->route_finder_append == FALSE && vtl->route_finder_current_track ) { - /* route_finder_append was originally TRUE but set to FALSE by filein_add_track */ - gchar *new_comment = g_strdup_printf("%s to: %f,%f", vtl->route_finder_current_track->comment, end.lat, end.lon ); - vik_track_set_comment_no_copy ( vtl->route_finder_current_track, new_comment ); - } + /* Give GTK a change to display the new status bar before querying the web */ + while ( gtk_events_pending ( ) ) + gtk_main_iteration ( ); - if ( vtl->route_finder_added_track ) - vik_track_calculate_bounds ( vtl->route_finder_added_track ); + gboolean find_status = vik_routing_default_find ( vtl, start, end ); - vtl->route_finder_added_track = NULL; - vtl->route_finder_check_added_track = FALSE; - vtl->route_finder_append = FALSE; + /* Update UI to say we're done */ + vik_window_clear_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) ); + msg = ( find_status ) ? g_strdup_printf ( _("%s returned route between (%.3f, %.3f) and (%.3f, %.3f)."), + vik_routing_engine_get_label ( engine ), + start.lat, start.lon, end.lat, end.lon ) + : g_strdup_printf ( _("Error getting route from %s."), + vik_routing_engine_get_label ( engine ) ); + vik_statusbar_set_message ( sb, VIK_STATUSBAR_INFO, msg ); + g_free ( msg ); vik_layer_emit_update ( VIK_LAYER(vtl) ); } else { + vtl->current_track = NULL; + + // create a new route where we will add the planned route to + gboolean ret = tool_new_route_click( vtl, event, vvp ); + vtl->route_finder_started = TRUE; - vtl->route_finder_coord = tmp; - vtl->route_finder_current_track = NULL; + + return ret; } return TRUE; } +static gboolean tool_extended_route_finder_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp ) +{ + if ( vtl->current_track && event->keyval == GDK_Escape ) { + vtl->route_finder_started = FALSE; + vtl->current_track = NULL; + vik_layer_emit_update ( VIK_LAYER(vtl) ); + return TRUE; + } else if ( vtl->current_track && event->keyval == GDK_BackSpace ) { + tool_extended_route_finder_undo ( vtl ); + } + return FALSE; +} + + + /*** Show picture ****/ static gpointer tool_show_picture_create ( VikWindow *vw, VikViewport *vvp) @@ -9932,7 +10602,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, @@ -10080,6 +10751,74 @@ static void trw_layer_sort_all ( VikTrwLayer *vtl ) vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), vtl->wp_sort_order ); } +/** + * 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, GtkWidget *vvp, gboolean from_file ) { if ( VIK_LAYER(vtl)->realized ) @@ -10107,47 +10846,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 ); @@ -10330,16 +11035,16 @@ void vik_track_download_map(VikTrack *tr, VikMapsLayer *vml, VikViewport *vvp, g g_message("%s: this feature works only in Mercator mode", __FUNCTION__); if (fillins) { - GList *iter = fillins; - while (iter) { - cur_coord = (VikCoord *)(iter->data); + GList *fiter = fillins; + while (fiter) { + cur_coord = (VikCoord *)(fiter->data); vik_coord_set_area(cur_coord, &wh, &tl, &br); rect = g_malloc(sizeof(Rect)); rect->tl = tl; rect->br = br; rect->center = *cur_coord; rects_to_download = g_list_prepend(rects_to_download, rect); - iter = iter->next; + fiter = fiter->next; } } @@ -10388,8 +11093,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;