X-Git-Url: https://git.street.me.uk/andy/viking.git/blobdiff_plain/03c97bc359e2823b1c968f82128e3d850576359b..1417eec83c55bc61a970a88222f0db5b6efee437:/src/viktrwlayer.c diff --git a/src/viktrwlayer.c b/src/viktrwlayer.c index 9f75cd5c..720ff8ff 100644 --- a/src/viktrwlayer.c +++ b/src/viktrwlayer.c @@ -59,10 +59,9 @@ #include "acquire.h" #include "datasources.h" #include "datasource_gps.h" +#include "vikexttools.h" #include "vikexttool_datasources.h" #include "ui_util.h" -#include "vikutils.h" - #include "vikrouting.h" #include "icons/icons.h" @@ -179,7 +178,6 @@ struct _VikTrwLayer { GdkGC *waypoint_text_gc; GdkColor waypoint_text_color; GdkGC *waypoint_bg_gc; GdkColor waypoint_bg_color; gboolean wpbgand; - GdkFont *waypoint_font; VikTrack *current_track; // ATM shared between new tracks and new routes guint16 ct_x1, ct_y1, ct_x2, ct_y2; gboolean draw_sync_done; @@ -369,6 +367,8 @@ static void trw_layer_cancel_current_tp ( VikTrwLayer *vtl, gboolean destroy ); static void trw_layer_tpwin_response ( VikTrwLayer *vtl, gint response ); static void trw_layer_tpwin_init ( VikTrwLayer *vtl ); +static void trw_layer_sort_all ( VikTrwLayer *vtl ); + static gpointer tool_edit_trackpoint_create ( VikWindow *vw, VikViewport *vvp); static void tool_edit_trackpoint_destroy ( tool_ed_t *t ); static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data ); @@ -687,7 +687,7 @@ enum { /* Layer Interface function definitions */ static VikTrwLayer* trw_layer_create ( VikViewport *vp ); static void trw_layer_realize ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter ); -static void trw_layer_post_read ( VikTrwLayer *vtl, GtkWidget *vvp, gboolean from_file ); +static void trw_layer_post_read ( VikTrwLayer *vtl, VikViewport *vvp, gboolean from_file ); static void trw_layer_free ( VikTrwLayer *trwlayer ); static void trw_layer_draw ( VikTrwLayer *l, gpointer data ); static void trw_layer_change_coord_mode ( VikTrwLayer *vtl, VikCoordMode dest_mode ); @@ -824,15 +824,17 @@ static void vik_trwlayer_class_init ( VikTrwLayerClass *klass ) else if ( mystderr ) tokens = g_strsplit(mystderr, " ", 0); - gint num = 0; - gchar *token = tokens[num]; - while ( token && num < 2 ) { - if (num == 1) { - if ( viking_version_to_number(token) >= viking_version_to_number("1.7.3") ) - have_diary_program = TRUE; + if ( tokens ) { + gint num = 0; + gchar *token = tokens[num]; + while ( token && num < 2 ) { + if (num == 1) { + if ( viking_version_to_number(token) >= viking_version_to_number("1.7.3") ) + have_diary_program = TRUE; + } + num++; + token = tokens[num]; } - num++; - token = tokens[num]; } g_strfreev ( tokens ); } @@ -913,8 +915,8 @@ typedef struct { const gchar *date_str; const VikTrack *trk; const VikWaypoint *wpt; - gpointer *trk_id; - gpointer *wpt_id; + gpointer trk_id; + gpointer wpt_id; } date_finder_type; static gboolean trw_layer_find_date_track ( const gpointer id, const VikTrack *trk, date_finder_type *df ) @@ -1026,7 +1028,7 @@ static void trw_layer_copy_item_cb ( menu_array_sublayer values) { VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]); gint subtype = GPOINTER_TO_INT (values[MA_SUBTYPE]); - gpointer * sublayer = values[MA_SUBLAYER_ID]; + gpointer sublayer = values[MA_SUBLAYER_ID]; guint8 *data = NULL; guint len; @@ -1169,6 +1171,12 @@ static void trw_layer_free_copied_item ( gint subtype, gpointer item ) } } +static void image_cache_free ( VikTrwLayer *vtl ) +{ + g_list_foreach ( vtl->image_cache->head, (GFunc)cached_pixbuf_free, NULL ); + g_queue_free ( vtl->image_cache ); +} + static gboolean trw_layer_set_param ( VikTrwLayer *vtl, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation ) { switch ( id ) @@ -1240,12 +1248,17 @@ static gboolean trw_layer_set_param ( VikTrwLayer *vtl, guint16 id, VikLayerPara case PARAM_IS: if ( data.u != vtl->image_size ) { vtl->image_size = data.u; - g_list_foreach ( vtl->image_cache->head, (GFunc) cached_pixbuf_free, NULL ); - g_queue_free ( vtl->image_cache ); + image_cache_free ( vtl ); + vtl->image_cache = g_queue_new (); + } + break; + case PARAM_IA: if ( data.u != vtl->image_alpha ) + { + vtl->image_alpha = data.u; + image_cache_free ( vtl ); vtl->image_cache = g_queue_new (); } break; - case PARAM_IA: vtl->image_alpha = data.u; break; case PARAM_ICS: vtl->image_cache_size = data.u; while ( vtl->image_cache->length > vtl->image_cache_size ) /* if shrinking cache_size, free pixbuf ASAP */ cached_pixbuf_free ( g_queue_pop_tail ( vtl->image_cache ) ); @@ -1522,23 +1535,27 @@ static VikTrwLayer *trw_layer_unmarshall( guint8 *data, gint len, VikViewport *v // Reuse pl to read the subtype from the data stream memcpy(&pl, data+sizeof(gint), sizeof(pl)); + // Also remember to (attempt to) convert each coordinate in case this is pasted into a different drawmode if ( pl == VIK_TRW_LAYER_SUBLAYER_TRACK ) { VikTrack *trk = vik_track_unmarshall ( data + sizeof_len_and_subtype, 0 ); gchar *name = g_strdup ( trk->name ); vik_trw_layer_add_track ( vtl, name, trk ); g_free ( name ); + vik_track_convert (trk, vtl->coord_mode); } if ( pl == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) { VikWaypoint *wp = vik_waypoint_unmarshall ( data + sizeof_len_and_subtype, 0 ); gchar *name = g_strdup ( wp->name ); vik_trw_layer_add_waypoint ( vtl, name, wp ); g_free ( name ); + waypoint_convert (NULL, wp, &vtl->coord_mode); } if ( pl == VIK_TRW_LAYER_SUBLAYER_ROUTE ) { VikTrack *trk = vik_track_unmarshall ( data + sizeof_len_and_subtype, 0 ); gchar *name = g_strdup ( trk->name ); vik_trw_layer_add_route ( vtl, name, trk ); g_free ( name ); + vik_track_convert (trk, vtl->coord_mode); } } consumed_length += tlm_size + sizeof_len_and_subtype; @@ -1614,6 +1631,7 @@ static VikTrwLayer* trw_layer_new1 ( VikViewport *vvp ) rv->metadata = vik_trw_metadata_new (); rv->draw_sync_done = TRUE; rv->draw_sync_do = TRUE; + rv->coord_mode = VIK_COORD_LATLON; // Everything else is 0, FALSE or NULL return rv; @@ -1662,8 +1680,7 @@ static void trw_layer_free ( VikTrwLayer *trwlayer ) if ( trwlayer->tracks_analysis_dialog != NULL ) gtk_widget_destroy ( GTK_WIDGET(trwlayer->tracks_analysis_dialog) ); - g_list_foreach ( trwlayer->image_cache->head, (GFunc) cached_pixbuf_free, NULL ); - g_queue_free ( trwlayer->image_cache ); + image_cache_free ( trwlayer ); } static void init_drawing_params ( struct DrawingParams *dp, VikTrwLayer *vtl, VikViewport *vp, gboolean highlight ) @@ -1858,6 +1875,9 @@ static void trw_layer_draw_dist_labels ( struct DrawingParams *dp, VikTrack *trk case VIK_UNITS_DISTANCE_MILES: units = g_strdup ( _("miles") ); break; + case VIK_UNITS_DISTANCE_NAUTICAL_MILES: + units = g_strdup ( _("NM") ); + break; // VIK_UNITS_DISTANCE_KILOMETRES: default: units = g_strdup ( _("km") ); @@ -2008,6 +2028,40 @@ static void trw_layer_draw_track_name_labels ( struct DrawingParams *dp, VikTrac g_free ( ename ); } + +/** + * trw_layer_draw_point_names: + * + * Draw a point labels along a track + * This might slow things down if there's many tracks being displayed with this on. + */ +static void trw_layer_draw_point_names ( struct DrawingParams *dp, VikTrack *trk, gboolean drawing_highlight ) +{ + GList *list = trk->trackpoints; + if (!list) return; + VikTrackpoint *tp = VIK_TRACKPOINT(list->data); + gchar *fgcolour; + if ( dp->vtl->drawmode == DRAWMODE_BY_TRACK ) + fgcolour = gdk_color_to_string ( &(trk->color) ); + else + fgcolour = gdk_color_to_string ( &(dp->vtl->track_color) ); + gchar *bgcolour; + if ( drawing_highlight ) + bgcolour = g_strdup ( vik_viewport_get_highlight_color ( dp->vp ) ); + else + bgcolour = gdk_color_to_string ( &(dp->vtl->track_bg_color) ); + if ( tp->name ) + trw_layer_draw_track_label ( tp->name, fgcolour, bgcolour, dp, &tp->coord ); + while ((list = g_list_next(list))) + { + tp = VIK_TRACKPOINT(list->data); + if ( tp->name ) + trw_layer_draw_track_label ( tp->name, fgcolour, bgcolour, dp, &tp->coord ); + }; + g_free ( fgcolour ); + g_free ( bgcolour ); +} + static void trw_layer_draw_track ( const gpointer id, VikTrack *track, struct DrawingParams *dp, gboolean draw_track_outline ) { if ( ! track->visible ) @@ -2108,6 +2162,16 @@ static void trw_layer_draw_track ( const gpointer id, VikTrack *track, struct Dr tp = VIK_TRACKPOINT(list->data); tp_size = (list == dp->vtl->current_tpl) ? tp_size_cur : tp_size_reg; + VikTrackpoint *tp2 = VIK_TRACKPOINT(list->prev->data); + // See if in a different lat/lon 'quadrant' so don't draw massively long lines (presumably wrong way around the Earth) + // Mainly to prevent wrong lines drawn when a track crosses the 180 degrees East-West longitude boundary + // (since vik_viewport_draw_line() only copes with pixel value and has no concept of the globe) + if ( dp->lat_lon && + (( tp2->coord.east_west < -90.0 && tp->coord.east_west > 90.0 ) || + ( tp2->coord.east_west > 90.0 && tp->coord.east_west < -90.0 )) ) { + useoldvals = FALSE; + continue; + } /* check some stuff -- but only if we're in UTM and there's only ONE ZONE; or lat lon */ if ( (!dp->one_zone && !dp->lat_lon) || /* UTM & zones; do everything */ ( ((!dp->one_zone) || tp->coord.utm_zone == dp->center->utm_zone) && /* only check zones if UTM & one_zone */ @@ -2124,12 +2188,11 @@ static void trw_layer_draw_track ( const gpointer id, VikTrack *track, struct Dr // Still need to process points to ensure 'stops' are drawn if required if ( drawstops && drawpoints && ! draw_track_outline && list->next && (VIK_TRACKPOINT(list->next->data)->timestamp - VIK_TRACKPOINT(list->data)->timestamp > dp->vtl->stop_length) ) - vik_viewport_draw_arc ( dp->vp, g_array_index(dp->vtl->track_gc, GdkGC *, 11), TRUE, x-(3*tp_size), y-(3*tp_size), 6*tp_size, 6*tp_size, 0, 360*64 ); + vik_viewport_draw_arc ( dp->vp, g_array_index(dp->vtl->track_gc, GdkGC *, VIK_TRW_LAYER_TRACK_GC_STOP), TRUE, x-(3*tp_size), y-(3*tp_size), 6*tp_size, 6*tp_size, 0, 360*64 ); goto skip; } - VikTrackpoint *tp2 = VIK_TRACKPOINT(list->prev->data); if ( drawpoints || dp->vtl->drawlines ) { // setup main_gc for both point and line drawing if ( !drawing_highlight && (dp->vtl->drawmode == DRAWMODE_BY_SPEED) ) { @@ -2227,7 +2290,6 @@ static void trw_layer_draw_track ( const gpointer id, VikTrack *track, struct Dr else { if (useoldvals && dp->vtl->drawlines && (!tp->newsegment)) { - VikTrackpoint *tp2 = VIK_TRACKPOINT(list->prev->data); if ( dp->vtl->coord_mode != VIK_COORD_UTM || tp->coord.utm_zone == dp->center->utm_zone ) { vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y ); @@ -2268,6 +2330,7 @@ static void trw_layer_draw_track ( const gpointer id, VikTrack *track, struct Dr if ( track->max_number_dist_labels > 0 ) { trw_layer_draw_dist_labels ( dp, track, drawing_highlight ); } + trw_layer_draw_point_names (dp, track, drawing_highlight ); if ( track->draw_name_mode != TRACK_DRAWNAME_NO ) { trw_layer_draw_track_name_labels ( dp, track, drawing_highlight ); @@ -2340,6 +2403,10 @@ static void trw_layer_draw_waypoint ( const gpointer id, VikWaypoint *wp, struct } cp->image = g_strdup ( image ); + // Apply alpha setting to the image before the pixbuf gets stored in the cache + if ( dp->vtl->image_alpha != 255 ) + cp->pixbuf = ui_pixbuf_set_alpha ( cp->pixbuf, dp->vtl->image_alpha ); + /* needed so 'click picture' tool knows how big the pic is; we don't * store it in cp because they may have been freed already. */ wp->image_width = gdk_pixbuf_get_width ( cp->pixbuf ); @@ -2373,10 +2440,7 @@ static void trw_layer_draw_waypoint ( const gpointer id, VikWaypoint *wp, struct x - (w/2) - 2, y - (h/2) - 2, w + 4, h + 4 ); } - if ( dp->vtl->image_alpha == 255 ) - vik_viewport_draw_pixbuf ( dp->vp, pixbuf, 0, 0, x - (w/2), y - (h/2), w, h ); - else - vik_viewport_draw_pixbuf_with_alpha ( dp->vp, pixbuf, dp->vtl->image_alpha, 0, 0, x - (w/2), y - (h/2), w, h ); + vik_viewport_draw_pixbuf ( dp->vp, pixbuf, 0, 0, x - (w/2), y - (h/2), w, h ); } return; /* if failed to draw picture, default to drawing regular waypoint (below) */ } @@ -2759,6 +2823,9 @@ static void trw_layer_realize ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter * vik_treeview_item_set_visible ( (VikTreeview *) vt, &(vtl->waypoints_iter), vtl->waypoints_visible ); } + trw_layer_verify_thumbnails ( vtl ); + + trw_layer_sort_all ( vtl ); } static gboolean trw_layer_sublayer_toggle_visible ( VikTrwLayer *l, gint subtype, gpointer sublayer ) @@ -2805,6 +2872,14 @@ gint vik_trw_layer_get_property_tracks_line_thickness ( VikTrwLayer *vtl ) return vtl->line_thickness; } +/* + * Build up multiple routes information + */ +static void trw_layer_routes_tooltip ( const gpointer id, VikTrack *tr, gdouble *length ) +{ + *length = *length + vik_track_get_length (tr); +} + // Structure to hold multiple track information for a layer typedef struct { gdouble length; @@ -2816,7 +2891,7 @@ 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); @@ -2858,7 +2933,7 @@ static void trw_layer_tracks_tooltip ( const gchar *name, VikTrack *tr, tooltip_ */ static const gchar* trw_layer_layer_tooltip ( VikTrwLayer *vtl ) { - gchar tbuf1[32]; + gchar tbuf1[64]; gchar tbuf2[64]; gchar tbuf3[64]; gchar tbuf4[10]; @@ -2918,23 +2993,45 @@ static const gchar* trw_layer_layer_tooltip ( VikTrwLayer *vtl ) // 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; @@ -2987,9 +3084,9 @@ static const gchar* trw_layer_sublayer_tooltip ( VikTrwLayer *l, gint subtype, g if ( tr->trackpoints && vik_track_get_tp_first(tr)->has_timestamp ) { // %x The preferred date representation for the current locale without the time. strftime (time_buf1, sizeof(time_buf1), "%x: ", gmtime(&(vik_track_get_tp_first(tr)->timestamp))); - time_t dur = vik_track_get_duration ( tr ); + time_t dur = vik_track_get_duration ( tr, TRUE ); if ( dur > 0 ) - g_snprintf ( time_buf2, sizeof(time_buf2), _("- %d:%02d hrs:mins"), (int)round(dur/3600), (int)round((dur/60)%60) ); + 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); @@ -3224,7 +3321,7 @@ GHashTable *vik_trw_layer_get_routes_iters ( VikTrwLayer *vtl ) GHashTable *vik_trw_layer_get_waypoints_iters ( VikTrwLayer *vtl ) { - return vtl->waypoints; + return vtl->waypoints_iters; } gboolean vik_trw_layer_is_empty ( VikTrwLayer *vtl ) @@ -3351,51 +3448,7 @@ static void trw_layer_centerize ( menu_array_layer values ) void trw_layer_zoom_to_show_latlons ( VikTrwLayer *vtl, VikViewport *vvp, struct LatLon maxmin[2] ) { - /* First set the center [in case previously viewing from elsewhere] */ - /* Then loop through zoom levels until provided positions are in view */ - /* This method is not particularly fast - but should work well enough */ - struct LatLon average = { (maxmin[0].lat+maxmin[1].lat)/2, (maxmin[0].lon+maxmin[1].lon)/2 }; - VikCoord coord; - vik_coord_load_from_latlon ( &coord, vtl->coord_mode, &average ); - vik_viewport_set_center_coord ( vvp, &coord, TRUE ); - - /* Convert into definite 'smallest' and 'largest' positions */ - struct LatLon minmin; - if ( maxmin[0].lat < maxmin[1].lat ) - minmin.lat = maxmin[0].lat; - else - minmin.lat = maxmin[1].lat; - - struct LatLon maxmax; - if ( maxmin[0].lon > maxmin[1].lon ) - maxmax.lon = maxmin[0].lon; - else - maxmax.lon = maxmin[1].lon; - - /* Never zoom in too far - generally not that useful, as too close ! */ - /* Always recalculate the 'best' zoom level */ - gdouble zoom = 1.0; - vik_viewport_set_zoom ( vvp, zoom ); - - gdouble min_lat, max_lat, min_lon, max_lon; - /* Should only be a maximum of about 18 iterations from min to max zoom levels */ - while ( zoom <= VIK_VIEWPORT_MAX_ZOOM ) { - vik_viewport_get_min_max_lat_lon ( vvp, &min_lat, &max_lat, &min_lon, &max_lon ); - /* NB I think the logic used in this test to determine if the bounds is within view - fails if track goes across 180 degrees longitude. - Hopefully that situation is not too common... - Mind you viking doesn't really do edge locations to well anyway */ - if ( min_lat < minmin.lat && - max_lat > minmin.lat && - min_lon < maxmax.lon && - max_lon > maxmax.lon ) - /* Found within zoom level */ - break; - - /* Try next */ - zoom = zoom * 2; - vik_viewport_set_zoom ( vvp, zoom ); - } + vu_zoom_to_show_latlons ( vtl->coord_mode, vvp, maxmin ); } gboolean vik_trw_layer_auto_set_view ( VikTrwLayer *vtl, VikViewport *vvp ) @@ -3532,13 +3585,13 @@ static void trw_layer_goto_wp ( menu_array_layer values ) GtkWidget *label, *entry; label = gtk_label_new(_("Waypoint Name:")); - entry = gtk_entry_new(); + entry = ui_entry_new ( NULL, GTK_ENTRY_ICON_SECONDARY ); gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dia))), label, FALSE, FALSE, 0); gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dia))), entry, FALSE, FALSE, 0); - gtk_widget_show_all ( label ); - gtk_widget_show_all ( entry ); - + gtk_widget_show_all ( dia ); + // 'ok' when press return in the entry + g_signal_connect_swapped ( entry, "activate", G_CALLBACK(a_dialog_response_accept), dia ); gtk_dialog_set_default_response ( GTK_DIALOG(dia), GTK_RESPONSE_ACCEPT ); while ( gtk_dialog_run ( GTK_DIALOG(dia) ) == GTK_RESPONSE_ACCEPT ) @@ -3560,7 +3613,7 @@ static void trw_layer_goto_wp ( menu_array_layer values ) udata.uuid = NULL; // Hmmm, want key of it - gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata ); + gpointer wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata ); if ( wpf && udata.uuid ) { GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udata.uuid ); @@ -3765,7 +3818,7 @@ static void trw_layer_acquire_geotagged_cb ( menu_array_layer values ) // Reverify thumbnails as they may have changed vtl->has_verified_thumbnails = FALSE; - trw_layer_verify_thumbnails ( vtl, NULL ); + trw_layer_verify_thumbnails ( vtl ); } #endif @@ -3890,9 +3943,10 @@ static void trw_layer_new_wp ( menu_array_layer values ) 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 ); } } @@ -4025,6 +4079,17 @@ void trw_layer_osm_traces_upload_track_cb ( menu_array_sublayer values ) } } +static GtkWidget* create_external_submenu ( GtkMenu *menu ) +{ + GtkWidget *external_submenu = gtk_menu_new (); + GtkWidget *item = gtk_image_menu_item_new_with_mnemonic ( _("Externa_l") ); + gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_EXECUTE, GTK_ICON_SIZE_MENU) ); + gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); + gtk_widget_show ( item ); + gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), external_submenu ); + return external_submenu; +} + static void trw_layer_add_menu_items ( VikTrwLayer *vtl, GtkMenu *menu, gpointer vlp ) { static menu_array_layer pass_along; @@ -4114,10 +4179,12 @@ 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 ); + } if ( have_geojson_export ) { item = gtk_menu_item_new_with_mnemonic ( _("Export as GEO_JSON...") ); @@ -4126,10 +4193,12 @@ static void trw_layer_add_menu_items ( VikTrwLayer *vtl, GtkMenu *menu, gpointer 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 ( 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 ); @@ -4251,11 +4320,13 @@ static void trw_layer_add_menu_items ( VikTrwLayer *vtl, GtkMenu *menu, gpointer gtk_widget_show ( item ); #endif - item = gtk_menu_item_new_with_mnemonic ( _("From _File...") ); - g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_file_cb), pass_along ); - gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item); - gtk_widget_set_tooltip_text (item, _("Import File With GPS_Babel...")); - gtk_widget_show ( item ); + if ( a_babel_available () ) { + item = gtk_menu_item_new_with_mnemonic ( _("From _File...") ); + g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_file_cb), pass_along ); + gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item); + gtk_widget_set_tooltip_text (item, _("Import File With GPS_Babel...")); + gtk_widget_show ( item ); + } vik_ext_tool_datasources_add_menu_items_to_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), GTK_MENU (acquire_submenu) ); @@ -4350,6 +4421,10 @@ static void trw_layer_add_menu_items ( VikTrwLayer *vtl, GtkMenu *menu, gpointer gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item ); gtk_widget_show ( item ); gtk_widget_set_sensitive ( item, (gboolean)(g_hash_table_size (vtl->waypoints)) ); + + GtkWidget *external_submenu = create_external_submenu ( menu ); + // TODO: Should use selected layer's centre - rather than implicitly using the current viewport + vik_ext_tools_add_menu_items_to_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), GTK_MENU (external_submenu), NULL ); } // Fake Waypoint UUIDs vi simple increasing integer @@ -4518,7 +4593,19 @@ gchar *trw_layer_new_unique_sublayer_name (VikTrwLayer *vtl, gint sublayer_type, } // If found a name already in use try adding 1 to it and we try again if ( id ) { - gchar *new_newname = g_strdup_printf("%s#%d", name, i); + const gchar *corename = newname; + gint newi = i; + // If name is already of the form text#N + // set name to text and i to N+1 + gchar **tokens = g_regex_split_simple ( "#(\\d+)", newname, G_REGEX_CASELESS, 0 ); + if ( tokens ) { + corename = tokens[0]; + if ( tokens[1] ) { + newi = atoi ( tokens[1] ) + 1; + } + } + gchar *new_newname = g_strdup_printf("%s#%d", corename, newi); + g_strfreev ( tokens ); g_free(newname); newname = new_newname; i++; @@ -4543,10 +4630,12 @@ void vik_trw_layer_filein_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *t // enforce end of current track equal to start of tr VikTrackpoint *cur_end = vik_track_get_tp_last ( vtl->current_track ); VikTrackpoint *new_start = vik_track_get_tp_first ( tr ); - if ( ! vik_coord_equals ( &cur_end->coord, &new_start->coord ) ) { - vik_track_add_trackpoint ( vtl->current_track, - vik_trackpoint_copy ( cur_end ), - FALSE ); + if ( cur_end && new_start ) { + if ( ! vik_coord_equals ( &cur_end->coord, &new_start->coord ) ) { + vik_track_add_trackpoint ( vtl->current_track, + vik_trackpoint_copy ( cur_end ), + FALSE ); + } } vik_track_steal_and_append_trackpoints ( vtl->current_track, tr ); @@ -4577,34 +4666,31 @@ static void trw_layer_enum_item ( gpointer id, GList **tr, GList **l ) */ static void trw_layer_move_item ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, gpointer id, gint type ) { + // When an item is moved the name is checked to see if it clashes with an existing name + // in the destination layer and if so then it is allocated a new name + // TODO reconsider strategy when moving within layer (if anything...) - gboolean rename = ( vtl_src != vtl_dest ); - if ( ! rename ) + if ( vtl_src == vtl_dest ) return; if (type == VIK_TRW_LAYER_SUBLAYER_TRACK) { VikTrack *trk = g_hash_table_lookup ( vtl_src->tracks, id ); - gchar *newname; - if ( rename ) - newname = trw_layer_new_unique_sublayer_name ( vtl_dest, type, trk->name ); - else - newname = g_strdup ( trk->name ); + gchar *newname = trw_layer_new_unique_sublayer_name ( vtl_dest, type, trk->name ); VikTrack *trk2 = vik_track_copy ( trk, TRUE ); vik_trw_layer_add_track ( vtl_dest, newname, trk2 ); g_free ( newname ); vik_trw_layer_delete_track ( vtl_src, trk ); + // Reset layer timestamps in case they have now changed + vik_treeview_item_set_timestamp ( vtl_dest->vl.vt, &vtl_dest->vl.iter, trw_layer_get_timestamp(vtl_dest) ); + vik_treeview_item_set_timestamp ( vtl_src->vl.vt, &vtl_src->vl.iter, trw_layer_get_timestamp(vtl_src) ); } if (type == VIK_TRW_LAYER_SUBLAYER_ROUTE) { VikTrack *trk = g_hash_table_lookup ( vtl_src->routes, id ); - gchar *newname; - if ( rename ) - newname = trw_layer_new_unique_sublayer_name ( vtl_dest, type, trk->name ); - else - newname = g_strdup ( trk->name ); + gchar *newname = trw_layer_new_unique_sublayer_name ( vtl_dest, type, trk->name ); VikTrack *trk2 = vik_track_copy ( trk, TRUE ); vik_trw_layer_add_route ( vtl_dest, newname, trk2 ); @@ -4615,11 +4701,7 @@ static void trw_layer_move_item ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, g if (type == VIK_TRW_LAYER_SUBLAYER_WAYPOINT) { VikWaypoint *wp = g_hash_table_lookup ( vtl_src->waypoints, id ); - gchar *newname; - if ( rename ) - newname = trw_layer_new_unique_sublayer_name ( vtl_dest, type, wp->name ); - else - newname = g_strdup ( wp->name ); + gchar *newname = trw_layer_new_unique_sublayer_name ( vtl_dest, type, wp->name ); VikWaypoint *wp2 = vik_waypoint_copy ( wp ); vik_trw_layer_add_waypoint ( vtl_dest, newname, wp2 ); @@ -4629,6 +4711,9 @@ static void trw_layer_move_item ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, g // Recalculate bounds even if not renamed as maybe dragged between layers trw_layer_calculate_bounds_waypoints ( vtl_dest ); trw_layer_calculate_bounds_waypoints ( vtl_src ); + // Reset layer timestamps in case they have now changed + vik_treeview_item_set_timestamp ( vtl_dest->vl.vt, &vtl_dest->vl.iter, trw_layer_get_timestamp(vtl_dest) ); + vik_treeview_item_set_timestamp ( vtl_src->vl.vt, &vtl_src->vl.iter, trw_layer_get_timestamp(vtl_src) ); } } @@ -4703,7 +4788,7 @@ gboolean vik_trw_layer_delete_track ( VikTrwLayer *vtl, VikTrack *trk ) udata.uuid = NULL; // Hmmm, want key of it - gpointer *trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata ); + gpointer trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata ); if ( trkf && udata.uuid ) { /* could be current_tp, so we have to check */ @@ -4751,7 +4836,7 @@ gboolean vik_trw_layer_delete_route ( VikTrwLayer *vtl, VikTrack *trk ) udata.uuid = NULL; // Hmmm, want key of it - gpointer *trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata ); + gpointer trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata ); if ( trkf && udata.uuid ) { /* could be current_tp, so we have to check */ @@ -4795,7 +4880,7 @@ static gboolean trw_layer_delete_waypoint ( VikTrwLayer *vtl, VikWaypoint *wp ) udata.uuid = NULL; // Hmmm, want key of it - gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata ); + gpointer wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata ); if ( wpf && udata.uuid ) { GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udata.uuid ); @@ -4847,7 +4932,7 @@ static gboolean trw_layer_delete_waypoint_by_name ( VikTrwLayer *vtl, const gcha udata.uuid = NULL; // Hmmm, want key of it - gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid_by_name, (gpointer) &udata ); + gpointer wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid_by_name, (gpointer) &udata ); vik_waypoint_free (udata.wp); @@ -4888,7 +4973,7 @@ static gboolean trw_layer_delete_track_by_name ( VikTrwLayer *vtl, const gchar * udata.uuid = NULL; // Hmmm, want key of it - gpointer *trkf = g_hash_table_find ( ht_tracks, (GHRFunc) trw_layer_track_find_uuid_by_name, &udata ); + gpointer trkf = g_hash_table_find ( ht_tracks, (GHRFunc) trw_layer_track_find_uuid_by_name, &udata ); vik_track_free (udata.trk); @@ -4919,9 +5004,10 @@ void vik_trw_layer_delete_all_routes ( VikTrwLayer *vtl ) g_hash_table_foreach(vtl->routes_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt); g_hash_table_remove_all(vtl->routes_iters); - g_hash_table_remove_all(vtl->routes); - vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter) ); + if ( g_hash_table_size (vtl->routes) > 0 ) + vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter) ); + g_hash_table_remove_all(vtl->routes); vik_layer_emit_update ( VIK_LAYER(vtl) ); } @@ -4936,9 +5022,10 @@ void vik_trw_layer_delete_all_tracks ( VikTrwLayer *vtl ) g_hash_table_foreach(vtl->tracks_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt); g_hash_table_remove_all(vtl->tracks_iters); - g_hash_table_remove_all(vtl->tracks); - vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter) ); + if ( g_hash_table_size (vtl->tracks) > 0 ) + vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter) ); + g_hash_table_remove_all(vtl->tracks); vik_layer_emit_update ( VIK_LAYER(vtl) ); } @@ -4953,9 +5040,10 @@ void vik_trw_layer_delete_all_waypoints ( VikTrwLayer *vtl ) g_hash_table_foreach(vtl->waypoints_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt); g_hash_table_remove_all(vtl->waypoints_iters); - g_hash_table_remove_all(vtl->waypoints); - vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter) ); + if ( g_hash_table_size (vtl->waypoints) > 0 ) + vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter) ); + g_hash_table_remove_all(vtl->waypoints); vik_layer_emit_update ( VIK_LAYER(vtl) ); } @@ -5007,6 +5095,8 @@ static void trw_layer_delete_item ( menu_array_sublayer values ) 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 ) @@ -5020,6 +5110,8 @@ static void trw_layer_delete_item ( menu_array_sublayer values ) trk->name ) ) return; was_visible = vik_trw_layer_delete_track ( vtl, trk ); + // Reset layer timestamp in case it has now changed + vik_treeview_item_set_timestamp ( vtl->vl.vt, &vtl->vl.iter, trw_layer_get_timestamp(vtl) ); } } else @@ -5052,7 +5144,7 @@ void trw_layer_waypoint_rename ( VikTrwLayer *vtl, VikWaypoint *wp, const gchar udataU.uuid = NULL; // Need key of it for treeview update - gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, &udataU ); + gpointer wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, &udataU ); if ( wpf && udataU.uuid ) { GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udataU.uuid ); @@ -5075,7 +5167,7 @@ void trw_layer_waypoint_reset_icon ( VikTrwLayer *vtl, VikWaypoint *wp ) udataU.uuid = NULL; // Need key of it for treeview update - gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, &udataU ); + gpointer wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, &udataU ); if ( wpf && udataU.uuid ) { GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udataU.uuid ); @@ -5162,7 +5254,7 @@ void trw_layer_update_treeview ( VikTrwLayer *vtl, VikTrack *trk ) udata.trk = trk; udata.uuid = NULL; - gpointer *trkf = NULL; + gpointer trkf = NULL; if ( trk->is_route ) trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata ); else @@ -5308,6 +5400,19 @@ static void trw_layer_anonymize_times ( menu_array_sublayer values ) vik_track_anonymize_times ( track ); } +static void trw_layer_interpolate_times ( menu_array_sublayer values ) +{ + VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL]; + VikTrack *track; + if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE ) + track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] ); + else + track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] ); + + if ( track ) + vik_track_interpolate_times ( track ); +} + static void trw_layer_extend_track_end ( menu_array_sublayer values ) { VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]); @@ -5795,11 +5900,11 @@ static void find_nearby_tracks_by_time (gpointer key, gpointer value, gpointer u guint threshold = GPOINTER_TO_UINT (((gpointer *)user_data)[2]); //g_print("Got track named %s, times %d, %d\n", trk->name, p1->timestamp, p2->timestamp); - if (! (abs(t1 - p2->timestamp) < threshold || - /* p1 p2 t1 t2 */ - abs(p1->timestamp - t2) < threshold) - /* t1 t2 p1 p2 */ - ) { + if (! (labs(t1 - p2->timestamp) < threshold || + /* p1 p2 t1 t2 */ + labs(p1->timestamp - t2) < threshold) + /* t1 t2 p1 p2 */ + ) { return; } } @@ -6255,7 +6360,7 @@ static void trw_layer_split_at_selected_trackpoint ( VikTrwLayer *vtl, gint subt udata.uuid = NULL; // Also need id of newly created track - gpointer *trkf; + gpointer trkf; if ( tr->is_route ) trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata ); else @@ -6725,7 +6830,12 @@ static void trw_layer_astro_open ( VikTrwLayer *vtl, const gchar *date_str, cons { GError *err = NULL; gchar *tmp; - g_file_open_tmp ( "vik-astro-XXXXXX.ini", &tmp, NULL ); + 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 ); @@ -6982,7 +7092,7 @@ static void vik_trw_layer_uniquify_tracks ( VikTrwLayer *vtl, VikLayersPanel *vl udataU.uuid = NULL; // Need want key of it for treeview update - gpointer *trkf = g_hash_table_find ( track_table, (GHRFunc) trw_layer_track_find_uuid, &udataU ); + gpointer trkf = g_hash_table_find ( track_table, (GHRFunc) trw_layer_track_find_uuid, &udataU ); if ( trkf && udataU.uuid ) { @@ -7000,6 +7110,7 @@ static void vik_trw_layer_uniquify_tracks ( VikTrwLayer *vtl, VikLayersPanel *vl 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; @@ -7105,6 +7216,9 @@ static void trw_layer_delete_tracks_from_selection ( menu_array_layer values ) trw_layer_delete_track_by_name (vtl, l->data, vtl->tracks); } g_list_free(delete_list); + // Reset layer timestamps in case they have now changed + vik_treeview_item_set_timestamp ( vtl->vl.vt, &vtl->vl.iter, trw_layer_get_timestamp(vtl) ); + vik_layer_emit_update( VIK_LAYER(vtl) ); } } @@ -7255,6 +7369,8 @@ static void vik_trw_layer_uniquify_waypoints ( VikTrwLayer *vtl, VikLayersPanel trw_layer_waypoint_rename ( vtl, waypoint, newname ); + g_free (newname); + // Start trying to find same names again... waypoint_names = NULL; g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names ); @@ -7316,6 +7432,8 @@ static void trw_layer_delete_waypoints_from_selection ( menu_array_layer values g_list_free(delete_list); trw_layer_calculate_bounds_waypoints ( vtl ); + // Reset layer timestamp in case it has now changed + vik_treeview_item_set_timestamp ( vtl->vl.vt, &vtl->vl.iter, trw_layer_get_timestamp(vtl) ); vik_layer_emit_update( VIK_LAYER(vtl) ); } @@ -7746,11 +7864,13 @@ static gboolean is_valid_geocache_name ( gchar *str ) return len >= 3 && len <= 7 && str[0] == 'G' && str[1] == 'C' && isalnum(str[2]) && (len < 4 || isalnum(str[3])) && (len < 5 || isalnum(str[4])) && (len < 6 || isalnum(str[5])) && (len < 7 || isalnum(str[6])); } +#ifndef WINDOWS static void trw_layer_track_use_with_filter ( menu_array_sublayer values ) { VikTrack *trk = g_hash_table_lookup ( VIK_TRW_LAYER(values[MA_VTL])->tracks, values[MA_SUBLAYER_ID] ); a_acquire_set_filter_track ( trk ); } +#endif #ifdef VIK_CONFIG_GOOGLE static gboolean is_valid_google_route ( VikTrwLayer *vtl, const gpointer track_id ) @@ -7763,7 +7883,7 @@ static void trw_layer_google_route_webpage ( menu_array_sublayer values ) { VikTrack *tr = g_hash_table_lookup ( VIK_TRW_LAYER(values[MA_VTL])->routes, values[MA_SUBLAYER_ID] ); if ( tr ) { - gchar *escaped = uri_escape ( tr->comment ); + gchar *escaped = g_uri_escape_string ( tr->comment, NULL, TRUE ); gchar *webpage = g_strdup_printf("http://maps.google.com/maps?f=q&hl=en&q=%s", escaped ); open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(values[MA_VTL])), webpage); g_free ( escaped ); @@ -8437,13 +8557,19 @@ static gboolean trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *men gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item ); gtk_widget_show ( item ); - // Routes don't have timestamps - so this is only available for tracks + // Routes don't have timestamps - so these are only available for tracks if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) { item = gtk_image_menu_item_new_with_mnemonic ( _("_Anonymize Times") ); g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_anonymize_times), pass_along ); gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item ); gtk_widget_set_tooltip_text (item, _("Shift timestamps to a relative offset from 1901-01-01")); gtk_widget_show ( item ); + + item = gtk_image_menu_item_new_with_mnemonic ( _("_Interpolate Times") ); + g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_interpolate_times), pass_along ); + gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item ); + gtk_widget_set_tooltip_text (item, _("Reset trackpoint timestamps between the first and last points such that track is traveled at equal speed")); + gtk_widget_show ( item ); } if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) @@ -8517,22 +8643,18 @@ static gboolean trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *men } } - // Only made available if a suitable program is installed + 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) ) { - 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(external_submenu), item ); + gtk_widget_set_tooltip_text (item, _("Open diary program at this date")); gtk_widget_show ( item ); } @@ -8540,10 +8662,27 @@ static gboolean trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *men 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 ) ) { @@ -8567,11 +8706,14 @@ static gboolean trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *men gtk_widget_show ( item ); #endif + // Currently filter with functions all use shellcommands and thus don't work in Windows +#ifndef WINDOWS item = gtk_image_menu_item_new_with_mnemonic ( _("Use with _Filter") ); gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) ); g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_use_with_filter), pass_along ); gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item ); gtk_widget_show ( item ); +#endif /* ATM This function is only available via the layers panel, due to needing a vlp */ if ( vlp ) { @@ -8928,7 +9070,7 @@ typedef struct { gint x, y; gint closest_x, closest_y; gboolean draw_images; - gpointer *closest_wp_id; + gpointer closest_wp_id; VikWaypoint *closest_wp; VikViewport *vvp; } WPSearchParams; @@ -9323,7 +9465,7 @@ static gboolean trw_layer_show_selected_viewport_menu ( VikTrwLayer *vtl, GdkEve udataU.trk = track; udataU.uuid = NULL; - gpointer *trkf; + gpointer trkf; if ( track->is_route ) trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udataU ); else @@ -9366,7 +9508,7 @@ static gboolean trw_layer_show_selected_viewport_menu ( VikTrwLayer *vtl, GdkEve udata.wp = waypoint; udata.uuid = NULL; - gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata ); + gpointer wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata ); if ( wpf && udata.uuid ) { GtkTreeIter *iter = g_hash_table_lookup ( vtl->waypoints_iters, udata.uuid ); @@ -9394,10 +9536,8 @@ static gboolean tool_sync_done = TRUE; static gboolean tool_sync(gpointer data) { VikViewport *vvp = data; - gdk_threads_enter(); vik_viewport_sync(vvp); tool_sync_done = TRUE; - gdk_threads_leave(); return FALSE; } @@ -9473,8 +9613,8 @@ static gboolean tool_edit_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *eve gint x, y; vik_viewport_coord_to_screen ( vvp, &(vtl->current_wp->coord), &x, &y ); - if ( abs(x - event->x) <= WAYPOINT_SIZE_APPROX && - abs(y - event->y) <= WAYPOINT_SIZE_APPROX ) + if ( abs(x - (int)round(event->x)) <= WAYPOINT_SIZE_APPROX && + abs(y - (int)round(event->y)) <= WAYPOINT_SIZE_APPROX ) { if ( event->button == 3 ) vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */ @@ -9639,13 +9779,11 @@ static gboolean draw_sync ( gpointer data ) // normally because another update has taken precedent such as panning the display // which means this pixmap is no longer valid if ( ds->vtl->draw_sync_do ) { - gdk_threads_enter(); gdk_draw_drawable (ds->drawable, ds->gc, ds->pixmap, 0, 0, 0, 0, -1, -1); ds->vtl->draw_sync_done = TRUE; - gdk_threads_leave(); } g_free ( ds ); return FALSE; @@ -10038,9 +10176,10 @@ static gboolean tool_new_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *even if (!vtl || vtl->vl.type != VIK_LAYER_TRW) return FALSE; vik_viewport_screen_to_coord ( vvp, event->x, event->y, &coord ); - if (vik_trw_layer_new_waypoint ( vtl, VIK_GTK_WINDOW_FROM_LAYER(vtl), &coord ) && VIK_LAYER(vtl)->visible) { + if ( vik_trw_layer_new_waypoint (vtl, VIK_GTK_WINDOW_FROM_LAYER(vtl), &coord) ) { trw_layer_calculate_bounds_waypoints ( vtl ); - vik_layer_emit_update ( VIK_LAYER(vtl) ); + if ( VIK_LAYER(vtl)->visible ) + vik_layer_emit_update ( VIK_LAYER(vtl) ); } return TRUE; } @@ -10061,22 +10200,24 @@ static void tool_edit_trackpoint_destroy ( tool_ed_t *t ) g_free ( t ); } +/** + * tool_edit_trackpoint_click: + * + * On 'initial' click: search for the nearest trackpoint or routepoint and store it as the current trackpoint + * Then update the viewport, statusbar and edit dialog to draw the point as being selected and it's information. + * On subsequent clicks: (as the current trackpoint is defined) and the click is very near the same point + * then initiate the move operation to drag the point to a new destination. + * NB The current trackpoint will get reset elsewhere. + */ static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data ) { tool_ed_t *t = data; VikViewport *vvp = t->vvp; TPSearchParams params; - /* OUTDATED DOCUMENTATION: - find 5 pixel range on each side. then put these UTM, and a pointer - to the winning track name (and maybe the winning track itself), and a - pointer to the winning trackpoint, inside an array or struct. pass - this along, do a foreach on the tracks which will do a foreach on the - trackpoints. */ params.vvp = vvp; params.x = event->x; params.y = event->y; params.closest_track_id = NULL; - /* TODO: should get track listitem so we can break it up, make a new track, mess it up, all that. */ params.closest_tp = NULL; params.closest_tpl = NULL; vik_viewport_get_min_max_lat_lon ( vvp, &(params.bbox.south), &(params.bbox.north), &(params.bbox.west), &(params.bbox.east) ); @@ -10087,7 +10228,7 @@ static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *e if (!vtl || vtl->vl.type != VIK_LAYER_TRW) return FALSE; - if ( !vtl->vl.visible || !vtl->tracks_visible || !vtl->routes_visible ) + if ( !vtl->vl.visible || !(vtl->tracks_visible || vtl->routes_visible) ) return FALSE; if ( vtl->current_tpl ) @@ -10095,6 +10236,8 @@ static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *e /* first check if it is within range of prev. tp. and if current_tp track is shown. (if it is, we are moving that trackpoint.) */ VikTrackpoint *tp = VIK_TRACKPOINT(vtl->current_tpl->data); VikTrack *current_tr = VIK_TRACK(g_hash_table_lookup(vtl->tracks, vtl->current_tp_id)); + if ( !current_tr ) + current_tr = VIK_TRACK(g_hash_table_lookup(vtl->routes, vtl->current_tp_id)); if ( !current_tr ) return FALSE; @@ -10102,8 +10245,8 @@ static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *e vik_viewport_coord_to_screen ( vvp, &(tp->coord), &x, &y ); if ( current_tr->visible && - abs(x - event->x) < TRACKPOINT_SIZE_APPROX && - abs(y - event->y) < TRACKPOINT_SIZE_APPROX ) { + abs(x - (int)round(event->x)) < TRACKPOINT_SIZE_APPROX && + abs(y - (int)round(event->y)) < TRACKPOINT_SIZE_APPROX ) { marker_begin_move ( t, event->x, event->y ); return TRUE; } @@ -10206,7 +10349,8 @@ static gboolean tool_edit_trackpoint_release ( VikTrwLayer *vtl, GdkEventButton /* diff dist is diff from orig */ if ( vtl->tpwin ) - my_tpwin_set_tp ( vtl ); + if ( vtl->current_tp_track ) + my_tpwin_set_tp ( vtl ); vik_layer_emit_update ( VIK_LAYER(vtl) ); return TRUE; @@ -10442,7 +10586,7 @@ static void thumbnail_create_thread_free ( thumbnail_create_thread_data *tctd ) g_free ( tctd ); } -void trw_layer_verify_thumbnails ( VikTrwLayer *vtl, GtkWidget *vp ) +void trw_layer_verify_thumbnails ( VikTrwLayer *vtl ) { if ( ! vtl->has_verified_thumbnails ) { @@ -10582,7 +10726,7 @@ static void trw_layer_calculate_bounds_track ( gpointer id, VikTrack *trk ) vik_track_calculate_bounds ( trk ); } -static void trw_layer_calculate_bounds_tracks ( VikTrwLayer *vtl ) +void trw_layer_calculate_bounds_tracks ( VikTrwLayer *vtl ) { g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_calculate_bounds_track, NULL ); g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_calculate_bounds_track, NULL ); @@ -10672,10 +10816,10 @@ static time_t trw_layer_get_timestamp ( VikTrwLayer *vtl ) return timestamp_waypoints; } -static void trw_layer_post_read ( VikTrwLayer *vtl, GtkWidget *vvp, gboolean from_file ) +static void trw_layer_post_read ( VikTrwLayer *vtl, VikViewport *vvp, gboolean from_file ) { if ( VIK_LAYER(vtl)->realized ) - trw_layer_verify_thumbnails ( vtl, vvp ); + trw_layer_verify_thumbnails ( vtl ); trw_layer_track_alloc_colors ( vtl ); trw_layer_calculate_bounds_waypoints ( vtl ); @@ -10946,8 +11090,8 @@ static void trw_layer_download_map_along_track_cb ( menu_array_sublayer values ) } // Convert from list of vmls to list of names. Allowing the user to select one of them - gchar **map_names = g_malloc(1 + num_maps * sizeof(gpointer)); - VikMapsLayer **map_layers = g_malloc(1 + num_maps * sizeof(gpointer)); + gchar **map_names = g_malloc_n(1 + num_maps, sizeof(gpointer)); + VikMapsLayer **map_layers = g_malloc_n(1 + num_maps, sizeof(gpointer)); gchar **np = map_names; VikMapsLayer **lp = map_layers; @@ -11000,7 +11144,7 @@ static gint highest_wp_number_name_to_number(const gchar *name) { static void highest_wp_number_reset(VikTrwLayer *vtl) { - vtl->highest_wp_number = -1; + vtl->highest_wp_number = 0; } static void highest_wp_number_add_wp(VikTrwLayer *vtl, const gchar *new_wp_name)