]> git.street.me.uk Git - andy/viking.git/blobdiff - src/viktrwlayer.c
Make more text translatable
[andy/viking.git] / src / viktrwlayer.c
index f69e03e4bb7a7904d6d2e789cf4b9e98cbde801d..7a1aab78bd19a521df524b0396352081b054fc62 100644 (file)
@@ -5,7 +5,7 @@
  * Copyright (C) 2005-2008, Alex Foobarian <foobarian@gmail.com>
  * Copyright (C) 2007, Quy Tonthat <qtonthat@gmail.com>
  * Copyright (C) 2009, Hein Ragas <viking@ragas.nl>
- * Copyright (c) 2012, Rob Norris <rw_norris@hotmail.com>
+ * Copyright (c) 2012-2015, Rob Norris <rw_norris@hotmail.com>
  * Copyright (c) 2012-2013, Guilhem Bonnefille <guilhem.bonnefille@gmail.com>
  *
  * This program is free software; you can redistribute it and/or modify
@@ -59,6 +59,7 @@
 #include "acquire.h"
 #include "datasources.h"
 #include "datasource_gps.h"
+#include "vikexttools.h"
 #include "vikexttool_datasources.h"
 #include "ui_util.h"
 #include "vikutils.h"
@@ -179,7 +180,6 @@ struct _VikTrwLayer {
   GdkGC *waypoint_text_gc; GdkColor waypoint_text_color;
   GdkGC *waypoint_bg_gc; GdkColor waypoint_bg_color;
   gboolean wpbgand;
-  GdkFont *waypoint_font;
   VikTrack *current_track; // ATM shared between new tracks and new routes
   guint16 ct_x1, ct_y1, ct_x2, ct_y2;
   gboolean draw_sync_done;
@@ -369,6 +369,8 @@ static void trw_layer_cancel_current_tp ( VikTrwLayer *vtl, gboolean destroy );
 static void trw_layer_tpwin_response ( VikTrwLayer *vtl, gint response );
 static void trw_layer_tpwin_init ( VikTrwLayer *vtl );
 
+static void trw_layer_sort_all ( VikTrwLayer *vtl );
+
 static gpointer tool_edit_trackpoint_create ( VikWindow *vw, VikViewport *vvp);
 static void tool_edit_trackpoint_destroy ( tool_ed_t *t );
 static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
@@ -533,6 +535,8 @@ static gchar* params_sort_order[] = {
   N_("None"),
   N_("Name Ascending"),
   N_("Name Descending"),
+  N_("Date Ascending"),
+  N_("Date Descending"),
   NULL
 };
 
@@ -689,6 +693,7 @@ static void trw_layer_post_read ( VikTrwLayer *vtl, GtkWidget *vvp, gboolean fro
 static void trw_layer_free ( VikTrwLayer *trwlayer );
 static void trw_layer_draw ( VikTrwLayer *l, gpointer data );
 static void trw_layer_change_coord_mode ( VikTrwLayer *vtl, VikCoordMode dest_mode );
+static time_t trw_layer_get_timestamp ( VikTrwLayer *vtl );
 static void trw_layer_set_menu_selection ( VikTrwLayer *vtl, guint16 );
 static guint16 trw_layer_get_menu_selection ( VikTrwLayer *vtl );
 static void trw_layer_add_menu_items ( VikTrwLayer *vtl, GtkMenu *menu, gpointer vlp );
@@ -739,6 +744,7 @@ VikLayerInterface vik_trw_layer_interface = {
   (VikLayerFuncProperties)              NULL,
   (VikLayerFuncDraw)                    trw_layer_draw,
   (VikLayerFuncChangeCoordMode)         trw_layer_change_coord_mode,
+  (VikLayerFuncGetTimestamp)            trw_layer_get_timestamp,
 
   (VikLayerFuncSetMenuItemsSelection)   trw_layer_set_menu_selection,
   (VikLayerFuncGetMenuItemsSelection)   trw_layer_get_menu_selection,
@@ -820,15 +826,17 @@ static void vik_trwlayer_class_init ( VikTrwLayerClass *klass )
       else if ( mystderr )
         tokens = g_strsplit(mystderr, " ", 0);
 
-      gint num = 0;
-      gchar *token = tokens[num];
-      while ( token && num < 2 ) {
-        if (num == 1) {
-          if ( viking_version_to_number(token) >= viking_version_to_number("1.7.3") )
-            have_diary_program = TRUE;
+      if ( tokens ) {
+        gint num = 0;
+        gchar *token = tokens[num];
+        while ( token && num < 2 ) {
+          if (num == 1) {
+            if ( viking_version_to_number(token) >= viking_version_to_number("1.7.3") )
+              have_diary_program = TRUE;
+          }
+          num++;
+          token = tokens[num];
         }
-        num++;
-        token = tokens[num];
       }
       g_strfreev ( tokens );
     }
@@ -909,8 +917,8 @@ typedef struct {
   const gchar *date_str;
   const VikTrack *trk;
   const VikWaypoint *wpt;
-  gpointer *trk_id;
-  gpointer *wpt_id;
+  gpointer trk_id;
+  gpointer wpt_id;
 } date_finder_type;
 
 static gboolean trw_layer_find_date_track ( const gpointer id, const VikTrack *trk, date_finder_type *df )
@@ -1022,7 +1030,7 @@ static void trw_layer_copy_item_cb ( menu_array_sublayer values)
 {
   VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
   gint subtype = GPOINTER_TO_INT (values[MA_SUBTYPE]);
-  gpointer sublayer = values[MA_SUBLAYER_ID];
+  gpointer sublayer = values[MA_SUBLAYER_ID];
   guint8 *data = NULL;
   guint len;
 
@@ -1165,6 +1173,12 @@ static void trw_layer_free_copied_item ( gint subtype, gpointer item )
   }
 }
 
+static void image_cache_free ( VikTrwLayer *vtl )
+{
+  g_list_foreach ( vtl->image_cache->head, (GFunc)cached_pixbuf_free, NULL );
+  g_queue_free ( vtl->image_cache );
+}
+
 static gboolean trw_layer_set_param ( VikTrwLayer *vtl, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation )
 {
   switch ( id )
@@ -1236,12 +1250,17 @@ static gboolean trw_layer_set_param ( VikTrwLayer *vtl, guint16 id, VikLayerPara
     case PARAM_IS: if ( data.u != vtl->image_size )
       {
         vtl->image_size = data.u;
-        g_list_foreach ( vtl->image_cache->head, (GFunc) cached_pixbuf_free, NULL );
-        g_queue_free ( vtl->image_cache );
+        image_cache_free ( vtl );
+        vtl->image_cache = g_queue_new ();
+      }
+      break;
+    case PARAM_IA: if ( data.u != vtl->image_alpha )
+      {
+        vtl->image_alpha = data.u;
+        image_cache_free ( vtl );
         vtl->image_cache = g_queue_new ();
       }
       break;
-    case PARAM_IA: vtl->image_alpha = data.u; break;
     case PARAM_ICS: vtl->image_cache_size = data.u;
       while ( vtl->image_cache->length > vtl->image_cache_size ) /* if shrinking cache_size, free pixbuf ASAP */
           cached_pixbuf_free ( g_queue_pop_tail ( vtl->image_cache ) );
@@ -1518,23 +1537,27 @@ static VikTrwLayer *trw_layer_unmarshall( guint8 *data, gint len, VikViewport *v
       // Reuse pl to read the subtype from the data stream
       memcpy(&pl, data+sizeof(gint), sizeof(pl));
 
+      // Also remember to (attempt to) convert each coordinate in case this is pasted into a different drawmode
       if ( pl == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
         VikTrack *trk = vik_track_unmarshall ( data + sizeof_len_and_subtype, 0 );
         gchar *name = g_strdup ( trk->name );
         vik_trw_layer_add_track ( vtl, name, trk );
         g_free ( name );
+        vik_track_convert (trk, vtl->coord_mode);
       }
       if ( pl == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
         VikWaypoint *wp = vik_waypoint_unmarshall ( data + sizeof_len_and_subtype, 0 );
         gchar *name = g_strdup ( wp->name );
         vik_trw_layer_add_waypoint ( vtl, name, wp );
         g_free ( name );
+        waypoint_convert (NULL, wp, &vtl->coord_mode);
       }
       if ( pl == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
         VikTrack *trk = vik_track_unmarshall ( data + sizeof_len_and_subtype, 0 );
         gchar *name = g_strdup ( trk->name );
         vik_trw_layer_add_route ( vtl, name, trk );
         g_free ( name );
+        vik_track_convert (trk, vtl->coord_mode);
       }
     }
     consumed_length += tlm_size + sizeof_len_and_subtype;
@@ -1610,6 +1633,7 @@ static VikTrwLayer* trw_layer_new1 ( VikViewport *vvp )
   rv->metadata = vik_trw_metadata_new ();
   rv->draw_sync_done = TRUE;
   rv->draw_sync_do = TRUE;
+  rv->coord_mode = VIK_COORD_LATLON;
   // Everything else is 0, FALSE or NULL
 
   return rv;
@@ -1658,8 +1682,7 @@ static void trw_layer_free ( VikTrwLayer *trwlayer )
   if ( trwlayer->tracks_analysis_dialog != NULL )
     gtk_widget_destroy ( GTK_WIDGET(trwlayer->tracks_analysis_dialog) );
 
-  g_list_foreach ( trwlayer->image_cache->head, (GFunc) cached_pixbuf_free, NULL );
-  g_queue_free ( trwlayer->image_cache );
+  image_cache_free ( trwlayer );
 }
 
 static void init_drawing_params ( struct DrawingParams *dp, VikTrwLayer *vtl, VikViewport *vp, gboolean highlight )
@@ -1854,6 +1877,9 @@ static void trw_layer_draw_dist_labels ( struct DrawingParams *dp, VikTrack *trk
       case VIK_UNITS_DISTANCE_MILES:
         units = g_strdup ( _("miles") );
         break;
+      case VIK_UNITS_DISTANCE_NAUTICAL_MILES:
+        units = g_strdup ( _("NM") );
+        break;
         // VIK_UNITS_DISTANCE_KILOMETRES:
       default:
         units = g_strdup ( _("km") );
@@ -2004,6 +2030,40 @@ static void trw_layer_draw_track_name_labels ( struct DrawingParams *dp, VikTrac
   g_free ( ename );
 }
 
+
+/**
+ * trw_layer_draw_point_names:
+ *
+ * Draw a point labels along a track
+ * This might slow things down if there's many tracks being displayed with this on.
+ */
+static void trw_layer_draw_point_names ( struct DrawingParams *dp, VikTrack *trk, gboolean drawing_highlight )
+{
+  GList *list = trk->trackpoints;
+  if (!list) return;
+  VikTrackpoint *tp = VIK_TRACKPOINT(list->data);
+  gchar *fgcolour;
+  if ( dp->vtl->drawmode == DRAWMODE_BY_TRACK )
+    fgcolour = gdk_color_to_string ( &(trk->color) );
+  else
+    fgcolour = gdk_color_to_string ( &(dp->vtl->track_color) );
+  gchar *bgcolour;
+  if ( drawing_highlight )
+    bgcolour = g_strdup ( vik_viewport_get_highlight_color ( dp->vp ) );
+  else
+    bgcolour = gdk_color_to_string ( &(dp->vtl->track_bg_color) );
+  if ( tp->name )
+    trw_layer_draw_track_label ( tp->name, fgcolour, bgcolour, dp, &tp->coord );
+  while ((list = g_list_next(list)))
+  {
+    tp = VIK_TRACKPOINT(list->data);
+    if ( tp->name )
+      trw_layer_draw_track_label ( tp->name, fgcolour, bgcolour, dp, &tp->coord );
+  };
+  g_free ( fgcolour );
+  g_free ( bgcolour );
+}
+
 static void trw_layer_draw_track ( const gpointer id, VikTrack *track, struct DrawingParams *dp, gboolean draw_track_outline )
 {
   if ( ! track->visible )
@@ -2104,6 +2164,16 @@ static void trw_layer_draw_track ( const gpointer id, VikTrack *track, struct Dr
       tp = VIK_TRACKPOINT(list->data);
       tp_size = (list == dp->vtl->current_tpl) ? tp_size_cur : tp_size_reg;
 
+      VikTrackpoint *tp2 = VIK_TRACKPOINT(list->prev->data);
+      // See if in a different lat/lon 'quadrant' so don't draw massively long lines (presumably wrong way around the Earth)
+      //  Mainly to prevent wrong lines drawn when a track crosses the 180 degrees East-West longitude boundary
+      //  (since vik_viewport_draw_line() only copes with pixel value and has no concept of the globe)
+      if ( dp->lat_lon &&
+           (( tp2->coord.east_west < -90.0 && tp->coord.east_west > 90.0 ) ||
+            ( tp2->coord.east_west > 90.0 && tp->coord.east_west < -90.0 )) ) {
+        useoldvals = FALSE;
+        continue;
+      }
       /* check some stuff -- but only if we're in UTM and there's only ONE ZONE; or lat lon */
       if ( (!dp->one_zone && !dp->lat_lon) ||     /* UTM & zones; do everything */
              ( ((!dp->one_zone) || tp->coord.utm_zone == dp->center->utm_zone) &&   /* only check zones if UTM & one_zone */
@@ -2120,12 +2190,11 @@ static void trw_layer_draw_track ( const gpointer id, VikTrack *track, struct Dr
          // Still need to process points to ensure 'stops' are drawn if required
          if ( drawstops && drawpoints && ! draw_track_outline && list->next &&
               (VIK_TRACKPOINT(list->next->data)->timestamp - VIK_TRACKPOINT(list->data)->timestamp > dp->vtl->stop_length) )
-           vik_viewport_draw_arc ( dp->vp, g_array_index(dp->vtl->track_gc, GdkGC *, 11), TRUE, x-(3*tp_size), y-(3*tp_size), 6*tp_size, 6*tp_size, 0, 360*64 );
+           vik_viewport_draw_arc ( dp->vp, g_array_index(dp->vtl->track_gc, GdkGC *, VIK_TRW_LAYER_TRACK_GC_STOP), TRUE, x-(3*tp_size), y-(3*tp_size), 6*tp_size, 6*tp_size, 0, 360*64 );
 
          goto skip;
        }
 
-        VikTrackpoint *tp2 = VIK_TRACKPOINT(list->prev->data);
         if ( drawpoints || dp->vtl->drawlines ) {
           // setup main_gc for both point and line drawing
           if ( !drawing_highlight && (dp->vtl->drawmode == DRAWMODE_BY_SPEED) ) {
@@ -2223,7 +2292,6 @@ static void trw_layer_draw_track ( const gpointer id, VikTrack *track, struct Dr
       else {
         if (useoldvals && dp->vtl->drawlines && (!tp->newsegment))
         {
-          VikTrackpoint *tp2 = VIK_TRACKPOINT(list->prev->data);
           if ( dp->vtl->coord_mode != VIK_COORD_UTM || tp->coord.utm_zone == dp->center->utm_zone )
           {
             vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
@@ -2264,6 +2332,7 @@ static void trw_layer_draw_track ( const gpointer id, VikTrack *track, struct Dr
       if ( track->max_number_dist_labels > 0 ) {
         trw_layer_draw_dist_labels ( dp, track, drawing_highlight );
       }
+      trw_layer_draw_point_names (dp, track, drawing_highlight );
 
       if ( track->draw_name_mode != TRACK_DRAWNAME_NO ) {
         trw_layer_draw_track_name_labels ( dp, track, drawing_highlight );
@@ -2336,6 +2405,10 @@ static void trw_layer_draw_waypoint ( const gpointer id, VikWaypoint *wp, struct
           }
           cp->image = g_strdup ( image );
 
+          // Apply alpha setting to the image before the pixbuf gets stored in the cache
+          if ( dp->vtl->image_alpha != 255 )
+            cp->pixbuf = ui_pixbuf_set_alpha ( cp->pixbuf, dp->vtl->image_alpha );
+
           /* needed so 'click picture' tool knows how big the pic is; we don't
            * store it in cp because they may have been freed already. */
           wp->image_width = gdk_pixbuf_get_width ( cp->pixbuf );
@@ -2369,10 +2442,7 @@ static void trw_layer_draw_waypoint ( const gpointer id, VikWaypoint *wp, struct
                                          x - (w/2) - 2, y - (h/2) - 2, w + 4, h + 4 );
           }
 
-          if ( dp->vtl->image_alpha == 255 )
-            vik_viewport_draw_pixbuf ( dp->vp, pixbuf, 0, 0, x - (w/2), y - (h/2), w, h );
-          else
-            vik_viewport_draw_pixbuf_with_alpha ( dp->vp, pixbuf, dp->vtl->image_alpha, 0, 0, x - (w/2), y - (h/2), w, h );
+          vik_viewport_draw_pixbuf ( dp->vp, pixbuf, 0, 0, x - (w/2), y - (h/2), w, h );
         }
         return; /* if failed to draw picture, default to drawing regular waypoint (below) */
       }
@@ -2668,7 +2738,12 @@ static void trw_layer_realize_track ( gpointer id, VikTrack *track, gpointer pas
     gdk_pixbuf_fill ( pixbuf, pixel );
   }
 
-  vik_treeview_add_sublayer ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[0], (GtkTreeIter *) pass_along[1], track->name, pass_along[2], id, GPOINTER_TO_INT (pass_along[4]), pixbuf, TRUE, TRUE );
+  time_t timestamp = 0;
+  VikTrackpoint *tpt = vik_track_get_tp_first(track);
+  if ( tpt && tpt->has_timestamp )
+    timestamp = tpt->timestamp;
+
+  vik_treeview_add_sublayer ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[0], (GtkTreeIter *) pass_along[1], track->name, pass_along[2], id, GPOINTER_TO_INT (pass_along[4]), pixbuf, TRUE, timestamp );
 
   if ( pixbuf )
     g_object_unref (pixbuf);
@@ -2687,7 +2762,11 @@ static void trw_layer_realize_waypoint ( gpointer id, VikWaypoint *wp, gpointer
 {
   GtkTreeIter *new_iter = g_malloc(sizeof(GtkTreeIter));
 
-  vik_treeview_add_sublayer ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[0], (GtkTreeIter *) pass_along[1], wp->name, pass_along[2], id, GPOINTER_TO_UINT (pass_along[4]), get_wp_sym_small (wp->symbol), TRUE, TRUE );
+  time_t timestamp = 0;
+  if ( wp->has_timestamp )
+    timestamp = wp->timestamp;
+
+  vik_treeview_add_sublayer ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[0], (GtkTreeIter *) pass_along[1], wp->name, pass_along[2], id, GPOINTER_TO_UINT (pass_along[4]), get_wp_sym_small (wp->symbol), TRUE, timestamp );
 
   *new_iter = *((GtkTreeIter *) pass_along[1]);
   g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->waypoints_iters, id, new_iter );
@@ -2698,17 +2777,17 @@ static void trw_layer_realize_waypoint ( gpointer id, VikWaypoint *wp, gpointer
 
 static void trw_layer_add_sublayer_tracks ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
 {
-  vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->tracks_iter), _("Tracks"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_TRACKS, NULL, TRUE, FALSE );
+  vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->tracks_iter), _("Tracks"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_TRACKS, NULL, FALSE, 0 );
 }
 
 static void trw_layer_add_sublayer_waypoints ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
 {
-  vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->waypoints_iter), _("Waypoints"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_WAYPOINTS, NULL, TRUE, FALSE );
+  vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->waypoints_iter), _("Waypoints"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_WAYPOINTS, NULL, FALSE, 0 );
 }
 
 static void trw_layer_add_sublayer_routes ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
 {
-  vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->routes_iter), _("Routes"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_ROUTES, NULL, TRUE, FALSE );
+  vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->routes_iter), _("Routes"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_ROUTES, NULL, FALSE, 0 );
 }
 
 static void trw_layer_realize ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
@@ -2746,6 +2825,9 @@ static void trw_layer_realize ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *
     vik_treeview_item_set_visible ( (VikTreeview *) vt, &(vtl->waypoints_iter), vtl->waypoints_visible );
   }
 
+  trw_layer_verify_thumbnails ( vtl, NULL );
+
+  trw_layer_sort_all ( vtl );
 }
 
 static gboolean trw_layer_sublayer_toggle_visible ( VikTrwLayer *l, gint subtype, gpointer sublayer )
@@ -2792,6 +2874,14 @@ gint vik_trw_layer_get_property_tracks_line_thickness ( VikTrwLayer *vtl )
   return vtl->line_thickness;
 }
 
+/*
+ * Build up multiple routes information
+ */
+static void trw_layer_routes_tooltip ( const gpointer id, VikTrack *tr, gdouble *length )
+{
+  *length = *length + vik_track_get_length (tr);
+}
+
 // Structure to hold multiple track information for a layer
 typedef struct {
   gdouble length;
@@ -2803,7 +2893,7 @@ typedef struct {
 /*
  * Build up layer multiple track information via updating the tooltip_tracks structure
  */
-static void trw_layer_tracks_tooltip ( const gchar *name, VikTrack *tr, tooltip_tracks *tt )
+static void trw_layer_tracks_tooltip ( const gpointer id, VikTrack *tr, tooltip_tracks *tt )
 {
   tt->length = tt->length + vik_track_get_length (tr);
 
@@ -2845,7 +2935,7 @@ static void trw_layer_tracks_tooltip ( const gchar *name, VikTrack *tr, tooltip_
  */
 static const gchar* trw_layer_layer_tooltip ( VikTrwLayer *vtl )
 {
-  gchar tbuf1[32];
+  gchar tbuf1[64];
   gchar tbuf2[64];
   gchar tbuf3[64];
   gchar tbuf4[10];
@@ -2905,23 +2995,45 @@ static const gchar* trw_layer_layer_tooltip ( VikTrwLayer *vtl )
       // Timing information if available
       tbuf1[0] = '\0';
       if ( tt.duration > 0 ) {
-       g_snprintf (tbuf1, sizeof(tbuf1),
-                   _(" in %d:%02d hrs:mins"),
-                   (int)round(tt.duration/3600), (int)round((tt.duration/60)%60));
+        g_snprintf (tbuf1, sizeof(tbuf1),
+                    _(" in %d:%02d hrs:mins"),
+                    (int)(tt.duration/3600), (int)round(tt.duration/60.0)%60);
       }
       g_snprintf (tbuf2, sizeof(tbuf2),
                  _("\n%sTotal Length %.1f %s%s"),
                  tbuf3, len_in_units, tbuf4, tbuf1);
     }
 
+    tbuf1[0] = '\0';
+    gdouble rlength = 0.0;
+    g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_routes_tooltip, &rlength );
+    if ( rlength > 0.0 ) {
+      gdouble len_in_units;
+      // Setup info dependent on distance units
+      switch ( a_vik_get_units_distance() ) {
+      case VIK_UNITS_DISTANCE_MILES:
+        g_snprintf (tbuf4, sizeof(tbuf4), "miles");
+        len_in_units = VIK_METERS_TO_MILES(rlength);
+        break;
+      case VIK_UNITS_DISTANCE_NAUTICAL_MILES:
+        g_snprintf (tbuf4, sizeof(tbuf4), "NM");
+        len_in_units = VIK_METERS_TO_NAUTICAL_MILES(rlength);
+        break;
+      default:
+        g_snprintf (tbuf4, sizeof(tbuf4), "kms");
+        len_in_units = rlength/1000.0;
+        break;
+      }
+      g_snprintf (tbuf1, sizeof(tbuf1), _("\nTotal route length %.1f %s"), len_in_units, tbuf4);
+    }
+
     // Put together all the elements to form compact tooltip text
     g_snprintf (tmp_buf, sizeof(tmp_buf),
-               _("Tracks: %d - Waypoints: %d - Routes: %d%s"),
-               g_hash_table_size (vtl->tracks), g_hash_table_size (vtl->waypoints), g_hash_table_size (vtl->routes), tbuf2);
+                _("Tracks: %d - Waypoints: %d - Routes: %d%s%s"),
+                g_hash_table_size (vtl->tracks), g_hash_table_size (vtl->waypoints), g_hash_table_size (vtl->routes), tbuf2, tbuf1);
 
     g_date_free (gdate_start);
     g_date_free (gdate_end);
-
   }
 
   return tmp_buf;
@@ -2974,9 +3086,9 @@ static const gchar* trw_layer_sublayer_tooltip ( VikTrwLayer *l, gint subtype, g
        if ( tr->trackpoints && vik_track_get_tp_first(tr)->has_timestamp ) {
          // %x     The preferred date representation for the current locale without the time.
          strftime (time_buf1, sizeof(time_buf1), "%x: ", gmtime(&(vik_track_get_tp_first(tr)->timestamp)));
-         time_t dur = vik_track_get_duration ( tr );
+         time_t dur = vik_track_get_duration ( tr, TRUE );
          if ( dur > 0 )
-           g_snprintf ( time_buf2, sizeof(time_buf2), _("- %d:%02d hrs:mins"), (int)round(dur/3600), (int)round((dur/60)%60) );
+           g_snprintf ( time_buf2, sizeof(time_buf2), _("- %d:%02d hrs:mins"), (int)(dur/3600), (int)round(dur/60.0)%60 );
        }
        // Get length and consider the appropriate distance units
        gdouble tr_len = vik_track_get_length(tr);
@@ -3211,7 +3323,7 @@ GHashTable *vik_trw_layer_get_routes_iters ( VikTrwLayer *vtl )
 
 GHashTable *vik_trw_layer_get_waypoints_iters ( VikTrwLayer *vtl )
 {
-  return vtl->waypoints;
+  return vtl->waypoints_iters;
 }
 
 gboolean vik_trw_layer_is_empty ( VikTrwLayer *vtl )
@@ -3338,51 +3450,7 @@ static void trw_layer_centerize ( menu_array_layer values )
 
 void trw_layer_zoom_to_show_latlons ( VikTrwLayer *vtl, VikViewport *vvp, struct LatLon maxmin[2] )
 {
-  /* First set the center [in case previously viewing from elsewhere] */
-  /* Then loop through zoom levels until provided positions are in view */
-  /* This method is not particularly fast - but should work well enough */
-  struct LatLon average = { (maxmin[0].lat+maxmin[1].lat)/2, (maxmin[0].lon+maxmin[1].lon)/2 };
-  VikCoord coord;
-  vik_coord_load_from_latlon ( &coord, vtl->coord_mode, &average );
-  vik_viewport_set_center_coord ( vvp, &coord, TRUE );
-
-  /* Convert into definite 'smallest' and 'largest' positions */
-  struct LatLon minmin;
-  if ( maxmin[0].lat < maxmin[1].lat )
-    minmin.lat = maxmin[0].lat;
-  else
-    minmin.lat = maxmin[1].lat;
-
-  struct LatLon maxmax;
-  if ( maxmin[0].lon > maxmin[1].lon )
-    maxmax.lon = maxmin[0].lon;
-  else
-    maxmax.lon = maxmin[1].lon;
-
-  /* Never zoom in too far - generally not that useful, as too close ! */
-  /* Always recalculate the 'best' zoom level */
-  gdouble zoom = 1.0;
-  vik_viewport_set_zoom ( vvp, zoom );
-
-  gdouble min_lat, max_lat, min_lon, max_lon;
-  /* Should only be a maximum of about 18 iterations from min to max zoom levels */
-  while ( zoom <= VIK_VIEWPORT_MAX_ZOOM ) {
-    vik_viewport_get_min_max_lat_lon ( vvp, &min_lat, &max_lat, &min_lon, &max_lon );
-    /* NB I think the logic used in this test to determine if the bounds is within view
-       fails if track goes across 180 degrees longitude.
-       Hopefully that situation is not too common...
-       Mind you viking doesn't really do edge locations to well anyway */
-    if ( min_lat < minmin.lat &&
-        max_lat > minmin.lat &&
-        min_lon < maxmax.lon &&
-        max_lon > maxmax.lon )
-      /* Found within zoom level */
-      break;
-
-    /* Try next */
-    zoom = zoom * 2;
-    vik_viewport_set_zoom ( vvp, zoom );
-  }
+  vu_zoom_to_show_latlons ( vtl->coord_mode, vvp, maxmin );
 }
 
 gboolean vik_trw_layer_auto_set_view ( VikTrwLayer *vtl, VikViewport *vvp )
@@ -3519,13 +3587,13 @@ static void trw_layer_goto_wp ( menu_array_layer values )
 
   GtkWidget *label, *entry;
   label = gtk_label_new(_("Waypoint Name:"));
-  entry = gtk_entry_new();
+  entry = ui_entry_new ( NULL, GTK_ENTRY_ICON_SECONDARY );
 
   gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dia))), label, FALSE, FALSE, 0);
   gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dia))), entry, FALSE, FALSE, 0);
