X-Git-Url: https://git.street.me.uk/andy/viking.git/blobdiff_plain/3b9970f8fc29c5f6c5fb683cd3b45fe1eea1bc51..327a25338deb8f0d91318cf6c55292478c539881:/src/viktrwlayer.c?ds=sidebyside diff --git a/src/viktrwlayer.c b/src/viktrwlayer.c index 8755a0d8..60082ce5 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) 2011, Rob Norris + * Copyright (c) 2012, Rob Norris * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -80,16 +80,28 @@ static gboolean return_true (gpointer a, gpointer b, gpointer c) { return TRUE; static g_hash_table_remove_all (GHashTable *ght) { g_hash_table_foreach_remove ( ght, (GHRFunc) return_true, FALSE ); } #endif +#ifdef VIK_CONFIG_GOOGLE_DIRECTIONS +// This is currently broken as Google have disabled the KML output in Google Maps API v3 +// It has been ifdefed out in the hope that Route Finding functionality will be restored one day... +// Only have 'JSON' and 'XML' see: +// https://developers.google.com/maps/documentation/directions/#DirectionsResponses #define GOOGLE_DIRECTIONS_STRING "maps.google.com/maps?q=from:%s,%s+to:%s,%s&output=kml" -#define VIK_TRW_LAYER_TRACK_GC 13 +#endif + +#define VIK_TRW_LAYER_TRACK_GC 16 #define VIK_TRW_LAYER_TRACK_GC_RATES 10 #define VIK_TRW_LAYER_TRACK_GC_MIN 0 #define VIK_TRW_LAYER_TRACK_GC_MAX 11 #define VIK_TRW_LAYER_TRACK_GC_BLACK 12 +#define VIK_TRW_LAYER_TRACK_GC_SLOW 13 +#define VIK_TRW_LAYER_TRACK_GC_AVER 14 +#define VIK_TRW_LAYER_TRACK_GC_FAST 15 #define DRAWMODE_BY_TRACK 0 -#define DRAWMODE_BY_VELOCITY 1 +#define DRAWMODE_BY_SPEED 1 #define DRAWMODE_ALL_BLACK 2 +// Note using DRAWMODE_BY_SPEED may be slow especially for vast numbers of trackpoints +// as we are (re)calculating the colour for every point #define POINTS 1 #define LINES 2 @@ -102,12 +114,6 @@ static g_hash_table_remove_all (GHashTable *ght) { g_hash_table_foreach_remove ( #define MAX_STOP_LENGTH 86400 #define DRAW_ELEVATION_FACTOR 30 /* height of elevation plotting, sort of relative to zoom level ("mpp" that isn't mpp necessarily) */ /* this is multiplied by user-inputted value from 1-100. */ -enum { -VIK_TRW_LAYER_SUBLAYER_TRACKS, -VIK_TRW_LAYER_SUBLAYER_WAYPOINTS, -VIK_TRW_LAYER_SUBLAYER_TRACK, -VIK_TRW_LAYER_SUBLAYER_WAYPOINT -}; enum { WP_SYMBOL_FILLED_SQUARE, WP_SYMBOL_SQUARE, WP_SYMBOL_CIRCLE, WP_SYMBOL_X, WP_NUM_SYMBOLS }; @@ -133,10 +139,12 @@ struct _VikTrwLayer { guint8 wp_size; gboolean wp_draw_symbols; - gdouble velocity_min, velocity_max; + gdouble track_draw_speed_factor; GArray *track_gc; - guint16 track_gc_iter; GdkGC *current_track_gc; + // Separate GC for a track's potential new point as drawn via separate method + // (compared to the actual track points drawn in the main trw_layer_draw_track function) + GdkGC *current_track_newpoint_gc; GdkGC *track_bg_gc; GdkGC *waypoint_gc; GdkGC *waypoint_text_gc; @@ -144,26 +152,23 @@ struct _VikTrwLayer { GdkFont *waypoint_font; VikTrack *current_track; guint16 ct_x1, ct_y1, ct_x2, ct_y2; - gboolean ct_sync_done; - + gboolean draw_sync_done; + gboolean draw_sync_do; VikCoordMode coord_mode; /* wp editing tool */ VikWaypoint *current_wp; - gchar *current_wp_name; + gpointer current_wp_id; gboolean moving_wp; gboolean waypoint_rightclick; /* track editing tool */ GList *current_tpl; - gchar *current_tp_track_name; + VikTrack *current_tp_track; + gpointer current_tp_id; VikTrwLayerTpwin *tpwin; - /* weird hack for joining tracks */ - GList *last_tpl; - gchar *last_tp_track_name; - /* track editing tool -- more specifically, moving tps */ gboolean moving_tp; @@ -171,7 +176,7 @@ struct _VikTrwLayer { gboolean route_finder_started; VikCoord route_finder_coord; gboolean route_finder_check_added_track; - gchar *route_finder_added_track_name; + VikTrack *route_finder_added_track; VikTrack *route_finder_current_track; gboolean route_finder_append; @@ -213,6 +218,8 @@ struct DrawingParams { gdouble ce1, ce2, cn1, cn2; }; +static gboolean trw_layer_delete_waypoint ( VikTrwLayer *vtl, VikWaypoint *wp ); + static void trw_layer_delete_item ( gpointer pass_along[6] ); static void trw_layer_copy_item_cb ( gpointer pass_along[6] ); static void trw_layer_cut_item_cb ( gpointer pass_along[6] ); @@ -224,7 +231,6 @@ static void trw_layer_find_maxmin (VikTrwLayer *vtl, struct LatLon maxmin[2]); static void trw_layer_new_track_gcs ( VikTrwLayer *vtl, VikViewport *vp ); static void trw_layer_free_track_gcs ( VikTrwLayer *vtl ); -static gint calculate_velocity ( VikTrwLayer *vtl, VikTrackpoint *tp1, VikTrackpoint *tp2 ); static void trw_layer_draw_track_cb ( const gchar *name, VikTrack *track, struct DrawingParams *dp ); static void trw_layer_draw_waypoint ( const gchar *name, VikWaypoint *wp, struct DrawingParams *dp ); @@ -235,10 +241,16 @@ static void trw_layer_goto_track_max_speed ( gpointer pass_along[6] ); static void trw_layer_goto_track_max_alt ( gpointer pass_along[6] ); static void trw_layer_goto_track_min_alt ( gpointer pass_along[6] ); static void trw_layer_goto_track_center ( gpointer pass_along[6] ); +static void trw_layer_merge_by_segment ( gpointer pass_along[6] ); static void trw_layer_merge_by_timestamp ( gpointer pass_along[6] ); static void trw_layer_merge_with_other ( gpointer pass_along[6] ); +static void trw_layer_append_track ( gpointer pass_along[6] ); static void trw_layer_split_by_timestamp ( gpointer pass_along[6] ); static void trw_layer_split_by_n_points ( gpointer pass_along[6] ); +static void trw_layer_split_at_trackpoint ( gpointer pass_along[6] ); +static void trw_layer_split_segments ( gpointer pass_along[6] ); +static void trw_layer_delete_points_same_position ( gpointer pass_along[6] ); +static void trw_layer_delete_points_same_time ( gpointer pass_along[6] ); static void trw_layer_reverse ( gpointer pass_along[6] ); static void trw_layer_download_map_along_track_cb ( gpointer pass_along[6] ); static void trw_layer_edit_trackpoint ( gpointer pass_along[6] ); @@ -246,7 +258,7 @@ static void trw_layer_show_picture ( gpointer pass_along[6] ); static void trw_layer_centerize ( gpointer layer_and_vlp[2] ); static void trw_layer_auto_view ( gpointer layer_and_vlp[2] ); -static void trw_layer_export ( gpointer layer_and_vlp[2], const gchar* title, const gchar* default_name, const gchar* trackname, guint file_type ); +static void trw_layer_export ( gpointer layer_and_vlp[2], const gchar* title, const gchar* default_name, VikTrack* trk, guint file_type ); static void trw_layer_goto_wp ( gpointer layer_and_vlp[2] ); static void trw_layer_new_wp ( gpointer lav[2] ); static void trw_layer_auto_waypoints_view ( gpointer lav[2] ); @@ -276,16 +288,15 @@ static void trw_layer_acquire_geotagged_cb ( gpointer lav[2] ); #endif /* pop-up items */ -static void trw_layer_properties_item ( gpointer pass_along[6] ); +static void trw_layer_properties_item ( gpointer pass_along[7] ); static void trw_layer_goto_waypoint ( gpointer pass_along[6] ); static void trw_layer_waypoint_gc_webpage ( gpointer pass_along[6] ); -static void trw_layer_realize_waypoint ( gchar *name, VikWaypoint *wp, gpointer pass_along[5] ); -static void trw_layer_realize_track ( gchar *name, VikTrack *track, gpointer pass_along[5] ); +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, VikViewport *vp ); static void trw_layer_insert_tp_after_current_tp ( VikTrwLayer *vtl ); -static void trw_layer_cancel_last_tp ( VikTrwLayer *vtl ); 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 ); @@ -305,12 +316,14 @@ static gboolean tool_begin_track_click ( VikTrwLayer *vtl, GdkEventButton *event static gpointer tool_new_track_create ( VikWindow *vw, VikViewport *vvp); static gboolean tool_new_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp ); static VikLayerToolFuncStatus tool_new_track_move ( VikTrwLayer *vtl, GdkEventMotion *event, VikViewport *vvp ); +static void tool_new_track_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp ); 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 ); +#ifdef VIK_CONFIG_GOOGLE_DIRECTIONS static gpointer tool_route_finder_create ( VikWindow *vw, VikViewport *vvp); static gboolean tool_route_finder_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp ); - +#endif static void cached_pixbuf_free ( CachedPixbuf *cp ); static gint cached_pixbuf_cmp ( CachedPixbuf *cp, const gchar *name ); @@ -318,8 +331,7 @@ static gint cached_pixbuf_cmp ( CachedPixbuf *cp, const gchar *name ); static VikTrackpoint *closest_tp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y ); static VikWaypoint *closest_wp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y ); -static gchar *get_new_unique_sublayer_name (VikTrwLayer *vtl, gint sublayer_type, const gchar *name); -static void waypoint_convert ( const gchar *name, VikWaypoint *wp, VikCoordMode *dest_mode ); +static void waypoint_convert ( const gpointer id, VikWaypoint *wp, VikCoordMode *dest_mode ); static void track_convert ( const gchar *name, VikTrack *tr, VikCoordMode *dest_mode ); static gchar *highest_wp_number_get(VikTrwLayer *vtl); @@ -327,33 +339,63 @@ static void highest_wp_number_reset(VikTrwLayer *vtl); static void highest_wp_number_add_wp(VikTrwLayer *vtl, const gchar *new_wp_name); static void highest_wp_number_remove_wp(VikTrwLayer *vtl, const gchar *old_wp_name); - +// Note for the following tool GtkRadioActionEntry texts: +// the very first text value is an internal name not displayed anywhere +// the first N_ text value is the name used for menu entries - hence has an underscore for the keyboard accelerator +// * remember not to clash with the values used for VikWindow level tools (Pan, Zoom, Ruler + Select) +// the second N_ text value is used for the button tooltip (i.e. generally don't want an underscore here) +// the value is always set to 0 and the tool loader in VikWindow will set the actual appropriate value used static VikToolInterface trw_layer_tools[] = { - { N_("Create Waypoint"), (VikToolConstructorFunc) tool_new_waypoint_create, NULL, NULL, NULL, - (VikToolMouseFunc) tool_new_waypoint_click, NULL, NULL, (VikToolKeyFunc) NULL, GDK_CURSOR_IS_PIXMAP, &cursor_addwp_pixbuf }, - - { N_("Create Track"), (VikToolConstructorFunc) tool_new_track_create, NULL, NULL, NULL, - (VikToolMouseFunc) tool_new_track_click, (VikToolMouseMoveFunc) tool_new_track_move, NULL, - (VikToolKeyFunc) tool_new_track_key_press, GDK_CURSOR_IS_PIXMAP, &cursor_addtr_pixbuf }, - - { N_("Begin Track"), (VikToolConstructorFunc) tool_begin_track_create, NULL, NULL, NULL, - (VikToolMouseFunc) tool_begin_track_click, NULL, NULL, (VikToolKeyFunc) NULL, GDK_CURSOR_IS_PIXMAP, &cursor_begintr_pixbuf }, - - { N_("Edit Waypoint"), (VikToolConstructorFunc) tool_edit_waypoint_create, NULL, NULL, NULL, + { { "CreateWaypoint", "vik-icon-Create Waypoint", N_("Create _Waypoint"), "W", N_("Create Waypoint"), 0 }, + (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 }, + + { { "CreateTrack", "vik-icon-Create Track", N_("Create _Track"), "T", N_("Create Track"), 0 }, + (VikToolConstructorFunc) tool_new_track_create, NULL, NULL, NULL, + (VikToolMouseFunc) tool_new_track_click, + (VikToolMouseMoveFunc) tool_new_track_move, + (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 }, + + { { "BeginTrack", "vik-icon-Begin Track", N_("_Begin Track"), "B", N_("Begin Track"), 0 }, + (VikToolConstructorFunc) tool_begin_track_create, NULL, NULL, NULL, + (VikToolMouseFunc) tool_begin_track_click, NULL, NULL, (VikToolKeyFunc) NULL, + FALSE, + GDK_CURSOR_IS_PIXMAP, &cursor_begintr_pixbuf }, + + { { "EditWaypoint", "vik-icon-Edit Waypoint", N_("_Edit Waypoint"), "E", N_("Edit Waypoint"), 0 }, + (VikToolConstructorFunc) tool_edit_waypoint_create, NULL, NULL, NULL, (VikToolMouseFunc) tool_edit_waypoint_click, (VikToolMouseMoveFunc) tool_edit_waypoint_move, - (VikToolMouseFunc) tool_edit_waypoint_release, (VikToolKeyFunc) NULL, GDK_CURSOR_IS_PIXMAP, &cursor_edwp_pixbuf }, + (VikToolMouseFunc) tool_edit_waypoint_release, (VikToolKeyFunc) NULL, + FALSE, + GDK_CURSOR_IS_PIXMAP, &cursor_edwp_pixbuf }, - { N_("Edit Trackpoint"), (VikToolConstructorFunc) tool_edit_trackpoint_create, NULL, NULL, NULL, + { { "EditTrackpoint", "vik-icon-Edit Trackpoint", N_("Edit Trac_kpoint"), "K", N_("Edit Trackpoint"), 0 }, + (VikToolConstructorFunc) tool_edit_trackpoint_create, NULL, NULL, NULL, (VikToolMouseFunc) tool_edit_trackpoint_click, (VikToolMouseMoveFunc) tool_edit_trackpoint_move, - (VikToolMouseFunc) tool_edit_trackpoint_release, (VikToolKeyFunc) NULL, GDK_CURSOR_IS_PIXMAP, &cursor_edtr_pixbuf }, - - { N_("Show Picture"), (VikToolConstructorFunc) tool_show_picture_create, NULL, NULL, NULL, - (VikToolMouseFunc) tool_show_picture_click, NULL, NULL, (VikToolKeyFunc) NULL, GDK_CURSOR_IS_PIXMAP, &cursor_showpic_pixbuf }, - - { N_("Route Finder"), (VikToolConstructorFunc) tool_route_finder_create, NULL, NULL, NULL, - (VikToolMouseFunc) tool_route_finder_click, NULL, NULL, (VikToolKeyFunc) NULL, GDK_CURSOR_IS_PIXMAP, &cursor_route_finder_pixbuf }, + (VikToolMouseFunc) tool_edit_trackpoint_release, (VikToolKeyFunc) NULL, + FALSE, + GDK_CURSOR_IS_PIXMAP, &cursor_edtr_pixbuf }, + + { { "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 }, + +#ifdef VIK_CONFIG_GOOGLE_DIRECTIONS + { { "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 }, +#endif }; enum { TOOL_CREATE_WAYPOINT=0, TOOL_CREATE_TRACK, TOOL_BEGIN_TRACK, TOOL_EDIT_WAYPOINT, TOOL_EDIT_TRACKPOINT, TOOL_SHOW_PICTURE, NUM_TOOLS }; @@ -362,15 +404,15 @@ enum { TOOL_CREATE_WAYPOINT=0, TOOL_CREATE_TRACK, TOOL_BEGIN_TRACK, TOOL_EDIT_WA static gchar *params_groups[] = { N_("Waypoints"), N_("Tracks"), N_("Waypoint Images") }; enum { GROUP_WAYPOINTS, GROUP_TRACKS, GROUP_IMAGES }; -static gchar *params_drawmodes[] = { N_("Draw by Track"), N_("Draw by Velocity"), N_("All Tracks Black"), 0 }; +static gchar *params_drawmodes[] = { N_("Draw by Track"), N_("Draw by Speed"), N_("All Tracks Black"), 0 }; static gchar *params_wpsymbols[] = { N_("Filled Square"), N_("Square"), N_("Circle"), N_("X"), 0 }; static VikLayerParamScale params_scales[] = { /* min max step digits */ { 1, 10, 1, 0 }, /* line_thickness */ - { 0.0, 99.0, 1, 2 }, /* velocity_min */ - { 1.0, 100.0, 1.0, 2 }, /* velocity_max */ + { 0, 100, 1, 0 }, /* track draw speed factor */ + { 1.0, 100.0, 1.0, 2 }, /* UNUSED */ /* 5 * step == how much to turn */ { 16, 128, 3.2, 0 }, /* image_size */ { 0, 255, 5, 0 }, /* image alpha */ @@ -397,8 +439,7 @@ VikLayerParam trw_layer_params[] = { { "line_thickness", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Track Thickness:"), VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 0 }, { "bg_line_thickness", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Track BG Thickness:"), VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 6 }, { "trackbgcolor", VIK_LAYER_PARAM_COLOR, GROUP_TRACKS, N_("Track Background Color"), VIK_LAYER_WIDGET_COLOR, 0 }, - { "velocity_min", VIK_LAYER_PARAM_DOUBLE, GROUP_TRACKS, N_("Min Track Velocity:"), VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 1 }, - { "velocity_max", VIK_LAYER_PARAM_DOUBLE, GROUP_TRACKS, N_("Max Track Velocity:"), VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 2 }, + { "speed_factor", VIK_LAYER_PARAM_DOUBLE, GROUP_TRACKS, N_("Draw by Speed Factor (%):"), VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 1 }, { "drawlabels", VIK_LAYER_PARAM_BOOLEAN, GROUP_WAYPOINTS, N_("Draw Labels"), VIK_LAYER_WIDGET_CHECKBUTTON }, { "wpcolor", VIK_LAYER_PARAM_COLOR, GROUP_WAYPOINTS, N_("Waypoint Color:"), VIK_LAYER_WIDGET_COLOR, 0 }, @@ -415,7 +456,7 @@ VikLayerParam trw_layer_params[] = { { "image_cache_size", VIK_LAYER_PARAM_UINT, GROUP_IMAGES, N_("Image Memory Cache Size:"), VIK_LAYER_WIDGET_HSCALE, params_scales + 5 }, }; -enum { PARAM_TV, PARAM_WV, PARAM_DM, PARAM_DL, PARAM_DP, PARAM_DE, PARAM_EF, PARAM_DS, PARAM_SL, PARAM_LT, PARAM_BLT, PARAM_TBGC, PARAM_VMIN, PARAM_VMAX, PARAM_DLA, PARAM_WPC, PARAM_WPTC, PARAM_WPBC, PARAM_WPBA, PARAM_WPSYM, PARAM_WPSIZE, PARAM_WPSYMS, PARAM_DI, PARAM_IS, PARAM_IA, PARAM_ICS, NUM_PARAMS }; +enum { PARAM_TV, PARAM_WV, PARAM_DM, PARAM_DL, PARAM_DP, PARAM_DE, PARAM_EF, PARAM_DS, PARAM_SL, PARAM_LT, PARAM_BLT, PARAM_TBGC, PARAM_TDSF, PARAM_DLA, PARAM_WPC, PARAM_WPTC, PARAM_WPBC, PARAM_WPBA, PARAM_WPSYM, PARAM_WPSIZE, PARAM_WPSYMS, PARAM_DI, PARAM_IS, PARAM_IA, PARAM_ICS, NUM_PARAMS }; /*** TO ADD A PARAM: *** 1) Add to trw_layer_params and enumeration @@ -457,7 +498,8 @@ static gboolean trw_layer_show_selected_viewport_menu ( VikTrwLayer *vtl, GdkEve /* End Layer Interface function definitions */ VikLayerInterface vik_trw_layer_interface = { - "TrackWaypoint", + N_("TrackWaypoint"), + "Y", &viktrwlayer_pixbuf, trw_layer_tools, @@ -514,12 +556,10 @@ VikLayerInterface vik_trw_layer_interface = { (VikLayerFuncSelectedViewportMenu) trw_layer_show_selected_viewport_menu, }; -/* for copy & paste (I think?) */ +// for copy & paste typedef struct { guint len; guint8 data[0]; - // gchar *name; - // VikWaypoint *wp; } FlatItem; GType vik_trw_layer_get_type () @@ -592,8 +632,24 @@ static void trw_layer_copy_item_cb ( gpointer pass_along[6]) trw_layer_copy_item( vtl, subtype, sublayer, &data, &len); if (data) { + const gchar* name; + if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) { + VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, sublayer); + if ( wp && wp->name ) + name = wp->name; + else + name = NULL; // Broken :( + } + else { + VikTrack *trk = g_hash_table_lookup ( vtl->tracks, sublayer); + if ( trk && trk->name ) + name = trk->name; + else + name = NULL; // Broken :( + } + a_clipboard_copy( VIK_CLIPBOARD_DATA_SUBLAYER, VIK_LAYER_TRW, - subtype, len, (const gchar*) sublayer, data); + subtype, len, name, data); } } @@ -618,15 +674,21 @@ static void trw_layer_copy_item ( VikTrwLayer *vtl, gint subtype, gpointer subla if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) { vik_waypoint_marshall ( g_hash_table_lookup ( vtl->waypoints, sublayer ), &id, &il ); + // 'Simple' memory copy of byte array from the marshalling above + *len = sizeof(FlatItem) + 1 + il; // not sure what the 1 is for yet... + fi = g_malloc ( *len ); + fi->len = *len; + memcpy(fi->data, id, il); } else { + vik_track_marshall ( g_hash_table_lookup ( vtl->tracks, sublayer ), &id, &il ); + // less magic than before... + *len = sizeof(FlatItem) + 1 + il; + fi = g_malloc ( *len ); + fi->len = *len; + memcpy(fi->data, id, il); } - *len = sizeof(FlatItem) + strlen(sublayer) + 1 + il; - fi = g_malloc ( *len ); - fi->len = strlen(sublayer) + 1; - memcpy(fi->data, sublayer, fi->len); - memcpy(fi->data + fi->len, id, il); g_free(id); *item = (guint8 *)fi; } @@ -640,10 +702,12 @@ static gboolean trw_layer_paste_item ( VikTrwLayer *vtl, gint subtype, guint8 *i VikWaypoint *w; gchar *name; - name = get_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, (gchar *)fi->data); - w = vik_waypoint_unmarshall(fi->data + fi->len, len - sizeof(*fi) - fi->len); + w = vik_waypoint_unmarshall(fi->data, fi->len); + // When copying - we'll create a new name based on the original + name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, w->name); vik_trw_layer_add_waypoint ( vtl, name, w ); - waypoint_convert(name, w, &vtl->coord_mode); + waypoint_convert (NULL, w, &vtl->coord_mode); + // Consider if redraw necessary for the new item if ( vtl->vl.visible && vtl->waypoints_visible && w->visible ) vik_layer_emit_update ( VIK_LAYER(vtl), FALSE ); @@ -653,10 +717,13 @@ static gboolean trw_layer_paste_item ( VikTrwLayer *vtl, gint subtype, guint8 *i { VikTrack *t; gchar *name; - name = get_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, (gchar *)fi->data); - t = vik_track_unmarshall(fi->data + fi->len, len - sizeof(*fi) - fi->len); + + t = vik_track_unmarshall(fi->data, fi->len); + // When copying - we'll create a new name based on the original + name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, t->name); vik_trw_layer_add_track ( vtl, name, t ); - track_convert(name, t, &vtl->coord_mode); + track_convert (name, t, &vtl->coord_mode); + // Consider if redraw necessary for the new item if ( vtl->vl.visible && vtl->tracks_visible && t->visible ) vik_layer_emit_update ( VIK_LAYER(vtl), FALSE ); @@ -701,39 +768,8 @@ static gboolean trw_layer_set_param ( VikTrwLayer *vtl, guint16 id, VikLayerPara trw_layer_new_track_gcs ( vtl, vp ); } break; - case PARAM_VMIN: - { - /* Convert to store internally - NB file operation always in internal units (metres per second) */ - vik_units_speed_t speed_units = a_vik_get_units_speed (); - if ( is_file_operation || speed_units == VIK_UNITS_SPEED_METRES_PER_SECOND ) - vtl->velocity_min = data.d; - else if ( speed_units == VIK_UNITS_SPEED_KILOMETRES_PER_HOUR ) - vtl->velocity_min = VIK_KPH_TO_MPS(data.d); - else if ( speed_units == VIK_UNITS_SPEED_MILES_PER_HOUR ) - vtl->velocity_min = VIK_MPH_TO_MPS(data.d); - else - /* Knots */ - vtl->velocity_min = VIK_KNOTS_TO_MPS(data.d); - break; - } - case PARAM_VMAX: - { - /* Convert to store internally - NB file operation always in internal units (metres per second) */ - vik_units_speed_t speed_units = a_vik_get_units_speed (); - if ( is_file_operation || speed_units == VIK_UNITS_SPEED_METRES_PER_SECOND ) - vtl->velocity_max = data.d; - else if ( speed_units == VIK_UNITS_SPEED_KILOMETRES_PER_HOUR ) - vtl->velocity_max = VIK_KPH_TO_MPS(data.d); - else if ( speed_units == VIK_UNITS_SPEED_MILES_PER_HOUR ) - vtl->velocity_max = VIK_MPH_TO_MPS(data.d); - else - /* Knots */ - vtl->velocity_max = VIK_KNOTS_TO_MPS(data.d); - break; - } case PARAM_TBGC: gdk_gc_set_rgb_fg_color(vtl->track_bg_gc, &(data.c)); break; + case PARAM_TDSF: vtl->track_draw_speed_factor = data.d; break; case PARAM_DLA: vtl->drawlabels = data.b; break; case PARAM_DI: vtl->drawimages = data.b; break; case PARAM_IS: if ( data.u != vtl->image_size ) @@ -776,41 +812,10 @@ static VikLayerParamData trw_layer_get_param ( VikTrwLayer *vtl, guint16 id, gbo case PARAM_DL: rv.b = vtl->drawlines; break; case PARAM_LT: rv.u = vtl->line_thickness; break; case PARAM_BLT: rv.u = vtl->bg_line_thickness; break; - case PARAM_VMIN: - { - /* Convert to store internally - NB file operation always in internal units (metres per second) */ - vik_units_speed_t speed_units = a_vik_get_units_speed (); - if ( is_file_operation || speed_units == VIK_UNITS_SPEED_METRES_PER_SECOND ) - rv.d = vtl->velocity_min; - else if ( speed_units == VIK_UNITS_SPEED_KILOMETRES_PER_HOUR ) - rv.d = VIK_MPS_TO_KPH(vtl->velocity_min); - else if ( speed_units == VIK_UNITS_SPEED_MILES_PER_HOUR ) - rv.d = VIK_MPS_TO_MPH(vtl->velocity_min); - else - /* Knots */ - rv.d = VIK_MPS_TO_KNOTS(vtl->velocity_min); - break; - } - case PARAM_VMAX: - { - /* Convert to store internally - NB file operation always in internal units (metres per second) */ - vik_units_speed_t speed_units = a_vik_get_units_speed (); - if ( is_file_operation || speed_units == VIK_UNITS_SPEED_METRES_PER_SECOND ) - rv.d = vtl->velocity_max; - else if ( speed_units == VIK_UNITS_SPEED_KILOMETRES_PER_HOUR ) - rv.d = VIK_MPS_TO_KPH(vtl->velocity_max); - else if ( speed_units == VIK_UNITS_SPEED_MILES_PER_HOUR ) - rv.d = VIK_MPS_TO_MPH(vtl->velocity_max); - else - /* Knots */ - rv.d = VIK_MPS_TO_KNOTS(vtl->velocity_max); - break; - } case PARAM_DLA: rv.b = vtl->drawlabels; break; case PARAM_DI: rv.b = vtl->drawimages; break; case PARAM_TBGC: vik_gc_get_fg_color(vtl->track_bg_gc, &(rv.c)); break; + case PARAM_TDSF: rv.d = vtl->track_draw_speed_factor; break; case PARAM_IS: rv.u = vtl->image_size; break; case PARAM_IA: rv.u = vtl->image_alpha; break; case PARAM_ICS: rv.u = vtl->image_cache_size; break; @@ -891,17 +896,14 @@ static GList * str_array_to_glist(gchar* data[]) return(g_list_reverse(gl)); } -static gboolean strcase_equal(gconstpointer s1, gconstpointer s2) -{ - return (strcasecmp(s1, s2) == 0); -} - +// Keep interesting hash function at least visible +/* static guint strcase_hash(gconstpointer v) { - /* 31 bit hash function */ + // 31 bit hash function int i; const gchar *t = v; - gchar s[128]; /* malloc is too slow for reading big files */ + gchar s[128]; // malloc is too slow for reading big files gchar *p = s; for (i = 0; (i < (sizeof(s)- 1)) && t[i]; i++) @@ -917,6 +919,7 @@ static guint strcase_hash(gconstpointer v) return h; } +*/ static VikTrwLayer* trw_layer_new ( gint drawmode ) { @@ -928,10 +931,22 @@ static VikTrwLayer* trw_layer_new ( gint drawmode ) VikTrwLayer *rv = VIK_TRW_LAYER ( g_object_new ( VIK_TRW_LAYER_TYPE, NULL ) ); vik_layer_init ( VIK_LAYER(rv), VIK_LAYER_TRW ); - rv->waypoints = g_hash_table_new_full ( strcase_hash, strcase_equal, g_free, (GDestroyNotify) vik_waypoint_free ); - rv->tracks = g_hash_table_new_full ( g_str_hash, g_str_equal, g_free, (GDestroyNotify) vik_track_free ); - rv->tracks_iters = g_hash_table_new_full ( g_str_hash, g_str_equal, NULL, g_free ); - rv->waypoints_iters = g_hash_table_new_full ( strcase_hash, strcase_equal, NULL, g_free ); + // It's not entirely clear the benefits of hash tables usage here - possibly the simplicity of first implementation for unique names + // Now with the name of the item stored as part of the item - these tables are effectively straightforward lists + + // For this reworking I've choosen to keep the use of hash tables since for the expected data sizes + // - even many hundreds of waypoints and tracks is quite small in the grand scheme of things, + // and with normal PC processing capabilities - it has negligibile performance impact + // This also minimized the amount of rework - as the management of the hash tables already exists. + + // The hash tables are indexed by simple integers acting as a UUID hash, which again shouldn't affect performance much + // we have to maintain a uniqueness (as before when multiple names where not allowed), + // this is to ensure it refers to the same item in the data structures used on the viewport and on the layers panel + + rv->waypoints = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) vik_waypoint_free ); + rv->waypoints_iters = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, g_free ); + rv->tracks = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) vik_track_free ); + rv->tracks_iters = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, g_free ); /* TODO: constants at top */ rv->waypoints_visible = rv->tracks_visible = TRUE; @@ -949,29 +964,27 @@ static VikTrwLayer* trw_layer_new ( gint drawmode ) rv->waypoint_text_gc = NULL; rv->waypoint_bg_gc = NULL; rv->track_gc = NULL; - rv->velocity_max = 5.0; - rv->velocity_min = 0.0; + rv->track_draw_speed_factor = 30.0; rv->line_thickness = 1; rv->bg_line_thickness = 0; rv->current_wp = NULL; - rv->current_wp_name = NULL; + rv->current_wp_id = NULL; rv->current_track = NULL; rv->current_tpl = NULL; - rv->current_tp_track_name = NULL; + rv->current_tp_track = NULL; + rv->current_tp_id = NULL; rv->moving_tp = FALSE; rv->moving_wp = FALSE; - rv->ct_sync_done = TRUE; + rv->draw_sync_done = TRUE; + rv->draw_sync_do = TRUE; rv->route_finder_started = FALSE; rv->route_finder_check_added_track = FALSE; - rv->route_finder_added_track_name = NULL; rv->route_finder_current_track = NULL; rv->route_finder_append = FALSE; rv->waypoint_rightclick = FALSE; - rv->last_tpl = NULL; - rv->last_tp_track_name = NULL; rv->tpwin = NULL; rv->image_cache = g_queue_new(); rv->image_size = 64; @@ -1056,27 +1069,28 @@ static void init_drawing_params ( struct DrawingParams *dp, VikViewport *vp ) dp->track_gc_iter = 0; } -static gint calculate_velocity ( VikTrwLayer *vtl, VikTrackpoint *tp1, VikTrackpoint *tp2 ) -{ - static gdouble rv = 0; - if ( tp1->has_timestamp && tp2->has_timestamp ) - { - rv = ( vik_coord_diff ( &(tp1->coord), &(tp2->coord) ) - / (tp1->timestamp - tp2->timestamp) ) - vtl->velocity_min; - - if ( rv < 0 ) - return VIK_TRW_LAYER_TRACK_GC_MIN; - else if ( vtl->velocity_min >= vtl->velocity_max ) - return VIK_TRW_LAYER_TRACK_GC_MAX; - - rv *= (VIK_TRW_LAYER_TRACK_GC_RATES / (vtl->velocity_max - vtl->velocity_min)); - - if ( rv >= VIK_TRW_LAYER_TRACK_GC_MAX ) - return VIK_TRW_LAYER_TRACK_GC_MAX; - return (gint) rv; - } - else - return VIK_TRW_LAYER_TRACK_GC_BLACK; +/* + * Determine the colour of the trackpoint (and/or trackline) relative to the average speed + * Here a simple traffic like light colour system is used: + * . slow points are red + * . average is yellow + * . fast points are green + */ +static gint track_section_colour_by_speed ( VikTrwLayer *vtl, VikTrackpoint *tp1, VikTrackpoint *tp2, gdouble average_speed, gdouble low_speed, gdouble high_speed ) +{ + gdouble rv = 0; + if ( tp1->has_timestamp && tp2->has_timestamp ) { + if ( average_speed > 0 ) { + rv = ( vik_coord_diff ( &(tp1->coord), &(tp2->coord) ) / (tp1->timestamp - tp2->timestamp) ); + if ( rv < low_speed ) + return VIK_TRW_LAYER_TRACK_GC_SLOW; + else if ( rv > high_speed ) + return VIK_TRW_LAYER_TRACK_GC_FAST; + else + return VIK_TRW_LAYER_TRACK_GC_AVER; + } + } + return VIK_TRW_LAYER_TRACK_GC_BLACK; } void draw_utm_skip_insignia ( VikViewport *vvp, GdkGC *gc, gint x, gint y ) @@ -1124,6 +1138,7 @@ static void trw_layer_draw_track ( const gchar *name, VikTrack *track, struct Dr drawstops = dp->vtl->drawstops; } + gboolean drawing_highlight = FALSE; /* Current track - used for creation */ if ( track == dp->vtl->current_track ) main_gc = dp->vtl->current_track_gc; @@ -1137,6 +1152,7 @@ static void trw_layer_draw_track ( const gchar *name, VikTrack *track, struct Dr ( dp->vtl->tracks == vik_window_get_selected_tracks ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ) || track == vik_window_get_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ) ) { main_gc = vik_viewport_get_gc_highlight (dp->vp); + drawing_highlight = TRUE; } else { if ( dp->vtl->drawmode == DRAWMODE_ALL_BLACK ) @@ -1170,6 +1186,17 @@ static void trw_layer_draw_track ( const gchar *name, VikTrack *track, struct Dr oldx = x; oldy = y; + gdouble average_speed = 0.0; + gdouble low_speed = 0.0; + gdouble high_speed = 0.0; + // If necessary calculate these values - which is done only once per track redraw + if ( dp->vtl->drawmode == DRAWMODE_BY_SPEED ) { + // the percentage factor away from the average speed determines transistions between the levels + average_speed = vik_track_get_average_speed_moving(track, dp->vtl->stop_length); + low_speed = average_speed - (average_speed*(dp->vtl->track_draw_speed_factor/100.0)); + high_speed = average_speed + (average_speed*(dp->vtl->track_draw_speed_factor/100.0)); + } + while ((list = g_list_next(list))) { tp = VIK_TRACKPOINT(list->data); @@ -1196,8 +1223,18 @@ static void trw_layer_draw_track ( const gchar *name, VikTrack *track, struct Dr 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) ) { + dp->track_gc_iter = track_section_colour_by_speed ( dp->vtl, tp, tp2, average_speed, low_speed, high_speed ); + main_gc = g_array_index(dp->vtl->track_gc, GdkGC *, dp->track_gc_iter); + } + } + if ( drawpoints && ! drawing_white_background ) { + if ( list->next ) { /* * The concept of drawing stops is that a trackpoint @@ -1221,17 +1258,11 @@ static void trw_layer_draw_track ( const gchar *name, VikTrack *track, struct Dr if ((!tp->newsegment) && (dp->vtl->drawlines)) { - VikTrackpoint *tp2 = VIK_TRACKPOINT(list->prev->data); /* UTM only: zone check */ if ( drawpoints && dp->vtl->coord_mode == VIK_COORD_UTM && tp->coord.utm_zone != dp->center->utm_zone ) draw_utm_skip_insignia ( dp->vp, main_gc, x, y); - if ( dp->vtl->drawmode == DRAWMODE_BY_VELOCITY ) { - dp->track_gc_iter = calculate_velocity ( dp->vtl, tp, tp2 ); - main_gc = g_array_index(dp->vtl->track_gc, GdkGC *, dp->track_gc_iter); - } - if (!useoldvals) vik_viewport_coord_to_screen ( dp->vp, &(tp2->coord), &oldx, &oldy ); @@ -1278,10 +1309,11 @@ static void trw_layer_draw_track ( const gchar *name, VikTrack *track, struct Dr 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 ); - if ( dp->vtl->drawmode == DRAWMODE_BY_VELOCITY ) { - dp->track_gc_iter = calculate_velocity ( dp->vtl, tp, tp2 ); + + if ( !drawing_highlight && (dp->vtl->drawmode == DRAWMODE_BY_SPEED) ) { + dp->track_gc_iter = track_section_colour_by_speed ( dp->vtl, tp, tp2, average_speed, low_speed, high_speed ); main_gc = g_array_index(dp->vtl->track_gc, GdkGC *, dp->track_gc_iter); - } + } /* * If points are the same in display coordinates, don't draw. @@ -1311,7 +1343,7 @@ static void trw_layer_draw_track ( const gchar *name, VikTrack *track, struct Dr } } if ( dp->vtl->drawmode == DRAWMODE_BY_TRACK ) - if ( ++(dp->track_gc_iter) >= VIK_TRW_LAYER_TRACK_GC ) + if ( ++(dp->track_gc_iter) >= VIK_TRW_LAYER_TRACK_GC_MAX ) dp->track_gc_iter = 0; } @@ -1452,7 +1484,7 @@ static void trw_layer_draw_waypoint ( const gchar *name, VikWaypoint *wp, struct /* thanks to the GPSDrive people (Fritz Ganter et al.) for hints on this part ... yah, I'm too lazy to study documentation */ gint label_x, label_y; gint width, height; - pango_layout_set_text ( dp->vtl->wplabellayout, name, -1 ); + pango_layout_set_text ( dp->vtl->wplabellayout, wp->name, -1 ); pango_layout_get_pixel_size ( dp->vtl->wplabellayout, &width, &height ); label_x = x - width/2; if (sym) @@ -1505,6 +1537,11 @@ static void trw_layer_free_track_gcs ( VikTrwLayer *vtl ) g_object_unref ( vtl->current_track_gc ); vtl->current_track_gc = NULL; } + if ( vtl->current_track_newpoint_gc ) + { + g_object_unref ( vtl->current_track_newpoint_gc ); + vtl->current_track_newpoint_gc = NULL; + } if ( ! vtl->track_gc ) return; @@ -1531,6 +1568,12 @@ static void trw_layer_new_track_gcs ( VikTrwLayer *vtl, VikViewport *vp ) vtl->current_track_gc = vik_viewport_new_gc ( vp, "#FF0000", 2 ); gdk_gc_set_line_attributes ( vtl->current_track_gc, 2, GDK_LINE_ON_OFF_DASH, GDK_CAP_ROUND, GDK_JOIN_ROUND ); + // 'newpoint' gc is exactly the same as the current track gc + if ( vtl->current_track_newpoint_gc ) + g_object_unref ( vtl->current_track_newpoint_gc ); + vtl->current_track_newpoint_gc = vik_viewport_new_gc ( vp, "#FF0000", 2 ); + gdk_gc_set_line_attributes ( vtl->current_track_newpoint_gc, 2, GDK_LINE_ON_OFF_DASH, GDK_CAP_ROUND, GDK_JOIN_ROUND ); + vtl->track_gc = g_array_sized_new ( FALSE, FALSE, sizeof ( GdkGC * ), VIK_TRW_LAYER_TRACK_GC ); gc[0] = vik_viewport_new_gc ( vp, "#2d870a", width ); /* below range */ @@ -1550,12 +1593,23 @@ static void trw_layer_new_track_gcs ( VikTrwLayer *vtl, VikViewport *vp ) gc[12] = vik_viewport_new_gc ( vp, "#000000", width ); /* black / no speed data */ + gc[VIK_TRW_LAYER_TRACK_GC_SLOW] = vik_viewport_new_gc ( vp, "#E6202E", width ); // red-ish + gc[VIK_TRW_LAYER_TRACK_GC_AVER] = vik_viewport_new_gc ( vp, "#D2CD26", width ); // yellow-ish + gc[VIK_TRW_LAYER_TRACK_GC_FAST] = vik_viewport_new_gc ( vp, "#2B8700", width ); // green-ish + g_array_append_vals ( vtl->track_gc, gc, VIK_TRW_LAYER_TRACK_GC ); } static VikTrwLayer* trw_layer_create ( VikViewport *vp ) { VikTrwLayer *rv = trw_layer_new ( DRAWMODE_BY_TRACK ); + vik_layer_rename ( VIK_LAYER(rv), vik_trw_layer_interface.name ); + + if ( vp == NULL || GTK_WIDGET(vp)->window == NULL ) { + /* early exit, as the rest is GUI related */ + return rv; + } + PangoFontDescription *pfd; rv->wplabellayout = gtk_widget_create_pango_layout (GTK_WIDGET(vp), NULL); pfd = pango_font_description_from_string (WAYPOINT_FONT); @@ -1563,8 +1617,6 @@ static VikTrwLayer* trw_layer_create ( VikViewport *vp ) /* freeing PangoFontDescription, cause it has been copied by prev. call */ pango_font_description_free (pfd); - vik_layer_rename ( VIK_LAYER(rv), vik_trw_layer_interface.name ); - trw_layer_new_track_gcs ( rv, vp ); rv->waypoint_gc = vik_viewport_new_gc ( vp, "#000000", 2 ); @@ -1586,34 +1638,34 @@ static VikTrwLayer* trw_layer_create ( VikViewport *vp ) return rv; } -static void trw_layer_realize_track ( gchar *name, VikTrack *track, gpointer pass_along[5] ) +static void trw_layer_realize_track ( gpointer id, VikTrack *track, gpointer pass_along[5] ) { GtkTreeIter *new_iter = g_malloc(sizeof(GtkTreeIter)); #ifdef VIK_CONFIG_ALPHABETIZED_TRW - vik_treeview_add_sublayer_alphabetized ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[0], (GtkTreeIter *) pass_along[1], name, pass_along[2], name, GPOINTER_TO_INT (pass_along[4]), NULL, TRUE, TRUE ); + vik_treeview_add_sublayer_alphabetized ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[0], (GtkTreeIter *) pass_along[1], track->name, pass_along[2], id, GPOINTER_TO_INT (pass_along[4]), NULL, TRUE, TRUE ); #else - vik_treeview_add_sublayer ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[0], (GtkTreeIter *) pass_along[1], name, pass_along[2], name, GPOINTER_TO_INT (pass_along[4]), NULL, TRUE, TRUE ); + 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]), NULL, TRUE, TRUE ); #endif *new_iter = *((GtkTreeIter *) pass_along[1]); - g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->tracks_iters, name, new_iter ); + g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->tracks_iters, id, new_iter ); if ( ! track->visible ) vik_treeview_item_set_visible ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[1], FALSE ); } -static void trw_layer_realize_waypoint ( gchar *name, VikWaypoint *wp, gpointer pass_along[5] ) +static void trw_layer_realize_waypoint ( gpointer id, VikWaypoint *wp, gpointer pass_along[5] ) { GtkTreeIter *new_iter = g_malloc(sizeof(GtkTreeIter)); #ifdef VIK_CONFIG_ALPHABETIZED_TRW - vik_treeview_add_sublayer_alphabetized ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[0], (GtkTreeIter *) pass_along[1], name, pass_along[2], name, GPOINTER_TO_INT (pass_along[4]), NULL, TRUE, TRUE ); + vik_treeview_add_sublayer_alphabetized ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[0], (GtkTreeIter *) pass_along[1], wp->name, pass_along[2], id, GPOINTER_TO_INT (pass_along[4]), NULL, TRUE, TRUE ); #else - vik_treeview_add_sublayer ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[0], (GtkTreeIter *) pass_along[1], name, pass_along[2], name, GPOINTER_TO_INT (pass_along[4]), NULL, TRUE, TRUE ); + 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]), NULL, TRUE, TRUE ); #endif *new_iter = *((GtkTreeIter *) pass_along[1]); - g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->waypoints_iters, name, new_iter ); + g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->waypoints_iters, id, new_iter ); if ( ! wp->visible ) vik_treeview_item_set_visible ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[1], FALSE ); @@ -1623,7 +1675,7 @@ static void trw_layer_realize_waypoint ( gchar *name, VikWaypoint *wp, gpointer static void trw_layer_realize ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter ) { GtkTreeIter iter2; - gpointer pass_along[5] = { &(vtl->tracks_iter), &iter2, vtl, vt, (gpointer) VIK_TRW_LAYER_SUBLAYER_TRACK }; + gpointer pass_along[5] = { &(vtl->tracks_iter), &iter2, vtl, vt, GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_TRACK) }; #ifdef VIK_CONFIG_ALPHABETIZED_TRW vik_treeview_add_sublayer_alphabetized ( (VikTreeview *) vt, layer_iter, &(vtl->tracks_iter), _("Tracks"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_TRACKS, NULL, TRUE, FALSE ); @@ -1645,7 +1697,7 @@ static void trw_layer_realize ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter * vik_treeview_item_set_visible ( (VikTreeview *) vt, &(vtl->waypoints_iter), FALSE ); pass_along[0] = &(vtl->waypoints_iter); - pass_along[4] = (gpointer) VIK_TRW_LAYER_SUBLAYER_WAYPOINT; + pass_along[4] = GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_WAYPOINT); g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_realize_waypoint, pass_along ); @@ -1861,7 +1913,8 @@ static const gchar* trw_layer_sublayer_tooltip ( VikTrwLayer *l, gint subtype, g { VikWaypoint *w = g_hash_table_lookup ( l->waypoints, sublayer ); // NB It's OK to return NULL - return w->comment; + if ( w ) + return w->comment; } break; default: break; @@ -1905,8 +1958,8 @@ static void set_statusbar_msg_info_trkpt ( VikTrwLayer *vtl, VikTrackpoint *trkp // Also name could be very long to not leave room for anything else gchar tmp_buf3[64]; tmp_buf3[0] = '\0'; - if ( vtl->current_tp_track_name ) { - g_snprintf(tmp_buf3, sizeof(tmp_buf3), _(" | Track: %s"), vtl->current_tp_track_name ); + if ( vtl->current_tp_track ) { + g_snprintf(tmp_buf3, sizeof(tmp_buf3), _(" | Track: %s"), vtl->current_tp_track->name ); } // Combine parts to make overall message @@ -1959,8 +2012,8 @@ static void set_statusbar_msg_info_wpt ( VikTrwLayer *vtl, VikWaypoint *wpt ) static gboolean trw_layer_selected ( VikTrwLayer *l, gint subtype, gpointer sublayer, gint type, gpointer vlp ) { // Reset - l->current_wp = NULL; - l->current_wp_name = NULL; + l->current_wp = NULL; + l->current_wp_id = NULL; trw_layer_cancel_current_tp ( l, FALSE ); // Clear statusbar @@ -2005,11 +2058,13 @@ static gboolean trw_layer_selected ( VikTrwLayer *l, gint subtype, gpointer subl case VIK_TRW_LAYER_SUBLAYER_WAYPOINT: { VikWaypoint *wpt = g_hash_table_lookup ( l->waypoints, sublayer ); - vik_window_set_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), (gpointer)wpt, l, sublayer ); - // Show some waypoint info - set_statusbar_msg_info_wpt ( l, wpt ); - /* Mark for redraw */ - return TRUE; + if ( wpt ) { + vik_window_set_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), (gpointer)wpt, l, sublayer ); + // Show some waypoint info + set_statusbar_msg_info_wpt ( l, wpt ); + /* Mark for redraw */ + return TRUE; + } } break; default: @@ -2038,6 +2093,48 @@ GHashTable *vik_trw_layer_get_waypoints ( VikTrwLayer *l ) return l->waypoints; } +/* + * ATM use a case sensitive find + * Finds the first one + */ +static gboolean trw_layer_waypoint_find ( const gpointer id, const VikWaypoint *wp, const gchar *name ) +{ + if ( wp && wp->name ) + if ( ! strcmp ( wp->name, name ) ) + return TRUE; + return FALSE; +} + +/* + * Get waypoint by name - not guaranteed to be unique + * Finds the first one + */ +VikWaypoint *vik_trw_layer_get_waypoint ( VikTrwLayer *vtl, const gchar *name ) +{ + return g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find, (gpointer) name ); +} + +/* + * ATM use a case sensitive find + * Finds the first one + */ +static gboolean trw_layer_track_find ( const gpointer id, const VikTrack *trk, const gchar *name ) +{ + if ( trk && trk->name ) + if ( ! strcmp ( trk->name, name ) ) + return TRUE; + return FALSE; +} + +/* + * Get track by name - not guaranteed to be unique + * Finds the first one + */ +VikTrack *vik_trw_layer_get_track ( VikTrwLayer *vtl, const gchar *name ) +{ + return g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find, (gpointer) name ); +} + static void trw_layer_find_maxmin_waypoints ( const gchar *name, const VikWaypoint *w, struct LatLon maxmin[2] ) { static VikCoord fixme; @@ -2200,7 +2297,7 @@ static void trw_layer_auto_view ( gpointer layer_and_vlp[2] ) a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("This layer has no waypoints or trackpoints.") ); } -static void trw_layer_export ( gpointer layer_and_vlp[2], const gchar *title, const gchar* default_name, const gchar* trackname, guint file_type ) +static void trw_layer_export ( gpointer layer_and_vlp[2], const gchar *title, const gchar* default_name, VikTrack* trk, guint file_type ) { GtkWidget *file_selector; const gchar *fn; @@ -2219,7 +2316,7 @@ static void trw_layer_export ( gpointer layer_and_vlp[2], const gchar *title, co if ( g_file_test ( fn, G_FILE_TEST_EXISTS ) == FALSE ) { gtk_widget_hide ( file_selector ); - failed = ! a_file_export ( VIK_TRW_LAYER(layer_and_vlp[0]), fn, file_type, trackname); + failed = ! a_file_export ( VIK_TRW_LAYER(layer_and_vlp[0]), fn, file_type, trk ); break; } else @@ -2227,7 +2324,7 @@ static void trw_layer_export ( gpointer layer_and_vlp[2], const gchar *title, co if ( a_dialog_yes_or_no ( GTK_WINDOW(file_selector), _("The file \"%s\" exists, do you wish to overwrite it?"), a_file_basename ( fn ) ) ) { gtk_widget_hide ( file_selector ); - failed = ! a_file_export ( VIK_TRW_LAYER(layer_and_vlp[0]), fn, file_type, trackname); + failed = ! a_file_export ( VIK_TRW_LAYER(layer_and_vlp[0]), fn, file_type, trk ); break; } } @@ -2271,25 +2368,87 @@ static void trw_layer_export_kml ( gpointer layer_and_vlp[2] ) g_free ( auto_save_name ); } +/** + * Convert the given TRW layer into a temporary GPX file and open it with the specified program + * + */ +static void trw_layer_export_external_gpx ( gpointer layer_and_vlp[2], const gchar* external_program ) +{ + gchar *name_used = NULL; + int fd; + + if ((fd = g_file_open_tmp("tmp-viking.XXXXXX.gpx", &name_used, NULL)) >= 0) { + gboolean failed = ! a_file_export ( VIK_TRW_LAYER(layer_and_vlp[0]), name_used, FILE_TYPE_GPX, NULL); + if (failed) { + a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("Could not create temporary file for export.") ); + } + else { + GError *err = NULL; + gchar *quoted_file = g_shell_quote ( name_used ); + gchar *cmd = g_strdup_printf ( "%s %s", external_program, quoted_file ); + g_free ( quoted_file ); + if ( ! g_spawn_command_line_async ( cmd, &err ) ) + { + a_dialog_error_msg_extra ( VIK_GTK_WINDOW_FROM_LAYER( layer_and_vlp[0]), _("Could not launch %s."), external_program ); + g_error_free ( err ); + } + g_free ( cmd ); + } + // Note ATM the 'temporary' file is not deleted, as loading via another program is not instantaneous + //g_remove ( name_used ); + // Perhaps should be deleted when the program ends? + // For now leave it to the user to delete it / use system temp cleanup methods. + g_free ( name_used ); + } +} + +static void trw_layer_export_external_gpx_1 ( gpointer layer_and_vlp[2] ) +{ + trw_layer_export_external_gpx ( layer_and_vlp, a_vik_get_external_gpx_program_1() ); +} + +static void trw_layer_export_external_gpx_2 ( gpointer layer_and_vlp[2] ) +{ + trw_layer_export_external_gpx ( layer_and_vlp, a_vik_get_external_gpx_program_2() ); +} + static void trw_layer_export_gpx_track ( gpointer pass_along[6] ) { gpointer layer_and_vlp[2]; layer_and_vlp[0] = pass_along[0]; layer_and_vlp[1] = pass_along[1]; + VikTrack *trk = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ); + + if ( !trk || !trk->name ) + return; /* Auto append '.gpx' to track name (providing it's not already there) for the default filename */ - gchar *auto_save_name = g_strdup ( pass_along[3] ); + gchar *auto_save_name = g_strdup ( trk->name ); if ( ! check_file_ext ( auto_save_name, ".gpx" ) ) auto_save_name = g_strconcat ( auto_save_name, ".gpx", NULL ); - trw_layer_export ( layer_and_vlp, _("Export Track as GPX"), auto_save_name, pass_along[3], FILE_TYPE_GPX ); + trw_layer_export ( layer_and_vlp, _("Export Track as GPX"), auto_save_name, trk, FILE_TYPE_GPX ); g_free ( auto_save_name ); } +typedef struct { + VikWaypoint *wp; // input + gpointer uuid; // output +} wpu_udata; + +static gboolean trw_layer_waypoint_find_uuid ( const gpointer id, const VikWaypoint *wp, gpointer udata ) +{ + wpu_udata *user_data = udata; + if ( wp == user_data->wp ) { + user_data->uuid = id; + return TRUE; + } + return FALSE; +} + static void trw_layer_goto_wp ( gpointer layer_and_vlp[2] ) { - GHashTable *wps = vik_trw_layer_get_waypoints ( VIK_TRW_LAYER(layer_and_vlp[0]) ); GtkWidget *dia = gtk_dialog_new_with_buttons (_("Find"), VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT, @@ -2312,26 +2471,34 @@ static void trw_layer_goto_wp ( gpointer layer_and_vlp[2] ) while ( gtk_dialog_run ( GTK_DIALOG(dia) ) == GTK_RESPONSE_ACCEPT ) { - VikWaypoint *wp; - gchar *upname = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry))); - int i; - - for ( i = strlen(upname)-1; i >= 0; i-- ) - upname[i] = toupper(upname[i]); + gchar *name = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry))); + // Find *first* wp with the given name + VikWaypoint *wp = vik_trw_layer_get_waypoint ( VIK_TRW_LAYER(layer_and_vlp[0]), name ); - wp = g_hash_table_lookup ( wps, upname ); - - if (!wp) + if ( !wp ) a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("Waypoint not found in this layer.") ); else { vik_viewport_set_center_coord ( vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(layer_and_vlp[1])), &(wp->coord) ); vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(layer_and_vlp[1]) ); - vik_treeview_select_iter ( VIK_LAYER(layer_and_vlp[0])->vt, g_hash_table_lookup ( VIK_TRW_LAYER(layer_and_vlp[0])->waypoints_iters, upname ), TRUE ); + + // Find and select on the side panel + wpu_udata udata; + udata.wp = wp; + udata.uuid = NULL; + + // Hmmm, want key of it + gpointer *wpf = g_hash_table_find ( VIK_TRW_LAYER(layer_and_vlp[0])->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata ); + + if ( wpf && udata.uuid ) { + GtkTreeIter *it = g_hash_table_lookup ( VIK_TRW_LAYER(layer_and_vlp[0])->waypoints_iters, udata.uuid ); + vik_treeview_select_iter ( VIK_LAYER(layer_and_vlp[0])->vt, it, TRUE ); + } + break; } - g_free ( upname ); + g_free ( name ); } gtk_widget_destroy ( dia ); @@ -2350,11 +2517,14 @@ gboolean vik_trw_layer_new_waypoint ( VikTrwLayer *vtl, GtkWindow *w, const VikC if ( elev != VIK_DEM_INVALID_ELEVATION ) wp->altitude = (gdouble)elev; - if ( ( returned_name = a_dialog_waypoint ( w, default_name, wp, vik_trw_layer_get_waypoints ( vtl ), vtl->coord_mode, TRUE, &updated ) ) ) + returned_name = a_dialog_waypoint ( w, default_name, wp, vtl->coord_mode, TRUE, &updated ); + + if ( returned_name ) { wp->visible = TRUE; vik_trw_layer_add_waypoint ( vtl, returned_name, wp ); g_free (default_name); + g_free (returned_name); return TRUE; } g_free (default_name); @@ -2435,7 +2605,7 @@ static void trw_layer_geotagging_track ( gpointer pass_along[6] ) trw_layer_geotag_dialog ( VIK_GTK_WINDOW_FROM_LAYER(vtl), vtl, track, - pass_along[3] ); + track->name ); } static void trw_layer_geotagging ( gpointer lav[2] ) @@ -2585,7 +2755,6 @@ static void trw_layer_add_menu_items ( VikTrwLayer *vtl, GtkMenu *menu, gpointer static gpointer pass_along[2]; GtkWidget *item; GtkWidget *export_submenu; - GtkWidget *wikipedia_submenu; pass_along[0] = vtl; pass_along[1] = vlp; @@ -2648,6 +2817,20 @@ 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 ); + gchar* external1 = g_strconcat ( _("Open with External Program_1: "), a_vik_get_external_gpx_program_1(), NULL ); + item = gtk_menu_item_new_with_mnemonic ( external1 ); + g_free ( external1 ); + g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_external_gpx_1), pass_along ); + gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item); + gtk_widget_show ( item ); + + gchar* external2 = g_strconcat ( _("Open with External Program_2: "), a_vik_get_external_gpx_program_2(), NULL ); + item = gtk_menu_item_new_with_mnemonic ( external2 ); + g_free ( external2 ); + g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_external_gpx_2), pass_along ); + gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item); + gtk_widget_show ( item ); + item = gtk_image_menu_item_new_with_mnemonic ( _("_New Waypoint...") ); gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) ); g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along ); @@ -2655,7 +2838,7 @@ static void trw_layer_add_menu_items ( VikTrwLayer *vtl, GtkMenu *menu, gpointer gtk_widget_show ( item ); #ifdef VIK_CONFIG_GEONAMES - wikipedia_submenu = gtk_menu_new(); + GtkWidget *wikipedia_submenu = gtk_menu_new(); item = gtk_image_menu_item_new_with_mnemonic ( _("_Add Wikipedia Waypoints") ); gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) ); gtk_menu_shell_append(GTK_MENU_SHELL (menu), item); @@ -2774,69 +2957,72 @@ static void trw_layer_add_menu_items ( VikTrwLayer *vtl, GtkMenu *menu, gpointer } } +// Fake Waypoint UUIDs vi simple increasing integer +static guint wp_uuid = 0; + void vik_trw_layer_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp ) { + wp_uuid++; + + vik_waypoint_set_name (wp, name); + if ( VIK_LAYER(vtl)->realized ) { - VikWaypoint *oldwp = VIK_WAYPOINT ( g_hash_table_lookup ( vtl->waypoints, name ) ); - if ( oldwp ) - wp->visible = oldwp->visible; /* same visibility so we don't have to update viktreeview */ - else - { - GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter)); - // Visibility column always needed for waypoints + GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter)); + + // Visibility column always needed for waypoints #ifdef VIK_CONFIG_ALPHABETIZED_TRW - vik_treeview_add_sublayer_alphabetized ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), iter, name, vtl, name, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, NULL, TRUE, TRUE ); + vik_treeview_add_sublayer_alphabetized ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), iter, name, vtl, GUINT_TO_POINTER(wp_uuid), VIK_TRW_LAYER_SUBLAYER_WAYPOINT, NULL, TRUE, TRUE ); #else - vik_treeview_add_sublayer ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), iter, name, vtl, name, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, NULL, 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, NULL, TRUE, TRUE ); #endif - // Actual setting of visibility dependent on the waypoint - vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, wp->visible ); - g_hash_table_insert ( vtl->waypoints_iters, name, iter ); - } + // Actual setting of visibility dependent on the waypoint + vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, wp->visible ); + + g_hash_table_insert ( vtl->waypoints_iters, GUINT_TO_POINTER(wp_uuid), iter ); } highest_wp_number_add_wp(vtl, name); - g_hash_table_insert ( vtl->waypoints, name, wp ); + g_hash_table_insert ( vtl->waypoints, GUINT_TO_POINTER(wp_uuid), wp ); } +// Fake Track UUIDs vi simple increasing integer +static guint tr_uuid = 0; + void vik_trw_layer_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *t ) { + tr_uuid++; + + vik_track_set_name (t, name); + if ( VIK_LAYER(vtl)->realized ) { - VikTrack *oldt = VIK_TRACK ( g_hash_table_lookup ( vtl->tracks, name ) ); - if ( oldt ) - t->visible = oldt->visible; /* same visibility so we don't have to update viktreeview */ - else - { - GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter)); - // Visibility column always needed for tracks + GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter)); + // Visibility column always needed for tracks #ifdef VIK_CONFIG_ALPHABETIZED_TRW - vik_treeview_add_sublayer_alphabetized ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), iter, name, vtl, name, VIK_TRW_LAYER_SUBLAYER_TRACK, NULL, TRUE, TRUE ); + vik_treeview_add_sublayer_alphabetized ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), iter, name, vtl, GUINT_TO_POINTER(tr_uuid), VIK_TRW_LAYER_SUBLAYER_TRACK, NULL, TRUE, TRUE ); #else - vik_treeview_add_sublayer ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), iter, name, vtl, name, 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, TRUE ); #endif - // Actual setting of visibility dependent on the track - vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, t->visible ); - g_hash_table_insert ( vtl->tracks_iters, name, iter ); - } + // Actual setting of visibility dependent on the track + vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, t->visible ); + + g_hash_table_insert ( vtl->tracks_iters, GUINT_TO_POINTER(tr_uuid), iter ); } - g_hash_table_insert ( vtl->tracks, name, t ); + g_hash_table_insert ( vtl->tracks, GUINT_TO_POINTER(tr_uuid), t ); } /* to be called whenever a track has been deleted or may have been changed. */ -void trw_layer_cancel_tps_of_track ( VikTrwLayer *vtl, const gchar *trk_name ) +void trw_layer_cancel_tps_of_track ( VikTrwLayer *vtl, VikTrack *trk ) { - if (vtl->current_tp_track_name && g_strcasecmp(trk_name, vtl->current_tp_track_name) == 0) + if (vtl->current_tp_track == trk ) trw_layer_cancel_current_tp ( vtl, FALSE ); - else if (vtl->last_tp_track_name && g_strcasecmp(trk_name, vtl->last_tp_track_name) == 0) - trw_layer_cancel_last_tp ( vtl ); } -static gchar *get_new_unique_sublayer_name (VikTrwLayer *vtl, gint sublayer_type, const gchar *name) +gchar *trw_layer_new_unique_sublayer_name (VikTrwLayer *vtl, gint sublayer_type, const gchar *name) { gint i = 2; gchar *newname = g_strdup(name); @@ -2852,10 +3038,11 @@ static gchar *get_new_unique_sublayer_name (VikTrwLayer *vtl, gint sublayer_type void vik_trw_layer_filein_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp ) { - vik_trw_layer_add_waypoint ( vtl, - get_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, name), - wp ); + // No more uniqueness of name forced when loading from a file + // This now makes this function a little redunant as we just flow the parameters through + vik_trw_layer_add_waypoint ( vtl, name, wp ); } + void vik_trw_layer_filein_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *tr ) { if ( vtl->route_finder_append && vtl->route_finder_current_track ) { @@ -2864,37 +3051,45 @@ void vik_trw_layer_filein_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *t vik_track_free ( tr ); vtl->route_finder_append = FALSE; /* this means we have added it */ } else { - gchar *new_name = get_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, name); - vik_trw_layer_add_track ( vtl, new_name, tr ); + + // No more uniqueness of name forced when loading from a file + vik_trw_layer_add_track ( vtl, name, tr ); if ( vtl->route_finder_check_added_track ) { vik_track_remove_dup_points ( tr ); /* make "double point" track work to undo */ - if ( vtl->route_finder_added_track_name ) /* for google routes */ - g_free ( vtl->route_finder_added_track_name ); - vtl->route_finder_added_track_name = g_strdup(new_name); + vtl->route_finder_added_track = tr; } } } -static void trw_layer_enum_item ( const gchar *name, GList **tr, GList **l ) +static void trw_layer_enum_item ( gpointer id, GList **tr, GList **l ) { - *l = g_list_append(*l, (gpointer)name); + *l = g_list_append(*l, id); } -static void trw_layer_move_item ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, gchar *name, gint type ) +/* + * Move an item from one TRW layer to another TRW layer + */ +static void trw_layer_move_item ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, gpointer id, gint type ) { - gchar *newname = get_new_unique_sublayer_name(vtl_dest, type, name); if (type == VIK_TRW_LAYER_SUBLAYER_TRACK) { - VikTrack *t; - t = vik_track_copy(vik_trw_layer_get_track(vtl_src, name)); - vik_trw_layer_delete_track(vtl_src, name); - vik_trw_layer_add_track(vtl_dest, newname, t); + VikTrack *trk = g_hash_table_lookup ( vtl_src->tracks, id ); + + gchar *newname = trw_layer_new_unique_sublayer_name(vtl_dest, type, trk->name); + + VikTrack *trk2 = vik_track_copy ( trk ); + vik_trw_layer_add_track ( vtl_dest, newname, trk2 ); + vik_trw_layer_delete_track ( vtl_src, trk ); } - if (type==VIK_TRW_LAYER_SUBLAYER_WAYPOINT) { - VikWaypoint *w; - w = vik_waypoint_copy(vik_trw_layer_get_waypoint(vtl_src, name)); - vik_trw_layer_delete_waypoint(vtl_src, name); - vik_trw_layer_add_waypoint(vtl_dest, newname, w); + + if (type == VIK_TRW_LAYER_SUBLAYER_WAYPOINT) { + VikWaypoint *wp = g_hash_table_lookup ( vtl_src->waypoints, id ); + + 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 ); + trw_layer_delete_waypoint ( vtl_src, wp ); } } @@ -2931,60 +3126,180 @@ static void trw_layer_drag_drop_request ( VikTrwLayer *vtl_src, VikTrwLayer *vtl } } -gboolean vik_trw_layer_delete_track ( VikTrwLayer *vtl, const gchar *trk_name ) +typedef struct { + VikTrack *trk; // input + gpointer uuid; // output +} trku_udata; + +static gboolean trw_layer_track_find_uuid ( const gpointer id, const VikTrack *trk, gpointer udata ) +{ + trku_udata *user_data = udata; + if ( trk == user_data->trk ) { + user_data->uuid = id; + return TRUE; + } + return FALSE; +} + +gboolean vik_trw_layer_delete_track ( VikTrwLayer *vtl, VikTrack *trk ) { - VikTrack *t = g_hash_table_lookup ( vtl->tracks, trk_name ); gboolean was_visible = FALSE; - if ( t ) - { - GtkTreeIter *it; - was_visible = t->visible; - if ( t == vtl->current_track ) { + + if ( trk && trk->name ) { + + if ( trk == vtl->current_track ) { vtl->current_track = NULL; + vtl->current_tp_track = NULL; + vtl->current_tp_id = NULL; + vtl->moving_tp = FALSE; } - if ( t == vtl->route_finder_current_track ) + + was_visible = trk->visible; + + if ( trk == vtl->route_finder_current_track ) vtl->route_finder_current_track = NULL; - /* could be current_tp, so we have to check */ - trw_layer_cancel_tps_of_track ( vtl, trk_name ); + if ( trk == vtl->route_finder_added_track ) + vtl->route_finder_added_track = NULL; + + trku_udata udata; + udata.trk = trk; + udata.uuid = NULL; + + // Hmmm, want key of it + gpointer *trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata ); - g_assert ( ( it = g_hash_table_lookup ( vtl->tracks_iters, trk_name ) ) ); - vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it ); - g_hash_table_remove ( vtl->tracks_iters, trk_name ); + if ( trkf && udata.uuid ) { + /* could be current_tp, so we have to check */ + trw_layer_cancel_tps_of_track ( vtl, trk ); - /* do this last because trk_name may be pointing to actual orig key */ - g_hash_table_remove ( vtl->tracks, trk_name ); + GtkTreeIter *it = g_hash_table_lookup ( vtl->tracks_iters, udata.uuid ); + + if ( it ) { + vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it ); + g_hash_table_remove ( vtl->tracks_iters, udata.uuid ); + g_hash_table_remove ( vtl->tracks, udata.uuid ); + } + } } return was_visible; } -gboolean vik_trw_layer_delete_waypoint ( VikTrwLayer *vtl, const gchar *wp_name ) +static gboolean trw_layer_delete_waypoint ( VikTrwLayer *vtl, VikWaypoint *wp ) { gboolean was_visible = FALSE; - VikWaypoint *wp; - wp = g_hash_table_lookup ( vtl->waypoints, wp_name ); - if ( wp ) { - GtkTreeIter *it; + if ( wp && wp->name ) { if ( wp == vtl->current_wp ) { vtl->current_wp = NULL; - vtl->current_wp_name = NULL; + vtl->current_wp_id = NULL; vtl->moving_wp = FALSE; } was_visible = wp->visible; - g_assert ( ( it = g_hash_table_lookup ( vtl->waypoints_iters, (gchar *) wp_name ) ) ); - vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it ); - g_hash_table_remove ( vtl->waypoints_iters, (gchar *) wp_name ); + + wpu_udata udata; + udata.wp = 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 ); + + if ( wpf && udata.uuid ) { + GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udata.uuid ); + + if ( it ) { + vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it ); + g_hash_table_remove ( vtl->waypoints_iters, udata.uuid ); + + highest_wp_number_remove_wp(vtl, wp->name); + g_hash_table_remove ( vtl->waypoints, udata.uuid ); // last because this frees the name + } + } - highest_wp_number_remove_wp(vtl, wp_name); - g_hash_table_remove ( vtl->waypoints, wp_name ); /* last because this frees name */ } return was_visible; } +// Only for temporary use by trw_layer_delete_waypoint_by_name +static gboolean trw_layer_waypoint_find_uuid_by_name ( const gpointer id, const VikWaypoint *wp, gpointer udata ) +{ + wpu_udata *user_data = udata; + if ( ! strcmp ( wp->name, user_data->wp->name ) ) { + user_data->uuid = id; + return TRUE; + } + return FALSE; +} + +/* + * Delete a waypoint by the given name + * NOTE: ATM this will delete the first encountered Waypoint with the specified name + * as there be multiple waypoints with the same name + */ +static gboolean trw_layer_delete_waypoint_by_name ( VikTrwLayer *vtl, const gchar *name ) +{ + wpu_udata udata; + // Fake a waypoint with the given name + udata.wp = vik_waypoint_new (); + vik_waypoint_set_name (udata.wp, name); + // Currently only the name is used in this waypoint find function + 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 ); + + vik_waypoint_free (udata.wp); + + if ( wpf && udata.uuid ) + return trw_layer_delete_waypoint (vtl, g_hash_table_lookup ( vtl->waypoints, udata.uuid )); + else + return FALSE; +} + +typedef struct { + VikTrack *trk; // input + gpointer uuid; // output +} tpu_udata; + +// Only for temporary use by trw_layer_delete_track_by_name +static gboolean trw_layer_track_find_uuid_by_name ( const gpointer id, const VikTrack *trk, gpointer udata ) +{ + tpu_udata *user_data = udata; + if ( ! strcmp ( trk->name, user_data->trk->name ) ) { + user_data->uuid = id; + return TRUE; + } + return FALSE; +} + +/* + * Delete a track by the given name + * NOTE: ATM this will delete the first encountered Track with the specified name + * as there be multiple track with the same name + */ +static gboolean trw_layer_delete_track_by_name ( VikTrwLayer *vtl, const gchar *name ) +{ + tpu_udata udata; + // Fake a track with the given name + udata.trk = vik_track_new (); + vik_track_set_name (udata.trk, name); + // Currently only the name is used in this waypoint find function + udata.uuid = NULL; + + // Hmmm, want key of it + gpointer *trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid_by_name, &udata ); + + vik_track_free (udata.trk); + + if ( trkf && udata.uuid ) + return vik_trw_layer_delete_track (vtl, g_hash_table_lookup ( vtl->tracks, udata.uuid )); + else + return FALSE; +} + static void remove_item_from_treeview(const gchar *name, GtkTreeIter *it, VikTreeview * vt) { vik_treeview_item_delete (vt, it ); @@ -2995,10 +3310,9 @@ void vik_trw_layer_delete_all_tracks ( VikTrwLayer *vtl ) vtl->current_track = NULL; vtl->route_finder_current_track = NULL; - if (vtl->current_tp_track_name) + vtl->route_finder_added_track = NULL; + if (vtl->current_tp_track) trw_layer_cancel_current_tp(vtl, FALSE); - if (vtl->last_tp_track_name) - trw_layer_cancel_last_tp ( 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); @@ -3010,7 +3324,7 @@ void vik_trw_layer_delete_all_tracks ( VikTrwLayer *vtl ) void vik_trw_layer_delete_all_waypoints ( VikTrwLayer *vtl ) { vtl->current_wp = NULL; - vtl->current_wp_name = NULL; + vtl->current_wp_id = NULL; vtl->moving_wp = FALSE; highest_wp_number_reset(vtl); @@ -3048,40 +3362,47 @@ static void trw_layer_delete_item ( gpointer pass_along[6] ) gboolean was_visible = FALSE; if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) { - if ( GPOINTER_TO_INT ( pass_along[4]) ) - // Get confirmation from the user - // Maybe this Waypoint Delete should be optional as is it could get annoying... - if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl), - _("Are you sure you want to delete the waypoint \"%s\""), - pass_along[3] ) ) - return; - was_visible = vik_trw_layer_delete_waypoint ( vtl, (gchar *) pass_along[3] ); + VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, pass_along[3] ); + if ( wp && wp->name ) { + if ( GPOINTER_TO_INT ( pass_along[4]) ) + // Get confirmation from the user + // Maybe this Waypoint Delete should be optional as is it could get annoying... + if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl), + _("Are you sure you want to delete the waypoint \"%s\""), + wp->name ) ) + return; + was_visible = trw_layer_delete_waypoint ( vtl, wp ); + } } else { - if ( GPOINTER_TO_INT ( pass_along[4]) ) - // Get confirmation from the user - if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl), + VikTrack *trk = g_hash_table_lookup ( vtl->tracks, pass_along[3] ); + if ( trk && trk->name ) { + if ( GPOINTER_TO_INT ( pass_along[4]) ) + // Get confirmation from the user + if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Are you sure you want to delete the track \"%s\""), - pass_along[3] ) ) - return; - was_visible = vik_trw_layer_delete_track ( vtl, (gchar *) pass_along[3] ); + trk->name ) ) + return; + was_visible = vik_trw_layer_delete_track ( vtl, trk ); + } } if ( was_visible ) vik_layer_emit_update ( VIK_LAYER(vtl), FALSE ); } -static void trw_layer_properties_item ( gpointer pass_along[6] ) +static void trw_layer_properties_item ( gpointer pass_along[7] ) { VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]); if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) { - VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, pass_along[3] ); - if ( wp ) + VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, pass_along[3] ); // sublayer + + if ( wp && wp->name ) { gboolean updated = FALSE; - a_dialog_waypoint ( VIK_GTK_WINDOW_FROM_LAYER(vtl), pass_along[3], wp, NULL, vtl->coord_mode, FALSE, &updated ); + a_dialog_waypoint ( VIK_GTK_WINDOW_FROM_LAYER(vtl), wp->name, wp, vtl->coord_mode, FALSE, &updated ); if ( updated && VIK_LAYER(vtl)->visible ) vik_layer_emit_update ( VIK_LAYER(vtl), FALSE ); @@ -3090,12 +3411,11 @@ static void trw_layer_properties_item ( gpointer pass_along[6] ) else { VikTrack *tr = g_hash_table_lookup ( vtl->tracks, pass_along[3] ); - if ( tr ) + if ( tr && tr->name ) { vik_trw_layer_propwin_run ( VIK_GTK_WINDOW_FROM_LAYER(vtl), vtl, tr, pass_along[1], /* vlp */ - pass_along[3], /* track name */ pass_along[5] ); /* vvp */ } } @@ -3156,6 +3476,7 @@ static void trw_layer_extend_track_end ( gpointer pass_along[6] ) goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(((VikTrackpoint *)g_list_last(track->trackpoints)->data)->coord) ); } +#ifdef VIK_CONFIG_GOOGLE_DIRECTIONS /** * extend a track using route finder */ @@ -3174,6 +3495,7 @@ static void trw_layer_extend_track_end_route_finder ( gpointer pass_along[6] ) goto_coord ( pass_along[1], pass_along[0], pass_along[5], &last_coord) ; } +#endif static void trw_layer_apply_dem_data ( gpointer pass_along[6] ) { @@ -3217,10 +3539,10 @@ static void trw_layer_goto_track_min_alt ( gpointer pass_along[6] ) goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(vtp->coord)); } -/* +/* * Automatically change the viewport to center on the track and zoom to see the extent of the track - */ -static void trw_layer_auto_track_view ( gpointer pass_along[5] ) + */ +static void trw_layer_auto_track_view ( gpointer pass_along[6] ) { GList **trps = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ); if ( trps && *trps ) @@ -3288,47 +3610,49 @@ static void find_tracks_with_timestamp_type(gpointer key, gpointer value, gpoint /* 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] */ -static void find_nearby_track(gpointer key, gpointer value, gpointer user_data) +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 *orig_track = ((gpointer *)user_data)[1]; - guint thr = GPOINTER_TO_UINT (((gpointer *)user_data)[2]); + GList *tpoints = ((gpointer *)user_data)[1]; /* outline: * detect reasons for not merging, and return * if no reason is found not to merge, then do it. */ - if (VIK_TRACK(value)->trackpoints == orig_track) { + // Exclude the original track from the compiled list + if (trk->trackpoints == tpoints) { return; } - t1 = VIK_TRACKPOINT(orig_track->data)->timestamp; - t2 = VIK_TRACKPOINT(g_list_last(orig_track)->data)->timestamp; + t1 = VIK_TRACKPOINT(g_list_first(tpoints)->data)->timestamp; + t2 = VIK_TRACKPOINT(g_list_last(tpoints)->data)->timestamp; - if (VIK_TRACK(value)->trackpoints) { - p1 = VIK_TRACKPOINT(VIK_TRACK(value)->trackpoints->data); - p2 = VIK_TRACKPOINT(g_list_last(VIK_TRACK(value)->trackpoints)->data); + if (trk->trackpoints) { + p1 = VIK_TRACKPOINT(g_list_first(trk->trackpoints)->data); + p2 = VIK_TRACKPOINT(g_list_last(trk->trackpoints)->data); if (!p1->has_timestamp || !p2->has_timestamp) { - g_print("no timestamp\n"); + //g_print("no timestamp\n"); return; } - /* g_print("Got track named %s, times %d, %d\n", (gchar *)key, p1->timestamp, p2->timestamp); */ - if (! (abs(t1 - p2->timestamp) < thr*60 || + 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) < thr*60) + abs(p1->timestamp - t2) < threshold) /* t1 t2 p1 p2 */ ) { return; } } - *nearby_tracks = g_list_prepend(*nearby_tracks, key); + *nearby_tracks = g_list_prepend(*nearby_tracks, value); } /* comparison function used to sort tracks; a and b are hash table keys */ @@ -3357,7 +3681,6 @@ static gint trackpoint_compare(gconstpointer a, gconstpointer b) return 0; } -#ifdef VIK_CONFIG_ALPHABETIZED_TRW /** * comparison function which can be used to sort tracks or waypoints by name */ @@ -3371,7 +3694,6 @@ static gint sort_alphabetically (gconstpointer a, gconstpointer b, gpointer user // Same sort method as used in the vik_treeview_*_alphabetize functions return strcmp ( namea, nameb ); } -#endif /** * Attempt to merge selected track with other tracks specified by the user @@ -3381,9 +3703,8 @@ static gint sort_alphabetically (gconstpointer a, gconstpointer b, gpointer user static void trw_layer_merge_with_other ( gpointer pass_along[6] ) { VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0]; - gchar *orig_track_name = pass_along[3]; GList *other_tracks = NULL; - VikTrack *track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, orig_track_name ); + VikTrack *track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] ); if ( !track->trackpoints ) return; @@ -3406,25 +3727,33 @@ static void trw_layer_merge_with_other ( gpointer pass_along[6] ) return; } -#ifdef VIK_CONFIG_ALPHABETIZED_TRW // Sort alphabetically for user presentation - other_tracks = g_list_sort_with_data (other_tracks, sort_alphabetically, NULL); -#endif + // Convert into list of names for usage with dialog function + // TODO: Need to consider how to work best when we can have multiple tracks the same name... + GList *other_tracks_names = NULL; + GList *iter = g_list_first ( other_tracks ); + while ( iter ) { + other_tracks_names = g_list_append ( other_tracks_names, VIK_TRACK(g_hash_table_lookup (vtl->tracks, iter->data))->name ); + iter = g_list_next ( iter ); + } + + other_tracks_names = g_list_sort_with_data (other_tracks_names, sort_alphabetically, NULL); GList *merge_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl), - other_tracks, TRUE, - _("Merge with..."), _("Select track to merge with")); + other_tracks_names, TRUE, + _("Merge with..."), _("Select track to merge with")); g_list_free(other_tracks); + g_list_free(other_tracks_names); if (merge_list) { GList *l; for (l = merge_list; l != NULL; l = g_list_next(l)) { - VikTrack *merge_track = (VikTrack *) g_hash_table_lookup (vtl->tracks, l->data ); + VikTrack *merge_track = vik_trw_layer_get_track ( vtl, l->data ); if (merge_track) { track->trackpoints = g_list_concat(track->trackpoints, merge_track->trackpoints); merge_track->trackpoints = NULL; - vik_trw_layer_delete_track(vtl, l->data); + vik_trw_layer_delete_track (vtl, merge_track); track->trackpoints = g_list_sort(track->trackpoints, trackpoint_compare); } } @@ -3436,61 +3765,139 @@ static void trw_layer_merge_with_other ( gpointer pass_along[6] ) } } +// c.f. trw_layer_sorted_track_id_by_name_list +// but don't add the specified track to the list (normally current track) +static void trw_layer_sorted_track_id_by_name_list_exclude_self (const gpointer id, const VikTrack *trk, gpointer udata) +{ + twt_udata *user_data = udata; + + // Skip self + if (trk->trackpoints == user_data->exclude) { + return; + } + + // Sort named list alphabetically + *(user_data->result) = g_list_insert_sorted_with_data (*(user_data->result), trk->name, sort_alphabetically, NULL); +} + +/** + * Join - this allows combining 'routes' and 'tracks' + * i.e. doesn't care about whether tracks have consistent timestamps + * ATM can only append one track at a time to the currently selected track + */ +static void trw_layer_append_track ( gpointer pass_along[6] ) +{ + + VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0]; + VikTrack *trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] ); + + GList *other_tracks_names = NULL; + + // Sort alphabetically for user presentation + // Convert into list of names for usage with dialog function + // 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; + + g_hash_table_foreach(vtl->tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list_exclude_self, (gpointer)&udata); + + // Note the limit to selecting one track only + // this is to control the ordering of appending tracks, i.e. the selected track always goes after the current track + // (otherwise with multiple select the ordering would not be controllable by the user - automatically being alphabetically) + GList *append_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl), + other_tracks_names, + FALSE, + _("Append Track"), + _("Select the track to append after the current track")); + + g_list_free(other_tracks_names); + + // It's a list, but shouldn't contain more than one other track! + if ( append_list ) { + GList *l; + for (l = append_list; l != NULL; l = g_list_next(l)) { + // TODO: at present this uses the first track found by name, + // which with potential multiple same named tracks may not be the one selected... + VikTrack *append_track = vik_trw_layer_get_track ( vtl, l->data ); + if ( append_track ) { + trk->trackpoints = g_list_concat(trk->trackpoints, append_track->trackpoints); + append_track->trackpoints = NULL; + vik_trw_layer_delete_track (vtl, append_track); + } + } + for (l = append_list; l != NULL; l = g_list_next(l)) + g_free(l->data); + g_list_free(append_list); + vik_layer_emit_update( VIK_LAYER(vtl), FALSE ); + } +} + +/* merge by segments */ +static void trw_layer_merge_by_segment ( gpointer pass_along[6] ) +{ + VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0]; + VikTrack *trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] ); + guint segments = vik_track_merge_segments ( trk ); + // NB currently no need to redraw as segments not actually shown on the display + // However inform the user of what happened: + gchar str[64]; + const gchar *tmp_str = ngettext("%d segment merged", "%d segments merged", segments); + g_snprintf(str, 64, tmp_str, segments); + a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str ); +} + /* merge by time routine */ static void trw_layer_merge_by_timestamp ( gpointer pass_along[6] ) { VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0]; - gchar *orig_track_name = strdup(pass_along[3]); //time_t t1, t2; - GList *nearby_tracks; - VikTrack *track; - GList *trps; - static guint thr = 1; - guint track_count = 0; GList *tracks_with_timestamp = NULL; - track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, orig_track_name ); - if (track->trackpoints && - !VIK_TRACKPOINT(track->trackpoints->data)->has_timestamp) { + VikTrack *orig_trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] ); + if (orig_trk->trackpoints && + !VIK_TRACKPOINT(orig_trk->trackpoints->data)->has_timestamp) { a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. This track does not have timestamp")); - free(orig_track_name); return; } twt_udata udata; udata.result = &tracks_with_timestamp; - udata.exclude = track->trackpoints; + udata.exclude = orig_trk->trackpoints; 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); if (!tracks_with_timestamp) { a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other track in this layer has timestamp")); - free(orig_track_name); return; } g_list_free(tracks_with_timestamp); - if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(vtl), - _("Merge Threshold..."), - _("Merge when time between tracks less than:"), - &thr)) { - free(orig_track_name); + static guint threshold_in_minutes = 1; + if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(vtl), + _("Merge Threshold..."), + _("Merge when time between tracks less than:"), + &threshold_in_minutes)) { return; } - /* merge tracks until we can't */ - nearby_tracks = NULL; - do { - gpointer params[3]; + // keep attempting to merge all tracks until no merges within the time specified is possible + gboolean attempt_merge = TRUE; + GList *nearby_tracks = NULL; + GList *trps; + static gpointer params[3]; + + while ( attempt_merge ) { - track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, orig_track_name ); - trps = track->trackpoints; + // Don't try again unless tracks have changed + attempt_merge = FALSE; + + trps = orig_trk->trackpoints; if ( !trps ) return; - if (nearby_tracks) { g_list_free(nearby_tracks); nearby_tracks = NULL; @@ -3501,63 +3908,97 @@ static void trw_layer_merge_by_timestamp ( gpointer pass_along[6] ) /* g_print("Original track times: %d and %d\n", t1, t2); */ params[0] = &nearby_tracks; - params[1] = trps; - params[2] = GUINT_TO_POINTER (thr); + params[1] = (gpointer)trps; + params[2] = GUINT_TO_POINTER (threshold_in_minutes*60); // In seconds /* get a list of adjacent-in-time tracks */ - g_hash_table_foreach(vtl->tracks, find_nearby_track, (gpointer)params); - - /* add original track */ - nearby_tracks = g_list_prepend(nearby_tracks, orig_track_name); + g_hash_table_foreach(vtl->tracks, find_nearby_tracks_by_time, params); /* merge them */ - { -#define get_track(x) VIK_TRACK(g_hash_table_lookup(vtl->tracks, (gchar *)((x)->data))) -#define get_first_trackpoint(x) VIK_TRACKPOINT(get_track(x)->trackpoints->data) -#define get_last_trackpoint(x) VIK_TRACKPOINT(g_list_last(get_track(x)->trackpoints)->data) - GList *l = nearby_tracks; - VikTrack *tr = vik_track_new(); - tr->visible = track->visible; - track_count = 0; - while (l) { - /* - time_t t1, t2; - t1 = get_first_trackpoint(l)->timestamp; - t2 = get_last_trackpoint(l)->timestamp; - g_print(" %20s: track %d - %d\n", (char *)l->data, (int)t1, (int)t2); - */ - + GList *l = nearby_tracks; + while ( l ) { + /* +#define get_first_trackpoint(x) VIK_TRACKPOINT(VIK_TRACK(x)->trackpoints->data) +#define get_last_trackpoint(x) VIK_TRACKPOINT(g_list_last(VIK_TRACK(x)->trackpoints)->data) + time_t t1, t2; + t1 = get_first_trackpoint(l)->timestamp; + t2 = get_last_trackpoint(l)->timestamp; +#undef get_first_trackpoint +#undef get_last_trackpoint + g_print(" %20s: track %d - %d\n", VIK_TRACK(l->data)->name, (int)t1, (int)t2); + */ - /* remove trackpoints from merged track, delete track */ - tr->trackpoints = g_list_concat(tr->trackpoints, get_track(l)->trackpoints); - get_track(l)->trackpoints = NULL; - vik_trw_layer_delete_track(vtl, l->data); + /* remove trackpoints from merged track, delete track */ + orig_trk->trackpoints = g_list_concat(orig_trk->trackpoints, VIK_TRACK(l->data)->trackpoints); + VIK_TRACK(l->data)->trackpoints = NULL; + vik_trw_layer_delete_track (vtl, VIK_TRACK(l->data)); - track_count ++; - l = g_list_next(l); - } - tr->trackpoints = g_list_sort(tr->trackpoints, trackpoint_compare); - vik_trw_layer_add_track(vtl, strdup(orig_track_name), tr); + // Tracks have changed, therefore retry again against all the remaining tracks + attempt_merge = TRUE; -#undef get_first_trackpoint -#undef get_last_trackpoint -#undef get_track + l = g_list_next(l); } - } while (track_count > 1); + + orig_trk->trackpoints = g_list_sort(orig_trk->trackpoints, trackpoint_compare); + } + g_list_free(nearby_tracks); - free(orig_track_name); vik_layer_emit_update( VIK_LAYER(vtl), FALSE ); } +/** + * Split a track at the currently selected trackpoint + */ +static void trw_layer_split_at_selected_trackpoint ( VikTrwLayer *vtl ) +{ + if ( !vtl->current_tpl ) + return; + + if ( vtl->current_tpl->next && vtl->current_tpl->prev ) { + gchar *name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, vtl->current_tp_track->name); + if ( name ) { + VikTrack *tr = vik_track_new (); + GList *newglist = g_list_alloc (); + newglist->prev = NULL; + newglist->next = vtl->current_tpl->next; + newglist->data = vik_trackpoint_copy(VIK_TRACKPOINT(vtl->current_tpl->data)); + tr->trackpoints = newglist; + + vtl->current_tpl->next->prev = newglist; /* end old track here */ + vtl->current_tpl->next = NULL; + + vtl->current_tpl = newglist; /* change tp to first of new track. */ + vtl->current_tp_track = tr; + + tr->visible = TRUE; + + vik_trw_layer_add_track ( vtl, name, tr ); + + trku_udata udata; + udata.trk = tr; + udata.uuid = NULL; + + // Also need id of newly created track + gpointer *trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata ); + if ( trkf && udata.uuid ) + vtl->current_tp_id = udata.uuid; + else + vtl->current_tp_id = NULL; + + vik_layer_emit_update(VIK_LAYER(vtl), FALSE); + } + } +} + /* split by time routine */ static void trw_layer_split_by_timestamp ( gpointer pass_along[6] ) { - VikTrack *track = (VikTrack *) g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ); + VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0]; + VikTrack *track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] ); GList *trps = track->trackpoints; GList *iter; GList *newlists = NULL; GList *newtps = NULL; - guint i; static guint thr = 1; time_t ts, prev_ts; @@ -3599,7 +4040,6 @@ static void trw_layer_split_by_timestamp ( gpointer pass_along[6] ) /* put lists of trackpoints into tracks */ iter = newlists; - i = 1; // Only bother updating if the split results in new tracks if (g_list_length (newlists) > 1) { while (iter) { @@ -3610,14 +4050,15 @@ static void trw_layer_split_by_timestamp ( gpointer pass_along[6] ) tr->visible = track->visible; tr->trackpoints = (GList *)(iter->data); - new_tr_name = g_strdup_printf("%s #%d", (gchar *) pass_along[3], i++); - vik_trw_layer_add_track(VIK_TRW_LAYER(pass_along[0]), new_tr_name, tr); + new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, track->name); + vik_trw_layer_add_track(vtl, new_tr_name, tr); /* g_print("adding track %s, times %d - %d\n", new_tr_name, VIK_TRACKPOINT(tr->trackpoints->data)->timestamp, VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp);*/ iter = g_list_next(iter); } - vik_trw_layer_delete_track(VIK_TRW_LAYER(pass_along[0]), (gchar *)pass_along[3]); + // Remove original track and then update the display + vik_trw_layer_delete_track (vtl, track); vik_layer_emit_update(VIK_LAYER(pass_along[0]), FALSE); } g_list_free(newlists); @@ -3675,7 +4116,6 @@ static void trw_layer_split_by_n_points ( gpointer pass_along[6] ) /* put lists of trackpoints into tracks */ iter = newlists; - guint i = 1; // Only bother updating if the split results in new tracks if (g_list_length (newlists) > 1) { while (iter) { @@ -3686,20 +4126,103 @@ static void trw_layer_split_by_n_points ( gpointer pass_along[6] ) tr->visible = track->visible; tr->trackpoints = (GList *)(iter->data); - new_tr_name = g_strdup_printf("%s #%d", (gchar *) pass_along[3], i++); - vik_trw_layer_add_track(VIK_TRW_LAYER(pass_along[0]), new_tr_name, tr); + new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, track->name); + vik_trw_layer_add_track(vtl, new_tr_name, tr); iter = g_list_next(iter); } // Remove original track and then update the display - vik_trw_layer_delete_track(VIK_TRW_LAYER(pass_along[0]), (gchar *)pass_along[3]); + vik_trw_layer_delete_track (vtl, track); vik_layer_emit_update(VIK_LAYER(pass_along[0]), FALSE); } g_list_free(newlists); } +/** + * Split a track at the currently selected trackpoint + */ +static void trw_layer_split_at_trackpoint ( gpointer pass_along[6] ) +{ + VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0]; + trw_layer_split_at_selected_trackpoint ( vtl ); +} + +/** + * Split a track by its segments + */ +static void trw_layer_split_segments ( gpointer pass_along[6] ) +{ + VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0]; + VikTrack *trk = (VikTrack *)g_hash_table_lookup ( vtl->tracks, pass_along[3] ); + guint ntracks; + + VikTrack **tracks = vik_track_split_into_segments (trk, &ntracks); + gchar *new_tr_name; + guint i; + for ( i = 0; i < ntracks; i++ ) { + if ( tracks[i] ) { + new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, trk->name); + vik_trw_layer_add_track ( vtl, new_tr_name, tracks[i] ); + } + } + if ( tracks ) { + g_free ( tracks ); + // Remove original track + vik_trw_layer_delete_track ( vtl, trk ); + vik_layer_emit_update ( VIK_LAYER(vtl), FALSE ); + } + else { + a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Can not split track as it has no segments")); + } +} /* end of split/merge routines */ +/** + * Delete adjacent track points at the same position + * AKA Delete Dulplicates on the Properties Window + */ +static void trw_layer_delete_points_same_position ( gpointer pass_along[6] ) +{ + VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0]; + VikTrack *trk = (VikTrack *)g_hash_table_lookup ( vtl->tracks, pass_along[3] ); + + gulong removed = vik_track_remove_dup_points ( trk ); + + // Track has been updated so update tps: + trw_layer_cancel_tps_of_track ( vtl, trk ); + + // Inform user how much was deleted as it's not obvious from the normal view + gchar str[64]; + const gchar *tmp_str = ngettext("Deleted %ld point", "Deleted %ld points", removed); + g_snprintf(str, 64, tmp_str, removed); + a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str); + + vik_layer_emit_update ( VIK_LAYER(vtl), FALSE ); +} + +/** + * Delete adjacent track points with the same timestamp + * Normally new tracks that are 'routes' won't have any timestamps so should be OK to clean up the track + */ +static void trw_layer_delete_points_same_time ( gpointer pass_along[6] ) +{ + VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0]; + VikTrack *trk = (VikTrack *)g_hash_table_lookup ( vtl->tracks, pass_along[3] ); + + gulong removed = vik_track_remove_same_time_points ( trk ); + + // Track has been updated so update tps: + trw_layer_cancel_tps_of_track ( vtl, trk ); + + // Inform user how much was deleted as it's not obvious from the normal view + gchar str[64]; + const gchar *tmp_str = ngettext("Deleted %ld point", "Deleted %ld points", removed); + g_snprintf(str, 64, tmp_str, removed); + a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str); + + vik_layer_emit_update ( VIK_LAYER(vtl), FALSE ); +} + /** * Reverse a track */ @@ -3721,13 +4244,168 @@ static void trw_layer_reverse ( gpointer pass_along[6] ) /** * Similar to trw_layer_enum_item, but this uses a sorted method */ +/* Currently unused static void trw_layer_sorted_name_list(gpointer key, gpointer value, gpointer udata) { GList **list = (GList**)udata; - //*list = g_list_prepend(*all, key); //unsorted method + // *list = g_list_prepend(*all, key); //unsorted method // Sort named list alphabetically *list = g_list_insert_sorted_with_data (*list, key, sort_alphabetically, NULL); } +*/ + +/** + * Now Waypoint specific sort + */ +static void trw_layer_sorted_wp_id_by_name_list (const gpointer id, const VikWaypoint *wp, gpointer udata) +{ + GList **list = (GList**)udata; + // Sort named list alphabetically + *list = g_list_insert_sorted_with_data (*list, wp->name, sort_alphabetically, NULL); +} + +/** + * Track specific sort + */ +static void trw_layer_sorted_track_id_by_name_list (const gpointer id, const VikTrack *trk, gpointer udata) +{ + GList **list = (GList**)udata; + // Sort named list alphabetically + *list = g_list_insert_sorted_with_data (*list, trk->name, sort_alphabetically, NULL); +} + + +typedef struct { + gboolean has_same_track_name; + const gchar *same_track_name; +} same_track_name_udata; + +static gint check_tracks_for_same_name ( gconstpointer aa, gconstpointer bb, gpointer udata ) +{ + const gchar* namea = (const gchar*) aa; + const gchar* nameb = (const gchar*) bb; + + // the test + gint result = strcmp ( namea, nameb ); + + if ( result == 0 ) { + // Found two names the same + same_track_name_udata *user_data = udata; + user_data->has_same_track_name = TRUE; + user_data->same_track_name = namea; + } + + // Leave ordering the same + return 0; +} + +/** + * Find out if any tracks have the same name in this layer + */ +static gboolean trw_layer_has_same_track_names ( VikTrwLayer *vtl ) +{ + // Sort items by name, then compare if any next to each other are the same + + GList *track_names = NULL; + g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names ); + + // No tracks + if ( ! track_names ) + return FALSE; + + same_track_name_udata udata; + udata.has_same_track_name = FALSE; + + // Use sort routine to traverse list comparing items + // Don't care how this list ends up ordered ( doesn't actually change ) - care about the returned status + GList *dummy_list = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata ); + // Still no tracks... + if ( ! dummy_list ) + return FALSE; + + return udata.has_same_track_name; +} + +/** + * Force unqiue track names for this layer + * Note the panel is a required parameter to enable the update of the names displayed + */ +static void vik_trw_layer_uniquify_tracks ( VikTrwLayer *vtl, VikLayersPanel *vlp ) +{ + // . Search list for an instance of repeated name + // . get track of this name + // . create new name + // . rename track & update equiv. treeview iter + // . repeat until all different + + same_track_name_udata udata; + + GList *track_names = NULL; + udata.has_same_track_name = FALSE; + udata.same_track_name = NULL; + + g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names ); + + // No tracks + if ( ! track_names ) + return; + + GList *dummy_list1 = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata ); + + // Still no tracks... + if ( ! dummy_list1 ) + return; + + while ( udata.has_same_track_name ) { + + // Find a track with the same name + VikTrack *trk = vik_trw_layer_get_track ( vtl, (gpointer) udata.same_track_name ); + + if ( ! trk ) { + // Broken :( + g_critical("Houston, we've had a problem."); + vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, + _("Internal Error in vik_trw_layer_uniquify_tracks") ); + return; + } + + // Rename it + gchar *newname = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, udata.same_track_name ); + vik_track_set_name ( trk, newname ); + + trku_udata udataU; + udataU.trk = trk; + udataU.uuid = NULL; + + // Need want key of it for treeview update + gpointer *trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udataU ); + + if ( trkf && udataU.uuid ) { + + GtkTreeIter *it = g_hash_table_lookup ( vtl->tracks_iters, udataU.uuid ); + + if ( it ) { + vik_treeview_item_set_name ( VIK_LAYER(vtl)->vt, it, newname ); +#ifdef VIK_CONFIG_ALPHABETIZED_TRW + vik_treeview_sublayer_realphabetize ( VIK_LAYER(vtl)->vt, it, newname ); +#endif + } + } + + // Start trying to find same names again... + track_names = NULL; + g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names ); + udata.has_same_track_name = FALSE; + GList *dummy_list2 = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata ); + + // No tracks any more - give up searching + if ( ! dummy_list2 ) + udata.has_same_track_name = FALSE; + } + + // Update + vik_layers_panel_emit_update ( vlp ); +} /** * @@ -3736,8 +4414,19 @@ static void trw_layer_delete_tracks_from_selection ( gpointer lav[2] ) { VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]); GList *all = NULL; + + // Ensure list of track names offered is unique + if ( trw_layer_has_same_track_names ( vtl ) ) { + if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl), + _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) { + vik_trw_layer_uniquify_tracks ( vtl, VIK_LAYERS_PANEL(lav[1]) ); + } + else + return; + } + // Sort list alphabetically for better presentation - g_hash_table_foreach(vtl->tracks, trw_layer_sorted_name_list, &all); + g_hash_table_foreach(vtl->tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list, &all); if ( ! all ) { a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No tracks found")); @@ -3757,13 +4446,146 @@ static void trw_layer_delete_tracks_from_selection ( gpointer lav[2] ) if ( delete_list ) { GList *l; for (l = delete_list; l != NULL; l = g_list_next(l)) { - vik_trw_layer_delete_track(vtl, l->data); + // This deletes first trk it finds of that name (but uniqueness is enforced above) + trw_layer_delete_track_by_name (vtl, l->data); } g_list_free(delete_list); vik_layer_emit_update( VIK_LAYER(vtl), FALSE ); } } +typedef struct { + gboolean has_same_waypoint_name; + const gchar *same_waypoint_name; +} same_waypoint_name_udata; + +static gint check_waypoints_for_same_name ( gconstpointer aa, gconstpointer bb, gpointer udata ) +{ + const gchar* namea = (const gchar*) aa; + const gchar* nameb = (const gchar*) bb; + + // the test + gint result = strcmp ( namea, nameb ); + + if ( result == 0 ) { + // Found two names the same + same_waypoint_name_udata *user_data = udata; + user_data->has_same_waypoint_name = TRUE; + user_data->same_waypoint_name = namea; + } + + // Leave ordering the same + return 0; +} + +/** + * Find out if any waypoints have the same name in this layer + */ +gboolean trw_layer_has_same_waypoint_names ( VikTrwLayer *vtl ) +{ + // Sort items by name, then compare if any next to each other are the same + + GList *waypoint_names = NULL; + g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names ); + + // No waypoints + if ( ! waypoint_names ) + return FALSE; + + same_waypoint_name_udata udata; + udata.has_same_waypoint_name = FALSE; + + // Use sort routine to traverse list comparing items + // Don't care how this list ends up ordered ( doesn't actually change ) - care about the returned status + GList *dummy_list = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata ); + // Still no waypoints... + if ( ! dummy_list ) + return FALSE; + + return udata.has_same_waypoint_name; +} + +/** + * Force unqiue waypoint names for this layer + * Note the panel is a required parameter to enable the update of the names displayed + */ +static void vik_trw_layer_uniquify_waypoints ( VikTrwLayer *vtl, VikLayersPanel *vlp ) +{ + // . Search list for an instance of repeated name + // . get waypoint of this name + // . create new name + // . rename waypoint & update equiv. treeview iter + // . repeat until all different + + same_waypoint_name_udata udata; + + GList *waypoint_names = NULL; + udata.has_same_waypoint_name = FALSE; + udata.same_waypoint_name = NULL; + + g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names ); + + // No waypoints + if ( ! waypoint_names ) + return; + + GList *dummy_list1 = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata ); + + // Still no waypoints... + if ( ! dummy_list1 ) + return; + + while ( udata.has_same_waypoint_name ) { + + // Find a waypoint with the same name + VikWaypoint *waypoint = vik_trw_layer_get_waypoint ( vtl, (gpointer) udata.same_waypoint_name ); + + if ( ! waypoint ) { + // Broken :( + g_critical("Houston, we've had a problem."); + vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, + _("Internal Error in vik_trw_layer_uniquify_waypoints") ); + return; + } + + // Rename it + gchar *newname = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, udata.same_waypoint_name ); + vik_waypoint_set_name ( waypoint, newname ); + + wpu_udata udataU; + udataU.wp = waypoint; + udataU.uuid = NULL; + + // Need want key of it for treeview update + 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 ); + + if ( it ) { + vik_treeview_item_set_name ( VIK_LAYER(vtl)->vt, it, newname ); +#ifdef VIK_CONFIG_ALPHABETIZED_TRW + vik_treeview_sublayer_realphabetize ( VIK_LAYER(vtl)->vt, it, newname ); +#endif + } + } + + // 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 ); + udata.has_same_waypoint_name = FALSE; + GList *dummy_list2 = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata ); + + // No waypoints any more - give up searching + if ( ! dummy_list2 ) + udata.has_same_waypoint_name = FALSE; + } + + // Update + vik_layers_panel_emit_update ( vlp ); +} + /** * */ @@ -3772,8 +4594,18 @@ static void trw_layer_delete_waypoints_from_selection ( gpointer lav[2] ) VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]); GList *all = NULL; + // Ensure list of waypoint names offered is unique + if ( trw_layer_has_same_waypoint_names ( vtl ) ) { + if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl), + _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) { + vik_trw_layer_uniquify_waypoints ( vtl, VIK_LAYERS_PANEL(lav[1]) ); + } + else + return; + } + // Sort list alphabetically for better presentation - g_hash_table_foreach ( vtl->waypoints, trw_layer_sorted_name_list, &all); + g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &all); if ( ! all ) { a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No waypoints found")); return; @@ -3794,7 +4626,8 @@ static void trw_layer_delete_waypoints_from_selection ( gpointer lav[2] ) if ( delete_list ) { GList *l; for (l = delete_list; l != NULL; l = g_list_next(l)) { - vik_trw_layer_delete_waypoint(vtl, l->data); + // This deletes first waypoint it finds of that name (but uniqueness is enforced above) + trw_layer_delete_waypoint_by_name (vtl, l->data); } g_list_free(delete_list); vik_layer_emit_update( VIK_LAYER(vtl), FALSE ); @@ -3820,92 +4653,69 @@ static const gchar* trw_layer_sublayer_rename_request ( VikTrwLayer *l, const gc { if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) { - gchar *rv; - VikWaypoint *wp; + VikWaypoint *wp = g_hash_table_lookup ( l->waypoints, sublayer ); - if (strcmp(newname, sublayer) == 0 ) + // No actual change to the name supplied + if (strcmp(newname, wp->name) == 0 ) return NULL; - if (strcasecmp(newname, sublayer)) { /* Not just changing case */ - if (g_hash_table_lookup( l->waypoints, newname)) - { - a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(l), _("Waypoint Already Exists") ); + VikWaypoint *wpf = vik_trw_layer_get_waypoint ( l, newname ); + + if ( wpf ) { + // An existing waypoint has been found with the requested name + if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l), + _("A waypoint with the name \"%s\" already exists. Really create one with the same name?"), + newname ) ) return NULL; - } } - iter = g_hash_table_lookup ( l->waypoints_iters, sublayer ); - g_hash_table_steal ( l->waypoints_iters, sublayer ); + // Update WP name and refresh the treeview + vik_waypoint_set_name (wp, newname); - wp = vik_waypoint_copy ( VIK_WAYPOINT(g_hash_table_lookup ( l->waypoints, sublayer )) ); - highest_wp_number_remove_wp(l, sublayer); - g_hash_table_remove ( l->waypoints, sublayer ); - - rv = g_strdup(newname); - - vik_treeview_item_set_pointer ( VIK_LAYER(l)->vt, iter, rv ); - - highest_wp_number_add_wp(l, rv); - g_hash_table_insert ( l->waypoints, rv, wp ); - g_hash_table_insert ( l->waypoints_iters, rv, iter ); - - /* it hasn't been updated yet so we pass new name */ #ifdef VIK_CONFIG_ALPHABETIZED_TRW - vik_treeview_sublayer_realphabetize ( VIK_LAYER(l)->vt, iter, rv ); + vik_treeview_sublayer_realphabetize ( VIK_LAYER(l)->vt, iter, newname ); #endif vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) ); - return rv; + + return newname; } + if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) { - gchar *rv; - VikTrack *tr; - GtkTreeIter *iter; - gchar *orig_key; + VikTrack *trk = g_hash_table_lookup ( l->tracks, sublayer ); - if (strcmp(newname, sublayer) == 0) + // No actual change to the name supplied + if (strcmp(newname, trk->name) == 0) return NULL; - if (strcasecmp(newname, sublayer)) { /* Not just changing case */ - if (g_hash_table_lookup( l->tracks, newname)) - { - a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(l), _("Track Already Exists") ); + VikTrack *trkf = vik_trw_layer_get_track ( l, (gpointer) newname ); + + if ( trkf ) { + // An existing track has been found with the requested name + if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l), + _("A track with the name \"%s\" already exists. Really create one with the same name?"), + newname ) ) return NULL; - } } + // Update track name and refresh GUI parts + vik_track_set_name (trk, newname); - g_hash_table_lookup_extended ( l->tracks, sublayer, (void *)&orig_key, (void *)&tr ); - g_hash_table_steal ( l->tracks, sublayer ); - - iter = g_hash_table_lookup ( l->tracks_iters, sublayer ); - g_hash_table_steal ( l->tracks_iters, sublayer ); - - rv = g_strdup(newname); - - vik_treeview_item_set_pointer ( VIK_LAYER(l)->vt, iter, rv ); - - g_hash_table_insert ( l->tracks, rv, tr ); - g_hash_table_insert ( l->tracks_iters, rv, iter ); - - /* don't forget about current_tp_track_name, update that too */ - if ( l->current_tp_track_name && g_strcasecmp(orig_key,l->current_tp_track_name) == 0 ) - { - l->current_tp_track_name = rv; - if ( l->tpwin ) - vik_trw_layer_tpwin_set_track_name ( l->tpwin, rv ); + // Update any subwindows that could be displaying this track which has changed name + // Only one Track Edit Window + if ( l->current_tp_track == trk && l->tpwin ) { + vik_trw_layer_tpwin_set_track_name ( l->tpwin, newname ); } - else if ( l->last_tp_track_name && g_strcasecmp(orig_key,l->last_tp_track_name) == 0 ) - l->last_tp_track_name = rv; - - g_free ( orig_key ); + // Property Dialog of the track + vik_trw_layer_propwin_update ( trk ); #ifdef VIK_CONFIG_ALPHABETIZED_TRW - vik_treeview_sublayer_realphabetize ( VIK_LAYER(l)->vt, iter, rv ); + vik_treeview_sublayer_realphabetize ( VIK_LAYER(l)->vt, iter, newname ); #endif vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) ); - return rv; + + return newname; } return NULL; } @@ -3918,21 +4728,19 @@ static gboolean is_valid_geocache_name ( gchar *str ) static void trw_layer_track_use_with_filter ( gpointer pass_along[6] ) { - gchar *track_name = (gchar *) pass_along[3]; - VikTrack *tr = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, track_name ); - a_acquire_set_filter_track ( tr, track_name ); + VikTrack *trk = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ); + a_acquire_set_filter_track ( trk ); } -static gboolean is_valid_google_route ( VikTrwLayer *vtl, const gchar *track_name ) +static gboolean is_valid_google_route ( VikTrwLayer *vtl, const gpointer track_id ) { - VikTrack *tr = g_hash_table_lookup ( vtl->tracks, track_name ); + VikTrack *tr = g_hash_table_lookup ( vtl->tracks, track_id ); return ( tr && tr->comment && strlen(tr->comment) > 7 && !strncmp(tr->comment, "from:", 5) ); } static void trw_layer_track_google_route_webpage ( gpointer pass_along[6] ) { - gchar *track_name = (gchar *) pass_along[3]; - VikTrack *tr = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, track_name ); + VikTrack *tr = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ); if ( tr ) { gchar *escaped = uri_escape ( tr->comment ); gchar *webpage = g_strdup_printf("http://maps.google.com/maps?f=q&hl=en&q=%s", escaped ); @@ -3946,7 +4754,7 @@ static void trw_layer_track_google_route_webpage ( gpointer pass_along[6] ) /* viewpoint is now available instead */ static gboolean trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *menu, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter, VikViewport *vvp ) { - static gpointer pass_along[6]; + static gpointer pass_along[8]; GtkWidget *item; gboolean rv = FALSE; @@ -3956,6 +4764,8 @@ static gboolean trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *men pass_along[3] = sublayer; pass_along[4] = GINT_TO_POINTER (1); // Confirm delete request pass_along[5] = vvp; + pass_along[6] = iter; + pass_along[7] = NULL; // For misc purposes - maybe track or waypoint if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT || subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) { @@ -4007,22 +4817,24 @@ static gboolean trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *men gtk_widget_show ( item ); } - if ( is_valid_geocache_name ( (gchar *) sublayer ) ) - { - if ( !separator_created ) { - item = gtk_menu_item_new (); - gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item ); - gtk_widget_show ( item ); - separator_created = TRUE; - } + VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(l)->waypoints, sublayer ); - item = gtk_menu_item_new_with_mnemonic ( _("_Visit Geocache Webpage") ); - g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_gc_webpage), pass_along ); - gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item ); - gtk_widget_show ( item ); - } + if ( wp && wp->name ) { + if ( is_valid_geocache_name ( wp->name ) ) { - VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(l)->waypoints, sublayer ); + if ( !separator_created ) { + item = gtk_menu_item_new (); + gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item ); + gtk_widget_show ( item ); + separator_created = TRUE; + } + + item = gtk_menu_item_new_with_mnemonic ( _("_Visit Geocache Webpage") ); + g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_gc_webpage), pass_along ); + gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item ); + gtk_widget_show ( item ); + } + } if ( wp && wp->image ) { @@ -4127,11 +4939,17 @@ static gboolean trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *men if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) { - GtkWidget *goto_submenu; item = gtk_menu_item_new (); gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item ); gtk_widget_show ( item ); + item = gtk_image_menu_item_new_with_mnemonic ( _("_View Track") ); + gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) ); + g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_track_view), pass_along ); + gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item ); + gtk_widget_show ( item ); + + GtkWidget *goto_submenu; goto_submenu = gtk_menu_new (); item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto") ); gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) ); @@ -4175,30 +4993,81 @@ static gboolean trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *men gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item ); gtk_widget_show ( item ); - item = gtk_image_menu_item_new_with_mnemonic ( _("_View Track") ); - gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) ); - g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_track_view), pass_along ); - gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item ); + GtkWidget *combine_submenu; + combine_submenu = gtk_menu_new (); + item = gtk_image_menu_item_new_with_mnemonic ( _("Co_mbine") ); + gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONNECT, 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), combine_submenu ); item = gtk_menu_item_new_with_mnemonic ( _("_Merge By Time...") ); g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_by_timestamp), pass_along ); - gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item ); + gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item ); gtk_widget_show ( item ); item = gtk_menu_item_new_with_mnemonic ( _("Merge _With Other Tracks...") ); g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_with_other), pass_along ); - gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item ); + gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item ); + gtk_widget_show ( item ); + + item = gtk_menu_item_new_with_mnemonic ( _("Merge _Segments") ); + g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_by_segment), pass_along ); + gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item ); gtk_widget_show ( item ); + item = gtk_menu_item_new_with_mnemonic ( _("_Append Track...") ); + g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_append_track), pass_along ); + gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item ); + gtk_widget_show ( item ); + + GtkWidget *split_submenu; + split_submenu = gtk_menu_new (); + item = gtk_image_menu_item_new_with_mnemonic ( _("_Split") ); + gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DISCONNECT, 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), split_submenu ); + item = gtk_menu_item_new_with_mnemonic ( _("_Split By Time...") ); g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_timestamp), pass_along ); - gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item ); + gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item ); gtk_widget_show ( item ); item = gtk_menu_item_new_with_mnemonic ( _("Split By _Number of Points...") ); g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_n_points), pass_along ); - gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item ); + gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item ); + gtk_widget_show ( item ); + + // ATM always enable this entry - don't want to have to analyse the track before displaying the menu - to keep the menu speedy + item = gtk_menu_item_new_with_mnemonic ( _("Split Se_gments") ); + g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_segments), pass_along ); + gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item ); + gtk_widget_show ( item ); + + item = gtk_menu_item_new_with_mnemonic ( _("Split at _Trackpoint") ); + g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_at_trackpoint), pass_along ); + gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item ); + gtk_widget_show ( item ); + // Make it available only when a trackpoint is selected. + gtk_widget_set_sensitive ( item, (gboolean)GPOINTER_TO_INT(l->current_tpl) ); + + GtkWidget *delete_submenu; + delete_submenu = gtk_menu_new (); + item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Poi_nts") ); + gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DELETE, 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), delete_submenu ); + + item = gtk_menu_item_new_with_mnemonic ( _("Delete Points With The Same _Position") ); + g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_points_same_position), pass_along ); + gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item ); + gtk_widget_show ( item ); + + item = gtk_menu_item_new_with_mnemonic ( _("Delete Points With The Same _Time") ); + g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_points_same_time), pass_along ); + gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item ); gtk_widget_show ( item ); item = gtk_image_menu_item_new_with_mnemonic ( _("_Reverse Track") ); @@ -4234,21 +5103,25 @@ static gboolean trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *men gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item ); gtk_widget_show ( item ); +#ifdef VIK_CONFIG_GOOGLE_DIRECTIONS item = gtk_image_menu_item_new_with_mnemonic ( _("Extend _Using Route Finder") ); gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock ("Route Finder", GTK_ICON_SIZE_MENU) ); // Own icon - see stock_icons in vikwindow.c g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end_route_finder), pass_along ); gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item ); gtk_widget_show ( item ); +#endif #ifdef VIK_CONFIG_OPENSTREETMAP item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _OSM...") ); + // Convert internal pointer into actual track for usage outside this file + pass_along[7] = g_hash_table_lookup ( l->tracks, sublayer); g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(osm_traces_upload_track_cb), pass_along ); gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) ); gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item ); gtk_widget_show ( item ); #endif - if ( is_valid_google_route ( l, (gchar *) sublayer ) ) + if ( is_valid_google_route ( l, sublayer ) ) { item = gtk_image_menu_item_new_with_mnemonic ( _("_View Google Directions") ); gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NETWORK, GTK_ICON_SIZE_MENU) ); @@ -4345,7 +5218,7 @@ static void trw_layer_insert_tp_after_current_tp ( VikTrwLayer *vtl ) /* DOP / sat values remain at defaults as not they do not seem applicable to a dreamt up point */ /* Insert new point into the trackpoints list after the current TP */ - VikTrack *tr = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_track_name ); + VikTrack *tr = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_id ); gint index = g_list_index ( tr->trackpoints, tp_current ); if ( index > -1 ) { tr->trackpoints = g_list_insert (tr->trackpoints, tp_new, index+1 ); @@ -4353,15 +5226,6 @@ static void trw_layer_insert_tp_after_current_tp ( VikTrwLayer *vtl ) } } -/* to be called when last_tpl no long exists. */ -static void trw_layer_cancel_last_tp ( VikTrwLayer *vtl ) -{ - if ( vtl->tpwin ) /* can't join with a non-existant TP. */ - vik_trw_layer_tpwin_disable_join ( vtl->tpwin ); - vtl->last_tpl = NULL; - vtl->last_tp_track_name = NULL; -} - static void trw_layer_cancel_current_tp ( VikTrwLayer *vtl, gboolean destroy ) { if ( vtl->tpwin ) @@ -4377,7 +5241,8 @@ static void trw_layer_cancel_current_tp ( VikTrwLayer *vtl, gboolean destroy ) if ( vtl->current_tpl ) { vtl->current_tpl = NULL; - vtl->current_tp_track_name = NULL; + vtl->current_tp_track = NULL; + vtl->current_tp_id = NULL; vik_layer_emit_update(VIK_LAYER(vtl), FALSE); } } @@ -4393,116 +5258,54 @@ 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 ) { - gchar *name = get_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, vtl->current_tp_track_name); - if ( ( name = a_dialog_new_track ( GTK_WINDOW(vtl->tpwin), vtl->tracks, name ) ) ) - { - VikTrack *tr = vik_track_new (); - GList *newglist = g_list_alloc (); - newglist->prev = NULL; - newglist->next = vtl->current_tpl->next; - newglist->data = vik_trackpoint_copy(VIK_TRACKPOINT(vtl->current_tpl->data)); - tr->trackpoints = newglist; - - vtl->current_tpl->next->prev = newglist; /* end old track here */ - vtl->current_tpl->next = NULL; - - vtl->current_tpl = newglist; /* change tp to first of new track. */ - vtl->current_tp_track_name = name; - - vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track_name ); - - tr->visible = TRUE; - - vik_trw_layer_add_track ( vtl, name, tr ); - vik_layer_emit_update(VIK_LAYER(vtl), FALSE); - } + trw_layer_split_at_selected_trackpoint ( vtl ); + vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name ); } else if ( response == VIK_TRW_LAYER_TPWIN_DELETE ) { - VikTrack *tr = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_track_name ); - GList *new_tpl; - g_assert(tr != NULL); + VikTrack *tr = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_id ); + if ( tr == NULL ) + return; - /* can't join with a non-existent trackpoint */ - vtl->last_tpl = NULL; - vtl->last_tp_track_name = NULL; + GList *new_tpl; + // Find available adjacent trackpoint if ( (new_tpl = vtl->current_tpl->next) || (new_tpl = vtl->current_tpl->prev) ) { if ( VIK_TRACKPOINT(vtl->current_tpl->data)->newsegment && vtl->current_tpl->next ) VIK_TRACKPOINT(vtl->current_tpl->next->data)->newsegment = TRUE; /* don't concat segments on del */ - tr->trackpoints = g_list_remove_link ( tr->trackpoints, vtl->current_tpl ); /* this nulls current_tpl->prev and next */ + // Delete current trackpoint + vik_trackpoint_free ( vtl->current_tpl->data ); + tr->trackpoints = g_list_delete_link ( tr->trackpoints, vtl->current_tpl ); - /* at this point the old trackpoint exists, but the list links are correct (new), so it is safe to do this. */ - vik_trw_layer_tpwin_set_tp ( vtl->tpwin, new_tpl, vtl->current_tp_track_name ); + // Set to current to the available adjacent trackpoint + vtl->current_tpl = new_tpl; - trw_layer_cancel_last_tp ( vtl ); + // Reset dialog with the available adjacent trackpoint + if ( vtl->current_tp_track ) + vik_trw_layer_tpwin_set_tp ( vtl->tpwin, new_tpl, vtl->current_tp_track->name ); - g_free ( vtl->current_tpl->data ); /* TODO: vik_trackpoint_free() */ - g_list_free_1 ( vtl->current_tpl ); - vtl->current_tpl = new_tpl; vik_layer_emit_update(VIK_LAYER(vtl), FALSE); } else { - tr->trackpoints = g_list_remove_link ( tr->trackpoints, vtl->current_tpl ); - g_free ( vtl->current_tpl->data ); /* TODO longone: vik_trackpoint_new() and vik_trackpoint_free() */ - g_list_free_1 ( vtl->current_tpl ); + // Delete current trackpoint + vik_trackpoint_free ( vtl->current_tpl->data ); + tr->trackpoints = g_list_delete_link ( tr->trackpoints, vtl->current_tpl ); trw_layer_cancel_current_tp ( vtl, FALSE ); } } else if ( response == VIK_TRW_LAYER_TPWIN_FORWARD && vtl->current_tpl->next ) { - vtl->last_tpl = vtl->current_tpl; - 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 ) + vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl = vtl->current_tpl->next, vtl->current_tp_track->name ); vik_layer_emit_update(VIK_LAYER(vtl), FALSE); /* 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 ) { - vtl->last_tpl = vtl->current_tpl; - vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl = vtl->current_tpl->prev, vtl->current_tp_track_name ); - vik_layer_emit_update(VIK_LAYER(vtl), FALSE); - } - else if ( response == VIK_TRW_LAYER_TPWIN_JOIN ) - { - // Check tracks exist and are different before joining - if ( ! vtl->last_tp_track_name || ! vtl->current_tp_track_name || vtl->last_tp_track_name == vtl->current_tp_track_name ) - return; - - VikTrack *tr1 = g_hash_table_lookup ( vtl->tracks, vtl->last_tp_track_name ); - VikTrack *tr2 = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_track_name ); - - VikTrack *tr_first = tr1, *tr_last = tr2; - - gchar *tmp; - - if ( (!vtl->last_tpl->next) && (!vtl->current_tpl->next) ) /* both endpoints */ - vik_track_reverse ( tr2 ); /* reverse the second, that way second track clicked will be later. */ - else if ( (!vtl->last_tpl->prev) && (!vtl->current_tpl->prev) ) - vik_track_reverse ( tr1 ); - else if ( (!vtl->last_tpl->prev) && (!vtl->current_tpl->next) ) /* clicked startpoint, then endpoint -- concat end to start */ - { - tr_first = tr2; - tr_last = tr1; - } - /* default -- clicked endpoint then startpoint -- connect endpoint to startpoint */ - - if ( tr_last->trackpoints ) /* deleting this part here joins them into 1 segmented track. useful but there's no place in the UI for this feature. segments should be deprecated anyway. */ - VIK_TRACKPOINT(tr_last->trackpoints->data)->newsegment = FALSE; - tr1->trackpoints = g_list_concat ( tr_first->trackpoints, tr_last->trackpoints ); - tr2->trackpoints = NULL; - - tmp = vtl->current_tp_track_name; - - vtl->current_tp_track_name = vtl->last_tp_track_name; /* current_tp stays the same (believe it or not!) */ - vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track_name ); - - /* if we did this before, trw_layer_delete_track would have canceled the current tp because - * it was the current track. canceling the current tp would have set vtl->current_tpl to NULL */ - vik_trw_layer_delete_track ( vtl, tmp ); - - trw_layer_cancel_last_tp ( vtl ); /* same TP, can't join. */ + 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 ); vik_layer_emit_update(VIK_LAYER(vtl), FALSE); } else if ( response == VIK_TRW_LAYER_TPWIN_INSERT && vtl->current_tpl->next ) @@ -4525,7 +5328,8 @@ static void trw_layer_tpwin_init ( VikTrwLayer *vtl ) gtk_widget_show_all ( GTK_WIDGET(vtl->tpwin) ); } if ( vtl->current_tpl ) - vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track_name ); + if ( vtl->current_tp_track ) + vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name ); /* set layer name and TP data */ } @@ -4538,7 +5342,7 @@ static void trw_layer_tpwin_init ( VikTrwLayer *vtl ) typedef struct { gint x, y; gint closest_x, closest_y; - gchar *closest_wp_name; + gpointer *closest_wp_id; VikWaypoint *closest_wp; VikViewport *vvp; } WPSearchParams; @@ -4546,13 +5350,13 @@ typedef struct { typedef struct { gint x, y; gint closest_x, closest_y; - gchar *closest_track_name; + gpointer closest_track_id; VikTrackpoint *closest_tp; VikViewport *vvp; GList *closest_tpl; } TPSearchParams; -static void waypoint_search_closest_tp ( gchar *name, VikWaypoint *wp, WPSearchParams *params ) +static void waypoint_search_closest_tp ( gpointer id, VikWaypoint *wp, WPSearchParams *params ) { gint x, y; if ( !wp->visible ) @@ -4568,7 +5372,7 @@ static void waypoint_search_closest_tp ( gchar *name, VikWaypoint *wp, WPSearchP if ( x <= params->x + slackx && x >= params->x - slackx && y <= params->y + slacky && y >= params->y - slacky ) { - params->closest_wp_name = name; + params->closest_wp_id = id; params->closest_wp = wp; params->closest_x = x; params->closest_y = y; @@ -4578,14 +5382,14 @@ static void waypoint_search_closest_tp ( gchar *name, VikWaypoint *wp, WPSearchP ((!params->closest_wp) || /* was the old waypoint we already found closer than this one? */ abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y))) { - params->closest_wp_name = name; + params->closest_wp_id = id; params->closest_wp = wp; params->closest_x = x; params->closest_y = y; } } -static void track_search_closest_tp ( gchar *name, VikTrack *t, TPSearchParams *params ) +static void track_search_closest_tp ( gpointer id, VikTrack *t, TPSearchParams *params ) { GList *tpl = t->trackpoints; VikTrackpoint *tp; @@ -4604,7 +5408,7 @@ static void track_search_closest_tp ( gchar *name, VikTrack *t, TPSearchParams * ((!params->closest_tp) || /* was the old trackpoint we already found closer than this one? */ abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y))) { - params->closest_track_name = name; + params->closest_track_id = id; params->closest_tp = tp; params->closest_tpl = tpl; params->closest_x = x; @@ -4620,7 +5424,7 @@ static VikTrackpoint *closest_tp_in_five_pixel_interval ( VikTrwLayer *vtl, VikV params.x = x; params.y = y; params.vvp = vvp; - params.closest_track_name = NULL; + params.closest_track_id = NULL; params.closest_tp = NULL; g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, ¶ms); return params.closest_tp; @@ -4633,7 +5437,7 @@ static VikWaypoint *closest_wp_in_five_pixel_interval ( VikTrwLayer *vtl, VikVie params.y = y; params.vvp = vvp; params.closest_wp = NULL; - params.closest_wp_name = NULL; + params.closest_wp_id = NULL; g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, ¶ms); return params.closest_wp; } @@ -4711,20 +5515,17 @@ static gboolean trw_layer_select_release ( VikTrwLayer *vtl, GdkEventButton *eve vtl->current_wp->coord = new_coord; else { if ( vtl->current_tpl ) { - VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord; + VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord; if ( vtl->tpwin ) - vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track_name ); - - // Don't really know what this is for but seems like it might be handy... - /* can't join with itself! */ - trw_layer_cancel_last_tp ( vtl ); + if ( vtl->current_tp_track ) + vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name ); } } // Reset - vtl->current_wp = NULL; - vtl->current_wp_name = NULL; + vtl->current_wp = NULL; + vtl->current_wp_id = NULL; trw_layer_cancel_current_tp ( vtl, FALSE ); vik_layer_emit_update ( VIK_LAYER(vtl), FALSE ); @@ -4756,7 +5557,7 @@ static gboolean trw_layer_select_click ( VikTrwLayer *vtl, GdkEventButton *event wp_params.vvp = vvp; wp_params.x = event->x; wp_params.y = event->y; - wp_params.closest_wp_name = NULL; + wp_params.closest_wp_id = NULL; wp_params.closest_wp = NULL; g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, &wp_params); @@ -4764,7 +5565,7 @@ static gboolean trw_layer_select_click ( VikTrwLayer *vtl, GdkEventButton *event if ( wp_params.closest_wp ) { // Select - vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, wp_params.closest_wp_name ), TRUE ); + vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, wp_params.closest_wp_id ), TRUE ); // Too easy to move it so must be holding shift to start immediately moving it // or otherwise be previously selected but not have an image (otherwise clicking within image bounds (again) moves it) @@ -4778,8 +5579,8 @@ static gboolean trw_layer_select_click ( VikTrwLayer *vtl, GdkEventButton *event marker_begin_move (tet, event->x, event->y); } - vtl->current_wp = wp_params.closest_wp; - vtl->current_wp_name = wp_params.closest_wp_name; + vtl->current_wp = wp_params.closest_wp; + vtl->current_wp_id = wp_params.closest_wp_id; vik_layer_emit_update ( VIK_LAYER(vtl), FALSE ); @@ -4792,7 +5593,7 @@ static gboolean trw_layer_select_click ( VikTrwLayer *vtl, GdkEventButton *event tp_params.vvp = vvp; tp_params.x = event->x; tp_params.y = event->y; - tp_params.closest_track_name = NULL; + tp_params.closest_track_id = NULL; tp_params.closest_tp = NULL; g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, &tp_params); @@ -4800,7 +5601,7 @@ static gboolean trw_layer_select_click ( VikTrwLayer *vtl, GdkEventButton *event if ( tp_params.closest_tp ) { // Always select + highlight the track - vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, tp_params.closest_track_name ), TRUE ); + vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, tp_params.closest_track_id ), TRUE ); tet->is_waypoint = FALSE; @@ -4815,12 +5616,13 @@ static gboolean trw_layer_select_click ( VikTrwLayer *vtl, GdkEventButton *event } vtl->current_tpl = tp_params.closest_tpl; - vtl->current_tp_track_name = tp_params.closest_track_name; + vtl->current_tp_id = tp_params.closest_track_id; + vtl->current_tp_track = g_hash_table_lookup ( vtl->tracks, tp_params.closest_track_id ); set_statusbar_msg_info_trkpt ( vtl, tp_params.closest_tp ); if ( vtl->tpwin ) - vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track_name ); + vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name ); vik_layer_emit_update ( VIK_LAYER(vtl), FALSE ); return TRUE; @@ -4828,8 +5630,8 @@ static gboolean trw_layer_select_click ( VikTrwLayer *vtl, GdkEventButton *event } /* these aren't the droids you're looking for */ - vtl->current_wp = NULL; - vtl->current_wp_name = NULL; + vtl->current_wp = NULL; + vtl->current_wp_id = NULL; trw_layer_cancel_current_tp ( vtl, FALSE ); // Blank info @@ -4855,20 +5657,31 @@ static gboolean trw_layer_show_selected_viewport_menu ( VikTrwLayer *vtl, GdkEve VikTrack *track = (VikTrack*)vik_window_get_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) ); if ( track && track->visible ) { - if ( vik_window_get_selected_name ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) ) ) { + if ( track->name ) { if ( vtl->track_right_click_menu ) - gtk_object_sink ( GTK_OBJECT(vtl->track_right_click_menu) ); + gtk_object_sink ( GTK_OBJECT(vtl->track_right_click_menu) ); vtl->track_right_click_menu = GTK_MENU ( gtk_menu_new () ); - trw_layer_sublayer_add_menu_items ( vtl, - vtl->track_right_click_menu, - NULL, - VIK_TRW_LAYER_SUBLAYER_TRACK, - vik_window_get_selected_name ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) ), - g_hash_table_lookup ( vtl->tracks_iters, vik_window_get_selected_name ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) ) ), - vvp); + trku_udata udataU; + udataU.trk = track; + udataU.uuid = NULL; + + gpointer *trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udataU ); + + if ( trkf && udataU.uuid ) { + + GtkTreeIter *iter = g_hash_table_lookup ( vtl->tracks_iters, udataU.uuid ); + + trw_layer_sublayer_add_menu_items ( vtl, + vtl->track_right_click_menu, + NULL, + VIK_TRW_LAYER_SUBLAYER_TRACK, + udataU.uuid, + iter, + vvp ); + } gtk_menu_popup ( vtl->track_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() ); @@ -4879,19 +5692,30 @@ static gboolean trw_layer_show_selected_viewport_menu ( VikTrwLayer *vtl, GdkEve /* See if a waypoint is selected */ VikWaypoint *waypoint = (VikWaypoint*)vik_window_get_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) ); if ( waypoint && waypoint->visible ) { - if ( vik_window_get_selected_name ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) ) ) { + if ( waypoint->name ) { if ( vtl->wp_right_click_menu ) - gtk_object_sink ( GTK_OBJECT(vtl->wp_right_click_menu) ); + gtk_object_sink ( GTK_OBJECT(vtl->wp_right_click_menu) ); vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () ); - trw_layer_sublayer_add_menu_items ( vtl, - vtl->wp_right_click_menu, - NULL, - VIK_TRW_LAYER_SUBLAYER_WAYPOINT, - vik_window_get_selected_name ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) ), - g_hash_table_lookup ( vtl->waypoints_iters, vik_window_get_selected_name ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) ) ), - vvp); + + wpu_udata udata; + udata.wp = waypoint; + udata.uuid = NULL; + + 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 ); + + trw_layer_sublayer_add_menu_items ( vtl, + vtl->wp_right_click_menu, + NULL, + VIK_TRW_LAYER_SUBLAYER_WAYPOINT, + udata.uuid, + iter, + vvp ); + } gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() ); return TRUE; @@ -4993,7 +5817,7 @@ static gboolean tool_edit_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *eve params.vvp = vvp; params.x = event->x; params.y = event->y; - params.closest_wp_name = NULL; + params.closest_wp_id = NULL; /* TODO: should get track listitem so we can break it up, make a new track, mess it up, all that. */ params.closest_wp = NULL; g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, ¶ms); @@ -5011,10 +5835,10 @@ static gboolean tool_edit_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *eve else vtl->waypoint_rightclick = FALSE; - vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, params.closest_wp_name ), TRUE ); + vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, params.closest_wp_id ), TRUE ); vtl->current_wp = params.closest_wp; - vtl->current_wp_name = params.closest_wp_name; + vtl->current_wp_id = params.closest_wp_id; /* could make it so don't update if old WP is off screen and new is null but oh well */ vik_layer_emit_update ( VIK_LAYER(vtl), FALSE ); @@ -5022,7 +5846,7 @@ static gboolean tool_edit_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *eve } vtl->current_wp = NULL; - vtl->current_wp_name = NULL; + vtl->current_wp_id = NULL; vtl->waypoint_rightclick = FALSE; vik_layer_emit_update ( VIK_LAYER(vtl), FALSE ); return FALSE; @@ -5109,7 +5933,7 @@ static gboolean tool_edit_waypoint_release ( VikTrwLayer *vtl, GdkEventButton *e g_object_ref_sink ( G_OBJECT(vtl->wp_right_click_menu) ); if ( vtl->current_wp ) { vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () ); - trw_layer_sublayer_add_menu_items ( vtl, vtl->wp_right_click_menu, NULL, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, vtl->current_wp_name, g_hash_table_lookup ( vtl->waypoints_iters, vtl->current_wp_name ), vvp ); + trw_layer_sublayer_add_menu_items ( vtl, vtl->wp_right_click_menu, NULL, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, vtl->current_wp_id, g_hash_table_lookup ( vtl->waypoints_iters, vtl->current_wp_id ), vvp ); gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() ); } vtl->waypoint_rightclick = FALSE; @@ -5138,25 +5962,29 @@ static gpointer tool_new_track_create ( VikWindow *vw, VikViewport *vvp) typedef struct { VikTrwLayer *vtl; - VikViewport *vvp; - gint x1,y1,x2,y2,x3,y3; - const gchar* str; -} new_track_move_passalong_t; - -/* sync and undraw, but only when we have time */ -static gboolean ct_sync ( gpointer passalong ) -{ - new_track_move_passalong_t *p = (new_track_move_passalong_t *) passalong; + GdkDrawable *drawable; + GdkGC *gc; + GdkPixmap *pixmap; +} draw_sync_t; - vik_viewport_sync ( p->vvp ); - gdk_gc_set_function ( p->vtl->current_track_gc, GDK_INVERT ); - vik_viewport_draw_line ( p->vvp, p->vtl->current_track_gc, p->x1, p->y1, p->x2, p->y2 ); - vik_viewport_draw_string (p->vvp, gdk_font_from_description (pango_font_description_from_string ("Sans 8")), p->vtl->current_track_gc, p->x3, p->y3, p->str); - gdk_gc_set_function ( p->vtl->current_track_gc, GDK_COPY ); - - g_free ( (gpointer) p->str ) ; - p->vtl->ct_sync_done = TRUE; - g_free ( p ); +/* + * Draw specified pixmap + */ +static gboolean draw_sync ( gpointer data ) +{ + draw_sync_t *ds = (draw_sync_t*) data; + // Sometimes don't want to draw + // 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(); + } return FALSE; } @@ -5234,16 +6062,41 @@ static void update_statusbar ( VikTrwLayer *vtl ) static VikLayerToolFuncStatus tool_new_track_move ( VikTrwLayer *vtl, GdkEventMotion *event, VikViewport *vvp ) { /* if we haven't sync'ed yet, we don't have time to do more. */ - if ( vtl->ct_sync_done && vtl->current_track && vtl->current_track->trackpoints ) { - GList *iter = vtl->current_track->trackpoints; - new_track_move_passalong_t *passalong; + if ( vtl->draw_sync_done && vtl->current_track && vtl->current_track->trackpoints ) { + GList *iter = g_list_last ( vtl->current_track->trackpoints ); + + static GdkPixmap *pixmap = NULL; + int w1, h1, w2, h2; + // Need to check in case window has been resized + w1 = vik_viewport_get_width(vvp); + h1 = vik_viewport_get_height(vvp); + if (!pixmap) { + pixmap = gdk_pixmap_new ( GTK_WIDGET(vvp)->window, w1, h1, -1 ); + } + gdk_drawable_get_size (pixmap, &w2, &h2); + if (w1 != w2 || h1 != h2) { + g_object_unref ( G_OBJECT ( pixmap ) ); + pixmap = gdk_pixmap_new ( GTK_WIDGET(vvp)->window, w1, h1, -1 ); + } + + // Reset to background + gdk_draw_drawable (pixmap, + vtl->current_track_newpoint_gc, + vik_viewport_get_pixmap(vvp), + 0, 0, 0, 0, -1, -1); + + draw_sync_t *passalong; gint x1, y1; - while ( iter->next ) - iter = iter->next; - gdk_gc_set_function ( vtl->current_track_gc, GDK_INVERT ); vik_viewport_coord_to_screen ( vvp, &(VIK_TRACKPOINT(iter->data)->coord), &x1, &y1 ); - vik_viewport_draw_line ( vvp, vtl->current_track_gc, x1, y1, event->x, event->y ); + + // FOR SCREEN OVERLAYS WE MUST DRAW INTO THIS PIXMAP (when using the reset method) + // otherwise using vik_viewport_draw_* functions puts the data into the base pixmap, + // thus when we come to reset to the background it would include what we have already drawn!! + gdk_draw_line ( pixmap, + vtl->current_track_newpoint_gc, + x1, y1, event->x, event->y ); + // Using this reset method is more reliable than trying to undraw previous efforts via the GDK_INVERT method /* Find out actual distance of current track */ gdouble distance = vik_track_get_length (vtl->current_track); @@ -5279,28 +6132,38 @@ static VikLayerToolFuncStatus tool_new_track_move ( VikTrwLayer *vtl, GdkEventMo /* offset from cursor a bit */ xd = event->x + 10; yd = event->y - 10; - /* note attempted setting text using pango layouts - but the 'undraw' technique didn't work */ - vik_viewport_draw_string (vvp, gdk_font_from_description (pango_font_description_from_string ("Sans 8")), vtl->current_track_gc, xd, yd, str); - gdk_gc_set_function ( vtl->current_track_gc, GDK_COPY ); + PangoLayout *pl = gtk_widget_create_pango_layout (GTK_WIDGET(vvp), NULL); + PangoFontDescription *pfd = pango_font_description_from_string ("Sans 8"); // FIXME: settable option? global variable? + pango_layout_set_font_description (pl, pfd); + pango_font_description_free (pfd); - passalong = g_new(new_track_move_passalong_t,1); /* freed by sync */ + pango_layout_set_text (pl, str, -1); + gint wd, hd; + pango_layout_get_pixel_size ( pl, &wd, &hd ); + + // Create a background block to make the text easier to read over the background map + GdkGC *background_block_gc = vik_viewport_new_gc ( vvp, "#cccccc", 1); + gdk_draw_rectangle (pixmap, background_block_gc, TRUE, xd-2, yd-2, wd+4, hd+2); + gdk_draw_layout (pixmap, vtl->current_track_newpoint_gc, xd, yd, pl); + + g_object_unref ( G_OBJECT ( pl ) ); + g_object_unref ( G_OBJECT ( background_block_gc ) ); + + passalong = g_new(draw_sync_t,1); // freed by draw_sync() passalong->vtl = vtl; - passalong->vvp = vvp; - passalong->x1 = x1; - passalong->y1 = y1; - passalong->x2 = event->x; - passalong->y2 = event->y; - passalong->x3 = xd; - passalong->y3 = yd; - passalong->str = str; + passalong->pixmap = pixmap; + passalong->drawable = GTK_WIDGET(vvp)->window; + passalong->gc = vtl->current_track_newpoint_gc; // Update statusbar with full gain/loss information statusbar_write (str, elev_gain, elev_loss, vtl); - /* this will sync and undraw when we have time to */ - g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, ct_sync, passalong, NULL); - vtl->ct_sync_done = FALSE; + g_free ((gpointer)str); + + // draw pixmap when we have time to + g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, draw_sync, passalong, NULL); + vtl->draw_sync_done = FALSE; return VIK_LAYER_TOOL_ACK_GRAB_FOCUS; } return VIK_LAYER_TOOL_ACK; @@ -5336,6 +6199,13 @@ static gboolean tool_new_track_click ( VikTrwLayer *vtl, GdkEventButton *event, if (!vtl || vtl->vl.type != VIK_LAYER_TRW) return FALSE; + if ( event->button == 2 ) { + // As the display is panning, the new track pixmap is now invalid so don't draw it + // otherwise this drawing done results in flickering back to an old image + vtl->draw_sync_do = FALSE; + return FALSE; + } + if ( event->button == 3 && vtl->current_track ) { /* undo */ @@ -5368,7 +6238,7 @@ static gboolean tool_new_track_click ( VikTrwLayer *vtl, GdkEventButton *event, if ( ! vtl->current_track ) { - gchar *name = get_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, _("Track")); + 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), vtl->tracks, name ) ) ) { vtl->current_track = vik_track_new(); @@ -5408,6 +6278,15 @@ static gboolean tool_new_track_click ( VikTrwLayer *vtl, GdkEventButton *event, return TRUE; } +static void tool_new_track_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp ) +{ + if ( event->button == 2 ) { + // Pan moving ended - enable potential point drawing again + vtl->draw_sync_do = TRUE; + vtl->draw_sync_done = TRUE; + } +} + /*** New waypoint ****/ static gpointer tool_new_waypoint_create ( VikWindow *vw, VikViewport *vvp) @@ -5451,7 +6330,7 @@ static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *e params.vvp = vvp; params.x = event->x; params.y = event->y; - params.closest_track_name = NULL; + 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; @@ -5468,10 +6347,11 @@ 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_track_name)); - gint x, y; - g_assert ( current_tr ); + VikTrack *current_tr = VIK_TRACK(g_hash_table_lookup(vtl->tracks, vtl->current_tp_id)); + if ( !current_tr ) + return FALSE; + gint x, y; vik_viewport_coord_to_screen ( vvp, &(tp->coord), &x, &y ); if ( current_tr->visible && @@ -5481,17 +6361,16 @@ static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *e return TRUE; } - vtl->last_tpl = vtl->current_tpl; - vtl->last_tp_track_name = vtl->current_tp_track_name; } g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, ¶ms); if ( params.closest_tp ) { - vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, params.closest_track_name ), TRUE ); + vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, params.closest_track_id ), TRUE ); vtl->current_tpl = params.closest_tpl; - vtl->current_tp_track_name = params.closest_track_name; + vtl->current_tp_id = params.closest_track_id; + vtl->current_tp_track = g_hash_table_lookup ( vtl->tracks, params.closest_track_id ); trw_layer_tpwin_init ( vtl ); set_statusbar_msg_info_trkpt ( vtl, params.closest_tp ); vik_layer_emit_update ( VIK_LAYER(vtl), FALSE ); @@ -5562,9 +6441,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 ); - /* can't join with itself! */ - trw_layer_cancel_last_tp ( vtl ); + vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name ); vik_layer_emit_update ( VIK_LAYER(vtl), FALSE ); return TRUE; @@ -5573,6 +6450,7 @@ static gboolean tool_edit_trackpoint_release ( VikTrwLayer *vtl, GdkEventButton } +#ifdef VIK_CONFIG_GOOGLE_DIRECTIONS /*** Route Finder ***/ static gpointer tool_route_finder_create ( VikWindow *vw, VikViewport *vvp) { @@ -5629,23 +6507,14 @@ static gboolean tool_route_finder_click ( VikTrwLayer *vtl, GdkEventButton *even g_free ( url ); /* see if anything was done -- a track was added or appended to */ - if ( vtl->route_finder_check_added_track && vtl->route_finder_added_track_name ) { - VikTrack *tr; - - tr = g_hash_table_lookup ( vtl->tracks, vtl->route_finder_added_track_name ); - - if ( tr ) - vik_track_set_comment_no_copy ( tr, g_strdup_printf("from: %f,%f to: %f,%f", start.lat, start.lon, end.lat, end.lon ) ); - - vtl->route_finder_current_track = tr; - - g_free ( vtl->route_finder_added_track_name ); - vtl->route_finder_added_track_name = NULL; + 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 ); } + vtl->route_finder_added_track = NULL; vtl->route_finder_check_added_track = FALSE; vtl->route_finder_append = FALSE; @@ -5657,6 +6526,7 @@ static gboolean tool_route_finder_click ( VikTrwLayer *vtl, GdkEventButton *even } return TRUE; } +#endif /*** Show picture ****/ @@ -5666,7 +6536,7 @@ static gpointer tool_show_picture_create ( VikWindow *vw, VikViewport *vvp) } /* Params are: vvp, event, last match found or NULL */ -static void tool_show_picture_wp ( char *name, VikWaypoint *wp, gpointer params[2] ) +static void tool_show_picture_wp ( char *name, VikWaypoint *wp, gpointer params[3] ) { if ( wp->image && wp->visible ) { @@ -5690,7 +6560,7 @@ static void trw_layer_show_picture ( gpointer pass_along[6] ) { /* thanks to the Gaim people for showing me ShellExecute and g_spawn_command_line_async */ #ifdef WINDOWS - ShellExecute(NULL, NULL, (char *) pass_along[2], NULL, ".\\", 0); + ShellExecute(NULL, "open", (char *) pass_along[5], NULL, NULL, SW_SHOWNORMAL); #else /* WINDOWS */ GError *err = NULL; gchar *quoted_file = g_shell_quote ( (gchar *) pass_along[5] ); @@ -5803,9 +6673,22 @@ VikCoordMode vik_trw_layer_get_coord_mode ( VikTrwLayer *vtl ) return vtl->coord_mode; } +/** + * Uniquify the whole layer + * Also requires the layers panel as the names shown there need updating too + * Returns whether the operation was successful or not + */ +gboolean vik_trw_layer_uniquify ( VikTrwLayer *vtl, VikLayersPanel *vlp ) +{ + if ( vtl && vlp ) { + vik_trw_layer_uniquify_tracks ( vtl, vlp ); + vik_trw_layer_uniquify_waypoints ( vtl, vlp ); + return TRUE; + } + return FALSE; +} - -static void waypoint_convert ( const gchar *name, VikWaypoint *wp, VikCoordMode *dest_mode ) +static void waypoint_convert ( const gpointer id, VikWaypoint *wp, VikCoordMode *dest_mode ) { vik_coord_convert ( &(wp->coord), *dest_mode ); } @@ -5825,16 +6708,6 @@ static void trw_layer_change_coord_mode ( VikTrwLayer *vtl, VikCoordMode dest_mo } } -VikWaypoint *vik_trw_layer_get_waypoint ( VikTrwLayer *vtl, gchar *name ) -{ - return g_hash_table_lookup ( vtl->waypoints, name ); -} - -VikTrack *vik_trw_layer_get_track ( VikTrwLayer *vtl, const gchar *name ) -{ - return g_hash_table_lookup ( vtl->tracks, name ); -} - static void trw_layer_set_menu_selection ( VikTrwLayer *vtl, guint16 selection ) { vtl->menu_selection = selection; @@ -6106,13 +6979,13 @@ static void highest_wp_number_remove_wp(VikTrwLayer *vtl, const gchar *old_wp_na gint old_wp_num = highest_wp_number_name_to_number(old_wp_name); if ( vtl->highest_wp_number == old_wp_num ) { gchar buf[4]; - vtl->highest_wp_number --; + vtl->highest_wp_number--; g_snprintf(buf,4,"%03d", vtl->highest_wp_number ); /* search down until we find something that *does* exist */ - while ( vtl->highest_wp_number > 0 && ! g_hash_table_lookup ( vtl->waypoints, buf ) ) { - vtl->highest_wp_number --; + while ( vtl->highest_wp_number > 0 && ! vik_trw_layer_get_waypoint ( vtl, buf )) { + vtl->highest_wp_number--; g_snprintf(buf,4,"%03d", vtl->highest_wp_number ); } }