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