-  gtk_widget_show_all ( label );
-  gtk_widget_show_all ( entry );
-
+  gtk_widget_show_all ( dia );
+  // 'ok' when press return in the entry
+  g_signal_connect_swapped ( entry, "activate", G_CALLBACK(a_dialog_response_accept), dia );
   gtk_dialog_set_default_response ( GTK_DIALOG(dia), GTK_RESPONSE_ACCEPT );
 
   while ( gtk_dialog_run ( GTK_DIALOG(dia) ) == GTK_RESPONSE_ACCEPT )
@@ -3547,7 +3615,7 @@ static void trw_layer_goto_wp ( menu_array_layer values )
       udata.uuid = NULL;
 
       // Hmmm, want key of it
-      gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
+      gpointer wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
 
       if ( wpf && udata.uuid ) {
         GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udata.uuid );
@@ -3877,9 +3945,10 @@ static void trw_layer_new_wp ( menu_array_layer values )
   VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
   /* TODO longone: okay, if layer above (aggregate) is invisible but vtl->visible is true, this redraws for no reason.
      instead return true if you want to update. */
-  if ( vik_trw_layer_new_waypoint ( vtl, VIK_GTK_WINDOW_FROM_LAYER(vtl), vik_viewport_get_center(vik_layers_panel_get_viewport(vlp))) && VIK_LAYER(vtl)->visible ) {
+  if ( vik_trw_layer_new_waypoint ( vtl, VIK_GTK_WINDOW_FROM_LAYER(vtl), vik_viewport_get_center(vik_layers_panel_get_viewport(vlp))) ) {
     trw_layer_calculate_bounds_waypoints ( vtl );
-    vik_layers_panel_emit_update ( vlp );
+    if ( VIK_LAYER(vtl)->visible )
+      vik_layers_panel_emit_update ( vlp );
   }
 }
 
