2006-08-30 Michael Dominic Kostrzewa <michael.kostrzewa@nokia.com>
[hildon] / hildon-widgets / hildon-time-editor.c
index 22aad0a..e2af240 100644 (file)
@@ -1,14 +1,14 @@
 /*
  * This file is part of hildon-libs
  *
- * Copyright (C) 2005 Nokia Corporation.
+ * Copyright (C) 2005, 2006 Nokia Corporation.
  *
- * Contact: Luc Pionchon <luc.pionchon@nokia.com>
+ * Contact: Michael Dominic Kostrzewa <michael.kostrzewa@nokia.com>
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public License
- * as published by the Free Software Foundation; either version 2.1 of
- * the License, or (at your option) any later version.
+ * as published by the Free Software Foundation; version 2.1 of
+ * the License.
  *
  * This library is distributed in the hope that it will be useful, but
  * WITHOUT ANY WARRANTY; without even the implied warranty of
  *
  */
 
-/* HILDON DOC
- * @shortdesc: TimeEditor is a widget for setting, getting and showing a
- * time.
- * @longdesc: The Time Editor widget is used to enter the system time
- * (hours and minutes) in the Date/Time system plugin. It is a composite
- * widget consisting of two GtkEntry widgets that are placed next to each
- * other. The leftmost GtkEntry is used to enter the hours, and it accepts
- * the values 0--23, while the rightmost GtkEntry accepts values 0--59
- * and is used to set the minutes. Between the two GtkEntries there
- * is a label displaying a colon.
- * </para><para>
- * From the usability point of view, the GtkSpinbutton widget would
- * have been a better choice than the GtkEntry widgets, but it uses
- * floating point operations and is thus not acceptable in this
- * project.
- *
- * @seealso: #HildonDateEditor
+/**
+ * SECTION:hildon-time-editor
+ * @short_description: A widget used to enter time or duration in hours, minutes,
+ * and optional seconds
+ * @see_also: #HildonTimePicker
+ * 
+ * HildonTimeEditor is used to edit time or duration. Time mode is
+ * restricted to normal 24 hour cycle, but Duration mode can select any
+ * amount of time up to 99 hours.  It consists of entries for hours,
+ * minutes and seconds, and pm/am indicator as well as a button which
+ * popups a #HildonTimePicker dialog.
  */
 
 #ifdef HAVE_CONFIG_H
 #include <hildon-widgets/hildon-time-picker.h>
 #include <hildon-widgets/hildon-banner.h>
 #include <hildon-widgets/hildon-input-mode-hint.h>
+#include <hildon-widgets/hildon-private.h>
 #include "hildon-composite-widget.h"
+#include "hildon-marshalers.h"
+#include "hildon-libs-enum-types.h"
 
 #define _(String) dgettext(PACKAGE, String)
 
@@ -110,6 +107,7 @@ enum
        PROP_SHOW_HOURS
 };
 
