]> git.street.me.uk Git - andy/viking.git/blobdiff - src/viktrwlayer.c
Remove OpenAerial
[andy/viking.git] / src / viktrwlayer.c
index 80745585f6ba7e5fa1d9ded0617a271d5ae00c89..a59e3759f0a58ec93ece828835e2d75d06416a9a 100644 (file)
@@ -30,7 +30,6 @@
 
 #include "viking.h"
 #include "vikmapslayer.h"
-#include "viktrwlayer_pixmap.h"
 #include "viktrwlayer_tpwin.h"
 #include "viktrwlayer_propwin.h"
 #include "garminsymbols.h"
@@ -40,7 +39,7 @@
 #include "babel.h"
 #include "dem.h"
 #include "dems.h"
-#include "googlesearch.h"
+#include "geonamessearch.h"
 #ifdef VIK_CONFIG_OPENSTREETMAP
 #include "osm-traces.h"
 #endif
 
 #include "icons/icons.h"
 
+#ifdef HAVE_MATH_H
 #include <math.h>
+#endif
+#ifdef HAVE_STRING_H
 #include <string.h>
+#endif
+#ifdef HAVE_STDLIB_H
 #include <stdlib.h>
+#endif
 #include <stdio.h>
 #include <ctype.h>
 
@@ -66,7 +71,7 @@ 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
 
-#define GOOGLE_DIRECTIONS_STRING "maps.google.com/maps?q=from:%s,%s+to:%s,%s&output=js"
+#define GOOGLE_DIRECTIONS_STRING "maps.google.com/maps?q=from:%s,%s+to:%s,%s&output=kml"
 #define VIK_TRW_LAYER_TRACK_GC 13
 #define VIK_TRW_LAYER_TRACK_GC_RATES 10
 #define VIK_TRW_LAYER_TRACK_GC_MIN 0
@@ -207,6 +212,7 @@ static void trw_layer_cut_item_cb( gpointer *pass_along);
 
 static void trw_layer_find_maxmin_waypoints ( const gchar *name, const VikWaypoint *w, struct LatLon maxmin[2] );
 static void trw_layer_find_maxmin_tracks ( const gchar *name, GList **t, struct LatLon maxmin[2] );    
+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 );
@@ -225,6 +231,9 @@ static void trw_layer_centerize ( gpointer layer_and_vlp[2] );
 static void trw_layer_export ( gpointer layer_and_vlp[2], guint file_type );
 static void trw_layer_goto_wp ( gpointer layer_and_vlp[2] );
 static void trw_layer_new_wp ( gpointer lav[2] );
+static void trw_layer_new_wikipedia_wp_viewport ( gpointer lav[2] );
+static void trw_layer_new_wikipedia_wp_layer ( gpointer lav[2] );
+static void trw_layer_merge_with_other ( gpointer pass_along[6] );
 
 /* pop-up items */
 static void trw_layer_properties_item ( gpointer pass_along[5] );
@@ -296,30 +305,30 @@ static void highest_wp_number_remove_wp(VikTrwLayer *vtl, const gchar *old_wp_na
 
 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 },
