* doc/tmpl/* * hildon-widgets/* moved widget descriptions to respective source file...
[hildon] / hildon-widgets / hildon-date-editor.c
index e84f8a7..0b2ae20 100644 (file)
  *
  */
 
-/* HILDON DOC
- * @shortdesc: DateEditor is a widget for setting, getting and showing a
- * date.
- * @longdesc: The date editor consists of a GtkLabel that shows the current
- * date in localized form and an icon. Clicking on either the label or the
- * icon opens the #HildonCalendarPopup used for selecting the date.
- * Similarly, if the editor has the focus, and space or enter key is
- * pressed, the #HildonCalendarPopup will open. 
+/**
+ * SECTION:hildon-date-editor
+ * @short_description: A widget which queries a date from user or opens
+ * a HildonCalendarPopup
+ * @see_also: #HildonCalendarPopup, #HildonTimeEditor
  * 
- * @seealso: #HildonTimeEditor, #HildonCalendarPopup
+ * HildonDateEditor is a widget with three entry fields (day, month,
+ * year) and an icon (eventbox): clicking on the icon opens up a
+ * HildonCalendarPopup.
  */
 
 #include <glib.h>
 #include <gtk/gtk.h>
-#include <gtk/gtkmarshal.h>
 #include <gdk/gdkkeysyms.h>
 #include <time.h>
 #include <stdlib.h>
 #include <stdio.h>
 #include <string.h>
-#include <langinfo.h>
 
 #include <hildon-widgets/hildon-date-editor.h>
 #include <hildon-widgets/hildon-calendar-popup.h>
 #include<libintl.h>
 #define _(string) dgettext(PACKAGE, string)
 
-#define MAX_DATE_LEN 256
 #define ENTRY_BORDERS 11
 #define DATE_EDITOR_HEIGHT 30
-#define BUTTON_SPACING 6
 
 #define DAY_ENTRY_WIDTH 2
 #define MONTH_ENTRY_WIDTH 2
@@ -79,8 +74,6 @@ static GtkContainerClass *parent_class;
 
 typedef struct _HildonDateEditorPrivate HildonDateEditorPrivate;
 
-static int hildon_date_editor_get_font_width(GtkWidget * widget);
-
 static void
 hildon_date_editor_class_init(HildonDateEditorClass * editor_class);
 
@@ -105,16 +98,10 @@ static gboolean
 hildon_date_editor_keyrelease(GtkWidget * widget, GdkEventKey * event,
                               gpointer data);
 static void
-hildon_date_editor_entry_validate(GtkEditable *widget, gpointer data);
-
-static void
-hildon_date_editor_d_entry_changed(GtkEditable *widget, gpointer data);
+hildon_date_editor_entry_validate(GtkWidget *widget, gpointer data);
 
 static void
-hildon_date_editor_m_entry_changed(GtkEditable *widget, gpointer data);
-
-static void
-hildon_date_editor_y_entry_changed(GtkEditable *widget, gpointer data);
+hildon_date_editor_entry_changed(GtkEditable *widget, gpointer data);
 
 static gboolean
 hildon_date_editor_entry_focus_out(GtkWidget * widget, GdkEventFocus * event,
@@ -130,14 +117,13 @@ static void hildon_date_editor_get_property( GObject *object, guint param_id,
                                          GValue *value, GParamSpec *pspec );
 static void hildon_date_editor_set_property (GObject *object, guint param_id,
                                        const GValue *value, GParamSpec *pspec);
-static gboolean
-hildon_date_editor_mnemonic_activate(GtkWidget *widget, gboolean group_cycling);
-
 static void
 hildon_child_forall(GtkContainer * container,
                     gboolean include_internals,
                     GtkCallback callback, gpointer callback_data);
+
 static void hildon_date_editor_destroy(GtkObject * self);
+
 static void
 hildon_date_editor_size_allocate(GtkWidget * widget,
                                  GtkAllocation * allocation);
@@ -146,14 +132,10 @@ static void
 hildon_date_editor_size_request(GtkWidget * widget,
                                 GtkRequisition * requisition);
 
-static guint
-hildon_date_editor_check_locale_settings(HildonDateEditor * editor);
-
-static void hildon_date_editor_finalize(GObject * object);
-
 static gboolean
 _hildon_date_editor_entry_select_all(GtkWidget *widget);
 
+/* Property indices */
 enum
 {
   PROP_DAY = 1,
@@ -164,45 +146,28 @@ enum
 };
 
 struct _HildonDateEditorPrivate {
-    guint year; /* current year in the entry */
-    guint month;        /* current month in the entry */
-    guint day;  /* current day in the entry */
-
-    guint y_orig;       /* roll back value for year (if entry has illegal
-                           value) */
-    guint m_orig;       /* roll back value for month */
-    guint d_orig;       /* roll back value for day */
-
-    gint button_press;  /* wheter to show pressed image */
+    /* Cache of values in the entries, used in setting only parts of the date */
+    guint year;   /* current year  in the entry */
+    guint month;  /* current month in the entry */
+    guint day;    /* current day   in the entry */
 
-    gboolean editor_pressed;
-    gboolean valid_value;
+    gboolean calendar_icon_pressed;
 
-    GtkWidget *frame;   /* borders around the date */
-    GtkWidget *d_event_box_image;       /* icon */
-    GtkWidget *d_box_date;      /* hbox for date */
+    GtkWidget *frame;             /* borders around the date */
+    GtkWidget *d_event_box_image; /* icon */
+    GtkWidget *d_box_date;        /* hbox for date */
 
     GtkWidget *d_entry; /* GtkEntry for day */
     GtkWidget *m_entry; /* GtkEntry for month */
     GtkWidget *y_entry; /* GtkEntry for year */
 
-    GtkWidget *dm_delim;  /* Delimeter between day and month entries */
-    GtkWidget *my_delim;  /* Delimeter between month and year entries */
+    GList *delims;      /* List of delimeters between the fields (and possible at the ends) */
+    GtkWidget *calendar_icon;
 
-    GtkWidget *d_image; /* normal icon image */
-    GtkWidget *d_image_pressed;
-    guint locale_type;
+    gboolean skip_validation; /* don't validate date at all */
 
-    gboolean skip_validation;
-
-    gint min_year;
-    gint max_year;
-};
-
-enum {
-    MONTH_DAY_YEAR,
-    DAY_MONTH_YEAR,
-    YEAR_MONTH_DAY
+    gint min_year; /* minimum year allowed */
+    gint max_year; /* maximum year allowed */
 };
 
 enum {
@@ -249,14 +214,12 @@ hildon_date_editor_class_init(HildonDateEditorClass * editor_class)
 
     gobject_class->set_property = hildon_date_editor_set_property;
     gobject_class->get_property = hildon_date_editor_get_property;
-    widget_class->mnemonic_activate = hildon_date_editor_mnemonic_activate;
     widget_class->size_request = hildon_date_editor_size_request;
     widget_class->size_allocate = hildon_date_editor_size_allocate;
     widget_class->focus = hildon_composite_widget_focus;
 
     container_class->forall = hildon_child_forall;
     GTK_OBJECT_CLASS(editor_class)->destroy = hildon_date_editor_destroy;
-    gobject_class->finalize = hildon_date_editor_finalize;
 
     editor_class->date_error = hildon_date_editor_date_error; 
     
@@ -335,11 +298,104 @@ hildon_date_editor_class_init(HildonDateEditorClass * editor_class)
                                    G_PARAM_READWRITE) );
 }
 
