Hello list,

* for better usage the timestamp label was made selectable
* the timestamp label autoupdates if the date_time_picker is changed, just as 
one would expect
* the local time zone is shown to let the user easily determine the format of 
the timestamps
* conversion between local and utc time is done by GDateTime
* the date_time_picker was ripped out of free tux tv project 
(http://freetuxtv.googlecode.com/svn/trunk/src/)
* the date_time_picker was modified to print a timezone label (the one of the 
GDateTime instance it is fed with)

* the methods to read and write from/to the date_time_picker should be in sync 
wrt timezone (i.e. if a GDateTime object using the local timezone is 
written/fed to it, the corresponding _to_local() getter should be used to 
read/get a changed time.  vice versa, if a GDateTime object in UTC is written 
to it, _to_utc() getter should be used to retrieve values)

* for the viking project usage of local timezone in the UI is hardcoded (time 
values are converted back and forth to UTC/Z timezone internally for GPX 
serializer)

* a tooltip has been added to explain unix epoch value
* both, unix epoch value and corresponding ISO Date may be selected for 
clipboard transferal


Cheers!
--- viking-1.5.1/src/gtk-date-time-picker.c	1970-01-01 01:00:00.000000000 +0100
+++ viking-1.5.1.new/src/gtk-date-time-picker.c	2015-03-11 11:03:11.658382200 +0100
@@ -0,0 +1,590 @@
+/*
+ * freetuxtv
+ * Copyright (C) Eric Beuque 2011 <eric.beu...@gmail.com>
+ * 
+ * freetuxtv 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 3 of the License, or
+ * (at your option) any later version.
+ * 
+ * freetuxtv 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#define _XOPEN_SOURCE /* For strptime */ 
+#include <time.h>
+
+#include "gtk-date-time-picker.h"
+
+#include <stdlib.h>
+
+typedef struct _GtkDateTimePickerPrivate GtkDateTimePickerPrivate;
+struct _GtkDateTimePickerPrivate
+{
+	gchar* szDateFormat;
+
+	GtkWidget* date_entry;
+	GtkWidget* hour_spinbutton;
+	GtkWidget* minute_spinbutton;
+	GtkWidget* seconds_spinbutton;
+	
+	GtkWidget* calendar_window;
+	GtkWidget* calendar;
+
+	GtkLabel* tzlabel;
+};
+
+#define GTK_DATE_TIME_PICKER_PRIVATE(o)  (G_TYPE_INSTANCE_GET_PRIVATE ((o), GTK_TYPE_DATE_TIME_PICKER, GtkDateTimePickerPrivate))
+
+enum
+{
+	PROP_0,
+
+	PROP_DATE_FORMAT,
+	PROP_SHOW_DATE,
+	PROP_SHOW_TIME
+};
+
+enum
+{
+	SIGNAL_DATETIME_CHANGED,
+
+	LAST_SIGNAL
+};
+
+
+static guint date_time_picker_signals[LAST_SIGNAL] = { 0 };
+
+G_DEFINE_TYPE (GtkDateTimePicker, gtk_date_time_picker, GTK_TYPE_HBOX);
+
+/*
+static void
+check_date_text_handler (
+    GtkEntry *entry, const gchar *text,
+    gint length, gint *position, gpointer data);
+	*/
+/*
+static gboolean
+on_dateentry_focusin (GtkWidget *widget, GdkEvent  *event, gpointer user_data);
+
+static gboolean
+on_dateentry_focusout (GtkWidget *widget, GdkEvent  *event, gpointer user_data);
+*/
+
+static gboolean
+on_spinbutton_output (GtkSpinButton *spin, gpointer data);
+
+static void
+on_editable_changed (GtkEditable *editable, gpointer user_data);
+
+static gboolean
+get_dmy_if_valid(GtkDateTimePicker* picker, gint* day, gint* month, gint* year);
+
+static void
+gtk_date_time_picker_init (GtkDateTimePicker *object)
+{
+	GtkDateTimePickerPrivate* priv;
+	priv = GTK_DATE_TIME_PICKER_PRIVATE(object);
+
+	priv->szDateFormat = NULL;
+	
+	priv->date_entry = NULL;
+	priv->hour_spinbutton = NULL;
+	priv->minute_spinbutton = NULL;
+	priv->seconds_spinbutton = NULL;
+
+	priv->calendar_window = NULL;
+	priv->calendar = NULL;
+
+	GtkWidget *label, *hbox_time;
+	gtk_box_set_spacing(GTK_BOX(object), 2);
+
+	// Date picker
+	priv->date_entry = gtk_entry_new ();
+	//priv->date_entry = gtk_combo_box_text_new_with_entry ();
+	//gtk_entry_set_icon_from_stock (GTK_ENTRY(priv->date_entry), GTK_ENTRY_ICON_SECONDARY, "gtk-edit");
+	gtk_entry_set_width_chars (GTK_ENTRY(priv->date_entry), 12);
+	g_signal_connect(G_OBJECT(priv->date_entry), "changed",
+	    G_CALLBACK(on_editable_changed),
+	    (gpointer)object);
+	gtk_widget_show(priv->date_entry);
+	gtk_box_pack_start(GTK_BOX(object), priv->date_entry, FALSE, FALSE, 0);
+	/*
+	g_signal_connect(G_OBJECT(priv->date_entry), "focus-in-event",
+	    G_CALLBACK(on_dateentry_focusin),
+	    (gpointer)object);
+	g_signal_connect(G_OBJECT(priv->date_entry), "focus-out-event",
+	    G_CALLBACK(on_dateentry_focusout),
+	    (gpointer)object);
+	g_signal_connect(G_OBJECT(entry), "insert_text",
+	    G_CALLBACK(check_date_text_handler),
+	    NULL);*/
+
+	// Hour
+	hbox_time = gtk_hbox_new(GTK_ORIENTATION_HORIZONTAL, 0);
+	gtk_widget_show(hbox_time);
+	gtk_box_pack_start(GTK_BOX(object), hbox_time, FALSE, FALSE, 0);
+	
+	priv->hour_spinbutton = gtk_spin_button_new_with_range (0.0, 23.0, 1.0);
+	gtk_spin_button_set_wrap (GTK_SPIN_BUTTON(priv->hour_spinbutton), TRUE);
+	g_signal_connect(G_OBJECT(priv->hour_spinbutton), "output",
+	    G_CALLBACK(on_spinbutton_output),
+	    NULL);
+	g_signal_connect(G_OBJECT(priv->hour_spinbutton), "changed",
+	    G_CALLBACK(on_editable_changed),
+	    (gpointer)object);
+	gtk_widget_show(priv->hour_spinbutton);
+	gtk_entry_set_width_chars (GTK_ENTRY(priv->hour_spinbutton), 2);
+	gtk_box_pack_start(GTK_BOX(hbox_time), priv->hour_spinbutton, FALSE, FALSE, 0);
+
+	label = gtk_label_new(":");
+	gtk_widget_show(label);
+	gtk_box_pack_start(GTK_BOX(hbox_time), label, FALSE, FALSE, 0);
+
+	priv->minute_spinbutton = gtk_spin_button_new_with_range (0.0, 59.0, 1.0);
+	gtk_spin_button_set_wrap (GTK_SPIN_BUTTON(priv->minute_spinbutton), TRUE);
+	g_signal_connect(G_OBJECT(priv->minute_spinbutton), "output",
+	    G_CALLBACK(on_spinbutton_output),
+	    NULL);
+	g_signal_connect(G_OBJECT(priv->minute_spinbutton), "changed",
+	    G_CALLBACK(on_editable_changed),
+	    (gpointer)object);
+	gtk_widget_show(priv->minute_spinbutton);
+	gtk_entry_set_width_chars (GTK_ENTRY(priv->minute_spinbutton), 2);
+	gtk_box_pack_start(GTK_BOX(hbox_time), priv->minute_spinbutton, FALSE, FALSE, 0);
+
+	label = gtk_label_new(":");
+	gtk_widget_show(label);
+	gtk_box_pack_start(GTK_BOX(hbox_time), label, FALSE, FALSE, 0);
+
+	priv->seconds_spinbutton = gtk_spin_button_new_with_range (0.0, 59.0, 1.0);
+	gtk_spin_button_set_wrap (GTK_SPIN_BUTTON(priv->seconds_spinbutton), TRUE);
+	g_signal_connect(G_OBJECT(priv->seconds_spinbutton), "output",
+	    G_CALLBACK(on_spinbutton_output),
+	    NULL);
+	g_signal_connect(G_OBJECT(priv->seconds_spinbutton), "changed",
+	    G_CALLBACK(on_editable_changed),
+	    (gpointer)object);
+	gtk_widget_show(priv->seconds_spinbutton);
+	gtk_entry_set_width_chars (GTK_ENTRY(priv->seconds_spinbutton), 2);
+	gtk_box_pack_start(GTK_BOX(hbox_time), priv->seconds_spinbutton, FALSE, FALSE, 0);
+
+	label = gtk_label_new("");
+	gtk_widget_show(label);
+	gtk_box_pack_start(GTK_BOX(hbox_time), label, FALSE, FALSE, 0);
+	gtk_label_set_width_chars(GTK_LABEL(label), 4);
+	priv->tzlabel = GTK_LABEL(label);
+	
+	//gtk_widget_set_sensitive (GTK_WIDGET(priv->date_entry), FALSE);
+	//gtk_widget_set_sensitive (GTK_WIDGET(priv->hour_spinbutton), FALSE);
+	//gtk_widget_set_sensitive (GTK_WIDGET(priv->minute_spinbutton), FALSE);
+}
+
+static void
+gtk_date_time_picker_finalize (GObject *object)
+{
+	GtkDateTimePickerPrivate* priv;
+	priv = GTK_DATE_TIME_PICKER_PRIVATE(object);
+
+	if(priv->calendar_window){
+		gtk_widget_destroy(priv->calendar_window);
+		priv->calendar_window = NULL;
+	}
+
+	if(priv->szDateFormat){
+		g_free(priv->szDateFormat);
+		priv->szDateFormat = NULL;
+	}
+	
+	G_OBJECT_CLASS (gtk_date_time_picker_parent_class)->finalize (object);
+}
+
+static void
+gtk_date_time_picker_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
+{
+	g_return_if_fail (GTK_IS_DATE_TIME_PICKER (object));
+	
+	GtkDateTimePickerPrivate* priv;
+	priv = GTK_DATE_TIME_PICKER_PRIVATE(object);
+
+	switch (prop_id)
+	{
+		case PROP_DATE_FORMAT:
+			if(priv->szDateFormat){
+				g_free(priv->szDateFormat);
+				priv->szDateFormat = NULL;
+			}
+			priv->szDateFormat = g_strdup(g_value_get_string(value));
+			break;
+		case PROP_SHOW_DATE:
+			/* TODO: Add setter for "show-date" property here */
+			break;
+		case PROP_SHOW_TIME:
+			/* TODO: Add setter for "show-time" property here */
+			break;
+		default:
+			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+			break;
+	}
+}
+
+static void
+gtk_date_time_picker_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
+{
+	g_return_if_fail (GTK_IS_DATE_TIME_PICKER (object));
+	
+	GtkDateTimePickerPrivate* priv;
+	priv = GTK_DATE_TIME_PICKER_PRIVATE(object);
+
+	switch (prop_id)
+	{
+		case PROP_DATE_FORMAT:
+			g_value_set_string(value, priv->szDateFormat);
+			break;
+		case PROP_SHOW_DATE:
+			/* TODO: Add getter for "show-date" property here */
+			break;
+		case PROP_SHOW_TIME:
+			/* TODO: Add getter for "show-time" property here */
+			break;
+		default:
+			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+			break;
+	}
+}
+
+static void
+gtk_date_time_picker_datetime_changed (GtkDateTimePicker *self, gpointer user_data)
+{
+	g_return_if_fail(self != NULL);
+	g_return_if_fail(GTK_IS_DATE_TIME_PICKER(self));
+	
+	GtkDateTimePickerPrivate* priv;
+	priv = GTK_DATE_TIME_PICKER_PRIVATE(self);
+	
+	const gchar* szCurIcon;
+	const gchar* szNewIcon;
+	szCurIcon = gtk_entry_get_icon_stock (GTK_ENTRY(priv->date_entry), GTK_ENTRY_ICON_SECONDARY);
+	
+	if(get_dmy_if_valid (self, NULL, NULL, NULL)){
+		szNewIcon = GTK_STOCK_YES;
+
+		GDateTime *dt = gtk_date_time_picker_to_local ( self );
+		gtk_label_set_text ( GTK_LABEL(priv->tzlabel), g_date_time_get_timezone_abbreviation(dt) );
+		g_date_time_unref ( dt );
+	}else{
+		szNewIcon = GTK_STOCK_NO;
+	}
+	if(szCurIcon == NULL || g_ascii_strcasecmp(szCurIcon, szNewIcon) != 0){
+		gtk_entry_set_icon_from_stock (GTK_ENTRY(priv->date_entry),
+		    GTK_ENTRY_ICON_SECONDARY, szNewIcon);
+	}
+}
+
+static void
+gtk_date_time_picker_class_init (GtkDateTimePickerClass *klass)
+{
+	GObjectClass* object_class = G_OBJECT_CLASS (klass);
+	//GtkBinClass* parent_class = GTK_BIN_CLASS (klass);
+
+	g_type_class_add_private (klass, sizeof (GtkDateTimePickerPrivate));
+
+	object_class->finalize = gtk_date_time_picker_finalize;
+	object_class->set_property = gtk_date_time_picker_set_property;
+	object_class->get_property = gtk_date_time_picker_get_property;
+
+	klass->datetime_changed = gtk_date_time_picker_datetime_changed;
+
+	g_object_class_install_property (object_class,
+	    PROP_DATE_FORMAT,
+	    g_param_spec_string ("date-format",
+		    "Date Format",
+		    "Date reprensentation format",
+		    "%m/%d/%y",
+		    G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+	g_object_class_install_property (object_class,
+	    PROP_SHOW_DATE,
+	    g_param_spec_boolean ("show-date",
+		    "Show date",
+		    "Display the date picker",
+		    TRUE,
+		    G_PARAM_READWRITE));
+
+	g_object_class_install_property (object_class,
+	    PROP_SHOW_TIME,
+	    g_param_spec_boolean ("show-time",
+		    "Show time",
+		    "Display the time picker",
+		    TRUE,
+		    G_PARAM_READWRITE));
+
+	date_time_picker_signals[SIGNAL_DATETIME_CHANGED] =
+		g_signal_new ("datetime-changed",
+		    G_OBJECT_CLASS_TYPE (klass),
+		    G_SIGNAL_RUN_FIRST,
+		    G_STRUCT_OFFSET (GtkDateTimePickerClass, datetime_changed),
+		    NULL, NULL,
+		    g_cclosure_marshal_VOID__VOID,
+		    G_TYPE_NONE, 0);
+}
+
+
+GtkWidget*
+gtk_date_time_picker_new (gchar* date_format)
+{
+	GtkWidget* self;
+
+	self = (GtkWidget*)g_object_new (GTK_TYPE_DATE_TIME_PICKER,
+	    "date-format", date_format, NULL);
+
+	return self;
+}
+
+void
+gtk_date_time_picker_set_datetime (GtkDateTimePicker* picker, GDateTime *datetime)
+{
+	g_return_if_fail(picker != NULL);
+	g_return_if_fail(GTK_IS_DATE_TIME_PICKER(picker));
+	g_return_if_fail(datetime != NULL);
+	
+	GtkDateTimePickerPrivate* priv;
+	priv = GTK_DATE_TIME_PICKER_PRIVATE(picker);
+	
+	gchar* szTmp;
+	
+	szTmp = g_date_time_format (datetime, priv->szDateFormat);
+	//gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT(priv->date_entry), szTmp);
+	//gtk_combo_box_set_active (GTK_COMBO_BOX(priv->date_entry), 0);
+	gtk_entry_set_text(GTK_ENTRY(priv->date_entry), szTmp);
+
+	gtk_spin_button_set_value (GTK_SPIN_BUTTON(priv->hour_spinbutton),
+	    g_date_time_get_hour (datetime));
+
+	gtk_spin_button_set_value (GTK_SPIN_BUTTON(priv->minute_spinbutton),
+	    g_date_time_get_minute (datetime));
+
+	gtk_spin_button_set_value (GTK_SPIN_BUTTON(priv->seconds_spinbutton),
+	    g_date_time_get_seconds (datetime));
+
+	//FIXME gtk_label_set_text (GTK_LABEL(priv->tzlabel),
+	//FIXME    g_date_time_get_timezone_abbreviation (datetime));
+}
+
+GDateTime*
+gtk_date_time_picker_get_datetime (GtkDateTimePicker* picker, GTimeZone *tz)
+{
+	g_return_val_if_fail(picker != NULL, NULL);
+	g_return_val_if_fail(GTK_IS_DATE_TIME_PICKER(picker), NULL);
+	g_return_val_if_fail(tz != NULL, NULL);
+	
+	GtkDateTimePickerPrivate* priv;
+	priv = GTK_DATE_TIME_PICKER_PRIVATE(picker);
+	
+	GDateTime* datetime = NULL;
+	gint year, month, day, hour, minute, seconds;
+
+	const gchar *szTmp;
+	
+	szTmp = gtk_entry_get_text(GTK_ENTRY(priv->hour_spinbutton));
+	hour = atoi(szTmp);
+
+	szTmp = gtk_entry_get_text(GTK_ENTRY(priv->minute_spinbutton));
+	minute = atoi(szTmp);
+
+	szTmp = gtk_entry_get_text(GTK_ENTRY(priv->seconds_spinbutton));
+	seconds = atoi(szTmp);
+
+	if(get_dmy_if_valid (picker, &day, &month, &year)){
+		datetime = g_date_time_new (tz, year, month, day, hour, minute, seconds);
+	}
+
+	return datetime;
+}
+
+GDateTime*
+gtk_date_time_picker_to_local (GtkDateTimePicker* picker)
+{
+	GTimeZone *tz = g_time_zone_new_local ();
+	GDateTime *dt = gtk_date_time_picker_get_datetime (picker, tz);
+	g_time_zone_unref ( tz );
+	return dt;
+}
+
+GDateTime*
+gtk_date_time_picker_to_utc (GtkDateTimePicker* picker)
+{
+	GTimeZone *tz = g_time_zone_new_utc ();
+	GDateTime *dt = gtk_date_time_picker_get_datetime (picker, tz);
+	g_time_zone_unref ( tz );
+	return dt;
+}
+
+/*
+static gboolean
+on_dateentry_focusin (GtkWidget *widget, GdkEvent  *event, gpointer user_data)
+{
+	GtkDateTimePicker *self;
+	self = (GtkDateTimePicker*)user_data;
+	
+	GtkDateTimePickerPrivate* priv;
+	priv = GTK_DATE_TIME_PICKER_PRIVATE(self);
+	
+	// Calendar
+	if(priv->calendar_window == NULL){
+		
+		priv->calendar_window = gtk_window_new(GTK_WINDOW_POPUP);
+		
+		GtkWidget *parent = gtk_widget_get_toplevel (widget);
+		if (gtk_widget_is_toplevel (parent))
+		{
+			gtk_window_set_transient_for(GTK_WINDOW(priv->calendar_window), GTK_WINDOW(parent));
+		}
+
+		GtkWidget* vbox;
+		vbox = gtk_vbox_new(FALSE, 0);
+
+		gtk_box_pack_start(GTK_BOX(vbox), gtk_entry_new(), TRUE, TRUE, 0);
+
+		//gtk_window_set_modal (GTK_WINDOW(priv->calendar_window), TRUE);
+		priv->calendar = gtk_calendar_new();
+		//gtk_calendar_set_display_options(GTK_CALENDAR(priv->calendar), GTK_CALENDAR_SHOW_DETAILS);
+		//gtk_container_add(GTK_CONTAINER(priv->calendar_window), priv->calendar);
+		gtk_box_pack_start(GTK_BOX(vbox), priv->calendar, TRUE, TRUE, 0);
+		gtk_container_add(GTK_CONTAINER(priv->calendar_window), vbox);
+	}
+	gtk_window_set_position (GTK_WINDOW(priv->calendar_window), GTK_WIN_POS_MOUSE);
+	gtk_widget_show_all(priv->calendar_window);
+
+	return FALSE;
+}
+
+static gboolean
+on_dateentry_focusout (GtkWidget *widget, GdkEvent  *event, gpointer user_data)
+{
+	GtkDateTimePicker *self;
+	self = (GtkDateTimePicker*)user_data;
+	
+	GtkDateTimePickerPrivate* priv;
+	priv = GTK_DATE_TIME_PICKER_PRIVATE(self);
+
+	if(priv->calendar_window){
+		gtk_widget_hide(priv->calendar_window);
+	}
+
+	return FALSE;
+}
+*/
+
+/*
+static void
+check_date_text_handler (
+    GtkEntry *entry, const gchar *text,
+    gint length, gint *position, gpointer data)
+{
+	GtkEditable *editable = GTK_EDITABLE(entry);
+	int i, count=0;
+	gchar *result = g_new (gchar, length);
+
+	for (i=0; i < length; i++) {
+		//if((i>=0 && i<=1) || (i>= && i<=1)
+		if (!isalpha(text[i]))
+			continue;
+		result[count++] = islower(text[i]) ? toupper(text[i]) : text[i];
+	}
+
+	if (count > 0) {
+		g_signal_handlers_block_by_func (G_OBJECT (editable),
+		    G_CALLBACK (check_date_text_handler),
+		    data);
+		gtk_editable_insert_text (editable, result, count, position);
+		g_signal_handlers_unblock_by_func (G_OBJECT (editable),
+		    G_CALLBACK (check_date_text_handler),
+		    data);
+	}
+	g_signal_stop_emission_by_name (G_OBJECT (editable), "insert_text");
+
+	g_free (result);
+}
+*/
+
+static gboolean
+on_spinbutton_output (GtkSpinButton *spin, gpointer data)
+{
+   GtkAdjustment *adj;
+   gchar *text;
+   int value;
+   adj = gtk_spin_button_get_adjustment (spin);
+   value = (int)gtk_adjustment_get_value (adj);
+   text = g_strdup_printf ("%02d", value);
+   gtk_entry_set_text (GTK_ENTRY (spin), text);
+   g_free (text);
+   
+   return TRUE;
+}
+
+static void
+on_editable_changed (GtkEditable *editable, gpointer user_data)
+{
+	GtkDateTimePicker *self;
+	self = (GtkDateTimePicker*)user_data;
+	
+	g_signal_emit (
+	    G_OBJECT (self),
+	    date_time_picker_signals [SIGNAL_DATETIME_CHANGED],
+	    0);
+}
+
+static gboolean
+get_dmy_if_valid(GtkDateTimePicker* picker, gint* day, gint* month, gint* year)
+{
+	g_return_val_if_fail(picker != NULL, FALSE);
+	g_return_val_if_fail(GTK_IS_DATE_TIME_PICKER(picker), FALSE);
+	
+	GtkDateTimePickerPrivate* priv;
+	priv = GTK_DATE_TIME_PICKER_PRIVATE(picker);
+	
+	struct tm tm;
+	gint iyear, imonth, iday;
+
+	gboolean bRes = FALSE;
+
+	const gchar *szDate;
+	szDate = gtk_entry_get_text(GTK_ENTRY(priv->date_entry));
+	strptime (szDate, priv->szDateFormat, &tm);
+
+	iyear = tm.tm_year + 1900;
+	imonth = tm.tm_mon + 1;
+	iday = tm.tm_mday;
+
+	GDate* date;
+	if(g_date_valid_dmy (iday, imonth, iyear)){
+		date = g_date_new_dmy (iday, imonth, iyear);
+		if(date){
+			gchar szBuf[80];
+			g_date_strftime (szBuf, 80, priv->szDateFormat, date);
+			if(g_ascii_strcasecmp(szDate, szBuf) == 0){
+				bRes = TRUE;
+			}
+
+			g_date_free (date);
+			date = NULL;
+		}
+	}
+
+	if(bRes){
+		if(day)
+			*day = iday;
+		if(month)
+			*month = imonth;
+		if(year)
+			*year = iyear;
+	}
+	
+	return bRes;
+}
--- viking-1.5.1/src/gtk-date-time-picker.h	1970-01-01 01:00:00.000000000 +0100
+++ viking-1.5.1.new/src/gtk-date-time-picker.h	2015-03-11 11:03:11.658382200 +0100
@@ -0,0 +1,67 @@
+/*
+ * freetuxtv
+ * Copyright (C) Eric Beuque 2011 <eric.beu...@gmail.com>
+ * 
+ * freetuxtv 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 3 of the License, or
+ * (at your option) any later version.
+ * 
+ * freetuxtv 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _GTK_DATE_TIME_PICKER_H_
+#define _GTK_DATE_TIME_PICKER_H_
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define GTK_TYPE_DATE_TIME_PICKER             (gtk_date_time_picker_get_type ())
+#define GTK_DATE_TIME_PICKER(obj)             (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_DATE_TIME_PICKER, GtkDateTimePicker))
+#define GTK_DATE_TIME_PICKER_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_DATE_TIME_PICKER, GtkDateTimePickerClass))
+#define GTK_IS_DATE_TIME_PICKER(obj)          (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_DATE_TIME_PICKER))
+#define GTK_IS_DATE_TIME_PICKER_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_DATE_TIME_PICKER))
+#define GTK_DATE_TIME_PICKER_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_DATE_TIME_PICKER, GtkDateTimePickerClass))
+
+typedef struct _GtkDateTimePickerClass GtkDateTimePickerClass;
+typedef struct _GtkDateTimePicker GtkDateTimePicker;
+
+struct _GtkDateTimePickerClass
+{
+	GtkHBoxClass parent_class;
+
+	/* Signals */
+	void(* datetime_changed) (GtkDateTimePicker *self, gpointer user_data);
+};
+
+struct _GtkDateTimePicker
+{
+	GtkHBox parent_instance;
+};
+
+GType gtk_date_time_picker_get_type (void) G_GNUC_CONST;
+
+GtkWidget* gtk_date_time_picker_new (gchar* date_format);
+
+void
+gtk_date_time_picker_set_datetime (GtkDateTimePicker* picker, GDateTime *datetime);
+
+GDateTime*
+gtk_date_time_picker_get_datetime (GtkDateTimePicker* picker, GTimeZone *tz);
+
+GDateTime*
+gtk_date_time_picker_to_local (GtkDateTimePicker* picker);
+
+GDateTime*
+gtk_date_time_picker_to_utc (GtkDateTimePicker* picker);
+
+G_END_DECLS
+
+#endif /* _GTK_DATE_TIME_PICKER_H_ */
--- viking-1.5.1/src/Makefile.am	2014-03-16 23:39:18.000000000 +0100
+++ viking-1.5.1.new/src/Makefile.am	2015-03-11 11:03:11.658382200 +0100
@@ -51,7 +51,7 @@
 	curl_download.c curl_download.h \
 	compression.c compression.h \
 	menu.xml.h \
-    degrees_converters.c degrees_converters.h \
+	degrees_converters.c degrees_converters.h \
 	globals.c globals.h \
 	viking.h mapcoord.h config.h \
 	viktrack.c viktrack.h \
@@ -85,6 +85,7 @@
 	vikgototool.c vikgototool.h \
 	vikgotoxmltool.c vikgotoxmltool.h \
 	vikgoto.c vikgoto.h \
+	gtk-date-time-picker.c gtk-date-time-picker.h \
 	viktrwlayer_tpwin.c viktrwlayer_tpwin.h \
 	viktrwlayer_propwin.c viktrwlayer_propwin.h \
 	viktrwlayer_analysis.c viktrwlayer_analysis.h \
--- viking-1.5.1/src/dialog.c	2014-03-16 23:39:18.000000000 +0100
+++ viking-1.5.1.new/src/dialog.c	2015-03-11 11:03:11.658382200 +0100
@@ -561,6 +561,9 @@
   {
     label = gtk_label_new(NULL);
     gtk_label_set_markup ( GTK_LABEL(label), _(texts[i]) );
+    if ( strchr(texts[i], ':') ) {
+      gtk_misc_set_alignment ( GTK_MISC(label), 1.0, 0.5 );
+    }
     gtk_box_pack_start ( GTK_BOX(vbox), label, FALSE, TRUE, padding );
   }
   return vbox;
--- viking-1.5.1/src/viktrack.c	2014-03-16 23:39:18.000000000 +0100
+++ viking-1.5.1.new/src/viktrack.c	2015-03-11 11:03:11.658382200 +0100
@@ -212,6 +212,12 @@
     tp->name = NULL;
 }
 
+void vik_trackpoint_set_loctime(VikTrackpoint *tp, time_t t)
+{
+  tp->has_timestamp = TRUE;
+  tp->timestamp = t;
+}
+
 VikTrackpoint *vik_trackpoint_copy(VikTrackpoint *tp)
 {
   VikTrackpoint *new_tp = vik_trackpoint_new();
@@ -1548,6 +1554,50 @@
   }
 }
 
