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