+/* Forces setting of the icon to certain state. Used initially
+   and from the actual setter function */
+static void
+real_set_calendar_icon_state(HildonDateEditorPrivate *priv, 
+    gboolean pressed)
+{
+    gtk_image_set_from_icon_name(GTK_IMAGE(priv->calendar_icon),
+        pressed ? "qgn_widg_datedit_pr" : "qgn_widg_datedit",
+        HILDON_ICON_SIZE_WIDG);
+
+    priv->calendar_icon_pressed = pressed;
+}
+
+/* Sets the icon to given state (normal/pressed). Returns
+   info if the state actually changed. */
+static gboolean 
+hildon_date_editor_set_calendar_icon_state(HildonDateEditor *editor,
+    gboolean pressed)
+{
+    HildonDateEditorPrivate *priv;
+
+    g_assert(HILDON_IS_DATE_EDITOR(editor));
+
+    priv = HILDON_DATE_EDITOR_GET_PRIVATE(editor);
+    if (pressed != priv->calendar_icon_pressed) {
+        real_set_calendar_icon_state(priv, pressed);
+        return TRUE;
+    }
+
+    return FALSE;
+}
+
+/* Packing day, month and year entries depend on locale settings
+   We find out the order and all separators by converting a known
+   date to default format and inspecting the result string */
+static void apply_locale_field_order(HildonDateEditorPrivate *priv)
+{
+    GDate locale_test_date;
+    GtkWidget *delim;
+    gchar buffer[256];
+    gchar *iter, *delim_text;
+
+    g_date_set_dmy(&locale_test_date, 1, 2, 1970);
+    (void) g_date_strftime(buffer, sizeof(buffer), "%x", &locale_test_date);    
+    iter = buffer;
+
+    while (*iter)
+    {
+       gchar *endp;
+       unsigned long value;
+
+       /* Try to convert the current location into number. */
+       value = strtoul(iter, &endp, 10);
+
+       /* If the conversion didn't progress or the detected value was
+          unknown (we used a fixed date, you remember), we treat 
+          current position as a literal */
+       switch (value)
+       {
+         case 1:
+            gtk_box_pack_start(GTK_BOX(priv->d_box_date),
+                priv->d_entry, FALSE, FALSE, 0);
+            break;
+         case 2:
+            gtk_box_pack_start(GTK_BOX(priv->d_box_date),
+                priv->m_entry, FALSE, FALSE, 0);
+            break;
+         case 70:   /* %x format uses only 2 numbers for some locales */
+         case 1970:
+            gtk_box_pack_start(GTK_BOX(priv->d_box_date),
+                priv->y_entry, FALSE, FALSE, 0);
+            break;
+         default:
+            /* All non-number characters starting from current position 
+               form the delimeter */
+            for (endp = iter; *endp; endp++)
+                if (g_ascii_isdigit(*endp))
+                    break;
+
+            /* Now endp points one place past the delimeter text */
+            delim_text = g_strndup(iter, endp - iter);
+            delim = gtk_label_new(delim_text);
+            gtk_box_pack_start(GTK_BOX(priv->d_box_date),
+                           delim, FALSE, FALSE, 0);
+            priv->delims = g_list_append(priv->delims, delim);
+            g_free(delim_text);
+
+            break;
+       };
+
+       iter = endp;
+    }
+}
+
 static void hildon_date_editor_init(HildonDateEditor * editor)
 {
     HildonDateEditorPrivate *priv;
     GDate cur_date;
-    guint locale_type;
 
     priv = HILDON_DATE_EDITOR_GET_PRIVATE(editor);
 
@@ -357,12 +413,6 @@ static void hildon_date_editor_init(HildonDateEditor * editor)
     priv->min_year = DEFAULT_MIN_YEAR;
     priv->max_year = DEFAULT_MAX_YEAR;
 
-    priv->y_orig = 0;
-    priv->m_orig = 0;
-    priv->d_orig = 0;
-    priv->button_press = FALSE;
-    priv->valid_value = TRUE;
-
     /* make widgets */
     priv->frame = gtk_frame_new(NULL);
     gtk_container_set_border_width(GTK_CONTAINER(priv->frame), 0);
@@ -370,7 +420,6 @@ static void hildon_date_editor_init(HildonDateEditor * editor)
     priv->d_entry = gtk_entry_new();
     priv->m_entry = gtk_entry_new();
     priv->y_entry = gtk_entry_new();
-    priv->editor_pressed = FALSE;
 
     g_object_set (G_OBJECT(priv->d_entry), "input-mode", 
                   HILDON_INPUT_MODE_HINT_NUMERIC, NULL);
@@ -392,80 +441,35 @@ static void hildon_date_editor_init(HildonDateEditor * editor)
     gtk_entry_set_has_frame(GTK_ENTRY(priv->m_entry), FALSE);
     gtk_entry_set_has_frame(GTK_ENTRY(priv->y_entry), FALSE);
 
-    priv->dm_delim = gtk_label_new(_("Ecdg_ti_date_editor_separator"));
-    priv->my_delim = gtk_label_new(_("Ecdg_ti_date_editor_separator"));
+    gtk_widget_set_composite_name(priv->d_entry, "day_entry");
+    gtk_widget_set_composite_name(priv->m_entry, "month_entry");
+    gtk_widget_set_composite_name(priv->y_entry, "year_entry");
 
     priv->d_box_date = gtk_hbox_new(FALSE, 0);
 
     priv->d_event_box_image = gtk_event_box_new();
+    priv->calendar_icon = gtk_image_new();
+    real_set_calendar_icon_state(priv, FALSE);
 
-    priv->d_image = gtk_image_new_from_icon_name("qgn_widg_datedit",
-                                                 HILDON_ICON_SIZE_WIDG);
-    priv->d_image_pressed = gtk_image_new_from_icon_name("qgn_widg_datedit_pr",
-                                                         HILDON_ICON_SIZE_WIDG);
-    g_object_ref(G_OBJECT(priv->d_image));
-    g_object_ref(G_OBJECT(priv->d_image_pressed));
-    gtk_object_sink(GTK_OBJECT(priv->d_image));
-    gtk_object_sink(GTK_OBJECT(priv->d_image_pressed));
-    /* packing */
-    /* Packing day, month and year entries depend on locale settings */
-    priv->locale_type = locale_type =
-        hildon_date_editor_check_locale_settings(editor);
-
-    if (locale_type == DAY_MONTH_YEAR)
-        gtk_box_pack_start(GTK_BOX(priv->d_box_date),
-                           priv->d_entry, FALSE, FALSE, 0);
-    else if (locale_type == MONTH_DAY_YEAR)
-        gtk_box_pack_start(GTK_BOX(priv->d_box_date),
-                           priv->m_entry, FALSE, FALSE, 0);
-    else
-        gtk_box_pack_start(GTK_BOX(priv->d_box_date),
-                           priv->y_entry, FALSE, FALSE, 0);
-    gtk_box_pack_start(GTK_BOX(priv->d_box_date),
-                       priv->dm_delim, FALSE, FALSE, 0);
-    if (locale_type == DAY_MONTH_YEAR)
-        gtk_box_pack_start(GTK_BOX(priv->d_box_date),
-                           priv->m_entry, FALSE, FALSE, 0);
-    else if (locale_type == MONTH_DAY_YEAR)
-        gtk_box_pack_start(GTK_BOX(priv->d_box_date),
-                           priv->d_entry, FALSE, FALSE, 0);
-    else
-        gtk_box_pack_start(GTK_BOX(priv->d_box_date),
-                           priv->m_entry, FALSE, FALSE, 0);
-    gtk_box_pack_start(GTK_BOX(priv->d_box_date),
-                       priv->my_delim, FALSE, FALSE, 0);
-    if (locale_type == DAY_MONTH_YEAR)
-        gtk_box_pack_start(GTK_BOX(priv->d_box_date),
-                           priv->y_entry, FALSE, FALSE, 0);
-    else if (locale_type == MONTH_DAY_YEAR)
-        gtk_box_pack_start(GTK_BOX(priv->d_box_date),
-                           priv->y_entry, FALSE, FALSE, 0);
-    else
-        gtk_box_pack_start(GTK_BOX(priv->d_box_date),
-                           priv->d_entry, FALSE, FALSE, 0);
+    apply_locale_field_order(priv);
 
     gtk_container_add(GTK_CONTAINER(priv->frame), priv->d_box_date);
-    gtk_container_add(GTK_CONTAINER(priv->d_event_box_image), priv->d_image);
+    gtk_container_add(GTK_CONTAINER(priv->d_event_box_image), priv->calendar_icon);
 
     gtk_widget_set_parent(priv->frame, GTK_WIDGET(editor));
     gtk_widget_set_parent(priv->d_event_box_image, GTK_WIDGET(editor));
     gtk_widget_show_all(priv->frame);
     gtk_widget_show_all(priv->d_event_box_image);
-
-
     
     /* image signal connects */
     g_signal_connect(GTK_OBJECT(priv->d_event_box_image), "button_press_event",
                      G_CALLBACK(hildon_date_editor_icon_press), editor);
-
     g_signal_connect(GTK_OBJECT(priv->d_event_box_image),
                      "button_release_event",
                      G_CALLBACK(hildon_date_editor_released), editor);
-
     g_signal_connect(GTK_OBJECT(priv->d_event_box_image), "key-press-event",
                      G_CALLBACK(hildon_date_editor_keypress), editor);
-
-    g_signal_connect(GTK_OBJECT(priv->d_image), "key-press-event",
+    g_signal_connect(GTK_OBJECT(priv->calendar_icon), "key-press-event",
                      G_CALLBACK(hildon_date_editor_keypress), editor);
 
 
@@ -518,13 +522,13 @@ static void hildon_date_editor_init(HildonDateEditor * editor)
     hildon_date_editor_set_date(editor, priv->year, priv->month, priv->day);
 
     g_signal_connect(GTK_OBJECT(priv->d_entry), "changed",
-                     G_CALLBACK(hildon_date_editor_d_entry_changed), editor);
+                     G_CALLBACK(hildon_date_editor_entry_changed), editor);
 
     g_signal_connect(GTK_OBJECT(priv->m_entry), "changed",
-                     G_CALLBACK(hildon_date_editor_m_entry_changed), editor);
+                     G_CALLBACK(hildon_date_editor_entry_changed), editor);
 
     g_signal_connect(GTK_OBJECT(priv->y_entry), "changed",
-                     G_CALLBACK(hildon_date_editor_y_entry_changed), editor);
+                     G_CALLBACK(hildon_date_editor_entry_changed), editor);
 
     gtk_widget_pop_composite_child();
 }
