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