@@ -4012,6 +4081,17 @@ void trw_layer_osm_traces_upload_track_cb ( menu_array_sublayer values )
   }
 }
 
+static GtkWidget* create_external_submenu ( GtkMenu *menu )
+{
+  GtkWidget *external_submenu = gtk_menu_new ();
+  GtkWidget *item = gtk_image_menu_item_new_with_mnemonic ( _("Externa_l") );
+  gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_EXECUTE, GTK_ICON_SIZE_MENU) );
+  gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
+  gtk_widget_show ( item );
+  gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), external_submenu );
+  return external_submenu;
+}
+
 static void trw_layer_add_menu_items ( VikTrwLayer *vtl, GtkMenu *menu, gpointer vlp )
 {
   static menu_array_layer pass_along;
@@ -4101,10 +4181,12 @@ static void trw_layer_add_menu_items ( VikTrwLayer *vtl, GtkMenu *menu, gpointer
   gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
   gtk_widget_show ( item );
 
-  item = gtk_menu_item_new_with_mnemonic ( _("Export as _KML...") );
-  g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_kml), pass_along );
-  gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
-  gtk_widget_show ( item );
+  if ( a_babel_available () ) {
+    item = gtk_menu_item_new_with_mnemonic ( _("Export as _KML...") );
+    g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_kml), pass_along );
+    gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
+    gtk_widget_show ( item );
+  }
 
   if ( have_geojson_export ) {
     item = gtk_menu_item_new_with_mnemonic ( _("Export as GEO_JSON...") );
@@ -4113,10 +4195,12 @@ static void trw_layer_add_menu_items ( VikTrwLayer *vtl, GtkMenu *menu, gpointer
     gtk_widget_show ( item );
   }
 
-  item = gtk_menu_item_new_with_mnemonic ( _("Export via GPSbabel...") );
-  g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_babel), pass_along );
-  gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
-  gtk_widget_show ( item );
+  if ( a_babel_available () ) {
+    item = gtk_menu_item_new_with_mnemonic ( _("Export via GPSbabel...") );
+    g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_babel), pass_along );
+    gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
+    gtk_widget_show ( item );
+  }
 
   gchar* external1 = g_strdup_printf ( _("Open with External Program_1: %s"), a_vik_get_external_gpx_program_1() );
   item = gtk_menu_item_new_with_mnemonic ( external1 );