+    (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 },
+    (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 },
+    (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, 
     (VikToolMouseFunc) tool_edit_waypoint_click,   
     (VikToolMouseMoveFunc) tool_edit_waypoint_move,
-    (VikToolMouseFunc) tool_edit_waypoint_release, (VikToolKeyFunc) NULL, GDK_CURSOR_IS_PIXMAP, &cursor_edwp },
+    (VikToolMouseFunc) tool_edit_waypoint_release, (VikToolKeyFunc) NULL, GDK_CURSOR_IS_PIXMAP, &cursor_edwp_pixbuf },
 
   { N_("Edit Trackpoint"), (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 },
+    (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 },
+    (VikToolMouseFunc) tool_show_picture_click,    NULL, NULL, (VikToolKeyFunc) NULL, GDK_CURSOR_IS_PIXMAP, &cursor_showpic_pixbuf },
 
   { N_("Magic Scissors"),  (VikToolConstructorFunc) tool_magic_scissors_create,  NULL, NULL, NULL,
-    (VikToolMouseFunc) tool_magic_scissors_click, NULL, NULL, (VikToolKeyFunc) NULL, GDK_CURSOR_IS_PIXMAP, &cursor_iscissors },
+    (VikToolMouseFunc) tool_magic_scissors_click, NULL, NULL, (VikToolKeyFunc) NULL, GDK_CURSOR_IS_PIXMAP, &cursor_iscissors_pixbuf },
 };
 enum { TOOL_CREATE_WAYPOINT=0, TOOL_CREATE_TRACK, TOOL_BEGIN_TRACK, TOOL_EDIT_WAYPOINT, TOOL_EDIT_TRACKPOINT, TOOL_SHOW_PICTURE, NUM_TOOLS };
 
@@ -392,7 +401,7 @@ enum { PARAM_TV, PARAM_WV, PARAM_DM, PARAM_DL, PARAM_DP, PARAM_DE, PARAM_EF, PAR
 
 VikLayerInterface vik_trw_layer_interface = {
   "TrackWaypoint",
-  &trwlayer_pixbuf,
+  &viktrwlayer_pixbuf,
 
   trw_layer_tools,
   sizeof(trw_layer_tools) / sizeof(VikToolInterface),
@@ -1114,7 +1123,7 @@ static void trw_layer_draw_waypoint ( const gchar *name, VikWaypoint *wp, struct
              wp->coord.north_south > dp->cn1 && wp->coord.north_south < dp->cn2 ) )
   {
     gint x, y;
-    GdkPixbuf *sym;
+    GdkPixbuf *sym = NULL;
     vik_viewport_coord_to_screen ( dp->vp, &(wp->coord), &x, &y );
 
     /* if in shrunken_cache, get that. If not, get and add to shrunken_cache */
@@ -1212,11 +1221,18 @@ static void trw_layer_draw_waypoint ( const gchar *name, VikWaypoint *wp, struct
     if ( dp->vtl->drawlabels )
     {
       /* thanks to the GPSDrive people (Fritz Ganter et al.) for hints on this part ... yah, I'm too lazy to study documentation */
+      gint label_x, label_y;
       gint width, height;
       pango_layout_set_text ( dp->vtl->wplabellayout, name, -1 );
       pango_layout_get_pixel_size ( dp->vtl->wplabellayout, &width, &height );
-      vik_viewport_draw_rectangle ( dp->vp, dp->vtl->waypoint_bg_gc, TRUE, x + dp->vtl->wp_size - 1, y-1,width+1,height-1);
-      vik_viewport_draw_layout ( dp->vp, dp->vtl->waypoint_text_gc, x + dp->vtl->wp_size, y, dp->vtl->wplabellayout );
+      label_x = x - width/2;
+      if (sym)
+        label_y = y - height - 2 - gdk_pixbuf_get_height(sym)/2;
+      else
+        label_y = y - dp->vtl->wp_size - height - 2;
+
+      vik_viewport_draw_rectangle ( dp->vp, dp->vtl->waypoint_bg_gc, TRUE, label_x - 1, label_y-1,width+2,height+2);
+      vik_viewport_draw_layout ( dp->vp, dp->vtl->waypoint_text_gc, label_x, label_y, dp->vtl->wplabellayout );
     }
   }
 }
@@ -1465,13 +1481,44 @@ static void trw_layer_find_maxmin_tracks ( const gchar *name, GList **t, struct
   }
 }
 
