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