+
+/**
+ * vik_track_interpolate_times:
+ *
+ * Interpolate the timestamps between first and last trackpoint,
+ * so that the track is driven at equal speed, regardless of the
+ * distance between individual trackpoints.
+ */
+void vik_track_interpolate_times ( VikTrack *tr )
+{
+  gdouble tr_dist, cur_dist;
+  time_t tsdiff, tsfirst;
+
+  GList *iter;
+  iter = tr->trackpoints;
+
+  VikTrackpoint *tp = VIK_TRACKPOINT(iter->data);
+  if ( tp->has_timestamp ) {
+    tsfirst = tp->timestamp;
+
+    while ( iter->next ) {
+      iter = iter->next;
+    }
+    tp = VIK_TRACKPOINT(iter->data);
+    if ( tp->has_timestamp && tp->timestamp > tsfirst) {
+      tsdiff = tp->timestamp - tsfirst;
+
+      tr_dist = vik_track_get_length_including_gaps ( tr );
+      cur_dist = 0.0;
+
+      if ( tr_dist > 0 ) {
+        iter = tr->trackpoints;
+        while ( iter->next && iter->next->next ) {
+          iter = iter->next;
+          tp = VIK_TRACKPOINT(iter->data);
+          cur_dist += vik_coord_diff ( &(tp->coord), &(VIK_TRACKPOINT(iter->prev->data)->coord) );
+
+          tp->timestamp = (cur_dist / tr_dist) * tsdiff + tsfirst;
+        }
+      }
+    }
+  }
+}
+
 
 /**
  * vik_track_apply_dem_data:
--- viking-1.5.1/src/viktrack.h	2014-03-16 23:39:18.000000000 +0100
+++ viking-1.5.1.new/src/viktrack.h	2015-03-11 11:03:11.662382287 +0100
@@ -103,6 +103,7 @@
 void vik_trackpoint_free(VikTrackpoint *tp);
 VikTrackpoint *vik_trackpoint_copy(VikTrackpoint *tp);
 void vik_trackpoint_set_name(VikTrackpoint *tp, const gchar *name);
+void vik_trackpoint_set_loctime(VikTrackpoint *tp, time_t t);
 
 void vik_track_add_trackpoint(VikTrack *tr, VikTrackpoint *tp, gboolean recalculate);
 gdouble vik_track_get_length_to_trackpoint (const VikTrack *tr, const VikTrackpoint *tp);
@@ -146,6 +147,7 @@
 void vik_track_calculate_bounds ( VikTrack *trk );
 
 void vik_track_anonymize_times ( VikTrack *tr );
+void vik_track_interpolate_times ( VikTrack *tr );
 gulong vik_track_apply_dem_data ( VikTrack *tr, gboolean skip_existing );
 void vik_track_apply_dem_data_last_trackpoint ( VikTrack *tr );
 gulong vik_track_smooth_missing_elevation_data ( VikTrack *tr, gboolean flat );
--- viking-1.5.1/src/viktrwlayer.c	2014-03-16 23:39:18.000000000 +0100
+++ viking-1.5.1.new/src/viktrwlayer.c	2015-03-11 11:03:11.666382322 +0100
@@ -4942,6 +4942,19 @@
     vik_track_anonymize_times ( track );
 }
 
+static void trw_layer_interpolate_times ( gpointer pass_along[6] )
+{
+  VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
+  VikTrack *track;
+  if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
+    track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
+  else
+    track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
+
+  if ( track )
+    vik_track_interpolate_times ( track );
+}
+
 static void trw_layer_extend_track_end ( gpointer pass_along[6] )
 {
   VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
@@ -7899,6 +7912,12 @@
       gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
       gtk_widget_set_tooltip_text (item, _("Shift timestamps to a relative offset from 1901-01-01"));
       gtk_widget_show ( item );
+
+      item = gtk_image_menu_item_new_with_mnemonic ( _("_Interpolate Times") );
+      g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_interpolate_times), pass_along );
+      gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
+      gtk_widget_set_tooltip_text (item, _("Interpolate TP-Times between first and last TP such that track is traveled at equal speed"));
+      gtk_widget_show ( item );
     }
 
     if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
--- viking-1.5.1/src/viktrwlayer_tpwin.c	2014-01-18 11:55:46.000000000 +0100
+++ viking-1.5.1.new/src/viktrwlayer_tpwin.c	2015-03-11 11:09:25.690019355 +0100
@@ -29,6 +29,7 @@
 #include <math.h>
 
 #include "coords.h"
+#include "gtk-date-time-picker.h"
 #include "vikcoord.h"
 #include "viktrack.h"
 #include "viktrwlayer_tpwin.h"
@@ -39,8 +40,9 @@
 struct _VikTrwLayerTpwin {
   GtkDialog parent;
   GtkSpinButton *lat, *lon, *alt;
-  GtkWidget *trkpt_name;
-  GtkLabel *course, *ts, *localtime, *diff_dist, *diff_time, *diff_speed, *speed, *hdop, *vdop, *pdop, *sat;
+  GtkWidget *trkpt_name, *trkpt_loctime;
+  GtkLabel *course, *ts, *diff_dist, *diff_time, *diff_speed, *speed, *hdop, *vdop, *pdop, *sat;
+  GtkLabel *diff_trk_start_abs, *diff_trk_end_abs, *diff_trk_start_rel, *diff_trk_end_rel;
   // 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;
@@ -123,6 +125,25 @@
   return FALSE;
 }
 
+void vik_trwlayer_tpwin_set_ts_label ( GtkLabel* l, GDateTime *dt ) {
+    gchar *dts = g_date_time_format ( dt, "%s -- <i>%a %FT%T %Z</i>" );
+    gtk_label_set_markup ( l, dts );
+    g_free ( dts );
+}
+
+static gboolean tpwin_set_loctime ( VikTrwLayerTpwin *tpwin )
+{
+  if ( tpwin->cur_tp && (!tpwin->sync_to_tp_block) ) {
+    GDateTime *dt = gtk_date_time_picker_to_local ( GTK_DATE_TIME_PICKER(tpwin->trkpt_loctime) );
+    if ( dt ) {
+      vik_trwlayer_tpwin_set_ts_label ( tpwin->ts, dt );
+      vik_trackpoint_set_loctime ( tpwin->cur_tp, g_date_time_to_unix(dt) );
+      g_date_time_unref ( dt );
+    }
+  }
+  return FALSE;
+}
+
 VikTrwLayerTpwin *vik_trw_layer_tpwin_new ( GtkWindow *parent )
 {
   static gchar *left_label_texts[] = { N_("<b>Name:</b>"),
@@ -131,7 +152,9 @@
                                        N_("<b>Altitude:</b>"),
                                        N_("<b>Course:</b>"),
                                        N_("<b>Timestamp:</b>"),
-                                       N_("<b>Time:</b>") };
+                                       N_("<b>Time:</b>"),
+                                       /*N_("<b>to TrkStart in %:</b>"),
+                                       N_("<b>to TrkEnd in %:</b>")*/ };
   static gchar *right_label_texts[] = { N_("<b>Distance Difference:</b>"),
                                         N_("<b>Time Difference:</b>"),
                                         N_("<b>\"Speed\" Between:</b>"),
@@ -139,7 +162,9 @@
                                         N_("<b>VDOP:</b>"),
                                         N_("<b>HDOP:</b>"),
                                         N_("<b>PDOP:</b>"),
-                                        N_("<b>SAT/FIX:</b>") };
+                                        N_("<b>SAT/FIX:</b>"),
+                                        /*N_("<b>Distance to TrkStart:</b>"),
+                                        N_("<b>Distance to TrkEnd:</b>")*/ };
 
   VikTrwLayerTpwin *tpwin = VIK_TRW_LAYER_TPWIN ( g_object_new ( VIK_TRW_LAYER_TPWIN_TYPE, NULL ) );
   GtkWidget *main_hbox, *left_vbox, *right_vbox;
