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