X-Git-Url: https://git.street.me.uk/andy/viking.git/blobdiff_plain/e13ab673e45ea48de49661f7838e75925f405514..c3092f0f56a7d416da8c506ae6d222d5d67e5379:/src/viktrwlayer.c?ds=sidebyside diff --git a/src/viktrwlayer.c b/src/viktrwlayer.c index 05dc83fe..202a8266 100644 --- a/src/viktrwlayer.c +++ b/src/viktrwlayer.c @@ -1,7 +1,10 @@ /* * viking -- GPS Data and Topo Analyzer, Explorer, and Manager * - * Copyright (C) 2003-2005, Evan Battaglia + * Copyright (C) 2003-2007, Evan Battaglia + * Copyright (C) 2005-2008, Alex Foobarian + * Copyright (C) 2007, Quy Tonthat + * Copyright (C) 2009, Hein Ragas * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -179,6 +182,7 @@ struct _VikTrwLayer { gboolean has_verified_thumbnails; GtkMenu *wp_right_click_menu; + GtkMenu *track_right_click_menu; /* menu */ VikStdLayerMenuItem menu_selection; @@ -206,9 +210,9 @@ struct DrawingParams { static void vik_trw_layer_set_menu_selection(VikTrwLayer *vtl, guint16); static guint16 vik_trw_layer_get_menu_selection(VikTrwLayer *vtl); -static void trw_layer_delete_item ( gpointer *pass_along ); -static void trw_layer_copy_item_cb( gpointer *pass_along); -static void trw_layer_cut_item_cb( gpointer *pass_along); +static void trw_layer_delete_item ( gpointer pass_along[6] ); +static void trw_layer_copy_item_cb ( gpointer pass_along[6] ); +static void trw_layer_cut_item_cb ( gpointer pass_along[6] ); 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] ); @@ -221,42 +225,68 @@ static gint calculate_velocity ( VikTrwLayer *vtl, VikTrackpoint *tp1, VikTrackp 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 ); -static void goto_coord ( VikLayersPanel *vlp, const VikCoord *coord ); -static void trw_layer_goto_track_startpoint ( gpointer pass_along[5] ); +static const gchar* trw_layer_layer_tooltip ( VikTrwLayer *vtl ); +static const gchar* trw_layer_sublayer_tooltip ( VikTrwLayer *l, gint subtype, gpointer sublayer ); + +static void goto_coord ( gpointer *vlp, gpointer vvp, gpointer vl, const VikCoord *coord ); +static void trw_layer_goto_track_startpoint ( gpointer pass_along[6] ); static void trw_layer_goto_track_endpoint ( gpointer pass_along[6] ); +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_timestamp ( gpointer pass_along[6] ); +static void trw_layer_merge_with_other ( gpointer pass_along[6] ); static void trw_layer_split_by_timestamp ( gpointer pass_along[6] ); -static void trw_layer_download_map_along_track_cb(gpointer pass_along[6]); +static void trw_layer_split_by_n_points ( 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] ); +static void trw_layer_show_picture ( gpointer pass_along[6] ); + 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_auto_view ( gpointer layer_and_vlp[2] ); +static void trw_layer_export ( gpointer layer_and_vlp[2], const gchar* title, const gchar* default_name, const gchar* trackname, guint file_type ); static void trw_layer_goto_wp ( gpointer layer_and_vlp[2] ); static void trw_layer_new_wp ( gpointer lav[2] ); +static void trw_layer_auto_waypoints_view ( gpointer lav[2] ); +static void trw_layer_auto_tracks_view ( gpointer lav[2] ); +static void trw_layer_delete_all_tracks ( gpointer lav[2] ); +static void trw_layer_delete_tracks_from_selection ( gpointer lav[2] ); +static void trw_layer_delete_all_waypoints ( gpointer lav[2] ); +static void trw_layer_delete_waypoints_from_selection ( 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] ); -static void trw_layer_goto_waypoint ( gpointer pass_along[5] ); -static void trw_layer_waypoint_gc_webpage ( gpointer pass_along[5] ); +static void trw_layer_properties_item ( gpointer pass_along[6] ); +static void trw_layer_goto_waypoint ( gpointer pass_along[6] ); +static void trw_layer_waypoint_gc_webpage ( gpointer pass_along[6] ); -static void trw_layer_realize_waypoint ( gchar *name, VikWaypoint *wp, gpointer pass_along[4] ); -static void trw_layer_realize_track ( gchar *name, VikTrack *track, gpointer pass_along[4] ); +static void trw_layer_realize_waypoint ( gchar *name, VikWaypoint *wp, gpointer pass_along[5] ); +static void trw_layer_realize_track ( gchar *name, VikTrack *track, gpointer pass_along[5] ); static void init_drawing_params ( struct DrawingParams *dp, VikViewport *vp ); static void trw_layer_marshall( VikTrwLayer *vtl, guint8 **data, gint *len ); static VikTrwLayer *trw_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp ); -static gboolean trw_layer_set_param ( VikTrwLayer *vtl, guint16 id, VikLayerParamData data, VikViewport *vp ); -static VikLayerParamData trw_layer_get_param ( VikTrwLayer *vtl, guint16 id ); +static gboolean trw_layer_set_param ( VikTrwLayer *vtl, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation ); +static VikLayerParamData trw_layer_get_param ( VikTrwLayer *vtl, guint16 id, gboolean is_file_operation ); static void trw_layer_del_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer ); +static void trw_layer_cut_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer ); + static void trw_layer_copy_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer, guint8 **item, guint *len ); static gboolean trw_layer_paste_item ( VikTrwLayer *vtl, gint subtype, guint8 *item, guint len ); static void trw_layer_free_copied_item ( gint subtype, gpointer item ); static void trw_layer_drag_drop_request ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, GtkTreeIter *src_item_iter, GtkTreePath *dest_path ); +static gboolean trw_layer_select_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t *t ); +static gboolean trw_layer_select_move ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t *t ); +static gboolean trw_layer_select_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t *t ); +static gboolean trw_layer_show_selected_viewport_menu ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp ); + +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 ); @@ -327,8 +357,8 @@ static VikToolInterface trw_layer_tools[] = { { 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_("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_pixbuf }, + { N_("Route Finder"), (VikToolConstructorFunc) tool_magic_scissors_create, NULL, NULL, NULL, + (VikToolMouseFunc) tool_magic_scissors_click, NULL, NULL, (VikToolKeyFunc) NULL, GDK_CURSOR_IS_PIXMAP, &cursor_route_finder_pixbuf }, }; enum { TOOL_CREATE_WAYPOINT=0, TOOL_CREATE_TRACK, TOOL_BEGIN_TRACK, TOOL_EDIT_WAYPOINT, TOOL_EDIT_TRACKPOINT, TOOL_SHOW_PICTURE, NUM_TOOLS }; @@ -430,6 +460,9 @@ VikLayerInterface vik_trw_layer_interface = { (VikLayerFuncSublayerRenameRequest) vik_trw_layer_sublayer_rename_request, (VikLayerFuncSublayerToggleVisible) vik_trw_layer_sublayer_toggle_visible, + (VikLayerFuncSublayerTooltip) trw_layer_sublayer_tooltip, + (VikLayerFuncLayerTooltip) trw_layer_layer_tooltip, + (VikLayerFuncLayerSelected) vik_trw_layer_selected, (VikLayerFuncMarshall) trw_layer_marshall, (VikLayerFuncUnmarshall) trw_layer_unmarshall, @@ -441,11 +474,17 @@ VikLayerInterface vik_trw_layer_interface = { (VikLayerFuncWriteFileData) a_gpspoint_write_file, (VikLayerFuncDeleteItem) trw_layer_del_item, + (VikLayerFuncCutItem) trw_layer_cut_item, (VikLayerFuncCopyItem) trw_layer_copy_item, (VikLayerFuncPasteItem) trw_layer_paste_item, (VikLayerFuncFreeCopiedItem) trw_layer_free_copied_item, (VikLayerFuncDragDropRequest) trw_layer_drag_drop_request, + + (VikLayerFuncSelectClick) trw_layer_select_click, + (VikLayerFuncSelectMove) trw_layer_select_move, + (VikLayerFuncSelectRelease) trw_layer_select_release, + (VikLayerFuncSelectedViewportMenu) trw_layer_show_selected_viewport_menu, }; /* for copy & paste (I think?) */ @@ -482,7 +521,7 @@ GType vik_trw_layer_get_type () static void trw_layer_del_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer ) { - static gpointer pass_along[5]; + static gpointer pass_along[6]; if (!sublayer) { return; } @@ -491,12 +530,31 @@ static void trw_layer_del_item ( VikTrwLayer *vtl, gint subtype, gpointer sublay pass_along[1] = NULL; pass_along[2] = GINT_TO_POINTER (subtype); pass_along[3] = sublayer; - pass_along[4] = NULL; + pass_along[4] = GINT_TO_POINTER (1); // Confirm delete request + pass_along[5] = NULL; trw_layer_delete_item ( pass_along ); } -static void trw_layer_copy_item_cb( gpointer pass_along[5]) +static void trw_layer_cut_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer ) +{ + static gpointer pass_along[6]; + if (!sublayer) { + return; + } + + pass_along[0] = vtl; + pass_along[1] = NULL; + pass_along[2] = GINT_TO_POINTER (subtype); + pass_along[3] = sublayer; + pass_along[4] = GINT_TO_POINTER (0); // No delete confirmation needed for auto delete + pass_along[5] = NULL; + + trw_layer_copy_item_cb(pass_along); + trw_layer_cut_item_cb(pass_along); +} + +static void trw_layer_copy_item_cb ( gpointer pass_along[6]) { VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]); gint subtype = GPOINTER_TO_INT (pass_along[2]); @@ -512,9 +570,10 @@ static void trw_layer_copy_item_cb( gpointer pass_along[5]) } } -static void trw_layer_cut_item_cb( gpointer pass_along[5]) +static void trw_layer_cut_item_cb ( gpointer pass_along[6]) { trw_layer_copy_item_cb(pass_along); + pass_along[4] = GINT_TO_POINTER (0); // Never need to confirm automatic delete trw_layer_delete_item(pass_along); } @@ -558,6 +617,9 @@ static gboolean trw_layer_paste_item ( VikTrwLayer *vtl, gint subtype, guint8 *i w = vik_waypoint_unmarshall(fi->data + fi->len, len - sizeof(*fi) - fi->len); vik_trw_layer_add_waypoint ( vtl, name, w ); waypoint_convert(name, w, &vtl->coord_mode); + // Consider if redraw necessary for the new item + if ( vtl->vl.visible && vtl->waypoints_visible && w->visible ) + vik_layer_emit_update ( VIK_LAYER(vtl) ); return TRUE; } if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK && fi ) @@ -568,6 +630,9 @@ static gboolean trw_layer_paste_item ( VikTrwLayer *vtl, gint subtype, guint8 *i t = vik_track_unmarshall(fi->data + fi->len, len - sizeof(*fi) - fi->len); vik_trw_layer_add_track ( vtl, name, t ); track_convert(name, t, &vtl->coord_mode); + // Consider if redraw necessary for the new item + if ( vtl->vl.visible && vtl->tracks_visible && t->visible ) + vik_layer_emit_update ( VIK_LAYER(vtl) ); return TRUE; } return FALSE; @@ -580,7 +645,7 @@ static void trw_layer_free_copied_item ( gint subtype, gpointer item ) } } -static gboolean trw_layer_set_param ( VikTrwLayer *vtl, guint16 id, VikLayerParamData data, VikViewport *vp ) +static gboolean trw_layer_set_param ( VikTrwLayer *vtl, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation ) { switch ( id ) { @@ -609,8 +674,38 @@ static gboolean trw_layer_set_param ( VikTrwLayer *vtl, guint16 id, VikLayerPara trw_layer_new_track_gcs ( vtl, vp ); } break; - case PARAM_VMIN: vtl->velocity_min = data.d; break; - case PARAM_VMAX: vtl->velocity_max = data.d; 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_DLA: vtl->drawlabels = data.b; break; case PARAM_DI: vtl->drawimages = data.b; break; @@ -638,7 +733,7 @@ static gboolean trw_layer_set_param ( VikTrwLayer *vtl, guint16 id, VikLayerPara return TRUE; } -static VikLayerParamData trw_layer_get_param ( VikTrwLayer *vtl, guint16 id ) +static VikLayerParamData trw_layer_get_param ( VikTrwLayer *vtl, guint16 id, gboolean is_file_operation ) { VikLayerParamData rv; switch ( id ) @@ -654,8 +749,38 @@ static VikLayerParamData trw_layer_get_param ( VikTrwLayer *vtl, guint16 id ) 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: rv.d = vtl->velocity_min; break; - case PARAM_VMAX: rv.d = vtl->velocity_max; 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; @@ -792,6 +917,7 @@ VikTrwLayer *vik_trw_layer_new ( gint drawmode ) rv->drawlines = TRUE; rv->wplabellayout = NULL; rv->wp_right_click_menu = NULL; + rv->track_right_click_menu = NULL; rv->waypoint_gc = NULL; rv->waypoint_text_gc = NULL; rv->waypoint_bg_gc = NULL; @@ -839,7 +965,10 @@ void vik_trw_layer_free ( VikTrwLayer *trwlayer ) trw_layer_free_track_gcs ( trwlayer ); if ( trwlayer->wp_right_click_menu ) - gtk_object_sink ( GTK_OBJECT(trwlayer->wp_right_click_menu) ); + g_object_ref_sink ( G_OBJECT(trwlayer->wp_right_click_menu) ); + + if ( trwlayer->track_right_click_menu ) + gtk_object_sink ( GTK_OBJECT(trwlayer->track_right_click_menu) ); if ( trwlayer->wplabellayout != NULL) g_object_unref ( G_OBJECT ( trwlayer->wplabellayout ) ); @@ -968,13 +1097,34 @@ static void trw_layer_draw_track ( const gchar *name, VikTrack *track, struct Dr drawstops = dp->vtl->drawstops; } - if ( dp->vtl->drawmode == DRAWMODE_ALL_BLACK ) - dp->track_gc_iter = VIK_TRW_LAYER_TRACK_GC_BLACK; - + /* Current track - used for creation */ if ( track == dp->vtl->current_track ) main_gc = dp->vtl->current_track_gc; - else - main_gc = g_array_index(dp->vtl->track_gc, GdkGC *, dp->track_gc_iter); + else { + if ( vik_viewport_get_draw_highlight ( dp->vp ) ) { + /* Draw all tracks of the layer in special colour */ + /* if track is member of selected layer or is the current selected track + then draw in the highlight colour. + NB this supercedes the drawmode */ + if ( dp->vtl && ( ( dp->vtl == vik_window_get_selected_trw_layer ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ) || + ( 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); + } + else { + if ( dp->vtl->drawmode == DRAWMODE_ALL_BLACK ) + dp->track_gc_iter = VIK_TRW_LAYER_TRACK_GC_BLACK; + + main_gc = g_array_index(dp->vtl->track_gc, GdkGC *, dp->track_gc_iter); + } + } + else { + if ( dp->vtl->drawmode == DRAWMODE_ALL_BLACK ) + dp->track_gc_iter = VIK_TRW_LAYER_TRACK_GC_BLACK; + + main_gc = g_array_index(dp->vtl->track_gc, GdkGC *, dp->track_gc_iter); + } + } if (list) { int x, y, oldx, oldy; @@ -1011,8 +1161,6 @@ static void trw_layer_draw_track ( const gchar *name, VikTrack *track, struct Dr if ( list->next ) { vik_viewport_draw_rectangle ( dp->vp, main_gc, TRUE, x-tp_size, y-tp_size, 2*tp_size, 2*tp_size ); - vik_viewport_draw_rectangle ( dp->vp, main_gc, TRUE, x-tp_size, y-tp_size, 2*tp_size, 2*tp_size ); - /* stops */ if ( drawstops && 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 ); @@ -1029,8 +1177,10 @@ static void trw_layer_draw_track ( const gchar *name, VikTrack *track, struct Dr 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 ) + 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 ); @@ -1077,8 +1227,10 @@ 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 ) + 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 ( drawing_white_background ) vik_viewport_draw_line ( dp->vp, dp->vtl->track_bg_gc, oldx, oldy, x, y); @@ -1188,6 +1340,18 @@ static void trw_layer_draw_waypoint ( const gchar *name, VikWaypoint *wp, struct if ( x+(w/2) > 0 && y+(h/2) > 0 && x-(w/2) < dp->width && y-(h/2) < dp->height ) /* always draw within boundaries */ { + if ( vik_viewport_get_draw_highlight ( dp->vp ) ) { + if ( dp->vtl == vik_window_get_selected_trw_layer ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) || + dp->vtl->waypoints == vik_window_get_selected_waypoints ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) || + wp == vik_window_get_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ) { + // Highlighted - so draw a little border around the chosen one + // single line seems a little weak so draw 2 of them + vik_viewport_draw_rectangle (dp->vp, vik_viewport_get_gc_highlight (dp->vp), FALSE, + x - (w/2) - 1, y - (h/2) - 1, w + 2, h + 2 ); + vik_viewport_draw_rectangle (dp->vp, vik_viewport_get_gc_highlight (dp->vp), FALSE, + 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 @@ -1233,7 +1397,18 @@ static void trw_layer_draw_waypoint ( const gchar *name, VikWaypoint *wp, struct 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); + /* if highlight mode on, then draw background text in highlight colour */ + if ( vik_viewport_get_draw_highlight ( dp->vp ) ) { + if ( dp->vtl == vik_window_get_selected_trw_layer ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) || + dp->vtl->waypoints == vik_window_get_selected_waypoints ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) || + wp == vik_window_get_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ) + vik_viewport_draw_rectangle ( dp->vp, vik_viewport_get_gc_highlight (dp->vp), TRUE, label_x - 1, label_y-1,width+2,height+2); + else + vik_viewport_draw_rectangle ( dp->vp, dp->vtl->waypoint_bg_gc, TRUE, label_x - 1, label_y-1,width+2,height+2); + } + else { + 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 ); } } @@ -1308,7 +1483,7 @@ static void trw_layer_new_track_gcs ( VikTrwLayer *vtl, VikViewport *vp ) gc[9] = vik_viewport_new_gc ( vp, "#96059f", width ); gc[10] = vik_viewport_new_gc ( vp, "#f22ef2", width ); - gc[11] = vik_viewport_new_gc ( vp, "#ff0000", width ); /* above range */ + gc[11] = vik_viewport_new_gc ( vp, "#874200", width ); /* above range */ gc[12] = vik_viewport_new_gc ( vp, "#000000", width ); /* black / no speed data */ @@ -1348,14 +1523,14 @@ VikTrwLayer *vik_trw_layer_create ( VikViewport *vp ) return rv; } -static void trw_layer_realize_track ( gchar *name, VikTrack *track, gpointer pass_along[4] ) +static void trw_layer_realize_track ( gchar *name, VikTrack *track, gpointer pass_along[5] ) { GtkTreeIter *new_iter = g_malloc(sizeof(GtkTreeIter)); #ifdef VIK_CONFIG_ALPHABETIZED_TRW vik_treeview_add_sublayer_alphabetized ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[0], (GtkTreeIter *) pass_along[1], name, pass_along[2], name, GPOINTER_TO_INT (pass_along[4]), NULL, TRUE, TRUE ); #else - vik_treeview_add_sublayer ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[0], (GtkTreeIter *) pass_along[1], name, pass_along[2], name, (gint) pass_along[4], NULL, TRUE, TRUE ); + vik_treeview_add_sublayer ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[0], (GtkTreeIter *) pass_along[1], name, pass_along[2], name, GPOINTER_TO_INT (pass_along[4]), NULL, TRUE, TRUE ); #endif *new_iter = *((GtkTreeIter *) pass_along[1]); @@ -1365,13 +1540,13 @@ static void trw_layer_realize_track ( gchar *name, VikTrack *track, gpointer pas vik_treeview_item_set_visible ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[1], FALSE ); } -static void trw_layer_realize_waypoint ( gchar *name, VikWaypoint *wp, gpointer pass_along[4] ) +static void trw_layer_realize_waypoint ( gchar *name, VikWaypoint *wp, gpointer pass_along[5] ) { GtkTreeIter *new_iter = g_malloc(sizeof(GtkTreeIter)); #ifdef VIK_CONFIG_ALPHABETIZED_TRW vik_treeview_add_sublayer_alphabetized ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[0], (GtkTreeIter *) pass_along[1], name, pass_along[2], name, GPOINTER_TO_INT (pass_along[4]), NULL, TRUE, TRUE ); #else - vik_treeview_add_sublayer ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[0], (GtkTreeIter *) pass_along[1], name, pass_along[2], name, (gint) pass_along[4], NULL, TRUE, TRUE ); + vik_treeview_add_sublayer ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[0], (GtkTreeIter *) pass_along[1], name, pass_along[2], name, GPOINTER_TO_INT (pass_along[4]), NULL, TRUE, TRUE ); #endif *new_iter = *((GtkTreeIter *) pass_along[1]); @@ -1439,6 +1614,258 @@ gboolean vik_trw_layer_sublayer_toggle_visible ( VikTrwLayer *l, gint subtype, g return TRUE; } +// Structure to hold multiple track information for a layer +typedef struct { + gdouble length; + time_t start_time; + time_t end_time; + gint duration; +} tooltip_tracks; + +/* + * 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 ) +{ + tt->length = tt->length + vik_track_get_length (tr); + + // Ensure times are available + if ( tr->trackpoints && + VIK_TRACKPOINT(tr->trackpoints->data)->has_timestamp && + VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->has_timestamp ) { + + time_t t1, t2; + t1 = VIK_TRACKPOINT(tr->trackpoints->data)->timestamp; + t2 = VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp; + + // Assume never actually have a track with a time of 0 (1st Jan 1970) + // Hence initialize to the first 'proper' value + if ( tt->start_time == 0 ) + tt->start_time = t1; + if ( tt->end_time == 0 ) + tt->end_time = t2; + + // Update find the earliest / last times + if ( t1 < tt->start_time ) + tt->start_time = t1; + if ( t2 > tt->end_time ) + tt->end_time = t2; + + // Keep track of total time + // there maybe gaps within a track (eg segments) + // but this should be generally good enough for a simple indicator + tt->duration = tt->duration + (int)(t2-t1); + } +} + +/* + * Generate tooltip text for the layer. + * This is relatively complicated as it considers information for + * no tracks, a single track or multiple tracks + * (which may or may not have timing information) + */ +static const gchar* trw_layer_layer_tooltip ( VikTrwLayer *vtl ) +{ + gchar tbuf1[32]; + gchar tbuf2[64]; + gchar tbuf3[64]; + gchar tbuf4[10]; + tbuf1[0] = '\0'; + tbuf2[0] = '\0'; + tbuf3[0] = '\0'; + tbuf4[0] = '\0'; + + static gchar tmp_buf[128]; + tmp_buf[0] = '\0'; + + // For compact date format I'm using '%x' [The preferred date representation for the current locale without the time.] + + // Safety check - I think these should always be valid + if ( vtl->tracks && vtl->waypoints ) { + tooltip_tracks tt = { 0.0, 0, 0 }; + g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_tooltip, &tt ); + + GDate* gdate_start = g_date_new (); + g_date_set_time_t (gdate_start, tt.start_time); + + GDate* gdate_end = g_date_new (); + g_date_set_time_t (gdate_end, tt.end_time); + + if ( g_date_compare (gdate_start, gdate_end) ) { + // Dates differ so print range on separate line + g_date_strftime (tbuf1, sizeof(tbuf1), "%x", gdate_start); + g_date_strftime (tbuf2, sizeof(tbuf2), "%x", gdate_end); + g_snprintf (tbuf3, sizeof(tbuf3), "%s to %s\n", tbuf1, tbuf2); + } + else { + // Same date so just show it and keep rest of text on the same line - provided it's a valid time! + if ( tt.start_time != 0 ) + g_date_strftime (tbuf3, sizeof(tbuf3), "%x: ", gdate_start); + } + + tbuf2[0] = '\0'; + if ( tt.length > 0.0 ) { + gdouble len_in_units; + + // Setup info dependent on distance units + if ( a_vik_get_units_distance() == VIK_UNITS_DISTANCE_MILES ) { + g_snprintf (tbuf4, sizeof(tbuf4), "miles"); + len_in_units = VIK_METERS_TO_MILES(tt.length); + } + else { + g_snprintf (tbuf4, sizeof(tbuf4), "kms"); + len_in_units = tt.length/1000.0; + } + + // 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 (tbuf2, sizeof(tbuf2), + _("\n%sTotal Length %.1f %s%s"), + tbuf3, len_in_units, tbuf4, tbuf1); + } + + // Put together all the elements to form compact tooltip text + g_snprintf (tmp_buf, sizeof(tmp_buf), + _("Tracks: %d - Waypoints: %d%s"), + g_hash_table_size (vtl->tracks), g_hash_table_size (vtl->waypoints), tbuf2); + + g_date_free (gdate_start); + g_date_free (gdate_end); + + } + + return tmp_buf; +} + +static const gchar* trw_layer_sublayer_tooltip ( VikTrwLayer *l, gint subtype, gpointer sublayer ) +{ + switch ( subtype ) + { + case VIK_TRW_LAYER_SUBLAYER_TRACKS: return NULL; + case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS: return NULL; + case VIK_TRW_LAYER_SUBLAYER_TRACK: + { + VikTrack *tr = g_hash_table_lookup ( l->tracks, sublayer ); + if ( tr ) { + // Could be a better way of handling strings - but this works... + gchar time_buf1[20]; + gchar time_buf2[20]; + time_buf1[0] = '\0'; + time_buf2[0] = '\0'; + static gchar tmp_buf[100]; + // Compact info: Short date eg (11/20/99), duration and length + // Hopefully these are the things that are most useful and so promoted into the tooltip + if ( tr->trackpoints && VIK_TRACKPOINT(tr->trackpoints->data)->has_timestamp ) { + // %x The preferred date representation for the current locale without the time. + strftime (time_buf1, sizeof(time_buf1), "%x: ", gmtime(&(VIK_TRACKPOINT(tr->trackpoints->data)->timestamp))); + if ( VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->has_timestamp ) { + gint dur = ( (VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp) - (VIK_TRACKPOINT(tr->trackpoints->data)->timestamp) ); + if ( dur > 0 ) + g_snprintf ( time_buf2, sizeof(time_buf2), _("- %d:%02d hrs:mins"), (int)round(dur/3600), (int)round((dur/60)%60) ); + } + } + // Get length and consider the appropriate distance units + gdouble tr_len = vik_track_get_length(tr); + vik_units_distance_t dist_units = a_vik_get_units_distance (); + switch (dist_units) { + case VIK_UNITS_DISTANCE_KILOMETRES: + g_snprintf (tmp_buf, sizeof(tmp_buf), _("%s%.1f km %s"), time_buf1, tr_len/1000.0, time_buf2); + break; + case VIK_UNITS_DISTANCE_MILES: + g_snprintf (tmp_buf, sizeof(tmp_buf), _("%s%.1f miles %s"), time_buf1, VIK_METERS_TO_MILES(tr_len), time_buf2); + break; + default: + break; + } + return tmp_buf; + } + } + break; + case VIK_TRW_LAYER_SUBLAYER_WAYPOINT: + { + VikWaypoint *w = g_hash_table_lookup ( l->waypoints, sublayer ); + // NB It's OK to return NULL + return w->comment; + } + break; + default: break; + } + return NULL; +} + +/** + * General layer selection function, find out which bit is selected and take appropriate action + */ +gboolean vik_trw_layer_selected ( VikTrwLayer *l, gint subtype, gpointer sublayer, gint type, gpointer vlp ) +{ + // Reset + l->current_wp = NULL; + l->current_wp_name = NULL; + trw_layer_cancel_current_tp ( l, FALSE ); + + switch ( type ) + { + case VIK_TREEVIEW_TYPE_LAYER: + { + vik_window_set_selected_trw_layer ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l ); + /* Mark for redraw */ + return TRUE; + } + break; + + case VIK_TREEVIEW_TYPE_SUBLAYER: + { + switch ( subtype ) + { + case VIK_TRW_LAYER_SUBLAYER_TRACKS: + { + vik_window_set_selected_tracks ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->tracks, l ); + /* Mark for redraw */ + return TRUE; + } + break; + case VIK_TRW_LAYER_SUBLAYER_TRACK: + { + vik_window_set_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), g_hash_table_lookup ( l->tracks, sublayer ), l, sublayer ); + /* Mark for redraw */ + return TRUE; + } + break; + case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS: + { + vik_window_set_selected_waypoints ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->waypoints, l ); + /* Mark for redraw */ + return TRUE; + } + break; + case VIK_TRW_LAYER_SUBLAYER_WAYPOINT: + { + vik_window_set_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), g_hash_table_lookup ( l->waypoints, sublayer ), l, sublayer ); + /* Mark for redraw */ + return TRUE; + } + break; + default: + { + return vik_window_clear_highlight ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l) ); + } + break; + } + return FALSE; + } + break; + + default: + return vik_window_clear_highlight ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l) ); + break; + } +} + GHashTable *vik_trw_layer_get_tracks ( VikTrwLayer *l ) { return l->tracks; @@ -1535,23 +1962,94 @@ static void trw_layer_centerize ( gpointer layer_and_vlp[2] ) { VikCoord coord; if ( vik_trw_layer_find_center ( VIK_TRW_LAYER(layer_and_vlp[0]), &coord ) ) - goto_coord ( VIK_LAYERS_PANEL(layer_and_vlp[1]), &coord ); + goto_coord ( layer_and_vlp[1], NULL, NULL, &coord ); + else + a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("This layer has no waypoints or trackpoints.") ); +} + +static 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 ); + + /* 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 ); + } +} + +gboolean vik_trw_layer_auto_set_view ( VikTrwLayer *vtl, VikViewport *vvp ) +{ + /* TODO: what if there's only one waypoint @ 0,0, it will think nothing found. */ + struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} }; + 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 { + trw_layer_zoom_to_show_latlons ( vtl, vvp, maxmin ); + return TRUE; + } +} + +static void trw_layer_auto_view ( gpointer layer_and_vlp[2] ) +{ + if ( vik_trw_layer_auto_set_view ( VIK_TRW_LAYER(layer_and_vlp[0]), vik_layers_panel_get_viewport (VIK_LAYERS_PANEL(layer_and_vlp[1])) ) ) { + vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(layer_and_vlp[1]) ); + } else a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("This layer has no waypoints or trackpoints.") ); } -static void trw_layer_export ( gpointer layer_and_vlp[2], guint file_type ) +static void trw_layer_export ( gpointer layer_and_vlp[2], const gchar *title, const gchar* default_name, const gchar* trackname, guint file_type ) { GtkWidget *file_selector; const gchar *fn; gboolean failed = FALSE; - file_selector = gtk_file_chooser_dialog_new (_("Export Layer"), - NULL, - GTK_FILE_CHOOSER_ACTION_SAVE, - GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, - GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT, - NULL); - gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER(file_selector), vik_layer_get_name(VIK_LAYER(layer_and_vlp[0]))); + file_selector = gtk_file_chooser_dialog_new (title, + NULL, + GTK_FILE_CHOOSER_ACTION_SAVE, + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT, + NULL); + gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER(file_selector), default_name); while ( gtk_dialog_run ( GTK_DIALOG(file_selector) ) == GTK_RESPONSE_ACCEPT ) { @@ -1559,15 +2057,15 @@ static void trw_layer_export ( gpointer layer_and_vlp[2], guint file_type ) if ( g_file_test ( fn, G_FILE_TEST_EXISTS ) == FALSE ) { gtk_widget_hide ( file_selector ); - failed = ! a_file_export ( VIK_TRW_LAYER(layer_and_vlp[0]), fn, file_type ); + failed = ! a_file_export ( VIK_TRW_LAYER(layer_and_vlp[0]), fn, file_type, trackname); break; } else { - if ( a_dialog_overwrite ( GTK_WINDOW(file_selector), _("The file \"%s\" exists, do you wish to overwrite it?"), a_file_basename ( fn ) ) ) + if ( a_dialog_yes_or_no ( GTK_WINDOW(file_selector), _("The file \"%s\" exists, do you wish to overwrite it?"), a_file_basename ( fn ) ) ) { gtk_widget_hide ( file_selector ); - failed = ! a_file_export ( VIK_TRW_LAYER(layer_and_vlp[0]), fn, file_type ); + failed = ! a_file_export ( VIK_TRW_LAYER(layer_and_vlp[0]), fn, file_type, trackname); break; } } @@ -1579,17 +2077,45 @@ static void trw_layer_export ( gpointer layer_and_vlp[2], guint file_type ) static void trw_layer_export_gpspoint ( gpointer layer_and_vlp[2] ) { - trw_layer_export ( layer_and_vlp, FILE_TYPE_GPSPOINT ); + trw_layer_export ( layer_and_vlp, _("Export Layer"), vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])), NULL, FILE_TYPE_GPSPOINT ); } static void trw_layer_export_gpsmapper ( gpointer layer_and_vlp[2] ) { - trw_layer_export ( layer_and_vlp, FILE_TYPE_GPSMAPPER ); + trw_layer_export ( layer_and_vlp, _("Export Layer"), vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])), NULL, FILE_TYPE_GPSMAPPER ); } static void trw_layer_export_gpx ( gpointer layer_and_vlp[2] ) { - trw_layer_export ( layer_and_vlp, FILE_TYPE_GPX ); + trw_layer_export ( layer_and_vlp, _("Export Layer"), vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])), NULL, FILE_TYPE_GPX ); +} + +static void trw_layer_export_kml ( gpointer layer_and_vlp[2] ) +{ + /* Auto append '.kml' to the name (providing it's not already there) for the default filename */ + gchar *auto_save_name = g_strdup ( vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])) ); + if ( ! check_file_ext ( auto_save_name, ".kml" ) ) + auto_save_name = g_strconcat ( auto_save_name, ".kml", NULL ); + + trw_layer_export ( layer_and_vlp, _("Export Layer"), auto_save_name, NULL, FILE_TYPE_KML ); + + g_free ( auto_save_name ); +} + +static void trw_layer_export_gpx_track ( gpointer pass_along[6] ) +{ + gpointer layer_and_vlp[2]; + layer_and_vlp[0] = pass_along[0]; + layer_and_vlp[1] = pass_along[1]; + + /* Auto append '.gpx' to track name (providing it's not already there) for the default filename */ + gchar *auto_save_name = g_strdup ( pass_along[3] ); + if ( ! check_file_ext ( auto_save_name, ".gpx" ) ) + auto_save_name = g_strconcat ( auto_save_name, ".gpx", NULL ); + + trw_layer_export ( layer_and_vlp, _("Export Track as GPX"), auto_save_name, pass_along[3], FILE_TYPE_GPX ); + + g_free ( auto_save_name ); } static void trw_layer_goto_wp ( gpointer layer_and_vlp[2] ) @@ -1613,6 +2139,8 @@ static void trw_layer_goto_wp ( gpointer layer_and_vlp[2] ) gtk_widget_show_all ( label ); gtk_widget_show_all ( entry ); + gtk_dialog_set_default_response ( GTK_DIALOG(dia), GTK_RESPONSE_ACCEPT ); + while ( gtk_dialog_run ( GTK_DIALOG(dia) ) == GTK_RESPONSE_ACCEPT ) { VikWaypoint *wp; @@ -1630,7 +2158,7 @@ static void trw_layer_goto_wp ( gpointer layer_and_vlp[2] ) { vik_viewport_set_center_coord ( vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(layer_and_vlp[1])), &(wp->coord) ); vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(layer_and_vlp[1]) ); - vik_treeview_select_iter ( VIK_LAYER(layer_and_vlp[0])->vt, g_hash_table_lookup ( VIK_TRW_LAYER(layer_and_vlp[0])->waypoints_iters, upname ) ); + vik_treeview_select_iter ( VIK_LAYER(layer_and_vlp[0])->vt, g_hash_table_lookup ( VIK_TRW_LAYER(layer_and_vlp[0])->waypoints_iters, upname ), TRUE ); break; } @@ -1642,16 +2170,20 @@ static void trw_layer_goto_wp ( gpointer layer_and_vlp[2] ) gboolean vik_trw_layer_new_waypoint ( VikTrwLayer *vtl, GtkWindow *w, const VikCoord *def_coord ) { - gchar *name = highest_wp_number_get(vtl); + gchar *default_name = highest_wp_number_get(vtl); VikWaypoint *wp = vik_waypoint_new(); + gchar *returned_name; + gboolean updated; wp->coord = *def_coord; - if ( a_dialog_new_waypoint ( w, &name, wp, vik_trw_layer_get_waypoints ( vtl ), vtl->coord_mode ) ) + if ( ( returned_name = a_dialog_waypoint ( w, default_name, wp, vik_trw_layer_get_waypoints ( vtl ), vtl->coord_mode, TRUE, &updated ) ) ) { wp->visible = TRUE; - vik_trw_layer_add_waypoint ( vtl, name, wp ); + vik_trw_layer_add_waypoint ( vtl, returned_name, wp ); + g_free (default_name); return TRUE; } + g_free (default_name); vik_waypoint_free(wp); return FALSE; } @@ -1709,6 +2241,46 @@ static void trw_layer_new_wp ( gpointer lav[2] ) vik_layers_panel_emit_update ( vlp ); } +static void trw_layer_auto_tracks_view ( gpointer lav[2] ) +{ + VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]); + VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]); + + if ( g_hash_table_size (vtl->tracks) > 0 ) { + struct LatLon maxmin[2] = { {0,0}, {0,0} }; + g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_find_maxmin_tracks, maxmin ); + trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin ); + vik_layers_panel_emit_update ( vlp ); + } +} + +static void trw_layer_single_waypoint_jump ( const gchar *name, const VikWaypoint *wp, gpointer vvp ) +{ + /* NB do not care if wp is visible or not */ + vik_viewport_set_center_coord ( VIK_VIEWPORT(vvp), &(wp->coord) ); +} + +static void trw_layer_auto_waypoints_view ( gpointer lav[2] ) +{ + VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]); + VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]); + + /* Only 1 waypoint - jump straight to it */ + if ( g_hash_table_size (vtl->waypoints) == 1 ) { + VikViewport *vvp = vik_layers_panel_get_viewport (vlp); + g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_single_waypoint_jump, (gpointer) vvp ); + } + /* If at least 2 waypoints - find center and then zoom to fit */ + else if ( g_hash_table_size (vtl->waypoints) > 1 ) + { + struct LatLon maxmin[2] = { {0,0}, {0,0} }; + g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_find_maxmin_waypoints, maxmin ); + trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin ); + } + + vik_layers_panel_emit_update ( vlp ); +} + void vik_trw_layer_add_menu_items ( VikTrwLayer *vtl, GtkMenu *menu, gpointer vlp ) { static gpointer pass_along[2]; @@ -1722,76 +2294,122 @@ 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 ); - item = gtk_menu_item_new_with_label ( _("Goto Center of Layer") ); + item = gtk_menu_item_new_with_mnemonic ( _("_View Layer") ); + g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_view), pass_along ); + gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); + gtk_widget_show ( item ); + + item = gtk_menu_item_new_with_mnemonic ( _("View All Trac_ks") ); + g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_tracks_view), pass_along ); + gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); + gtk_widget_show ( item ); + + item = gtk_menu_item_new_with_mnemonic ( _("V_iew All Waypoints") ); + g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_waypoints_view), pass_along ); + gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); + gtk_widget_show ( item ); + + item = gtk_menu_item_new_with_mnemonic ( _("_Goto Center of Layer") ); g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_centerize), pass_along ); gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); gtk_widget_show ( item ); - item = gtk_menu_item_new_with_label ( _("Goto Waypoint") ); + item = gtk_menu_item_new_with_mnemonic ( _("Goto _Waypoint...") ); g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_wp), pass_along ); gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); gtk_widget_show ( item ); export_submenu = gtk_menu_new (); - item = gtk_menu_item_new_with_label ( _("Export layer") ); + item = gtk_menu_item_new_with_mnemonic ( _("_Export Layer") ); gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); gtk_widget_show ( item ); gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), export_submenu ); - item = gtk_menu_item_new_with_label ( _("Export as GPSPoint") ); + item = gtk_menu_item_new_with_mnemonic ( _("Export as GPS_Point...") ); g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpspoint), pass_along ); gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item); gtk_widget_show ( item ); - item = gtk_menu_item_new_with_label ( _("Export as GPSMapper") ); + item = gtk_menu_item_new_with_mnemonic ( _("Export as GPS_Mapper...") ); g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpsmapper), pass_along ); gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item); gtk_widget_show ( item ); - item = gtk_menu_item_new_with_label ( _("Export as GPX") ); + item = gtk_menu_item_new_with_mnemonic ( _("Export as _GPX...") ); g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpx), pass_along ); gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item); gtk_widget_show ( item ); - item = gtk_menu_item_new_with_label ( _("New Waypoint") ); + 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 ); + + item = gtk_menu_item_new_with_mnemonic ( _("_New Waypoint...") ); g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along ); 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") ); + item = gtk_menu_item_new_with_mnemonic ( _("_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") ); + item = gtk_menu_item_new_with_mnemonic ( _("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") ); + item = gtk_menu_item_new_with_mnemonic ( _("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") ); + item = gtk_menu_item_new_with_mnemonic ( _("Upload to _OSM...") ); g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(osm_traces_upload_cb), pass_along ); gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); gtk_widget_show ( item ); #endif + GtkWidget *delete_submenu = gtk_menu_new (); + item = gtk_menu_item_new_with_mnemonic ( _("De_lete") ); + 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 All _Tracks") ); + g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_tracks), pass_along ); + gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item ); + gtk_widget_show ( item ); + + item = gtk_menu_item_new_with_mnemonic ( _("Delete Tracks _From Selection...") ); + g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_tracks_from_selection), pass_along ); + gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item ); + gtk_widget_show ( item ); + + item = gtk_menu_item_new_with_mnemonic ( _("Delete All _Waypoints") ); + g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_waypoints), pass_along ); + gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item ); + gtk_widget_show ( item ); + + item = gtk_menu_item_new_with_mnemonic ( _("Delete Waypoints From _Selection...") ); + g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_waypoints_from_selection), pass_along ); + gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item ); + gtk_widget_show ( item ); + item = a_acquire_trwlayer_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vlp, - vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)), vtl ); + vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)), vtl ); if ( item ) { gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); gtk_widget_show ( item ); } item = a_acquire_trwlayer_track_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vlp, - vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)), vtl ); + vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)), vtl ); if ( item ) { gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); gtk_widget_show ( item ); @@ -1808,18 +2426,17 @@ void vik_trw_layer_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp else { GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter)); + // Visibility column always needed for waypoints #ifdef VIK_CONFIG_ALPHABETIZED_TRW vik_treeview_add_sublayer_alphabetized ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), iter, name, vtl, name, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, NULL, TRUE, TRUE ); #else vik_treeview_add_sublayer ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), iter, name, vtl, name, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, NULL, TRUE, TRUE ); #endif - vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, iter ); + // Actual setting of visibility dependent on the waypoint + vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, wp->visible ); g_hash_table_insert ( vtl->waypoints_iters, name, iter ); - wp->visible = TRUE; } } - else - wp->visible = TRUE; highest_wp_number_add_wp(vtl, name); g_hash_table_insert ( vtl->waypoints, name, wp ); @@ -1836,18 +2453,17 @@ void vik_trw_layer_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *t ) else { GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter)); + // Visibility column always needed for tracks #ifdef VIK_CONFIG_ALPHABETIZED_TRW - vik_treeview_add_sublayer_alphabetized ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), iter, name, vtl, name, VIK_TRW_LAYER_SUBLAYER_TRACK, NULL, t->visible, TRUE ); + vik_treeview_add_sublayer_alphabetized ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), iter, name, vtl, name, VIK_TRW_LAYER_SUBLAYER_TRACK, NULL, TRUE, TRUE ); #else - vik_treeview_add_sublayer ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), iter, name, vtl, name, VIK_TRW_LAYER_SUBLAYER_TRACK, NULL, t->visible, TRUE ); + vik_treeview_add_sublayer ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), iter, name, vtl, name, VIK_TRW_LAYER_SUBLAYER_TRACK, NULL, TRUE, TRUE ); #endif - vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, iter ); + // Actual setting of visibility dependent on the track + vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, t->visible ); g_hash_table_insert ( vtl->tracks_iters, name, iter ); - /* t->visible = TRUE; */ } } - else - ; /* t->visible = TRUE; // this is now used by file input functions */ g_hash_table_insert ( vtl->tracks, name, t ); @@ -1957,7 +2573,6 @@ static void trw_layer_drag_drop_request ( VikTrwLayer *vtl_src, VikTrwLayer *vtl } } - gboolean vik_trw_layer_delete_track ( VikTrwLayer *vtl, const gchar *trk_name ) { VikTrack *t = g_hash_table_lookup ( vtl->tracks, trk_name ); @@ -1966,11 +2581,11 @@ gboolean vik_trw_layer_delete_track ( VikTrwLayer *vtl, const gchar *trk_name ) { GtkTreeIter *it; was_visible = t->visible; - if ( t == vtl->current_track ) + if ( t == vtl->current_track ) { vtl->current_track = NULL; + } if ( t == vtl->magic_scissors_current_track ) vtl->magic_scissors_current_track = NULL; - /* could be current_tp, so we have to check */ trw_layer_cancel_tps_of_track ( vtl, trk_name ); @@ -2050,16 +2665,49 @@ void vik_trw_layer_delete_all_waypoints ( VikTrwLayer *vtl ) vik_layer_emit_update ( VIK_LAYER(vtl) ); } -static void trw_layer_delete_item ( gpointer pass_along[5] ) +static void trw_layer_delete_all_tracks ( gpointer lav[2] ) +{ + VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]); + // Get confirmation from the user + if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl), + _("Are you sure you want to delete all tracks in %s?"), + vik_layer_get_name ( VIK_LAYER(vtl) ) ) ) + vik_trw_layer_delete_all_tracks (vtl); +} + +static void trw_layer_delete_all_waypoints ( gpointer lav[2] ) +{ + VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]); + // Get confirmation from the user + if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl), + _("Are you sure you want to delete all waypoints in %s?"), + vik_layer_get_name ( VIK_LAYER(vtl) ) ) ) + vik_trw_layer_delete_all_waypoints (vtl); +} + +static void trw_layer_delete_item ( gpointer pass_along[6] ) { VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]); gboolean was_visible = FALSE; if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) { + if ( GPOINTER_TO_INT ( pass_along[4]) ) + // Get confirmation from the user + // Maybe this Waypoint Delete should be optional as is it could get annoying... + if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl), + _("Are you sure you want to delete the waypoint \"%s\""), + pass_along[3] ) ) + return; was_visible = vik_trw_layer_delete_waypoint ( vtl, (gchar *) pass_along[3] ); } else { + if ( GPOINTER_TO_INT ( pass_along[4]) ) + // Get confirmation from the user + if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl), + _("Are you sure you want to delete the track \"%s\""), + pass_along[3] ) ) + return; was_visible = vik_trw_layer_delete_track ( vtl, (gchar *) pass_along[3] ); } if ( was_visible ) @@ -2067,7 +2715,7 @@ static void trw_layer_delete_item ( gpointer pass_along[5] ) } -static void trw_layer_properties_item ( gpointer pass_along[5] ) +static void trw_layer_properties_item ( gpointer pass_along[6] ) { VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]); if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) @@ -2075,10 +2723,11 @@ static void trw_layer_properties_item ( gpointer pass_along[5] ) VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, pass_along[3] ); if ( wp ) { - if ( a_dialog_new_waypoint ( VIK_GTK_WINDOW_FROM_LAYER(vtl), NULL, wp, NULL, vtl->coord_mode ) ) + gboolean updated = FALSE; + a_dialog_waypoint ( VIK_GTK_WINDOW_FROM_LAYER(vtl), pass_along[3], wp, NULL, vtl->coord_mode, FALSE, &updated ); - if ( VIK_LAYER(vtl)->visible ) - vik_layer_emit_update ( VIK_LAYER(vtl) ); + if ( updated && VIK_LAYER(vtl)->visible ) + vik_layer_emit_update ( VIK_LAYER(vtl) ); } } else @@ -2087,27 +2736,42 @@ static void trw_layer_properties_item ( gpointer pass_along[5] ) if ( tr ) { vik_trw_layer_propwin_run ( VIK_GTK_WINDOW_FROM_LAYER(vtl), - vtl, tr, - pass_along[1], /* vlp */ - pass_along[3] /* track name */); + vtl, tr, + pass_along[1], /* vlp */ + pass_along[3], /* track name */ + pass_along[5] ); /* vvp */ } } } -static void goto_coord ( VikLayersPanel *vlp, const VikCoord *coord ) -{ - vik_viewport_set_center_coord ( vik_layers_panel_get_viewport(vlp), coord ); - vik_layers_panel_emit_update ( vlp ); +/* + Parameter 1 -> VikLayersPanel + Parameter 2 -> VikLayer + Parameter 3 -> VikViewport +*/ +static void goto_coord ( gpointer *vlp, gpointer vl, gpointer vvp, const VikCoord *coord ) +{ + if ( vlp ) { + vik_viewport_set_center_coord ( vik_layers_panel_get_viewport (VIK_LAYERS_PANEL(vlp)), coord ); + vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) ); + } + else { + /* since vlp not set, vl & vvp should be valid instead! */ + if ( vl && vvp ) { + vik_viewport_set_center_coord ( VIK_VIEWPORT(vvp), coord ); + vik_layer_emit_update ( VIK_LAYER(vl) ); + } + } } -static void trw_layer_goto_track_startpoint ( gpointer pass_along[5] ) +static void trw_layer_goto_track_startpoint ( gpointer pass_along[6] ) { GList *trps = ((VikTrack *) g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ))->trackpoints; if ( trps && trps->data ) - goto_coord ( VIK_LAYERS_PANEL(pass_along[1]), &(((VikTrackpoint *) trps->data)->coord)); + goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(((VikTrackpoint *) trps->data)->coord)); } -static void trw_layer_goto_track_center ( gpointer pass_along[5] ) +static void trw_layer_goto_track_center ( gpointer pass_along[6] ) { /* FIXME: get this into viktrack.c, and should be ->trackpoints right? */ GList **trps = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ); @@ -2119,7 +2783,7 @@ static void trw_layer_goto_track_center ( gpointer pass_along[5] ) average.lat = (maxmin[0].lat+maxmin[1].lat)/2; average.lon = (maxmin[0].lon+maxmin[1].lon)/2; vik_coord_load_from_latlon ( &coord, VIK_TRW_LAYER(pass_along[0])->coord_mode, &average ); - goto_coord ( VIK_LAYERS_PANEL(pass_along[1]), &coord); + goto_coord ( pass_along[1], pass_along[0], pass_along[5], &coord); } } @@ -2132,7 +2796,7 @@ static void trw_layer_extend_track_end ( gpointer pass_along[6] ) 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) ); + goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(((VikTrackpoint *)g_list_last(track->trackpoints)->data)->coord) ); } /** @@ -2147,9 +2811,10 @@ static void trw_layer_extend_track_end_ms ( gpointer pass_along[6] ) 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; + vtl->magic_scissors_started = TRUE; if ( track->trackpoints ) - goto_coord ( VIK_LAYERS_PANEL(pass_along[1]), &last_coord) ; + goto_coord ( pass_along[1], pass_along[0], pass_along[5], &last_coord) ; } @@ -2168,9 +2833,56 @@ static void trw_layer_goto_track_endpoint ( gpointer pass_along[6] ) if ( !trps ) return; trps = g_list_last(trps); - goto_coord ( VIK_LAYERS_PANEL(pass_along[1]), &(((VikTrackpoint *) trps->data)->coord)); + goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(((VikTrackpoint *) trps->data)->coord)); +} + +static void trw_layer_goto_track_max_speed ( gpointer pass_along[6] ) +{ + VikTrackpoint* vtp = vik_track_get_tp_by_max_speed ( g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ) ); + if ( !vtp ) + return; + goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(vtp->coord)); +} + +static void trw_layer_goto_track_max_alt ( gpointer pass_along[6] ) +{ + VikTrackpoint* vtp = vik_track_get_tp_by_max_alt ( g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ) ); + if ( !vtp ) + return; + goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(vtp->coord)); +} + +static void trw_layer_goto_track_min_alt ( gpointer pass_along[6] ) +{ + VikTrackpoint* vtp = vik_track_get_tp_by_min_alt ( g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ) ); + if ( !vtp ) + return; + goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(vtp->coord)); +} + +/* + * Automatically change the viewport to center on the track and zoom to see the extent of the track + */ +static void trw_layer_auto_track_view ( gpointer pass_along[5] ) +{ + GList **trps = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ); + if ( trps && *trps ) + { + struct LatLon maxmin[2] = { {0,0}, {0,0} }; + trw_layer_find_maxmin_tracks ( NULL, trps, maxmin ); + trw_layer_zoom_to_show_latlons ( VIK_TRW_LAYER(pass_along[0]), pass_along[5], maxmin ); + if ( pass_along[1] ) + vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(pass_along[1]) ); + else + vik_layer_emit_update ( VIK_LAYER(pass_along[0]) ); + } } +static void trw_layer_edit_trackpoint ( gpointer pass_along[6] ) +{ + VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]); + trw_layer_tpwin_init ( vtl ); +} /************************************* * merge/split by time routines @@ -2256,6 +2968,7 @@ static void find_nearby_track(gpointer key, gpointer value, gpointer user_data) } /* comparison function used to sort tracks; a and b are hash table keys */ +/* Not actively used - can be restored if needed static gint track_compare(gconstpointer a, gconstpointer b, gpointer user_data) { GHashTable *tracks = user_data; @@ -2268,6 +2981,7 @@ static gint track_compare(gconstpointer a, gconstpointer b, gpointer user_data) if (t1 > t2) return 1; return 0; } +*/ /* comparison function used to sort trackpoints */ static gint trackpoint_compare(gconstpointer a, gconstpointer b) @@ -2279,6 +2993,22 @@ static gint trackpoint_compare(gconstpointer a, gconstpointer b) return 0; } +#ifdef VIK_CONFIG_ALPHABETIZED_TRW +/** + * comparison function which can be used to sort tracks or waypoints by name + */ +static gint sort_alphabetically (gconstpointer a, gconstpointer b, gpointer user_data) +{ + const gchar* namea = (const gchar*) a; + const gchar* nameb = (const gchar*) b; + if ( namea == NULL || nameb == NULL) + return 0; + else + // Same sort method as used in the vik_treeview_*_alphabetize functions + return strcmp ( namea, nameb ); +} +#endif + static void trw_layer_merge_with_other ( gpointer pass_along[6] ) { VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0]; @@ -2306,8 +3036,13 @@ static void trw_layer_merge_with_other ( gpointer pass_along[6] ) return; } +#ifdef VIK_CONFIG_ALPHABETIZED_TRW + // Sort alphabetically for user presentation + tracks_with_timestamp = g_list_sort_with_data (tracks_with_timestamp, sort_alphabetically, NULL); +#endif + GList *merge_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl), - vtl->tracks, tracks_with_timestamp, TRUE, + tracks_with_timestamp, TRUE, _("Merge with..."), _("Select track to merge with")); g_list_free(tracks_with_timestamp); @@ -2337,7 +3072,7 @@ static void trw_layer_merge_by_timestamp ( gpointer pass_along[6] ) VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0]; gchar *orig_track_name = strdup(pass_along[3]); - time_t t1, t2; + //time_t t1, t2; GList *nearby_tracks; VikTrack *track; GList *trps; @@ -2368,8 +3103,8 @@ static void trw_layer_merge_by_timestamp ( gpointer pass_along[6] ) nearby_tracks = NULL; } - t1 = ((VikTrackpoint *)trps->data)->timestamp; - t2 = ((VikTrackpoint *)g_list_last(trps)->data)->timestamp; + //t1 = ((VikTrackpoint *)trps->data)->timestamp; + //t2 = ((VikTrackpoint *)g_list_last(trps)->data)->timestamp; /* g_print("Original track times: %d and %d\n", t1, t2); */ params[0] = &nearby_tracks; @@ -2456,7 +3191,7 @@ static void trw_layer_split_by_timestamp ( gpointer pass_along[6] ) } if (ts - prev_ts > thr*60) { /* flush accumulated trackpoints into new list */ - newlists = g_list_prepend(newlists, g_list_reverse(newtps)); + newlists = g_list_append(newlists, g_list_reverse(newtps)); newtps = NULL; } @@ -2466,43 +3201,204 @@ static void trw_layer_split_by_timestamp ( gpointer pass_along[6] ) iter = g_list_next(iter); } if (newtps) { - newlists = g_list_prepend(newlists, g_list_reverse(newtps)); + newlists = g_list_append(newlists, g_list_reverse(newtps)); } /* put lists of trackpoints into tracks */ iter = newlists; i = 1; - while (iter) { - gchar *new_tr_name; - VikTrack *tr; + // Only bother updating if the split results in new tracks + if (g_list_length (newlists) > 1) { + while (iter) { + gchar *new_tr_name; + VikTrack *tr; - tr = vik_track_new(); - tr->visible = track->visible; - tr->trackpoints = (GList *)(iter->data); + tr = vik_track_new(); + tr->visible = track->visible; + tr->trackpoints = (GList *)(iter->data); - new_tr_name = g_strdup_printf("%s #%d", (gchar *) pass_along[3], i++); - vik_trw_layer_add_track(VIK_TRW_LAYER(pass_along[0]), new_tr_name, tr); - /* g_print("adding track %s, times %d - %d\n", new_tr_name, VIK_TRACKPOINT(tr->trackpoints->data)->timestamp, + new_tr_name = g_strdup_printf("%s #%d", (gchar *) pass_along[3], i++); + vik_trw_layer_add_track(VIK_TRW_LAYER(pass_along[0]), new_tr_name, tr); + /* g_print("adding track %s, times %d - %d\n", new_tr_name, VIK_TRACKPOINT(tr->trackpoints->data)->timestamp, VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp);*/ + iter = g_list_next(iter); + } + vik_trw_layer_delete_track(VIK_TRW_LAYER(pass_along[0]), (gchar *)pass_along[3]); + vik_layer_emit_update(VIK_LAYER(pass_along[0])); + } + g_list_free(newlists); +} + +/** + * Split a track by the number of points as specified by the user + */ +static void trw_layer_split_by_n_points ( gpointer pass_along[6] ) +{ + VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0]; + VikTrack *track = (VikTrack *)g_hash_table_lookup ( vtl->tracks, pass_along[3] ); + + // Check valid track + GList *trps = track->trackpoints; + if ( !trps ) + return; + + gint points = a_dialog_get_positive_number(VIK_GTK_WINDOW_FROM_LAYER(pass_along[0]), + _("Split Every Nth Point"), + _("Split on every Nth point:"), + 250, // Default value as per typical limited track capacity of various GPS devices + 2, // Min + 65536, // Max + 5); // Step + // Was a valid number returned? + if (!points) + return; + + // Now split... + GList *iter; + GList *newlists = NULL; + GList *newtps = NULL; + gint count = 0; + iter = trps; + + while (iter) { + /* accumulate trackpoint copies in newtps, in reverse order */ + newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data))); + count++; + if (count >= points) { + /* flush accumulated trackpoints into new list */ + newlists = g_list_append(newlists, g_list_reverse(newtps)); + newtps = NULL; + count = 0; + } iter = g_list_next(iter); } + + // If there is a remaining chunk put that into the new split list + // This may well be the whole track if no split points were encountered + if (newtps) { + newlists = g_list_append(newlists, g_list_reverse(newtps)); + } + + /* 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) { + gchar *new_tr_name; + VikTrack *tr; + + tr = vik_track_new(); + tr->visible = track->visible; + tr->trackpoints = (GList *)(iter->data); + + new_tr_name = g_strdup_printf("%s #%d", (gchar *) pass_along[3], i++); + vik_trw_layer_add_track(VIK_TRW_LAYER(pass_along[0]), new_tr_name, tr); + + iter = g_list_next(iter); + } + // Remove original track and then update the display + vik_trw_layer_delete_track(VIK_TRW_LAYER(pass_along[0]), (gchar *)pass_along[3]); + vik_layer_emit_update(VIK_LAYER(pass_along[0])); + } g_list_free(newlists); - vik_trw_layer_delete_track(VIK_TRW_LAYER(pass_along[0]), (gchar *)pass_along[3]); - vik_layer_emit_update(VIK_LAYER(pass_along[0])); } /* end of split/merge routines */ +/** + * Similar to trw_layer_enum_item, but this uses a sorted method + */ +static void trw_layer_sorted_name_list(gpointer key, gpointer value, gpointer udata) +{ + GList **list = (GList**)udata; + //*list = g_list_prepend(*all, key); //unsorted method + // Sort named list alphabetically + *list = g_list_insert_sorted_with_data (*list, key, sort_alphabetically, NULL); +} + +/** + * + */ +static void trw_layer_delete_tracks_from_selection ( gpointer lav[2] ) +{ + VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]); + GList *all = NULL; + // Sort list alphabetically for better presentation + g_hash_table_foreach(vtl->tracks, trw_layer_sorted_name_list, &all); + + if ( ! all ) { + a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No tracks found")); + return; + } + + // Get list of items to delete from the user + GList *delete_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl), + all, + TRUE, + _("Delete Selection"), + _("Select tracks to delete")); + g_list_free(all); + + // Delete requested tracks + // since specificly requested, IMHO no need for extra confirmation + if ( delete_list ) { + GList *l; + for (l = delete_list; l != NULL; l = g_list_next(l)) { + vik_trw_layer_delete_track(vtl, l->data); + } + g_list_free(delete_list); + vik_layer_emit_update( VIK_LAYER(vtl) ); + } +} + +/** + * + */ +static void trw_layer_delete_waypoints_from_selection ( gpointer lav[2] ) +{ + VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]); + GList *all = NULL; + + // Sort list alphabetically for better presentation + g_hash_table_foreach ( vtl->waypoints, trw_layer_sorted_name_list, &all); + if ( ! all ) { + a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No waypoints found")); + return; + } + + all = g_list_sort_with_data(all, sort_alphabetically, NULL); + + // Get list of items to delete from the user + GList *delete_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl), + all, + TRUE, + _("Delete Selection"), + _("Select waypoints to delete")); + g_list_free(all); + + // Delete requested waypoints + // since specificly requested, IMHO no need for extra confirmation + if ( delete_list ) { + GList *l; + for (l = delete_list; l != NULL; l = g_list_next(l)) { + vik_trw_layer_delete_waypoint(vtl, l->data); + } + g_list_free(delete_list); + vik_layer_emit_update( VIK_LAYER(vtl) ); + } -static void trw_layer_goto_waypoint ( gpointer pass_along[5] ) +} + +static void trw_layer_goto_waypoint ( gpointer pass_along[6] ) { VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] ); if ( wp ) - goto_coord ( VIK_LAYERS_PANEL(pass_along[1]), &(wp->coord) ); + goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(wp->coord) ); } -static void trw_layer_waypoint_gc_webpage ( gpointer pass_along[5] ) +static void trw_layer_waypoint_gc_webpage ( gpointer pass_along[6] ) { gchar *webpage = g_strdup_printf("http://www.geocaching.com/seek/cache_details.aspx?wp=%s", (gchar *) pass_along[3] ); open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(pass_along[0])), webpage); @@ -2609,7 +3505,7 @@ 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])); } -static void trw_layer_track_use_with_filter ( gpointer *pass_along ) +static void trw_layer_track_use_with_filter ( gpointer pass_along[6] ) { gchar *track_name = (gchar *) pass_along[3]; VikTrack *tr = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, track_name ); @@ -2622,7 +3518,7 @@ static gboolean is_valid_google_route ( VikTrwLayer *vtl, const gchar *track_nam return ( tr && tr->comment && strlen(tr->comment) > 7 && !strncmp(tr->comment, "from:", 5) ); } -static void trw_layer_track_google_route_webpage( gpointer *pass_along ) +static void trw_layer_track_google_route_webpage ( gpointer pass_along[6] ) { gchar *track_name = (gchar *) pass_along[3]; VikTrack *tr = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, track_name ); @@ -2635,11 +3531,11 @@ static void trw_layer_track_google_route_webpage( gpointer *pass_along ) } } -/* vlp can be NULL if necessary - i.e. right-click from a tool -- but be careful, some functions may try to use it */ -gboolean vik_trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *menu, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter ) +/* vlp can be NULL if necessary - i.e. right-click from a tool */ +/* viewpoint is now available instead */ +gboolean vik_trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *menu, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter, VikViewport *vvp ) { - static GtkTreeIter staticiter; - static gpointer pass_along[5]; + static gpointer pass_along[6]; GtkWidget *item; gboolean rv = FALSE; @@ -2647,8 +3543,8 @@ gboolean vik_trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *menu, pass_along[1] = vlp; pass_along[2] = GINT_TO_POINTER (subtype); pass_along[3] = sublayer; - staticiter = *iter; /* will exist after function has ended */ - pass_along[4] = &staticiter; + pass_along[4] = GINT_TO_POINTER (1); // Confirm delete request + pass_along[5] = vvp; if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT || subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) { @@ -2683,9 +3579,17 @@ gboolean vik_trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *menu, if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) { + gboolean separator_created = FALSE; + /* could be a right-click using the tool */ if ( vlp != NULL ) { - item = gtk_menu_item_new_with_label ( _("Goto") ); + item = gtk_menu_item_new (); + gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item ); + gtk_widget_show ( item ); + + separator_created = TRUE; + + item = gtk_menu_item_new_with_mnemonic ( _("_Goto") ); g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_waypoint), pass_along ); gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item ); gtk_widget_show ( item ); @@ -2693,73 +3597,192 @@ gboolean vik_trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *menu, if ( is_valid_geocache_name ( (gchar *) sublayer ) ) { - item = gtk_menu_item_new_with_label ( _("Visit Geocache Webpage") ); + if ( !separator_created ) { + item = gtk_menu_item_new (); + gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item ); + gtk_widget_show ( item ); + separator_created = TRUE; + } + + item = gtk_menu_item_new_with_mnemonic ( _("_Visit Geocache Webpage") ); g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_gc_webpage), pass_along ); gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item ); gtk_widget_show ( item ); } + VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(l)->waypoints, sublayer ); + + if ( wp && wp->image ) + { + if ( !separator_created ) { + item = gtk_menu_item_new (); + gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item ); + gtk_widget_show ( item ); + separator_created = TRUE; + } + + // Set up image paramater + pass_along[5] = wp->image; + + item = gtk_menu_item_new_with_mnemonic ( _("_Show Picture...") ); + g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_show_picture), pass_along ); + gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item ); + gtk_widget_show ( item ); + } + } } + if ( vlp && (subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT) ) + { + rv = TRUE; + item = gtk_menu_item_new_with_mnemonic ( _("_New Waypoint...") ); + g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along ); + gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); + gtk_widget_show ( item ); + } + + if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS ) + { + item = gtk_menu_item_new_with_mnemonic ( _("_View All Waypoints") ); + g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_waypoints_view), pass_along ); + gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); + gtk_widget_show ( item ); + + item = gtk_menu_item_new_with_mnemonic ( _("Goto _Waypoint...") ); + g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_wp), pass_along ); + gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); + gtk_widget_show ( item ); + + item = gtk_menu_item_new_with_mnemonic ( _("Delete _All Waypoints") ); + g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_waypoints), pass_along ); + gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item ); + gtk_widget_show ( item ); + + item = gtk_menu_item_new_with_mnemonic ( _("_Delete Waypoints From Selection...") ); + g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_waypoints_from_selection), pass_along ); + gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item ); + gtk_widget_show ( item ); + } + + if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS ) + { + rv = TRUE; + + item = gtk_menu_item_new_with_mnemonic ( _("_View All Tracks") ); + g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_tracks_view), pass_along ); + gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); + gtk_widget_show ( item ); + + item = gtk_menu_item_new_with_mnemonic ( _("Delete _All Tracks") ); + g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_tracks), pass_along ); + gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item ); + gtk_widget_show ( item ); + + item = gtk_menu_item_new_with_mnemonic ( _("_Delete Tracks From Selection...") ); + g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_tracks_from_selection), pass_along ); + gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item ); + gtk_widget_show ( item ); + } + 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_menu_item_new_with_label ( _("Goto Startpoint") ); + goto_submenu = gtk_menu_new (); + item = gtk_menu_item_new_with_mnemonic ( _("_Goto") ); + gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); + gtk_widget_show ( item ); + gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), goto_submenu ); + + item = gtk_menu_item_new_with_mnemonic ( _("_Startpoint") ); g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_startpoint), pass_along ); - gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item ); + gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item ); gtk_widget_show ( item ); - item = gtk_menu_item_new_with_label ( _("Goto \"Center\"") ); + item = gtk_menu_item_new_with_mnemonic ( _("\"_Center\"") ); g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_center), pass_along ); - gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item ); + gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item ); gtk_widget_show ( item ); - item = gtk_menu_item_new_with_label ( _("Goto Endpoint") ); + item = gtk_menu_item_new_with_mnemonic ( _("_Endpoint") ); g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_endpoint), pass_along ); + gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item ); + gtk_widget_show ( item ); + + item = gtk_menu_item_new_with_mnemonic ( _("_Highest Altitude") ); + g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_alt), pass_along ); + gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item ); + gtk_widget_show ( item ); + + item = gtk_menu_item_new_with_mnemonic ( _("_Lowest Altitude") ); + g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_min_alt), pass_along ); + gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item ); + gtk_widget_show ( item ); + + item = gtk_menu_item_new_with_mnemonic ( _("_Maximum Speed") ); + g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_speed), pass_along ); + gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item ); + gtk_widget_show ( item ); + + item = gtk_menu_item_new_with_mnemonic ( _("_View Track") ); + 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 ); - item = gtk_menu_item_new_with_label ( _("Merge By Time") ); + 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_widget_show ( item ); - item = gtk_menu_item_new_with_label ( _("Merge With Other Tracks...") ); + 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_widget_show ( item ); - item = gtk_menu_item_new_with_label ( _("Split By Time") ); + 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_widget_show ( item ); - item = gtk_menu_item_new_with_label ( _("Download maps along track...") ); - g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_download_map_along_track_cb), pass_along ); + 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_widget_show ( item ); - item = gtk_menu_item_new_with_label ( _("Apply DEM Data") ); + /* ATM This function is only available via the layers panel, due to the method in finding out the maps in use */ + if ( vlp ) { + item = gtk_menu_item_new_with_mnemonic ( _("Down_load Maps Along Track...") ); + g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_download_map_along_track_cb), pass_along ); + gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item ); + gtk_widget_show ( item ); + } + + item = gtk_menu_item_new_with_mnemonic ( _("_Apply DEM Data") ); g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data), pass_along ); 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_mnemonic ( _("Export Trac_k as GPX...") ); + g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpx_track), pass_along ); + gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item ); + gtk_widget_show ( item ); + + item = gtk_menu_item_new_with_mnemonic ( _("E_xtend 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") ); + item = gtk_menu_item_new_with_mnemonic ( _("Extend _Using Route Finder") ); 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") ); + item = gtk_menu_item_new_with_mnemonic ( _("Upload to _OSM...") ); g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(osm_traces_upload_track_cb), pass_along ); gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item ); gtk_widget_show ( item ); @@ -2767,41 +3790,99 @@ 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_mnemonic ( _("_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_mnemonic ( _("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 ); - item = a_acquire_track_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(l)), vlp, - vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)), - g_hash_table_lookup ( l->tracks, (gchar *) sublayer ) ); - if ( item ) { - gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); + /* ATM This function is only available via the layers panel, due to needing a vlp */ + if ( vlp ) { + item = a_acquire_track_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(l)), vlp, + vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)), + g_hash_table_lookup ( l->tracks, (gchar *) sublayer ) ); + if ( item ) { + gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); + gtk_widget_show ( item ); + } + } + + // Only show on viewport popmenu when a trackpoint is selected + if ( ! vlp && l->current_tpl ) { + // Add separator + item = gtk_menu_item_new (); + gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item ); gtk_widget_show ( item ); - } - } - if ( vlp && (subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT) ) - { - 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 ( _("_Edit Trackpoint") ); + gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_PROPERTIES, GTK_ICON_SIZE_MENU) ); + g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_edit_trackpoint), pass_along ); + gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item ); + gtk_widget_show ( item ); + } - item = gtk_menu_item_new_with_label ( _("New Waypoint") ); - g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along ); - gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); - gtk_widget_show ( item ); } return rv; } +static void trw_layer_insert_tp_after_current_tp ( VikTrwLayer *vtl ) +{ + /* sanity checks */ + if (!vtl->current_tpl) + return; + if (!vtl->current_tpl->next) + return; + + VikTrackpoint *tp_current = VIK_TRACKPOINT(vtl->current_tpl->data); + VikTrackpoint *tp_next = VIK_TRACKPOINT(vtl->current_tpl->next->data); + + /* Use current and next trackpoints to form a new track point which is inserted into the tracklist */ + if ( tp_next ) { + + VikTrackpoint *tp_new = vik_trackpoint_new(); + struct LatLon ll_current, ll_next; + vik_coord_to_latlon ( &tp_current->coord, &ll_current ); + vik_coord_to_latlon ( &tp_next->coord, &ll_next ); + + /* main positional interpolation */ + struct LatLon ll_new = { (ll_current.lat+ll_next.lat)/2, (ll_current.lon+ll_next.lon)/2 }; + vik_coord_load_from_latlon ( &(tp_new->coord), vtl->coord_mode, &ll_new ); + + /* Now other properties that can be interpolated */ + tp_new->altitude = (tp_current->altitude + tp_next->altitude) / 2; + + if (tp_current->has_timestamp && tp_next->has_timestamp) { + /* Note here the division is applied to each part, then added + This is to avoid potential overflow issues with a 32 time_t for dates after midpoint of this Unix time on 2004/01/04 */ + tp_new->timestamp = (tp_current->timestamp/2) + (tp_next->timestamp/2); + tp_new->has_timestamp = TRUE; + } + + if (tp_current->speed != NAN && tp_next->speed != NAN) + tp_new->speed = (tp_current->speed + tp_next->speed)/2; + + /* TODO - improve interpolation of course, as it may not be correct. + if courses in degrees are 350 + 020, the mid course more likely to be 005 (not 185) + [similar applies if value is in radians] */ + if (tp_current->course != NAN && tp_next->course != NAN) + tp_new->speed = (tp_current->course + tp_next->course)/2; + + /* DOP / sat values remain at defaults as not they do not seem applicable to a dreamt up point */ + + /* Insert new point into the trackpoints list after the current TP */ + VikTrack *tr = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_track_name ); + gint index = g_list_index ( tr->trackpoints, tp_current ); + if ( index > -1 ) { + tr->trackpoints = g_list_insert (tr->trackpoints, tp_new, index+1 ); + } + } +} /* to be called when last_tpl no long exists. */ static void trw_layer_cancel_last_tp ( VikTrwLayer *vtl ) @@ -2837,10 +3918,14 @@ static void trw_layer_tpwin_response ( VikTrwLayer *vtl, gint response ) g_assert ( vtl->tpwin != NULL ); if ( response == VIK_TRW_LAYER_TPWIN_CLOSE ) trw_layer_cancel_current_tp ( vtl, TRUE ); - else if ( response == VIK_TRW_LAYER_TPWIN_SPLIT && vtl->current_tpl->next && vtl->current_tpl->prev ) + + if ( vtl->current_tpl == NULL ) + return; + + if ( response == VIK_TRW_LAYER_TPWIN_SPLIT && vtl->current_tpl->next && vtl->current_tpl->prev ) { - gchar *name; - if ( ( name = a_dialog_new_track ( GTK_WINDOW(vtl->tpwin), vtl->tracks, NULL ) ) ) + 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 (); @@ -2947,6 +4032,11 @@ static void trw_layer_tpwin_response ( VikTrwLayer *vtl, gint response ) trw_layer_cancel_last_tp ( vtl ); /* same TP, can't join. */ vik_layer_emit_update(VIK_LAYER(vtl)); } + else if ( response == VIK_TRW_LAYER_TPWIN_INSERT && vtl->current_tpl->next ) + { + trw_layer_insert_tp_after_current_tp ( vtl ); + vik_layer_emit_update(VIK_LAYER(vtl)); + } else if ( response == VIK_TRW_LAYER_TPWIN_DATA_CHANGED ) vik_layer_emit_update (VIK_LAYER(vtl)); } @@ -2996,16 +4086,30 @@ static void waypoint_search_closest_tp ( gchar *name, VikWaypoint *wp, WPSearchP return; vik_viewport_coord_to_screen ( params->vvp, &(wp->coord), &x, &y ); - - if ( abs (x - params->x) <= WAYPOINT_SIZE_APPROX && abs (y - params->y) <= WAYPOINT_SIZE_APPROX && - ((!params->closest_wp) || /* was the old waypoint we already found closer than this one? */ - abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y))) - { - params->closest_wp_name = name; - params->closest_wp = wp; - params->closest_x = x; - params->closest_y = y; + + // If waypoint has an image then use the image size to select + if ( wp->image ) { + gint slackx, slacky; + slackx = wp->image_width / 2; + slacky = wp->image_height / 2; + + if ( x <= params->x + slackx && x >= params->x - slackx + && y <= params->y + slacky && y >= params->y - slacky ) { + params->closest_wp_name = name; + params->closest_wp = wp; + params->closest_x = x; + params->closest_y = y; + } } + else if ( abs (x - params->x) <= WAYPOINT_SIZE_APPROX && abs (y - params->y) <= WAYPOINT_SIZE_APPROX && + ((!params->closest_wp) || /* was the old waypoint we already found closer than this one? */ + abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y))) + { + params->closest_wp_name = name; + params->closest_wp = wp; + params->closest_x = x; + params->closest_y = y; + } } static void track_search_closest_tp ( gchar *name, VikTrack *t, TPSearchParams *params ) @@ -3061,6 +4165,263 @@ static VikWaypoint *closest_wp_in_five_pixel_interval ( VikTrwLayer *vtl, VikVie return params.closest_wp; } +// Some forward declarations +static void marker_begin_move ( tool_ed_t *t, gint x, gint y ); +static void marker_moveto ( tool_ed_t *t, gint x, gint y ); +static void marker_end_move ( tool_ed_t *t ); +// + +static gboolean trw_layer_select_move ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* t ) +{ + if ( t->holding ) { + VikCoord new_coord; + vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord ); + + // Here always allow snapping back to the original location + // this is useful when one decides not to move the thing afterall + // If one wants to move the item only a little bit then don't hold down the 'snap' key! + + // snap to TP + if ( event->state & GDK_CONTROL_MASK ) + { + VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y ); + if ( tp ) + new_coord = tp->coord; + } + + // snap to WP + if ( event->state & GDK_SHIFT_MASK ) + { + VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y ); + if ( wp ) + new_coord = wp->coord; + } + + gint x, y; + vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y ); + + marker_moveto ( t, x, y ); + + return TRUE; + } + return FALSE; +} + +static gboolean trw_layer_select_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* t ) +{ + if ( t->holding && event->button == 1 ) + { + VikCoord new_coord; + vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord ); + + // snap to TP + if ( event->state & GDK_CONTROL_MASK ) + { + VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y ); + if ( tp ) + new_coord = tp->coord; + } + + // snap to WP + if ( event->state & GDK_SHIFT_MASK ) + { + VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y ); + if ( wp ) + new_coord = wp->coord; + } + + marker_end_move ( t ); + + // Determine if working on a waypoint or a trackpoint + if ( t->is_waypoint ) + vtl->current_wp->coord = new_coord; + else { + if ( vtl->current_tpl ) { + VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord; + + if ( vtl->tpwin ) + vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track_name ); + + // Don't really know what this is for but seems like it might be handy... + /* can't join with itself! */ + trw_layer_cancel_last_tp ( vtl ); + } + } + + // Reset + vtl->current_wp = NULL; + vtl->current_wp_name = NULL; + trw_layer_cancel_current_tp ( vtl, FALSE ); + + vik_layer_emit_update ( VIK_LAYER(vtl) ); + return TRUE; + } + return FALSE; +} + +/* + Returns true if a waypoint or track is found near the requested event position for this particular layer + The item found is automatically selected + This is a tool like feature but routed via the layer interface, since it's instigated by a 'global' layer tool in vikwindow.c + */ +static gboolean trw_layer_select_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* tet ) +{ + if ( event->button != 1 ) + return FALSE; + + if (!vtl || vtl->vl.type != VIK_LAYER_TRW) + return FALSE; + + if ( !vtl->tracks_visible && !vtl->waypoints_visible ) + return FALSE; + + // Go for waypoints first as these often will be near a track, but it's likely the wp is wanted rather then the track + + if (vtl->waypoints_visible) { + WPSearchParams wp_params; + wp_params.vvp = vvp; + wp_params.x = event->x; + wp_params.y = event->y; + wp_params.closest_wp_name = NULL; + wp_params.closest_wp = NULL; + + g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, &wp_params); + + if ( wp_params.closest_wp ) { + + // Select + vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, wp_params.closest_wp_name ), TRUE ); + + // Too easy to move it so must be holding shift to start immediately moving it + // or otherwise be previously selected + if ( event->state & GDK_SHIFT_MASK || + vtl->current_wp == wp_params.closest_wp ) { + // Put into 'move buffer' + // NB vvp & vw already set in tet + tet->vtl = (gpointer)vtl; + tet->is_waypoint = TRUE; + + marker_begin_move (tet, event->x, event->y); + } + + vtl->current_wp = wp_params.closest_wp; + vtl->current_wp_name = wp_params.closest_wp_name; + + vik_layer_emit_update ( VIK_LAYER(vtl) ); + + return TRUE; + } + } + + if (vtl->tracks_visible) { + TPSearchParams tp_params; + tp_params.vvp = vvp; + tp_params.x = event->x; + tp_params.y = event->y; + tp_params.closest_track_name = NULL; + tp_params.closest_tp = NULL; + + g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, &tp_params); + + if ( tp_params.closest_tp ) { + + // Always select + highlight the track + vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, tp_params.closest_track_name ), TRUE ); + + tet->is_waypoint = FALSE; + + // Select the Trackpoint + // Can move it immediately when control held or it's the previously selected tp + if ( event->state & GDK_CONTROL_MASK || + vtl->current_tpl == tp_params.closest_tpl ) { + // Put into 'move buffer' + // NB vvp & vw already set in tet + tet->vtl = (gpointer)vtl; + marker_begin_move (tet, event->x, event->y); + } + + vtl->current_tpl = tp_params.closest_tpl; + vtl->current_tp_track_name = tp_params.closest_track_name; + + if ( vtl->tpwin ) + vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track_name ); + + vik_layer_emit_update ( VIK_LAYER(vtl) ); + return TRUE; + } + } + + /* these aren't the droids you're looking for */ + vtl->current_wp = NULL; + vtl->current_wp_name = NULL; + trw_layer_cancel_current_tp ( vtl, FALSE ); + + return FALSE; +} + +static gboolean trw_layer_show_selected_viewport_menu ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp ) +{ + if ( event->button != 3 ) + return FALSE; + + if (!vtl || vtl->vl.type != VIK_LAYER_TRW) + return FALSE; + + if ( !vtl->tracks_visible && !vtl->waypoints_visible ) + return FALSE; + + /* Post menu for the currently selected item */ + + /* See if a track is selected */ + 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 ( 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 () ); + + vik_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); + + gtk_menu_popup ( vtl->track_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() ); + + return TRUE; + } + } + + /* 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 ( 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 () ); + vik_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); + gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() ); + + return TRUE; + } + } + + return FALSE; +} + /* background drawing hook, to be passed the viewport */ static gboolean tool_sync_done = TRUE; @@ -3074,13 +4435,6 @@ static gboolean tool_sync(gpointer data) return FALSE; } -typedef struct { - VikViewport *vvp; - gboolean holding; - GdkGC *gc; - int oldx, oldy; -} tool_ed_t; - static void marker_begin_move ( tool_ed_t *t, gint x, gint y ) { t->holding = TRUE; @@ -3136,6 +4490,9 @@ static gboolean tool_edit_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *eve return TRUE; } + if ( !vtl->vl.visible || !vtl->waypoints_visible ) + return FALSE; + if ( vtl->current_wp && vtl->current_wp->visible ) { /* first check if current WP is within area (other may be 'closer', but we want to move the current) */ @@ -3179,7 +4536,7 @@ static gboolean tool_edit_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *eve vtl->current_wp_name = params.closest_wp_name; if ( params.closest_wp ) - vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, vtl->current_wp_name ) ); + vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, vtl->current_wp_name ), TRUE ); /* could make it so don't update if old WP is off screen and new is null but oh well */ vik_layer_emit_update ( VIK_LAYER(vtl) ); @@ -3271,9 +4628,9 @@ static gboolean tool_edit_waypoint_release ( VikTrwLayer *vtl, GdkEventButton *e if ( event->button == 3 && vtl->waypoint_rightclick ) { if ( vtl->wp_right_click_menu ) - gtk_object_sink ( GTK_OBJECT(vtl->wp_right_click_menu) ); + g_object_ref_sink ( G_OBJECT(vtl->wp_right_click_menu) ); vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () ); - vik_trw_layer_sublayer_add_menu_items ( vtl, vtl->wp_right_click_menu, NULL, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, vtl->current_wp_name, g_hash_table_lookup ( vtl->waypoints_iters, vtl->current_wp_name ) ); + vik_trw_layer_sublayer_add_menu_items ( vtl, vtl->wp_right_click_menu, NULL, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, vtl->current_wp_name, g_hash_table_lookup ( vtl->waypoints_iters, vtl->current_wp_name ), vvp ); gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() ); vtl->waypoint_rightclick = FALSE; } @@ -3444,7 +4801,6 @@ static gboolean tool_new_track_click ( VikTrwLayer *vtl, GdkEventButton *event, return TRUE; } - /*** New waypoint ****/ static gpointer tool_new_waypoint_create ( VikWindow *vw, VikViewport *vvp) @@ -3498,6 +4854,9 @@ 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 ) + return FALSE; + if ( vtl->current_tpl ) { /* 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.) */ @@ -3525,7 +4884,7 @@ static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *e { vtl->current_tpl = params.closest_tpl; vtl->current_tp_track_name = params.closest_track_name; - vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, vtl->current_tp_track_name ) ); + vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, vtl->current_tp_track_name ), TRUE ); trw_layer_tpwin_init ( vtl ); vik_layer_emit_update ( VIK_LAYER(vtl) ); return TRUE; @@ -3718,6 +5077,25 @@ static void tool_show_picture_wp ( char *name, VikWaypoint *wp, gpointer params[ } } +static void trw_layer_show_picture ( gpointer pass_along[6] ) +{ + /* thanks to the Gaim people for showing me ShellExecute and g_spawn_command_line_async */ +#ifdef WINDOWS + ShellExecute(NULL, NULL, (char *) pass_along[2], NULL, ".\\", 0); +#else /* WINDOWS */ + GError *err = NULL; + gchar *quoted_file = g_shell_quote ( (gchar *) pass_along[5] ); + gchar *cmd = g_strdup_printf ( "eog %s", quoted_file ); + g_free ( quoted_file ); + if ( ! g_spawn_command_line_async ( cmd, &err ) ) + { + a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER( pass_along[0]), _("Could not launch eog to open file.") ); + g_error_free ( err ); + } + g_free ( cmd ); +#endif /* WINDOWS */ +} + static gboolean tool_show_picture_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp ) { gpointer params[3] = { vvp, event, NULL }; @@ -3726,21 +5104,10 @@ static gboolean tool_show_picture_click ( VikTrwLayer *vtl, GdkEventButton *even g_hash_table_foreach ( vtl->waypoints, (GHFunc) tool_show_picture_wp, params ); if ( params[2] ) { - /* thanks to the Gaim people for showing me ShellExecute and g_spawn_command_line_async */ -#ifdef WINDOWS - ShellExecute(NULL, NULL, (char *) params[2], NULL, ".\\", 0); -#else /* WINDOWS */ - GError *err = NULL; - gchar *quoted_file = g_shell_quote ( (gchar *) params[2] ); - gchar *cmd = g_strdup_printf ( "eog %s", quoted_file ); - g_free ( quoted_file ); - if ( ! g_spawn_command_line_async ( cmd, &err ) ) - { - a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Could not launch eog to open file.") ); - g_error_free ( err ); - } - g_free ( cmd ); -#endif /* WINDOWS */ + static gpointer pass_along[6]; + pass_along[0] = vtl; + pass_along[5] = params[2]; + trw_layer_show_picture ( pass_along ); return TRUE; /* found a match */ } else @@ -3761,24 +5128,42 @@ static void image_wp_make_list ( char *name, VikWaypoint *wp, GSList **pics ) *pics = g_slist_append ( *pics, (gpointer) g_strdup ( wp->image ) ); } -static void create_thumbnails_thread ( GSList *pics, gpointer threaddata ) +/* Structure for thumbnail creating data used in the background thread */ +typedef struct { + VikTrwLayer *vtl; // Layer needed for redrawing + GSList *pics; // Image list +} thumbnail_create_thread_data; + +static int create_thumbnails_thread ( thumbnail_create_thread_data *tctd, gpointer threaddata ) { - guint total = g_slist_length(pics), done = 0; - while ( pics ) + guint total = g_slist_length(tctd->pics), done = 0; + while ( tctd->pics ) { - a_thumbnails_create ( (gchar *) pics->data ); - a_background_thread_progress ( threaddata, ((gdouble) ++done) / total ); - pics = pics->next; + a_thumbnails_create ( (gchar *) tctd->pics->data ); + int result = a_background_thread_progress ( threaddata, ((gdouble) ++done) / total ); + if ( result != 0 ) + return -1; /* Abort thread */ + + tctd->pics = tctd->pics->next; } + + // Redraw to show the thumbnails as they are now created + gdk_threads_enter(); + if ( IS_VIK_LAYER(tctd->vtl) ) + vik_layer_emit_update ( VIK_LAYER(tctd->vtl) ); + gdk_threads_leave(); + + return 0; } -static void free_pics_slist ( GSList *pics ) +static void thumbnail_create_thread_free ( thumbnail_create_thread_data *tctd ) { - while ( pics ) + while ( tctd->pics ) { - g_free ( pics->data ); - pics = g_slist_delete_link ( pics, pics ); + g_free ( tctd->pics->data ); + tctd->pics = g_slist_delete_link ( tctd->pics, tctd->pics ); } + g_free ( tctd ); } static void trw_layer_verify_thumbnails ( VikTrwLayer *vtl, GtkWidget *vp ) @@ -3791,7 +5176,16 @@ static void trw_layer_verify_thumbnails ( VikTrwLayer *vtl, GtkWidget *vp ) { gint len = g_slist_length ( pics ); gchar *tmp = g_strdup_printf ( _("Creating %d Image Thumbnails..."), len ); - a_background_thread ( VIK_GTK_WINDOW_FROM_LAYER(vtl), tmp, (vik_thr_func) create_thumbnails_thread, pics, (vik_thr_free_func) free_pics_slist, NULL, len ); + 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), + tmp, + (vik_thr_func) create_thumbnails_thread, + tctd, + (vik_thr_free_func) thumbnail_create_thread_free, + NULL, + len ); g_free ( tmp ); } } @@ -3829,7 +5223,7 @@ VikWaypoint *vik_trw_layer_get_waypoint ( VikTrwLayer *vtl, gchar *name ) return g_hash_table_lookup ( vtl->waypoints, name ); } -VikTrack *vik_trw_layer_get_track ( VikTrwLayer *vtl, gchar *name ) +VikTrack *vik_trw_layer_get_track ( VikTrwLayer *vtl, const gchar *name ) { return g_hash_table_lookup ( vtl->tracks, name ); } @@ -3949,17 +5343,22 @@ void vik_track_download_map(VikTrack *tr, VikMapsLayer *vml, VikViewport *vvp, g new_map = TRUE; } - /* fill-ins for far apart points */ - GList *cur_rect, *next_rect; GList *fillins = NULL; - for (cur_rect = rects_to_download; - (next_rect = cur_rect->next) != NULL; - cur_rect = cur_rect->next) { - if ((wh.lon < ABS(GLRECT(cur_rect)->center.east_west - GLRECT(next_rect)->center.east_west)) || - (wh.lat < ABS(GLRECT(cur_rect)->center.north_south - GLRECT(next_rect)->center.north_south))) { - fillins = add_fillins(fillins, &GLRECT(cur_rect)->center, &GLRECT(next_rect)->center, &wh); + /* 'fillin' doesn't work in UTM mode - potentially ending up in massive loop continually allocating memory - hence don't do it */ + /* seems that ATM the function get_next_coord works only for LATLON */ + if ( cur_coord->mode == VIK_COORD_LATLON ) { + /* fill-ins for far apart points */ + GList *cur_rect, *next_rect; + for (cur_rect = rects_to_download; + (next_rect = cur_rect->next) != NULL; + cur_rect = cur_rect->next) { + if ((wh.lon < ABS(GLRECT(cur_rect)->center.east_west - GLRECT(next_rect)->center.east_west)) || + (wh.lat < ABS(GLRECT(cur_rect)->center.north_south - GLRECT(next_rect)->center.north_south))) { + fillins = add_fillins(fillins, &GLRECT(cur_rect)->center, &GLRECT(next_rect)->center, &wh); + } } - } + } else + g_message("%s: this feature works only in Mercator mode", __FUNCTION__); if (fillins) { GList *iter = fillins; @@ -3991,7 +5390,7 @@ void vik_track_download_map(VikTrack *tr, VikMapsLayer *vml, VikViewport *vvp, g } } -static void trw_layer_download_map_along_track_cb(gpointer pass_along[6]) +static void trw_layer_download_map_along_track_cb ( gpointer pass_along[6] ) { VikMapsLayer *vml; gint selected_map, default_map; @@ -4006,7 +5405,7 @@ static void trw_layer_download_map_along_track_cb(gpointer pass_along[6]) VikTrack *tr = (VikTrack *) g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ); VikViewport *vvp = vik_window_viewport((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl))); - GList *vmls = vik_layers_panel_get_all_layers_of_type(vlp, VIK_LAYER_MAPS); + GList *vmls = vik_layers_panel_get_all_layers_of_type(vlp, VIK_LAYER_MAPS, TRUE); // Includes hidden map layer types int num_maps = g_list_length(vmls); if (!num_maps) {