From: Rob Norris Date: Thu, 24 Apr 2014 19:49:04 +0000 (+0100) Subject: SF Features#100: Allow 'easy' changing of the time of trackpoints & waypoints. X-Git-Url: https://git.street.me.uk/andy/viking.git/commitdiff_plain/93cfbc211a3fde049f0c8eb3d619186fd31dd7aa?ds=sidebyside SF Features#100: Allow 'easy' changing of the time of trackpoints & waypoints. Add and use a dialog to get the time and date in the users timezone to allow setting the timestamp. The dialog uses the calendar widget to get the date and spin boxes for the hours, minutes and seconds. --- diff --git a/src/Makefile.am b/src/Makefile.am index 7c732dbe..14d2a075 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -128,6 +128,7 @@ libviking_a_SOURCES = \ googlesearch.c googlesearch.h \ dem.c dem.h \ vikdemlayer.h vikdemlayer.c \ + vikdatetime_edit_dialog.c vikdatetime_edit_dialog.h \ vikfilelist.c vikfilelist.h \ vikexttool.c vikexttool.h \ vikexttools.c vikexttools.h \ diff --git a/src/dialog.c b/src/dialog.c index 79d0f38b..ca6bcdb1 100644 --- a/src/dialog.c +++ b/src/dialog.c @@ -34,6 +34,7 @@ #include "vikgoto.h" #include "util.h" #include "geotag_exif.h" +#include "vikdatetime_edit_dialog.h" #include @@ -180,6 +181,34 @@ void a_dialog_response_accept ( GtkDialog *dialog ) gtk_dialog_response ( dialog, GTK_RESPONSE_ACCEPT ); } +static void update_time ( GtkWidget *widget, time_t timestamp ) +{ + gchar tmp_str[64]; + strftime ( tmp_str, sizeof(tmp_str), "%c", localtime(&(timestamp)) ); + gtk_button_set_label ( GTK_BUTTON(widget), tmp_str ); +} + +static VikWaypoint *edit_wp; + +static void time_edit_click ( GtkWidget *widget, VikWaypoint *wp ) +{ + GTimeZone *gtz = g_time_zone_new_local (); + time_t mytime = vik_datetime_edit_dialog ( GTK_WINDOW(gtk_widget_get_toplevel(widget)), + _("Date/Time Edit"), + wp->timestamp, + gtz ); + g_time_zone_unref ( gtz ); + + // Was the dialog cancelled? + if ( mytime == 0 ) + return; + + // Otherwise use new value in the edit buffer + edit_wp->timestamp = mytime; + + update_time ( widget, edit_wp->timestamp ); +} + static void symbol_entry_changed_cb(GtkWidget *combo, GtkListStore *store) { GtkTreeIter iter; @@ -213,7 +242,7 @@ gchar *a_dialog_waypoint ( GtkWindow *parent, gchar *default_name, VikTrwLayer * GtkWidget *latlabel, *lonlabel, *namelabel, *latentry, *lonentry, *altentry, *altlabel, *nameentry=NULL; GtkWidget *commentlabel, *commententry, *descriptionlabel, *descriptionentry, *imagelabel, *imageentry, *symbollabel, *symbolentry; GtkWidget *timelabel = NULL; - GtkWidget *timevaluelabel = NULL; // No editing of time allowed ATM + GtkWidget *timevaluebutton = NULL; GtkWidget *hasGeotagCB = NULL; GtkWidget *consistentGeotagCB = NULL; GtkListStore *store; @@ -346,12 +375,15 @@ gchar *a_dialog_waypoint ( GtkWindow *parent, gchar *default_name, VikTrwLayer * } } + + if ( !edit_wp ) + edit_wp = vik_waypoint_new (); if ( !is_new && wp->has_timestamp ) { - gchar tmp_str[64]; timelabel = gtk_label_new ( _("Time:") ); - timevaluelabel = gtk_label_new ( NULL ); - strftime ( tmp_str, sizeof(tmp_str), "%c", localtime(&(wp->timestamp)) ); - gtk_label_set_text ( GTK_LABEL(timevaluelabel), tmp_str ); + timevaluebutton = gtk_button_new(); + gtk_button_set_relief ( GTK_BUTTON(timevaluebutton), GTK_RELIEF_NONE ); + update_time ( timevaluebutton, wp->timestamp ); + g_signal_connect ( G_OBJECT(timevaluebutton), "clicked", G_CALLBACK(time_edit_click), edit_wp ); } gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), latlabel, FALSE, FALSE, 0); @@ -360,7 +392,7 @@ gchar *a_dialog_waypoint ( GtkWindow *parent, gchar *default_name, VikTrwLayer * gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), lonentry, FALSE, FALSE, 0); if ( timelabel ) { gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), timelabel, FALSE, FALSE, 0); - gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), timevaluelabel, FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), timevaluebutton, FALSE, FALSE, 0); } gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), altlabel, FALSE, FALSE, 0); gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), altentry, FALSE, FALSE, 0); @@ -420,6 +452,10 @@ gchar *a_dialog_waypoint ( GtkWindow *parent, gchar *default_name, VikTrwLayer * vik_waypoint_set_image ( wp, vik_file_entry_get_filename ( VIK_FILE_ENTRY(imageentry) ) ); if ( wp->image && *(wp->image) && (!a_thumbnails_exists(wp->image)) ) a_thumbnails_create ( wp->image ); + if ( edit_wp->timestamp ) { + wp->timestamp = edit_wp->timestamp; + wp->has_timestamp = TRUE; + } GtkTreeIter iter, first; gtk_tree_model_get_iter_first ( GTK_TREE_MODEL(store), &first ); diff --git a/src/vikdatetime_edit_dialog.c b/src/vikdatetime_edit_dialog.c new file mode 100644 index 00000000..528b4410 --- /dev/null +++ b/src/vikdatetime_edit_dialog.c @@ -0,0 +1,138 @@ +/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */ +/* + * viking -- GPS Data and Topo Analyzer, Explorer, and Manager + * + * Copyright (C) 2014, Rob Norris + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#include "vikdatetime_edit_dialog.h" + +// Show leading zeros +static gboolean on_output ( GtkSpinButton *spin, gpointer data ) +{ + GtkAdjustment *adjustment = gtk_spin_button_get_adjustment ( spin ); + gint value = (gint)gtk_adjustment_get_value ( adjustment ); + gchar *text = g_strdup_printf ( "%02d", value ); + gtk_entry_set_text ( GTK_ENTRY (spin), text ); + g_free ( text ); + + return TRUE; +} + +/** + * vik_datetime_edit_dialog: + * @parent: The parent window + * @title: The title to use for the dialog + * @initial_time: The inital date/time to be shown + * @tz: The #GTimeZone this dialog will operate in + * + * Returns: A time selected by the user via this dialog + * Even though a time of zero is notionally valid - consider it unlikely to be actually wanted! + * Thus if the time is zero then the dialog was cancelled or somehow an invalid date was encountered. + */ +time_t vik_datetime_edit_dialog ( GtkWindow *parent, const gchar *title, time_t initial_time, GTimeZone *tz ) +{ + g_return_val_if_fail ( tz, 0 ); + + GtkWidget *dialog = gtk_dialog_new_with_buttons ( title, + parent, + GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, + GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, + NULL ); + + gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT ); + GtkWidget *response_w = NULL; +#if GTK_CHECK_VERSION (2, 20, 0) + response_w = gtk_dialog_get_widget_for_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT ); +#endif + + GtkWidget *label; + GtkWidget *cal = gtk_calendar_new (); + + // Set according to the given date/time + timezone for display + GDateTime *gdt_in = g_date_time_new_from_unix_utc ( (gint64)initial_time ); + GDateTime *gdt_tz = g_date_time_to_timezone ( gdt_in, tz ); + g_date_time_unref ( gdt_in ); + + gtk_calendar_select_month ( GTK_CALENDAR(cal), g_date_time_get_month(gdt_tz)-1, g_date_time_get_year (gdt_tz) ); + gtk_calendar_select_day ( GTK_CALENDAR(cal), g_date_time_get_day_of_month(gdt_tz) ); + + GtkWidget *hbox_time = gtk_hbox_new ( FALSE, 1 ); + + label = gtk_label_new ( g_date_time_get_timezone_abbreviation(gdt_tz) ); + gtk_box_pack_start ( GTK_BOX(hbox_time), label, FALSE, FALSE, 5 ); + + GtkWidget *sb_hours = gtk_spin_button_new_with_range ( 0.0, 23.0, 1.0 ); + gtk_box_pack_start ( GTK_BOX(hbox_time), sb_hours, FALSE, FALSE, 0 ); + gtk_spin_button_set_value ( GTK_SPIN_BUTTON(sb_hours), g_date_time_get_hour(gdt_tz) ); + g_signal_connect ( sb_hours, "output", G_CALLBACK(on_output), NULL ); + + label = gtk_label_new ( ":" ); + gtk_box_pack_start ( GTK_BOX(hbox_time), label, FALSE, FALSE, 0 ); + + GtkWidget *sb_minutes = gtk_spin_button_new_with_range ( 0.0, 59.0, 1.0 ); + gtk_box_pack_start ( GTK_BOX(hbox_time), sb_minutes, FALSE, FALSE, 0); + gtk_spin_button_set_value ( GTK_SPIN_BUTTON(sb_minutes), g_date_time_get_minute(gdt_tz) ); + g_signal_connect ( sb_minutes, "output", G_CALLBACK(on_output), NULL ); + + label = gtk_label_new ( ":" ); + gtk_box_pack_start(GTK_BOX(hbox_time), label, FALSE, FALSE, 0); + + GtkWidget *sb_seconds = gtk_spin_button_new_with_range ( 0.0, 59.0, 1.0 ); + gtk_box_pack_start ( GTK_BOX(hbox_time), sb_seconds, FALSE, FALSE, 0 ); + gtk_spin_button_set_value ( GTK_SPIN_BUTTON(sb_seconds), g_date_time_get_second(gdt_tz) ); + g_signal_connect ( sb_seconds, "output", G_CALLBACK(on_output), NULL ); + + gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), cal, FALSE, FALSE, 0 ); + gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), hbox_time, FALSE, FALSE, 5 ); + + if ( response_w ) + gtk_widget_grab_focus ( response_w ); + + g_date_time_unref ( gdt_tz ); + + gtk_widget_show_all ( dialog ); + if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) != GTK_RESPONSE_ACCEPT ) { + gtk_widget_destroy ( dialog ); + return 0; + } + + // Read values + guint year = 0; + guint month = 0; + guint day = 0; + guint hours = 0; + guint minutes = 0; + guint seconds = 0; + + gtk_calendar_get_date ( GTK_CALENDAR(cal), &year, &month, &day ); + hours = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(sb_hours) ); + minutes = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(sb_minutes) ); + seconds = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(sb_seconds) ); + + gtk_widget_destroy(dialog); + + time_t ans = initial_time; + GDateTime *gdt_ans = g_date_time_new ( tz, year, month+1, day, hours, minutes, (gdouble)seconds ); + if ( gdt_ans ) { + ans = g_date_time_to_unix ( gdt_ans ); + g_date_time_unref ( gdt_ans ); + } + + return ans; +} diff --git a/src/vikdatetime_edit_dialog.h b/src/vikdatetime_edit_dialog.h new file mode 100644 index 00000000..d513e2b0 --- /dev/null +++ b/src/vikdatetime_edit_dialog.h @@ -0,0 +1,35 @@ +/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */ +/* + * viking -- GPS Data and Topo Analyzer, Explorer, and Manager + * + * Copyright (C) 2014, Rob Norris + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#ifndef _VIK_DATE_TIME_EDIT_H +#define _VIK_DATE_TIME_EDIT_H + +#include +#include +#include + +G_BEGIN_DECLS + +time_t vik_datetime_edit_dialog ( GtkWindow *parent, const gchar *title, time_t initial_time, GTimeZone *tz ); + +G_END_DECLS + +#endif diff --git a/src/viktrwlayer_tpwin.c b/src/viktrwlayer_tpwin.c index 5f7bf812..45d23591 100644 --- a/src/viktrwlayer_tpwin.c +++ b/src/viktrwlayer_tpwin.c @@ -35,12 +35,14 @@ #include "vikwaypoint.h" #include "dialog.h" #include "globals.h" +#include "vikdatetime_edit_dialog.h" struct _VikTrwLayerTpwin { GtkDialog parent; GtkSpinButton *lat, *lon, *alt, *ts; GtkWidget *trkpt_name; - GtkLabel *course, *localtime, *diff_dist, *diff_time, *diff_speed, *speed, *hdop, *vdop, *pdop, *sat; + GtkWidget *localtime; + GtkLabel *course, *diff_dist, *diff_time, *diff_speed, *speed, *hdop, *vdop, *pdop, *sat; // Previously these buttons were in a glist, however I think the ordering behaviour is implicit // thus control manually to ensure operating on the correct button GtkWidget *button_close; @@ -87,11 +89,11 @@ static void tpwin_update_times ( VikTrwLayerTpwin *tpwin, VikTrackpoint *tp ) if ( tp->has_timestamp ) { gtk_spin_button_set_value ( tpwin->ts, tp->timestamp ); strftime ( tmp_str, sizeof(tmp_str), "%c", localtime(&(tp->timestamp)) ); - gtk_label_set_text ( tpwin->localtime, tmp_str ); + gtk_button_set_label ( GTK_BUTTON(tpwin->localtime), tmp_str ); } else { gtk_spin_button_set_value ( tpwin->ts, 0 ); - gtk_label_set_text (tpwin->localtime, NULL ); + gtk_button_set_label ( GTK_BUTTON(tpwin->localtime), "" ); } } @@ -145,6 +147,36 @@ static void tpwin_sync_ts_to_tp ( VikTrwLayerTpwin *tpwin ) } } +/** + * tpwin_sync_time_to_tp: + * + */ +static void tpwin_sync_time_to_tp ( VikTrwLayerTpwin *tpwin ) +{ + if ( !tpwin->cur_tp || tpwin->sync_to_tp_block ) + return; + // Currently disable setting the time via this way when the point doesn't have one + if ( !tpwin->cur_tp->has_timestamp ) + return; + + GTimeZone *gtz = g_time_zone_new_local (); + guint mytime = vik_datetime_edit_dialog ( GTK_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(&tpwin->parent))), + _("Date/Time Edit"), + tpwin->cur_tp->timestamp, + gtz ); + g_time_zone_unref ( gtz ); + + // Was the dialog cancelled? + if ( mytime == 0 ) + return; + + // Otherwise use the new value + tpwin->cur_tp->timestamp = mytime; + // TODO: consider warning about unsorted times? + + tpwin_update_times ( tpwin, tpwin->cur_tp ); +} + static gboolean tpwin_set_name ( VikTrwLayerTpwin *tpwin ) { if ( tpwin->cur_tp && (!tpwin->sync_to_tp_block) ) { @@ -204,7 +236,9 @@ VikTrwLayerTpwin *vik_trw_layer_tpwin_new ( GtkWindow *parent ) g_signal_connect_swapped ( G_OBJECT(tpwin->trkpt_name), "focus-out-event", G_CALLBACK(tpwin_set_name), tpwin ); tpwin->course = GTK_LABEL(gtk_label_new(NULL)); - tpwin->localtime = GTK_LABEL(gtk_label_new(NULL)); + tpwin->localtime = gtk_button_new(); + gtk_button_set_relief ( GTK_BUTTON(tpwin->localtime), GTK_RELIEF_NONE ); + g_signal_connect_swapped ( G_OBJECT(tpwin->localtime), "clicked", G_CALLBACK(tpwin_sync_time_to_tp), tpwin ); tpwin->lat = GTK_SPIN_BUTTON(gtk_spin_button_new( GTK_ADJUSTMENT(gtk_adjustment_new ( 0, -90, 90, 0.00005, 0.01, 0 )), 0.00005, 6)); @@ -281,13 +315,14 @@ void vik_trw_layer_tpwin_set_empty ( VikTrwLayerTpwin *tpwin ) gtk_editable_delete_text ( GTK_EDITABLE(tpwin->trkpt_name), 0, -1 ); gtk_widget_set_sensitive ( tpwin->trkpt_name, FALSE ); - gtk_label_set_text ( tpwin->localtime, NULL ); + gtk_button_set_label ( GTK_BUTTON(tpwin->localtime), "" ); gtk_label_set_text ( tpwin->course, NULL ); gtk_widget_set_sensitive ( GTK_WIDGET(tpwin->lat), FALSE ); gtk_widget_set_sensitive ( GTK_WIDGET(tpwin->lon), FALSE ); gtk_widget_set_sensitive ( GTK_WIDGET(tpwin->alt), FALSE ); gtk_widget_set_sensitive ( GTK_WIDGET(tpwin->ts), FALSE ); + gtk_widget_set_sensitive ( GTK_WIDGET(tpwin->localtime), FALSE ); // Only keep close button enabled gtk_widget_set_sensitive ( tpwin->button_insert, FALSE ); @@ -334,6 +369,7 @@ void vik_trw_layer_tpwin_set_tp ( VikTrwLayerTpwin *tpwin, GList *tpl, const gch gtk_widget_set_sensitive ( GTK_WIDGET(tpwin->lon), TRUE ); gtk_widget_set_sensitive ( GTK_WIDGET(tpwin->alt), TRUE ); gtk_widget_set_sensitive ( GTK_WIDGET(tpwin->ts), tp->has_timestamp ); + gtk_widget_set_sensitive ( GTK_WIDGET(tpwin->localtime), tp->has_timestamp ); vik_trw_layer_tpwin_set_track_name ( tpwin, track_name );