]> git.street.me.uk Git - andy/viking.git/blobdiff - src/viktrwlayer.c
Add basic warning message if an issue has been detected in loading a .vik file.
[andy/viking.git] / src / viktrwlayer.c
index 18fc1864e2691b636aa516d0a78dd82a7ba3afd7..60082ce5a07af410a56a01dab2374a4dfcfdc463 100644 (file)
@@ -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,8 +152,8 @@ 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;
 
@@ -161,10 +169,6 @@ struct _VikTrwLayer {
   gpointer current_tp_id;
   VikTrwLayerTpwin *tpwin;
 
-  /* weird hack for joining tracks */
-  GList *last_tpl;
-  VikTrack *last_tp_track;
-
   /* track editing tool -- more specifically, moving tps */
   gboolean moving_tp;
 
@@ -227,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 );
 
@@ -238,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] );
@@ -288,7 +297,6 @@ static void trw_layer_realize_track ( gpointer id, VikTrack *track, gpointer pas
 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 );
@@ -308,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 );
@@ -321,7 +331,6 @@ 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 gpointer id, VikWaypoint *wp, VikCoordMode *dest_mode );
 static void track_convert ( const gchar *name, VikTrack *tr, VikCoordMode *dest_mode );
 
@@ -330,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"), "<control><shift>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"), "<control><shift>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"), "<control><shift>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"), "<control><shift>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"), "<control><shift>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"), "<control><shift>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"), "<control><shift>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 };
 
@@ -365,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 */
@@ -400,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 },
@@ -418,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
@@ -460,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"),
+  "<control><shift>Y",
   &viktrwlayer_pixbuf,
 
   trw_layer_tools,
@@ -665,7 +704,7 @@ static gboolean trw_layer_paste_item ( VikTrwLayer *vtl, gint subtype, guint8 *i
 
     w = vik_waypoint_unmarshall(fi->data, fi->len);
     // When copying - we'll create a new name based on the original
-    name = get_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, w->name);
+    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 (NULL, w, &vtl->coord_mode);
 
@@ -681,7 +720,7 @@ static gboolean trw_layer_paste_item ( VikTrwLayer *vtl, gint subtype, guint8 *i
 
     t = vik_track_unmarshall(fi->data, fi->len);
     // When copying - we'll create a new name based on the original
-    name = get_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, t->name);
+    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);
 
@@ -729,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 )
@@ -804,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;
@@ -987,8 +964,7 @@ 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;
@@ -1000,7 +976,8 @@ static VikTrwLayer* trw_layer_new ( gint drawmode )
   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;
@@ -1008,8 +985,6 @@ static VikTrwLayer* trw_layer_new ( gint drawmode )
   rv->route_finder_append = FALSE;
 
   rv->waypoint_rightclick = FALSE;
-  rv->last_tpl = NULL;
-  rv->last_tp_track = NULL;
   rv->tpwin = NULL;
   rv->image_cache = g_queue_new();
   rv->image_size = 64;
@@ -1094,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 )
@@ -1162,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;
@@ -1175,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 )
@@ -1208,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);
@@ -1234,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
@@ -1259,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 );
 
@@ -1316,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.
@@ -1349,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;
 }
 
@@ -1543,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;
@@ -1569,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 */
@@ -1588,6 +1593,10 @@ 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 );
 }
 
@@ -2596,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] )
@@ -3011,11 +3020,9 @@ void trw_layer_cancel_tps_of_track ( VikTrwLayer *vtl, VikTrack *trk )
 {
   if (vtl->current_tp_track == trk )
     trw_layer_cancel_current_tp ( vtl, FALSE );
-  else if (vtl->last_tp_track == trk )
-    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);
@@ -3068,7 +3075,7 @@ static void trw_layer_move_item ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, g
   if (type == VIK_TRW_LAYER_SUBLAYER_TRACK) {
     VikTrack *trk = g_hash_table_lookup ( vtl_src->tracks, id );
 
-    gchar *newname = get_new_unique_sublayer_name(vtl_dest, type, trk->name);
+    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 );
@@ -3078,7 +3085,7 @@ static void trw_layer_move_item ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, g
   if (type == VIK_TRW_LAYER_SUBLAYER_WAYPOINT) {
     VikWaypoint *wp = g_hash_table_lookup ( vtl_src->waypoints, id );
 
-    gchar *newname = get_new_unique_sublayer_name(vtl_dest, type, wp->name);
+    gchar *newname = trw_layer_new_unique_sublayer_name(vtl_dest, type, wp->name);
 
     VikWaypoint *wp2 = vik_waypoint_copy ( wp );
     vik_trw_layer_add_waypoint ( vtl_dest, newname, wp2 );
@@ -3306,8 +3313,6 @@ void vik_trw_layer_delete_all_tracks ( VikTrwLayer *vtl )
   vtl->route_finder_added_track = NULL;
   if (vtl->current_tp_track)
     trw_layer_cancel_current_tp(vtl, FALSE);
-  if (vtl->last_tp_track)
-    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);
@@ -3471,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
  */