+static void trw_layer_find_maxmin (VikTrwLayer *vtl, struct LatLon maxmin[2])
+{
+  struct LatLon wpt_maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
+  struct LatLon trk_maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
+  
+  g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_find_maxmin_waypoints, wpt_maxmin );
+  g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_find_maxmin_tracks, trk_maxmin );
+  if ((wpt_maxmin[0].lat != 0.0 && wpt_maxmin[0].lat > trk_maxmin[0].lat) || trk_maxmin[0].lat == 0.0) {
+    maxmin[0].lat = wpt_maxmin[0].lat;
+  }
+  else {
+    maxmin[0].lat = trk_maxmin[0].lat;
+  }
+  if ((wpt_maxmin[0].lon != 0.0 && wpt_maxmin[0].lon > trk_maxmin[0].lon) || trk_maxmin[0].lon == 0.0) {
+    maxmin[0].lon = wpt_maxmin[0].lon;
+  }
+  else {
+    maxmin[0].lon = trk_maxmin[0].lon;
+  }
+  if ((wpt_maxmin[1].lat != 0.0 && wpt_maxmin[1].lat < trk_maxmin[1].lat) || trk_maxmin[1].lat == 0.0) {
+    maxmin[1].lat = wpt_maxmin[1].lat;
+  }
+  else {
+    maxmin[1].lat = trk_maxmin[1].lat;
+  }
+  if ((wpt_maxmin[1].lon != 0.0 && wpt_maxmin[1].lon < trk_maxmin[1].lon) || trk_maxmin[1].lon == 0.0) {
+    maxmin[1].lon = wpt_maxmin[1].lon;
+  }
+  else {
+    maxmin[1].lon = trk_maxmin[1].lon;
+  }
+}
 
 gboolean vik_trw_layer_find_center ( VikTrwLayer *vtl, VikCoord *dest )
 {
   /* TODO: what if there's only one waypoint @ 0,0, it will think nothing found. like I don't have more important things to worry about... */
   struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
-  g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_find_maxmin_waypoints, maxmin );
-  g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
+  trw_layer_find_maxmin (vtl, maxmin);
   if (maxmin[0].lat == 0.0 && maxmin[0].lon == 0.0 && maxmin[1].lat == 0.0 && maxmin[1].lon == 0.0)
     return FALSE;
   else