@@ -174,8 +199,14 @@
   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));
+  gtk_label_set_selectable ( GTK_LABEL(tpwin->course), TRUE );
+
   tpwin->ts = GTK_LABEL(gtk_label_new(NULL));
-  tpwin->localtime = GTK_LABEL(gtk_label_new(NULL));
+  gtk_label_set_selectable ( GTK_LABEL(tpwin->ts), TRUE );
+  gtk_widget_set_tooltip_text ( GTK_WIDGET(tpwin->ts), _("number of seconds that have elapsed since 1970-01-01 00:00:00 UTC") );
+
+  tpwin->trkpt_loctime = gtk_date_time_picker_new ( "%Y-%m-%d" );
+  g_signal_connect_swapped ( G_OBJECT(tpwin->trkpt_loctime), "datetime-changed", G_CALLBACK(tpwin_set_loctime), 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));
@@ -190,6 +221,9 @@
 
   g_signal_connect_swapped ( G_OBJECT(tpwin->alt), "value-changed", G_CALLBACK(tpwin_sync_alt_to_tp), tpwin );
 
+  tpwin->diff_trk_start_rel = GTK_LABEL(gtk_label_new(NULL));
+  tpwin->diff_trk_end_rel = GTK_LABEL(gtk_label_new(NULL));
+
   right_vbox = gtk_vbox_new ( TRUE, 1 );
   gtk_box_pack_start ( GTK_BOX(right_vbox), GTK_WIDGET(tpwin->trkpt_name), FALSE, FALSE, 3 );
   gtk_box_pack_start ( GTK_BOX(right_vbox), GTK_WIDGET(tpwin->lat), FALSE, FALSE, 3 );
