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