@@ -555,6 +559,7 @@ static void hildon_date_editor_set_property (GObject *object, guint param_id,
       if (val <= priv->max_year)
         {
           priv->min_year = val;
+          /* Clamp current year */
           if (hildon_date_editor_get_year (editor) < priv->min_year)
             hildon_date_editor_set_year (editor, priv->min_year);
         }
@@ -567,6 +572,7 @@ static void hildon_date_editor_set_property (GObject *object, guint param_id,
       if (val >= priv->min_year)
         {
           priv->max_year = val;
+          /* Clamp current year */
           if (hildon_date_editor_get_year (editor) > priv->max_year)
             hildon_date_editor_set_year (editor, priv->max_year);
         }
@@ -614,48 +620,6 @@ static void hildon_date_editor_get_property( GObject *object, guint param_id,
   }
 }
 
-static gboolean
-hildon_date_editor_mnemonic_activate(GtkWidget *widget, gboolean group_cycling)
-
-{
-  GtkWidget *entry;
-  HildonDateEditorPrivate *priv = HILDON_DATE_EDITOR_GET_PRIVATE(widget);
-
-  if( priv->locale_type == MONTH_DAY_YEAR )
-    entry = priv->m_entry;
-  else
-    entry = priv->d_entry;
-
-  gtk_widget_grab_focus( entry );
-  g_idle_add((GSourceFunc)_hildon_date_editor_entry_select_all, entry);
-  return TRUE;
-}
-
-static void hildon_date_editor_finalize(GObject * object)
-{
-    HildonDateEditorPrivate *priv = HILDON_DATE_EDITOR_GET_PRIVATE(object);
-
-    g_object_unref(G_OBJECT(priv->d_image));
-    g_object_unref(G_OBJECT(priv->d_image_pressed));
-    if( G_OBJECT_CLASS(parent_class)->finalize )
-      G_OBJECT_CLASS(parent_class)->finalize(object);
-}
-
-static guint
-hildon_date_editor_check_locale_settings(HildonDateEditor * editor)
-{
-    gchar *dfmt;
-
-    dfmt = nl_langinfo(D_FMT);
-
-    if (!strncmp(dfmt, "%d", 2))
-        return DAY_MONTH_YEAR;
-    else if (!strncmp(dfmt, "%m", 2))
-        return MONTH_DAY_YEAR;
-    else
-        return YEAR_MONTH_DAY;
-}
-
 static void hildon_child_forall(GtkContainer * container,
                                 gboolean include_internals,
                                 GtkCallback callback,
@@ -664,8 +628,8 @@ static void hildon_child_forall(GtkContainer * container,
     HildonDateEditor *editor;
     HildonDateEditorPrivate *priv;
 
-    g_return_if_fail(container);
-    g_return_if_fail(callback);
+    g_assert(HILDON_IS_DATE_EDITOR(container));
+    g_assert(callback);
 
     editor = HILDON_DATE_EDITOR(container);
     priv = HILDON_DATE_EDITOR_GET_PRIVATE(editor);
@@ -690,6 +654,10 @@ static void hildon_date_editor_destroy(GtkObject * self)
         gtk_widget_unparent(priv->d_event_box_image);
         priv->d_event_box_image = NULL;
     }
+    if (priv->delims) {
+        g_list_free(priv->delims);
+        priv->delims = NULL;
+    }
 
     if (GTK_OBJECT_CLASS(parent_class)->destroy)
         GTK_OBJECT_CLASS(parent_class)->destroy(self);
@@ -698,11 +666,11 @@ static void hildon_date_editor_destroy(GtkObject * self)
 /**
  * hildon_date_editor_new:
  *
- * This function creates a new time editor. The current system date
+ * Creates a new date editor. The current system date
  * is shown in the editor.
  *
- * Return value: Pointer to a new @HildonDateEditor widget.
- **/
+ * Returns: pointer to a new @HildonDateEditor widget.
+ */
 GtkWidget *hildon_date_editor_new(void)
 {
     return GTK_WIDGET(g_object_new(HILDON_DATE_EDITOR_TYPE, NULL));
@@ -715,19 +683,65 @@ GtkWidget *hildon_date_editor_new(void)
  * @month: month
  * @day: day
  *
- * This function sets the date shown in the editor. The function returns 
- * if the date specified by the arguments is not valid, the
- * function returns.
- **/
-void hildon_date_editor_set_date(HildonDateEditor * date,
+ * Sets the date shown in the editor. 
+ */
+void hildon_date_editor_set_date(HildonDateEditor * editor,
                                  guint year, guint month, guint day)
 {
-    g_return_if_fail(date);
-    g_return_if_fail(HILDON_IS_DATE_EDITOR(date));
+    HildonDateEditorPrivate *priv;
+
+    g_return_if_fail(HILDON_IS_DATE_EDITOR(editor));
+
+    /* This function cannot be implemented by calling
+       component setters, since applying the individual
+       values one by one can make the date temporarily
+       invalid (depending on what the previous values were), 
+       which in turn causes that the desired date
+       is not set (even though it's valid). We must set all the
+       components at one go and not try to do any validation etc
+       there in between. */
 
-    hildon_date_editor_set_year(date, year);
-    hildon_date_editor_set_month(date, month);
-    hildon_date_editor_set_day(date, day);
+    g_return_if_fail(HILDON_IS_DATE_EDITOR(editor));
+    priv = HILDON_DATE_EDITOR_GET_PRIVATE(editor);
+
+    if (g_date_valid_dmy(day, month, year))
+    {
+        GDate date;
+        gchar buffer[256];
+        
+        priv->year = year;
+        priv->month = month;
+        priv->day = day;
+        
+        g_date_set_dmy(&date, day, month, year);
+
+        /* We apply the new values, but do not want automatic focus move
+           etc to take place */
+        g_snprintf(buffer, sizeof(buffer), "%04d", year);
+        g_signal_handlers_block_by_func(priv->y_entry, 
+            (gpointer) hildon_date_editor_entry_changed, editor);
+        gtk_entry_set_text(GTK_ENTRY(priv->y_entry), buffer);
+        g_signal_handlers_unblock_by_func(priv->y_entry, 
+            (gpointer) hildon_date_editor_entry_changed, editor);
+
+        g_date_strftime(buffer, sizeof(buffer), "%m", &date);
+        g_signal_handlers_block_by_func(priv->m_entry, 
+            (gpointer) hildon_date_editor_entry_changed, editor);
+        gtk_entry_set_text(GTK_ENTRY(priv->m_entry), buffer);
+        g_signal_handlers_unblock_by_func(priv->m_entry, 
+            (gpointer) hildon_date_editor_entry_changed, editor);
+
+        g_date_strftime(buffer, sizeof(buffer), "%d", &date);
+        g_signal_handlers_block_by_func(priv->d_entry, 
+            (gpointer) hildon_date_editor_entry_changed, editor);
+        gtk_entry_set_text(GTK_ENTRY(priv->d_entry), buffer);
+        g_signal_handlers_unblock_by_func(priv->d_entry, 
+            (gpointer) hildon_date_editor_entry_changed, editor);
+  
+        g_object_notify(G_OBJECT(editor), "year");
+        g_object_notify(G_OBJECT(editor), "month");
+        g_object_notify(G_OBJECT(editor), "day");
+    }
 }
 
 /**
@@ -737,15 +751,14 @@ void hildon_date_editor_set_date(HildonDateEditor * date,
  * @month: month
  * @day: day
  *
- * This function returns the year, month, and day currently on the
+ * Returns: the year, month, and day currently on the
  * date editor.
- **/
+ */
 void hildon_date_editor_get_date(HildonDateEditor * date,
                                  guint * year, guint * month, guint * day)
 {
     HildonDateEditorPrivate *priv;
 
-    g_return_if_fail(date);
     g_return_if_fail(HILDON_IS_DATE_EDITOR(date));
     g_return_if_fail(year);
     g_return_if_fail(month);
@@ -753,8 +766,32 @@ void hildon_date_editor_get_date(HildonDateEditor * date,
 
     priv = HILDON_DATE_EDITOR_GET_PRIVATE(date);
 
-    /*dont know why these variable are used, i think it makes more
-     * sense to directly get the content from the current text entry field*/
+    /* FIXME: The role of priv->{day,month,year} members vs. entry contents
+              is unclear. They do not neccesarily match and still the texts are
+              used as return values and members for some internal validation!!
+              At least a partly reason is to allow empty text to become
+              0 return value, while members are restricted to valid ranges?!
+              However, if we change the current way, we are likely to break 
+              some applications  if they rely on some specific way how this 
+              widget currently handles empty values and temporarily invalid values.
+
+              The key issue is this: What should the _get methods return while
+              user is editing a field and the result is incomplete. The
+              partial result? The last good result? If we return partial result
+              we also need a way to inform if the date is not valid. Current
+              implementation is some kind of hybrid of these two...
+
+              for example:
+                 hildon_date_editor_set_day(editor, hildon_date_editor_get_day(editor));
+
+              easily fails, since set_day tries to force validity while get_day
+              doesn't do that.
+
+              Proposal: Always return the same values that are shown in the
+                        fields. We add a separate flag (Or use GDate) to 
+                        indicate if the current date is valid. This would allow 
+                        setters to make the date invalid as well.
+    */
     *year = /*priv->year;*/
       (guint) atoi(gtk_entry_get_text(GTK_ENTRY(priv->y_entry)));
     *month = /*priv->month;*/
@@ -767,24 +804,13 @@ static gboolean hildon_date_editor_icon_press(GtkWidget * widget,
                                               GdkEventButton * event,
                                               gpointer data)
 {
-    HildonDateEditor *ed;
-    HildonDateEditorPrivate *priv;
+    g_assert(GTK_IS_WIDGET(widget));
+    g_assert(event != NULL);
+    g_assert(HILDON_IS_DATE_EDITOR(data));
 
-    g_return_val_if_fail(widget, FALSE);
-    g_return_val_if_fail(event, FALSE);
-    g_return_val_if_fail(data, FALSE);
+    if (event->button == 1)
+       hildon_date_editor_set_calendar_icon_state(HILDON_DATE_EDITOR(data), TRUE);
 
-    ed = HILDON_DATE_EDITOR(data);
-    priv = HILDON_DATE_EDITOR_GET_PRIVATE(ed);
-
-    if (event->button == 1 && !priv->button_press) {
-        gtk_container_remove(GTK_CONTAINER(priv->d_event_box_image),
-                             priv->d_image);
-        gtk_container_add(GTK_CONTAINER(priv->d_event_box_image),
-                          priv->d_image_pressed);
-        gtk_widget_show_all(priv->d_event_box_image);
-        priv->button_press = TRUE;
-    }
     return FALSE;
 }
 
@@ -798,14 +824,13 @@ static gboolean hildon_date_editor_entry_released(GtkWidget * widget,
     ed = HILDON_DATE_EDITOR(data);
     priv = HILDON_DATE_EDITOR_GET_PRIVATE(ed);
 
-    if (priv->valid_value && event->button == 1) {
-
-        /* We might not get focus because unvalid values in entries */
+    if (event->button == 1) {
+        /* We might not get focus because of invalid values in entries */
         if (GTK_WIDGET_HAS_FOCUS(widget))
                        g_idle_add((GSourceFunc)
                                        _hildon_date_editor_entry_select_all, GTK_ENTRY(widget));
-    } else
-        priv->valid_value = TRUE;
+    }
+
     return FALSE;
 }
 
@@ -823,30 +848,27 @@ static gboolean hildon_date_editor_entry_focusin(GtkWidget * widget,
 }
 
 
-/* This handler is called from mainloop
-   after button exposes are processed */
-static gboolean idle_popup(gpointer data)
+static void popup_calendar_dialog(HildonDateEditor *ed)
 {
     guint y = 0, m = 0, d = 0;
-    HildonDateEditor *ed;
     GtkWidget *popup;
     GtkWidget *parent;
     guint result;
     GValue val = {0, };
 
-    ed = HILDON_DATE_EDITOR(data);
-
     hildon_date_editor_get_date(ed, &y, &m, &d);
 
     parent = gtk_widget_get_ancestor(GTK_WIDGET(ed), GTK_TYPE_WINDOW);
     popup = hildon_calendar_popup_new(GTK_WINDOW(parent), y, m, d);
 
     g_value_init(&val, G_TYPE_INT);
-    g_object_get_property(G_OBJECT(data), "min-year", &val);
+    /* Set max/min year in calendar popup to date editor values */
+    g_object_get_property(G_OBJECT(ed), "min-year", &val);
     g_object_set_property(G_OBJECT(popup), "min-year", &val);
-    g_object_get_property(G_OBJECT(data), "max-year", &val);
+    g_object_get_property(G_OBJECT(ed), "max-year", &val);
     g_object_set_property(G_OBJECT(popup), "max-year", &val);
 
+    /* Pop up calendar */
     result = gtk_dialog_run(GTK_DIALOG(popup));
     switch (result) {
     case GTK_RESPONSE_OK:
@@ -857,8 +879,6 @@ static gboolean idle_popup(gpointer data)
     }
 
     gtk_widget_destroy(popup);
-
-    return FALSE;
 }
 
 /* button released */
@@ -867,193 +887,160 @@ static gboolean hildon_date_editor_released(GtkWidget * widget,
                                             gpointer data)
 {
     HildonDateEditor *ed;
-    HildonDateEditorPrivate *priv;
 
-    g_return_val_if_fail(widget, FALSE);
-    g_return_val_if_fail(event, FALSE);
-    g_return_val_if_fail(data, FALSE);
+    g_assert(GTK_IS_WIDGET(widget));
+    g_assert(event != NULL);
+    g_assert(HILDON_IS_DATE_EDITOR(data));
 
     ed = HILDON_DATE_EDITOR(data);
-    priv = HILDON_DATE_EDITOR_GET_PRIVATE(ed);
-
-    if (!priv->button_press)
-        return FALSE;
-
-    /* change the icon image back to normal */
-    gtk_container_remove(GTK_CONTAINER(priv->d_event_box_image),
-                         priv->d_image_pressed);
-    gtk_container_add(GTK_CONTAINER(priv->d_event_box_image),
-                      priv->d_image);
-    gtk_widget_show_all(priv->d_event_box_image);
-
-    /* Wait until exposes are ready */
-    g_idle_add(idle_popup, ed);
 
-    priv->button_press = FALSE;
+    if (hildon_date_editor_set_calendar_icon_state(ed, FALSE))
+        popup_calendar_dialog(ed);
 
     return FALSE;
 }
 
+/* This is called whenever some editor filed loses the focus and
+   when the all of the fields are filled. 
+   Earlier this was called whenever an entry changed */
+/* FIXME: Validation on focus_out is broken by concept */
 static void
-hildon_date_editor_entry_validate(GtkEditable *widget, gpointer data)
+hildon_date_editor_entry_validate(GtkWidget *widget, gpointer data)
 {
     HildonDateEditor *ed;
     HildonDateEditorPrivate *priv;
+    gint d, m, y, max_days;
+    gboolean r;  /* temp return values for signals */
+    const gchar *text;        
+    gint error_code = NO_ERROR;
 
-    gint d, m, y;
-    gboolean r; /*return value storage needed, but no real use*/
-
-    g_return_if_fail(data);
-    g_return_if_fail(widget);
+    g_assert(HILDON_IS_DATE_EDITOR(data));
+    g_assert(GTK_IS_ENTRY(widget));
 
     ed = HILDON_DATE_EDITOR(data);
     priv = HILDON_DATE_EDITOR_GET_PRIVATE(ed);
 
-    /*if the field is empty, we skip the checking*/
-    if(gtk_entry_get_text(GTK_ENTRY(widget)) == NULL || 
-            *gtk_entry_get_text(GTK_ENTRY(widget)) == '\0')
-        return;
-
-    if(!priv->skip_validation) {
-        d = atoi(gtk_entry_get_text(GTK_ENTRY(priv->d_entry)));
-        m = atoi(gtk_entry_get_text(GTK_ENTRY(priv->m_entry)));
-        y = atoi(gtk_entry_get_text(GTK_ENTRY(priv->y_entry)));
-
-        /*NOTICE we could/should use hildon_date_editor_set_year and such functions
-         * to set the date, instead of use gtk_entry_set_text, and then change the priv member
-         * but hildon_date_editor_set_year and such functions check if the date is valid,
-         * we do want to do date validation check here according to spec*/
-        if(GTK_WIDGET(widget) == priv->d_entry)
-        {
-            if(d > 0 && d < 32) {
-                priv->day = d;
-                return;
-            }
-            else if(d < 1) {
-                g_signal_emit(ed, date_editor_signals[DATE_ERROR], 0, 
-                        MIN_DAY, &r);
-                gtk_entry_set_text(GTK_ENTRY(priv->d_entry), "01");
-                priv->day = priv->d_orig = 1;
+    if (priv->skip_validation)
+       return;
+
+    /*check if the calling entry is empty*/
+    text = gtk_entry_get_text(GTK_ENTRY(widget));
+    if(text == NULL || text[0] == 0)
+    {
+      if (widget == priv->d_entry)
+         g_signal_emit(ed, date_editor_signals[DATE_ERROR], 0, EMPTY_DAY, &r);
+      else if(widget == priv->m_entry)
+         g_signal_emit(ed, date_editor_signals[DATE_ERROR], 0, EMPTY_MONTH, &r);
+      else
+         g_signal_emit(ed, date_editor_signals[DATE_ERROR], 0, EMPTY_YEAR, &r);
+
+      return;
+    }
+
+    /* Ok, we now check validity. Some fields can be empty */
+    text = gtk_entry_get_text(GTK_ENTRY(priv->d_entry));
+    if (text == NULL || text[0] == 0) return;
+    d = atoi(text);
+    text = gtk_entry_get_text(GTK_ENTRY(priv->m_entry));
+    if (text == NULL || text[0] == 0) return;
+    m = atoi(text);
+    text = gtk_entry_get_text(GTK_ENTRY(priv->y_entry));
+    if (text == NULL || text[0] == 0) return;
+    y = atoi(text);
+
+    /* Did it actually change? */
+    if (d != priv->day || m != priv->month || y != priv->year)
+    {
+        /* We could/should use hildon_date_editor_set_year and such functions
+         * to set the date, instead of use gtk_entry_set_text, and then change
+        * the priv member but hildon_date_editor_set_year and such functions
+        * check if the date is valid, we do want to do date validation check
+        * here according to spec */
+
+       /* Validate month */
+        if(widget == priv->m_entry) {
+            if(m < 1) {
+                error_code = MIN_MONTH;
+                m = 1;
             }
-            else {
-                g_signal_emit(ed, date_editor_signals[DATE_ERROR], 0, 
-                        MAX_DAY, &r);
-                gtk_entry_set_text(GTK_ENTRY(priv->d_entry), "31");
-                priv->day = priv->d_orig = 31;
+            else if (m > 12) {
+                error_code = MAX_MONTH;
+                m = 12;
             }
-            g_idle_add ((GSourceFunc)
-                    _hildon_date_editor_entry_select_all, 
-                    priv->d_entry);
-            gtk_widget_grab_focus(priv->d_entry);
-            return;
         }
 
-        if(GTK_WIDGET(widget) == priv->m_entry)
-        {
-            if(m > 0 && m < 13)
-            {
-                priv->month = m;
-                return;
-            }
-            else if(m < 1)
-            {
-                g_signal_emit(ed, date_editor_signals[DATE_ERROR], 0, 
-                        MIN_MONTH, &r);
-                gtk_entry_set_text(GTK_ENTRY(priv->m_entry), "01");
-                priv->month = priv->m_orig = 1;
-
+       /* Validate year */
+        if(widget == priv->y_entry) {
+            if (y < priv->min_year) {
+                error_code = MIN_YEAR;
+                y = priv->min_year;
             }
-            else
-            {
-                g_signal_emit(ed, date_editor_signals[DATE_ERROR], 0, 
-                        MAX_MONTH, &r);
-                gtk_entry_set_text(GTK_ENTRY(priv->m_entry), "12");
-                priv->month = priv->m_orig = 12;
+            else if (y > priv->max_year) {
+                error_code = MAX_YEAR;
+                y = priv->max_year;
             }
+        }
 
-            g_idle_add ((GSourceFunc) 
-                    _hildon_date_editor_entry_select_all, 
-                    priv->m_entry);
-            gtk_widget_grab_focus(priv->m_entry);
-            return;
+       /* Validate day. We have to do this in every case, since
+           changing month or year can make the day number to be invalid */
+        max_days = g_date_get_days_in_month(m,y);
+        if(d < 1) {
+           error_code = MIN_DAY;
+           d = 1;
+        }
+        else if (d > max_days) {
+           if (d > 31) {         
+               error_code = MAX_DAY;
+               d = max_days;
+           }
+           else {                /* the date does not exist (is invalid) */
+               error_code = INVALID_DATE;
+               /* check what was changed and restore previous value */
+               if ( widget == priv->y_entry )
+                   y = priv->year;
+               else if ( widget == priv->m_entry )
+                   m = priv->month;
+               else
+                   d = priv->day;
+           }
         }
 
-        if(GTK_WIDGET(widget) == priv->y_entry)
+        if (error_code != NO_ERROR)
         {
-            if(y >= priv->min_year && y <= priv->max_year) {
-                priv->year = y;
-                return;
-            }
-            else if (y < priv->min_year) {
-                char year[5];
-
-                g_signal_emit(ed, date_editor_signals[DATE_ERROR], 0, 
-                              MIN_YEAR, &r);
-                sprintf(year, "%04d", priv->min_year);
-                gtk_entry_set_text(GTK_ENTRY(priv->y_entry), year);
-                priv->year = priv->y_orig = priv->min_year;
-            }
-            /* y > priv->max_year */
-            else {
-                char year[5];
-
-                g_signal_emit(ed, date_editor_signals[DATE_ERROR], 0, 
-                              MAX_YEAR, &r);
-                sprintf(year, "%04d", priv->max_year);
-                gtk_entry_set_text(GTK_ENTRY(priv->y_entry), year);
-                priv->year = priv->y_orig = priv->max_year;
-            }
+            g_signal_emit(ed, date_editor_signals[DATE_ERROR], 0, error_code, &r);
+
             g_idle_add ((GSourceFunc) 
                     _hildon_date_editor_entry_select_all, 
-                    priv->y_entry);
-            gtk_widget_grab_focus(priv->y_entry);
-            return;
+                    widget);
+
+           /* Set focus back to invalid entry */
+            gtk_widget_grab_focus(widget);
         }
     }
-}
 
-static void
-hildon_date_editor_d_entry_changed(GtkEditable *ed, gpointer data)
-{
-  HildonDateEditorPrivate *priv = HILDON_DATE_EDITOR_GET_PRIVATE(data);
-
-  if (strlen(gtk_entry_get_text(GTK_ENTRY(ed))) == DAY_ENTRY_WIDTH)
-    {
-      if (priv->locale_type == DAY_MONTH_YEAR)
-        gtk_widget_grab_focus(priv->m_entry);
-      else if (priv->locale_type == MONTH_DAY_YEAR)
-        gtk_widget_grab_focus(priv->y_entry);
-      else
-        hildon_date_editor_entry_validate(ed, data);
-    }
+    /* Fix and reformat the date after error signal is processed. 
+       reformatting can be needed even in a such case that numerical
+       values of the date components are the same as earlier. */
+    hildon_date_editor_set_date(ed, y, m, d);
 }
 
+/* When entry becomes full, we move the focus to the next field.
+   If we are on the last field, the whole contents are validated. */
 static void
-hildon_date_editor_m_entry_changed(GtkEditable *ed, gpointer data)
+hildon_date_editor_entry_changed(GtkEditable *ed, gpointer data)
 {
-  HildonDateEditorPrivate *priv = HILDON_DATE_EDITOR_GET_PRIVATE(data);
+  GtkEntry *entry;
 
-  if (strlen(gtk_entry_get_text(GTK_ENTRY(ed))) == MONTH_ENTRY_WIDTH)
-    {
-      if (priv->locale_type == DAY_MONTH_YEAR)
-        gtk_widget_grab_focus(priv->y_entry);
-      else if (priv->locale_type == MONTH_DAY_YEAR ||
-               priv->locale_type == YEAR_MONTH_DAY)
-        gtk_widget_grab_focus(priv->d_entry);
-    }
-}
+  g_assert(GTK_IS_ENTRY(ed));
+  g_assert(HILDON_IS_DATE_EDITOR(data));
 
-static void
-hildon_date_editor_y_entry_changed(GtkEditable *ed, gpointer data)
-{
-  HildonDateEditorPrivate *priv = HILDON_DATE_EDITOR_GET_PRIVATE(data);
+  entry = GTK_ENTRY(ed);
 
-  if (strlen(gtk_entry_get_text(GTK_ENTRY(ed))) == YEAR_ENTRY_WIDTH)
+  /* If day entry is full, move to next entry or validate */
+  if (g_utf8_strlen(gtk_entry_get_text(entry), -1) == gtk_entry_get_max_length(entry))
     {
-      if (priv->locale_type == YEAR_MONTH_DAY)
-        gtk_widget_grab_focus(priv->m_entry);
-      else
-        hildon_date_editor_entry_validate(ed, data);
+      if (!gtk_widget_child_focus(GTK_WIDGET(data), GTK_DIR_RIGHT))
+        hildon_date_editor_entry_validate(GTK_WIDGET(entry), data);
     }
 }
 
@@ -1072,14 +1059,9 @@ static gboolean hildon_date_editor_keyrelease(GtkWidget * widget,
 
     if (event->keyval == GDK_KP_Enter || event->keyval == GDK_Return ||
         event->keyval == GDK_ISO_Enter) {
-        if (priv->editor_pressed) {
-            gtk_container_remove(GTK_CONTAINER(priv->d_event_box_image),
-                                 priv->d_image_pressed);
-            gtk_container_add(GTK_CONTAINER(priv->d_event_box_image),
-                              priv->d_image);
-            gtk_widget_show_all(priv->d_event_box_image);
-            g_idle_add(idle_popup, ed);
-            priv->editor_pressed = FALSE;
+        if (hildon_date_editor_set_calendar_icon_state(ed, FALSE))
+        {
+            popup_calendar_dialog(ed);
             return TRUE;
         }
     } else if (event->keyval == GDK_Escape)
@@ -1093,322 +1075,48 @@ static gboolean hildon_date_editor_keypress(GtkWidget * widget,
                                             GdkEventKey * event,
                                             gpointer data)
 {
-
     HildonDateEditor *ed;
     HildonDateEditorPrivate *priv;
     gint pos;
 
-    g_return_val_if_fail(data, FALSE);
-    g_return_val_if_fail(widget, FALSE);
+    g_assert(HILDON_IS_DATE_EDITOR(data));
+    g_assert(GTK_IS_ENTRY(widget));
 
     ed = HILDON_DATE_EDITOR(data);
     priv = HILDON_DATE_EDITOR_GET_PRIVATE(ed);
     pos = gtk_editable_get_position(GTK_EDITABLE(widget));
-
-    if (event->keyval == GDK_Return || event->keyval == GDK_ISO_Enter) {
-        if (!priv->editor_pressed) {
-            gtk_container_remove(GTK_CONTAINER(priv->d_event_box_image),
-                                 priv->d_image);
-            gtk_container_add(GTK_CONTAINER(priv->d_event_box_image),
-                              priv->d_image_pressed);
-            gtk_widget_show_all(priv->d_event_box_image);
-            priv->editor_pressed = TRUE;
-            return TRUE;
-        }
-        return FALSE;
-    }
-
-    if  (event->keyval == GDK_KP_Enter)
-           return FALSE;
        
-
-    
-    /* We don't want wrap */
-    if (event->keyval == GDK_KP_Left || event->keyval == GDK_Left)
-        if (pos == 0) {
-            if (priv->locale_type == DAY_MONTH_YEAR) {
-                if (widget == priv->d_entry)
-                    return TRUE;
-            } else {
-                if (widget == priv->m_entry)
-                    return TRUE;
-            }
-        }
-
-    if (event->keyval == GDK_KP_Right || event->keyval == GDK_Right)
-        if (widget == priv->y_entry
-            && pos >= strlen(GTK_ENTRY(widget)->text))
-            return TRUE;
-
     switch (event->keyval) {
     case GDK_Left:
-        /* left on day entry */
-        if (widget == priv->d_entry) {
-            gint pos =
-                gtk_editable_get_position(GTK_EDITABLE(priv->d_entry));
-            if (pos == 0) {
-                gtk_editable_select_region(GTK_EDITABLE(priv->d_entry), 0,
-                                           0);
-                if (priv->locale_type == DAY_MONTH_YEAR) {
-                    gtk_widget_grab_focus(priv->y_entry);
-                    gtk_editable_set_position(GTK_EDITABLE(priv->y_entry),
-                                              -1);
-                } else {
-                    gtk_widget_grab_focus(priv->m_entry);
-                    gtk_editable_set_position(GTK_EDITABLE(priv->m_entry),
-                                              -1);
-                }
-                return TRUE;
-            }
-        }
-        /* left on month entry */
-        else if (widget == priv->m_entry) {
-            gint pos =
-                gtk_editable_get_position(GTK_EDITABLE(priv->m_entry));
-
-            /* switch to day entry */
-            if (pos == 0) {
-                gtk_editable_select_region(GTK_EDITABLE(priv->m_entry), 0,
-                                           0);
-                if (priv->locale_type == DAY_MONTH_YEAR) {
-                    gtk_widget_grab_focus(priv->d_entry);
-                    gtk_editable_set_position(GTK_EDITABLE(priv->d_entry),
-                                              -1);
-                } else {
-                    gtk_widget_grab_focus(priv->y_entry);
-                }
-                gtk_editable_set_position(GTK_EDITABLE(priv->y_entry), -1);
-                return TRUE;
-            }
-        }
-        /* left on year entry */
-        else if (widget == priv->y_entry) {
-            gint pos =
-                gtk_editable_get_position(GTK_EDITABLE(priv->y_entry));
-
-            /* switch to month entry */
-            if (pos == 0) {
-                gtk_editable_select_region(GTK_EDITABLE(priv->y_entry), 0,
-                                           0);
-                if (priv->locale_type == DAY_MONTH_YEAR) {
-                    gtk_widget_grab_focus(priv->m_entry);
-                    gtk_editable_set_position(GTK_EDITABLE(priv->m_entry),
-                                              -1);
-                } else {
-                    gtk_widget_grab_focus(priv->d_entry);
-                    gtk_editable_set_position(GTK_EDITABLE(priv->d_entry),
-                                              -1);
-                }
-                return TRUE;
-            }
-        }
-        return FALSE;
-
+        (void) gtk_widget_child_focus(GTK_WIDGET(data), GTK_DIR_LEFT);
+        return TRUE;
+        break;
     case GDK_Right:
-        /* right on day entry */
-        if (widget == priv->d_entry) {
-            gint pos =
-                gtk_editable_get_position(GTK_EDITABLE(priv->d_entry));
-            gint len = gtk_entry_get_max_length(GTK_ENTRY(priv->d_entry));
-            gint chars =
-                g_utf8_strlen(gtk_entry_get_text(GTK_ENTRY(priv->d_entry)),
-                              len);
-
-            /* switch to month entry */
-            if ((pos == len) || (pos > chars)) {
-                gtk_editable_select_region(GTK_EDITABLE(priv->d_entry), 0,
-                                           0);
-                if (priv->locale_type == DAY_MONTH_YEAR) {
-                    gtk_widget_grab_focus(priv->m_entry);
-                    gtk_editable_set_position(GTK_EDITABLE(priv->m_entry),
-                                              0);
-                } else {
-                    gtk_widget_grab_focus(priv->y_entry);
-                    gtk_editable_set_position(GTK_EDITABLE(priv->y_entry),
-                                              0);
-                }
-                return TRUE;
-            }
-        }
-        /* right on month entry */
-        else if (widget == priv->m_entry) {
-            gint pos =
-                gtk_editable_get_position(GTK_EDITABLE(priv->m_entry));
-            gint len = gtk_entry_get_max_length(GTK_ENTRY(priv->m_entry));
-            gint chars =
-                g_utf8_strlen(gtk_entry_get_text(GTK_ENTRY(priv->m_entry)),
-                              len);
-
-            /* switch to year entry */
-            if ((pos == len) || (pos > chars)) {
-                gtk_editable_select_region(GTK_EDITABLE(priv->m_entry), 0,
-                                           0);
-                if (priv->locale_type == DAY_MONTH_YEAR) {
-                    gtk_widget_grab_focus(priv->y_entry);
-                    gtk_editable_set_position(GTK_EDITABLE(priv->y_entry),
-                                              0);
-                } else {
-                    gtk_widget_grab_focus(priv->d_entry);
-                    gtk_editable_set_position(GTK_EDITABLE(priv->d_entry),
-                                              0);
-                }
-                return TRUE;
-            }
-        }
-        /* right on year entry */
-        else if (widget == priv->y_entry) {
-            gint pos =
-                gtk_editable_get_position(GTK_EDITABLE(priv->y_entry));
-            gint len = gtk_entry_get_max_length(GTK_ENTRY(priv->y_entry));
-            gint chars =
-                g_utf8_strlen(gtk_entry_get_text(GTK_ENTRY(priv->y_entry)),
-                              len);
-
-            /* switch to day entry */
-            if ((pos == len) || (pos > chars)) {
-                gtk_editable_select_region(GTK_EDITABLE(priv->y_entry), 0,
-                                           0);
-                if (priv->locale_type == DAY_MONTH_YEAR) {
-                    gtk_widget_grab_focus(priv->d_entry);
-                    gtk_editable_set_position(GTK_EDITABLE(priv->d_entry),
-                                              0);
-                } else {
-                    gtk_widget_grab_focus(priv->y_entry);
-                    gtk_editable_set_position(GTK_EDITABLE(priv->y_entry),
-                                              0);
-                }
-                return TRUE;
-            }
-        }
-        return FALSE;
-
-        /* all digit keys */
-    case GDK_0:
-    case GDK_1:
-    case GDK_2:
-    case GDK_3:
-    case GDK_4:
-    case GDK_5:
-    case GDK_6:
-    case GDK_7:
-    case GDK_8:
-    case GDK_9:
-      {
-        if ((widget == priv->d_entry) || (widget == priv->m_entry) ||
-            (widget == priv->y_entry)) {
-            GtkWidgetClass *cl = GTK_WIDGET_GET_CLASS(widget);
-
-            cl->key_press_event(widget, event);
-        } else
-            return TRUE;
-      }
-      return TRUE;
-
-        /* accepts these as is */
-    case GDK_Tab:
-    case GDK_Shift_L:
-    case GDK_Shift_R:
-    case GDK_BackSpace:
-    case GDK_Delete:
-    case GDK_Up:
-    case GDK_Down:
-        return FALSE;
+        (void) gtk_widget_child_focus(GTK_WIDGET(data), GTK_DIR_RIGHT);
+        return TRUE;
+        break;
+    case GDK_Return:
+    case GDK_ISO_Enter:
+        /* Ignore return value, since we want to handle event at all times.
+           otherwise vkb would popup when the keyrepeat starts. */
+        (void) hildon_date_editor_set_calendar_icon_state(ed, TRUE);
+        return TRUE;
 
     case GDK_Escape:
         priv->skip_validation = TRUE;
-        return FALSE;
-
-        /* select date */
+        break;
     default:
-        return TRUE;
+        break;
     }
+
+    return FALSE;
 }
 
 static gboolean hildon_date_editor_entry_focus_out(GtkWidget * widget,
                                              GdkEventFocus * event,
                                              gpointer data)
 {
-  HildonDateEditor *ed;
-  HildonDateEditorPrivate *priv;
-  gint d, m, y;
-  gboolean r; /*for the sake of signal emission*/
-  GDate gd;
-
-  ed = HILDON_DATE_EDITOR(data);
-  priv = HILDON_DATE_EDITOR_GET_PRIVATE(ed);
-
-  if (priv->skip_validation)
-    return FALSE;
-
-  /*check if the calling entry is empty*/
-  if(gtk_entry_get_text(GTK_ENTRY(widget)) == NULL ||
-     *(gtk_entry_get_text(GTK_ENTRY(widget))) == '\0')
-    {
-      if(widget == priv->d_entry)
-       g_signal_emit(ed, date_editor_signals[DATE_ERROR], 0, 
-                     EMPTY_DAY, &r);
-      else if(widget == priv->m_entry)
-       g_signal_emit(ed, date_editor_signals[DATE_ERROR], 0, 
-                     EMPTY_MONTH, &r);
-      else
-       g_signal_emit(ed, date_editor_signals[DATE_ERROR], 0, 
-                     EMPTY_YEAR, &r);
-
-      return FALSE;
-    }
-
-  hildon_date_editor_entry_validate(GTK_EDITABLE(widget), ed);
-
-  /*date validation starts*/
-  d = atoi(gtk_entry_get_text(GTK_ENTRY(priv->d_entry)));
-  m = atoi(gtk_entry_get_text(GTK_ENTRY(priv->m_entry)));
-  y = atoi(gtk_entry_get_text(GTK_ENTRY(priv->y_entry)));
-
-  /*the only reason why a date is not valid is because that 
-   * some months dont have 31st 30th or even 29th(since we 
-   * have done max and min range checking for each field), for 
-   * now we will only fix day field, if trying to fix day
-   * field fails to make the date valid, we will set the 
-   * date to be current date, if any value is 0, that means
-   * this entry is empty, therefore skip validation*/
-   
-  if(d != 0 && m != 0 && y != 0 && !g_date_valid_dmy(d, m, y))
-    {
-      gint new_d;
-      gint max_d;
-      gchar day_str[3];
-      
-      g_signal_emit(ed, date_editor_signals[DATE_ERROR], 0, 
-                   INVALID_DATE, &r);
-      
-      max_d = g_date_get_days_in_month(m,y);
-       
-      if(priv->d_orig <= max_d && priv->d_orig > 0)
-       new_d = priv->d_orig;
-      else
-       new_d = priv->d_orig = max_d;
-      
-      if(g_date_valid_dmy(new_d, m, y))
-       {
-         d = priv->day = new_d;
-         sprintf(day_str, "%02d", new_d);
-         gtk_entry_set_text(GTK_ENTRY(priv->d_entry), day_str);
-       }
-      else
-       {
-         g_date_clear(&gd, 1);
-         g_date_set_time(&gd, time(NULL));
-         d = g_date_get_year(&gd);
-         m = g_date_get_month(&gd);
-         y = g_date_get_day(&gd);
-       }
-      
-      gtk_widget_grab_focus(priv->d_entry);
-      g_idle_add((GSourceFunc)
-                _hildon_date_editor_entry_select_all, priv->d_entry);
-    }
-  /*make sure to have 0 in front single digits*/
-  hildon_date_editor_set_date(ed, (guint) y, (guint) m, (guint) d);
+  hildon_date_editor_entry_validate(widget, data);
   return FALSE;
 }
 
@@ -1461,37 +1169,25 @@ static void hildon_date_editor_size_request(GtkWidget * widget,
 {
     HildonDateEditor *ed;
     HildonDateEditorPrivate *priv;
-    GtkRequisition d_req, m_req, y_req, f_req, img_req;
+    GtkRequisition f_req, img_req;
 
-    g_return_if_fail(widget);
-    g_return_if_fail(requisition);
+    g_assert(GTK_IS_WIDGET(widget));
+    g_assert(requisition != NULL);
 
     ed = HILDON_DATE_EDITOR(widget);
     priv = HILDON_DATE_EDITOR_GET_PRIVATE(ed);
 
-    /* call size request for entries */
-    gtk_widget_size_request(priv->d_entry, &d_req);
-    gtk_widget_size_request(priv->m_entry, &m_req);
-    gtk_widget_size_request(priv->y_entry, &y_req);
-
-    /* set entry widths to width_of_digit * 2 + border */
-    d_req.width = (hildon_date_editor_get_font_width(priv->d_entry) << 1)
-        + ENTRY_BORDERS;
-    m_req.width = (hildon_date_editor_get_font_width(priv->m_entry) << 1)
-        + ENTRY_BORDERS;
-    y_req.width = (hildon_date_editor_get_font_width(priv->y_entry) << 2)
-        + ENTRY_BORDERS;
-
-    gtk_widget_set_size_request(priv->d_entry, d_req.width, d_req.height);
-    gtk_widget_set_size_request(priv->m_entry, m_req.width, m_req.height);
-    gtk_widget_set_size_request(priv->y_entry, y_req.width, y_req.height);
-
-
+    /* Our own children affect our size */
     gtk_widget_size_request(priv->frame, &f_req);
     gtk_widget_size_request(priv->d_event_box_image, &img_req);
 
     /* calculate our size */
-    requisition->width = f_req.width + img_req.width + BUTTON_SPACING;
+    requisition->width = f_req.width + img_req.width + HILDON_MARGIN_DEFAULT;
+
+    /* FIXME: Fixed size is bad! We should use the maximum of our children, but
+              doing so would break current pixel specifications, since
+              the text entry by itself is already 30px tall + then frame takes
+              something */
     requisition->height = DATE_EDITOR_HEIGHT;
 }
 
@@ -1503,9 +1199,10 @@ static void hildon_date_editor_size_allocate(GtkWidget * widget,
     GtkAllocation f_alloc, img_alloc;
     GtkRequisition req;
     GtkRequisition max_req;
+    GList *iter;
 
-    g_return_if_fail(widget);
-    g_return_if_fail(allocation);
+    g_assert(GTK_IS_WIDGET(widget));
+    g_assert(allocation != NULL);
 
     ed = HILDON_DATE_EDITOR(widget);
     priv = HILDON_DATE_EDITOR_GET_PRIVATE(ed);
@@ -1514,14 +1211,15 @@ static void hildon_date_editor_size_allocate(GtkWidget * widget,
 
     gtk_widget_get_child_requisition(widget, &max_req);
 
+    /* Center vertically */
     f_alloc.y = img_alloc.y = allocation->y +
             MAX(allocation->height - max_req.height, 0) / 2;
 
+    /* Center horizontally */
     f_alloc.x = img_alloc.x = allocation->x +
             MAX(allocation->width - max_req.width, 0) / 2;
 
     /* allocate frame */
-    if (priv->frame)
         if (GTK_WIDGET_VISIBLE(priv->frame)) {
             gtk_widget_get_child_requisition(priv->frame, &req);
 
@@ -1530,44 +1228,33 @@ static void hildon_date_editor_size_allocate(GtkWidget * widget,
             gtk_widget_size_allocate(priv->frame, &f_alloc);
         }
 
-    /* allocate entry box */
-    if (priv->d_event_box_image)
+    /* allocate icon */
         if (GTK_WIDGET_VISIBLE(priv->d_event_box_image)) {
             gtk_widget_get_child_requisition(priv->d_event_box_image,
                                              &req);
 
-            img_alloc.x += f_alloc.width + BUTTON_SPACING;
+            img_alloc.x += f_alloc.width + HILDON_MARGIN_DEFAULT;
             img_alloc.width = req.width;
             img_alloc.height = max_req.height;
             gtk_widget_size_allocate(priv->d_event_box_image, &img_alloc);
         }
 
-    priv->dm_delim->allocation.height = max_req.height; 
-    priv->my_delim->allocation.height = max_req.height; 
-    priv->my_delim->allocation.y = priv->d_entry->allocation.y-5;
-    priv->dm_delim->allocation.y = priv->d_entry->allocation.y-5;
-    gtk_widget_size_allocate(priv->dm_delim, &priv->dm_delim->allocation);
-    gtk_widget_size_allocate(priv->my_delim, &priv->my_delim->allocation);
-}
-
-/* calculate approximate width of a digit */
-static int hildon_date_editor_get_font_width(GtkWidget * widget)
-{
-    PangoContext *context;
-    PangoFontMetrics *metrics;
-    gint digit_width;
-
-    context = gtk_widget_get_pango_context(widget);
-    metrics = pango_context_get_metrics(context, widget->style->font_desc,
-                                        pango_context_get_language
-                                        (context));
-
-    digit_width = pango_font_metrics_get_approximate_digit_width(metrics);
-    digit_width = PANGO_PIXELS(digit_width);
+    /* FIXME: We really should not alloc delimeters by hand (since they
+              are not our own children, but we need to force to appear 
+              higher. This ugly hack is needed to compensate the forced
+              height in size_request. */
+    for (iter = priv->delims; iter; iter = iter->next)
+    {
+        GtkWidget *delim;
+        GtkAllocation alloc;
 
-    pango_font_metrics_unref(metrics);
+        delim = GTK_WIDGET(iter->data);
+        alloc = delim->allocation;
+        alloc.height = max_req.height; 
+        alloc.y = priv->d_entry->allocation.y - 2;
 
-    return digit_width;
+        gtk_widget_size_allocate(delim, &alloc);
+    }
 }
 
 /**
@@ -1575,10 +1262,10 @@ static int hildon_date_editor_get_font_width(GtkWidget * widget)
  * @editor: the @HildonDateEditor widget
  * @year: year
  *
- * This function sets the year shown in the editor. 
+ * Sets the year shown in the editor. 
  *
- * Return: Returns TRUE if the year is valid.
- **/
+ * Returns: TRUE if the year is valid
+ */
 gboolean hildon_date_editor_set_year(HildonDateEditor *editor, guint year)
 {
   HildonDateEditorPrivate *priv;
@@ -1587,11 +1274,18 @@ gboolean hildon_date_editor_set_year(HildonDateEditor *editor, guint year)
 
   if (g_date_valid_dmy(priv->day, priv->month, year))
   {
-    gchar str[MAX_DATE_LEN + 1];
+    gchar buffer[256];
     priv->year = year;
 
-    sprintf(str, "%04d", year);
-    gtk_entry_set_text(GTK_ENTRY(priv->y_entry), str);
+    g_snprintf(buffer, sizeof(buffer), "%04d", year);
+
+    /* We apply the new day, but do not want automatic focus move
+       etc to take place */
+    g_signal_handlers_block_by_func(priv->y_entry, 
+        (gpointer) hildon_date_editor_entry_changed, editor);
+    gtk_entry_set_text(GTK_ENTRY(priv->y_entry), buffer);
+    g_signal_handlers_unblock_by_func(priv->y_entry, 
+        (gpointer) hildon_date_editor_entry_changed, editor);
 
     g_object_notify(G_OBJECT(editor), "year");
     return TRUE;
@@ -1605,10 +1299,10 @@ gboolean hildon_date_editor_set_year(HildonDateEditor *editor, guint year)
  * @editor: the @HildonDateEditor widget
  * @month: month
  *
- * This function sets the month shown in the editor. 
+ * Sets the month shown in the editor. 
  *
- * Return: Returns TRUE if the month is valid.
- **/
+ * Returns: TRUE if the month is valid
+ */
 gboolean hildon_date_editor_set_month(HildonDateEditor *editor, guint month)
 {
   HildonDateEditorPrivate *priv;
@@ -1618,12 +1312,19 @@ gboolean hildon_date_editor_set_month(HildonDateEditor *editor, guint month)
   if (g_date_valid_dmy(priv->day, month, priv->year))
   {
     GDate date;
-    gchar str[MAX_DATE_LEN + 1];
+    gchar buffer[256];
+
     priv->month = month;
     g_date_set_dmy(&date, priv->day, month, priv->year);
+    g_date_strftime(buffer, sizeof(buffer), "%m", &date);
 
-    g_date_strftime(str, MAX_DATE_LEN, "%m", &date);
-    gtk_entry_set_text(GTK_ENTRY(priv->m_entry), str);
+    /* We apply the new day, but do not want automatic focus move
+       etc to take place */
+    g_signal_handlers_block_by_func(priv->m_entry, 
+        (gpointer) hildon_date_editor_entry_changed, editor);
+    gtk_entry_set_text(GTK_ENTRY(priv->m_entry), buffer);
+    g_signal_handlers_unblock_by_func(priv->m_entry, 
+        (gpointer) hildon_date_editor_entry_changed, editor);
 
     g_object_notify(G_OBJECT(editor), "month");
     return TRUE;
@@ -1636,10 +1337,10 @@ gboolean hildon_date_editor_set_month(HildonDateEditor *editor, guint month)
  * @editor: the @HildonDateEditor widget
  * @day: day
  *
- * This function sets the day shown in the editor. 
+ * Sets the day shown in the editor. 
  *
- * Return: Returns TRUE if the day is valid.
- **/
+ * Returns: TRUE if the day is valid
+ */
 gboolean hildon_date_editor_set_day(HildonDateEditor *editor, guint day)
 {
   HildonDateEditorPrivate *priv;
@@ -1650,12 +1351,19 @@ gboolean hildon_date_editor_set_day(HildonDateEditor *editor, guint day)
   if (g_date_valid_dmy(day, priv->month, priv->year))
   {
     GDate date;
-    gchar str[MAX_DATE_LEN + 1];
+    gchar buffer[256];
+
     priv->day = day;
     g_date_set_dmy(&date, day, priv->month, priv->year);
+    g_date_strftime(buffer, sizeof(buffer), "%d", &date);
 
-    g_date_strftime(str, MAX_DATE_LEN, "%d", &date);
-    gtk_entry_set_text(GTK_ENTRY(priv->d_entry), str);
+    /* We apply the new day, but do not want automatic focus move
+       etc to take place */
+    g_signal_handlers_block_by_func(priv->d_entry, 
+        (gpointer) hildon_date_editor_entry_changed, editor);
+    gtk_entry_set_text(GTK_ENTRY(priv->d_entry), buffer);
+    g_signal_handlers_unblock_by_func(priv->d_entry, 
+        (gpointer) hildon_date_editor_entry_changed, editor);
 
     g_object_notify(G_OBJECT(editor), "day");
     return TRUE;
@@ -1667,16 +1375,13 @@ gboolean hildon_date_editor_set_day(HildonDateEditor *editor, guint day)
  * hildon_date_editor_get_year:
  * @editor: the @HildonDateEditor widget
  *
- * This function gets the year shown in the editor. 
- *
- * Return: Returns the current year shown in the editor.
- **/
+ * Returns: the current year shown in the editor.
+ */
 guint hildon_date_editor_get_year(HildonDateEditor *editor)
 {
   HildonDateEditorPrivate *priv;
   g_return_val_if_fail( HILDON_IS_DATE_EDITOR(editor), 0 );
   priv = HILDON_DATE_EDITOR_GET_PRIVATE(editor);
-  /*change to have the content in the entry*/
   return (guint) atoi(gtk_entry_get_text(GTK_ENTRY(priv->y_entry)));
 }
 
@@ -1684,10 +1389,10 @@ guint hildon_date_editor_get_year(HildonDateEditor *editor)
  * hildon_date_editor_get_month:
  * @editor: the @HildonDateEditor widget
  *
- * This function gets the month shown in the editor. 
+ * Gets the month shown in the editor. 
  *
- * Return: Returns the current month shown in the editor.
- **/
+ * Returns: the current month shown in the editor.
+ */
 
 guint hildon_date_editor_get_month(HildonDateEditor *editor)
 {
@@ -1701,10 +1406,10 @@ guint hildon_date_editor_get_month(HildonDateEditor *editor)
  * hildon_date_editor_get_day:
  * @editor: the @HildonDateEditor widget
  *
- * This function gets the day shown in the editor. 
+ * Gets the day shown in the editor. 
  *
- * Return: Returns the current day shown in the editor.
- **/
+ * Returns: the current day shown in the editor
+ */
 
 guint hildon_date_editor_get_day(HildonDateEditor *editor)
 {
@@ -1714,6 +1419,7 @@ guint hildon_date_editor_get_day(HildonDateEditor *editor)
   return (guint) atoi(gtk_entry_get_text(GTK_ENTRY(priv->d_entry)));
 }
 
+/* Idle callback */
 static gboolean
 _hildon_date_editor_entry_select_all (GtkWidget *widget)
 {