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