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