@@ -3489,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] )
 {
@@ -3603,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 */
@@ -3756,16 +3765,94 @@ 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];
 
   //time_t t1, t2;
-  GList *nearby_tracks;
-  GList *trps;
-  static  guint thr = 1;
-  guint track_count = 0;
 
   GList *tracks_with_timestamp = NULL;
   VikTrack *orig_trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
@@ -3788,26 +3875,29 @@ static void trw_layer_merge_by_timestamp ( gpointer pass_along[6] )
   }
   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)) {
+  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 */
-  //VikTrack *track;
-  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 ) {
+
+    // Don't try again unless tracks have changed
+    attempt_merge = FALSE;
 
-    // Need to refind original track incase we've deleted and recreated it??
-    //track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
     trps = orig_trk->trackpoints;
     if ( !trps )
       return;
 
-
     if (nearby_tracks) {
       g_list_free(nearby_tracks);
       nearby_tracks = NULL;
@@ -3818,62 +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_trk);
+    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, ((x)->data)))
-      GList *l = nearby_tracks;
-      //      VikTrack *tr = vik_track_new();
-      //tr->visible = track->visible;
-      track_count = 0;
-      while (l) {
-       /*
-#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)
-       time_t t1, t2;
-       t1 = get_first_trackpoint(l)->timestamp;
-       t2 = get_last_trackpoint(l)->timestamp;
+    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", (char *)l->data, (int)t1, (int)t2);
-       */
+        g_print("     %20s: track %d - %d\n", VIK_TRACK(l->data)->name, (int)t1, (int)t2);
+       */
 
+      /* 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));
 
-       /* remove trackpoints from merged track, delete track */
-       orig_trk->trackpoints = g_list_concat(orig_trk->trackpoints, get_track(l)->trackpoints);
-       get_track(l)->trackpoints = NULL;
-       vik_trw_layer_delete_track (vtl, l->data);
-
-       track_count++;
-       l = g_list_next(l);
-      }
-#undef get_track
-      orig_trk->trackpoints = g_list_sort(orig_trk->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;
 
+      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);
   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;
@@ -3915,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) {
@@ -3926,15 +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", track->name, 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);
     }
     // Remove original track and then update the display
-    vik_trw_layer_delete_track (VIK_TRW_LAYER(pass_along[0]), track);
+    vik_trw_layer_delete_track (vtl, track);
     vik_layer_emit_update(VIK_LAYER(pass_along[0]), FALSE);
   }
   g_list_free(newlists);
@@ -3992,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) {
@@ -4003,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", track->name, 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]), track);
+    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
  */
@@ -4068,6 +4274,139 @@ static void trw_layer_sorted_track_id_by_name_list (const gpointer id, const Vik
   *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 );
+}
+
 /**
  *
  */
@@ -4076,9 +4415,15 @@ static void trw_layer_delete_tracks_from_selection ( gpointer lav[2] )
   VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
   GList *all = NULL;
 
-  // TODO consider disabling this with warning message about
-  //  not working due to multiple same names
-  //  enable calling (yet to be defined) uniqify method?
+  // 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, (GHFunc) trw_layer_sorted_track_id_by_name_list, &all);
@@ -4101,10 +4446,7 @@ 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)) {
-      // TODO Hmmm conversion needed here -- not 1:1 relationship any more of name to reference
-      //   -- new functionality / or need to extend list to have uuid with it / or have uuids and ways to find out the name for display
-      //    may need to rework the general dialog functions
-      // ATM This deletes first trk it finds of that name
+      // 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);
@@ -4112,6 +4454,138 @@ static void trw_layer_delete_tracks_from_selection ( gpointer lav[2] )
   }
 }
 