@@ -197,7 +231,10 @@
   gtk_box_pack_start ( GTK_BOX(right_vbox), GTK_WIDGET(tpwin->alt), FALSE, FALSE, 3 );
   gtk_box_pack_start ( GTK_BOX(right_vbox), GTK_WIDGET(tpwin->course), FALSE, FALSE, 3 );
   gtk_box_pack_start ( GTK_BOX(right_vbox), GTK_WIDGET(tpwin->ts), FALSE, FALSE, 3 );
-  gtk_box_pack_start ( GTK_BOX(right_vbox), GTK_WIDGET(tpwin->localtime), FALSE, FALSE, 3 );
+  gtk_box_pack_start ( GTK_BOX(right_vbox), GTK_WIDGET(tpwin->trkpt_loctime), FALSE, FALSE, 3 );
+
+  /*gtk_box_pack_start ( GTK_BOX(right_vbox), GTK_WIDGET(tpwin->diff_trk_start_rel), FALSE, FALSE, 3 );
+  gtk_box_pack_start ( GTK_BOX(right_vbox), GTK_WIDGET(tpwin->diff_trk_end_rel), FALSE, FALSE, 3 );*/
 
   /* diff info */
   diff_left_vbox = a_dialog_create_label_vbox ( right_label_texts, G_N_ELEMENTS(right_label_texts), 1, 3 );
