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