@@ -4238,11 +4322,13 @@ static void trw_layer_add_menu_items ( VikTrwLayer *vtl, GtkMenu *menu, gpointer
   gtk_widget_show ( item );
 #endif
 
-  item = gtk_menu_item_new_with_mnemonic ( _("From _File...") );
-  g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_file_cb), pass_along );
-  gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
-  gtk_widget_set_tooltip_text (item, _("Import File With GPS_Babel..."));
-  gtk_widget_show ( item );
+  if ( a_babel_available () ) {
+    item = gtk_menu_item_new_with_mnemonic ( _("From _File...") );
+    g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_file_cb), pass_along );
+    gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
+    gtk_widget_set_tooltip_text (item, _("Import File With GPS_Babel..."));
+    gtk_widget_show ( item );
+  }
 
   vik_ext_tool_datasources_add_menu_items_to_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), GTK_MENU (acquire_submenu) );
 
@@ -4337,6 +4423,10 @@ static void trw_layer_add_menu_items ( VikTrwLayer *vtl, GtkMenu *menu, gpointer
   gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
   gtk_widget_show ( item );
   gtk_widget_set_sensitive ( item, (gboolean)(g_hash_table_size (vtl->waypoints)) );
+
+  GtkWidget *external_submenu = create_external_submenu ( menu );
+  // TODO: Should use selected layer's centre - rather than implicitly using the current viewport
+  vik_ext_tools_add_menu_items_to_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), GTK_MENU (external_submenu), NULL );
 }
 
 // Fake Waypoint UUIDs vi simple increasing integer
@@ -4357,8 +4447,12 @@ void vik_trw_layer_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp
 
     GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
 
+    time_t timestamp = 0;
+    if ( wp->has_timestamp )
+      timestamp = wp->timestamp;
+
     // Visibility column always needed for waypoints
-    vik_treeview_add_sublayer ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), iter, name, vtl, GUINT_TO_POINTER(wp_uuid), VIK_TRW_LAYER_SUBLAYER_WAYPOINT, get_wp_sym_small (wp->symbol), TRUE, TRUE );
+    vik_treeview_add_sublayer ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), iter, name, vtl, GUINT_TO_POINTER(wp_uuid), VIK_TRW_LAYER_SUBLAYER_WAYPOINT, get_wp_sym_small (wp->symbol), TRUE, timestamp );
 
     // Actual setting of visibility dependent on the waypoint
     vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, wp->visible );
@@ -4391,8 +4485,14 @@ void vik_trw_layer_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *t )
     }
 
     GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
+
+    time_t timestamp = 0;
+    VikTrackpoint *tpt = vik_track_get_tp_first(t);
+    if ( tpt && tpt->has_timestamp )
+      timestamp = tpt->timestamp;
+
     // Visibility column always needed for tracks
-    vik_treeview_add_sublayer ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), iter, name, vtl, GUINT_TO_POINTER(tr_uuid), VIK_TRW_LAYER_SUBLAYER_TRACK, NULL, TRUE, TRUE );
+    vik_treeview_add_sublayer ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), iter, name, vtl, GUINT_TO_POINTER(tr_uuid), VIK_TRW_LAYER_SUBLAYER_TRACK, NULL, TRUE, timestamp );
 
     // Actual setting of visibility dependent on the track
     vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, t->visible );
@@ -4426,7 +4526,7 @@ void vik_trw_layer_add_route ( VikTrwLayer *vtl, gchar *name, VikTrack *t )
 
     GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
     // Visibility column always needed for routes
-    vik_treeview_add_sublayer ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter), iter, name, vtl, GUINT_TO_POINTER(rt_uuid), VIK_TRW_LAYER_SUBLAYER_ROUTE, NULL, TRUE, TRUE );
+    vik_treeview_add_sublayer ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter), iter, name, vtl, GUINT_TO_POINTER(rt_uuid), VIK_TRW_LAYER_SUBLAYER_ROUTE, NULL, TRUE, 0 ); // Routes don't have times
     // Actual setting of visibility dependent on the route
     vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, t->visible );
 