@@ -1596,7 +1643,6 @@ gboolean vik_trw_layer_new_waypoint ( VikTrwLayer *vtl, GtkWindow *w, const VikC
   gchar *name = highest_wp_number_get(vtl);
   VikWaypoint *wp = vik_waypoint_new();
   wp->coord = *def_coord;
-  wp->altitude = VIK_DEFAULT_ALTITUDE;
 
   if ( a_dialog_new_waypoint ( w, &name, wp, vik_trw_layer_get_waypoints ( vtl ), vtl->coord_mode ) )
   {
@@ -1608,6 +1654,49 @@ gboolean vik_trw_layer_new_waypoint ( VikTrwLayer *vtl, GtkWindow *w, const VikC
   return FALSE;
 }
 
+static void trw_layer_new_wikipedia_wp_viewport ( gpointer lav[2] )
+{
+  VikCoord one, two;
+  struct LatLon one_ll, two_ll;
+  struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
+
+  VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
+  VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
+  VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
+  VikViewport *vvp =  vik_window_viewport(vw);
+  vik_viewport_screen_to_coord ( vvp, 0, 0, &one);
+  vik_viewport_screen_to_coord ( vvp, vik_viewport_get_width(vvp), vik_viewport_get_height(vvp), &two);
+  vik_coord_to_latlon(&one, &one_ll);
+  vik_coord_to_latlon(&two, &two_ll);
+  if (one_ll.lat > two_ll.lat) {
+    maxmin[0].lat = one_ll.lat;
+    maxmin[1].lat = two_ll.lat;
+  }
+  else {
+    maxmin[0].lat = two_ll.lat;
+    maxmin[1].lat = one_ll.lat;
+  }
+  if (one_ll.lon > two_ll.lon) {
+    maxmin[0].lon = one_ll.lon;
+    maxmin[1].lon = two_ll.lon;
+  }
+  else {
+    maxmin[0].lon = two_ll.lon;
+    maxmin[1].lon = one_ll.lon;
+  }
+  a_geonames_wikipedia_box((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vtl, vlp, maxmin);
+}
+
+static void trw_layer_new_wikipedia_wp_layer ( gpointer lav[2] )
+{
+  VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
+  VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
+  struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
+  
+  trw_layer_find_maxmin (vtl, maxmin);
+  a_geonames_wikipedia_box((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vtl, vlp, maxmin);
+}
+
 static void trw_layer_new_wp ( gpointer lav[2] )
 {
   VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
@@ -1623,6 +1712,7 @@ void vik_trw_layer_add_menu_items ( VikTrwLayer *vtl, GtkMenu *menu, gpointer vl
   static gpointer pass_along[2];
   GtkWidget *item;
   GtkWidget *export_submenu;
+  GtkWidget *wikipedia_submenu;
   pass_along[0] = vtl;
   pass_along[1] = vlp;
 
@@ -1666,6 +1756,24 @@ void vik_trw_layer_add_menu_items ( VikTrwLayer *vtl, GtkMenu *menu, gpointer vl
   gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
   gtk_widget_show ( item );
 
+#ifdef VIK_CONFIG_GEONAMES
+  wikipedia_submenu = gtk_menu_new();
+  item = gtk_menu_item_new_with_label ( _("Add Wikipedia Waypoints") );
+  gtk_menu_shell_append(GTK_MENU_SHELL (menu), item);
+  gtk_widget_show(item);
+  gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), wikipedia_submenu);
+
+  item = gtk_menu_item_new_with_label ( _("Within layer bounds") );
+  g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wikipedia_wp_layer), pass_along );
+  gtk_menu_shell_append (GTK_MENU_SHELL (wikipedia_submenu), item);
+  gtk_widget_show ( item );
+
+  item = gtk_menu_item_new_with_label ( _("Within current view") );
+  g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wikipedia_wp_viewport), pass_along );
+  gtk_menu_shell_append (GTK_MENU_SHELL (wikipedia_submenu), item);
+  gtk_widget_show ( item );
+#endif
+
 #ifdef VIK_CONFIG_OPENSTREETMAP 
   item = gtk_menu_item_new_with_label ( _("Upload to OSM") );
   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(osm_traces_upload_cb), pass_along );
@@ -1727,9 +1835,9 @@ void vik_trw_layer_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *t )
     {
       GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
-      vik_treeview_add_sublayer_alphabetized ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), iter, name, vtl, name, VIK_TRW_LAYER_SUBLAYER_TRACK, NULL, TRUE, TRUE );
+      vik_treeview_add_sublayer_alphabetized ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), iter, name, vtl, name, VIK_TRW_LAYER_SUBLAYER_TRACK, NULL, t->visible, TRUE );
 #else
-      vik_treeview_add_sublayer ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), iter, name, vtl, name, VIK_TRW_LAYER_SUBLAYER_TRACK, NULL, TRUE, TRUE );
+      vik_treeview_add_sublayer ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), iter, name, vtl, name, VIK_TRW_LAYER_SUBLAYER_TRACK, NULL, t->visible, TRUE );
 #endif
       vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, iter );
       g_hash_table_insert ( vtl->tracks_iters, name, iter );
@@ -2019,12 +2127,30 @@ static void trw_layer_extend_track_end ( gpointer pass_along[6] )
   VikTrack *track = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
 
   vtl->current_track = track;
-  vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_CREATE_TRACK );
+  vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_CREATE_TRACK);
 
   if ( track->trackpoints )
     goto_coord ( VIK_LAYERS_PANEL(pass_along[1]), &(((VikTrackpoint *)g_list_last(track->trackpoints)->data)->coord) );
 }
 