@@ -212,6 +249,9 @@
   tpwin->pdop = GTK_LABEL(gtk_label_new(NULL));
   tpwin->sat = GTK_LABEL(gtk_label_new(NULL));
 
+  tpwin->diff_trk_start_abs = GTK_LABEL(gtk_label_new(NULL));
+  tpwin->diff_trk_end_abs = GTK_LABEL(gtk_label_new(NULL));
+
   diff_right_vbox = gtk_vbox_new ( TRUE, 1 );
   gtk_box_pack_start ( GTK_BOX(diff_right_vbox), GTK_WIDGET(tpwin->diff_dist), FALSE, FALSE, 3 );
   gtk_box_pack_start ( GTK_BOX(diff_right_vbox), GTK_WIDGET(tpwin->diff_time), FALSE, FALSE, 3 );
@@ -223,11 +263,14 @@
   gtk_box_pack_start ( GTK_BOX(diff_right_vbox), GTK_WIDGET(tpwin->pdop), FALSE, FALSE, 3 );
   gtk_box_pack_start ( GTK_BOX(diff_right_vbox), GTK_WIDGET(tpwin->sat), FALSE, FALSE, 3 );
 
+  /*gtk_box_pack_start ( GTK_BOX(diff_right_vbox), GTK_WIDGET(tpwin->diff_trk_start_abs), FALSE, FALSE, 3 );
+  gtk_box_pack_start ( GTK_BOX(diff_right_vbox), GTK_WIDGET(tpwin->diff_trk_end_abs), FALSE, FALSE, 3 );*/
+
   main_hbox = gtk_hbox_new( FALSE, 0 );