+/* Indices for h/m/s entries in priv->entries */
 enum {
   ENTRY_HOURS,
   ENTRY_MINS,
@@ -118,6 +116,25 @@ enum {
   ENTRY_COUNT
 };
 
+/* Signals */
+enum {
+  TIME_ERROR,
+  LAST_SIGNAL
+};
+
+/* Error codes categories */
+enum {
+  MAX_VALUE,
+  MIN_VALUE,
+  WITHIN_RANGE,
+  NUM_ERROR_CODES
+};
+
+static guint time_editor_signals[LAST_SIGNAL] = { 0 };
+static guint hour_errors[NUM_ERROR_CODES] = { MAX_HOURS, MIN_HOURS, EMPTY_HOURS  };
+static guint  min_errors[NUM_ERROR_CODES] = { MAX_MINS,  MIN_MINS,  EMPTY_MINS   };
+static guint  sec_errors[NUM_ERROR_CODES] = { MAX_SECS,  MIN_SECS,  EMPTY_SECS   };
+
 struct _HildonTimeEditorPrivate {
     guint      ticks;                /* Current duration in seconds  */
 
@@ -188,6 +205,9 @@ static gboolean hildon_time_editor_entry_focusin(GtkWidget      *widget,
                                                  GdkEventFocus  *event, 
                                                  gpointer        data);
 
+static gboolean hildon_time_editor_time_error(HildonTimeEditor *editor,
+                                             HildonTimeEditorErrorType type);
+
 static gboolean hildon_time_editor_ampm_clicked(GtkWidget       *widget,
                                                 GdkEventButton  *event,
                                                 gpointer         data);
@@ -229,14 +249,20 @@ static void hildon_time_editor_set_to_current_time (HildonTimeEditor * editor);
  * Utility functions
  */
  
-static void convert_to_12h (guint *h, guint *m, guint *s, gboolean *am);
-static void convert_to_24h (guint *h, guint *m, guint *s, gboolean  am);
+static void convert_to_12h (guint *h, gboolean *am);
+static void convert_to_24h (guint *h, gboolean  am);
 
 static void ticks_to_time (guint  ticks,
                            guint *hours,
                            guint *minutes,
                            guint *seconds);
 
+static void
+hildon_time_editor_inserted_text  (GtkEditable * editable,
+                                   gchar * new_text,
+                                   gint new_text_length,
+                                   gint * position,
+                                   gpointer user_data);
 
 GType hildon_time_editor_get_type(void)
 {
@@ -328,6 +354,17 @@ hildon_time_editor_class_init(HildonTimeEditorClass * editor_class)
 
     object_class->finalize = hildon_time_editor_finalize;
 
+    editor_class->time_error = hildon_time_editor_time_error; 
+    
+    time_editor_signals[TIME_ERROR] =
+        g_signal_new("time-error",
+                    G_OBJECT_CLASS_TYPE(object_class),
+                    G_SIGNAL_RUN_LAST,
+                    G_STRUCT_OFFSET(HildonTimeEditorClass, time_error),
+                    g_signal_accumulator_true_handled, NULL,
+                    _hildon_marshal_BOOLEAN__ENUM,
+                    G_TYPE_BOOLEAN, 1, HILDON_TYPE_TIME_EDITOR_ERROR_TYPE);
+
   /**
    * HildonTimeEditor:ticks:
    *
@@ -451,8 +488,8 @@ static void hildon_time_editor_init(HildonTimeEditor * editor)
     priv->duration_mode  = FALSE;
     priv->iconbutton     = gtk_button_new();
     priv->ampm_label     = gtk_label_new(NULL);
-    priv->hm_label       = gtk_label_new(_("Ecdg_ti_time_editor_separator"));
-    priv->sec_label      = gtk_label_new(_("Ecdg_ti_time_editor_separator"));
+    priv->hm_label       = gtk_label_new(NULL);
+    priv->sec_label      = gtk_label_new(NULL);
     priv->frame          = gtk_frame_new(NULL);
     priv->eventbox       = gtk_event_box_new();
 
@@ -496,6 +533,11 @@ static void hildon_time_editor_init(HildonTimeEditor * editor)
                        G_CALLBACK(hildon_time_editor_entry_keypress), editor);
       g_signal_connect(priv->entries[i], "changed",
                        G_CALLBACK(hildon_time_editor_entry_changed), editor);
+    
+      /* inserted signal sets time */
+      g_signal_connect_after (G_OBJECT(priv->entries[i]), "insert_text",
+                             G_CALLBACK (hildon_time_editor_inserted_text), 
+                             editor);
     }
     
     /* clicked signal for am/pm label */
@@ -522,14 +564,14 @@ static void hildon_time_editor_init(HildonTimeEditor * editor)
     gtk_widget_show_all(priv->frame);
     gtk_widget_show_all(priv->iconbutton);
 
-    /* Update AM/PM settings from locale */
+    /* Update AM/PM and time separators settings from locale */
     if (!hildon_time_editor_check_locale(editor)) {
         /* Using 12h clock */
         priv->clock_24h = FALSE;
     } else {
         gtk_widget_hide(priv->eventbox);
     }
-
     if (!priv->show_seconds) {
         gtk_widget_hide(priv->sec_label);
         gtk_widget_hide(priv->entries[ENTRY_SECS]);
@@ -623,8 +665,8 @@ static void hildon_time_editor_get_property (GObject    *object,
  *
  * This function creates a new time editor. 
  *
- * Return value: pointer to a new #HildonTimeEditor widget.
- **/
+ * Returns: pointer to a new #HildonTimeEditor widget
+ */
 
 GtkWidget *hildon_time_editor_new(void)
 {
@@ -645,6 +687,60 @@ static void hildon_time_editor_finalize(GObject * obj_self)
         G_OBJECT_CLASS(parent_class)->finalize(obj_self);
 }
 
+/**
+ * _hildon_time_editor_get_time_separators:
+ * @editor: the #HildonTimeEditor
+ * @hm_sep_label: the label that will show the hour:minutes separator
+ * @ms_sep_label: the label that will show the minutes:seconds separator
+ *
+ * Gets hour-minute separator and minute-second separator from current
+ * locale and sets then to the labels we set as parameters. Both
+ * parameters can be NULL if you just want to assing one separator.
+ *
+ */
+void 
+_hildon_time_editor_get_time_separators(GtkLabel *hm_sep_label,
+                                        GtkLabel *ms_sep_label)
+{
+    gchar buffer[256];
+    gchar *separator;
+    GDate locale_test_date;
+    gchar *iter, *endp;
+
+    /* Get localized time string */
+    g_date_set_dmy(&locale_test_date, 1, 2, 1970);
+    (void) g_date_strftime(buffer, sizeof(buffer), "%X", &locale_test_date);
+
+    if (hm_sep_label != NULL)
+      {
+        /* Find h-m separator */
+        iter = buffer;
+        while (*iter && g_ascii_isdigit(*iter)) iter++;
+    
+        /* Extract h-m separator*/
+        endp = iter;
+        while (*endp && !g_ascii_isdigit(*endp)) endp++;
+        separator = g_strndup(iter, endp - iter);
+        gtk_label_set_label(hm_sep_label, separator);
+        g_free(separator);
+      }
+
+    if (ms_sep_label != NULL)
+      {      
+        /* Find m-s separator */
+        iter = endp;
+        while (*iter && g_ascii_isdigit(*iter)) iter++;
+    
+        /* Extract m-s separator*/
+        endp = iter;
+        while (*endp && !g_ascii_isdigit(*endp)) endp++;
+        separator = g_strndup(iter, endp - iter);
+        gtk_label_set_label(ms_sep_label, separator);
+        g_free(separator);
+      }
+
+}
+
 /* Convert ticks to H:M:S. Ticks = seconds since 00:00:00. */
 static void ticks_to_time (guint ticks,
                            guint *hours,
@@ -661,14 +757,13 @@ static void ticks_to_time (guint ticks,
 
 /**
  * hildon_time_editor_set_ticks:
- * @self: the @HildonTimeEditor widget.
- * @ticks: The duration to set, in seconds.
+ * @editor: the #HildonTimeEditor widget
+ * @ticks: the duration to set, in seconds
  *
  * Sets the current duration in seconds. This means seconds from
  * midnight, if not in duration mode. In case of any errors, it tries
  * to fix it.
- * 
- **/
+ */
 
 void hildon_time_editor_set_ticks (HildonTimeEditor * editor,
                                    guint ticks)
@@ -701,7 +796,7 @@ void hildon_time_editor_set_ticks (HildonTimeEditor * editor,
     if (!priv->clock_24h && !priv->duration_mode)
       {
         /* Convert 24h H:M:S values to 12h mode, and update AM/PM state */
-        convert_to_12h (&h, &m, &s, &priv->am);
+        convert_to_12h (&h, &priv->am);
       }
 
     /* Set H:M:S values to entries. We  do not want to invoke validation
@@ -711,6 +806,13 @@ void hildon_time_editor_set_ticks (HildonTimeEditor * editor,
     {
       g_signal_handlers_block_by_func(priv->entries[i],
         (gpointer) hildon_time_editor_entry_changed, editor);
+
+      g_signal_handlers_block_by_func(priv->entries[i],
+        (gpointer) hildon_time_editor_inserted_text, editor);
+      
+      g_signal_handlers_block_by_func(priv->entries[i],
+       (gpointer) hildon_time_editor_entry_focusout, editor);
+
     }
 
     g_snprintf(str, sizeof(str), "%02u", h);
@@ -726,7 +828,14 @@ void hildon_time_editor_set_ticks (HildonTimeEditor * editor,
     {
       g_signal_handlers_unblock_by_func(priv->entries[i],
         (gpointer) hildon_time_editor_entry_changed, editor);
-    }
+
+      g_signal_handlers_unblock_by_func(priv->entries[i],
+        (gpointer) hildon_time_editor_inserted_text, editor);
+
+      g_signal_handlers_unblock_by_func(priv->entries[i],
+       (gpointer) hildon_time_editor_entry_focusout, editor);
+
+   }
 
     /* Update AM/PM label in case we're in 12h mode */
     gtk_label_set_label(GTK_LABEL(priv->ampm_label),
@@ -750,13 +859,13 @@ hildon_time_editor_set_to_current_time (HildonTimeEditor * editor)
 
 /**
  * hildon_time_editor_get_ticks:
- * @self: the @HildonTimeEditor widget.
+ * @editor: the #HildonTimeEditor widget
  *
  * This function returns the current duration, in seconds.
  * This means seconds from midnight, if not in duration mode.
  * 
- * Return value: Current duration in seconds. 
- **/
+ * Returns: current duration in seconds 
+ */
  
 guint hildon_time_editor_get_ticks (HildonTimeEditor * editor)
 {
@@ -772,12 +881,11 @@ guint hildon_time_editor_get_ticks (HildonTimeEditor * editor)
 
 /**
  * hildon_time_editor_set_show_seconds:
- * @editor: The #HildonTimeEditor.
- * @enable: Enable or disable showing of seconds.
+ * @editor: the #HildonTimeEditor
+ * @show_seconds: enable or disable showing of seconds
  *
  * This function shows or hides the seconds field.
- *
- **/
+ */
 
 void hildon_time_editor_set_show_seconds (HildonTimeEditor * editor,
                                         gboolean show_seconds)
@@ -806,13 +914,13 @@ void hildon_time_editor_set_show_seconds (HildonTimeEditor * editor,
 
 /**
  * hildon_time_editor_get_show_seconds:
- * @self: the @HildonTimeEditor widget.
+ * @editor: the #HildonTimeEditor widget
  *
  * This function returns a boolean indicating the visibility of
- * seconds in the @HildonTimeEditor
+ * seconds in the #HildonTimeEditor
  *
- * Return value: TRUE if the seconds are visible. 
- **/
+ * Returns: TRUE if the seconds are visible 
+ */
 
 gboolean hildon_time_editor_get_show_seconds (HildonTimeEditor * editor)
 {
@@ -826,13 +934,12 @@ gboolean hildon_time_editor_get_show_seconds (HildonTimeEditor * editor)
 
 /**
  * hildon_time_editor_set_duration_mode:
- * @editor: The #HildonTimeEditor.
- * @enable: Enable or disable duration editor mode
+ * @editor: the #HildonTimeEditor
+ * @duration_mode: enable or disable duration editor mode
  *
  * This function sets the duration editor mode in which the maximum hours
- * is 99 and the #HildonTimePicker is disabled.
- *
- **/
+ * is 99.
+ */
  
 void hildon_time_editor_set_duration_mode (HildonTimeEditor * editor,
                                          gboolean duration_mode)
@@ -878,13 +985,13 @@ void hildon_time_editor_set_duration_mode (HildonTimeEditor * editor,
 
 /**
  * hildon_time_editor_get_duration_mode:
- * @self: the @HildonTimeEditor widget.
+ * @editor: the #HildonTimeEditor widget
  *
- * This function returns a boolean indicating whether the @HildonTimeEditor
+ * This function returns a boolean indicating whether the #HildonTimeEditor
  * is in the duration mode.
  * 
- * Return value: TRUE if the @HildonTimeEditor is in duration mode. 
- **/
+ * Returns: TRUE if the #HildonTimeEditor is in duration mode 
+ */
 
 gboolean hildon_time_editor_get_duration_mode (HildonTimeEditor * editor)
 {
@@ -898,12 +1005,12 @@ gboolean hildon_time_editor_get_duration_mode (HildonTimeEditor * editor)
 
 /**
  * hildon_time_editor_set_duration_min:
- * @self: the @HildonTimeEditor widget.
- * @duration_min: Mimimum allowed duration.
+ * @editor: the #HildonTimeEditor widget
+ * @duration_min: mimimum allowed duration
  *
  * Sets the minimum allowed duration for the duration mode.
  * Note: Has no effect in time mode
- **/
+ */
 
 void hildon_time_editor_set_duration_min (HildonTimeEditor * editor,
                                           guint duration_min)
@@ -931,13 +1038,13 @@ void hildon_time_editor_set_duration_min (HildonTimeEditor * editor,
 
 /**
  * hildon_time_editor_get_duration_min:
- * @self: the @HildonTimeEditor widget.
+ * @editor: the #HildonTimeEditor widget
  *
- * This function returns the smallest duration the @HildonTimeEditor
+ * This function returns the smallest duration the #HildonTimeEditor
  * allows in the duration mode.
  * 
- * Return value: Mimimum allowed duration in seconds. 
- **/
+ * Returns: minimum allowed duration in seconds 
+ */
  
 guint hildon_time_editor_get_duration_min (HildonTimeEditor * editor)
 {
@@ -955,13 +1062,12 @@ guint hildon_time_editor_get_duration_min (HildonTimeEditor * editor)
 
 /**
  * hildon_time_editor_set_duration_max:
- * @self: the @HildonTimeEditor widget.
- * @duration_min: Maximum allowed duration in seconds.
+ * @editor: the #HildonTimeEditor widget
+ * @duration_max: maximum allowed duration in seconds
  *
  * Sets the maximum allowed duration in seconds for the duration mode.
  * Note: Has no effect in time mode
- * 
- **/
+ */
  
 void hildon_time_editor_set_duration_max (HildonTimeEditor * editor,
                                           guint duration_max)
@@ -989,13 +1095,13 @@ void hildon_time_editor_set_duration_max (HildonTimeEditor * editor,
 
 /**
  * hildon_time_editor_get_duration_max:
- * @self: the @HildonTimeEditor widget.
+ * @editor: the #HildonTimeEditor widget
  *
- * This function returns the longest duration the @HildonTimeEditor
+ * This function returns the longest duration the #HildonTimeEditor
  * allows in the duration mode.
  * 
- * Return value: Maximum allowed duration in seconds. 
- **/
+ * Returns: maximum allowed duration in seconds 
+ */
  
 guint hildon_time_editor_get_duration_max (HildonTimeEditor * editor)
 {
@@ -1014,7 +1120,7 @@ guint hildon_time_editor_get_duration_max (HildonTimeEditor * editor)
 
 /**
  * hildon_time_editor_set_time:
- * @editor: the @HildonTimeEditor widget.
+ * @editor: the #HildonTimeEditor widget
  * @hours: hours
  * @minutes: minutes
  * @seconds: seconds
@@ -1022,8 +1128,7 @@ guint hildon_time_editor_get_duration_max (HildonTimeEditor * editor)
  * This function sets the time on an existing time editor. If the
  * time specified by the arguments is invalid, it's fixed.
  * The time is assumed to be in 24h format.
- *  
- **/
+ */
 
 void hildon_time_editor_set_time(HildonTimeEditor * editor, guint hours,
                                  guint minutes, guint seconds)
@@ -1035,14 +1140,14 @@ void hildon_time_editor_set_time(HildonTimeEditor * editor, guint hours,
 
 /**
  * hildon_time_editor_get_time:
- * @editor: the @HildonTimeEditor widget.
+ * @editor: the #HildonTimeEditor widget
  * @hours: hours
  * @minutes: minutes
  * @seconds: seconds
  *
- * Gets the time of the @HildonTimeEditor widget. The time returned is
+ * Gets the time of the #HildonTimeEditor widget. The time returned is
  * always in 24h format.
- **/
+ */
 
 void hildon_time_editor_get_time(HildonTimeEditor * editor,
                                  guint * hours,
@@ -1060,12 +1165,12 @@ void hildon_time_editor_get_time(HildonTimeEditor * editor,
 
 /**
  * hildon_time_editor_set_duration_range:
- * @editor: the @HildonTimeEditor widget.
+ * @editor: the #HildonTimeEditor widget
  * @min_seconds: minimum allowed time in seconds
  * @max_seconds: maximum allowed time in seconds
  *
- * Sets the duration editor time range of the @HildonTimeEditor widget.
- **/
+ * Sets the duration editor time range of the #HildonTimeEditor widget.
+ */
 
 void hildon_time_editor_set_duration_range(HildonTimeEditor * editor,
                                            guint min_seconds,
@@ -1098,12 +1203,12 @@ void hildon_time_editor_set_duration_range(HildonTimeEditor * editor,
 
 /**
  * hildon_time_editor_get_duration_range:
- * @editor: the @HildonTimeEditor widget.
+ * @editor: the #HildonTimeEditor widget
  * @min_seconds: pointer to guint
  * @max_seconds: pointer to guint
  *
- * Gets the duration editor time range of the @HildonTimeEditor widget.
- **/
+ * Gets the duration editor time range of the #HildonTimeEditor widget.
+ */
 
 void hildon_time_editor_get_duration_range(HildonTimeEditor * editor,
                                            guint * min_seconds,
@@ -1125,9 +1230,12 @@ static gboolean hildon_time_editor_check_locale(HildonTimeEditor * editor)
 
     priv = HILDON_TIME_EDITOR_GET_PRIVATE(editor);
 
-    /* Get AM/PM symbols. We want to show them in lowercase. */
-    priv->am_symbol = g_ascii_strdown(nl_langinfo(AM_STR), -1);
-    priv->pm_symbol = g_ascii_strdown(nl_langinfo(PM_STR), -1);
+    /* Update time separator symbols */
+    _hildon_time_editor_get_time_separators(GTK_LABEL(priv->hm_label), GTK_LABEL(priv->sec_label));
+    /* Get AM/PM symbols. */
+    priv->am_symbol = g_strdup(nl_langinfo(AM_STR));
+    priv->pm_symbol = g_strdup(nl_langinfo(PM_STR));
 
     if (priv->am_symbol[0] == '\0')
         return TRUE;
@@ -1160,6 +1268,13 @@ static gboolean hildon_time_editor_entry_focusin(GtkWidget * widget,
     return FALSE;
 }
 
+static gboolean 
+hildon_time_editor_time_error(HildonTimeEditor *editor,
+                             HildonTimeEditorErrorType type)
+{
+  return TRUE;
+}
+
 /* Returns negative if we didn't get value,
  * and should stop further validation 
  */
@@ -1168,6 +1283,7 @@ static gint validated_conversion(HildonTimeEditorPrivate *priv,
                                  gint                     min,
                                  gint                     max,
                                  gboolean                 allow_intermediate,
+                                 guint                   *error_code,
                                  GString                 *error_string)
 {
     const gchar *text;
@@ -1185,13 +1301,15 @@ static gint validated_conversion(HildonTimeEditorPrivate *priv,
         if (tail[0] == 0)
         {    
             if (value > max) {
-                g_string_printf(error_string, _("Ckct_ib_maximum_value"), max);
+                g_string_printf(error_string, _("ckct_ib_maximum_value"), max);
                 priv->error_widget = field;
+                *error_code = MAX_VALUE;
                 return max;
                    }
             if (value < min && !allow_intermediate) {
-                g_string_printf(error_string, _("Ckct_ib_minimum_value"), min);
+                g_string_printf(error_string, _("ckct_ib_minimum_value"), min);
                 priv->error_widget = field;
+                *error_code = MIN_VALUE;
                 return min;
             }
 
@@ -1202,10 +1320,18 @@ static gint validated_conversion(HildonTimeEditorPrivate *priv,
     else if (allow_intermediate) 
         return -1;  /* Empty field while user is still editing. No error, but
                        cannot validate either... */
+    else /* Empty field: show error and set value to minimum allowed */
+      {
+        g_string_printf(error_string, _("ckct_ib_set_a_value_within_range"), min, max);
+        priv->error_widget = field;
+        *error_code = WITHIN_RANGE;
+        return min;
+      }
 
     /* Empty field and not allowed intermediated OR failed conversion */
-    g_string_printf(error_string, _("Ckct_ib_set_a_value_within_range"), min, max);
+    g_string_printf(error_string, _("ckct_ib_set_a_value_within_range"), min, max);
     priv->error_widget = field;
+    *error_code = WITHIN_RANGE;
     return -1;
 }
 
@@ -1215,7 +1341,9 @@ hildon_time_editor_real_validate(HildonTimeEditor *editor,
 {
     HildonTimeEditorPrivate *priv;
     guint h, m, s, ticks;
+    guint error_code;
     guint max_hours, min_hours, max_minutes, min_minutes, max_seconds, min_seconds;
+    gboolean r;
 
     g_assert(HILDON_IS_TIME_EDITOR(editor));
 
@@ -1239,16 +1367,22 @@ hildon_time_editor_real_validate(HildonTimeEditor *editor,
     /* Get time components from fields and validate them... */
     if (priv->show_hours) {
         h = validated_conversion(priv, priv->entries[ENTRY_HOURS], min_hours, max_hours, 
-            allow_intermediate, error_string);
+            allow_intermediate, &error_code, error_string);
+       if (priv->error_widget == priv->entries[ENTRY_HOURS])
+         g_signal_emit (editor, time_editor_signals [TIME_ERROR], 0, hour_errors[error_code], &r);
         if ((gint) h < 0) return;
     }
     else h = 0;
     m = validated_conversion(priv, priv->entries[ENTRY_MINS], MINUTES_MIN, MINUTES_MAX, 
-        allow_intermediate, error_string);
+        allow_intermediate, &error_code, error_string);
+    if (priv->error_widget == priv->entries[ENTRY_MINS])
+         g_signal_emit (editor, time_editor_signals [TIME_ERROR], 0, min_errors[error_code], &r);
     if ((gint) m < 0) return;
     if (priv->show_seconds) {
         s = validated_conversion(priv, priv->entries[ENTRY_SECS], SECONDS_MIN, SECONDS_MAX, 
-            allow_intermediate, error_string);
+            allow_intermediate, &error_code, error_string);
+       if (priv->error_widget == priv->entries[ENTRY_SECS])
+             g_signal_emit (editor, time_editor_signals [TIME_ERROR], 0, sec_errors[error_code], &r);
         if ((gint) s < 0) return;
     } 
     else s = 0;
@@ -1261,24 +1395,26 @@ hildon_time_editor_real_validate(HildonTimeEditor *editor,
         if (ticks < priv->duration_min && !allow_intermediate)
         {
             g_string_printf(error_string,
-                _("Ckct_ib_min_allowed_duration_hts"), 
+                _("ckct_ib_min_allowed_duration_hts"), 
                 min_hours, min_minutes, min_seconds);
             hildon_time_editor_set_ticks (editor, priv->duration_min);
             priv->error_widget = priv->show_hours ? priv->entries[ENTRY_HOURS] : priv->entries[ENTRY_MINS];
+           g_signal_emit (editor, time_editor_signals[TIME_ERROR], 0, MIN_DUR, &r);
             return;
         }
         else if (ticks > priv->duration_max)
         {
             g_string_printf(error_string,
-                _("Ckct_ib_max_allowed_duration_hts"), 
+                _("ckct_ib_max_allowed_duration_hts"), 
                 max_hours, max_minutes, max_seconds);
             hildon_time_editor_set_ticks (editor, priv->duration_max);
             priv->error_widget = priv->show_hours ? priv->entries[ENTRY_HOURS] : priv->entries[ENTRY_MINS];
+           g_signal_emit (editor, time_editor_signals[TIME_ERROR], 0, MAX_DUR, &r);
             return;
         }
     }
     else if (!priv->clock_24h)
-        convert_to_24h (&h, &m, &s, priv->am);
+        convert_to_24h (&h, priv->am);
 
     /* The only case when we do not want to refresh the
        time display, is when the user is editing a value 
@@ -1294,7 +1430,10 @@ static gboolean highlight_callback(gpointer data)
 {
     HildonTimeEditorPrivate *priv;
     GtkWidget *widget;
+    gint i;
 
+    GDK_THREADS_ENTER ();
+    
     g_assert(HILDON_IS_TIME_EDITOR(data));
 
     priv = HILDON_TIME_EDITOR_GET_PRIVATE(data);
@@ -1304,10 +1443,20 @@ static gboolean highlight_callback(gpointer data)
 
     g_assert(GTK_IS_ENTRY(widget));
 
-    /* Grabbing focus can cause re-validation, priv->error widget
-       can be set to something else, including NULL */
+    /* Avoid revalidation because it will issue the date_error signal
+       twice when there is an empty field. We must block the signal
+       for all the entries because we do not know where the focus
+       comes from */
+    for (i = 0; i < ENTRY_COUNT; i++)
+      g_signal_handlers_block_by_func(priv->entries[i],
+                                     (gpointer) hildon_time_editor_entry_focusout, data);
     gtk_editable_select_region(GTK_EDITABLE(widget), 0, -1);
     gtk_widget_grab_focus(widget);
+    for (i = 0; i < ENTRY_COUNT; i++)
+      g_signal_handlers_unblock_by_func(priv->entries[i],
+                                       (gpointer) hildon_time_editor_entry_focusout, data);
+
+    GDK_THREADS_LEAVE ();
 
     return FALSE;
 }
@@ -1323,19 +1472,86 @@ hildon_time_editor_validate (HildonTimeEditor *editor, gboolean allow_intermedia
     g_assert(HILDON_IS_TIME_EDITOR(editor));
 
     priv = HILDON_TIME_EDITOR_GET_PRIVATE(editor);
-    priv->error_widget = NULL;
 
-    error_message = g_string_new(NULL);
-    hildon_time_editor_real_validate(editor, 
-        allow_intermediate, error_message);
-
-    if (priv->error_widget) {
-        hildon_banner_show_information(priv->error_widget, NULL,
-                                       error_message->str);
-        if (priv->highlight_idle == 0)
+    /* if there is already an error we do nothing until it will be managed by the idle */
+    if (priv->highlight_idle == 0)
+      {
+        error_message = g_string_new(NULL);
+        hildon_time_editor_real_validate(editor, 
+                                         allow_intermediate, error_message);
+        
+        if (priv->error_widget) 
+          {
+            hildon_banner_show_information(priv->error_widget, NULL,
+                                           error_message->str);
+            
             priv->highlight_idle = g_idle_add(highlight_callback, editor);
-    }
-    g_string_free(error_message, TRUE);
+          }
+
+        g_string_free(error_message, TRUE);
+      }
+}
+
+/* on inserted text, if entry has two digits, jumps to the next field. */
+static void
+hildon_time_editor_inserted_text  (GtkEditable * editable,
+                                   gchar * new_text,
+                                   gint new_text_length,
+                                   gint * position,
+                                   gpointer user_data) 
+{
+  HildonTimeEditor *editor;
+  GtkEntry *entry;
+  gchar *value;
+  HildonTimeEditorPrivate *priv;
+
+  entry = GTK_ENTRY(editable);
+  editor = HILDON_TIME_EDITOR(user_data);
+
+  priv = HILDON_TIME_EDITOR_GET_PRIVATE(editor);
+
+  /* if there is already an error we don't have to do anything */ 
+  if (!priv->error_widget)
+    {
+      value = (gchar *) gtk_entry_get_text(entry);
+  
+      if (strlen(value) == 2)
+        {
+          HildonTimeEditorPrivate *priv;
+      
+          priv = HILDON_TIME_EDITOR_GET_PRIVATE(editor);
+      
+          if (GTK_WIDGET(editable) == priv->entries[ENTRY_HOURS]) 
+            { 
+              /* we don't want a focusout signal with this grab_focus
+                 because the validation was already done during the
+                 changed signal */
+              g_signal_handlers_block_by_func(priv->entries[ENTRY_HOURS],
+                                              (gpointer) hildon_time_editor_entry_focusout, editor);
+              
+              gtk_widget_grab_focus(priv->entries[ENTRY_MINS]);
+              *position = -1;
+
+              g_signal_handlers_unblock_by_func(priv->entries[ENTRY_HOURS],
+                                                (gpointer) hildon_time_editor_entry_focusout, editor);
+              
+            }
+          else if (GTK_WIDGET(editable) == priv->entries[ENTRY_MINS] &&
+                   GTK_WIDGET_VISIBLE (priv->entries[ENTRY_SECS])) 
+            {
+              g_signal_handlers_block_by_func(priv->entries[ENTRY_MINS],
+                                              (gpointer) hildon_time_editor_entry_focusout, editor);
+
+              gtk_widget_grab_focus(priv->entries[ENTRY_SECS]);
+              *position = -1;
+
+              g_signal_handlers_unblock_by_func(priv->entries[ENTRY_MINS],
+                                                (gpointer) hildon_time_editor_entry_focusout, editor);
+              
+            }
+        }
+    }   
 }
 
 static gboolean hildon_time_editor_entry_focusout(GtkWidget * widget,
@@ -1538,9 +1754,9 @@ static gboolean hildon_time_editor_entry_keypress(GtkWidget * widget,
                the time picker icon was clicked. Before opening the time picker
                the fields are first validated and fixed. */
             hildon_time_editor_validate (editor, FALSE);
-            _gtk_button_set_depressed(GTK_BUTTON(priv->iconbutton), TRUE);
+            hildon_gtk_button_set_depressed(GTK_BUTTON(priv->iconbutton), TRUE);
             hildon_time_editor_icon_clicked(widget, data);
-            _gtk_button_set_depressed(GTK_BUTTON(priv->iconbutton), FALSE);
+            hildon_gtk_button_set_depressed(GTK_BUTTON(priv->iconbutton), FALSE);
             return TRUE;
 
         case GDK_Left:
@@ -1573,7 +1789,7 @@ static gboolean hildon_time_editor_entry_keypress(GtkWidget * widget,
  */
 
 static void
-convert_to_12h (guint *h, guint *m, guint *s, gboolean *am)
+convert_to_12h (guint *h, gboolean *am)
 {
   g_assert(0 <= *h && *h < 24);
 
@@ -1589,7 +1805,7 @@ convert_to_12h (guint *h, guint *m, guint *s, gboolean *am)
 }
 
 static void
-convert_to_24h (guint *h, guint *m, guint *s, gboolean am)
+convert_to_24h (guint *h, gboolean am)
 {
   if (*h == 12 && am) /* 12 midnight - 12:59 AM  subtract 12 hours  */
     {
@@ -1608,6 +1824,7 @@ convert_to_24h (guint *h, guint *m, guint *s, gboolean am)
  *
  * This function shows or hides the hours field.
  *
+ * Since: 0.12.4
  **/
 void hildon_time_editor_set_show_hours(HildonTimeEditor * editor,
                                        gboolean show_hours)
@@ -1642,6 +1859,8 @@ void hildon_time_editor_set_show_hours(HildonTimeEditor * editor,
  * hours in the @HildonTimeEditor
  *
  * Return value: TRUE if hours are visible. 
+ *
+ * Since: 0.12.4-1
  **/
 gboolean hildon_time_editor_get_show_hours(HildonTimeEditor *editor)
 {
@@ -1659,12 +1878,12 @@ gboolean hildon_time_editor_get_show_hours(HildonTimeEditor *editor)
 
 /**
  * hildon_time_editor_show_seconds:
- * @editor: The #HildonTimeEditor.
- * @enable: Enable or disable showing of seconds.
- *
- * This function is deprecated, use @hildon_time_editor_set_show_seconds instead.
+ * @editor: the #HildonTimeEditor
+ * @enable: enable or disable showing of seconds
  *
- **/
+ * This function is deprecated, 
+ * use #hildon_time_editor_set_show_seconds instead.
+ */
 void hildon_time_editor_show_seconds(HildonTimeEditor * editor,
                                      gboolean enable)
 {
@@ -1672,12 +1891,12 @@ void hildon_time_editor_show_seconds(HildonTimeEditor * editor,
 }
 /**
  * hildon_time_editor_enable_duration_mode:
- * @editor: The #HildonTimeEditor.
- * @enable: Enable or disable duration editor mode
- *
- * This function is deprecated, use @hildon_time_editor_set_duration_mode instead.
+ * @editor: the #HildonTimeEditor
+ * @enable: enable or disable duration editor mode
  *
- **/
+ * This function is deprecated, 
+ * use #hildon_time_editor_set_duration_mode instead.
+ */
 void hildon_time_editor_enable_duration_mode(HildonTimeEditor * editor,
                                              gboolean enable)
 {