@@ -4495,7 +4595,19 @@ gchar *trw_layer_new_unique_sublayer_name (VikTrwLayer *vtl, gint sublayer_type,
     }
     // If found a name already in use try adding 1 to it and we try again
     if ( id ) {
-      gchar *new_newname = g_strdup_printf("%s#%d", name, i);
+      const gchar *corename = newname;
+      gint newi = i;
+      // If name is already of the form text#N
+      //  set name to text and i to N+1
+      gchar **tokens = g_regex_split_simple ( "#(\\d+)", newname, G_REGEX_CASELESS, 0 );
+      if ( tokens ) {
+        corename = tokens[0];
+        if ( tokens[1] ) {
+          newi = atoi ( tokens[1] ) + 1;
+        }
+      }
+      gchar *new_newname = g_strdup_printf("%s#%d", corename, newi);
+      g_strfreev ( tokens );
       g_free(newname);
       newname = new_newname;
       i++;
@@ -4520,10 +4632,12 @@ void vik_trw_layer_filein_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *t
     // enforce end of current track equal to start of tr
     VikTrackpoint *cur_end = vik_track_get_tp_last ( vtl->current_track );
     VikTrackpoint *new_start = vik_track_get_tp_first ( tr );
-    if ( ! vik_coord_equals ( &cur_end->coord, &new_start->coord ) ) {
-        vik_track_add_trackpoint ( vtl->current_track,
-                                   vik_trackpoint_copy ( cur_end ),
-                                   FALSE );
+    if ( cur_end && new_start ) {
+      if ( ! vik_coord_equals ( &cur_end->coord, &new_start->coord ) ) {
+          vik_track_add_trackpoint ( vtl->current_track,
+                                     vik_trackpoint_copy ( cur_end ),
+                                     FALSE );
+      }
     }
 
     vik_track_steal_and_append_trackpoints ( vtl->current_track, tr );
@@ -4554,34 +4668,31 @@ static void trw_layer_enum_item ( gpointer id, GList **tr, GList **l )
  */
 static void trw_layer_move_item ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, gpointer id, gint type )
 {
+  // When an item is moved the name is checked to see if it clashes with an existing name
+  //  in the destination layer and if so then it is allocated a new name
+
   // TODO reconsider strategy when moving within layer (if anything...)
-  gboolean rename = ( vtl_src != vtl_dest );
-  if ( ! rename )
+  if ( vtl_src == vtl_dest )
     return;
 
   if (type == VIK_TRW_LAYER_SUBLAYER_TRACK) {
     VikTrack *trk = g_hash_table_lookup ( vtl_src->tracks, id );
 
-    gchar *newname;
-    if ( rename )
-      newname = trw_layer_new_unique_sublayer_name ( vtl_dest, type, trk->name );
-    else
-      newname = g_strdup ( trk->name );
+    gchar *newname = trw_layer_new_unique_sublayer_name ( vtl_dest, type, trk->name );
 
     VikTrack *trk2 = vik_track_copy ( trk, TRUE );
     vik_trw_layer_add_track ( vtl_dest, newname, trk2 );
     g_free ( newname );
     vik_trw_layer_delete_track ( vtl_src, trk );
+    // Reset layer timestamps in case they have now changed
+    vik_treeview_item_set_timestamp ( vtl_dest->vl.vt, &vtl_dest->vl.iter, trw_layer_get_timestamp(vtl_dest) );
+    vik_treeview_item_set_timestamp ( vtl_src->vl.vt, &vtl_src->vl.iter, trw_layer_get_timestamp(vtl_src) );
   }
 
   if (type == VIK_TRW_LAYER_SUBLAYER_ROUTE) {
     VikTrack *trk = g_hash_table_lookup ( vtl_src->routes, id );
 
-    gchar *newname;
-    if ( rename )
-      newname = trw_layer_new_unique_sublayer_name ( vtl_dest, type, trk->name );
-    else
-      newname = g_strdup ( trk->name );
+    gchar *newname = trw_layer_new_unique_sublayer_name ( vtl_dest, type, trk->name );
 
     VikTrack *trk2 = vik_track_copy ( trk, TRUE );
     vik_trw_layer_add_route ( vtl_dest, newname, trk2 );
@@ -4592,11 +4703,7 @@ static void trw_layer_move_item ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, g
   if (type == VIK_TRW_LAYER_SUBLAYER_WAYPOINT) {
     VikWaypoint *wp = g_hash_table_lookup ( vtl_src->waypoints, id );
 
-    gchar *newname;
-    if ( rename )
-      newname = trw_layer_new_unique_sublayer_name ( vtl_dest, type, wp->name );
-    else
-      newname = g_strdup ( wp->name );
+    gchar *newname = trw_layer_new_unique_sublayer_name ( vtl_dest, type, wp->name );
 
     VikWaypoint *wp2 = vik_waypoint_copy ( wp );
     vik_trw_layer_add_waypoint ( vtl_dest, newname, wp2 );
@@ -4606,6 +4713,9 @@ static void trw_layer_move_item ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, g
     // Recalculate bounds even if not renamed as maybe dragged between layers
     trw_layer_calculate_bounds_waypoints ( vtl_dest );
     trw_layer_calculate_bounds_waypoints ( vtl_src );
+    // Reset layer timestamps in case they have now changed
+    vik_treeview_item_set_timestamp ( vtl_dest->vl.vt, &vtl_dest->vl.iter, trw_layer_get_timestamp(vtl_dest) );
+    vik_treeview_item_set_timestamp ( vtl_src->vl.vt, &vtl_src->vl.iter, trw_layer_get_timestamp(vtl_src) );
   }
 }
 
@@ -4680,7 +4790,7 @@ gboolean vik_trw_layer_delete_track ( VikTrwLayer *vtl, VikTrack *trk )
     udata.uuid = NULL;
 
     // Hmmm, want key of it
-    gpointer *trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
+    gpointer trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
 
     if ( trkf && udata.uuid ) {
       /* could be current_tp, so we have to check */
@@ -4728,7 +4838,7 @@ gboolean vik_trw_layer_delete_route ( VikTrwLayer *vtl, VikTrack *trk )
     udata.uuid = NULL;
 
     // Hmmm, want key of it
-    gpointer *trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata );
+    gpointer trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata );
 
     if ( trkf && udata.uuid ) {
       /* could be current_tp, so we have to check */
@@ -4772,7 +4882,7 @@ static gboolean trw_layer_delete_waypoint ( VikTrwLayer *vtl, VikWaypoint *wp )
     udata.uuid = NULL;
 
     // Hmmm, want key of it
-    gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
+    gpointer wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
 
     if ( wpf && udata.uuid ) {
       GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udata.uuid );
@@ -4824,7 +4934,7 @@ static gboolean trw_layer_delete_waypoint_by_name ( VikTrwLayer *vtl, const gcha
   udata.uuid = NULL;
 
   // Hmmm, want key of it
-  gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid_by_name, (gpointer) &udata );
+  gpointer wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid_by_name, (gpointer) &udata );
 
   vik_waypoint_free (udata.wp);
 
@@ -4865,7 +4975,7 @@ static gboolean trw_layer_delete_track_by_name ( VikTrwLayer *vtl, const gchar *
   udata.uuid = NULL;
 
   // Hmmm, want key of it
-  gpointer *trkf = g_hash_table_find ( ht_tracks, (GHRFunc) trw_layer_track_find_uuid_by_name, &udata );
+  gpointer trkf = g_hash_table_find ( ht_tracks, (GHRFunc) trw_layer_track_find_uuid_by_name, &udata );
 
   vik_track_free (udata.trk);
 
@@ -4984,6 +5094,8 @@ static void trw_layer_delete_item ( menu_array_sublayer values )
           return;
       was_visible = trw_layer_delete_waypoint ( vtl, wp );
       trw_layer_calculate_bounds_waypoints ( vtl );
+      // Reset layer timestamp in case it has now changed
+      vik_treeview_item_set_timestamp ( vtl->vl.vt, &vtl->vl.iter, trw_layer_get_timestamp(vtl) );
     }
   }
   else if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_TRACK )
@@ -4997,6 +5109,8 @@ static void trw_layer_delete_item ( menu_array_sublayer values )
                                  trk->name ) )
           return;
       was_visible = vik_trw_layer_delete_track ( vtl, trk );
+      // Reset layer timestamp in case it has now changed
+      vik_treeview_item_set_timestamp ( vtl->vl.vt, &vtl->vl.iter, trw_layer_get_timestamp(vtl) );
     }
   }
   else
@@ -5029,7 +5143,7 @@ void trw_layer_waypoint_rename ( VikTrwLayer *vtl, VikWaypoint *wp, const gchar
   udataU.uuid = NULL;
 
   // Need key of it for treeview update
-  gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, &udataU );
+  gpointer wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, &udataU );
 
   if ( wpf && udataU.uuid ) {
     GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udataU.uuid );
@@ -5052,7 +5166,7 @@ void trw_layer_waypoint_reset_icon ( VikTrwLayer *vtl, VikWaypoint *wp )
   udataU.uuid = NULL;
 
   // Need key of it for treeview update
-  gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, &udataU );
+  gpointer wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, &udataU );
 
   if ( wpf && udataU.uuid ) {
     GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udataU.uuid );