+/**
+ * extend a track using magic scissors
+ */
+static void trw_layer_extend_track_end_ms ( gpointer pass_along[6] )
+{
+  VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
+  VikTrack *track = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
+  VikCoord last_coord = (((VikTrackpoint *)g_list_last(track->trackpoints)->data)->coord);
+
+  vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, NUM_TOOLS );
+  vtl->magic_scissors_coord =  last_coord;
+  vtl->magic_scissors_current_track = track;
+
+  if ( track->trackpoints )
+    goto_coord ( VIK_LAYERS_PANEL(pass_along[1]), &last_coord) ;
+
+}
+
 static void trw_layer_apply_dem_data ( gpointer pass_along[6] )
 {
   /* TODO: check & warn if no DEM data, or no applicable DEM data. */
@@ -2048,6 +2174,39 @@ static void trw_layer_goto_track_endpoint ( gpointer pass_along[6] )
  * merge/split by time routines 
  *************************************/
 
+/* called for each key in track hash table.
+ * If the current track has time stamp, add it to the result,
+ * except the one pointed by "exclude".
+ * set exclude to NULL if there is no exclude to check.
+ * Not that result is in reverse (for performance reason).
+ */
+typedef struct {
+  GList **result;
+  GList  *exclude;
+} twt_udata;
+static void find_tracks_with_timestamp(gpointer key, gpointer value, gpointer udata)
+{
+  twt_udata *user_data = udata;
+  VikTrackpoint *p1, *p2;
+
+  if (VIK_TRACK(value)->trackpoints == user_data->exclude) {
+    return;
+  }
+
+  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 (!p1->has_timestamp || !p2->has_timestamp) {
+      g_print("no timestamp\n");
+      return;
+    }
+
+  }
+
+  *(user_data->result) = g_list_prepend(*(user_data->result), key);
+}
+
 /* called for each key in track hash table. if original track user_data[1] is close enough
  * to the passed one, add it to list in user_data[0] 
  */
@@ -2118,6 +2277,58 @@ static gint trackpoint_compare(gconstpointer a, gconstpointer b)
   return 0;
 }
 
+static void trw_layer_merge_with_other ( gpointer pass_along[6] )
+{
+  VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
+  gchar *orig_track_name = pass_along[3];
+  GList *tracks_with_timestamp = NULL;
+  VikTrack *track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, orig_track_name );
+
+  if (track->trackpoints &&
+      !VIK_TRACKPOINT(track->trackpoints->data)->has_timestamp) {
+    a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. This track does not have timestamp"));
+    return;
+  }
+
+  if (1) {
+
+    twt_udata udata;
+    udata.result = &tracks_with_timestamp;
+    udata.exclude = track->trackpoints;
+    g_hash_table_foreach(vtl->tracks, find_tracks_with_timestamp, (gpointer)&udata);
+    tracks_with_timestamp = g_list_reverse(tracks_with_timestamp);
+  }
+
+  if (!tracks_with_timestamp) {
+    a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other track in this layer has timestamp"));
+    return;
+  }
+
+  GList *merge_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
+      vtl->tracks, tracks_with_timestamp, TRUE,
+      _("Merge with..."), _("Select track to merge with"));
+  g_list_free(tracks_with_timestamp);
+
+  if (merge_list)
+  {
+    GList *l;
+    for (l = merge_list; l != NULL; l = g_list_next(l)) {
+      VikTrack *merge_track = (VikTrack *) g_hash_table_lookup (vtl->tracks, l->data );
+      if (merge_track) {
+        track->trackpoints = g_list_concat(track->trackpoints, merge_track->trackpoints);
+        merge_track->trackpoints = NULL;
+        vik_trw_layer_delete_track(vtl, l->data);
+        track->trackpoints = g_list_sort(track->trackpoints, trackpoint_compare);
+      }
+    }
+    /* TODO: free data before free merge_list */
+    for (l = merge_list; l != NULL; l = g_list_next(l))
+      g_free(l->data);
+    g_list_free(merge_list);
+    vik_layer_emit_update( VIK_LAYER(vtl) );
+  }
+}
+
 /* merge by time routine */
 static void trw_layer_merge_by_timestamp ( gpointer pass_along[6] )
 {
@@ -2135,6 +2346,7 @@ static void trw_layer_merge_by_timestamp ( gpointer pass_along[6] )
                               _("Merge Threshold..."), 
                               _("Merge when time between tracks less than:"), 
                               &thr)) {
+    free(orig_track_name);
     return;
   }
 
