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