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