X-Git-Url: https://git.street.me.uk/andy/viking.git/blobdiff_plain/415b0e21cfbd2d8a1e22ac02d7e6ccd2f9743f7c..d7e776d05a21567c670586d1d57fdc3f1abb04fc:/src/viktrwlayer.c diff --git a/src/viktrwlayer.c b/src/viktrwlayer.c index c2476fa1..f69e03e4 100644 --- a/src/viktrwlayer.c +++ b/src/viktrwlayer.c @@ -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" @@ -58,7 +60,7 @@ #include "datasources.h" #include "datasource_gps.h" #include "vikexttool_datasources.h" -#include "util.h" +#include "ui_util.h" #include "vikutils.h" #include "vikrouting.h" @@ -708,7 +710,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 */ @@ -775,26 +777,48 @@ VikLayerInterface vik_trw_layer_interface = { }; static gboolean have_diary_program = FALSE; +static gchar *diary_program = NULL; +#define VIK_SETTINGS_EXTERNAL_DIARY_PROGRAM "external_diary_program" + +static gboolean have_geojson_export = FALSE; + +static gboolean have_astro_program = FALSE; +static gchar *astro_program = NULL; +#define VIK_SETTINGS_EXTERNAL_ASTRO_PROGRAM "external_astro_program" // NB Only performed once per program run static void vik_trwlayer_class_init ( VikTrwLayerClass *klass ) { - if ( g_find_program_in_path( "rednotebook" ) ) { - gchar *stdout = NULL; - gchar *stderr = NULL; + if ( ! a_settings_get_string ( VIK_SETTINGS_EXTERNAL_DIARY_PROGRAM, &diary_program ) ) { +#ifdef WINDOWS + //diary_program = g_strdup ( "C:\\Program Files\\Rednotebook\\rednotebook.exe" ); + diary_program = g_strdup ( "C:/Progra~1/Rednotebook/rednotebook.exe" ); +#else + diary_program = g_strdup ( "rednotebook" ); +#endif + } + else { + // User specified so assume it works + have_diary_program = TRUE; + } + + if ( g_find_program_in_path( diary_program ) ) { + gchar *mystdout = NULL; + gchar *mystderr = NULL; // Needs RedNotebook 1.7.3+ for support of opening on a specified date - if ( g_spawn_command_line_sync ( "rednotebook --version", &stdout, &stderr, NULL, NULL ) ) { + gchar *cmd = g_strconcat ( diary_program, " --version", NULL ); // "rednotebook --version" + if ( g_spawn_command_line_sync ( cmd, &mystdout, &mystderr, NULL, NULL ) ) { // Annoyingly 1.7.1|2|3 versions of RedNotebook prints the version to stderr!! - if ( stdout ) - g_debug ("Diary: %s", stdout ); // Should be something like 'RedNotebook 1.4' - if ( stderr ) - g_warning ("Diary: stderr: %s", stderr ); + if ( mystdout ) + g_debug ("Diary: %s", mystdout ); // Should be something like 'RedNotebook 1.4' + if ( mystderr ) + g_warning ("Diary: stderr: %s", mystderr ); gchar **tokens = NULL; - if ( stdout && g_strcmp0(stdout, "") ) - tokens = g_strsplit(stdout, " ", 0); - else if ( stderr ) - tokens = g_strsplit(stderr, " ", 0); + if ( mystdout && g_strcmp0(mystdout, "") ) + tokens = g_strsplit(mystdout, " ", 0); + else if ( mystderr ) + tokens = g_strsplit(mystderr, " ", 0); gint num = 0; gchar *token = tokens[num]; @@ -808,8 +832,30 @@ static void vik_trwlayer_class_init ( VikTrwLayerClass *klass ) } g_strfreev ( tokens ); } - g_free ( stdout ); - g_free ( stderr ); + g_free ( mystdout ); + g_free ( mystderr ); + g_free ( cmd ); + } + + if ( g_find_program_in_path ( a_geojson_program_export() ) ) { + have_geojson_export = TRUE; + } + + // Astronomy Domain + if ( ! a_settings_get_string ( VIK_SETTINGS_EXTERNAL_ASTRO_PROGRAM, &astro_program ) ) { +#ifdef WINDOWS + //astro_program = g_strdup ( "C:\\Program Files\\Stellarium\\stellarium.exe" ); + astro_program = g_strdup ( "C:/Progra~1/Stellarium/stellarium.exe" ); +#else + astro_program = g_strdup ( "stellarium" ); +#endif + } + else { + // User specified so assume it works + have_astro_program = TRUE; + } + if ( g_find_program_in_path( astro_program ) ) { + have_astro_program = TRUE; } } @@ -1732,6 +1778,9 @@ static gdouble distance_in_preferred_units ( gdouble dist ) case VIK_UNITS_DISTANCE_MILES: mydist = VIK_METERS_TO_MILES(dist); break; + case VIK_UNITS_DISTANCE_NAUTICAL_MILES: + mydist = VIK_METERS_TO_NAUTICAL_MILES(dist); + break; // VIK_UNITS_DISTANCE_KILOMETRES: default: mydist = dist/1000.0; @@ -1777,6 +1826,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; @@ -2756,31 +2808,32 @@ static void trw_layer_tracks_tooltip ( const gchar *name, VikTrack *tr, tooltip_ 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 ) { - - time_t t1, t2; - t1 = vik_track_get_tp_first(tr)->timestamp; - t2 = vik_track_get_tp_last(tr)->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; - // 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); + } } } @@ -2834,13 +2887,19 @@ 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 @@ -2915,11 +2974,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 ); + if ( dur > 0 ) + g_snprintf ( time_buf2, sizeof(time_buf2), _("- %d:%02d hrs:mins"), (int)round(dur/3600), (int)round((dur/60)%60) ); } // Get length and consider the appropriate distance units gdouble tr_len = vik_track_get_length(tr); @@ -2931,6 +2988,9 @@ static const gchar* trw_layer_sublayer_tooltip ( VikTrwLayer *l, gint subtype, g case VIK_UNITS_DISTANCE_MILES: g_snprintf (tmp_buf, sizeof(tmp_buf), _("%s%.1f miles %s"), time_buf1, VIK_METERS_TO_MILES(tr_len), time_buf2); break; + case VIK_UNITS_DISTANCE_NAUTICAL_MILES: + g_snprintf (tmp_buf, sizeof(tmp_buf), _("%s%.1f NM %s"), time_buf1, VIK_METERS_TO_NAUTICAL_MILES(tr_len), time_buf2); + break; default: break; } @@ -2977,13 +3037,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 ); @@ -3380,6 +3445,15 @@ static void trw_layer_export_kml ( menu_array_layer values ) g_free ( auto_save_name ); } +static void trw_layer_export_geojson ( menu_array_layer values ) +{ + gchar *auto_save_name = append_file_ext ( vik_layer_get_name(VIK_LAYER(values[MA_VTL])), FILE_TYPE_GEOJSON ); + + vik_trw_layer_export ( VIK_TRW_LAYER (values[MA_VTL]), _("Export Layer"), auto_save_name, NULL, FILE_TYPE_GEOJSON ); + + g_free ( auto_save_name ); +} + static void trw_layer_export_babel ( gpointer layer_and_vlp[2] ) { const gchar *auto_save_name = vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])); @@ -3608,7 +3682,10 @@ static void trw_layer_acquire ( menu_array_layer values, VikDataSourceInterface VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)); VikViewport *vvp = vik_window_viewport(vw); - a_acquire ( vw, vlp, vvp, datasource, NULL, NULL ); + vik_datasource_mode_t mode = datasource->mode; + if ( mode == VIK_DATASOURCE_AUTO_LAYER_MANAGEMENT ) + mode = VIK_DATASOURCE_ADDTOLAYER; + a_acquire ( vw, vlp, vvp, mode, datasource, NULL, NULL ); } /* @@ -3616,7 +3693,6 @@ static void trw_layer_acquire ( menu_array_layer values, VikDataSourceInterface */ static void trw_layer_acquire_gps_cb ( menu_array_layer values ) { - vik_datasource_gps_interface.mode = VIK_DATASOURCE_ADDTOLAYER; trw_layer_acquire ( values, &vik_datasource_gps_interface ); } @@ -3633,7 +3709,6 @@ static void trw_layer_acquire_routing_cb ( menu_array_layer values ) */ static void trw_layer_acquire_url_cb ( menu_array_layer values ) { - vik_datasource_url_interface.mode = VIK_DATASOURCE_ADDTOLAYER; trw_layer_acquire ( values, &vik_datasource_url_interface ); } @@ -3673,7 +3748,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 @@ -3682,6 +3756,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; @@ -3789,19 +3871,6 @@ 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]); @@ -4037,6 +4106,13 @@ 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 ); + 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 ); + } + 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); @@ -4907,6 +4983,7 @@ 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 ); } } else if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_TRACK ) @@ -5652,16 +5729,22 @@ static void find_tracks_with_timestamp_type(gpointer key, gpointer value, gpoint *(user_data->result) = g_list_prepend(*(user_data->result), key); } -/* called for each key in track hash table. if original track user_data[1] is close enough - * to the passed one, add it to list in user_data[0] +/** + * find_nearby_tracks_by_time: + * + * Called for each track in track hash table. + * If the original track (in user_data[1]) is close enough (threshold period in user_data[2]) + * to the current track, then the current track is added to the list in user_data[0] */ static void find_nearby_tracks_by_time (gpointer key, gpointer value, gpointer user_data) { - time_t t1, t2; - VikTrackpoint *p1, *p2; VikTrack *trk = VIK_TRACK(value); GList **nearby_tracks = ((gpointer *)user_data)[0]; + VikTrack *orig_trk = VIK_TRACK(((gpointer *)user_data)[1]); + + if ( !orig_trk || !orig_trk->trackpoints ) + return; /* outline: * detect reasons for not merging, and return @@ -5674,12 +5757,13 @@ static void find_nearby_tracks_by_time (gpointer key, gpointer value, gpointer u return; } - t1 = vik_track_get_tp_first(trk)->timestamp; - t2 = vik_track_get_tp_last(trk)->timestamp; + time_t t1 = vik_track_get_tp_first(orig_trk)->timestamp; + time_t t2 = vik_track_get_tp_last(orig_trk)->timestamp; if (trk->trackpoints) { - p1 = vik_track_get_tp_first(trk); - p2 = vik_track_get_tp_last(trk); + + VikTrackpoint *p1 = vik_track_get_tp_first(trk); + VikTrackpoint *p2 = vik_track_get_tp_last(trk); if (!p1->has_timestamp || !p2->has_timestamp) { //g_print("no timestamp\n"); @@ -6081,7 +6165,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 */ @@ -6555,14 +6639,17 @@ static void trw_layer_reverse ( menu_array_sublayer values ) } /** - * Open a diary at the specified date + * Open a program at the specified date + * Mainly for RedNotebook - http://rednotebook.sourceforge.net/ + * But could work with any program that accepts a command line of --date= + * FUTURE: Allow configuring of command line options + date format */ static void trw_layer_diary_open ( VikTrwLayer *vtl, const gchar *date_str ) { GError *err = NULL; - gchar *cmd = g_strdup_printf ( "%s%s", "rednotebook --date=", date_str ); + gchar *cmd = g_strdup_printf ( "%s %s%s", diary_program, "--date=", date_str ); if ( ! g_spawn_command_line_async ( cmd, &err ) ) { - a_dialog_error_msg_extra ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Could not launch %s to open file."), "rednotebook" ); + a_dialog_error_msg_extra ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Could not launch %s to open file."), diary_program ); g_error_free ( err ); } g_free ( cmd ); @@ -6605,6 +6692,131 @@ static void trw_layer_diary ( menu_array_sublayer values ) } } +/** + * Open a program at the specified date + * Mainly for Stellarium - http://stellarium.org/ + * But could work with any program that accepts the same command line options... + * FUTURE: Allow configuring of command line options + format or parameters + */ +static void trw_layer_astro_open ( VikTrwLayer *vtl, const gchar *date_str, const gchar *time_str, const gchar *lat_str, const gchar *lon_str, const gchar *alt_str ) +{ + GError *err = NULL; + gchar *tmp; + g_file_open_tmp ( "vik-astro-XXXXXX.ini", &tmp, NULL ); + 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 */ @@ -8271,12 +8483,28 @@ static gboolean trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *men } // Only made available if a suitable program is installed - if ( have_diary_program ) { - if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) { - item = gtk_image_menu_item_new_with_mnemonic ( _("Diar_y") ); + if ( (have_astro_program || have_diary_program) && + (subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT) ) { + GtkWidget *external_submenu; + external_submenu = gtk_menu_new (); + 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 ); + + if ( have_diary_program ) { + item = gtk_image_menu_item_new_with_mnemonic ( _("_Diary") ); gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_SPELL_CHECK, GTK_ICON_SIZE_MENU) ); g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_diary), pass_along ); - gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item ); + gtk_menu_shell_append ( GTK_MENU_SHELL(external_submenu), item ); + gtk_widget_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_show ( item ); } } @@ -8471,6 +8699,16 @@ static void trw_layer_cancel_current_tp ( VikTrwLayer *vtl, gboolean destroy ) } } +static void my_tpwin_set_tp ( VikTrwLayer *vtl ) +{ + VikTrack *trk = vtl->current_tp_track; + VikCoord vc; + // Notional center of a track is simply an average of the bounding box extremities + struct LatLon center = { (trk->bbox.north+trk->bbox.south)/2, (trk->bbox.east+trk->bbox.west)/2 }; + vik_coord_load_from_latlon ( &vc, vtl->coord_mode, ¢er ); + vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, trk->name, vtl->current_tp_track->is_route ); +} + static void trw_layer_tpwin_response ( VikTrwLayer *vtl, gint response ) { g_assert ( vtl->tpwin != NULL ); @@ -8483,7 +8721,7 @@ static void trw_layer_tpwin_response ( VikTrwLayer *vtl, gint response ) if ( response == VIK_TRW_LAYER_TPWIN_SPLIT && vtl->current_tpl->next && vtl->current_tpl->prev ) { trw_layer_split_at_selected_trackpoint ( vtl, vtl->current_tp_track->is_route ? VIK_TRW_LAYER_SUBLAYER_ROUTE : VIK_TRW_LAYER_SUBLAYER_TRACK ); - vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name ); + my_tpwin_set_tp ( vtl ); } else if ( response == VIK_TRW_LAYER_TPWIN_DELETE ) { @@ -8497,20 +8735,24 @@ static void trw_layer_tpwin_response ( VikTrwLayer *vtl, gint response ) if ( vtl->current_tpl ) // Reset dialog with the available adjacent trackpoint - vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name ); + my_tpwin_set_tp ( vtl ); vik_layer_emit_update(VIK_LAYER(vtl)); } else if ( response == VIK_TRW_LAYER_TPWIN_FORWARD && vtl->current_tpl->next ) { - if ( vtl->current_tp_track ) - vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl = vtl->current_tpl->next, vtl->current_tp_track->name ); + if ( vtl->current_tp_track ) { + vtl->current_tpl = vtl->current_tpl->next; + my_tpwin_set_tp ( vtl ); + } vik_layer_emit_update(VIK_LAYER(vtl)); /* TODO longone: either move or only update if tp is inside drawing window */ } else if ( response == VIK_TRW_LAYER_TPWIN_BACK && vtl->current_tpl->prev ) { - if ( vtl->current_tp_track ) - vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl = vtl->current_tpl->prev, vtl->current_tp_track->name ); + if ( vtl->current_tp_track ) { + vtl->current_tpl = vtl->current_tpl->prev; + my_tpwin_set_tp ( vtl ); + } vik_layer_emit_update(VIK_LAYER(vtl)); } else if ( response == VIK_TRW_LAYER_TPWIN_INSERT && vtl->current_tpl->next ) @@ -8637,7 +8879,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 */ } @@ -8766,7 +9008,7 @@ static void marker_moveto ( tool_ed_t *t, gint x, gint y ); static void marker_end_move ( tool_ed_t *t ); // -static gboolean trw_layer_select_move ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* t ) +static gboolean trw_layer_select_move ( VikTrwLayer *vtl, GdkEventMotion *event, VikViewport *vvp, tool_ed_t* t ) { if ( t->holding ) { VikCoord new_coord; @@ -8806,6 +9048,11 @@ static gboolean trw_layer_select_release ( VikTrwLayer *vtl, GdkEventButton *eve { if ( t->holding && event->button == 1 ) { + // Prevent accidental (small) shifts when specific movement has not been requested + // (as the click release has occurred within the click object detection area) + if ( !t->moving ) + return FALSE; + VikCoord new_coord; vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord ); @@ -8845,7 +9092,7 @@ static gboolean trw_layer_select_release ( VikTrwLayer *vtl, GdkEventButton *eve if ( vtl->tpwin ) if ( vtl->current_tp_track ) - vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name ); + my_tpwin_set_tp ( vtl ); // NB don't reset the selected trackpoint, thus ensuring it's still in the tpwin } } @@ -8960,7 +9207,7 @@ static gboolean trw_layer_select_click ( VikTrwLayer *vtl, GdkEventButton *event set_statusbar_msg_info_trkpt ( vtl, tp_params.closest_tp ); if ( vtl->tpwin ) - vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name ); + my_tpwin_set_tp ( vtl ); vik_layer_emit_update ( VIK_LAYER(vtl) ); return TRUE; @@ -8995,7 +9242,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; @@ -9128,6 +9375,7 @@ static void marker_begin_move ( tool_ed_t *t, gint x, gint y ) vik_viewport_sync(t->vvp); t->oldx = x; t->oldy = y; + t->moving = FALSE; } static void marker_moveto ( tool_ed_t *t, gint x, gint y ) @@ -9137,6 +9385,7 @@ static void marker_moveto ( tool_ed_t *t, gint x, gint y ) vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, x-3, y-3, 6, 6 ); t->oldx = x; t->oldy = y; + t->moving = TRUE; if (tool_sync_done) { g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, tool_sync, vvp, NULL); @@ -9149,6 +9398,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 ****/ @@ -9382,6 +9632,15 @@ static gchar* distance_string (gdouble distance) g_sprintf(str, "%d miles", (int)VIK_METERS_TO_MILES(distance)); } break; + case VIK_UNITS_DISTANCE_NAUTICAL_MILES: + if (distance >= VIK_NAUTICAL_MILES_TO_METERS(1) && distance < VIK_NAUTICAL_MILES_TO_METERS(100)) { + g_sprintf(str, "%3.2f NM", VIK_METERS_TO_NAUTICAL_MILES(distance)); + } else if (distance < VIK_NAUTICAL_MILES_TO_METERS(1)) { + g_sprintf(str, "%d yards", (int)(distance*1.0936133)); + } else { + g_sprintf(str, "%d NM", (int)VIK_METERS_TO_NAUTICAL_MILES(distance)); + } + break; default: // VIK_UNITS_DISTANCE_KILOMETRES if (distance >= 1000 && distance < 100000) { @@ -9582,6 +9841,13 @@ static void undo_trackpoint_add ( VikTrwLayer *vtl ) static gboolean tool_new_track_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp ) { if ( vtl->current_track && event->keyval == GDK_Escape ) { + // Bin track if only one point as it's not very useful + if ( vik_track_get_tp_count(vtl->current_track) == 1 ) { + if ( vtl->current_track->is_route ) + vik_trw_layer_delete_route ( vtl, vtl->current_track ); + else + vik_trw_layer_delete_track ( vtl, vtl->current_track ); + } vtl->current_track = NULL; vik_layer_emit_update ( VIK_LAYER(vtl) ); return TRUE; @@ -9676,13 +9942,13 @@ static gboolean tool_new_track_click ( VikTrwLayer *vtl, GdkEventButton *event, if ( event->button == 1 && ( ! vtl->current_track || (vtl->current_track && vtl->current_track->is_route ) )) { gchar *name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, _("Track")); - if ( ( name = a_dialog_new_track ( VIK_GTK_WINDOW_FROM_LAYER(vtl), name, FALSE ) ) ) - { - new_track_create_common ( vtl, name ); - g_free ( name ); + if ( a_vik_get_ask_for_create_track_name() ) { + name = a_dialog_new_track ( VIK_GTK_WINDOW_FROM_LAYER(vtl), name, FALSE ); + if ( !name ) + return FALSE; } - else - return TRUE; + new_track_create_common ( vtl, name ); + g_free ( name ); } return tool_new_track_or_route_click ( vtl, event, vvp ); } @@ -9713,12 +9979,13 @@ static gboolean tool_new_route_click ( VikTrwLayer *vtl, GdkEventButton *event, (vtl->current_track && !vtl->current_track->is_route ) ) ) { gchar *name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, _("Route")); - if ( ( name = a_dialog_new_track ( VIK_GTK_WINDOW_FROM_LAYER(vtl), name, TRUE ) ) ) { - new_route_create_common ( vtl, name ); - g_free ( name ); + if ( a_vik_get_ask_for_create_track_name() ) { + name = a_dialog_new_track ( VIK_GTK_WINDOW_FROM_LAYER(vtl), name, TRUE ); + if ( !name ) + return FALSE; } - else - return TRUE; + new_route_create_common ( vtl, name ); + g_free ( name ); } return tool_new_track_or_route_click ( vtl, event, vvp ); } @@ -9904,7 +10171,7 @@ 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 ); + my_tpwin_set_tp ( vtl ); vik_layer_emit_update ( VIK_LAYER(vtl) ); return TRUE;