2006-09-11 Michael Dominic Kostrzewa <michael.kostrzewa@nokia.com>
[hildon] / hildon-widgets / hildon-date-editor.c
1 /*
2  * This file is part of hildon-libs
3  *
4  * Copyright (C) 2005, 2006 Nokia Corporation.
5  *
6  * Contact: Michael Dominic Kostrzewa <michael.kostrzewa@nokia.com>
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public License
10  * as published by the Free Software Foundation; version 2.1 of
11  * the License.
12  *
13  * This library is distributed in the hope that it will be useful, but
14  * WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
21  * 02110-1301 USA
22  *
23  */
24
25 /**
26  * SECTION:hildon-date-editor
27  * @short_description: A widget which queries a date from user or opens
28  * a HildonCalendarPopup
29  * @see_also: #HildonCalendarPopup, #HildonTimeEditor
30  * 
31  * HildonDateEditor is a widget with three entry fields (day, month,
32  * year) and an icon (eventbox): clicking on the icon opens up a
33  * HildonCalendarPopup.
34  */
35
36 #include <glib.h>
37 #include <gtk/gtk.h>
38 #include <gdk/gdkkeysyms.h>
39 #include <time.h>
40 #include <stdlib.h>
41 #include <stdio.h>
42 #include <string.h>
43
44 #include <hildon-widgets/hildon-date-editor.h>
45 #include <hildon-widgets/hildon-calendar-popup.h>
46 #include <hildon-widgets/gtk-infoprint.h>
47 #include <hildon-widgets/hildon-defines.h>
48 #include <hildon-widgets/hildon-input-mode-hint.h>
49 #include "hildon-composite-widget.h"
50 #include "hildon-marshalers.h"
51 #include "hildon-libs-enum-types.h"
52
53 #ifdef HAVE_CONFIG_H
54 #include<config.h>
55 #endif
56
57 #include<libintl.h>
58 #define _(string) dgettext(PACKAGE, string)
59
60 #define ENTRY_BORDERS 11
61 #define DATE_EDITOR_HEIGHT 30
62
63 #define DAY_ENTRY_WIDTH 2
64 #define MONTH_ENTRY_WIDTH 2
65 #define YEAR_ENTRY_WIDTH 4
66
67 #define DEFAULT_MIN_YEAR 1970
68 #define DEFAULT_MAX_YEAR 2037
69
70 #define HILDON_DATE_EDITOR_GET_PRIVATE(obj) \
71         (G_TYPE_INSTANCE_GET_PRIVATE((obj),\
72         HILDON_TYPE_DATE_EDITOR, HildonDateEditorPrivate));
73
74 static GtkContainerClass *parent_class;
75
76 typedef struct _HildonDateEditorPrivate HildonDateEditorPrivate;
77
78 static void
79 hildon_date_editor_class_init(HildonDateEditorClass * editor_class);
80
81 static void hildon_date_editor_init(HildonDateEditor * editor);
82
83 static gboolean
84 hildon_date_editor_icon_press(GtkWidget * widget, GdkEventButton * event,
85                               gpointer data);
86
87 static gboolean
88 hildon_date_editor_entry_released(GtkWidget * widget,
89                                   GdkEventButton * event, gpointer data);
90 static gboolean
91 hildon_date_editor_released(GtkWidget * widget, GdkEventButton * event,
92                             gpointer data);
93
94 static gboolean
95 hildon_date_editor_keypress(GtkWidget * widget, GdkEventKey * event,
96                             gpointer data);
97
98 static gboolean
99 hildon_date_editor_keyrelease(GtkWidget * widget, GdkEventKey * event,
100                               gpointer data);
101 static void
102 hildon_date_editor_entry_validate(GtkWidget *widget, gpointer data);
103
104 static void
105 hildon_date_editor_entry_changed(GtkEditable *widget, gpointer data);
106
107 static gboolean
108 hildon_date_editor_entry_focus_out(GtkWidget * widget, GdkEventFocus * event,
109                                    gpointer data);
110
111 static gboolean hildon_date_editor_date_error(HildonDateEditor *editor, 
112                                               HildonDateEditorErrorType type);
113
114 static gboolean hildon_date_editor_entry_focusin(GtkWidget * widget,
115                                                  GdkEventFocus * event,
116                                                  gpointer data);
117 static void hildon_date_editor_get_property( GObject *object, guint param_id,
118                                          GValue *value, GParamSpec *pspec );
119 static void hildon_date_editor_set_property (GObject *object, guint param_id,
120                                        const GValue *value, GParamSpec *pspec);
121 static void
122 hildon_child_forall(GtkContainer * container,
123                     gboolean include_internals,
124                     GtkCallback callback, gpointer callback_data);
125
126 static void hildon_date_editor_destroy(GtkObject * self);
127
128 static void
129 hildon_date_editor_size_allocate(GtkWidget * widget,
130                                  GtkAllocation * allocation);
131
132 static void
133 hildon_date_editor_size_request(GtkWidget * widget,
134                                 GtkRequisition * requisition);
135
136 static gboolean
137 _hildon_date_editor_entry_select_all(GtkWidget *widget);
138
139 /* Property indices */
140 enum
141 {
142   PROP_DAY = 1,
143   PROP_MONTH,
144   PROP_YEAR,
145   PROP_MIN_YEAR,
146   PROP_MAX_YEAR
147 };
148
149 struct _HildonDateEditorPrivate {
150     /* Cache of values in the entries, used in setting only parts of the date */
151     guint year;   /* current year  in the entry */
152     guint month;  /* current month in the entry */
153     guint day;    /* current day   in the entry */
154
155     gboolean calendar_icon_pressed;
156
157     GtkWidget *frame;             /* borders around the date */
158     GtkWidget *d_event_box_image; /* icon */
159     GtkWidget *d_box_date;        /* hbox for date */
160
161     GtkWidget *d_entry; /* GtkEntry for day */
162     GtkWidget *m_entry; /* GtkEntry for month */
163     GtkWidget *y_entry; /* GtkEntry for year */
164
165     GList *delims;      /* List of delimeters between the fields (and possible at the ends) */
166     GtkWidget *calendar_icon;
167
168     gboolean skip_validation; /* don't validate date at all */
169
170     gint min_year; /* minimum year allowed */
171     gint max_year; /* maximum year allowed */
172 };
173
174 enum {
175     DATE_ERROR,
176     LAST_SIGNAL
177 };
178
179 static guint date_editor_signals[LAST_SIGNAL] = { 0 };
180
181 GType hildon_date_editor_get_type(void)
182 {
183     static GType editor_type = 0;
184
185     if (!editor_type) {
186         static const GTypeInfo editor_info = {
187             sizeof(HildonDateEditorClass),
188             NULL,       /* base_init */
189             NULL,       /* base_finalize */
190             (GClassInitFunc) hildon_date_editor_class_init,
191             NULL,       /* class_finalize */
192             NULL,       /* class_data */
193             sizeof(HildonDateEditor),
194             0,  /* n_preallocs */
195             (GInstanceInitFunc) hildon_date_editor_init,
196         };
197         editor_type = g_type_register_static(GTK_TYPE_CONTAINER,
198                                              "HildonDateEditor",
199                                              &editor_info, 0);
200     }
201     return editor_type;
202 }
203
204 static void
205 hildon_date_editor_class_init(HildonDateEditorClass * editor_class)
206 {
207     GtkContainerClass *container_class = GTK_CONTAINER_CLASS(editor_class);
208     GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(editor_class);
209     GObjectClass *gobject_class = G_OBJECT_CLASS (editor_class);
210     
211     parent_class = g_type_class_peek_parent(editor_class);
212
213     g_type_class_add_private(editor_class,
214                              sizeof(HildonDateEditorPrivate));
215
216     gobject_class->set_property = hildon_date_editor_set_property;
217     gobject_class->get_property = hildon_date_editor_get_property;
218     widget_class->size_request = hildon_date_editor_size_request;
219     widget_class->size_allocate = hildon_date_editor_size_allocate;
220     widget_class->focus = hildon_composite_widget_focus;
221
222     container_class->forall = hildon_child_forall;
223     GTK_OBJECT_CLASS(editor_class)->destroy = hildon_date_editor_destroy;
224
225     editor_class->date_error = hildon_date_editor_date_error; 
226     
227     date_editor_signals[DATE_ERROR] =
228         g_signal_new("date-error",
229                 G_OBJECT_CLASS_TYPE(gobject_class),
230                 G_SIGNAL_RUN_LAST,
231                 G_STRUCT_OFFSET(HildonDateEditorClass, date_error),
232                 g_signal_accumulator_true_handled, NULL,
233                 _hildon_marshal_BOOLEAN__ENUM,
234                 G_TYPE_BOOLEAN, 1, HILDON_TYPE_DATE_EDITOR_ERROR_TYPE);
235
236   /**
237    * HildonDateEditor:year:
238    *
239    * Current year.
240    */
241   g_object_class_install_property( gobject_class, PROP_YEAR,
242                                    g_param_spec_uint("year",
243                                    "Current year",
244                                    "Current year",
245                                    1, 2100,
246                                    2005,
247                                    G_PARAM_READABLE | G_PARAM_WRITABLE) );
248
249   /**
250    * HildonDateEditor:month:
251    *
252    * Current month.
253    */
254   g_object_class_install_property( gobject_class, PROP_MONTH,
255                                    g_param_spec_uint("month",
256                                    "Current month",
257                                    "Current month",
258                                    1, 12,
259                                    1,
260                                    G_PARAM_READABLE | G_PARAM_WRITABLE) );
261
262   /**
263    * HildonDateEditor:day:
264    *
265    * Current day.
266    */
267   g_object_class_install_property( gobject_class, PROP_DAY,
268                                    g_param_spec_uint("day",
269                                    "Current day",
270                                    "Current day",
271                                    1, 31,
272                                    1,
273                                    G_PARAM_READABLE | G_PARAM_WRITABLE) );
274
275   /**
276    * HildonDateEditor:min-year:
277    *
278    * Minimum valid year.
279    */
280   g_object_class_install_property( gobject_class, PROP_MIN_YEAR,
281                                    g_param_spec_uint("min-year",
282                                    "Minimum valid year",
283                                    "Minimum valid year",
284                                    1, 2100,
285                                    DEFAULT_MIN_YEAR,
286                                    G_PARAM_READWRITE) );
287
288   /**
289    * HildonDateEditor:max-year:
290    *
291    * Maximum valid year.
292    */
293   g_object_class_install_property( gobject_class, PROP_MAX_YEAR,
294                                    g_param_spec_uint("max-year",
295                                    "Maximum valid year",
296                                    "Maximum valid year",
297                                    1, 2100,
298                                    DEFAULT_MAX_YEAR,
299                                    G_PARAM_READWRITE) );
300 }
301
302 /* Forces setting of the icon to certain state. Used initially
303    and from the actual setter function */
304 static void
305 real_set_calendar_icon_state(HildonDateEditorPrivate *priv, 
306     gboolean pressed)
307 {
308     gtk_image_set_from_icon_name(GTK_IMAGE(priv->calendar_icon),
309         pressed ? "qgn_widg_datedit_pr" : "qgn_widg_datedit",
310         HILDON_ICON_SIZE_WIDG);
311
312     priv->calendar_icon_pressed = pressed;
313 }
314
315 /* Sets the icon to given state (normal/pressed). Returns
316    info if the state actually changed. */
317 static gboolean 
318 hildon_date_editor_set_calendar_icon_state(HildonDateEditor *editor,
319     gboolean pressed)
320 {
321     HildonDateEditorPrivate *priv;
322
323     g_assert(HILDON_IS_DATE_EDITOR(editor));
324
325     priv = HILDON_DATE_EDITOR_GET_PRIVATE(editor);
326     if (pressed != priv->calendar_icon_pressed) {
327         real_set_calendar_icon_state(priv, pressed);
328         return TRUE;
329     }
330
331     return FALSE;
332 }
333
334 /* Packing day, month and year entries depend on locale settings
335    We find out the order and all separators by converting a known
336    date to default format and inspecting the result string */
337 static void apply_locale_field_order(HildonDateEditorPrivate *priv)
338 {
339     GDate locale_test_date;
340     GtkWidget *delim;
341     gchar buffer[256];
342     gchar *iter, *delim_text;
343
344     g_date_set_dmy(&locale_test_date, 1, 2, 1970);
345     (void) g_date_strftime(buffer, sizeof(buffer), "%x", &locale_test_date);    
346     iter = buffer;
347
348     while (*iter)
349     {
350        gchar *endp;
351        unsigned long value;
352
353        /* Try to convert the current location into number. */
354        value = strtoul(iter, &endp, 10);
355
356        /* If the conversion didn't progress or the detected value was
357           unknown (we used a fixed date, you remember), we treat 
358           current position as a literal */
359        switch (value)
360        {
361          case 1:
362             gtk_box_pack_start(GTK_BOX(priv->d_box_date),
363                 priv->d_entry, FALSE, FALSE, 0);
364             break;
365          case 2:
366             gtk_box_pack_start(GTK_BOX(priv->d_box_date),
367                 priv->m_entry, FALSE, FALSE, 0);
368             break;
369          case 70:   /* %x format uses only 2 numbers for some locales */
370          case 1970:
371             gtk_box_pack_start(GTK_BOX(priv->d_box_date),
372                 priv->y_entry, FALSE, FALSE, 0);
373             break;
374          default:
375             /* All non-number characters starting from current position 
376                form the delimeter */
377             for (endp = iter; *endp; endp++)
378                 if (g_ascii_isdigit(*endp))
379                     break;
380
381             /* Now endp points one place past the delimeter text */
382             delim_text = g_strndup(iter, endp - iter);
383             delim = gtk_label_new(delim_text);
384             gtk_box_pack_start(GTK_BOX(priv->d_box_date),
385                            delim, FALSE, FALSE, 0);
386             priv->delims = g_list_append(priv->delims, delim);
387             g_free(delim_text);
388
389             break;
390        };
391
392        iter = endp;
393     }
394 }
395
396 static void hildon_date_editor_init(HildonDateEditor * editor)
397 {
398     HildonDateEditorPrivate *priv;
399     GDate cur_date;
400
401     priv = HILDON_DATE_EDITOR_GET_PRIVATE(editor);
402
403     GTK_WIDGET_SET_FLAGS(GTK_WIDGET(editor), GTK_NO_WINDOW);
404
405     gtk_widget_push_composite_child();
406
407     /* initialize values */
408     g_date_clear(&cur_date, 1);
409     g_date_set_time(&cur_date, time(NULL));
410
411     priv->day = g_date_get_day(&cur_date);
412     priv->month = g_date_get_month(&cur_date);
413     priv->year = g_date_get_year(&cur_date);
414     priv->min_year = DEFAULT_MIN_YEAR;
415     priv->max_year = DEFAULT_MAX_YEAR;
416
417     /* make widgets */
418     priv->frame = gtk_frame_new(NULL);
419     gtk_container_set_border_width(GTK_CONTAINER(priv->frame), 0);
420
421     priv->d_entry = gtk_entry_new();
422     priv->m_entry = gtk_entry_new();
423     priv->y_entry = gtk_entry_new();
424
425     g_object_set (G_OBJECT(priv->d_entry), "input-mode", 
426                   HILDON_INPUT_MODE_HINT_NUMERIC, NULL);
427     g_object_set (G_OBJECT(priv->m_entry), "input-mode", 
428                   HILDON_INPUT_MODE_HINT_NUMERIC, NULL);
429     g_object_set (G_OBJECT(priv->y_entry), "input-mode", 
430                   HILDON_INPUT_MODE_HINT_NUMERIC, NULL);
431
432     /* set entry look */
433     gtk_entry_set_width_chars(GTK_ENTRY(priv->d_entry), DAY_ENTRY_WIDTH);
434     gtk_entry_set_width_chars(GTK_ENTRY(priv->m_entry), MONTH_ENTRY_WIDTH);
435     gtk_entry_set_width_chars(GTK_ENTRY(priv->y_entry), YEAR_ENTRY_WIDTH);
436
437     gtk_entry_set_max_length(GTK_ENTRY(priv->d_entry), DAY_ENTRY_WIDTH);
438     gtk_entry_set_max_length(GTK_ENTRY(priv->m_entry), MONTH_ENTRY_WIDTH);
439     gtk_entry_set_max_length(GTK_ENTRY(priv->y_entry), YEAR_ENTRY_WIDTH);
440
441     gtk_entry_set_has_frame(GTK_ENTRY(priv->d_entry), FALSE);
442     gtk_entry_set_has_frame(GTK_ENTRY(priv->m_entry), FALSE);
443     gtk_entry_set_has_frame(GTK_ENTRY(priv->y_entry), FALSE);
444
445     gtk_widget_set_composite_name(priv->d_entry, "day_entry");
446     gtk_widget_set_composite_name(priv->m_entry, "month_entry");
447     gtk_widget_set_composite_name(priv->y_entry, "year_entry");
448
449     priv->d_box_date = gtk_hbox_new(FALSE, 0);
450
451     priv->d_event_box_image = gtk_event_box_new();
452     priv->calendar_icon = gtk_image_new();
453     real_set_calendar_icon_state(priv, FALSE);
454
455     apply_locale_field_order(priv);
456
457     gtk_container_add(GTK_CONTAINER(priv->frame), priv->d_box_date);
458     gtk_container_add(GTK_CONTAINER(priv->d_event_box_image), priv->calendar_icon);
459
460     gtk_widget_set_parent(priv->frame, GTK_WIDGET(editor));
461     gtk_widget_set_parent(priv->d_event_box_image, GTK_WIDGET(editor));
462     gtk_widget_show_all(priv->frame);
463     gtk_widget_show_all(priv->d_event_box_image);
464     
465     /* image signal connects */
466     g_signal_connect(GTK_OBJECT(priv->d_event_box_image), "button_press_event",
467                      G_CALLBACK(hildon_date_editor_icon_press), editor);
468     g_signal_connect(GTK_OBJECT(priv->d_event_box_image),
469                      "button_release_event",
470                      G_CALLBACK(hildon_date_editor_released), editor);
471     g_signal_connect(GTK_OBJECT(priv->d_event_box_image), "key-press-event",
472                      G_CALLBACK(hildon_date_editor_keypress), editor);
473     g_signal_connect(GTK_OBJECT(priv->calendar_icon), "key-press-event",
474                      G_CALLBACK(hildon_date_editor_keypress), editor);
475
476
477     /* entry signal connects */
478     g_signal_connect(GTK_OBJECT(priv->d_entry), "button_release_event",
479                      G_CALLBACK(hildon_date_editor_entry_released), editor);
480     
481     g_signal_connect(GTK_OBJECT(priv->d_entry), "focus-in-event",
482                      G_CALLBACK(hildon_date_editor_entry_focusin), editor);
483
484     g_signal_connect(GTK_OBJECT(priv->m_entry), "button_release_event",
485                      G_CALLBACK(hildon_date_editor_entry_released), editor);
486
487     g_signal_connect(GTK_OBJECT(priv->m_entry), "focus-in-event",
488                      G_CALLBACK(hildon_date_editor_entry_focusin), editor);
489
490     g_signal_connect(GTK_OBJECT(priv->y_entry), "button_release_event",
491                      G_CALLBACK(hildon_date_editor_entry_released), editor);
492
493     g_signal_connect(GTK_OBJECT(priv->y_entry), "focus-in-event",
494                      G_CALLBACK(hildon_date_editor_entry_focusin), editor);
495
496     g_signal_connect(GTK_OBJECT(priv->d_entry), "focus-out-event",
497                      G_CALLBACK(hildon_date_editor_entry_focus_out), editor);
498
499     g_signal_connect(GTK_OBJECT(priv->m_entry), "focus-out-event",
500                      G_CALLBACK(hildon_date_editor_entry_focus_out), editor);
501
502     g_signal_connect(GTK_OBJECT(priv->y_entry), "focus-out-event",
503                      G_CALLBACK(hildon_date_editor_entry_focus_out), editor);
504
505     g_signal_connect(GTK_OBJECT(priv->d_entry), "key-press-event",
506                      G_CALLBACK(hildon_date_editor_keypress), editor);
507
508     g_signal_connect(GTK_OBJECT(priv->m_entry), "key-press-event",
509                      G_CALLBACK(hildon_date_editor_keypress), editor);
510
511     g_signal_connect(GTK_OBJECT(priv->y_entry), "key-press-event",
512                      G_CALLBACK(hildon_date_editor_keypress), editor);
513
514     g_signal_connect(GTK_OBJECT(priv->d_entry), "key-release-event",
515                      G_CALLBACK(hildon_date_editor_keyrelease), editor);
516
517     g_signal_connect(GTK_OBJECT(priv->m_entry), "key-release-event",
518                      G_CALLBACK(hildon_date_editor_keyrelease), editor);
519
520     g_signal_connect(GTK_OBJECT(priv->y_entry), "key-release-event",
521                      G_CALLBACK(hildon_date_editor_keyrelease), editor);
522
523     hildon_date_editor_set_date(editor, priv->year, priv->month, priv->day);
524
525     g_signal_connect(GTK_OBJECT(priv->d_entry), "changed",
526                      G_CALLBACK(hildon_date_editor_entry_changed), editor);
527
528     g_signal_connect(GTK_OBJECT(priv->m_entry), "changed",
529                      G_CALLBACK(hildon_date_editor_entry_changed), editor);
530
531     g_signal_connect(GTK_OBJECT(priv->y_entry), "changed",
532                      G_CALLBACK(hildon_date_editor_entry_changed), editor);
533
534     gtk_widget_pop_composite_child();
535 }
536
537 static void hildon_date_editor_set_property (GObject *object, guint param_id,
538                                        const GValue *value, GParamSpec *pspec)
539 {
540   HildonDateEditor *editor = HILDON_DATE_EDITOR(object);
541   HildonDateEditorPrivate *priv = HILDON_DATE_EDITOR_GET_PRIVATE(editor);
542   gint val;
543
544   switch (param_id)
545   {
546     case PROP_YEAR:
547       hildon_date_editor_set_year (editor, g_value_get_uint(value));
548       break;
549
550     case PROP_MONTH:
551       hildon_date_editor_set_month (editor, g_value_get_uint(value));
552       break;
553
554     case PROP_DAY:
555       hildon_date_editor_set_day (editor, g_value_get_uint(value));
556       break;
557
558     case PROP_MIN_YEAR:
559       val = g_value_get_uint(value);
560       if (val <= priv->max_year)
561         {
562           priv->min_year = val;
563           /* Clamp current year */
564           if (hildon_date_editor_get_year (editor) < priv->min_year)
565             hildon_date_editor_set_year (editor, priv->min_year);
566         }
567       else
568         g_warning("min-year cannot be greater than max-year");
569       break;
570
571     case PROP_MAX_YEAR:
572       val = g_value_get_uint(value);
573       if (val >= priv->min_year)
574         {
575           priv->max_year = val;
576           /* Clamp current year */
577           if (hildon_date_editor_get_year (editor) > priv->max_year)
578             hildon_date_editor_set_year (editor, priv->max_year);
579         }
580       else
581         g_warning("max-year cannot be less than min-year");
582       break;
583
584     default:
585       G_OBJECT_WARN_INVALID_PROPERTY_ID(object, param_id, pspec);
586       break;
587   }
588 }
589
590 static void hildon_date_editor_get_property( GObject *object, guint param_id,
591                                          GValue *value, GParamSpec *pspec )
592 {
593   HildonDateEditor *editor = HILDON_DATE_EDITOR(object);
594   HildonDateEditorPrivate *priv = HILDON_DATE_EDITOR_GET_PRIVATE(editor);
595
596   switch (param_id)
597   {
598     case PROP_YEAR:
599       g_value_set_uint (value, hildon_date_editor_get_year (editor));
600       break;
601
602     case PROP_MONTH:
603       g_value_set_uint (value, hildon_date_editor_get_month (editor));
604       break;
605
606     case PROP_DAY:
607       g_value_set_uint (value, hildon_date_editor_get_day (editor));
608       break;
609
610     case PROP_MIN_YEAR:
611       g_value_set_uint (value, priv->min_year);
612       break;
613
614     case PROP_MAX_YEAR:
615       g_value_set_uint (value, priv->max_year);
616       break;
617     
618     default:
619       G_OBJECT_WARN_INVALID_PROPERTY_ID(object, param_id, pspec);
620       break;
621   }
622 }
623
624 static void hildon_child_forall(GtkContainer * container,
625                                 gboolean include_internals,
626                                 GtkCallback callback,
627                                 gpointer callback_data)
628 {
629     HildonDateEditor *editor;
630     HildonDateEditorPrivate *priv;
631
632     g_assert(HILDON_IS_DATE_EDITOR(container));
633     g_assert(callback);
634
635     editor = HILDON_DATE_EDITOR(container);
636     priv = HILDON_DATE_EDITOR_GET_PRIVATE(editor);
637
638     if (include_internals) {
639         (*callback) (priv->frame, callback_data);
640         (*callback) (priv->d_event_box_image, callback_data);
641     }
642 }
643
644 static void hildon_date_editor_destroy(GtkObject * self)
645 {
646     HildonDateEditorPrivate *priv;
647
648     priv = HILDON_DATE_EDITOR_GET_PRIVATE(self);
649
650     if (priv->frame) {
651         gtk_widget_unparent(priv->frame);
652         priv->frame = NULL;
653     }
654     if (priv->d_event_box_image) {
655         gtk_widget_unparent(priv->d_event_box_image);
656         priv->d_event_box_image = NULL;
657     }
658     if (priv->delims) {
659         g_list_free(priv->delims);
660         priv->delims = NULL;
661     }
662
663     if (GTK_OBJECT_CLASS(parent_class)->destroy)
664         GTK_OBJECT_CLASS(parent_class)->destroy(self);
665 }
666
667 /**
668  * hildon_date_editor_new:
669  *
670  * Creates a new date editor. The current system date
671  * is shown in the editor.
672  *
673  * Returns: pointer to a new @HildonDateEditor widget.
674  */
675 GtkWidget *hildon_date_editor_new(void)
676 {
677     return GTK_WIDGET(g_object_new(HILDON_TYPE_DATE_EDITOR, NULL));
678 }
679
680 /**
681  * hildon_date_editor_set_date:
682  * @date: the @HildonDateEditor widget
683  * @year: year
684  * @month: month
685  * @day: day
686  *
687  * Sets the date shown in the editor. 
688  */
689 void hildon_date_editor_set_date(HildonDateEditor * editor,
690                                  guint year, guint month, guint day)
691 {
692     HildonDateEditorPrivate *priv;
693
694     g_return_if_fail(HILDON_IS_DATE_EDITOR(editor));
695
696     /* This function cannot be implemented by calling
697        component setters, since applying the individual
698        values one by one can make the date temporarily
699        invalid (depending on what the previous values were), 
700        which in turn causes that the desired date
701        is not set (even though it's valid). We must set all the
702        components at one go and not try to do any validation etc
703        there in between. */
704
705     g_return_if_fail(HILDON_IS_DATE_EDITOR(editor));
706     priv = HILDON_DATE_EDITOR_GET_PRIVATE(editor);
707
708     if (g_date_valid_dmy(day, month, year))
709     {
710         GDate date;
711         gchar buffer[256];
712         
713         priv->year = year;
714         priv->month = month;
715         priv->day = day;
716         
717         g_date_set_dmy(&date, day, month, year);
718
719         /* We apply the new values, but do not want automatic focus move
720            etc to take place */
721         g_snprintf(buffer, sizeof(buffer), "%04d", year);
722         g_signal_handlers_block_by_func(priv->y_entry, 
723             (gpointer) hildon_date_editor_entry_changed, editor);
724         gtk_entry_set_text(GTK_ENTRY(priv->y_entry), buffer);
725         g_signal_handlers_unblock_by_func(priv->y_entry, 
726             (gpointer) hildon_date_editor_entry_changed, editor);
727
728         g_date_strftime(buffer, sizeof(buffer), "%m", &date);
729         g_signal_handlers_block_by_func(priv->m_entry, 
730             (gpointer) hildon_date_editor_entry_changed, editor);
731         gtk_entry_set_text(GTK_ENTRY(priv->m_entry), buffer);
732         g_signal_handlers_unblock_by_func(priv->m_entry, 
733             (gpointer) hildon_date_editor_entry_changed, editor);
734
735         g_date_strftime(buffer, sizeof(buffer), "%d", &date);
736         g_signal_handlers_block_by_func(priv->d_entry, 
737             (gpointer) hildon_date_editor_entry_changed, editor);
738         gtk_entry_set_text(GTK_ENTRY(priv->d_entry), buffer);
739         g_signal_handlers_unblock_by_func(priv->d_entry, 
740             (gpointer) hildon_date_editor_entry_changed, editor);
741   
742         g_object_notify(G_OBJECT(editor), "year");
743         g_object_notify(G_OBJECT(editor), "month");
744         g_object_notify(G_OBJECT(editor), "day");
745     }
746 }
747
748 /**
749  * hildon_date_editor_get_date:
750  * @date: the @HildonDateEditor widget
751  * @year: year
752  * @month: month
753  * @day: day
754  *
755  * Returns: the year, month, and day currently on the
756  * date editor.
757  */
758 void hildon_date_editor_get_date(HildonDateEditor * date,
759                                  guint * year, guint * month, guint * day)
760 {
761     HildonDateEditorPrivate *priv;
762
763     g_return_if_fail(HILDON_IS_DATE_EDITOR(date));
764     g_return_if_fail(year);
765     g_return_if_fail(month);
766     g_return_if_fail(day);
767
768     priv = HILDON_DATE_EDITOR_GET_PRIVATE(date);
769
770     /* FIXME: The role of priv->{day,month,year} members vs. entry contents
771               is unclear. They do not neccesarily match and still the texts are
772               used as return values and members for some internal validation!!
773               At least a partly reason is to allow empty text to become
774               0 return value, while members are restricted to valid ranges?!
775               However, if we change the current way, we are likely to break 
776               some applications  if they rely on some specific way how this 
777               widget currently handles empty values and temporarily invalid values.
778
779               The key issue is this: What should the _get methods return while
780               user is editing a field and the result is incomplete. The
781               partial result? The last good result? If we return partial result
782               we also need a way to inform if the date is not valid. Current
783               implementation is some kind of hybrid of these two...
784
785               for example:
786                  hildon_date_editor_set_day(editor, hildon_date_editor_get_day(editor));
787
788               easily fails, since set_day tries to force validity while get_day
789               doesn't do that.
790
791               Proposal: Always return the same values that are shown in the
792                         fields. We add a separate flag (Or use GDate) to 
793                         indicate if the current date is valid. This would allow 
794                         setters to make the date invalid as well.
795     */
796     *year = /*priv->year;*/
797       (guint) atoi(gtk_entry_get_text(GTK_ENTRY(priv->y_entry)));
798     *month = /*priv->month;*/
799       (guint) atoi(gtk_entry_get_text(GTK_ENTRY(priv->m_entry)));
800     *day = /*priv->day;*/ 
801       (guint) atoi(gtk_entry_get_text(GTK_ENTRY(priv->d_entry)));
802 }
803
804 static gboolean hildon_date_editor_icon_press(GtkWidget * widget,
805                                               GdkEventButton * event,
806                                               gpointer data)
807 {
808     g_assert(GTK_IS_WIDGET(widget));
809     g_assert(event != NULL);
810     g_assert(HILDON_IS_DATE_EDITOR(data));
811
812     if (event->button == 1)
813        hildon_date_editor_set_calendar_icon_state(HILDON_DATE_EDITOR(data), TRUE);
814
815     return FALSE;
816 }
817
818 static gboolean hildon_date_editor_entry_released(GtkWidget * widget,
819                                                   GdkEventButton * event,
820                                                   gpointer data)
821 {
822     HildonDateEditor *ed;
823     HildonDateEditorPrivate *priv;
824
825     ed = HILDON_DATE_EDITOR(data);
826     priv = HILDON_DATE_EDITOR_GET_PRIVATE(ed);
827
828     if (event->button == 1) {
829         /* We might not get focus because of invalid values in entries */
830         if (GTK_WIDGET_HAS_FOCUS(widget))
831                         g_idle_add((GSourceFunc)
832                                         _hildon_date_editor_entry_select_all, GTK_ENTRY(widget));
833     }
834
835     return FALSE;
836 }
837
838 static gboolean hildon_date_editor_entry_focusin(GtkWidget * widget,
839                                                  GdkEventFocus * event,
840                                                  gpointer data)
841 {
842     if (!GTK_ENTRY(widget)->button)
843     {
844                 g_idle_add((GSourceFunc)
845                                 _hildon_date_editor_entry_select_all, GTK_ENTRY(widget));
846     }
847
848     return FALSE;
849 }
850
851
852 static void popup_calendar_dialog(HildonDateEditor *ed)
853 {
854     guint y = 0, m = 0, d = 0;
855     GtkWidget *popup;
856     GtkWidget *parent;
857     guint result;
858     GValue val = {0, };
859
860     hildon_date_editor_get_date(ed, &y, &m, &d);
861
862     parent = gtk_widget_get_ancestor(GTK_WIDGET(ed), GTK_TYPE_WINDOW);
863     popup = hildon_calendar_popup_new(GTK_WINDOW(parent), y, m, d);
864
865     g_value_init(&val, G_TYPE_INT);
866     /* Set max/min year in calendar popup to date editor values */
867     g_object_get_property(G_OBJECT(ed), "min-year", &val);
868     g_object_set_property(G_OBJECT(popup), "min-year", &val);
869     g_object_get_property(G_OBJECT(ed), "max-year", &val);
870     g_object_set_property(G_OBJECT(popup), "max-year", &val);
871
872     /* Pop up calendar */
873     result = gtk_dialog_run(GTK_DIALOG(popup));
874     switch (result) {
875     case GTK_RESPONSE_OK:
876     case GTK_RESPONSE_ACCEPT:
877         hildon_calendar_popup_get_date(HILDON_CALENDAR_POPUP(popup), &y,
878                                        &m, &d);
879         hildon_date_editor_set_date(ed, y, m, d);
880     }
881
882     gtk_widget_destroy(popup);
883 }
884
885 /* button released */
886 static gboolean hildon_date_editor_released(GtkWidget * widget,
887                                             GdkEventButton * event,
888                                             gpointer data)
889 {
890     HildonDateEditor *ed;
891
892     g_assert(GTK_IS_WIDGET(widget));
893     g_assert(event != NULL);
894     g_assert(HILDON_IS_DATE_EDITOR(data));
895
896     ed = HILDON_DATE_EDITOR(data);
897
898     if (hildon_date_editor_set_calendar_icon_state(ed, FALSE))
899         popup_calendar_dialog(ed);
900
901     return FALSE;
902 }
903
904 /* This is called whenever some editor filed loses the focus and
905    when the all of the fields are filled. 
906    Earlier this was called whenever an entry changed */
907 /* FIXME: Validation on focus_out is broken by concept */
908 static void
909 hildon_date_editor_entry_validate(GtkWidget *widget, gpointer data)
910 {
911     HildonDateEditor *ed;
912     HildonDateEditorPrivate *priv;
913     gint d, m, y, max_days;
914     gboolean r;  /* temp return values for signals */
915     const gchar *text;        
916     gint error_code = NO_ERROR;
917
918     g_assert(HILDON_IS_DATE_EDITOR(data));
919     g_assert(GTK_IS_ENTRY(widget));
920
921     ed = HILDON_DATE_EDITOR(data);
922     priv = HILDON_DATE_EDITOR_GET_PRIVATE(ed);
923
924     if (priv->skip_validation)
925        return;
926
927     /*check if the calling entry is empty*/
928     text = gtk_entry_get_text(GTK_ENTRY(widget));
929     if(text == NULL || text[0] == 0)
930     {
931       if (widget == priv->d_entry)
932          g_signal_emit(ed, date_editor_signals[DATE_ERROR], 0, EMPTY_DAY, &r);
933       else if(widget == priv->m_entry)
934          g_signal_emit(ed, date_editor_signals[DATE_ERROR], 0, EMPTY_MONTH, &r);
935       else
936          g_signal_emit(ed, date_editor_signals[DATE_ERROR], 0, EMPTY_YEAR, &r);
937
938       return;
939     }
940
941     /* Ok, we now check validity. Some fields can be empty */
942     text = gtk_entry_get_text(GTK_ENTRY(priv->d_entry));
943     if (text == NULL || text[0] == 0) return;
944     d = atoi(text);
945     text = gtk_entry_get_text(GTK_ENTRY(priv->m_entry));
946     if (text == NULL || text[0] == 0) return;
947     m = atoi(text);
948     text = gtk_entry_get_text(GTK_ENTRY(priv->y_entry));
949     if (text == NULL || text[0] == 0) return;
950     y = atoi(text);
951
952     /* Did it actually change? */
953     if (d != priv->day || m != priv->month || y != priv->year)
954     {
955         /* We could/should use hildon_date_editor_set_year and such functions
956          * to set the date, instead of use gtk_entry_set_text, and then change
957          * the priv member but hildon_date_editor_set_year and such functions
958          * check if the date is valid, we do want to do date validation check
959          * here according to spec */
960
961         /* Validate month */
962         if(widget == priv->m_entry) {
963             if(m < 1) {
964                 error_code = MIN_MONTH;
965                 m = 1;
966             }
967             else if (m > 12) {
968                 error_code = MAX_MONTH;
969                 m = 12;
970             }
971         }
972
973         /* Validate year */
974         if(widget == priv->y_entry) {
975             if (y < priv->min_year) {
976                 error_code = MIN_YEAR;
977                 y = priv->min_year;
978             }
979             else if (y > priv->max_year) {
980                 error_code = MAX_YEAR;
981                 y = priv->max_year;
982             }
983         }
984
985         /* Validate day. We have to do this in every case, since
986            changing month or year can make the day number to be invalid */
987         max_days = g_date_get_days_in_month(m,y);
988         if(d < 1) {
989            error_code = MIN_DAY;
990            d = 1;
991         }
992         else if (d > max_days) {
993            if (d > 31) {         
994                error_code = MAX_DAY;
995                d = max_days;
996            }
997            else {                /* the date does not exist (is invalid) */
998                error_code = INVALID_DATE;
999                /* check what was changed and restore previous value */
1000                if ( widget == priv->y_entry )
1001                    y = priv->year;
1002                else if ( widget == priv->m_entry )
1003                    m = priv->month;
1004                else
1005                    d = priv->day;
1006            }
1007         }
1008
1009         if (error_code != NO_ERROR)
1010         {
1011             g_signal_emit(ed, date_editor_signals[DATE_ERROR], 0, error_code, &r);
1012
1013             g_idle_add ((GSourceFunc) 
1014                     _hildon_date_editor_entry_select_all, 
1015                     widget);
1016
1017             /* Set focus back to invalid entry */
1018             gtk_widget_grab_focus(widget);
1019         }
1020     }
1021
1022     /* Fix and reformat the date after error signal is processed. 
1023        reformatting can be needed even in a such case that numerical
1024        values of the date components are the same as earlier. */
1025     hildon_date_editor_set_date(ed, y, m, d);
1026 }
1027
1028 /* When entry becomes full, we move the focus to the next field.
1029    If we are on the last field, the whole contents are validated. */
1030 static void
1031 hildon_date_editor_entry_changed(GtkEditable *ed, gpointer data)
1032 {
1033   GtkEntry *entry;
1034
1035   g_assert(GTK_IS_ENTRY(ed));
1036   g_assert(HILDON_IS_DATE_EDITOR(data));
1037
1038   entry = GTK_ENTRY(ed);
1039
1040   /* If day entry is full, move to next entry or validate */
1041   if (g_utf8_strlen(gtk_entry_get_text(entry), -1) == gtk_entry_get_max_length(entry))
1042     {
1043       if (!gtk_widget_child_focus(GTK_WIDGET(data), GTK_DIR_RIGHT))
1044         hildon_date_editor_entry_validate(GTK_WIDGET(entry), data);
1045     }
1046 }
1047
1048 static gboolean hildon_date_editor_keyrelease(GtkWidget * widget,
1049                                               GdkEventKey * event,
1050                                               gpointer data)
1051 {
1052     HildonDateEditor *ed;
1053     HildonDateEditorPrivate *priv;
1054
1055     g_return_val_if_fail(data, FALSE);
1056     g_return_val_if_fail(widget, FALSE);
1057
1058     ed = HILDON_DATE_EDITOR(data);
1059     priv = HILDON_DATE_EDITOR_GET_PRIVATE(ed);
1060
1061     if (event->keyval == GDK_KP_Enter || event->keyval == GDK_Return ||
1062         event->keyval == GDK_ISO_Enter) {
1063         if (hildon_date_editor_set_calendar_icon_state(ed, FALSE))
1064         {
1065             popup_calendar_dialog(ed);
1066             return TRUE;
1067         }
1068     } else if (event->keyval == GDK_Escape)
1069                 priv->skip_validation = FALSE;
1070     
1071     return FALSE;
1072 }
1073
1074 /* keyboard handling */
1075 static gboolean hildon_date_editor_keypress(GtkWidget * widget,
1076                                             GdkEventKey * event,
1077                                             gpointer data)
1078 {
1079     HildonDateEditor *ed;
1080     HildonDateEditorPrivate *priv;
1081     gint pos;
1082
1083     g_assert(HILDON_IS_DATE_EDITOR(data));
1084     g_assert(GTK_IS_ENTRY(widget));
1085
1086     ed = HILDON_DATE_EDITOR(data);
1087     priv = HILDON_DATE_EDITOR_GET_PRIVATE(ed);
1088     pos = gtk_editable_get_position(GTK_EDITABLE(widget));
1089         
1090     switch (event->keyval) {
1091     case GDK_Left:
1092         (void) gtk_widget_child_focus(GTK_WIDGET(data), GTK_DIR_LEFT);
1093         return TRUE;
1094         break;
1095     case GDK_Right:
1096         (void) gtk_widget_child_focus(GTK_WIDGET(data), GTK_DIR_RIGHT);
1097         return TRUE;
1098         break;
1099     case GDK_Return:
1100     case GDK_ISO_Enter:
1101         /* Ignore return value, since we want to handle event at all times.
1102            otherwise vkb would popup when the keyrepeat starts. */
1103         (void) hildon_date_editor_set_calendar_icon_state(ed, TRUE);
1104         return TRUE;
1105
1106     case GDK_Escape:
1107         priv->skip_validation = TRUE;
1108         break;
1109     default:
1110         break;
1111     }
1112
1113     return FALSE;
1114 }
1115
1116 static gboolean hildon_date_editor_entry_focus_out(GtkWidget * widget,
1117                                              GdkEventFocus * event,
1118                                              gpointer data)
1119 {
1120   hildon_date_editor_entry_validate(widget, data);
1121   return FALSE;
1122 }
1123
1124 static gboolean 
1125 hildon_date_editor_date_error(HildonDateEditor *editor,
1126                               HildonDateEditorErrorType type)
1127 {
1128   HildonDateEditorPrivate *priv = HILDON_DATE_EDITOR_GET_PRIVATE(editor);
1129
1130   switch(type)
1131     {
1132     case MAX_DAY:
1133       gtk_infoprintf(NULL, _("ckct_ib_maximum_value"), 31);
1134       break;
1135     case MAX_MONTH:
1136       gtk_infoprintf(NULL, _("ckct_ib_maximum_value"), 12);
1137       break;
1138     case MAX_YEAR:
1139       gtk_infoprintf(NULL, _("ckct_ib_maximum_value"), priv->max_year);
1140       break;
1141     case MIN_DAY:
1142     case MIN_MONTH:
1143       gtk_infoprintf(NULL, _("ckct_ib_minimum_value"), 1);
1144       break;
1145     case MIN_YEAR:
1146       gtk_infoprintf(NULL, _("ckct_ib_minimum_value"), priv->min_year);
1147       break;
1148     case EMPTY_DAY:
1149       gtk_infoprintf(NULL, _("ckct_ib_set_a_value_within_range"), 1, 31);
1150       break;
1151     case EMPTY_MONTH:
1152       gtk_infoprintf(NULL, _("ckct_ib_set_a_value_within_range"), 1, 12);
1153       break;
1154     case EMPTY_YEAR:
1155       gtk_infoprintf(NULL, _("ckct_ib_set_a_value_within_range"),
1156                      priv->min_year, priv->max_year);
1157       break;
1158     case INVALID_DATE:
1159       gtk_infoprint(NULL, _("ckct_ib_date_does_not_exist"));
1160       break;
1161    default:
1162       /*default error message ?*/
1163       break;
1164     }
1165   return TRUE;
1166 }
1167
1168 static void hildon_date_editor_size_request(GtkWidget * widget,
1169                                             GtkRequisition * requisition)
1170 {
1171     HildonDateEditor *ed;
1172     HildonDateEditorPrivate *priv;
1173     GtkRequisition f_req, img_req;
1174
1175     g_assert(GTK_IS_WIDGET(widget));
1176     g_assert(requisition != NULL);
1177
1178     ed = HILDON_DATE_EDITOR(widget);
1179     priv = HILDON_DATE_EDITOR_GET_PRIVATE(ed);
1180
1181     /* Our own children affect our size */
1182     gtk_widget_size_request(priv->frame, &f_req);
1183     gtk_widget_size_request(priv->d_event_box_image, &img_req);
1184
1185     /* calculate our size */
1186     requisition->width = f_req.width + img_req.width + HILDON_MARGIN_DEFAULT;
1187
1188     /* FIXME: Fixed size is bad! We should use the maximum of our children, but
1189               doing so would break current pixel specifications, since
1190               the text entry by itself is already 30px tall + then frame takes
1191               something */
1192     requisition->height = DATE_EDITOR_HEIGHT;
1193 }
1194
1195 static void hildon_date_editor_size_allocate(GtkWidget * widget,
1196                                              GtkAllocation * allocation)
1197 {
1198     HildonDateEditor *ed;
1199     HildonDateEditorPrivate *priv;
1200     GtkAllocation f_alloc, img_alloc;
1201     GtkRequisition req;
1202     GtkRequisition max_req;
1203     GList *iter;
1204
1205     g_assert(GTK_IS_WIDGET(widget));
1206     g_assert(allocation != NULL);
1207
1208     ed = HILDON_DATE_EDITOR(widget);
1209     priv = HILDON_DATE_EDITOR_GET_PRIVATE(ed);
1210
1211     widget->allocation = *allocation;
1212
1213     gtk_widget_get_child_requisition(widget, &max_req);
1214
1215     /* Center vertically */
1216     f_alloc.y = img_alloc.y = allocation->y +
1217             MAX(allocation->height - max_req.height, 0) / 2;
1218
1219     /* Center horizontally */
1220     f_alloc.x = img_alloc.x = allocation->x +
1221             MAX(allocation->width - max_req.width, 0) / 2;
1222
1223     /* allocate frame */
1224         if (GTK_WIDGET_VISIBLE(priv->frame)) {
1225             gtk_widget_get_child_requisition(priv->frame, &req);
1226
1227             f_alloc.width = req.width;
1228             f_alloc.height = max_req.height;
1229             gtk_widget_size_allocate(priv->frame, &f_alloc);
1230         }
1231
1232     /* allocate icon */
1233         if (GTK_WIDGET_VISIBLE(priv->d_event_box_image)) {
1234             gtk_widget_get_child_requisition(priv->d_event_box_image,
1235                                              &req);
1236
1237             img_alloc.x += f_alloc.width + HILDON_MARGIN_DEFAULT;
1238             img_alloc.width = req.width;
1239             img_alloc.height = max_req.height;
1240             gtk_widget_size_allocate(priv->d_event_box_image, &img_alloc);
1241         }
1242
1243     /* FIXME: We really should not alloc delimeters by hand (since they
1244               are not our own children, but we need to force to appear 
1245               higher. This ugly hack is needed to compensate the forced
1246               height in size_request. */
1247     for (iter = priv->delims; iter; iter = iter->next)
1248     {
1249         GtkWidget *delim;
1250         GtkAllocation alloc;
1251
1252         delim = GTK_WIDGET(iter->data);
1253         alloc = delim->allocation;
1254         alloc.height = max_req.height; 
1255         alloc.y = priv->d_entry->allocation.y - 2;
1256
1257         gtk_widget_size_allocate(delim, &alloc);
1258     }
1259 }
1260
1261 /**
1262  * hildon_date_editor_set_year:
1263  * @editor: the @HildonDateEditor widget
1264  * @year: year
1265  *
1266  * Sets the year shown in the editor. 
1267  *
1268  * Returns: TRUE if the year is valid
1269  */
1270 gboolean hildon_date_editor_set_year(HildonDateEditor *editor, guint year)
1271 {
1272   HildonDateEditorPrivate *priv;
1273   g_return_val_if_fail( HILDON_IS_DATE_EDITOR(editor), FALSE );
1274   priv = HILDON_DATE_EDITOR_GET_PRIVATE(editor);
1275
1276   if (g_date_valid_dmy(priv->day, priv->month, year))
1277   {
1278     gchar buffer[256];
1279     priv->year = year;
1280
1281     g_snprintf(buffer, sizeof(buffer), "%04d", year);
1282
1283     /* We apply the new day, but do not want automatic focus move
1284        etc to take place */
1285     g_signal_handlers_block_by_func(priv->y_entry, 
1286         (gpointer) hildon_date_editor_entry_changed, editor);
1287     gtk_entry_set_text(GTK_ENTRY(priv->y_entry), buffer);
1288     g_signal_handlers_unblock_by_func(priv->y_entry, 
1289         (gpointer) hildon_date_editor_entry_changed, editor);
1290
1291     g_object_notify(G_OBJECT(editor), "year");
1292     return TRUE;
1293   }
1294
1295   return FALSE;
1296 }
1297
1298 /**
1299  * hildon_date_editor_set_month:
1300  * @editor: the @HildonDateEditor widget
1301  * @month: month
1302  *
1303  * Sets the month shown in the editor. 
1304  *
1305  * Returns: TRUE if the month is valid
1306  */
1307 gboolean hildon_date_editor_set_month(HildonDateEditor *editor, guint month)
1308 {
1309   HildonDateEditorPrivate *priv;
1310   g_return_val_if_fail( HILDON_IS_DATE_EDITOR(editor), FALSE );
1311   priv = HILDON_DATE_EDITOR_GET_PRIVATE(editor);
1312
1313   if (g_date_valid_dmy(priv->day, month, priv->year))
1314   {
1315     GDate date;
1316     gchar buffer[256];
1317
1318     priv->month = month;
1319     g_date_set_dmy(&date, priv->day, month, priv->year);
1320     g_date_strftime(buffer, sizeof(buffer), "%m", &date);
1321
1322     /* We apply the new day, but do not want automatic focus move
1323        etc to take place */
1324     g_signal_handlers_block_by_func(priv->m_entry, 
1325         (gpointer) hildon_date_editor_entry_changed, editor);
1326     gtk_entry_set_text(GTK_ENTRY(priv->m_entry), buffer);
1327     g_signal_handlers_unblock_by_func(priv->m_entry, 
1328         (gpointer) hildon_date_editor_entry_changed, editor);
1329
1330     g_object_notify(G_OBJECT(editor), "month");
1331     return TRUE;
1332   }
1333   return FALSE;
1334 }
1335
1336 /**
1337  * hildon_date_editor_set_day:
1338  * @editor: the @HildonDateEditor widget
1339  * @day: day
1340  *
1341  * Sets the day shown in the editor. 
1342  *
1343  * Returns: TRUE if the day is valid
1344  */
1345 gboolean hildon_date_editor_set_day(HildonDateEditor *editor, guint day)
1346 {
1347   HildonDateEditorPrivate *priv;
1348
1349   g_return_val_if_fail( HILDON_IS_DATE_EDITOR(editor), FALSE );
1350   priv = HILDON_DATE_EDITOR_GET_PRIVATE(editor);
1351
1352   if (g_date_valid_dmy(day, priv->month, priv->year))
1353   {
1354     GDate date;
1355     gchar buffer[256];
1356
1357     priv->day = day;
1358     g_date_set_dmy(&date, day, priv->month, priv->year);
1359     g_date_strftime(buffer, sizeof(buffer), "%d", &date);
1360
1361     /* We apply the new day, but do not want automatic focus move
1362        etc to take place */
1363     g_signal_handlers_block_by_func(priv->d_entry, 
1364         (gpointer) hildon_date_editor_entry_changed, editor);
1365     gtk_entry_set_text(GTK_ENTRY(priv->d_entry), buffer);
1366     g_signal_handlers_unblock_by_func(priv->d_entry, 
1367         (gpointer) hildon_date_editor_entry_changed, editor);
1368
1369     g_object_notify(G_OBJECT(editor), "day");
1370     return TRUE;
1371   }
1372   return FALSE;
1373 }
1374
1375 /**
1376  * hildon_date_editor_get_year:
1377  * @editor: the @HildonDateEditor widget
1378  *
1379  * Returns: the current year shown in the editor.
1380  */
1381 guint hildon_date_editor_get_year(HildonDateEditor *editor)
1382 {
1383   HildonDateEditorPrivate *priv;
1384   g_return_val_if_fail( HILDON_IS_DATE_EDITOR(editor), 0 );
1385   priv = HILDON_DATE_EDITOR_GET_PRIVATE(editor);
1386   return (guint) atoi(gtk_entry_get_text(GTK_ENTRY(priv->y_entry)));
1387 }
1388
1389 /**
1390  * hildon_date_editor_get_month:
1391  * @editor: the @HildonDateEditor widget
1392  *
1393  * Gets the month shown in the editor. 
1394  *
1395  * Returns: the current month shown in the editor.
1396  */
1397
1398 guint hildon_date_editor_get_month(HildonDateEditor *editor)
1399 {
1400   HildonDateEditorPrivate *priv;
1401   g_return_val_if_fail( HILDON_IS_DATE_EDITOR(editor), 0 );
1402   priv = HILDON_DATE_EDITOR_GET_PRIVATE(editor);
1403   return (guint) atoi(gtk_entry_get_text(GTK_ENTRY(priv->m_entry)));
1404 }
1405
1406 /**
1407  * hildon_date_editor_get_day:
1408  * @editor: the @HildonDateEditor widget
1409  *
1410  * Gets the day shown in the editor. 
1411  *
1412  * Returns: the current day shown in the editor
1413  */
1414
1415 guint hildon_date_editor_get_day(HildonDateEditor *editor)
1416 {
1417   HildonDateEditorPrivate *priv;
1418   g_return_val_if_fail( HILDON_IS_DATE_EDITOR(editor), 0 );
1419   priv = HILDON_DATE_EDITOR_GET_PRIVATE(editor);
1420   return (guint) atoi(gtk_entry_get_text(GTK_ENTRY(priv->d_entry)));
1421 }
1422
1423 /* Idle callback */
1424 static gboolean
1425 _hildon_date_editor_entry_select_all (GtkWidget *widget)
1426 {
1427         GDK_THREADS_ENTER ();
1428         gtk_editable_select_region(GTK_EDITABLE(widget), 0, -1);
1429         GDK_THREADS_LEAVE ();
1430         return FALSE;
1431 }