@@ -5139,7 +5253,7 @@ void trw_layer_update_treeview ( VikTrwLayer *vtl, VikTrack *trk )
   udata.trk  = trk;
   udata.uuid = NULL;
 
-  gpointer *trkf = NULL;
+  gpointer trkf = NULL;
   if ( trk->is_route )
     trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata );
   else
@@ -5285,6 +5399,19 @@ static void trw_layer_anonymize_times ( menu_array_sublayer values )
     vik_track_anonymize_times ( track );
 }
 
+static void trw_layer_interpolate_times ( menu_array_sublayer values )
+{
+  VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
+  VikTrack *track;
+  if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
+    track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
+  else
+    track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
+
+  if ( track )
+    vik_track_interpolate_times ( track );
+}
+
 static void trw_layer_extend_track_end ( menu_array_sublayer values )
 {
   VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
@@ -5772,11 +5899,11 @@ static void find_nearby_tracks_by_time (gpointer key, gpointer value, gpointer u
 
     guint threshold = GPOINTER_TO_UINT (((gpointer *)user_data)[2]);
     //g_print("Got track named %s, times %d, %d\n", trk->name, p1->timestamp, p2->timestamp);
-    if (! (abs(t1 - p2->timestamp) < threshold ||
-       /*  p1 p2      t1 t2 */
-          abs(p1->timestamp - t2) < threshold)
-       /*  t1 t2      p1 p2 */
-       ) {
+    if (! (labs(t1 - p2->timestamp) < threshold ||
+      /*  p1 p2      t1 t2 */
+      labs(p1->timestamp - t2) < threshold)
+      /*  t1 t2      p1 p2 */
+    ) {
       return;
     }
   }
@@ -6232,7 +6359,7 @@ static void trw_layer_split_at_selected_trackpoint ( VikTrwLayer *vtl, gint subt
       udata.uuid = NULL;
 
       // Also need id of newly created track
-      gpointer *trkf;
+      gpointer trkf;
       if ( tr->is_route )
          trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata );
       else
@@ -6702,7 +6829,12 @@ static void trw_layer_astro_open ( VikTrwLayer *vtl, const gchar *date_str, cons
 {
   GError *err = NULL;
   gchar *tmp;
-  g_file_open_tmp ( "vik-astro-XXXXXX.ini", &tmp, NULL );
+  gint fd = g_file_open_tmp ( "vik-astro-XXXXXX.ini", &tmp, &err );
+  if (fd < 0) {
+    g_warning ( "%s: Failed to open temporary file: %s", __FUNCTION__, err->message );
+    g_clear_error ( &err );
+    return;
+  }
   gchar *cmd = g_strdup_printf ( "%s %s %s %s %s %s %s %s %s %s %s %s %s %s",
                                   astro_program, "-c", tmp, "--full-screen no", "--sky-date", date_str, "--sky-time", time_str, "--latitude", lat_str, "--longitude", lon_str, "--altitude", alt_str );
   g_warning ( "%s", cmd );
@@ -6959,7 +7091,7 @@ static void vik_trw_layer_uniquify_tracks ( VikTrwLayer *vtl, VikLayersPanel *vl
     udataU.uuid = NULL;
 
     // Need want key of it for treeview update
-    gpointer *trkf = g_hash_table_find ( track_table, (GHRFunc) trw_layer_track_find_uuid, &udataU );
+    gpointer trkf = g_hash_table_find ( track_table, (GHRFunc) trw_layer_track_find_uuid, &udataU );
 
     if ( trkf && udataU.uuid ) {
 
@@ -6972,11 +7104,12 @@ static void vik_trw_layer_uniquify_tracks ( VikTrwLayer *vtl, VikLayersPanel *vl
       if ( it ) {
         vik_treeview_item_set_name ( VIK_LAYER(vtl)->vt, it, newname );
         if ( ontrack )
-          vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), vtl->wp_sort_order );
-       else
-          vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter), vtl->wp_sort_order );
+          vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), vtl->track_sort_order );
+        else
+          vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter), vtl->track_sort_order );
       }
     }
+    g_free ( newname );
 
     // Start trying to find same names again...
     track_names = NULL;
@@ -6993,50 +7126,50 @@ static void vik_trw_layer_uniquify_tracks ( VikTrwLayer *vtl, VikLayersPanel *vl
   vik_layers_panel_emit_update ( vlp );
 }
 
-static void trw_layer_sort_order_a2z ( menu_array_sublayer values )
+static void trw_layer_sort_order_specified ( VikTrwLayer *vtl, guint sublayer_type, vik_layer_sort_order_t order )
 {
-  VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
   GtkTreeIter *iter;
 
-  switch (GPOINTER_TO_INT (values[MA_SUBTYPE])) {
+  switch (sublayer_type) {
   case VIK_TRW_LAYER_SUBLAYER_TRACKS:
     iter = &(vtl->tracks_iter);
-    vtl->track_sort_order = VL_SO_ALPHABETICAL_ASCENDING;
+    vtl->track_sort_order = order;
     break;
   case VIK_TRW_LAYER_SUBLAYER_ROUTES:
     iter = &(vtl->routes_iter);
-    vtl->track_sort_order = VL_SO_ALPHABETICAL_ASCENDING;
+    vtl->track_sort_order = order;
     break;
   default: // VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
     iter = &(vtl->waypoints_iter);
-    vtl->wp_sort_order = VL_SO_ALPHABETICAL_ASCENDING;
+    vtl->wp_sort_order = order;
     break;
   }
 
-  vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, iter, VL_SO_ALPHABETICAL_ASCENDING );
+  vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, iter, order );
+}
+
+static void trw_layer_sort_order_a2z ( menu_array_sublayer values )
+{
+  VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
+  trw_layer_sort_order_specified ( vtl, GPOINTER_TO_INT(values[MA_SUBTYPE]), VL_SO_ALPHABETICAL_ASCENDING );
 }
 
 static void trw_layer_sort_order_z2a ( menu_array_sublayer values )
 {
   VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
-  GtkTreeIter *iter;
+  trw_layer_sort_order_specified ( vtl, GPOINTER_TO_INT(values[MA_SUBTYPE]), VL_SO_ALPHABETICAL_DESCENDING );
+}
 
-  switch (GPOINTER_TO_INT (values[MA_SUBTYPE])) {
-  case VIK_TRW_LAYER_SUBLAYER_TRACKS:
-    iter = &(vtl->tracks_iter);
-    vtl->track_sort_order = VL_SO_ALPHABETICAL_DESCENDING;
-    break;
-  case VIK_TRW_LAYER_SUBLAYER_ROUTES:
-    iter = &(vtl->routes_iter);
-    vtl->track_sort_order = VL_SO_ALPHABETICAL_DESCENDING;
-    break;
-  default: // VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
-    iter = &(vtl->waypoints_iter);
-    vtl->wp_sort_order = VL_SO_ALPHABETICAL_DESCENDING;
-    break;
-  }
+static void trw_layer_sort_order_timestamp_ascend ( menu_array_sublayer values )
+{
+  VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
+  trw_layer_sort_order_specified ( vtl, GPOINTER_TO_INT(values[MA_SUBTYPE]), VL_SO_DATE_ASCENDING );
+}
 