-  gtk_box_pack_start ( GTK_BOX(main_hbox), left_vbox, TRUE, TRUE, 0 );
-  gtk_box_pack_start ( GTK_BOX(main_hbox), right_vbox, TRUE, TRUE, 0 );
-  gtk_box_pack_start ( GTK_BOX(main_hbox), diff_left_vbox, TRUE, TRUE, 0 );
-  gtk_box_pack_start ( GTK_BOX(main_hbox), diff_right_vbox, TRUE, TRUE, 0 );
+  gtk_box_pack_start ( GTK_BOX(main_hbox), left_vbox, TRUE, TRUE, 3 );
+  gtk_box_pack_start ( GTK_BOX(main_hbox), right_vbox, TRUE, TRUE, 3 );
+  gtk_box_pack_start ( GTK_BOX(main_hbox), diff_left_vbox, TRUE, TRUE, 3 );
+  gtk_box_pack_start ( GTK_BOX(main_hbox), diff_right_vbox, TRUE, TRUE, 3 );
 
   gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(tpwin))), main_hbox, FALSE, FALSE, 0 );
 
@@ -247,9 +290,9 @@
 {
   gtk_editable_delete_text ( GTK_EDITABLE(tpwin->trkpt_name), 0, -1 );
   gtk_widget_set_sensitive ( tpwin->trkpt_name, FALSE );
+  gtk_widget_set_sensitive ( tpwin->trkpt_loctime, FALSE );
 
   gtk_label_set_text ( tpwin->ts, NULL );
-  gtk_label_set_text ( tpwin->localtime, NULL );
   gtk_label_set_text ( tpwin->course, NULL );
 
   gtk_widget_set_sensitive ( GTK_WIDGET(tpwin->lat), FALSE );
@@ -325,15 +368,16 @@
 
   if ( tp->has_timestamp )
   {
-    g_snprintf ( tmp_str, sizeof(tmp_str), "%ld", tp->timestamp );
-    gtk_label_set_text ( tpwin->ts, tmp_str );
-    strftime ( tmp_str, sizeof(tmp_str), "%c", localtime(&(tp->timestamp)) );
-    gtk_label_set_text ( tpwin->localtime, tmp_str );
+    GDateTime *dt = g_date_time_new_from_unix_local ( tp->timestamp );
+    gtk_date_time_picker_set_datetime ( GTK_DATE_TIME_PICKER(tpwin->trkpt_loctime), dt );
+    gtk_widget_set_sensitive ( tpwin->trkpt_loctime, TRUE );
+    vik_trwlayer_tpwin_set_ts_label ( tpwin->ts, dt );
+    g_date_time_unref ( dt );
   }
   else
   {
-    gtk_label_set_text (tpwin->ts, NULL );
-    gtk_label_set_text (tpwin->localtime, NULL );
+    gtk_widget_set_sensitive ( tpwin->trkpt_loctime, FALSE );
+    gtk_label_set_text ( tpwin->ts, NULL );
   }
 
   vik_units_speed_t speed_units = a_vik_get_units_speed ();
------------------------------------------------------------------------------
Dive into the World of Parallel Programming The Go Parallel Website, sponsored
by Intel and developed in partnership with Slashdot Media, is your hub for all
things parallel software development, from weekly thought leadership blogs to
news, videos, case studies, tutorials and more. Take a look and join the 
conversation now. http://goparallel.sourceforge.net/
_______________________________________________
Viking-devel mailing list
Viking-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/viking-devel
Viking home page: http://viking.sf.net/

Reply via email to