+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 );
+}
+
 /**
  *
  */
@@ -4120,9 +4594,15 @@ static void trw_layer_delete_waypoints_from_selection ( gpointer lav[2] )
   VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
   GList *all = NULL;
 
-  // TODO consider disabling this with warning message about
-  //  not working due to multiple same names
-  //  enable calling (yet to be defined) uniqify method?
+  // 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, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &all);
@@ -4146,10 +4626,7 @@ 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)) {
-      // TODO Hmmm conversion needed here -- not 1:1 relationship any more of name to reference
-      //   -- new functionality / or need to extend list to have uuid with it / or have uuids and ways to find out the name for display
-      //    may need to rework the general dialog functions
-      // ARM This deletes first WP it finds of that name
+      // 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);
@@ -4462,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) );
@@ -4510,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") );
@@ -4569,11 +5103,13 @@ 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...") );
@@ -4690,15 +5226,6 @@ static void trw_layer_insert_tp_after_current_tp ( VikTrwLayer *vtl )
   }
 }
 
-/* to be called when last_tpl no longer 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 = NULL;
-}
-
 static void trw_layer_cancel_current_tp ( VikTrwLayer *vtl, gboolean destroy )
 {
   if ( vtl->tpwin )
@@ -4731,29 +5258,8 @@ 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 )
   {
@@ -4762,87 +5268,46 @@ static void trw_layer_tpwin_response ( VikTrwLayer *vtl, gint response )
       return;
 
     GList *new_tpl;
-    /* can't join with a non-existent trackpoint */
-    vtl->last_tpl = NULL;
-    vtl->last_tp_track = NULL;
 
+    // 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. */
+      // Set to current to the available adjacent trackpoint
+      vtl->current_tpl = new_tpl;
+
+      // 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 );
 
-      trw_layer_cancel_last_tp ( vtl );
-
-      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;
     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;
     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_JOIN )
-  {
-    // Check tracks exist and are different before joining
-    if ( ! vtl->last_tp_track || ! vtl->current_tp_track || vtl->last_tp_track == vtl->current_tp_track )
-      return;
-
-    VikTrack *tr1 = vtl->last_tp_track;
-    VikTrack *tr2 = vtl->current_tp_track;
-
-    VikTrack *tr_first = tr1, *tr_last = tr2;
-
-    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;
-
-    vtl->current_tp_track = vtl->last_tp_track; /* 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, vtl->current_tp_track );
-
-    trw_layer_cancel_last_tp ( vtl ); /* same TP, can't join. */
-    vik_layer_emit_update(VIK_LAYER(vtl), FALSE);
-  }
   else if ( response == VIK_TRW_LAYER_TPWIN_INSERT && vtl->current_tpl->next )
   {
     trw_layer_insert_tp_after_current_tp ( vtl );
@@ -5055,10 +5520,6 @@ static gboolean trw_layer_select_release ( VikTrwLayer *vtl, GdkEventButton *eve
        if ( vtl->tpwin )
           if ( vtl->current_tp_track )
             vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
-
-       // 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 );
       }
     }
 
@@ -5196,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() );
        
@@ -5220,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;
@@ -5479,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;
 }
 
@@ -5575,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);
@@ -5620,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;
@@ -5677,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 */
@@ -5709,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();
@@ -5749,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)
@@ -5823,8 +6361,6 @@ static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *e
       return TRUE;
     }
 
-    vtl->last_tpl = vtl->current_tpl;
-    vtl->last_tp_track = vtl->current_tp_track;
   }
 
   g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, &params);
@@ -5834,6 +6370,7 @@ static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *e
     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_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 );
@@ -5905,8 +6442,6 @@ 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_layer_emit_update ( VIK_LAYER(vtl), FALSE );
     return TRUE;
@@ -5915,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)
 {
@@ -5990,6 +6526,7 @@ static gboolean tool_route_finder_click ( VikTrwLayer *vtl, GdkEventButton *even
   }
   return TRUE;
 }
+#endif
 
 /*** Show picture ****/
 
@@ -6023,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[5], 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] );
@@ -6136,7 +6673,20 @@ 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 gpointer id, VikWaypoint *wp, VikCoordMode *dest_mode )
 {