-  vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, iter, VL_SO_ALPHABETICAL_DESCENDING );
+static void trw_layer_sort_order_timestamp_descend ( menu_array_sublayer values )
+{
+  VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
+  trw_layer_sort_order_specified ( vtl, GPOINTER_TO_INT(values[MA_SUBTYPE]), VL_SO_DATE_DESCENDING );
 }
 
 /**
@@ -7082,6 +7215,9 @@ static void trw_layer_delete_tracks_from_selection ( menu_array_layer values )
       trw_layer_delete_track_by_name (vtl, l->data, vtl->tracks);
     }
     g_list_free(delete_list);
+    // Reset layer timestamps in case they have now changed
+    vik_treeview_item_set_timestamp ( vtl->vl.vt, &vtl->vl.iter, trw_layer_get_timestamp(vtl) );
+
     vik_layer_emit_update( VIK_LAYER(vtl) );
   }
 }
@@ -7232,6 +7368,8 @@ static void vik_trw_layer_uniquify_waypoints ( VikTrwLayer *vtl, VikLayersPanel
 
     trw_layer_waypoint_rename ( vtl, waypoint, newname );
 
+    g_free (newname);
+
     // Start trying to find same names again...
     waypoint_names = NULL;
     g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
@@ -7293,6 +7431,8 @@ static void trw_layer_delete_waypoints_from_selection ( menu_array_layer values
     g_list_free(delete_list);
 
     trw_layer_calculate_bounds_waypoints ( vtl );
+    // Reset layer timestamp in case it has now changed
+    vik_treeview_item_set_timestamp ( vtl->vl.vt, &vtl->vl.iter, trw_layer_get_timestamp(vtl) );
     vik_layer_emit_update( VIK_LAYER(vtl) );
   }
 
@@ -7723,11 +7863,13 @@ static gboolean is_valid_geocache_name ( gchar *str )
   return len >= 3 && len <= 7 && str[0] == 'G' && str[1] == 'C' && isalnum(str[2]) && (len < 4 || isalnum(str[3])) && (len < 5 || isalnum(str[4])) && (len < 6 || isalnum(str[5])) && (len < 7 || isalnum(str[6]));
 }
 
+#ifndef WINDOWS
 static void trw_layer_track_use_with_filter ( menu_array_sublayer values )
 {
   VikTrack *trk = g_hash_table_lookup ( VIK_TRW_LAYER(values[MA_VTL])->tracks, values[MA_SUBLAYER_ID] );
   a_acquire_set_filter_track ( trk );
 }
+#endif
 
 #ifdef VIK_CONFIG_GOOGLE
 static gboolean is_valid_google_route ( VikTrwLayer *vtl, const gpointer track_id )
@@ -7740,7 +7882,7 @@ static void trw_layer_google_route_webpage ( menu_array_sublayer values )
 {
   VikTrack *tr = g_hash_table_lookup ( VIK_TRW_LAYER(values[MA_VTL])->routes, values[MA_SUBLAYER_ID] );
   if ( tr ) {
-    gchar *escaped = uri_escape ( tr->comment );
+    gchar *escaped = g_uri_escape_string ( tr->comment, NULL, TRUE );
     gchar *webpage = g_strdup_printf("http://maps.google.com/maps?f=q&hl=en&q=%s", escaped );
     open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(values[MA_VTL])), webpage);
     g_free ( escaped );
@@ -8138,6 +8280,18 @@ static gboolean trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *men
     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_sort_order_z2a), pass_along );
     gtk_menu_shell_append ( GTK_MENU_SHELL(submenu_sort), item );
     gtk_widget_show ( item );
+
+    item = gtk_image_menu_item_new_with_mnemonic ( _("Date Ascending") );
+    gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_SORT_ASCENDING, GTK_ICON_SIZE_MENU) );
+    g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_sort_order_timestamp_ascend), pass_along );
+    gtk_menu_shell_append ( GTK_MENU_SHELL(submenu_sort), item );
+    gtk_widget_show ( item );
+
+    item = gtk_image_menu_item_new_with_mnemonic ( _("Date Descending") );
+    gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_SORT_DESCENDING, GTK_ICON_SIZE_MENU) );
+    g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_sort_order_timestamp_descend), pass_along );
+    gtk_menu_shell_append ( GTK_MENU_SHELL(submenu_sort), item );
+    gtk_widget_show ( item );
   }
 
   GtkWidget *upload_submenu = gtk_menu_new ();
@@ -8402,13 +8556,19 @@ static gboolean trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *men
     gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
     gtk_widget_show ( item );
 
-    // Routes don't have timestamps - so this is only available for tracks
+    // Routes don't have timestamps - so these are only available for tracks
     if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
       item = gtk_image_menu_item_new_with_mnemonic ( _("_Anonymize Times") );
       g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_anonymize_times), pass_along );
       gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
       gtk_widget_set_tooltip_text (item, _("Shift timestamps to a relative offset from 1901-01-01"));
       gtk_widget_show ( item );
+
+      item = gtk_image_menu_item_new_with_mnemonic ( _("_Interpolate Times") );
+      g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_interpolate_times), pass_along );
+      gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
+      gtk_widget_set_tooltip_text (item, _("Reset trackpoint timestamps between the first and last points such that track is traveled at equal speed"));
+      gtk_widget_show ( item );
     }
 
     if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
@@ -8482,22 +8642,18 @@ static gboolean trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *men
     }
   }
 
-  // Only made available if a suitable program is installed
+  GtkWidget *external_submenu = create_external_submenu ( menu );
+
+  // These are only made available if a suitable program is installed
   if ( (have_astro_program || have_diary_program) &&
        (subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT) ) {
-    GtkWidget *external_submenu;
-    external_submenu = gtk_menu_new ();
-    item = gtk_image_menu_item_new_with_mnemonic ( _("Externa_l") );
-    gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_EXECUTE, GTK_ICON_SIZE_MENU) );
-    gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
-    gtk_widget_show ( item );
-    gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), external_submenu );
 
     if ( have_diary_program ) {
       item = gtk_image_menu_item_new_with_mnemonic ( _("_Diary") );
       gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_SPELL_CHECK, GTK_ICON_SIZE_MENU) );
       g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_diary), pass_along );
       gtk_menu_shell_append ( GTK_MENU_SHELL(external_submenu), item );
+      gtk_widget_set_tooltip_text (item, _("Open diary program at this date"));
       gtk_widget_show ( item );
     }
 
@@ -8505,10 +8661,27 @@ static gboolean trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *men
       item = gtk_image_menu_item_new_with_mnemonic ( _("_Astronomy") );
       g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_astro), pass_along );
       gtk_menu_shell_append ( GTK_MENU_SHELL(external_submenu), item );
+      gtk_widget_set_tooltip_text (item, _("Open astronomy program at this date and location"));
       gtk_widget_show ( item );
     }
   }
 
+  if ( l->current_tpl || l->current_wp ) {
+    // For the selected point
+    VikCoord *vc;
+    if ( l->current_tpl )
+      vc = &(VIK_TRACKPOINT(l->current_tpl->data)->coord);
+    else
+      vc = &(l->current_wp->coord);
+    vik_ext_tools_add_menu_items_to_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(l)), GTK_MENU (external_submenu), vc );
+  }
+  else {
+    // Otherwise for the selected sublayer
+    // TODO: Should use selected items centre - rather than implicitly using the current viewport
+    vik_ext_tools_add_menu_items_to_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(l)), GTK_MENU (external_submenu), NULL );
+  }
+
+
 #ifdef VIK_CONFIG_GOOGLE
   if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE && is_valid_google_route ( l, sublayer ) )
   {
@@ -8532,11 +8705,14 @@ static gboolean trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *men
     gtk_widget_show ( item );
 #endif
 
+    // Currently filter with functions all use shellcommands and thus don't work in Windows
+#ifndef WINDOWS
     item = gtk_image_menu_item_new_with_mnemonic ( _("Use with _Filter") );
     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_use_with_filter), pass_along );
     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
     gtk_widget_show ( item );
+#endif
 
     /* ATM This function is only available via the layers panel, due to needing a vlp */
     if ( vlp ) {
@@ -8893,7 +9069,7 @@ typedef struct {
   gint x, y;
   gint closest_x, closest_y;
   gboolean draw_images;
-  gpointer *closest_wp_id;
+  gpointer closest_wp_id;
   VikWaypoint *closest_wp;
   VikViewport *vvp;
 } WPSearchParams;
@@ -9288,7 +9464,7 @@ static gboolean trw_layer_show_selected_viewport_menu ( VikTrwLayer *vtl, GdkEve
       udataU.trk  = track;
       udataU.uuid = NULL;
 
-      gpointer *trkf;
+      gpointer trkf;
       if ( track->is_route )
         trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udataU );
       else
@@ -9331,7 +9507,7 @@ static gboolean trw_layer_show_selected_viewport_menu ( VikTrwLayer *vtl, GdkEve
       udata.wp   = waypoint;
       udata.uuid = NULL;
 
-      gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
+      gpointer wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
 
       if ( wpf && udata.uuid ) {
         GtkTreeIter *iter = g_hash_table_lookup ( vtl->waypoints_iters, udata.uuid );
@@ -9438,8 +9614,8 @@ static gboolean tool_edit_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *eve
     gint x, y;
     vik_viewport_coord_to_screen ( vvp, &(vtl->current_wp->coord), &x, &y );
 
-    if ( abs(x - event->x) <= WAYPOINT_SIZE_APPROX &&
-         abs(y - event->y) <= WAYPOINT_SIZE_APPROX )
+    if ( abs(x - (int)round(event->x)) <= WAYPOINT_SIZE_APPROX &&
+         abs(y - (int)round(event->y)) <= WAYPOINT_SIZE_APPROX )
     {
       if ( event->button == 3 )
         vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
@@ -10003,9 +10179,10 @@ static gboolean tool_new_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *even
   if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
     return FALSE;
   vik_viewport_screen_to_coord ( vvp, event->x, event->y, &coord );
-  if (vik_trw_layer_new_waypoint ( vtl, VIK_GTK_WINDOW_FROM_LAYER(vtl), &coord ) && VIK_LAYER(vtl)->visible) {
+  if ( vik_trw_layer_new_waypoint (vtl, VIK_GTK_WINDOW_FROM_LAYER(vtl), &coord) ) {
     trw_layer_calculate_bounds_waypoints ( vtl );
-    vik_layer_emit_update ( VIK_LAYER(vtl) );
+    if ( VIK_LAYER(vtl)->visible )
+      vik_layer_emit_update ( VIK_LAYER(vtl) );
   }
   return TRUE;
 }
@@ -10026,22 +10203,24 @@ static void tool_edit_trackpoint_destroy ( tool_ed_t *t )
   g_free ( t );
 }
 
+/**
+ * tool_edit_trackpoint_click:
+ *
+ * On 'initial' click: search for the nearest trackpoint or routepoint and store it as the current trackpoint
+ * Then update the viewport, statusbar and edit dialog to draw the point as being selected and it's information.
+ * On subsequent clicks: (as the current trackpoint is defined) and the click is very near the same point
+ *  then initiate the move operation to drag the point to a new destination.
+ * NB The current trackpoint will get reset elsewhere.
+ */
 static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
 {
   tool_ed_t *t = data;
   VikViewport *vvp = t->vvp;
   TPSearchParams params;
-  /* OUTDATED DOCUMENTATION:
-   find 5 pixel range on each side. then put these UTM, and a pointer
-   to the winning track name (and maybe the winning track itself), and a
-   pointer to the winning trackpoint, inside an array or struct. pass 
-   this along, do a foreach on the tracks which will do a foreach on the 
-   trackpoints. */
   params.vvp = vvp;
   params.x = event->x;
   params.y = event->y;
   params.closest_track_id = NULL;
-  /* TODO: should get track listitem so we can break it up, make a new track, mess it up, all that. */
   params.closest_tp = NULL;
   params.closest_tpl = NULL;
   vik_viewport_get_min_max_lat_lon ( vvp, &(params.bbox.south), &(params.bbox.north), &(params.bbox.west), &(params.bbox.east) );
@@ -10052,7 +10231,7 @@ static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *e
   if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
     return FALSE;
 
-  if ( !vtl->vl.visible || !vtl->tracks_visible || !vtl->routes_visible )
+  if ( !vtl->vl.visible || !(vtl->tracks_visible || vtl->routes_visible) )
     return FALSE;
 
   if ( vtl->current_tpl )
@@ -10060,6 +10239,8 @@ static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *e
     /* first check if it is within range of prev. tp. and if current_tp track is shown. (if it is, we are moving that trackpoint.) */
     VikTrackpoint *tp = VIK_TRACKPOINT(vtl->current_tpl->data);
     VikTrack *current_tr = VIK_TRACK(g_hash_table_lookup(vtl->tracks, vtl->current_tp_id));
+    if ( !current_tr )
+      current_tr = VIK_TRACK(g_hash_table_lookup(vtl->routes, vtl->current_tp_id));
     if ( !current_tr )
       return FALSE;
 
@@ -10067,8 +10248,8 @@ static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *e
     vik_viewport_coord_to_screen ( vvp, &(tp->coord), &x, &y );
 
     if ( current_tr->visible && 
-         abs(x - event->x) < TRACKPOINT_SIZE_APPROX &&
-         abs(y - event->y) < TRACKPOINT_SIZE_APPROX ) {
+         abs(x - (int)round(event->x)) < TRACKPOINT_SIZE_APPROX &&
+         abs(y - (int)round(event->y)) < TRACKPOINT_SIZE_APPROX ) {
       marker_begin_move ( t, event->x, event->y );
       return TRUE;
     }
@@ -10171,7 +10352,8 @@ static gboolean tool_edit_trackpoint_release ( VikTrwLayer *vtl, GdkEventButton
 
     /* diff dist is diff from orig */
     if ( vtl->tpwin )
-      my_tpwin_set_tp ( vtl );
+      if ( vtl->current_tp_track )
+        my_tpwin_set_tp ( vtl );
 
     vik_layer_emit_update ( VIK_LAYER(vtl) );
     return TRUE;
@@ -10420,7 +10602,8 @@ void trw_layer_verify_thumbnails ( VikTrwLayer *vtl, GtkWidget *vp )
       thumbnail_create_thread_data *tctd = g_malloc ( sizeof(thumbnail_create_thread_data) );
       tctd->vtl = vtl;
       tctd->pics = pics;
-      a_background_thread ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
+      a_background_thread ( BACKGROUND_POOL_LOCAL,
+                            VIK_GTK_WINDOW_FROM_LAYER(vtl),
                            tmp,
                            (vik_thr_func) create_thumbnails_thread,
                            tctd,
@@ -10568,6 +10751,74 @@ static void trw_layer_sort_all ( VikTrwLayer *vtl )
     vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), vtl->wp_sort_order );
 }
 
+/**
+ * Get the earliest timestamp available from all tracks
+ */
+static time_t trw_layer_get_timestamp_tracks ( VikTrwLayer *vtl )
+{
+  time_t timestamp = 0;
+  GList *gl = g_hash_table_get_values ( vtl->tracks );
+  gl = g_list_sort ( gl, vik_track_compare_timestamp );
+  gl = g_list_first ( gl );
+
+  if ( gl ) {
+    // Only need to check the first track as they have been sorted by time
+    VikTrack *trk = (VikTrack*)gl->data;
+    // Assume trackpoints already sorted by time
+    VikTrackpoint *tpt = vik_track_get_tp_first(trk);
+    if ( tpt && tpt->has_timestamp ) {
+      timestamp = tpt->timestamp;
+    }
+    g_list_free ( gl );
+  }
+  return timestamp;
+}
+
+/**
+ * Get the earliest timestamp available from all waypoints
+ */
+static time_t trw_layer_get_timestamp_waypoints ( VikTrwLayer *vtl )
+{
+  time_t timestamp = 0;
+  GList *gl = g_hash_table_get_values ( vtl->waypoints );
+  GList *iter;
+  for (iter = g_list_first (gl); iter != NULL; iter = g_list_next (iter)) {
+    VikWaypoint *wpt = (VikWaypoint*)iter->data;
+    if ( wpt->has_timestamp ) {
+      // When timestamp not set yet - use the first value encountered
+      if ( timestamp == 0 )
+        timestamp = wpt->timestamp;
+      else if ( timestamp > wpt->timestamp )
+        timestamp = wpt->timestamp;
+    }
+  }
+  g_list_free ( gl );
+
+  return timestamp;
+}
+
+/**
+ * Get the earliest timestamp available for this layer
+ */
+static time_t trw_layer_get_timestamp ( VikTrwLayer *vtl )
+{
+  time_t timestamp_tracks = trw_layer_get_timestamp_tracks ( vtl );
+  time_t timestamp_waypoints = trw_layer_get_timestamp_waypoints ( vtl );
+  // NB routes don't have timestamps - hence they are not considered
+
+  if ( !timestamp_tracks && !timestamp_waypoints ) {
+    // Fallback to get time from the metadata when no other timestamps available
+    GTimeVal gtv;
+    if  ( vtl->metadata && vtl->metadata->timestamp && g_time_val_from_iso8601 ( vtl->metadata->timestamp, &gtv ) )
+      return gtv.tv_sec;
+  }
+  if ( timestamp_tracks && !timestamp_waypoints )
+    return timestamp_tracks;
+  if ( timestamp_tracks && timestamp_waypoints && (timestamp_tracks < timestamp_waypoints) )
+    return timestamp_tracks;
+  return timestamp_waypoints;
+}
+
 static void trw_layer_post_read ( VikTrwLayer *vtl, GtkWidget *vvp, gboolean from_file )
 {
   if ( VIK_LAYER(vtl)->realized )
@@ -10595,47 +10846,13 @@ static void trw_layer_post_read ( VikTrwLayer *vtl, GtkWidget *vvp, gboolean fro
     }
 
     if ( need_to_set_time ) {
-      // Could rewrite this as a general get first time of a TRW Layer function
       GTimeVal timestamp;
       timestamp.tv_usec = 0;
-      gboolean has_timestamp = FALSE;
-
-      GList *gl = NULL;
-      gl = g_hash_table_get_values ( vtl->tracks );
-      gl = g_list_sort ( gl, vik_track_compare_timestamp );
-      gl = g_list_first ( gl );
-
-      // Check times of tracks
-      if ( gl ) {
-        // Only need to check the first track as they have been sorted by time
-        VikTrack *trk = (VikTrack*)gl->data;
-        // Assume trackpoints already sorted by time
-        VikTrackpoint *tpt = vik_track_get_tp_first(trk);
-        if ( tpt && tpt->has_timestamp ) {
-          timestamp.tv_sec = tpt->timestamp;
-          has_timestamp = TRUE;
-        }
-        g_list_free ( gl );
-      }
+      timestamp.tv_sec = trw_layer_get_timestamp ( vtl );
 
-      if ( !has_timestamp ) {
-        // 'Last' resort - current time
-        // Get before waypoint tests - so that if a waypoint time value (in the past) is found it should be used
+      // No time found - so use 'now' for the metadata time
+      if ( timestamp.tv_sec == 0 ) {
         g_get_current_time ( &timestamp );
-
-        // Check times of waypoints
-        gl = g_hash_table_get_values ( vtl->waypoints );
-        GList *iter;
-        for (iter = g_list_first (gl); iter != NULL; iter = g_list_next (iter)) {
-          VikWaypoint *wpt = (VikWaypoint*)iter->data;
-          if ( wpt->has_timestamp ) {
-            if ( timestamp.tv_sec > wpt->timestamp ) {
-              timestamp.tv_sec = wpt->timestamp;
-              has_timestamp = TRUE;
-            }
-          }
-        }
-        g_list_free ( gl );
       }
 
       vtl->metadata->timestamp = g_time_val_to_iso8601 ( &timestamp );
@@ -10876,8 +11093,8 @@ static void trw_layer_download_map_along_track_cb ( menu_array_sublayer values )
   }
 
   // Convert from list of vmls to list of names. Allowing the user to select one of them
-  gchar **map_names = g_malloc(1 + num_maps * sizeof(gpointer));
-  VikMapsLayer **map_layers = g_malloc(1 + num_maps * sizeof(gpointer));
+  gchar **map_names = g_malloc_n(1 + num_maps, sizeof(gpointer));
+  VikMapsLayer **map_layers = g_malloc_n(1 + num_maps, sizeof(gpointer));
 
   gchar **np = map_names;
   VikMapsLayer **lp = map_layers;