@@ -2347,7 +2559,7 @@ const gchar *vik_trw_layer_sublayer_rename_request ( VikTrwLayer *l, const gchar
       return NULL;
 
     if (strcasecmp(newname, sublayer)) { /* Not just changing case */
-      if (g_hash_table_lookup( l->waypoints, newname))
+      if (g_hash_table_lookup( l->tracks, newname))
       {
         a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(l), _("Track Already Exists") );
         return NULL;
@@ -2514,6 +2726,11 @@ gboolean vik_trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *menu,
     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
     gtk_widget_show ( item );
 
+    item = gtk_menu_item_new_with_label ( _("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_widget_show ( item );
+
     item = gtk_menu_item_new_with_label ( _("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 );
@@ -2529,11 +2746,16 @@ gboolean vik_trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *menu,
     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
     gtk_widget_show ( item );
 
-    item = gtk_menu_item_new_with_label ( "Extend track end" );
+    item = gtk_menu_item_new_with_label ( _("Extend track end") );
     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end), pass_along );
     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
     gtk_widget_show ( item );
 
+    item = gtk_menu_item_new_with_label ( _("Extend using magic scissors") );
+    g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end_ms), pass_along );
+    gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
+    gtk_widget_show ( item );
+
 #ifdef VIK_CONFIG_OPENSTREETMAP
     item = gtk_menu_item_new_with_label ( _("Upload to OSM") );
     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(osm_traces_upload_track_cb), pass_along );
@@ -2543,13 +2765,13 @@ gboolean vik_trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *menu,
 
     if ( is_valid_google_route ( l, (gchar *) sublayer ) )
     {
-      item = gtk_menu_item_new_with_label ( "View Google Directions" );
+      item = gtk_menu_item_new_with_label ( _("View Google Directions") );
       g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_google_route_webpage), pass_along );
       gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
       gtk_widget_show ( item );
     }
 
-    item = gtk_menu_item_new_with_label ( "Use with filter" );
+    item = gtk_menu_item_new_with_label ( _("Use with filter") );
     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 );
@@ -2633,6 +2855,8 @@ static void trw_layer_tpwin_response ( VikTrwLayer *vtl, gint response )
 
       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));
     }
@@ -3207,7 +3431,6 @@ static gboolean tool_new_track_click ( VikTrwLayer *vtl, GdkEventButton *event,
   tp->newsegment = FALSE;
   tp->has_timestamp = FALSE;
   tp->timestamp = 0;
-  tp->altitude = VIK_DEFAULT_ALTITUDE;
   vtl->current_track->trackpoints = g_list_append ( vtl->current_track->trackpoints, tp );
 
   vtl->ct_x1 = vtl->ct_x2;
@@ -3432,7 +3655,7 @@ static gboolean tool_magic_scissors_click ( VikTrwLayer *vtl, GdkEventButton *ev
                           g_ascii_dtostr (startlon, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) start.lon),
                           g_ascii_dtostr (endlat, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) end.lat),
                           g_ascii_dtostr (endlon, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) end.lon));
-    a_babel_convert_from_url ( vtl, url, "google", NULL, NULL );
+    a_babel_convert_from_url ( vtl, url, "kml", NULL, NULL );
     g_free ( url );
 
     /* see if anything was done -- a track was added or appended to */