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